From 5067f63f88f5b02985f830049d4a82e85a3c2e9d Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Wed, 11 Jun 2025 16:43:55 +0100 Subject: [PATCH 1/7] Add NeoForge support --- .gitignore | 1 + neoforge/build.gradle | 186 ++++++++++++++++++ neoforge/gradle.properties | 44 +++++ .../neoforge/CSSMinecraftLoader.java | 35 ++++ .../neoforge/NeoForgeCSSMinecraftPlugin.java | 75 +++++++ .../adapter/ForgeServerChatAdapter.java | 42 ++++ .../neoforge/command/ForgeCommandService.java | 68 +++++++ .../executor/ForgeServerExecutor.java | 21 ++ .../neoforge/listener/ForgeEventAdapter.java | 83 ++++++++ .../neoforge/logger/ForgeLogger.java | 29 +++ .../templates/META-INF/neoforge.mods.toml | 69 +++++++ settings.gradle | 8 +- 12 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 neoforge/build.gradle create mode 100644 neoforge/gradle.properties create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/NeoForgeCSSMinecraftPlugin.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/adapter/ForgeServerChatAdapter.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/command/ForgeCommandService.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/executor/ForgeServerExecutor.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java create mode 100644 neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/logger/ForgeLogger.java create mode 100644 neoforge/src/main/templates/META-INF/neoforge.mods.toml diff --git a/.gitignore b/.gitignore index 4bb6260..d67acaa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ .idea .gradle **/build/ +**/run !src/**/build/ gradle-app.setting !gradle-wrapper.jar diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 0000000..34ab5ff --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,186 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'idea' + id 'net.neoforged.moddev' version '2.0.92' +} + +version = mod_version +group = mod_group_id + +repositories { + mavenLocal() +} + +base { + archivesName = mod_id +} + +java.toolchain.languageVersion = JavaLanguageVersion.of(21) + +neoForge { + // Specify the version of NeoForge to use. + version = project.neo_version + + parchment { + mappingsVersion = project.parchment_mappings_version + minecraftVersion = project.parchment_minecraft_version + } + + // This line is optional. Access Transformers are automatically detected + // accessTransformers.add('src/main/resources/META-INF/accesstransformer.cfg') + + // Default run configurations. + // These can be tweaked, removed, or duplicated as needed. + runs { + client { + client() + + // Comma-separated list of namespaces to load gametests from. Empty = all namespaces. + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + server { + server() + programArgument '--nogui' + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + // This run config launches GameTestServer and runs all registered gametests, then exits. + // By default, the server will crash when no gametests are provided. + // The gametest system is also enabled by default for other run configs under the /test command. + gameTestServer { + type = "gameTestServer" + systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id + } + + data { + data() + + // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it + // gameDirectory = project.file('run-data') + + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + } + + // applies to all the run configs above + configureEach { + // Recommended logging data for a userdev environment + // The markers can be added/remove as needed separated by commas. + // "SCAN": For mods scan. + // "REGISTRIES": For firing of registry events. + // "REGISTRYDUMP": For getting the contents of all registries. + systemProperty 'forge.logging.markers', 'REGISTRIES' + + // Recommended logging level for the console + // You can set various levels here. + // Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels + logLevel = org.slf4j.event.Level.DEBUG + } + } + + mods { + // define mod <-> source bindings + // these are used to tell the game which sources are for which mod + // mostly optional in a single mod project + // but multi mod projects should define one per mod + "${mod_id}" { + sourceSet(sourceSets.main) + } + } +} + +// Include resources generated by data generators. +sourceSets.main.resources { srcDir 'src/generated/resources' } + + +dependencies { + // Example mod dependency with JEI + // The JEI API is declared for compile time use, while the full JEI artifact is used at runtime + // compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}" + // compileOnly "mezz.jei:jei-${mc_version}-forge-api:${jei_version}" + // runtimeOnly "mezz.jei:jei-${mc_version}-forge:${jei_version}" + + // Example mod dependency using a mod jar from ./libs with a flat dir repository + // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar + // The group id is ignored when searching -- in this case, it is "blank" + // implementation "blank:coolmod-${mc_version}:${coolmod_version}" + + // Example mod dependency using a file as dependency + // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") + + // Example project dependency using a sister or child project: + // implementation project(":myproject") + + // For more info: + // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html + // http://www.gradle.org/docs/current/userguide/dependency_management.html + + // implementation "net.neoforged:neoforge:${neo_version}" + + implementation ("net.kyori:adventure-api:4.17.0") { + exclude(module: "adventure-bom") + exclude(module: "annotations") + } + implementation ("net.kyori:adventure-text-serializer-gson:4.17.0") { + exclude(module: "adventure-bom") + exclude(module: "adventure-api") + exclude(module: "annotations") + exclude(module: "auto-service-annotations") + exclude(module: "gson") + } + + implementation project(path: ":common", configuration: "shadow") +} + +// This block of code expands all declared replace properties in the specified resource targets. +// A missing property will result in an error. Properties are expanded using ${} Groovy notation. +var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) { + var replaceProperties = [ + minecraft_version : minecraft_version, + minecraft_version_range: minecraft_version_range, + neo_version : neo_version, + neo_version_range : neo_version_range, + loader_version_range : loader_version_range, + mod_id : mod_id, + mod_name : mod_name, + mod_license : mod_license, + mod_version : mod_version, + mod_authors : mod_authors, + mod_description : mod_description + ] + inputs.properties replaceProperties + expand replaceProperties + from "src/main/templates" + into "build/generated/sources/modMetadata" +} + +// Include the output of "generateModMetadata" as an input directory for the build +// this works with both building through Gradle and the IDE. +sourceSets.main.resources.srcDir generateModMetadata +// To avoid having to run "generateModMetadata" manually, make it run on every project reload +neoForge.ideSyncTask generateModMetadata + +// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. +idea { + module { + downloadSources = true + downloadJavadoc = true + } +} + +shadowJar { + dependencies { + include(project(":common")) + include(dependency("net.kyori:.*")) + exclude(dependency("net.neoforged:.*")) + exclude(dependency("io.github.llamalad7:mixinextras-neoforge:.*")) + } + + relocate "net.kyori", "com.cssbham.cssminecraft.lib.adventure" + + archiveFileName = "cssminecraft-neoforge-${project.version}.jar" + + minimize() +} diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 0000000..d13e764 --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1,44 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +org.gradle.jvmargs=-Xmx2G +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true + +## Environment Properties +# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge +# The Minecraft version must agree with the Neo version to get a valid artifact +minecraft_version=1.21.1 +# The Minecraft version range can use any release version of Minecraft as bounds. +# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly +# as they do not follow standard versioning conventions. +minecraft_version_range=[1.21.1,1.22) +# The Neo version must agree with the Minecraft version to get a valid artifact +neo_version=21.1.176 +# The Neo version range can use any version of Neo as bounds +neo_version_range=[21,) +# The loader version range can only use the major version of FML as bounds +loader_version_range=[4,) + +parchment_minecraft_version=1.21.4 +parchment_mappings_version=2025.03.23 + +## Mod Properties + +# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} +# Must match the String constant located in the main mod class annotated with @Mod. +mod_id=cssminecraft +# The human-readable display name for the mod. +mod_name=CSS-Minecraft +# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. +mod_license=todo +# The mod version. See https://semver.org/ +mod_version=1.0-SNAPSHOT +# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. +# This should match the base package used for the mod sources. +# See https://maven.apache.org/guides/mini/guide-naming-conventions.html +mod_group_id=com.cssbham.cssminecraft.neoforge +# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. +mod_authors= +# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. +mod_description= diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java new file mode 100644 index 0000000..7dc6b57 --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java @@ -0,0 +1,35 @@ +package com.cssbham.cssminecraft.neoforge; + +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; + +/** + * Entrypoint for Forge + */ +@Mod(value = "cssminecraft") +public class CSSMinecraftLoader { + + private final NeoForgeCSSMinecraftPlugin plugin; + + public CSSMinecraftLoader() { + this.plugin = new NeoForgeCSSMinecraftPlugin(); + NeoForge.EVENT_BUS.addListener(this::onServerStarted); + } + + public void onServerStarted(ServerStartingEvent event) { + this.plugin.setServer(event.getServer()); + try { + this.plugin.enable(); + } catch (Exception e) { + this.plugin.getLogger().severe("Mod initialisation failed - disabling"); + this.plugin.disable(); + } + } + + public void onServerStopping(ServerStoppingEvent event) { + this.plugin.disable(); + } + +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/NeoForgeCSSMinecraftPlugin.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/NeoForgeCSSMinecraftPlugin.java new file mode 100644 index 0000000..5bf0f5c --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/NeoForgeCSSMinecraftPlugin.java @@ -0,0 +1,75 @@ +package com.cssbham.cssminecraft.neoforge; + +import com.cssbham.cssminecraft.common.AbstractCSSMinecraftPlugin; +import com.cssbham.cssminecraft.common.adapter.ServerChatAdapter; +import com.cssbham.cssminecraft.common.command.CommandService; +import com.cssbham.cssminecraft.common.executor.ServerExecutor; +import com.cssbham.cssminecraft.common.logger.Logger; +import com.cssbham.cssminecraft.neoforge.adapter.ForgeServerChatAdapter; +import com.cssbham.cssminecraft.neoforge.command.ForgeCommandService; +import com.cssbham.cssminecraft.neoforge.executor.ForgeServerExecutor; +import com.cssbham.cssminecraft.neoforge.listener.ForgeEventAdapter; +import com.cssbham.cssminecraft.neoforge.logger.ForgeLogger; +import net.minecraft.server.MinecraftServer; +import net.neoforged.fml.loading.FMLPaths; + +import java.nio.file.Path; + +/** + * Implementation of CSS Minecraft Plugin for Forge + */ +public class NeoForgeCSSMinecraftPlugin extends AbstractCSSMinecraftPlugin { + + public static final String MOD_ID = "cssminecraft"; + private final ForgeLogger logger; + private ForgeServerChatAdapter serverChatAdapter; + + private MinecraftServer server; + private ForgeServerExecutor executor; + private ForgeCommandService commandService; + + public NeoForgeCSSMinecraftPlugin() { + this.logger = new ForgeLogger(MOD_ID); + } + + @Override + public void enable() { + this.serverChatAdapter = new ForgeServerChatAdapter(server); + this.executor = new ForgeServerExecutor(logger, server); + this.commandService = new ForgeCommandService(logger, executor, serverChatAdapter, server); + + super.enable(); + + ForgeEventAdapter eventAdapter = new ForgeEventAdapter(server, executor); + eventAdapter.bindPlatformToEventBus(super.getEventBus()); + } + + @Override + public Logger getLogger() { + return logger; + } + + @Override + public ServerChatAdapter provideServerChatAdapter() { + return serverChatAdapter; + } + + @Override + public Path provideConfigurationPath() { + return FMLPaths.CONFIGDIR.get().resolve(MOD_ID).resolve("config.yml"); + } + + @Override + public ServerExecutor provideServerExecutor() { + return executor; + } + + @Override + public CommandService provideCommandService() { + return commandService; + } + + public void setServer(MinecraftServer server) { + this.server = server; + } +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/adapter/ForgeServerChatAdapter.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/adapter/ForgeServerChatAdapter.java new file mode 100644 index 0000000..0eb730a --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/adapter/ForgeServerChatAdapter.java @@ -0,0 +1,42 @@ +package com.cssbham.cssminecraft.neoforge.adapter; + +import com.cssbham.cssminecraft.common.adapter.ServerChatAdapter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.minecraft.core.RegistryAccess; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; + +import java.util.UUID; + +public class ForgeServerChatAdapter implements ServerChatAdapter { + + private final MinecraftServer server; + + public ForgeServerChatAdapter(MinecraftServer server) { + this.server = server; + } + + @Override + public void broadcastMessage(Component message) { + server.getPlayerList().broadcastSystemMessage(componentToMinecraftComponent(message), false); + } + + @Override + public void sendMessageToPlayer(UUID user, Component component) { + ServerPlayer player = server.getPlayerList().getPlayer(user); + if (null != player) { + player.sendSystemMessage(componentToMinecraftComponent(component)); + } + } + + @Override + public void sendMessageToConsole(Component component) { + server.sendSystemMessage(componentToMinecraftComponent(component)); + } + + public net.minecraft.network.chat.Component componentToMinecraftComponent(Component component) { + return net.minecraft.network.chat.Component.Serializer.fromJson(GsonComponentSerializer.gson().serializeToTree(component), RegistryAccess.EMPTY); + } + +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/command/ForgeCommandService.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/command/ForgeCommandService.java new file mode 100644 index 0000000..f98ebda --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/command/ForgeCommandService.java @@ -0,0 +1,68 @@ +package com.cssbham.cssminecraft.neoforge.command; + +import com.cssbham.cssminecraft.common.adapter.ServerChatAdapter; +import com.cssbham.cssminecraft.common.command.AbstractCommandService; +import com.cssbham.cssminecraft.common.command.CommandContext; +import com.cssbham.cssminecraft.common.command.CommandSender; +import com.cssbham.cssminecraft.common.executor.ServerExecutor; +import com.cssbham.cssminecraft.common.logger.Logger; +import com.cssbham.cssminecraft.common.util.CommandUtil; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; + +import java.util.UUID; + +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; +import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; + +public class ForgeCommandService extends AbstractCommandService { + + private final ServerChatAdapter chatAdapter; + + public ForgeCommandService(Logger logger, ServerExecutor executor, ServerChatAdapter chatAdapter, MinecraftServer server) { + super(logger, executor); + + this.chatAdapter = chatAdapter; + + for (String label : CommandUtil.ALL_COMMANDS) { + // this is "unsafe" only because brigadier is not obfuscated + server.getCommands().getDispatcher().register((LiteralArgumentBuilder) literal(label) + .executes(context -> { + super.execute( + getCommandSenderForSource((CommandSourceStack) context.getSource()), + new CommandContext(label, new String[0]) + ); + return 1; + }) + .then(argument("args", greedyString()) + .executes(context -> { + super.execute( + getCommandSenderForSource((CommandSourceStack) context.getSource()), + new CommandContext(label, getString(context, "args").split(" ")) + ); + return 1; + }))); + } + } + + private CommandSender getCommandSenderForSource(CommandSourceStack source) { + if (source.isPlayer()) { + return new CommandSender( + chatAdapter, + source.getPlayer().getUUID(), + source.getTextName(), + false + ); + } else { + return new CommandSender( + chatAdapter, + new UUID(0, 0), + source.getTextName(), + true + ); + } + } +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/executor/ForgeServerExecutor.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/executor/ForgeServerExecutor.java new file mode 100644 index 0000000..b9a3d23 --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/executor/ForgeServerExecutor.java @@ -0,0 +1,21 @@ +package com.cssbham.cssminecraft.neoforge.executor; + +import com.cssbham.cssminecraft.common.executor.AsyncServerExecutor; +import com.cssbham.cssminecraft.common.logger.Logger; +import net.minecraft.server.MinecraftServer; + +public class ForgeServerExecutor extends AsyncServerExecutor { + + private final MinecraftServer server; + + public ForgeServerExecutor(Logger logger, MinecraftServer server) { + super(logger); + this.server = server; + } + + @Override + public void doSync(Runnable runnable) { + server.executeIfPossible(runnable); + } + +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java new file mode 100644 index 0000000..29c1252 --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java @@ -0,0 +1,83 @@ +package com.cssbham.cssminecraft.neoforge.listener; + +import com.cssbham.cssminecraft.common.event.Event; +import com.cssbham.cssminecraft.common.event.EventBus; +import com.cssbham.cssminecraft.common.event.PlatformEventAdapter; +import com.cssbham.cssminecraft.common.event.events.PlayerJoinEvent; +import com.cssbham.cssminecraft.common.event.events.PlayerQuitEvent; +import com.cssbham.cssminecraft.common.event.events.ServerMessageEvent; +import com.cssbham.cssminecraft.common.executor.ServerExecutor; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.ServerChatEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; + +import java.util.Objects; + +public class ForgeEventAdapter implements PlatformEventAdapter { + + private final MinecraftServer server; + private final ServerExecutor executor; + + public ForgeEventAdapter(MinecraftServer server, ServerExecutor executor) { + this.server = server; + this.executor = executor; + } + + private EventBus eventBus; + + @Override + public void bindPlatformToEventBus(EventBus eventBus) { + this.eventBus = eventBus; + + NeoForge.EVENT_BUS.register(this); + } + + private void dispatchEvent(Event event) { + Objects.requireNonNull(event, "event bus not bound"); + + executor.doAsync(() -> eventBus.dispatch(event)); + } + + @SubscribeEvent + public void onChat(ServerChatEvent event) { + ServerPlayer player = event.getPlayer(); + String name = event.getUsername(); + + dispatchEvent(new ServerMessageEvent( + player.getUUID(), + name, + (null == player.getDisplayName()) ? name : player.getDisplayName().getString(), + event.getRawText() + )); + } + + @SubscribeEvent + public void onLogin(PlayerEvent.PlayerLoggedInEvent event) { + Player player = event.getEntity(); + String name = player.getName().getString(); + + dispatchEvent(new PlayerJoinEvent( + player.getUUID(), + name, + (null == player.getDisplayName()) ? name : player.getDisplayName().getString(), + server.getPlayerCount() + )); + } + + @SubscribeEvent + public void onLogout(PlayerEvent.PlayerLoggedOutEvent event) { + Player player = event.getEntity(); + String name = player.getName().getString(); + + dispatchEvent(new PlayerQuitEvent( + player.getUUID(), + name, + (null == player.getDisplayName()) ? name : player.getDisplayName().getString(), + server.getPlayerCount() - 1 + )); + } +} diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/logger/ForgeLogger.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/logger/ForgeLogger.java new file mode 100644 index 0000000..b536c08 --- /dev/null +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/logger/ForgeLogger.java @@ -0,0 +1,29 @@ +package com.cssbham.cssminecraft.neoforge.logger; + +import com.cssbham.cssminecraft.common.logger.AbstractLogger; +import org.apache.logging.log4j.LogManager; + +public class ForgeLogger extends AbstractLogger { + + private final org.apache.logging.log4j.Logger logger; + + public ForgeLogger(String name) { + this.logger = LogManager.getLogger(name); + } + + @Override + protected void logInfo(String string) { + logger.info(string); + } + + @Override + protected void logError(String string) { + logger.error(string); + } + + @Override + protected void logWarning(String string) { + logger.warn(string); + } + +} diff --git a/neoforge/src/main/templates/META-INF/neoforge.mods.toml b/neoforge/src/main/templates/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..fc09d22 --- /dev/null +++ b/neoforge/src/main/templates/META-INF/neoforge.mods.toml @@ -0,0 +1,69 @@ + +modLoader="javafml" #mandatory +loaderVersion="${loader_version_range}" #mandatory +license="todo" + +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory +modId="cssminecraft" +version="1.0.0" +displayName="CSSMinecraft" +logoFile="logo.png" +authors="LMBishop" +description=''' + CSS' Minecraft plugin + ''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +#[[mixins]] +#config="${mod_id}.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.cssminecraft]] #optional +# the modid of the dependency +modId="neoforge" #mandatory +# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). +# 'required' requires the mod to exist, 'optional' does not +# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning +type="required" #mandatory +# Optional field describing why the dependency is required or why it is incompatible +# reason="..." +# The version range of the dependency +versionRange="${neo_version_range}" #mandatory +# An ordering relationship for the dependency. +# BEFORE - This mod is loaded BEFORE the dependency +# AFTER - This mod is loaded AFTER the dependency +ordering="NONE" +# Side this dependency is applied on - BOTH, CLIENT, or SERVER +side="BOTH" + +[[dependencies.cssminecraft]] +modId="minecraft" +type="required" +# This version range declares a minimum of the current minecraft version up to but not including the next major version +versionRange="${minecraft_version_range}" +ordering="NONE" +side="BOTH" + +[[dependencies.cssminecraft]] +modId="luckperms" +mandatory=false +versionRange="*" +ordering="AFTER" +side="SERVER" + + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features."${mod_id}"] +#openGLVersion="[3.2,)" diff --git a/settings.gradle b/settings.gradle index b9d5520..37540d3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,14 +3,20 @@ pluginManagement { gradlePluginPortal() maven { url = "https://maven.fabricmc.net/" } maven { url = "https://maven.minecraftforge.net/" } + maven { url = 'https://maven.neoforged.net/releases' } } } +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} + rootProject.name = "css-minecraft" include( "common", "bukkit", "fabric", - "forge" + "forge", + "neoforge" ) \ No newline at end of file From 4765ba78c50679c91372ceaa2c2355f31f7d181e Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:43:49 +0100 Subject: [PATCH 2/7] Use JarInJar for jar shading --- neoforge/build.gradle | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 34ab5ff..c8c029f 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -13,7 +13,7 @@ repositories { } base { - archivesName = mod_id + archivesName = mod_id + "-neoforge" } java.toolchain.languageVersion = JavaLanguageVersion.of(21) @@ -119,11 +119,11 @@ dependencies { // implementation "net.neoforged:neoforge:${neo_version}" - implementation ("net.kyori:adventure-api:4.17.0") { + jarJar(implementation("net.kyori:adventure-api:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "annotations") } - implementation ("net.kyori:adventure-text-serializer-gson:4.17.0") { + jarJar(implementation("net.kyori:adventure-text-serializer-gson:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "adventure-api") exclude(module: "annotations") @@ -131,9 +131,15 @@ dependencies { exclude(module: "gson") } - implementation project(path: ":common", configuration: "shadow") + jarJar(implementation(project(path: ":common", configuration: "shadow"))) + + // additionalRuntimeClasspath("com.cssbham:common:.*") + additionalRuntimeClasspath("net.kyori:adventure-api:4.17.0") + additionalRuntimeClasspath("net.kyori:adventure-text-serializer-gson:4.17.0") } +tasks.shadowJar.enabled = false + // This block of code expands all declared replace properties in the specified resource targets. // A missing property will result in an error. Properties are expanded using ${} Groovy notation. var generateModMetadata = tasks.register("generateModMetadata", ProcessResources) { @@ -169,18 +175,3 @@ idea { downloadJavadoc = true } } - -shadowJar { - dependencies { - include(project(":common")) - include(dependency("net.kyori:.*")) - exclude(dependency("net.neoforged:.*")) - exclude(dependency("io.github.llamalad7:mixinextras-neoforge:.*")) - } - - relocate "net.kyori", "com.cssbham.cssminecraft.lib.adventure" - - archiveFileName = "cssminecraft-neoforge-${project.version}.jar" - - minimize() -} From c5031e21a0d8236502b5bbb1d685066cdbe01496 Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Wed, 11 Jun 2025 23:04:25 +0100 Subject: [PATCH 3/7] Tidy up properties and neoforge.mods.toml file --- neoforge/gradle.properties | 2 +- .../templates/META-INF/neoforge.mods.toml | 56 ++++--------------- 2 files changed, 12 insertions(+), 46 deletions(-) diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index d13e764..fcc5ca4 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -33,7 +33,7 @@ mod_name=CSS-Minecraft # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=todo # The mod version. See https://semver.org/ -mod_version=1.0-SNAPSHOT +mod_version=1.0.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/neoforge/src/main/templates/META-INF/neoforge.mods.toml b/neoforge/src/main/templates/META-INF/neoforge.mods.toml index fc09d22..0238ff2 100644 --- a/neoforge/src/main/templates/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/templates/META-INF/neoforge.mods.toml @@ -1,69 +1,35 @@ modLoader="javafml" #mandatory loaderVersion="${loader_version_range}" #mandatory -license="todo" +license="${mod_license}" -# A URL to refer people to when problems occur with this mod -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional -# A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory -modId="cssminecraft" -version="1.0.0" -displayName="CSSMinecraft" +modId="${mod_id}" +version="${mod_version}" +displayName="${mod_name}" logoFile="logo.png" authors="LMBishop" description=''' CSS' Minecraft plugin ''' -# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. -#[[mixins]] -#config="${mod_id}.mixins.json" - -# The [[accessTransformers]] block allows you to declare where your AT file is. -# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg -#[[accessTransformers]] -#file="META-INF/accesstransformer.cfg" - -# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json - -# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. -[[dependencies.cssminecraft]] #optional -# the modid of the dependency -modId="neoforge" #mandatory -# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). -# 'required' requires the mod to exist, 'optional' does not -# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning -type="required" #mandatory -# Optional field describing why the dependency is required or why it is incompatible -# reason="..." -# The version range of the dependency -versionRange="${neo_version_range}" #mandatory -# An ordering relationship for the dependency. -# BEFORE - This mod is loaded BEFORE the dependency -# AFTER - This mod is loaded AFTER the dependency +[[dependencies."${mod_id}"]] +modId="neoforge" +type="required" +versionRange="${neo_version_range}" ordering="NONE" -# Side this dependency is applied on - BOTH, CLIENT, or SERVER side="BOTH" -[[dependencies.cssminecraft]] +[[dependencies."${mod_id}"]] modId="minecraft" type="required" -# This version range declares a minimum of the current minecraft version up to but not including the next major version versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" -[[dependencies.cssminecraft]] +[[dependencies."${mod_id}"]] modId="luckperms" -mandatory=false +type="optional" versionRange="*" ordering="AFTER" side="SERVER" - - -# Features are specific properties of the game environment, that you may want to declare you require. This example declares -# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't -# stop your mod loading on the server for example. -#[features."${mod_id}"] -#openGLVersion="[3.2,)" From 0f0a900202fcdcc2840e5a2460f13aedfed3f07e Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Wed, 11 Jun 2025 23:04:39 +0100 Subject: [PATCH 4/7] Add neoforge to workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04c2b2c..50e44fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - target: [ bukkit, fabric, forge ] + target: [ bukkit, fabric, forge, neoforge ] name: Build ${{ matrix.target }} runs-on: ubuntu-latest From 256f470286761bf4f5d6162f4249723f34046eee Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:36:55 +0100 Subject: [PATCH 5/7] Add NeoForge Adventure platform --- neoforge/build.gradle | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/neoforge/build.gradle b/neoforge/build.gradle index c8c029f..d8baaa6 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -119,11 +119,11 @@ dependencies { // implementation "net.neoforged:neoforge:${neo_version}" - jarJar(implementation("net.kyori:adventure-api:4.17.0")) { + implementation(jarJar("net.kyori:adventure-api:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "annotations") } - jarJar(implementation("net.kyori:adventure-text-serializer-gson:4.17.0")) { + implementation(jarJar("net.kyori:adventure-text-serializer-gson:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "adventure-api") exclude(module: "annotations") @@ -131,7 +131,11 @@ dependencies { exclude(module: "gson") } - jarJar(implementation(project(path: ":common", configuration: "shadow"))) + implementation(jarJar("net.kyori:adventure-platform-neoforge:6.0.0")) + + implementation(jarJar(project(path: ":common", configuration: "shadow"))) + + // additionalRuntimeClasspath("com.cssbham:common:.*") additionalRuntimeClasspath("net.kyori:adventure-api:4.17.0") From 44101ad35d0a2b8ae240debf363cb1dd8b0a8eec Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Fri, 13 Jun 2025 20:03:13 +0100 Subject: [PATCH 6/7] Fixes to event priority and jar-in-jar shading --- common/build.gradle | 17 +++ .../handler/MakeGreenCommandHandler.java | 13 +- .../LuckPermsPermissionPluginService.java | 9 +- neoforge/build.gradle | 111 ++++++++++++++++-- .../neoforge/listener/ForgeEventAdapter.java | 3 +- .../templates/META-INF/neoforge.mods.toml | 12 +- 6 files changed, 141 insertions(+), 24 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 8cb6aff..6ab0912 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,6 +1,11 @@ plugins { id "java" id "java-library" + id "maven-publish" +} + +compileJava { + options.compilerArgs += "-g" } dependencies { @@ -34,3 +39,15 @@ shadowJar { minimize() } + +publishing { + publications { + maven(MavenPublication) { + groupId = 'com.cssbham' + artifactId = 'common' + version = '1.0.0' + + from components.java + } + } +} diff --git a/common/src/main/java/com/cssbham/cssminecraft/common/command/handler/MakeGreenCommandHandler.java b/common/src/main/java/com/cssbham/cssminecraft/common/command/handler/MakeGreenCommandHandler.java index b9ba9dd..8f178f7 100644 --- a/common/src/main/java/com/cssbham/cssminecraft/common/command/handler/MakeGreenCommandHandler.java +++ b/common/src/main/java/com/cssbham/cssminecraft/common/command/handler/MakeGreenCommandHandler.java @@ -45,13 +45,20 @@ public void handle(CommandSender sender, CommandContext context) { if (discordClientService.getDiscordClient().isMember(arg)) { sender.sendMessage(Component.text("Making you green...").color(NamedTextColor.GRAY)); try { - permissionPluginService.grantMemberRole(sender.getUuid()).get(); - } catch (InterruptedException | ExecutionException e) { + permissionPluginService.grantMemberRole(sender.getUuid()).whenComplete((v, err) -> { + if (err != null) { + sender.sendMessage(Component.text("There was a problem making you green. Try again later.") + .color(NamedTextColor.RED)); + throw new RuntimeException(err); + } + + sender.sendMessage(Component.text("Congratulations, you are now green!").color(NamedTextColor.GREEN)); + }); + } catch (Exception e) { sender.sendMessage(Component.text("There was a problem making you green. Try again later.") .color(NamedTextColor.RED)); throw new RuntimeException(e); } - sender.sendMessage(Component.text("Congratulations, you are now green!").color(NamedTextColor.GREEN)); } else { sender.sendMessage(Component.text("You don't appear to be a ").color(NamedTextColor.RED).append( Component.text("Member").color(NamedTextColor.GREEN) diff --git a/common/src/main/java/com/cssbham/cssminecraft/common/permission/LuckPermsPermissionPluginService.java b/common/src/main/java/com/cssbham/cssminecraft/common/permission/LuckPermsPermissionPluginService.java index d77e783..cb05dbf 100644 --- a/common/src/main/java/com/cssbham/cssminecraft/common/permission/LuckPermsPermissionPluginService.java +++ b/common/src/main/java/com/cssbham/cssminecraft/common/permission/LuckPermsPermissionPluginService.java @@ -20,7 +20,14 @@ public CompletableFuture grantMemberRole(UUID player) { user.data().add(Node.builder("group.member").build()); user.data().remove(Node.builder("group.guest").build()); } - ); + ).whenCompleteAsync((v, err) -> { + if (err != null) { + err.printStackTrace(); + return; + } + + System.out.println("Success"); + }); } @Override diff --git a/neoforge/build.gradle b/neoforge/build.gradle index d8baaa6..27ec48f 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -18,6 +18,8 @@ base { java.toolchain.languageVersion = JavaLanguageVersion.of(21) +jarJar.enabled = true + neoForge { // Specify the version of NeoForge to use. version = project.neo_version @@ -87,6 +89,7 @@ neoForge { // but multi mod projects should define one per mod "${mod_id}" { sourceSet(sourceSets.main) + sourceSet(project(":common").sourceSets.main) } } } @@ -94,6 +97,9 @@ neoForge { // Include resources generated by data generators. sourceSets.main.resources { srcDir 'src/generated/resources' } +compileJava { + options.compilerArgs += "-g" +} dependencies { // Example mod dependency with JEI @@ -119,30 +125,60 @@ dependencies { // implementation "net.neoforged:neoforge:${neo_version}" - implementation(jarJar("net.kyori:adventure-api:4.17.0")) { + /* compileOnly(jarJar("net.kyori:adventure-api:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "annotations") } - implementation(jarJar("net.kyori:adventure-text-serializer-gson:4.17.0")) { + compileOnly(jarJar("net.kyori:adventure-text-serializer-gson:4.17.0")) { exclude(module: "adventure-bom") exclude(module: "adventure-api") exclude(module: "annotations") exclude(module: "auto-service-annotations") exclude(module: "gson") - } - - implementation(jarJar("net.kyori:adventure-platform-neoforge:6.0.0")) + } */ - implementation(jarJar(project(path: ":common", configuration: "shadow"))) - - - - // additionalRuntimeClasspath("com.cssbham:common:.*") - additionalRuntimeClasspath("net.kyori:adventure-api:4.17.0") - additionalRuntimeClasspath("net.kyori:adventure-text-serializer-gson:4.17.0") + jarJar(implementation(group: "net.kyori", name: "adventure-platform-neoforge", version: "6.0.0") { + exclude(module: "annotations") + exclude(module: "auto-service-annotations") + exclude(module: "gson") + }) + jarJar(implementation ("net.dv8tion:JDA:5.0.2") { + exclude(module: "opus-java") + exclude(module: "annotations") + exclude(module: "slf4j-api") + }) + jarJar(implementation ("club.minnced:discord-webhooks:0.8.4") { + exclude(module: "slf4j-api") + }) + jarJar(implementation("org.yaml:snakeyaml:2.2")) + jarJar(implementation("com.neovisionaries:nv-websocket-client:2.14")) + jarJar(implementation("com.squareup.okhttp3:okhttp:4.12.0")) + jarJar(implementation("org.apache.commons:commons-collections4:4.4")) + jarJar(implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10")) + jarJar(implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10")) + jarJar(implementation("org.jetbrains.kotlin:kotlin-stdlib-common:1.9.10")) + jarJar(implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.10")) + jarJar(implementation("com.squareup.okio:okio:3.6.0")) + jarJar(implementation("net.sf.trove4j:core:3.1.0")) + jarJar(implementation("com.fasterxml.jackson.core:jackson-core:2.17.2")) + jarJar(implementation("com.fasterxml.jackson:jackson-bom:2.17.2")) + jarJar(implementation("com.fasterxml.jackson.core:jackson-annotations:2.17.2")) + jarJar(implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")) + jarJar(implementation("org.json:json:20230618")) + + shadow(compileOnly(project(path: ":common"))) { + transitive(false) + } + // additionalRuntimeClasspath("org.yaml:snakeyaml:2.2") + // additionalRuntimeClasspath("net.dv8tion:JDA:5.0.2") + // additionalRuntimeClasspath("club.minnced:discord-webhooks:0.8.4") + // additionalRuntimeClasspath("com.cssbham:common:1.0.0") + // additionalRuntimeClasspath("net.kyori:adventure-api:4.17.0") + // additionalRuntimeClasspath("net.kyori:adventure-platform-neoforge:6.0.0") + // additionalRuntimeClasspath("net.kyori:adventure-text-serializer-gson:4.17.0") } -tasks.shadowJar.enabled = false +// tasks.shadowJar.enabled = false // This block of code expands all declared replace properties in the specified resource targets. // A missing property will result in an error. Properties are expanded using ${} Groovy notation. @@ -179,3 +215,52 @@ idea { downloadJavadoc = true } } +/* +tasks.shadowJar { + configurations = [project.configurations.shadow] + + dependencies { + exclude(dependency("net.kyori:adventure-platform-neoforge:6.0.0")) + } + + doLast { + copy { + from("build/generated/jarjar/META-INF/jarjar") + into("META-INF/jarjar") + } + } + + // exclude("net/**") + // exclude("META-INF/services/*") + // minimize() + + archiveFileName = "cssminecraft-neoforge-${project.version}.jar" +} + + */ + +tasks.shadowJar.enabled = false +/* +tasks.register("shadeCommon") { + copy { + from("../common/build/classes/main/com/cssbham/cssminecraft/common") + into("build/classes/") + } +} */ + +tasks.jar { + from("../common/build/classes/java/main/com/cssbham/cssminecraft/common") { + into("com/cssbham/cssminecraft/common") + } +} +/* +tasks.register("copyJarJar") { + copy { + from ("build/generated/jarjar/META-INF/jarjar") + into("META-INF/jarjar") + } +} + +tasks.copyJarJar.dependsOn(jarJar) +*/ + diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java index 29c1252..988cb7b 100644 --- a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/listener/ForgeEventAdapter.java @@ -10,6 +10,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; +import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.ServerChatEvent; @@ -42,7 +43,7 @@ private void dispatchEvent(Event event) { executor.doAsync(() -> eventBus.dispatch(event)); } - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.HIGH) public void onChat(ServerChatEvent event) { ServerPlayer player = event.getPlayer(); String name = event.getUsername(); diff --git a/neoforge/src/main/templates/META-INF/neoforge.mods.toml b/neoforge/src/main/templates/META-INF/neoforge.mods.toml index 0238ff2..4332c7c 100644 --- a/neoforge/src/main/templates/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/templates/META-INF/neoforge.mods.toml @@ -4,7 +4,7 @@ loaderVersion="${loader_version_range}" #mandatory license="${mod_license}" [[mods]] #mandatory -modId="${mod_id}" +modId="cssminecraft" version="${mod_version}" displayName="${mod_name}" logoFile="logo.png" @@ -13,23 +13,23 @@ description=''' CSS' Minecraft plugin ''' -[[dependencies."${mod_id}"]] +[[dependencies.cssminecraft]] modId="neoforge" type="required" versionRange="${neo_version_range}" ordering="NONE" side="BOTH" -[[dependencies."${mod_id}"]] +[[dependencies.cssminecraft]] modId="minecraft" type="required" versionRange="${minecraft_version_range}" ordering="NONE" side="BOTH" -[[dependencies."${mod_id}"]] +[[dependencies.cssminecraft]] modId="luckperms" -type="optional" +type="required" versionRange="*" -ordering="AFTER" +ordering="BEFORE" side="SERVER" From 9159a38ac0b8e09fb69cce5fb63ffd98677a17b1 Mon Sep 17 00:00:00 2001 From: Thatsmusic99 <25277367+thatsmusic99@users.noreply.github.com> Date: Sun, 15 Jun 2025 11:27:31 +0100 Subject: [PATCH 7/7] Fix JDA not shutting down --- .../com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java | 1 + 1 file changed, 1 insertion(+) diff --git a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java index 7dc6b57..9d11763 100644 --- a/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java +++ b/neoforge/src/main/java/com/cssbham/cssminecraft/neoforge/CSSMinecraftLoader.java @@ -16,6 +16,7 @@ public class CSSMinecraftLoader { public CSSMinecraftLoader() { this.plugin = new NeoForgeCSSMinecraftPlugin(); NeoForge.EVENT_BUS.addListener(this::onServerStarted); + NeoForge.EVENT_BUS.addListener(this::onServerStopping); } public void onServerStarted(ServerStartingEvent event) {