diff --git a/atlas/root/distribution.json b/atlas/root/distribution.json index 97a53fd..04b5d37 100644 --- a/atlas/root/distribution.json +++ b/atlas/root/distribution.json @@ -407,13 +407,13 @@ } }, { - "id": "fr.surviepote:nosingleplayer:1.1.2@jar", + "id": "fr.surviepote:nosingleplayer:1.1.3@jar", "name": "NoSingleplayer (SurviePote)", "type": "FabricMod", "artifact": { - "size": 2292471, - "url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.2.jar", - "MD5": "2ba50ec99adb670cdb34fe41b0e3d7dd" + "size": 2294172, + "url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.3.jar", + "MD5": "577674ec296cbc75ac9da009b8d6532e" } }, { diff --git a/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.2.jar b/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.3.jar similarity index 99% rename from atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.2.jar rename to atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.3.jar index f3a2068..d534fae 100644 Binary files a/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.2.jar and b/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.3.jar differ diff --git a/mods/nosingleplayer/README.md b/mods/nosingleplayer/README.md index 4dd0ca2..dc704e0 100644 --- a/mods/nosingleplayer/README.md +++ b/mods/nosingleplayer/README.md @@ -1,6 +1,11 @@ # nosingleplayer (mod maison SurviePote) -Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`). +Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`) et adapte l'UI +au serveur unique : +- Menu titre : bouton **Rejoindre SurviePote** (connexion directe à `mcpv.ihorizon.org:25565`, + sans transfert) ; Solo, Multi, Realms, réseau social et icônes bas retirés. +- Menu Échap : le bouton de déconnexion devient **Quitter SurviePote** et renvoie à l'écran + d'accueil du jeu (et non la liste des serveurs). - MC 26.3, Fabric Loader 0.20.0, Java 25, `environment: client`. - Particularité 26.x : Mojang ne publie plus de mappings (ni Yarn ni `client_mappings`) et le jeu n'est plus obfusqué → **pas de Loom**, compilation directe contre le jar client Mojang @@ -8,4 +13,4 @@ Désactive le bouton **Solo** du menu principal (Mixin sur `TitleScreen.init`). pas de refmap nécessaire. Build : `cd mods/nosingleplayer && ./gradlew build` (dans nix-shell racine pour Java 25). -Jar : `build/libs/nosingleplayer-1.0.0.jar` → copié dans `atlas/.../fabricmods/required/`. +Jar : `build/libs/nosingleplayer-1.1.3.jar` → copié dans `atlas/.../fabricmods/required/`. diff --git a/mods/nosingleplayer/build.gradle b/mods/nosingleplayer/build.gradle index 72f2881..d73948b 100644 --- a/mods/nosingleplayer/build.gradle +++ b/mods/nosingleplayer/build.gradle @@ -64,3 +64,97 @@ processResources { java { withSourcesJar() } + +// --------------------------------------------------------------------------- +// Dev : lance le vrai client MC 26.3 + nosingleplayer (build sans loom). +// Réutilise l'installation commune du launcher SurviePote (~/.surviepote/common : +// versions, libraries, assets — remplie au premier lancement). Le mod est copié +// dans runClient/game/mods/ (Knot le découvre comme un mod Fabric classique). +// Usage : ./gradlew runClient +// --------------------------------------------------------------------------- +def survieCommon = new File(System.getProperty('user.home'), '.surviepote/common') +def mcVersion = project.minecraft_version +def loaderVersion = project.loader_version + +tasks.register('extractClientNatives', Copy) { + def versionJson = new groovy.json.JsonSlurper().parse( + new File(survieCommon, "versions/${mcVersion}/${mcVersion}.json")) + from(versionJson.libraries.findAll { lib -> + lib.name.contains(':natives-linux') && (lib.rules == null || lib.rules.any { it.action == 'allow' && (it.os == null || it.os.name == 'linux') }) + }.collect { lib -> + zipTree(new File(survieCommon, "libraries/${lib.downloads.artifact.path}")) + }) + into(layout.buildDirectory.dir('runClient/natives')) + include('**/*.so') + eachFile { f -> f.path = f.name } +} + +tasks.register('runClient', JavaExec) { + dependsOn('extractClientNatives', 'jar') + group = 'fabric' + description = 'Lance le client Minecraft avec le mod (dev visuel, sans loom).' + + def versionJson = new groovy.json.JsonSlurper().parse( + new File(survieCommon, "versions/${mcVersion}/${mcVersion}.json")) + def fabricJson = new groovy.json.JsonSlurper().parse( + new File(survieCommon, "versions/${mcVersion}-fabric-${loaderVersion}/${mcVersion}-fabric-${loaderVersion}.json")) + def mavenToPath = { String coord -> + def parts = coord.split(':') + return "${parts[0].replace('.', '/')}/${parts[1]}/${parts[2]}/${parts[1]}-${parts[2]}.jar" + } + + doFirst { + def modsDir = layout.buildDirectory.dir('runClient/game/mods').get().asFile + delete(modsDir) + modsDir.mkdirs() + copy { + from(tasks.named('jar')) + into(modsDir) + } + } + + // NixOS : dlopen ne voit ni libGL ni libvulkan sans LD_LIBRARY_PATH (cf launcher). + def nixStore = new File('/nix/store') + def glDirs = [] + ['/run/opengl-driver/lib', '/run/current-system/sw/lib'].each { d -> + if (new File(d).exists()) glDirs << d + } + if (nixStore.exists()) { + nixStore.eachDir { e -> + def n = e.name + if (n.contains('libglvnd') || n.contains('vulkan-loader') || n.contains('alsa-lib') + || n.contains('pulseaudio') || (n.contains('pipewire') && !n.contains('python'))) { + def lib = new File(e, 'lib') + if (lib.exists()) glDirs << lib.absolutePath + } + } + } + def prevLd = System.getenv('LD_LIBRARY_PATH') + environment('LD_LIBRARY_PATH', (prevLd ? prevLd + ':' : '') + glDirs.join(':')) + if (!System.getenv('SDL_VIDEODRIVER') && (System.getenv('WAYLAND_DISPLAY') || new File('/run/user/1000/wayland-1').exists())) { + environment('SDL_VIDEODRIVER', 'wayland') + } + + javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(25) } + classpath = files( + new File(survieCommon, "versions/${mcVersion}/${mcVersion}.jar"), + versionJson.libraries.findAll { lib -> + !(lib.name.contains(':natives-')) && (lib.rules == null || lib.rules.any { it.action == 'allow' && (it.os == null || it.os.name == 'linux') }) + }.collect { new File(survieCommon, "libraries/${it.downloads.artifact.path}") }, + fabricJson.libraries.collect { new File(survieCommon, "libraries/${mavenToPath(it.name)}") } + ) + mainClass = 'net.fabricmc.loader.impl.launch.knot.KnotClient' + args = [ + '--gameDir', layout.buildDirectory.dir('runClient/game').get().asFile.absolutePath, + '--assetsDir', new File(survieCommon, 'assets').absolutePath, + '--assetIndex', versionJson.assetIndex.id, + '--version', "${project.archives_base_name}-dev", + '--username', 'Dev', '--uuid', '00000000-0000-0000-0000-000000000000', + '--accessToken', '0', '--versionType', 'release' + ] + jvmArgs = [ + "-Djava.library.path=${layout.buildDirectory.dir('runClient/natives').get().asFile.absolutePath}", + '-Xmx4G' + ] + systemProperties['org.lwjgl.util.Debug'] = 'false' +} diff --git a/mods/nosingleplayer/gradle.properties b/mods/nosingleplayer/gradle.properties index e7c3c26..767c77e 100644 --- a/mods/nosingleplayer/gradle.properties +++ b/mods/nosingleplayer/gradle.properties @@ -5,7 +5,7 @@ loader_version=0.20.0 loom_version=1.19.1 # Mod Properties -mod_version=1.1.2 +mod_version=1.1.3 maven_group=fr.surviepote archives_base_name=nosingleplayer diff --git a/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/PauseScreenMixin.java b/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/PauseScreenMixin.java new file mode 100644 index 0000000..5026de9 --- /dev/null +++ b/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/PauseScreenMixin.java @@ -0,0 +1,45 @@ +package fr.surviepote.nosingleplayer.mixin; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.PauseScreen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(PauseScreen.class) +public abstract class PauseScreenMixin { + + @Unique + private static final Component SURVIEPOTE_QUIT = Component.literal("Quitter SurviePote"); + + // Quitte le monde comme le bouton vanilla, mais renvoie vers l'écran d'accueil + // (au lieu de la liste des serveurs pour un serveur distant). + @Unique + private static void surviepote$quitToTitle(Button button) { + button.active = false; + Minecraft minecraft = Minecraft.getInstance(); + // Conserve la gestion vanilla du brouillon de signalement, puis redirige. + minecraft.getReportingContext().draftReportHandled(minecraft, minecraft.gui.screen(), () -> { + minecraft.disconnectFromWorld(ClientLevel.DEFAULT_QUIT_MESSAGE); + minecraft.setScreenAndShow(new TitleScreen()); + }, true); + } + + @Redirect(method = "createPauseMenu", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/gui/components/Button;builder(Lnet/minecraft/network/chat/Component;Lnet/minecraft/client/gui/components/Button$OnPress;)Lnet/minecraft/client/gui/components/Button$Builder;")) + private static Button.Builder surviepote$relabelDisconnect(Component message, Button.OnPress onPress) { + // createPauseMenu ne crée que deux boutons via Button.builder : "Retour au jeu" + // et "Déconnexion". On ne remplace que ce dernier. + if (message.getString().equals(CommonComponents.GUI_DISCONNECT.getString())) { + return Button.builder(SURVIEPOTE_QUIT, PauseScreenMixin::surviepote$quitToTitle); + } + return Button.builder(message, onPress); + } +} diff --git a/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/TitleScreenMixin.java b/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/TitleScreenMixin.java index 4edcc96..989f442 100644 --- a/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/TitleScreenMixin.java +++ b/mods/nosingleplayer/src/main/java/fr/surviepote/nosingleplayer/mixin/TitleScreenMixin.java @@ -2,7 +2,6 @@ package fr.surviepote.nosingleplayer.mixin; import java.util.ArrayList; import java.util.List; -import java.util.Map; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; @@ -13,7 +12,6 @@ import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.ConnectScreen; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.multiplayer.ServerData; -import net.minecraft.client.multiplayer.TransferState; import net.minecraft.client.multiplayer.resolver.ServerAddress; import net.minecraft.network.chat.Component; import fr.surviepote.nosingleplayer.NoSingleplayer; @@ -86,8 +84,9 @@ public abstract class TitleScreenMixin { Button join = Button.builder(Component.literal("Rejoindre SurviePote"), pressed -> { ServerData data = new ServerData("SurviePote", SERVER_HOST, ServerData.Type.OTHER); - ConnectScreen.startConnecting(self, minecraft, ServerAddress.parseString(SERVER_HOST), data, false, - new TransferState(Map.of(), Map.of(), false)); + // TransferState null = connexion normale. En passer un déclenche une connexion + // de type "transfert", que le serveur refuse ("This server does not accept transfers"). + ConnectScreen.startConnecting(self, minecraft, ServerAddress.parseString(SERVER_HOST), data, false, null); }).bounds(joinX, joinY, 200, 20).build(); ((ScreenAccessor) self).surviepote$getRenderables().add(join); @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/mods/nosingleplayer/src/main/resources/nosingleplayer.mixins.json b/mods/nosingleplayer/src/main/resources/nosingleplayer.mixins.json index 81e2174..36c4f74 100644 --- a/mods/nosingleplayer/src/main/resources/nosingleplayer.mixins.json +++ b/mods/nosingleplayer/src/main/resources/nosingleplayer.mixins.json @@ -7,6 +7,7 @@ "TitleScreenRenderMixin", "TitleScreenTextMixin", "RealmsNotificationsMixin", + "PauseScreenMixin", "ScreenAccessor" ], "injectors": { diff --git a/sync-mods.sh b/sync-mods.sh index 7aa6595..7588888 100755 --- a/sync-mods.sh +++ b/sync-mods.sh @@ -54,10 +54,11 @@ cp -u "$HOME/.local/share/PrismLauncher/instances/26.3/minecraft/resourcepacks/F cp -u "$HOME/.local/share/PrismLauncher/instances/26.3/minecraft/shaderpacks/Revelation-dev.zip" "atlas/root/servers/SurviePote-26.3/files/shaderpacks/" 2>/dev/null || true echo "== mod maison nosingleplayer (build si besoin) ==" -if [ ! -f mods/nosingleplayer/build/libs/nosingleplayer-1.1.2.jar ]; then +if [ ! -f mods/nosingleplayer/build/libs/nosingleplayer-1.1.3.jar ]; then (cd mods/nosingleplayer && ./gradlew build 2>&1 | tail -n 2) || echo "BUILD nosingleplayer échoué (réseau/gradle requis)" fi -cp -u mods/nosingleplayer/build/libs/nosingleplayer-1.1.2.jar "$ATLAS_MODS/" 2>/dev/null || true +cp -u mods/nosingleplayer/build/libs/nosingleplayer-1.1.3.jar "$ATLAS_MODS/" 2>/dev/null || true +rm -f "$ATLAS_MODS"/nosingleplayer-1.1.2.jar "$ATLAS_MODS"/nosingleplayer-1.1.1.jar "$ATLAS_MODS"/nosingleplayer-1.1.0.jar "$ATLAS_MODS"/nosingleplayer-1.0.0.jar # Note : environment=client (menu titre), inutile côté serveur. echo "== regen distribution.json =="