From 9176242ff35f075702e090720c6224bbac8d4a72 Mon Sep 17 00:00:00 2001 From: Tunfisch96 <65600302+Tunfisch96@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:09:48 +0200 Subject: [PATCH 1/6] Update to 1.21.3 --- build.gradle | 2 +- .../mod/fabric/BaseFabricProvider.java | 1 - .../fabric/mixins/MinecraftServerMixin.java | 2 +- fabric/src/main/resources/fabric.mod.json | 2 +- gradle.properties | 20 ++++++++-------- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 ++++-- gradlew.bat | 22 ++++++++++-------- 9 files changed, 31 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index fbde8332..b992b1fd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false + id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false } diff --git a/fabric/src/main/java/net/pcal/fastback/mod/fabric/BaseFabricProvider.java b/fabric/src/main/java/net/pcal/fastback/mod/fabric/BaseFabricProvider.java index 7e40ca30..dfbb700d 100644 --- a/fabric/src/main/java/net/pcal/fastback/mod/fabric/BaseFabricProvider.java +++ b/fabric/src/main/java/net/pcal/fastback/mod/fabric/BaseFabricProvider.java @@ -27,7 +27,6 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.LevelSummary; import net.pcal.fastback.logging.Log4jLogger; import net.pcal.fastback.logging.SystemLogger; import net.pcal.fastback.logging.UserMessage; diff --git a/fabric/src/main/java/net/pcal/fastback/mod/fabric/mixins/MinecraftServerMixin.java b/fabric/src/main/java/net/pcal/fastback/mod/fabric/mixins/MinecraftServerMixin.java index 6978bd16..87964a5d 100644 --- a/fabric/src/main/java/net/pcal/fastback/mod/fabric/mixins/MinecraftServerMixin.java +++ b/fabric/src/main/java/net/pcal/fastback/mod/fabric/mixins/MinecraftServerMixin.java @@ -41,7 +41,7 @@ public class MinecraftServerMixin { * Intercept the call to saveAll that triggers on autosave, pass it through and then send out notification that * the autosave is done. */ - @Redirect(method = "tickServer(Ljava/util/function/BooleanSupplier;)V", + @Redirect(method = "autoSave()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;saveEverything(ZZZ)Z")) public boolean fastback_saveAll(MinecraftServer instance, boolean suppressLogs, boolean flush, boolean force) { boolean result = instance.saveEverything(suppressLogs, flush, force); diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 0a3f151c..37c2e70b 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -25,7 +25,7 @@ "fastback.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.16.4", "fabric": "*", "minecraft": "1.21.x", "java": ">=21" diff --git a/gradle.properties b/gradle.properties index 8b32086b..4836b4f5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # # Fastback # -mod_version = 0.19.1+1.21-prerelease +mod_version = 0.19.2+1.21.3-prerelease maven_group = net.pcal maven_name = fastback archives_base_name = fastback @@ -10,9 +10,9 @@ archives_base_name = fastback # # Fabric & Minecraft - https://fabricmc.net/develop # -minecraft_version=1.21 -loader_version=0.15.11 -fabric_version=0.100.6+1.21 +minecraft_version=1.21.3 +loader_version=0.16.4 +fabric_version=0.106.1+1.21.3 # # Forge @@ -27,10 +27,10 @@ fabric_version=0.100.6+1.21 # # https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -jgit_version = 6.8.0.202311291450-r +jgit_version = 6.10.0.202406032230-r # https://mvnrepository.com/artifact/org.apache.sshd/sshd-core -apache_sshd_version = 2.12.1 +apache_sshd_version = 2.14.0 # https://mvnrepository.com/artifact/com.googlecode.javaewah/JavaEWAH JavaEWAH_version = 1.2.3 @@ -39,10 +39,10 @@ JavaEWAH_version = 1.2.3 eddsa_version = 0.3.0 # https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -test_log4j_version = 2.23.1 +test_log4j_version = 2.24.1 # https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -junit_jupiter_version = 5.10.2 +junit_jupiter_version = 5.11.2 # @@ -54,13 +54,13 @@ fabric_permissions_version = 0.3.1 # https://github.com/NucleoidMC/Server-Translations/releases # https://maven.nucleoid.xyz/xyz/nucleoid/server-translations-api/ -server_translations_version = 2.3.1+1.21-pre2 +server_translations_version = 2.4.0+1.21.2-rc1 # # Build settings # -architectury_version = 12.1.2 +architectury_version = 13.0.8 org.gradle.daemon = true org.gradle.parallel = true org.gradle.jvmargs = -Xmx4G diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 12612 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 HJ4oML2Lo<@i8BwbL^1~GkG`E7C$SEa_ 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a..9355b415 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..9b42019c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From a01ae15a2516958656ea1376b23e3ab1bbd114f1 Mon Sep 17 00:00:00 2001 From: Stewart Borle Date: Wed, 20 Nov 2024 17:14:13 +0800 Subject: [PATCH 2/6] Copy forge to neoforge directory This allows changes made to neoforge to be seen more clearly. --- neoforge/build.gradle | 137 +++++++++++ neoforge/gradle.properties | 1 + .../mod/neoforge/ForgeClientProvider.java | 111 +++++++++ .../mod/neoforge/ForgeCommonProvider.java | 222 ++++++++++++++++++ .../mod/neoforge/ForgeInitializer.java | 30 +++ .../mod/neoforge/Slf4jSystemLogger.java | 63 +++++ .../pcal/fastback/mod/neoforge/SshHacks.java | 113 +++++++++ .../resources/META-INF/accesstransformer.cfg | 7 + .../src/main/resources/META-INF/mods.toml | 29 +++ neoforge/src/main/resources/fastback-icon.png | Bin 0 -> 23557 bytes neoforge/src/main/resources/pack.mcmeta | 6 + 11 files changed, 719 insertions(+) create mode 100644 neoforge/build.gradle create mode 100644 neoforge/gradle.properties create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java create mode 100644 neoforge/src/main/resources/META-INF/accesstransformer.cfg create mode 100644 neoforge/src/main/resources/META-INF/mods.toml create mode 100644 neoforge/src/main/resources/fastback-icon.png create mode 100644 neoforge/src/main/resources/pack.mcmeta diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 00000000..e7a6b8d9 --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,137 @@ +plugins { + id "com.github.johnrengelman.shadow" version "8.1.1" + id "com.modrinth.minotaur" version "2.8.7" + id "com.matthewprenger.cursegradle" version "1.4.0" +} + +architectury { + platformSetupLoomIde() + forge() +} + +shadowJar { + //https://stackoverflow.com/questions/73286776/grpc-unsupportedaddresstypeexception-but-only-when-packaged-with-shadowjar + // This does in fact make service discovery work when packaged; still doesn't work in the IDE. see SshHacks. + mergeServiceFiles() +} + +configurations { + common + shadowCommon + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common + developmentForge.extendsFrom common +} + +archivesBaseName = "${project.archives_base_name}" +version = "${project.mod_version}-forge" +group = project.maven_group + +dependencies { + forge("net.minecraftforge:forge:${project.forge_version}") { transitive false } + + // note to self: implementation, NOT include. include does implicit jarjar + + common(project(path: ":common", configuration: "namedElements")) { transitive false } + shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } + + // FIXME? I still don't understand if I need to declare all of these things as forgeRuntimeLibrary. It sort + // of seems like I do. + + forgeRuntimeLibrary implementation("org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}") { transitive = false } + shadowCommon("org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}") { transitive = false } + + forgeRuntimeLibrary runtimeOnly("org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${project.jgit_version}") { transitive = false; } + shadowCommon("org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${project.jgit_version}") { transitive = false } + + forgeRuntimeLibrary runtimeOnly('com.jcraft:jsch:0.1.55') + shadowCommon('com.jcraft:jsch:0.1.55') + + forgeRuntimeLibrary runtimeOnly("com.googlecode.javaewah:JavaEWAH:${project.JavaEWAH_version}") { transitive = false } + shadowCommon("com.googlecode.javaewah:JavaEWAH:${project.JavaEWAH_version}") { transitive = false } +} + +processResources { + inputs.property "version", project.version + + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} + +shadowJar { + configurations = [project.configurations.shadowCommon] + exclude('META-INF/maven/**') + + // https://stackoverflow.com/questions/36659980/java-jar-classnotfoundexception-even-though-dependent-library-exists + // Forge has full control over loading the classes of a mod and it specifically checks the package information + // of every class it loads against a set of restricted package paths to protect its own dependencies from + // accidentally being overwritten by loading a different version of a similar dependency. In this case, Forge + // uses a few Apache libs, so it prevents the loading of classes from the org.apache package namespace. + + relocate 'org/eclipse', 'net/pcal/fastback/shaded/org/eclipse' + relocate 'com/jcraft', 'net/pcal/fastback/shaded/com/jcraft' + archiveClassifier = 'dev-shadow' +} + +remapJar { + inputFile.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier = null +} + +jar { + + archiveClassifier = 'dev' +} + +sourcesJar { + def commonSources = project(":common").sourcesJar + dependsOn commonSources + from commonSources.archiveFile.map { zipTree(it) } +} + +processResources { + inputs.property "version", project.version + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } +} + + +// https://github.com/modrinth/minotaur +modrinth { + token = System.getenv("MODRINTH_TOKEN") ?: 'MODRINTH_TOKEN_NOT_SET' + projectId = "fastback" + versionNumber = "${project.version}" + versionType = "alpha" + uploadFile = remapJar + changelog = "

https://github.com/pcal43/fastback/releases/tag/${project.mod_version}

" + gameVersions = ["${project.minecraft_version}"] + loaders = ["forge"] + dependencies {} +} + + +// https://github.com/matthewprenger/CurseGradle +curseforge { + apiKey = System.getenv("CURSEFORGE_TOKEN") ?: 'CURSEFORGE_TOKEN_NOT_SET' + + project { + id = "667417" + releaseType = "alpha" + changelog = "https://github.com/pcal43/fastback/releases/tag/${project.mod_version}" + changelogType = "markdown" + mod_version = "${project.version}" + addGameVersion((String) project.minecraft_version) + addGameVersion "Forge" + mainArtifact(remapJar) + afterEvaluate { + uploadTask.dependsOn("remapJar") + } + } + + options { + forgeGradleIntegration = false + } +} diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 00000000..82425854 --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=forge diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java new file mode 100644 index 00000000..5c9bed33 --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java @@ -0,0 +1,111 @@ +package net.pcal.fastback.mod.forge; + +import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; +import net.minecraftforge.client.event.ScreenEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.pcal.fastback.logging.UserMessage; + +import static java.util.Objects.requireNonNull; +import static net.pcal.fastback.logging.SystemLogger.syslog; +import static net.pcal.fastback.mod.MinecraftProvider.messageToText; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; + +/** + * Handles client-specific tasks. + * + * @author pcal + * @since 0.16.0 + */ +final class ForgeClientProvider extends ForgeCommonProvider { + + // ====================================================================== + // Constants + + private static final long TEXT_TIMEOUT = 10 * 1000; + + // ====================================================================== + // Fields + + //private MinecraftClient client = null; + private Component hudText; + private long hudTextTime; + private final Minecraft client; + + public ForgeClientProvider() { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + modEventBus.addListener(this::onClientStartupEvent); + MinecraftForge.EVENT_BUS.addListener(this::onGuiOverlayEvent); + MinecraftForge.EVENT_BUS.addListener(this::onScreenRenderEvent); + this.client = requireNonNull(Minecraft.getInstance(), "MinecraftClient.getInstance() returned null"); + } + + // ====================================================================== + // Forge Event handlers + + private void onClientStartupEvent(FMLClientSetupEvent event) { + this.onInitialize(); + } + + private void onGuiOverlayEvent(CustomizeGuiOverlayEvent event) { + this.renderOverlayText(event.getGuiGraphics()); + } + + private void onScreenRenderEvent(ScreenEvent.Render.Post event) { + this.renderOverlayText(event.getGuiGraphics()); + } + + // ====================================================================== + // MinecraftProvider implementation + + @Override + public boolean isClient() { + return true; + } + + @Override + public void setHudText(UserMessage userMessage) { + if (userMessage == null) { + clearHudText(); + } else { + this.hudText = messageToText(userMessage); // so the hud renderer can find it + this.hudTextTime = System.currentTimeMillis(); + } + } + + @Override + public void clearHudText() { + this.hudText = null; + // TODO someday it might be nice to bring back the fading text effect. But getting to it properly + // clean up 100% of the time is more than I want to deal with right now. + } + + @Override + public void setMessageScreenText(UserMessage userMessage) { + final Component text = messageToText(userMessage); + this.hudText = text; + final Screen screen = client.screen; + if (screen != null) screen.title = text; + } + + @Override + void renderOverlayText(final GuiGraphics drawContext) { + if (this.hudText == null) return; + // if (!this.client.options.getShowAutosaveIndicator().getValue()) return; FIXME + if (System.currentTimeMillis() - this.hudTextTime > TEXT_TIMEOUT) { + // Don't leave it sitting up there forever if we fail to call clearHudText() + this.hudText = null; + syslog().debug("hud text timed out. somebody forgot to clean up"); + return; + } + if (client != null) { + drawContext.drawString(this.client.font, this.hudText, 2, 2, 1); + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java new file mode 100644 index 00000000..ba4921d0 --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java @@ -0,0 +1,222 @@ +package net.pcal.fastback.mod.forge; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.event.server.ServerStoppingEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.pcal.fastback.logging.SystemLogger; +import net.pcal.fastback.logging.UserMessage; +import net.pcal.fastback.mod.LifecycleListener; +import net.pcal.fastback.mod.MinecraftProvider; +import org.slf4j.LoggerFactory; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.requireNonNull; +import static net.pcal.fastback.commands.Commands.createBackupCommand; +import static net.pcal.fastback.logging.SystemLogger.syslog; +import static net.pcal.fastback.mod.MinecraftProvider.messageToText; + +/** + * @author pcal + * @since 0.16.0 + */ +class ForgeCommonProvider implements MinecraftProvider { + + static final String MOD_ID = "fastback"; + private MinecraftServer logicalServer; + private LifecycleListener lifecycleListener = null; + private Runnable autoSaveListener; + private boolean isWorldSaveEnabled; + + ForgeCommonProvider() { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + modEventBus.addListener(this::onDedicatedServerStartupEvent); + MinecraftForge.EVENT_BUS.addListener(this::onServerStartupEvent); + MinecraftForge.EVENT_BUS.addListener(this::onServerStoppingEvent); + MinecraftForge.EVENT_BUS.addListener(this::onRegisterCommandEvent); + } + + + // ====================================================================== + // Forge Event handlers + + private void onDedicatedServerStartupEvent(FMLDedicatedServerSetupEvent event) { + this.onInitialize(); + } + + private void onServerStartupEvent(ServerStartedEvent event) { + this.logicalServer = event.getServer(); + requireNonNull(this.lifecycleListener).onWorldStart(); + } + + private void onServerStoppingEvent(ServerStoppingEvent event) { + requireNonNull(this.lifecycleListener).onWorldStop(); + this.logicalServer = null; + } + + private void onRegisterCommandEvent(RegisterCommandsEvent event) { + final CommandDispatcher commandDispatcher = event.getDispatcher(); + final LiteralArgumentBuilder backupCommand = + createBackupCommand(permName -> x -> true); + commandDispatcher.register(backupCommand); + } + + /** + TODO This one isn't it. We need to hear about it when an autosaves (and only autosaves) are completed. + Might have to delve into Forge mixins to do this. + private void onLevelSaveEvent(LevelEvent.Save event) { + provider.onAutoSaveComplete(); + } + **/ + + + // ====================================================================== + // Protected + + /** + * This is the key initialization routine. Registers the logger, the frameworkprovider and the commands + * where the rest of the mod can get at them. + */ + void onInitialize() { + SystemLogger.Singleton.register(new Slf4jSystemLogger(LoggerFactory.getLogger(MOD_ID))); + this.lifecycleListener = MinecraftProvider.register(this); + syslog().debug("registered backup command"); + this.lifecycleListener.onInitialize(); + SshHacks.ensureSshSessionFactoryIsAvailable(); + syslog().info("Fastback initialized"); + syslog().warn("------------------------------------------------------------------------------------"); + syslog().warn("Thanks for trying the new Forge version of Fastback. For help, go to:"); + syslog().warn("https://pcal43.github.io/fastback/"); + syslog().warn("Please note that this is an alpha release. A list of known issues is available here:"); + syslog().warn("https://github.com/pcal43/fastback/issues?q=is%3Aissue+is%3Aopen+label%3Aforge"); + syslog().warn("------------------------------------------------------------------------------------"); + } + + + // ====================================================================== + // Fastback MinecraftProvider implementation + + @Override + public boolean isClient() { + return false; + } + + @Override + public void setHudText(UserMessage userMessage) { + } + + @Override + public void clearHudText() { + } + + @Override + public void setMessageScreenText(UserMessage userMessage) { + } + + void renderOverlayText(GuiGraphics drawContext) { + } + + @Override + public String getModVersion() { + return "0.15.3+1.20.1-alpha"; //FIXME + } + + //FIXME!! + void onAutoSaveComplete() { + syslog().debug("onAutoSaveComplete"); + this.autoSaveListener.run(); + } + + @Override + public Path getWorldDirectory() { + if (this.logicalServer == null) throw new IllegalStateException("minecraftServer is null"); + final LevelStorageSource.LevelStorageAccess session = logicalServer.storageSource; + Path out = session.getWorldDir().toAbsolutePath().normalize(); + return out; + } + + @Override + public void setWorldSaveEnabled(boolean enabled) { + for (ServerLevel world : logicalServer.getAllLevels()) { + world.noSave = !enabled; + } + } + + @Override + public void saveWorld() { + if (this.logicalServer == null) throw new IllegalStateException(); + this.logicalServer.saveEverything(false, true, true); // suppressLogs, flush, force + } + + @Override + public void sendBroadcast(UserMessage userMessage) { + if (this.logicalServer != null && this.logicalServer.isDedicatedServer()) { + logicalServer.getPlayerList().broadcastSystemMessage(messageToText(userMessage), false); + } + } + + @Override + public void setAutoSaveListener(Runnable runnable) { + this.autoSaveListener = requireNonNull(runnable); + } + + @Override + public Path getSavesDir() { + if (this.isClient()) { + return logicalServer.getServerDirectory().resolve("saves"); + } else { + return null; + } + } + + @Override + public String getWorldName() { + return this.logicalServer.getWorldData().getLevelName(); + } + + /** + * Add extra properties that will be stored in .fastback/backup.properties. + */ + @Override + public void addBackupProperties(Map props) { + props.put("fastback-version", this.getModVersion()); + if (this.logicalServer != null) { + props.put("minecraft-version", logicalServer.getServerVersion()); + props.put("minecraft-game-mode", String.valueOf(logicalServer.getWorldData().getGameType())); + props.put("minecraft-level-name", logicalServer.getWorldData().getLevelName()); + } + } + + /** + * @return paths to the files and directories that should be backed up when config-backup is enabled. + */ + @Override + public Collection getModsBackupPaths() { + final List out = new ArrayList<>(); + /** + final FabricLoader fl = FabricLoader.getInstance(); + final Path gameDir = fl.getGameDir(); + out.add(gameDir.resolve("options.txt´")); + out.add(gameDir.resolve("mods")); + out.add(gameDir.resolve("config")); + out.add(gameDir.resolve("resourcepacks")); + **/ + return out; + } + +} diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java new file mode 100644 index 00000000..40f18e54 --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java @@ -0,0 +1,30 @@ +package net.pcal.fastback.mod.forge; + +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.loading.FMLEnvironment; + +import java.lang.reflect.InvocationTargetException; + +/** + * @author pcal + * @since 0.16.0 + */ +@Mod("fastback") +final public class ForgeInitializer { + + public ForgeInitializer() { + try { + if (FMLEnvironment.dist.isDedicatedServer()) { + new ForgeCommonProvider(); + } else if (FMLEnvironment.dist.isClient()) { + // Forge yells at us if we touch any client classes in a server. So, + Class.forName("net.pcal.fastback.mod.forge.ForgeClientProvider").getConstructor().newInstance(); + } else { + throw new IllegalStateException("where am i? server or client?"); + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java new file mode 100644 index 00000000..0212a6fe --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java @@ -0,0 +1,63 @@ +package net.pcal.fastback.mod.forge; + +import net.pcal.fastback.logging.SystemLogger; +import org.slf4j.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * @author pcal + * @since 0.16.0 + */ +class Slf4jSystemLogger implements SystemLogger { + + private final Logger slf4j; + private boolean forceDebugEnabled = false; + + Slf4jSystemLogger(Logger slf4j) { + this.slf4j = requireNonNull(slf4j); + } + + @Override + public void setForceDebugEnabled(boolean forceDebugEnabled) { + this.forceDebugEnabled = forceDebugEnabled; + } + + @Override + public void error(String message) { + this.slf4j.error(message); + } + + @Override + public void error(String message, Throwable t) { + this.slf4j.error(message, t); + } + + @Override + public void warn(String message) { + this.slf4j.warn(message); + } + + @Override + public void info(String message) { + this.slf4j.info(message); + } + + @Override + public void debug(String message) { + if (this.forceDebugEnabled) { + this.slf4j.info("[DEBUG] " + message); + } else { + this.slf4j.debug(message); + } + } + + @Override + public void debug(String message, Throwable t) { + if (this.forceDebugEnabled) { + this.slf4j.info("[DEBUG] " + message, t); + } else { + this.slf4j.debug(message, t); + } + } +} diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java new file mode 100644 index 00000000..fcc95fc0 --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java @@ -0,0 +1,113 @@ +package net.pcal.fastback.mod.forge; + +import org.eclipse.jgit.transport.SshSessionFactory; + +import java.lang.reflect.InvocationTargetException; + +import static net.pcal.fastback.logging.SystemLogger.syslog; + +public class SshHacks { + + /** + * This is necessary because JGit looks for it's SshSessionFactory with java.util.ServiceLoader, and that + * just doesn't seem to be something that Forge is willing to accommodate. + */ + public static void ensureSshSessionFactoryIsAvailable() { + try { + if (SshSessionFactory.getInstance() == null) { + try { + final String clazz = "org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory"; + SshSessionFactory.setInstance((SshSessionFactory) Class.forName(clazz).getConstructor().newInstance()); + // AFAICT this only happens in Intellij. Something about shadowJar and relocate isn't working in dev environments. + // Seems fine in the launcher. + syslog().warn("A SshSessionFactory was not located via java services; a " + clazz + " has been installed manually. This is probably ok."); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | + NoSuchMethodException | InvocationTargetException ohwell) { + syslog().error("Unable to manually set SshSessionFactory. SSH connections will probably not work.", ohwell); + } + } + // + } catch (Error err) { + syslog().error("WAT", err); + } + } + + //JschConfigSessionFactory csf = new JschConfigSessionFactory(); + //SshSessionFactory.setInstance(csf); + // This works fine but the jsch provider doesn't support ed25519 keys; for that we need to get mina + // working, but that brings us into a whole other world of classloading and shading pain. Because + // Forge excludes everything under org.apache.*? Is that true? Ugh, FIXME. + // This all works perfectly fine with Fabric. :( + + + //https://stackoverflow.com/questions/67767455/setting-ssh-keys-to-use-with-jgit-with-ssh-from-apache-sshd + //https://www.eclipse.org/forums/index.php/t/1107487/ + //https://github.com/AzBuilder/terrakube/blob/67f992c84cb2f66ce17a2e2ab85796872429720b/api/src/main/java/org/terrakube/api/plugin/ssh/TerrakubeSshdSessionFactory.java#L6 + + //https://stackoverflow.com/questions/65566138/apache-mina-sshd-ssh-client-always-prints-eddsa-provider-not-supported + //If you don't fix this, then you will not be able to validate the host keys. My testing was not impacted because I was not validating the host keys yet. However, once deployed to production, I would have been impacted because host keys must be validated. + //https://dzone.com/articles/jgit-library-examples-in-java + + //SecurityUtils.setDefaultProviderChoice(new EdDSASecurityProviderRegistrar()); + + /** + String CLAZZ = EdDSASecurityProviderRegistrar.class.getNa this.lifecycleListener.onInitialize(); + me();//"net.pcal.fastback.relocated.org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"; + syslog().warn("!!!! "+CLAZZ); + try { + Class.forName(CLAZZ); + } catch (ClassNotFoundException e) { + syslog().error(new RuntimeException(e)); + } + System.setProperty("org.apache.sshd.security.registrars", CLAZZ); + **/ +// + + + /** + * + then we can try this, but i'm having a heck of a time getting the i2p provider to load >:( + if (false) { + File sshDir = new File(FS.DETECTED.userHome(), "/.ssh"); + + SshdSessionFactory sshSessionFactory = new SshdSessionFactoryBuilder() + .setPreferredAuthentications("publickey") + .setHomeDirectory(FS.DETECTED.userHome()) + .setSshDirectory(sshDir) + + .setServerKeyDatabase((h, s) -> new ServerKeyDatabase() { + + @Override public List lookup(String connectAddress, + InetSocketAddress remoteAddress, + Configuration config) { + return Collections.emptyList(); + } + + @Override public boolean accept(String connectAddress, + InetSocketAddress remoteAddress, + PublicKey serverKey, Configuration config, + CredentialsProvider provider) { + return true; + } + + }) + .build(new JGitKeyCache()); + SshSessionFactory.setInstance(sshSessionFactory); + } + **/ + /** + + //SshdSessionFactory factory = new SshdSessionFactory(new JGitKeyCache(), new DefaultProxyDataFactory()); + SshdSessionFactory factory = new SshdSessionFactory(); + try { + Runtime.getRuntime() + .addShutdownHook(new Thread(factory::close)); + } catch (IllegalStateException e) { + // ignore - the VM is already shutting down + } + SshSessionFactory.setInstance(factory); + + + } + **/ +} diff --git a/neoforge/src/main/resources/META-INF/accesstransformer.cfg b/neoforge/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 00000000..21873836 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1,7 @@ +public net.minecraft.server.MinecraftServer f_129744_ # storageSource +public net.minecraft.server.MinecraftServer session +public net.minecraft.world.level.storage.LevelStorage$Session directory + +# Note it's 'gui.screen' in my dev but 'gui.screens' at runtimein a launcher. +# Mapping difference I guess, not sure which is best to use for Forge. +public-f net.minecraft.client.gui.screens.Screen f_96539_ # title \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neoforge/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..7280224b --- /dev/null +++ b/neoforge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,29 @@ +modLoader = "javafml" +loaderVersion = "[49,)" +issueTrackerURL = "https://github.com/pcal43/fastback" +license = "GPL2" + +[[mods]] +modId = "fastback" +version = "0.17.4+1.20.6-prerelease" +displayName = "Fast Backups" +authors = "pcal" +description = ''' +Fast, incremental world backups powered by Git. +https://pcal43.github.io/fastback/ +''' +logoFile = "fastback-icon.png" + +[[dependencies.fastback]] +modId = "forge" +mandatory = true +versionRange = "[49,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.fastback]] +modId = "minecraft" +mandatory = true +versionRange = "1.20.6" +ordering = "NONE" +side = "CLIENT" diff --git a/neoforge/src/main/resources/fastback-icon.png b/neoforge/src/main/resources/fastback-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0bbcbfa55a4b5dde1a52d92013fe6c35f83f18c2 GIT binary patch literal 23557 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 Date: Wed, 20 Nov 2024 17:38:52 +0800 Subject: [PATCH 3/6] Reconfigure for NeoForge This configures the neoforge build, but does not address critical bugs that prevent the neoforge build from functioning. --- Makefile | 7 ++++- README.md | 2 +- build.gradle | 1 + common/build.gradle | 1 + etc/release.sh | 4 ++- gradle.properties | 5 ++++ neoforge/build.gradle | 16 +++++----- neoforge/gradle.properties | 2 +- .../mod/neoforge/ForgeClientProvider.java | 25 ++++++++-------- .../mod/neoforge/ForgeCommonProvider.java | 30 +++++++++---------- .../mod/neoforge/ForgeInitializer.java | 8 ++--- .../mod/neoforge/Slf4jSystemLogger.java | 2 +- .../pcal/fastback/mod/neoforge/SshHacks.java | 2 +- .../{mods.toml => neoforge.mods.toml} | 10 +++---- settings.gradle | 9 +++--- 15 files changed, 69 insertions(+), 55 deletions(-) rename neoforge/src/main/resources/META-INF/{mods.toml => neoforge.mods.toml} (77%) diff --git a/Makefile b/Makefile index 01f94485..801c16e3 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,14 @@ .PHONY: clean clean: - rm -rf build common/build fabric/build forge/build + rm -rf build common/build fabric/build neoforge/build #forge/build .PHONY: jar jar: ./gradlew remapJar ls -1 fabric/build/libs + ls -1 neoforge/build/libs # ls -1 forge/build/libs test: @@ -39,14 +40,18 @@ deps: .PHONY: inst inst: # rm -f ~/minecraft/instances/1.20.1-forge-dev/.minecraft/mods/fastback* + rm -f ~/minecraft/instances/1.20.1-neoforge-dev/.minecraft/mods/fastback* rm -f ~/minecraft/instances/1.20.1-fabric-dev/.minecraft/mods/fasback* cp fabric/build/libs/fastback*-fabric.jar ~/minecraft/instances/1.20.1-fabric-dev/.minecraft/mods/ + cp neoforge/build/libs/fastback*-neoforge.jar ~/minecraft/instances/1.20.1-neoforge-dev/.minecraft/mods/ # cp forge/build/libs/fastback*-forge.jar ~/minecraft/instances/1.20.1-forge-dev/.minecraft/mods/ .PHONY: tvf tvf: + jar -tvf neoforge/build/libs/fastback*-neoforge.jar # jar -tvf forge/build/libs/fastback*-forge.jar .PHONY: tvfs tvfs: + jar -tvf neoforge/build/libs/fastback*-shadow.jar # jar -tvf forge/build/libs/fastback*-shadow.jar diff --git a/README.md b/README.md index c2f1a73d..45ba2934 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ of disk space. ## Features +* **Now with NeoForge support!** * Incrementally backup just the changed files * Faster, smaller backups than zipping * Back up locally @@ -31,7 +32,6 @@ of disk space. * Support for restoring remote snapshots * Better management of remote snapshots * UI for managing backups from the title screen -* ~~Forge support (maybe)~~ ## Acknowledgements diff --git a/build.gradle b/build.gradle index b992b1fd..ec7ac4b1 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ subprojects { maven { url 'https://maven.fabricmc.net/' } maven { url 'https://maven.nucleoid.xyz' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://maven.neoforged.net/releases/' } } java { diff --git a/common/build.gradle b/common/build.gradle index 79f8757a..637840e5 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,6 +1,7 @@ architectury { common("fabric") + common("neoforge") // NOTE: Forge is no longer supported and the build has been disabled // See: https://github.com/pcal43/fastback/issues/333 //common("forge") diff --git a/etc/release.sh b/etc/release.sh index a66e8781..01efb8b8 100755 --- a/etc/release.sh +++ b/etc/release.sh @@ -50,6 +50,7 @@ fi # FABRIC_LIBS_DIR='fabric/build/libs' +NEOFORGE_LIBS_DIR='neoforge/build/libs' # NOTE: Forge is no longer supported and the build has been disabled # See: https://github.com/pcal43/fastback/issues/333 @@ -69,6 +70,7 @@ rm gradle.properties mv gradle.properties.temp gradle.properties rm -rf "${FABRIC_LIBS_DIR}" +rm -rf "${NEOFORGE_LIBS_DIR}" # NOTE: Forge is no longer supported and the build has been disabled # See: https://github.com/pcal43/fastback/issues/333 @@ -84,7 +86,7 @@ git push # Do github release # set -x -gh release create --generate-notes --title "${RELEASE_VERSION}" --notes "release ${RELEASE_VERSION}" ${RELEASE_VERSION} "${FABRIC_LIBS_DIR}"/* # "${FORGE_LIBS_DIR}"/* +gh release create --generate-notes --title "${RELEASE_VERSION}" --notes "release ${RELEASE_VERSION}" ${RELEASE_VERSION} "${FABRIC_LIBS_DIR}"/* "${NEOFORGE_LIBS_DIR}"/* # "${FORGE_LIBS_DIR}"/* set +x diff --git a/gradle.properties b/gradle.properties index 4836b4f5..c40eb9ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,6 +22,11 @@ fabric_version=0.106.1+1.21.3 # NOTE: Forge is no longer supported and the build has been disabled # See: https://github.com/pcal43/fastback/issues/333 +# NeoForge +# +# https://projects.neoforged.net/neoforged/neoforge +neo_version = 21.3.31-beta + # # common dependencies # diff --git a/neoforge/build.gradle b/neoforge/build.gradle index e7a6b8d9..91d65a84 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -6,7 +6,7 @@ plugins { architectury { platformSetupLoomIde() - forge() + neoForge() } shadowJar { @@ -20,20 +20,20 @@ configurations { shadowCommon compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common - developmentForge.extendsFrom common + developmentNeoForge.extendsFrom common } archivesBaseName = "${project.archives_base_name}" -version = "${project.mod_version}-forge" +version = "${project.mod_version}-neoforge" group = project.maven_group dependencies { - forge("net.minecraftforge:forge:${project.forge_version}") { transitive false } + neoForge("net.neoforged:neoforge:${project.neo_version}") { transitive false } // note to self: implementation, NOT include. include does implicit jarjar common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive = false } + shadowCommon(project(path: ":common", configuration: "transformProductionNeoForge")) { transitive = false } // FIXME? I still don't understand if I need to declare all of these things as forgeRuntimeLibrary. It sort // of seems like I do. @@ -93,7 +93,7 @@ sourcesJar { processResources { inputs.property "version", project.version - filesMatching("META-INF/mods.toml") { + filesMatching("META-INF/neoforge.mods.toml") { expand "version": project.version } } @@ -108,7 +108,7 @@ modrinth { uploadFile = remapJar changelog = "

https://github.com/pcal43/fastback/releases/tag/${project.mod_version}

" gameVersions = ["${project.minecraft_version}"] - loaders = ["forge"] + loaders = ["neoforge"] dependencies {} } @@ -124,7 +124,7 @@ curseforge { changelogType = "markdown" mod_version = "${project.version}" addGameVersion((String) project.minecraft_version) - addGameVersion "Forge" + addGameVersion "NeoForge" mainArtifact(remapJar) afterEvaluate { uploadTask.dependsOn("remapJar") diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index 82425854..7da18ea6 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -1 +1 @@ -loom.platform=forge +loom.platform=neoforge diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java index 5c9bed33..bc9d8410 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java @@ -1,11 +1,11 @@ -package net.pcal.fastback.mod.forge; - -import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; -import net.minecraftforge.client.event.ScreenEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +package net.pcal.fastback.mod.neoforge; + +import net.neoforged.neoforge.client.event.CustomizeGuiOverlayEvent; +import net.neoforged.neoforge.client.event.ScreenEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.ModLoadingContext; import net.pcal.fastback.logging.UserMessage; import static java.util.Objects.requireNonNull; @@ -39,10 +39,10 @@ final class ForgeClientProvider extends ForgeCommonProvider { private final Minecraft client; public ForgeClientProvider() { - final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + final IEventBus modEventBus = ModLoadingContext.get().getActiveContainer().getEventBus(); modEventBus.addListener(this::onClientStartupEvent); - MinecraftForge.EVENT_BUS.addListener(this::onGuiOverlayEvent); - MinecraftForge.EVENT_BUS.addListener(this::onScreenRenderEvent); + NeoForge.EVENT_BUS.addListener(this::onGuiOverlayEvent); + NeoForge.EVENT_BUS.addListener(this::onScreenRenderEvent); this.client = requireNonNull(Minecraft.getInstance(), "MinecraftClient.getInstance() returned null"); } @@ -91,7 +91,8 @@ public void setMessageScreenText(UserMessage userMessage) { final Component text = messageToText(userMessage); this.hudText = text; final Screen screen = client.screen; - if (screen != null) screen.title = text; + // TODO; fix this + //if (screen != null) screen.title = text; } @Override diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java index ba4921d0..57ef6a02 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java @@ -1,4 +1,4 @@ -package net.pcal.fastback.mod.forge; +package net.pcal.fastback.mod.neoforge; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -6,14 +6,14 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.server.ServerStartedEvent; -import net.minecraftforge.event.server.ServerStoppingEvent; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraft.world.level.storage.LevelResource; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent; +import net.neoforged.fml.ModLoadingContext; import net.pcal.fastback.logging.SystemLogger; import net.pcal.fastback.logging.UserMessage; import net.pcal.fastback.mod.LifecycleListener; @@ -44,11 +44,11 @@ class ForgeCommonProvider implements MinecraftProvider { private boolean isWorldSaveEnabled; ForgeCommonProvider() { - final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + final IEventBus modEventBus = ModLoadingContext.get().getActiveContainer().getEventBus(); modEventBus.addListener(this::onDedicatedServerStartupEvent); - MinecraftForge.EVENT_BUS.addListener(this::onServerStartupEvent); - MinecraftForge.EVENT_BUS.addListener(this::onServerStoppingEvent); - MinecraftForge.EVENT_BUS.addListener(this::onRegisterCommandEvent); + NeoForge.EVENT_BUS.addListener(this::onServerStartupEvent); + NeoForge.EVENT_BUS.addListener(this::onServerStoppingEvent); + NeoForge.EVENT_BUS.addListener(this::onRegisterCommandEvent); } @@ -145,9 +145,7 @@ void onAutoSaveComplete() { @Override public Path getWorldDirectory() { if (this.logicalServer == null) throw new IllegalStateException("minecraftServer is null"); - final LevelStorageSource.LevelStorageAccess session = logicalServer.storageSource; - Path out = session.getWorldDir().toAbsolutePath().normalize(); - return out; + return logicalServer.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize(); } @Override diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java index 40f18e54..df89eaac 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java @@ -1,7 +1,7 @@ -package net.pcal.fastback.mod.forge; +package net.pcal.fastback.mod.neoforge; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.loading.FMLEnvironment; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.loading.FMLEnvironment; import java.lang.reflect.InvocationTargetException; @@ -18,7 +18,7 @@ public ForgeInitializer() { new ForgeCommonProvider(); } else if (FMLEnvironment.dist.isClient()) { // Forge yells at us if we touch any client classes in a server. So, - Class.forName("net.pcal.fastback.mod.forge.ForgeClientProvider").getConstructor().newInstance(); + Class.forName("net.pcal.fastback.mod.neoforge.ForgeClientProvider").getConstructor().newInstance(); } else { throw new IllegalStateException("where am i? server or client?"); } diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java index 0212a6fe..b1831a40 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java @@ -1,4 +1,4 @@ -package net.pcal.fastback.mod.forge; +package net.pcal.fastback.mod.neoforge; import net.pcal.fastback.logging.SystemLogger; import org.slf4j.Logger; diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java index fcc95fc0..11b082b6 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java @@ -1,4 +1,4 @@ -package net.pcal.fastback.mod.forge; +package net.pcal.fastback.mod.neoforge; import org.eclipse.jgit.transport.SshSessionFactory; diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml similarity index 77% rename from neoforge/src/main/resources/META-INF/mods.toml rename to neoforge/src/main/resources/META-INF/neoforge.mods.toml index 7280224b..a4d329af 100644 --- a/neoforge/src/main/resources/META-INF/mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -1,11 +1,11 @@ modLoader = "javafml" -loaderVersion = "[49,)" +loaderVersion = "[5,)" issueTrackerURL = "https://github.com/pcal43/fastback" license = "GPL2" [[mods]] modId = "fastback" -version = "0.17.4+1.20.6-prerelease" +version = "0.19.2+1.21.3-prerelease" displayName = "Fast Backups" authors = "pcal" description = ''' @@ -15,15 +15,15 @@ https://pcal43.github.io/fastback/ logoFile = "fastback-icon.png" [[dependencies.fastback]] -modId = "forge" +modId = "neoforge" mandatory = true -versionRange = "[49,)" +versionRange = "[21.3.29-beta,)" ordering = "NONE" side = "CLIENT" [[dependencies.fastback]] modId = "minecraft" mandatory = true -versionRange = "1.20.6" +versionRange = "1.21.3" ordering = "NONE" side = "CLIENT" diff --git a/settings.gradle b/settings.gradle index d103e060..689bdd52 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,12 +11,12 @@ pluginManagement { } //mavenCentral() gradlePluginPortal() + maven { + name = 'NeoForged' + url = 'https://maven.neoforged.net/releases' + } // NOTE: Forge is no longer supported and the build has been disabled // See: https://github.com/pcal43/fastback/issues/333 - // maven { - // name = 'NeoForged' - // url = 'https://maven.neoforged.net/releases' - //} // architectury-loom still demands this for some reason maven { name = "Forge" @@ -29,6 +29,7 @@ rootProject.name = "fastback" include(":common") include(":fabric") +include(":neoforge") // NOTE: Forge is no longer supported and the build has been disabled // See: https://github.com/pcal43/fastback/issues/333 From 851213adf81a233f1c7e2d806e97c3f7feea65ff Mon Sep 17 00:00:00 2001 From: Stewart Borle Date: Mon, 18 Nov 2024 15:47:42 +0800 Subject: [PATCH 4/6] Resolve NeoForge equivalent of 'https://github.com/pcal43/fastback/issues/260 ed25519 keys are not support in fasback-forge' At-least I believe it does. Uploading was failing with a jgit error aboud failing to negotiate the algorithm. Uploading to gitea and github now work after this change, which indicates to me that this issue is resolved. However, org.apache.sshd goes missing in development environments, and the SshHacks fix does not work anymore because of the library change. I've made an issue in architectury-loom for this: https://github.com/architectury/architectury-loom/issues/248 In addition, the error thrown due to org.apache.sshd going missing is now being logged, but will not prevent fastback from loading. Allowing the development environment to still be used without support for ssh. --- .../pcal/fastback/logging/Log4jLogger.java | 5 + .../pcal/fastback/logging/SystemLogger.java | 2 + .../java/net/pcal/fastback/mod/ModImpl.java | 13 +- neoforge/build.gradle | 23 +++- .../mod/neoforge/ForgeCommonProvider.java | 1 - .../mod/neoforge/Slf4jSystemLogger.java | 5 + .../pcal/fastback/mod/neoforge/SshHacks.java | 113 ------------------ 7 files changed, 38 insertions(+), 124 deletions(-) delete mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java diff --git a/common/src/main/java/net/pcal/fastback/logging/Log4jLogger.java b/common/src/main/java/net/pcal/fastback/logging/Log4jLogger.java index 68636615..4abcd1b4 100644 --- a/common/src/main/java/net/pcal/fastback/logging/Log4jLogger.java +++ b/common/src/main/java/net/pcal/fastback/logging/Log4jLogger.java @@ -54,6 +54,11 @@ public void warn(String message) { this.log4j.warn(message); } + @Override + public void warn(String message, Throwable t) { + this.log4j.warn(message, t); + } + @Override public void info(String message) { this.log4j.info(message); diff --git a/common/src/main/java/net/pcal/fastback/logging/SystemLogger.java b/common/src/main/java/net/pcal/fastback/logging/SystemLogger.java index 8fcc60c8..4d7266f5 100644 --- a/common/src/main/java/net/pcal/fastback/logging/SystemLogger.java +++ b/common/src/main/java/net/pcal/fastback/logging/SystemLogger.java @@ -44,6 +44,8 @@ default void error(Throwable e) { void warn(String message); + void warn(String message, Throwable t); + void info(String message); void debug(String message); diff --git a/common/src/main/java/net/pcal/fastback/mod/ModImpl.java b/common/src/main/java/net/pcal/fastback/mod/ModImpl.java index 69b6df30..0082ca53 100644 --- a/common/src/main/java/net/pcal/fastback/mod/ModImpl.java +++ b/common/src/main/java/net/pcal/fastback/mod/ModImpl.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Map; +import java.util.ServiceConfigurationError; import static java.nio.file.Files.createTempDirectory; import static java.util.Objects.requireNonNull; @@ -162,10 +163,14 @@ public void onInitialize() { syslog().info("git-lfs is installed: " + gitLfsVersion); } } - if (SshSessionFactory.getInstance() == null) { - syslog().warn("An ssh provider was not initialized for jgit. Operations on a remote repo over ssh will fail."); - } else { - syslog().info("SshSessionFactory: " + SshSessionFactory.getInstance().toString()); + try { + if (SshSessionFactory.getInstance() == null) { + syslog().warn("An ssh provider was not initialized for jgit. Operations on a remote repo over ssh will fail."); + } else { + syslog().info("SshSessionFactory: " + SshSessionFactory.getInstance().toString()); + } + } catch (Exception | ServiceConfigurationError e) { + syslog().warn("An ssh provider was not initialized for jgit. Operations on a remote repo over ssh will fail.", e); } syslog().debug("onInitialize complete"); } diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 91d65a84..e4196efd 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -41,14 +41,25 @@ dependencies { forgeRuntimeLibrary implementation("org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}") { transitive = false } shadowCommon("org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}") { transitive = false } - forgeRuntimeLibrary runtimeOnly("org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${project.jgit_version}") { transitive = false; } - shadowCommon("org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:${project.jgit_version}") { transitive = false } - - forgeRuntimeLibrary runtimeOnly('com.jcraft:jsch:0.1.55') - shadowCommon('com.jcraft:jsch:0.1.55') - forgeRuntimeLibrary runtimeOnly("com.googlecode.javaewah:JavaEWAH:${project.JavaEWAH_version}") { transitive = false } shadowCommon("com.googlecode.javaewah:JavaEWAH:${project.JavaEWAH_version}") { transitive = false } + + // https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit.ssh.apache + forgeRuntimeLibrary runtimeOnly("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:${project.jgit_version}") { transitive = false } + shadowCommon("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:${project.jgit_version}") { transitive = false } + + // https://mvnrepository.com/artifact/org.apache.sshd/sshd-core + forgeRuntimeLibrary runtimeOnly("org.apache.sshd:sshd-core:${project.apache_sshd_version}") { transitive = false } + shadowCommon("org.apache.sshd:sshd-core:${project.apache_sshd_version}") { transitive = false } + + // https://mvnrepository.com/artifact/org.apache.sshd/sshd-common + forgeRuntimeLibrary runtimeOnly("org.apache.sshd:sshd-common:${project.apache_sshd_version}") { transitive = false } + shadowCommon("org.apache.sshd:sshd-common:${project.apache_sshd_version}") { transitive = false } + + // this enables ed25519 support in apache_sshd + // https://github.com/apache/mina-sshd/blob/dfa109b7b535d64e8ee395ddd0419e7696fb24ee/docs/dependencies.md + forgeRuntimeLibrary runtimeOnly("net.i2p.crypto:eddsa:${project.eddsa_version}") { transitive = false } + shadowCommon("net.i2p.crypto:eddsa:${project.eddsa_version}") { transitive = false } } processResources { diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java index 57ef6a02..8d827c6b 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java @@ -97,7 +97,6 @@ void onInitialize() { this.lifecycleListener = MinecraftProvider.register(this); syslog().debug("registered backup command"); this.lifecycleListener.onInitialize(); - SshHacks.ensureSshSessionFactoryIsAvailable(); syslog().info("Fastback initialized"); syslog().warn("------------------------------------------------------------------------------------"); syslog().warn("Thanks for trying the new Forge version of Fastback. For help, go to:"); diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java index b1831a40..f388be5b 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java @@ -38,6 +38,11 @@ public void warn(String message) { this.slf4j.warn(message); } + @Override + public void warn(String message, Throwable t) { + this.slf4j.warn(message, t); + } + @Override public void info(String message) { this.slf4j.info(message); diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java deleted file mode 100644 index 11b082b6..00000000 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/SshHacks.java +++ /dev/null @@ -1,113 +0,0 @@ -package net.pcal.fastback.mod.neoforge; - -import org.eclipse.jgit.transport.SshSessionFactory; - -import java.lang.reflect.InvocationTargetException; - -import static net.pcal.fastback.logging.SystemLogger.syslog; - -public class SshHacks { - - /** - * This is necessary because JGit looks for it's SshSessionFactory with java.util.ServiceLoader, and that - * just doesn't seem to be something that Forge is willing to accommodate. - */ - public static void ensureSshSessionFactoryIsAvailable() { - try { - if (SshSessionFactory.getInstance() == null) { - try { - final String clazz = "org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory"; - SshSessionFactory.setInstance((SshSessionFactory) Class.forName(clazz).getConstructor().newInstance()); - // AFAICT this only happens in Intellij. Something about shadowJar and relocate isn't working in dev environments. - // Seems fine in the launcher. - syslog().warn("A SshSessionFactory was not located via java services; a " + clazz + " has been installed manually. This is probably ok."); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | - NoSuchMethodException | InvocationTargetException ohwell) { - syslog().error("Unable to manually set SshSessionFactory. SSH connections will probably not work.", ohwell); - } - } - // - } catch (Error err) { - syslog().error("WAT", err); - } - } - - //JschConfigSessionFactory csf = new JschConfigSessionFactory(); - //SshSessionFactory.setInstance(csf); - // This works fine but the jsch provider doesn't support ed25519 keys; for that we need to get mina - // working, but that brings us into a whole other world of classloading and shading pain. Because - // Forge excludes everything under org.apache.*? Is that true? Ugh, FIXME. - // This all works perfectly fine with Fabric. :( - - - //https://stackoverflow.com/questions/67767455/setting-ssh-keys-to-use-with-jgit-with-ssh-from-apache-sshd - //https://www.eclipse.org/forums/index.php/t/1107487/ - //https://github.com/AzBuilder/terrakube/blob/67f992c84cb2f66ce17a2e2ab85796872429720b/api/src/main/java/org/terrakube/api/plugin/ssh/TerrakubeSshdSessionFactory.java#L6 - - //https://stackoverflow.com/questions/65566138/apache-mina-sshd-ssh-client-always-prints-eddsa-provider-not-supported - //If you don't fix this, then you will not be able to validate the host keys. My testing was not impacted because I was not validating the host keys yet. However, once deployed to production, I would have been impacted because host keys must be validated. - //https://dzone.com/articles/jgit-library-examples-in-java - - //SecurityUtils.setDefaultProviderChoice(new EdDSASecurityProviderRegistrar()); - - /** - String CLAZZ = EdDSASecurityProviderRegistrar.class.getNa this.lifecycleListener.onInitialize(); - me();//"net.pcal.fastback.relocated.org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"; - syslog().warn("!!!! "+CLAZZ); - try { - Class.forName(CLAZZ); - } catch (ClassNotFoundException e) { - syslog().error(new RuntimeException(e)); - } - System.setProperty("org.apache.sshd.security.registrars", CLAZZ); - **/ -// - - - /** - * - then we can try this, but i'm having a heck of a time getting the i2p provider to load >:( - if (false) { - File sshDir = new File(FS.DETECTED.userHome(), "/.ssh"); - - SshdSessionFactory sshSessionFactory = new SshdSessionFactoryBuilder() - .setPreferredAuthentications("publickey") - .setHomeDirectory(FS.DETECTED.userHome()) - .setSshDirectory(sshDir) - - .setServerKeyDatabase((h, s) -> new ServerKeyDatabase() { - - @Override public List lookup(String connectAddress, - InetSocketAddress remoteAddress, - Configuration config) { - return Collections.emptyList(); - } - - @Override public boolean accept(String connectAddress, - InetSocketAddress remoteAddress, - PublicKey serverKey, Configuration config, - CredentialsProvider provider) { - return true; - } - - }) - .build(new JGitKeyCache()); - SshSessionFactory.setInstance(sshSessionFactory); - } - **/ - /** - - //SshdSessionFactory factory = new SshdSessionFactory(new JGitKeyCache(), new DefaultProxyDataFactory()); - SshdSessionFactory factory = new SshdSessionFactory(); - try { - Runtime.getRuntime() - .addShutdownHook(new Thread(factory::close)); - } catch (IllegalStateException e) { - // ignore - the VM is already shutting down - } - SshSessionFactory.setInstance(factory); - - - } - **/ -} From cd6cbc201bf253c0a622a39e6d54191f66695ea1 Mon Sep 17 00:00:00 2001 From: Stewart Borle Date: Mon, 18 Nov 2024 17:08:40 +0800 Subject: [PATCH 5/6] Resolve NeoForge equivilent of 'https://github.com/pcal43/fastback/issues/261 autoback (backup after autosaves) is not currently supported on Forge' Mixins are used in much the same way they are in the fabric version. --- .../mod/neoforge/ForgeCommonProvider.java | 31 ++++--- .../fastback/mod/neoforge/MixinGateway.java | 44 ++++++++++ .../neoforge/mixins/MinecraftServerMixin.java | 84 +++++++++++++++++++ .../resources/META-INF/neoforge.mods.toml | 3 + .../src/main/resources/fastback.mixins.json | 12 +++ 5 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/MixinGateway.java create mode 100644 neoforge/src/main/java/net/pcal/fastback/mod/neoforge/mixins/MinecraftServerMixin.java create mode 100644 neoforge/src/main/resources/fastback.mixins.json diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java index 8d827c6b..1aa8408a 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java @@ -9,6 +9,7 @@ import net.minecraft.world.level.storage.LevelResource; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.bus.api.IEventBus; @@ -35,13 +36,13 @@ * @author pcal * @since 0.16.0 */ -class ForgeCommonProvider implements MinecraftProvider { +class ForgeCommonProvider implements MinecraftProvider, MixinGateway { static final String MOD_ID = "fastback"; private MinecraftServer logicalServer; private LifecycleListener lifecycleListener = null; private Runnable autoSaveListener; - private boolean isWorldSaveEnabled; + private boolean isWorldSaveEnabled = true; ForgeCommonProvider() { final IEventBus modEventBus = ModLoadingContext.get().getActiveContainer().getEventBus(); @@ -76,15 +77,6 @@ private void onRegisterCommandEvent(RegisterCommandsEvent event) { commandDispatcher.register(backupCommand); } - /** - TODO This one isn't it. We need to hear about it when an autosaves (and only autosaves) are completed. - Might have to delve into Forge mixins to do this. - private void onLevelSaveEvent(LevelEvent.Save event) { - provider.onAutoSaveComplete(); - } - **/ - - // ====================================================================== // Protected @@ -104,6 +96,7 @@ void onInitialize() { syslog().warn("Please note that this is an alpha release. A list of known issues is available here:"); syslog().warn("https://github.com/pcal43/fastback/issues?q=is%3Aissue+is%3Aopen+label%3Aforge"); syslog().warn("------------------------------------------------------------------------------------"); + MixinGateway.Singleton.register(this); } @@ -135,25 +128,32 @@ public String getModVersion() { return "0.15.3+1.20.1-alpha"; //FIXME } - //FIXME!! - void onAutoSaveComplete() { - syslog().debug("onAutoSaveComplete"); + @Override + public void autoSaveCompleted() { + syslog().debug("autoSaveCompleted"); this.autoSaveListener.run(); } @Override public Path getWorldDirectory() { - if (this.logicalServer == null) throw new IllegalStateException("minecraftServer is null"); + if (logicalServer == null) throw new IllegalStateException("minecraftServer is null"); return logicalServer.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize(); } @Override public void setWorldSaveEnabled(boolean enabled) { + isWorldSaveEnabled = enabled; + if (logicalServer == null) throw new IllegalStateException("minecraftServer is null"); for (ServerLevel world : logicalServer.getAllLevels()) { world.noSave = !enabled; } } + @Override + public boolean isWorldSaveEnabled() { + return isWorldSaveEnabled; + } + @Override public void saveWorld() { if (this.logicalServer == null) throw new IllegalStateException(); @@ -215,5 +215,4 @@ public Collection getModsBackupPaths() { **/ return out; } - } diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/MixinGateway.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/MixinGateway.java new file mode 100644 index 00000000..2929e0f7 --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/MixinGateway.java @@ -0,0 +1,44 @@ +/* + * FastBack - Fast, incremental Minecraft backups powered by Git. + * Copyright (C) 2022 pcal.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + */ + +package net.pcal.fastback.mod.neoforge; + +/** + * Singleton 'gateway' that mixin code goes through to call back into the mod. + * + * @author pcal + * @since 0.13.1 + */ +public interface MixinGateway { + + static MixinGateway get() { + return Singleton.INSTANCE; + } + + boolean isWorldSaveEnabled(); + + void autoSaveCompleted(); + + class Singleton { + private static MixinGateway INSTANCE = null; + + public static void register(MixinGateway gateway) { + Singleton.INSTANCE = gateway; + } + } +} diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/mixins/MinecraftServerMixin.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/mixins/MinecraftServerMixin.java new file mode 100644 index 00000000..e1d6d83a --- /dev/null +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/mixins/MinecraftServerMixin.java @@ -0,0 +1,84 @@ +/* + * FastBack - Fast, incremental Minecraft backups powered by Git. + * Copyright (C) 2022 pcal.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see . + */ +package net.pcal.fastback.mod.neoforge.mixins; + +import net.minecraft.server.MinecraftServer; +import net.pcal.fastback.mod.neoforge.MixinGateway; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import static net.pcal.fastback.logging.SystemLogger.syslog; + +/** + * Allows us to disable vanilla saving during 'git add' to avoid coherency problems in the backup snapshots. Also + * sends notifications when autosaving completes so we can follow them with automated backups. + * + * @author pcal + * @since 0.0.1 + */ +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin { + + /** + * Intercept the call to saveAll that triggers on autosave, pass it through and then send out notification that + * the autosave is done. + */ + @Redirect(method = "autoSave()V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;saveEverything(ZZZ)Z")) + public boolean fastback_saveAll(MinecraftServer instance, boolean suppressLogs, boolean flush, boolean force) { + boolean result = instance.saveEverything(suppressLogs, flush, force); + MixinGateway.get().autoSaveCompleted(); + return result; + } + + /** + * Intercept save so we can hard-disable saving during critical parts of the backup. + */ + @Inject(at = @At("HEAD"), method = "saveAllChunks(ZZZ)Z", cancellable = true) + public void fastback_save(boolean suppressLogs, boolean flush, boolean force, CallbackInfoReturnable ci) { + synchronized (this) { + if (MixinGateway.get().isWorldSaveEnabled()) { + syslog().debug("world saves are enabled, doing requested save"); + } else { + syslog().warn("Skipping requested save because a backup is in progress."); + ci.setReturnValue(false); + ci.cancel(); + } + } + } + + /** + * Intercept saveAll so we can hard-disable saving during critical parts of the backup. + */ + @Inject(at = @At("HEAD"), method = "saveEverything(ZZZ)Z", cancellable = true) + public void fastback_saveAll(boolean suppressLogs, boolean flush, boolean force, CallbackInfoReturnable ci) { + synchronized (this) { + if (MixinGateway.get().isWorldSaveEnabled()) { + syslog().debug("world saves are enabled, doing requested saveAll"); + //TODO should call save here to ensure all synced? + } else { + syslog().warn("Skipping requested saveAll because a backup is in progress."); + ci.setReturnValue(false); + ci.cancel(); + } + } + } +} diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index a4d329af..24070ba1 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -27,3 +27,6 @@ mandatory = true versionRange = "1.21.3" ordering = "NONE" side = "CLIENT" + +[[mixins]] +config="fastback.mixins.json" diff --git a/neoforge/src/main/resources/fastback.mixins.json b/neoforge/src/main/resources/fastback.mixins.json new file mode 100644 index 00000000..cc2278b9 --- /dev/null +++ b/neoforge/src/main/resources/fastback.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.pcal.fastback.mod.neoforge.mixins", + "compatibilityLevel": "JAVA_16", + "mixins": [ + "MinecraftServerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} From ba95909d3575b6b816f571973b87907c0a00c730 Mon Sep 17 00:00:00 2001 From: Stewart Borle Date: Wed, 20 Nov 2024 00:17:11 +0800 Subject: [PATCH 6/6] Resolve some NeoForge FIXMEs ModLoadingContext can be supplied to the Mod's constructor, so using it. --- .../mod/neoforge/ForgeClientProvider.java | 9 +++--- .../mod/neoforge/ForgeCommonProvider.java | 29 +++++++++---------- .../mod/neoforge/ForgeInitializer.java | 10 ++++--- .../mod/neoforge/Slf4jSystemLogger.java | 4 +-- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java index bc9d8410..3057e4e0 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeClientProvider.java @@ -1,5 +1,6 @@ package net.pcal.fastback.mod.neoforge; +import net.neoforged.fml.ModContainer; import net.neoforged.neoforge.client.event.CustomizeGuiOverlayEvent; import net.neoforged.neoforge.client.event.ScreenEvent; import net.neoforged.neoforge.common.NeoForge; @@ -38,9 +39,9 @@ final class ForgeClientProvider extends ForgeCommonProvider { private long hudTextTime; private final Minecraft client; - public ForgeClientProvider() { - final IEventBus modEventBus = ModLoadingContext.get().getActiveContainer().getEventBus(); - modEventBus.addListener(this::onClientStartupEvent); + public ForgeClientProvider(ModContainer container) { + super(container); + container.getEventBus().addListener(this::onClientStartupEvent); NeoForge.EVENT_BUS.addListener(this::onGuiOverlayEvent); NeoForge.EVENT_BUS.addListener(this::onScreenRenderEvent); this.client = requireNonNull(Minecraft.getInstance(), "MinecraftClient.getInstance() returned null"); @@ -53,7 +54,7 @@ private void onClientStartupEvent(FMLClientSetupEvent event) { this.onInitialize(); } - private void onGuiOverlayEvent(CustomizeGuiOverlayEvent event) { + private void onGuiOverlayEvent(CustomizeGuiOverlayEvent.Chat event) { this.renderOverlayText(event.getGuiGraphics()); } diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java index 1aa8408a..24c18e16 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeCommonProvider.java @@ -7,9 +7,10 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.storage.LevelResource; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.loading.FMLPaths; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.RegisterCommandsEvent; -import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.bus.api.IEventBus; @@ -40,13 +41,15 @@ class ForgeCommonProvider implements MinecraftProvider, MixinGateway { static final String MOD_ID = "fastback"; private MinecraftServer logicalServer; + private ModContainer container; private LifecycleListener lifecycleListener = null; private Runnable autoSaveListener; private boolean isWorldSaveEnabled = true; - ForgeCommonProvider() { - final IEventBus modEventBus = ModLoadingContext.get().getActiveContainer().getEventBus(); - modEventBus.addListener(this::onDedicatedServerStartupEvent); + ForgeCommonProvider(ModContainer container) { + this.container = container; + + container.getEventBus().addListener(this::onDedicatedServerStartupEvent); NeoForge.EVENT_BUS.addListener(this::onServerStartupEvent); NeoForge.EVENT_BUS.addListener(this::onServerStoppingEvent); NeoForge.EVENT_BUS.addListener(this::onRegisterCommandEvent); @@ -89,9 +92,9 @@ void onInitialize() { this.lifecycleListener = MinecraftProvider.register(this); syslog().debug("registered backup command"); this.lifecycleListener.onInitialize(); - syslog().info("Fastback initialized"); + syslog().info("Fastback " + getModVersion() + " initialized"); syslog().warn("------------------------------------------------------------------------------------"); - syslog().warn("Thanks for trying the new Forge version of Fastback. For help, go to:"); + syslog().warn("Thanks for trying the new NeoForge version of Fastback. For help, go to:"); syslog().warn("https://pcal43.github.io/fastback/"); syslog().warn("Please note that this is an alpha release. A list of known issues is available here:"); syslog().warn("https://github.com/pcal43/fastback/issues?q=is%3Aissue+is%3Aopen+label%3Aforge"); @@ -125,7 +128,7 @@ void renderOverlayText(GuiGraphics drawContext) { @Override public String getModVersion() { - return "0.15.3+1.20.1-alpha"; //FIXME + return container.getModInfo().getVersion().toString(); } @Override @@ -205,14 +208,10 @@ public void addBackupProperties(Map props) { @Override public Collection getModsBackupPaths() { final List out = new ArrayList<>(); - /** - final FabricLoader fl = FabricLoader.getInstance(); - final Path gameDir = fl.getGameDir(); - out.add(gameDir.resolve("options.txt´")); - out.add(gameDir.resolve("mods")); - out.add(gameDir.resolve("config")); - out.add(gameDir.resolve("resourcepacks")); - **/ + out.add(FMLPaths.GAMEDIR.get().resolve("options.txt")); + out.add(FMLPaths.MODSDIR.get()); + out.add(FMLPaths.CONFIGDIR.get()); + out.add(FMLPaths.GAMEDIR.get().resolve("resourcepacks")); return out; } } diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java index df89eaac..34903d06 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/ForgeInitializer.java @@ -1,5 +1,7 @@ package net.pcal.fastback.mod.neoforge; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.ModLoadingContext; import net.neoforged.fml.common.Mod; import net.neoforged.fml.loading.FMLEnvironment; @@ -12,13 +14,13 @@ @Mod("fastback") final public class ForgeInitializer { - public ForgeInitializer() { + public ForgeInitializer(ModContainer container) { try { if (FMLEnvironment.dist.isDedicatedServer()) { - new ForgeCommonProvider(); + new ForgeCommonProvider(container); } else if (FMLEnvironment.dist.isClient()) { // Forge yells at us if we touch any client classes in a server. So, - Class.forName("net.pcal.fastback.mod.neoforge.ForgeClientProvider").getConstructor().newInstance(); + Class.forName("net.pcal.fastback.mod.neoforge.ForgeClientProvider").getConstructor(ModContainer.class).newInstance(container); } else { throw new IllegalStateException("where am i? server or client?"); } @@ -27,4 +29,4 @@ public ForgeInitializer() { throw new RuntimeException(e); } } -} \ No newline at end of file +} diff --git a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java index f388be5b..080d05a7 100644 --- a/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java +++ b/neoforge/src/main/java/net/pcal/fastback/mod/neoforge/Slf4jSystemLogger.java @@ -39,9 +39,7 @@ public void warn(String message) { } @Override - public void warn(String message, Throwable t) { - this.slf4j.warn(message, t); - } + public void warn(String message, Throwable t) {this.slf4j.warn(message, t);} @Override public void info(String message) {