mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 12:10:30 +00:00
Switch the core library to be non-null by default
See comments in c8c128d335
for further
details. This requires /relatively/ few changes - mostly cases we were
missing @Nullable annotations.
This commit is contained in:
parent
c8c128d335
commit
c82f37d3bf
@ -6,6 +6,7 @@ import java.nio.charset.StandardCharsets
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
|
idea
|
||||||
jacoco
|
jacoco
|
||||||
checkstyle
|
checkstyle
|
||||||
id("com.diffplug.spotless")
|
id("com.diffplug.spotless")
|
||||||
@ -139,3 +140,12 @@ spotless {
|
|||||||
ktlint().editorConfigOverride(ktlintConfig)
|
ktlint().editorConfigOverride(ktlintConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idea.module {
|
||||||
|
excludeDirs.addAll(project.files("run", "out", "logs").files)
|
||||||
|
|
||||||
|
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
|
||||||
|
// This is required for Loom, and we patch Forge's run configurations to work there.
|
||||||
|
// TODO: Submit a patch to Forge to support ProjectRootManager.
|
||||||
|
inheritOutputDirs = true
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ dependencies {
|
|||||||
compileOnly(project(":mc-stubs"))
|
compileOnly(project(":mc-stubs"))
|
||||||
compileOnlyApi(libs.jsr305)
|
compileOnlyApi(libs.jsr305)
|
||||||
compileOnlyApi(libs.checkerFramework)
|
compileOnlyApi(libs.checkerFramework)
|
||||||
|
compileOnlyApi(libs.jetbrainsAnnotations)
|
||||||
|
|
||||||
"docApi"(project(":"))
|
"docApi"(project(":"))
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -387,7 +389,9 @@ 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 {
|
@Nullable
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default String optString(int index, @Nullable String def) throws LuaException {
|
||||||
return optString(index).orElse(def);
|
return optString(index).orElse(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +403,9 @@ 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 {
|
@Nullable
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default Map<?, ?> optTable(int index, @Nullable Map<Object, Object> def) throws LuaException {
|
||||||
return optTable(index).orElse(def);
|
return optTable(index).orElse(def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ public interface IComputerAccess {
|
|||||||
* @see #unmount(String)
|
* @see #unmount(String)
|
||||||
* @see IMount
|
* @see IMount
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
String mountWritable(String desiredLocation, IWritableMount mount, String driveName);
|
String mountWritable(String desiredLocation, IWritableMount mount, String driveName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,6 +5,7 @@ plugins {
|
|||||||
id("cc-tweaked.kotlin-convention")
|
id("cc-tweaked.kotlin-convention")
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
id("cc-tweaked.publishing")
|
id("cc-tweaked.publishing")
|
||||||
|
id("cc-tweaked.errorprone")
|
||||||
id("cc-tweaked")
|
id("cc-tweaked")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.apis;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
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;
|
||||||
@ -20,7 +19,7 @@ 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);
|
||||||
|
|
||||||
public static synchronized void register(@Nonnull ILuaAPIFactory factory) {
|
public static synchronized void register(ILuaAPIFactory factory) {
|
||||||
Objects.requireNonNull(factory, "provider cannot be null");
|
Objects.requireNonNull(factory, "provider cannot be null");
|
||||||
factories.add(factory);
|
factories.add(factory);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import dan200.computercraft.core.filesystem.FileSystemException;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -40,8 +40,9 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
mounts.clear();
|
mounts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName) {
|
public synchronized String mount(String desiredLoc, IMount mount, 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");
|
||||||
@ -49,14 +50,14 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
// Mount the location
|
// Mount the location
|
||||||
String location;
|
String location;
|
||||||
var fileSystem = environment.getFileSystem();
|
var fileSystem = environment.getFileSystem();
|
||||||
if (fileSystem == null) throw new IllegalStateException("File system has not been created");
|
|
||||||
|
|
||||||
synchronized (fileSystem) {
|
synchronized (fileSystem) {
|
||||||
location = findFreeLocation(desiredLoc);
|
location = findFreeLocation(desiredLoc);
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
try {
|
try {
|
||||||
fileSystem.mount(driveName, location, mount);
|
fileSystem.mount(driveName, location, mount);
|
||||||
} catch (FileSystemException ignored) {
|
} catch (FileSystemException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,8 +66,9 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName) {
|
public synchronized String mountWritable(String desiredLoc, IWritableMount mount, 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");
|
||||||
@ -74,14 +76,14 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
// Mount the location
|
// Mount the location
|
||||||
String location;
|
String location;
|
||||||
var fileSystem = environment.getFileSystem();
|
var fileSystem = environment.getFileSystem();
|
||||||
if (fileSystem == null) throw new IllegalStateException("File system has not been created");
|
|
||||||
|
|
||||||
synchronized (fileSystem) {
|
synchronized (fileSystem) {
|
||||||
location = findFreeLocation(desiredLoc);
|
location = findFreeLocation(desiredLoc);
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
try {
|
try {
|
||||||
fileSystem.mountWritable(driveName, location, mount);
|
fileSystem.mountWritable(driveName, location, mount);
|
||||||
} catch (FileSystemException ignored) {
|
} catch (FileSystemException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +93,7 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unmount(String location) {
|
public void unmount(@Nullable String location) {
|
||||||
if (location == null) return;
|
if (location == null) return;
|
||||||
if (!mounts.contains(location)) throw new IllegalStateException("You didn't mount this location");
|
if (!mounts.contains(location)) throw new IllegalStateException("You didn't mount this location");
|
||||||
|
|
||||||
@ -105,17 +107,17 @@ public abstract class ComputerAccess implements IComputerAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent(@Nonnull String event, Object... arguments) {
|
public void queueEvent(String event, @Nullable Object... arguments) {
|
||||||
Objects.requireNonNull(event, "event cannot be null");
|
Objects.requireNonNull(event, "event cannot be null");
|
||||||
environment.queueEvent(event, arguments);
|
environment.queueEvent(event, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkMonitor getMainThreadMonitor() {
|
public IWorkMonitor getMainThreadMonitor() {
|
||||||
return environment.getMainThreadMonitor();
|
return environment.getMainThreadMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private String findFreeLocation(String desiredLoc) {
|
private String findFreeLocation(String desiredLoc) {
|
||||||
try {
|
try {
|
||||||
var fileSystem = environment.getFileSystem();
|
var fileSystem = environment.getFileSystem();
|
||||||
|
@ -17,6 +17,7 @@ import dan200.computercraft.core.filesystem.FileSystem;
|
|||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.nio.file.attribute.FileTime;
|
import java.nio.file.attribute.FileTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -58,7 +59,7 @@ import java.util.function.Function;
|
|||||||
*/
|
*/
|
||||||
public class FSAPI implements ILuaAPI {
|
public class FSAPI implements ILuaAPI {
|
||||||
private final IAPIEnvironment environment;
|
private final IAPIEnvironment environment;
|
||||||
private FileSystem fileSystem = null;
|
private @Nullable FileSystem fileSystem = null;
|
||||||
|
|
||||||
public FSAPI(IAPIEnvironment env) {
|
public FSAPI(IAPIEnvironment env) {
|
||||||
environment = env;
|
environment = env;
|
||||||
@ -79,6 +80,12 @@ public class FSAPI implements ILuaAPI {
|
|||||||
fileSystem = null;
|
fileSystem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FileSystem getFileSystem() {
|
||||||
|
var filesystem = fileSystem;
|
||||||
|
if (filesystem == null) throw new IllegalStateException("File system is not mounted");
|
||||||
|
return filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of files in a directory.
|
* Returns a list of files in a directory.
|
||||||
*
|
*
|
||||||
@ -97,7 +104,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final String[] list(String path) throws LuaException {
|
public final String[] list(String path) throws LuaException {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
try {
|
try {
|
||||||
return fileSystem.list(path);
|
return getFileSystem().list(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -178,7 +185,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final long getSize(String path) throws LuaException {
|
public final long getSize(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
return fileSystem.getSize(path);
|
return getFileSystem().getSize(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -193,7 +200,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final boolean exists(String path) {
|
public final boolean exists(String path) {
|
||||||
try {
|
try {
|
||||||
return fileSystem.exists(path);
|
return getFileSystem().exists(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -208,7 +215,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final boolean isDir(String path) {
|
public final boolean isDir(String path) {
|
||||||
try {
|
try {
|
||||||
return fileSystem.isDir(path);
|
return getFileSystem().isDir(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -223,7 +230,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final boolean isReadOnly(String path) {
|
public final boolean isReadOnly(String path) {
|
||||||
try {
|
try {
|
||||||
return fileSystem.isReadOnly(path);
|
return getFileSystem().isReadOnly(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -239,7 +246,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final void makeDir(String path) throws LuaException {
|
public final void makeDir(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
fileSystem.makeDir(path);
|
getFileSystem().makeDir(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -258,7 +265,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final void move(String path, String dest) throws LuaException {
|
public final void move(String path, String dest) throws LuaException {
|
||||||
try {
|
try {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
fileSystem.move(path, dest);
|
getFileSystem().move(path, dest);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -277,7 +284,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final void copy(String path, String dest) throws LuaException {
|
public final void copy(String path, String dest) throws LuaException {
|
||||||
try {
|
try {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
fileSystem.copy(path, dest);
|
getFileSystem().copy(path, dest);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -296,7 +303,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final void delete(String path) throws LuaException {
|
public final void delete(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
fileSystem.delete(path);
|
getFileSystem().delete(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -363,32 +370,32 @@ public class FSAPI implements ILuaAPI {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "r" -> {
|
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
|
||||||
var reader = fileSystem.openForRead(path, EncodedReadableHandle::openUtf8);
|
var reader = getFileSystem().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
|
||||||
var writer = fileSystem.openForWrite(path, false, EncodedWritableHandle::openUtf8);
|
var writer = getFileSystem().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
|
||||||
var writer = fileSystem.openForWrite(path, true, EncodedWritableHandle::openUtf8);
|
var writer = getFileSystem().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
|
||||||
var reader = fileSystem.openForRead(path, Function.identity());
|
var reader = getFileSystem().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
|
||||||
var writer = fileSystem.openForWrite(path, false, Function.identity());
|
var writer = getFileSystem().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
|
||||||
var writer = fileSystem.openForWrite(path, true, Function.identity());
|
var writer = getFileSystem().openForWrite(path, true, Function.identity());
|
||||||
return new Object[]{ BinaryWritableHandle.of(writer.get(), writer) };
|
return new Object[]{ BinaryWritableHandle.of(writer.get(), writer) };
|
||||||
}
|
}
|
||||||
default -> throw new LuaException("Unsupported mode");
|
default -> throw new LuaException("Unsupported mode");
|
||||||
@ -404,7 +411,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
* @param path The path to get the drive of.
|
* @param path The path to get the drive of.
|
||||||
* @return The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
* @return The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||||
* @throws LuaException If the path doesn't exist.
|
* @throws LuaException If the path doesn't exist.
|
||||||
* @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|nil The name of the drive that the file is on; e.g. {@code hdd} for local files, or {@code rom} for ROM files.
|
||||||
* @cc.usage Print the drives of a couple of mounts:
|
* @cc.usage Print the drives of a couple of mounts:
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
@ -412,10 +419,11 @@ public class FSAPI implements ILuaAPI {
|
|||||||
* print("/rom/: " .. fs.getDrive("rom"))
|
* print("/rom/: " .. fs.getDrive("rom"))
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] getDrive(String path) throws LuaException {
|
public final Object[] getDrive(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
return fileSystem.exists(path) ? new Object[]{ fileSystem.getMountLabel(path) } : null;
|
return getFileSystem().exists(path) ? new Object[]{ getFileSystem().getMountLabel(path) } : null;
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -435,7 +443,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object getFreeSpace(String path) throws LuaException {
|
public final Object getFreeSpace(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
var freeSpace = fileSystem.getFreeSpace(path);
|
var freeSpace = getFileSystem().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());
|
||||||
@ -459,7 +467,7 @@ public class FSAPI implements ILuaAPI {
|
|||||||
public final String[] find(String path) throws LuaException {
|
public final String[] find(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
environment.observe(Metrics.FS_OPS);
|
environment.observe(Metrics.FS_OPS);
|
||||||
return fileSystem.find(path);
|
return getFileSystem().find(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
throw new LuaException(e.getMessage());
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -476,10 +484,11 @@ public class FSAPI implements ILuaAPI {
|
|||||||
* @cc.since 1.87.0
|
* @cc.since 1.87.0
|
||||||
* @see #getFreeSpace To get the free space available on this drive.
|
* @see #getFreeSpace To get the free space available on this drive.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object getCapacity(String path) throws LuaException {
|
public final Object getCapacity(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
var capacity = fileSystem.getCapacity(path);
|
var capacity = getFileSystem().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());
|
||||||
@ -508,21 +517,21 @@ public class FSAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Map<String, Object> attributes(String path) throws LuaException {
|
public final Map<String, Object> attributes(String path) throws LuaException {
|
||||||
try {
|
try {
|
||||||
var attributes = fileSystem.getAttributes(path);
|
var attributes = getFileSystem().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());
|
||||||
result.put("isReadOnly", fileSystem.isReadOnly(path));
|
result.put("isReadOnly", getFileSystem().isReadOnly(path));
|
||||||
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(@Nullable FileTime time) {
|
||||||
return time == null ? 0 : time.toMillis();
|
return time == null ? 0 : time.toMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import io.netty.handler.codec.http.HttpHeaderNames;
|
|||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -35,7 +34,7 @@ import static dan200.computercraft.core.apis.TableHelper.*;
|
|||||||
public class HTTPAPI implements ILuaAPI {
|
public class HTTPAPI implements ILuaAPI {
|
||||||
private final IAPIEnvironment apiEnvironment;
|
private final IAPIEnvironment apiEnvironment;
|
||||||
|
|
||||||
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>(ResourceGroup.DEFAULT);
|
private final ResourceGroup<CheckUrl> checkUrls = new ResourceGroup<>(() -> ResourceGroup.DEFAULT_LIMIT);
|
||||||
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>(() -> CoreConfig.httpMaxRequests);
|
private final ResourceGroup<HttpRequest> requests = new ResourceQueue<>(() -> CoreConfig.httpMaxRequests);
|
||||||
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>(() -> CoreConfig.httpMaxWebsockets);
|
private final ResourceGroup<Websocket> websockets = new ResourceGroup<>(() -> CoreConfig.httpMaxWebsockets);
|
||||||
|
|
||||||
@ -155,8 +154,7 @@ public class HTTPAPI implements ILuaAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
private HttpHeaders getHeaders(Map<?, ?> headerTable) throws LuaException {
|
||||||
private 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()) {
|
||||||
var value = entry.getValue();
|
var value = entry.getValue();
|
||||||
|
@ -14,7 +14,6 @@ import dan200.computercraft.core.filesystem.FileSystem;
|
|||||||
import dan200.computercraft.core.metrics.Metric;
|
import dan200.computercraft.core.metrics.Metric;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface IAPIEnvironment {
|
public interface IAPIEnvironment {
|
||||||
@ -27,16 +26,12 @@ public interface IAPIEnvironment {
|
|||||||
|
|
||||||
int getComputerID();
|
int getComputerID();
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
ComputerEnvironment getComputerEnvironment();
|
ComputerEnvironment getComputerEnvironment();
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
GlobalEnvironment getGlobalEnvironment();
|
GlobalEnvironment getGlobalEnvironment();
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
IWorkMonitor getMainThreadMonitor();
|
IWorkMonitor getMainThreadMonitor();
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
Terminal getTerminal();
|
Terminal getTerminal();
|
||||||
|
|
||||||
FileSystem getFileSystem();
|
FileSystem getFileSystem();
|
||||||
@ -45,7 +40,7 @@ public interface IAPIEnvironment {
|
|||||||
|
|
||||||
void reboot();
|
void reboot();
|
||||||
|
|
||||||
void queueEvent(String event, Object... args);
|
void queueEvent(String event, @Nullable Object... args);
|
||||||
|
|
||||||
void setOutput(ComputerSide side, int output);
|
void setOutput(ComputerSide side, int output);
|
||||||
|
|
||||||
@ -73,7 +68,7 @@ public interface IAPIEnvironment {
|
|||||||
|
|
||||||
void cancelTimer(int id);
|
void cancelTimer(int id);
|
||||||
|
|
||||||
void observe(@Nonnull Metric.Event event, long change);
|
void observe(Metric.Event event, long change);
|
||||||
|
|
||||||
void observe(@Nonnull Metric.Counter counter);
|
void observe(Metric.Counter counter);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.core.apis;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
@ -117,6 +118,7 @@ final class LuaDateTime {
|
|||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private static Boolean getBoolField(Map<?, ?> table, String field) throws LuaException {
|
private static Boolean getBoolField(Map<?, ?> table, String field) throws LuaException {
|
||||||
var value = table.get(field);
|
var value = table.get(field);
|
||||||
if (value instanceof Boolean || value == null) return (Boolean) value;
|
if (value instanceof Boolean || value == null) return (Boolean) value;
|
||||||
|
@ -13,7 +13,7 @@ import dan200.computercraft.core.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 javax.annotation.Nullable;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@ -40,7 +40,7 @@ public class OSAPI implements ILuaAPI {
|
|||||||
|
|
||||||
private record Alarm(double time, int day) implements Comparable<Alarm> {
|
private record Alarm(double time, int day) implements Comparable<Alarm> {
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@Nonnull Alarm o) {
|
public int compareTo(Alarm o) {
|
||||||
var t = day * 24.0 + time;
|
var t = day * 24.0 + time;
|
||||||
var ot = day * 24.0 + time;
|
var ot = day * 24.0 + time;
|
||||||
return Double.compare(t, ot);
|
return Double.compare(t, ot);
|
||||||
@ -241,9 +241,10 @@ public class OSAPI implements ILuaAPI {
|
|||||||
* Returns the label of the computer, or {@code nil} if none is set.
|
* Returns the label of the computer, or {@code nil} if none is set.
|
||||||
*
|
*
|
||||||
* @return The label of the computer.
|
* @return The label of the computer.
|
||||||
* @cc.treturn string The label of the computer.
|
* @cc.treturn string|nil The label of the computer.
|
||||||
* @cc.since 1.3
|
* @cc.since 1.3
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction({ "getComputerLabel", "computerLabel" })
|
@LuaFunction({ "getComputerLabel", "computerLabel" })
|
||||||
public final Object[] getComputerLabel() {
|
public final Object[] getComputerLabel() {
|
||||||
var label = apiEnvironment.getLabel();
|
var label = apiEnvironment.getLabel();
|
||||||
@ -258,7 +259,7 @@ public class OSAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final void setComputerLabel(Optional<String> label) {
|
public final void setComputerLabel(Optional<String> label) {
|
||||||
apiEnvironment.setLabel(StringUtil.normaliseLabel(label.orElse(null)));
|
apiEnvironment.setLabel(label.map(StringUtil::normaliseLabel).orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,6 @@ import dan200.computercraft.core.computer.ComputerSide;
|
|||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
import dan200.computercraft.core.util.LuaUtil;
|
import dan200.computercraft.core.util.LuaUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -99,20 +98,23 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IComputerAccess implementation
|
// IComputerAccess implementation
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public synchronized String mount(@Nonnull String desiredLoc, @Nonnull IMount mount, @Nonnull String driveName) {
|
public synchronized String mount(String desiredLoc, IMount mount, String driveName) {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
return super.mount(desiredLoc, mount, driveName);
|
return super.mount(desiredLoc, mount, driveName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public synchronized String mountWritable(@Nonnull String desiredLoc, @Nonnull IWritableMount mount, @Nonnull String driveName) {
|
public synchronized String mountWritable(String desiredLoc, IWritableMount mount, String driveName) {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
return super.mountWritable(desiredLoc, mount, driveName);
|
return super.mountWritable(desiredLoc, mount, driveName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmount(String location) {
|
public synchronized void unmount(@Nullable String location) {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
super.unmount(location);
|
super.unmount(location);
|
||||||
}
|
}
|
||||||
@ -124,19 +126,17 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent(@Nonnull String event, Object... arguments) {
|
public void queueEvent(String event, @Nullable Object... arguments) {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
super.queueEvent(event, arguments);
|
super.queueEvent(event, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttachmentName() {
|
public String getAttachmentName() {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
return side;
|
return side;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, IPeripheral> getAvailablePeripherals() {
|
public Map<String, IPeripheral> getAvailablePeripherals() {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
@ -153,7 +153,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public IPeripheral getAvailablePeripheral(@Nonnull String name) {
|
public IPeripheral getAvailablePeripheral(String name) {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
|
|
||||||
for (var wrapper : peripherals) {
|
for (var wrapper : peripherals) {
|
||||||
@ -164,7 +164,6 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkMonitor getMainThreadMonitor() {
|
public IWorkMonitor getMainThreadMonitor() {
|
||||||
if (!attached) throw new NotAttachedException();
|
if (!attached) throw new NotAttachedException();
|
||||||
@ -185,7 +184,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
// IPeripheralChangeListener
|
// IPeripheralChangeListener
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPeripheralChanged(ComputerSide side, IPeripheral newPeripheral) {
|
public void onPeripheralChanged(ComputerSide side, @Nullable IPeripheral newPeripheral) {
|
||||||
synchronized (peripherals) {
|
synchronized (peripherals) {
|
||||||
var index = side.ordinal();
|
var index = side.ordinal();
|
||||||
if (peripherals[index] != null) {
|
if (peripherals[index] != null) {
|
||||||
@ -253,6 +252,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] getType(String sideName) {
|
public final Object[] getType(String sideName) {
|
||||||
var side = ComputerSide.valueOfInsensitive(sideName);
|
var side = ComputerSide.valueOfInsensitive(sideName);
|
||||||
@ -264,6 +264,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] hasType(String sideName, String type) {
|
public final Object[] hasType(String sideName, String type) {
|
||||||
var side = ComputerSide.valueOfInsensitive(sideName);
|
var side = ComputerSide.valueOfInsensitive(sideName);
|
||||||
@ -278,6 +279,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] getMethods(String sideName) {
|
public final Object[] getMethods(String sideName) {
|
||||||
var side = ComputerSide.valueOfInsensitive(sideName);
|
var side = ComputerSide.valueOfInsensitive(sideName);
|
||||||
|
@ -10,6 +10,8 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get and set redstone signals adjacent to this computer.
|
* Get and set redstone signals adjacent to this computer.
|
||||||
* <p>
|
* <p>
|
||||||
@ -72,7 +74,7 @@ public class RedstoneAPI implements ILuaAPI {
|
|||||||
* @cc.since 1.2
|
* @cc.since 1.2
|
||||||
*/
|
*/
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final String[] getSides() {
|
public final List<String> getSides() {
|
||||||
return ComputerSide.NAMES;
|
return ComputerSide.NAMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ package dan200.computercraft.core.apis;
|
|||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.api.lua.LuaValues;
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -22,17 +21,15 @@ public final class TableHelper {
|
|||||||
throw new IllegalStateException("Cannot instantiate singleton " + getClass().getName());
|
throw new IllegalStateException("Cannot instantiate singleton " + getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public static LuaException badKey(String key, String expected, @Nullable Object actual) {
|
||||||
public static LuaException badKey(@Nonnull String key, @Nonnull String expected, @Nullable Object actual) {
|
|
||||||
return badKey(key, expected, LuaValues.getType(actual));
|
return badKey(key, expected, LuaValues.getType(actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public static LuaException badKey(String key, String expected, String actual) {
|
||||||
public static LuaException badKey(@Nonnull String key, @Nonnull String expected, @Nonnull String actual) {
|
|
||||||
return new LuaException("bad field '" + key + "' (" + expected + " expected, got " + actual + ")");
|
return new LuaException("bad field '" + key + "' (" + expected + " expected, got " + actual + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getNumberField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
public static double getNumberField(Map<?, ?> table, String key) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value instanceof Number) {
|
if (value instanceof Number) {
|
||||||
return ((Number) value).doubleValue();
|
return ((Number) value).doubleValue();
|
||||||
@ -41,7 +38,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIntField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
public static int getIntField(Map<?, ?> table, String key) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value instanceof Number) {
|
if (value instanceof Number) {
|
||||||
return (int) ((Number) value).longValue();
|
return (int) ((Number) value).longValue();
|
||||||
@ -50,11 +47,11 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getRealField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
public static double getRealField(Map<?, ?> table, String key) throws LuaException {
|
||||||
return checkReal(key, getNumberField(table, key));
|
return checkReal(key, getNumberField(table, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean getBooleanField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
public static boolean getBooleanField(Map<?, ?> table, String key) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value instanceof Boolean) {
|
if (value instanceof Boolean) {
|
||||||
return (Boolean) value;
|
return (Boolean) value;
|
||||||
@ -63,8 +60,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public static String getStringField(Map<?, ?> table, String key) throws LuaException {
|
||||||
public static String getStringField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
@ -74,8 +70,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nonnull
|
public static Map<Object, Object> getTableField(Map<?, ?> table, String key) throws LuaException {
|
||||||
public static Map<Object, Object> getTableField(@Nonnull Map<?, ?> table, @Nonnull String key) throws LuaException {
|
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value instanceof Map) {
|
if (value instanceof Map) {
|
||||||
return (Map<Object, Object>) value;
|
return (Map<Object, Object>) value;
|
||||||
@ -84,7 +79,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double optNumberField(@Nonnull Map<?, ?> table, @Nonnull String key, double def) throws LuaException {
|
public static double optNumberField(Map<?, ?> table, String key, double def) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return def;
|
return def;
|
||||||
@ -95,7 +90,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int optIntField(@Nonnull Map<?, ?> table, @Nonnull String key, int def) throws LuaException {
|
public static int optIntField(Map<?, ?> table, String key, int def) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return def;
|
return def;
|
||||||
@ -106,11 +101,11 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double optRealField(@Nonnull Map<?, ?> table, @Nonnull String key, double def) throws LuaException {
|
public static double optRealField(Map<?, ?> table, String key, double def) throws LuaException {
|
||||||
return checkReal(key, optNumberField(table, key, def));
|
return checkReal(key, optNumberField(table, key, def));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean optBooleanField(@Nonnull Map<?, ?> table, @Nonnull String key, boolean def) throws LuaException {
|
public static boolean optBooleanField(Map<?, ?> table, String key, boolean def) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return def;
|
return def;
|
||||||
@ -121,7 +116,8 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String optStringField(@Nonnull Map<?, ?> table, @Nonnull String key, String def) throws LuaException {
|
@Nullable
|
||||||
|
public static String optStringField(Map<?, ?> table, String key, @Nullable String def) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return def;
|
return def;
|
||||||
@ -133,7 +129,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static Map<Object, Object> optTableField(@Nonnull Map<?, ?> table, @Nonnull String key, Map<Object, Object> def) throws LuaException {
|
public static Map<Object, Object> optTableField(Map<?, ?> table, String key, Map<Object, Object> def) throws LuaException {
|
||||||
var value = table.get(key);
|
var value = table.get(key);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return def;
|
return def;
|
||||||
@ -144,7 +140,7 @@ public final class TableHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double checkReal(@Nonnull String key, double value) throws LuaException {
|
private static double checkReal(String key, double value) throws LuaException {
|
||||||
if (!Double.isFinite(value)) throw badKey(key, "number", getNumericType(value));
|
if (!Double.isFinite(value)) throw badKey(key, "number", getNumericType(value));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.api.lua.LuaFunction;
|
|||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interact with a computer's terminal or monitors, writing text and drawing
|
* Interact with a computer's terminal or monitors, writing text and drawing
|
||||||
@ -51,7 +50,6 @@ public class TermAPI extends TermMethods implements ILuaAPI {
|
|||||||
return new Object[]{ c.getR(), c.getG(), c.getB() };
|
return new Object[]{ c.getR(), c.getG(), c.getB() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Terminal getTerminal() {
|
public Terminal getTerminal() {
|
||||||
return terminal;
|
return terminal;
|
||||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.core.terminal.Palette;
|
|||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.util.StringUtil;
|
import dan200.computercraft.core.util.StringUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +29,6 @@ public abstract class TermMethods {
|
|||||||
return bit;
|
return bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public abstract Terminal getTerminal() throws LuaException;
|
public abstract Terminal getTerminal() throws LuaException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,7 +8,9 @@ package dan200.computercraft.core.apis.handles;
|
|||||||
import dan200.computercraft.api.lua.LuaException;
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||||
|
import dan200.computercraft.core.util.Nullability;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -28,10 +30,10 @@ public class BinaryReadableHandle extends HandleGeneric {
|
|||||||
private static final int BUFFER_SIZE = 8192;
|
private static final int BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
private final ReadableByteChannel reader;
|
private final ReadableByteChannel reader;
|
||||||
final SeekableByteChannel seekable;
|
final @Nullable SeekableByteChannel seekable;
|
||||||
private final ByteBuffer single = ByteBuffer.allocate(1);
|
private final ByteBuffer single = ByteBuffer.allocate(1);
|
||||||
|
|
||||||
BinaryReadableHandle(ReadableByteChannel reader, SeekableByteChannel seekable, TrackingCloseable closeable) {
|
BinaryReadableHandle(ReadableByteChannel reader, @Nullable SeekableByteChannel seekable, TrackingCloseable closeable) {
|
||||||
super(closeable);
|
super(closeable);
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.seekable = seekable;
|
this.seekable = seekable;
|
||||||
@ -59,6 +61,7 @@ public class BinaryReadableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn [3] string The bytes read as a string. This is returned when the {@code count} is given.
|
* @cc.treturn [3] string The bytes read as a string. This is returned when the {@code count} is given.
|
||||||
* @cc.changed 1.80pr1 Now accepts an integer argument to read multiple bytes, returning a string instead of a number.
|
* @cc.changed 1.80pr1 Now accepts an integer argument to read multiple bytes, returning a string instead of a number.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] read(Optional<Integer> countArg) throws LuaException {
|
public final Object[] read(Optional<Integer> countArg) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@ -130,6 +133,7 @@ public class BinaryReadableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
|
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
|
||||||
* @cc.since 1.80pr1
|
* @cc.since 1.80pr1
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] readAll() throws LuaException {
|
public final Object[] readAll() throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@ -164,6 +168,7 @@ public class BinaryReadableHandle extends HandleGeneric {
|
|||||||
* @cc.since 1.80pr1.9
|
* @cc.since 1.80pr1.9
|
||||||
* @cc.changed 1.81.0 `\r` is now stripped.
|
* @cc.changed 1.81.0 `\r` is now stripped.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
|
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@ -230,10 +235,11 @@ public class BinaryReadableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn string The reason seeking failed.
|
* @cc.treturn string The reason seeking failed.
|
||||||
* @cc.since 1.80pr1.9
|
* @cc.since 1.80pr1.9
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
|
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return handleSeek(seekable, whence, offset);
|
return handleSeek(Nullability.assertNonNull(seekable), whence, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,9 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.api.lua.LuaValues;
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||||
|
import dan200.computercraft.core.util.Nullability;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
@ -26,10 +28,10 @@ import java.util.Optional;
|
|||||||
*/
|
*/
|
||||||
public class BinaryWritableHandle extends HandleGeneric {
|
public class BinaryWritableHandle extends HandleGeneric {
|
||||||
private final WritableByteChannel writer;
|
private final WritableByteChannel writer;
|
||||||
final SeekableByteChannel seekable;
|
final @Nullable SeekableByteChannel seekable;
|
||||||
private final ByteBuffer single = ByteBuffer.allocate(1);
|
private final ByteBuffer single = ByteBuffer.allocate(1);
|
||||||
|
|
||||||
protected BinaryWritableHandle(WritableByteChannel writer, SeekableByteChannel seekable, TrackingCloseable closeable) {
|
protected BinaryWritableHandle(WritableByteChannel writer, @Nullable SeekableByteChannel seekable, TrackingCloseable closeable) {
|
||||||
super(closeable);
|
super(closeable);
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.seekable = seekable;
|
this.seekable = seekable;
|
||||||
@ -86,7 +88,8 @@ public class BinaryWritableHandle extends HandleGeneric {
|
|||||||
try {
|
try {
|
||||||
// Technically this is not needed
|
// Technically this is not needed
|
||||||
if (writer instanceof FileChannel channel) channel.force(false);
|
if (writer instanceof FileChannel channel) channel.force(false);
|
||||||
} catch (IOException ignored) {
|
} catch (IOException e) {
|
||||||
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,10 +117,11 @@ public class BinaryWritableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn string The reason seeking failed.
|
* @cc.treturn string The reason seeking failed.
|
||||||
* @cc.since 1.80pr1.9
|
* @cc.since 1.80pr1.9
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
|
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return handleSeek(seekable, whence, offset);
|
return handleSeek(Nullability.assertNonNull(seekable), whence, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
@ -30,12 +30,12 @@ public class EncodedReadableHandle extends HandleGeneric {
|
|||||||
|
|
||||||
private final BufferedReader reader;
|
private final BufferedReader reader;
|
||||||
|
|
||||||
public EncodedReadableHandle(@Nonnull BufferedReader reader, @Nonnull TrackingCloseable closable) {
|
public EncodedReadableHandle(BufferedReader reader, TrackingCloseable closable) {
|
||||||
super(closable);
|
super(closable);
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EncodedReadableHandle(@Nonnull BufferedReader reader) {
|
public EncodedReadableHandle(BufferedReader reader) {
|
||||||
this(reader, new TrackingCloseable.Impl(reader));
|
this(reader, new TrackingCloseable.Impl(reader));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +48,7 @@ public class EncodedReadableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
|
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
|
||||||
* @cc.changed 1.81.0 Added option to return trailing newline.
|
* @cc.changed 1.81.0 Added option to return trailing newline.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
|
public final Object[] readLine(Optional<Boolean> withTrailingArg) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@ -73,6 +74,7 @@ public class EncodedReadableHandle extends HandleGeneric {
|
|||||||
* @throws LuaException If the file has been closed.
|
* @throws LuaException If the file has been closed.
|
||||||
* @cc.treturn nil|string The remaining contents of the file, or {@code nil} if we are at the end.
|
* @cc.treturn nil|string The remaining contents of the file, or {@code nil} if we are at the end.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] readAll() throws LuaException {
|
public final Object[] readAll() throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
@ -102,6 +104,7 @@ public class EncodedReadableHandle extends HandleGeneric {
|
|||||||
* @cc.treturn string|nil The read characters, or {@code nil} if at the of the file.
|
* @cc.treturn string|nil The read characters, or {@code nil} if at the of the file.
|
||||||
* @cc.since 1.80pr1.4
|
* @cc.since 1.80pr1.4
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] read(Optional<Integer> countA) throws LuaException {
|
public final Object[] read(Optional<Integer> countA) throws LuaException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
@ -11,7 +11,6 @@ import dan200.computercraft.api.lua.LuaFunction;
|
|||||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||||
import dan200.computercraft.core.util.StringUtil;
|
import dan200.computercraft.core.util.StringUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
@ -28,7 +27,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
public class EncodedWritableHandle extends HandleGeneric {
|
public class EncodedWritableHandle extends HandleGeneric {
|
||||||
private final BufferedWriter writer;
|
private final BufferedWriter writer;
|
||||||
|
|
||||||
public EncodedWritableHandle(@Nonnull BufferedWriter writer, @Nonnull TrackingCloseable closable) {
|
public EncodedWritableHandle(BufferedWriter writer, TrackingCloseable closable) {
|
||||||
super(closable);
|
super(closable);
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
}
|
}
|
||||||
@ -80,7 +79,8 @@ public class EncodedWritableHandle extends HandleGeneric {
|
|||||||
checkOpen();
|
checkOpen();
|
||||||
try {
|
try {
|
||||||
writer.flush();
|
writer.flush();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException e) {
|
||||||
|
throw new LuaException(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,16 @@ import dan200.computercraft.api.lua.LuaFunction;
|
|||||||
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
import dan200.computercraft.core.filesystem.TrackingCloseable;
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channel;
|
import java.nio.channels.Channel;
|
||||||
import java.nio.channels.SeekableByteChannel;
|
import java.nio.channels.SeekableByteChannel;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public abstract class HandleGeneric {
|
public abstract class HandleGeneric {
|
||||||
private TrackingCloseable closeable;
|
private @Nullable TrackingCloseable closeable;
|
||||||
|
|
||||||
protected HandleGeneric(@Nonnull TrackingCloseable closeable) {
|
protected HandleGeneric(TrackingCloseable closeable) {
|
||||||
this.closeable = closeable;
|
this.closeable = closeable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ public abstract class HandleGeneric {
|
|||||||
* @throws LuaException If the arguments were invalid
|
* @throws LuaException If the arguments were invalid
|
||||||
* @see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:seek">{@code file:seek} in the Lua manual.</a>
|
* @see <a href="https://www.lua.org/manual/5.1/manual.html#pdf-file:seek">{@code file:seek} in the Lua manual.</a>
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
protected static Object[] handleSeek(SeekableByteChannel channel, Optional<String> whence, Optional<Long> offset) throws LuaException {
|
protected static Object[] handleSeek(SeekableByteChannel channel, Optional<String> whence, Optional<Long> offset) throws LuaException {
|
||||||
long actualOffset = offset.orElse(0L);
|
long actualOffset = offset.orElse(0L);
|
||||||
try {
|
try {
|
||||||
@ -75,6 +76,7 @@ public abstract class HandleGeneric {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
protected static SeekableByteChannel asSeekable(Channel channel) {
|
protected static SeekableByteChannel asSeekable(Channel channel) {
|
||||||
if (!(channel instanceof SeekableByteChannel seekable)) return null;
|
if (!(channel instanceof SeekableByteChannel seekable)) return null;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http;
|
|||||||
|
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ import java.util.concurrent.Future;
|
|||||||
public class CheckUrl extends Resource<CheckUrl> {
|
public class CheckUrl extends Resource<CheckUrl> {
|
||||||
private static final String EVENT = "http_check";
|
private static final String EVENT = "http_check";
|
||||||
|
|
||||||
private Future<?> future;
|
private @Nullable Future<?> future;
|
||||||
|
|
||||||
private final IAPIEnvironment environment;
|
private final IAPIEnvironment environment;
|
||||||
private final String address;
|
private final String address;
|
||||||
@ -47,7 +48,7 @@ public class CheckUrl extends Resource<CheckUrl> {
|
|||||||
|
|
||||||
if (tryClose()) environment.queueEvent(EVENT, address, true);
|
if (tryClose()) environment.queueEvent(EVENT, address, true);
|
||||||
} catch (HTTPRequestException e) {
|
} catch (HTTPRequestException e) {
|
||||||
if (tryClose()) environment.queueEvent(EVENT, address, false, e.getMessage());
|
if (tryClose()) environment.queueEvent(EVENT, address, false, NetworkUtils.toFriendlyError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import io.netty.handler.traffic.GlobalTrafficShapingHandler;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
@ -65,10 +65,11 @@ public final class NetworkUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final Object sslLock = new Object();
|
private static final Object sslLock = new Object();
|
||||||
private static TrustManagerFactory trustManager;
|
private static @Nullable TrustManagerFactory trustManager;
|
||||||
private static SslContext sslContext;
|
private static @Nullable SslContext sslContext;
|
||||||
private static boolean triedSslContext = false;
|
private static boolean triedSslContext = false;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private static TrustManagerFactory getTrustManager() {
|
private static TrustManagerFactory getTrustManager() {
|
||||||
if (trustManager != null) return trustManager;
|
if (trustManager != null) return trustManager;
|
||||||
synchronized (sslLock) {
|
synchronized (sslLock) {
|
||||||
@ -86,6 +87,7 @@ public final class NetworkUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static SslContext getSslContext() throws HTTPRequestException {
|
public static SslContext getSslContext() throws HTTPRequestException {
|
||||||
if (sslContext != null || triedSslContext) return sslContext;
|
if (sslContext != null || triedSslContext) return sslContext;
|
||||||
synchronized (sslLock) {
|
synchronized (sslLock) {
|
||||||
@ -171,10 +173,10 @@ public final class NetworkUtils {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public static String toFriendlyError(Throwable cause) {
|
||||||
public static String toFriendlyError(@Nonnull Throwable cause) {
|
|
||||||
if (cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException) {
|
if (cause instanceof WebSocketHandshakeException || cause instanceof HTTPRequestException) {
|
||||||
return cause.getMessage();
|
var message = cause.getMessage();
|
||||||
|
return message == null ? "Could not connect" : message;
|
||||||
} else if (cause instanceof TooLongFrameException) {
|
} else if (cause instanceof TooLongFrameException) {
|
||||||
return "Message is too large";
|
return "Message is too large";
|
||||||
} else if (cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException) {
|
} else if (cause instanceof ReadTimeoutException || cause instanceof ConnectTimeoutException) {
|
||||||
|
@ -8,6 +8,7 @@ package dan200.computercraft.core.apis.http;
|
|||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
@ -94,12 +95,14 @@ public abstract class Resource<T extends Resource<T>> implements Closeable {
|
|||||||
return limiter.queue(thisT, () -> task.accept(thisT));
|
return limiter.queue(thisT, () -> task.accept(thisT));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static <T extends Closeable> T closeCloseable(T closeable) {
|
@Nullable
|
||||||
|
protected static <T extends Closeable> T closeCloseable(@Nullable T closeable) {
|
||||||
IoUtil.closeQuietly(closeable);
|
IoUtil.closeQuietly(closeable);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ChannelFuture closeChannel(ChannelFuture future) {
|
@Nullable
|
||||||
|
protected static ChannelFuture closeChannel(@Nullable ChannelFuture future) {
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
future.cancel(false);
|
future.cancel(false);
|
||||||
var channel = future.channel();
|
var channel = future.channel();
|
||||||
@ -109,7 +112,8 @@ public abstract class Resource<T extends Resource<T>> implements Closeable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static <T extends Future<?>> T closeFuture(T future) {
|
@Nullable
|
||||||
|
protected static <T extends Future<?>> T closeFuture(@Nullable T future) {
|
||||||
if (future != null) future.cancel(true);
|
if (future != null) future.cancel(true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,6 @@ import java.util.function.Supplier;
|
|||||||
*/
|
*/
|
||||||
public class ResourceGroup<T extends Resource<T>> {
|
public class ResourceGroup<T extends Resource<T>> {
|
||||||
public static final int DEFAULT_LIMIT = 512;
|
public static final int DEFAULT_LIMIT = 512;
|
||||||
public static final IntSupplier DEFAULT = () -> DEFAULT_LIMIT;
|
|
||||||
|
|
||||||
private static final IntSupplier ZERO = () -> 0;
|
|
||||||
|
|
||||||
final IntSupplier limit;
|
final IntSupplier limit;
|
||||||
|
|
||||||
@ -32,10 +29,6 @@ public class ResourceGroup<T extends Resource<T>> {
|
|||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceGroup() {
|
|
||||||
limit = ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startup() {
|
public void startup() {
|
||||||
active = true;
|
active = true;
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,6 @@ public class ResourceQueue<T extends Resource<T>> extends ResourceGroup<T> {
|
|||||||
super(limit);
|
super(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceQueue() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void shutdown() {
|
public synchronized void shutdown() {
|
||||||
super.shutdown();
|
super.shutdown();
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.apis.http.options;
|
package dan200.computercraft.core.apis.http.options;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.OptionalLong;
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
@ -17,7 +16,6 @@ public enum Action {
|
|||||||
this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
|
this, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
|
||||||
);
|
);
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public PartialOptions toPartial() {
|
public PartialOptions toPartial() {
|
||||||
return partial;
|
return partial;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import com.google.common.net.InetAddresses;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -51,6 +52,7 @@ interface AddressPredicate {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static HostRange parse(String addressStr, String prefixSizeStr) {
|
public static HostRange parse(String addressStr, String prefixSizeStr) {
|
||||||
int prefixSize;
|
int prefixSize;
|
||||||
try {
|
try {
|
||||||
@ -79,11 +81,11 @@ interface AddressPredicate {
|
|||||||
var size = prefixSize;
|
var size = prefixSize;
|
||||||
for (var i = 0; i < minBytes.length; i++) {
|
for (var i = 0; i < minBytes.length; i++) {
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
minBytes[i] &= 0;
|
minBytes[i] = (byte) 0;
|
||||||
maxBytes[i] |= 0xFF;
|
maxBytes[i] = (byte) 0xFF;
|
||||||
} else if (size < 8) {
|
} else if (size < 8) {
|
||||||
minBytes[i] &= 0xFF << (8 - size);
|
minBytes[i] = (byte) (minBytes[i] & 0xFF << (8 - size));
|
||||||
maxBytes[i] |= ~(0xFF << (8 - size));
|
maxBytes[i] = (byte) (maxBytes[i] | ~(0xFF << (8 - size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= 8;
|
size -= 8;
|
||||||
|
@ -10,7 +10,6 @@ import dan200.computercraft.core.apis.http.options.AddressPredicate.DomainPatter
|
|||||||
import dan200.computercraft.core.apis.http.options.AddressPredicate.HostRange;
|
import dan200.computercraft.core.apis.http.options.AddressPredicate.HostRange;
|
||||||
import dan200.computercraft.core.apis.http.options.AddressPredicate.PrivatePattern;
|
import dan200.computercraft.core.apis.http.options.AddressPredicate.PrivatePattern;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
@ -32,14 +31,14 @@ public final class AddressRule {
|
|||||||
private final OptionalInt port;
|
private final OptionalInt port;
|
||||||
private final PartialOptions partial;
|
private final PartialOptions partial;
|
||||||
|
|
||||||
private AddressRule(@Nonnull AddressPredicate predicate, OptionalInt port, @Nonnull PartialOptions partial) {
|
private AddressRule(AddressPredicate predicate, OptionalInt port, PartialOptions partial) {
|
||||||
this.predicate = predicate;
|
this.predicate = predicate;
|
||||||
this.partial = partial;
|
this.partial = partial;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static AddressRule parse(String filter, OptionalInt port, @Nonnull PartialOptions partial) {
|
public static AddressRule parse(String filter, OptionalInt port, PartialOptions partial) {
|
||||||
var cidr = filter.indexOf('/');
|
var cidr = filter.indexOf('/');
|
||||||
if (cidr >= 0) {
|
if (cidr >= 0) {
|
||||||
var addressStr = filter.substring(0, cidr);
|
var addressStr = filter.substring(0, cidr);
|
||||||
@ -63,7 +62,7 @@ public final class AddressRule {
|
|||||||
* @param ipv4Address An ipv4 version of the address, if the original was an ipv6 address.
|
* @param ipv4Address An ipv4 version of the address, if the original was an ipv6 address.
|
||||||
* @return Whether it matches any of these patterns.
|
* @return Whether it matches any of these patterns.
|
||||||
*/
|
*/
|
||||||
private boolean matches(String domain, int port, InetAddress address, Inet4Address ipv4Address) {
|
private boolean matches(String domain, int port, InetAddress address, @Nullable Inet4Address ipv4Address) {
|
||||||
if (this.port.isPresent() && this.port.getAsInt() != port) return false;
|
if (this.port.isPresent() && this.port.getAsInt() != port) return false;
|
||||||
return predicate.matches(domain)
|
return predicate.matches(domain)
|
||||||
|| predicate.matches(address)
|
|| predicate.matches(address)
|
||||||
|
@ -5,20 +5,18 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.apis.http.options;
|
package dan200.computercraft.core.apis.http.options;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options about a specific domain.
|
* Options about a specific domain.
|
||||||
*/
|
*/
|
||||||
public final class Options {
|
public final class Options {
|
||||||
@Nonnull
|
|
||||||
public final Action action;
|
public final Action action;
|
||||||
public final long maxUpload;
|
public final long maxUpload;
|
||||||
public final long maxDownload;
|
public final long maxDownload;
|
||||||
public final int timeout;
|
public final int timeout;
|
||||||
public final int websocketMessage;
|
public final int websocketMessage;
|
||||||
|
|
||||||
Options(@Nonnull Action action, long maxUpload, long maxDownload, int timeout, int websocketMessage) {
|
Options(Action action, long maxUpload, long maxDownload, int timeout, int websocketMessage) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.maxUpload = maxUpload;
|
this.maxUpload = maxUpload;
|
||||||
this.maxDownload = maxDownload;
|
this.maxDownload = maxDownload;
|
||||||
|
@ -5,10 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.apis.http.options;
|
package dan200.computercraft.core.apis.http.options;
|
||||||
|
|
||||||
|
import com.google.errorprone.annotations.Immutable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.OptionalLong;
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
public final class PartialOptions {
|
public final class PartialOptions {
|
||||||
public static final PartialOptions DEFAULT = new PartialOptions(
|
public static final PartialOptions DEFAULT = new PartialOptions(
|
||||||
null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
|
null, OptionalLong.empty(), OptionalLong.empty(), OptionalInt.empty(), OptionalInt.empty()
|
||||||
@ -20,6 +23,7 @@ public final class PartialOptions {
|
|||||||
private final OptionalInt timeout;
|
private final OptionalInt timeout;
|
||||||
private final OptionalInt websocketMessage;
|
private final OptionalInt websocketMessage;
|
||||||
|
|
||||||
|
@SuppressWarnings("Immutable") // Lazily initialised, so this mutation is invisible in the public API
|
||||||
private @Nullable Options options;
|
private @Nullable Options options;
|
||||||
|
|
||||||
public PartialOptions(@Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt timeout, OptionalInt websocketMessage) {
|
public PartialOptions(@Nullable Action action, OptionalLong maxUpload, OptionalLong maxDownload, OptionalInt timeout, OptionalInt websocketMessage) {
|
||||||
|
@ -24,6 +24,7 @@ import io.netty.handler.timeout.ReadTimeoutHandler;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -42,9 +43,9 @@ public class HttpRequest extends Resource<HttpRequest> {
|
|||||||
|
|
||||||
private static final int MAX_REDIRECTS = 16;
|
private static final int MAX_REDIRECTS = 16;
|
||||||
|
|
||||||
private Future<?> executorFuture;
|
private @Nullable Future<?> executorFuture;
|
||||||
private ChannelFuture connectFuture;
|
private @Nullable ChannelFuture connectFuture;
|
||||||
private HttpRequestHandler currentRequest;
|
private @Nullable HttpRequestHandler currentRequest;
|
||||||
|
|
||||||
private final IAPIEnvironment environment;
|
private final IAPIEnvironment environment;
|
||||||
|
|
||||||
@ -55,7 +56,10 @@ public class HttpRequest extends Resource<HttpRequest> {
|
|||||||
|
|
||||||
final AtomicInteger redirects;
|
final AtomicInteger redirects;
|
||||||
|
|
||||||
public HttpRequest(ResourceGroup<HttpRequest> limiter, IAPIEnvironment environment, String address, String postText, HttpHeaders headers, boolean binary, boolean followRedirects) {
|
public HttpRequest(
|
||||||
|
ResourceGroup<HttpRequest> limiter, IAPIEnvironment environment, String address, @Nullable String postText,
|
||||||
|
HttpHeaders headers, boolean binary, boolean followRedirects
|
||||||
|
) {
|
||||||
super(limiter);
|
super(limiter);
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
@ -171,7 +175,7 @@ public class HttpRequest extends Resource<HttpRequest> {
|
|||||||
// Do an additional check for cancellation
|
// Do an additional check for cancellation
|
||||||
checkClosed();
|
checkClosed();
|
||||||
} catch (HTTPRequestException e) {
|
} catch (HTTPRequestException e) {
|
||||||
failure(e.getMessage());
|
failure(NetworkUtils.toFriendlyError(e));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
failure(NetworkUtils.toFriendlyError(e));
|
failure(NetworkUtils.toFriendlyError(e));
|
||||||
LOG.error(Logging.HTTP_ERROR, "Error in HTTP request", e);
|
LOG.error(Logging.HTTP_ERROR, "Error in HTTP request", e);
|
||||||
|
@ -20,6 +20,7 @@ import io.netty.handler.codec.http.*;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -27,6 +28,7 @@ import java.nio.charset.Charset;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.http.request.HttpRequest.getHeaderSize;
|
import static dan200.computercraft.core.apis.http.request.HttpRequest.getHeaderSize;
|
||||||
|
|
||||||
@ -47,10 +49,10 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
|||||||
private final HttpMethod method;
|
private final HttpMethod method;
|
||||||
private final Options options;
|
private final Options options;
|
||||||
|
|
||||||
private Charset responseCharset;
|
private @Nullable Charset responseCharset;
|
||||||
private final HttpHeaders responseHeaders = new DefaultHttpHeaders();
|
private final HttpHeaders responseHeaders = new DefaultHttpHeaders();
|
||||||
private HttpResponseStatus responseStatus;
|
private @Nullable HttpResponseStatus responseStatus;
|
||||||
private CompositeByteBuf responseBody;
|
private @Nullable CompositeByteBuf responseBody;
|
||||||
|
|
||||||
HttpRequestHandler(HttpRequest request, URI uri, HttpMethod method, Options options) {
|
HttpRequestHandler(HttpRequest request, URI uri, HttpMethod method, Options options) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
@ -112,7 +114,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
|||||||
HttpRequest.checkUri(redirect);
|
HttpRequest.checkUri(redirect);
|
||||||
} catch (HTTPRequestException e) {
|
} catch (HTTPRequestException e) {
|
||||||
// If we cannot visit this uri, then fail.
|
// If we cannot visit this uri, then fail.
|
||||||
request.failure(e.getMessage());
|
request.failure(NetworkUtils.toFriendlyError(e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +169,9 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendResponse() {
|
private void sendResponse() {
|
||||||
|
Objects.requireNonNull(responseStatus, "Status has not been set");
|
||||||
|
Objects.requireNonNull(responseCharset, "Charset has not been set");
|
||||||
|
|
||||||
// Read the ByteBuf into a channel.
|
// Read the ByteBuf into a channel.
|
||||||
var body = responseBody;
|
var body = responseBody;
|
||||||
var bytes = body == null ? EMPTY_BYTES : NetworkUtils.toBytes(body);
|
var bytes = body == null ? EMPTY_BYTES : NetworkUtils.toBytes(body);
|
||||||
@ -203,6 +208,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
|||||||
* @param headers The headers of the HTTP response.
|
* @param headers The headers of the HTTP response.
|
||||||
* @return The URI to redirect to, or {@code null} if no redirect should occur.
|
* @return The URI to redirect to, or {@code null} if no redirect should occur.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
private URI getRedirect(HttpResponseStatus status, HttpHeaders headers) {
|
private URI getRedirect(HttpResponseStatus status, HttpHeaders headers) {
|
||||||
var code = status.code();
|
var code = status.code();
|
||||||
if (code < 300 || code > 307 || code == 304 || code == 306) return null;
|
if (code < 300 || code > 307 || code == 304 || code == 306) return null;
|
||||||
|
@ -13,7 +13,6 @@ import dan200.computercraft.core.apis.handles.EncodedReadableHandle;
|
|||||||
import dan200.computercraft.core.apis.handles.HandleGeneric;
|
import dan200.computercraft.core.apis.handles.HandleGeneric;
|
||||||
import dan200.computercraft.core.asm.ObjectSource;
|
import dan200.computercraft.core.asm.ObjectSource;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ public class HttpResponseHandle implements ObjectSource {
|
|||||||
private final String responseStatus;
|
private final String responseStatus;
|
||||||
private final Map<String, String> responseHeaders;
|
private final Map<String, String> responseHeaders;
|
||||||
|
|
||||||
public HttpResponseHandle(@Nonnull HandleGeneric reader, int responseCode, String responseStatus, @Nonnull Map<String, String> responseHeaders) {
|
public HttpResponseHandle(HandleGeneric reader, int responseCode, String responseStatus, Map<String, String> responseHeaders) {
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.responseCode = responseCode;
|
this.responseCode = responseCode;
|
||||||
this.responseStatus = responseStatus;
|
this.responseStatus = responseStatus;
|
||||||
|
@ -29,6 +29,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -51,9 +52,9 @@ public class Websocket extends Resource<Websocket> {
|
|||||||
static final String CLOSE_EVENT = "websocket_closed";
|
static final String CLOSE_EVENT = "websocket_closed";
|
||||||
static final String MESSAGE_EVENT = "websocket_message";
|
static final String MESSAGE_EVENT = "websocket_message";
|
||||||
|
|
||||||
private Future<?> executorFuture;
|
private @Nullable Future<?> executorFuture;
|
||||||
private ChannelFuture connectFuture;
|
private @Nullable ChannelFuture connectFuture;
|
||||||
private WeakReference<WebsocketHandle> websocketHandle;
|
private @Nullable WeakReference<WebsocketHandle> websocketHandle;
|
||||||
|
|
||||||
private final IAPIEnvironment environment;
|
private final IAPIEnvironment environment;
|
||||||
private final URI uri;
|
private final URI uri;
|
||||||
@ -73,12 +74,14 @@ public class Websocket extends Resource<Websocket> {
|
|||||||
try {
|
try {
|
||||||
uri = new URI(address);
|
uri = new URI(address);
|
||||||
} catch (URISyntaxException ignored) {
|
} catch (URISyntaxException ignored) {
|
||||||
|
// Fall through to the case below
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri == null || uri.getHost() == null) {
|
if (uri == null || uri.getHost() == null) {
|
||||||
try {
|
try {
|
||||||
uri = new URI("ws://" + address);
|
uri = new URI("ws://" + address);
|
||||||
} catch (URISyntaxException ignored) {
|
} catch (URISyntaxException ignored) {
|
||||||
|
// Fall through to the case below
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +155,7 @@ public class Websocket extends Resource<Websocket> {
|
|||||||
// Do an additional check for cancellation
|
// Do an additional check for cancellation
|
||||||
checkClosed();
|
checkClosed();
|
||||||
} catch (HTTPRequestException e) {
|
} catch (HTTPRequestException e) {
|
||||||
failure(e.getMessage());
|
failure(NetworkUtils.toFriendlyError(e));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
failure(NetworkUtils.toFriendlyError(e));
|
failure(NetworkUtils.toFriendlyError(e));
|
||||||
LOG.error(Logging.HTTP_ERROR, "Error in websocket", e);
|
LOG.error(Logging.HTTP_ERROR, "Error in websocket", e);
|
||||||
|
@ -15,7 +15,7 @@ import io.netty.channel.Channel;
|
|||||||
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
|
||||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -36,7 +36,7 @@ public class WebsocketHandle implements Closeable {
|
|||||||
private final Options options;
|
private final Options options;
|
||||||
private boolean closed = false;
|
private boolean closed = false;
|
||||||
|
|
||||||
private Channel channel;
|
private @Nullable Channel channel;
|
||||||
|
|
||||||
public WebsocketHandle(Websocket websocket, Options options, Channel channel) {
|
public WebsocketHandle(Websocket websocket, Options options, Channel channel) {
|
||||||
this.websocket = websocket;
|
this.websocket = websocket;
|
||||||
@ -127,7 +127,6 @@ public class WebsocketHandle implements Closeable {
|
|||||||
this.timeoutId = timeoutId;
|
this.timeoutId = timeoutId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public MethodResult resume(Object[] event) {
|
public MethodResult resume(Object[] event) {
|
||||||
if (event.length >= 3 && Objects.equal(event[0], MESSAGE_EVENT) && Objects.equal(event[1], websocket.address())) {
|
if (event.length >= 3 && Objects.equal(event[0], MESSAGE_EVENT) && Objects.equal(event[1], websocket.address())) {
|
||||||
|
@ -21,7 +21,6 @@ import org.objectweb.asm.Type;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
@ -77,8 +76,7 @@ public final class Generator<T> {
|
|||||||
this.methodDesc = methodDesc.toString();
|
this.methodDesc = methodDesc.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public List<NamedMethod<T>> getMethods(Class<?> klass) {
|
||||||
public List<NamedMethod<T>> getMethods(@Nonnull Class<?> klass) {
|
|
||||||
try {
|
try {
|
||||||
return classCache.get(klass);
|
return classCache.get(klass);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
@ -87,7 +85,6 @@ public final class Generator<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private List<NamedMethod<T>> build(Class<?> klass) {
|
private List<NamedMethod<T>> build(Class<?> klass) {
|
||||||
ArrayList<NamedMethod<T>> methods = null;
|
ArrayList<NamedMethod<T>> methods = null;
|
||||||
for (var method : klass.getMethods()) {
|
for (var method : klass.getMethods()) {
|
||||||
@ -121,7 +118,7 @@ public final class Generator<T> {
|
|||||||
return Collections.unmodifiableList(methods);
|
return Collections.unmodifiableList(methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMethod(List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance) {
|
private void addMethod(List<NamedMethod<T>> methods, Method method, LuaFunction annotation, @Nullable PeripheralType genericType, T instance) {
|
||||||
var names = annotation.value();
|
var names = annotation.value();
|
||||||
var isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
|
var isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
|
||||||
if (names.length == 0) {
|
if (names.length == 0) {
|
||||||
@ -133,7 +130,6 @@ public final class Generator<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private Optional<T> build(Method method) {
|
private Optional<T> build(Method method) {
|
||||||
var name = method.getDeclaringClass().getName() + "." + method.getName();
|
var name = method.getDeclaringClass().getName() + "." + method.getName();
|
||||||
var modifiers = method.getModifiers();
|
var modifiers = method.getModifiers();
|
||||||
@ -259,6 +255,7 @@ public final class Generator<T> {
|
|||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private Boolean loadArg(MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex) {
|
private Boolean loadArg(MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex) {
|
||||||
if (genericArg == target) {
|
if (genericArg == target) {
|
||||||
mw.visitVarInsn(ALOAD, 1);
|
mw.visitVarInsn(ALOAD, 1);
|
||||||
|
@ -12,7 +12,7 @@ import dan200.computercraft.api.peripheral.PeripheralType;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -30,12 +30,12 @@ public class GenericMethod {
|
|||||||
final Method method;
|
final Method method;
|
||||||
final LuaFunction annotation;
|
final LuaFunction annotation;
|
||||||
final Class<?> target;
|
final Class<?> target;
|
||||||
final PeripheralType peripheralType;
|
final @Nullable PeripheralType peripheralType;
|
||||||
|
|
||||||
private static final List<GenericSource> sources = new ArrayList<>();
|
private static final List<GenericSource> sources = new ArrayList<>();
|
||||||
private static List<GenericMethod> cache;
|
private static @Nullable List<GenericMethod> cache;
|
||||||
|
|
||||||
GenericMethod(Method method, LuaFunction annotation, Class<?> target, PeripheralType peripheralType) {
|
GenericMethod(Method method, LuaFunction annotation, Class<?> target, @Nullable PeripheralType peripheralType) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
@ -52,7 +52,7 @@ public class GenericMethod {
|
|||||||
return cache = sources.stream().flatMap(GenericMethod::getMethods).toList();
|
return cache = sources.stream().flatMap(GenericMethod::getMethods).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void register(@Nonnull GenericSource source) {
|
public static synchronized void register(GenericSource source) {
|
||||||
Objects.requireNonNull(source, "Source cannot be null");
|
Objects.requireNonNull(source, "Source cannot be null");
|
||||||
|
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.asm;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public interface LuaMethod {
|
public interface LuaMethod {
|
||||||
@ -21,6 +20,5 @@ public interface LuaMethod {
|
|||||||
|
|
||||||
String[] EMPTY_METHODS = new String[0];
|
String[] EMPTY_METHODS = new String[0];
|
||||||
|
|
||||||
@Nonnull
|
MethodResult apply(Object target, ILuaContext context, IArguments args) throws LuaException;
|
||||||
MethodResult apply(@Nonnull Object target, @Nonnull ILuaContext context, @Nonnull IArguments args) throws LuaException;
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.asm;
|
|||||||
|
|
||||||
import dan200.computercraft.api.peripheral.PeripheralType;
|
import dan200.computercraft.api.peripheral.PeripheralType;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class NamedMethod<T> {
|
public final class NamedMethod<T> {
|
||||||
@ -15,21 +14,19 @@ public final class NamedMethod<T> {
|
|||||||
private final T method;
|
private final T method;
|
||||||
private final boolean nonYielding;
|
private final boolean nonYielding;
|
||||||
|
|
||||||
private final PeripheralType genericType;
|
private final @Nullable PeripheralType genericType;
|
||||||
|
|
||||||
NamedMethod(String name, T method, boolean nonYielding, PeripheralType genericType) {
|
NamedMethod(String name, T method, boolean nonYielding, @Nullable PeripheralType genericType) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.nonYielding = nonYielding;
|
this.nonYielding = nonYielding;
|
||||||
this.genericType = genericType;
|
this.genericType = genericType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public T getMethod() {
|
public T getMethod() {
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.api.lua.MethodResult;
|
|||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public interface PeripheralMethod {
|
public interface PeripheralMethod {
|
||||||
@ -24,6 +23,5 @@ public interface PeripheralMethod {
|
|||||||
method -> (instance, context, computer, args) -> ((IDynamicPeripheral) instance).callMethod(computer, context, method, args)
|
method -> (instance, context, computer, args) -> ((IDynamicPeripheral) instance).callMethod(computer, context, method, args)
|
||||||
);
|
);
|
||||||
|
|
||||||
@Nonnull
|
MethodResult apply(Object target, ILuaContext context, IComputerAccess computer, IArguments args) throws LuaException;
|
||||||
MethodResult apply(@Nonnull Object target, @Nonnull ILuaContext context, @Nonnull IComputerAccess computer, @Nonnull IArguments args) throws LuaException;
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,13 @@ package dan200.computercraft.core.asm;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.MethodResult;
|
import dan200.computercraft.api.lua.MethodResult;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
final class ResultHelpers {
|
final class ResultHelpers {
|
||||||
private ResultHelpers() {
|
private ResultHelpers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
static Object[] checkNormalResult(MethodResult result) {
|
static Object[] checkNormalResult(MethodResult result) {
|
||||||
if (result.getCallback() != null) {
|
if (result.getCallback() != null) {
|
||||||
// Due to how tasks are implemented, we can't currently return a MethodResult. This is an
|
// Due to how tasks are implemented, we can't currently return a MethodResult. This is an
|
||||||
|
@ -16,6 +16,7 @@ import dan200.computercraft.core.computer.mainthread.MainThreadScheduler;
|
|||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
import dan200.computercraft.core.filesystem.FileSystem;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ public class Computer {
|
|||||||
|
|
||||||
// Various properties of the computer
|
// Various properties of the computer
|
||||||
private final int id;
|
private final int id;
|
||||||
private String label = null;
|
private @Nullable String label = null;
|
||||||
|
|
||||||
// Read-only fields about the computer
|
// Read-only fields about the computer
|
||||||
private final GlobalEnvironment globalEnvironment;
|
private final GlobalEnvironment globalEnvironment;
|
||||||
@ -112,7 +113,7 @@ public class Computer {
|
|||||||
executor.queueStop(false, true);
|
executor.queueStop(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueEvent(String event, Object[] args) {
|
public void queueEvent(String event, @Nullable Object[] args) {
|
||||||
executor.queueEvent(event, args);
|
executor.queueEvent(event, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,11 +135,12 @@ public class Computer {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLabel(String label) {
|
public void setLabel(@Nullable String label) {
|
||||||
if (!Objects.equal(label, this.label)) {
|
if (!Objects.equal(label, this.label)) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
externalOutputChanged.set(true);
|
externalOutputChanged.set(true);
|
||||||
|
@ -19,10 +19,10 @@ import dan200.computercraft.core.metrics.Metrics;
|
|||||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
|
import dan200.computercraft.core.util.Nullability;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -63,9 +63,9 @@ final class ComputerExecutor {
|
|||||||
private final ComputerThread scheduler;
|
private final ComputerThread scheduler;
|
||||||
final TimeoutState timeout;
|
final TimeoutState timeout;
|
||||||
|
|
||||||
private FileSystem fileSystem;
|
private @Nullable FileSystem fileSystem;
|
||||||
|
|
||||||
private ILuaMachine machine;
|
private @Nullable ILuaMachine machine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the computer is currently on. This is set to false when a shutdown starts, or when turning on completes
|
* Whether the computer is currently on. This is set to false when a shutdown starts, or when turning on completes
|
||||||
@ -126,7 +126,7 @@ final class ComputerExecutor {
|
|||||||
* Note, if command is not {@code null}, then some command is scheduled to be executed. Otherwise it is not
|
* Note, if command is not {@code null}, then some command is scheduled to be executed. Otherwise it is not
|
||||||
* currently in the queue (or is currently being executed).
|
* currently in the queue (or is currently being executed).
|
||||||
*/
|
*/
|
||||||
private volatile StateCommand command;
|
private volatile @Nullable StateCommand command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The queue of events which should be executed when this computer is on.
|
* The queue of events which should be executed when this computer is on.
|
||||||
@ -150,7 +150,7 @@ final class ComputerExecutor {
|
|||||||
*/
|
*/
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
private IWritableMount rootMount;
|
private @Nullable IWritableMount rootMount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The thread the executor is running on. This is non-null when performing work. We use this to ensure we're only
|
* The thread the executor is running on. This is non-null when performing work. We use this to ensure we're only
|
||||||
@ -193,6 +193,8 @@ final class ComputerExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileSystem getFileSystem() {
|
FileSystem getFileSystem() {
|
||||||
|
var fileSystem = this.fileSystem;
|
||||||
|
if (fileSystem == null) throw new IllegalStateException("FileSystem has not been created yet");
|
||||||
return fileSystem;
|
return fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +278,7 @@ final class ComputerExecutor {
|
|||||||
* @param event The event's name
|
* @param event The event's name
|
||||||
* @param args The event's arguments
|
* @param args The event's arguments
|
||||||
*/
|
*/
|
||||||
void queueEvent(@Nonnull String event, @Nullable Object[] args) {
|
void queueEvent(String event, @Nullable Object[] args) {
|
||||||
// Events should be skipped if we're not on.
|
// Events should be skipped if we're not on.
|
||||||
if (!isOn) return;
|
if (!isOn) return;
|
||||||
|
|
||||||
@ -320,19 +322,28 @@ final class ComputerExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private IMount getRomMount() {
|
private IMount getRomMount() {
|
||||||
return computer.getGlobalEnvironment().createResourceMount("computercraft", "lua/rom");
|
return computer.getGlobalEnvironment().createResourceMount("computercraft", "lua/rom");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private IWritableMount getRootMount() {
|
private IWritableMount getRootMount() {
|
||||||
if (rootMount == null) rootMount = computerEnvironment.createRootMount();
|
if (rootMount == null) rootMount = computerEnvironment.createRootMount();
|
||||||
return rootMount;
|
return rootMount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private FileSystem createFileSystem() {
|
private FileSystem createFileSystem() {
|
||||||
FileSystem filesystem = null;
|
FileSystem filesystem = null;
|
||||||
try {
|
try {
|
||||||
filesystem = new FileSystem("hdd", getRootMount());
|
var mount = getRootMount();
|
||||||
|
if (mount == null) {
|
||||||
|
displayFailure("Cannot mount computer mount", null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
filesystem = new FileSystem("hdd", mount);
|
||||||
|
|
||||||
var romMount = getRomMount();
|
var romMount = getRomMount();
|
||||||
if (romMount == null) {
|
if (romMount == null) {
|
||||||
@ -351,12 +362,14 @@ final class ComputerExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private ILuaMachine createLuaMachine() {
|
private ILuaMachine createLuaMachine() {
|
||||||
// Load the bios resource
|
// Load the bios resource
|
||||||
InputStream biosStream = null;
|
InputStream biosStream = null;
|
||||||
try {
|
try {
|
||||||
biosStream = computer.getGlobalEnvironment().createResourceFile("computercraft", "lua/bios.lua");
|
biosStream = computer.getGlobalEnvironment().createResourceFile("computercraft", "lua/bios.lua");
|
||||||
} catch (Exception ignored) {
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to load BIOS", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (biosStream == null) {
|
if (biosStream == null) {
|
||||||
@ -562,7 +575,7 @@ final class ComputerExecutor {
|
|||||||
if (machine != null) machine.printExecutionState(out);
|
if (machine != null) machine.printExecutionState(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayFailure(String message, String extra) {
|
private void displayFailure(String message, @Nullable String extra) {
|
||||||
var terminal = computer.getTerminal();
|
var terminal = computer.getTerminal();
|
||||||
terminal.reset();
|
terminal.reset();
|
||||||
|
|
||||||
@ -583,8 +596,8 @@ final class ComputerExecutor {
|
|||||||
terminal.write("ComputerCraft may be installed incorrectly");
|
terminal.write("ComputerCraft may be installed incorrectly");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resumeMachine(String event, Object[] args) throws InterruptedException {
|
private void resumeMachine(@Nullable String event, @Nullable Object[] args) throws InterruptedException {
|
||||||
var result = machine.handleEvent(event, args);
|
var result = Nullability.assertNonNull(machine).handleEvent(event, args);
|
||||||
interruptedEvent = result.isPause();
|
interruptedEvent = result.isPause();
|
||||||
if (!result.isError()) return;
|
if (!result.isError()) return;
|
||||||
|
|
||||||
@ -600,6 +613,6 @@ final class ComputerExecutor {
|
|||||||
ERROR,
|
ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Event(String name, Object[] args) {
|
private record Event(String name, @Nullable Object[] args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.computer;
|
package dan200.computercraft.core.computer;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A side on a computer. This is relative to the direction the computer is facing.
|
* A side on a computer. This is relative to the direction the computer is facing.
|
||||||
@ -19,7 +19,7 @@ public enum ComputerSide {
|
|||||||
RIGHT("right"),
|
RIGHT("right"),
|
||||||
LEFT("left");
|
LEFT("left");
|
||||||
|
|
||||||
public static final String[] NAMES = new String[]{ "bottom", "top", "back", "front", "right", "left" };
|
public static final List<String> NAMES = List.of("bottom", "top", "back", "front", "right", "left");
|
||||||
|
|
||||||
public static final int COUNT = 6;
|
public static final int COUNT = 6;
|
||||||
|
|
||||||
@ -31,13 +31,12 @@ public enum ComputerSide {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static ComputerSide valueOf(int side) {
|
public static ComputerSide valueOf(int side) {
|
||||||
return VALUES[side];
|
return VALUES[side];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ComputerSide valueOfInsensitive(@Nonnull String name) {
|
public static ComputerSide valueOfInsensitive(String name) {
|
||||||
for (var side : VALUES) {
|
for (var side : VALUES) {
|
||||||
if (side.name.equalsIgnoreCase(name)) return side;
|
if (side.name.equalsIgnoreCase(name)) return side;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.core.apis.ComputerAccess;
|
import dan200.computercraft.core.apis.ComputerAccess;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -33,7 +32,6 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem {
|
|||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttachmentName() {
|
public String getAttachmentName() {
|
||||||
return "computer";
|
return "computer";
|
||||||
@ -52,7 +50,6 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem {
|
|||||||
return environment.getLabel();
|
return environment.getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, IPeripheral> getAvailablePeripherals() {
|
public Map<String, IPeripheral> getAvailablePeripherals() {
|
||||||
// TODO: Should this return peripherals on the current computer?
|
// TODO: Should this return peripherals on the current computer?
|
||||||
@ -61,7 +58,7 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public IPeripheral getAvailablePeripheral(@Nonnull String name) {
|
public IPeripheral getAvailablePeripheral(String name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import dan200.computercraft.core.util.ThreadUtils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@ -53,6 +52,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
* @see TimeoutState For how hard timeouts are handled.
|
* @see TimeoutState For how hard timeouts are handled.
|
||||||
* @see ComputerExecutor For how computers actually do execution.
|
* @see ComputerExecutor For how computers actually do execution.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("GuardedBy") // FIXME: Hard to know what the correct thing to do is.
|
||||||
public final class ComputerThread {
|
public final class ComputerThread {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ComputerThread.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ComputerThread.class);
|
||||||
private static final ThreadFactory monitorFactory = ThreadUtils.factory("Computer-Monitor");
|
private static final ThreadFactory monitorFactory = ThreadUtils.factory("Computer-Monitor");
|
||||||
@ -537,7 +537,7 @@ public final class ComputerThread {
|
|||||||
/**
|
/**
|
||||||
* The thread this runner runs on.
|
* The thread this runner runs on.
|
||||||
*/
|
*/
|
||||||
final @Nonnull Thread owner;
|
final Thread owner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this runner is currently executing. This may be set to false when this worker terminates, or when
|
* Whether this runner is currently executing. This may be set to false when this worker terminates, or when
|
||||||
|
@ -16,7 +16,7 @@ import dan200.computercraft.core.terminal.Terminal;
|
|||||||
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 javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
private final int[] bundledInput = new int[ComputerSide.COUNT];
|
private final int[] bundledInput = new int[ComputerSide.COUNT];
|
||||||
|
|
||||||
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT];
|
||||||
private IPeripheralChangeListener peripheralListener = null;
|
private @Nullable IPeripheralChangeListener peripheralListener = null;
|
||||||
|
|
||||||
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
private final Int2ObjectMap<Timer> timers = new Int2ObjectOpenHashMap<>();
|
||||||
private int nextTimerToken = 0;
|
private int nextTimerToken = 0;
|
||||||
@ -72,25 +72,21 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
return computer.getID();
|
return computer.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ComputerEnvironment getComputerEnvironment() {
|
public ComputerEnvironment getComputerEnvironment() {
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public GlobalEnvironment getGlobalEnvironment() {
|
public GlobalEnvironment getGlobalEnvironment() {
|
||||||
return computer.getGlobalEnvironment();
|
return computer.getGlobalEnvironment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkMonitor getMainThreadMonitor() {
|
public IWorkMonitor getMainThreadMonitor() {
|
||||||
return computer.getMainThreadMonitor();
|
return computer.getMainThreadMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Terminal getTerminal() {
|
public Terminal getTerminal() {
|
||||||
return computer.getTerminal();
|
return computer.getTerminal();
|
||||||
@ -112,7 +108,7 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent(String event, Object... args) {
|
public void queueEvent(String event, @Nullable Object... args) {
|
||||||
computer.queueEvent(event, args);
|
computer.queueEvent(event, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +258,7 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public IPeripheral getPeripheral(ComputerSide side) {
|
public IPeripheral getPeripheral(ComputerSide side) {
|
||||||
synchronized (peripherals) {
|
synchronized (peripherals) {
|
||||||
@ -269,7 +266,7 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPeripheral(ComputerSide side, IPeripheral peripheral) {
|
public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) {
|
||||||
synchronized (peripherals) {
|
synchronized (peripherals) {
|
||||||
var index = side.ordinal();
|
var index = side.ordinal();
|
||||||
var existing = peripherals[index];
|
var existing = peripherals[index];
|
||||||
@ -283,19 +280,20 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPeripheralChangeListener(IPeripheralChangeListener listener) {
|
public void setPeripheralChangeListener(@Nullable IPeripheralChangeListener listener) {
|
||||||
synchronized (peripherals) {
|
synchronized (peripherals) {
|
||||||
peripheralListener = listener;
|
peripheralListener = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
return computer.getLabel();
|
return computer.getLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLabel(String label) {
|
public void setLabel(@Nullable String label) {
|
||||||
computer.setLabel(label);
|
computer.setLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,12 +313,12 @@ public final class Environment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void observe(@Nonnull Metric.Event event, long change) {
|
public void observe(Metric.Event event, long change) {
|
||||||
metrics.observe(event, change);
|
metrics.observe(event, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void observe(@Nonnull Metric.Counter counter) {
|
public void observe(Metric.Counter counter) {
|
||||||
metrics.observe(counter);
|
metrics.observe(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.core.Logging;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
class LuaContext implements ILuaContext {
|
class LuaContext implements ILuaContext {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(LuaContext.class);
|
private static final Logger LOG = LoggerFactory.getLogger(LuaContext.class);
|
||||||
@ -23,7 +22,7 @@ class LuaContext implements ILuaContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long issueMainThreadTask(@Nonnull final ILuaTask task) throws LuaException {
|
public long issueMainThreadTask(final ILuaTask task) throws LuaException {
|
||||||
// Issue command
|
// Issue command
|
||||||
final var taskID = computer.getUniqueTaskId();
|
final var taskID = computer.getUniqueTaskId();
|
||||||
final Runnable iTask = () -> {
|
final Runnable iTask = () -> {
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.computer.mainthread;
|
|||||||
|
|
||||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +37,7 @@ public class NoWorkMainThreadScheduler implements MainThreadScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void trackWork(long time, @Nonnull TimeUnit unit) {
|
public void trackWork(long time, TimeUnit unit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.filesystem;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ComboMount implements IMount {
|
|
||||||
private final IMount[] parts;
|
|
||||||
|
|
||||||
public ComboMount(IMount[] parts) {
|
|
||||||
this.parts = parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMount implementation
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean exists(@Nonnull String path) throws IOException {
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.exists(path)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDirectory(@Nonnull String path) throws IOException {
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.isDirectory(path)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
|
|
||||||
// Combine the lists from all the mounts
|
|
||||||
List<String> foundFiles = null;
|
|
||||||
var foundDirs = 0;
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.exists(path) && part.isDirectory(path)) {
|
|
||||||
if (foundFiles == null) {
|
|
||||||
foundFiles = new ArrayList<>();
|
|
||||||
}
|
|
||||||
part.list(path, foundFiles);
|
|
||||||
foundDirs++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundDirs == 1) {
|
|
||||||
// We found one directory, so we know it already doesn't contain duplicates
|
|
||||||
contents.addAll(foundFiles);
|
|
||||||
} else if (foundDirs > 1) {
|
|
||||||
// We found multiple directories, so filter for duplicates
|
|
||||||
Set<String> seen = new HashSet<>();
|
|
||||||
for (var file : foundFiles) {
|
|
||||||
if (seen.add(file)) {
|
|
||||||
contents.add(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new FileOperationException(path, "Not a directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSize(@Nonnull String path) throws IOException {
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.exists(path)) {
|
|
||||||
return part.getSize(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new FileOperationException(path, "No such file");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.exists(path) && !part.isDirectory(path)) {
|
|
||||||
return part.openForRead(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new FileOperationException(path, "No such file");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
|
|
||||||
for (var i = parts.length - 1; i >= 0; --i) {
|
|
||||||
var part = parts[i];
|
|
||||||
if (part.exists(path) && !part.isDirectory(path)) {
|
|
||||||
return part.getAttributes(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new FileOperationException(path, "No such file");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
|
||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
|
||||||
*/
|
|
||||||
package dan200.computercraft.core.filesystem;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class EmptyMount implements IMount {
|
|
||||||
@Override
|
|
||||||
public boolean exists(@Nonnull String path) {
|
|
||||||
return path.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDirectory(@Nonnull String path) {
|
|
||||||
return path.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSize(@Nonnull String path) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
|
||||||
throw new FileOperationException(path, "No such file");
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,6 @@ import dan200.computercraft.api.filesystem.IWritableMount;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -25,7 +24,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class FileMount implements IWritableMount {
|
public class FileMount implements IWritableMount {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FileMount.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FileMount.class);
|
||||||
private static final int MINIMUM_FILE_SIZE = 500;
|
private static final long MINIMUM_FILE_SIZE = 500;
|
||||||
private static final Set<OpenOption> READ_OPTIONS = Collections.singleton(StandardOpenOption.READ);
|
private static final Set<OpenOption> READ_OPTIONS = Collections.singleton(StandardOpenOption.READ);
|
||||||
private static final Set<OpenOption> WRITE_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
private static final Set<OpenOption> WRITE_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
private static final Set<OpenOption> APPEND_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
|
private static final Set<OpenOption> APPEND_OPTIONS = Sets.newHashSet(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
|
||||||
@ -41,7 +40,7 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int write(@Nonnull ByteBuffer b) throws IOException {
|
public int write(ByteBuffer b) throws IOException {
|
||||||
count(b.remaining());
|
count(b.remaining());
|
||||||
return inner.write(b);
|
return inner.write(b);
|
||||||
}
|
}
|
||||||
@ -129,7 +128,7 @@ public class FileMount implements IWritableMount {
|
|||||||
// IMount implementation
|
// IMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@Nonnull String path) {
|
public boolean exists(String path) {
|
||||||
if (!created()) return path.isEmpty();
|
if (!created()) return path.isEmpty();
|
||||||
|
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
@ -137,7 +136,7 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory(@Nonnull String path) {
|
public boolean isDirectory(String path) {
|
||||||
if (!created()) return path.isEmpty();
|
if (!created()) return path.isEmpty();
|
||||||
|
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
@ -145,7 +144,7 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
|
public void list(String path, List<String> contents) throws IOException {
|
||||||
if (!created()) {
|
if (!created()) {
|
||||||
if (!path.isEmpty()) throw new FileOperationException(path, "Not a directory");
|
if (!path.isEmpty()) throw new FileOperationException(path, "Not a directory");
|
||||||
return;
|
return;
|
||||||
@ -161,7 +160,7 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize(@Nonnull String path) throws IOException {
|
public long getSize(String path) throws IOException {
|
||||||
if (!created()) {
|
if (!created()) {
|
||||||
if (path.isEmpty()) return 0;
|
if (path.isEmpty()) return 0;
|
||||||
} else {
|
} else {
|
||||||
@ -172,9 +171,8 @@ public class FileMount implements IWritableMount {
|
|||||||
throw new FileOperationException(path, "No such file");
|
throw new FileOperationException(path, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
public ReadableByteChannel openForRead(String path) throws IOException {
|
||||||
if (created()) {
|
if (created()) {
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
if (file.exists() && !file.isDirectory()) return FileChannel.open(file.toPath(), READ_OPTIONS);
|
if (file.exists() && !file.isDirectory()) return FileChannel.open(file.toPath(), READ_OPTIONS);
|
||||||
@ -183,9 +181,8 @@ public class FileMount implements IWritableMount {
|
|||||||
throw new FileOperationException(path, "No such file");
|
throw new FileOperationException(path, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
|
public BasicFileAttributes getAttributes(String path) throws IOException {
|
||||||
if (created()) {
|
if (created()) {
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
if (file.exists()) return Files.readAttributes(file.toPath(), BasicFileAttributes.class);
|
if (file.exists()) return Files.readAttributes(file.toPath(), BasicFileAttributes.class);
|
||||||
@ -197,7 +194,7 @@ public class FileMount implements IWritableMount {
|
|||||||
// IWritableMount implementation
|
// IWritableMount implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void makeDirectory(@Nonnull String path) throws IOException {
|
public void makeDirectory(String path) throws IOException {
|
||||||
create();
|
create();
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
@ -224,7 +221,7 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(@Nonnull String path) throws IOException {
|
public void delete(String path) throws IOException {
|
||||||
if (path.isEmpty()) throw new FileOperationException(path, "Access denied");
|
if (path.isEmpty()) throw new FileOperationException(path, "Access denied");
|
||||||
|
|
||||||
if (created()) {
|
if (created()) {
|
||||||
@ -252,9 +249,8 @@ public class FileMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForWrite(@Nonnull String path) throws IOException {
|
public WritableByteChannel openForWrite(String path) throws IOException {
|
||||||
create();
|
create();
|
||||||
var file = getRealPath(path);
|
var file = getRealPath(path);
|
||||||
if (file.exists() && file.isDirectory()) throw new FileOperationException(path, "Cannot write to directory");
|
if (file.exists() && file.isDirectory()) throw new FileOperationException(path, "Cannot write to directory");
|
||||||
@ -269,9 +265,8 @@ public class FileMount implements IWritableMount {
|
|||||||
return new SeekableCountingChannel(Files.newByteChannel(file.toPath(), WRITE_OPTIONS), MINIMUM_FILE_SIZE);
|
return new SeekableCountingChannel(Files.newByteChannel(file.toPath(), WRITE_OPTIONS), MINIMUM_FILE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForAppend(@Nonnull String path) throws IOException {
|
public WritableByteChannel openForAppend(String path) throws IOException {
|
||||||
if (!created()) {
|
if (!created()) {
|
||||||
throw new FileOperationException(path, "No such file");
|
throw new FileOperationException(path, "No such file");
|
||||||
}
|
}
|
||||||
@ -292,7 +287,6 @@ public class FileMount implements IWritableMount {
|
|||||||
return Math.max(capacity - usedSpace, 0);
|
return Math.max(capacity - usedSpace, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public OptionalLong getCapacity() {
|
public OptionalLong getCapacity() {
|
||||||
return OptionalLong.of(capacity - MINIMUM_FILE_SIZE);
|
return OptionalLong.of(capacity - MINIMUM_FILE_SIZE);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.filesystem;
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
@ -12,7 +13,6 @@ import dan200.computercraft.api.filesystem.IWritableMount;
|
|||||||
import dan200.computercraft.core.CoreConfig;
|
import dan200.computercraft.core.CoreConfig;
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.Reference;
|
import java.lang.ref.Reference;
|
||||||
@ -60,16 +60,15 @@ public class FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void mount(String label, String location, IMount mount) throws FileSystemException {
|
public synchronized void mount(String label, String location, IMount mount) throws FileSystemException {
|
||||||
if (mount == null) throw new NullPointerException();
|
Objects.requireNonNull(mount, "mount cannot be null");
|
||||||
location = sanitizePath(location);
|
location = sanitizePath(location);
|
||||||
if (location.contains("..")) throw new FileSystemException("Cannot mount below the root");
|
if (location.contains("..")) throw new FileSystemException("Cannot mount below the root");
|
||||||
mount(new MountWrapper(label, location, mount));
|
mount(new MountWrapper(label, location, mount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void mountWritable(String label, String location, IWritableMount mount) throws FileSystemException {
|
public synchronized void mountWritable(String label, String location, IWritableMount mount) throws FileSystemException {
|
||||||
if (mount == null) {
|
Objects.requireNonNull(mount, "mount cannot be null");
|
||||||
throw new NullPointerException();
|
|
||||||
}
|
|
||||||
location = sanitizePath(location);
|
location = sanitizePath(location);
|
||||||
if (location.contains("..")) {
|
if (location.contains("..")) {
|
||||||
throw new FileSystemException("Cannot mount below the root");
|
throw new FileSystemException("Cannot mount below the root");
|
||||||
@ -313,7 +312,7 @@ public class FileSystem {
|
|||||||
} catch (AccessDeniedException e) {
|
} catch (AccessDeniedException e) {
|
||||||
throw new FileSystemException("Access denied");
|
throw new FileSystemException("Access denied");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemException(e.getMessage());
|
throw FileSystemException.of(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,7 +326,7 @@ public class FileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized <T extends Closeable> FileSystemWrapper<T> openFile(@Nonnull MountWrapper mount, @Nonnull Channel channel, @Nonnull T file) throws FileSystemException {
|
private synchronized <T extends Closeable> FileSystemWrapper<T> openFile(MountWrapper mount, Channel channel, T file) throws FileSystemException {
|
||||||
synchronized (openFiles) {
|
synchronized (openFiles) {
|
||||||
if (CoreConfig.maximumFilesOpen > 0 &&
|
if (CoreConfig.maximumFilesOpen > 0 &&
|
||||||
openFiles.size() >= CoreConfig.maximumFilesOpen) {
|
openFiles.size() >= CoreConfig.maximumFilesOpen) {
|
||||||
@ -355,7 +354,7 @@ public class FileSystem {
|
|||||||
path = sanitizePath(path);
|
path = sanitizePath(path);
|
||||||
var mount = getMount(path);
|
var mount = getMount(path);
|
||||||
var channel = mount.openForRead(path);
|
var channel = mount.openForRead(path);
|
||||||
return channel != null ? openFile(mount, channel, open.apply(channel)) : null;
|
return openFile(mount, channel, open.apply(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T extends Closeable> FileSystemWrapper<T> openForWrite(String path, boolean append, Function<WritableByteChannel, T> open) throws FileSystemException {
|
public synchronized <T extends Closeable> FileSystemWrapper<T> openForWrite(String path, boolean append, Function<WritableByteChannel, T> open) throws FileSystemException {
|
||||||
@ -364,7 +363,7 @@ public class FileSystem {
|
|||||||
path = sanitizePath(path);
|
path = sanitizePath(path);
|
||||||
var mount = getMount(path);
|
var mount = getMount(path);
|
||||||
var channel = append ? mount.openForAppend(path) : mount.openForWrite(path);
|
var channel = append ? mount.openForAppend(path) : mount.openForWrite(path);
|
||||||
return channel != null ? openFile(mount, channel, open.apply(channel)) : null;
|
return openFile(mount, channel, open.apply(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized long getFreeSpace(String path) throws FileSystemException {
|
public synchronized long getFreeSpace(String path) throws FileSystemException {
|
||||||
@ -373,7 +372,6 @@ public class FileSystem {
|
|||||||
return mount.getFreeSpace();
|
return mount.getFreeSpace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public synchronized OptionalLong getCapacity(String path) throws FileSystemException {
|
public synchronized OptionalLong getCapacity(String path) throws FileSystemException {
|
||||||
path = sanitizePath(path);
|
path = sanitizePath(path);
|
||||||
var mount = getMount(path);
|
var mount = getMount(path);
|
||||||
@ -430,9 +428,8 @@ public class FileSystem {
|
|||||||
path = cleanName.toString();
|
path = cleanName.toString();
|
||||||
|
|
||||||
// Collapse the string into its component parts, removing ..'s
|
// Collapse the string into its component parts, removing ..'s
|
||||||
var parts = path.split("/");
|
var outputParts = new ArrayDeque<String>();
|
||||||
var outputParts = new Stack<String>();
|
for (var part : Splitter.on('/').split(path)) {
|
||||||
for (var part : parts) {
|
|
||||||
if (part.isEmpty() || part.equals(".") || threeDotsPattern.matcher(part).matches()) {
|
if (part.isEmpty() || part.equals(".") || threeDotsPattern.matcher(part).matches()) {
|
||||||
// . is redundant
|
// . is redundant
|
||||||
// ... and more are treated as .
|
// ... and more are treated as .
|
||||||
@ -441,37 +438,26 @@ public class FileSystem {
|
|||||||
|
|
||||||
if (part.equals("..")) {
|
if (part.equals("..")) {
|
||||||
// .. can cancel out the last folder entered
|
// .. can cancel out the last folder entered
|
||||||
if (!outputParts.empty()) {
|
if (!outputParts.isEmpty()) {
|
||||||
var top = outputParts.peek();
|
var top = outputParts.peekLast();
|
||||||
if (!top.equals("..")) {
|
if (!top.equals("..")) {
|
||||||
outputParts.pop();
|
outputParts.removeLast();
|
||||||
} else {
|
} else {
|
||||||
outputParts.push("..");
|
outputParts.addLast("..");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outputParts.push("..");
|
outputParts.addLast("..");
|
||||||
}
|
}
|
||||||
} else if (part.length() >= 255) {
|
} else if (part.length() >= 255) {
|
||||||
// If part length > 255 and it is the last part
|
// If part length > 255 and it is the last part
|
||||||
outputParts.push(part.substring(0, 255));
|
outputParts.addLast(part.substring(0, 255));
|
||||||
} else {
|
} else {
|
||||||
// Anything else we add to the stack
|
// Anything else we add to the stack
|
||||||
outputParts.push(part);
|
outputParts.addLast(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recombine the output parts into a new string
|
return String.join("/", outputParts);
|
||||||
var result = new StringBuilder();
|
|
||||||
var it = outputParts.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
var part = it.next();
|
|
||||||
result.append(part);
|
|
||||||
if (it.hasNext()) {
|
|
||||||
result.append('/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean contains(String pathA, String pathB) {
|
public static boolean contains(String pathA, String pathB) {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.filesystem;
|
package dan200.computercraft.core.filesystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
public class FileSystemException extends Exception {
|
public class FileSystemException extends Exception {
|
||||||
@ -14,4 +15,12 @@ public class FileSystemException extends Exception {
|
|||||||
FileSystemException(String s) {
|
FileSystemException(String s) {
|
||||||
super(s);
|
super(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FileSystemException of(IOException e) {
|
||||||
|
return new FileSystemException(getMessage(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMessage(IOException e) {
|
||||||
|
return e.getMessage() == null ? "Access denied" : e.getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem;
|
|||||||
|
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
@ -57,7 +56,6 @@ public class FileSystemWrapper<T extends Closeable> implements TrackingCloseable
|
|||||||
return isOpen;
|
return isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public T get() {
|
public T get() {
|
||||||
return closeable.get();
|
return closeable.get();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem;
|
|||||||
|
|
||||||
import dan200.computercraft.api.filesystem.IFileSystem;
|
import dan200.computercraft.api.filesystem.IFileSystem;
|
||||||
|
|
||||||
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.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
@ -23,7 +22,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void makeDirectory(@Nonnull String path) throws IOException {
|
public void makeDirectory(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
filesystem.makeDir(path);
|
filesystem.makeDir(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -32,7 +31,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(@Nonnull String path) throws IOException {
|
public void delete(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
filesystem.delete(path);
|
filesystem.delete(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -40,9 +39,8 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
public ReadableByteChannel openForRead(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
// FIXME: Think of a better way of implementing this, so closing this will close on the computer.
|
// FIXME: Think of a better way of implementing this, so closing this will close on the computer.
|
||||||
return filesystem.openForRead(path, Function.identity()).get();
|
return filesystem.openForRead(path, Function.identity()).get();
|
||||||
@ -51,9 +49,8 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForWrite(@Nonnull String path) throws IOException {
|
public WritableByteChannel openForWrite(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
return filesystem.openForWrite(path, false, Function.identity()).get();
|
return filesystem.openForWrite(path, false, Function.identity()).get();
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -61,9 +58,8 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForAppend(@Nonnull String path) throws IOException {
|
public WritableByteChannel openForAppend(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
return filesystem.openForWrite(path, true, Function.identity()).get();
|
return filesystem.openForWrite(path, true, Function.identity()).get();
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -81,7 +77,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@Nonnull String path) throws IOException {
|
public boolean exists(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
return filesystem.exists(path);
|
return filesystem.exists(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -90,7 +86,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory(@Nonnull String path) throws IOException {
|
public boolean isDirectory(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
return filesystem.isDir(path);
|
return filesystem.isDir(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -99,7 +95,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
|
public void list(String path, List<String> contents) throws IOException {
|
||||||
try {
|
try {
|
||||||
Collections.addAll(contents, filesystem.list(path));
|
Collections.addAll(contents, filesystem.list(path));
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
@ -108,7 +104,7 @@ public class FileSystemWrapperMount implements IFileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize(@Nonnull String path) throws IOException {
|
public long getSize(String path) throws IOException {
|
||||||
try {
|
try {
|
||||||
return filesystem.getSize(path);
|
return filesystem.getSize(path);
|
||||||
} catch (FileSystemException e) {
|
} catch (FileSystemException e) {
|
||||||
|
@ -8,12 +8,13 @@ package dan200.computercraft.core.filesystem;
|
|||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.errorprone.annotations.concurrent.LazyInit;
|
||||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
import dan200.computercraft.api.filesystem.FileOperationException;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
import dan200.computercraft.core.util.IoUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -100,6 +101,7 @@ public class JarMount implements IMount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private FileEntry get(String path) {
|
private FileEntry get(String path) {
|
||||||
var lastEntry = root;
|
var lastEntry = root;
|
||||||
var lastIndex = 0;
|
var lastIndex = 0;
|
||||||
@ -139,18 +141,18 @@ public class JarMount implements IMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@Nonnull String path) {
|
public boolean exists(String path) {
|
||||||
return get(path) != null;
|
return get(path) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory(@Nonnull String path) {
|
public boolean isDirectory(String path) {
|
||||||
var file = get(path);
|
var file = get(path);
|
||||||
return file != null && file.isDirectory();
|
return file != null && file.isDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
|
public void list(String path, List<String> contents) throws IOException {
|
||||||
var file = get(path);
|
var file = get(path);
|
||||||
if (file == null || !file.isDirectory()) throw new FileOperationException(path, "Not a directory");
|
if (file == null || !file.isDirectory()) throw new FileOperationException(path, "Not a directory");
|
||||||
|
|
||||||
@ -158,15 +160,14 @@ public class JarMount implements IMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize(@Nonnull String path) throws IOException {
|
public long getSize(String path) throws IOException {
|
||||||
var file = get(path);
|
var file = get(path);
|
||||||
if (file != null) return file.size;
|
if (file != null) return file.size;
|
||||||
throw new FileOperationException(path, "No such file");
|
throw new FileOperationException(path, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
public ReadableByteChannel openForRead(String path) throws IOException {
|
||||||
var file = get(path);
|
var file = get(path);
|
||||||
if (file != null && !file.isDirectory()) {
|
if (file != null && !file.isDirectory()) {
|
||||||
var contents = CONTENTS_CACHE.getIfPresent(file);
|
var contents = CONTENTS_CACHE.getIfPresent(file);
|
||||||
@ -191,9 +192,8 @@ public class JarMount implements IMount {
|
|||||||
throw new FileOperationException(path, "No such file");
|
throw new FileOperationException(path, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
|
public BasicFileAttributes getAttributes(String path) throws IOException {
|
||||||
var file = get(path);
|
var file = get(path);
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
var entry = zip.getEntry(file.path);
|
var entry = zip.getEntry(file.path);
|
||||||
@ -204,8 +204,12 @@ public class JarMount implements IMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class FileEntry {
|
private static class FileEntry {
|
||||||
|
@LazyInit // TODO: Might be nicer to use @Initializer on setup(...)
|
||||||
String path;
|
String path;
|
||||||
|
|
||||||
long size;
|
long size;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
Map<String, FileEntry> children;
|
Map<String, FileEntry> children;
|
||||||
|
|
||||||
void setup(ZipEntry entry) {
|
void setup(ZipEntry entry) {
|
||||||
@ -285,6 +289,7 @@ public class JarMount implements IMount {
|
|||||||
return entry.getSize();
|
return entry.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object fileKey() {
|
public Object fileKey() {
|
||||||
return null;
|
return null;
|
||||||
@ -292,7 +297,7 @@ public class JarMount implements IMount {
|
|||||||
|
|
||||||
private static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
|
private static final FileTime EPOCH = FileTime.from(Instant.EPOCH);
|
||||||
|
|
||||||
private static FileTime orEpoch(FileTime time) {
|
private static FileTime orEpoch(@Nullable FileTime time) {
|
||||||
return time == null ? EPOCH : time;
|
return time == null ? EPOCH : time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import dan200.computercraft.api.filesystem.FileOperationException;
|
|||||||
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 javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
@ -24,7 +23,7 @@ class MountWrapper {
|
|||||||
private final String location;
|
private final String location;
|
||||||
|
|
||||||
private final IMount mount;
|
private final IMount mount;
|
||||||
private final IWritableMount writableMount;
|
private final @Nullable IWritableMount writableMount;
|
||||||
|
|
||||||
MountWrapper(String label, String location, IMount mount) {
|
MountWrapper(String label, String location, IMount mount) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
@ -107,7 +106,6 @@ class MountWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public BasicFileAttributes getAttributes(String path) throws FileSystemException {
|
public BasicFileAttributes getAttributes(String path) throws FileSystemException {
|
||||||
path = toLocal(path);
|
path = toLocal(path);
|
||||||
try {
|
try {
|
||||||
@ -213,9 +211,9 @@ class MountWrapper {
|
|||||||
return FileSystem.toLocal(path, location);
|
return FileSystem.toLocal(path, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileSystemException localExceptionOf(@Nullable String localPath, @Nonnull IOException e) {
|
private FileSystemException localExceptionOf(@Nullable String localPath, IOException e) {
|
||||||
if (!location.isEmpty() && e instanceof FileOperationException ex) {
|
if (!location.isEmpty() && e instanceof FileOperationException ex) {
|
||||||
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), ex.getMessage());
|
if (ex.getFilename() != null) return localExceptionOf(ex.getFilename(), FileSystemException.getMessage(ex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e instanceof java.nio.file.FileSystemException ex) {
|
if (e instanceof java.nio.file.FileSystemException ex) {
|
||||||
@ -225,7 +223,7 @@ class MountWrapper {
|
|||||||
return localPath == null ? new FileSystemException(message) : localExceptionOf(localPath, message);
|
return localPath == null ? new FileSystemException(message) : localExceptionOf(localPath, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FileSystemException(e.getMessage());
|
return FileSystemException.of(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileSystemException localExceptionOf(String path, String message) {
|
private FileSystemException localExceptionOf(String path, String message) {
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem;
|
|||||||
|
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
|
|
||||||
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;
|
||||||
@ -23,34 +22,32 @@ public class SubMount implements IMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@Nonnull String path) throws IOException {
|
public boolean exists(String path) throws IOException {
|
||||||
return parent.exists(getFullPath(path));
|
return parent.exists(getFullPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory(@Nonnull String path) throws IOException {
|
public boolean isDirectory(String path) throws IOException {
|
||||||
return parent.isDirectory(getFullPath(path));
|
return parent.isDirectory(getFullPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> contents) throws IOException {
|
public void list(String path, List<String> contents) throws IOException {
|
||||||
parent.list(getFullPath(path), contents);
|
parent.list(getFullPath(path), contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize(@Nonnull String path) throws IOException {
|
public long getSize(String path) throws IOException {
|
||||||
return parent.getSize(getFullPath(path));
|
return parent.getSize(getFullPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) throws IOException {
|
public ReadableByteChannel openForRead(String path) throws IOException {
|
||||||
return parent.openForRead(getFullPath(path));
|
return parent.openForRead(getFullPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public BasicFileAttributes getAttributes(@Nonnull String path) throws IOException {
|
public BasicFileAttributes getAttributes(String path) throws IOException {
|
||||||
return parent.getAttributes(getFullPath(path));
|
return parent.getAttributes(getFullPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,14 +28,14 @@ class BasicFunction extends VarArgFunction {
|
|||||||
private final LuaMethod method;
|
private final LuaMethod method;
|
||||||
private final Object instance;
|
private final Object instance;
|
||||||
private final ILuaContext context;
|
private final ILuaContext context;
|
||||||
private final String name;
|
private final String funcName;
|
||||||
|
|
||||||
BasicFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
|
BasicFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
|
||||||
this.machine = machine;
|
this.machine = machine;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.name = name;
|
funcName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -47,7 +47,7 @@ class BasicFunction extends VarArgFunction {
|
|||||||
} catch (LuaException e) {
|
} catch (LuaException e) {
|
||||||
throw wrap(e);
|
throw wrap(e);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, instance, t);
|
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, instance, t);
|
||||||
throw new LuaError("Java Exception Thrown: " + t, 0);
|
throw new LuaError("Java Exception Thrown: " + t, 0);
|
||||||
} finally {
|
} finally {
|
||||||
arguments.close();
|
arguments.close();
|
||||||
|
@ -27,7 +27,6 @@ import org.squiddev.cobalt.debug.DebugState;
|
|||||||
import org.squiddev.cobalt.lib.*;
|
import org.squiddev.cobalt.lib.*;
|
||||||
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
|
import org.squiddev.cobalt.lib.platform.VoidResourceManipulator;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
@ -58,11 +57,11 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
private final TimeoutDebugHandler debug;
|
private final TimeoutDebugHandler debug;
|
||||||
private final ILuaContext context;
|
private final ILuaContext context;
|
||||||
|
|
||||||
private LuaState state;
|
private @Nullable LuaState state;
|
||||||
private LuaTable globals;
|
private @Nullable LuaTable globals;
|
||||||
|
|
||||||
private LuaThread mainRoutine = null;
|
private @Nullable LuaThread mainRoutine = null;
|
||||||
private String eventFilter = null;
|
private @Nullable String eventFilter = null;
|
||||||
|
|
||||||
public CobaltLuaMachine(MachineEnvironment environment) {
|
public CobaltLuaMachine(MachineEnvironment environment) {
|
||||||
timeout = environment.timeout();
|
timeout = environment.timeout();
|
||||||
@ -115,7 +114,9 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAPI(@Nonnull ILuaAPI api) {
|
public void addAPI(ILuaAPI api) {
|
||||||
|
if (globals == null) throw new IllegalStateException("Machine has been closed");
|
||||||
|
|
||||||
// Add the methods of an API to the global table
|
// Add the methods of an API to the global table
|
||||||
var table = wrapLuaObject(api);
|
var table = wrapLuaObject(api);
|
||||||
if (table == null) {
|
if (table == null) {
|
||||||
@ -128,9 +129,9 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MachineResult loadBios(@Nonnull InputStream bios) {
|
public MachineResult loadBios(InputStream bios) {
|
||||||
// Begin executing a file (ie, the bios)
|
if (mainRoutine != null) throw new IllegalStateException("Already set up the machine");
|
||||||
if (mainRoutine != null) return MachineResult.OK;
|
if (state == null || globals == null) throw new IllegalStateException("Machine has been destroyed.");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var value = LoadState.load(state, bios, "@bios.lua", globals);
|
var value = LoadState.load(state, bios, "@bios.lua", globals);
|
||||||
@ -147,8 +148,8 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MachineResult handleEvent(String eventName, Object[] arguments) {
|
public MachineResult handleEvent(@Nullable String eventName, @Nullable Object[] arguments) {
|
||||||
if (mainRoutine == null) return MachineResult.OK;
|
if (mainRoutine == null || state == null) throw new IllegalStateException("Machine has been closed");
|
||||||
|
|
||||||
if (eventFilter != null && eventName != null && !eventName.equals(eventFilter) && !eventName.equals("terminate")) {
|
if (eventFilter != null && eventName != null && !eventName.equals(eventFilter) && !eventName.equals("terminate")) {
|
||||||
return MachineResult.OK;
|
return MachineResult.OK;
|
||||||
@ -232,13 +233,13 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
try {
|
try {
|
||||||
if (table.keyCount() == 0) return null;
|
if (table.keyCount() == 0) return null;
|
||||||
} catch (LuaError ignored) {
|
} catch (LuaError ignored) {
|
||||||
|
// next should never throw on nil.
|
||||||
}
|
}
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) {
|
||||||
private LuaValue toValue(@Nullable Object object, @Nullable Map<Object, LuaValue> values) {
|
|
||||||
if (object == null) return Constants.NIL;
|
if (object == null) return Constants.NIL;
|
||||||
if (object instanceof Number num) return valueOf(num.doubleValue());
|
if (object instanceof Number num) return valueOf(num.doubleValue());
|
||||||
if (object instanceof Boolean bool) return valueOf(bool);
|
if (object instanceof Boolean bool) return valueOf(bool);
|
||||||
@ -304,11 +305,11 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
return Constants.NIL;
|
return Constants.NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Varargs toValues(Object[] objects) {
|
Varargs toValues(@Nullable Object[] objects) {
|
||||||
if (objects == null || objects.length == 0) return Constants.NONE;
|
if (objects == null || objects.length == 0) return Constants.NONE;
|
||||||
if (objects.length == 1) return toValue(objects[0], null);
|
if (objects.length == 1) return toValue(objects[0], null);
|
||||||
|
|
||||||
Map<Object, LuaValue> result = new IdentityHashMap<>(0);
|
var result = new IdentityHashMap<Object, LuaValue>(0);
|
||||||
var values = new LuaValue[objects.length];
|
var values = new LuaValue[objects.length];
|
||||||
for (var i = 0; i < values.length; i++) {
|
for (var i = 0; i < values.length; i++) {
|
||||||
var object = objects[i];
|
var object = objects[i];
|
||||||
@ -317,7 +318,8 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
return varargsOf(values);
|
return varargsOf(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object toObject(LuaValue value, Map<LuaValue, Object> objects) {
|
@Nullable
|
||||||
|
static Object toObject(LuaValue value, @Nullable IdentityHashMap<LuaValue, Object> objects) {
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
case Constants.TNIL:
|
case Constants.TNIL:
|
||||||
case Constants.TNONE:
|
case Constants.TNONE:
|
||||||
@ -448,6 +450,7 @@ public class CobaltLuaMachine implements ILuaMachine {
|
|||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 7954092008586367501L;
|
private static final long serialVersionUID = 7954092008586367501L;
|
||||||
|
|
||||||
|
@SuppressWarnings("StaticAssignmentOfThrowable")
|
||||||
static final HardAbortError INSTANCE = new HardAbortError();
|
static final HardAbortError INSTANCE = new HardAbortError();
|
||||||
|
|
||||||
private HardAbortError() {
|
private HardAbortError() {
|
||||||
|
@ -8,7 +8,6 @@ package dan200.computercraft.core.lua;
|
|||||||
import dan200.computercraft.api.lua.IDynamicLuaObject;
|
import dan200.computercraft.api.lua.IDynamicLuaObject;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ public interface ILuaMachine {
|
|||||||
*
|
*
|
||||||
* @param api The API to register.
|
* @param api The API to register.
|
||||||
*/
|
*/
|
||||||
void addAPI(@Nonnull ILuaAPI api);
|
void addAPI(ILuaAPI api);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a function from the provided program, and set it up to run when {@link #handleEvent(String, Object[])} is
|
* Create a function from the provided program, and set it up to run when {@link #handleEvent(String, Object[])} is
|
||||||
@ -43,7 +42,7 @@ public interface ILuaMachine {
|
|||||||
* @param bios The stream containing the boot program.
|
* @param bios The stream containing the boot program.
|
||||||
* @return The result of loading this machine. Will either be OK, or the error message when loading the bios.
|
* @return The result of loading this machine. Will either be OK, or the error message when loading the bios.
|
||||||
*/
|
*/
|
||||||
MachineResult loadBios(@Nonnull InputStream bios);
|
MachineResult loadBios(InputStream bios);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resume the machine, either starting or resuming the coroutine.
|
* Resume the machine, either starting or resuming the coroutine.
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.lua;
|
|||||||
|
|
||||||
import dan200.computercraft.core.computer.TimeoutState;
|
import dan200.computercraft.core.computer.TimeoutState;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -42,19 +41,19 @@ public final class MachineResult {
|
|||||||
|
|
||||||
private final boolean error;
|
private final boolean error;
|
||||||
private final boolean pause;
|
private final boolean pause;
|
||||||
private final String message;
|
private final @Nullable String message;
|
||||||
|
|
||||||
private MachineResult(boolean error, boolean pause, String message) {
|
private MachineResult(boolean error, boolean pause, @Nullable String message) {
|
||||||
this.pause = pause;
|
this.pause = pause;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MachineResult error(@Nonnull String error) {
|
public static MachineResult error(String error) {
|
||||||
return new MachineResult(true, false, error);
|
return new MachineResult(true, false, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MachineResult error(@Nonnull Exception error) {
|
public static MachineResult error(Exception error) {
|
||||||
return new MachineResult(true, false, error.getMessage());
|
return new MachineResult(true, false, error.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ import org.squiddev.cobalt.*;
|
|||||||
import org.squiddev.cobalt.debug.DebugFrame;
|
import org.squiddev.cobalt.debug.DebugFrame;
|
||||||
import org.squiddev.cobalt.function.ResumableVarArgFunction;
|
import org.squiddev.cobalt.function.ResumableVarArgFunction;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls a {@link LuaMethod}, and interprets the resulting {@link MethodResult}, either returning the result or yielding
|
* Calls a {@link LuaMethod}, and interprets the resulting {@link MethodResult}, either returning the result or yielding
|
||||||
@ -26,7 +25,6 @@ import javax.annotation.Nonnull;
|
|||||||
class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterpreterFunction.Container> {
|
class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterpreterFunction.Container> {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ResultInterpreterFunction.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ResultInterpreterFunction.class);
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
static class Container {
|
static class Container {
|
||||||
ILuaCallback callback;
|
ILuaCallback callback;
|
||||||
final int errorAdjust;
|
final int errorAdjust;
|
||||||
@ -41,14 +39,14 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete
|
|||||||
private final LuaMethod method;
|
private final LuaMethod method;
|
||||||
private final Object instance;
|
private final Object instance;
|
||||||
private final ILuaContext context;
|
private final ILuaContext context;
|
||||||
private final String name;
|
private final String funcName;
|
||||||
|
|
||||||
ResultInterpreterFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
|
ResultInterpreterFunction(CobaltLuaMachine machine, LuaMethod method, Object instance, ILuaContext context, String name) {
|
||||||
this.machine = machine;
|
this.machine = machine;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.name = name;
|
funcName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -60,7 +58,7 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete
|
|||||||
} catch (LuaException e) {
|
} catch (LuaException e) {
|
||||||
throw wrap(e, 0);
|
throw wrap(e, 0);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, instance, t);
|
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, instance, t);
|
||||||
throw new LuaError("Java Exception Thrown: " + t, 0);
|
throw new LuaError("Java Exception Thrown: " + t, 0);
|
||||||
} finally {
|
} finally {
|
||||||
arguments.close();
|
arguments.close();
|
||||||
@ -84,7 +82,7 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete
|
|||||||
} catch (LuaException e) {
|
} catch (LuaException e) {
|
||||||
throw wrap(e, container.errorAdjust);
|
throw wrap(e, container.errorAdjust);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", name, container.callback, t);
|
LOG.error(Logging.JAVA_ERROR, "Error calling {} on {}", funcName, container.callback, t);
|
||||||
throw new LuaError("Java Exception Thrown: " + t, 0);
|
throw new LuaError("Java Exception Thrown: " + t, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaValues;
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static dan200.computercraft.api.lua.LuaValues.badTableItem;
|
import static dan200.computercraft.api.lua.LuaValues.badTableItem;
|
||||||
@ -18,7 +18,7 @@ import static dan200.computercraft.api.lua.LuaValues.getNumericType;
|
|||||||
class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object> {
|
class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object> {
|
||||||
private final VarargArguments arguments;
|
private final VarargArguments arguments;
|
||||||
private final LuaTable table;
|
private final LuaTable table;
|
||||||
private Map<Object, Object> backingMap;
|
private @Nullable Map<Object, Object> backingMap;
|
||||||
|
|
||||||
TableImpl(VarargArguments arguments, LuaTable table) {
|
TableImpl(VarargArguments arguments, LuaTable table) {
|
||||||
this.arguments = arguments;
|
this.arguments = arguments;
|
||||||
@ -61,7 +61,6 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private LuaValue getImpl(Object o) {
|
private LuaValue getImpl(Object o) {
|
||||||
checkValid();
|
checkValid();
|
||||||
if (o instanceof String) return table.rawget((String) o);
|
if (o instanceof String) return table.rawget((String) o);
|
||||||
@ -74,12 +73,12 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
|||||||
return !getImpl(o).isNil();
|
return !getImpl(o).isNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object get(Object o) {
|
public Object get(Object o) {
|
||||||
return CobaltLuaMachine.toObject(getImpl(o), null);
|
return CobaltLuaMachine.toObject(getImpl(o), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private Map<Object, Object> getBackingMap() {
|
private Map<Object, Object> getBackingMap() {
|
||||||
checkValid();
|
checkValid();
|
||||||
if (backingMap != null) return backingMap;
|
if (backingMap != null) return backingMap;
|
||||||
@ -93,19 +92,16 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
|
|||||||
return getBackingMap().containsKey(o);
|
return getBackingMap().containsKey(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Object> keySet() {
|
public Set<Object> keySet() {
|
||||||
return getBackingMap().keySet();
|
return getBackingMap().keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Object> values() {
|
public Collection<Object> values() {
|
||||||
return getBackingMap().values();
|
return getBackingMap().values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<Object, Object>> entrySet() {
|
public Set<Entry<Object, Object>> entrySet() {
|
||||||
return getBackingMap().entrySet();
|
return getBackingMap().entrySet();
|
||||||
|
@ -10,7 +10,6 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaValues;
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
import org.squiddev.cobalt.*;
|
import org.squiddev.cobalt.*;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -20,7 +19,7 @@ final class VarargArguments implements IArguments {
|
|||||||
|
|
||||||
boolean closed;
|
boolean closed;
|
||||||
private final Varargs varargs;
|
private final Varargs varargs;
|
||||||
private Object[] cache;
|
private @Nullable Object[] cache;
|
||||||
|
|
||||||
private VarargArguments(Varargs varargs) {
|
private VarargArguments(Varargs varargs) {
|
||||||
this.varargs = varargs;
|
this.varargs = varargs;
|
||||||
@ -72,7 +71,6 @@ final class VarargArguments implements IArguments {
|
|||||||
return value instanceof LuaInteger ? value.toInteger() : (long) LuaValues.checkFinite(index, value.toDouble());
|
return value instanceof LuaInteger ? value.toInteger() : (long) LuaValues.checkFinite(index, value.toDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getBytes(int index) throws LuaException {
|
public ByteBuffer getBytes(int index) throws LuaException {
|
||||||
var value = varargs.arg(index + 1);
|
var value = varargs.arg(index + 1);
|
||||||
@ -92,7 +90,6 @@ final class VarargArguments implements IArguments {
|
|||||||
return Optional.of(ByteBuffer.wrap(str.bytes, str.offset, str.length).asReadOnlyBuffer());
|
return Optional.of(ByteBuffer.wrap(str.bytes, str.offset, str.length).asReadOnlyBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public dan200.computercraft.api.lua.LuaTable<?, ?> getTableUnsafe(int index) throws LuaException {
|
public dan200.computercraft.api.lua.LuaTable<?, ?> getTableUnsafe(int index) throws LuaException {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
@ -104,7 +101,6 @@ final class VarargArguments implements IArguments {
|
|||||||
return new TableImpl(this, (LuaTable) value);
|
return new TableImpl(this, (LuaTable) value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<dan200.computercraft.api.lua.LuaTable<?, ?>> optTableUnsafe(int index) throws LuaException {
|
public Optional<dan200.computercraft.api.lua.LuaTable<?, ?>> optTableUnsafe(int index) throws LuaException {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ComputerCraft's core Lua runtime and APIs.
|
||||||
|
* <p>
|
||||||
|
* This is not considered part of the stable API, and so should not be consumed by other Minecraft mods. However,
|
||||||
|
* emulators or other CC-tooling may find this useful.
|
||||||
|
*/
|
||||||
|
@DefaultQualifier(value = NonNull.class, locations = {
|
||||||
|
TypeUseLocation.RETURN,
|
||||||
|
TypeUseLocation.PARAMETER,
|
||||||
|
TypeUseLocation.FIELD,
|
||||||
|
})
|
||||||
|
package dan200.computercraft.core;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
import org.checkerframework.framework.qual.TypeUseLocation;
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.terminal;
|
|||||||
|
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
public class Palette {
|
public class Palette {
|
||||||
public static final int PALETTE_SIZE = 16;
|
public static final int PALETTE_SIZE = 16;
|
||||||
@ -46,7 +45,7 @@ public class Palette {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double[] getColour(int i) {
|
public double[] getColour(int i) {
|
||||||
return i >= 0 && i < PALETTE_SIZE ? colours[i] : null;
|
return colours[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,7 +57,6 @@ public class Palette {
|
|||||||
* @param i The colour index.
|
* @param i The colour index.
|
||||||
* @return The number as a tuple of bytes.
|
* @return The number as a tuple of bytes.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
public byte[] getRenderColours(int i) {
|
public byte[] getRenderColours(int i) {
|
||||||
return byteColours[i];
|
return byteColours[i];
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.core.terminal;
|
|||||||
|
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ public class Terminal {
|
|||||||
this(width, height, colour, null);
|
this(width, height, colour, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Terminal(int width, int height, boolean colour, Runnable changedCallback) {
|
public Terminal(int width, int height, boolean colour, @Nullable Runnable changedCallback) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.colour = colour;
|
this.colour = colour;
|
||||||
@ -163,7 +162,6 @@ public class Terminal {
|
|||||||
return cursorBackgroundColour;
|
return cursorBackgroundColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public Palette getPalette() {
|
public Palette getPalette() {
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
@ -234,10 +232,7 @@ public class Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized TextBuffer getLine(int y) {
|
public synchronized TextBuffer getLine(int y) {
|
||||||
if (y >= 0 && y < height) {
|
return text[y];
|
||||||
return text[y];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void setLine(int y, String text, String textColour, String backgroundColour) {
|
public synchronized void setLine(int y, String text, String textColour, String backgroundColour) {
|
||||||
@ -248,17 +243,11 @@ public class Terminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized TextBuffer getTextColourLine(int y) {
|
public synchronized TextBuffer getTextColourLine(int y) {
|
||||||
if (y >= 0 && y < height) {
|
return textColour[y];
|
||||||
return textColour[y];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized TextBuffer getBackgroundColourLine(int y) {
|
public synchronized TextBuffer getBackgroundColourLine(int y) {
|
||||||
if (y >= 0 && y < height) {
|
return backgroundColour[y];
|
||||||
return backgroundColour[y];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setChanged() {
|
public final void setChanged() {
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.util;
|
package dan200.computercraft.core.util;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public enum Colour {
|
public enum Colour {
|
||||||
BLACK(0x111111),
|
BLACK(0x111111),
|
||||||
RED(0xcc4c4c),
|
RED(0xcc4c4c),
|
||||||
@ -29,6 +31,7 @@ public enum Colour {
|
|||||||
return Colour.VALUES[colour];
|
return Colour.VALUES[colour];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public static Colour fromHex(int colour) {
|
public static Colour fromHex(int colour) {
|
||||||
for (var entry : VALUES) {
|
for (var entry : VALUES) {
|
||||||
if (entry.getHex() == colour) return entry;
|
if (entry.getHex() == colour) return entry;
|
||||||
|
@ -17,6 +17,7 @@ public final class IoUtil {
|
|||||||
try {
|
try {
|
||||||
if (closeable != null) closeable.close();
|
if (closeable != null) closeable.close();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
|
// The whole point here is to suppress these exceptions!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@ public final class StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String normaliseLabel(String label) {
|
public static String normaliseLabel(String label) {
|
||||||
if (label == null) return null;
|
|
||||||
|
|
||||||
var length = Math.min(32, label.length());
|
var length = Math.min(32, label.length());
|
||||||
var builder = new StringBuilder(length);
|
var builder = new StringBuilder(length);
|
||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
|
@ -23,7 +23,6 @@ import org.opentest4j.AssertionFailedError;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -249,7 +248,6 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class FakeModem implements IPeripheral {
|
public static class FakeModem implements IPeripheral {
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "modem";
|
return "modem";
|
||||||
@ -267,7 +265,6 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class FakePeripheralHub implements IPeripheral {
|
public static class FakePeripheralHub implements IPeripheral {
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "peripheral_hub";
|
return "peripheral_hub";
|
||||||
|
@ -8,7 +8,6 @@ package dan200.computercraft.core.apis;
|
|||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
import dan200.computercraft.core.asm.LuaMethod;
|
import dan200.computercraft.core.asm.LuaMethod;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -51,7 +50,7 @@ public class ObjectWrapper implements ILuaContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long issueMainThreadTask(@Nonnull ILuaTask task) {
|
public long issueMainThreadTask(ILuaTask task) {
|
||||||
throw new IllegalStateException("Method should never queue events");
|
throw new IllegalStateException("Method should never queue events");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import dan200.computercraft.core.computer.ComputerSide;
|
|||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -241,7 +240,7 @@ public class GeneratorTest {
|
|||||||
|
|
||||||
private static final ILuaContext CONTEXT = new ILuaContext() {
|
private static final ILuaContext CONTEXT = new ILuaContext() {
|
||||||
@Override
|
@Override
|
||||||
public long issueMainThreadTask(@Nonnull ILuaTask task) {
|
public long issueMainThreadTask(ILuaTask task) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,6 @@ import dan200.computercraft.core.computer.ComputerBootstrap;
|
|||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@ -99,7 +98,6 @@ public class MethodTest {
|
|||||||
return 123;
|
return 123;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "main_thread";
|
return "main_thread";
|
||||||
@ -112,21 +110,18 @@ public class MethodTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Dynamic implements IDynamicLuaObject, ILuaAPI, IDynamicPeripheral {
|
public static class Dynamic implements IDynamicLuaObject, ILuaAPI, IDynamicPeripheral {
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames() {
|
public String[] getMethodNames() {
|
||||||
return new String[]{ "foo" };
|
return new String[]{ "foo" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public MethodResult callMethod(@Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) {
|
public MethodResult callMethod(ILuaContext context, int method, IArguments arguments) {
|
||||||
return MethodResult.of(123);
|
return MethodResult.of(123);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public MethodResult callMethod(@Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) {
|
public MethodResult callMethod(IComputerAccess computer, ILuaContext context, int method, IArguments arguments) {
|
||||||
return callMethod(context, method, arguments);
|
return callMethod(context, method, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +135,6 @@ public class MethodTest {
|
|||||||
return new String[]{ "dynamic" };
|
return new String[]{ "dynamic" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "dynamic";
|
return "dynamic";
|
||||||
@ -179,7 +173,6 @@ public class MethodTest {
|
|||||||
throw new LuaException("!");
|
throw new LuaException("!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "throw";
|
return "throw";
|
||||||
@ -192,7 +185,6 @@ public class MethodTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ManyMethods implements IDynamicLuaObject, ILuaAPI {
|
public static class ManyMethods implements IDynamicLuaObject, ILuaAPI {
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames() {
|
public String[] getMethodNames() {
|
||||||
var methods = new String[40];
|
var methods = new String[40];
|
||||||
@ -200,9 +192,8 @@ public class MethodTest {
|
|||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public MethodResult callMethod(@Nonnull ILuaContext context, int method, @Nonnull IArguments arguments) throws LuaException {
|
public MethodResult callMethod(ILuaContext context, int method, IArguments arguments) throws LuaException {
|
||||||
return MethodResult.of();
|
return MethodResult.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,20 +34,6 @@ class TerminalTest {
|
|||||||
assertEquals("fedcba9876543210", terminal.getBackgroundColourLine(1).toString());
|
assertEquals("fedcba9876543210", terminal.getBackgroundColourLine(1).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void testGetLineOutOfBounds() {
|
|
||||||
var terminal = new Terminal(16, 9, true);
|
|
||||||
|
|
||||||
assertNull(terminal.getLine(-5));
|
|
||||||
assertNull(terminal.getLine(12));
|
|
||||||
|
|
||||||
assertNull(terminal.getTextColourLine(-5));
|
|
||||||
assertNull(terminal.getTextColourLine(12));
|
|
||||||
|
|
||||||
assertNull(terminal.getBackgroundColourLine(-5));
|
|
||||||
assertNull(terminal.getBackgroundColourLine(12));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testDefaults() {
|
void testDefaults() {
|
||||||
var terminal = new Terminal(16, 9, true);
|
var terminal = new Terminal(16, 9, true);
|
||||||
|
@ -8,7 +8,6 @@ package dan200.computercraft.test.core;
|
|||||||
import net.jqwik.api.*;
|
import net.jqwik.api.*;
|
||||||
import net.jqwik.api.arbitraries.SizableArbitrary;
|
import net.jqwik.api.arbitraries.SizableArbitrary;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -43,25 +42,21 @@ public final class ArbitraryByteBuffer implements SizableArbitrary<ByteBuffer> {
|
|||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public SizableArbitrary<ByteBuffer> ofMinSize(int minSize) {
|
public SizableArbitrary<ByteBuffer> ofMinSize(int minSize) {
|
||||||
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public SizableArbitrary<ByteBuffer> ofMaxSize(int maxSize) {
|
public SizableArbitrary<ByteBuffer> ofMaxSize(int maxSize) {
|
||||||
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public SizableArbitrary<ByteBuffer> withSizeDistribution(@Nonnull RandomDistribution distribution) {
|
public SizableArbitrary<ByteBuffer> withSizeDistribution(RandomDistribution distribution) {
|
||||||
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
return new ArbitraryByteBuffer(minSize, maxSize, distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public RandomGenerator<ByteBuffer> generator(int genSize) {
|
public RandomGenerator<ByteBuffer> generator(int genSize) {
|
||||||
var min = BigInteger.valueOf(minSize);
|
var min = BigInteger.valueOf(minSize);
|
||||||
@ -78,7 +73,6 @@ public final class ArbitraryByteBuffer implements SizableArbitrary<ByteBuffer> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public EdgeCases<ByteBuffer> edgeCases(int maxEdgeCases) {
|
public EdgeCases<ByteBuffer> edgeCases(int maxEdgeCases) {
|
||||||
return EdgeCases.fromSuppliers(Arrays.asList(
|
return EdgeCases.fromSuppliers(Arrays.asList(
|
||||||
@ -133,13 +127,11 @@ public final class ArbitraryByteBuffer implements SizableArbitrary<ByteBuffer> {
|
|||||||
this.minSize = minSize;
|
this.minSize = minSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer value() {
|
public ByteBuffer value() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<Shrinkable<ByteBuffer>> shrink() {
|
public Stream<Shrinkable<ByteBuffer>> shrink() {
|
||||||
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Shrinkable<ByteBuffer>>(3, 0) {
|
return StreamSupport.stream(new Spliterators.AbstractSpliterator<Shrinkable<ByteBuffer>>(3, 0) {
|
||||||
@ -160,7 +152,6 @@ public final class ArbitraryByteBuffer implements SizableArbitrary<ByteBuffer> {
|
|||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ShrinkingDistance distance() {
|
public ShrinkingDistance distance() {
|
||||||
return ShrinkingDistance.of(value.remaining() - minSize);
|
return ShrinkingDistance.of(value.remaining() - minSize);
|
||||||
|
@ -16,7 +16,6 @@ import dan200.computercraft.core.metrics.Metric;
|
|||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.test.core.computer.BasicEnvironment;
|
import dan200.computercraft.test.core.computer.BasicEnvironment;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public abstract class BasicApiEnvironment implements IAPIEnvironment {
|
public abstract class BasicApiEnvironment implements IAPIEnvironment {
|
||||||
@ -32,25 +31,21 @@ public abstract class BasicApiEnvironment implements IAPIEnvironment {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ComputerEnvironment getComputerEnvironment() {
|
public ComputerEnvironment getComputerEnvironment() {
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public GlobalEnvironment getGlobalEnvironment() {
|
public GlobalEnvironment getGlobalEnvironment() {
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public IWorkMonitor getMainThreadMonitor() {
|
public IWorkMonitor getMainThreadMonitor() {
|
||||||
throw new IllegalStateException("Main thread monitor not available");
|
throw new IllegalStateException("Main thread monitor not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public Terminal getTerminal() {
|
public Terminal getTerminal() {
|
||||||
throw new IllegalStateException("Terminal not available");
|
throw new IllegalStateException("Terminal not available");
|
||||||
@ -128,10 +123,10 @@ public abstract class BasicApiEnvironment implements IAPIEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void observe(@Nonnull Metric.Event summary, long value) {
|
public void observe(Metric.Event summary, long value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void observe(@Nonnull Metric.Counter counter) {
|
public void observe(Metric.Counter counter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import dan200.computercraft.core.metrics.Metric;
|
|||||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.test.core.filesystem.MemoryMount;
|
import dan200.computercraft.test.core.filesystem.MemoryMount;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -60,13 +59,11 @@ public class BasicEnvironment implements ComputerEnvironment, GlobalEnvironment,
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getHostString() {
|
public String getHostString() {
|
||||||
return "ComputerCraft 1.0 (Test environment)";
|
return "ComputerCraft 1.0 (Test environment)";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public String getUserAgent() {
|
public String getUserAgent() {
|
||||||
return "ComputerCraft/1.0";
|
return "ComputerCraft/1.0";
|
||||||
|
@ -5,16 +5,17 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.test.core.filesystem;
|
package dan200.computercraft.test.core.filesystem;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.filesystem.FileOperationException;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.ReadableByteChannel;
|
import java.nio.channels.ReadableByteChannel;
|
||||||
import java.nio.channels.WritableByteChannel;
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +31,7 @@ public class MemoryMount implements IWritableMount {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void makeDirectory(@Nonnull String path) {
|
public void makeDirectory(String path) {
|
||||||
var file = new File(path);
|
var file = new File(path);
|
||||||
while (file != null) {
|
while (file != null) {
|
||||||
directories.add(file.getPath());
|
directories.add(file.getPath());
|
||||||
@ -39,7 +40,7 @@ public class MemoryMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(@Nonnull String path) {
|
public void delete(String path) {
|
||||||
if (files.containsKey(path)) {
|
if (files.containsKey(path)) {
|
||||||
files.remove(path);
|
files.remove(path);
|
||||||
} else {
|
} else {
|
||||||
@ -55,9 +56,8 @@ public class MemoryMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForWrite(@Nonnull final String path) {
|
public WritableByteChannel openForWrite(final String path) {
|
||||||
return Channels.newChannel(new ByteArrayOutputStream() {
|
return Channels.newChannel(new ByteArrayOutputStream() {
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@ -67,9 +67,8 @@ public class MemoryMount implements IWritableMount {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public WritableByteChannel openForAppend(@Nonnull final String path) throws IOException {
|
public WritableByteChannel openForAppend(final String path) throws IOException {
|
||||||
var stream = new ByteArrayOutputStream() {
|
var stream = new ByteArrayOutputStream() {
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@ -90,35 +89,36 @@ public class MemoryMount implements IWritableMount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@Nonnull String path) {
|
public boolean exists(String path) {
|
||||||
return files.containsKey(path) || directories.contains(path);
|
return files.containsKey(path) || directories.contains(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory(@Nonnull String path) {
|
public boolean isDirectory(String path) {
|
||||||
return directories.contains(path);
|
return directories.contains(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void list(@Nonnull String path, @Nonnull List<String> files) {
|
public void list(String path, List<String> files) {
|
||||||
for (var file : this.files.keySet()) {
|
for (var file : this.files.keySet()) {
|
||||||
if (file.startsWith(path)) files.add(file.substring(path.length() + 1));
|
if (file.startsWith(path)) files.add(file.substring(path.length() + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getSize(@Nonnull String path) {
|
public long getSize(String path) {
|
||||||
throw new RuntimeException("Not implemented");
|
throw new RuntimeException("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public ReadableByteChannel openForRead(@Nonnull String path) {
|
public ReadableByteChannel openForRead(String path) throws FileOperationException {
|
||||||
return new ArrayByteChannel(files.get(path));
|
var file = files.get(path);
|
||||||
|
if (file == null) throw new FileOperationException(path, "File not found");
|
||||||
|
return new ArrayByteChannel(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryMount addFile(String file, String contents) {
|
public MemoryMount addFile(String file, String contents) {
|
||||||
files.put(file, contents.getBytes());
|
files.put(file, contents.getBytes(StandardCharsets.UTF_8));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test helpers for ComputerCraft's core Lua runtime and APIs.
|
||||||
|
*/
|
||||||
|
@DefaultQualifier(value = NonNull.class, locations = {
|
||||||
|
TypeUseLocation.RETURN,
|
||||||
|
TypeUseLocation.PARAMETER,
|
||||||
|
TypeUseLocation.FIELD,
|
||||||
|
})
|
||||||
|
package dan200.computercraft.test.core;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
import org.checkerframework.framework.qual.TypeUseLocation;
|
@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.shared.computer.terminal;
|
package dan200.computercraft.shared.computer.terminal;
|
||||||
|
|
||||||
import dan200.computercraft.core.util.IoUtil;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufInputStream;
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
import io.netty.buffer.ByteBufOutputStream;
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
@ -119,14 +118,10 @@ public class TerminalState {
|
|||||||
if (compressed != null) return compressed;
|
if (compressed != null) return compressed;
|
||||||
|
|
||||||
var compressed = Unpooled.buffer();
|
var compressed = Unpooled.buffer();
|
||||||
OutputStream stream = null;
|
try (OutputStream stream = new GZIPOutputStream(new ByteBufOutputStream(compressed))) {
|
||||||
try {
|
|
||||||
stream = new GZIPOutputStream(new ByteBufOutputStream(compressed));
|
|
||||||
stream.write(buffer.array(), buffer.arrayOffset(), buffer.readableBytes());
|
stream.write(buffer.array(), buffer.arrayOffset(), buffer.readableBytes());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
} finally {
|
|
||||||
IoUtil.closeQuietly(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.compressed = compressed;
|
return this.compressed = compressed;
|
||||||
@ -135,9 +130,7 @@ public class TerminalState {
|
|||||||
private static ByteBuf readCompressed(ByteBuf buf, int length, boolean compress) {
|
private static ByteBuf readCompressed(ByteBuf buf, int length, boolean compress) {
|
||||||
if (compress) {
|
if (compress) {
|
||||||
var buffer = Unpooled.buffer();
|
var buffer = Unpooled.buffer();
|
||||||
InputStream stream = null;
|
try (InputStream stream = new GZIPInputStream(new ByteBufInputStream(buf, length))) {
|
||||||
try {
|
|
||||||
stream = new GZIPInputStream(new ByteBufInputStream(buf, length));
|
|
||||||
var swap = new byte[8192];
|
var swap = new byte[8192];
|
||||||
while (true) {
|
while (true) {
|
||||||
var bytes = stream.read(swap);
|
var bytes = stream.read(swap);
|
||||||
@ -146,8 +139,6 @@ public class TerminalState {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
} finally {
|
|
||||||
IoUtil.closeQuietly(stream);
|
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
} else {
|
} else {
|
||||||
|
@ -9,9 +9,9 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
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 dan200.computercraft.core.util.StringUtil;
|
||||||
import dan200.computercraft.shared.MediaProviders;
|
import dan200.computercraft.shared.MediaProviders;
|
||||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||||
import dan200.computercraft.core.util.StringUtil;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -79,17 +79,16 @@ public class DiskDrivePeripheral implements IPeripheral {
|
|||||||
* If the inserted disk's label can't be changed (for example, a record),
|
* If the inserted disk's label can't be changed (for example, a record),
|
||||||
* an error will be thrown.
|
* an error will be thrown.
|
||||||
*
|
*
|
||||||
* @param labelA The new label of the disk, or {@code nil} to clear.
|
* @param label The new label of the disk, or {@code nil} to clear.
|
||||||
* @throws LuaException If the disk's label can't be changed.
|
* @throws LuaException If the disk's label can't be changed.
|
||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final void setDiskLabel(Optional<String> labelA) throws LuaException {
|
public final void setDiskLabel(Optional<String> label) throws LuaException {
|
||||||
var label = labelA.orElse(null);
|
|
||||||
var stack = diskDrive.getDiskStack();
|
var stack = diskDrive.getDiskStack();
|
||||||
var media = MediaProviders.get(stack);
|
var media = MediaProviders.get(stack);
|
||||||
if (media == null) return;
|
if (media == null) return;
|
||||||
|
|
||||||
if (!media.setLabel(stack, StringUtil.normaliseLabel(label))) {
|
if (!media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) {
|
||||||
throw new LuaException("Disk label cannot be changed");
|
throw new LuaException("Disk label cannot be changed");
|
||||||
}
|
}
|
||||||
diskDrive.setDiskStack(stack);
|
diskDrive.setDiskStack(stack);
|
||||||
|
@ -135,7 +135,7 @@ public class PrinterPeripheral implements IPeripheral {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final void setPageTitle(Optional<String> title) throws LuaException {
|
public final void setPageTitle(Optional<String> title) throws LuaException {
|
||||||
getCurrentPage();
|
getCurrentPage();
|
||||||
printer.setPageTitle(StringUtil.normaliseLabel(title.orElse("")));
|
printer.setPageTitle(title.map(StringUtil::normaliseLabel).orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,13 +73,15 @@ object ManagedComputers : ILuaMachine.Factory {
|
|||||||
apis.add(api)
|
apis.add(api)
|
||||||
|
|
||||||
if (api is OSAPI) {
|
if (api is OSAPI) {
|
||||||
val newMachine = if (api.computerID != 1) {
|
val id = api.computerID
|
||||||
CobaltLuaMachine(environment)
|
val label = api.computerLabel
|
||||||
} else if (api.computerLabel != null) {
|
val newMachine = when {
|
||||||
KotlinMachine(environment, api.computerLabel[0] as String)
|
id != 1 -> CobaltLuaMachine(environment)
|
||||||
} else {
|
label != null && label[0] != null -> KotlinMachine(environment, label[0] as String)
|
||||||
LOGGER.error("Kotlin Lua machine must have a label")
|
else -> {
|
||||||
CobaltLuaMachine(environment)
|
LOGGER.error("Kotlin Lua machine must have a label")
|
||||||
|
CobaltLuaMachine(environment)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.delegate = newMachine
|
this.delegate = newMachine
|
||||||
@ -106,7 +108,8 @@ object ManagedComputers : ILuaMachine.Factory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KotlinMachine(environment: MachineEnvironment, private val label: String) : KotlinLuaMachine(environment) {
|
private class KotlinMachine(environment: MachineEnvironment, private val label: String) :
|
||||||
|
KotlinLuaMachine(environment) {
|
||||||
override fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? = computers[label]?.poll()
|
override fun getTask(): (suspend KotlinLuaMachine.() -> Unit)? = computers[label]?.poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user