1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-30 00:57:55 +00:00
This commit is contained in:
Devan-Kerman
2020-09-04 18:07:48 -05:00
parent 3fa6b5bc9d
commit c5eb7a9501
401 changed files with 23699 additions and 25873 deletions

View File

@@ -64,9 +64,6 @@ import net.fabricmc.loader.api.FabricLoader;
public final class ComputerCraft implements ModInitializer { public final class ComputerCraft implements ModInitializer {
public static final String MOD_ID = "computercraft"; public static final String MOD_ID = "computercraft";
public static ItemGroup MAIN_GROUP = FabricItemGroupBuilder.build(new Identifier(MOD_ID, "main"), () -> new ItemStack(ModBlocks.COMPUTER_NORMAL));
// Configuration options // Configuration options
public static final String[] DEFAULT_HTTP_WHITELIST = new String[] {"*"}; public static final String[] DEFAULT_HTTP_WHITELIST = new String[] {"*"};
public static final String[] DEFAULT_HTTP_BLACKLIST = new String[] { public static final String[] DEFAULT_HTTP_BLACKLIST = new String[] {
@@ -76,6 +73,18 @@ public final class ComputerCraft implements ModInitializer {
"192.168.0.0/16", "192.168.0.0/16",
"fd00::/8", "fd00::/8",
}; };
public static final int terminalWidth_computer = 51;
public static final int terminalHeight_computer = 19;
public static final int terminalWidth_turtle = 39;
public static final int terminalHeight_turtle = 13;
public static final int terminalWidth_pocketComputer = 26;
public static final int terminalHeight_pocketComputer = 20;
// Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry();
// Logging
public static final Logger log = LogManager.getLogger(MOD_ID);
public static ItemGroup MAIN_GROUP = FabricItemGroupBuilder.build(new Identifier(MOD_ID, "main"), () -> new ItemStack(ModBlocks.COMPUTER_NORMAL));
public static List<AddressRule> httpRules = Collections.unmodifiableList(Stream.concat(Stream.of(DEFAULT_HTTP_BLACKLIST) public static List<AddressRule> httpRules = Collections.unmodifiableList(Stream.concat(Stream.of(DEFAULT_HTTP_BLACKLIST)
.map(x -> AddressRule.parse(x, Action.DENY.toPartial())) .map(x -> AddressRule.parse(x, Action.DENY.toPartial()))
.filter(Objects::nonNull), .filter(Objects::nonNull),
@@ -85,12 +94,6 @@ public final class ComputerCraft implements ModInitializer {
.collect(Collectors.toList())); .collect(Collectors.toList()));
public static boolean commandRequireCreative = false; public static boolean commandRequireCreative = false;
public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST; public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST;
public static final int terminalWidth_computer = 51;
public static final int terminalHeight_computer = 19;
public static final int terminalWidth_turtle = 39;
public static final int terminalHeight_turtle = 13;
public static final int terminalWidth_pocketComputer = 26;
public static final int terminalHeight_pocketComputer = 20;
public static int computerSpaceLimit = 1000 * 1000; public static int computerSpaceLimit = 1000 * 1000;
public static int floppySpaceLimit = 125 * 1000; public static int floppySpaceLimit = 125 * 1000;
public static int maximumFilesOpen = 128; public static int maximumFilesOpen = 128;
@@ -123,11 +126,34 @@ public final class ComputerCraft implements ModInitializer {
public static boolean turtlesObeyBlockProtection = true; public static boolean turtlesObeyBlockProtection = true;
public static boolean turtlesCanPush = true; public static boolean turtlesCanPush = true;
public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf(TurtleAction.class); public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf(TurtleAction.class);
public static int monitorWidth = 8; public static int monitorWidth = 8;
public static int monitorHeight = 6; public static int monitorHeight = 6;
public static double monitorDistanceSq = 4096; public static double monitorDistanceSq = 4096;
@Override
public void onInitialize() {
Config.load(Paths.get(FabricLoader.getInstance()
.getConfigDir()
.toFile()
.getPath(), MOD_ID + ".json5"));
ComputerCraftProxyCommon.init();
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "colour"), ColourableRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "computer_upgrade"), ComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER,
new Identifier(ComputerCraft.MOD_ID, "pocket_computer_upgrade"),
PocketComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "disk"), DiskRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "printout"), PrintoutRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle"), TurtleRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle_upgrade"), TurtleUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shaped"), ImpostorRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shapeless"), ImpostorShapelessRecipe.SERIALIZER);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "block_named"), BlockNamedEntityLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "player_creative"), PlayerCreativeLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "has_id"), HasComputerIdLootCondition.TYPE);
init();
}
public static final class TurtleUpgrades { public static final class TurtleUpgrades {
public static TurtleModem wirelessModemNormal; public static TurtleModem wirelessModemNormal;
public static TurtleModem wirelessModemAdvanced; public static TurtleModem wirelessModemAdvanced;
@@ -147,36 +173,4 @@ public final class ComputerCraft implements ModInitializer {
public static PocketSpeaker speaker; public static PocketSpeaker speaker;
} }
// Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry();
// Logging
public static final Logger log = LogManager.getLogger(MOD_ID);
@Override
public void onInitialize() {
Config.load(Paths.get(FabricLoader.getInstance()
.getConfigDir()
.toFile()
.getPath(), MOD_ID + ".json5"));
ComputerCraftProxyCommon.init();
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "colour"), ColourableRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "computer_upgrade"), ComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER,
new Identifier(ComputerCraft.MOD_ID, "pocket_computer_upgrade"),
PocketComputerUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "disk"), DiskRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "printout"), PrintoutRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle"), TurtleRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "turtle_upgrade"), TurtleUpgradeRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shaped"), ImpostorRecipe.SERIALIZER);
Registry.register(Registry.RECIPE_SERIALIZER, new Identifier(ComputerCraft.MOD_ID, "impostor_shapeless"), ImpostorShapelessRecipe.SERIALIZER);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier( ComputerCraft.MOD_ID, "block_named" ), BlockNamedEntityLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier( ComputerCraft.MOD_ID, "player_creative" ), PlayerCreativeLootCondition.TYPE);
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier( ComputerCraft.MOD_ID, "has_id" ), HasComputerIdLootCondition.TYPE);
init();
}
} }

View File

@@ -3,8 +3,16 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft; package dan200.computercraft;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
@@ -20,14 +28,18 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories; import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.filesystem.FileMount; import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.ResourceMount; import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.shared.*; import dan200.computercraft.shared.BundledRedstone;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.Peripherals;
import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.peripheral.modem.wired.TileCable; import dan200.computercraft.shared.peripheral.modem.wired.TileCable;
import dan200.computercraft.shared.peripheral.modem.wired.TileWiredModemFull; import dan200.computercraft.shared.peripheral.modem.wired.TileWiredModemFull;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.wired.WiredNode; import dan200.computercraft.shared.wired.WiredNode;
import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils; import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.resource.ReloadableResourceManager; import net.minecraft.resource.ReloadableResourceManager;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -37,29 +49,23 @@ import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull; import net.fabricmc.loader.api.FabricLoader;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public final class ComputerCraftAPIImpl implements IComputerCraftAPI public final class ComputerCraftAPIImpl implements IComputerCraftAPI {
{
public static final ComputerCraftAPIImpl INSTANCE = new ComputerCraftAPIImpl(); public static final ComputerCraftAPIImpl INSTANCE = new ComputerCraftAPIImpl();
private String version; private String version;
private ComputerCraftAPIImpl() private ComputerCraftAPIImpl() {
{
} }
public static InputStream getResourceFile( String domain, String subPath ) public static InputStream getResourceFile(String domain, String subPath) {
{
MinecraftServer server = GameInstanceUtils.getServer(); MinecraftServer server = GameInstanceUtils.getServer();
if (server != null) { if (server != null) {
ReloadableResourceManager manager = (ReloadableResourceManager) server.serverResourceManager.getResourceManager(); ReloadableResourceManager manager = (ReloadableResourceManager) server.serverResourceManager.getResourceManager();
try { try {
return manager.getResource(new Identifier(domain, subPath)).getInputStream(); return manager.getResource(new Identifier(domain, subPath))
.getInputStream();
} catch (IOException ignored) { } catch (IOException ignored) {
return null; return null;
} }
@@ -69,39 +75,36 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
@Nonnull @Nonnull
@Override @Override
public String getInstalledVersion() public String getInstalledVersion() {
{ if (this.version != null) {
if( version != null ) return version; return this.version;
return version = FabricLoader.getInstance().getModContainer( ComputerCraft.MOD_ID ) }
.map( x -> x.getMetadata().getVersion().toString() ) return this.version = FabricLoader.getInstance()
.orElse( "unknown" ); .getModContainer(ComputerCraft.MOD_ID)
.map(x -> x.getMetadata()
.getVersion()
.toString())
.orElse("unknown");
} }
@Override @Override
public int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath ) public int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath) {
{ return IDAssigner.getNextId(parentSubPath);
return IDAssigner.getNextId( parentSubPath );
} }
@Override @Override
public IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity ) public IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity) {
{ try {
try return new FileMount(new File(IDAssigner.getDir(), subPath), capacity);
{ } catch (Exception e) {
return new FileMount( new File( IDAssigner.getDir(), subPath ), capacity );
}
catch( Exception e )
{
return null; return null;
} }
} }
@Override @Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ) public IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath) {
{
MinecraftServer server = GameInstanceUtils.getServer(); MinecraftServer server = GameInstanceUtils.getServer();
if ( server != null ) if (server != null) {
{
ReloadableResourceManager manager = (ReloadableResourceManager) server.serverResourceManager.getResourceManager(); ReloadableResourceManager manager = (ReloadableResourceManager) server.serverResourceManager.getResourceManager();
ResourceMount mount = ResourceMount.get(domain, subPath, manager); ResourceMount mount = ResourceMount.get(domain, subPath, manager);
return mount.exists("") ? mount : null; return mount.exists("") ? mount : null;
@@ -110,65 +113,55 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
} }
@Override @Override
public void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ) public void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) {
{ Peripherals.register(provider);
Peripherals.register( provider );
} }
@Override @Override
public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ) public void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade) {
{ TurtleUpgrades.register(upgrade);
TurtleUpgrades.register( upgrade );
} }
@Override @Override
public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ) public void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider) {
{ BundledRedstone.register(provider);
BundledRedstone.register( provider );
} }
@Override @Override
public int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ) public int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side) {
{ return BundledRedstone.getDefaultOutput(world, pos, side);
return BundledRedstone.getDefaultOutput( world, pos, side );
} }
@Override @Override
public void registerMediaProvider( @Nonnull IMediaProvider provider ) public void registerMediaProvider(@Nonnull IMediaProvider provider) {
{ MediaProviders.register(provider);
MediaProviders.register( provider );
} }
@Override @Override
public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) public void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade) {
{ PocketUpgrades.register(upgrade);
PocketUpgrades.register( upgrade );
} }
@Nonnull @Nonnull
@Override @Override
public IPacketNetwork getWirelessNetwork() public IPacketNetwork getWirelessNetwork() {
{
return WirelessNetwork.getUniversal(); return WirelessNetwork.getUniversal();
} }
@Override @Override
public void registerAPIFactory( @Nonnull ILuaAPIFactory factory ) public void registerAPIFactory(@Nonnull ILuaAPIFactory factory) {
{ ApiFactories.register(factory);
ApiFactories.register( factory );
} }
@Nonnull @Nonnull
@Override @Override
public IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element ) public IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element) {
{ return new WiredNode(element);
return new WiredNode( element );
} }
@Nullable @Nullable
@Override @Override
public IWiredElement getWiredElementAt( @Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side ) public IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side) {
{
BlockEntity tile = world.getBlockEntity(pos); BlockEntity tile = world.getBlockEntity(pos);
if (tile instanceof TileCable) { if (tile instanceof TileCable) {
return ((TileCable) tile).getElement(side); return ((TileCable) tile).getElement(side);

View File

@@ -3,8 +3,12 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api; package dan200.computercraft.api;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaAPIFactory; import dan200.computercraft.api.lua.ILuaAPIFactory;
@@ -19,34 +23,44 @@ import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;
/** /**
* The static entry point to the ComputerCraft API. * The static entry point to the ComputerCraft API.
* *
* Members in this class must be called after mod_ComputerCraft has been initialised, but may be called before it is * Members in this class must be called after mod_ComputerCraft has been initialised, but may be called before it is fully loaded.
* fully loaded.
*/ */
public final class ComputerCraftAPI public final class ComputerCraftAPI {
{ private static IComputerCraftAPI instance;
@Nonnull @Nonnull
public static String getInstalledVersion() @Deprecated
{ public static String getAPIVersion() {
return getInstalledVersion();
}
@Nonnull
public static String getInstalledVersion() {
return getInstance().getInstalledVersion(); return getInstance().getInstalledVersion();
} }
@Nonnull @Nonnull
@Deprecated private static IComputerCraftAPI getInstance() {
public static String getAPIVersion() if (instance != null) {
{ return instance;
return getInstalledVersion(); }
try {
return instance = (IComputerCraftAPI) Class.forName("dan200.computercraft.ComputerCraftAPIImpl")
.getField("INSTANCE")
.get(null);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot find ComputerCraft API", e);
}
} }
/** /**
@@ -58,27 +72,25 @@ public final class ComputerCraftAPI
* @param parentSubPath The folder path within the save directory where the new directory should be created. eg: "computercraft/disk" * @param parentSubPath The folder path within the save directory where the new directory should be created. eg: "computercraft/disk"
* @return The numerical value of the name of the new folder, or -1 if the folder could not be created for some reason. * @return The numerical value of the name of the new folder, or -1 if the folder could not be created for some reason.
* *
* eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now * eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now available for writing.
* available for writing.
* @see #createSaveDirMount(World, String, long) * @see #createSaveDirMount(World, String, long)
*/ */
public static int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath ) public static int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath) {
{ return getInstance().createUniqueNumberedSaveDir(world, parentSubPath);
return getInstance().createUniqueNumberedSaveDir( world, parentSubPath );
} }
/** /**
* Creates a file system mount that maps to a subfolder of the save directory for a given world, and returns it. * Creates a file system mount that maps to a subfolder of the save directory for a given world, and returns it.
* *
* Use in conjunction with IComputerAccess.mount() or IComputerAccess.mountWritable() to mount a folder from the * Use in conjunction with IComputerAccess.mount() or IComputerAccess.mountWritable() to mount a folder from the users save directory onto a computers
* users save directory onto a computers file system. * file system.
* *
* @param world The world for which the save dir can be found. This should be the server side world object. * @param world The world for which the save dir can be found. This should be the server side world object.
* @param subPath The folder path within the save directory that the mount should map to. eg: "computer/disk/42". * @param subPath The folder path within the save directory that the mount should map to. eg: "computer/disk/42". Use createUniqueNumberedSaveDir()
* Use createUniqueNumberedSaveDir() to create a new numbered folder to use. * to create a new numbered folder to use.
* @param capacity The amount of data that can be stored in the directory before it fills up, in bytes. * @param capacity The amount of data that can be stored in the directory before it fills up, in bytes.
* @return The mount, or null if it could be created for some reason. Use IComputerAccess.mount() or IComputerAccess.mountWritable() * @return The mount, or null if it could be created for some reason. Use IComputerAccess.mount() or IComputerAccess.mountWritable() to mount this on a
* to mount this on a Computers' file system. * Computers' file system.
* @see #createUniqueNumberedSaveDir(World, String) * @see #createUniqueNumberedSaveDir(World, String)
* @see IComputerAccess#mount(String, IMount) * @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount) * @see IComputerAccess#mountWritable(String, IWritableMount)
@@ -86,19 +98,17 @@ public final class ComputerCraftAPI
* @see IWritableMount * @see IWritableMount
*/ */
@Nullable @Nullable
public static IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity ) public static IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity) {
{ return getInstance().createSaveDirMount(world, subPath, capacity);
return getInstance().createSaveDirMount( world, subPath, capacity );
} }
/** /**
* Creates a file system mount to a resource folder, and returns it. * Creates a file system mount to a resource folder, and returns it.
* *
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a * Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a resource folder onto a computer's file
* resource folder onto a computer's file system. * system.
* *
* The files in this mount will be a combination of files in all mod jar, and data packs that contain * The files in this mount will be a combination of files in all mod jar, and data packs that contain resources with the same domain and path.
* resources with the same domain and path.
* *
* @param domain The domain under which to look for resources. eg: "mymod". * @param domain The domain under which to look for resources. eg: "mymod".
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles". * @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
@@ -108,9 +118,8 @@ public final class ComputerCraftAPI
* @see IMount * @see IMount
*/ */
@Nullable @Nullable
public static IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ) public static IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath) {
{ return getInstance().createResourceMount(domain, subPath);
return getInstance().createResourceMount( domain, subPath );
} }
/** /**
@@ -120,22 +129,19 @@ public final class ComputerCraftAPI
* @see IPeripheral * @see IPeripheral
* @see IPeripheralProvider * @see IPeripheralProvider
*/ */
public static void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ) public static void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) {
{ getInstance().registerPeripheralProvider(provider);
getInstance().registerPeripheralProvider( provider );
} }
/** /**
* Registers a new turtle turtle for use in ComputerCraft. After calling this, * 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
* users should be able to craft Turtles with your new turtle. It is recommended to call * recommended to call this during the load() method of your mod.
* this during the load() method of your mod.
* *
* @param upgrade The turtle upgrade to register. * @param upgrade The turtle upgrade to register.
* @see ITurtleUpgrade * @see ITurtleUpgrade
*/ */
public static void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ) public static void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade) {
{ getInstance().registerTurtleUpgrade(upgrade);
getInstance().registerTurtleUpgrade( upgrade );
} }
/** /**
@@ -144,9 +150,8 @@ public final class ComputerCraftAPI
* @param provider The bundled redstone provider to register. * @param provider The bundled redstone provider to register.
* @see IBundledRedstoneProvider * @see IBundledRedstoneProvider
*/ */
public static void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ) public static void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider) {
{ getInstance().registerBundledRedstoneProvider(provider);
getInstance().registerBundledRedstoneProvider( provider );
} }
/** /**
@@ -155,13 +160,12 @@ public final class ComputerCraftAPI
* @param world The world this block is in. * @param world The world this block is in.
* @param pos The position this block is at. * @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from. * @param side The side to extract the bundled redstone output from.
* @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned. * @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned. If there is no block
* If there is no block capable of emitting bundled redstone at the location, -1 will be returned. * capable of emitting bundled redstone at the location, -1 will be returned.
* @see IBundledRedstoneProvider * @see IBundledRedstoneProvider
*/ */
public static int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ) public static int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side) {
{ return getInstance().getBundledRedstoneOutput(world, pos, side);
return getInstance().getBundledRedstoneOutput( world, pos, side );
} }
/** /**
@@ -170,14 +174,12 @@ public final class ComputerCraftAPI
* @param provider The media provider to register. * @param provider The media provider to register.
* @see IMediaProvider * @see IMediaProvider
*/ */
public static void registerMediaProvider( @Nonnull IMediaProvider provider ) public static void registerMediaProvider(@Nonnull IMediaProvider provider) {
{ getInstance().registerMediaProvider(provider);
getInstance().registerMediaProvider( provider );
} }
public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) public static void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade) {
{ getInstance().registerPocketUpgrade(upgrade);
getInstance().registerPocketUpgrade( upgrade );
} }
/** /**
@@ -185,14 +187,12 @@ public final class ComputerCraftAPI
* *
* @return The global wireless network, or {@code null} if it could not be fetched. * @return The global wireless network, or {@code null} if it could not be fetched.
*/ */
public static IPacketNetwork getWirelessNetwork() public static IPacketNetwork getWirelessNetwork() {
{
return getInstance().getWirelessNetwork(); return getInstance().getWirelessNetwork();
} }
public static void registerAPIFactory( @Nonnull ILuaAPIFactory factory ) public static void registerAPIFactory(@Nonnull ILuaAPIFactory factory) {
{ getInstance().registerAPIFactory(factory);
getInstance().registerAPIFactory( factory );
} }
/** /**
@@ -203,9 +203,8 @@ public final class ComputerCraftAPI
* @see IWiredElement#getNode() * @see IWiredElement#getNode()
*/ */
@Nonnull @Nonnull
public static IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element ) public static IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element) {
{ return getInstance().createWiredNodeForElement(element);
return getInstance().createWiredNodeForElement( element );
} }
/** /**
@@ -218,63 +217,43 @@ public final class ComputerCraftAPI
* @see IWiredElement#getNode() * @see IWiredElement#getNode()
*/ */
@Nullable @Nullable
public static IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side ) public static IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side) {
{ return getInstance().getWiredElementAt(world, pos, side);
return getInstance().getWiredElementAt( world, pos, side );
} }
private static IComputerCraftAPI instance; public interface IComputerCraftAPI {
@Nonnull
private static IComputerCraftAPI getInstance()
{
if( instance != null ) return instance;
try
{
return instance = (IComputerCraftAPI) Class.forName( "dan200.computercraft.ComputerCraftAPIImpl" )
.getField( "INSTANCE" ).get( null );
}
catch( ReflectiveOperationException e )
{
throw new IllegalStateException( "Cannot find ComputerCraft API", e );
}
}
public interface IComputerCraftAPI
{
@Nonnull @Nonnull
String getInstalledVersion(); String getInstalledVersion();
int createUniqueNumberedSaveDir( @Nonnull World world, @Nonnull String parentSubPath ); int createUniqueNumberedSaveDir(@Nonnull World world, @Nonnull String parentSubPath);
@Nullable @Nullable
IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String subPath, long capacity ); IWritableMount createSaveDirMount(@Nonnull World world, @Nonnull String subPath, long capacity);
@Nullable @Nullable
IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ); IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath);
void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ); void registerPeripheralProvider(@Nonnull IPeripheralProvider provider);
void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ); void registerTurtleUpgrade(@Nonnull ITurtleUpgrade upgrade);
void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ); void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider);
int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ); int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
void registerMediaProvider( @Nonnull IMediaProvider provider ); void registerMediaProvider(@Nonnull IMediaProvider provider);
void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ); void registerPocketUpgrade(@Nonnull IPocketUpgrade upgrade);
@Nonnull @Nonnull
IPacketNetwork getWirelessNetwork(); IPacketNetwork getWirelessNetwork();
void registerAPIFactory( @Nonnull ILuaAPIFactory factory ); void registerAPIFactory(@Nonnull ILuaAPIFactory factory);
@Nonnull @Nonnull
IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element ); IWiredNode createWiredNodeForElement(@Nonnull IWiredElement element);
@Nullable @Nullable
IWiredElement getWiredElementAt( @Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side ); IWiredElement getWiredElementAt(@Nonnull BlockView world, @Nonnull BlockPos pos, @Nonnull Direction side);
} }
} }

View File

@@ -3,75 +3,75 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.client; package dan200.computercraft.api.client;
import net.fabricmc.api.EnvType; import java.util.Objects;
import net.fabricmc.api.Environment;
import javax.annotation.Nonnull;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.util.ModelIdentifier; import net.minecraft.client.util.ModelIdentifier;
import net.minecraft.client.util.math.AffineTransformation; import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull;
import java.util.Objects; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/** /**
* A model to render, combined with a transformation matrix to apply. * A model to render, combined with a transformation matrix to apply.
*/ */
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
public final class TransformedModel public final class TransformedModel {
{
private final BakedModel model; private final BakedModel model;
private final AffineTransformation matrix; private final AffineTransformation matrix;
public TransformedModel( @Nonnull BakedModel model, @Nonnull AffineTransformation matrix ) public TransformedModel(@Nonnull BakedModel model, @Nonnull AffineTransformation matrix) {
{ this.model = Objects.requireNonNull(model);
this.model = Objects.requireNonNull( model ); this.matrix = Objects.requireNonNull(matrix);
this.matrix = Objects.requireNonNull( matrix );
} }
public TransformedModel( @Nonnull BakedModel model ) public TransformedModel(@Nonnull BakedModel model) {
{ this.model = Objects.requireNonNull(model);
this.model = Objects.requireNonNull( model );
this.matrix = AffineTransformation.identity(); this.matrix = AffineTransformation.identity();
} }
public static TransformedModel of( @Nonnull ModelIdentifier location ) public static TransformedModel of(@Nonnull ModelIdentifier location) {
{ BakedModelManager modelManager = MinecraftClient.getInstance()
BakedModelManager modelManager = MinecraftClient.getInstance().getBakedModelManager(); .getBakedModelManager();
return new TransformedModel( modelManager.getModel( location ) ); return new TransformedModel(modelManager.getModel(location));
} }
public static TransformedModel of( @Nonnull ItemStack item, @Nonnull AffineTransformation transform ) public static TransformedModel of(@Nonnull ItemStack item, @Nonnull AffineTransformation transform) {
{ BakedModel model = MinecraftClient.getInstance()
BakedModel model = MinecraftClient.getInstance().getItemRenderer().getModels().getModel( item ); .getItemRenderer()
return new TransformedModel( model, transform ); .getModels()
.getModel(item);
return new TransformedModel(model, transform);
} }
@Nonnull @Nonnull
public BakedModel getModel() public BakedModel getModel() {
{ return this.model;
return model;
} }
@Nonnull @Nonnull
public AffineTransformation getMatrix() public AffineTransformation getMatrix() {
{ return this.matrix;
return matrix;
} }
public void push(MatrixStack matrixStack) { public void push(MatrixStack matrixStack) {
matrixStack.push(); matrixStack.push();
matrixStack.translate(matrix.translation.getX(), matrix.translation.getY(), matrix.translation.getZ()); matrixStack.translate(this.matrix.translation.getX(), this.matrix.translation.getY(), this.matrix.translation.getZ());
matrixStack.multiply(matrix.getRotation2()); matrixStack.multiply(this.matrix.getRotation2());
matrixStack.scale(matrix.scale.getX(), matrix.scale.getY(), matrix.scale.getZ()); matrixStack.scale(this.matrix.scale.getX(), this.matrix.scale.getY(), this.matrix.scale.getZ());
matrixStack.multiply(matrix.rotation1); matrixStack.multiply(this.matrix.rotation1);
} }
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.filesystem; package dan200.computercraft.api.filesystem;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
@@ -12,70 +13,59 @@ import java.time.Instant;
/** /**
* A simple version of {@link BasicFileAttributes}, which provides what information a {@link IMount} already exposes. * A simple version of {@link BasicFileAttributes}, which provides what information a {@link IMount} already exposes.
*/ */
final class FileAttributes implements BasicFileAttributes final class FileAttributes implements BasicFileAttributes {
{ private static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
private static final FileTime EPOCH = FileTime.from( Instant.EPOCH );
private final boolean isDirectory; private final boolean isDirectory;
private final long size; private final long size;
FileAttributes( boolean isDirectory, long size ) FileAttributes(boolean isDirectory, long size) {
{
this.isDirectory = isDirectory; this.isDirectory = isDirectory;
this.size = size; this.size = size;
} }
@Override @Override
public FileTime lastModifiedTime() public FileTime lastModifiedTime() {
{
return EPOCH; return EPOCH;
} }
@Override @Override
public FileTime lastAccessTime() public FileTime lastAccessTime() {
{
return EPOCH; return EPOCH;
} }
@Override @Override
public FileTime creationTime() public FileTime creationTime() {
{
return EPOCH; return EPOCH;
} }
@Override @Override
public boolean isRegularFile() public boolean isRegularFile() {
{ return !this.isDirectory;
return !isDirectory;
} }
@Override @Override
public boolean isDirectory() public boolean isDirectory() {
{ return this.isDirectory;
return isDirectory;
} }
@Override @Override
public boolean isSymbolicLink() public boolean isSymbolicLink() {
{
return false; return false;
} }
@Override @Override
public boolean isOther() public boolean isOther() {
{
return false; return false;
} }
@Override @Override
public long size() public long size() {
{ return this.size;
return size;
} }
@Override @Override
public Object fileKey() public Object fileKey() {
{
return null; return null;
} }
} }

View File

@@ -3,39 +3,37 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.filesystem; package dan200.computercraft.api.filesystem;
import java.io.IOException;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Objects;
/** /**
* An {@link IOException} which occurred on a specific file. * An {@link IOException} which occurred on a specific file.
* *
* This may be thrown from a {@link IMount} or {@link IWritableMount} to give more information about a failure. * This may be thrown from a {@link IMount} or {@link IWritableMount} to give more information about a failure.
*/ */
public class FileOperationException extends IOException public class FileOperationException extends IOException {
{
private static final long serialVersionUID = -8809108200853029849L; private static final long serialVersionUID = -8809108200853029849L;
private final String filename; private final String filename;
public FileOperationException( @Nullable String filename, @Nonnull String message ) public FileOperationException(@Nullable String filename, @Nonnull String message) {
{ super(Objects.requireNonNull(message, "message cannot be null"));
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = filename; this.filename = filename;
} }
public FileOperationException( @Nonnull String message ) public FileOperationException(@Nonnull String message) {
{ super(Objects.requireNonNull(message, "message cannot be null"));
super( Objects.requireNonNull( message, "message cannot be null" ) );
this.filename = null; this.filename = null;
} }
@Nullable @Nullable
public String getFilename() public String getFilename() {
{ return this.filename;
return filename;
} }
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.filesystem; package dan200.computercraft.api.filesystem;
import java.io.IOException; import java.io.IOException;
@@ -12,8 +13,7 @@ import java.io.IOException;
* *
* This exists for use by various APIs - one should not attempt to mount it. * This exists for use by various APIs - one should not attempt to mount it.
*/ */
public interface IFileSystem extends IWritableMount public interface IFileSystem extends IWritableMount {
{
/** /**
* Combine two paths together, reducing them into a normalised form. * Combine two paths together, reducing them into a normalised form.
* *
@@ -21,7 +21,7 @@ public interface IFileSystem extends IWritableMount
* @param child The path to append. * @param child The path to append.
* @return The combined, normalised path. * @return The combined, normalised path.
*/ */
String combine( String path, String child ); String combine(String path, String child);
/** /**
* Copy files from one location to another. * Copy files from one location to another.
@@ -30,7 +30,7 @@ public interface IFileSystem extends IWritableMount
* @param to The location to copy to. This should not exist. * @param to The location to copy to. This should not exist.
* @throws IOException If the copy failed. * @throws IOException If the copy failed.
*/ */
void copy( String from, String to ) throws IOException; void copy(String from, String to) throws IOException;
/** /**
* Move files from one location to another. * Move files from one location to another.
@@ -39,5 +39,5 @@ public interface IFileSystem extends IWritableMount
* @param to The location to move to. This should not exist. * @param to The location to move to. This should not exist.
* @throws IOException If the move failed. * @throws IOException If the move failed.
*/ */
void move( String from, String to ) throws IOException; void move(String from, String to) throws IOException;
} }

View File

@@ -3,51 +3,33 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.filesystem; package dan200.computercraft.api.filesystem;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
/** /**
* Represents a read only part of a virtual filesystem that can be mounted onto a computer using * Represents a read only part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)}.
* {@link IComputerAccess#mount(String, IMount)}.
* *
* Ready made implementations of this interface can be created using * Ready made implementations of this interface can be created using {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or {@link
* {@link ComputerCraftAPI#createSaveDirMount(World, String, long)} or * ComputerCraftAPI#createResourceMount(String, String)}, or you're free to implement it yourselves!
* {@link ComputerCraftAPI#createResourceMount(String, String)}, or you're free to implement it yourselves!
* *
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(String, String)
* @see IComputerAccess#mount(String, IMount) * @see IComputerAccess#mount(String, IMount)
* @see IWritableMount * @see IWritableMount
*/ */
public interface IMount public interface IMount {
{
/**
* Returns whether a file with a given path exists or not.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram"
* @return If the file exists.
* @throws IOException If an error occurs when checking the existence of the file.
*/
boolean exists( @Nonnull String path ) throws IOException;
/**
* Returns whether a file with a given path is a directory or not.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprograms".
* @return If the file exists and is a directory
* @throws IOException If an error occurs when checking whether the file is a directory.
*/
boolean isDirectory( @Nonnull String path ) throws IOException;
/** /**
* Returns the file names of all the files in a directory. * Returns the file names of all the files in a directory.
* *
@@ -55,28 +37,18 @@ public interface IMount
* @param contents A list of strings. Add all the file names to this list. * @param contents A list of strings. Add all the file names to this list.
* @throws IOException If the file was not a directory, or could not be listed. * @throws IOException If the file was not a directory, or could not be listed.
*/ */
void list( @Nonnull String path, @Nonnull List<String> contents ) throws IOException; void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException;
/**
* Returns the size of a file with a given path, in bytes.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return The size of the file, in bytes.
* @throws IOException If the file does not exist, or its size could not be determined.
*/
long getSize( @Nonnull String path ) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents. * Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
* *
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram". * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A channel representing the contents of the file. If the channel implements * @return A channel representing the contents of the file. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to
* {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary positions when using binary * seek to arbitrary positions when using binary mode.
* mode.
* @throws IOException If the file does not exist, or could not be opened. * @throws IOException If the file does not exist, or could not be opened.
*/ */
@Nonnull @Nonnull
ReadableByteChannel openForRead( @Nonnull String path ) throws IOException; ReadableByteChannel openForRead(@Nonnull String path) throws IOException;
/** /**
* Get attributes about the given file. * Get attributes about the given file.
@@ -86,9 +58,37 @@ public interface IMount
* @throws IOException If the file does not exist, or attributes could not be fetched. * @throws IOException If the file does not exist, or attributes could not be fetched.
*/ */
@Nonnull @Nonnull
default BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException default BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
{ if (!this.exists(path)) {
if( !exists( path ) ) throw new FileOperationException( path, "No such file" ); throw new FileOperationException(path, "No such file");
return new FileAttributes( isDirectory( path ), getSize( path ) );
} }
return new FileAttributes(this.isDirectory(path), this.getSize(path));
}
/**
* Returns whether a file with a given path exists or not.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram"
* @return If the file exists.
* @throws IOException If an error occurs when checking the existence of the file.
*/
boolean exists(@Nonnull String path) throws IOException;
/**
* Returns whether a file with a given path is a directory or not.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprograms".
* @return If the file exists and is a directory
* @throws IOException If an error occurs when checking whether the file is a directory.
*/
boolean isDirectory(@Nonnull String path) throws IOException;
/**
* Returns the size of a file with a given path, in bytes.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return The size of the file, in bytes.
* @throws IOException If the file does not exist, or its size could not be determined.
*/
long getSize(@Nonnull String path) throws IOException;
} }

View File

@@ -3,39 +3,41 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.filesystem; package dan200.computercraft.api.filesystem;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.util.OptionalLong; import java.util.OptionalLong;
import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.world.World;
/** /**
* Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)} * Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)} or {@link
* or {@link IComputerAccess#mountWritable(String, IWritableMount)}, that can also be written to. * IComputerAccess#mountWritable(String, IWritableMount)}, that can also be written to.
* *
* Ready made implementations of this interface can be created using * Ready made implementations of this interface can be created using {@link ComputerCraftAPI#createSaveDirMount(World, String, long)}, or you're free to
* {@link ComputerCraftAPI#createSaveDirMount(World, String, long)}, or you're free to implement it yourselves! * implement it yourselves!
* *
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see IComputerAccess#mount(String, IMount) * @see IComputerAccess#mount(String, IMount)
* @see IComputerAccess#mountWritable(String, IWritableMount) * @see IComputerAccess#mountWritable(String, IWritableMount)
* @see IMount * @see IMount
*/ */
public interface IWritableMount extends IMount public interface IWritableMount extends IMount {
{
/** /**
* Creates a directory at a given path inside the virtual file system. * Creates a directory at a given path inside the virtual file system.
* *
* @param path A file path in normalised format, relative to the mount location. ie: "programs/mynewprograms". * @param path A file path in normalised format, relative to the mount location. ie: "programs/mynewprograms".
* @throws IOException If the directory already exists or could not be created. * @throws IOException If the directory already exists or could not be created.
*/ */
void makeDirectory( @Nonnull String path ) throws IOException; void makeDirectory(@Nonnull String path) throws IOException;
/** /**
* Deletes a directory at a given path inside the virtual file system. * Deletes a directory at a given path inside the virtual file system.
@@ -43,33 +45,33 @@ public interface IWritableMount extends IMount
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myoldprograms". * @param path A file path in normalised format, relative to the mount location. ie: "programs/myoldprograms".
* @throws IOException If the file does not exist or could not be deleted. * @throws IOException If the file does not exist or could not be deleted.
*/ */
void delete( @Nonnull String path ) throws IOException; void delete(@Nonnull String path) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link OutputStream} for writing to it. * Opens a file with a given path, and returns an {@link OutputStream} for writing to it.
* *
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram". * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one * @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary
* will be able to seek to arbitrary positions when using binary mode. * positions when using binary mode.
* @throws IOException If the file could not be opened for writing. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
WritableByteChannel openForWrite( @Nonnull String path ) throws IOException; WritableByteChannel openForWrite(@Nonnull String path) throws IOException;
/** /**
* Opens a file with a given path, and returns an {@link OutputStream} for appending to it. * Opens a file with a given path, and returns an {@link OutputStream} for appending to it.
* *
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram". * @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one * @return A stream for writing to. If the channel implements {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary
* will be able to seek to arbitrary positions when using binary mode. * positions when using binary mode.
* @throws IOException If the file could not be opened for writing. * @throws IOException If the file could not be opened for writing.
*/ */
@Nonnull @Nonnull
WritableByteChannel openForAppend( @Nonnull String path ) throws IOException; WritableByteChannel openForAppend(@Nonnull String path) throws IOException;
/** /**
* Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the * Get the amount of free space on the mount, in bytes. You should decrease this value as the user writes to the mount, and write operations should fail
* mount, and write operations should fail once it reaches zero. * once it reaches zero.
* *
* @return The amount of free space, in bytes. * @return The amount of free space, in bytes.
* @throws IOException If the remaining space could not be computed. * @throws IOException If the remaining space could not be computed.
@@ -77,14 +79,12 @@ public interface IWritableMount extends IMount
long getRemainingSpace() throws IOException; long getRemainingSpace() throws IOException;
/** /**
* Get the capacity of this mount. This should be equal to the size of all files/directories on this mount, minus * Get the capacity of this mount. This should be equal to the size of all files/directories on this mount, minus the {@link #getRemainingSpace()}.
* the {@link #getRemainingSpace()}.
* *
* @return The capacity of this mount, in bytes. * @return The capacity of this mount, in bytes.
*/ */
@Nonnull @Nonnull
default OptionalLong getCapacity() default OptionalLong getCapacity() {
{
return OptionalLong.empty(); return OptionalLong.empty();
} }
} }

View File

@@ -3,21 +3,39 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import static dan200.computercraft.api.lua.LuaValues.checkFinite;
import javax.annotation.Nullable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static dan200.computercraft.api.lua.LuaValues.checkFinite; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* The arguments passed to a function. * The arguments passed to a function.
*/ */
public interface IArguments public interface IArguments {
{ /**
* Drop a number of arguments. The returned arguments instance will access arguments at position {@code i + count}, rather than {@code i}. However,
* errors will still use the given argument index.
*
* @param count The number of arguments to drop.
* @return The new {@link IArguments} instance.
*/
IArguments drop(int count);
default Object[] getAll() {
Object[] result = new Object[this.count()];
for (int i = 0; i < result.length; i++) {
result[i] = this.get(i);
}
return result;
}
/** /**
* Get the number of arguments passed to this function. * Get the number of arguments passed to this function.
* *
@@ -40,22 +58,44 @@ public interface IArguments
* @return The argument's value, or {@code null} if not present. * @return The argument's value, or {@code null} if not present.
*/ */
@Nullable @Nullable
Object get( int index ); Object get(int index);
/** /**
* Drop a number of arguments. The returned arguments instance will access arguments at position {@code i + count}, * Get an argument as an integer.
* rather than {@code i}. However, errors will still use the given argument index.
* *
* @param count The number of arguments to drop. * @param index The argument number.
* @return The new {@link IArguments} instance. * @return The argument's value.
* @throws LuaException If the value is not an integer.
*/ */
IArguments drop( int count ); default int getInt(int index) throws LuaException {
return (int) this.getLong(index);
}
default Object[] getAll() /**
{ * Get an argument as a long.
Object[] result = new Object[count()]; *
for( int i = 0; i < result.length; i++ ) result[i] = get( i ); * @param index The argument number.
return result; * @return The argument's value.
* @throws LuaException If the value is not a long.
*/
default long getLong(int index) throws LuaException {
Object value = this.get(index);
if (!(value instanceof Number)) {
throw LuaValues.badArgumentOf(index, "number", value);
}
return LuaValues.checkFiniteNum(index, (Number) value)
.longValue();
}
/**
* Get an argument as a finite number (not infinite or NaN).
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not finite.
*/
default double getFiniteDouble(int index) throws LuaException {
return checkFinite(index, this.getDouble(index));
} }
/** /**
@@ -66,51 +106,14 @@ public interface IArguments
* @throws LuaException If the value is not a number. * @throws LuaException If the value is not a number.
* @see #getFiniteDouble(int) if you require this to be finite (i.e. not infinite or NaN). * @see #getFiniteDouble(int) if you require this to be finite (i.e. not infinite or NaN).
*/ */
default double getDouble( int index ) throws LuaException default double getDouble(int index) throws LuaException {
{ Object value = this.get(index);
Object value = get( index ); if (!(value instanceof Number)) {
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value ); throw LuaValues.badArgumentOf(index, "number", value);
}
return ((Number) value).doubleValue(); return ((Number) value).doubleValue();
} }
/**
* Get an argument as an integer.
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not an integer.
*/
default int getInt( int index ) throws LuaException
{
return (int) getLong( index );
}
/**
* Get an argument as a long.
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not a long.
*/
default long getLong( int index ) throws LuaException
{
Object value = get( index );
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
return LuaValues.checkFiniteNum( index, (Number) value ).longValue();
}
/**
* Get an argument as a finite number (not infinite or NaN).
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not finite.
*/
default double getFiniteDouble( int index ) throws LuaException
{
return checkFinite( index, getDouble( index ) );
}
/** /**
* Get an argument as a boolean. * Get an argument as a boolean.
* *
@@ -118,26 +121,12 @@ public interface IArguments
* @return The argument's value. * @return The argument's value.
* @throws LuaException If the value is not a boolean. * @throws LuaException If the value is not a boolean.
*/ */
default boolean getBoolean( int index ) throws LuaException default boolean getBoolean(int index) throws LuaException {
{ Object value = this.get(index);
Object value = get( index ); if (!(value instanceof Boolean)) {
if( !(value instanceof Boolean) ) throw LuaValues.badArgumentOf( index, "boolean", value ); throw LuaValues.badArgumentOf(index, "boolean", value);
return (Boolean) value;
} }
return (Boolean) value;
/**
* Get an argument as a string.
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not a string.
*/
@Nonnull
default String getString( int index ) throws LuaException
{
Object value = get( index );
if( !(value instanceof String) ) throw LuaValues.badArgumentOf( index, "string", value );
return (String) value;
} }
/** /**
@@ -148,9 +137,24 @@ public interface IArguments
* @throws LuaException If the value is not a string. * @throws LuaException If the value is not a string.
*/ */
@Nonnull @Nonnull
default ByteBuffer getBytes( int index ) throws LuaException default ByteBuffer getBytes(int index) throws LuaException {
{ return LuaValues.encode(this.getString(index));
return LuaValues.encode( getString( index ) ); }
/**
* Get an argument as a string.
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not a string.
*/
@Nonnull
default String getString(int index) throws LuaException {
Object value = this.get(index);
if (!(value instanceof String)) {
throw LuaValues.badArgumentOf(index, "string", value);
}
return (String) value;
} }
/** /**
@@ -163,9 +167,8 @@ public interface IArguments
* @throws LuaException If the value is not a string or not a valid option for this enum. * @throws LuaException If the value is not a string or not a valid option for this enum.
*/ */
@Nonnull @Nonnull
default <T extends Enum<T>> T getEnum( int index, Class<T> klass ) throws LuaException default <T extends Enum<T>> T getEnum(int index, Class<T> klass) throws LuaException {
{ return LuaValues.checkEnum(index, klass, this.getString(index));
return LuaValues.checkEnum( index, klass, getString( index ) );
} }
/** /**
@@ -176,101 +179,14 @@ public interface IArguments
* @throws LuaException If the value is not a table. * @throws LuaException If the value is not a table.
*/ */
@Nonnull @Nonnull
default Map<?, ?> getTable( int index ) throws LuaException default Map<?, ?> getTable(int index) throws LuaException {
{ Object value = this.get(index);
Object value = get( index ); if (!(value instanceof Map)) {
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "table", value ); throw LuaValues.badArgumentOf(index, "table", value);
}
return (Map<?, ?>) value; return (Map<?, ?>) value;
} }
/**
* Get an argument as a double.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
@Nonnull
default Optional<Double> optDouble( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
return Optional.of( ((Number) value).doubleValue() );
}
/**
* Get an argument as an int.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
@Nonnull
default Optional<Integer> optInt( int index ) throws LuaException
{
return optLong( index ).map( Long::intValue );
}
/**
* Get an argument as a long.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
default Optional<Long> optLong( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof Number) ) throw LuaValues.badArgumentOf( index, "number", value );
return Optional.of( LuaValues.checkFiniteNum( index, (Number) value ).longValue() );
}
/**
* Get an argument as a finite number (not infinite or NaN).
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not finite.
*/
default Optional<Double> optFiniteDouble( int index ) throws LuaException
{
Optional<Double> value = optDouble( index );
if( value.isPresent() ) LuaValues.checkFiniteNum( index, value.get() );
return value;
}
/**
* Get an argument as a boolean.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a boolean.
*/
default Optional<Boolean> optBoolean( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof Boolean) ) throw LuaValues.badArgumentOf( index, "boolean", value );
return Optional.of( (Boolean) value );
}
/**
* Get an argument as a string.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a string.
*/
default Optional<String> optString( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof String) ) throw LuaValues.badArgumentOf( index, "string", value );
return Optional.of( (String) value );
}
/** /**
* Get a string argument as a byte array. * Get a string argument as a byte array.
* *
@@ -278,9 +194,26 @@ public interface IArguments
* @return The argument's value, or {@link Optional#empty()} if not present. This is a <em>read only</em> buffer. * @return The argument's value, or {@link Optional#empty()} if not present. This is a <em>read only</em> buffer.
* @throws LuaException If the value is not a string. * @throws LuaException If the value is not a string.
*/ */
default Optional<ByteBuffer> optBytes( int index ) throws LuaException default Optional<ByteBuffer> optBytes(int index) throws LuaException {
{ return this.optString(index).map(LuaValues::encode);
return optString( index ).map( LuaValues::encode ); }
/**
* Get an argument as a string.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a string.
*/
default Optional<String> optString(int index) throws LuaException {
Object value = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof String)) {
throw LuaValues.badArgumentOf(index, "string", value);
}
return Optional.of((String) value);
} }
/** /**
@@ -293,25 +226,9 @@ public interface IArguments
* @throws LuaException If the value is not a string or not a valid option for this enum. * @throws LuaException If the value is not a string or not a valid option for this enum.
*/ */
@Nonnull @Nonnull
default <T extends Enum<T>> Optional<T> optEnum( int index, Class<T> klass ) throws LuaException default <T extends Enum<T>> Optional<T> optEnum(int index, Class<T> klass) throws LuaException {
{ Optional<String> str = this.optString(index);
Optional<String> str = optString( index ); return str.isPresent() ? Optional.of(LuaValues.checkEnum(index, klass, str.get())) : Optional.empty();
return str.isPresent() ? Optional.of( LuaValues.checkEnum( index, klass, str.get() ) ) : Optional.empty();
}
/**
* Get an argument as a table.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a table.
*/
default Optional<Map<?, ?>> optTable( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "map", value );
return Optional.of( (Map<?, ?>) value );
} }
/** /**
@@ -322,9 +239,27 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a number. * @throws LuaException If the value is not a number.
*/ */
default double optDouble( int index, double def ) throws LuaException default double optDouble(int index, double def) throws LuaException {
{ return this.optDouble(index).orElse(def);
return optDouble( index ).orElse( def ); }
/**
* Get an argument as a double.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
@Nonnull
default Optional<Double> optDouble(int index) throws LuaException {
Object value = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof Number)) {
throw LuaValues.badArgumentOf(index, "number", value);
}
return Optional.of(((Number) value).doubleValue());
} }
/** /**
@@ -335,9 +270,39 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a number. * @throws LuaException If the value is not a number.
*/ */
default int optInt( int index, int def ) throws LuaException default int optInt(int index, int def) throws LuaException {
{ return this.optInt(index).orElse(def);
return optInt( index ).orElse( def ); }
/**
* Get an argument as an int.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
@Nonnull
default Optional<Integer> optInt(int index) throws LuaException {
return this.optLong(index).map(Long::intValue);
}
/**
* Get an argument as a long.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a number.
*/
default Optional<Long> optLong(int index) throws LuaException {
Object value = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof Number)) {
throw LuaValues.badArgumentOf(index, "number", value);
}
return Optional.of(LuaValues.checkFiniteNum(index, (Number) value)
.longValue());
} }
/** /**
@@ -348,9 +313,8 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a number. * @throws LuaException If the value is not a number.
*/ */
default long optLong( int index, long def ) throws LuaException default long optLong(int index, long def) throws LuaException {
{ return this.optLong(index).orElse(def);
return optLong( index ).orElse( def );
} }
/** /**
@@ -361,9 +325,23 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not finite. * @throws LuaException If the value is not finite.
*/ */
default double optFiniteDouble( int index, double def ) throws LuaException default double optFiniteDouble(int index, double def) throws LuaException {
{ return this.optFiniteDouble(index).orElse(def);
return optFiniteDouble( index ).orElse( def ); }
/**
* Get an argument as a finite number (not infinite or NaN).
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not finite.
*/
default Optional<Double> optFiniteDouble(int index) throws LuaException {
Optional<Double> value = this.optDouble(index);
if (value.isPresent()) {
LuaValues.checkFiniteNum(index, value.get());
}
return value;
} }
/** /**
@@ -374,9 +352,26 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a boolean. * @throws LuaException If the value is not a boolean.
*/ */
default boolean optBoolean( int index, boolean def ) throws LuaException default boolean optBoolean(int index, boolean def) throws LuaException {
{ return this.optBoolean(index).orElse(def);
return optBoolean( index ).orElse( def ); }
/**
* Get an argument as a boolean.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a boolean.
*/
default Optional<Boolean> optBoolean(int index) throws LuaException {
Object value = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof Boolean)) {
throw LuaValues.badArgumentOf(index, "boolean", value);
}
return Optional.of((Boolean) value);
} }
/** /**
@@ -387,9 +382,8 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a string. * @throws LuaException If the value is not a string.
*/ */
default String optString( int index, String def ) throws LuaException default String optString(int index, String def) throws LuaException {
{ return this.optString(index).orElse(def);
return optString( index ).orElse( def );
} }
/** /**
@@ -400,8 +394,25 @@ public interface IArguments
* @return The argument's value, or {@code def} if none was provided. * @return The argument's value, or {@code def} if none was provided.
* @throws LuaException If the value is not a table. * @throws LuaException If the value is not a table.
*/ */
default Map<?, ?> optTable( int index, Map<Object, Object> def ) throws LuaException default Map<?, ?> optTable(int index, Map<Object, Object> def) throws LuaException {
{ return this.optTable(index).orElse(def);
return optTable( index ).orElse( def ); }
/**
* Get an argument as a table.
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a table.
*/
default Optional<Map<?, ?>> optTable(int index) throws LuaException {
Object value = this.get(index);
if (value == null) {
return Optional.empty();
}
if (!(value instanceof Map)) {
throw LuaValues.badArgumentOf(index, "map", value);
}
return Optional.of((Map<?, ?>) value);
} }
} }

View File

@@ -3,19 +3,18 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IFileSystem; import dan200.computercraft.api.filesystem.IFileSystem;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import javax.annotation.Nullable;
/** /**
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information * An interface passed to {@link ILuaAPIFactory} in order to provide additional information about a computer.
* about a computer.
*/ */
public interface IComputerSystem extends IComputerAccess public interface IComputerSystem extends IComputerAccess {
{
/** /**
* Get the file system for this computer. * Get the file system for this computer.
* *

View File

@@ -3,24 +3,22 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
/** /**
* An interface for representing custom objects returned by peripherals or other Lua objects. * An interface for representing custom objects returned by peripherals or other Lua objects.
* *
* Generally, one does not need to implement this type - it is sufficient to return an object with some methods * Generally, one does not need to implement this type - it is sufficient to return an object with some methods annotated with {@link LuaFunction}. {@link
* annotated with {@link LuaFunction}. {@link IDynamicLuaObject} is useful when you wish your available methods to * IDynamicLuaObject} is useful when you wish your available methods to change at runtime.
* change at runtime.
*/ */
public interface IDynamicLuaObject public interface IDynamicLuaObject {
{
/** /**
* Get the names of the methods that this object implements. This should not change over the course of the object's * Get the names of the methods that this object implements. This should not change over the course of the object's lifetime.
* lifetime.
* *
* @return The method names this object provides. * @return The method names this object provides.
* @see IDynamicPeripheral#getMethodNames() * @see IDynamicPeripheral#getMethodNames()
@@ -31,15 +29,12 @@ public interface IDynamicLuaObject
/** /**
* Called when a user calls one of the methods that this object implements. * Called when a user calls one of the methods that this object implements.
* *
* @param context The context of the currently running lua thread. This can be used to wait for events * @param context The context of the currently running lua thread. This can be used to wait for events or otherwise yield.
* or otherwise yield. * @param method An integer identifying which method index from {@link #getMethodNames()} the computer wishes to call.
* @param method An integer identifying which method index from {@link #getMethodNames()} the computer wishes
* to call.
* @param arguments The arguments for this method. * @param arguments The arguments for this method.
* @return The result of this function. Either an immediate value ({@link MethodResult#of(Object...)} or an * @return The result of this function. Either an immediate value ({@link MethodResult#of(Object...)} or an instruction to yield.
* instruction to yield.
* @throws LuaException If the function threw an exception. * @throws LuaException If the function threw an exception.
*/ */
@Nonnull @Nonnull
MethodResult callMethod( @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException; MethodResult callMethod(@Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) throws LuaException;
} }

View File

@@ -3,22 +3,22 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
/** /**
* Represents a Lua object which is stored as a global variable on computer startup. This must either provide * Represents a Lua object which is stored as a global variable on computer startup. This must either provide {@link LuaFunction} annotated functions or
* {@link LuaFunction} annotated functions or implement {@link IDynamicLuaObject}. * implement {@link IDynamicLuaObject}.
* *
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred * Before implementing this interface, consider alternative methods of providing methods. It is generally preferred to use peripherals to provide
* to use peripherals to provide functionality to users. * functionality to users.
* *
* @see ILuaAPIFactory * @see ILuaAPIFactory
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory) * @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/ */
public interface ILuaAPI public interface ILuaAPI {
{
/** /**
* Get the globals this API will be assigned to. This will override any other global, so you should * Get the globals this API will be assigned to. This will override any other global, so you should
* *
@@ -31,15 +31,13 @@ public interface ILuaAPI
* *
* One should only interact with the file system. * One should only interact with the file system.
*/ */
default void startup() default void startup() {
{
} }
/** /**
* Called every time the computer is ticked. This can be used to process various. * Called every time the computer is ticked. This can be used to process various.
*/ */
default void update() default void update() {
{
} }
/** /**
@@ -47,7 +45,6 @@ public interface ILuaAPI
* *
* This should reset the state of the object, disposing any remaining file handles, or other resources. * This should reset the state of the object, disposing any remaining file handles, or other resources.
*/ */
default void shutdown() default void shutdown() {
{
} }
} }

View File

@@ -3,13 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua;
import dan200.computercraft.api.ComputerCraftAPI; package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI;
/** /**
* Construct an {@link ILuaAPI} for a specific computer. * Construct an {@link ILuaAPI} for a specific computer.
* *
@@ -17,8 +18,7 @@ import javax.annotation.Nullable;
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory) * @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/ */
@FunctionalInterface @FunctionalInterface
public interface ILuaAPIFactory public interface ILuaAPIFactory {
{
/** /**
* Create a new API instance for a given computer. * Create a new API instance for a given computer.
* *
@@ -26,5 +26,5 @@ public interface ILuaAPIFactory
* @return The created API, or {@code null} if one should not be injected. * @return The created API, or {@code null} if one should not be injected.
*/ */
@Nullable @Nullable
ILuaAPI create( @Nonnull IComputerSystem computer ); ILuaAPI create(@Nonnull IComputerSystem computer);
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -12,16 +13,14 @@ import javax.annotation.Nonnull;
* *
* @see MethodResult#yield(Object[], ILuaCallback) * @see MethodResult#yield(Object[], ILuaCallback)
*/ */
public interface ILuaCallback public interface ILuaCallback {
{
/** /**
* Resume this coroutine. * Resume this coroutine.
* *
* @param args The result of resuming this coroutine. These will have the same form as described in * @param args The result of resuming this coroutine. These will have the same form as described in {@link LuaFunction}.
* {@link LuaFunction}.
* @return The result of this continuation. Either the result to return to the callee, or another yield. * @return The result of this continuation. Either the result to return to the callee, or another yield.
* @throws LuaException On an error. * @throws LuaException On an error.
*/ */
@Nonnull @Nonnull
MethodResult resume( Object[] args ) throws LuaException; MethodResult resume(Object[] args) throws LuaException;
} }

View File

@@ -3,28 +3,27 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods * An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods that allow the peripheral call to interface
* that allow the peripheral call to interface with the computer. * with the computer.
*/ */
public interface ILuaContext public interface ILuaContext {
{
/** /**
* Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to * Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to complete. This should be used when you
* complete. This should be used when you need to interact with the world in a thread-safe manner but do not care * need to interact with the world in a thread-safe manner but do not care about the result or you wish to run asynchronously.
* about the result or you wish to run asynchronously.
* *
* When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success * When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success value and the return values, or an
* value and the return values, or an error message if it failed. * error message if it failed.
* *
* @param task The task to execute on the main thread. * @param task The task to execute on the main thread.
* @return The "id" of the task. This will be the first argument to the {@code task_completed} event. * @return The "id" of the task. This will be the first argument to the {@code task_completed} event.
* @throws LuaException If the task could not be queued. * @throws LuaException If the task could not be queued.
* @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously. * @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously.
*/ */
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; long issueMainThreadTask(@Nonnull ILuaTask task) throws LuaException;
} }

View File

@@ -3,27 +3,27 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* A function, which can be called from Lua. If you need to return a table of functions, it is recommended to use * A function, which can be called from Lua. If you need to return a table of functions, it is recommended to use an object with {@link LuaFunction}
* an object with {@link LuaFunction} methods, or implement {@link IDynamicLuaObject}. * methods, or implement {@link IDynamicLuaObject}.
* *
* @see MethodResult#of(Object) * @see MethodResult#of(Object)
*/ */
@FunctionalInterface @FunctionalInterface
public interface ILuaFunction public interface ILuaFunction {
{
/** /**
* Call this function with a series of arguments. Note, this will <em>always</em> be called on the computer thread, * Call this function with a series of arguments. Note, this will <em>always</em> be called on the computer thread, and so its implementation must be
* and so its implementation must be thread-safe. * thread-safe.
* *
* @param arguments The arguments for this function * @param arguments The arguments for this function
* @return The result of calling this function. * @return The result of calling this function.
* @throws LuaException Upon Lua errors. * @throws LuaException Upon Lua errors.
*/ */
@Nonnull @Nonnull
MethodResult call( @Nonnull IArguments arguments ) throws LuaException; MethodResult call(@Nonnull IArguments arguments) throws LuaException;
} }

View File

@@ -9,9 +9,6 @@ package dan200.computercraft.api.lua;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
public interface ILuaObject { public interface ILuaObject {
@Nonnull @Nonnull
String[] getMethodNames(); String[] getMethodNames();

View File

@@ -3,27 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A task which can be executed via {@link ILuaContext#issueMainThreadTask(ILuaTask)} This will be run on the main * A task which can be executed via {@link ILuaContext#issueMainThreadTask(ILuaTask)} This will be run on the main thread, at the beginning of the next
* thread, at the beginning of the * tick.
* next tick.
* *
* @see ILuaContext#issueMainThreadTask(ILuaTask) * @see ILuaContext#issueMainThreadTask(ILuaTask)
*/ */
@FunctionalInterface @FunctionalInterface
public interface ILuaTask public interface ILuaTask {
{
/** /**
* Execute this task. * Execute this task.
* *
* @return The arguments to add to the {@code task_completed} event. * @return The arguments to add to the {@code task_completed} event.
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the * @throws LuaException If you throw any exception from this function, a lua error will be raised with the same message as your exception. Use this
* same message as your exception. Use this to throw appropriate errors if the wrong * to throw appropriate errors if the wrong arguments are supplied to your method.
* arguments are supplied to your method.
*/ */
@Nullable @Nullable
Object[] execute() throws LuaException; Object[] execute() throws LuaException;

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -10,22 +11,19 @@ import javax.annotation.Nullable;
/** /**
* An exception representing an error in Lua, like that raised by the {@code error()} function. * An exception representing an error in Lua, like that raised by the {@code error()} function.
*/ */
public class LuaException extends Exception public class LuaException extends Exception {
{
private static final long serialVersionUID = -6136063076818512651L; private static final long serialVersionUID = -6136063076818512651L;
private final boolean hasLevel; private final boolean hasLevel;
private final int level; private final int level;
public LuaException( @Nullable String message ) public LuaException(@Nullable String message) {
{ super(message);
super( message );
this.hasLevel = false; this.hasLevel = false;
this.level = 1; this.level = 1;
} }
public LuaException( @Nullable String message, int level ) public LuaException(@Nullable String message, int level) {
{ super(message);
super( message );
this.hasLevel = true; this.hasLevel = true;
this.level = level; this.level = level;
} }
@@ -35,19 +33,16 @@ public class LuaException extends Exception
* *
* @return Whether this has an explicit level. * @return Whether this has an explicit level.
*/ */
public boolean hasLevel() public boolean hasLevel() {
{ return this.hasLevel;
return hasLevel;
} }
/** /**
* The level this error is raised at. Level 1 is the function's caller, level 2 is that function's caller, and so * The level this error is raised at. Level 1 is the function's caller, level 2 is that function's caller, and so on.
* on.
* *
* @return The level to raise the error at. * @return The level to raise the error at.
*/ */
public int getLevel() public int getLevel() {
{ return this.level;
return level;
} }
} }

View File

@@ -3,20 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;
import java.util.Optional;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import java.lang.annotation.*;
import java.util.Map;
import java.util.Optional;
/** /**
* Used to mark a Java function which is callable from Lua. * Used to mark a Java function which is callable from Lua.
* *
* Methods annotated with {@link LuaFunction} must be public final instance methods. They can have any number of * Methods annotated with {@link LuaFunction} must be public final instance methods. They can have any number of parameters, but they must be of the
* parameters, but they must be of the following types: * following types:
* *
* <ul> * <ul>
* <li>{@link ILuaContext} (and {@link IComputerAccess} if on a {@link IPeripheral})</li> * <li>{@link ILuaContext} (and {@link IComputerAccess} if on a {@link IPeripheral})</li>
@@ -36,10 +41,9 @@ import java.util.Optional;
* {@link MethodResult#of(Object...)}. * {@link MethodResult#of(Object...)}.
*/ */
@Documented @Documented
@Retention( RetentionPolicy.RUNTIME ) @Retention (RetentionPolicy.RUNTIME)
@Target( ElementType.METHOD ) @Target (ElementType.METHOD)
public @interface LuaFunction public @interface LuaFunction {
{
/** /**
* Explicitly specify the method names of this function. If not given, it uses the name of the annotated method. * Explicitly specify the method names of this function. If not given, it uses the name of the annotated method.
* *
@@ -48,8 +52,7 @@ public @interface LuaFunction
String[] value() default {}; String[] value() default {};
/** /**
* Run this function on the main server thread. This should be specified for any method which interacts with * Run this function on the main server thread. This should be specified for any method which interacts with Minecraft in a thread-unsafe manner.
* Minecraft in a thread-unsafe manner.
* *
* @return Whether this functi * @return Whether this functi
* @see ILuaContext#issueMainThreadTask(ILuaTask) * @see ILuaContext#issueMainThreadTask(ILuaTask)

View File

@@ -3,22 +3,22 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import java.nio.ByteBuffer;
import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Map;
/** /**
* Various utility functions for operating with Lua values. * Various utility functions for operating with Lua values.
* *
* @see IArguments * @see IArguments
*/ */
public final class LuaValues public final class LuaValues {
{ private LuaValues() {
private LuaValues()
{
} }
/** /**
@@ -28,50 +28,15 @@ public final class LuaValues
* @return The encoded string. * @return The encoded string.
*/ */
@Nonnull @Nonnull
public static ByteBuffer encode( @Nonnull String string ) public static ByteBuffer encode(@Nonnull String string) {
{
byte[] chars = new byte[string.length()]; byte[] chars = new byte[string.length()];
for( int i = 0; i < chars.length; i++ ) for (int i = 0; i < chars.length; i++) {
{ char c = string.charAt(i);
char c = string.charAt( i );
chars[i] = c < 256 ? (byte) c : 63; chars[i] = c < 256 ? (byte) c : 63;
} }
return ByteBuffer.wrap( chars ).asReadOnlyBuffer(); return ByteBuffer.wrap(chars)
} .asReadOnlyBuffer();
/**
* Returns a more detailed representation of this number's type. If this is finite, it will just return "number",
* otherwise it returns whether it is infinite or NaN.
*
* @param value The value to extract the type for.
* @return This value's numeric type.
*/
@Nonnull
public static String getNumericType( double value )
{
if( Double.isNaN( value ) ) return "nan";
if( value == Double.POSITIVE_INFINITY ) return "inf";
if( value == Double.NEGATIVE_INFINITY ) return "-inf";
return "number";
}
/**
* Get a string representation of the given value's type.
*
* @param value The value whose type we are trying to compute.
* @return A string representation of the given value's type, in a similar format to that provided by Lua's
* {@code type} function.
*/
@Nonnull
public static String getType( @Nullable Object value )
{
if( value == null ) return "nil";
if( value instanceof String ) return "string";
if( value instanceof Boolean ) return "boolean";
if( value instanceof Number ) return "number";
if( value instanceof Map ) return "table";
return "userdata";
} }
/** /**
@@ -83,9 +48,8 @@ public final class LuaValues
* @return The constructed exception, which should be thrown immediately. * @return The constructed exception, which should be thrown immediately.
*/ */
@Nonnull @Nonnull
public static LuaException badArgumentOf( int index, @Nonnull String expected, @Nullable Object actual ) public static LuaException badArgumentOf(int index, @Nonnull String expected, @Nullable Object actual) {
{ return badArgument(index, expected, getType(actual));
return badArgument( index, expected, getType( actual ) );
} }
/** /**
@@ -97,9 +61,34 @@ public final class LuaValues
* @return The constructed exception, which should be thrown immediately. * @return The constructed exception, which should be thrown immediately.
*/ */
@Nonnull @Nonnull
public static LuaException badArgument( int index, @Nonnull String expected, @Nonnull String actual ) public static LuaException badArgument(int index, @Nonnull String expected, @Nonnull String actual) {
{ return new LuaException("bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")");
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" ); }
/**
* Get a string representation of the given value's type.
*
* @param value The value whose type we are trying to compute.
* @return A string representation of the given value's type, in a similar format to that provided by Lua's {@code type} function.
*/
@Nonnull
public static String getType(@Nullable Object value) {
if (value == null) {
return "nil";
}
if (value instanceof String) {
return "string";
}
if (value instanceof Boolean) {
return "boolean";
}
if (value instanceof Number) {
return "number";
}
if (value instanceof Map) {
return "table";
}
return "userdata";
} }
/** /**
@@ -110,9 +99,8 @@ public final class LuaValues
* @return The input {@code value}. * @return The input {@code value}.
* @throws LuaException If this is not a finite number. * @throws LuaException If this is not a finite number.
*/ */
public static Number checkFiniteNum( int index, Number value ) throws LuaException public static Number checkFiniteNum(int index, Number value) throws LuaException {
{ checkFinite(index, value.doubleValue());
checkFinite( index, value.doubleValue() );
return value; return value;
} }
@@ -124,12 +112,34 @@ public final class LuaValues
* @return The input {@code value}. * @return The input {@code value}.
* @throws LuaException If this is not a finite number. * @throws LuaException If this is not a finite number.
*/ */
public static double checkFinite( int index, double value ) throws LuaException public static double checkFinite(int index, double value) throws LuaException {
{ if (!Double.isFinite(value)) {
if( !Double.isFinite( value ) ) throw badArgument( index, "number", getNumericType( value ) ); throw badArgument(index, "number", getNumericType(value));
}
return value; return value;
} }
/**
* Returns a more detailed representation of this number's type. If this is finite, it will just return "number", otherwise it returns whether it is
* infinite or NaN.
*
* @param value The value to extract the type for.
* @return This value's numeric type.
*/
@Nonnull
public static String getNumericType(double value) {
if (Double.isNaN(value)) {
return "nan";
}
if (value == Double.POSITIVE_INFINITY) {
return "inf";
}
if (value == Double.NEGATIVE_INFINITY) {
return "-inf";
}
return "number";
}
/** /**
* Ensure a string is a valid enum value. * Ensure a string is a valid enum value.
* *
@@ -140,13 +150,14 @@ public final class LuaValues
* @return The parsed enum value. * @return The parsed enum value.
* @throws LuaException If this is not a known enum value. * @throws LuaException If this is not a known enum value.
*/ */
public static <T extends Enum<T>> T checkEnum( int index, Class<T> klass, String value ) throws LuaException public static <T extends Enum<T>> T checkEnum(int index, Class<T> klass, String value) throws LuaException {
{ for (T possibility : klass.getEnumConstants()) {
for( T possibility : klass.getEnumConstants() ) if (possibility.name()
{ .equalsIgnoreCase(value)) {
if( possibility.name().equalsIgnoreCase( value ) ) return possibility; return possibility;
}
} }
throw new LuaException( "bad argument #" + (index + 1) + " (unknown option " + value + ")" ); throw new LuaException("bad argument #" + (index + 1) + " (unknown option " + value + ")");
} }
} }

View File

@@ -3,40 +3,39 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import dan200.computercraft.api.peripheral.IComputerAccess;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.peripheral.IComputerAccess;
/** /**
* The result of invoking a Lua method. * The result of invoking a Lua method.
* *
* Method results either return a value immediately ({@link #of(Object...)} or yield control to the parent coroutine. * Method results either return a value immediately ({@link #of(Object...)} or yield control to the parent coroutine. When the current coroutine is resumed,
* When the current coroutine is resumed, we invoke the provided {@link ILuaCallback#resume(Object[])} callback. * we invoke the provided {@link ILuaCallback#resume(Object[])} callback.
*/ */
public final class MethodResult public final class MethodResult {
{ private static final MethodResult empty = new MethodResult(null, null);
private static final MethodResult empty = new MethodResult( null, null );
private final Object[] result; private final Object[] result;
private final ILuaCallback callback; private final ILuaCallback callback;
private final int adjust; private final int adjust;
private MethodResult( Object[] arguments, ILuaCallback callback ) private MethodResult(Object[] arguments, ILuaCallback callback) {
{
this.result = arguments; this.result = arguments;
this.callback = callback; this.callback = callback;
this.adjust = 0; this.adjust = 0;
} }
private MethodResult( Object[] arguments, ILuaCallback callback, int adjust ) private MethodResult(Object[] arguments, ILuaCallback callback, int adjust) {
{
this.result = arguments; this.result = arguments;
this.callback = callback; this.callback = callback;
this.adjust = adjust; this.adjust = adjust;
@@ -48,28 +47,25 @@ public final class MethodResult
* @return A method result which returns immediately with no values. * @return A method result which returns immediately with no values.
*/ */
@Nonnull @Nonnull
public static MethodResult of() public static MethodResult of() {
{
return empty; return empty;
} }
/** /**
* Return a single value immediately. * Return a single value immediately.
* *
* Integers, doubles, floats, strings, booleans, {@link Map}, {@link Collection}s, arrays and {@code null} will be * Integers, doubles, floats, strings, booleans, {@link Map}, {@link Collection}s, arrays and {@code null} will be converted to their corresponding Lua
* converted to their corresponding Lua type. {@code byte[]} and {@link ByteBuffer} will be treated as binary * type. {@code byte[]} and {@link ByteBuffer} will be treated as binary strings. {@link ILuaFunction} will be treated as a function.
* strings. {@link ILuaFunction} will be treated as a function.
* *
* In order to provide a custom object with methods, one may return a {@link IDynamicLuaObject}, or an arbitrary * In order to provide a custom object with methods, one may return a {@link IDynamicLuaObject}, or an arbitrary class with {@link LuaFunction}
* class with {@link LuaFunction} annotations. Anything else will be converted to {@code nil}. * annotations. Anything else will be converted to {@code nil}.
* *
* @param value The value to return to the calling Lua function. * @param value The value to return to the calling Lua function.
* @return A method result which returns immediately with the given value. * @return A method result which returns immediately with the given value.
*/ */
@Nonnull @Nonnull
public static MethodResult of( @Nullable Object value ) public static MethodResult of(@Nullable Object value) {
{ return new MethodResult(new Object[] {value}, null);
return new MethodResult( new Object[] { value }, null );
} }
/** /**
@@ -79,14 +75,13 @@ public final class MethodResult
* @return A method result which returns immediately with the given values. * @return A method result which returns immediately with the given values.
*/ */
@Nonnull @Nonnull
public static MethodResult of( @Nullable Object... values ) public static MethodResult of(@Nullable Object... values) {
{ return values == null || values.length == 0 ? empty : new MethodResult(values, null);
return values == null || values.length == 0 ? empty : new MethodResult( values, null );
} }
/** /**
* Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly * Wait for an event to occur on the computer, suspending the thread until it arises. This method is exactly equivalent to {@code os.pullEvent()} in
* equivalent to {@code os.pullEvent()} in lua. * lua.
* *
* @param filter A specific event to wait for, or null to wait for any event. * @param filter A specific event to wait for, or null to wait for any event.
* @param callback The callback to resume with the name of the event that occurred, and any event parameters. * @param callback The callback to resume with the name of the event that occurred, and any event parameters.
@@ -94,19 +89,19 @@ public final class MethodResult
* @see IComputerAccess#queueEvent(String, Object[]) * @see IComputerAccess#queueEvent(String, Object[])
*/ */
@Nonnull @Nonnull
public static MethodResult pullEvent( @Nullable String filter, @Nonnull ILuaCallback callback ) public static MethodResult pullEvent(@Nullable String filter, @Nonnull ILuaCallback callback) {
{ Objects.requireNonNull(callback, "callback cannot be null");
Objects.requireNonNull( callback, "callback cannot be null" ); return new MethodResult(new Object[] {filter}, results -> {
return new MethodResult( new Object[] { filter }, results -> { if (results.length >= 1 && results[0].equals("terminate")) {
if( results.length >= 1 && results[0].equals( "terminate" ) ) throw new LuaException( "Terminated", 0 ); throw new LuaException("Terminated", 0);
return callback.resume( results ); }
} ); return callback.resume(results);
});
} }
/** /**
* The same as {@link #pullEvent(String, ILuaCallback)}, except "terminated" events are ignored. Only use this if * The same as {@link #pullEvent(String, ILuaCallback)}, except "terminated" events are ignored. Only use this if you want to prevent program
* you want to prevent program termination, which is not recommended. This method is exactly equivalent to * termination, which is not recommended. This method is exactly equivalent to {@code os.pullEventRaw()} in Lua.
* {@code os.pullEventRaw()} in Lua.
* *
* @param filter A specific event to wait for, or null to wait for any event. * @param filter A specific event to wait for, or null to wait for any event.
* @param callback The callback to resume with the name of the event that occurred, and any event parameters. * @param callback The callback to resume with the name of the event that occurred, and any event parameters.
@@ -114,15 +109,14 @@ public final class MethodResult
* @see #pullEvent(String, ILuaCallback) * @see #pullEvent(String, ILuaCallback)
*/ */
@Nonnull @Nonnull
public static MethodResult pullEventRaw( @Nullable String filter, @Nonnull ILuaCallback callback ) public static MethodResult pullEventRaw(@Nullable String filter, @Nonnull ILuaCallback callback) {
{ Objects.requireNonNull(callback, "callback cannot be null");
Objects.requireNonNull( callback, "callback cannot be null" ); return new MethodResult(new Object[] {filter}, callback);
return new MethodResult( new Object[] { filter }, callback );
} }
/** /**
* Yield the current coroutine with some arguments until it is resumed. This method is exactly equivalent to * Yield the current coroutine with some arguments until it is resumed. This method is exactly equivalent to {@code coroutine.yield()} in lua. Use
* {@code coroutine.yield()} in lua. Use {@code pullEvent()} if you wish to wait for events. * {@code pullEvent()} if you wish to wait for events.
* *
* @param arguments An object array containing the arguments to pass to coroutine.yield() * @param arguments An object array containing the arguments to pass to coroutine.yield()
* @param callback The callback to resume with an array containing the return values from coroutine.yield() * @param callback The callback to resume with an array containing the return values from coroutine.yield()
@@ -130,41 +124,39 @@ public final class MethodResult
* @see #pullEvent(String, ILuaCallback) * @see #pullEvent(String, ILuaCallback)
*/ */
@Nonnull @Nonnull
public static MethodResult yield( @Nullable Object[] arguments, @Nonnull ILuaCallback callback ) public static MethodResult yield(@Nullable Object[] arguments, @Nonnull ILuaCallback callback) {
{ Objects.requireNonNull(callback, "callback cannot be null");
Objects.requireNonNull( callback, "callback cannot be null" ); return new MethodResult(arguments, callback);
return new MethodResult( arguments, callback );
} }
@Nullable @Nullable
public Object[] getResult() public Object[] getResult() {
{ return this.result;
return result;
} }
@Nullable @Nullable
public ILuaCallback getCallback() public ILuaCallback getCallback() {
{ return this.callback;
return callback;
} }
public int getErrorAdjust() public int getErrorAdjust() {
{ return this.adjust;
return adjust;
} }
/** /**
* Increase the Lua error by a specific amount. One should never need to use this function - it largely exists for * Increase the Lua error by a specific amount. One should never need to use this function - it largely exists for some CC internal code.
* some CC internal code.
* *
* @param adjust The amount to increase the level by. * @param adjust The amount to increase the level by.
* @return The new {@link MethodResult} with an adjusted error. This has no effect on immediate results. * @return The new {@link MethodResult} with an adjusted error. This has no effect on immediate results.
*/ */
@Nonnull @Nonnull
public MethodResult adjustError( int adjust ) public MethodResult adjustError(int adjust) {
{ if (adjust < 0) {
if( adjust < 0 ) throw new IllegalArgumentException( "cannot adjust by a negative amount" ); throw new IllegalArgumentException("cannot adjust by a negative amount");
if( adjust == 0 || callback == null ) return this; }
return new MethodResult( result, callback, this.adjust + adjust ); if (adjust == 0 || this.callback == null) {
return this;
}
return new MethodResult(this.result, this.callback, this.adjust + adjust);
} }
} }

View File

@@ -3,64 +3,64 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.lua; package dan200.computercraft.api.lua;
import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nullable;
/** /**
* An implementation of {@link IArguments} which wraps an array of {@link Object}. * An implementation of {@link IArguments} which wraps an array of {@link Object}.
*/ */
public final class ObjectArguments implements IArguments public final class ObjectArguments implements IArguments {
{
private static final IArguments EMPTY = new ObjectArguments(); private static final IArguments EMPTY = new ObjectArguments();
private final List<Object> args; private final List<Object> args;
@Deprecated @Deprecated
@SuppressWarnings( "unused" ) @SuppressWarnings ("unused")
public ObjectArguments( IArguments arguments ) public ObjectArguments(IArguments arguments) {
{
throw new IllegalStateException(); throw new IllegalStateException();
} }
public ObjectArguments( Object... args ) public ObjectArguments(Object... args) {
{ this.args = Arrays.asList(args);
this.args = Arrays.asList( args );
} }
public ObjectArguments( List<Object> args ) public ObjectArguments(List<Object> args) {
{ this.args = Objects.requireNonNull(args);
this.args = Objects.requireNonNull( args );
} }
@Override @Override
public int count() public IArguments drop(int count) {
{ if (count < 0) {
return args.size(); throw new IllegalStateException("count cannot be negative");
}
if (count == 0) {
return this;
}
if (count >= this.args.size()) {
return EMPTY;
}
return new ObjectArguments(this.args.subList(count, this.args.size()));
} }
@Override @Override
public IArguments drop( int count ) public Object[] getAll() {
{ return this.args.toArray();
if( count < 0 ) throw new IllegalStateException( "count cannot be negative" ); }
if( count == 0 ) return this;
if( count >= args.size() ) return EMPTY;
return new ObjectArguments( args.subList( count, args.size() ) ); @Override
public int count() {
return this.args.size();
} }
@Nullable @Nullable
@Override @Override
public Object get( int index ) public Object get(int index) {
{ return index >= this.args.size() ? null : this.args.get(index);
return index >= args.size() ? null : args.get( index );
}
@Override
public Object[] getAll()
{
return args.toArray();
} }
} }

View File

@@ -3,25 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.media; package dan200.computercraft.api.media;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvent;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* Represents an item that can be placed in a disk drive and used by a Computer. * Represents an item that can be placed in a disk drive and used by a Computer.
* *
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register * Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register a {@link IMediaProvider}.
* a {@link IMediaProvider}.
*/ */
public interface IMedia public interface IMedia {
{
/** /**
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua. * Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
* *
@@ -29,7 +29,7 @@ public interface IMedia
* @return The label. ie: "Dan's Programs". * @return The label. ie: "Dan's Programs".
*/ */
@Nullable @Nullable
String getLabel( @Nonnull ItemStack stack ); String getLabel(@Nonnull ItemStack stack);
/** /**
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua. * Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
@@ -38,21 +38,18 @@ public interface IMedia
* @param label The string to set the label to. * @param label The string to set the label to.
* @return true if the label was updated, false if the label may not be modified. * @return true if the label was updated, false if the label may not be modified.
*/ */
default boolean setLabel( @Nonnull ItemStack stack, @Nullable String label ) default boolean setLabel(@Nonnull ItemStack stack, @Nullable String label) {
{
return false; return false;
} }
/** /**
* If this disk represents an item with audio (like a record), get the readable name of the audio track. ie: * If this disk represents an item with audio (like a record), get the readable name of the audio track. ie: "Jonathan Coulton - Still Alive"
* "Jonathan Coulton - Still Alive"
* *
* @param stack The {@link ItemStack} to modify. * @param stack The {@link ItemStack} to modify.
* @return The name, or null if this item does not represent an item with audio. * @return The name, or null if this item does not represent an item with audio.
*/ */
@Nullable @Nullable
default String getAudioTitle( @Nonnull ItemStack stack ) default String getAudioTitle(@Nonnull ItemStack stack) {
{
return null; return null;
} }
@@ -63,27 +60,25 @@ public interface IMedia
* @return The name, or null if this item does not represent an item with audio. * @return The name, or null if this item does not represent an item with audio.
*/ */
@Nullable @Nullable
default SoundEvent getAudio( @Nonnull ItemStack stack ) default SoundEvent getAudio(@Nonnull ItemStack stack) {
{
return null; return null;
} }
/** /**
* If this disk represents an item with data (like a floppy disk), get a mount representing it's contents. This will * If this disk represents an item with data (like a floppy disk), get a mount representing it's contents. This will be mounted onto the filesystem of
* be mounted onto the filesystem of the computer while the media is in the disk drive. * the computer while the media is in the disk drive.
* *
* @param stack The {@link ItemStack} to modify. * @param stack The {@link ItemStack} to modify.
* @param world The world in which the item and disk drive reside. * @param world The world in which the item and disk drive reside.
* @return The mount, or null if this item does not represent an item with data. If the mount returned also * @return The mount, or null if this item does not represent an item with data. If the mount returned also implements {@link
* implements {@link dan200.computercraft.api.filesystem.IWritableMount}, it will mounted using mountWritable() * dan200.computercraft.api.filesystem.IWritableMount}, it will mounted using mountWritable()
* @see IMount * @see IMount
* @see dan200.computercraft.api.filesystem.IWritableMount * @see dan200.computercraft.api.filesystem.IWritableMount
* @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long) * @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String) * @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String)
*/ */
@Nullable @Nullable
default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull World world ) default IMount createDataMount(@Nonnull ItemStack stack, @Nonnull World world) {
{
return null; return null;
} }
} }

View File

@@ -3,21 +3,21 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.media;
import net.minecraft.item.ItemStack; package dan200.computercraft.api.media;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
/** /**
* This interface is used to provide {@link IMedia} implementations for {@link ItemStack}. * This interface is used to provide {@link IMedia} implementations for {@link ItemStack}.
* *
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
*/ */
@FunctionalInterface @FunctionalInterface
public interface IMediaProvider public interface IMediaProvider {
{
/** /**
* Produce an IMedia implementation from an ItemStack. * Produce an IMedia implementation from an ItemStack.
* *
@@ -26,5 +26,5 @@ public interface IMediaProvider
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
*/ */
@Nullable @Nullable
IMedia getMedia( @Nonnull ItemStack stack ); IMedia getMedia(@Nonnull ItemStack stack);
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network; package dan200.computercraft.api.network;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -13,21 +14,20 @@ import javax.annotation.Nonnull;
* @see Packet * @see Packet
* @see IPacketReceiver * @see IPacketReceiver
*/ */
public interface IPacketNetwork public interface IPacketNetwork {
{
/** /**
* Add a receiver to the network. * Add a receiver to the network.
* *
* @param receiver The receiver to register to the network. * @param receiver The receiver to register to the network.
*/ */
void addReceiver( @Nonnull IPacketReceiver receiver ); void addReceiver(@Nonnull IPacketReceiver receiver);
/** /**
* Remove a receiver from the network. * Remove a receiver from the network.
* *
* @param receiver The device to remove from the network. * @param receiver The device to remove from the network.
*/ */
void removeReceiver( @Nonnull IPacketReceiver receiver ); void removeReceiver(@Nonnull IPacketReceiver receiver);
/** /**
* Determine whether this network is wireless. * Determine whether this network is wireless.
@@ -37,23 +37,23 @@ public interface IPacketNetwork
boolean isWireless(); boolean isWireless();
/** /**
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it * Submit a packet for transmitting across the network. This will route the packet through the network, sending it to all receivers within range (or any
* to all receivers within range (or any interdimensional ones). * interdimensional ones).
* *
* @param packet The packet to send. * @param packet The packet to send.
* @param range The maximum distance this packet will be sent. * @param range The maximum distance this packet will be sent.
* @see #transmitInterdimensional(Packet) * @see #transmitInterdimensional(Packet)
* @see IPacketReceiver#receiveSameDimension(Packet, double) * @see IPacketReceiver#receiveSameDimension(Packet, double)
*/ */
void transmitSameDimension( @Nonnull Packet packet, double range ); void transmitSameDimension(@Nonnull Packet packet, double range);
/** /**
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it * Submit a packet for transmitting across the network. This will route the packet through the network, sending it to all receivers across all
* to all receivers across all dimensions. * dimensions.
* *
* @param packet The packet to send. * @param packet The packet to send.
* @see #transmitSameDimension(Packet, double) * @see #transmitSameDimension(Packet, double)
* @see IPacketReceiver#receiveDifferentDimension(Packet) * @see IPacketReceiver#receiveDifferentDimension(Packet)
*/ */
void transmitInterdimensional( @Nonnull Packet packet ); void transmitInterdimensional(@Nonnull Packet packet);
} }

View File

@@ -3,18 +3,18 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network; package dan200.computercraft.api.network;
import javax.annotation.Nonnull;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
/** /**
* An object on an {@link IPacketNetwork}, capable of receiving packets. * An object on an {@link IPacketNetwork}, capable of receiving packets.
*/ */
public interface IPacketReceiver public interface IPacketReceiver {
{
/** /**
* Get the world in which this packet receiver exists. * Get the world in which this packet receiver exists.
* *
@@ -34,9 +34,8 @@ public interface IPacketReceiver
/** /**
* Get the maximum distance this receiver can send and receive messages. * Get the maximum distance this receiver can send and receive messages.
* *
* When determining whether a receiver can receive a message, the largest distance of the packet and receiver is * When determining whether a receiver can receive a message, the largest distance of the packet and receiver is used - ensuring it is within range. If
* used - ensuring it is within range. If the packet or receiver is inter-dimensional, then the packet will always * the packet or receiver is inter-dimensional, then the packet will always be received.
* be received.
* *
* @return The maximum distance this device can send and receive messages. * @return The maximum distance this device can send and receive messages.
* @see #isInterdimensional() * @see #isInterdimensional()
@@ -60,25 +59,25 @@ public interface IPacketReceiver
/** /**
* Receive a network packet from the same dimension. * Receive a network packet from the same dimension.
* *
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and, * @param packet The packet to receive. Generally you should check that you are listening on the given channel and, if so, queue the appropriate
* if so, queue the appropriate modem event. * modem event.
* @param distance The distance this packet has travelled from the source. * @param distance The distance this packet has travelled from the source.
* @see Packet * @see Packet
* @see #getRange() * @see #getRange()
* @see IPacketNetwork#transmitSameDimension(Packet, double) * @see IPacketNetwork#transmitSameDimension(Packet, double)
* @see IPacketNetwork#transmitInterdimensional(Packet) * @see IPacketNetwork#transmitInterdimensional(Packet)
*/ */
void receiveSameDimension( @Nonnull Packet packet, double distance ); void receiveSameDimension(@Nonnull Packet packet, double distance);
/** /**
* Receive a network packet from a different dimension. * Receive a network packet from a different dimension.
* *
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and, * @param packet The packet to receive. Generally you should check that you are listening on the given channel and, if so, queue the appropriate
* if so, queue the appropriate modem event. * modem event.
* @see Packet * @see Packet
* @see IPacketNetwork#transmitInterdimensional(Packet) * @see IPacketNetwork#transmitInterdimensional(Packet)
* @see IPacketNetwork#transmitSameDimension(Packet, double) * @see IPacketNetwork#transmitSameDimension(Packet, double)
* @see #isInterdimensional() * @see #isInterdimensional()
*/ */
void receiveDifferentDimension( @Nonnull Packet packet ); void receiveDifferentDimension(@Nonnull Packet packet);
} }

View File

@@ -3,18 +3,18 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network; package dan200.computercraft.api.network;
import javax.annotation.Nonnull;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
/** /**
* An object on a {@link IPacketNetwork}, capable of sending packets. * An object on a {@link IPacketNetwork}, capable of sending packets.
*/ */
public interface IPacketSender public interface IPacketSender {
{
/** /**
* Get the world in which this packet sender exists. * Get the world in which this packet sender exists.
* *
@@ -32,8 +32,8 @@ public interface IPacketSender
Vec3d getPosition(); Vec3d getPosition();
/** /**
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you * Get some sort of identification string for this sender. This does not strictly need to be unique, but you should be able to extract some identifiable
* should be able to extract some identifiable information from it. * information from it.
* *
* @return This device's id. * @return This device's id.
*/ */

View File

@@ -3,11 +3,13 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network; package dan200.computercraft.api.network;
import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Objects;
/** /**
* Represents a packet which may be sent across a {@link IPacketNetwork}. * Represents a packet which may be sent across a {@link IPacketNetwork}.
@@ -18,8 +20,7 @@ import java.util.Objects;
* @see IPacketReceiver#receiveDifferentDimension(Packet) * @see IPacketReceiver#receiveDifferentDimension(Packet)
* @see IPacketReceiver#receiveSameDimension(Packet, double) * @see IPacketReceiver#receiveSameDimension(Packet, double)
*/ */
public class Packet public class Packet {
{
private final int channel; private final int channel;
private final int replyChannel; private final int replyChannel;
private final Object payload; private final Object payload;
@@ -29,16 +30,14 @@ public class Packet
/** /**
* Create a new packet, ready for transmitting across the network. * Create a new packet, ready for transmitting across the network.
* *
* @param channel The channel to send the packet along. Receiving devices should only process packets from on * @param channel The channel to send the packet along. Receiving devices should only process packets from on channels they are listening to.
* channels they are listening to.
* @param replyChannel The channel to reply on. * @param replyChannel The channel to reply on.
* @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an * @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an event or returning from a peripheral
* event or returning from a peripheral call. * call.
* @param sender The object which sent this packet. * @param sender The object which sent this packet.
*/ */
public Packet( int channel, int replyChannel, @Nullable Object payload, @Nonnull IPacketSender sender ) public Packet(int channel, int replyChannel, @Nullable Object payload, @Nonnull IPacketSender sender) {
{ Objects.requireNonNull(sender, "sender cannot be null");
Objects.requireNonNull( sender, "sender cannot be null" );
this.channel = channel; this.channel = channel;
this.replyChannel = replyChannel; this.replyChannel = replyChannel;
@@ -47,14 +46,12 @@ public class Packet
} }
/** /**
* Get the channel this packet is sent along. Receivers should generally only process packets from on channels they * Get the channel this packet is sent along. Receivers should generally only process packets from on channels they are listening to.
* are listening to.
* *
* @return This packet's channel. * @return This packet's channel.
*/ */
public int getChannel() public int getChannel() {
{ return this.channel;
return channel;
} }
/** /**
@@ -62,21 +59,18 @@ public class Packet
* *
* @return This channel to reply on. * @return This channel to reply on.
*/ */
public int getReplyChannel() public int getReplyChannel() {
{ return this.replyChannel;
return replyChannel;
} }
/** /**
* The actual data of this packet. This should be a "valid" Lua object, safe for queuing as an * The actual data of this packet. This should be a "valid" Lua object, safe for queuing as an event or returning from a peripheral call.
* event or returning from a peripheral call.
* *
* @return The packet's payload * @return The packet's payload
*/ */
@Nullable @Nullable
public Object getPayload() public Object getPayload() {
{ return this.payload;
return payload;
} }
/** /**
@@ -85,33 +79,40 @@ public class Packet
* @return The sending object. * @return The sending object.
*/ */
@Nonnull @Nonnull
public IPacketSender getSender() public IPacketSender getSender() {
{ return this.sender;
return sender;
} }
@Override @Override
public boolean equals( Object o ) public int hashCode() {
{ int result;
if( this == o ) return true; result = this.channel;
if( o == null || getClass() != o.getClass() ) return false; result = 31 * result + this.replyChannel;
result = 31 * result + (this.payload != null ? this.payload.hashCode() : 0);
result = 31 * result + this.sender.hashCode();
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
Packet packet = (Packet) o; Packet packet = (Packet) o;
if( channel != packet.channel ) return false; if (this.channel != packet.channel) {
if( replyChannel != packet.replyChannel ) return false; return false;
if( !Objects.equals( payload, packet.payload ) ) return false;
return sender.equals( packet.sender );
} }
if (this.replyChannel != packet.replyChannel) {
@Override return false;
public int hashCode() }
{ if (!Objects.equals(this.payload, packet.payload)) {
int result; return false;
result = channel; }
result = 31 * result + replyChannel; return this.sender.equals(packet.sender);
result = 31 * result + (payload != null ? payload.hashCode() : 0);
result = 31 * result + sender.hashCode();
return result;
} }
} }

View File

@@ -3,32 +3,29 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network.wired; package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.ComputerCraftAPI;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import dan200.computercraft.api.ComputerCraftAPI;
/** /**
* An object which may be part of a wired network. * An object which may be part of a wired network.
* *
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts * Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts as a proxy for all network objects.
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant * Whilst the node may change networks, an element's node should remain constant for its lifespan.
* for its lifespan.
* *
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the * Elements are generally tied to a block or tile entity in world. In such as case, one should provide the {@link IWiredElement} capability for the
* {@link IWiredElement} capability for the appropriate sides. * appropriate sides.
*/ */
public interface IWiredElement extends IWiredSender public interface IWiredElement extends IWiredSender {
{
/** /**
* Called when objects on the network change. This may occur when network nodes are added or removed, or when * Called when objects on the network change. This may occur when network nodes are added or removed, or when peripherals change.
* peripherals change.
* *
* @param change The change which occurred. * @param change The change which occurred.
* @see IWiredNetworkChange * @see IWiredNetworkChange
*/ */
default void networkChanged( @Nonnull IWiredNetworkChange change ) default void networkChanged(@Nonnull IWiredNetworkChange change) {
{
} }
} }

View File

@@ -3,29 +3,28 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network.wired; package dan200.computercraft.api.network.wired;
import java.util.Map;
import javax.annotation.Nonnull;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import javax.annotation.Nonnull;
import java.util.Map;
/** /**
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series * A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series of peripherals.
* of peripherals.
* *
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if * Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if there is some path between two nodes
* there is some path between two nodes then they must be on the same network. {@link IWiredNetwork} will automatically * then they must be on the same network. {@link IWiredNetwork} will automatically handle the merging and splitting of networks (and thus changing of
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections * available nodes and peripherals) as connections change.
* change.
* *
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently, * This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently, it is generally preferred to use the
* it is generally preferred to use the methods provided by {@link IWiredNode}. * methods provided by {@link IWiredNode}.
* *
* @see IWiredNode#getNetwork() * @see IWiredNode#getNetwork()
*/ */
public interface IWiredNetwork public interface IWiredNetwork {
{
/** /**
* Create a connection between two nodes. * Create a connection between two nodes.
* *
@@ -39,7 +38,7 @@ public interface IWiredNetwork
* @see IWiredNode#connectTo(IWiredNode) * @see IWiredNode#connectTo(IWiredNode)
* @see IWiredNetwork#connect(IWiredNode, IWiredNode) * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
*/ */
boolean connect( @Nonnull IWiredNode left, @Nonnull IWiredNode right ); boolean connect(@Nonnull IWiredNode left, @Nonnull IWiredNode right);
/** /**
* Destroy a connection between this node and another. * Destroy a connection between this node and another.
@@ -54,32 +53,29 @@ public interface IWiredNetwork
* @see IWiredNode#disconnectFrom(IWiredNode) * @see IWiredNode#disconnectFrom(IWiredNode)
* @see IWiredNetwork#connect(IWiredNode, IWiredNode) * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
*/ */
boolean disconnect( @Nonnull IWiredNode left, @Nonnull IWiredNode right ); boolean disconnect(@Nonnull IWiredNode left, @Nonnull IWiredNode right);
/** /**
* Sever all connections this node has, removing it from this network. * Sever all connections this node has, removing it from this network.
* *
* This should only be used on the server thread. You should only call this on nodes * This should only be used on the server thread. You should only call this on nodes that your network element owns.
* that your network element owns.
* *
* @param node The node to remove * @param node The node to remove
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the only element.
* only element.
* @throws IllegalArgumentException If the node is not in the network. * @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNode#remove() * @see IWiredNode#remove()
*/ */
boolean remove( @Nonnull IWiredNode node ); boolean remove(@Nonnull IWiredNode node);
/** /**
* Update the peripherals a node provides. * Update the peripherals a node provides.
* *
* This should only be used on the server thread. You should only call this on nodes * This should only be used on the server thread. You should only call this on nodes that your network element owns.
* that your network element owns.
* *
* @param node The node to attach peripherals for. * @param node The node to attach peripherals for.
* @param peripherals The new peripherals for this node. * @param peripherals The new peripherals for this node.
* @throws IllegalArgumentException If the node is not in the network. * @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNode#updatePeripherals(Map) * @see IWiredNode#updatePeripherals(Map)
*/ */
void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals ); void updatePeripherals(@Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals);
} }

View File

@@ -3,23 +3,24 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network.wired; package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.peripheral.IPeripheral; import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Map;
import dan200.computercraft.api.peripheral.IPeripheral;
/** /**
* Represents a change to the objects on a wired network. * Represents a change to the objects on a wired network.
* *
* @see IWiredElement#networkChanged(IWiredNetworkChange) * @see IWiredElement#networkChanged(IWiredNetworkChange)
*/ */
public interface IWiredNetworkChange public interface IWiredNetworkChange {
{
/** /**
* A set of peripherals which have been removed. Note that there may be entries with the same name * A set of peripherals which have been removed. Note that there may be entries with the same name in the added and removed set, but with a different
* in the added and removed set, but with a different peripheral. * peripheral.
* *
* @return The set of removed peripherals. * @return The set of removed peripherals.
*/ */
@@ -27,8 +28,8 @@ public interface IWiredNetworkChange
Map<String, IPeripheral> peripheralsRemoved(); Map<String, IPeripheral> peripheralsRemoved();
/** /**
* A set of peripherals which have been added. Note that there may be entries with the same name * A set of peripherals which have been added. Note that there may be entries with the same name in the added and removed set, but with a different
* in the added and removed set, but with a different peripheral. * peripheral.
* *
* @return The set of added peripherals. * @return The set of added peripherals.
*/ */

View File

@@ -3,29 +3,29 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network.wired; package dan200.computercraft.api.network.wired;
import java.util.Map;
import javax.annotation.Nonnull;
import dan200.computercraft.api.network.IPacketNetwork; import dan200.computercraft.api.network.IPacketNetwork;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import javax.annotation.Nonnull;
import java.util.Map;
/** /**
* Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s. * Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
* *
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These * Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These methods may be safely used on any
* methods may be safely used on any thread. * thread.
* *
* When sending a packet, the system will attempt to find the shortest path between the two nodes based on their * When sending a packet, the system will attempt to find the shortest path between the two nodes based on their element's position. Note that packet
* element's position. Note that packet senders and receivers can have different locations from their associated * senders and receivers can have different locations from their associated element: the distance between the two will be added to the total packet's
* element: the distance between the two will be added to the total packet's distance. * distance.
* *
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever * Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever be used on the main server thread.
* be used on the main server thread.
*/ */
public interface IWiredNode extends IPacketNetwork public interface IWiredNode extends IPacketNetwork {
{
/** /**
* The associated element for this network node. * The associated element for this network node.
* *
@@ -34,17 +34,6 @@ public interface IWiredNode extends IPacketNetwork
@Nonnull @Nonnull
IWiredElement getElement(); IWiredElement getElement();
/**
* The network this node is currently connected to. Note that this may change
* after any network operation, so it should not be cached.
*
* This should only be used on the server thread.
*
* @return This node's network.
*/
@Nonnull
IWiredNetwork getNetwork();
/** /**
* Create a connection from this node to another. * Create a connection from this node to another.
* *
@@ -55,11 +44,20 @@ public interface IWiredNode extends IPacketNetwork
* @see IWiredNetwork#connect(IWiredNode, IWiredNode) * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
* @see IWiredNode#disconnectFrom(IWiredNode) * @see IWiredNode#disconnectFrom(IWiredNode)
*/ */
default boolean connectTo( @Nonnull IWiredNode node ) default boolean connectTo(@Nonnull IWiredNode node) {
{ return this.getNetwork().connect(this, node);
return getNetwork().connect( this, node );
} }
/**
* The network this node is currently connected to. Note that this may change after any network operation, so it should not be cached.
*
* This should only be used on the server thread.
*
* @return This node's network.
*/
@Nonnull
IWiredNetwork getNetwork();
/** /**
* Destroy a connection between this node and another. * Destroy a connection between this node and another.
* *
@@ -71,38 +69,32 @@ public interface IWiredNode extends IPacketNetwork
* @see IWiredNetwork#disconnect(IWiredNode, IWiredNode) * @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
* @see IWiredNode#connectTo(IWiredNode) * @see IWiredNode#connectTo(IWiredNode)
*/ */
default boolean disconnectFrom( @Nonnull IWiredNode node ) default boolean disconnectFrom(@Nonnull IWiredNode node) {
{ return this.getNetwork().disconnect(this, node);
return getNetwork().disconnect( this, node );
} }
/** /**
* Sever all connections this node has, removing it from this network. * Sever all connections this node has, removing it from this network.
* *
* This should only be used on the server thread. You should only call this on nodes * This should only be used on the server thread. You should only call this on nodes that your network element owns.
* that your network element owns.
* *
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the only element.
* only element.
* @throws IllegalArgumentException If the node is not in the network. * @throws IllegalArgumentException If the node is not in the network.
* @see IWiredNetwork#remove(IWiredNode) * @see IWiredNetwork#remove(IWiredNode)
*/ */
default boolean remove() default boolean remove() {
{ return this.getNetwork().remove(this);
return getNetwork().remove( this );
} }
/** /**
* Mark this node's peripherals as having changed. * Mark this node's peripherals as having changed.
* *
* This should only be used on the server thread. You should only call this on nodes * This should only be used on the server thread. You should only call this on nodes that your network element owns.
* that your network element owns.
* *
* @param peripherals The new peripherals for this node. * @param peripherals The new peripherals for this node.
* @see IWiredNetwork#updatePeripherals(IWiredNode, Map) * @see IWiredNetwork#updatePeripherals(IWiredNode, Map)
*/ */
default void updatePeripherals( @Nonnull Map<String, IPeripheral> peripherals ) default void updatePeripherals(@Nonnull Map<String, IPeripheral> peripherals) {
{ this.getNetwork().updatePeripherals(this, peripherals);
getNetwork().updatePeripherals( this, peripherals );
} }
} }

View File

@@ -3,25 +3,23 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.network.wired; package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.network.IPacketSender;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import dan200.computercraft.api.network.IPacketSender;
/** /**
* An object on a {@link IWiredNetwork} capable of sending packets. * An object on a {@link IWiredNetwork} capable of sending packets.
* *
* Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to * Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to to send the packet from.
* to send the packet from.
*/ */
public interface IWiredSender extends IPacketSender public interface IWiredSender extends IPacketSender {
{
/** /**
* The node in the network representing this object. * The node in the network representing this object.
* *
* This should be used as a proxy for the main network. One should send packets * This should be used as a proxy for the main network. One should send packets and register receivers through this object.
* and register receivers through this object.
* *
* @return The node for this element. * @return The node for this element.
*/ */

View File

@@ -3,8 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
@@ -12,26 +18,21 @@ import dan200.computercraft.api.lua.ILuaCallback;
import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask; import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.lua.MethodResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
/** /**
* The interface passed to peripherals by computers or turtles, providing methods * The interface passed to peripherals by computers or turtles, providing methods that they can call. This should not be implemented by your classes. Do not
* that they can call. This should not be implemented by your classes. Do not interact * interact with computers except via this interface.
* with computers except via this interface.
*/ */
public interface IComputerAccess public interface IComputerAccess {
{
/** /**
* Mount a mount onto the computer's file system in a read only mode. * Mount a mount onto the computer's file system in a read only mode.
* *
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted. * @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a file in the desired location.
* file in the desired location. Store this value if you wish to unmount the mount later. * Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(String, String)
@@ -41,9 +42,8 @@ public interface IComputerAccess
* @see IMount * @see IMount
*/ */
@Nullable @Nullable
default String mount( @Nonnull String desiredLocation, @Nonnull IMount mount ) default String mount(@Nonnull String desiredLocation, @Nonnull IMount mount) {
{ return this.mount(desiredLocation, mount, this.getAttachmentName());
return mount( desiredLocation, mount, getAttachmentName() );
} }
/** /**
@@ -52,8 +52,8 @@ public interface IComputerAccess
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted. * @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}. * @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a * @return The location on the computer's file system where you the mount mounted, or {@code null} if there was already a file in the desired location.
* file in the desired location. Store this value if you wish to unmount the mount later. * Store this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(String, String)
@@ -63,15 +63,26 @@ public interface IComputerAccess
* @see IMount * @see IMount
*/ */
@Nullable @Nullable
String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName ); String mount(@Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName);
/**
* Get a string, unique to the computer, by which the computer refers to this peripheral. For directly attached peripherals this will be
* "left","right","front","back",etc, but for peripherals attached remotely it will be different. It is good practice to supply this string when raising
* events to the computer, so that the computer knows from which peripheral the event came.
*
* @return A string unique to the computer, but not globally.
* @throws NotAttachedException If the peripheral has been detached.
*/
@Nonnull
String getAttachmentName();
/** /**
* Mount a mount onto the computer's file system in a writable mode. * Mount a mount onto the computer's file system in a writable mode.
* *
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted. * @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a * @return The location on the computer's file system where you the mount mounted, or null if there was already a file in the desired location. Store
* file in the desired location. Store this value if you wish to unmount the mount later. * this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(String, String)
@@ -80,9 +91,8 @@ public interface IComputerAccess
* @see IMount * @see IMount
*/ */
@Nullable @Nullable
default String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount ) default String mountWritable(@Nonnull String desiredLocation, @Nonnull IWritableMount mount) {
{ return this.mountWritable(desiredLocation, mount, this.getAttachmentName());
return mountWritable( desiredLocation, mount, getAttachmentName() );
} }
/** /**
@@ -91,8 +101,8 @@ public interface IComputerAccess
* @param desiredLocation The location on the computer's file system where you would like the mount to be mounted. * @param desiredLocation The location on the computer's file system where you would like the mount to be mounted.
* @param mount The mount object to mount on the computer. * @param mount The mount object to mount on the computer.
* @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}. * @param driveName A custom name to give for this mount location, as returned by {@code fs.getDrive()}.
* @return The location on the computer's file system where you the mount mounted, or null if there was already a * @return The location on the computer's file system where you the mount mounted, or null if there was already a file in the desired location. Store
* file in the desired location. Store this value if you wish to unmount the mount later. * this value if you wish to unmount the mount later.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @see ComputerCraftAPI#createSaveDirMount(World, String, long) * @see ComputerCraftAPI#createSaveDirMount(World, String, long)
* @see ComputerCraftAPI#createResourceMount(String, String) * @see ComputerCraftAPI#createResourceMount(String, String)
@@ -100,70 +110,51 @@ public interface IComputerAccess
* @see #unmount(String) * @see #unmount(String)
* @see IMount * @see IMount
*/ */
String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName ); String mountWritable(@Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName);
/** /**
* Unmounts a directory previously mounted onto the computers file system by {@link #mount(String, IMount)} * Unmounts a directory previously mounted onto the computers file system by {@link #mount(String, IMount)} or {@link #mountWritable(String,
* or {@link #mountWritable(String, IWritableMount)}. * IWritableMount)}.
* *
* When a directory is unmounted, it will disappear from the computers file system, and the user will no longer be * When a directory is unmounted, it will disappear from the computers file system, and the user will no longer be able to access it. All directories
* able to access it. All directories mounted by a mount or mountWritable are automatically unmounted when the * mounted by a mount or mountWritable are automatically unmounted when the peripheral is attached if they have not been explicitly unmounted.
* peripheral is attached if they have not been explicitly unmounted.
* *
* Note that you cannot unmount another peripheral's mounts. * Note that you cannot unmount another peripheral's mounts.
* *
* @param location The desired location in the computers file system of the directory to unmount. * @param location The desired location in the computers file system of the directory to unmount. This must be the location of a directory
* This must be the location of a directory previously mounted by {@link #mount(String, IMount)} or * previously mounted by {@link #mount(String, IMount)} or {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
* {@link #mountWritable(String, IWritableMount)}, as indicated by their return value.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @throws IllegalStateException If the mount does not exist, or was mounted by another peripheral. * @throws IllegalStateException If the mount does not exist, or was mounted by another peripheral.
* @see #mount(String, IMount) * @see #mount(String, IMount)
* @see #mountWritable(String, IWritableMount) * @see #mountWritable(String, IWritableMount)
*/ */
void unmount( @Nullable String location ); void unmount(@Nullable String location);
/** /**
* Returns the numerical ID of this computer. * Returns the numerical ID of this computer.
* *
* This is the same number obtained by calling {@code os.getComputerID()} or running the "id" program from lua, * This is the same number obtained by calling {@code os.getComputerID()} or running the "id" program from lua, and is guaranteed unique. This number
* and is guaranteed unique. This number will be positive. * will be positive.
* *
* @return The identifier. * @return The identifier.
*/ */
int getID(); int getID();
/** /**
* Causes an event to be raised on this computer, which the computer can respond to by calling * Causes an event to be raised on this computer, which the computer can respond to by calling {@code os.pullEvent()}. This can be used to notify the
* {@code os.pullEvent()}. This can be used to notify the computer when things happen in the world or to * computer when things happen in the world or to this peripheral.
* this peripheral.
* *
* @param event A string identifying the type of event that has occurred, this will be * @param event A string identifying the type of event that has occurred, this will be returned as the first value from {@code os.pullEvent()}. It
* returned as the first value from {@code os.pullEvent()}. It is recommended that you * is recommended that you you choose a name that is unique, and recognisable as originating from your peripheral. eg: If your peripheral type is
* you choose a name that is unique, and recognisable as originating from your * "button", a suitable event would be "button_pressed".
* peripheral. eg: If your peripheral type is "button", a suitable event would be * @param arguments In addition to a name, you may pass an array of extra arguments to the event, that will be supplied as extra return values to
* "button_pressed". * os.pullEvent(). Objects in the array will be converted to lua data types in the same fashion as the return values of IPeripheral.callMethod().
* @param arguments In addition to a name, you may pass an array of extra arguments to the event, that will
* be supplied as extra return values to os.pullEvent(). Objects in the array will be converted
* to lua data types in the same fashion as the return values of IPeripheral.callMethod().
* *
* You may supply {@code null} to indicate that no arguments are to be supplied. * You may supply {@code null} to indicate that no arguments are to be supplied.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.
* @see MethodResult#pullEvent(String, ILuaCallback) * @see MethodResult#pullEvent(String, ILuaCallback)
*/ */
void queueEvent( @Nonnull String event, @Nullable Object... arguments ); void queueEvent(@Nonnull String event, @Nullable Object... arguments);
/**
* Get a string, unique to the computer, by which the computer refers to this peripheral.
* For directly attached peripherals this will be "left","right","front","back",etc, but
* for peripherals attached remotely it will be different. It is good practice to supply
* this string when raising events to the computer, so that the computer knows from
* which peripheral the event came.
*
* @return A string unique to the computer, but not globally.
* @throws NotAttachedException If the peripheral has been detached.
*/
@Nonnull
String getAttachmentName();
/** /**
* Get a set of peripherals that this computer access can "see", along with their attachment name. * Get a set of peripherals that this computer access can "see", along with their attachment name.
@@ -179,26 +170,24 @@ public interface IComputerAccess
Map<String, IPeripheral> getAvailablePeripherals(); Map<String, IPeripheral> getAvailablePeripherals();
/** /**
* Get a reachable peripheral with the given attachment name. This is a equivalent to * Get a reachable peripheral with the given attachment name. This is a equivalent to {@link #getAvailablePeripherals()}{@code .get(name)}, though may
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more efficient. * be more efficient.
* *
* @param name The peripheral's attached name * @param name The peripheral's attached name
* @return The reachable peripheral, or {@code null} if none can be found. * @return The reachable peripheral, or {@code null} if none can be found.
* @see #getAvailablePeripherals() * @see #getAvailablePeripherals()
*/ */
@Nullable @Nullable
IPeripheral getAvailablePeripheral( @Nonnull String name ); IPeripheral getAvailablePeripheral(@Nonnull String name);
/** /**
* Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread. * Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
* *
* This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much * This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much server time each computer consumes.
* server time each computer consumes. You should not need to use this if you use * You should not need to use this if you use {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for
* {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for running * running work on the main thread.
* work on the main thread.
* *
* Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main * Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main thread.
* thread.
* *
* @return The work monitor for the main thread, or {@code null} if this computer does not have one. * @return The work monitor for the main thread, or {@code null} if this computer does not have one.
* @throws NotAttachedException If the peripheral has been detached. * @throws NotAttachedException If the peripheral has been detached.

View File

@@ -3,23 +3,28 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.lua.*;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.IDynamicLuaObject;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.lua.MethodResult;
/** /**
* A peripheral whose methods are not known at runtime. * A peripheral whose methods are not known at runtime.
* *
* This behaves similarly to {@link IDynamicLuaObject}, though also accepting the current {@link IComputerAccess}. * This behaves similarly to {@link IDynamicLuaObject}, though also accepting the current {@link IComputerAccess}. Generally one may use {@link LuaFunction}
* Generally one may use {@link LuaFunction} instead of implementing this interface. * instead of implementing this interface.
*/ */
public interface IDynamicPeripheral extends IPeripheral public interface IDynamicPeripheral extends IPeripheral {
{
/** /**
* Should return an array of strings that identify the methods that this peripheral exposes to Lua. This will be * Should return an array of strings that identify the methods that this peripheral exposes to Lua. This will be called once before each attachment, and
* called once before each attachment, and should not change when called multiple times. * should not change when called multiple times.
* *
* @return An array of strings representing method names. * @return An array of strings representing method names.
* @see #callMethod * @see #callMethod
@@ -28,26 +33,21 @@ public interface IDynamicPeripheral extends IPeripheral
String[] getMethodNames(); String[] getMethodNames();
/** /**
* This is called when a lua program on an attached computer calls {@code peripheral.call()} with * This is called when a lua program on an attached computer calls {@code peripheral.call()} with one of the methods exposed by {@link
* one of the methods exposed by {@link #getMethodNames()}. * #getMethodNames()}.
* *
* Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe when interacting * Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe when interacting with Minecraft objects.
* with Minecraft objects.
* *
* @param computer The interface to the computer that is making the call. Remember that multiple * @param computer The interface to the computer that is making the call. Remember that multiple computers can be attached to a peripheral at once.
* computers can be attached to a peripheral at once. * @param context The context of the currently running lua thread. This can be used to wait for events or otherwise yield.
* @param context The context of the currently running lua thread. This can be used to wait for events * @param method An integer identifying which of the methods from getMethodNames() the computercraft wishes to call. The integer indicates the index
* or otherwise yield. * into the getMethodNames() table that corresponds to the string passed into peripheral.call()
* @param method An integer identifying which of the methods from getMethodNames() the computercraft
* wishes to call. The integer indicates the index into the getMethodNames() table
* that corresponds to the string passed into peripheral.call()
* @param arguments The arguments for this method. * @param arguments The arguments for this method.
* @return A {@link MethodResult} containing the values to return or the action to perform. * @return A {@link MethodResult} containing the values to return or the action to perform.
* @throws LuaException If you throw any exception from this function, a lua error will be raised with the * @throws LuaException If you throw any exception from this function, a lua error will be raised with the same message as your exception. Use this
* same message as your exception. Use this to throw appropriate errors if the wrong * to throw appropriate errors if the wrong arguments are supplied to your method.
* arguments are supplied to your method.
* @see #getMethodNames() * @see #getMethodNames()
*/ */
@Nonnull @Nonnull
MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException; MethodResult callMethod(@Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) throws LuaException;
} }

View File

@@ -3,27 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.lua.LuaFunction; package dan200.computercraft.api.peripheral;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dan200.computercraft.api.lua.LuaFunction;
/** /**
* The interface that defines a peripheral. * The interface that defines a peripheral.
* *
* In order to expose a peripheral for your block or tile entity, you register a {@link IPeripheralProvider}. * In order to expose a peripheral for your block or tile entity, you register a {@link IPeripheralProvider}. This <em>cannot</em> be implemented {@link
* This <em>cannot</em> be implemented {@link IPeripheral} directly on the tile. * IPeripheral} directly on the tile.
* *
* Peripherals should provide a series of methods to the user, either using {@link LuaFunction} or by implementing * Peripherals should provide a series of methods to the user, either using {@link LuaFunction} or by implementing {@link IDynamicPeripheral}.
* {@link IDynamicPeripheral}.
*/ */
public interface IPeripheral public interface IPeripheral {
{
/** /**
* Should return a string that uniquely identifies this type of peripheral. * Should return a string that uniquely identifies this type of peripheral. This can be queried from lua by calling {@code peripheral.getType()}
* This can be queried from lua by calling {@code peripheral.getType()}
* *
* @return A string identifying the type of peripheral. * @return A string identifying the type of peripheral.
*/ */
@@ -33,66 +31,54 @@ public interface IPeripheral
/** /**
* Is called when when a computer is attaching to the peripheral. * Is called when when a computer is attaching to the peripheral.
* *
* This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a * This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a peripheral, when a turtle travels into
* peripheral, when a turtle travels into a square next to a peripheral, or when a wired modem adjacent to this * a square next to a peripheral, or when a wired modem adjacent to this peripheral is does any of the above.
* peripheral is does any of the above.
* *
* Between calls to attach and {@link #detach}, the attached computer can make method calls on the peripheral using * Between calls to attach and {@link #detach}, the attached computer can make method calls on the peripheral using {@code peripheral.call()}. This
* {@code peripheral.call()}. This method can be used to keep track of which computers are attached to the * method can be used to keep track of which computers are attached to the peripheral, or to take action when attachment occurs.
* peripheral, or to take action when attachment occurs.
* *
* Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe * Be aware that will be called from both the server thread and ComputerCraft Lua thread, and so must be thread-safe and reentrant.
* and reentrant.
* *
* @param computer The interface to the computer that is being attached. Remember that multiple computers can be * @param computer The interface to the computer that is being attached. Remember that multiple computers can be attached to a peripheral at once.
* attached to a peripheral at once.
* @see #detach * @see #detach
*/ */
default void attach( @Nonnull IComputerAccess computer ) default void attach(@Nonnull IComputerAccess computer) {
{
} }
/** /**
* Called when a computer is detaching from the peripheral. * Called when a computer is detaching from the peripheral.
* *
* This will occur when a computer shuts down, when the peripheral is removed while attached to computers, when a * This will occur when a computer shuts down, when the peripheral is removed while attached to computers, when a turtle moves away from a block
* turtle moves away from a block attached to a peripheral, or when a wired modem adjacent to this peripheral is * attached to a peripheral, or when a wired modem adjacent to this peripheral is detached.
* detached.
* *
* This method can be used to keep track of which computers are attached to the peripheral, or to take action when * This method can be used to keep track of which computers are attached to the peripheral, or to take action when detachment occurs.
* detachment occurs.
* *
* Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe * Be aware that this will be called from both the server and ComputerCraft Lua thread, and must be thread-safe and reentrant.
* and reentrant.
* *
* @param computer The interface to the computer that is being detached. Remember that multiple computers can be * @param computer The interface to the computer that is being detached. Remember that multiple computers can be attached to a peripheral at once.
* attached to a peripheral at once.
* @see #attach * @see #attach
*/ */
default void detach( @Nonnull IComputerAccess computer ) default void detach(@Nonnull IComputerAccess computer) {
{
} }
/** /**
* Get the object that this peripheral provides methods for. This will generally be the tile entity * Get the object that this peripheral provides methods for. This will generally be the tile entity or block, but may be an inventory, entity, etc...
* or block, but may be an inventory, entity, etc...
* *
* @return The object this peripheral targets * @return The object this peripheral targets
*/ */
@Nullable @Nullable
default Object getTarget() default Object getTarget() {
{
return null; return null;
} }
/** /**
* Determine whether this peripheral is equivalent to another one. * Determine whether this peripheral is equivalent to another one.
* *
* The minimal example should at least check whether they are the same object. However, you may wish to check if * The minimal example should at least check whether they are the same object. However, you may wish to check if they point to the same block or tile
* they point to the same block or tile entity. * entity.
* *
* @param other The peripheral to compare against. This may be {@code null}. * @param other The peripheral to compare against. This may be {@code null}.
* @return Whether these peripherals are equivalent. * @return Whether these peripherals are equivalent.
*/ */
boolean equals( @Nullable IPeripheral other ); boolean equals(@Nullable IPeripheral other);
} }

View File

@@ -3,27 +3,27 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
import java.util.Optional;
import javax.annotation.Nonnull;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.Optional;
/** /**
* This interface is used to create peripheral implementations for blocks. * This interface is used to create peripheral implementations for blocks.
* *
* If you have a {@link BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral} * If you have a {@link BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral} capability.
* capability.
* *
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/ */
@FunctionalInterface @FunctionalInterface
public interface IPeripheralProvider public interface IPeripheralProvider {
{
/** /**
* Produce an peripheral implementation from a block location. * Produce an peripheral implementation from a block location.
* *
@@ -34,5 +34,5 @@ public interface IPeripheralProvider
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/ */
@Nonnull @Nonnull
IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ); IPeripheral getPeripheral(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
} }

View File

@@ -3,29 +3,61 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
import javax.annotation.Nonnull;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
/** /**
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every * Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every computer receives a fair share of any
* computer receives a fair share of any processing time. * processing time.
* *
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but * This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but could be used for other purposes (such
* could be used for other purposes (such as complex computations done on another thread). * as complex computations done on another thread).
* *
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to * Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to execute work. If that returns true, you
* execute work. If that returns true, you should execute the task and use {@link #trackWork(long, TimeUnit)} to inform * should execute the task and use {@link #trackWork(long, TimeUnit)} to inform the monitor how long that task took.
* the monitor how long that task took.
* *
* Alternatively, use {@link #runWork(Runnable)} to run and keep track of work. * Alternatively, use {@link #runWork(Runnable)} to run and keep track of work.
* *
* @see IComputerAccess#getMainThreadMonitor() * @see IComputerAccess#getMainThreadMonitor()
*/ */
public interface IWorkMonitor public interface IWorkMonitor {
{ /**
* If the owning computer is currently allowed to execute work, and has ample time to do so.
*
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if you may do an initial piece of work,
* and shouldWork to determine if any additional task may be performed.
*
* @return If we should execute work right now.
*/
boolean shouldWork();
/**
* Run a task if possible, and inform the monitor of how long it took.
*
* @param runnable The task to run.
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
*/
default boolean runWork(@Nonnull Runnable runnable) {
Objects.requireNonNull(runnable, "runnable should not be null");
if (!this.canWork()) {
return false;
}
long start = System.nanoTime();
try {
runnable.run();
} finally {
this.trackWork(System.nanoTime() - start, TimeUnit.NANOSECONDS);
}
return true;
}
/** /**
* If the owning computer is currently allowed to execute work. * If the owning computer is currently allowed to execute work.
* *
@@ -33,45 +65,11 @@ public interface IWorkMonitor
*/ */
boolean canWork(); boolean canWork();
/**
* If the owning computer is currently allowed to execute work, and has ample time to do so.
*
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if
* you may do an initial piece of work, and shouldWork to determine if any additional task may be performed.
*
* @return If we should execute work right now.
*/
boolean shouldWork();
/** /**
* Inform the monitor how long some piece of work took to execute. * Inform the monitor how long some piece of work took to execute.
* *
* @param time The time some task took to run * @param time The time some task took to run
* @param unit The unit that {@code time} was measured in. * @param unit The unit that {@code time} was measured in.
*/ */
void trackWork( long time, @Nonnull TimeUnit unit ); void trackWork(long time, @Nonnull TimeUnit unit);
/**
* Run a task if possible, and inform the monitor of how long it took.
*
* @param runnable The task to run.
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
*/
default boolean runWork( @Nonnull Runnable runnable )
{
Objects.requireNonNull( runnable, "runnable should not be null" );
if( !canWork() ) return false;
long start = System.nanoTime();
try
{
runnable.run();
}
finally
{
trackWork( System.nanoTime() - start, TimeUnit.NANOSECONDS );
}
return true;
}
} }

View File

@@ -3,23 +3,20 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
/** /**
* Thrown when performing operations on {@link IComputerAccess} when the current peripheral is no longer attached to * Thrown when performing operations on {@link IComputerAccess} when the current peripheral is no longer attached to the computer.
* the computer.
*/ */
public class NotAttachedException extends IllegalStateException public class NotAttachedException extends IllegalStateException {
{
private static final long serialVersionUID = 1221244785535553536L; private static final long serialVersionUID = 1221244785535553536L;
public NotAttachedException() public NotAttachedException() {
{ super("You are not attached to this computer");
super( "You are not attached to this computer" );
} }
public NotAttachedException( String s ) public NotAttachedException(String s) {
{ super(s);
super( s );
} }
} }

View File

@@ -3,42 +3,37 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import net.minecraft.item.Item; import javax.annotation.Nonnull;
import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/** /**
* A base class for {@link IPocketUpgrade}s. * A base class for {@link IPocketUpgrade}s.
* *
* One does not have to use this, but it does provide a convenient template. * One does not have to use this, but it does provide a convenient template.
*/ */
public abstract class AbstractPocketUpgrade implements IPocketUpgrade public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
{
private final Identifier id; private final Identifier id;
private final String adjective; private final String adjective;
private final ItemStack stack; private final ItemStack stack;
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemConvertible item ) protected AbstractPocketUpgrade(Identifier id, ItemConvertible item) {
{ this(id, Util.createTranslationKey("upgrade", id) + ".adjective", item);
}
protected AbstractPocketUpgrade(Identifier id, String adjective, ItemConvertible item) {
this.id = id; this.id = id;
this.adjective = adjective; this.adjective = adjective;
this.stack = new ItemStack(item); this.stack = new ItemStack(item);
} }
protected AbstractPocketUpgrade( Identifier id, ItemConvertible item ) protected AbstractPocketUpgrade(Identifier id, String adjective, ItemStack stack) {
{
this( id, Util.createTranslationKey( "upgrade", id ) + ".adjective", item );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemStack stack )
{
this.id = id; this.id = id;
this.adjective = adjective; this.adjective = adjective;
this.stack = stack; this.stack = stack;
@@ -47,22 +42,19 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
@Nonnull @Nonnull
@Override @Override
public final Identifier getUpgradeID() public final Identifier getUpgradeID() {
{ return this.id;
return id;
} }
@Nonnull @Nonnull
@Override @Override
public final String getUnlocalisedAdjective() public final String getUnlocalisedAdjective() {
{ return this.adjective;
return adjective;
} }
@Nonnull @Nonnull
@Override @Override
public final ItemStack getCraftingItem() public final ItemStack getCraftingItem() {
{ return this.stack;
return stack;
} }
} }

View File

@@ -3,21 +3,24 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
/** /**
* Wrapper class for pocket computers. * Wrapper class for pocket computers.
*/ */
public interface IPocketAccess public interface IPocketAccess {
{
/** /**
* Gets the entity holding this item. * Gets the entity holding this item.
* *
@@ -31,8 +34,7 @@ public interface IPocketAccess
/** /**
* Get the colour of this pocket computer as a RGB number. * Get the colour of this pocket computer as a RGB number.
* *
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and * @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* {@code 0xFFFFFF} or -1 if it has no colour.
* @see #setColour(int) * @see #setColour(int)
*/ */
int getColour(); int getColour();
@@ -40,17 +42,16 @@ public interface IPocketAccess
/** /**
* Set the colour of the pocket computer to a RGB number. * Set the colour of the pocket computer to a RGB number.
* *
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between * @param colour The colour this pocket computer should be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour. * -1 to reset to the default colour.
* @see #getColour() * @see #getColour()
*/ */
void setColour( int colour ); void setColour(int colour);
/** /**
* Get the colour of this pocket computer's light as a RGB number. * Get the colour of this pocket computer's light as a RGB number.
* *
* @return The colour this light is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or * @return The colour this light is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* -1 if it has no colour.
* @see #setLight(int) * @see #setLight(int)
*/ */
int getLight(); int getLight();
@@ -58,11 +59,11 @@ public interface IPocketAccess
/** /**
* Set the colour of the pocket computer's light to a RGB number. * Set the colour of the pocket computer's light to a RGB number.
* *
* @param colour The colour this modem's light will be changed to. This should be a RGB colour between * @param colour The colour this modem's light will be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour. * to reset to the default colour.
* @see #getLight() * @see #getLight()
*/ */
void setLight( int colour ); void setLight(int colour);
/** /**
* Get the upgrade-specific NBT. * Get the upgrade-specific NBT.

View File

@@ -3,32 +3,32 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* Additional peripherals for pocket computers. * Additional peripherals for pocket computers.
* *
* This is similar to {@link ITurtleUpgrade}. * This is similar to {@link ITurtleUpgrade}.
*/ */
public interface IPocketUpgrade public interface IPocketUpgrade {
{
/** /**
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or * Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade".
* "my_mod:my_upgrade".
* *
* You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail * You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail registration if an already used ID is
* registration if an already used ID is specified. * specified.
* *
* @return The upgrade's id. * @return The upgrade's id.
* @see IPocketUpgrade#getUpgradeID() * @see IPocketUpgrade#getUpgradeID()
@@ -49,12 +49,11 @@ public interface IPocketUpgrade
String getUnlocalisedAdjective(); String getUnlocalisedAdjective();
/** /**
* Return an item stack representing the type of item that a pocket computer must be crafted with to create a * Return an item stack representing the type of item that a pocket computer must be crafted with to create a pocket computer which holds this upgrade.
* pocket computer which holds this upgrade. This item stack is also used to determine the upgrade given by * This item stack is also used to determine the upgrade given by {@code pocket.equip()}/{@code pocket.unequip()}.
* {@code pocket.equip()}/{@code pocket.unequip()}.
* *
* Ideally this should be constant over a session. It is recommended that you cache * Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
* the item too, in order to prevent constructing it every time the method is called. * method is called.
* *
* @return The item stack used for crafting. This can be {@link ItemStack#EMPTY} if crafting is disabled. * @return The item stack used for crafting. This can be {@link ItemStack#EMPTY} if crafting is disabled.
*/ */
@@ -64,16 +63,15 @@ public interface IPocketUpgrade
/** /**
* Creates a peripheral for the pocket computer. * Creates a peripheral for the pocket computer.
* *
* The peripheral created will be stored for the lifetime of the upgrade, will be passed an argument to * The peripheral created will be stored for the lifetime of the upgrade, will be passed an argument to {@link #update(IPocketAccess, IPeripheral)} and
* {@link #update(IPocketAccess, IPeripheral)} and will be attached, detached and have methods called in the same * will be attached, detached and have methods called in the same manner as an ordinary peripheral.
* manner as an ordinary peripheral.
* *
* @param access The access object for the pocket item stack. * @param access The access object for the pocket item stack.
* @return The newly created peripheral. * @return The newly created peripheral.
* @see #update(IPocketAccess, IPeripheral) * @see #update(IPocketAccess, IPeripheral)
*/ */
@Nullable @Nullable
IPeripheral createPeripheral( @Nonnull IPocketAccess access ); IPeripheral createPeripheral(@Nonnull IPocketAccess access);
/** /**
* Called when the pocket computer item stack updates. * Called when the pocket computer item stack updates.
@@ -82,8 +80,7 @@ public interface IPocketUpgrade
* @param peripheral The peripheral for this upgrade. * @param peripheral The peripheral for this upgrade.
* @see #createPeripheral(IPocketAccess) * @see #createPeripheral(IPocketAccess)
*/ */
default void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) default void update(@Nonnull IPocketAccess access, @Nullable IPeripheral peripheral) {
{
} }
/** /**
@@ -92,13 +89,11 @@ public interface IPocketUpgrade
* @param world The world the computer is in. * @param world The world the computer is in.
* @param access The access object for the pocket item stack. * @param access The access object for the pocket item stack.
* @param peripheral The peripheral for this upgrade. * @param peripheral The peripheral for this upgrade.
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path * @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path which returns {@code false}, such as
* which returns {@code false}, such as requiring the player to be sneaking - otherwise they will be unable to * requiring the player to be sneaking - otherwise they will be unable to access the GUI.
* access the GUI.
* @see #createPeripheral(IPocketAccess) * @see #createPeripheral(IPocketAccess)
*/ */
default boolean onRightClick( @Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) default boolean onRightClick(@Nonnull World world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral) {
{
return false; return false;
} }
} }

View File

@@ -3,31 +3,30 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.redstone; package dan200.computercraft.api.redstone;
import javax.annotation.Nonnull;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
/** /**
* This interface is used to provide bundled redstone output for blocks. * This interface is used to provide bundled redstone output for blocks.
* *
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
*/ */
@FunctionalInterface @FunctionalInterface
public interface IBundledRedstoneProvider public interface IBundledRedstoneProvider {
{
/** /**
* Produce an bundled redstone output from a block location. * Produce an bundled redstone output from a block location.
* *
* @param world The world this block is in. * @param world The world this block is in.
* @param pos The position this block is at. * @param pos The position this block is at.
* @param side The side to extract the bundled redstone output from. * @param side The side to extract the bundled redstone output from.
* @return A number in the range 0-65535 to indicate this block is providing output, or -1 if you do not wish to * @return A number in the range 0-65535 to indicate this block is providing output, or -1 if you do not wish to handle this block.
* handle this block.
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider) * @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
*/ */
int getBundledRedstoneOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ); int getBundledRedstoneOutput(@Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side);
} }

View File

@@ -3,24 +3,22 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import net.minecraft.item.Item; import javax.annotation.Nonnull;
import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.Util; import net.minecraft.util.Util;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/** /**
* A base class for {@link ITurtleUpgrade}s. * A base class for {@link ITurtleUpgrade}s.
* *
* One does not have to use this, but it does provide a convenient template. * One does not have to use this, but it does provide a convenient template.
*/ */
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
{
private final Identifier id; private final Identifier id;
private final TurtleUpgradeType type; private final TurtleUpgradeType type;
private final String adjective; private final String adjective;
@@ -48,29 +46,25 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
@Nonnull @Nonnull
@Override @Override
public final Identifier getUpgradeID() public final Identifier getUpgradeID() {
{ return this.id;
return id;
} }
@Nonnull @Nonnull
@Override @Override
public final String getUnlocalisedAdjective() public final String getUnlocalisedAdjective() {
{ return this.adjective;
return adjective;
} }
@Nonnull @Nonnull
@Override @Override
public final TurtleUpgradeType getType() public final TurtleUpgradeType getType() {
{ return this.type;
return type;
} }
@Nonnull @Nonnull
@Override @Override
public final ItemStack getCraftingItem() public final ItemStack getCraftingItem() {
{ return this.stack;
return stack;
} }
} }

View File

@@ -50,7 +50,6 @@ import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.village.TraderOfferList; import net.minecraft.village.TraderOfferList;
import net.minecraft.world.GameMode; import net.minecraft.world.GameMode;
import net.minecraft.world.dimension.DimensionType;
/** /**
* A wrapper for {@link ServerPlayerEntity} which denotes a "fake" player. * A wrapper for {@link ServerPlayerEntity} which denotes a "fake" player.
@@ -79,6 +78,16 @@ public class FakePlayer extends ServerPlayerEntity {
@Override @Override
public void onDeath(DamageSource damage) { } public void onDeath(DamageSource damage) { }
@Override
public Entity moveToWorld(ServerWorld destination) {
return this;
}
@Override
public void wakeUp(boolean bl, boolean updateSleepingPlayers) {
}
@Override @Override
public boolean startRiding(Entity entity, boolean flag) { public boolean startRiding(Entity entity, boolean flag) {
return false; return false;
@@ -160,6 +169,11 @@ public class FakePlayer extends ServerPlayerEntity {
@Override @Override
public void setGameMode(GameMode gameMode) { } public void setGameMode(GameMode gameMode) { }
@Override
public void sendMessage(Text message, MessageType type, UUID senderUuid) {
}
@Override @Override
public String getIp() { public String getIp() {
return "[Fake Player]"; return "[Fake Player]";
@@ -186,21 +200,6 @@ public class FakePlayer extends ServerPlayerEntity {
@Override @Override
public void playSound(SoundEvent soundEvent, SoundCategory soundCategory, float volume, float pitch) { } public void playSound(SoundEvent soundEvent, SoundCategory soundCategory, float volume, float pitch) { }
@Override
public Entity moveToWorld(ServerWorld destination) {
return this;
}
@Override
public void wakeUp(boolean bl, boolean updateSleepingPlayers) {
}
@Override
public void sendMessage(Text message, MessageType type, UUID senderUuid) {
}
private static class FakeNetHandler extends ServerPlayNetworkHandler { private static class FakeNetHandler extends ServerPlayNetworkHandler {
FakeNetHandler(ServerPlayerEntity player) { FakeNetHandler(ServerPlayerEntity player) {
super(player.server, new FakeConnection(), player); super(player.server, new FakeConnection(), player);

View File

@@ -3,13 +3,16 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import dan200.computercraft.api.lua.ILuaCallback; import dan200.computercraft.api.lua.ILuaCallback;
import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.ItemStorage; import dan200.computercraft.shared.util.ItemStorage;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
@@ -19,17 +22,12 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/** /**
* The interface passed to turtle by turtles, providing methods that they can call. * The interface passed to turtle by turtles, providing methods that they can call.
* *
* This should not be implemented by your classes. Do not interact with turtles except via this interface and * This should not be implemented by your classes. Do not interact with turtles except via this interface and {@link ITurtleUpgrade}.
* {@link ITurtleUpgrade}.
*/ */
public interface ITurtleAccess public interface ITurtleAccess {
{
/** /**
* Returns the world in which the turtle resides. * Returns the world in which the turtle resides.
* *
@@ -49,27 +47,25 @@ public interface ITurtleAccess
/** /**
* Attempt to move this turtle to a new position. * Attempt to move this turtle to a new position.
* *
* This will preserve the turtle's internal state, such as it's inventory, computer and upgrades. It should * This will preserve the turtle's internal state, such as it's inventory, computer and upgrades. It should be used before playing a movement animation
* be used before playing a movement animation using {@link #playAnimation(TurtleAnimation)}. * using {@link #playAnimation(TurtleAnimation)}.
* *
* @param world The new world to move it to * @param world The new world to move it to
* @param pos The new position to move it to. * @param pos The new position to move it to.
* @return Whether the movement was successful. It may fail if the block was not loaded or the block placement * @return Whether the movement was successful. It may fail if the block was not loaded or the block placement was cancelled.
* was cancelled.
* @throws UnsupportedOperationException When attempting to teleport on the client side. * @throws UnsupportedOperationException When attempting to teleport on the client side.
*/ */
boolean teleportTo( @Nonnull World world, @Nonnull BlockPos pos ); boolean teleportTo(@Nonnull World world, @Nonnull BlockPos pos);
/** /**
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered. * Returns a vector containing the floating point co-ordinates at which the turtle is rendered. This will shift when the turtle is moving.
* This will shift when the turtle is moving.
* *
* @param f The subframe fraction. * @param f The subframe fraction.
* @return A vector containing the floating point co-ordinates at which the turtle resides. * @return A vector containing the floating point co-ordinates at which the turtle resides.
* @see #getVisualYaw(float) * @see #getVisualYaw(float)
*/ */
@Nonnull @Nonnull
Vec3d getVisualPosition( float f ); Vec3d getVisualPosition(float f);
/** /**
* Returns the yaw the turtle is facing when it is rendered. * Returns the yaw the turtle is facing when it is rendered.
@@ -78,7 +74,7 @@ public interface ITurtleAccess
* @return The yaw the turtle is facing. * @return The yaw the turtle is facing.
* @see #getVisualPosition(float) * @see #getVisualPosition(float)
*/ */
float getVisualYaw( float f ); float getVisualYaw(float f);
/** /**
* Returns the world direction the turtle is currently facing. * Returns the world direction the turtle is currently facing.
@@ -90,13 +86,13 @@ public interface ITurtleAccess
Direction getDirection(); Direction getDirection();
/** /**
* Set the direction the turtle is facing. Note that this will not play a rotation animation, you will also need to * Set the direction the turtle is facing. Note that this will not play a rotation animation, you will also need to call {@link
* call {@link #playAnimation(TurtleAnimation)} to do so. * #playAnimation(TurtleAnimation)} to do so.
* *
* @param dir The new direction to set. This should be on either the x or z axis (so north, south, east or west). * @param dir The new direction to set. This should be on either the x or z axis (so north, south, east or west).
* @see #getDirection() * @see #getDirection()
*/ */
void setDirection( @Nonnull Direction dir ); void setDirection(@Nonnull Direction dir);
/** /**
* Get the currently selected slot in the turtle's inventory. * Get the currently selected slot in the turtle's inventory.
@@ -110,32 +106,30 @@ public interface ITurtleAccess
/** /**
* Set the currently selected slot in the turtle's inventory. * Set the currently selected slot in the turtle's inventory.
* *
* @param slot The slot to set. This must be greater or equal to 0 and less than the inventory size. Otherwise no * @param slot The slot to set. This must be greater or equal to 0 and less than the inventory size. Otherwise no action will be taken.
* action will be taken.
* @throws UnsupportedOperationException When attempting to change the slot on the client side. * @throws UnsupportedOperationException When attempting to change the slot on the client side.
* @see #getInventory() * @see #getInventory()
* @see #getSelectedSlot() * @see #getSelectedSlot()
*/ */
void setSelectedSlot( int slot ); void setSelectedSlot(int slot);
/**
* Set the colour of the turtle to a RGB number.
*
* @param colour The colour this turtle should be changed to. This should be a RGB colour between {@code 0x000000}
* and {@code 0xFFFFFF} or -1 to reset to the default colour.
* @see #getColour()
*/
void setColour( int colour );
/** /**
* Get the colour of this turtle as a RGB number. * Get the colour of this turtle as a RGB number.
* *
* @return The colour this turtle is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or * @return The colour this turtle is. This will be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 if it has no colour.
* -1 if it has no colour.
* @see #setColour(int) * @see #setColour(int)
*/ */
int getColour(); int getColour();
/**
* Set the colour of the turtle to a RGB number.
*
* @param colour The colour this turtle should be changed to. This should be a RGB colour between {@code 0x000000} and {@code 0xFFFFFF} or -1 to
* reset to the default colour.
* @see #getColour()
*/
void setColour(int colour);
/** /**
* Get the player who owns this turtle, namely whoever placed it. * Get the player who owns this turtle, namely whoever placed it.
* *
@@ -144,16 +138,6 @@ public interface ITurtleAccess
@Nonnull @Nonnull
GameProfile getOwningPlayer(); GameProfile getOwningPlayer();
/**
* Get the inventory of this turtle.
*
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory
*/
@Nonnull
Inventory getInventory();
/** /**
* Determine whether this turtle will require fuel when performing actions. * Determine whether this turtle will require fuel when performing actions.
* *
@@ -173,8 +157,7 @@ public interface ITurtleAccess
int getFuelLevel(); int getFuelLevel();
/** /**
* Set the fuel level to a new value. It is generally preferred to use {@link #consumeFuel(int)}} or {@link #addFuel(int)} * Set the fuel level to a new value. It is generally preferred to use {@link #consumeFuel(int)}} or {@link #addFuel(int)} instead.
* instead.
* *
* @param fuel The new amount of fuel. This must be between 0 and the fuel limit. * @param fuel The new amount of fuel. This must be between 0 and the fuel limit.
* @see #getFuelLevel() * @see #getFuelLevel()
@@ -182,7 +165,7 @@ public interface ITurtleAccess
* @see #addFuel(int) * @see #addFuel(int)
* @see #consumeFuel(int) * @see #consumeFuel(int)
*/ */
void setFuelLevel( int fuel ); void setFuelLevel(int fuel);
/** /**
* Get the maximum amount of fuel a turtle can hold. * Get the maximum amount of fuel a turtle can hold.
@@ -195,11 +178,11 @@ public interface ITurtleAccess
* Removes some fuel from the turtles fuel supply. Negative numbers can be passed in to INCREASE the fuel level of the turtle. * Removes some fuel from the turtles fuel supply. Negative numbers can be passed in to INCREASE the fuel level of the turtle.
* *
* @param fuel The amount of fuel to consume. * @param fuel The amount of fuel to consume.
* @return Whether the turtle was able to consume the amount of fuel specified. Will return false if you supply a number * @return Whether the turtle was able to consume the amount of fuel specified. Will return false if you supply a number greater than the current fuel
* greater than the current fuel level of the turtle. No fuel will be consumed if {@code false} is returned. * level of the turtle. No fuel will be consumed if {@code false} is returned.
* @throws UnsupportedOperationException When attempting to consume fuel on the client side. * @throws UnsupportedOperationException When attempting to consume fuel on the client side.
*/ */
boolean consumeFuel( int fuel ); boolean consumeFuel(int fuel);
/** /**
* Increase the turtle's fuel level by the given amount. * Increase the turtle's fuel level by the given amount.
@@ -207,34 +190,31 @@ public interface ITurtleAccess
* @param fuel The amount to refuel with. * @param fuel The amount to refuel with.
* @throws UnsupportedOperationException When attempting to refuel on the client side. * @throws UnsupportedOperationException When attempting to refuel on the client side.
*/ */
void addFuel( int fuel ); void addFuel(int fuel);
/** /**
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed * Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed on the main thread, so are
* on the main thread, so are guaranteed to be able to access Minecraft objects safely, and will be queued up * guaranteed to be able to access Minecraft objects safely, and will be queued up with the turtles standard movement and tool commands. An issued
* with the turtles standard movement and tool commands. An issued command will return an unique integer, which will * command will return an unique integer, which will be supplied as a parameter to a "turtle_response" event issued to the turtle after the command has
* be supplied as a parameter to a "turtle_response" event issued to the turtle after the command has completed. Look at the * completed. Look at the lua source code for "rom/apis/turtle" for how to build a lua wrapper around this functionality.
* lua source code for "rom/apis/turtle" for how to build a lua wrapper around this functionality.
* *
* @param command An object which will execute the custom command when its point in the queue is reached * @param command An object which will execute the custom command when its point in the queue is reached
* @return The objects the command returned when executed. you should probably return these to the player * @return The objects the command returned when executed. you should probably return these to the player unchanged if called from a peripheral method.
* unchanged if called from a peripheral method.
* @throws UnsupportedOperationException When attempting to execute a command on the client side. * @throws UnsupportedOperationException When attempting to execute a command on the client side.
* @see ITurtleCommand * @see ITurtleCommand
* @see MethodResult#pullEvent(String, ILuaCallback) * @see MethodResult#pullEvent(String, ILuaCallback)
*/ */
@Nonnull @Nonnull
MethodResult executeCommand( @Nonnull ITurtleCommand command ); MethodResult executeCommand(@Nonnull ITurtleCommand command);
/** /**
* Start playing a specific animation. This will prevent other turtle commands from executing until * Start playing a specific animation. This will prevent other turtle commands from executing until it is finished.
* it is finished.
* *
* @param animation The animation to play. * @param animation The animation to play.
* @throws UnsupportedOperationException When attempting to execute play an animation on the client side. * @throws UnsupportedOperationException When attempting to execute play an animation on the client side.
* @see TurtleAnimation * @see TurtleAnimation
*/ */
void playAnimation( @Nonnull TurtleAnimation animation ); void playAnimation(@Nonnull TurtleAnimation animation);
/** /**
* Returns the turtle on the specified side of the turtle, if there is one. * Returns the turtle on the specified side of the turtle, if there is one.
@@ -244,7 +224,7 @@ public interface ITurtleAccess
* @see #setUpgrade(TurtleSide, ITurtleUpgrade) * @see #setUpgrade(TurtleSide, ITurtleUpgrade)
*/ */
@Nullable @Nullable
ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side ); ITurtleUpgrade getUpgrade(@Nonnull TurtleSide side);
/** /**
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data. * Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
@@ -253,7 +233,7 @@ public interface ITurtleAccess
* @param upgrade The upgrade to set, may be {@code null} to clear. * @param upgrade The upgrade to set, may be {@code null} to clear.
* @see #getUpgrade(TurtleSide) * @see #getUpgrade(TurtleSide)
*/ */
void setUpgrade( @Nonnull TurtleSide side, @Nullable ITurtleUpgrade upgrade ); void setUpgrade(@Nonnull TurtleSide side, @Nullable ITurtleUpgrade upgrade);
/** /**
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one. * Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
@@ -262,31 +242,40 @@ public interface ITurtleAccess
* @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists. * @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists.
*/ */
@Nullable @Nullable
IPeripheral getPeripheral( @Nonnull TurtleSide side ); IPeripheral getPeripheral(@Nonnull TurtleSide side);
/** /**
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data. * Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
* *
* This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must * This will be persisted across turtle restarts and chunk loads, as well as being synced to the client. You must call {@link
* call {@link #updateUpgradeNBTData(TurtleSide)} after modifying it. * #updateUpgradeNBTData(TurtleSide)} after modifying it.
* *
* @param side The side to get the upgrade data for. * @param side The side to get the upgrade data for.
* @return The upgrade-specific data. * @return The upgrade-specific data.
* @see #updateUpgradeNBTData(TurtleSide) * @see #updateUpgradeNBTData(TurtleSide)
*/ */
@Nonnull @Nonnull
CompoundTag getUpgradeNBTData( @Nullable TurtleSide side ); CompoundTag getUpgradeNBTData(@Nullable TurtleSide side);
/** /**
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the * Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the client and persisted.
* client and persisted.
* *
* @param side The side to mark dirty. * @param side The side to mark dirty.
* @see #updateUpgradeNBTData(TurtleSide) * @see #updateUpgradeNBTData(TurtleSide)
*/ */
void updateUpgradeNBTData( @Nonnull TurtleSide side ); void updateUpgradeNBTData(@Nonnull TurtleSide side);
default ItemStorage getItemHandler() { default ItemStorage getItemHandler() {
return ItemStorage.wrap(this.getInventory()); return ItemStorage.wrap(this.getInventory());
} }
/**
* Get the inventory of this turtle.
*
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory
*/
@Nonnull
Inventory getInventory();
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -13,13 +14,12 @@ import javax.annotation.Nonnull;
* @see ITurtleAccess#executeCommand(ITurtleCommand) * @see ITurtleAccess#executeCommand(ITurtleCommand)
*/ */
@FunctionalInterface @FunctionalInterface
public interface ITurtleCommand public interface ITurtleCommand {
{
/** /**
* Will be called by the turtle on the main thread when it is time to execute the custom command. * Will be called by the turtle on the main thread when it is time to execute the custom command.
* *
* The handler should either perform the work of the command, and return success, or return * The handler should either perform the work of the command, and return success, or return failure with an error message to indicate the command cannot
* failure with an error message to indicate the command cannot be executed at this time. * be executed at this time.
* *
* @param turtle Access to the turtle for whom the command was issued. * @param turtle Access to the turtle for whom the command was issued.
* @return A result, indicating whether this action succeeded or not. * @return A result, indicating whether this action succeeded or not.
@@ -29,5 +29,5 @@ public interface ITurtleCommand
* @see TurtleCommandResult * @see TurtleCommandResult
*/ */
@Nonnull @Nonnull
TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ); TurtleCommandResult execute(@Nonnull ITurtleAccess turtle);
} }

View File

@@ -3,32 +3,32 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI; package dan200.computercraft.api.turtle;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/** /**
* The primary interface for defining an update for Turtles. A turtle update * The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new peripheral.
* can either be a new tool, or a new peripheral.
* *
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade) * @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
*/ */
public interface ITurtleUpgrade public interface ITurtleUpgrade {
{
/** /**
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade". * Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade". You should use a unique
* You should use a unique resource domain to ensure this upgrade is uniquely identified. * resource domain to ensure this upgrade is uniquely identified. The turtle will fail registration if an already used ID is specified.
* The turtle will fail registration if an already used ID is specified.
* *
* @return The unique ID for this upgrade. * @return The unique ID for this upgrade.
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade) * @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
@@ -56,12 +56,11 @@ public interface ITurtleUpgrade
TurtleUpgradeType getType(); TurtleUpgradeType getType();
/** /**
* Return an item stack representing the type of item that a turtle must be crafted * Return an item stack representing the type of item that a turtle must be crafted with to create a turtle which holds this upgrade. This item stack is
* with to create a turtle which holds this upgrade. This item stack is also used * also used to determine the upgrade given by {@code turtle.equip()}
* to determine the upgrade given by {@code turtle.equip()}
* *
* Ideally this should be constant over a session. It is recommended that you cache * Ideally this should be constant over a session. It is recommended that you cache the item too, in order to prevent constructing it every time the
* the item too, in order to prevent constructing it every time the method is called. * method is called.
* *
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted. * @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
*/ */
@@ -71,39 +70,32 @@ public interface ITurtleUpgrade
/** /**
* Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade. * Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
* *
* The peripheral created will be stored for the lifetime of the upgrade and will be passed as an argument to * The peripheral created will be stored for the lifetime of the upgrade and will be passed as an argument to {@link #update(ITurtleAccess,
* {@link #update(ITurtleAccess, TurtleSide)}. It will be attached, detached and have methods called in the same * TurtleSide)}. It will be attached, detached and have methods called in the same manner as a Computer peripheral.
* manner as a Computer peripheral.
* *
* @param turtle Access to the turtle that the peripheral is being created for. * @param turtle Access to the turtle that the peripheral is being created for.
* @param side Which side of the turtle (left or right) that the upgrade resides on. * @param side Which side of the turtle (left or right) that the upgrade resides on.
* @return The newly created peripheral. You may return {@code null} if this upgrade is a Tool * @return The newly created peripheral. You may return {@code null} if this upgrade is a Tool and this method is not expected to be called.
* and this method is not expected to be called.
*/ */
@Nullable @Nullable
default IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side ) default IPeripheral createPeripheral(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side) {
{
return null; return null;
} }
/** /**
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called * Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called by the turtle, and the tool is required to do some work.
* by the turtle, and the tool is required to do some work.
* *
* @param turtle Access to the turtle that the tool resides on. * @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on. * @param side Which side of the turtle (left or right) the tool resides on.
* @param verb Which action (dig or attack) the turtle is being called on to perform. * @param verb Which action (dig or attack) the turtle is being called on to perform.
* @param direction Which world direction the action should be performed in, relative to the turtles * @param direction Which world direction the action should be performed in, relative to the turtles position. This will either be up, down, or the
* position. This will either be up, down, or the direction the turtle is facing, depending on * direction the turtle is facing, depending on whether dig, digUp or digDown was called.
* whether dig, digUp or digDown was called. * @return Whether the turtle was able to perform the action, and hence whether the {@code turtle.dig()} or {@code turtle.attack()} lua method should
* @return Whether the turtle was able to perform the action, and hence whether the {@code turtle.dig()} * return true. If true is returned, the tool will perform a swinging animation. You may return {@code null} if this turtle is a Peripheral and
* or {@code turtle.attack()} lua method should return true. If true is returned, the tool will perform * this method is not expected to be called.
* a swinging animation. You may return {@code null} if this turtle is a Peripheral and this method is not expected
* to be called.
*/ */
@Nonnull @Nonnull
default TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction ) default TurtleCommandResult useTool(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction) {
{
return TurtleCommandResult.failure(); return TurtleCommandResult.failure();
} }
@@ -115,8 +107,8 @@ public interface ITurtleUpgrade
* @return The model that you wish to be used to render your upgrade. * @return The model that you wish to be used to render your upgrade.
*/ */
@Nonnull @Nonnull
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
TransformedModel getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side ); TransformedModel getModel(@Nullable ITurtleAccess turtle, @Nonnull TurtleSide side);
/** /**
* Called once per tick for each turtle which has the upgrade equipped. * Called once per tick for each turtle which has the upgrade equipped.
@@ -124,7 +116,6 @@ public interface ITurtleUpgrade
* @param turtle Access to the turtle that the upgrade resides on. * @param turtle Access to the turtle that the upgrade resides on.
* @param side Which side of the turtle (left or right) the upgrade resides on. * @param side Which side of the turtle (left or right) the upgrade resides on.
*/ */
default void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side ) default void update(@Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side) {
{
} }
} }

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
/** /**
@@ -12,8 +13,7 @@ package dan200.computercraft.api.turtle;
* *
* @see ITurtleAccess#playAnimation(TurtleAnimation) * @see ITurtleAccess#playAnimation(TurtleAnimation)
*/ */
public enum TurtleAnimation public enum TurtleAnimation {
{
/** /**
* An animation which does nothing. This takes no time to complete. * An animation which does nothing. This takes no time to complete.
* *
@@ -23,38 +23,34 @@ public enum TurtleAnimation
NONE, NONE,
/** /**
* Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and * Make the turtle move forward. Note that the animation starts from the block <em>behind</em> it, and moves into this one.
* moves into this one.
*/ */
MOVE_FORWARD, MOVE_FORWARD,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>in front</em> it, and moves into this one.
* moves into this one.
*/ */
MOVE_BACK, MOVE_BACK,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>above</em> it, and moves into this one.
* moves into this one.
*/ */
MOVE_UP, MOVE_UP,
/** /**
* Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and * Make the turtle move backwards. Note that the animation starts from the block <em>below</em> it, and moves into this one.
* moves into this one.
*/ */
MOVE_DOWN, MOVE_DOWN,
/** /**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and the turtle turns to face in the current
* the turtle turns to face in the current direction. * direction.
*/ */
TURN_LEFT, TURN_LEFT,
/** /**
* Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and * Turn the turtle to the left. Note that the animation starts with the turtle facing <em>right</em>, and the turtle turns to face in the current
* the turtle turns to face in the current direction. * direction.
*/ */
TURN_RIGHT, TURN_RIGHT,

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -14,10 +15,18 @@ import javax.annotation.Nullable;
* @see ITurtleCommand#execute(ITurtleAccess) * @see ITurtleCommand#execute(ITurtleAccess)
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction) * @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
*/ */
public final class TurtleCommandResult public final class TurtleCommandResult {
{ private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult(true, null, null);
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult( true, null, null ); private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult(false, null, null);
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult( false, null, null ); private final boolean success;
private final String errorMessage;
private final Object[] results;
private TurtleCommandResult(boolean success, String errorMessage, Object[] results) {
this.success = success;
this.errorMessage = errorMessage;
this.results = results;
}
/** /**
* Create a successful command result with no result. * Create a successful command result with no result.
@@ -25,8 +34,7 @@ public final class TurtleCommandResult
* @return A successful command result with no values. * @return A successful command result with no values.
*/ */
@Nonnull @Nonnull
public static TurtleCommandResult success() public static TurtleCommandResult success() {
{
return EMPTY_SUCCESS; return EMPTY_SUCCESS;
} }
@@ -37,10 +45,11 @@ public final class TurtleCommandResult
* @return A successful command result with the given values. * @return A successful command result with the given values.
*/ */
@Nonnull @Nonnull
public static TurtleCommandResult success( @Nullable Object[] results ) public static TurtleCommandResult success(@Nullable Object[] results) {
{ if (results == null || results.length == 0) {
if( results == null || results.length == 0 ) return EMPTY_SUCCESS; return EMPTY_SUCCESS;
return new TurtleCommandResult( true, null, results ); }
return new TurtleCommandResult(true, null, results);
} }
/** /**
@@ -49,8 +58,7 @@ public final class TurtleCommandResult
* @return A failed command result with no message. * @return A failed command result with no message.
*/ */
@Nonnull @Nonnull
public static TurtleCommandResult failure() public static TurtleCommandResult failure() {
{
return EMPTY_FAILURE; return EMPTY_FAILURE;
} }
@@ -61,21 +69,11 @@ public final class TurtleCommandResult
* @return A failed command result with a message. * @return A failed command result with a message.
*/ */
@Nonnull @Nonnull
public static TurtleCommandResult failure( @Nullable String errorMessage ) public static TurtleCommandResult failure(@Nullable String errorMessage) {
{ if (errorMessage == null) {
if( errorMessage == null ) return EMPTY_FAILURE; return EMPTY_FAILURE;
return new TurtleCommandResult( false, errorMessage, null );
} }
return new TurtleCommandResult(false, errorMessage, null);
private final boolean success;
private final String errorMessage;
private final Object[] results;
private TurtleCommandResult( boolean success, String errorMessage, Object[] results )
{
this.success = success;
this.errorMessage = errorMessage;
this.results = results;
} }
/** /**
@@ -83,9 +81,8 @@ public final class TurtleCommandResult
* *
* @return If the command was successful. * @return If the command was successful.
*/ */
public boolean isSuccess() public boolean isSuccess() {
{ return this.success;
return success;
} }
/** /**
@@ -94,9 +91,8 @@ public final class TurtleCommandResult
* @return The command's error message, or {@code null} if it was a success. * @return The command's error message, or {@code null} if it was a success.
*/ */
@Nullable @Nullable
public String getErrorMessage() public String getErrorMessage() {
{ return this.errorMessage;
return errorMessage;
} }
/** /**
@@ -105,8 +101,7 @@ public final class TurtleCommandResult
* @return The command's result, or {@code null} if it was a failure. * @return The command's result, or {@code null} if it was a failure.
*/ */
@Nullable @Nullable
public Object[] getResults() public Object[] getResults() {
{ return this.results;
return results;
} }
} }

View File

@@ -3,13 +3,13 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
/** /**
* An enum representing the two sides of the turtle that a turtle turtle might reside. * An enum representing the two sides of the turtle that a turtle turtle might reside.
*/ */
public enum TurtleSide public enum TurtleSide {
{
/** /**
* The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle). * The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle).
*/ */

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
/** /**
@@ -10,34 +11,30 @@ package dan200.computercraft.api.turtle;
* *
* @see ITurtleUpgrade#getType() * @see ITurtleUpgrade#getType()
*/ */
public enum TurtleUpgradeType public enum TurtleUpgradeType {
{
/** /**
* A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()} * A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()} and {@code turtle.attack()} methods (Such as
* and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles). * pickaxe or sword on Mining and Melee turtles).
*/ */
TOOL, TOOL,
/** /**
* A peripheral adds a special peripheral which is attached to the side of the turtle, * A peripheral adds a special peripheral which is attached to the side of the turtle, and can be interacted with the {@code peripheral} API (Such as
* and can be interacted with the {@code peripheral} API (Such as the modem on Wireless Turtles). * the modem on Wireless Turtles).
*/ */
PERIPHERAL, PERIPHERAL,
/** /**
* An upgrade which provides both a tool and a peripheral. This can be used when you wish * An upgrade which provides both a tool and a peripheral. This can be used when you wish your upgrade to also provide methods. For example, a pickaxe
* your upgrade to also provide methods. For example, a pickaxe could provide methods * could provide methods determining whether it can break the given block or not.
* determining whether it can break the given block or not.
*/ */
BOTH; BOTH;
public boolean isTool() public boolean isTool() {
{
return this == TOOL || this == BOTH; return this == TOOL || this == BOTH;
} }
public boolean isPeripheral() public boolean isPeripheral() {
{
return this == PERIPHERAL || this == BOTH; return this == PERIPHERAL || this == BOTH;
} }
} }

View File

@@ -3,17 +3,16 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
/** /**
* An enum representing the different actions that an {@link ITurtleUpgrade} of type Tool may be called on to perform by * An enum representing the different actions that an {@link ITurtleUpgrade} of type Tool may be called on to perform by a turtle.
* a turtle.
* *
* @see ITurtleUpgrade#getType() * @see ITurtleUpgrade#getType()
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction) * @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
*/ */
public enum TurtleVerb public enum TurtleVerb {
{
/** /**
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}. * The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
*/ */

View File

@@ -3,6 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
/** /**
@@ -10,8 +11,7 @@ package dan200.computercraft.api.turtle.event;
* *
* @see TurtleActionEvent * @see TurtleActionEvent
*/ */
public enum TurtleAction public enum TurtleAction {
{
/** /**
* A turtle moves to a new position. * A turtle moves to a new position.
* *

View File

@@ -3,35 +3,34 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.api.turtle.TurtleCommandResult;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
/** /**
* An event fired when a turtle is performing a known action. * An event fired when a turtle is performing a known action.
*/ */
public class TurtleActionEvent extends TurtleEvent public class TurtleActionEvent extends TurtleEvent {
{
private final TurtleAction action; private final TurtleAction action;
private String failureMessage; private String failureMessage;
private boolean cancelled = false; private boolean cancelled = false;
public TurtleActionEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action ) public TurtleActionEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action) {
{ super(turtle);
super( turtle );
Objects.requireNonNull( action, "action cannot be null" ); Objects.requireNonNull(action, "action cannot be null");
this.action = action; this.action = action;
} }
public TurtleAction getAction() public TurtleAction getAction() {
{ return this.action;
return action;
} }
/** /**
@@ -44,9 +43,8 @@ public class TurtleActionEvent extends TurtleEvent
* @deprecated Use {@link #setCanceled(boolean, String)} instead. * @deprecated Use {@link #setCanceled(boolean, String)} instead.
*/ */
@Deprecated @Deprecated
public void setCanceled( boolean cancel ) public void setCanceled(boolean cancel) {
{ this.setCanceled(cancel, null);
setCanceled( cancel, null );
} }
/** /**
@@ -58,8 +56,7 @@ public class TurtleActionEvent extends TurtleEvent
* @param failureMessage The message to return to the user explaining the failure. * @param failureMessage The message to return to the user explaining the failure.
* @see TurtleCommandResult#failure(String) * @see TurtleCommandResult#failure(String)
*/ */
public void setCanceled( boolean cancel, @Nullable String failureMessage ) public void setCanceled(boolean cancel, @Nullable String failureMessage) {
{
this.cancelled = true; this.cancelled = true;
this.failureMessage = cancel ? failureMessage : null; this.failureMessage = cancel ? failureMessage : null;
} }
@@ -72,12 +69,11 @@ public class TurtleActionEvent extends TurtleEvent
* @see #setCanceled(boolean, String) * @see #setCanceled(boolean, String)
*/ */
@Nullable @Nullable
public String getFailureMessage() public String getFailureMessage() {
{ return this.failureMessage;
return failureMessage;
} }
public boolean isCancelled() { public boolean isCancelled() {
return cancelled; return this.cancelled;
} }
} }

View File

@@ -3,31 +3,36 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import dan200.computercraft.api.turtle.*; import java.util.Objects;
import net.minecraft.entity.Entity;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Objects;
import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import net.minecraft.entity.Entity;
/** /**
* Fired when a turtle attempts to attack an entity. * Fired when a turtle attempts to attack an entity.
* *
* @see TurtleAction#ATTACK * @see TurtleAction#ATTACK
*/ */
public class TurtleAttackEvent extends TurtlePlayerEvent public class TurtleAttackEvent extends TurtlePlayerEvent {
{
private final Entity target; private final Entity target;
private final ITurtleUpgrade upgrade; private final ITurtleUpgrade upgrade;
private final TurtleSide side; private final TurtleSide side;
public TurtleAttackEvent(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side ) public TurtleAttackEvent(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade,
{ @Nonnull TurtleSide side) {
super( turtle, TurtleAction.ATTACK, player ); super(turtle, TurtleAction.ATTACK, player);
Objects.requireNonNull( target, "target cannot be null" ); Objects.requireNonNull(target, "target cannot be null");
Objects.requireNonNull( upgrade, "upgrade cannot be null" ); Objects.requireNonNull(upgrade, "upgrade cannot be null");
Objects.requireNonNull( side, "side cannot be null" ); Objects.requireNonNull(side, "side cannot be null");
this.target = target; this.target = target;
this.upgrade = upgrade; this.upgrade = upgrade;
this.side = side; this.side = side;
@@ -39,9 +44,8 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The entity being attacked. * @return The entity being attacked.
*/ */
@Nonnull @Nonnull
public Entity getTarget() public Entity getTarget() {
{ return this.target;
return target;
} }
/** /**
@@ -50,9 +54,8 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The upgrade responsible for attacking. * @return The upgrade responsible for attacking.
*/ */
@Nonnull @Nonnull
public ITurtleUpgrade getUpgrade() public ITurtleUpgrade getUpgrade() {
{ return this.upgrade;
return upgrade;
} }
/** /**
@@ -61,8 +64,7 @@ public class TurtleAttackEvent extends TurtlePlayerEvent
* @return The upgrade's side. * @return The upgrade's side.
*/ */
@Nonnull @Nonnull
public TurtleSide getSide() public TurtleSide getSide() {
{ return this.side;
return side;
} }
} }

View File

@@ -3,42 +3,44 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.Map;
import java.util.Objects;
/** /**
* A general event for when a turtle interacts with a block or region. * A general event for when a turtle interacts with a block or region.
* *
* You should generally listen to one of the sub-events instead, cancelling them where * You should generally listen to one of the sub-events instead, cancelling them where appropriate.
* appropriate.
* *
* Note that you are not guaranteed to receive this event, if it has been cancelled by other * Note that you are not guaranteed to receive this event, if it has been cancelled by other mechanisms, such as block protection systems.
* mechanisms, such as block protection systems.
* *
* Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact * Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact with a block, simply objects within that block space.
* with a block, simply objects within that block space.
*/ */
public abstract class TurtleBlockEvent extends TurtlePlayerEvent public abstract class TurtleBlockEvent extends TurtlePlayerEvent {
{
private final World world; private final World world;
private final BlockPos pos; private final BlockPos pos;
protected TurtleBlockEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos ) protected TurtleBlockEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world,
{ @Nonnull BlockPos pos) {
super( turtle, action, player ); super(turtle, action, player);
Objects.requireNonNull( world, "world cannot be null" ); Objects.requireNonNull(world, "world cannot be null");
Objects.requireNonNull( pos, "pos cannot be null" ); Objects.requireNonNull(pos, "pos cannot be null");
this.world = world; this.world = world;
this.pos = pos; this.pos = pos;
} }
@@ -48,20 +50,17 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* *
* @return The world the turtle is interacting in. * @return The world the turtle is interacting in.
*/ */
public World getWorld() public World getWorld() {
{ return this.world;
return world;
} }
/** /**
* Get the position the turtle is interacting with. Note that this is different * Get the position the turtle is interacting with. Note that this is different to {@link ITurtleAccess#getPosition()}.
* to {@link ITurtleAccess#getPosition()}.
* *
* @return The position the turtle is interacting with. * @return The position the turtle is interacting with.
*/ */
public BlockPos getPos() public BlockPos getPos() {
{ return this.pos;
return pos;
} }
/** /**
@@ -69,19 +68,18 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* *
* @see TurtleAction#DIG * @see TurtleAction#DIG
*/ */
public static class Dig extends TurtleBlockEvent public static class Dig extends TurtleBlockEvent {
{
private final BlockState block; private final BlockState block;
private final ITurtleUpgrade upgrade; private final ITurtleUpgrade upgrade;
private final TurtleSide side; private final TurtleSide side;
public Dig( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState block, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side ) public Dig(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState block,
{ @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side) {
super( turtle, TurtleAction.DIG, player, world, pos ); super(turtle, TurtleAction.DIG, player, world, pos);
Objects.requireNonNull( block, "block cannot be null" ); Objects.requireNonNull(block, "block cannot be null");
Objects.requireNonNull( upgrade, "upgrade cannot be null" ); Objects.requireNonNull(upgrade, "upgrade cannot be null");
Objects.requireNonNull( side, "side cannot be null" ); Objects.requireNonNull(side, "side cannot be null");
this.block = block; this.block = block;
this.upgrade = upgrade; this.upgrade = upgrade;
this.side = side; this.side = side;
@@ -93,9 +91,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The block which is going to be broken. * @return The block which is going to be broken.
*/ */
@Nonnull @Nonnull
public BlockState getBlock() public BlockState getBlock() {
{ return this.block;
return block;
} }
/** /**
@@ -104,9 +101,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The upgrade doing the digging. * @return The upgrade doing the digging.
*/ */
@Nonnull @Nonnull
public ITurtleUpgrade getUpgrade() public ITurtleUpgrade getUpgrade() {
{ return this.upgrade;
return upgrade;
} }
/** /**
@@ -115,9 +111,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The upgrade's side. * @return The upgrade's side.
*/ */
@Nonnull @Nonnull
public TurtleSide getSide() public TurtleSide getSide() {
{ return this.side;
return side;
} }
} }
@@ -126,11 +121,9 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* *
* @see TurtleAction#MOVE * @see TurtleAction#MOVE
*/ */
public static class Move extends TurtleBlockEvent public static class Move extends TurtleBlockEvent {
{ public Move(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos) {
public Move( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos ) super(turtle, TurtleAction.MOVE, player, world, pos);
{
super( turtle, TurtleAction.MOVE, player, world, pos );
} }
} }
@@ -139,15 +132,13 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* *
* @see TurtleAction#PLACE * @see TurtleAction#PLACE
*/ */
public static class Place extends TurtleBlockEvent public static class Place extends TurtleBlockEvent {
{
private final ItemStack stack; private final ItemStack stack;
public Place( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack ) public Place(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack) {
{ super(turtle, TurtleAction.PLACE, player, world, pos);
super( turtle, TurtleAction.PLACE, player, world, pos );
Objects.requireNonNull( stack, "stack cannot be null" ); Objects.requireNonNull(stack, "stack cannot be null");
this.stack = stack; this.stack = stack;
} }
@@ -157,9 +148,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The item stack to be placed. * @return The item stack to be placed.
*/ */
@Nonnull @Nonnull
public ItemStack getStack() public ItemStack getStack() {
{ return this.stack;
return stack;
} }
} }
@@ -170,17 +160,16 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* *
* @see TurtleAction#INSPECT * @see TurtleAction#INSPECT
*/ */
public static class Inspect extends TurtleBlockEvent public static class Inspect extends TurtleBlockEvent {
{
private final BlockState state; private final BlockState state;
private final Map<String, Object> data; private final Map<String, Object> data;
public Inspect( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull Map<String, Object> data ) public Inspect(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull BlockState state,
{ @Nonnull Map<String, Object> data) {
super( turtle, TurtleAction.INSPECT, player, world, pos ); super(turtle, TurtleAction.INSPECT, player, world, pos);
Objects.requireNonNull( state, "state cannot be null" ); Objects.requireNonNull(state, "state cannot be null");
Objects.requireNonNull( data, "data cannot be null" ); Objects.requireNonNull(data, "data cannot be null");
this.data = data; this.data = data;
this.state = state; this.state = state;
} }
@@ -191,9 +180,8 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return The inspected block state. * @return The inspected block state.
*/ */
@Nonnull @Nonnull
public BlockState getState() public BlockState getState() {
{ return this.state;
return state;
} }
/** /**
@@ -202,21 +190,18 @@ public abstract class TurtleBlockEvent extends TurtlePlayerEvent
* @return This block's inspection data. * @return This block's inspection data.
*/ */
@Nonnull @Nonnull
public Map<String, Object> getData() public Map<String, Object> getData() {
{ return this.data;
return data;
} }
/** /**
* Add new information to the inspection result. Note this will override fields with the same name. * Add new information to the inspection result. Note this will override fields with the same name.
* *
* @param newData The data to add. Note all values should be convertible to Lua (see * @param newData The data to add. Note all values should be convertible to Lua (see {@link MethodResult#of(Object)}).
* {@link MethodResult#of(Object)}).
*/ */
public void addData( @Nonnull Map<String, ?> newData ) public void addData(@Nonnull Map<String, ?> newData) {
{ Objects.requireNonNull(newData, "newData cannot be null");
Objects.requireNonNull( newData, "newData cannot be null" ); this.data.putAll(newData);
data.putAll( newData );
} }
} }
} }

View File

@@ -3,24 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Objects;
import javax.annotation.Nonnull;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import javax.annotation.Nonnull;
import java.util.Objects;
/** /**
* A base class for all events concerning a turtle. This will only ever constructed and fired on the server side, * A base class for all events concerning a turtle. This will only ever constructed and fired on the server side, so sever specific methods on {@link
* so sever specific methods on {@link ITurtleAccess} are safe to use. * ITurtleAccess} are safe to use.
* *
* You should generally not need to subscribe to this event, preferring one of the more specific classes. * You should generally not need to subscribe to this event, preferring one of the more specific classes.
* *
* @see TurtleActionEvent * @see TurtleActionEvent
*/ */
public abstract class TurtleEvent public abstract class TurtleEvent {
{
public static final EventBus EVENT_BUS = new EventBus(); public static final EventBus EVENT_BUS = new EventBus();
private final ITurtleAccess turtle; private final ITurtleAccess turtle;

View File

@@ -3,42 +3,42 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull; import net.minecraft.item.ItemStack;
import java.util.Map;
import java.util.Objects;
/** /**
* Fired when a turtle gathers data on an item in its inventory. * Fired when a turtle gathers data on an item in its inventory.
* *
* You may prevent items being inspected, or add additional information to the result. Be aware that this may be fired * You may prevent items being inspected, or add additional information to the result. Be aware that this may be fired on the computer thread, and so any
* on the computer thread, and so any operations on it must be thread safe. * operations on it must be thread safe.
* *
* @see TurtleAction#INSPECT_ITEM * @see TurtleAction#INSPECT_ITEM
*/ */
public class TurtleInspectItemEvent extends TurtleActionEvent public class TurtleInspectItemEvent extends TurtleActionEvent {
{
private final ItemStack stack; private final ItemStack stack;
private final Map<String, Object> data; private final Map<String, Object> data;
private final boolean mainThread; private final boolean mainThread;
@Deprecated @Deprecated
public TurtleInspectItemEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data ) public TurtleInspectItemEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data) {
{ this(turtle, stack, data, false);
this( turtle, stack, data, false );
} }
public TurtleInspectItemEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data, boolean mainThread ) public TurtleInspectItemEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, @Nonnull Map<String, Object> data, boolean mainThread) {
{ super(turtle, TurtleAction.INSPECT_ITEM);
super( turtle, TurtleAction.INSPECT_ITEM );
Objects.requireNonNull( stack, "stack cannot be null" ); Objects.requireNonNull(stack, "stack cannot be null");
Objects.requireNonNull( data, "data cannot be null" ); Objects.requireNonNull(data, "data cannot be null");
this.stack = stack; this.stack = stack;
this.data = data; this.data = data;
this.mainThread = mainThread; this.mainThread = mainThread;
@@ -50,9 +50,8 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
* @return The item stack which is being inspected. This should <b>not</b> be modified. * @return The item stack which is being inspected. This should <b>not</b> be modified.
*/ */
@Nonnull @Nonnull
public ItemStack getStack() public ItemStack getStack() {
{ return this.stack;
return stack;
} }
/** /**
@@ -61,31 +60,26 @@ public class TurtleInspectItemEvent extends TurtleActionEvent
* @return This items's inspection data. * @return This items's inspection data.
*/ */
@Nonnull @Nonnull
public Map<String, Object> getData() public Map<String, Object> getData() {
{ return this.data;
return data;
} }
/** /**
* If this event is being fired on the server thread. When true, information which relies on server state may be * If this event is being fired on the server thread. When true, information which relies on server state may be exposed.
* exposed.
* *
* @return If this is run on the main thread. * @return If this is run on the main thread.
*/ */
public boolean onMainThread() public boolean onMainThread() {
{ return this.mainThread;
return mainThread;
} }
/** /**
* Add new information to the inspection result. Note this will override fields with the same name. * Add new information to the inspection result. Note this will override fields with the same name.
* *
* @param newData The data to add. Note all values should be convertible to Lua (see * @param newData The data to add. Note all values should be convertible to Lua (see {@link MethodResult#of(Object)}).
* {@link MethodResult#of(Object)}).
*/ */
public void addData( @Nonnull Map<String, ?> newData ) public void addData(@Nonnull Map<String, ?> newData) {
{ Objects.requireNonNull(newData, "newData cannot be null");
Objects.requireNonNull( newData, "newData cannot be null" ); this.data.putAll(newData);
data.putAll( newData );
} }
} }

View File

@@ -3,29 +3,31 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.turtle.FakePlayer; import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
/** /**
* Fired when a turtle attempts to interact with an inventory. * Fired when a turtle attempts to interact with an inventory.
*/ */
public abstract class TurtleInventoryEvent extends TurtleBlockEvent public abstract class TurtleInventoryEvent extends TurtleBlockEvent {
{
private final Inventory handler; private final Inventory handler;
protected TurtleInventoryEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler ) protected TurtleInventoryEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world,
{ @Nonnull BlockPos pos, @Nullable Inventory handler) {
super( turtle, action, player, world, pos ); super(turtle, action, player, world, pos);
this.handler = handler; this.handler = handler;
} }
@@ -35,9 +37,8 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
* @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world. * @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world.
*/ */
@Nullable @Nullable
public Inventory getItemHandler() public Inventory getItemHandler() {
{ return this.handler;
return handler;
} }
/** /**
@@ -45,11 +46,9 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
* *
* @see TurtleAction#SUCK * @see TurtleAction#SUCK
*/ */
public static class Suck extends TurtleInventoryEvent public static class Suck extends TurtleInventoryEvent {
{ public Suck(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler) {
public Suck( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler ) super(turtle, TurtleAction.SUCK, player, world, pos, handler);
{
super( turtle, TurtleAction.SUCK, player, world, pos, handler );
} }
} }
@@ -58,15 +57,14 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
* *
* @see TurtleAction#DROP * @see TurtleAction#DROP
*/ */
public static class Drop extends TurtleInventoryEvent public static class Drop extends TurtleInventoryEvent {
{
private final ItemStack stack; private final ItemStack stack;
public Drop( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler, @Nonnull ItemStack stack ) public Drop(@Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable Inventory handler,
{ @Nonnull ItemStack stack) {
super( turtle, TurtleAction.DROP, player, world, pos, handler ); super(turtle, TurtleAction.DROP, player, world, pos, handler);
Objects.requireNonNull( stack, "stack cannot be null" ); Objects.requireNonNull(stack, "stack cannot be null");
this.stack = stack; this.stack = stack;
} }
@@ -76,9 +74,8 @@ public abstract class TurtleInventoryEvent extends TurtleBlockEvent
* @return The item stack which will be dropped. This should <b>not</b> be modified. * @return The item stack which will be dropped. This should <b>not</b> be modified.
*/ */
@Nonnull @Nonnull
public ItemStack getStack() public ItemStack getStack() {
{ return this.stack;
return stack;
} }
} }
} }

View File

@@ -3,28 +3,28 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import java.util.Objects;
import javax.annotation.Nonnull;
import dan200.computercraft.api.turtle.FakePlayer; import dan200.computercraft.api.turtle.FakePlayer;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import javax.annotation.Nonnull;
import java.util.Objects;
/** /**
* An action done by a turtle which is normally done by a player. * An action done by a turtle which is normally done by a player.
* *
* {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks. * {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks.
*/ */
public abstract class TurtlePlayerEvent extends TurtleActionEvent public abstract class TurtlePlayerEvent extends TurtleActionEvent {
{
private final FakePlayer player; private final FakePlayer player;
protected TurtlePlayerEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player ) protected TurtlePlayerEvent(@Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player) {
{ super(turtle, action);
super( turtle, action );
Objects.requireNonNull( player, "player cannot be null" ); Objects.requireNonNull(player, "player cannot be null");
this.player = player; this.player = player;
} }
@@ -36,8 +36,7 @@ public abstract class TurtlePlayerEvent extends TurtleActionEvent
* @return A {@link FakePlayer} representing this turtle. * @return A {@link FakePlayer} representing this turtle.
*/ */
@Nonnull @Nonnull
public FakePlayer getPlayer() public FakePlayer getPlayer() {
{ return this.player;
return player;
} }
} }

View File

@@ -3,31 +3,32 @@
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. * Copyright Daniel Ratcliffe, 2011-2020. 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. * For help using the API, and posting your mods, visit the forums at computercraft.info.
*/ */
package dan200.computercraft.api.turtle.event; package dan200.computercraft.api.turtle.event;
import dan200.computercraft.api.turtle.ITurtleAccess; import java.util.Objects;
import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Objects;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
/** /**
* Fired when a turtle attempts to refuel from an item. * Fired when a turtle attempts to refuel from an item.
* *
* One may use {@link #setCanceled(boolean, String)} to prevent refueling from this specific item. Additionally, you * One may use {@link #setCanceled(boolean, String)} to prevent refueling from this specific item. Additionally, you may use {@link #setHandler(Handler)} to
* may use {@link #setHandler(Handler)} to register a custom fuel provider. * register a custom fuel provider.
*/ */
public class TurtleRefuelEvent extends TurtleActionEvent public class TurtleRefuelEvent extends TurtleActionEvent {
{
private final ItemStack stack; private final ItemStack stack;
private Handler handler; private Handler handler;
public TurtleRefuelEvent( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack ) public TurtleRefuelEvent(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack) {
{ super(turtle, TurtleAction.REFUEL);
super( turtle, TurtleAction.REFUEL );
Objects.requireNonNull( turtle, "turtle cannot be null" ); Objects.requireNonNull(turtle, "turtle cannot be null");
this.stack = stack; this.stack = stack;
} }
@@ -38,9 +39,8 @@ public class TurtleRefuelEvent extends TurtleActionEvent
* *
* @return The stack to refuel from. * @return The stack to refuel from.
*/ */
public ItemStack getStack() public ItemStack getStack() {
{ return this.stack;
return stack;
} }
/** /**
@@ -50,22 +50,19 @@ public class TurtleRefuelEvent extends TurtleActionEvent
* @see #setHandler(Handler) * @see #setHandler(Handler)
*/ */
@Nullable @Nullable
public Handler getHandler() public Handler getHandler() {
{ return this.handler;
return handler;
} }
/** /**
* Set the refuel handler for this stack. * Set the refuel handler for this stack.
* *
* You should call this if you can actually refuel from this item, and ideally only if there are no existing * You should call this if you can actually refuel from this item, and ideally only if there are no existing handlers.
* handlers.
* *
* @param handler The new refuel handler. * @param handler The new refuel handler.
* @see #getHandler() * @see #getHandler()
*/ */
public void setHandler( @Nullable Handler handler ) public void setHandler(@Nullable Handler handler) {
{
this.handler = handler; this.handler = handler;
} }
@@ -73,18 +70,16 @@ public class TurtleRefuelEvent extends TurtleActionEvent
* Handles refuelling a turtle from a specific item. * Handles refuelling a turtle from a specific item.
*/ */
@FunctionalInterface @FunctionalInterface
public interface Handler public interface Handler {
{
/** /**
* Refuel a turtle using an item. * Refuel a turtle using an item.
* *
* @param turtle The turtle to refuel. * @param turtle The turtle to refuel.
* @param stack The stack to refuel with. * @param stack The stack to refuel with.
* @param slot The slot the stack resides within. This may be used to modify the inventory afterwards. * @param slot The slot the stack resides within. This may be used to modify the inventory afterwards.
* @param limit The maximum number of refuel operations to perform. This will often correspond to the number of * @param limit The maximum number of refuel operations to perform. This will often correspond to the number of items to consume.
* items to consume.
* @return The amount of fuel gained. * @return The amount of fuel gained.
*/ */
int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, int slot, int limit ); int refuel(@Nonnull ITurtleAccess turtle, @Nonnull ItemStack stack, int slot, int limit);
} }
} }

View File

@@ -87,7 +87,8 @@ public final class ClientRegistry {
return layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF; return layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF;
}, ComputerCraftRegistry.ModItems.DISK); }, ComputerCraftRegistry.ModItems.DISK);
ColorProviderRegistry.ITEM.register((stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF, ComputerCraftRegistry.ModItems.TREASURE_DISK); ColorProviderRegistry.ITEM.register((stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF,
ComputerCraftRegistry.ModItems.TREASURE_DISK);
ColorProviderRegistry.ITEM.register((stack, layer) -> { ColorProviderRegistry.ITEM.register((stack, layer) -> {
switch (layer) { switch (layer) {
@@ -116,6 +117,7 @@ public final class ClientRegistry {
spriteIdentifier -> MinecraftClient.getInstance() spriteIdentifier -> MinecraftClient.getInstance()
.getSpriteAtlas(spriteIdentifier.getAtlasId()) .getSpriteAtlas(spriteIdentifier.getAtlasId())
.apply(spriteIdentifier.getTextureId()), .apply(spriteIdentifier.getTextureId()),
ModelRotation.X0_Y0, identifier); ModelRotation.X0_Y0,
identifier);
} }
} }

View File

@@ -3,64 +3,62 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client; package dan200.computercraft.client;
import javax.annotation.Nullable;
import dan200.computercraft.shared.command.text.ChatHelpers; import dan200.computercraft.shared.command.text.ChatHelpers;
import dan200.computercraft.shared.command.text.TableBuilder; import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.command.text.TableFormatter; import dan200.computercraft.shared.command.text.TableFormatter;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.apache.commons.lang3.StringUtils;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.hud.ChatHud; import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable; public class ClientTableFormatter implements TableFormatter {
public class ClientTableFormatter implements TableFormatter
{
public static final ClientTableFormatter INSTANCE = new ClientTableFormatter(); public static final ClientTableFormatter INSTANCE = new ClientTableFormatter();
private static Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap(); private static Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap();
private static TextRenderer renderer() @Override
{ @Nullable
public Text getPadding(Text component, int width) {
int extraWidth = width - this.getWidth(component);
if (extraWidth <= 0) {
return null;
}
TextRenderer renderer = renderer();
float spaceWidth = renderer.getWidth(" ");
int spaces = MathHelper.floor(extraWidth / spaceWidth);
int extra = extraWidth - (int) (spaces * spaceWidth);
return ChatHelpers.coloured(StringUtils.repeat(' ', spaces) + StringUtils.repeat((char) 712, extra), Formatting.GRAY);
}
private static TextRenderer renderer() {
return MinecraftClient.getInstance().textRenderer; return MinecraftClient.getInstance().textRenderer;
} }
@Override @Override
@Nullable public int getColumnPadding() {
public Text getPadding( Text component, int width )
{
int extraWidth = width - getWidth( component );
if( extraWidth <= 0 ) return null;
TextRenderer renderer = renderer();
float spaceWidth = renderer.getWidth( " " );
int spaces = MathHelper.floor( extraWidth / spaceWidth );
int extra = extraWidth - (int) (spaces * spaceWidth);
return ChatHelpers.coloured( StringUtils.repeat( ' ', spaces ) + StringUtils.repeat( (char) 712, extra ), Formatting.GRAY );
}
@Override
public int getColumnPadding()
{
return 3; return 3;
} }
@Override @Override
public int getWidth( Text component ) public int getWidth(Text component) {
{ return renderer().getWidth(component);
return renderer().getWidth( component );
} }
@Override @Override
public void writeLine( int id, Text component ) public void writeLine(int id, Text component) {
{
MinecraftClient mc = MinecraftClient.getInstance(); MinecraftClient mc = MinecraftClient.getInstance();
ChatHud chat = mc.inGameHud.getChatHud(); ChatHud chat = mc.inGameHud.getChatHud();
@@ -68,20 +66,21 @@ public class ClientTableFormatter implements TableFormatter
// int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() ); // int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
// List<ITextProperties> list = RenderComponentsUtil.func_238505_a_( component, maxWidth, mc.fontRenderer ); // List<ITextProperties> list = RenderComponentsUtil.func_238505_a_( component, maxWidth, mc.fontRenderer );
// if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id ); // if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id );
chat.addMessage( component, id ); chat.addMessage(component, id);
} }
@Override @Override
public int display( TableBuilder table ) public int display(TableBuilder table) {
{
ChatHud chat = MinecraftClient.getInstance().inGameHud.getChatHud(); ChatHud chat = MinecraftClient.getInstance().inGameHud.getChatHud();
int lastHeight = lastHeights.get( table.getId() ); int lastHeight = lastHeights.get(table.getId());
int height = TableFormatter.super.display( table ); int height = TableFormatter.super.display(table);
lastHeights.put( table.getId(), height ); lastHeights.put(table.getId(), height);
for( int i = height; i < lastHeight; i++ ) chat.removeMessage( i + table.getId() ); for (int i = height; i < lastHeight; i++) {
chat.removeMessage(i + table.getId());
}
return height; return height;
} }
} }

View File

@@ -8,23 +8,23 @@ package dan200.computercraft.client;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
public final class FrameInfo public final class FrameInfo {
{
private static int tick; private static int tick;
private static long renderFrame; private static long renderFrame;
static { static {
} }
private FrameInfo() {
}
public static void init() { public static void init() {
ClientTickEvents.START_CLIENT_TICK.register(m -> { ClientTickEvents.START_CLIENT_TICK.register(m -> {
tick++; tick++;
}); });
} }
private FrameInfo() {
}
public static boolean getGlobalCursorBlink() { public static boolean getGlobalCursorBlink() {
return (tick / 8) % 2 == 0; return (tick / 8) % 2 == 0;
} }

View File

@@ -3,8 +3,12 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@@ -13,8 +17,6 @@ import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPhase; import net.minecraft.client.render.RenderPhase;
@@ -26,39 +28,138 @@ import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
public final class FixedWidthFontRenderer public final class FixedWidthFontRenderer {
{
private static final Matrix4f IDENTITY = AffineTransformation.identity().getMatrix();
private static final Identifier FONT = new Identifier( "computercraft", "textures/gui/term_font.png" );
public static final int FONT_HEIGHT = 9; public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6; public static final int FONT_WIDTH = 6;
public static final float WIDTH = 256.0f; public static final float WIDTH = 256.0f;
public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
public static final RenderLayer TYPE = Type.MAIN; public static final RenderLayer TYPE = Type.MAIN;
private static final Matrix4f IDENTITY = AffineTransformation.identity()
.getMatrix();
private static final Identifier FONT = new Identifier("computercraft", "textures/gui/term_font.png");
private FixedWidthFontRenderer() private FixedWidthFontRenderer() {
{
} }
public static float toGreyscale( double[] rgb ) public static void drawString(float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
{ @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize) {
bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
.getBufferBuilders()
.getEntityVertexConsumers();
drawString(IDENTITY,
((VertexConsumerProvider) renderer).getBuffer(TYPE),
x,
y,
text,
textColour,
backgroundColour,
palette,
greyscale,
leftMarginSize,
rightMarginSize);
renderer.draw();
}
private static void bindFont() {
MinecraftClient.getInstance()
.getTextureManager()
.bindTexture(FONT);
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
}
public static void drawString(@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, @Nonnull TextBuffer text,
@Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize) {
if (backgroundColour != null) {
drawBackground(transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT);
}
for (int i = 0; i < text.length(); i++) {
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);
}
}
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) {
drawQuad(transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt(0));
}
if (rightMarginSize > 0) {
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.
int blockStart = 0;
char blockColour = '\0';
for (int i = 0; i < backgroundColour.length(); i++) {
char colourIndex = backgroundColour.charAt(i);
if (colourIndex == blockColour) {
continue;
}
if (blockColour != '\0') {
drawQuad(transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour);
}
blockColour = colourIndex;
blockStart = i;
}
if (blockColour != '\0') {
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) {
return 15 - Terminal.getColour(c, def);
}
public static float toGreyscale(double[] rgb) {
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
} }
public static int getColour( char c, Colour def ) private static void drawChar(Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b) {
{
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 )
{
// Short circuit to avoid the common case - the texture should be blank here after all. // Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return; if (index == '\0' || index == ' ') {
return;
}
int column = index % 16; int column = index % 16;
int row = index / 16; int row = index / 16;
@@ -66,284 +167,217 @@ public final class FixedWidthFontRenderer
int xStart = 1 + column * (FONT_WIDTH + 2); int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2); int yStart = 1 + row * (FONT_HEIGHT + 2);
buffer.vertex( transform, x, y, 0f ).color( r, g, b, 1.0f ).texture( xStart / WIDTH, yStart / WIDTH ).next(); buffer.vertex(transform, x, y, 0f)
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).texture( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).next(); .color(r, g, b, 1.0f)
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).texture( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).next(); .texture(xStart / WIDTH, yStart / WIDTH)
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).texture( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).next(); .next();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).texture( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).next(); buffer.vertex(transform, x, y + FONT_HEIGHT, 0f)
buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).texture( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).next(); .color(r, g, b, 1.0f)
.texture(xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH)
.next();
buffer.vertex(transform, x, y + FONT_HEIGHT, 0f)
.color(r, g, b, 1.0f)
.texture(xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
buffer.vertex(transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f)
.color(r, g, b, 1.0f)
.texture((xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH)
.next();
} }
private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b ) private static void drawQuad(Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette,
{ boolean greyscale, char colourIndex) {
buffer.vertex( transform, x, y, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_START, BACKGROUND_START ).next(); double[] colour = palette.getColour(getColour(colourIndex, Colour.BLACK));
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_START, BACKGROUND_END ).next();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_END, BACKGROUND_START ).next();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_END, BACKGROUND_START ).next();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_START, BACKGROUND_END ).next();
buffer.vertex( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).texture( BACKGROUND_END, BACKGROUND_END ).next();
}
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; float r, g, b;
if( greyscale ) if (greyscale) {
{ r = g = b = toGreyscale(colour);
r = g = b = toGreyscale( colour ); } else {
}
else
{
r = (float) colour[0]; r = (float) colour[0];
g = (float) colour[1]; g = (float) colour[1];
b = (float) colour[2]; b = (float) colour[2];
} }
drawQuad( transform, buffer, x, y, width, height, r, g, b ); drawQuad(transform, buffer, x, y, width, height, r, g, b);
} }
private static void drawBackground( private static void drawQuad(Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b) {
@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, buffer.vertex(transform, x, y, 0)
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, .color(r, g, b, 1.0f)
float leftMarginSize, float rightMarginSize, float height .texture(BACKGROUND_START, BACKGROUND_START)
) .next();
{ buffer.vertex(transform, x, y + height, 0)
if( leftMarginSize > 0 ) .color(r, g, b, 1.0f)
{ .texture(BACKGROUND_START, BACKGROUND_END)
drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) ); .next();
buffer.vertex(transform, x + width, y, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_START)
.next();
buffer.vertex(transform, x + width, y, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_START)
.next();
buffer.vertex(transform, x, y + height, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_START, BACKGROUND_END)
.next();
buffer.vertex(transform, x + width, y + height, 0)
.color(r, g, b, 1.0f)
.texture(BACKGROUND_END, BACKGROUND_END)
.next();
} }
if( rightMarginSize > 0 ) public static void drawTerminalWithoutCursor(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y,
{ @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize,
drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); float leftMarginSize, float rightMarginSize) {
}
// Batch together runs of identical background cells.
int blockStart = 0;
char blockColour = '\0';
for( int i = 0; i < backgroundColour.length(); i++ )
{
char colourIndex = backgroundColour.charAt( i );
if( colourIndex == blockColour ) continue;
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
}
}
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
)
{
if( backgroundColour != null )
{
drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
}
for( int i = 0; i < text.length(); i++ )
{
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 );
}
}
public static void drawString(
float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
)
{
bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
drawString( IDENTITY, ((VertexConsumerProvider) renderer).getBuffer( TYPE ), x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
renderer.draw();
}
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(); Palette palette = terminal.getPalette();
int height = terminal.getHeight(); int height = terminal.getHeight();
// Top and bottom margins // Top and bottom margins
drawBackground( drawBackground(transform,
transform, buffer, x, y - topMarginSize, buffer,
terminal.getBackgroundColourLine( 0 ), palette, greyscale, x,
leftMarginSize, rightMarginSize, topMarginSize y - topMarginSize,
); terminal.getBackgroundColourLine(0),
palette,
greyscale,
leftMarginSize,
rightMarginSize,
topMarginSize);
drawBackground( drawBackground(transform,
transform, buffer, x, y + height * FONT_HEIGHT, buffer,
terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, x,
leftMarginSize, rightMarginSize, bottomMarginSize y + height * FONT_HEIGHT,
); terminal.getBackgroundColourLine(height - 1),
palette,
greyscale,
leftMarginSize,
rightMarginSize,
bottomMarginSize);
// The main text // The main text
for( int i = 0; i < height; i++ ) for (int i = 0; i < height; i++) {
{ drawString(transform,
drawString( buffer,
transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i, x,
terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ), y + FixedWidthFontRenderer.FONT_HEIGHT * i,
palette, greyscale, leftMarginSize, rightMarginSize terminal.getLine(i),
); terminal.getTextColourLine(i),
terminal.getBackgroundColourLine(i),
palette,
greyscale,
leftMarginSize,
rightMarginSize);
} }
} }
public static void drawCursor( public static void drawCursor(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal,
@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, boolean greyscale) {
@Nonnull Terminal terminal, boolean greyscale
)
{
Palette palette = terminal.getPalette(); Palette palette = terminal.getPalette();
int width = terminal.getWidth(); int width = terminal.getWidth();
int height = terminal.getHeight(); int height = terminal.getHeight();
int cursorX = terminal.getCursorX(); int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY(); int cursorY = terminal.getCursorY();
if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink() ) if (terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink()) {
{ double[] colour = palette.getColour(15 - terminal.getTextColour());
double[] colour = palette.getColour( 15 - terminal.getTextColour() );
float r, g, b; float r, g, b;
if( greyscale ) if (greyscale) {
{ r = g = b = toGreyscale(colour);
r = g = b = toGreyscale( colour ); } else {
}
else
{
r = (float) colour[0]; r = (float) colour[0];
g = (float) colour[1]; g = (float) colour[1];
b = (float) colour[2]; b = (float) colour[2];
} }
drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b ); drawChar(transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b);
} }
} }
public static void drawTerminal( public static void drawTerminal(@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal,
@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize) {
@Nonnull Terminal terminal, boolean greyscale, drawTerminalWithoutCursor(transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize drawCursor(transform, buffer, x, y, terminal, greyscale);
)
{
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale );
} }
public static void drawTerminal( public static void drawTerminal(@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize,
@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float bottomMarginSize, float leftMarginSize, float rightMarginSize) {
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
bindFont(); bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
VertexConsumer buffer = renderer.getBuffer( TYPE ); .getBufferBuilders()
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); .getEntityVertexConsumers();
renderer.draw( TYPE ); VertexConsumer buffer = renderer.getBuffer(TYPE);
drawTerminal(transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
renderer.draw(TYPE);
} }
public static void drawTerminal( public static void drawTerminal(float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize,
float x, float y, @Nonnull Terminal terminal, boolean greyscale, float leftMarginSize, float rightMarginSize) {
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize drawTerminal(IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize);
)
{
drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
} }
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width, float height ) public static void drawEmptyTerminal(float x, float y, float width, float height) {
{ drawEmptyTerminal(IDENTITY, x, y, width, height);
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( TYPE ), 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 ) public static void drawEmptyTerminal(@Nonnull Matrix4f transform, float x, float y, float width, float height) {
{
bindFont(); bindFont();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
drawEmptyTerminal( transform, renderer, x, y, width, height ); .getBufferBuilders()
.getEntityVertexConsumers();
drawEmptyTerminal(transform, renderer, x, y, width, height);
renderer.draw(); renderer.draw();
} }
public static void drawEmptyTerminal( float x, float y, float width, float height ) public static void drawEmptyTerminal(@Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width,
{ float height) {
drawEmptyTerminal( IDENTITY, x, y, width, height );
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK; Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); drawQuad(transform, renderer.getBuffer(TYPE), x, y, width, height, colour.getR(), colour.getG(), colour.getB());
} }
private static void bindFont() public static void drawBlocker(@Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width, float height) {
{ Colour colour = Colour.BLACK;
MinecraftClient.getInstance().getTextureManager().bindTexture( FONT ); drawQuad(transform, renderer.getBuffer(Type.BLOCKER), x, y, width, height, colour.getR(), colour.getG(), colour.getB());
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
} }
private static final class Type extends RenderPhase private static final class Type extends RenderPhase {
{
private static final int GL_MODE = GL11.GL_TRIANGLES; private static final int GL_MODE = GL11.GL_TRIANGLES;
private static final VertexFormat FORMAT = VertexFormats.POSITION_COLOR_TEXTURE; private static final VertexFormat FORMAT = VertexFormats.POSITION_COLOR_TEXTURE;
static final RenderLayer MAIN = RenderLayer.of( static final RenderLayer MAIN = RenderLayer.of("terminal_font", FORMAT, GL_MODE, 1024, false, false, // useDelegate, needsSorting
"terminal_font", FORMAT, GL_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder() RenderLayer.MultiPhaseParameters.builder()
.texture( new RenderPhase.Texture( FONT, false, false ) ) // blur, minimap .texture(new RenderPhase.Texture(FONT,
.alpha( ONE_TENTH_ALPHA ) false,
.lightmap( DISABLE_LIGHTMAP ) false)) // blur, minimap
.writeMaskState( COLOR_MASK ) .alpha(ONE_TENTH_ALPHA)
.build( false ) .lightmap(DISABLE_LIGHTMAP)
); .writeMaskState(COLOR_MASK)
.build(false));
static final RenderLayer BLOCKER = RenderLayer.of( static final RenderLayer BLOCKER = RenderLayer.of("terminal_blocker", FORMAT, GL_MODE, 256, false, false, // useDelegate, needsSorting
"terminal_blocker", FORMAT, GL_MODE, 256,
false, false, // useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder() RenderLayer.MultiPhaseParameters.builder()
.texture( new RenderPhase.Texture( FONT, false, false ) ) // blur, minimap .texture(new RenderPhase.Texture(FONT,
.alpha( ONE_TENTH_ALPHA ) false,
.writeMaskState( DEPTH_MASK ) false)) // blur, minimap
.lightmap( DISABLE_LIGHTMAP ) .alpha(ONE_TENTH_ALPHA)
.build( false ) .writeMaskState(DEPTH_MASK)
); .lightmap(DISABLE_LIGHTMAP)
.build(false));
private Type( String name, Runnable setup, Runnable destroy ) private Type(String name, Runnable setup, Runnable destroy) {
{ super(name, setup, destroy);
super( name, setup, destroy );
} }
} }
} }

View File

@@ -3,8 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.gui.widgets.WidgetTerminal;
@@ -16,19 +22,14 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull; public final class GuiComputer<T extends ContainerComputerBase> extends HandledScreen<T> {
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
public final class GuiComputer<T extends ContainerComputerBase> extends HandledScreen<T>
{
private final ComputerFamily family; private final ComputerFamily family;
private final ClientComputer computer; private final ClientComputer computer;
private final int termWidth; private final int termWidth;
@@ -37,123 +38,97 @@ public final class GuiComputer<T extends ContainerComputerBase> extends HandledS
private WidgetTerminal terminal; private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper; private WidgetWrapper terminalWrapper;
private GuiComputer( private GuiComputer(T container, PlayerInventory player, Text title, int termWidth, int termHeight) {
T container, PlayerInventory player, Text title, int termWidth, int termHeight super(container, player, title);
) this.family = container.getFamily();
{ this.computer = (ClientComputer) container.getComputer();
super( container, player, title );
family = container.getFamily();
computer = (ClientComputer) container.getComputer();
this.termWidth = termWidth; this.termWidth = termWidth;
this.termHeight = termHeight; this.termHeight = termHeight;
terminal = null; this.terminal = null;
} }
public static GuiComputer<ContainerComputer> create( ContainerComputer container, PlayerInventory inventory, Text component ) public static GuiComputer<ContainerComputer> create(ContainerComputer container, PlayerInventory inventory, Text component) {
{ return new GuiComputer<>(container, inventory, component, ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer);
return new GuiComputer<>(
container, inventory, component,
ComputerCraft.terminalWidth_computer, ComputerCraft.terminalHeight_computer
);
} }
public static GuiComputer<ContainerPocketComputer> createPocket( ContainerPocketComputer container, PlayerInventory inventory, Text component ) public static GuiComputer<ContainerPocketComputer> createPocket(ContainerPocketComputer container, PlayerInventory inventory, Text component) {
{ return new GuiComputer<>(container, inventory, component, ComputerCraft.terminalWidth_pocketComputer, ComputerCraft.terminalHeight_pocketComputer);
return new GuiComputer<>(
container, inventory, component,
ComputerCraft.terminalWidth_pocketComputer, ComputerCraft.terminalHeight_pocketComputer
);
} }
public static GuiComputer<ContainerViewComputer> createView( ContainerViewComputer container, PlayerInventory inventory, Text component ) public static GuiComputer<ContainerViewComputer> createView(ContainerViewComputer container, PlayerInventory inventory, Text component) {
{ return new GuiComputer<>(container, inventory, component, container.getWidth(), container.getHeight());
return new GuiComputer<>(
container, inventory, component,
container.getWidth(), container.getHeight()
);
} }
@Override @Override
protected void init() protected void init() {
{ this.client.keyboard.setRepeatEvents(true);
client.keyboard.setRepeatEvents( true );
int termPxWidth = termWidth * FixedWidthFontRenderer.FONT_WIDTH; int termPxWidth = this.termWidth * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = termHeight * FixedWidthFontRenderer.FONT_HEIGHT; int termPxHeight = this.termHeight * FixedWidthFontRenderer.FONT_HEIGHT;
backgroundWidth = termPxWidth + MARGIN * 2 + BORDER * 2; this.backgroundWidth = termPxWidth + MARGIN * 2 + BORDER * 2;
backgroundHeight = termPxHeight + MARGIN * 2 + BORDER * 2; this.backgroundHeight = termPxHeight + MARGIN * 2 + BORDER * 2;
super.init(); super.init();
terminal = new WidgetTerminal( client, () -> computer, termWidth, termHeight, MARGIN, MARGIN, MARGIN, MARGIN ); this.terminal = new WidgetTerminal(this.client, () -> this.computer, this.termWidth, this.termHeight, MARGIN, MARGIN, MARGIN, MARGIN);
terminalWrapper = new WidgetWrapper( terminal, MARGIN + BORDER + x, MARGIN + BORDER + y, termPxWidth, termPxHeight ); this.terminalWrapper = new WidgetWrapper(this.terminal, MARGIN + BORDER + this.x, MARGIN + BORDER + this.y, termPxWidth, termPxHeight);
children.add( terminalWrapper ); this.children.add(this.terminalWrapper);
setFocused( terminalWrapper ); this.setFocused(this.terminalWrapper);
} }
@Override @Override
public void removed() public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
{ super.render(stack, mouseX, mouseY, partialTicks);
super.removed(); this.drawMouseoverTooltip(stack, mouseX, mouseY);
children.remove( terminal );
terminal = null;
client.keyboard.setRepeatEvents( false );
} }
@Override @Override
public void tick() protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
{
super.tick();
terminal.update();
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
@Override
public void drawBackground( @Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY )
{
// Draw terminal
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw a border around the terminal
RenderSystem.color4f( 1, 1, 1, 1 );
client.getTextureManager().bindTexture( ComputerBorderRenderer.getTexture( family ) );
ComputerBorderRenderer.render(
terminalWrapper.getX() - MARGIN, terminalWrapper.getY() - MARGIN, getZOffset(),
terminalWrapper.getWidth() + MARGIN * 2, terminalWrapper.getHeight() + MARGIN * 2
);
}
@Override
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
super.render( stack, mouseX, mouseY, partialTicks );
drawMouseoverTooltip( stack, mouseX, mouseY );
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
}
@Override
protected void drawForeground( @Nonnull MatrixStack transform, int mouseX, int mouseY )
{
// Skip rendering labels. // Skip rendering labels.
} }
@Override
public void drawBackground(@Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY) {
// Draw terminal
this.terminal.draw(this.terminalWrapper.getX(), this.terminalWrapper.getY());
// Draw a border around the terminal
RenderSystem.color4f(1, 1, 1, 1);
this.client.getTextureManager()
.bindTexture(ComputerBorderRenderer.getTexture(this.family));
ComputerBorderRenderer.render(this.terminalWrapper.getX() - MARGIN, this.terminalWrapper.getY() - MARGIN,
this.getZOffset(), this.terminalWrapper.getWidth() + MARGIN * 2, this.terminalWrapper.getHeight() + MARGIN * 2);
}
@Override
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
return (this.getFocused() != null && this.getFocused().mouseDragged(x, y, button, deltaX, deltaY)) || super.mouseDragged(x, y, button, deltaX, deltaY);
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
// Forward the tab key to the terminal, rather than moving between controls.
if (key == GLFW.GLFW_KEY_TAB && this.getFocused() != null && this.getFocused() == this.terminalWrapper) {
return this.getFocused().keyPressed(key, scancode, modifiers);
}
return super.keyPressed(key, scancode, modifiers);
}
@Override
public void removed() {
super.removed();
this.children.remove(this.terminal);
this.terminal = null;
this.client.keyboard.setRepeatEvents(false);
}
@Override
public void tick() {
super.tick();
this.terminal.update();
}
} }

View File

@@ -3,39 +3,39 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import javax.annotation.Nonnull;
public class GuiDiskDrive extends HandledScreen<ContainerDiskDrive> public class GuiDiskDrive extends HandledScreen<ContainerDiskDrive> {
{ private static final Identifier BACKGROUND = new Identifier("computercraft", "textures/gui/disk_drive.png");
private static final Identifier BACKGROUND = new Identifier( "computercraft", "textures/gui/disk_drive.png" );
public GuiDiskDrive( ContainerDiskDrive container, PlayerInventory player, Text title ) public GuiDiskDrive(ContainerDiskDrive container, PlayerInventory player, Text title) {
{ super(container, player, title);
super( container, player, title );
} }
@Override @Override
protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) public void render(@Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks) {
{ this.renderBackground(transform);
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); super.render(transform, mouseX, mouseY, partialTicks);
client.getTextureManager().bindTexture( BACKGROUND ); this.drawMouseoverTooltip(transform, mouseX, mouseY);
drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight );
} }
@Override @Override
public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks ) protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
{ RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
renderBackground( transform ); this.client.getTextureManager()
super.render( transform, mouseX, mouseY, partialTicks ); .bindTexture(BACKGROUND);
drawMouseoverTooltip( transform, mouseX, mouseY ); this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
} }
} }

View File

@@ -3,24 +3,25 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import javax.annotation.Nonnull;
public class GuiPrinter extends HandledScreen<ContainerPrinter> public class GuiPrinter extends HandledScreen<ContainerPrinter> {
{ private static final Identifier BACKGROUND = new Identifier("computercraft", "textures/gui/printer.png");
private static final Identifier BACKGROUND = new Identifier( "computercraft", "textures/gui/printer.png" );
public GuiPrinter( ContainerPrinter container, PlayerInventory player, Text title ) public GuiPrinter(ContainerPrinter container, PlayerInventory player, Text title) {
{ super(container, player, title);
super( container, player, title );
} }
/*@Override /*@Override
@@ -32,20 +33,21 @@ public class GuiPrinter extends HandledScreen<ContainerPrinter>
}*/ }*/
@Override @Override
protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
{ this.renderBackground(stack);
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); super.render(stack, mouseX, mouseY, partialTicks);
client.getTextureManager().bindTexture( BACKGROUND ); this.drawMouseoverTooltip(stack, mouseX, mouseY);
drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight );
if( getScreenHandler().isPrinting() ) drawTexture( transform, x + 34, y + 21, 176, 0, 25, 45 );
} }
@Override @Override
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
{ RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
renderBackground( stack ); this.client.getTextureManager()
super.render( stack, mouseX, mouseY, partialTicks ); .bindTexture(BACKGROUND);
drawMouseoverTooltip( stack, mouseX, mouseY ); this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
if (this.getScreenHandler().isPrinting()) {
this.drawTexture(transform, this.x + 34, this.y + 21, 176, 0, 25, 45);
}
} }
} }

View File

@@ -3,12 +3,23 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import static dan200.computercraft.client.render.PrintoutRenderer.X_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_SIZE;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.drawBorder;
import static dan200.computercraft.client.render.PrintoutRenderer.drawText;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.media.items.ItemPrintout;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
@@ -16,74 +27,55 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull; public class GuiPrintout extends HandledScreen<ContainerHeldItem> {
import static dan200.computercraft.client.render.PrintoutRenderer.*;
public class GuiPrintout extends HandledScreen<ContainerHeldItem>
{
private final boolean m_book; private final boolean m_book;
private final int m_pages; private final int m_pages;
private final TextBuffer[] m_text; private final TextBuffer[] m_text;
private final TextBuffer[] m_colours; private final TextBuffer[] m_colours;
private int m_page; private int m_page;
public GuiPrintout( ContainerHeldItem container, PlayerInventory player, Text title ) public GuiPrintout(ContainerHeldItem container, PlayerInventory player, Text title) {
{ super(container, player, title);
super( container, player, title );
backgroundHeight = Y_SIZE; this.backgroundHeight = Y_SIZE;
String[] text = ItemPrintout.getText( container.getStack() ); String[] text = ItemPrintout.getText(container.getStack());
m_text = new TextBuffer[text.length]; this.m_text = new TextBuffer[text.length];
for( int i = 0; i < m_text.length; i++ ) m_text[i] = new TextBuffer( text[i] ); for (int i = 0; i < this.m_text.length; i++) {
this.m_text[i] = new TextBuffer(text[i]);
}
String[] colours = ItemPrintout.getColours( container.getStack() ); String[] colours = ItemPrintout.getColours(container.getStack());
m_colours = new TextBuffer[colours.length]; this.m_colours = new TextBuffer[colours.length];
for( int i = 0; i < m_colours.length; i++ ) m_colours[i] = new TextBuffer( colours[i] ); for (int i = 0; i < this.m_colours.length; i++) {
this.m_colours[i] = new TextBuffer(colours[i]);
}
m_page = 0; this.m_page = 0;
m_pages = Math.max( m_text.length / ItemPrintout.LINES_PER_PAGE, 1 ); this.m_pages = Math.max(this.m_text.length / ItemPrintout.LINES_PER_PAGE, 1);
m_book = ((ItemPrintout) container.getStack().getItem()).getType() == ItemPrintout.Type.BOOK; this.m_book = ((ItemPrintout) container.getStack()
.getItem()).getType() == ItemPrintout.Type.BOOK;
} }
@Override @Override
public boolean keyPressed( int key, int scancode, int modifiers ) public boolean mouseScrolled(double x, double y, double delta) {
{ if (super.mouseScrolled(x, y, delta)) {
if( super.keyPressed( key, scancode, modifiers ) ) return true;
if( key == GLFW.GLFW_KEY_RIGHT )
{
if( m_page < m_pages - 1 ) m_page++;
return true; return true;
} }
if (delta < 0) {
if( key == GLFW.GLFW_KEY_LEFT )
{
if( m_page > 0 ) m_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 // Scroll up goes to the next page
if( m_page < m_pages - 1 ) m_page++; if (this.m_page < this.m_pages - 1) {
this.m_page++;
}
return true; return true;
} }
if( delta > 0 ) if (delta > 0) {
{
// Scroll down goes to the previous page // Scroll down goes to the previous page
if( m_page > 0 ) m_page--; if (this.m_page > 0) {
this.m_page--;
}
return true; return true;
} }
@@ -91,33 +83,56 @@ public class GuiPrintout extends HandledScreen<ContainerHeldItem>
} }
@Override @Override
protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
{ // We must take the background further back in order to not overlap with our printed pages.
this.setZOffset(this.getZOffset() - 1);
this.renderBackground(stack);
this.setZOffset(this.getZOffset() + 1);
super.render(stack, mouseX, mouseY, partialTicks);
}
@Override
protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
// Skip rendering labels.
}
@Override
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
// Draw the printout // Draw the printout
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f ); RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();
VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance()
Matrix4f matrix = transform.peek().getModel(); .getBufferBuilders()
drawBorder( matrix, renderer, x, y, getZOffset(), m_page, m_pages, m_book ); .getEntityVertexConsumers();
drawText( matrix, renderer, x + X_TEXT_MARGIN, y + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours ); Matrix4f matrix = transform.peek()
.getModel();
drawBorder(matrix, renderer, this.x, this.y, this.getZOffset(), this.m_page, this.m_pages, this.m_book);
drawText(matrix, renderer, this.x + X_TEXT_MARGIN, this.y + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * this.m_page, this.m_text, this.m_colours);
renderer.draw(); renderer.draw();
} }
@Override @Override
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) public boolean keyPressed(int key, int scancode, int modifiers) {
{ if (super.keyPressed(key, scancode, modifiers)) {
// We must take the background further back in order to not overlap with our printed pages. return true;
setZOffset( getZOffset() - 1 );
renderBackground( stack );
setZOffset( getZOffset() + 1 );
super.render( stack, mouseX, mouseY, partialTicks );
} }
@Override if (key == GLFW.GLFW_KEY_RIGHT) {
protected void drawForeground( @Nonnull MatrixStack transform, int mouseX, int mouseY ) if (this.m_page < this.m_pages - 1) {
{ this.m_page++;
// Skip rendering labels. }
return true;
}
if (key == GLFW.GLFW_KEY_LEFT) {
if (this.m_page > 0) {
this.m_page--;
}
return true;
}
return false;
} }
} }

View File

@@ -3,8 +3,11 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.gui.widgets.WidgetTerminal;
@@ -12,133 +15,112 @@ import dan200.computercraft.client.gui.widgets.WidgetWrapper;
import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
public class GuiTurtle extends HandledScreen<ContainerTurtle>
{
private static final Identifier BACKGROUND_NORMAL = new Identifier( "computercraft", "textures/gui/turtle_normal.png" );
private static final Identifier BACKGROUND_ADVANCED = new Identifier( "computercraft", "textures/gui/turtle_advanced.png" );
private ContainerTurtle m_container;
public class GuiTurtle extends HandledScreen<ContainerTurtle> {
private static final Identifier BACKGROUND_NORMAL = new Identifier("computercraft", "textures/gui/turtle_normal.png");
private static final Identifier BACKGROUND_ADVANCED = new Identifier("computercraft", "textures/gui/turtle_advanced.png");
private final ComputerFamily m_family; private final ComputerFamily m_family;
private final ClientComputer m_computer; private final ClientComputer m_computer;
private ContainerTurtle m_container;
private WidgetTerminal terminal; private WidgetTerminal terminal;
private WidgetWrapper terminalWrapper; private WidgetWrapper terminalWrapper;
public GuiTurtle( ContainerTurtle container, PlayerInventory player, Text title ) public GuiTurtle(ContainerTurtle container, PlayerInventory player, Text title) {
{ super(container, player, title);
super( container, player, title );
m_container = container; this.m_container = container;
m_family = container.getFamily(); this.m_family = container.getFamily();
m_computer = (ClientComputer) container.getComputer(); this.m_computer = (ClientComputer) container.getComputer();
backgroundWidth = 254; this.backgroundWidth = 254;
backgroundHeight = 217; this.backgroundHeight = 217;
} }
@Override @Override
protected void init() protected void init() {
{
super.init(); super.init();
client.keyboard.setRepeatEvents( true ); this.client.keyboard.setRepeatEvents(true);
int termPxWidth = ComputerCraft.terminalWidth_turtle * FixedWidthFontRenderer.FONT_WIDTH; int termPxWidth = ComputerCraft.terminalWidth_turtle * FixedWidthFontRenderer.FONT_WIDTH;
int termPxHeight = ComputerCraft.terminalHeight_turtle * FixedWidthFontRenderer.FONT_HEIGHT; int termPxHeight = ComputerCraft.terminalHeight_turtle * FixedWidthFontRenderer.FONT_HEIGHT;
terminal = new WidgetTerminal( this.terminal = new WidgetTerminal(this.client, () -> this.m_computer, ComputerCraft.terminalWidth_turtle, ComputerCraft.terminalHeight_turtle, 2, 2, 2, 2);
client, () -> m_computer, this.terminalWrapper = new WidgetWrapper(this.terminal, 2 + 8 + this.x, 2 + 8 + this.y, termPxWidth, termPxHeight);
ComputerCraft.terminalWidth_turtle,
ComputerCraft.terminalHeight_turtle,
2, 2, 2, 2
);
terminalWrapper = new WidgetWrapper( terminal, 2 + 8 + x, 2 + 8 + y, termPxWidth, termPxHeight );
children.add( terminalWrapper ); this.children.add(this.terminalWrapper);
setFocused( terminalWrapper ); this.setFocused(this.terminalWrapper);
} }
@Override @Override
public void removed() public void render(@Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks) {
{ this.renderBackground(stack);
super.removed(); super.render(stack, mouseX, mouseY, partialTicks);
children.remove( terminal ); this.drawMouseoverTooltip(stack, mouseX, mouseY);
terminal = null;
client.keyboard.setRepeatEvents( false );
} }
@Override @Override
public void tick() protected void drawForeground(@Nonnull MatrixStack transform, int mouseX, int mouseY) {
{
super.tick();
terminal.update();
}
@Override
public boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
@Override
protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY )
{
// Draw term
Identifier texture = m_family == ComputerFamily.ADVANCED ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() );
// Draw border/inventory
RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
client.getTextureManager().bindTexture( texture );
drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight );
// Draw selection slot
int slot = m_container.getSelectedSlot();
if( slot >= 0 )
{
int slotX = slot % 4;
int slotY = slot / 4;
drawTexture( transform,
x + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18,
y + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18,
0, 217, 24, 24
);
}
}
@Override
public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks )
{
renderBackground( stack );
super.render( stack, mouseX, mouseY, partialTicks );
drawMouseoverTooltip( stack, mouseX, mouseY );
}
@Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{
return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ))
|| super.mouseDragged( x, y, button, deltaX, deltaY );
}
@Override
protected void drawForeground( @Nonnull MatrixStack transform, int mouseX, int mouseY )
{
// Skip rendering labels. // Skip rendering labels.
} }
@Override
protected void drawBackground(@Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY) {
// Draw term
Identifier texture = this.m_family == ComputerFamily.ADVANCED ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
this.terminal.draw(this.terminalWrapper.getX(), this.terminalWrapper.getY());
// Draw border/inventory
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
this.client.getTextureManager()
.bindTexture(texture);
this.drawTexture(transform, this.x, this.y, 0, 0, this.backgroundWidth, this.backgroundHeight);
// Draw selection slot
int slot = this.m_container.getSelectedSlot();
if (slot >= 0) {
int slotX = slot % 4;
int slotY = slot / 4;
this.drawTexture(transform, this.x + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, this.y + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18,
0,
217,
24,
24);
}
}
@Override
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
return (this.getFocused() != null && this.getFocused().mouseDragged(x, y, button, deltaX, deltaY)) || super.mouseDragged(x, y, button, deltaX, deltaY);
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
// Forward the tab key to the terminal, rather than moving between controls.
if (key == GLFW.GLFW_KEY_TAB && this.getFocused() != null && this.getFocused() == this.terminalWrapper) {
return this.getFocused().keyPressed(key, scancode, modifiers);
}
return super.keyPressed(key, scancode, modifiers);
}
@Override
public void removed() {
super.removed();
this.children.remove(this.terminal);
this.terminal = null;
this.client.keyboard.setRepeatEvents(false);
}
@Override
public void tick() {
super.tick();
this.terminal.update();
}
} }

View File

@@ -3,52 +3,47 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui.widgets; package dan200.computercraft.client.gui.widgets;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import java.util.BitSet;
import java.util.function.Supplier;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.IComputer; import dan200.computercraft.shared.computer.core.IComputer;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.util.BitSet;
import java.util.function.Supplier;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Element;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; public class WidgetTerminal implements Element {
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
public class WidgetTerminal implements Element
{
private static final float TERMINATE_TIME = 0.5f; private static final float TERMINATE_TIME = 0.5f;
private final MinecraftClient client; private final MinecraftClient client;
private boolean focused;
private final Supplier<ClientComputer> computer; private final Supplier<ClientComputer> computer;
private final int termWidth; private final int termWidth;
private final int termHeight; private final int termHeight;
private float terminateTimer = -1;
private float rebootTimer = -1;
private float shutdownTimer = -1;
private int lastMouseButton = -1;
private int lastMouseX = -1;
private int lastMouseY = -1;
private final int leftMargin; private final int leftMargin;
private final int rightMargin; private final int rightMargin;
private final int topMargin; private final int topMargin;
private final int bottomMargin; private final int bottomMargin;
private final BitSet keysDown = new BitSet(256);
private boolean focused;
private float terminateTimer = -1;
private float rebootTimer = -1;
private float shutdownTimer = -1;
private int lastMouseButton = -1;
private int lastMouseX = -1;
private int lastMouseY = -1;
private final BitSet keysDown = new BitSet( 256 ); public WidgetTerminal(MinecraftClient client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin,
int topMargin, int bottomMargin) {
public WidgetTerminal( MinecraftClient client, Supplier<ClientComputer> computer, int termWidth, int termHeight, int leftMargin, int rightMargin, int topMargin, int bottomMargin )
{
this.client = client; this.client = client;
this.computer = computer; this.computer = computer;
this.termWidth = termWidth; this.termWidth = termWidth;
@@ -60,63 +55,148 @@ public class WidgetTerminal implements Element
} }
@Override @Override
public boolean charTyped( char ch, int modifiers ) public boolean mouseClicked(double mouseX, double mouseY, int button) {
{ ClientComputer computer = this.computer.get();
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range if (computer == null || !computer.isColour() || button < 0 || button > 2) {
{ return false;
// Queue the "char" event }
queueEvent( "char", Character.toString( ch ) );
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
computer.mouseClick(button + 1, charX + 1, charY + 1);
this.lastMouseButton = button;
this.lastMouseX = charX;
this.lastMouseY = charY;
} }
return true; return true;
} }
@Override @Override
public boolean keyPressed( int key, int scancode, int modifiers ) public boolean mouseReleased(double mouseX, double mouseY, int button) {
{ ClientComputer computer = this.computer.get();
if( key == GLFW.GLFW_KEY_ESCAPE ) return false; if (computer == null || !computer.isColour() || button < 0 || button > 2) {
if( (modifiers & GLFW.GLFW_MOD_CONTROL) != 0 ) return false;
{ }
switch( key )
{ Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
if (this.lastMouseButton == button) {
computer.mouseUp(this.lastMouseButton + 1, charX + 1, charY + 1);
this.lastMouseButton = -1;
}
this.lastMouseX = charX;
this.lastMouseY = charY;
}
return false;
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double v2, double v3) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || button < 0 || button > 2) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
if (button == this.lastMouseButton && (charX != this.lastMouseX || charY != this.lastMouseY)) {
computer.mouseDrag(button + 1, charX + 1, charY + 1);
this.lastMouseX = charX;
this.lastMouseY = charY;
}
}
return false;
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
ClientComputer computer = this.computer.get();
if (computer == null || !computer.isColour() || delta == 0) {
return false;
}
Terminal term = computer.getTerminal();
if (term != null) {
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min(Math.max(charX, 0), term.getWidth() - 1);
charY = Math.min(Math.max(charY, 0), term.getHeight() - 1);
computer.mouseScroll(delta < 0 ? 1 : -1, charX + 1, charY + 1);
this.lastMouseX = charX;
this.lastMouseY = charY;
}
return true;
}
@Override
public boolean keyPressed(int key, int scancode, int modifiers) {
if (key == GLFW.GLFW_KEY_ESCAPE) {
return false;
}
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
switch (key) {
case GLFW.GLFW_KEY_T: case GLFW.GLFW_KEY_T:
if( terminateTimer < 0 ) terminateTimer = 0; if (this.terminateTimer < 0) {
this.terminateTimer = 0;
}
return true; return true;
case GLFW.GLFW_KEY_S: case GLFW.GLFW_KEY_S:
if( shutdownTimer < 0 ) shutdownTimer = 0; if (this.shutdownTimer < 0) {
this.shutdownTimer = 0;
}
return true; return true;
case GLFW.GLFW_KEY_R: case GLFW.GLFW_KEY_R:
if( rebootTimer < 0 ) rebootTimer = 0; if (this.rebootTimer < 0) {
this.rebootTimer = 0;
}
return true; return true;
case GLFW.GLFW_KEY_V: case GLFW.GLFW_KEY_V:
// Ctrl+V for paste // Ctrl+V for paste
String clipboard = client.keyboard.getClipboard(); String clipboard = this.client.keyboard.getClipboard();
if( clipboard != null ) if (clipboard != null) {
{
// Clip to the first occurrence of \r or \n // Clip to the first occurrence of \r or \n
int newLineIndex1 = clipboard.indexOf( "\r" ); int newLineIndex1 = clipboard.indexOf("\r");
int newLineIndex2 = clipboard.indexOf( "\n" ); int newLineIndex2 = clipboard.indexOf("\n");
if( newLineIndex1 >= 0 && newLineIndex2 >= 0 ) if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
{ clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
clipboard = clipboard.substring( 0, Math.min( newLineIndex1, newLineIndex2 ) ); } else if (newLineIndex1 >= 0) {
} clipboard = clipboard.substring(0, newLineIndex1);
else if( newLineIndex1 >= 0 ) } else if (newLineIndex2 >= 0) {
{ clipboard = clipboard.substring(0, newLineIndex2);
clipboard = clipboard.substring( 0, newLineIndex1 );
}
else if( newLineIndex2 >= 0 )
{
clipboard = clipboard.substring( 0, newLineIndex2 );
} }
// Filter the string // Filter the string
clipboard = SharedConstants.stripInvalidChars( clipboard ); clipboard = SharedConstants.stripInvalidChars(clipboard);
if( !clipboard.isEmpty() ) if (!clipboard.isEmpty()) {
{
// Clip to 512 characters and queue the event // Clip to 512 characters and queue the event
if( clipboard.length() > 512 ) clipboard = clipboard.substring( 0, 512 ); if (clipboard.length() > 512) {
queueEvent( "paste", clipboard ); clipboard = clipboard.substring(0, 512);
}
this.queueEvent("paste", clipboard);
} }
return true; return true;
@@ -124,43 +204,43 @@ public class WidgetTerminal implements Element
} }
} }
if( key >= 0 && terminateTimer < 0 && rebootTimer < 0 && shutdownTimer < 0 ) if (key >= 0 && this.terminateTimer < 0 && this.rebootTimer < 0 && this.shutdownTimer < 0) {
{
// Queue the "key" event and add to the down set // Queue the "key" event and add to the down set
boolean repeat = keysDown.get( key ); boolean repeat = this.keysDown.get(key);
keysDown.set( key ); this.keysDown.set(key);
IComputer computer = this.computer.get(); IComputer computer = this.computer.get();
if( computer != null ) computer.keyDown( key, repeat ); if (computer != null) {
computer.keyDown(key, repeat);
}
} }
return true; return true;
} }
@Override @Override
public boolean keyReleased( int key, int scancode, int modifiers ) public boolean keyReleased(int key, int scancode, int modifiers) {
{
// Queue the "key_up" event and remove from the down set // Queue the "key_up" event and remove from the down set
if( key >= 0 && keysDown.get( key ) ) if (key >= 0 && this.keysDown.get(key)) {
{ this.keysDown.set(key, false);
keysDown.set( key, false );
IComputer computer = this.computer.get(); IComputer computer = this.computer.get();
if( computer != null ) computer.keyUp( key ); if (computer != null) {
computer.keyUp(key);
}
} }
switch( key ) switch (key) {
{
case GLFW.GLFW_KEY_T: case GLFW.GLFW_KEY_T:
terminateTimer = -1; this.terminateTimer = -1;
break; break;
case GLFW.GLFW_KEY_R: case GLFW.GLFW_KEY_R:
rebootTimer = -1; this.rebootTimer = -1;
break; break;
case GLFW.GLFW_KEY_S: case GLFW.GLFW_KEY_S:
shutdownTimer = -1; this.shutdownTimer = -1;
break; break;
case GLFW.GLFW_KEY_LEFT_CONTROL: case GLFW.GLFW_KEY_LEFT_CONTROL:
case GLFW.GLFW_KEY_RIGHT_CONTROL: case GLFW.GLFW_KEY_RIGHT_CONTROL:
terminateTimer = rebootTimer = shutdownTimer = -1; this.terminateTimer = this.rebootTimer = this.shutdownTimer = -1;
break; break;
} }
@@ -168,187 +248,94 @@ public class WidgetTerminal implements Element
} }
@Override @Override
public boolean mouseClicked( double mouseX, double mouseY, int button ) public boolean charTyped(char ch, int modifiers) {
if (ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255) // printable chars in byte range
{ {
ClientComputer computer = this.computer.get(); // Queue the "char" event
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false; this.queueEvent("char", Character.toString(ch));
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
computer.mouseClick( button + 1, charX + 1, charY + 1 );
lastMouseButton = button;
lastMouseX = charX;
lastMouseY = charY;
} }
return true; return true;
} }
@Override @Override
public boolean mouseReleased( double mouseX, double mouseY, int button ) public boolean changeFocus(boolean reversed) {
{ if (this.focused) {
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
if( lastMouseButton == button )
{
computer.mouseUp( lastMouseButton + 1, charX + 1, charY + 1 );
lastMouseButton = -1;
}
lastMouseX = charX;
lastMouseY = charY;
}
return false;
}
@Override
public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || button < 0 || button > 2 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
if( button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY) )
{
computer.mouseDrag( button + 1, charX + 1, charY + 1 );
lastMouseX = charX;
lastMouseY = charY;
}
}
return false;
}
@Override
public boolean mouseScrolled( double mouseX, double mouseY, double delta )
{
ClientComputer computer = this.computer.get();
if( computer == null || !computer.isColour() || delta == 0 ) return false;
Terminal term = computer.getTerminal();
if( term != null )
{
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
computer.mouseScroll( delta < 0 ? 1 : -1, charX + 1, charY + 1 );
lastMouseX = charX;
lastMouseY = charY;
}
return true;
}
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 )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.shutdown();
}
if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.reboot();
}
}
@Override
public boolean changeFocus( boolean reversed )
{
if( focused )
{
// When blurring, we should make all keys go up // When blurring, we should make all keys go up
for( int key = 0; key < keysDown.size(); key++ ) for (int key = 0; key < this.keysDown.size(); key++) {
{ if (this.keysDown.get(key)) {
if( keysDown.get( key ) ) queueEvent( "key_up", key ); this.queueEvent("key_up", key);
} }
keysDown.clear(); }
this.keysDown.clear();
// When blurring, we should make the last mouse button go up // When blurring, we should make the last mouse button go up
if( lastMouseButton > 0 ) if (this.lastMouseButton > 0) {
{
IComputer computer = this.computer.get(); IComputer computer = this.computer.get();
if( computer != null ) computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 ); if (computer != null) {
lastMouseButton = -1; computer.mouseUp(this.lastMouseButton + 1, this.lastMouseX + 1, this.lastMouseY + 1);
}
this.lastMouseButton = -1;
} }
shutdownTimer = terminateTimer = rebootTimer = -1; this.shutdownTimer = this.terminateTimer = this.rebootTimer = -1;
} }
focused = !focused; this.focused = !this.focused;
return true; return true;
} }
public void draw( int originX, int originY ) @Override
{ public boolean isMouseOver(double x, double y) {
synchronized( computer ) return true;
{ }
private void queueEvent(String event, Object... args) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.queueEvent(event, args);
}
}
public void update() {
if (this.terminateTimer >= 0 && this.terminateTimer < TERMINATE_TIME && (this.terminateTimer += 0.05f) > TERMINATE_TIME) {
this.queueEvent("terminate");
}
if (this.shutdownTimer >= 0 && this.shutdownTimer < TERMINATE_TIME && (this.shutdownTimer += 0.05f) > TERMINATE_TIME) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.shutdown();
}
}
if (this.rebootTimer >= 0 && this.rebootTimer < TERMINATE_TIME && (this.rebootTimer += 0.05f) > TERMINATE_TIME) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.reboot();
}
}
}
private void queueEvent(String event) {
ClientComputer computer = this.computer.get();
if (computer != null) {
computer.queueEvent(event);
}
}
public void draw(int originX, int originY) {
synchronized (this.computer) {
// Draw the screen contents // Draw the screen contents
ClientComputer computer = this.computer.get(); ClientComputer computer = this.computer.get();
Terminal terminal = computer != null ? computer.getTerminal() : null; Terminal terminal = computer != null ? computer.getTerminal() : null;
if( terminal != null ) if (terminal != null) {
{ FixedWidthFontRenderer.drawTerminal(originX, originY, terminal, !computer.isColour(), this.topMargin, this.bottomMargin, this.leftMargin,
FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin ); this.rightMargin);
} } else {
else FixedWidthFontRenderer.drawEmptyTerminal(originX - this.leftMargin,
{ originY - this.rightMargin, this.termWidth * FONT_WIDTH + this.leftMargin + this.rightMargin,
FixedWidthFontRenderer.drawEmptyTerminal( this.termHeight * FONT_HEIGHT + this.topMargin + this.bottomMargin);
originX - leftMargin, originY - rightMargin,
termWidth * FONT_WIDTH + leftMargin + rightMargin,
termHeight * FONT_HEIGHT + topMargin + bottomMargin
);
} }
} }
} }
private void queueEvent( String event )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event );
}
private void queueEvent( String event, Object... args )
{
ClientComputer computer = this.computer.get();
if( computer != null ) computer.queueEvent( event, args );
}
@Override
public boolean isMouseOver( double x, double y )
{
return true;
}
} }

View File

@@ -3,20 +3,19 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.gui.widgets; package dan200.computercraft.client.gui.widgets;
import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Element;
public class WidgetWrapper implements Element public class WidgetWrapper implements Element {
{
private final Element listener; private final Element listener;
private final int x; private final int x;
private final int y; private final int y;
private final int width; private final int width;
private final int height; private final int height;
public WidgetWrapper( Element listener, int x, int y, int width, int height ) public WidgetWrapper(Element listener, int x, int y, int width, int height) {
{
this.listener = listener; this.listener = listener;
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -25,81 +24,68 @@ public class WidgetWrapper implements Element
} }
@Override @Override
public boolean changeFocus( boolean b ) public boolean mouseClicked(double x, double y, int button) {
{
return listener.changeFocus( b );
}
@Override
public boolean mouseClicked( double x, double y, int button )
{
double dx = x - this.x, dy = y - this.y; double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseClicked( dx, dy, button ); return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseClicked(dx, dy, button);
} }
@Override @Override
public boolean mouseReleased( double x, double y, int button ) public boolean mouseReleased(double x, double y, int button) {
{
double dx = x - this.x, dy = y - this.y; double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseReleased( dx, dy, button ); return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseReleased(dx, dy, button);
} }
@Override @Override
public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
{
double dx = x - this.x, dy = y - this.y; double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseDragged( dx, dy, button, deltaX, deltaY ); return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseDragged(dx, dy, button, deltaX, deltaY);
} }
@Override @Override
public boolean mouseScrolled( double x, double y, double delta ) public boolean mouseScrolled(double x, double y, double delta) {
{
double dx = x - this.x, dy = y - this.y; double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseScrolled( dx, dy, delta ); return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height && this.listener.mouseScrolled(dx, dy, delta);
} }
@Override @Override
public boolean keyPressed( int key, int scancode, int modifiers ) public boolean keyPressed(int key, int scancode, int modifiers) {
{ return this.listener.keyPressed(key, scancode, modifiers);
return listener.keyPressed( key, scancode, modifiers );
} }
@Override @Override
public boolean keyReleased( int key, int scancode, int modifiers ) public boolean keyReleased(int key, int scancode, int modifiers) {
{ return this.listener.keyReleased(key, scancode, modifiers);
return listener.keyReleased( key, scancode, modifiers );
} }
@Override @Override
public boolean charTyped( char character, int modifiers ) public boolean charTyped(char character, int modifiers) {
{ return this.listener.charTyped(character, modifiers);
return listener.charTyped( character, modifiers );
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
} }
@Override @Override
public boolean isMouseOver( double x, double y ) public boolean changeFocus(boolean b) {
{ return this.listener.changeFocus(b);
}
@Override
public boolean isMouseOver(double x, double y) {
double dx = x - this.x, dy = y - this.y; double dx = x - this.x, dy = y - this.y;
return dx >= 0 && dx < width && dy >= 0 && dy < height; return dx >= 0 && dx < this.width && dy >= 0 && dy < this.height;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
} }
} }

View File

@@ -21,12 +21,25 @@ import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader; import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer; import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.ComputerCraftRegistry;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import net.minecraft.client.item.ModelPredicateProvider;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.item.Item;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
@@ -38,38 +51,11 @@ import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.mixin.object.builder.ModelPredicateProviderRegistrySpecificAccessor; import net.fabricmc.fabric.mixin.object.builder.ModelPredicateProviderRegistrySpecificAccessor;
import net.minecraft.client.item.ModelPredicateProvider; @Environment (EnvType.CLIENT)
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.item.Item;
import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
import net.fabricmc.api.ClientModInitializer;
@Environment(EnvType.CLIENT)
public final class ComputerCraftProxyClient implements ClientModInitializer { public final class ComputerCraftProxyClient implements ClientModInitializer {
@SafeVarargs public static void initEvents() {
private static void registerItemProperty(String name, ModelPredicateProvider getter, Supplier<? extends Item>... items) {
Identifier id = new Identifier(ComputerCraft.MOD_ID, name);
for (Supplier<? extends Item> item : items) {
ModelPredicateProviderRegistrySpecificAccessor.callRegister(item.get(), id, getter);
}
}
private static void registerContainers() {
// My IDE doesn't think so, but we do actually need these generics.
ScreenRegistry.<ContainerComputer, GuiComputer<ContainerComputer>>register(ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create);
ScreenRegistry.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register(ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, GuiComputer::createPocket);
ScreenRegistry.register(ComputerCraftRegistry.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.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, GuiComputer::createView);
} }
@Override @Override
@@ -118,18 +104,38 @@ public final class ComputerCraftProxyClient implements ClientModInitializer {
})); }));
} }
public static void initEvents() {
// My IDE doesn't think so, but we do actually need these generics.
private static void registerContainers() {
ScreenRegistry.<ContainerComputer, GuiComputer<ContainerComputer>>register(ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create);
ScreenRegistry.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register(ComputerCraftRegistry.ModContainers.POCKET_COMPUTER,
GuiComputer::createPocket);
ScreenRegistry.<ContainerTurtle, GuiTurtle>register(ComputerCraftRegistry.ModContainers.TURTLE, GuiTurtle::new);
ScreenRegistry.<ContainerPrinter, GuiPrinter>register(ComputerCraftRegistry.ModContainers.PRINTER, GuiPrinter::new);
ScreenRegistry.<ContainerDiskDrive, GuiDiskDrive>register(ComputerCraftRegistry.ModContainers.DISK_DRIVE, GuiDiskDrive::new);
ScreenRegistry.<ContainerHeldItem, GuiPrintout>register(ComputerCraftRegistry.ModContainers.PRINTOUT, GuiPrintout::new);
ScreenRegistry.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(ComputerCraftRegistry.ModContainers.VIEW_COMPUTER,
GuiComputer::createView);
} }
// @Mod.EventBusSubscriber (modid = ComputerCraft.MOD_ID, value = Dist.CLIENT) @SafeVarargs
// public static final class ForgeHandlers { private static void registerItemProperty(String name, ModelPredicateProvider getter, Supplier<? extends Item>... items) {
// @SubscribeEvent Identifier id = new Identifier(ComputerCraft.MOD_ID, name);
// public static void onWorldUnload(WorldEvent.Unload event) { for (Supplier<? extends Item> item : items) {
// if (event.getWorld() ModelPredicateProviderRegistrySpecificAccessor.callRegister(item.get(), id, getter);
// .isClient()) { }
// ClientMonitor.destroyAll(); }
// }
// } // @Mod.EventBusSubscriber (modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
// } // public static final class ForgeHandlers {
// @SubscribeEvent
// public static void onWorldUnload(WorldEvent.Unload event) {
// if (event.getWorld()
// .isClient()) {
// ClientMonitor.destroyAll();
// }
// }
// }
} }

View File

@@ -3,14 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.ComputerCraftRegistry;
import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; import dan200.computercraft.shared.peripheral.modem.wired.BlockCable;
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
import dan200.computercraft.shared.util.WorldUtil; import dan200.computercraft.shared.util.WorldUtil;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera; import net.minecraft.client.render.Camera;
@@ -23,42 +23,48 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.World; import net.minecraft.world.World;
@Environment(EnvType.CLIENT) import net.fabricmc.api.EnvType;
public final class CableHighlightRenderer import net.fabricmc.api.Environment;
{
private CableHighlightRenderer() @Environment (EnvType.CLIENT)
{ public final class CableHighlightRenderer {
private CableHighlightRenderer() {
} }
/** /**
* Draw an outline for a specific part of a cable "Multipart". * Draw an outline for a specific part of a cable "Multipart".
*/ */
public static boolean drawHighlight(MatrixStack stack, VertexConsumer consumer, Entity entity, double d, double e, double f, BlockPos pos, BlockState state) public static boolean drawHighlight(MatrixStack stack, VertexConsumer consumer, Entity entity, double d, double e, double f, BlockPos pos,
{ BlockState state) {
World world = entity.getEntityWorld(); World world = entity.getEntityWorld();
Camera info = MinecraftClient.getInstance().gameRenderer.getCamera(); Camera info = MinecraftClient.getInstance().gameRenderer.getCamera();
// We only care about instances with both cable and modem. // We only care about instances with both cable and modem.
if( state.getBlock() != ComputerCraftRegistry.ModBlocks.CABLE || state.get( BlockCable.MODEM ).getFacing() == null || !state.get( BlockCable.CABLE ) ) if (state.getBlock() != ComputerCraftRegistry.ModBlocks.CABLE || state.get(BlockCable.MODEM)
{ .getFacing() == null || !state.get(BlockCable.CABLE)) {
return false; return false;
} }
VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), new Vec3d(d, e, f).subtract( pos.getX(), pos.getY(), pos.getZ() ) ) VoxelShape shape = WorldUtil.isVecInside(CableShapes.getModemShape(state),
? CableShapes.getModemShape( state ) new Vec3d(d, e, f).subtract(pos.getX(),
: CableShapes.getCableShape( state ); pos.getY(),
pos.getZ())) ? CableShapes.getModemShape(state) : CableShapes.getCableShape(
state);
Vec3d cameraPos = info.getPos(); Vec3d cameraPos = info.getPos();
double xOffset = pos.getX() - cameraPos.getX(); double xOffset = pos.getX() - cameraPos.getX();
double yOffset = pos.getY() - cameraPos.getY(); double yOffset = pos.getY() - cameraPos.getY();
double zOffset = pos.getZ() - cameraPos.getZ(); double zOffset = pos.getZ() - cameraPos.getZ();
Matrix4f matrix4f = stack.peek().getModel(); Matrix4f matrix4f = stack.peek()
shape.forEachEdge( ( x1, y1, z1, x2, y2, z2 ) -> { .getModel();
consumer.vertex( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) ) shape.forEachEdge((x1, y1, z1, x2, y2, z2) -> {
.color( 0, 0, 0, 0.4f ).next(); consumer.vertex(matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset))
consumer.vertex( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) ) .color(0, 0, 0, 0.4f)
.color( 0, 0, 0, 0.4f ).next(); .next();
} ); consumer.vertex(matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset))
.color(0, 0, 0, 0.4f)
.next();
});
return true; return true;
} }

View File

@@ -6,12 +6,13 @@
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
@@ -19,46 +20,38 @@ import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
public class ComputerBorderRenderer public class ComputerBorderRenderer {
{ public static final Identifier BACKGROUND_NORMAL = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_normal.png");
public static final Identifier BACKGROUND_NORMAL = new Identifier( ComputerCraft.MOD_ID, "textures/gui/corners_normal.png" ); public static final Identifier BACKGROUND_ADVANCED = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png");
public static final Identifier BACKGROUND_ADVANCED = new Identifier( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" ); public static final Identifier BACKGROUND_COMMAND = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_command.png");
public static final Identifier BACKGROUND_COMMAND = new Identifier( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" ); public static final Identifier BACKGROUND_COLOUR = new Identifier(ComputerCraft.MOD_ID, "textures/gui/corners_colour.png");
public static final Identifier BACKGROUND_COLOUR = new Identifier( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
private static final Matrix4f IDENTITY = new Matrix4f();
static
{
IDENTITY.loadIdentity();
}
/** /**
* The margin between the terminal and its border. * The margin between the terminal and its border.
*/ */
public static final int MARGIN = 2; public static final int MARGIN = 2;
/** /**
* The width of the terminal border. * The width of the terminal border.
*/ */
public static final int BORDER = 12; 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_TOP_Y = 28;
private static final int CORNER_BOTTOM_Y = CORNER_TOP_Y + BORDER; private static final int CORNER_BOTTOM_Y = CORNER_TOP_Y + BORDER;
private static final int CORNER_LEFT_X = BORDER; private static final int CORNER_LEFT_X = BORDER;
private static final int CORNER_RIGHT_X = CORNER_LEFT_X + BORDER; private static final int CORNER_RIGHT_X = CORNER_LEFT_X + BORDER;
private static final int BORDER_RIGHT_X = 36; private static final int BORDER_RIGHT_X = 36;
private static final int GAP = 4; private static final int GAP = 4;
private static final float TEX_SCALE = 1 / 256.0f; private static final float TEX_SCALE = 1 / 256.0f;
static {
IDENTITY.loadIdentity();
}
private final Matrix4f transform; private final Matrix4f transform;
private final VertexConsumer builder; private final VertexConsumer builder;
private final int z; private final int z;
private final float r, g, b; private final float r, g, b;
public ComputerBorderRenderer( Matrix4f transform, VertexConsumer builder, int z, float r, float g, float b ) public ComputerBorderRenderer(Matrix4f transform, VertexConsumer builder, int z, float r, float g, float b) {
{
this.transform = transform; this.transform = transform;
this.builder = builder; this.builder = builder;
this.z = z; this.z = z;
@@ -69,10 +62,8 @@ public class ComputerBorderRenderer
@Nonnull @Nonnull
public static Identifier getTexture( @Nonnull ComputerFamily family ) public static Identifier getTexture(@Nonnull ComputerFamily family) {
{ switch (family) {
switch( family )
{
case NORMAL: case NORMAL:
default: default:
return BACKGROUND_NORMAL; return BACKGROUND_NORMAL;
@@ -83,93 +74,94 @@ public class ComputerBorderRenderer
} }
} }
public static void render( int x, int y, int z, int width, int height ) public static void render(int x, int y, int z, int width, int height) {
{
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE ); buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE);
render( IDENTITY, buffer, x, y, z, width, height ); render(IDENTITY, buffer, x, y, z, width, height);
RenderSystem.enableAlphaTest(); RenderSystem.enableAlphaTest();
tessellator.draw(); tessellator.draw();
} }
public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height ) public static void render(Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height) {
{ render(transform, buffer, x, y, z, width, height, 1, 1, 1);
render( transform, buffer, x, y, z, width, height, 1, 1, 1 );
} }
public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, float r, float g, float b ) public static void render(Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, float r, float g, float b) {
{ render(transform, buffer, x, y, z, width, height, 0, r, g, b);
render( transform, buffer, x, y, z, width, height, 0, r, g, b );
} }
public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, int borderHeight, float r, float g, float b ) public static void render(Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, int borderHeight, float r, float g,
{ float b) {
new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, borderHeight ); new ComputerBorderRenderer(transform, buffer, z, r, g, b).doRender(x, y, width, height, borderHeight);
} }
public void doRender( int x, int y, int width, int height, int bottomHeight ) public void doRender(int x, int y, int width, int height, int bottomHeight) {
{
int endX = x + width; int endX = x + width;
int endY = y + height; int endY = y + height;
// Vertical bars // Vertical bars
renderLine( x - BORDER, y, 0, CORNER_TOP_Y, BORDER, endY - y ); this.renderLine(x - BORDER, y, 0, CORNER_TOP_Y, BORDER, endY - y);
renderLine( endX, y, BORDER_RIGHT_X, CORNER_TOP_Y, BORDER, endY - y ); this.renderLine(endX, y, BORDER_RIGHT_X, CORNER_TOP_Y, BORDER, endY - y);
// Top bar // Top bar
renderLine( x, y - BORDER, 0, 0, endX - x, BORDER ); this.renderLine(x, y - BORDER, 0, 0, endX - x, BORDER);
renderCorner( x - BORDER, y - BORDER, CORNER_LEFT_X, CORNER_TOP_Y ); this.renderCorner(x - BORDER, y - BORDER, CORNER_LEFT_X, CORNER_TOP_Y);
renderCorner( endX, y - BORDER, CORNER_RIGHT_X, CORNER_TOP_Y ); this.renderCorner(endX, y - BORDER, CORNER_RIGHT_X, CORNER_TOP_Y);
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the // Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
// pocket computer's lights). // pocket computer's lights).
if( bottomHeight <= 0 ) if (bottomHeight <= 0) {
{ this.renderLine(x, endY, 0, BORDER, endX - x, BORDER);
renderLine( x, endY, 0, BORDER, endX - x, BORDER ); this.renderCorner(x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y);
renderCorner( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y ); this.renderCorner(endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y);
renderCorner( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y ); } else {
}
else
{
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for // Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
// lights, and then the bottom outer corners. // lights, and then the bottom outer corners.
renderTexture( x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 ); this.renderTexture(x - BORDER, endY, CORNER_LEFT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2);
renderTexture( x, endY, 0, BORDER, width, BORDER / 2, BORDER, BORDER / 2 ); this.renderTexture(x, endY, 0, BORDER, width, BORDER / 2, BORDER, BORDER / 2);
renderTexture( endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2 ); this.renderTexture(endX, endY, CORNER_RIGHT_X, CORNER_BOTTOM_Y, BORDER, BORDER / 2);
renderTexture( x - BORDER, endY + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP ); this.renderTexture(x - BORDER, endY + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP);
renderTexture( x, endY + BORDER / 2, 0, BORDER + GAP, width, bottomHeight, BORDER, GAP ); this.renderTexture(x, endY + BORDER / 2, 0, BORDER + GAP, width, bottomHeight, BORDER, GAP);
renderTexture( endX, endY + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP ); this.renderTexture(endX, endY + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + GAP, BORDER, bottomHeight, BORDER, GAP);
renderTexture( x - BORDER, endY + bottomHeight + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 ); this.renderTexture(x - BORDER, endY + bottomHeight + BORDER / 2, CORNER_LEFT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2);
renderTexture( x, endY + bottomHeight + BORDER / 2, 0, BORDER + BORDER / 2, width, BORDER / 2 ); this.renderTexture(x, endY + bottomHeight + BORDER / 2, 0, BORDER + BORDER / 2, width, BORDER / 2);
renderTexture( endX, endY + bottomHeight + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2 ); this.renderTexture(endX, endY + bottomHeight + BORDER / 2, CORNER_RIGHT_X, CORNER_BOTTOM_Y + BORDER / 2, BORDER, BORDER / 2);
} }
} }
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) {
{ this.renderTexture(x, y, u, v, width, height, BORDER, BORDER);
renderTexture( x, y, u, v, BORDER, BORDER, BORDER, BORDER );
} }
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) {
{ this.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 ) private void renderTexture(int x, int y, int u, int v, int width, int height) {
{ this.renderTexture(x, y, u, v, width, height, width, height);
renderTexture( x, y, u, v, width, height, width, height );
} }
private void renderTexture( int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight ) private void renderTexture(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) {
{ this.builder.vertex(this.transform, x, y + height, this.z)
builder.vertex( transform, x, y + height, z ).color( r, g, b, 1.0f ).texture( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).next(); .color(this.r, this.g, this.b, 1.0f)
builder.vertex( transform, x + width, y + height, z ).color( r, g, b, 1.0f ).texture( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).next(); .texture(u * TEX_SCALE, (v + textureHeight) * TEX_SCALE)
builder.vertex( transform, x + width, y, z ).color( r, g, b, 1.0f ).texture( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ).next(); .next();
builder.vertex( transform, x, y, z ).color( r, g, b, 1.0f ).texture( u * TEX_SCALE, v * TEX_SCALE ).next(); this.builder.vertex(this.transform, x + width, y + height, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture((u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE)
.next();
this.builder.vertex(this.transform, x + width, y, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture((u + textureWidth) * TEX_SCALE, v * TEX_SCALE)
.next();
this.builder.vertex(this.transform, x, y, this.z)
.color(this.r, this.g, this.b, 1.0f)
.texture(u * TEX_SCALE, v * TEX_SCALE)
.next();
} }
} }

View File

@@ -3,10 +3,9 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.HeldItemRenderer; import net.minecraft.client.render.item.HeldItemRenderer;
@@ -18,79 +17,29 @@ import net.minecraft.util.Arm;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@Environment(EnvType.CLIENT) import net.fabricmc.api.EnvType;
public abstract class ItemMapLikeRenderer import net.fabricmc.api.Environment;
{
/**
* 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
*/
protected abstract void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack );
public void renderItemFirstPerson(MatrixStack transform, VertexConsumerProvider render, int lightTexture, Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) @Environment (EnvType.CLIENT)
{ public abstract class ItemMapLikeRenderer {
public void renderItemFirstPerson(MatrixStack transform, VertexConsumerProvider render, int lightTexture, Hand hand, float pitch, float equipProgress
, float swingProgress, ItemStack stack) {
PlayerEntity player = MinecraftClient.getInstance().player; PlayerEntity player = MinecraftClient.getInstance().player;
transform.push(); transform.push();
if( hand == Hand.MAIN_HAND && player.getOffHandStack().isEmpty() ) if (hand == Hand.MAIN_HAND && player.getOffHandStack()
{ .isEmpty()) {
renderItemFirstPersonCenter( transform, render, lightTexture, pitch, equipProgress, swingProgress, stack ); this.renderItemFirstPersonCenter(transform, render, lightTexture, pitch, equipProgress, swingProgress, stack);
} else {
this.renderItemFirstPersonSide(transform,
render,
lightTexture,
hand == Hand.MAIN_HAND ? player.getMainArm() : player.getMainArm()
.getOpposite(),
equipProgress,
swingProgress,
stack);
} }
else
{
renderItemFirstPersonSide(
transform, render, lightTexture,
hand == Hand.MAIN_HAND ? player.getMainArm() : player.getMainArm().getOpposite(),
equipProgress, swingProgress, stack
);
}
transform.pop();
}
/**
* Renders the item to one side of the player.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param side The side to render on
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
*/
private void renderItemFirstPersonSide( MatrixStack transform, VertexConsumerProvider render, int combinedLight, Arm side, float equipProgress, float swingProgress, ItemStack stack )
{
MinecraftClient minecraft = MinecraftClient.getInstance();
float offset = side == Arm.RIGHT ? 1f : -1f;
transform.translate( offset * 0.125f, -0.125f, 0f );
// If the player is not invisible then render a single arm
if( !minecraft.player.isInvisible() )
{
transform.push();
transform.multiply( Vector3f.POSITIVE_Z.getDegreesQuaternion( offset * 10f ) );
minecraft.getHeldItemRenderer().renderArmHoldingItem( transform, render, combinedLight, equipProgress, swingProgress, side );
transform.pop();
}
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
transform.push();
transform.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f );
float f1 = MathHelper.sqrt( swingProgress );
float f2 = MathHelper.sin( f1 * (float) Math.PI );
float f3 = -0.5f * f2;
float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) );
float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI );
transform.translate( offset * f3, f4 - 0.3f * f2, f5 );
transform.multiply( Vector3f.POSITIVE_X.getDegreesQuaternion( f2 * -45f ) );
transform.multiply( Vector3f.POSITIVE_Y.getDegreesQuaternion( offset * f2 * -30f ) );
renderItem( transform, render, stack );
transform.pop(); transform.pop();
} }
@@ -105,34 +54,86 @@ public abstract class ItemMapLikeRenderer
* @param swingProgress The swing progress of this item * @param swingProgress The swing progress of this item
* @param stack The stack to render * @param stack The stack to render
*/ */
private void renderItemFirstPersonCenter( MatrixStack transform, VertexConsumerProvider render, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack ) private void renderItemFirstPersonCenter(MatrixStack transform, VertexConsumerProvider render, int combinedLight, float pitch, float equipProgress,
{ float swingProgress, ItemStack stack) {
MinecraftClient minecraft = MinecraftClient.getInstance(); MinecraftClient minecraft = MinecraftClient.getInstance();
HeldItemRenderer renderer = minecraft.getHeldItemRenderer(); HeldItemRenderer renderer = minecraft.getHeldItemRenderer();
// Setup the appropriate transformations. This is just copied from the // Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer. // corresponding method in ItemRenderer.
float swingRt = MathHelper.sqrt( swingProgress ); float swingRt = MathHelper.sqrt(swingProgress);
float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI ); float tX = -0.2f * MathHelper.sin(swingProgress * (float) Math.PI);
float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI ); float tZ = -0.4f * MathHelper.sin(swingRt * (float) Math.PI);
transform.translate( 0, -tX / 2, tZ ); transform.translate(0, -tX / 2, tZ);
float pitchAngle = renderer.getMapAngle( pitch ); float pitchAngle = renderer.getMapAngle(pitch);
transform.translate( 0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f ); transform.translate(0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f);
transform.multiply( Vector3f.POSITIVE_X.getDegreesQuaternion( pitchAngle * -85.0f ) ); transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(pitchAngle * -85.0f));
if( !minecraft.player.isInvisible() ) if (!minecraft.player.isInvisible()) {
{
transform.push(); transform.push();
transform.multiply( Vector3f.POSITIVE_Y.getDegreesQuaternion( 90.0F ) ); transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(90.0F));
renderer.renderArm( transform, render, combinedLight, Arm.RIGHT ); renderer.renderArm(transform, render, combinedLight, Arm.RIGHT);
renderer.renderArm( transform, render, combinedLight, Arm.LEFT ); renderer.renderArm(transform, render, combinedLight, Arm.LEFT);
transform.pop(); transform.pop();
} }
float rX = MathHelper.sin( swingRt * (float) Math.PI ); float rX = MathHelper.sin(swingRt * (float) Math.PI);
transform.multiply( Vector3f.POSITIVE_X.getDegreesQuaternion( rX * 20.0F ) ); transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(rX * 20.0F));
transform.scale( 2.0F, 2.0F, 2.0F ); transform.scale(2.0F, 2.0F, 2.0F);
renderItem( transform, render, stack ); this.renderItem(transform, render, stack);
} }
/**
* Renders the item to one side of the player.
*
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param combinedLight The current light level
* @param side The side to render on
* @param equipProgress The equip progress of this item
* @param swingProgress The swing progress of this item
* @param stack The stack to render
*/
private void renderItemFirstPersonSide(MatrixStack transform, VertexConsumerProvider render, int combinedLight, Arm side, float equipProgress,
float swingProgress, ItemStack stack) {
MinecraftClient minecraft = MinecraftClient.getInstance();
float offset = side == Arm.RIGHT ? 1f : -1f;
transform.translate(offset * 0.125f, -0.125f, 0f);
// If the player is not invisible then render a single arm
if (!minecraft.player.isInvisible()) {
transform.push();
transform.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(offset * 10f));
minecraft.getHeldItemRenderer()
.renderArmHoldingItem(transform, render, combinedLight, equipProgress, swingProgress, side);
transform.pop();
}
// Setup the appropriate transformations. This is just copied from the
// corresponding method in ItemRenderer.
transform.push();
transform.translate(offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f);
float f1 = MathHelper.sqrt(swingProgress);
float f2 = MathHelper.sin(f1 * (float) Math.PI);
float f3 = -0.5f * f2;
float f4 = 0.4f * MathHelper.sin(f1 * ((float) Math.PI * 2f));
float f5 = -0.3f * MathHelper.sin(swingProgress * (float) Math.PI);
transform.translate(offset * f3, f4 - 0.3f * f2, f5);
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(f2 * -45f));
transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(offset * f2 * -30f));
this.renderItem(transform, render, stack);
transform.pop();
}
/**
* 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
*/
protected abstract void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack);
} }

View File

@@ -3,8 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
@@ -13,6 +19,8 @@ import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
@@ -22,40 +30,27 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f; import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
/** /**
* Emulates map rendering for pocket computers. * Emulates map rendering for pocket computers.
*/ */
public final class ItemPocketRenderer extends ItemMapLikeRenderer public final class ItemPocketRenderer extends ItemMapLikeRenderer {
{ public static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private static final int LIGHT_HEIGHT = 8; private static final int LIGHT_HEIGHT = 8;
public static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer(); private ItemPocketRenderer() {
private ItemPocketRenderer()
{
} }
@Override @Override
protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ) protected void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
{ ClientComputer computer = ItemPocketComputer.createClientComputer(stack);
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal(); Terminal terminal = computer == null ? null : computer.getTerminal();
int termWidth, termHeight; int termWidth, termHeight;
if( terminal == null ) if (terminal == null) {
{
termWidth = ComputerCraft.terminalWidth_pocketComputer; termWidth = ComputerCraft.terminalWidth_pocketComputer;
termHeight = ComputerCraft.terminalHeight_pocketComputer; termHeight = ComputerCraft.terminalHeight_pocketComputer;
} } else {
else
{
termWidth = terminal.getWidth(); termWidth = terminal.getWidth();
termHeight = terminal.getHeight(); termHeight = terminal.getHeight();
} }
@@ -66,43 +61,43 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
// Setup various transformations. Note that these are partially adapted from the corresponding method // Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer // in ItemRenderer
transform.push(); transform.push();
transform.multiply( Vector3f.POSITIVE_Y.getDegreesQuaternion( 180f ) ); transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180f));
transform.multiply( Vector3f.POSITIVE_Z.getDegreesQuaternion( 180f ) ); transform.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(180f));
transform.scale( 0.5f, 0.5f, 0.5f ); transform.scale(0.5f, 0.5f, 0.5f);
float scale = 0.75f / Math.max( width + BORDER * 2, height + BORDER * 2 + LIGHT_HEIGHT ); float scale = 0.75f / Math.max(width + BORDER * 2, height + BORDER * 2 + LIGHT_HEIGHT);
transform.scale( scale, scale, 0 ); transform.scale(scale, scale, 0);
transform.translate( -0.5 * width, -0.5 * height, 0 ); transform.translate(-0.5 * width, -0.5 * height, 0);
// Render the main frame // Render the main frame
ItemPocketComputer item = (ItemPocketComputer) stack.getItem(); ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
ComputerFamily family = item.getFamily(); ComputerFamily family = item.getFamily();
int frameColour = item.getColour( stack ); int frameColour = item.getColour(stack);
Matrix4f matrix = transform.peek().getModel(); Matrix4f matrix = transform.peek()
renderFrame( matrix, family, frameColour, width, height ); .getModel();
renderFrame(matrix, family, frameColour, width, height);
// Render the light // Render the light
int lightColour = ItemPocketComputer.getLightState( stack ); int lightColour = ItemPocketComputer.getLightState(stack);
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); if (lightColour == -1) {
renderLight( matrix, lightColour, width, height ); lightColour = Colour.BLACK.getHex();
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
} }
else renderLight(matrix, lightColour, width, height);
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, 0, 0, width, height ); if (computer != null && terminal != null) {
FixedWidthFontRenderer.drawTerminal(matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN);
} else {
FixedWidthFontRenderer.drawEmptyTerminal(matrix, 0, 0, width, height);
} }
transform.pop(); transform.pop();
} }
private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height ) private static void renderFrame(Matrix4f transform, ComputerFamily family, int colour, int width, int height) {
{ MinecraftClient.getInstance()
MinecraftClient.getInstance().getTextureManager() .getTextureManager()
.bindTexture( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) ); .bindTexture(colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture(family));
float r = ((colour >>> 16) & 0xFF) / 255.0f; float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f; float g = ((colour >>> 8) & 0xFF) / 255.0f;
@@ -110,15 +105,14 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE ); buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE);
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, LIGHT_HEIGHT, r, g, b ); ComputerBorderRenderer.render(transform, buffer, 0, 0, 0, width, height, LIGHT_HEIGHT, r, g, b);
tessellator.draw(); tessellator.draw();
} }
private static void renderLight( Matrix4f transform, int colour, int width, int height ) private static void renderLight(Matrix4f transform, int colour, int width, int height) {
{
RenderSystem.enableBlend(); RenderSystem.enableBlend();
RenderSystem.disableTexture(); RenderSystem.disableTexture();
@@ -128,11 +122,19 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR ); buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).next(); buffer.vertex(transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0)
buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).next(); .color(r, g, b, 1.0f)
buffer.vertex( transform, width, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).next(); .next();
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).next(); buffer.vertex(transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
buffer.vertex(transform, width, height + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
buffer.vertex(transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0)
.color(r, g, b, 1.0f)
.next();
tessellator.draw(); tessellator.draw();
RenderSystem.enableTexture(); RenderSystem.enableTexture();

View File

@@ -3,90 +3,92 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import dan200.computercraft.ComputerCraft; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.PrintoutRenderer.COVER_SIZE;
import static dan200.computercraft.client.render.PrintoutRenderer.X_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.Y_TEXT_MARGIN;
import static dan200.computercraft.client.render.PrintoutRenderer.drawBorder;
import static dan200.computercraft.client.render.PrintoutRenderer.drawText;
import static dan200.computercraft.client.render.PrintoutRenderer.offsetAt;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.util.math.Vector3f; import net.minecraft.client.util.math.Vector3f;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
/** /**
* Emulates map and item-frame rendering for printouts. * Emulates map and item-frame rendering for printouts.
*/ */
public final class ItemPrintoutRenderer extends ItemMapLikeRenderer public final class ItemPrintoutRenderer extends ItemMapLikeRenderer {
{
public static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer(); public static final ItemPrintoutRenderer INSTANCE = new ItemPrintoutRenderer();
private ItemPrintoutRenderer() private ItemPrintoutRenderer() {
{
} }
@Override @Override
protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ) protected void renderItem(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
{ transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(180f));
transform.multiply( Vector3f.POSITIVE_X.getDegreesQuaternion( 180f ) ); transform.scale(0.42f, 0.42f, -0.42f);
transform.scale( 0.42f, 0.42f, -0.42f ); transform.translate(-0.5f, -0.48f, 0.0f);
transform.translate( -0.5f, -0.48f, 0.0f );
drawPrintout( transform, render, stack ); drawPrintout(transform, render, stack);
} }
public boolean renderInFrame(MatrixStack matrixStack, VertexConsumerProvider consumerProvider, ItemStack stack ) private static void drawPrintout(MatrixStack transform, VertexConsumerProvider render, ItemStack stack) {
{ int pages = ItemPrintout.getPageCount(stack);
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.multiply( Vector3f.POSITIVE_Z.getDegreesQuaternion( 180f ) );
matrixStack.scale( 0.95f, 0.95f, -0.95f );
matrixStack.translate( -0.5f, -0.5f, 0.0f );
drawPrintout( matrixStack, consumerProvider, stack );
return true;
}
private static void drawPrintout( MatrixStack transform, VertexConsumerProvider render, ItemStack stack )
{
int pages = ItemPrintout.getPageCount( stack );
boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK; boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK;
double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2; double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2; double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
// Non-books will be left aligned // Non-books will be left aligned
if( !book ) width += offsetAt( pages ); if (!book) {
width += offsetAt(pages);
}
double visualWidth = width, visualHeight = height; double visualWidth = width, visualHeight = height;
// Meanwhile books will be centred // Meanwhile books will be centred
if( book ) if (book) {
{ visualWidth += 2 * COVER_SIZE + 2 * offsetAt(pages);
visualWidth += 2 * COVER_SIZE + 2 * offsetAt( pages );
visualHeight += 2 * COVER_SIZE; visualHeight += 2 * COVER_SIZE;
} }
double max = Math.max( visualHeight, visualWidth ); double max = Math.max(visualHeight, visualWidth);
// Scale the printout to fit correctly. // Scale the printout to fit correctly.
float scale = (float) (1.0 / max); float scale = (float) (1.0 / max);
transform.scale( scale, scale, scale ); transform.scale(scale, scale, scale);
transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 ); transform.translate((max - width) / 2.0, (max - height) / 2.0, 0.0);
Matrix4f matrix = transform.peek().getModel(); Matrix4f matrix = transform.peek()
drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book ); .getModel();
drawText( matrix, render, drawBorder(matrix, render, 0, 0, -0.01f, 0, pages, book);
X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) drawText(matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText(stack), ItemPrintout.getColours(stack));
); }
public boolean renderInFrame(MatrixStack matrixStack, VertexConsumerProvider consumerProvider, ItemStack stack) {
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.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(180f));
matrixStack.scale(0.95f, 0.95f, -0.95f);
matrixStack.translate(-0.5f, -0.5f, 0.0f);
drawPrintout(matrixStack, consumerProvider, stack);
return true;
} }
} }

View File

@@ -8,10 +8,6 @@ package dan200.computercraft.client.render;
import java.util.List; import java.util.List;
import javax.vecmath.Vector4f;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormatElement; import net.minecraft.client.render.VertexFormatElement;
import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.VertexFormats;
@@ -19,10 +15,13 @@ import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Quaternion; import net.minecraft.util.math.Quaternion;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
/** /**
* Transforms vertices of a model, remaining aware of winding order, and rearranging vertices if needed. * Transforms vertices of a model, remaining aware of winding order, and rearranging vertices if needed.
*/ */
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
public final class ModelTransformer { public final class ModelTransformer {
private static final Matrix4f identity; private static final Matrix4f identity;
@@ -53,9 +52,11 @@ public final class ModelTransformer {
.clone(); .clone();
int offset = 0; int offset = 0;
BakedQuad copy = new BakedQuad(vertexData, -1, quad.getFace(), quad.sprite, true); BakedQuad copy = new BakedQuad(vertexData, -1, quad.getFace(), quad.sprite, true);
for (int i = 0; i < format.getElements().size(); ++i) // For each vertex element for (int i = 0; i < format.getElements()
.size(); ++i) // For each vertex element
{ {
VertexFormatElement element = format.getElements().get(i); VertexFormatElement element = format.getElements()
.get(i);
if (element.getType() == VertexFormatElement.Type.POSITION && element.getFormat() == VertexFormatElement.Format.FLOAT && element.getSize() == 3) // When we find a position if (element.getType() == VertexFormatElement.Type.POSITION && element.getFormat() == VertexFormatElement.Format.FLOAT && element.getSize() == 3) // When we find a position
// element // element
{ {

View File

@@ -3,11 +3,20 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import static net.minecraft.util.math.Direction.DOWN;
import static net.minecraft.util.math.Direction.EAST;
import static net.minecraft.util.math.Direction.NORTH;
import static net.minecraft.util.math.Direction.SOUTH;
import static net.minecraft.util.math.Direction.UP;
import static net.minecraft.util.math.Direction.WEST;
import java.util.EnumSet;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@@ -20,73 +29,109 @@ import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.EnumSet; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import static net.minecraft.util.math.Direction.*;
/** /**
* Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current * Overrides monitor highlighting to only render the outline of the <em>whole</em> monitor, rather than the current block. This means you do not get an
* block. This means you do not get an intrusive outline on top of the screen. * intrusive outline on top of the screen.
*/ */
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
public final class MonitorHighlightRenderer public final class MonitorHighlightRenderer {
{ private MonitorHighlightRenderer() {
private MonitorHighlightRenderer()
{
} }
public static boolean drawHighlight(MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos, BlockState blockState) public static boolean drawHighlight(MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos
{ , BlockState blockState) {
// Preserve normal behaviour when crouching. // Preserve normal behaviour when crouching.
if( entity.isInSneakingPose() ) return false; if (entity.isInSneakingPose()) {
return false;
}
World world = entity.getEntityWorld(); World world = entity.getEntityWorld();
BlockEntity tile = world.getBlockEntity( pos ); BlockEntity tile = world.getBlockEntity(pos);
if( !(tile instanceof TileMonitor) ) return false; if (!(tile instanceof TileMonitor)) {
return false;
}
TileMonitor monitor = (TileMonitor) tile; TileMonitor monitor = (TileMonitor) tile;
// Determine which sides are part of the external faces of the monitor, and so which need to be rendered. // Determine which sides are part of the external faces of the monitor, and so which need to be rendered.
EnumSet<Direction> faces = EnumSet.allOf( Direction.class ); EnumSet<Direction> faces = EnumSet.allOf(Direction.class);
Direction front = monitor.getFront(); Direction front = monitor.getFront();
faces.remove( front ); faces.remove(front);
if( monitor.getXIndex() != 0 ) faces.remove( monitor.getRight().getOpposite() ); if (monitor.getXIndex() != 0) {
if( monitor.getXIndex() != monitor.getWidth() - 1 ) faces.remove( monitor.getRight() ); faces.remove(monitor.getRight()
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() ); .getOpposite());
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() ); }
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());
}
Vec3d cameraPos = MinecraftClient.getInstance().gameRenderer.getCamera().getPos(); Vec3d cameraPos = MinecraftClient.getInstance().gameRenderer.getCamera()
.getPos();
matrixStack.push(); matrixStack.push();
matrixStack.translate( pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ() ); matrixStack.translate(pos.getX() - cameraPos.getX(), pos.getY() - cameraPos.getY(), pos.getZ() - cameraPos.getZ());
// I wish I could think of a better way to do this // I wish I could think of a better way to do this
Matrix4f transform = matrixStack.peek().getModel(); Matrix4f transform = matrixStack.peek()
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( vertexConsumer, transform, 0, 0, 0, UP ); .getModel();
if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( vertexConsumer, transform, 0, 0, 1, UP ); if (faces.contains(NORTH) || faces.contains(WEST)) {
if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( vertexConsumer, transform, 1, 0, 0, UP ); line(vertexConsumer, transform, 0, 0, 0, UP);
if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( vertexConsumer, transform, 1, 0, 1, UP ); }
if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( vertexConsumer, transform, 0, 0, 0, EAST ); if (faces.contains(SOUTH) || faces.contains(WEST)) {
if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( vertexConsumer, transform, 0, 0, 1, EAST ); line(vertexConsumer, transform, 0, 0, 1, UP);
if( faces.contains( NORTH ) || faces.contains( UP ) ) line( vertexConsumer, transform, 0, 1, 0, EAST ); }
if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( vertexConsumer, transform, 0, 1, 1, EAST ); if (faces.contains(NORTH) || faces.contains(EAST)) {
if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( vertexConsumer, transform, 0, 0, 0, SOUTH ); line(vertexConsumer, transform, 1, 0, 0, UP);
if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( vertexConsumer, transform, 1, 0, 0, SOUTH ); }
if( faces.contains( WEST ) || faces.contains( UP ) ) line( vertexConsumer, transform, 0, 1, 0, SOUTH ); if (faces.contains(SOUTH) || faces.contains(EAST)) {
if( faces.contains( EAST ) || faces.contains( UP ) ) line( vertexConsumer, transform, 1, 1, 0, SOUTH ); line(vertexConsumer, transform, 1, 0, 1, UP);
}
if (faces.contains(NORTH) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 0, EAST);
}
if (faces.contains(SOUTH) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 1, EAST);
}
if (faces.contains(NORTH) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 0, EAST);
}
if (faces.contains(SOUTH) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 1, EAST);
}
if (faces.contains(WEST) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 0, 0, 0, SOUTH);
}
if (faces.contains(EAST) || faces.contains(DOWN)) {
line(vertexConsumer, transform, 1, 0, 0, SOUTH);
}
if (faces.contains(WEST) || faces.contains(UP)) {
line(vertexConsumer, transform, 0, 1, 0, SOUTH);
}
if (faces.contains(EAST) || faces.contains(UP)) {
line(vertexConsumer, transform, 1, 1, 0, SOUTH);
}
matrixStack.pop(); matrixStack.pop();
return true; return true;
} }
private static void line( VertexConsumer buffer, Matrix4f transform, float x, float y, float z, Direction direction ) private static void line(VertexConsumer buffer, Matrix4f transform, 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 ).next(); .color(0, 0, 0, 0.4f)
buffer.vertex( transform, .next();
x + direction.getOffsetX(), buffer.vertex(transform, x + direction.getOffsetX(), y + direction.getOffsetY(), z + direction.getOffsetZ())
y + direction.getOffsetY(), .color(0, 0, 0, 0.4f)
z + direction.getOffsetZ() .next();
).color( 0, 0, 0, 0.4f ).next();
} }
} }

View File

@@ -6,6 +6,9 @@
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import java.io.InputStream;
import java.nio.FloatBuffer;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
@@ -16,17 +19,14 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
import java.io.InputStream;
import java.nio.FloatBuffer;
import net.minecraft.client.texture.TextureUtil; import net.minecraft.client.texture.TextureUtil;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
class MonitorTextureBufferShader class MonitorTextureBufferShader {
{
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer( 16 ); private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 ); private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer(16 * 3);
private static int uniformMv; private static int uniformMv;
@@ -40,125 +40,126 @@ class MonitorTextureBufferShader
private static boolean ok; private static boolean ok;
private static int program; private static int program;
static void setupUniform( Matrix4f transform, int width, int height, Palette palette, boolean greyscale ) static void setupUniform(Matrix4f transform, int width, int height, Palette palette, boolean greyscale) {
{
MATRIX_BUFFER.rewind(); MATRIX_BUFFER.rewind();
transform.writeToBuffer( MATRIX_BUFFER ); transform.writeToBuffer(MATRIX_BUFFER);
MATRIX_BUFFER.rewind(); MATRIX_BUFFER.rewind();
RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER ); RenderSystem.glUniformMatrix4(uniformMv, false, MATRIX_BUFFER);
RenderSystem.glUniform1i( uniformWidth, width ); RenderSystem.glUniform1i(uniformWidth, width);
RenderSystem.glUniform1i( uniformHeight, height ); RenderSystem.glUniform1i(uniformHeight, height);
// TODO: Cache this? Maybe?? // TODO: Cache this? Maybe??
PALETTE_BUFFER.rewind(); PALETTE_BUFFER.rewind();
for( int i = 0; i < 16; i++ ) for (int i = 0; i < 16; i++) {
{ double[] colour = palette.getColour(i);
double[] colour = palette.getColour( i ); if (greyscale) {
if( greyscale ) float f = FixedWidthFontRenderer.toGreyscale(colour);
{ PALETTE_BUFFER.put(f)
float f = FixedWidthFontRenderer.toGreyscale( colour ); .put(f)
PALETTE_BUFFER.put( f ).put( f ).put( f ); .put(f);
} } else {
else PALETTE_BUFFER.put((float) colour[0])
{ .put((float) colour[1])
PALETTE_BUFFER.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] ); .put((float) colour[2]);
} }
} }
PALETTE_BUFFER.flip(); PALETTE_BUFFER.flip();
RenderSystem.glUniform3( uniformPalette, PALETTE_BUFFER ); RenderSystem.glUniform3(uniformPalette, PALETTE_BUFFER);
} }
static boolean use() static boolean use() {
{ if (initialised) {
if( initialised ) if (ok) {
{ GlStateManager.useProgram(program);
if( ok ) GlStateManager.useProgram( program ); }
return ok; return ok;
} }
if( ok = load() ) if (ok = load()) {
{ GL20.glUseProgram(program);
GL20.glUseProgram( program ); RenderSystem.glUniform1i(uniformFont, 0);
RenderSystem.glUniform1i( uniformFont, 0 ); RenderSystem.glUniform1i(uniformTbo, TEXTURE_INDEX - GL13.GL_TEXTURE0);
RenderSystem.glUniform1i( uniformTbo, TEXTURE_INDEX - GL13.GL_TEXTURE0 );
} }
return ok; return ok;
} }
private static boolean load() private static boolean load() {
{
initialised = true; initialised = true;
try try {
{ int vertexShader = loadShader(GL20.GL_VERTEX_SHADER, "assets/computercraft/shaders/monitor.vert");
int vertexShader = loadShader( GL20.GL_VERTEX_SHADER, "assets/computercraft/shaders/monitor.vert" ); int fragmentShader = loadShader(GL20.GL_FRAGMENT_SHADER, "assets/computercraft/shaders/monitor.frag");
int fragmentShader = loadShader( GL20.GL_FRAGMENT_SHADER, "assets/computercraft/shaders/monitor.frag" );
program = GlStateManager.createProgram(); program = GlStateManager.createProgram();
GlStateManager.attachShader( program, vertexShader ); GlStateManager.attachShader(program, vertexShader);
GlStateManager.attachShader( program, fragmentShader ); GlStateManager.attachShader(program, fragmentShader);
GL20.glBindAttribLocation( program, 0, "v_pos" ); GL20.glBindAttribLocation(program, 0, "v_pos");
GlStateManager.linkProgram( program ); GlStateManager.linkProgram(program);
boolean ok = GlStateManager.getProgram( program, GL20.GL_LINK_STATUS ) != 0; boolean ok = GlStateManager.getProgram(program, GL20.GL_LINK_STATUS) != 0;
String log = GlStateManager.getProgramInfoLog( program, Short.MAX_VALUE ).trim(); String log = GlStateManager.getProgramInfoLog(program, Short.MAX_VALUE)
if( !Strings.isNullOrEmpty( log ) ) .trim();
{ if (!Strings.isNullOrEmpty(log)) {
ComputerCraft.log.warn( "Problems when linking monitor shader: {}", log ); ComputerCraft.log.warn("Problems when linking monitor shader: {}", log);
} }
GL20.glDetachShader( program, vertexShader ); GL20.glDetachShader(program, vertexShader);
GL20.glDetachShader( program, fragmentShader ); GL20.glDetachShader(program, fragmentShader);
GlStateManager.deleteShader( vertexShader ); GlStateManager.deleteShader(vertexShader);
GlStateManager.deleteShader( fragmentShader ); GlStateManager.deleteShader(fragmentShader);
if( !ok ) return false; if (!ok) {
return false;
}
uniformMv = getUniformLocation( program, "u_mv" ); uniformMv = getUniformLocation(program, "u_mv");
uniformFont = getUniformLocation( program, "u_font" ); uniformFont = getUniformLocation(program, "u_font");
uniformWidth = getUniformLocation( program, "u_width" ); uniformWidth = getUniformLocation(program, "u_width");
uniformHeight = getUniformLocation( program, "u_height" ); uniformHeight = getUniformLocation(program, "u_height");
uniformTbo = getUniformLocation( program, "u_tbo" ); uniformTbo = getUniformLocation(program, "u_tbo");
uniformPalette = getUniformLocation( program, "u_palette" ); uniformPalette = getUniformLocation(program, "u_palette");
ComputerCraft.log.info( "Loaded monitor shader." ); ComputerCraft.log.info("Loaded monitor shader.");
return true; return true;
} } catch (Exception e) {
catch( Exception e ) ComputerCraft.log.error("Cannot load monitor shaders", e);
{
ComputerCraft.log.error( "Cannot load monitor shaders", e );
return false; return false;
} }
} }
private static int loadShader( int kind, String path ) private static int loadShader(int kind, String path) {
{ InputStream stream = TileEntityMonitorRenderer.class.getClassLoader()
InputStream stream = TileEntityMonitorRenderer.class.getClassLoader().getResourceAsStream( path ); .getResourceAsStream(path);
if( stream == null ) throw new IllegalArgumentException( "Cannot find " + path ); if (stream == null) {
String contents = TextureUtil.readAllToString( stream ); throw new IllegalArgumentException("Cannot find " + path);
}
String contents = TextureUtil.readAllToString(stream);
int shader = GlStateManager.createShader( kind ); int shader = GlStateManager.createShader(kind);
GlStateManager.shaderSource( shader, contents ); GlStateManager.shaderSource(shader, contents);
GlStateManager.compileShader( shader ); GlStateManager.compileShader(shader);
boolean ok = GlStateManager.getShader( shader, GL20.GL_COMPILE_STATUS ) != 0; boolean ok = GlStateManager.getShader(shader, GL20.GL_COMPILE_STATUS) != 0;
String log = GlStateManager.getShaderInfoLog( shader, Short.MAX_VALUE ).trim(); String log = GlStateManager.getShaderInfoLog(shader, Short.MAX_VALUE)
if( !Strings.isNullOrEmpty( log ) ) .trim();
{ if (!Strings.isNullOrEmpty(log)) {
ComputerCraft.log.warn( "Problems when loading monitor shader {}: {}", path, log ); ComputerCraft.log.warn("Problems when loading monitor shader {}: {}", path, log);
} }
if( !ok ) throw new IllegalStateException( "Cannot compile shader " + path ); if (!ok) {
throw new IllegalStateException("Cannot compile shader " + path);
}
return shader; return shader;
} }
private static int getUniformLocation( int program, String name ) private static int getUniformLocation(int program, String name) {
{ int uniform = GlStateManager.getUniformLocation(program, name);
int uniform = GlStateManager.getUniformLocation( program, name ); if (uniform == -1) {
if( uniform == -1 ) throw new IllegalStateException( "Cannot find uniform " + name ); throw new IllegalStateException("Cannot find uniform " + name);
}
return uniform; return uniform;
} }
} }

View File

@@ -3,11 +3,17 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Palette; import dan200.computercraft.shared.util.Palette;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPhase; import net.minecraft.client.render.RenderPhase;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
@@ -15,171 +21,179 @@ import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import org.lwjgl.opengl.GL11;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
public final class PrintoutRenderer
{
private static final Identifier BG = new Identifier( "computercraft", "textures/gui/printout.png" );
private static final float BG_SIZE = 256.0f;
public final class PrintoutRenderer {
/** /**
* Width of a page. * Width of a page.
*/ */
public static final int X_SIZE = 172; public static final int X_SIZE = 172;
/** /**
* Height of a page. * Height of a page.
*/ */
public static final int Y_SIZE = 209; public static final int Y_SIZE = 209;
/** /**
* Padding between the left and right of a page and the text. * Padding between the left and right of a page and the text.
*/ */
public static final int X_TEXT_MARGIN = 13; public static final int X_TEXT_MARGIN = 13;
/** /**
* Padding between the top and bottom of a page and the text. * Padding between the top and bottom of a page and the text.
*/ */
public static final int Y_TEXT_MARGIN = 11; public static final int Y_TEXT_MARGIN = 11;
/**
* Width of the extra page texture.
*/
private static final int X_FOLD_SIZE = 12;
/** /**
* Size of the leather cover. * Size of the leather cover.
*/ */
public static final int COVER_SIZE = 12; public static final int COVER_SIZE = 12;
private static final Identifier BG = new Identifier("computercraft", "textures/gui/printout.png");
private static final float BG_SIZE = 256.0f;
/**
* Width of the extra page texture.
*/
private static final int X_FOLD_SIZE = 12;
private static final int COVER_Y = Y_SIZE; private static final int COVER_Y = Y_SIZE;
private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE; private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE;
private PrintoutRenderer() {} private PrintoutRenderer() {}
public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours ) public static void drawText(Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours) {
{ VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); for (int line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) FixedWidthFontRenderer.drawString(transform,
{ buffer,
FixedWidthFontRenderer.drawString( transform, buffer, x,
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT, y + line * FONT_HEIGHT,
false, 0, 0 text[start + line],
); colours[start + line],
null,
Palette.DEFAULT,
false,
0,
0);
} }
} }
public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, String[] text, String[] colours ) public static void drawText(Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, String[] text, String[] colours) {
{ VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); for (int line = 0; line < LINES_PER_PAGE && line < text.length; line++) {
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) FixedWidthFontRenderer.drawString(transform,
{ buffer,
FixedWidthFontRenderer.drawString( transform, buffer, x,
x, y + line * FONT_HEIGHT, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ), new TextBuffer(text[start + line]),
null, Palette.DEFAULT, false, 0, 0 new TextBuffer(colours[start + line]),
); null,
Palette.DEFAULT,
false,
0,
0);
} }
} }
public static void drawBorder( Matrix4f transform, VertexConsumerProvider renderer, float x, float y, float z, int page, int pages, boolean isBook ) public static void drawBorder(Matrix4f transform, VertexConsumerProvider renderer, float x, float y, float z, int page, int pages, boolean isBook) {
{
int leftPages = page; int leftPages = page;
int rightPages = pages - page - 1; int rightPages = pages - page - 1;
VertexConsumer buffer = renderer.getBuffer( Type.TYPE ); VertexConsumer buffer = renderer.getBuffer(Type.TYPE);
if( isBook ) if (isBook) {
{
// Border // Border
float offset = offsetAt( pages ); float offset = offsetAt(pages);
float left = x - 4 - offset; float left = x - 4 - offset;
float right = x + X_SIZE + offset - 4; float right = x + X_SIZE + offset - 4;
// Left and right border // Left and right border
drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); drawTexture(transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2);
drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); drawTexture(transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2);
// Draw centre panel (just stretched texture, sorry). // Draw centre panel (just stretched texture, sorry).
drawTexture( transform, buffer, drawTexture(transform,
x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE, buffer,
COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE x - offset,
); y,
z - 0.02f,
X_SIZE + offset * 2,
Y_SIZE,
COVER_X + COVER_SIZE / 2.0f,
COVER_SIZE,
COVER_SIZE,
Y_SIZE);
float borderX = left; float borderX = left;
while( borderX < right ) while (borderX < right) {
{ double thisWidth = Math.min(right - borderX, X_SIZE);
double thisWidth = Math.min( right - borderX, X_SIZE ); drawTexture(transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE);
drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE ); drawTexture(transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE);
drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE );
borderX += thisWidth; borderX += thisWidth;
} }
} }
// Left half // Left half
drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE ); drawTexture(transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE);
for( int n = 0; n <= leftPages; n++ ) 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 // Use the left "bold" fold for the outermost page
n == leftPages ? 0 : X_FOLD_SIZE, 0, n == leftPages ? 0 : X_FOLD_SIZE, 0, X_FOLD_SIZE, Y_SIZE);
X_FOLD_SIZE, Y_SIZE
);
} }
// Right half // Right half
drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE ); drawTexture(transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE);
for( int n = 0; n <= rightPages; n++ ) 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. // 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 * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, X_FOLD_SIZE, Y_SIZE);
X_FOLD_SIZE, Y_SIZE
);
} }
} }
private static void drawTexture( Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float u, float v, float width, float height ) public static float offsetAt(int page) {
{ return (float) (32 * (1 - Math.pow(1.2, -page)));
buffer.vertex( matrix, x, y + height, z ).texture( u / BG_SIZE, (v + height) / BG_SIZE ).next();
buffer.vertex( matrix, x + width, y + height, z ).texture( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).next();
buffer.vertex( matrix, x + width, y, z ).texture( (u + width) / BG_SIZE, v / BG_SIZE ).next();
buffer.vertex( matrix, x, y, z ).texture( u / BG_SIZE, v / BG_SIZE ).next();
} }
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 ) private static void drawTexture(Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float u, float v, float width, float height) {
{ buffer.vertex(matrix, x, y + height, z)
buffer.vertex( matrix, x, y + height, z ).texture( u / BG_SIZE, (v + tHeight) / BG_SIZE ).next(); .texture(u / BG_SIZE, (v + height) / BG_SIZE)
buffer.vertex( matrix, x + width, y + height, z ).texture( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).next(); .next();
buffer.vertex( matrix, x + width, y, z ).texture( (u + tWidth) / BG_SIZE, v / BG_SIZE ).next(); buffer.vertex(matrix, x + width, y + height, z)
buffer.vertex( matrix, x, y, z ).texture( u / BG_SIZE, v / BG_SIZE ).next(); .texture((u + width) / BG_SIZE, (v + height) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y, z)
.texture((u + width) / BG_SIZE, v / BG_SIZE)
.next();
buffer.vertex(matrix, x, y, z)
.texture(u / BG_SIZE, v / BG_SIZE)
.next();
} }
public static float offsetAt( int page ) 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) {
return (float) (32 * (1 - Math.pow( 1.2, -page ))); buffer.vertex(matrix, x, y + height, z)
.texture(u / BG_SIZE, (v + tHeight) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y + height, z)
.texture((u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE)
.next();
buffer.vertex(matrix, x + width, y, z)
.texture((u + tWidth) / BG_SIZE, v / BG_SIZE)
.next();
buffer.vertex(matrix, x, y, z)
.texture(u / BG_SIZE, v / BG_SIZE)
.next();
} }
private static final class Type extends RenderPhase private static final class Type extends RenderPhase {
{ static final RenderLayer TYPE = RenderLayer.of("printout_background",
static final RenderLayer TYPE = RenderLayer.of( VertexFormats.POSITION_TEXTURE,
"printout_background", VertexFormats.POSITION_TEXTURE, GL11.GL_QUADS, 1024, GL11.GL_QUADS,
false, false, // useDelegate, needsSorting 1024,
false,
false,
// useDelegate, needsSorting
RenderLayer.MultiPhaseParameters.builder() RenderLayer.MultiPhaseParameters.builder()
.texture( new RenderPhase.Texture( BG, false, false ) ) // blur, minimap .texture(new RenderPhase.Texture(BG, false, false)) // blur, minimap
.alpha( ONE_TENTH_ALPHA ) .alpha(ONE_TENTH_ALPHA)
.lightmap( DISABLE_LIGHTMAP ) .lightmap(DISABLE_LIGHTMAP)
.build( false ) .build(false));
);
private Type( String name, Runnable setup, Runnable destroy ) private Type(String name, Runnable setup, Runnable destroy) {
{ super(name, setup, destroy);
super( name, setup, destroy );
} }
} }
} }

View File

@@ -3,10 +3,18 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.getColour;
import java.nio.ByteBuffer;
import javax.annotation.Nonnull;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@@ -16,6 +24,11 @@ import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.gl.VertexBuffer;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
@@ -31,39 +44,29 @@ import net.minecraft.client.util.math.Vector3f;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import javax.annotation.Nonnull; public class TileEntityMonitorRenderer extends BlockEntityRenderer<TileMonitor> {
import java.nio.ByteBuffer;
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
public class TileEntityMonitorRenderer extends BlockEntityRenderer<TileMonitor>
{
/** /**
* {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between * {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between the monitor frame and contents.
* the monitor frame and contents.
*/ */
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1); private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
private static final Matrix4f IDENTITY = AffineTransformation.identity()
.getMatrix();
private static ByteBuffer tboContents; private static ByteBuffer tboContents;
private static final Matrix4f IDENTITY = AffineTransformation.identity().getMatrix(); public TileEntityMonitorRenderer(BlockEntityRenderDispatcher rendererDispatcher) {
super(rendererDispatcher);
public TileEntityMonitorRenderer( BlockEntityRenderDispatcher rendererDispatcher )
{
super( rendererDispatcher );
} }
@Override @Override
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer, int lightmapCoord, int overlayLight ) public void render(@Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer,
{ int lightmapCoord, int overlayLight) {
// Render from the origin monitor // Render from the origin monitor
ClientMonitor originTerminal = monitor.getClientMonitor(); ClientMonitor originTerminal = monitor.getClientMonitor();
if( originTerminal == null ) return; if (originTerminal == null) {
return;
}
TileMonitor origin = originTerminal.getOrigin(); TileMonitor origin = originTerminal.getOrigin();
BlockPos monitorPos = monitor.getPos(); BlockPos monitorPos = monitor.getPos();
@@ -71,8 +74,7 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer<TileMonitor>
// multiple times in a single frame to ensure compatibility with shaders which may run a // multiple times in a single frame to ensure compatibility with shaders which may run a
// pass multiple times. // pass multiple times.
long renderFrame = FrameInfo.getRenderFrame(); long renderFrame = FrameInfo.getRenderFrame();
if( originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals( originTerminal.lastRenderPos ) ) if (originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals(originTerminal.lastRenderPos)) {
{
return; return;
} }
@@ -85,158 +87,163 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer<TileMonitor>
Direction dir = origin.getDirection(); Direction dir = origin.getDirection();
Direction front = origin.getFront(); Direction front = origin.getFront();
float yaw = dir.asRotation(); float yaw = dir.asRotation();
float pitch = DirectionUtil.toPitchAngle( front ); float pitch = DirectionUtil.toPitchAngle(front);
// Setup initial transform // Setup initial transform
transform.push(); transform.push();
transform.translate( transform.translate(originPos.getX() - monitorPos.getX() + 0.5,
originPos.getX() - monitorPos.getX() + 0.5,
originPos.getY() - monitorPos.getY() + 0.5, originPos.getY() - monitorPos.getY() + 0.5,
originPos.getZ() - monitorPos.getZ() + 0.5 originPos.getZ() - monitorPos.getZ() + 0.5);
);
transform.multiply( Vector3f.NEGATIVE_Y.getDegreesQuaternion( yaw ) ); transform.multiply(Vector3f.NEGATIVE_Y.getDegreesQuaternion(yaw));
transform.multiply( Vector3f.POSITIVE_X.getDegreesQuaternion( pitch ) ); transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(pitch));
transform.translate( transform.translate(-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
-0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN,
origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0, origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0,
0.5 0.5);
);
double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER);
// Draw the contents // Draw the contents
Terminal terminal = originTerminal.getTerminal(); Terminal terminal = originTerminal.getTerminal();
if( terminal != null ) if (terminal != null) {
{
// Draw a terminal // Draw a terminal
int width = terminal.getWidth(), height = terminal.getHeight(); 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;
double xScale = xSize / pixelWidth; double xScale = xSize / pixelWidth;
double yScale = ySize / pixelHeight; double yScale = ySize / pixelHeight;
transform.push(); transform.push();
transform.scale( (float) xScale, (float) -yScale, 1.0f ); transform.scale((float) xScale, (float) -yScale, 1.0f);
Matrix4f matrix = transform.peek().getModel(); Matrix4f matrix = transform.peek()
.getModel();
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate // Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick // render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now. // for now.
VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); VertexConsumer buffer = renderer.getBuffer(FixedWidthFontRenderer.TYPE);
FixedWidthFontRenderer.TYPE.startDrawing(); FixedWidthFontRenderer.TYPE.startDrawing();
renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) ); renderTerminal(matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale));
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is // 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. // reasonable.
FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() ); FixedWidthFontRenderer.drawCursor(matrix, buffer, 0, 0, terminal, !originTerminal.isColour());
transform.pop(); transform.pop();
} } else {
else FixedWidthFontRenderer.drawEmptyTerminal(transform.peek()
{ .getModel(),
FixedWidthFontRenderer.drawEmptyTerminal( renderer,
transform.peek().getModel(), renderer, -MARGIN,
-MARGIN, MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) (float) (xSize + 2 * MARGIN),
); (float) -(ySize + MARGIN * 2));
} }
FixedWidthFontRenderer.drawBlocker( FixedWidthFontRenderer.drawBlocker(transform.peek()
transform.peek().getModel(), renderer, .getModel(),
(float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN, renderer,
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2) (float) -TileMonitor.RENDER_MARGIN,
); (float) TileMonitor.RENDER_MARGIN,
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN),
(float) -(ySize + TileMonitor.RENDER_MARGIN * 2));
transform.pop(); transform.pop();
} }
private static void renderTerminal( Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) private static void renderTerminal(Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin) {
{
Terminal terminal = monitor.getTerminal(); Terminal terminal = monitor.getTerminal();
MonitorRenderer renderType = MonitorRenderer.current(); MonitorRenderer renderType = MonitorRenderer.current();
boolean redraw = monitor.pollTerminalChanged(); boolean redraw = monitor.pollTerminalChanged();
if( monitor.createBuffer( renderType ) ) redraw = true; if (monitor.createBuffer(renderType)) {
redraw = true;
}
switch( renderType ) switch (renderType) {
{ case TBO: {
case TBO: if (!MonitorTextureBufferShader.use()) {
{ return;
if( !MonitorTextureBufferShader.use() ) return; }
int width = terminal.getWidth(), height = terminal.getHeight(); 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 ) if (redraw) {
{
int size = width * height * 3; int size = width * height * 3;
if( tboContents == null || tboContents.capacity() < size ) if (tboContents == null || tboContents.capacity() < size) {
{ tboContents = GlAllocationUtils.allocateByteBuffer(size);
tboContents = GlAllocationUtils.allocateByteBuffer( size );
} }
ByteBuffer monitorBuffer = tboContents; ByteBuffer monitorBuffer = tboContents;
monitorBuffer.clear(); monitorBuffer.clear();
for( int y = 0; y < height; y++ ) for (int y = 0; y < height; y++) {
{ TextBuffer text = terminal.getLine(y), textColour = terminal.getTextColourLine(y), background = terminal.getBackgroundColourLine(y);
TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y ); for (int x = 0; x < width; x++) {
for( int x = 0; x < width; x++ ) monitorBuffer.put((byte) (text.charAt(x) & 0xFF));
{ monitorBuffer.put((byte) getColour(textColour.charAt(x), Colour.WHITE));
monitorBuffer.put( (byte) (text.charAt( x ) & 0xFF) ); monitorBuffer.put((byte) getColour(background.charAt(x), Colour.BLACK));
monitorBuffer.put( (byte) getColour( textColour.charAt( x ), Colour.WHITE ) );
monitorBuffer.put( (byte) getColour( background.charAt( x ), Colour.BLACK ) );
} }
} }
monitorBuffer.flip(); monitorBuffer.flip();
GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer ); GlStateManager.bindBuffers(GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer);
GlStateManager.bufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW ); GlStateManager.bufferData(GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW);
GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, 0 ); GlStateManager.bindBuffers(GL31.GL_TEXTURE_BUFFER, 0);
} }
// Nobody knows what they're doing! // Nobody knows what they're doing!
GlStateManager.activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX ); GlStateManager.activeTexture(MonitorTextureBufferShader.TEXTURE_INDEX);
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture ); GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, monitor.tboTexture);
GlStateManager.activeTexture( GL13.GL_TEXTURE0 ); GlStateManager.activeTexture(GL13.GL_TEXTURE0);
MonitorTextureBufferShader.setupUniform( matrix, width, height, terminal.getPalette(), !monitor.isColour() ); MonitorTextureBufferShader.setupUniform(matrix, width, height, terminal.getPalette(), !monitor.isColour());
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer(); BufferBuilder buffer = tessellator.getBuffer();
buffer.begin( GL11.GL_TRIANGLE_STRIP, VertexFormats.POSITION ); buffer.begin(GL11.GL_TRIANGLE_STRIP, VertexFormats.POSITION);
buffer.vertex( -xMargin, -yMargin, 0 ).next(); buffer.vertex(-xMargin, -yMargin, 0)
buffer.vertex( -xMargin, pixelHeight + yMargin, 0 ).next(); .next();
buffer.vertex( pixelWidth + xMargin, -yMargin, 0 ).next(); buffer.vertex(-xMargin, pixelHeight + yMargin, 0)
buffer.vertex( pixelWidth + xMargin, pixelHeight + yMargin, 0 ).next(); .next();
buffer.vertex(pixelWidth + xMargin, -yMargin, 0)
.next();
buffer.vertex(pixelWidth + xMargin, pixelHeight + yMargin, 0)
.next();
tessellator.draw(); tessellator.draw();
GlStateManager.useProgram( 0 ); GlStateManager.useProgram(0);
break; break;
} }
case VBO: case VBO: {
{
VertexBuffer vbo = monitor.buffer; VertexBuffer vbo = monitor.buffer;
if( redraw ) if (redraw) {
{
Tessellator tessellator = Tessellator.getInstance(); Tessellator tessellator = Tessellator.getInstance();
BufferBuilder builder = tessellator.getBuffer(); BufferBuilder builder = tessellator.getBuffer();
builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() ); builder.begin(FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat());
FixedWidthFontRenderer.drawTerminalWithoutCursor( FixedWidthFontRenderer.drawTerminalWithoutCursor(IDENTITY,
IDENTITY, builder, 0, 0, builder,
terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin 0,
); 0,
terminal,
!monitor.isColour(),
yMargin,
yMargin,
xMargin,
xMargin);
builder.end(); builder.end();
vbo.upload( builder ); vbo.upload(builder);
} }
vbo.bind(); vbo.bind();
FixedWidthFontRenderer.TYPE.getVertexFormat().startDrawing( 0L ); FixedWidthFontRenderer.TYPE.getVertexFormat()
vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() ); .startDrawing(0L);
vbo.draw(matrix, FixedWidthFontRenderer.TYPE.getDrawMode());
VertexBuffer.unbind(); VertexBuffer.unbind();
FixedWidthFontRenderer.TYPE.getVertexFormat().endDrawing(); FixedWidthFontRenderer.TYPE.getVertexFormat()
.endDrawing();
break; break;
} }
} }

View File

@@ -3,8 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
@@ -13,6 +19,7 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil; import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer; import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.render.TexturedRenderLayers; import net.minecraft.client.render.TexturedRenderLayers;
@@ -33,28 +40,20 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import javax.annotation.Nonnull; public class TileEntityTurtleRenderer extends BlockEntityRenderer<TileTurtle> {
import java.util.List; private static final ModelIdentifier NORMAL_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_normal", "inventory");
import java.util.Random; private static final ModelIdentifier ADVANCED_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_advanced", "inventory");
private static final ModelIdentifier COLOUR_TURTLE_MODEL = new ModelIdentifier("computercraft:turtle_colour", "inventory");
private static final ModelIdentifier ELF_OVERLAY_MODEL = new ModelIdentifier("computercraft:turtle_elf_overlay", "inventory");
public class TileEntityTurtleRenderer extends BlockEntityRenderer<TileTurtle> private final Random random = new Random(0);
{
private static final ModelIdentifier NORMAL_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_normal", "inventory" );
private static final ModelIdentifier ADVANCED_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_advanced", "inventory" );
private static final ModelIdentifier COLOUR_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_colour", "inventory" );
private static final ModelIdentifier ELF_OVERLAY_MODEL = new ModelIdentifier( "computercraft:turtle_elf_overlay", "inventory" );
private final Random random = new Random( 0 ); public TileEntityTurtleRenderer(BlockEntityRenderDispatcher renderDispatcher) {
super(renderDispatcher);
public TileEntityTurtleRenderer( BlockEntityRenderDispatcher renderDispatcher )
{
super( renderDispatcher );
} }
public static ModelIdentifier getTurtleModel( ComputerFamily family, boolean coloured ) public static ModelIdentifier getTurtleModel(ComputerFamily family, boolean coloured) {
{ switch (family) {
switch( family )
{
case NORMAL: case NORMAL:
default: default:
return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL; return coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
@@ -63,34 +62,78 @@ public class TileEntityTurtleRenderer extends BlockEntityRenderer<TileTurtle>
} }
} }
public static ModelIdentifier getTurtleOverlayModel( Identifier overlay, boolean christmas ) public static ModelIdentifier getTurtleOverlayModel(Identifier overlay, boolean christmas) {
{ if (overlay != null) {
if( overlay != null ) return new ModelIdentifier( overlay, "inventory" ); return new ModelIdentifier(overlay, "inventory");
if( christmas ) return ELF_OVERLAY_MODEL; }
if (christmas) {
return ELF_OVERLAY_MODEL;
}
return null; return null;
} }
private static void renderQuads(@Nonnull MatrixStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight,
List<BakedQuad> quads, int[] tints) {
MatrixStack.Entry matrix = transform.peek();
for (BakedQuad bakedquad : quads) {
int tint = -1;
if (tints != null && bakedquad.hasColor()) {
int idx = bakedquad.getColorIndex();
if (idx >= 0 && idx < tints.length) {
tint = tints[bakedquad.getColorIndex()];
}
}
float f = (float) (tint >> 16 & 255) / 255.0F;
float f1 = (float) (tint >> 8 & 255) / 255.0F;
float f2 = (float) (tint & 255) / 255.0F;
buffer.quad(matrix,
bakedquad,
new float[] {
1.0F,
1.0F,
1.0F,
1.0F
},
f,
f1,
f2,
new int[] {
lightmapCoord,
lightmapCoord,
lightmapCoord,
lightmapCoord
},
overlayLight,
true);
}
}
@Override @Override
public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer, int lightmapCoord, int overlayLight ) public void render(@Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer,
{ int lightmapCoord, int overlayLight) {
// Render the label // Render the label
String label = turtle.createProxy().getLabel(); String label = turtle.createProxy()
HitResult hit = dispatcher.crosshairTarget; .getLabel();
if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos().equals( ((BlockHitResult) hit).getBlockPos() ) ) HitResult hit = this.dispatcher.crosshairTarget;
{ if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos()
.equals(((BlockHitResult) hit).getBlockPos())) {
MinecraftClient mc = MinecraftClient.getInstance(); MinecraftClient mc = MinecraftClient.getInstance();
TextRenderer font = mc.textRenderer; TextRenderer font = mc.textRenderer;
transform.push(); transform.push();
transform.translate( 0.5, 1.2, 0.5 ); transform.translate(0.5, 1.2, 0.5);
transform.multiply( mc.getEntityRenderDispatcher().getRotation() ); transform.multiply(mc.getEntityRenderDispatcher()
transform.scale( -0.025f, -0.025f, 0.025f ); .getRotation());
transform.scale(-0.025f, -0.025f, 0.025f);
Matrix4f matrix = transform.peek().getModel(); Matrix4f matrix = transform.peek()
int opacity = (int) (mc.options.getTextBackgroundOpacity( 0.25f ) * 255) << 24; .getModel();
float width = -font.getWidth( label ) / 2.0f; int opacity = (int) (mc.options.getTextBackgroundOpacity(0.25f) * 255) << 24;
font.draw( label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord ); float width = -font.getWidth(label) / 2.0f;
font.draw( label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord ); font.draw(label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord);
font.draw(label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord);
transform.pop(); transform.pop();
} }
@@ -98,93 +141,75 @@ public class TileEntityTurtleRenderer extends BlockEntityRenderer<TileTurtle>
transform.push(); transform.push();
// Setup the transform. // Setup the transform.
Vec3d offset = turtle.getRenderOffset( partialTicks ); Vec3d offset = turtle.getRenderOffset(partialTicks);
float yaw = turtle.getRenderYaw( partialTicks ); float yaw = turtle.getRenderYaw(partialTicks);
transform.translate( offset.x, offset.y, offset.z ); transform.translate(offset.x, offset.y, offset.z);
transform.translate( 0.5f, 0.5f, 0.5f ); transform.translate(0.5f, 0.5f, 0.5f);
transform.multiply( Vector3f.POSITIVE_Y.getDegreesQuaternion( 180.0f - yaw ) ); transform.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180.0f - yaw));
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) ) if (label != null && (label.equals("Dinnerbone") || label.equals("Grumm"))) {
{
// Flip the model // Flip the model
transform.scale( 1.0f, -1.0f, 1.0f ); transform.scale(1.0f, -1.0f, 1.0f);
} }
transform.translate( -0.5f, -0.5f, -0.5f ); transform.translate(-0.5f, -0.5f, -0.5f);
// Render the turtle // Render the turtle
int colour = turtle.getColour(); int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily(); ComputerFamily family = turtle.getFamily();
Identifier overlay = turtle.getOverlay(); Identifier overlay = turtle.getOverlay();
VertexConsumer buffer = renderer.getBuffer( TexturedRenderLayers.getEntityTranslucentCull() ); VertexConsumer buffer = renderer.getBuffer(TexturedRenderLayers.getEntityTranslucentCull());
renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } ); this.renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[] {colour});
// Render the overlay // Render the overlay
ModelIdentifier overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS ); ModelIdentifier overlayModel = getTurtleOverlayModel(overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS);
if( overlayModel != null ) if (overlayModel != null) {
{ this.renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null);
renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null );
} }
// Render the upgrades // Render the upgrades
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks ); this.renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks ); this.renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
transform.pop(); transform.pop();
} }
private void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, TurtleSide side, float f ) private void renderUpgrade(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle,
{ TurtleSide side, float f) {
ITurtleUpgrade upgrade = turtle.getUpgrade( side ); ITurtleUpgrade upgrade = turtle.getUpgrade(side);
if( upgrade == null ) return; if (upgrade == null) {
return;
}
transform.push(); transform.push();
float toolAngle = turtle.getToolRenderAngle( side, f ); float toolAngle = turtle.getToolRenderAngle(side, f);
transform.translate( 0.0f, 0.5f, 0.5f ); transform.translate(0.0f, 0.5f, 0.5f);
transform.multiply( Vector3f.NEGATIVE_X.getDegreesQuaternion( toolAngle ) ); transform.multiply(Vector3f.NEGATIVE_X.getDegreesQuaternion(toolAngle));
transform.translate( 0.0f, -0.5f, -0.5f ); transform.translate(0.0f, -0.5f, -0.5f);
TransformedModel model = upgrade.getModel( turtle.getAccess(), side ); TransformedModel model = upgrade.getModel(turtle.getAccess(), side);
model.push(transform); model.push(transform);
renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null ); this.renderModel(transform, renderer, lightmapCoord, overlayLight, model.getModel(), null);
transform.pop(); transform.pop();
transform.pop(); transform.pop();
} }
private void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, ModelIdentifier modelLocation, int[] tints ) private void renderModel(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight,
{ ModelIdentifier modelLocation, int[] tints) {
BakedModelManager modelManager = MinecraftClient.getInstance().getItemRenderer().getModels().getModelManager(); BakedModelManager modelManager = MinecraftClient.getInstance()
renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints ); .getItemRenderer()
.getModels()
.getModelManager();
this.renderModel(transform, renderer, lightmapCoord, overlayLight, modelManager.getModel(modelLocation), tints);
} }
private void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, int[] tints ) private void renderModel(@Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model,
{ int[] tints) {
random.setSeed( 0 ); this.random.setSeed(0);
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random ), tints ); renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, this.random), tints);
for( Direction facing : DirectionUtil.FACINGS ) for (Direction facing : DirectionUtil.FACINGS) {
{ renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, this.random), tints);
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random ), tints );
}
}
private static void renderQuads( @Nonnull MatrixStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, int[] tints )
{
MatrixStack.Entry matrix = transform.peek();
for( BakedQuad bakedquad : quads )
{
int tint = -1;
if( tints != null && bakedquad.hasColor() )
{
int idx = bakedquad.getColorIndex();
if( idx >= 0 && idx < tints.length ) tint = tints[bakedquad.getColorIndex()];
}
float f = (float) (tint >> 16 & 255) / 255.0F;
float f1 = (float) (tint >> 8 & 255) / 255.0F;
float f2 = (float) (tint & 255) / 255.0F;
buffer.quad( matrix, bakedquad, new float[]{1.0F, 1.0F, 1.0F, 1.0F}, f, f1, f2, new int[] {lightmapCoord, lightmapCoord, lightmapCoord, lightmapCoord}, overlayLight, true );
} }
} }
} }

View File

@@ -13,13 +13,10 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelLoader; import net.minecraft.client.render.model.ModelLoader;
@@ -28,7 +25,10 @@ import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@Environment(EnvType.CLIENT) import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment (EnvType.CLIENT)
public final class TurtleModelLoader { public final class TurtleModelLoader {
public static final TurtleModelLoader INSTANCE = new TurtleModelLoader(); public static final TurtleModelLoader INSTANCE = new TurtleModelLoader();
private static final Identifier NORMAL_TURTLE_MODEL = new Identifier(ComputerCraft.MOD_ID, "block/turtle_normal"); private static final Identifier NORMAL_TURTLE_MODEL = new Identifier(ComputerCraft.MOD_ID, "block/turtle_normal");
@@ -66,8 +66,10 @@ public final class TurtleModelLoader {
private TurtleModel(Identifier family) {this.family = family;} private TurtleModel(Identifier family) {this.family = family;}
@Override @Override
public Collection<SpriteIdentifier> getTextureDependencies(Function<Identifier, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) { public Collection<SpriteIdentifier> getTextureDependencies(Function<Identifier, UnbakedModel> modelGetter,
return this.getModelDependencies().stream() Set<Pair<String, String>> missingTextureErrors) {
return this.getModelDependencies()
.stream()
.flatMap(x -> modelGetter.apply(x) .flatMap(x -> modelGetter.apply(x)
.getTextureDependencies(modelGetter, missingTextureErrors) .getTextureDependencies(modelGetter, missingTextureErrors)
.stream()) .stream())
@@ -81,7 +83,8 @@ public final class TurtleModelLoader {
} }
@Override @Override
public BakedModel bake(@Nonnull ModelLoader loader, @Nonnull Function<SpriteIdentifier, Sprite> spriteGetter, @Nonnull ModelBakeSettings state, Identifier modelId) { public BakedModel bake(@Nonnull ModelLoader loader, @Nonnull Function<SpriteIdentifier, Sprite> spriteGetter, @Nonnull ModelBakeSettings state,
Identifier modelId) {
return new TurtleSmartItemModel(loader.getOrLoadModel(this.family) return new TurtleSmartItemModel(loader.getOrLoadModel(this.family)
.bake(loader, spriteGetter, state, modelId), .bake(loader, spriteGetter, state, modelId),
loader.getOrLoadModel(COLOUR_TURTLE_MODEL) loader.getOrLoadModel(COLOUR_TURTLE_MODEL)

View File

@@ -3,11 +3,19 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.annotation.Nonnull;
import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.client.TransformedModel;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
@@ -16,114 +24,106 @@ import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.math.AffineTransformation; import net.minecraft.client.util.math.AffineTransformation;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import javax.annotation.Nonnull; import net.fabricmc.api.EnvType;
import java.util.*; import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
public class TurtleMultiModel implements BakedModel public class TurtleMultiModel implements BakedModel {
{
private final BakedModel m_baseModel; private final BakedModel m_baseModel;
private final BakedModel m_overlayModel; private final BakedModel m_overlayModel;
private final AffineTransformation m_generalTransform; private final AffineTransformation m_generalTransform;
private final TransformedModel m_leftUpgradeModel; private final TransformedModel m_leftUpgradeModel;
private final TransformedModel m_rightUpgradeModel; private final TransformedModel m_rightUpgradeModel;
private List<BakedQuad> m_generalQuads = null; private List<BakedQuad> m_generalQuads = null;
private Map<Direction, List<BakedQuad>> m_faceQuads = new EnumMap<>( Direction.class ); private Map<Direction, List<BakedQuad>> m_faceQuads = new EnumMap<>(Direction.class);
public TurtleMultiModel( BakedModel baseModel, BakedModel overlayModel, AffineTransformation generalTransform, TransformedModel leftUpgradeModel, TransformedModel rightUpgradeModel ) public TurtleMultiModel(BakedModel baseModel, BakedModel overlayModel, AffineTransformation generalTransform, TransformedModel leftUpgradeModel,
{ TransformedModel rightUpgradeModel) {
// Get the models // Get the models
m_baseModel = baseModel; this.m_baseModel = baseModel;
m_overlayModel = overlayModel; this.m_overlayModel = overlayModel;
m_leftUpgradeModel = leftUpgradeModel; this.m_leftUpgradeModel = leftUpgradeModel;
m_rightUpgradeModel = rightUpgradeModel; this.m_rightUpgradeModel = rightUpgradeModel;
m_generalTransform = generalTransform; this.m_generalTransform = generalTransform;
} }
@Nonnull @Nonnull
@Override @Override
public List<BakedQuad> getQuads( BlockState state, Direction side, @Nonnull Random rand ) public List<BakedQuad> getQuads(BlockState state, Direction side, @Nonnull Random rand) {
{ if (side != null) {
if( side != null ) if (!this.m_faceQuads.containsKey(side)) {
{ this.m_faceQuads.put(side, this.buildQuads(state, side, rand));
if( !m_faceQuads.containsKey( side ) ) m_faceQuads.put( side, buildQuads( state, side, rand ) );
return m_faceQuads.get( side );
} }
else return this.m_faceQuads.get(side);
{ } else {
if( m_generalQuads == null ) m_generalQuads = buildQuads( state, side, rand ); if (this.m_generalQuads == null) {
return m_generalQuads; this.m_generalQuads = this.buildQuads(state, side, rand);
}
return this.m_generalQuads;
} }
} }
private List<BakedQuad> buildQuads( BlockState state, Direction side, Random rand ) private List<BakedQuad> buildQuads(BlockState state, Direction side, Random rand) {
{
ArrayList<BakedQuad> quads = new ArrayList<>(); ArrayList<BakedQuad> quads = new ArrayList<>();
ModelTransformer.transformQuadsTo( quads, m_baseModel.getQuads( state, side, rand ), m_generalTransform.getMatrix() ); ModelTransformer.transformQuadsTo(quads, this.m_baseModel.getQuads(state, side, rand), this.m_generalTransform.getMatrix());
if( m_overlayModel != null ) if (this.m_overlayModel != null) {
{ ModelTransformer.transformQuadsTo(quads, this.m_overlayModel.getQuads(state, side, rand), this.m_generalTransform.getMatrix());
ModelTransformer.transformQuadsTo( quads, m_overlayModel.getQuads( state, side, rand ), m_generalTransform.getMatrix() );
} }
if( m_leftUpgradeModel != null ) if (this.m_leftUpgradeModel != null) {
{ AffineTransformation upgradeTransform = this.m_generalTransform.multiply(this.m_leftUpgradeModel.getMatrix());
AffineTransformation upgradeTransform = m_generalTransform.multiply( m_leftUpgradeModel.getMatrix() ); ModelTransformer.transformQuadsTo(quads, this.m_leftUpgradeModel.getModel()
ModelTransformer.transformQuadsTo( quads, m_leftUpgradeModel.getModel().getQuads( state, side, rand ), upgradeTransform.getMatrix() ); .getQuads(state, side, rand),
upgradeTransform.getMatrix());
} }
if( m_rightUpgradeModel != null ) if (this.m_rightUpgradeModel != null) {
{ AffineTransformation upgradeTransform = this.m_generalTransform.multiply(this.m_rightUpgradeModel.getMatrix());
AffineTransformation upgradeTransform = m_generalTransform.multiply( m_rightUpgradeModel.getMatrix() ); ModelTransformer.transformQuadsTo(quads, this.m_rightUpgradeModel.getModel()
ModelTransformer.transformQuadsTo( quads, m_rightUpgradeModel.getModel().getQuads( state, side, rand ), upgradeTransform.getMatrix() ); .getQuads(state, side, rand),
upgradeTransform.getMatrix());
} }
quads.trimToSize(); quads.trimToSize();
return quads; return quads;
} }
@Override @Override
public boolean useAmbientOcclusion() public boolean useAmbientOcclusion() {
{ return this.m_baseModel.useAmbientOcclusion();
return m_baseModel.useAmbientOcclusion();
} }
@Override @Override
public boolean hasDepth() public boolean hasDepth() {
{ return this.m_baseModel.hasDepth();
return m_baseModel.hasDepth();
} }
@Override @Override
public boolean isBuiltin() public boolean isSideLit() {
{ return this.m_baseModel.isSideLit();
return m_baseModel.isBuiltin();
} }
@Override @Override
public boolean isSideLit() public boolean isBuiltin() {
{ return this.m_baseModel.isBuiltin();
return m_baseModel.isSideLit();
} }
@Nonnull @Nonnull
@Override @Override
@Deprecated @Deprecated
public Sprite getSprite() public Sprite getSprite() {
{ return this.m_baseModel.getSprite();
return m_baseModel.getSprite();
} }
@Nonnull @Nonnull
@Override @Override
@Deprecated @Deprecated
public net.minecraft.client.render.model.json.ModelTransformation getTransformation() public net.minecraft.client.render.model.json.ModelTransformation getTransformation() {
{ return this.m_baseModel.getTransformation();
return m_baseModel.getTransformation();
} }
@Nonnull @Nonnull
@Override @Override
public ModelOverrideList getOverrides() public ModelOverrideList getOverrides() {
{
return ModelOverrideList.EMPTY; return ModelOverrideList.EMPTY;
} }
} }

View File

@@ -6,36 +6,35 @@
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry; import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer> import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
{
public TurtlePlayerRenderer( EntityRenderDispatcher renderManager ) public class TurtlePlayerRenderer extends EntityRenderer<TurtlePlayer> {
{ public TurtlePlayerRenderer(EntityRenderDispatcher renderManager) {
super( renderManager ); super(renderManager);
} }
public TurtlePlayerRenderer(EntityRenderDispatcher entityRenderDispatcher, EntityRendererRegistry.Context context) { public TurtlePlayerRenderer(EntityRenderDispatcher entityRenderDispatcher, EntityRendererRegistry.Context context) {
super(entityRenderDispatcher); super(entityRenderDispatcher);
} }
@Nonnull
@Override @Override
public Identifier getTexture( @Nonnull TurtlePlayer entity ) public void render(@Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull MatrixStack transform,
{ @Nonnull VertexConsumerProvider buffer, int packedLightIn) {
return ComputerBorderRenderer.BACKGROUND_NORMAL;
} }
@Nonnull
@Override @Override
public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider buffer, int packedLightIn ) public Identifier getTexture(@Nonnull TurtlePlayer entity) {
{ return ComputerBorderRenderer.BACKGROUND_NORMAL;
} }
} }

View File

@@ -3,8 +3,16 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.client.render; package dan200.computercraft.client.render;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
@@ -12,8 +20,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.turtle.items.ItemTurtle; import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil; import dan200.computercraft.shared.util.HolidayUtil;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
@@ -31,29 +38,118 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import javax.annotation.Nonnull; import net.fabricmc.api.EnvType;
import javax.annotation.Nullable; import net.fabricmc.api.Environment;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
@Environment(EnvType.CLIENT) @Environment (EnvType.CLIENT)
public class TurtleSmartItemModel implements BakedModel public class TurtleSmartItemModel implements BakedModel {
{
private static final AffineTransformation identity, flip; private static final AffineTransformation identity, flip;
static static {
{
MatrixStack stack = new MatrixStack(); MatrixStack stack = new MatrixStack();
stack.scale( 0, -1, 0 ); stack.scale(0, -1, 0);
stack.translate( 0, 0, 1 ); stack.translate(0, 0, 1);
identity = AffineTransformation.identity(); identity = AffineTransformation.identity();
flip = new AffineTransformation( stack.peek().getModel() ); flip = new AffineTransformation(stack.peek()
.getModel());
} }
private static class TurtleModelCombination private final BakedModel familyModel;
{ private final BakedModel colourModel;
private final HashMap<TurtleModelCombination, BakedModel> m_cachedModels = new HashMap<>();
private final ModelOverrideList m_overrides;
public TurtleSmartItemModel(BakedModel familyModel, BakedModel colourModel) {
this.familyModel = familyModel;
this.colourModel = colourModel;
this.m_overrides = new ModelOverrideList() {
@Nonnull
@Override
public BakedModel apply(@Nonnull BakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientWorld world,
@Nullable LivingEntity entity) {
ItemTurtle turtle = (ItemTurtle) stack.getItem();
int colour = turtle.getColour(stack);
ITurtleUpgrade leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT);
ITurtleUpgrade rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT);
Identifier overlay = turtle.getOverlay(stack);
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS;
String label = turtle.getLabel(stack);
boolean flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
TurtleModelCombination combo = new TurtleModelCombination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
BakedModel model = TurtleSmartItemModel.this.m_cachedModels.get(combo);
if (model == null) {
TurtleSmartItemModel.this.m_cachedModels.put(combo, model = TurtleSmartItemModel.this.buildModel(combo));
}
return model;
}
};
}
private BakedModel buildModel(TurtleModelCombination combo) {
MinecraftClient mc = MinecraftClient.getInstance();
BakedModelManager modelManager = mc.getItemRenderer()
.getModels()
.getModelManager();
ModelIdentifier overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel(combo.m_overlay, combo.m_christmas);
BakedModel baseModel = combo.m_colour ? this.colourModel : this.familyModel;
BakedModel overlayModel = overlayModelLocation != null ? modelManager.getModel(overlayModelLocation) : null;
AffineTransformation transform = combo.m_flip ? flip : identity;
TransformedModel leftModel = combo.m_leftUpgrade != null ? combo.m_leftUpgrade.getModel(null, TurtleSide.LEFT) : null;
TransformedModel rightModel = combo.m_rightUpgrade != null ? combo.m_rightUpgrade.getModel(null, TurtleSide.RIGHT) : null;
return new TurtleMultiModel(baseModel, overlayModel, transform, leftModel, rightModel);
}
@Nonnull
@Override
@Deprecated
public List<BakedQuad> getQuads(BlockState state, Direction facing, @Nonnull Random rand) {
return this.familyModel.getQuads(state, facing, rand);
}
@Override
public boolean useAmbientOcclusion() {
return this.familyModel.useAmbientOcclusion();
}
@Override
public boolean hasDepth() {
return this.familyModel.hasDepth();
}
@Override
public boolean isSideLit() {
return this.familyModel.isSideLit();
}
@Override
public boolean isBuiltin() {
return this.familyModel.isBuiltin();
}
@Nonnull
@Override
@Deprecated
public Sprite getSprite() {
return this.familyModel.getSprite();
}
@Nonnull
@Override
@Deprecated
public ModelTransformation getTransformation() {
return this.familyModel.getTransformation();
}
@Nonnull
@Override
public ModelOverrideList getOverrides() {
return this.m_overrides;
}
private static class TurtleModelCombination {
final boolean m_colour; final boolean m_colour;
final ITurtleUpgrade m_leftUpgrade; final ITurtleUpgrade m_leftUpgrade;
final ITurtleUpgrade m_rightUpgrade; final ITurtleUpgrade m_rightUpgrade;
@@ -61,147 +157,42 @@ public class TurtleSmartItemModel implements BakedModel
final boolean m_christmas; final boolean m_christmas;
final boolean m_flip; final boolean m_flip;
TurtleModelCombination( boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, Identifier overlay, boolean christmas, boolean flip ) TurtleModelCombination(boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, Identifier overlay, boolean christmas,
{ boolean flip) {
m_colour = colour; this.m_colour = colour;
m_leftUpgrade = leftUpgrade; this.m_leftUpgrade = leftUpgrade;
m_rightUpgrade = rightUpgrade; this.m_rightUpgrade = rightUpgrade;
m_overlay = overlay; this.m_overlay = overlay;
m_christmas = christmas; this.m_christmas = christmas;
m_flip = flip; this.m_flip = flip;
} }
@Override @Override
public boolean equals( Object other ) public int hashCode() {
{
if( other == this ) return true;
if( !(other instanceof TurtleModelCombination) ) return false;
TurtleModelCombination otherCombo = (TurtleModelCombination) other;
return otherCombo.m_colour == m_colour &&
otherCombo.m_leftUpgrade == m_leftUpgrade &&
otherCombo.m_rightUpgrade == m_rightUpgrade &&
Objects.equal( otherCombo.m_overlay, m_overlay ) &&
otherCombo.m_christmas == m_christmas &&
otherCombo.m_flip == m_flip;
}
@Override
public int hashCode()
{
final int prime = 31; final int prime = 31;
int result = 0; int result = 0;
result = prime * result + (m_colour ? 1 : 0); result = prime * result + (this.m_colour ? 1 : 0);
result = prime * result + (m_leftUpgrade != null ? m_leftUpgrade.hashCode() : 0); result = prime * result + (this.m_leftUpgrade != null ? this.m_leftUpgrade.hashCode() : 0);
result = prime * result + (m_rightUpgrade != null ? m_rightUpgrade.hashCode() : 0); result = prime * result + (this.m_rightUpgrade != null ? this.m_rightUpgrade.hashCode() : 0);
result = prime * result + (m_overlay != null ? m_overlay.hashCode() : 0); result = prime * result + (this.m_overlay != null ? this.m_overlay.hashCode() : 0);
result = prime * result + (m_christmas ? 1 : 0); result = prime * result + (this.m_christmas ? 1 : 0);
result = prime * result + (m_flip ? 1 : 0); result = prime * result + (this.m_flip ? 1 : 0);
return result; return result;
} }
}
private final BakedModel familyModel;
private final BakedModel colourModel;
private final HashMap<TurtleModelCombination, BakedModel> m_cachedModels = new HashMap<>();
private final ModelOverrideList m_overrides;
public TurtleSmartItemModel( BakedModel familyModel, BakedModel colourModel )
{
this.familyModel = familyModel;
this.colourModel = colourModel;
m_overrides = new ModelOverrideList()
{
@Nonnull
@Override
public BakedModel apply( @Nonnull BakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity )
{
ItemTurtle turtle = (ItemTurtle) stack.getItem();
int colour = turtle.getColour( stack );
ITurtleUpgrade leftUpgrade = turtle.getUpgrade( stack, TurtleSide.LEFT );
ITurtleUpgrade rightUpgrade = turtle.getUpgrade( stack, TurtleSide.RIGHT );
Identifier overlay = turtle.getOverlay( stack );
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS;
String label = turtle.getLabel( stack );
boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" ));
TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip );
BakedModel model = m_cachedModels.get( combo );
if( model == null ) m_cachedModels.put( combo, model = buildModel( combo ) );
return model;
}
};
}
@Nonnull
@Override
public ModelOverrideList getOverrides()
{
return m_overrides;
}
private BakedModel buildModel( TurtleModelCombination combo )
{
MinecraftClient mc = MinecraftClient.getInstance();
BakedModelManager modelManager = mc.getItemRenderer().getModels().getModelManager();
ModelIdentifier overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.m_overlay, combo.m_christmas );
BakedModel baseModel = combo.m_colour ? colourModel : familyModel;
BakedModel overlayModel = overlayModelLocation != null ? modelManager.getModel( overlayModelLocation ) : null;
AffineTransformation transform = combo.m_flip ? flip : identity;
TransformedModel leftModel = combo.m_leftUpgrade != null ? combo.m_leftUpgrade.getModel( null, TurtleSide.LEFT ) : null;
TransformedModel rightModel = combo.m_rightUpgrade != null ? combo.m_rightUpgrade.getModel( null, TurtleSide.RIGHT ) : null;
return new TurtleMultiModel( baseModel, overlayModel, transform, leftModel, rightModel );
}
@Nonnull
@Override
@Deprecated
public List<BakedQuad> getQuads( BlockState state, Direction facing, @Nonnull Random rand )
{
return familyModel.getQuads( state, facing, rand );
}
@Override @Override
public boolean useAmbientOcclusion() public boolean equals(Object other) {
{ if (other == this) {
return familyModel.useAmbientOcclusion(); return true;
}
if (!(other instanceof TurtleModelCombination)) {
return false;
} }
@Override TurtleModelCombination otherCombo = (TurtleModelCombination) other;
public boolean hasDepth() return otherCombo.m_colour == this.m_colour && otherCombo.m_leftUpgrade == this.m_leftUpgrade && otherCombo.m_rightUpgrade == this.m_rightUpgrade && Objects.equal(
{ otherCombo.m_overlay, this.m_overlay) && otherCombo.m_christmas == this.m_christmas && otherCombo.m_flip == this.m_flip;
return familyModel.hasDepth();
} }
@Override
public boolean isBuiltin()
{
return familyModel.isBuiltin();
}
@Override
public boolean isSideLit()
{
return familyModel.isSideLit();
}
@Nonnull
@Override
@Deprecated
public Sprite getSprite()
{
return familyModel.getSprite();
}
@Nonnull
@Override
@Deprecated
public ModelTransformation getTransformation()
{
return familyModel.getTransformation();
} }
} }

View File

@@ -22,6 +22,7 @@ import dan200.computercraft.ComputerCraft;
public class AddressPredicate { public class AddressPredicate {
private final List<Pattern> wildcards; private final List<Pattern> wildcards;
private final List<HostRange> ranges; private final List<HostRange> ranges;
public AddressPredicate(String... filters) { public AddressPredicate(String... filters) {
this(Arrays.asList(filters)); this(Arrays.asList(filters));
} }
@@ -95,7 +96,8 @@ public class AddressPredicate {
} }
// If we're an IPv4 address in disguise then let's check that. // If we're an IPv4 address in disguise then let's check that.
return address instanceof Inet6Address && InetAddresses.is6to4Address((Inet6Address) address) && this.matchesAddress(InetAddresses.get6to4IPv4Address((Inet6Address) address)); return address instanceof Inet6Address && InetAddresses.is6to4Address((Inet6Address) address) && this.matchesAddress(InetAddresses.get6to4IPv4Address(
(Inet6Address) address));
} }

View File

@@ -3,33 +3,30 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import javax.annotation.Nonnull;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Objects; import java.util.Objects;
public final class ApiFactories import javax.annotation.Nonnull;
{
private ApiFactories()
{
}
import dan200.computercraft.api.lua.ILuaAPIFactory;
public final class ApiFactories {
private static final Collection<ILuaAPIFactory> factories = new LinkedHashSet<>(); private static final Collection<ILuaAPIFactory> factories = new LinkedHashSet<>();
private static final Collection<ILuaAPIFactory> factoriesView = Collections.unmodifiableCollection( factories ); private static final Collection<ILuaAPIFactory> factoriesView = Collections.unmodifiableCollection(factories);
private ApiFactories() {
public static synchronized void register( @Nonnull ILuaAPIFactory factory )
{
Objects.requireNonNull( factory, "provider cannot be null" );
factories.add( factory );
} }
public static Iterable<ILuaAPIFactory> getAll() public static synchronized void register(@Nonnull ILuaAPIFactory factory) {
{ Objects.requireNonNull(factory, "provider cannot be null");
factories.add(factory);
}
public static Iterable<ILuaAPIFactory> getAll() {
return factoriesView; return factoriesView;
} }
} }

View File

@@ -3,8 +3,15 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nonnull;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
@@ -12,135 +19,121 @@ import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.core.filesystem.FileSystemException;
import javax.annotation.Nonnull; public abstract class ComputerAccess implements IComputerAccess {
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public abstract class ComputerAccess implements IComputerAccess
{
private final IAPIEnvironment m_environment; private final IAPIEnvironment m_environment;
private final Set<String> m_mounts = new HashSet<>(); private final Set<String> m_mounts = new HashSet<>();
protected ComputerAccess( IAPIEnvironment environment ) protected ComputerAccess(IAPIEnvironment environment) {
{
this.m_environment = environment; this.m_environment = environment;
} }
public void unmountAll() public void unmountAll() {
{ FileSystem fileSystem = this.m_environment.getFileSystem();
FileSystem fileSystem = m_environment.getFileSystem(); for (String mount : this.m_mounts) {
for( String mount : m_mounts ) fileSystem.unmount(mount);
{
fileSystem.unmount( mount );
} }
m_mounts.clear(); this.m_mounts.clear();
} }
@Override @Override
public synchronized String mount( @Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName ) public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName) {
{ Objects.requireNonNull(desiredLoc, "desiredLocation cannot be null");
Objects.requireNonNull( desiredLoc, "desiredLocation cannot be null" ); Objects.requireNonNull(mount, "mount cannot be null");
Objects.requireNonNull( mount, "mount cannot be null" ); Objects.requireNonNull(driveName, "driveName cannot be null");
Objects.requireNonNull( driveName, "driveName cannot be null" );
// Mount the location // Mount the location
String location; String location;
FileSystem fileSystem = m_environment.getFileSystem(); FileSystem fileSystem = this.m_environment.getFileSystem();
if( fileSystem == null ) throw new IllegalStateException( "File system has not been created" ); if (fileSystem == null) {
throw new IllegalStateException("File system has not been created");
synchronized( fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try
{
fileSystem.mount( driveName, location, mount );
} }
catch( FileSystemException ignored )
{ synchronized (fileSystem) {
location = this.findFreeLocation(desiredLoc);
if (location != null) {
try {
fileSystem.mount(driveName, location, mount);
} catch (FileSystemException ignored) {
} }
} }
} }
if( location != null ) m_mounts.add( location ); if (location != null) {
this.m_mounts.add(location);
}
return location; return location;
} }
@Override @Override
public synchronized String mountWritable( @Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName ) public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName) {
{ Objects.requireNonNull(desiredLoc, "desiredLocation cannot be null");
Objects.requireNonNull( desiredLoc, "desiredLocation cannot be null" ); Objects.requireNonNull(mount, "mount cannot be null");
Objects.requireNonNull( mount, "mount cannot be null" ); Objects.requireNonNull(driveName, "driveName cannot be null");
Objects.requireNonNull( driveName, "driveName cannot be null" );
// Mount the location // Mount the location
String location; String location;
FileSystem fileSystem = m_environment.getFileSystem(); FileSystem fileSystem = this.m_environment.getFileSystem();
if( fileSystem == null ) throw new IllegalStateException( "File system has not been created" ); if (fileSystem == null) {
throw new IllegalStateException("File system has not been created");
synchronized( fileSystem )
{
location = findFreeLocation( desiredLoc );
if( location != null )
{
try
{
fileSystem.mountWritable( driveName, location, mount );
} }
catch( FileSystemException ignored )
{ synchronized (fileSystem) {
location = this.findFreeLocation(desiredLoc);
if (location != null) {
try {
fileSystem.mountWritable(driveName, location, mount);
} catch (FileSystemException ignored) {
} }
} }
} }
if( location != null ) m_mounts.add( location ); if (location != null) {
this.m_mounts.add(location);
}
return location; return location;
} }
@Override @Override
public void unmount( String location ) public void unmount(String location) {
{ if (location == null) {
if( location == null ) return; return;
if( !m_mounts.contains( location ) ) throw new IllegalStateException( "You didn't mount this location" ); }
if (!this.m_mounts.contains(location)) {
throw new IllegalStateException("You didn't mount this location");
}
m_environment.getFileSystem().unmount( location ); this.m_environment.getFileSystem()
m_mounts.remove( location ); .unmount(location);
this.m_mounts.remove(location);
} }
@Override @Override
public int getID() public int getID() {
{ return this.m_environment.getComputerID();
return m_environment.getComputerID();
} }
@Override @Override
public void queueEvent( @Nonnull String event, Object... arguments ) public void queueEvent(@Nonnull String event, Object... arguments) {
{ Objects.requireNonNull(event, "event cannot be null");
Objects.requireNonNull( event, "event cannot be null" ); this.m_environment.queueEvent(event, arguments);
m_environment.queueEvent( event, arguments );
} }
@Nonnull @Nonnull
@Override @Override
public IWorkMonitor getMainThreadMonitor() public IWorkMonitor getMainThreadMonitor() {
{ return this.m_environment.getMainThreadMonitor();
return m_environment.getMainThreadMonitor();
} }
private String findFreeLocation( String desiredLoc ) private String findFreeLocation(String desiredLoc) {
{ try {
try FileSystem fileSystem = this.m_environment.getFileSystem();
{ if (!fileSystem.exists(desiredLoc)) {
FileSystem fileSystem = m_environment.getFileSystem(); return desiredLoc;
if( !fileSystem.exists( desiredLoc ) ) return desiredLoc; }
// We used to check foo2, foo3, foo4, etc here but the disk drive does this itself now // We used to check foo2, foo3, foo4, etc here but the disk drive does this itself now
return null; return null;
} } catch (FileSystemException e) {
catch( FileSystemException e )
{
return null; return null;
} }
} }

View File

@@ -3,8 +3,20 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
import java.util.OptionalLong;
import java.util.function.Function;
import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.LuaFunction;
@@ -17,48 +29,32 @@ import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.filesystem.FileSystemWrapper; import dan200.computercraft.core.filesystem.FileSystemWrapper;
import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.core.tracking.TrackingField;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
import java.util.OptionalLong;
import java.util.function.Function;
/** /**
* The FS API allows you to manipulate files and the filesystem. * The FS API allows you to manipulate files and the filesystem.
* *
* @cc.module fs * @cc.module fs
*/ */
public class FSAPI implements ILuaAPI public class FSAPI implements ILuaAPI {
{
private final IAPIEnvironment environment; private final IAPIEnvironment environment;
private FileSystem fileSystem = null; private FileSystem fileSystem = null;
public FSAPI( IAPIEnvironment env ) public FSAPI(IAPIEnvironment env) {
{ this.environment = env;
environment = env;
} }
@Override @Override
public String[] getNames() public String[] getNames() {
{ return new String[] {"fs"};
return new String[] { "fs" };
} }
@Override @Override
public void startup() public void startup() {
{ this.fileSystem = this.environment.getFileSystem();
fileSystem = environment.getFileSystem();
} }
@Override @Override
public void shutdown() public void shutdown() {
{ this.fileSystem = null;
fileSystem = null;
} }
/** /**
@@ -69,31 +65,25 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the path doesn't exist. * @throws LuaException If the path doesn't exist.
*/ */
@LuaFunction @LuaFunction
public final String[] list( String path ) throws LuaException public final String[] list(String path) throws LuaException {
{ this.environment.addTrackingChange(TrackingField.FS_OPS);
environment.addTrackingChange( TrackingField.FS_OPS ); try {
try return this.fileSystem.list(path);
{ } catch (FileSystemException e) {
return fileSystem.list( path ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Combines two parts of a path into one full path, adding separators as * Combines two parts of a path into one full path, adding separators as needed.
* needed.
* *
* @param pathA The first part of the path. For example, a parent directory path. * @param pathA The first part of the path. For example, a parent directory path.
* @param pathB The second part of the path. For example, a file name. * @param pathB The second part of the path. For example, a file name.
* @return The new path, with separators added between parts as needed. * @return The new path, with separators added between parts as needed.
*/ */
@LuaFunction @LuaFunction
public final String combine( String pathA, String pathB ) public final String combine(String pathA, String pathB) {
{ return this.fileSystem.combine(pathA, pathB);
return fileSystem.combine( pathA, pathB );
} }
/** /**
@@ -103,9 +93,8 @@ public class FSAPI implements ILuaAPI
* @return The final part of the path (the file name). * @return The final part of the path (the file name).
*/ */
@LuaFunction @LuaFunction
public final String getName( String path ) public final String getName(String path) {
{ return FileSystem.getName(path);
return FileSystem.getName( path );
} }
/** /**
@@ -115,9 +104,8 @@ public class FSAPI implements ILuaAPI
* @return The path with the final part removed (the parent directory). * @return The path with the final part removed (the parent directory).
*/ */
@LuaFunction @LuaFunction
public final String getDir( String path ) public final String getDir(String path) {
{ return FileSystem.getDirectory(path);
return FileSystem.getDirectory( path );
} }
/** /**
@@ -128,15 +116,11 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the path doesn't exist. * @throws LuaException If the path doesn't exist.
*/ */
@LuaFunction @LuaFunction
public final long getSize( String path ) throws LuaException public final long getSize(String path) throws LuaException {
{ try {
try return this.fileSystem.getSize(path);
{ } catch (FileSystemException e) {
return fileSystem.getSize( path ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
@@ -147,14 +131,10 @@ public class FSAPI implements ILuaAPI
* @return Whether the path exists. * @return Whether the path exists.
*/ */
@LuaFunction @LuaFunction
public final boolean exists( String path ) public final boolean exists(String path) {
{ try {
try return this.fileSystem.exists(path);
{ } catch (FileSystemException e) {
return fileSystem.exists( path );
}
catch( FileSystemException e )
{
return false; return false;
} }
} }
@@ -166,14 +146,10 @@ public class FSAPI implements ILuaAPI
* @return Whether the path is a directory. * @return Whether the path is a directory.
*/ */
@LuaFunction @LuaFunction
public final boolean isDir( String path ) public final boolean isDir(String path) {
{ try {
try return this.fileSystem.isDir(path);
{ } catch (FileSystemException e) {
return fileSystem.isDir( path );
}
catch( FileSystemException e )
{
return false; return false;
} }
} }
@@ -185,14 +161,10 @@ public class FSAPI implements ILuaAPI
* @return Whether the path cannot be written to. * @return Whether the path cannot be written to.
*/ */
@LuaFunction @LuaFunction
public final boolean isReadOnly( String path ) public final boolean isReadOnly(String path) {
{ try {
try return this.fileSystem.isReadOnly(path);
{ } catch (FileSystemException e) {
return fileSystem.isReadOnly( path );
}
catch( FileSystemException e )
{
return false; return false;
} }
} }
@@ -204,16 +176,12 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the directory couldn't be created. * @throws LuaException If the directory couldn't be created.
*/ */
@LuaFunction @LuaFunction
public final void makeDir( String path ) throws LuaException public final void makeDir(String path) throws LuaException {
{ try {
try this.environment.addTrackingChange(TrackingField.FS_OPS);
{ this.fileSystem.makeDir(path);
environment.addTrackingChange( TrackingField.FS_OPS ); } catch (FileSystemException e) {
fileSystem.makeDir( path ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
@@ -227,16 +195,12 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the file or directory couldn't be moved. * @throws LuaException If the file or directory couldn't be moved.
*/ */
@LuaFunction @LuaFunction
public final void move( String path, String dest ) throws LuaException public final void move(String path, String dest) throws LuaException {
{ try {
try this.environment.addTrackingChange(TrackingField.FS_OPS);
{ this.fileSystem.move(path, dest);
environment.addTrackingChange( TrackingField.FS_OPS ); } catch (FileSystemException e) {
fileSystem.move( path, dest ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
@@ -250,39 +214,30 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the file or directory couldn't be copied. * @throws LuaException If the file or directory couldn't be copied.
*/ */
@LuaFunction @LuaFunction
public final void copy( String path, String dest ) throws LuaException public final void copy(String path, String dest) throws LuaException {
{ try {
try this.environment.addTrackingChange(TrackingField.FS_OPS);
{ this.fileSystem.copy(path, dest);
environment.addTrackingChange( TrackingField.FS_OPS ); } catch (FileSystemException e) {
fileSystem.copy( path, dest ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Deletes a file or directory. * Deletes a file or directory.
* *
* If the path points to a directory, all of the enclosed files and * If the path points to a directory, all of the enclosed files and subdirectories are also deleted.
* subdirectories are also deleted.
* *
* @param path The path to the file or directory to delete. * @param path The path to the file or directory to delete.
* @throws LuaException If the file or directory couldn't be deleted. * @throws LuaException If the file or directory couldn't be deleted.
*/ */
@LuaFunction @LuaFunction
public final void delete( String path ) throws LuaException public final void delete(String path) throws LuaException {
{ try {
try this.environment.addTrackingChange(TrackingField.FS_OPS);
{ this.fileSystem.delete(path);
environment.addTrackingChange( TrackingField.FS_OPS ); } catch (FileSystemException e) {
fileSystem.delete( path ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
@@ -291,10 +246,8 @@ public class FSAPI implements ILuaAPI
/** /**
* Opens a file for reading or writing at a path. * Opens a file for reading or writing at a path.
* *
* The mode parameter can be {@code r} to read, {@code w} to write (deleting * The mode parameter can be {@code r} to read, {@code w} to write (deleting all contents), or {@code a} to append (keeping contents). If {@code b} is
* all contents), or {@code a} to append (keeping contents). If {@code b} is * added to the end, the file will be opened in binary mode; otherwise, it's opened in text mode.
* added to the end, the file will be opened in binary mode; otherwise, it's
* opened in text mode.
* *
* @param path The path to the file to open. * @param path The path to the file to open.
* @param mode The mode to open the file with. * @param mode The mode to open the file with.
@@ -305,56 +258,48 @@ public class FSAPI implements ILuaAPI
* @cc.treturn string|nil A message explaining why the file cannot be opened. * @cc.treturn string|nil A message explaining why the file cannot be opened.
*/ */
@LuaFunction @LuaFunction
public final Object[] open( String path, String mode ) throws LuaException public final Object[] open(String path, String mode) throws LuaException {
{ this.environment.addTrackingChange(TrackingField.FS_OPS);
environment.addTrackingChange( TrackingField.FS_OPS ); try {
try switch (mode) {
{ case "r": {
switch( mode )
{
case "r":
{
// Open the file for reading, then create a wrapper around the reader // Open the file for reading, then create a wrapper around the reader
FileSystemWrapper<BufferedReader> reader = fileSystem.openForRead( path, EncodedReadableHandle::openUtf8 ); FileSystemWrapper<BufferedReader> reader = this.fileSystem.openForRead(path, EncodedReadableHandle::openUtf8);
return new Object[] { new EncodedReadableHandle( reader.get(), reader ) }; return new Object[] {new EncodedReadableHandle(reader.get(), reader)};
} }
case "w": case "w": {
{
// Open the file for writing, then create a wrapper around the writer // Open the file for writing, then create a wrapper around the writer
FileSystemWrapper<BufferedWriter> writer = fileSystem.openForWrite( path, false, EncodedWritableHandle::openUtf8 ); FileSystemWrapper<BufferedWriter> writer = this.fileSystem.openForWrite(path, false, EncodedWritableHandle::openUtf8);
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) }; return new Object[] {new EncodedWritableHandle(writer.get(), writer)};
} }
case "a": case "a": {
{
// Open the file for appending, then create a wrapper around the writer // Open the file for appending, then create a wrapper around the writer
FileSystemWrapper<BufferedWriter> writer = fileSystem.openForWrite( path, true, EncodedWritableHandle::openUtf8 ); FileSystemWrapper<BufferedWriter> writer = this.fileSystem.openForWrite(path, true, EncodedWritableHandle::openUtf8);
return new Object[] { new EncodedWritableHandle( writer.get(), writer ) }; return new Object[] {new EncodedWritableHandle(writer.get(), writer)};
} }
case "rb": case "rb": {
{
// Open the file for binary reading, then create a wrapper around the reader // Open the file for binary reading, then create a wrapper around the reader
FileSystemWrapper<ReadableByteChannel> reader = fileSystem.openForRead( path, Function.identity() ); FileSystemWrapper<ReadableByteChannel> reader = this.fileSystem.openForRead(path, Function.identity());
return new Object[] { BinaryReadableHandle.of( reader.get(), reader ) }; return new Object[] {BinaryReadableHandle.of(reader.get(), reader)};
} }
case "wb": case "wb": {
{
// Open the file for binary writing, then create a wrapper around the writer // Open the file for binary writing, then create a wrapper around the writer
FileSystemWrapper<WritableByteChannel> writer = fileSystem.openForWrite( path, false, Function.identity() ); FileSystemWrapper<WritableByteChannel> writer = this.fileSystem.openForWrite(path, false, Function.identity());
return new Object[] { BinaryWritableHandle.of( writer.get(), writer ) }; return new Object[] {BinaryWritableHandle.of(writer.get(), writer)};
} }
case "ab": case "ab": {
{
// Open the file for binary appending, then create a wrapper around the reader // Open the file for binary appending, then create a wrapper around the reader
FileSystemWrapper<WritableByteChannel> writer = fileSystem.openForWrite( path, true, Function.identity() ); FileSystemWrapper<WritableByteChannel> writer = this.fileSystem.openForWrite(path, true, Function.identity());
return new Object[] { BinaryWritableHandle.of( writer.get(), writer ) }; return new Object[] {BinaryWritableHandle.of(writer.get(), writer)};
} }
default: default:
throw new LuaException( "Unsupported mode" ); throw new LuaException("Unsupported mode");
} }
} } catch (FileSystemException e) {
catch( FileSystemException e ) return new Object[] {
{ null,
return new Object[] { null, e.getMessage() }; e.getMessage()
};
} }
} }
@@ -367,21 +312,16 @@ public class FSAPI implements ILuaAPI
* @cc.treturn string The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files. * @cc.treturn string The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
*/ */
@LuaFunction @LuaFunction
public final Object[] getDrive( String path ) throws LuaException public final Object[] getDrive(String path) throws LuaException {
{ try {
try return this.fileSystem.exists(path) ? new Object[] {this.fileSystem.getMountLabel(path)} : null;
{ } catch (FileSystemException e) {
return fileSystem.exists( path ) ? new Object[] { fileSystem.getMountLabel( path ) } : null; throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Returns the amount of free space available on the drive the path is * Returns the amount of free space available on the drive the path is located on.
* located on.
* *
* @param path The path to check the free space for. * @param path The path to check the free space for.
* @return The amount of free space available, in bytes. * @return The amount of free space available, in bytes.
@@ -389,80 +329,63 @@ public class FSAPI implements ILuaAPI
* @cc.treturn number|"unlimited" The amount of free space available, in bytes, or "unlimited". * @cc.treturn number|"unlimited" The amount of free space available, in bytes, or "unlimited".
*/ */
@LuaFunction @LuaFunction
public final Object getFreeSpace( String path ) throws LuaException public final Object getFreeSpace(String path) throws LuaException {
{ try {
try long freeSpace = this.fileSystem.getFreeSpace(path);
{
long freeSpace = fileSystem.getFreeSpace( path );
return freeSpace >= 0 ? freeSpace : "unlimited"; return freeSpace >= 0 ? freeSpace : "unlimited";
} } catch (FileSystemException e) {
catch( FileSystemException e ) throw new LuaException(e.getMessage());
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Searches for files matching a string with wildcards. * Searches for files matching a string with wildcards.
* *
* This string is formatted like a normal path string, but can include any * This string is formatted like a normal path string, but can include any number of wildcards ({@code *}) to look for files matching anything. For
* number of wildcards ({@code *}) to look for files matching anything. * example, {@code rom/* /command*} will look for any path starting with {@code command} inside any subdirectory of {@code /rom}.
* For example, {@code 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. * @param path The wildcard-qualified path to search for.
* @return A list of paths that match the search string. * @return A list of paths that match the search string.
* @throws LuaException If the path doesn't exist. * @throws LuaException If the path doesn't exist.
*/ */
@LuaFunction @LuaFunction
public final String[] find( String path ) throws LuaException public final String[] find(String path) throws LuaException {
{ try {
try this.environment.addTrackingChange(TrackingField.FS_OPS);
{ return this.fileSystem.find(path);
environment.addTrackingChange( TrackingField.FS_OPS ); } catch (FileSystemException e) {
return fileSystem.find( path ); throw new LuaException(e.getMessage());
}
catch( FileSystemException e )
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Returns true if a path is mounted to the parent filesystem. * Returns true if a path is mounted to the parent filesystem.
* *
* The root filesystem "/" is considered a mount, along with disk folders and the rom folder. Other programs * The root filesystem "/" is considered a mount, along with disk folders and the rom folder. Other programs (such as network shares) can extend this to
* (such as network shares) can extend this to make other mount types by correctly assigning their return value for * make other mount types by correctly assigning their return value for getDrive.
* getDrive.
* *
* @param path The path of the drive to get. * @param path The path of the drive to get.
* @return The drive's capacity. * @return The drive's capacity.
* @throws LuaException If the capacity cannot be determined. * @throws LuaException If the capacity cannot be determined.
* @cc.treturn number|nil This drive's capacity. This will be nil for "read-only" drives, such as the ROM or * @cc.treturn number|nil This drive's capacity. This will be nil for "read-only" drives, such as the ROM or treasure disks.
* treasure disks.
*/ */
@LuaFunction @LuaFunction
public final Object getCapacity( String path ) throws LuaException public final Object getCapacity(String path) throws LuaException {
{ try {
try OptionalLong capacity = this.fileSystem.getCapacity(path);
{
OptionalLong capacity = fileSystem.getCapacity( path );
return capacity.isPresent() ? capacity.getAsLong() : null; return capacity.isPresent() ? capacity.getAsLong() : null;
} } catch (FileSystemException e) {
catch( FileSystemException e ) throw new LuaException(e.getMessage());
{
throw new LuaException( e.getMessage() );
} }
} }
/** /**
* Get attributes about a specific file or folder. * Get attributes about a specific file or folder.
* *
* The returned attributes table contains information about the size of the file, whether it is a directory, and * The returned attributes table contains information about the size of the file, whether it is a directory, and when it was created and last modified.
* when it was created and last modified.
* *
* The creation and modification times are given as the number of milliseconds since the UNIX epoch. This may be * The creation and modification times are given as the number of milliseconds since the UNIX epoch. This may be given to {@link OSAPI#date} in order to
* given to {@link OSAPI#date} in order to convert it to more usable form. * convert it to more usable form.
* *
* @param path The path to get attributes for. * @param path The path to get attributes for.
* @return The resulting attributes. * @return The resulting attributes.
@@ -472,27 +395,22 @@ public class FSAPI implements ILuaAPI
* @see #isDir If you only care whether a path is a directory or not. * @see #isDir If you only care whether a path is a directory or not.
*/ */
@LuaFunction @LuaFunction
public final Map<String, Object> attributes( String path ) throws LuaException public final Map<String, Object> attributes(String path) throws LuaException {
{ try {
try BasicFileAttributes attributes = this.fileSystem.getAttributes(path);
{
BasicFileAttributes attributes = fileSystem.getAttributes( path );
Map<String, Object> result = new HashMap<>(); Map<String, Object> result = new HashMap<>();
result.put( "modification", getFileTime( attributes.lastModifiedTime() ) ); result.put("modification", getFileTime(attributes.lastModifiedTime()));
result.put( "modified", getFileTime( attributes.lastModifiedTime() ) ); result.put("modified", getFileTime(attributes.lastModifiedTime()));
result.put( "created", getFileTime( attributes.creationTime() ) ); result.put("created", getFileTime(attributes.creationTime()));
result.put( "size", attributes.isDirectory() ? 0 : attributes.size() ); result.put("size", attributes.isDirectory() ? 0 : attributes.size());
result.put( "isDir", attributes.isDirectory() ); result.put("isDir", attributes.isDirectory());
return result; return result;
} } catch (FileSystemException e) {
catch( FileSystemException e ) throw new LuaException(e.getMessage());
{
throw new LuaException( e.getMessage() );
} }
} }
private static long getFileTime( FileTime time ) private static long getFileTime(FileTime time) {
{
return time == null ? 0 : time.toMillis(); return time == null ? 0 : time.toMillis();
} }
} }

View File

@@ -6,30 +6,26 @@
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.LuaException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import dan200.computercraft.api.lua.LuaException;
/** /**
* A Lua exception which does not contain its stack trace. * A Lua exception which does not contain its stack trace.
*/ */
public class FastLuaException extends LuaException public class FastLuaException extends LuaException {
{
private static final long serialVersionUID = 5957864899303561143L; private static final long serialVersionUID = 5957864899303561143L;
public FastLuaException( @Nullable String message ) public FastLuaException(@Nullable String message) {
{ super(message);
super( message );
} }
public FastLuaException( @Nullable String message, int level ) public FastLuaException(@Nullable String message, int level) {
{ super(message, level);
super( message, level );
} }
@Override @Override
public synchronized Throwable fillInStackTrace() public synchronized Throwable fillInStackTrace() {
{
return this; return this;
} }
} }

View File

@@ -3,28 +3,37 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import dan200.computercraft.ComputerCraft; import static dan200.computercraft.core.apis.TableHelper.getStringField;
import dan200.computercraft.api.lua.IArguments; import static dan200.computercraft.core.apis.TableHelper.optBooleanField;
import dan200.computercraft.api.lua.ILuaAPI; import static dan200.computercraft.core.apis.TableHelper.optStringField;
import dan200.computercraft.api.lua.LuaException; import static dan200.computercraft.core.apis.TableHelper.optTableField;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.apis.http.*;
import dan200.computercraft.core.apis.http.request.HttpRequest;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import javax.annotation.Nonnull;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static dan200.computercraft.core.apis.TableHelper.*; import javax.annotation.Nonnull;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.apis.http.CheckUrl;
import dan200.computercraft.core.apis.http.HTTPRequestException;
import dan200.computercraft.core.apis.http.Resource;
import dan200.computercraft.core.apis.http.ResourceGroup;
import dan200.computercraft.core.apis.http.ResourceQueue;
import dan200.computercraft.core.apis.http.request.HttpRequest;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
/** /**
* The http library allows communicating with web servers, sending and receiving data from them. * The http library allows communicating with web servers, sending and receiving data from them.
@@ -32,171 +41,147 @@ import static dan200.computercraft.core.apis.TableHelper.*;
* @cc.module http * @cc.module http
* @hidden * @hidden
*/ */
public class HTTPAPI implements ILuaAPI public class HTTPAPI implements ILuaAPI {
{
private final IAPIEnvironment m_apiEnvironment; private final IAPIEnvironment m_apiEnvironment;
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>(); private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>();
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>( () -> ComputerCraft.httpMaxRequests ); private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>(() -> ComputerCraft.httpMaxRequests);
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>( () -> ComputerCraft.httpMaxWebsockets ); private final ResourceGroup<Websocket> websockets = new ResourceGroup<>(() -> ComputerCraft.httpMaxWebsockets);
public HTTPAPI( IAPIEnvironment environment ) public HTTPAPI(IAPIEnvironment environment) {
{ this.m_apiEnvironment = environment;
m_apiEnvironment = environment;
} }
@Override @Override
public String[] getNames() public String[] getNames() {
{ return new String[] {"http"};
return new String[] { "http" };
} }
@Override @Override
public void startup() public void startup() {
{ this.checkUrls.startup();
checkUrls.startup(); this.requests.startup();
requests.startup(); this.websockets.startup();
websockets.startup();
} }
@Override @Override
public void shutdown() public void update() {
{
checkUrls.shutdown();
requests.shutdown();
websockets.shutdown();
}
@Override
public void update()
{
// It's rather ugly to run this here, but we need to clean up // It's rather ugly to run this here, but we need to clean up
// resources as often as possible to reduce blocking. // resources as often as possible to reduce blocking.
Resource.cleanup(); Resource.cleanup();
} }
@Override
public void shutdown() {
this.checkUrls.shutdown();
this.requests.shutdown();
this.websockets.shutdown();
}
@LuaFunction @LuaFunction
public final Object[] request( IArguments args ) throws LuaException public final Object[] request(IArguments args) throws LuaException {
{
String address, postString, requestMethod; String address, postString, requestMethod;
Map<?, ?> headerTable; Map<?, ?> headerTable;
boolean binary, redirect; boolean binary, redirect;
if( args.get( 0 ) instanceof Map ) if (args.get(0) instanceof Map) {
{ Map<?, ?> options = args.getTable(0);
Map<?, ?> options = args.getTable( 0 ); address = getStringField(options, "url");
address = getStringField( options, "url" ); postString = optStringField(options, "body", null);
postString = optStringField( options, "body", null ); headerTable = optTableField(options, "headers", Collections.emptyMap());
headerTable = optTableField( options, "headers", Collections.emptyMap() ); binary = optBooleanField(options, "binary", false);
binary = optBooleanField( options, "binary", false ); requestMethod = optStringField(options, "method", null);
requestMethod = optStringField( options, "method", null ); redirect = optBooleanField(options, "redirect", true);
redirect = optBooleanField( options, "redirect", true );
} } else {
else
{
// Get URL and post information // Get URL and post information
address = args.getString( 0 ); address = args.getString(0);
postString = args.optString( 1, null ); postString = args.optString(1, null);
headerTable = args.optTable( 2, Collections.emptyMap() ); headerTable = args.optTable(2, Collections.emptyMap());
binary = args.optBoolean( 3, false ); binary = args.optBoolean(3, false);
requestMethod = null; requestMethod = null;
redirect = true; redirect = true;
} }
HttpHeaders headers = getHeaders( headerTable ); HttpHeaders headers = getHeaders(headerTable);
HttpMethod httpMethod; HttpMethod httpMethod;
if( requestMethod == null ) if (requestMethod == null) {
{
httpMethod = postString == null ? HttpMethod.GET : HttpMethod.POST; httpMethod = postString == null ? HttpMethod.GET : HttpMethod.POST;
} } else {
else httpMethod = HttpMethod.valueOf(requestMethod.toUpperCase(Locale.ROOT));
{ if (httpMethod == null || requestMethod.equalsIgnoreCase("CONNECT")) {
httpMethod = HttpMethod.valueOf( requestMethod.toUpperCase( Locale.ROOT ) ); throw new LuaException("Unsupported HTTP method");
if( httpMethod == null || requestMethod.equalsIgnoreCase( "CONNECT" ) )
{
throw new LuaException( "Unsupported HTTP method" );
} }
} }
try try {
{ URI uri = HttpRequest.checkUri(address);
URI uri = HttpRequest.checkUri( address ); HttpRequest request = new HttpRequest(this.requests, this.m_apiEnvironment, address, postString, headers, binary, redirect);
HttpRequest request = new HttpRequest( requests, m_apiEnvironment, address, postString, headers, binary, redirect );
// Make the request // Make the request
request.queue( r -> r.request( uri, httpMethod ) ); request.queue(r -> r.request(uri, httpMethod));
return new Object[] { true }; return new Object[] {true};
} } catch (HTTPRequestException e) {
catch( HTTPRequestException e ) return new Object[] {
{ false,
return new Object[] { false, e.getMessage() }; e.getMessage()
} };
}
@LuaFunction
public final Object[] checkURL( String address )
{
try
{
URI uri = HttpRequest.checkUri( address );
new CheckUrl( checkUrls, m_apiEnvironment, address, uri ).queue( CheckUrl::run );
return new Object[] { true };
}
catch( HTTPRequestException e )
{
return new Object[] { false, e.getMessage() };
}
}
@LuaFunction
public final Object[] websocket( String address, Optional<Map<?, ?>> headerTbl ) throws LuaException
{
if( !ComputerCraft.http_websocket_enable )
{
throw new LuaException( "Websocket connections are disabled" );
}
HttpHeaders headers = getHeaders( headerTbl.orElse( Collections.emptyMap() ) );
try
{
URI uri = Websocket.checkUri( address );
if( !new Websocket( websockets, m_apiEnvironment, uri, address, headers ).queue( Websocket::connect ) )
{
throw new LuaException( "Too many websockets already open" );
}
return new Object[] { true };
}
catch( HTTPRequestException e )
{
return new Object[] { false, e.getMessage() };
} }
} }
@Nonnull @Nonnull
private static HttpHeaders getHeaders( @Nonnull Map<?, ?> headerTable ) throws LuaException private static HttpHeaders getHeaders(@Nonnull Map<?, ?> headerTable) throws LuaException {
{
HttpHeaders headers = new DefaultHttpHeaders(); HttpHeaders headers = new DefaultHttpHeaders();
for( Map.Entry<?, ?> entry : headerTable.entrySet() ) for (Map.Entry<?, ?> entry : headerTable.entrySet()) {
{
Object value = entry.getValue(); Object value = entry.getValue();
if( entry.getKey() instanceof String && value instanceof String ) if (entry.getKey() instanceof String && value instanceof String) {
{ try {
try headers.add((String) entry.getKey(), value);
{ } catch (IllegalArgumentException e) {
headers.add( (String) entry.getKey(), value ); throw new LuaException(e.getMessage());
}
catch( IllegalArgumentException e )
{
throw new LuaException( e.getMessage() );
} }
} }
} }
return headers; return headers;
} }
@LuaFunction
public final Object[] checkURL(String address) {
try {
URI uri = HttpRequest.checkUri(address);
new CheckUrl(this.checkUrls, this.m_apiEnvironment, address, uri).queue(CheckUrl::run);
return new Object[] {true};
} catch (HTTPRequestException e) {
return new Object[] {
false,
e.getMessage()
};
}
}
@LuaFunction
public final Object[] websocket(String address, Optional<Map<?, ?>> headerTbl) throws LuaException {
if (!ComputerCraft.http_websocket_enable) {
throw new LuaException("Websocket connections are disabled");
}
HttpHeaders headers = getHeaders(headerTbl.orElse(Collections.emptyMap()));
try {
URI uri = Websocket.checkUri(address);
if (!new Websocket(this.websockets, this.m_apiEnvironment, uri, address, headers).queue(Websocket::connect)) {
throw new LuaException("Too many websockets already open");
}
return new Object[] {true};
} catch (HTTPRequestException e) {
return new Object[] {
false,
e.getMessage()
};
}
}
} }

View File

@@ -3,8 +3,12 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor; import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
@@ -13,19 +17,9 @@ import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.core.tracking.TrackingField;
import javax.annotation.Nonnull; public interface IAPIEnvironment {
import javax.annotation.Nullable;
public interface IAPIEnvironment
{
String TIMER_EVENT = "timer"; String TIMER_EVENT = "timer";
@FunctionalInterface
interface IPeripheralChangeListener
{
void onPeripheralChanged( ComputerSide side, @Nullable IPeripheral newPeripheral );
}
int getComputerID(); int getComputerID();
@Nonnull @Nonnull
@@ -43,37 +37,41 @@ public interface IAPIEnvironment
void reboot(); void reboot();
void queueEvent( String event, Object... args ); void queueEvent(String event, Object... args);
void setOutput( ComputerSide side, int output ); void setOutput(ComputerSide side, int output);
int getOutput( ComputerSide side ); int getOutput(ComputerSide side);
int getInput( ComputerSide side ); int getInput(ComputerSide side);
void setBundledOutput( ComputerSide side, int output ); void setBundledOutput(ComputerSide side, int output);
int getBundledOutput( ComputerSide side ); int getBundledOutput(ComputerSide side);
int getBundledInput( ComputerSide side ); int getBundledInput(ComputerSide side);
void setPeripheralChangeListener( @Nullable IPeripheralChangeListener listener ); void setPeripheralChangeListener(@Nullable IPeripheralChangeListener listener);
@Nullable @Nullable
IPeripheral getPeripheral( ComputerSide side ); IPeripheral getPeripheral(ComputerSide side);
String getLabel(); String getLabel();
void setLabel( @Nullable String label ); void setLabel(@Nullable String label);
int startTimer( long ticks ); int startTimer(long ticks);
void cancelTimer( int id ); void cancelTimer(int id);
void addTrackingChange( @Nonnull TrackingField field, long change ); default void addTrackingChange(@Nonnull TrackingField field) {
this.addTrackingChange(field, 1);
}
default void addTrackingChange( @Nonnull TrackingField field ) void addTrackingChange(@Nonnull TrackingField field, long change);
{
addTrackingChange( field, 1 ); @FunctionalInterface
interface IPeripheralChangeListener {
void onPeripheralChanged(ComputerSide side, @Nullable IPeripheral newPeripheral);
} }
} }

View File

@@ -3,9 +3,8 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis;
import dan200.computercraft.api.lua.LuaException; package dan200.computercraft.core.apis;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -13,138 +12,147 @@ import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle; import java.time.format.TextStyle;
import java.time.temporal.*; import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.LongUnaryOperator; import java.util.function.LongUnaryOperator;
final class LuaDateTime import dan200.computercraft.api.lua.LuaException;
{
private LuaDateTime() final class LuaDateTime {
{ private static final TemporalField CENTURY = map(ChronoField.YEAR, ValueRange.of(0, 6), x -> (x / 100) % 100);
private static final TemporalField ZERO_WEEK = map(WeekFields.SUNDAY_START.dayOfWeek(), ValueRange.of(0, 6), x -> x - 1);
private LuaDateTime() {
} }
static void format( DateTimeFormatterBuilder formatter, String format, ZoneOffset offset ) throws LuaException static void format(DateTimeFormatterBuilder formatter, String format, ZoneOffset offset) throws LuaException {
{ for (int i = 0; i < format.length(); ) {
for( int i = 0; i < format.length(); )
{
char c; char c;
switch( c = format.charAt( i++ ) ) switch (c = format.charAt(i++)) {
{
case '\n': case '\n':
formatter.appendLiteral( '\n' ); formatter.appendLiteral('\n');
break; break;
default: default:
formatter.appendLiteral( c ); formatter.appendLiteral(c);
break; break;
case '%': case '%':
if( i >= format.length() ) break; if (i >= format.length()) {
switch( c = format.charAt( i++ ) ) break;
{ }
switch (c = format.charAt(i++)) {
default: default:
throw new LuaException( "bad argument #1: invalid conversion specifier '%" + c + "'" ); throw new LuaException("bad argument #1: invalid conversion specifier '%" + c + "'");
case '%': case '%':
formatter.appendLiteral( '%' ); formatter.appendLiteral('%');
break; break;
case 'a': case 'a':
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.SHORT ); formatter.appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT);
break; break;
case 'A': case 'A':
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.FULL ); formatter.appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL);
break; break;
case 'b': case 'b':
case 'h': case 'h':
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.SHORT ); formatter.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT);
break; break;
case 'B': case 'B':
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.FULL ); formatter.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL);
break; break;
case 'c': case 'c':
format( formatter, "%a %b %e %H:%M:%S %Y", offset ); format(formatter, "%a %b %e %H:%M:%S %Y", offset);
break; break;
case 'C': case 'C':
formatter.appendValueReduced( CENTURY, 2, 2, 0 ); formatter.appendValueReduced(CENTURY, 2, 2, 0);
break; break;
case 'd': case 'd':
formatter.appendValue( ChronoField.DAY_OF_MONTH, 2 ); formatter.appendValue(ChronoField.DAY_OF_MONTH, 2);
break; break;
case 'D': case 'D':
case 'x': case 'x':
format( formatter, "%m/%d/%y", offset ); format(formatter, "%m/%d/%y", offset);
break; break;
case 'e': case 'e':
formatter.padNext( 2 ).appendValue( ChronoField.DAY_OF_MONTH ); formatter.padNext(2)
.appendValue(ChronoField.DAY_OF_MONTH);
break; break;
case 'F': case 'F':
format( formatter, "%Y-%m-%d", offset ); format(formatter, "%Y-%m-%d", offset);
break; break;
case 'g': case 'g':
formatter.appendValueReduced( IsoFields.WEEK_BASED_YEAR, 2, 2, 0 ); formatter.appendValueReduced(IsoFields.WEEK_BASED_YEAR, 2, 2, 0);
break; break;
case 'G': case 'G':
formatter.appendValue( IsoFields.WEEK_BASED_YEAR ); formatter.appendValue(IsoFields.WEEK_BASED_YEAR);
break; break;
case 'H': case 'H':
formatter.appendValue( ChronoField.HOUR_OF_DAY, 2 ); formatter.appendValue(ChronoField.HOUR_OF_DAY, 2);
break; break;
case 'I': case 'I':
formatter.appendValue( ChronoField.HOUR_OF_AMPM ); formatter.appendValue(ChronoField.HOUR_OF_AMPM);
break; break;
case 'j': case 'j':
formatter.appendValue( ChronoField.DAY_OF_YEAR, 3 ); formatter.appendValue(ChronoField.DAY_OF_YEAR, 3);
break; break;
case 'm': case 'm':
formatter.appendValue( ChronoField.MONTH_OF_YEAR, 2 ); formatter.appendValue(ChronoField.MONTH_OF_YEAR, 2);
break; break;
case 'M': case 'M':
formatter.appendValue( ChronoField.MINUTE_OF_HOUR, 2 ); formatter.appendValue(ChronoField.MINUTE_OF_HOUR, 2);
break; break;
case 'n': case 'n':
formatter.appendLiteral( '\n' ); formatter.appendLiteral('\n');
break; break;
case 'p': case 'p':
formatter.appendText( ChronoField.AMPM_OF_DAY ); formatter.appendText(ChronoField.AMPM_OF_DAY);
break; break;
case 'r': case 'r':
format( formatter, "%I:%M:%S %p", offset ); format(formatter, "%I:%M:%S %p", offset);
break; break;
case 'R': case 'R':
format( formatter, "%H:%M", offset ); format(formatter, "%H:%M", offset);
break; break;
case 'S': case 'S':
formatter.appendValue( ChronoField.SECOND_OF_MINUTE, 2 ); formatter.appendValue(ChronoField.SECOND_OF_MINUTE, 2);
break; break;
case 't': case 't':
formatter.appendLiteral( '\t' ); formatter.appendLiteral('\t');
break; break;
case 'T': case 'T':
case 'X': case 'X':
format( formatter, "%H:%M:%S", offset ); format(formatter, "%H:%M:%S", offset);
break; break;
case 'u': case 'u':
formatter.appendValue( ChronoField.DAY_OF_WEEK ); formatter.appendValue(ChronoField.DAY_OF_WEEK);
break; break;
case 'U': case 'U':
formatter.appendValue( ChronoField.ALIGNED_WEEK_OF_YEAR, 2 ); formatter.appendValue(ChronoField.ALIGNED_WEEK_OF_YEAR, 2);
break; break;
case 'V': case 'V':
formatter.appendValue( IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2 ); formatter.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2);
break; break;
case 'w': case 'w':
formatter.appendValue( ZERO_WEEK ); formatter.appendValue(ZERO_WEEK);
break; break;
case 'W': case 'W':
formatter.appendValue( WeekFields.ISO.weekOfYear(), 2 ); formatter.appendValue(WeekFields.ISO.weekOfYear(), 2);
break; break;
case 'y': case 'y':
formatter.appendValueReduced( ChronoField.YEAR, 2, 2, 0 ); formatter.appendValueReduced(ChronoField.YEAR, 2, 2, 0);
break; break;
case 'Y': case 'Y':
formatter.appendValue( ChronoField.YEAR ); formatter.appendValue(ChronoField.YEAR);
break; break;
case 'z': case 'z':
formatter.appendOffset( "+HHMM", "+0000" ); formatter.appendOffset("+HHMM", "+0000");
break; break;
case 'Z': case 'Z':
formatter.appendChronologyId(); formatter.appendChronologyId();
@@ -154,126 +162,120 @@ final class LuaDateTime
} }
} }
static long fromTable( Map<?, ?> table ) throws LuaException static long fromTable(Map<?, ?> table) throws LuaException {
{ int year = getField(table, "year", -1);
int year = getField( table, "year", -1 ); int month = getField(table, "month", -1);
int month = getField( table, "month", -1 ); int day = getField(table, "day", -1);
int day = getField( table, "day", -1 ); int hour = getField(table, "hour", 12);
int hour = getField( table, "hour", 12 ); int minute = getField(table, "min", 12);
int minute = getField( table, "min", 12 ); int second = getField(table, "sec", 12);
int second = getField( table, "sec", 12 ); LocalDateTime time = LocalDateTime.of(year, month, day, hour, minute, second);
LocalDateTime time = LocalDateTime.of( year, month, day, hour, minute, second );
Boolean isDst = getBoolField( table, "isdst" ); Boolean isDst = getBoolField(table, "isdst");
if( isDst != null ) if (isDst != null) {
{
boolean requireDst = isDst; boolean requireDst = isDst;
for( ZoneOffset possibleOffset : ZoneOffset.systemDefault().getRules().getValidOffsets( time ) ) for (ZoneOffset possibleOffset : ZoneOffset.systemDefault()
{ .getRules()
Instant instant = time.toInstant( possibleOffset ); .getValidOffsets(time)) {
if( possibleOffset.getRules().getDaylightSavings( instant ).isZero() == requireDst ) Instant instant = time.toInstant(possibleOffset);
{ if (possibleOffset.getRules()
.getDaylightSavings(instant)
.isZero() == requireDst) {
return instant.getEpochSecond(); return instant.getEpochSecond();
} }
} }
} }
ZoneOffset offset = ZoneOffset.systemDefault().getRules().getOffset( time ); ZoneOffset offset = ZoneOffset.systemDefault()
return time.toInstant( offset ).getEpochSecond(); .getRules()
.getOffset(time);
return time.toInstant(offset)
.getEpochSecond();
} }
static Map<String, ?> toTable( TemporalAccessor date, ZoneId offset, Instant instant ) private static int getField(Map<?, ?> table, String field, int def) throws LuaException {
{ Object value = table.get(field);
HashMap<String, Object> table = new HashMap<>( 9 ); if (value instanceof Number) {
table.put( "year", date.getLong( ChronoField.YEAR ) ); return ((Number) value).intValue();
table.put( "month", date.getLong( ChronoField.MONTH_OF_YEAR ) ); }
table.put( "day", date.getLong( ChronoField.DAY_OF_MONTH ) ); if (def < 0) {
table.put( "hour", date.getLong( ChronoField.HOUR_OF_DAY ) ); throw new LuaException("field \"" + field + "\" missing in date table");
table.put( "min", date.getLong( ChronoField.MINUTE_OF_HOUR ) );
table.put( "sec", date.getLong( ChronoField.SECOND_OF_MINUTE ) );
table.put( "wday", date.getLong( WeekFields.SUNDAY_START.dayOfWeek() ) );
table.put( "yday", date.getLong( ChronoField.DAY_OF_YEAR ) );
table.put( "isdst", offset.getRules().isDaylightSavings( instant ) );
return table;
} }
private static int getField( Map<?, ?> table, String field, int def ) throws LuaException
{
Object value = table.get( field );
if( value instanceof Number ) return ((Number) value).intValue();
if( def < 0 ) throw new LuaException( "field \"" + field + "\" missing in date table" );
return def; return def;
} }
private static Boolean getBoolField( Map<?, ?> table, String field ) throws LuaException private static Boolean getBoolField(Map<?, ?> table, String field) throws LuaException {
{ Object value = table.get(field);
Object value = table.get( field ); if (value instanceof Boolean || value == null) {
if( value instanceof Boolean || value == null ) return (Boolean) value; return (Boolean) value;
throw new LuaException( "field \"" + field + "\" missing in date table" ); }
throw new LuaException("field \"" + field + "\" missing in date table");
} }
private static final TemporalField CENTURY = map( ChronoField.YEAR, ValueRange.of( 0, 6 ), x -> (x / 100) % 100 ); static Map<String, ?> toTable(TemporalAccessor date, ZoneId offset, Instant instant) {
private static final TemporalField ZERO_WEEK = map( WeekFields.SUNDAY_START.dayOfWeek(), ValueRange.of( 0, 6 ), x -> x - 1 ); HashMap<String, Object> table = new HashMap<>(9);
table.put("year", date.getLong(ChronoField.YEAR));
table.put("month", date.getLong(ChronoField.MONTH_OF_YEAR));
table.put("day", date.getLong(ChronoField.DAY_OF_MONTH));
table.put("hour", date.getLong(ChronoField.HOUR_OF_DAY));
table.put("min", date.getLong(ChronoField.MINUTE_OF_HOUR));
table.put("sec", date.getLong(ChronoField.SECOND_OF_MINUTE));
table.put("wday", date.getLong(WeekFields.SUNDAY_START.dayOfWeek()));
table.put("yday", date.getLong(ChronoField.DAY_OF_YEAR));
table.put("isdst",
offset.getRules()
.isDaylightSavings(instant));
return table;
}
private static TemporalField map( TemporalField field, ValueRange range, LongUnaryOperator convert ) private static TemporalField map(TemporalField field, ValueRange range, LongUnaryOperator convert) {
{ return new TemporalField() {
return new TemporalField() private final ValueRange range = ValueRange.of(0, 99);
{
private final ValueRange range = ValueRange.of( 0, 99 );
@Override @Override
public TemporalUnit getBaseUnit() public TemporalUnit getBaseUnit() {
{
return field.getBaseUnit(); return field.getBaseUnit();
} }
@Override @Override
public TemporalUnit getRangeUnit() public TemporalUnit getRangeUnit() {
{
return field.getRangeUnit(); return field.getRangeUnit();
} }
@Override @Override
public ValueRange range() public ValueRange range() {
{ return this.range;
return range;
} }
@Override @Override
public boolean isDateBased() public boolean isDateBased() {
{
return field.isDateBased(); return field.isDateBased();
} }
@Override @Override
public boolean isTimeBased() public boolean isTimeBased() {
{
return field.isTimeBased(); return field.isTimeBased();
} }
@Override @Override
public boolean isSupportedBy( TemporalAccessor temporal ) public boolean isSupportedBy(TemporalAccessor temporal) {
{ return field.isSupportedBy(temporal);
return field.isSupportedBy( temporal );
} }
@Override @Override
public ValueRange rangeRefinedBy( TemporalAccessor temporal ) public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
{ return this.range;
return range;
} }
@Override @Override
public long getFrom( TemporalAccessor temporal ) public long getFrom(TemporalAccessor temporal) {
{ return convert.applyAsLong(temporal.getLong(field));
return convert.applyAsLong( temporal.getLong( field ) );
} }
@Override @Override
@SuppressWarnings( "unchecked" ) @SuppressWarnings ("unchecked")
public <R extends Temporal> R adjustInto( R temporal, long newValue ) public <R extends Temporal> R adjustInto(R temporal, long newValue) {
{ return (R) temporal.with(field, newValue);
return (R) temporal.with( field, newValue );
} }
}; };
} }

View File

@@ -3,8 +3,26 @@
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TimeZone;
import javax.annotation.Nonnull;
import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.IArguments;
import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
@@ -13,23 +31,12 @@ import dan200.computercraft.shared.util.StringUtil;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import javax.annotation.Nonnull;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatterBuilder;
import java.util.*;
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
/** /**
* The {@link OSAPI} API allows interacting with the current computer. * The {@link OSAPI} API allows interacting with the current computer.
* *
* @cc.module os * @cc.module os
*/ */
public class OSAPI implements ILuaAPI public class OSAPI implements ILuaAPI {
{
private final IAPIEnvironment apiEnvironment; private final IAPIEnvironment apiEnvironment;
private final Int2ObjectMap<Alarm> m_alarms = new Int2ObjectOpenHashMap<>(); private final Int2ObjectMap<Alarm> m_alarms = new Int2ObjectOpenHashMap<>();
@@ -39,123 +46,70 @@ public class OSAPI implements ILuaAPI
private int m_nextAlarmToken = 0; private int m_nextAlarmToken = 0;
private static class Alarm implements Comparable<Alarm> public OSAPI(IAPIEnvironment environment) {
{ this.apiEnvironment = environment;
final double m_time;
final int m_day;
Alarm( double time, int day )
{
m_time = time;
m_day = day;
} }
@Override @Override
public int compareTo( @Nonnull Alarm o ) public String[] getNames() {
{ return new String[] {"os"};
double t = m_day * 24.0 + m_time;
double ot = m_day * 24.0 + m_time;
return Double.compare( t, ot );
}
}
public OSAPI( IAPIEnvironment environment )
{
apiEnvironment = environment;
} }
@Override @Override
public String[] getNames() public void startup() {
{ this.m_time = this.apiEnvironment.getComputerEnvironment()
return new String[] { "os" }; .getTimeOfDay();
} this.m_day = this.apiEnvironment.getComputerEnvironment()
.getDay();
this.m_clock = 0;
@Override synchronized (this.m_alarms) {
public void startup() this.m_alarms.clear();
{
m_time = apiEnvironment.getComputerEnvironment().getTimeOfDay();
m_day = apiEnvironment.getComputerEnvironment().getDay();
m_clock = 0;
synchronized( m_alarms )
{
m_alarms.clear();
} }
} }
@Override @Override
public void update() public void update() {
{ this.m_clock++;
m_clock++;
// Wait for all of our alarms // Wait for all of our alarms
synchronized( m_alarms ) synchronized (this.m_alarms) {
{ double previousTime = this.m_time;
double previousTime = m_time; int previousDay = this.m_day;
int previousDay = m_day; double time = this.apiEnvironment.getComputerEnvironment()
double time = apiEnvironment.getComputerEnvironment().getTimeOfDay(); .getTimeOfDay();
int day = apiEnvironment.getComputerEnvironment().getDay(); int day = this.apiEnvironment.getComputerEnvironment()
.getDay();
if( time > previousTime || day > previousDay ) if (time > previousTime || day > previousDay) {
{ double now = this.m_day * 24.0 + this.m_time;
double now = m_day * 24.0 + m_time; Iterator<Int2ObjectMap.Entry<Alarm>> it = this.m_alarms.int2ObjectEntrySet()
Iterator<Int2ObjectMap.Entry<Alarm>> it = m_alarms.int2ObjectEntrySet().iterator(); .iterator();
while( it.hasNext() ) while (it.hasNext()) {
{
Int2ObjectMap.Entry<Alarm> entry = it.next(); Int2ObjectMap.Entry<Alarm> entry = it.next();
Alarm alarm = entry.getValue(); Alarm alarm = entry.getValue();
double t = alarm.m_day * 24.0 + alarm.m_time; double t = alarm.m_day * 24.0 + alarm.m_time;
if( now >= t ) if (now >= t) {
{ this.apiEnvironment.queueEvent("alarm", entry.getIntKey());
apiEnvironment.queueEvent( "alarm", entry.getIntKey() );
it.remove(); it.remove();
} }
} }
} }
m_time = time; this.m_time = time;
m_day = day; this.m_day = day;
} }
} }
@Override @Override
public void shutdown() public void shutdown() {
{ synchronized (this.m_alarms) {
synchronized( m_alarms ) this.m_alarms.clear();
{
m_alarms.clear();
} }
} }
private static float getTimeForCalendar( Calendar c )
{
float time = c.get( Calendar.HOUR_OF_DAY );
time += c.get( Calendar.MINUTE ) / 60.0f;
time += c.get( Calendar.SECOND ) / (60.0f * 60.0f);
return time;
}
private static int getDayForCalendar( Calendar c )
{
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
int year = c.get( Calendar.YEAR );
int day = 0;
for( int y = 1970; y < year; y++ )
{
day += g.isLeapYear( y ) ? 366 : 365;
}
day += c.get( Calendar.DAY_OF_YEAR );
return day;
}
private static long getEpochForCalendar( Calendar c )
{
return c.getTime().getTime();
}
/** /**
* Adds an event to the event queue. This event can later be pulled with * Adds an event to the event queue. This event can later be pulled with os.pullEvent.
* os.pullEvent.
* *
* @param name The name of the event to queue. * @param name The name of the event to queue.
* @param args The parameters of the event. * @param args The parameters of the event.
@@ -164,92 +118,83 @@ public class OSAPI implements ILuaAPI
* @cc.see os.pullEvent To pull the event queued * @cc.see os.pullEvent To pull the event queued
*/ */
@LuaFunction @LuaFunction
public final void queueEvent( String name, IArguments args ) public final void queueEvent(String name, IArguments args) {
{ this.apiEnvironment.queueEvent(name,
apiEnvironment.queueEvent( name, args.drop( 1 ).getAll() ); args.drop(1)
.getAll());
} }
/** /**
* Starts a timer that will run for the specified number of seconds. Once * Starts a timer that will run for the specified number of seconds. Once the timer fires, a timer event will be added to the queue with the ID returned
* the timer fires, a timer event will be added to the queue with the ID * from this function as the first parameter.
* returned from this function as the first parameter.
* *
* @param timer The number of seconds until the timer fires. * @param timer The number of seconds until the timer fires.
* @return The ID of the new timer. * @return The ID of the new timer.
* @throws LuaException If the time is below zero. * @throws LuaException If the time is below zero.
*/ */
@LuaFunction @LuaFunction
public final int startTimer( double timer ) throws LuaException public final int startTimer(double timer) throws LuaException {
{ return this.apiEnvironment.startTimer(Math.round(checkFinite(0, timer) / 0.05));
return apiEnvironment.startTimer( Math.round( checkFinite( 0, timer ) / 0.05 ) );
} }
/** /**
* Cancels a timer previously started with startTimer. This will stop the * Cancels a timer previously started with startTimer. This will stop the timer from firing.
* timer from firing.
* *
* @param token The ID of the timer to cancel. * @param token The ID of the timer to cancel.
* @see #startTimer To start a timer. * @see #startTimer To start a timer.
*/ */
@LuaFunction @LuaFunction
public final void cancelTimer( int token ) public final void cancelTimer(int token) {
{ this.apiEnvironment.cancelTimer(token);
apiEnvironment.cancelTimer( token );
} }
/** /**
* Sets an alarm that will fire at the specified world time. When it fires, * Sets an alarm that will fire at the specified world time. When it fires, an alarm event will be added to the event queue.
* an alarm event will be added to the event queue.
* *
* @param time The time at which to fire the alarm, in the range [0.0, 24.0). * @param time The time at which to fire the alarm, in the range [0.0, 24.0).
* @return The ID of the alarm that was set. * @return The ID of the alarm that was set.
* @throws LuaException If the time is out of range. * @throws LuaException If the time is out of range.
*/ */
@LuaFunction @LuaFunction
public final int setAlarm( double time ) throws LuaException public final int setAlarm(double time) throws LuaException {
{ checkFinite(0, time);
checkFinite( 0, time ); if (time < 0.0 || time >= 24.0) {
if( time < 0.0 || time >= 24.0 ) throw new LuaException( "Number out of range" ); throw new LuaException("Number out of range");
synchronized( m_alarms ) }
{ synchronized (this.m_alarms) {
int day = time > m_time ? m_day : m_day + 1; int day = time > this.m_time ? this.m_day : this.m_day + 1;
m_alarms.put( m_nextAlarmToken, new Alarm( time, day ) ); this.m_alarms.put(this.m_nextAlarmToken, new Alarm(time, day));
return m_nextAlarmToken++; return this.m_nextAlarmToken++;
} }
} }
/** /**
* Cancels an alarm previously started with setAlarm. This will stop the * Cancels an alarm previously started with setAlarm. This will stop the alarm from firing.
* alarm from firing.
* *
* @param token The ID of the alarm to cancel. * @param token The ID of the alarm to cancel.
* @see #setAlarm To set an alarm. * @see #setAlarm To set an alarm.
*/ */
@LuaFunction @LuaFunction
public final void cancelAlarm( int token ) public final void cancelAlarm(int token) {
{ synchronized (this.m_alarms) {
synchronized( m_alarms ) this.m_alarms.remove(token);
{
m_alarms.remove( token );
} }
} }
/** /**
* Shuts down the computer immediately. * Shuts down the computer immediately.
*/ */
@LuaFunction( "shutdown" ) @LuaFunction ("shutdown")
public final void doShutdown() public final void doShutdown() {
{ this.apiEnvironment.shutdown();
apiEnvironment.shutdown();
} }
/** /**
* Reboots the computer immediately. * Reboots the computer immediately.
*/ */
@LuaFunction( "reboot" ) @LuaFunction ("reboot")
public final void doReboot() public final void doReboot() {
{ this.apiEnvironment.reboot();
apiEnvironment.reboot();
} }
/** /**
@@ -257,10 +202,12 @@ public class OSAPI implements ILuaAPI
* *
* @return The ID of the computer. * @return The ID of the computer.
*/ */
@LuaFunction( { "getComputerID", "computerID" } ) @LuaFunction ({
public final int getComputerID() "getComputerID",
{ "computerID"
return apiEnvironment.getComputerID(); })
public final int getComputerID() {
return this.apiEnvironment.getComputerID();
} }
/** /**
@@ -269,11 +216,13 @@ public class OSAPI implements ILuaAPI
* @return The label of the computer. * @return The label of the computer.
* @cc.treturn string The label of the computer. * @cc.treturn string The label of the computer.
*/ */
@LuaFunction( { "getComputerLabel", "computerLabel" } ) @LuaFunction ({
public final Object[] getComputerLabel() "getComputerLabel",
{ "computerLabel"
String label = apiEnvironment.getLabel(); })
return label == null ? null : new Object[] { label }; public final Object[] getComputerLabel() {
String label = this.apiEnvironment.getLabel();
return label == null ? null : new Object[] {label};
} }
/** /**
@@ -282,9 +231,8 @@ public class OSAPI implements ILuaAPI
* @param label The new label. May be {@code nil} in order to clear it. * @param label The new label. May be {@code nil} in order to clear it.
*/ */
@LuaFunction @LuaFunction
public final void setComputerLabel( Optional<String> label ) public final void setComputerLabel(Optional<String> label) {
{ this.apiEnvironment.setLabel(StringUtil.normaliseLabel(label.orElse(null)));
apiEnvironment.setLabel( StringUtil.normaliseLabel( label.orElse( null ) ) );
} }
/** /**
@@ -293,137 +241,139 @@ public class OSAPI implements ILuaAPI
* @return The computer's uptime. * @return The computer's uptime.
*/ */
@LuaFunction @LuaFunction
public final double clock() public final double clock() {
{ return this.m_clock * 0.05;
return m_clock * 0.05;
} }
/** /**
* Returns the current time depending on the string passed in. This will * Returns the current time depending on the string passed in. This will always be in the range [0.0, 24.0).
* always be in the range [0.0, 24.0).
* *
* * If called with {@code ingame}, the current world time will be returned. * * If called with {@code ingame}, the current world time will be returned. This is the default if nothing is passed. * If called with {@code utc},
* This is the default if nothing is passed. * returns the hour of the day in UTC time. * If called with {@code local}, returns the hour of the day in the timezone the server is located in.
* * If called with {@code utc}, returns the hour of the day in UTC time.
* * If called with {@code local}, returns the hour of the day in the
* timezone the server is located in.
* *
* This function can also be called with a table returned from {@link #date}, * This function can also be called with a table returned from {@link #date}, which will convert the date fields into a UNIX timestamp (number of
* which will convert the date fields into a UNIX timestamp (number of
* seconds since 1 January 1970). * seconds since 1 January 1970).
* *
* @param args The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame} locale if not specified. * @param args The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame} locale if not specified.
* @return The hour of the selected locale, or a UNIX timestamp from the table, depending on the argument passed in. * @return The hour of the selected locale, or a UNIX timestamp from the table, depending on the argument passed in.
* @throws LuaException If an invalid locale is passed. * @throws LuaException If an invalid locale is passed.
* @cc.tparam [opt] string|table locale The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame} locale if not specified. * @cc.tparam [opt] string|table locale The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code ingame}
* locale if not specified.
* @see #date To get a date table that can be converted with this function. * @see #date To get a date table that can be converted with this function.
*/ */
@LuaFunction @LuaFunction
public final Object time( IArguments args ) throws LuaException public final Object time(IArguments args) throws LuaException {
{ Object value = args.get(0);
Object value = args.get( 0 ); if (value instanceof Map) {
if( value instanceof Map ) return LuaDateTime.fromTable( (Map<?, ?>) value ); return LuaDateTime.fromTable((Map<?, ?>) value);
String param = args.optString( 0, "ingame" );
switch( param.toLowerCase( Locale.ROOT ) )
{
case "utc": // Get Hour of day (UTC)
return getTimeForCalendar( Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ) );
case "local": // Get Hour of day (local time)
return getTimeForCalendar( Calendar.getInstance() );
case "ingame": // Get in-game hour
return m_time;
default:
throw new LuaException( "Unsupported operation" );
} }
String param = args.optString(0, "ingame");
switch (param.toLowerCase(Locale.ROOT)) {
case "utc": // Get Hour of day (UTC)
return getTimeForCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
case "local": // Get Hour of day (local time)
return getTimeForCalendar(Calendar.getInstance());
case "ingame": // Get in-game hour
return this.m_time;
default:
throw new LuaException("Unsupported operation");
}
}
private static float getTimeForCalendar(Calendar c) {
float time = c.get(Calendar.HOUR_OF_DAY);
time += c.get(Calendar.MINUTE) / 60.0f;
time += c.get(Calendar.SECOND) / (60.0f * 60.0f);
return time;
} }
/** /**
* Returns the day depending on the locale specified. * Returns the day depending on the locale specified.
* *
* * If called with {@code ingame}, returns the number of days since the * * If called with {@code ingame}, returns the number of days since the world was created. This is the default. * If called with {@code utc}, returns
* world was created. This is the default. * the number of days since 1 January 1970 in the UTC timezone. * If called with {@code local}, returns the number of days since 1 January 1970 in the
* * If called with {@code utc}, returns the number of days since 1 January * server's local timezone.
* 1970 in the UTC timezone.
* * If called with {@code local}, returns the number of days since 1
* January 1970 in the server's local timezone.
* *
* @param args The locale to get the day for. Defaults to {@code ingame} if not set. * @param args The locale to get the day for. Defaults to {@code ingame} if not set.
* @return The day depending on the selected locale. * @return The day depending on the selected locale.
* @throws LuaException If an invalid locale is passed. * @throws LuaException If an invalid locale is passed.
*/ */
@LuaFunction @LuaFunction
public final int day( Optional<String> args ) throws LuaException public final int day(Optional<String> args) throws LuaException {
{ switch (args.orElse("ingame")
switch( args.orElse( "ingame" ).toLowerCase( Locale.ROOT ) ) .toLowerCase(Locale.ROOT)) {
{
case "utc": // Get numbers of days since 1970-01-01 (utc) case "utc": // Get numbers of days since 1970-01-01 (utc)
return getDayForCalendar( Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ) ); return getDayForCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
case "local": // Get numbers of days since 1970-01-01 (local time) case "local": // Get numbers of days since 1970-01-01 (local time)
return getDayForCalendar( Calendar.getInstance() ); return getDayForCalendar(Calendar.getInstance());
case "ingame":// Get game day case "ingame":// Get game day
return m_day; return this.m_day;
default: default:
throw new LuaException( "Unsupported operation" ); throw new LuaException("Unsupported operation");
} }
} }
private static int getDayForCalendar(Calendar c) {
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
int year = c.get(Calendar.YEAR);
int day = 0;
for (int y = 1970; y < year; y++) {
day += g.isLeapYear(y) ? 366 : 365;
}
day += c.get(Calendar.DAY_OF_YEAR);
return day;
}
/** /**
* Returns the number of seconds since an epoch depending on the locale. * Returns the number of seconds since an epoch depending on the locale.
* *
* * If called with {@code ingame}, returns the number of seconds since the * * If called with {@code ingame}, returns the number of seconds since the world was created. This is the default. * If called with {@code utc},
* world was created. This is the default. * returns the number of seconds since 1 January 1970 in the UTC timezone. * If called with {@code local}, returns the number of seconds since 1 January
* * If called with {@code utc}, returns the number of seconds since 1 * 1970 in the server's local timezone.
* January 1970 in the UTC timezone.
* * If called with {@code local}, returns the number of seconds since 1
* January 1970 in the server's local timezone.
* *
* @param args The locale to get the seconds for. Defaults to {@code ingame} if not set. * @param args The locale to get the seconds for. Defaults to {@code ingame} if not set.
* @return The seconds since the epoch depending on the selected locale. * @return The seconds since the epoch depending on the selected locale.
* @throws LuaException If an invalid locale is passed. * @throws LuaException If an invalid locale is passed.
*/ */
@LuaFunction @LuaFunction
public final long epoch( Optional<String> args ) throws LuaException public final long epoch(Optional<String> args) throws LuaException {
{ switch (args.orElse("ingame")
switch( args.orElse( "ingame" ).toLowerCase( Locale.ROOT ) ) .toLowerCase(Locale.ROOT)) {
{ case "utc": {
case "utc":
{
// Get utc epoch // Get utc epoch
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
return getEpochForCalendar( c ); return getEpochForCalendar(c);
} }
case "local": case "local": {
{
// Get local epoch // Get local epoch
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
return getEpochForCalendar( c ); return getEpochForCalendar(c);
} }
case "ingame": case "ingame":
// Get in-game epoch // Get in-game epoch
synchronized( m_alarms ) synchronized (this.m_alarms) {
{ return this.m_day * 86400000 + (int) (this.m_time * 3600000.0f);
return m_day * 86400000 + (int) (m_time * 3600000.0f);
} }
default: default:
throw new LuaException( "Unsupported operation" ); throw new LuaException("Unsupported operation");
} }
} }
private static long getEpochForCalendar(Calendar c) {
return c.getTime()
.getTime();
}
/** /**
* Returns a date string (or table) using a specified format string and * Returns a date string (or table) using a specified format string and optional time to format.
* optional time to format.
* *
* The format string takes the same formats as C's {@code strftime} function * The format string takes the same formats as C's {@code strftime} function (http://www.cplusplus.com/reference/ctime/strftime/). In extension, it can
* (http://www.cplusplus.com/reference/ctime/strftime/). In extension, it * be prefixed with an exclamation mark ({@code !}) to use UTC time instead of the server's local timezone.
* can be prefixed with an exclamation mark ({@code !}) to use UTC time
* instead of the server's local timezone.
* *
* If the format is exactly {@code *t} (optionally prefixed with {@code !}), a * If the format is exactly {@code *t} (optionally prefixed with {@code !}), a table will be returned instead. This table has fields for the year,
* table will be returned instead. This table has fields for the year, month, * month, day, hour, minute, second, day of the week, day of the year, and whether Daylight Savings Time is in effect. This table can be converted to a
* day, hour, minute, second, day of the week, day of the year, and whether * UNIX timestamp (days since 1 January 1970) with {@link #date}.
* Daylight Savings Time is in effect. This table can be converted to a UNIX
* timestamp (days since 1 January 1970) with {@link #date}.
* *
* @param formatA The format of the string to return. This defaults to {@code %c}, which expands to a string similar to "Sat Dec 24 16:58:00 2011". * @param formatA The format of the string to return. This defaults to {@code %c}, which expands to a string similar to "Sat Dec 24 16:58:00 2011".
* @param timeA The time to convert to a string. This defaults to the current time. * @param timeA The time to convert to a string. This defaults to the current time.
@@ -431,32 +381,50 @@ public class OSAPI implements ILuaAPI
* @throws LuaException If an invalid format is passed. * @throws LuaException If an invalid format is passed.
*/ */
@LuaFunction @LuaFunction
public final Object date( Optional<String> formatA, Optional<Long> timeA ) throws LuaException public final Object date(Optional<String> formatA, Optional<Long> timeA) throws LuaException {
{ String format = formatA.orElse("%c");
String format = formatA.orElse( "%c" ); long time = timeA.orElseGet(() -> Instant.now()
long time = timeA.orElseGet( () -> Instant.now().getEpochSecond() ); .getEpochSecond());
Instant instant = Instant.ofEpochSecond( time ); Instant instant = Instant.ofEpochSecond(time);
ZonedDateTime date; ZonedDateTime date;
ZoneOffset offset; ZoneOffset offset;
if( format.startsWith( "!" ) ) if (format.startsWith("!")) {
{
offset = ZoneOffset.UTC; offset = ZoneOffset.UTC;
date = ZonedDateTime.ofInstant( instant, offset ); date = ZonedDateTime.ofInstant(instant, offset);
format = format.substring( 1 ); format = format.substring(1);
} } else {
else
{
ZoneId id = ZoneId.systemDefault(); ZoneId id = ZoneId.systemDefault();
offset = id.getRules().getOffset( instant ); offset = id.getRules()
date = ZonedDateTime.ofInstant( instant, id ); .getOffset(instant);
date = ZonedDateTime.ofInstant(instant, id);
} }
if( format.equals( "*t" ) ) return LuaDateTime.toTable( date, offset, instant ); if (format.equals("*t")) {
return LuaDateTime.toTable(date, offset, instant);
}
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder(); DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
LuaDateTime.format( formatter, format, offset ); LuaDateTime.format(formatter, format, offset);
return formatter.toFormatter( Locale.ROOT ).format( date ); return formatter.toFormatter(Locale.ROOT)
.format(date);
}
private static class Alarm implements Comparable<Alarm> {
final double m_time;
final int m_day;
Alarm(double time, int day) {
this.m_time = time;
this.m_day = day;
}
@Override
public int compareTo(@Nonnull Alarm o) {
double t = this.m_day * 24.0 + this.m_time;
double ot = this.m_day * 24.0 + this.m_time;
return Double.compare(t, ot);
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More