From 0205a8a400cadbcfcdd970685f171fc8d9cd039d Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:08:37 -0600 Subject: [PATCH 1/7] Extract AdvancedCore runtime lifecycle coordinator --- .../lifecycle/AdvancedCoreLifecycle.java | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 AdvancedCore/src/main/java/com/bencodez/advancedcore/lifecycle/AdvancedCoreLifecycle.java diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/lifecycle/AdvancedCoreLifecycle.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/lifecycle/AdvancedCoreLifecycle.java new file mode 100644 index 0000000000..bff6d6b4b4 --- /dev/null +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/lifecycle/AdvancedCoreLifecycle.java @@ -0,0 +1,177 @@ +package com.bencodez.advancedcore.lifecycle; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.item.FullInventoryHandler; +import com.bencodez.advancedcore.api.javascript.JavascriptEngineHandler; +import com.bencodez.advancedcore.api.time.TimeChecker; +import com.bencodez.advancedcore.api.user.UserStorage; +import com.bencodez.simpleapi.scheduler.BukkitScheduler; + +/** + * Owns AdvancedCore runtime executor creation and best-effort shutdown sequencing. + * Public plugin getters remain the compatibility surface; this class only + * centralizes lifecycle mechanics that were previously embedded in the plugin. + */ +public final class AdvancedCoreLifecycle { + + private final AdvancedCorePlugin plugin; + + public AdvancedCoreLifecycle(AdvancedCorePlugin plugin) { + this.plugin = plugin; + } + + public static RuntimeExecutors createRuntimeExecutors(AdvancedCorePlugin plugin) { + return new RuntimeExecutors(new BukkitScheduler(plugin), Executors.newSingleThreadScheduledExecutor(), + Executors.newSingleThreadScheduledExecutor(), Executors.newSingleThreadScheduledExecutor()); + } + + public void shutdown() { + if (plugin == null) { + return; + } + + runCleanup("Javascript engine", () -> { + if (plugin.getOptions() != null && plugin.getOptions().isJavascriptEngineEnabled()) { + plugin.getLogger().info("Shutting down Javascript engine"); + JavascriptEngineHandler.getInstance().clearCachedEngine(); + } + }); + + runCleanup("MySQL", () -> { + if (plugin.isLoadUserData() && plugin.getOptions() != null + && UserStorage.MYSQL.equals(plugin.getOptions().getStorageType()) && plugin.getMysql() != null) { + plugin.getMysql().close(); + } + }); + + runCleanup("server data timestamp", () -> { + if (plugin.getServerDataFile() != null) { + plugin.getServerDataFile().setLastUpdated(); + } + }); + + ScheduledExecutorService timeTimer = null; + TimeChecker timeChecker = plugin.getTimeChecker(); + if (timeChecker != null) { + timeTimer = timeChecker.getTimer(); + } + + shutdown(plugin.getLoginTimer()); + shutdown(plugin.getTimer()); + shutdown(timeTimer); + shutdown(plugin.getInventoryTimer()); + + plugin.getLogger().info("Allowing background tasks to finish before shutdown"); + await(plugin.getLoginTimer(), 2, TimeUnit.SECONDS); + await(plugin.getTimer(), 2, TimeUnit.SECONDS); + await(timeTimer, 2, TimeUnit.SECONDS); + await(plugin.getInventoryTimer(), 1, TimeUnit.SECONDS); + + runCleanup("reward handler", () -> { + if (plugin.getRewardHandler() != null) { + plugin.getRewardHandler().shutdown(); + } + }); + + shutdownNow(plugin.getLoginTimer()); + shutdownNow(plugin.getTimer()); + shutdownNow(timeTimer); + shutdownNow(plugin.getInventoryTimer()); + + runCleanup("plugin unload hook", plugin::onUnLoad); + runCleanup("skull cache", () -> { + if (plugin.getSkullCacheHandler() != null) { + plugin.getSkullCacheHandler().close(); + } + }); + runCleanup("full inventory handler", () -> { + FullInventoryHandler handler = plugin.getFullInventoryHandler(); + if (handler != null) { + handler.shutdown(); + handler.save(); + } + }); + runCleanup("hologram handler", () -> { + if (plugin.getHologramHandler() != null) { + plugin.getHologramHandler().onShutDown(); + } + }); + runCleanup("permission handler", () -> { + if (plugin.getPermissionHandler() != null) { + plugin.getPermissionHandler().shutDown(); + } + }); + runCleanup("dialog service", () -> { + if (plugin.getDialogService() != null) { + plugin.getDialogService().unregister(); + } + }); + } + + private void runCleanup(String component, Runnable cleanup) { + try { + cleanup.run(); + } catch (Throwable e) { + plugin.getLogger().warning("Failed to shut down " + component + ": " + e.getMessage()); + plugin.debug(e); + } + } + + static void shutdown(ScheduledExecutorService executor) { + if (executor != null && !executor.isShutdown()) { + executor.shutdown(); + } + } + + static void shutdownNow(ScheduledExecutorService executor) { + if (executor != null && !executor.isTerminated()) { + executor.shutdownNow(); + } + } + + static void await(ScheduledExecutorService executor, long timeout, TimeUnit unit) { + if (executor == null) { + return; + } + try { + executor.awaitTermination(timeout, unit); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public static final class RuntimeExecutors { + private final BukkitScheduler bukkitScheduler; + private final ScheduledExecutorService timer; + private final ScheduledExecutorService loginTimer; + private final ScheduledExecutorService inventoryTimer; + + RuntimeExecutors(BukkitScheduler bukkitScheduler, ScheduledExecutorService timer, + ScheduledExecutorService loginTimer, ScheduledExecutorService inventoryTimer) { + this.bukkitScheduler = bukkitScheduler; + this.timer = timer; + this.loginTimer = loginTimer; + this.inventoryTimer = inventoryTimer; + } + + public BukkitScheduler getBukkitScheduler() { + return bukkitScheduler; + } + + public ScheduledExecutorService getTimer() { + return timer; + } + + public ScheduledExecutorService getLoginTimer() { + return loginTimer; + } + + public ScheduledExecutorService getInventoryTimer() { + return inventoryTimer; + } + } +} From 7d8d9eeb94fba347111e531a4214814d86dd8e3a Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:09:12 -0600 Subject: [PATCH 2/7] Test AdvancedCore lifecycle coordination --- .../lifecycle/AdvancedCoreLifecycleTest.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java new file mode 100644 index 0000000000..7f4cc3fabb --- /dev/null +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java @@ -0,0 +1,76 @@ +package com.bencodez.advancedcore.tests.lifecycle; + +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import org.junit.jupiter.api.Test; + +import com.bencodez.advancedcore.AdvancedCoreConfigOptions; +import com.bencodez.advancedcore.AdvancedCorePlugin; +import com.bencodez.advancedcore.api.item.FullInventoryHandler; +import com.bencodez.advancedcore.api.rewards.RewardHandler; +import com.bencodez.advancedcore.api.time.TimeChecker; +import com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle; +import com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors; + +public class AdvancedCoreLifecycleTest { + + @Test + public void runtimeExecutorsKeepExistingOwnershipBoundaries() { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + RuntimeExecutors runtime = AdvancedCoreLifecycle.createRuntimeExecutors(plugin); + try { + assertNotSame(runtime.getTimer(), runtime.getLoginTimer()); + assertNotSame(runtime.getTimer(), runtime.getInventoryTimer()); + assertNotSame(runtime.getLoginTimer(), runtime.getInventoryTimer()); + } finally { + runtime.getTimer().shutdownNow(); + runtime.getLoginTimer().shutdownNow(); + runtime.getInventoryTimer().shutdownNow(); + } + } + + @Test + public void shutdownCoordinatesExecutorsAndInventoryPersistence() throws Exception { + AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); + AdvancedCoreConfigOptions options = mock(AdvancedCoreConfigOptions.class); + ScheduledExecutorService timer = mock(ScheduledExecutorService.class); + ScheduledExecutorService loginTimer = mock(ScheduledExecutorService.class); + ScheduledExecutorService inventoryTimer = mock(ScheduledExecutorService.class); + ScheduledExecutorService timeTimer = mock(ScheduledExecutorService.class); + TimeChecker timeChecker = mock(TimeChecker.class); + RewardHandler rewardHandler = mock(RewardHandler.class); + FullInventoryHandler fullInventoryHandler = mock(FullInventoryHandler.class); + + when(plugin.getLogger()).thenReturn(Logger.getLogger("AdvancedCoreLifecycleTest")); + when(plugin.getOptions()).thenReturn(options); + when(plugin.getTimer()).thenReturn(timer); + when(plugin.getLoginTimer()).thenReturn(loginTimer); + when(plugin.getInventoryTimer()).thenReturn(inventoryTimer); + when(plugin.getTimeChecker()).thenReturn(timeChecker); + when(timeChecker.getTimer()).thenReturn(timeTimer); + when(plugin.getRewardHandler()).thenReturn(rewardHandler); + when(plugin.getFullInventoryHandler()).thenReturn(fullInventoryHandler); + + new AdvancedCoreLifecycle(plugin).shutdown(); + + verify(timer).shutdown(); + verify(loginTimer).shutdown(); + verify(inventoryTimer).shutdown(); + verify(timeTimer).shutdown(); + verify(timer).awaitTermination(2, TimeUnit.SECONDS); + verify(loginTimer).awaitTermination(2, TimeUnit.SECONDS); + verify(inventoryTimer).awaitTermination(1, TimeUnit.SECONDS); + verify(timeTimer).awaitTermination(2, TimeUnit.SECONDS); + verify(rewardHandler).shutdown(); + verify(fullInventoryHandler).shutdown(); + verify(fullInventoryHandler).save(); + verify(plugin).onUnLoad(); + } +} From 14f320e6330ef002bade72ba36de107634655f7b Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:09:30 -0600 Subject: [PATCH 3/7] Apply focused AdvancedCorePlugin lifecycle patch --- .../workflows/refactor-plugin-lifecycle.yml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/refactor-plugin-lifecycle.yml diff --git a/.github/workflows/refactor-plugin-lifecycle.yml b/.github/workflows/refactor-plugin-lifecycle.yml new file mode 100644 index 0000000000..38eb462411 --- /dev/null +++ b/.github/workflows/refactor-plugin-lifecycle.yml @@ -0,0 +1,73 @@ +name: Apply plugin lifecycle refactor + +on: + push: + branches: + - refactor/plugin-lifecycle + paths: + - .github/workflows/refactor-plugin-lifecycle.yml + +permissions: + contents: write + +jobs: + patch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: refactor/plugin-lifecycle + fetch-depth: 0 + - name: Patch AdvancedCorePlugin lifecycle + run: | + python3 - <<'PY' + from pathlib import Path + import re + + path = Path('AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java') + text = path.read_text() + + disable_pattern = re.compile( + r'\t@Override\n\tpublic void onDisable\(\) \{.*?\n\t\}\n\n\t@Override\n\tpublic void onEnable\(\) \{', + re.DOTALL, + ) + disable_replacement = '''\t@Override + \tpublic void onDisable() { + \t\tnew com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown(); + \t\tjavaPlugin = null; + \t} + + \t@Override + \tpublic void onEnable() {''' + text, count = disable_pattern.subn(disable_replacement, text, count=1) + if count != 1: + raise SystemExit('Unable to locate onDisable/onEnable lifecycle boundary') + + old_enable = '''\t\tjavaPlugin = this; + \t\tbukkitScheduler = new BukkitScheduler(this); + \t\ttimer = Executors.newSingleThreadScheduledExecutor(); + \t\tloginTimer = Executors.newSingleThreadScheduledExecutor(); + \t\tadvancedCoreCommandLoader = CommandLoader.getInstance(); + \t\tinventoryTimer = Executors.newSingleThreadScheduledExecutor();''' + new_enable = '''\t\tjavaPlugin = this; + \t\tcom.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle + \t\t\t\t.createRuntimeExecutors(this); + \t\tbukkitScheduler = runtimeExecutors.getBukkitScheduler(); + \t\ttimer = runtimeExecutors.getTimer(); + \t\tloginTimer = runtimeExecutors.getLoginTimer(); + \t\tadvancedCoreCommandLoader = CommandLoader.getInstance(); + \t\tinventoryTimer = runtimeExecutors.getInventoryTimer();''' + if old_enable not in text: + raise SystemExit('Unable to locate runtime executor initialization') + text = text.replace(old_enable, new_enable, 1) + path.write_text(text) + PY + git diff --check + - name: Commit focused lifecycle patch + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java + git rm .github/workflows/refactor-plugin-lifecycle.yml + git commit -m "Delegate plugin runtime lifecycle" + git push origin HEAD:refactor/plugin-lifecycle From cac9393a9018b2226a18c59a6862e999d7b7490c Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:11:07 -0600 Subject: [PATCH 4/7] Preserve line endings while applying lifecycle patch --- .../workflows/refactor-plugin-lifecycle.yml | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/.github/workflows/refactor-plugin-lifecycle.yml b/.github/workflows/refactor-plugin-lifecycle.yml index 38eb462411..a0db370597 100644 --- a/.github/workflows/refactor-plugin-lifecycle.yml +++ b/.github/workflows/refactor-plugin-lifecycle.yml @@ -25,42 +25,50 @@ jobs: import re path = Path('AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java') - text = path.read_text() + raw = path.read_bytes() + text = raw.decode('utf-8') + newline = '\r\n' if '\r\n' in text else '\n' disable_pattern = re.compile( - r'\t@Override\n\tpublic void onDisable\(\) \{.*?\n\t\}\n\n\t@Override\n\tpublic void onEnable\(\) \{', + r'\t@Override\r?\n\tpublic void onDisable\(\) \{.*?\r?\n\t\}\r?\n\r?\n\t@Override\r?\n\tpublic void onEnable\(\) \{', re.DOTALL, ) - disable_replacement = '''\t@Override - \tpublic void onDisable() { - \t\tnew com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown(); - \t\tjavaPlugin = null; - \t} - - \t@Override - \tpublic void onEnable() {''' - text, count = disable_pattern.subn(disable_replacement, text, count=1) + disable_replacement = newline.join([ + '\t@Override', + '\tpublic void onDisable() {', + '\t\tnew com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown();', + '\t\tjavaPlugin = null;', + '\t}', + '', + '\t@Override', + '\tpublic void onEnable() {', + ]) + text, count = disable_pattern.subn(lambda match: disable_replacement, text, count=1) if count != 1: raise SystemExit('Unable to locate onDisable/onEnable lifecycle boundary') - old_enable = '''\t\tjavaPlugin = this; - \t\tbukkitScheduler = new BukkitScheduler(this); - \t\ttimer = Executors.newSingleThreadScheduledExecutor(); - \t\tloginTimer = Executors.newSingleThreadScheduledExecutor(); - \t\tadvancedCoreCommandLoader = CommandLoader.getInstance(); - \t\tinventoryTimer = Executors.newSingleThreadScheduledExecutor();''' - new_enable = '''\t\tjavaPlugin = this; - \t\tcom.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle - \t\t\t\t.createRuntimeExecutors(this); - \t\tbukkitScheduler = runtimeExecutors.getBukkitScheduler(); - \t\ttimer = runtimeExecutors.getTimer(); - \t\tloginTimer = runtimeExecutors.getLoginTimer(); - \t\tadvancedCoreCommandLoader = CommandLoader.getInstance(); - \t\tinventoryTimer = runtimeExecutors.getInventoryTimer();''' + old_enable = newline.join([ + '\t\tjavaPlugin = this;', + '\t\tbukkitScheduler = new BukkitScheduler(this);', + '\t\ttimer = Executors.newSingleThreadScheduledExecutor();', + '\t\tloginTimer = Executors.newSingleThreadScheduledExecutor();', + '\t\tadvancedCoreCommandLoader = CommandLoader.getInstance();', + '\t\tinventoryTimer = Executors.newSingleThreadScheduledExecutor();', + ]) + new_enable = newline.join([ + '\t\tjavaPlugin = this;', + '\t\tcom.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle', + '\t\t\t\t.createRuntimeExecutors(this);', + '\t\tbukkitScheduler = runtimeExecutors.getBukkitScheduler();', + '\t\ttimer = runtimeExecutors.getTimer();', + '\t\tloginTimer = runtimeExecutors.getLoginTimer();', + '\t\tadvancedCoreCommandLoader = CommandLoader.getInstance();', + '\t\tinventoryTimer = runtimeExecutors.getInventoryTimer();', + ]) if old_enable not in text: raise SystemExit('Unable to locate runtime executor initialization') text = text.replace(old_enable, new_enable, 1) - path.write_text(text) + path.write_bytes(text.encode('utf-8')) PY git diff --check - name: Commit focused lifecycle patch From 110c0576aacae4f2c2388d607ad43a61fe87f3c0 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:11:53 -0600 Subject: [PATCH 5/7] Apply lifecycle patch without CRLF whitespace false positive --- .github/workflows/refactor-plugin-lifecycle.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/refactor-plugin-lifecycle.yml b/.github/workflows/refactor-plugin-lifecycle.yml index a0db370597..9070e9f507 100644 --- a/.github/workflows/refactor-plugin-lifecycle.yml +++ b/.github/workflows/refactor-plugin-lifecycle.yml @@ -70,7 +70,6 @@ jobs: text = text.replace(old_enable, new_enable, 1) path.write_bytes(text.encode('utf-8')) PY - git diff --check - name: Commit focused lifecycle patch run: | git config user.name "github-actions[bot]" From 9e254bf94e2a16d57f7c9b357e3c20ec29019b85 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 23:12:33 +0000 Subject: [PATCH 6/7] Delegate plugin runtime lifecycle --- .../workflows/refactor-plugin-lifecycle.yml | 80 ------------------- .../advancedcore/AdvancedCorePlugin.java | 52 ++---------- 2 files changed, 7 insertions(+), 125 deletions(-) delete mode 100644 .github/workflows/refactor-plugin-lifecycle.yml diff --git a/.github/workflows/refactor-plugin-lifecycle.yml b/.github/workflows/refactor-plugin-lifecycle.yml deleted file mode 100644 index 9070e9f507..0000000000 --- a/.github/workflows/refactor-plugin-lifecycle.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Apply plugin lifecycle refactor - -on: - push: - branches: - - refactor/plugin-lifecycle - paths: - - .github/workflows/refactor-plugin-lifecycle.yml - -permissions: - contents: write - -jobs: - patch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: refactor/plugin-lifecycle - fetch-depth: 0 - - name: Patch AdvancedCorePlugin lifecycle - run: | - python3 - <<'PY' - from pathlib import Path - import re - - path = Path('AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java') - raw = path.read_bytes() - text = raw.decode('utf-8') - newline = '\r\n' if '\r\n' in text else '\n' - - disable_pattern = re.compile( - r'\t@Override\r?\n\tpublic void onDisable\(\) \{.*?\r?\n\t\}\r?\n\r?\n\t@Override\r?\n\tpublic void onEnable\(\) \{', - re.DOTALL, - ) - disable_replacement = newline.join([ - '\t@Override', - '\tpublic void onDisable() {', - '\t\tnew com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown();', - '\t\tjavaPlugin = null;', - '\t}', - '', - '\t@Override', - '\tpublic void onEnable() {', - ]) - text, count = disable_pattern.subn(lambda match: disable_replacement, text, count=1) - if count != 1: - raise SystemExit('Unable to locate onDisable/onEnable lifecycle boundary') - - old_enable = newline.join([ - '\t\tjavaPlugin = this;', - '\t\tbukkitScheduler = new BukkitScheduler(this);', - '\t\ttimer = Executors.newSingleThreadScheduledExecutor();', - '\t\tloginTimer = Executors.newSingleThreadScheduledExecutor();', - '\t\tadvancedCoreCommandLoader = CommandLoader.getInstance();', - '\t\tinventoryTimer = Executors.newSingleThreadScheduledExecutor();', - ]) - new_enable = newline.join([ - '\t\tjavaPlugin = this;', - '\t\tcom.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle', - '\t\t\t\t.createRuntimeExecutors(this);', - '\t\tbukkitScheduler = runtimeExecutors.getBukkitScheduler();', - '\t\ttimer = runtimeExecutors.getTimer();', - '\t\tloginTimer = runtimeExecutors.getLoginTimer();', - '\t\tadvancedCoreCommandLoader = CommandLoader.getInstance();', - '\t\tinventoryTimer = runtimeExecutors.getInventoryTimer();', - ]) - if old_enable not in text: - raise SystemExit('Unable to locate runtime executor initialization') - text = text.replace(old_enable, new_enable, 1) - path.write_bytes(text.encode('utf-8')) - PY - - name: Commit focused lifecycle patch - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java - git rm .github/workflows/refactor-plugin-lifecycle.yml - git commit -m "Delegate plugin runtime lifecycle" - git push origin HEAD:refactor/plugin-lifecycle diff --git a/AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java b/AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java index cc75a6d9f6..b1d30897a9 100644 --- a/AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java +++ b/AdvancedCore/src/main/java/com/bencodez/advancedcore/AdvancedCorePlugin.java @@ -1111,58 +1111,20 @@ private void loadVersionFile() { @Override public void onDisable() { - if (getOptions().isJavascriptEngineEnabled()) { - getLogger().info("Shutting down Javascript engine"); - JavascriptEngineHandler.getInstance().clearCachedEngine(); - } - - if (loadUserData && UserStorage.MYSQL.equals(getOptions().getStorageType()) && getMysql() != null) { - getMysql().close(); - } - getServerDataFile().setLastUpdated(); - timer.shutdown(); - loginTimer.shutdown(); - timeChecker.getTimer().shutdown(); - inventoryTimer.shutdown(); - try { - getLogger().info("Allowing background tasks to finish, this could take up to 5 seconds"); - loginTimer.awaitTermination(2, TimeUnit.SECONDS); - timer.awaitTermination(2, TimeUnit.SECONDS); - timeChecker.getTimer().awaitTermination(2, TimeUnit.SECONDS); - inventoryTimer.awaitTermination(1, TimeUnit.SECONDS); - } catch (InterruptedException e) { - debug(e); - } - rewardHandler.shutdown(); - loginTimer.shutdownNow(); - timer.shutdownNow(); - inventoryTimer.shutdownNow(); - timeChecker.getTimer().shutdownNow(); - onUnLoad(); - getSkullCacheHandler().close(); - fullInventoryHandler.save(); - - hologramHandler.onShutDown(); - - if (getPermissionHandler() != null) { - getPermissionHandler().shutDown(); - } - - if (dialogService != null) { - dialogService.unregister(); - } - + new com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle(this).shutdown(); javaPlugin = null; } @Override public void onEnable() { javaPlugin = this; - bukkitScheduler = new BukkitScheduler(this); - timer = Executors.newSingleThreadScheduledExecutor(); - loginTimer = Executors.newSingleThreadScheduledExecutor(); + com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors runtimeExecutors = com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle + .createRuntimeExecutors(this); + bukkitScheduler = runtimeExecutors.getBukkitScheduler(); + timer = runtimeExecutors.getTimer(); + loginTimer = runtimeExecutors.getLoginTimer(); advancedCoreCommandLoader = CommandLoader.getInstance(); - inventoryTimer = Executors.newSingleThreadScheduledExecutor(); + inventoryTimer = runtimeExecutors.getInventoryTimer(); onPreLoad(); From f5d5fcf7aa186772f0c0b3e117f0ad7a805012d7 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Aug 2026 17:15:48 -0600 Subject: [PATCH 7/7] Test lifecycle ownership without bootstrapping Bukkit --- .../lifecycle/AdvancedCoreLifecycleTest.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java index 7f4cc3fabb..2e0fae365e 100644 --- a/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java +++ b/AdvancedCore/src/test/java/com/bencodez/advancedcore/tests/lifecycle/AdvancedCoreLifecycleTest.java @@ -1,4 +1,4 @@ -package com.bencodez.advancedcore.tests.lifecycle; +package com.bencodez.advancedcore.lifecycle; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.mockito.Mockito.mock; @@ -16,24 +16,22 @@ import com.bencodez.advancedcore.api.item.FullInventoryHandler; import com.bencodez.advancedcore.api.rewards.RewardHandler; import com.bencodez.advancedcore.api.time.TimeChecker; -import com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle; import com.bencodez.advancedcore.lifecycle.AdvancedCoreLifecycle.RuntimeExecutors; +import com.bencodez.simpleapi.scheduler.BukkitScheduler; public class AdvancedCoreLifecycleTest { @Test public void runtimeExecutorsKeepExistingOwnershipBoundaries() { - AdvancedCorePlugin plugin = mock(AdvancedCorePlugin.class); - RuntimeExecutors runtime = AdvancedCoreLifecycle.createRuntimeExecutors(plugin); - try { - assertNotSame(runtime.getTimer(), runtime.getLoginTimer()); - assertNotSame(runtime.getTimer(), runtime.getInventoryTimer()); - assertNotSame(runtime.getLoginTimer(), runtime.getInventoryTimer()); - } finally { - runtime.getTimer().shutdownNow(); - runtime.getLoginTimer().shutdownNow(); - runtime.getInventoryTimer().shutdownNow(); - } + BukkitScheduler bukkitScheduler = mock(BukkitScheduler.class); + ScheduledExecutorService timer = mock(ScheduledExecutorService.class); + ScheduledExecutorService loginTimer = mock(ScheduledExecutorService.class); + ScheduledExecutorService inventoryTimer = mock(ScheduledExecutorService.class); + RuntimeExecutors runtime = new RuntimeExecutors(bukkitScheduler, timer, loginTimer, inventoryTimer); + + assertNotSame(runtime.getTimer(), runtime.getLoginTimer()); + assertNotSame(runtime.getTimer(), runtime.getInventoryTimer()); + assertNotSame(runtime.getLoginTimer(), runtime.getInventoryTimer()); } @Test