diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java b/projects/common-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java similarity index 89% rename from projects/core-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java rename to projects/common-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java index 2a0c4ab8e..f851d45cd 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java @@ -5,6 +5,7 @@ package dan200.computercraft.api.lua; import dan200.computercraft.api.peripheral.IComputerAccess; +import org.jetbrains.annotations.ApiStatus; import javax.annotation.Nullable; @@ -12,6 +13,7 @@ import javax.annotation.Nullable; * An interface passed to {@link ILuaAPIFactory} in order to provide additional information * about a computer. */ +@ApiStatus.NonExtendable public interface IComputerSystem extends IComputerAccess { /** * Get the label for this computer. diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java b/projects/common-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java similarity index 76% rename from projects/core-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java rename to projects/common-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java index 75167739b..cb0f35500 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java @@ -4,13 +4,15 @@ package dan200.computercraft.api.lua; +import dan200.computercraft.api.ComputerCraftAPI; + import javax.annotation.Nullable; /** - * Construct an {@link ILuaAPI} for a specific computer. + * Construct an {@link ILuaAPI} for a computer. * * @see ILuaAPI - * @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory) + * @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory) */ @FunctionalInterface public interface ILuaAPIFactory { diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerSystem.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java similarity index 83% rename from projects/core/src/main/java/dan200/computercraft/core/computer/ComputerSystem.java rename to projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java index 12bb8cc8c..737be3c0d 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerSystem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ComputerSystem.java @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MPL-2.0 -package dan200.computercraft.core.computer; +package dan200.computercraft.shared.computer.core; import dan200.computercraft.api.lua.IComputerSystem; import dan200.computercraft.api.lua.ILuaAPIFactory; @@ -10,6 +10,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.apis.ComputerAccess; import dan200.computercraft.core.apis.IAPIEnvironment; +import dan200.computercraft.core.computer.ApiLifecycle; import javax.annotation.Nullable; import java.util.Map; @@ -18,9 +19,8 @@ import java.util.Map; * Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs. * * @see ILuaAPIFactory - * @see ApiWrapper */ -public class ComputerSystem extends ComputerAccess implements IComputerSystem { +class ComputerSystem extends ComputerAccess implements IComputerSystem, ApiLifecycle { private final IAPIEnvironment environment; ComputerSystem(IAPIEnvironment environment) { @@ -28,6 +28,11 @@ public class ComputerSystem extends ComputerAccess implements IComputerSystem { this.environment = environment; } + @Override + public void shutdown() { + unmountAll(); + } + @Override public String getAttachmentName() { return "computer"; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index 60d350c7e..22da0cb5a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -13,6 +13,7 @@ import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ComputerEnvironment; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.metrics.MetricsObserver; +import dan200.computercraft.impl.ApiFactories; import dan200.computercraft.shared.computer.apis.CommandAPI; import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; @@ -63,6 +64,13 @@ public class ServerComputer implements InputHandler, ComputerEnvironment { computer = new Computer(context.computerContext(), this, terminal, computerID); computer.setLabel(label); + // Load in the externally registered APIs. + for (var factory : ApiFactories.getAll()) { + var system = new ComputerSystem(computer.getAPIEnvironment()); + var api = factory.create(system); + if (api != null) computer.addApi(api, system); + } + if (family == ComputerFamily.COMMAND) addAPI(new CommandAPI(this)); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java index 64480353b..3131c667a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/core/ServerContext.java @@ -17,7 +17,6 @@ import dan200.computercraft.core.lua.ILuaMachine; import dan200.computercraft.core.methods.MethodSupplier; import dan200.computercraft.core.methods.PeripheralMethod; import dan200.computercraft.impl.AbstractComputerCraftAPI; -import dan200.computercraft.impl.ApiFactories; import dan200.computercraft.impl.GenericSources; import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.computer.metrics.GlobalMetrics; @@ -74,7 +73,6 @@ public final class ServerContext { .computerThreads(ConfigSpec.computerThreads.get()) .mainThreadScheduler(mainThread) .luaFactory(luaMachine) - .apiFactories(ApiFactories.getAll()) .genericMethods(GenericSources.getAllMethods()) .build(); idAssigner = new IDAssigner(storageDir.resolve("ids.json")); diff --git a/projects/core/src/main/java/dan200/computercraft/core/ComputerContext.java b/projects/core/src/main/java/dan200/computercraft/core/ComputerContext.java index d251e2a7e..d77b21c95 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/ComputerContext.java +++ b/projects/core/src/main/java/dan200/computercraft/core/ComputerContext.java @@ -4,7 +4,6 @@ package dan200.computercraft.core; -import dan200.computercraft.api.lua.ILuaAPIFactory; import dan200.computercraft.core.asm.GenericMethod; import dan200.computercraft.core.asm.LuaMethodSupplier; import dan200.computercraft.core.asm.PeripheralMethodSupplier; @@ -35,21 +34,19 @@ public final class ComputerContext { private final ComputerScheduler computerScheduler; private final MainThreadScheduler mainThreadScheduler; private final ILuaMachine.Factory luaFactory; - private final List apiFactories; private final MethodSupplier luaMethods; private final MethodSupplier peripheralMethods; - ComputerContext( + private ComputerContext( GlobalEnvironment globalEnvironment, ComputerScheduler computerScheduler, MainThreadScheduler mainThreadScheduler, ILuaMachine.Factory luaFactory, - List apiFactories, MethodSupplier luaMethods, + MethodSupplier luaMethods, MethodSupplier peripheralMethods ) { this.globalEnvironment = globalEnvironment; this.computerScheduler = computerScheduler; this.mainThreadScheduler = mainThreadScheduler; this.luaFactory = luaFactory; - this.apiFactories = apiFactories; this.luaMethods = luaMethods; this.peripheralMethods = peripheralMethods; } @@ -91,15 +88,6 @@ public final class ComputerContext { return luaFactory; } - /** - * Additional APIs to inject into each computer. - * - * @return All available API factories. - */ - public List apiFactories() { - return apiFactories; - } - /** * Get the {@link MethodSupplier} used to find methods on Lua values. * @@ -166,7 +154,6 @@ public final class ComputerContext { private @Nullable ComputerScheduler computerScheduler = null; private @Nullable MainThreadScheduler mainThreadScheduler; private @Nullable ILuaMachine.Factory luaFactory; - private @Nullable List apiFactories; private @Nullable List genericMethods; Builder(GlobalEnvironment environment) { @@ -227,20 +214,6 @@ public final class ComputerContext { return this; } - /** - * Set the additional {@linkplain ILuaAPIFactory APIs} to add to each computer. - * - * @param apis A list of API factories. - * @return {@code this}, for chaining - * @see ComputerContext#apiFactories() - */ - public Builder apiFactories(Collection apis) { - Objects.requireNonNull(apis); - if (apiFactories != null) throw new IllegalStateException("Main-thread scheduler already specified"); - apiFactories = List.copyOf(apis); - return this; - } - /** * Set the set of {@link GenericMethod}s used by the {@linkplain MethodSupplier method suppliers}. * @@ -267,7 +240,6 @@ public final class ComputerContext { computerScheduler == null ? new ComputerThread(1) : computerScheduler, mainThreadScheduler == null ? new NoWorkMainThreadScheduler() : mainThreadScheduler, luaFactory == null ? CobaltLuaMachine::new : luaFactory, - apiFactories == null ? List.of() : apiFactories, LuaMethodSupplier.create(genericMethods == null ? List.of() : genericMethods), PeripheralMethodSupplier.create(genericMethods == null ? List.of() : genericMethods) ); diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java b/projects/core/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java index 1844db6c6..8184f3639 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java @@ -21,7 +21,7 @@ public abstract class ComputerAccess implements IComputerAccess { private static final Logger LOG = LoggerFactory.getLogger(ComputerAccess.class); private final IAPIEnvironment environment; - private final Set mounts = new HashSet<>(); + private final Set mounts = new HashSet<>(0); protected ComputerAccess(IAPIEnvironment environment) { this.environment = environment; diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/ApiLifecycle.java b/projects/core/src/main/java/dan200/computercraft/core/computer/ApiLifecycle.java new file mode 100644 index 000000000..e3920bd59 --- /dev/null +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/ApiLifecycle.java @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.computer; + +import dan200.computercraft.api.lua.ILuaAPI; + +/** + * Hooks for managing the lifecycle of an API. This allows adding additional logic to an API's {@link ILuaAPI#startup()} + * and {@link ILuaAPI#shutdown()} methods. + * + * @see ILuaAPI + * @see Computer#addApi(ILuaAPI, ApiLifecycle) + */ +public interface ApiLifecycle { + /** + * Called before the API's {@link ILuaAPI#startup()} method, may be used to set up resources. + */ + default void startup() { + } + + /** + * Called after the API's {@link ILuaAPI#shutdown()} method, may be used to tear down resources. + */ + default void shutdown() { + } +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/ApiWrapper.java b/projects/core/src/main/java/dan200/computercraft/core/computer/ApiWrapper.java index 535cb6b8d..94394724a 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/ApiWrapper.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/ApiWrapper.java @@ -9,18 +9,14 @@ import dan200.computercraft.api.lua.ILuaAPI; import javax.annotation.Nullable; /** - * A wrapper for {@link ILuaAPI}s which optionally manages the lifecycle of a {@link ComputerSystem}. + * A wrapper for {@link ILuaAPI}s which provides an optional shutdown hook to clean up resources. + * + * @param api The original API. + * @param lifecycle The optional lifecycle hooks for this API. */ -final class ApiWrapper { - private final ILuaAPI api; - private final @Nullable ComputerSystem system; - - ApiWrapper(ILuaAPI api, @Nullable ComputerSystem system) { - this.api = api; - this.system = system; - } - +record ApiWrapper(ILuaAPI api, @Nullable ApiLifecycle lifecycle) { public void startup() { + if (lifecycle != null) lifecycle.startup(); api.startup(); } @@ -30,7 +26,7 @@ final class ApiWrapper { public void shutdown() { api.shutdown(); - if (system != null) system.unmountAll(); + if (lifecycle != null) lifecycle.shutdown(); } public ILuaAPI api() { diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java b/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java index b2d4f1e13..9b853bab6 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/Computer.java @@ -184,6 +184,10 @@ public class Computer { executor.addApi(api); } + public void addApi(ILuaAPI api, ApiLifecycle lifecycleHooks) { + executor.addApi(api, lifecycleHooks); + } + long getUniqueTaskId() { return lastTaskId.incrementAndGet(); } diff --git a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java b/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java index 3bb1c551e..4abed1117 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java +++ b/projects/core/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java @@ -162,13 +162,6 @@ final class ComputerExecutor implements ComputerScheduler.Worker { addApi(new PeripheralAPI(environment, context.peripheralMethods())); addApi(new OSAPI(environment)); if (CoreConfig.httpEnabled) addApi(new HTTPAPI(environment)); - - // Load in the externally registered APIs. - for (var factory : context.apiFactories()) { - var system = new ComputerSystem(environment); - var api = factory.create(system); - if (api != null) apis.add(new ApiWrapper(api, system)); - } } @Override @@ -190,6 +183,10 @@ final class ComputerExecutor implements ComputerScheduler.Worker { apis.add(new ApiWrapper(api, null)); } + void addApi(ILuaAPI api, ApiLifecycle lifecycleHooks) { + apis.add(new ApiWrapper(api, lifecycleHooks)); + } + /** * Schedule this computer to be started if not already on. */