From dc3d8ea198ac400175de65abbfab53bacdd5e9ae Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Mon, 29 Jul 2024 19:46:25 +0100 Subject: [PATCH] Move API factories to the common package We don't actually use this functionality in other projects (e.g. emulators). In fact the method to add new APIs only exists in the mod itself! We still need some mechanism to remove mounts when the computer is shutdown. We add a new ApiLifecycle interface (with startup and shutdown hooks), and use those in the ComputerSystem impl. --- .../api/lua/IComputerSystem.java | 2 ++ .../computercraft/api/lua/ILuaAPIFactory.java | 6 ++-- .../shared/computer/core}/ComputerSystem.java | 11 +++++-- .../shared/computer/core/ServerComputer.java | 8 +++++ .../shared/computer/core/ServerContext.java | 2 -- .../computercraft/core/ComputerContext.java | 32 ++----------------- .../core/apis/ComputerAccess.java | 2 +- .../core/computer/ApiLifecycle.java | 28 ++++++++++++++++ .../core/computer/ApiWrapper.java | 18 ++++------- .../computercraft/core/computer/Computer.java | 4 +++ .../core/computer/ComputerExecutor.java | 11 +++---- 11 files changed, 68 insertions(+), 56 deletions(-) rename projects/{core-api => common-api}/src/main/java/dan200/computercraft/api/lua/IComputerSystem.java (89%) rename projects/{core-api => common-api}/src/main/java/dan200/computercraft/api/lua/ILuaAPIFactory.java (76%) rename projects/{core/src/main/java/dan200/computercraft/core/computer => common/src/main/java/dan200/computercraft/shared/computer/core}/ComputerSystem.java (83%) create mode 100644 projects/core/src/main/java/dan200/computercraft/core/computer/ApiLifecycle.java 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. */