121 Commits
Author SHA1 Message Date
Anaïs Saraiva d02bb4b0ac launcher 1.1.0 : release (AppImage + exe compilés)
- Version du launcher 1.0.0 -> 1.1.0 (package.json).
- Artefacts de la release versionnés dans releases/v1.1.0/ :
  SurviePote-setup-1.1.0.AppImage (92 Mo) et SurviePote-setup-1.1.0.exe (98 Mo),
  construits avec electron-builder (npm run dist -- -l -w).
- Distribution : https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/distribution.json
2026-10-19 22:19:45 +02:00
Anaïs Saraiva 49e429ce1b atlas : nosingleplayer 1.1.8 + autoconnect désactivé
- Remplace nosingleplayer-1.1.4.jar par nosingleplayer-1.1.8.jar dans
  fabricmods/required/ (fond du menu titre, boutons icônes, statut serveur).
- servermeta.json : autoconnect false (le menu titre custom doit s'afficher,
  pas la connexion automatique).
- distribution.json régénéré (npm run faststart -- g distro) : nouvel id
  fr.surviepote:nosingleplayer:1.1.8@jar, taille et MD5 à jour.
2026-10-19 22:10:11 +02:00
Anaïs Saraiva 4f95e82812 nosingleplayer 1.1.8 : menu titre rework (boutons icônes, Discord, statut serveur)
Menu principal :
- Bouton "Join Discord" (logo Discord lissé + texte) sous "Rejoindre
  SurviePote", ouvre https://discord.gg/EpkgEPv5c.
- "Options..." et "Quit Game" remplacés par des boutons icônes sans texte
  (engrenage / symbole power) qui conservent l'action du bouton vanilla
  (ButtonAccessor -> onPress).
- Panneau de statut du serveur en haut à gauche : En ligne / Hors ligne,
  joueurs current/max et ping.
  Attention : dans ServerStatusPinger.pingServer les 2 Runnable ne sont pas
  des callbacks succès/échec (le 1er ne fire que si le favicon change, le
  2nd est le pong) -> on lit directement ServerData.status ("0/20" quand le
  statut est arrivé, vidé en cas d'échec) + timeout 8s.
- Suppression du texte "26.3 mc" (bas à gauche) : le mixin TitleScreenTextMixin
  qui le dessinait est supprimé, la redirection masque le texte de version.
- Suppression du splash, du logo Mojang, des boutons amis/icônes et du
  copyright (TitleScreenMixin : FriendsButton/SpriteIconButton/PlainTextButton
  sont des sous-classes de Button, ils étaient avalés par "instanceof Button").

Icônes : PNG lisses 64x64 (supersampling 8x8) au lieu du pixel art.
Chargement des textures du mod via ModTextures (les assets des mods ne sont
plus exposés au ResourceManager en MC 26.3).
2026-10-19 22:08:21 +02:00
Anaïs Saraiva 89e6b6f680 nosingleplayer 1.1.7 : menu titre nettoyé (splash, amis, copyright) + titre de fenêtre
- Titre de fenêtre : "Minecraft* 26.3" -> "SurviePote [26.3]"
  (mixin sur Minecraft.createTitle(), la classe Window a changé de package
  en 26.3 : com.mojang.blaze3d.platform.Window).
- Suppression du texte de splash (message rotatif) du menu principal.
- Bouton "amis" (FriendsButton) réellement supprimé : le if/else if de
  TitleScreenMixin testait "instanceof Button" en premier, et FriendsButton /
  SpriteIconButton / PlainTextButton en héritent -> ils tombaient dans cette
  branche et n'étaient jamais retirés. Ordre des tests corrigé (icônes et
  texte cliquable testés avant Button).
2026-10-19 21:31:41 +02:00
Anaïs Saraiva fc80845f5c nosingleplayer 1.1.6 : fond du menu titre enfin affiché
Trois bugs cumulés, tous vérifiés contre le jar client 26.3 :

1. blit() : l'overload 11 args réutilise w/h comme région source
   (u0 = u/tw, u1 = (u+w)/tw) -> on n'affichait que le coin haut-gauche
   de l'image (noir). On utilise l'overload 13 args avec une région
   source explicite + recadrage "cover" (pas de déformation).

2. Texture introuvable : en MC 26.3 le ResourceManager ne lit plus les
   packs depuis les jars du classpath (seulement des dossiers), donc les
   assets d'un mod ne sont plus exposés -> placeholder magenta.
   Vérifié : "Reloading ResourceManager: vanilla, vanilla" et
   namespaces=[minecraft, realms]. Le PNG est donc lu dans le jar du
   mod (getResourceAsStream) et uploadé en DynamicTexture, enregistrée
   dans le TextureManager.

3. Filtrage : sampler NEAREST par défaut (aliasing visible) -> sampler
   linéaire + clamp-to-edge.

Asset : PNG RGBA 3840x2400.
2026-10-19 21:23:41 +02:00
Anaïs Saraiva 8053411565 nosingleplayer 1.1.5: fond en vrai PNG (MC rejette le JPEG) + launcher: autoconnect désactivé 2026-10-19 20:48:49 +02:00
Anaïs Saraiva c92059db26 server: config PM2 (lancement du serveur dans le nix-shell) 2026-10-19 20:40:38 +02:00
Anaïs Saraiva fdc7d867ec server: écouter uniquement sur l'interface 10.7.0.2 (server-ip) 2026-10-19 20:16:39 +02:00
Anaïs Saraiva b8a283a177 server: start.sh/setup.sh relancent automatiquement via nix-shell si Java 25 absent
Sur NixOS, sans nix-shell le java systeme manque souvent. Le script detecte
l'absence de Java 25 et se re-execute dans le shell Nix du repo (shell.nix),
avec garde SURVIEPOTE_NIX pour eviter toute boucle.
2026-10-19 20:11:09 +02:00
Anaïs Saraiva 3a2b403acc scripts: retirer shebang #!/bin/bash (incompatible NixOS, /bin/bash absent)
start.sh/setup.sh/sync-mods.sh s'executent via le shebang du shell appelant
(./start.sh depuis bash fonctionne).
2026-10-19 20:06:43 +02:00
Anaïs Saraiva 37fcdfb277 server: installation auto du runtime Fabric (setup.sh/.bat)
Le runtime (jars vanilla/Fabric, libraries/, versions/) n'est pas versionne.
- setup.sh / setup.bat : telecharge fabric-installer 1.1.2 et installe
  le serveur Fabric (MC 26.3 / Loader 0.20.0) + server.jar vanilla
- start.sh / start.bat : lance setup automatiquement si le runtime manque
- README : premier lancement mis a jour
- teste en dossier vierge : installation OK
2026-10-19 20:03:37 +02:00
Anaïs Saraiva 20330310fe gitignore: ignorer .direnv/ (racine + mods) 2026-10-19 19:55:48 +02:00
Anaïs Saraiva f4372ab8a4 gitignore: logs des mods (mods/*/logs/) + untrack mods/pala/logs 2026-10-19 19:54:44 +02:00
Anaïs Saraiva 7a1a4306b1 gitignore: ignorer server/config/ (configs runtime) + dedup logs; suppr ADD_THIS
- server/config/ sorti du versionnement (configs regenerees au boot)
- bloc runtime duplique (world/logs/crash-reports/*.log) consolide
- server/config/**/update/ devenu redondant, retire
2026-10-19 19:53:11 +02:00
Anaïs Saraiva 058c18c4cb nosingleplayer 1.1.4: retirer completement le bouton Solo (au lieu de le griser) 2026-10-19 19:51:36 +02:00
Anaïs Saraiva 3f389f65f2 mods: +15 communs (serveur+atlas) et +2 client-only (atlas)
Ajoutés depuis ADD_THIS/ :
- communs (env *) : advancednetherite, betterarcheology, better-nether,
  constructionwand, elevatorid, ImprovedPillagerOutpost, mythicupgrades,
  NoChatReports, simple-warp-tpa-home-back, travelersbackpack,
  universalenchants, yyzsbackpack
- libs communes : ResourcefulConfig, ForgeConfigAPIPort, puzzleslib (Modrinth)
- client-only (atlas) : spatial-gui, yet_another_config_lib_v3
- doublons ignorés : cloth-config, placeholder-api (déjà présents)
- distribution.json régénéré ; doc serveur mise à jour
- boot-test serveur validé : Done (1.202s), 140 mods, 0 erreur
2026-10-19 19:50:26 +02:00
Anaïs Saraiva 19f0e05c8e atlas+serveur: retirer ImmersiveOres (mod cassé)
- retiré de atlas/.../fabricmods/required/ (clients) et de server/mods-client-only/
- retiré les configs atlas/.../files/config/immersiveores/*.toml
- sync-mods.sh: ImmersiveOres ajouté à ATLAS_EXCLUDE + cleanup serveur/atlas
- docs (SOURCE.md, quarantine/README.md) mises à jour
- distribution.json régénéré (retrait du mod + de ses 3 configs)
2026-10-19 19:27:50 +02:00
Anaïs Saraiva 722802c001 launcher: purge auth Mojang (depreciee), Microsoft uniquement
- bootstrap: validateSelected() ne tente plus de sign-in Mojang
- suppression formulaire login.ejs / login.js et de la vue login
- authmanager: retrait addMojangAccount/removeMojangAccount/mojangErrorDisplayable
- configmanager: retrait clientToken et helpers comptes Mojang
- settings/processbuilder: flux Microsoft uniquement (user_type=msa)
- landing: retrait du refresh de statut Mojang (fonction + UI + CSS)
2026-10-19 18:57:41 +02:00
Anaïs Saraiva 4a48d2c721 nosingleplayer 1.1.3: fix connexion + bouton Quitter SurviePote
- TitleScreenMixin : TransferState null sur startConnecting -> connexion
  normale, corrige "This server does not accept transfers".
- PauseScreenMixin (nouveau) : renomme le bouton de deconnexion du menu
  Echap en "Quitter SurviePote" et redirige vers l'ecran d'accueil
  (TitleScreen) au lieu de la liste des serveurs.
- build.gradle : tache runClient (dev sans loom) reutilisant
  ~/.surviepote/common.
- Jar 1.1.3 copie dans l'atlas + distribution.json regenere.
2026-10-19 18:41:56 +02:00
Anaïs Saraiva 159a594b2c launcher: exiger Java 25 (servermeta javaOptions + distribution.json)
La config serveur declare desormais javaOptions (TEMURIN, supported
>=25.x, suggestedMajor 25) au lieu du defaut Helios 21 pour MC >= 1.20.5.
Le launcher exigera/detectera Java 25 et telechargera Temurin 25.x depuis
Adoptium si absent. Genere via `npm run faststart -- generate distro`.
2026-10-19 18:36:40 +02:00
Anaïs Saraiva 6dc8e02278 pala 0.2.1: release v1.0.1 (jar atlas resync + distribution.json + flake JDK25)
- flake: jdk8 -> jdk25, nixpkgs 25.05 -> nixos-unstable (glibc 2.44 aligne
  systeme NixOS 26.11). Corrige runClient: plus de mismatch GLIBC_2.44
  sur le driver GPU (mesa/LLVM via /run/opengl-driver). Attributs xorg.*
  depréciés remplacés par noms top-level.
- jar pala rebuild (MD5 5dd172a0d8ac14c924729cea9685174c) déployé dans
  atlas/.../fabricmods/required/ (server/mods/ était déjà à jour).
- atlas/root/distribution.json régénéré: entrée pala 0.2.1
  size 308198 -> 512504, MD5 530092... -> 5dd172...
2026-10-19 18:29:27 +02:00
Anaïs Saraiva bd22c3c4e6 pala M-audit-2: stats 4 tiers + endium 3x3 et gates 2026-10-19 08:56:36 +02:00
Anaïs Saraiva 72978469aa pala M-audit-1: stats outils paladium corrigees wiki 2026-10-19 08:50:54 +02:00
Anaïs Saraiva 7dac46275c pala M-hoes-1 + M-fix-2: 4 grandes houes AoE + capture +7 2026-10-19 08:45:47 +02:00
Anaïs Saraiva 16e980c133 pala M-fix-1 + M-roller-1: canne 128 + rollers vitesse/saut 2026-10-19 08:39:35 +02:00
Anaïs Saraiva b91cf69e6f pala M-hunter-6: etiquette endium (rename, max 3) 2026-10-19 08:33:18 +02:00
Anaïs Saraiva dcbf327a57 pala M-hunter-5: capture sword/stone (14 types, CUSTOM_DATA) 2026-10-19 08:26:38 +02:00
Anaïs Saraiva 4da821104d pala M-hunter-4: knocker 4 degats + canne peche paladium 2026-10-19 08:19:08 +02:00
Anaïs Saraiva d24d863a64 pala M-hunter-3: kills vanilla supplementaires + mergeDefaults config 2026-10-19 08:08:17 +02:00
Anaïs Saraiva 8c3ae8d90e pala M-homes-1: homes personnels (/sethome /home /delhome /homes) 2026-10-19 07:53:09 +02:00
Anaïs Saraiva 44c172021c pala M-tp-zones: /minage + /farmland (zones config) 2026-10-19 07:42:49 +02:00
Anaïs Saraiva 89ea9fe43a pala M-faction-4: /f map (carte ASCII claims 9x9) 2026-10-19 07:37:16 +02:00
Anaïs Saraiva ca01bd796c pala M-pillage-1b: nettoie PNG renommes 2026-10-19 07:29:16 +02:00
Anaïs Saraiva 90615db6ff pala M-pillage-1: dynamites controlees (4640-4642, menage guns) 2026-10-19 07:29:09 +02:00
Anaïs Saraiva b3c9e8e207 pala M-spawner-1: chercheur de spawners (scan 9 chunks, gate 10) 2026-10-19 07:21:25 +02:00
Anaïs Saraiva 65dbaaec22 pala M-hunter-2: infernal knocker (knockback wither x3) 2026-10-19 07:15:49 +02:00
Anaïs Saraiva 1c6cd2bff6 pala M-hunter-1: marteau des cavernes (dura 256, craft) 2026-10-19 07:09:47 +02:00
Anaïs Saraiva 9222e9fa95 pala M-enchants-3: amulette magma (incrustation + feu) 2026-10-19 07:04:38 +02:00
Anaïs Saraiva 1c4efce590 pala M-enchants-2: Experience Bottler (fiole XP 1000) 2026-10-19 06:59:36 +02:00
Anaïs Saraiva b30d3128b1 pala M-enchants-1: voleur dXP (datapack + hook vol) 2026-10-19 06:53:58 +02:00
Anaïs Saraiva bb44266215 pala M-alchi-4: bibliotheque mystique (decorative v1) 2026-10-19 06:47:51 +02:00
Anaïs Saraiva aecf8fa031 pala M-alchi-3: fondations chaudron (bloc+coeur+support) 2026-10-19 06:43:00 +02:00
Anaïs Saraiva 4195574341 pala M-alchi-2: lightning potion (capture eclair, mort si bue) 2026-10-19 06:37:51 +02:00
Anaïs Saraiva 1e29bfef67 pala M-alchi-1: fiole + XP craft alchimiste data-driven 2026-10-19 06:32:10 +02:00
Anaïs Saraiva 53c7416aae pala M-market-2: expiration annonces 7j + purge flush 2026-10-19 06:22:56 +02:00
Anaïs Saraiva ee42ccca7c pala M-market-1b: CDC corrige (taxe 5% §3.8 confirmee, expiry TODO) + scripts/parencheck.py 2026-10-19 06:17:39 +02:00
Anaïs Saraiva 5bfebd92ae pala M-market-1: market commandes (sell/list/buy/cancel) 2026-10-19 06:16:46 +02:00
Anaïs Saraiva 48e73d2a18 pala M-gadget-8: cagoule + masque de garde (fix enchant 0) 2026-10-19 06:08:01 +02:00
Anaïs Saraiva ab2467c65d pala M-gadget-7: banane (vitesse III 15s, Consumable) 2026-10-19 05:58:55 +02:00
Anaïs Saraiva 8fdd7fb1e1 pala M-gadget-6: sac de couchage (spawn au clic droit) 2026-10-19 05:53:05 +02:00
Anaïs Saraiva 66e0705505 pala M-gadget-5: pierre stats + epee couac (AFTER_DAMAGE) 2026-10-19 05:48:04 +02:00
Anaïs Saraiva 6900627536 pala M-gadget-4: bottes lestees (explosion atterrissage, legacy) 2026-10-19 05:41:01 +02:00
Anaïs Saraiva 32c4e4069a pala M-gadget-3: bocal a poissons (casque = cecite) 2026-10-19 05:34:38 +02:00
Anaïs Saraiva d9f1f2af1d pala M-gadget-2: grappin (raycast 32, dura 255) 2026-10-19 05:30:09 +02:00
Anaïs Saraiva efc7267306 pala M-gadget-1: analyseur de chunk (dura 5, scan top10) 2026-10-19 05:24:50 +02:00
Anaïs Saraiva 101384e4bf pala M-faction-3b: elo PvP inter-factions + /f top 2026-10-19 05:19:15 +02:00
Anaïs Saraiva 9a5b5acb03 pala M-compress: compressed paladium + titane (legacy pattern) 2026-10-19 05:13:57 +02:00
Anaïs Saraiva e8bfd0d99c pala M-faction-3a: grades co-chefs + /f promote/demote 2026-10-19 05:08:15 +02:00
Anaïs Saraiva 7878f006bd pala M-faction-2c: /f sethome + /f home multi-dimensions 2026-10-19 04:54:03 +02:00
Anaïs Saraiva 76cc105f19 pala M-faction-2b: /f claim/unclaim/list + /fc chat faction 2026-10-19 04:42:04 +02:00
Anaïs Saraiva 7da06c6581 pala M-faction-2: claims chunks + protection territoire 2026-10-19 04:31:30 +02:00
Anaïs Saraiva 4379f63e8a pala M-faction-1c: /f desc + /f help 2026-10-19 04:21:20 +02:00
Anaïs Saraiva a57b614de7 pala M-faction-1b: /f kick + /f transfer 2026-10-19 04:15:27 +02:00
Anaïs Saraiva d5a88d4516 pala M-faction-1: socle factions + /f create/info/invite/join/leave/disband 2026-10-19 04:06:55 +02:00
Anaïs Saraiva 061f19d4ee pala M-rtp: teleportation aleatoire safe + cooldown 15min (wiki) 2026-10-19 04:00:54 +02:00
Anaïs Saraiva 8c4a6748ef pala M-mineur-xp: gates niveaux, XP cuisson, XP hammer, bonbon jaune 2026-10-19 03:56:22 +02:00
Anaïs Saraiva 588e516dbe pala M-palaforge: recyclage usure + gate mineur 14 (wiki Palaforge) 2026-10-19 03:51:43 +02:00
Anaïs Saraiva 77fbb485d7 pala M-afk: kick inactivite + bypass grade (wiki reglement) 2026-10-19 03:46:47 +02:00
Anaïs Saraiva 1720463a87 pala M-candy: 7 bonbons + effet 50k XP (vert/rainbow, wiki metiers) 2026-10-19 03:42:25 +02:00
Anaïs Saraiva 0e97130479 pala M-xpcurve: seuils XP wiki + XP broyer crusher + gate silk-touch 2026-10-19 03:38:25 +02:00
Anaïs Saraiva 52c3229684 pala M-recompenses: lots de palier wiki (formule + extras) + furnace complete 2026-10-19 03:32:59 +02:00
Anaïs Saraiva 8e280e751f pala M-grinder-4: fix jauge fuel +1 + 3 recettes patterns (wiki live) 2026-10-19 03:28:05 +02:00
Anaïs Saraiva 58441d5048 pala M-mixin: pipeline mixin + XP craft (pain/tarte) et cuisson (viandes) 2026-10-19 03:23:31 +02:00
Anaïs Saraiva baf29d5c11 pala M-grinder-3: BE dediee + GUI + jauge fuel (diagnostic deplace bloc) 2026-10-19 03:18:29 +02:00
Anaïs Saraiva 41f4ff8f0a pala M-grinder-2: detecteur multibloc 3x3x3 + diagnostic clic droit 2026-10-19 03:14:31 +02:00
Anaïs Saraiva be6c0306e3 pala M-grinder-1: 3 blocs + 9 items grinder/broadsword/fastsword (textures legacy, crafts) 2026-10-19 03:10:20 +02:00
Anaïs Saraiva fdff425a48 pala M-eco: /pay /baltop /balance (monnaie persistee, wiki economie) 2026-10-19 03:01:12 +02:00
Anaïs Saraiva da60aa2228 pala M-infos: /serv /wiki /discord /boutique /couleurs + liens en config 2026-10-19 02:53:41 +02:00
Anaïs Saraiva 9efa6c5548 pala M-msg: /msg /r messages prives (wiki commandes) 2026-10-19 02:49:17 +02:00
Anaïs Saraiva ba78a3b1e2 pala M-tpa: /tpa /tpaccept /tpdeny (delai 3s + invisibilite 15s, wiki commandes) 2026-10-19 02:45:19 +02:00
Anaïs Saraiva e3b4500bd6 pala M-cmd-util: /craft table portable + /spawn delai 3s (wiki commandes) 2026-10-19 02:41:23 +02:00
Anaïs Saraiva 6e0bed343b docs: broadsword = systeme grinder (vague M-machines), pas un item isole 2026-10-19 02:24:33 +02:00
Anaïs Saraiva cbc742046d docs: cloture TODO worldgen-couches (conforme) et pog-os (M2-OS), entree M2-OS 2026-10-19 02:24:12 +02:00
Anaïs Saraiva c984c9ae9c pala M-TEXFIX: 162 definitions assets/pala/items (fin du damier, verifie en jeu) 2026-10-19 02:22:43 +02:00
Anaïs Saraiva 17b5b92fa5 pala M1c+docs-texture: 3 marteaux 3x3, pack test diamant-orange, 8 screens + LOG 2026-10-19 01:56:11 +02:00
Anaïs Saraiva 0e2284a26b docs: boucle visuelle Xvfb/runClient, 6 screens + LOG session texture 2026-10-19 01:47:31 +02:00
Anaïs Saraiva 5141e38ea2 pala M5a-final+M2os+lang: config auto-complete par cles, one-shot POG 11/21/26/31, 39 cles item.* blockitems 2026-10-19 01:47:24 +02:00
Anaïs Saraiva c76d67a2fd pala M5a+M4a: XP chasseur/fermier + recettes machines data-driven + config auto-complete 2026-10-19 00:47:13 +02:00
Anaïs Saraiva 610dc597fb pala M3: crusher fidele legacy (fruits/luck/auto-generate) + furnace mixed fuels + gate fermier 8 2026-10-18 23:54:37 +02:00
Anaïs Saraiva 3330b5bd04 pala M2: tooltip XP POG via LORE serveur + craft verifie conforme wiki 2026-10-18 23:48:42 +02:00
Anaïs Saraiva cc7a5f7c77 pala M1: worldgen amethyste/titane calibre wiki (size 8, trapezoid 6-10, deep ocean) + /pala orestats 2026-10-18 23:44:02 +02:00
Anaïs Saraiva 86cc852c81 pala M0: socle core (config/permissions/storage/jobs) + /pala /jobs + gate /nether, jar serveur redeploye 2026-10-18 23:38:52 +02:00
Anaïs Saraiva a1c86f8be9 docs: PALAMOD_CDC.md — cahier des charges refonte mod serveur (wiki 137 articles, mapping 307 elements, specs M0-M10) 2026-10-18 23:20:49 +02:00
kisakay ba73b2080f Pala: rebuild clean + redéploiement (resources stales) 2026-10-18 23:03:42 +02:00
kisakay dab383966d Pala runClient: fabric-api copié dans game/mods 2026-10-18 22:54:33 +02:00
kisakay 2e9223f117 Pala: downloadMcLibs toujours vérifié (pas d'up-to-date) 2026-10-18 22:53:23 +02:00
kisakay cd5965a022 Pala runClient: fabric-api sur le classpath 2026-10-18 22:50:45 +02:00
kisakay 7169310cdc Pala runClient: LD_LIBRARY_PATH GL + copie jar, SDL wayland 2026-10-18 22:49:27 +02:00
kisakay bc45c619a5 Pala runClient: pré-crée game/mods 2026-10-18 22:47:53 +02:00
kisakay 07986c3883 Pala runClient: retire resources brut du classpath (confond le loader) 2026-10-18 22:46:59 +02:00
kisakay 4eb9f850ca Pala: tâche runClient (dev visuel sans loom) 2026-10-18 22:44:59 +02:00
kisakay 4575154fc8 Pala: purge assets legacy, fix textures fluides + 2 faux PNG 2026-10-18 22:42:03 +02:00
kisakay 45fb11c3ef Atlas: pala en requis (sync registries serveur obligatoire) 2026-10-18 22:36:13 +02:00
kisakay 156a23397c Pala 0.2.1: fix ids 26.3 (setId items/blocs), enchant>0, recipes/advancements valides 2026-10-18 22:08:39 +02:00
kisakay 7e2b5011ee Atlas: pala en optionaloff (crash Item id, à fixer côté mod) 2026-10-18 21:41:42 +02:00
kisakay e800fde2ca nosingleplayer 1.1.2: fond via Redirect appel (extractPanorama hérité de Screen) 2026-10-18 21:33:51 +02:00
kisakay 3be064ad25 Pala 0.2.0: procedures (53), loot (38), guns, fluides, machines, commands 2026-10-18 21:31:37 +02:00
kisakay 37c955d67a nosingleplayer: zero Shadow (résolution superclass cassée en 26.3) 2026-10-18 21:28:03 +02:00
kisakay 5782acc08a Pala 0.1.0: portage contenu (116 items, 38 blocs, 95 recipes, 7 filons) + atlas optionalon 2026-10-18 21:18:20 +02:00
kisakay 3b5e8e7a13 nosingleplayer 1.1.1: fond custom via blit + log debug widgets 2026-10-18 21:14:36 +02:00
kisakay 3a2dfeb21e Nettoie fichiers locaux pala (.direnv, Eclipse) 2026-10-18 21:08:58 +02:00
kisakay cedc5c50c0 nosingleplayer: fix crash Shadow (listes+accesseur), retire RenderMixin en double 2026-10-18 21:08:45 +02:00
kisakay e8e99fdb9d Mod nosingleplayer 1.1.0: menu custom (join serveur, fond wallpaper, sans logo/copyright) 2026-10-18 20:56:36 +02:00
kisakay bae95baed3 Launcher: SDL_VIDEODRIVER=wayland sur session Wayland (EGL/GL pour Iris) 2026-10-18 20:43:11 +02:00
kisakay f278884cfa Gitignore: cache/build Gradle hors git 2026-10-18 20:37:17 +02:00
kisakay 11f5976dde Mod maison nosingleplayer 1.0.0 (bouton Solo désactivé) + atlas 2026-10-18 20:36:27 +02:00
kisakay d666a65dac Atlas: 15 mods client (sodium, iris, xaero...) en optionalon (ON par défaut) 2026-10-18 20:27:33 +02:00
kisakay d7102cf7af SurviePote.desktop: chemins install réels (~/Applications) 2026-10-18 20:25:45 +02:00
kisakay 50e1030442 Launcher: LD_LIBRARY_PATH + SPA_PLUGIN_DIR audio (fix NixOS pas de son OpenAL) 2026-10-18 19:52:09 +02:00
kisakay 3143443a8d SurviePote.sh: shebang env bash + chemins absolus (fix NixOS sans /bin/bash) 2026-10-18 19:45:34 +02:00
kisakay 101698b75b Launcher: .desktop freedesktop + SurviePote.sh (lancement nix-shell) 2026-10-18 19:42:18 +02:00
2754 changed files with 58842 additions and 2451 deletions
+8 -7
View File
@@ -22,16 +22,11 @@ server/crash-reports/
server/versions/ server/versions/
server/*.log server/*.log
server/*.json server/*.json
server/config/
server/fabricloader.log server/fabricloader.log
server/fabric-server-launcher.properties server/fabric-server-launcher.properties
quarantine/*.jar quarantine/*.jar
# server runtime
server/world/
server/logs/
server/crash-reports/
server/*.log
# OS / IDE # OS / IDE
.DS_Store .DS_Store
Thumbs.db Thumbs.db
@@ -43,4 +38,10 @@ Thumbs.db
.env .env
atlas/.env atlas/.env
server/.fabric/ server/.fabric/
server/config/**/update/ mods/*/.gradle/
mods/*/build/
mods/*/logs/
.gradle/
.direnv/
mods/pala/.classpath
mods/pala/.project
+41
View File
@@ -0,0 +1,41 @@
# AGENTS.md — mc (SurviePote)
## Launcher (`launcher/`, base HeliosLauncher)
### Rebuild propre
Prérequis : Node (voir `.nvmrc`), `wine` pour la cible Windows, cache Electron présent (`~/.cache/electron`).
```bash
cd launcher
rm -rf dist
npm run dist -- -l -w # AppImage Linux + setup NSIS Windows
```
Artefacts générés dans `launcher/dist/` :
- `SurviePote-setup-1.0.0.AppImage` (Linux)
- `SurviePote-setup-1.0.0.exe` (Windows, via wine)
Cibles seules : `npm run dist:linux` / `npm run dist:win` / `npm run dist:mac`.
### Déploiement desktop Linux (norme freedesktop.org)
Entrée release : `SurviePote.desktop` (racine, validée par `desktop-file-validate`).
Entrée dev : `launcher/SurviePote.desktop` ("SurviePote (Dev)", `electron .` via `SurviePote.sh`, non installée).
```bash
cp launcher/dist/SurviePote-setup-1.0.0.AppImage ~/Applications/
mkdir -p ~/.local/share/icons/hicolor/256x256/apps ~/.local/share/applications
cp launcher/build/icon.png ~/.local/share/icons/hicolor/256x256/apps/surviepote.png # Icon=surviepote
cp SurviePote.desktop ~/.local/share/applications/
update-desktop-database ~/.local/share/applications
```
Clés : `Exec` absolu (pas de `~` expansé), `TryExec` masque l'entrée si l'AppImage manque,
`StartupWMClass=SurviePote` pour le groupement fenêtre, `Categories=Game;`, `SingleMainWindow=true`.
Lancement dev sans build : `./SurviePote.sh` (nix-shell + `npx electron .`).
### Particularités du fork
- Auth Mojang supprimée (Microsoft uniquement) : `app/settings.ejs`, `app/loginOptions.ejs`, `scripts/settings.js`, `scripts/loginOptions.js`.
- Splash de boot Helios remplacé par `SealCircle.png` (`app/app.ejs`).
- Wallpaper unique local : `app/assets/images/backgrounds/0.jpg`, `bkid` fixé à `0` dans `index.js`.
- Icône landing + avatar carrés (pas de `border-radius`) dans `app/assets/css/launcher.css`.
- News désactivées (blocs masqués, JS neutralisé).
+810
View File
@@ -0,0 +1,810 @@
# PALAMOD — Cahier des charges : refonte en mod serveur
Statut lecture : VERIFIE = constate par outil (extrait wiki, fichier lu, commande executee).
UNVERIFIED = propose mais non teste. TODO:<raison> = bloqueur explicite.
Protocoles : WAVES (reconnaitre, isoler, corriger, verifier) + ETERNAL CODE (zero affirmation sans execution).
Date : 2026-10-18. Repo : /home/kisakay/Documents/Code/HomeLabGitea/mc.
Cible : mod SERVEUR Fabric, MC 26.3, Loader 0.20.0, Java 25, serveur existant server/ (mcpv.ihorizon.org:25565).
Point d'architecture tranche (detail §2) : sur Fabric il n'existe pas de "plugin" au sens Bukkit.
La cible est donc un MOD serveur pur (aucun code client requis) + resource pack serveur genere
depuis les textures d'origine. Aucun mod client obligatoire. Tout le §6 decoule de ce choix.
## 0. Sources ingerees (VERIFIE)
Wiki https://wiki.paladium-pvp.fr/fr/ :
- 137 articles FR (37 items-et-machines, 20 metiers, 8 faction, 34 gameplay, 5 commandes,
13 mecaniques-events, 17 donjons, 3 reglements), categories Bedrock exclues (hors cible Java).
- 664 538 caracteres extraits en texte (137/137 articles, 0 erreur) + 322 420 caracteres
de re-extraction navigateur pour 50 pages rendues en JS (stubs web_extract).
- ~1 000 visuels recenses sur les pages (ex : lucky mensuel 158, saisonnier 108, stockages 97,
palamachine 78, pillage 80, construction 82, POG 43). Fichiers bruts :
~/.hermes/cache/scratch/palawiki/*.md + workspace navigateur stub1/2/3.txt.
- 516 IDs numeriques historiques distincts extraits (ex : amethyst ore 582, amethyst ingot 4504,
amethyst block 508) — utiles comme reference, NON reutilisables tels quels en 26.3 (registres par noms).
- 93 commandes slash distinctes citees dans le corpus (tri §1.9).
OCR visuels (exige par la mission) : 3 echantillons telecharges (crafts palamachine, GUI crusher)
et passes a vision_analyze. Resultat VERIFIE : sur des thumbnails pixel-art 2-6 Ko, le modele
confabule (decrivait un "autel Blood Magic / Thaumcraft" pour une recette Paladium — Paladium
n'utilise ni l'un ni l'autre). Conclusion : OCR automatique des images wiki = NON FIABLE.
Decision : les patterns de craft retiennent (1) le texte wiki "Ressources necessaires" quand present,
(2) les slots exacts de elements/*.mod.json (90 recettes, donnees machine VERIFIEES, ex §3.1),
(3) le reste est marque UNVERIFIED et devra etre releve en jeu via JEI (screenshots table de craft).
TODO:ocr-fiable : re-essayer l'OCR uniquement sur images >= 50 Ko ou captures JEI en jeu. Raison :
en dessous de ce seuil, hallucination demontree sur 3/3 echantillons.
Reference existante (etat des lieux, PAS copiee) : mods/pala/ :
- CUSTOM_MAPPING.json : 307 elements = 95 recipe, 63 procedure, 53 tool, 37 item, 37 block,
7 armor, 3 gun, 3 command, 2 fluid, 2 achievement, 2 gui, 1 food, 1 tab, 1 plant.
- textures/ : 939 fichiers (725 items, 190 blocks, 14 armor, 10 other), dont 891 PNG.
Toutes les textures d'origine sont LA. Aucune texture ne doit etre recreee.
- src/ porte Fabric 26.3 : ~1 942 lignes (PalaItems, PalaBlocks 39 blocs, PalaToolMaterials,
PalaArmorMaterials, PalaWorldgen data-driven 7 minerais, PalaCommands /end /nether /overworld
perm niveau 4, 2 BlockEntities partagees, events armor/crop/itemuse/tool). Fichiers lus :
PalaItems.java (147 l.), PalaBlocks.java (77 l.), PalaCommands.java (62 l.),
PalaToolMaterials.java (93 l.), PalaWorldgen.java (54 l.), PalaMachineBlockEntity.java (106 l.).
- data/pala/recipe/ : 95 JSON recettes. server/mods/ : 31 jars dont pala-0.2.1.jar actif.
- Java 25.0.4.1 via nix-shell VERIFIE (openjdk "25.0.4.1", node v22.23.3). Java systeme = 21 (inutilisable).
- Boot serveur ./server/start.sh via nix-shell : test lance en fond pendant la redaction, resultat §8.
## 1. Inventaire exhaustif fonctionnel + mapping existant
Convention : [PORTE] = present dans mods/pala/src ou data/ (partiel = logique incomplete).
[MANQUANT] = wiki documente, rien dans le portage. [ECART] = present mais non conforme au wiki.
### 1.1 Minerais (7 familles)
| Famille | Minerai | Lingot/gemme | Bloc | Worldgen wiki (VERIFIE) | Mapping |
|---|---|---|---|---|---|
| Amethyste | amethyst_ore [PORTE] | amethyst_ingot [PORTE] | amethyst_ore_block [PORTE] | filons 1-8, couches 6-10 + deep ocean des couche 25 | M1 : size 8, trapezoid 6-10, placed deep uniform 25-40 (4 biomes deep) |
| Titane | titane_ore [PORTE] | titane_ingot [PORTE] | titane_ore_block [PORTE] | deep ocean + grottes, filons 1-8, couches 6-10 | M1 : idem amethyste. ECART : wiki ne cite pas l'End → verifier |
| Paladium | paladium_ore [PORTE] | paladium_ingot [PORTE] | paladium_ore_block [PORTE] | page minerais (texte tronque JS, re-crawl partiel) | PORTE, couches UNVERIFIED |
| Paladium vert | paladium_green_ore [PORTE] | paladium_green_ingot [PORTE] | paladium_green_ore_block [PORTE] | idem | PORTE, obtention (fonderie ?) UNVERIFIED |
| Trixium | trixium_ore [PORTE] | trixium_gem [PORTE] | trixium_ore_block [PORTE] | idem | PORTE |
| Findium | findium_ore [PORTE] | findium_gem [PORTE] | findium_ore_block [PORTE] | idem | PORTE |
| Endium | [MANQUANT : pas de endium_ore dans PalaBlocks] | endium_ingot [PORTE] + endium_nugget [PORTE] | [MANQUANT] | rituel endium (niveau 20 hunter), graines cultivables | ECART : endium sans minerai = normal si rituel seul ; a confirmer wiki |
| Kisakay (staff/fun) | kisakay_ore [PORTE] | kisakay_ingot [PORTE] | kisakay_ore_block [PORTE] | non wiki (= ajout local) | PORTE, garder en namespace separe |
Drops/smelting : *_ore_smelting + block<->lingot 9x/1x/9x dans elements (ex AmethystBlockOreRecipe
shapeless 1 bloc → 9 lingots VERIFIE). Patterns compacts standards : VERIFIE via elements.
CLOS 2026-10-19 ~02:40 (verifie sans changement : tailles 4/8/3/4 et max 18/25/33/18
deja conformes dans feature/*.json + placed_feature/*.json ; seule la borne max deep 40
reste UNVERIFIED : wiki dit "a partir de la couche 25" sans max).
Lecon M1 VERIFIEE au boot : minecraft:triangle N'EXISTE PAS en 26.3 (crash datapacks) → trapezoid.
### 1.2 Outils et armes
Familles completes attendues (pioche/pelle/hache/epee + armure 4 pieces) :
- Amethyste [PORTE] : epee 7 degats / 1999 dura (wiki) ; portage : sword 5.0+mat 6.0, pick 1164 dura.
ECART : valeurs MCreator vs wiki a reconciler (le wiki exclut les enchantements, pas les donnees).
- Titane [PORTE], Paladium [PORTE], Paladium vert [PORTE partiel : pas de houe], Endium [PORTE partiel :
hache/pioche/epee + armure, pas de pelle/houe], Kisakay [PORTE + houe].
- Marteaux 3x3 : amethyst_hammer, titane_hammer, paladium_hammer [PORTE items + event
paladium_hammer_block_destroyed_with_tool]. ECART : zone 3x3 UNVERIFIED (PalaToolEvents 220 l. a relire).
- Broadsword amethyste ID 4770 (wiki armes) : [MANQUANT] dans le portage. TODO:broadsword : ce
n'est PAS un item isole mais un SYSTEME (grinder multibloc 26 blocs + lave, tetes broadsword/
fastsword/hammer, 4 modifieurs). Releve d'une vague M-machines future, pas d'un ajout unitaire.
- POG (Pickaxe of the Gods) : 26 items PORTE (base + leve6 + level1-25). Wiki VERIFIE : craft = 1 pioche
amethyste + 1 pioche paladium + 1 pioche titane + 2 sticks paladium ; non enchantable ; niveau dans
le nom + XP next dans le tooltip ; paliers : 1-5 bois, 6-10 pierre, 11-15 fer (OS neige/feuilles),
16-20 diamant, 21-25 or (OS netherrack/cactus), 26-30 amethyste (OS terre/sable/gravier/glace),
31-35 titane (OS laine), 36+ (tableau tronque). ECART : items niveaux sans logique XP/evolution
(pas de composant custom ni event dans le portage). Spec §3.2.
- Excavatrice amethyste, outil de construction amethyste, grandes houes (recompenses metiers) :
[MANQUANT]. Pelle amethyste = "Excavatrice" ? UNVERIFIED.
### 1.3 Armures (6 sets)
Amethyste [PORTE], Titane [PORTE], Paladium + effets tick [PORTE — VERIFIE PalaArmorEvents :
casque night vision II, plastron strength IV, jambieres resistance IV, bottes regen II],
Paladium vert [PORTE], Endium + effets tick [PORTE — VERIFIE : night vision V / strength VII +
regen V / resistance VII / haste II, full set = invisibilite], Kisakay [PORTE],
weighted_boots [PORTE — VERIFIE : chute ralentie + explosion a l'atterrissage].
POG25 en main : speed III + haste IV (VERIFIE). Slime pad : rebond +2 (VERIFIE).
### 1.4 Sticks PvP (palamachine, breaking change potentiel)
heal_stick, hyper_jump_stick, speed_stick, strenght_stick (sic, faute d'origine a conserver en id),
stick_of_god [TOUS PORTES en items, effets clic-droit dans PalaItemUseEvents 220 l.].
Wiki : enchantables solidite 3 max, 1 seul utilisable a la fois quel que soit le nombre en inventaire.
ECART : cooldown/exclusivite UNVERIFIED. Spec §3.3.
### 1.5 Machines (23 + chaudron + golem)
| Machine | Wiki (VERIFIE) | Mapping |
|---|---|---|
| Palamachine (coeur du mod) | 17 297 car. + 78 visuels ; ameliore unclaim finders etc. | [MANQUANT : pas de bloc porte, seulement items]. Coeur a specifier §3.4 |
| Palafurnace | four modde | [PORTE : bloc + BE partagee DispenserMenu 9 slots ; ne cuit que paladium/green au charbon vanilla — ECART : ni mixed coals ni fruits] |
| Palaforge | fusion (page 1 593 car.) | [MANQUANT] |
| Crusher | craft 4 coeurs paladium + 3 sticks paladium + 1 findium + 1 amethyste compressee ; niveau 8 fermier ; carburant = mixed coals, comburant = fruits ; GUI : fruits haut-gauche, combustible bas-gauche, jauges lingots 0-100% haut-droite, stockage bas-droite | [PORTE partiel : meme BE que furnace, pas de jauges, pas de fruits]. Spec §3.4 |
| Grinder | 7 238 car. (recyclage) | [MANQUANT] |
| Autocrafter | 4 783 car. | [MANQUANT] |
| Alchemy creator | 6 399 car. + table enchantements moddes alchimiste | [MANQUANT] |
| Cobblebreaker | 4 824 car. (recompense mineur niv.5) | [MANQUANT] |
| Cobblestone compressor | niveau 18 mineur | [MANQUANT] |
| Bow machine | ameliore l'arc (3 686 car.) | [MANQUANT] |
| Banc de menuiserie | 1 706 car. | [MANQUANT] |
| Controleur de spawners | 6 989 car. | [MANQUANT] |
| Drawbridge | pont (2 057 car.) | [MANQUANT] |
| Home remover | trouve/supprime homes (7 828 car.) | [MANQUANT] |
| Machine a croquettes | nourriture gardiens (5 248 car.) | [MANQUANT] |
| Receptacle a wither | invoque "toutes sor[tes]" (10 335 car.) | [MANQUANT] |
| Table d'ecriture + presse | bibliocraft-like (7 107 car.) | [MANQUANT] |
| Totem de fertilite | boost production (2 435 car.) | [MANQUANT] |
| Totem fleuri | genere coquelicots (2 747 car.) | [MANQUANT] |
| Colle / glueballs | jetables a effets (2 890 car. + objets alchimiste 6 906 car.) | [MANQUANT] |
| Ghost blocks | copie l'apparence du bloc adjacent (3 895 car.) | [MANQUANT : infaisable pur serveur, §6] |
| Stockages | 18 032 car., 97 visuels ; detruits = drop contenu sauf drawers | [MANQUANT : StorageDrawers deja sur le serveur comme substitut partiel] |
| Chaudron (+blocs exclusifs) | 5 955 car. | [MANQUANT] |
| Golem (+blocs exclusifs) | page mecanique 3 761 car. | [MANQUANT] |
| Legendary stones (power/tp) | 2 977 car. | [PORTE items, effets clic-droit a verifier] |
| Clicker | 3 101 car. | [MANQUANT] |
Fluides : angelic water, fake water (+sulfuric vu en textures), extrapolated bucket [PORTES MCreator ;
portage PalaFluids 189 l. UNVERIFIED]. Spikes (amethyst_spike PORTE + textures spike titane/bois/diamant),
slime pads (bleu/rouge/orange PORTE slime_pad), cave_block [PORTE], tranny [PORTE], xp_bush/xp_berries
[PORTES], dynamites + 3 guns (dynamite, endium, ninja) [items MCreator, portage DynamiteGunItem 39 l.].
### 1.6 Plantes (4 cultures + XP)
fikutsi/eggplant (4 stades), kiwano (5 stades), okami (4 stades), orange_blue (5 stades) :
blocs de stades PORTES (PalaBlocks) + events pousse/casse (PalaCropEvents 131 l., procedures
BreakBlockUnderCrops* portees partiellement). Fruits + graines PORTES en items. ECART : tables de loot
graines/fruits, chance nugget endium (metier fermier), cuisson eggplant→lingot amethyste (wiki minerais).
Crusher consomme : eggplants, chervil, kiwanos, orangeblues. ECART VERIFIE (grep 2026-10-18) :
aucun CUSTOM:Chervil dans CUSTOM_MAPPING.json (0 match) — le chervil wiki n'existe pas dans
le portage (ni item ni bloc). TODO:chervil : TRANCHE M3 (grep+legacy) : les textures chervil.png/chervils.png sont deja
utilisees par okami (seeds=fruit, VERIFIE models), aucun asset okami* n'existe, et le mapping
crusher legacy donne okami->titane = le role wiki du chervil ("donne des lingots de titane",
corpus VERIFIE). Hypothese : chervil (wiki actuel) == okami (legacy). RESTE : confirmation
visuelle en jeu (comparer chervils.png au fruit chervil du wiki) avant de clore. Raison : pas de
doublon cree sans preuve.
### 1.7 Metiers (4)
Mineur, Fermier, Chasseur, Alchimiste. Wiki VERIFIE : 4 metiers, ~100 niveaux chacun (page
"approfondir"), recompenses par palier (paladium + trixium + $), crafts debloques par niveau
(voidstone niv.2, excavatrice/pelle amethyste + /nether niv.3, outil construction niv.4,
cobblebreaker niv.5, ...), seuils cites : 2,3,4,5,8,10,12,13,14,16,18,20,50,60 (+30,35,40,55,65,80).
Tables XP : mineur (roche 0.5, charbon 4, quartz/obsidienne 6, lapis/redstone 15, emeraude 50 ;
fer 2, or 4, diamant 8, amethyste 12, titane 16, paladium 20), fermier (pain 1, tarte citrouille 4 ;
recoltes), chasseur (kills + farmlands), alchimiste (page evo 5 181 car., detail a reprendre).
Farmlands (serveurs metier hunter, 2 218 car.), rituel endium (niv.20 hunter, 2 707 car.),
spawner customise (3 867 car.), boss metier (7 601 car.), objets debloques par metier
(9 469 / 12 405 / 10 160 / 14 900 car.). [TOUT MANQUANT] dans le portage (aucune persistence XP).
Spec §3.6. Commandes : /farmland, /boss ? UNVERIFIED.
### 1.8 Factions (V12 rework)
Creation : 100 lingots paladium + GUI touche "F". Onglets (profil faction : nom, membres, date,
division, classement, ELO, arbre Global/Defense/Pillage). Grades custom (recrue...). Regions
(territory wrench, prioritaires sur grades). Classement ELO Bronze/Argent/Or/Platine/Diamant.
Event faction hebdo. Investissement (reserve d'or). Village (niveau 25+, parcelles, hotel de ville,
attaque 200 000 $ banque faction). Avant-poste AP. Prix parcelles (jusqu'a 9, saison Summer Rush).
BCC (base claim controller). [TOUT MANQUANT] sauf /f vclaim/vunclaim/attackvillage documentes.
Spec §3.7. Note : SimpleClans-like ? Rien sur le serveur actuel → a implementer.
### 1.9 Commandes (93 citees, boulons : Vanilla/essentials fournis par autres mods)
Deja fournies par les mods serveur (VERIFIE server/mods) : homes (/sethome /home /delhome —
easy_homes), rtp, waystones, market ? (non : pas de mod market → [MANQUANT]), trade [MANQUANT],
/msg /r (essentials-like ? [MANQUANT] probable), /tpa /tpahere /tpaccept /tpdeny /tptoggle [MANQUANT],
/spawn /lobby /hub, /money /pay /baltop /ah /hdv /shop (economie [MANQUANT]), /fly /feed /ec /hat /god ?
(par grade [CONFIG]), /warp /warzone, /minage /farmland /serv, /classement, /trixium, /palapass,
/palalag time, /trade, /lucky, /profil, /familiers→/pet, /sorts, /claim BCC, /f... (faction),
/end /nether /overworld [PORTES, perm 4 — ECART : /nether debloque metier mineur niv.3, pas OP].
/godpickaxe (POG info ?). Permissions par grade (Heros : 5 homes, 5 RTP, 18 slots enderchest, +5% XP,
anti-AFK 10 min, pseudo/couleurs). Spec §3.8. TODO:commandes-exactes : releve exhaustif des 93
vsalias vanilla — la liste brute contient du bruit (fragments fr). Raison : extraction regex.
### 1.10 Economie
Market (11 827 car. : achats/ventes, source principale), shop admin (prix fixes), trade securise
(3 744 car.), black market (5 marchands, quelques heures, 2 575 car.), boutique site (grades, lucky
blocks, points, cles), concours trixium, prix parcelles. Monnaie $ + banque faction. [TOUT MANQUANT].
Choix persistence §2 (SQLite : historique transactions + listings ; perfs : index vendeur+expiration).
### 1.11 Donjons (V11/V12)
4 donjons lies aux metiers (Talikus=fermier, Nimbria=mineur, Vitalys=chasseur, Manelios=alchimiste),
paliers + recompenses, 6 monstres chacun (pages ennemis), mini-boss/boss + table recompenses
(10 477 car.), sorts mana (page donjon 7 139 car. + page gameplay 14 sorts 11 805 car. — 14 sorts
debloques ?), runes (boost stats, 1 996 car.), arbre de competence (bouton Profil RPG, 1 987 car.).
[TOUT MANQUANT]. Structures = datapack jigsaw (§6 : faisable serveur).
### 1.12 Events & quetes
KOTH, totem (casser sommet→base), warzone (4 604 car.), boss (8 021 car.), boss metier, a-vos-marques
(farming minage quotidien), evenements hub (1 592 car.), lucky blocks (537 events ; commun 16 569 car.,
saisonnier 16 447 car., mensuel 25 107 car.), lucky pass/stats, palapass (quetes jour/mois, 2 489 car.),
14 sorts gameplay, market/events liees. [TOUT MANQUANT]. 537 events lucky = table de loot versionnee.
### 1.13 Montures, familiers, golem, divers
3 montures (19 937 car.), familiers + competences actives/passives (7 238 + 3 119 + 3 122 car.),
golem utilitaire, BCC, profil RPG, helios (HUD/client — [HORS CIBLE serveur, §6]), voice chat
([MANQUANT] : aucun mod voice sur le serveur → Simple Voice Chat a ajouter ou TODO),
blueprint (mod client — [HORS CIBLE]), enchantements vanilla (page ref) + moddes alchimiste
(table d'enchantement moddee — [MANQUANT]), reglements (hors code ; a lier via /wiki).
### 1.14 Comptes
Features wiki individualisables : ~210 entrees (7 minerais×3 + ~40 outils/armes + 24 armures +
6 sticks + 25 machines + 4 fluides + ~20 plantes/stades + 4 metiers×(xp+recompenses+unlocks) +
faction 8 pages + 4 donjons×3 + 14 sorts + 537 lucky events + ~30 commandes + economie 7 + events 8).
Couvert par le portage (items/blocs/recettes/worldgen de base) : ~120 entrees (~57 %, assets + registres).
Logique manquante : ~90 entrees (machines fonctionnelles, metiers, factions, economie, donjons, events).
TODO explicites dans ce document : 11 nommes (voir §7).
## 2. Architecture (choix tranches, 3 lignes chacun)
Base gardee : MOD FABRIC serveur (package fr.surviepote.pala, loader 0.20.0, API 0.163.0+26.3,
Java 25). Pourquoi : (1) le serveur fait deja tourner pala-0.2.1.jar, compat immediate ;
(2) Fabric Events + Brigadier + Components couvrent machines/metiers/GUI sans client ;
(3) le worldgen datapack existant est conserve tel quel.
2.1 Modules (packages) :
- core (config, permissions, cooldowns, text utils), content (items/blocs isomorphismes pala:* existants),
worldgen (datapack conserve), machines (1 BlockEntity abstraite + recettes JSON par machine),
jobs (XP metiers + gates de crafts), factions (claims, grades, regions, ELO, village),
economy (market, trade, shop, banque faction), dungeons (structures + boss + sorts mana + runes),
events (KOTH/totem/boss/lucky/palapass), compat (migration NBT legacy, IDs historiques en commentaire).
Pourquoi : (1) chaque systeme wiki = 1 package testable isole ; (2) les milestones §5 mappent 1:1 ;
(3) compat isole la dette MCreator du code propre.
2.2 Persistence : SQLite (1 fichier pala.db via JDBC sqlite, pool 1 connexion + WAL) pour
joueurs (jobs XP, homes metier ? non : homes=easy_homes), factions, market listings, transactions,
palapass, cooldowns ; YAML (config/) pour balances (prix, XP tables, seuils) afin d'editer sans SQL.
Pourquoi SQLite : (1) zero service a operer vs MySQL, backup = 1 fichier ; (2) WAL + index
(vendeur, expiration) absorbent market + XP sans lag tick ; (3) requetes jointes ELO/banque
impossibles proprement en YAML a 100+ joueurs. YAML pour la balance : (1) reload /pala reload
sans restart ; (2) diff git lisible ; (3) le staff edite sans SQLite.
2.3 Events : Fabric API events serveur (ServerTick, ServerLivingEntityAfterDeath, UseBlock/Item,
ServerPlayConnectionJoin) + rien d'autre. Pourquoi : (1) zero dependance ; (2) granularite suffisante
(XP minage = break event + gates) ; (3) perfs : handlers courts, batch DB hors tick (file d'ecriture
toutes les 5 s). Regle perfs : aucun I/O SQLite sur le thread tick ; caches ConcurrentHashMap + flush.
2.4 Resource pack serveur : generer pala-rp.zip depuis textures/ (891 PNG) avec script
tools/build_rp.py (VERIFIE a ecrire M0) : items → modeles retexturant des items vanilla
(choix : composants custom + resource pack ; blocs custom : mappes sur blocs vanilla via CIS-like ?
non — §6). server.properties resource-pack pointe le zip ; require-resource-pack=false (Bedrock-like
non concerne). Pourquoi : (1) textures d'origine obligatoires, zero placeholder ; (2) sans mod client ;
(3) versionne avec le mod (hash dans pala.db config pour forcer maj).
## 3. Specifications par systeme
### 3.1 Crafts (patterns exacts VERIFIES depuis elements/*.mod.json)
Gabarits standards (9 slots, ordre ligne par ligne) — exemples lus :
- Plastron amethyste : [L,L,L / L,L,L / L,_,L]... exact : slots 0,2,3,4,5,6,7 = lingot (l. recipe ci-dessus).
Forme : haut "L_L / LLL / LLL"? Releve : [A,_,A / A,A,A / A,A,A] (slots: 0=A,1=_,2=A,3-8=A).
- Casque : [A,A,A / A,_,A / _,_,_] ; Bottes : [_,_,_ / A,_,A / A,_,A] ;
Jambières : [A,A,A / A,_,A / A,_,A].
- Epee : [A / A / stick vanille] vertical centre-gauche ? Releve : slots 0=A,1=A,3=A,4=STICK,7=STICK
→ colonne hybride (lame 2 haut + manche decale). Hache : [A,A,_ / A,S,_ / _,S,_].
- Marteau : [A,A,A / A,A,A / _,stick,_] (6 lingots + stick).
- Bloc→9 lingots : shapeless 1 bloc → 9 (retstackSize 9 VERIFIE).
- Mixed coal amethyste → 8 : [stickPala,A,stickPala / A,COAL,A / _,A,_].
Regle : les 90 recipe elements font foi pour TOUS les crafts standards ; le porteur data/*.json
(95 fichiers) est la spec executable. TODO:crafts-machines : patterns palamachine/crusher/forge
non listes en texte (images seules, OCR rejete) → releve JEI en jeu avant M4. Raison : §0 OCR.
Stats outils (portage, VERIFIE PalaToolMaterials) : amethyste pick 1164 dura/6.0 ; epee 1999/6.0 ;
titane pick 1741/7.0 ; paladium pick 3072/9.0, epee 4999/9.0 ; endium 4595/11.0 ; POG level25 126.8 (!).
A reconciler avec wiki (epee amethyste 7 dmg/1999) en M1 (tableau d'ecarts genere par test).
### 3.2 POG — spec
Item unique id pala:pickaxe_of_the_gods + composant custom POG_LEVEL (int) + POG_XP (long) au lieu
de 26 items separes (migration §4 convertit les 26 legacy vers le composant). Craft VERIFIE wiki :
1 pioche amethyste + 1 pioche paladium + 1 pioche titane + 2 sticks paladium (pattern UNVERIFIED →
releve JEI). Non enchantable (flag). Tooltip : "Niveau N" + "XP : x / y". Paliers wiki (table §1.2
jusqu'a 35 + 36+ a completer depuis la page). OS (one-shot) par palier : table de correspondance
bloc→niveau min (neige/feuilles 11+, netherrack/cactus 21+, terre/sable/gravier/glace 26+, laine 31+).
Gain XP : par bloc casse (balance YAML, defaut 1 ; minerais moddes bonus). Seuil pre-rempli depuis
les stats MCreator levelN (vitesse/enchant values comme courbe de reference UNVERIFIED a valider).
Test : M2 donne POG niv.1, casse 50 roches, assert niveau>1 (commande /pala debug pog_xp).
### 3.3 Sticks PvP — spec
5 sticks, cooldowns YAML (defaut : heal 60 s, speed 30 s, jump 30 s, strength 120 s, god 600 s),
exclusivite : 1 buff stick a la fois (retire l'ancien). Unbreaking III max (cap enchanteur).
Effets : heal (regen instant partielle), speed II 30 s, jump boost IV 30 s, strength I 15 s,
god (combo court + resistance). Permissions : pala.stick.<nom>. UNVERIFIED : durees exactes
(pas dans le texte extrait) → balance staff en M5.
### 3.4 Machines — spec
Framework commun : PalaMachineBlockEntity etendue (slots N config, progress, fuel map, recipe registry
JSON data/pala/machine/*.json : {inputs, fuel?, ticks, outputs, job_lock?}). GUI : menus serveur
(Generic 9xN) avec layout documente par machine — ex crusher wiki VERIFIE : slot 10 = fruits,
slot 19 = combustible, jauge 0-100 % slots haut-droite (bouteilles/items fantomes de progression),
stockage bas-droite ; le portage actuel (DispenserMenu 0/1/2) est un placeholder a remplacer.
Palamachine : crafter special (recettes non 3x3 vanilla ; GUI 9x5) — liste recettes = releve JEI
TODO:crafts-machines. Palafurnace : four ×1 + accepte mixed coals. Palaforge : alliages (green ?).
Grinder : recycle outils → fragments. Autocrafter : pattern memorise + redstone pulse. Alchemy :
table + enchantements moddes (liste page 2 864 car. a reprendre). Cobblebreaker : generateur pave
auto (tick lent, cap faction). Bow machine : arc→arc pala (stats). Controleur spawners : lie 1 spawner,
taux custom. Drawbridge : extension/retraction N blocs. Home remover : liste homes adverses (gates
faction/guilde). Croquettes : ble→croquettes. Receptacle wither : rituel invocation boss custom.
Chaudron/golem : multiblocs (detection structure + tables). Colle : crafts + throwables a effets.
Stockages : barils/drawers simples (ou deleguer a StorageDrawers existant — decision M4 :
ne pas reimplementer, integrer). Cooldowns/consommation : ticks serveur, pas de tile client custom.
### 3.5 Plantes & worldgen vivant
4 cultures, stades portes ; spec : sol requis, temps pousse (ticks aleatoires Oui/non), loot
(graines 1-2 + fruit 0-1 ; endium : fruit→chance nugget, taux YAML defaut 2 %), cuisson eggplant→
lingot amethyste (furnace + palafurnace), crusher : fruit→jauge→lingot (100 % = 1 lingot, 4 jauges
paralleles par metal). chervil : UNVERIFIED (grep elements a faire en M0). xp_bush : clic-droit
donne xp_berries (cooldown). Totems : fertilite (×2 vitesse rayon 9), fleuri (coquelicots).
Worldgen : placed features existantes + calibrer couches 6-10/deep ocean (TODO §1.1) + biomes
moddes : HORS CIBLE (BiomesOPlenty deja present ; structures donjons en datapack M8).
### 3.6 Metiers — spec
Table jobs.yml : 4 metiers, xp_sources (bloc→xp : roche 0.5, charbon 4, quartz/obs 6, lapis/redstone 15,
emeraude 50, fer 2, or 4, diamant 8, amethyste 12, titane 16, paladium 20 VERIFIE wiki ; fermier :
pain 1, tarte citrouille 4 + recoltes ; chasseur : table kills ; alchimiste : crafts), curve :
seuils 1-100 (formule defaut : xp(n)=100*n^1.5, a caler sur durees wiki UNVERIFIED), rewards :
niveaux 2-5+ (listes wiki : 10/15/20/25 paladium+trixium+$+findium ; crafts : voidstone,
excavatrice, /nether, outil construction, cobblebreaker) + paliers 10..80 a reprendre des pages.
Gates : crafts machines verrouilles par niveau (grinder/crusher-8/autocrafter/compressor-18...),
GUI metiers (inventaire + onglet bouclier → ecran custom : livrets + boutons paliers).
Farmlands/rituel/spawner/boss-metier : modules lies (portails ou warp + instances simples).
Commandes : /job(s), /jobs top. Permissions : pala.job.<metier>.
### 3.7 Factions — spec
Creation : /f create (100 lingots paladium consommes) ; GUI onglets (infos/membres/grades/regions/
techno/village/banque). Grades : CRUD + matrice permissions (casser/poser/coffres/inviter/claim...).
Claims : chunk-based, table SQLite (world,x,z,faction) + cache memoire + check dans break/place/use.
Regions : polygones intra-claim + wrench item, prioritaires. Tech tree : Global/Defense/Pillage,
noeuds YAML (couts ELOressources). ELO : +/− sur events (KOTH/totem/boss), ligues seuils YAML.
Village : niveau faction 25+, parcelles (max 9, prix page parcelles), hotel de ville, attaque
/f attackvillage (200 000 $ banque, cooldown 7 j). AP : zone geree staff. BCC : perimetre base.
Commandes /f * + /f vclaim/vunclaim/attackvillage (wiki). Permissions : pala.faction.*.
### 3.8 Commandes & permissions & economie — spec
Porter en commandes propres : /end /nether /overworld (garder, mais gates : /nether = mineur niv.3
au lieu d'OP — ECART corrige M0). Nouvelles : /pala (reload/debug), /jobs, /f, /market, /trade,
/shop, /money /pay /baltop, /tpa(+here/accept/deny/toggle), /rtp, /spawn /hub /lobby, /classement,
/palapass, /lucky, /trade, /profil, /pet, /sorts, /trixium, /palalag. Homes/teleports : DELEGUER a
easy_homes/waystones existants (ne pas reimplementer). Economie : market (listings SQLite, taxe 5 %,
expiration 7 j), shop admin (prix YAML), trade (GUI 2 joueurs + validation), black market
(rotation stock timer), boutique (hors jeu : stub API + cles/lucky manuels). Cooldowns : map
centralisee Cooldowns.java (ticks). Permissions : LuckPerms absent → systeme interne
rangs YAML (defaut/heros/...) + noeuds pala.* ; TODO:luckperms : evaluer ajout du mod en M7.
Raison : 93 commandes citees incluent grades boutique ; scopes exacts page "par grade" (5 homes,
5 RTP, 18 enderchest, +5 % XP, anti-AFK 10 min).
### 3.9 Donjons/events/quetes — spec
Donjons : 4 structures datapack (nbt + jigsaw simplifie), portails d'entree gates par metier,
mobs custom (attributs YAML : 6/donjon), boss + table recompenses (page 10 477 car.), sorts mana
(14 : couts/effets/durees — pages sorts), runes (slots equipement + bonus), arbre competence
(points/palier). Events : KOTH (zone + timer + points ELO), totem (regen + casse sommet→base),
warzone (region PvP), boss (spawn timer + annonces), boss metier, a-vos-marques (classement farming
quotidien minage), concours trixium. Lucky : table lucky_events (537 entrees versionnees ;
familles commun/saisonnier/mensuel ; poids YAML). Palapass : quetes jour/mois + paliers.
Familiers (actifs/passifs YAML), montures 3 (stats + crafts selles), golem (portage + IA taches).
## 4. Migration depuis le mod Fabric actuel
GARDER : tous les ids pala:* (items/blocs — compat monde : les blocs poses restent valides),
worldgen datapack + PalaWorldgen, 95 recettes JSON, textures/ (source du resource pack),
PalaToolMaterials/PalaArmorMaterials (apres reconciliation wiki), events utilitaires (crops...),
/end /nether /overworld (avec gates).
JETER/REECRIRE : PalaMachineBlockEntity fourre-tout → framework §3.4 (BE par machine + NBT versionne ;
migration : BE legacy {Items[9], Progress} → nouveau format avec "v":2, slots remappes, Progress conserve) ;
26 items POG → 1 item + composants (migration : levelN → POG_LEVEL=N, XP=seuil(N)) ;
procedures MCreator restantes (63) → events propres ; GUI MCreator (2) → menus serveur ;
commandes OP → permissions.
Compatibilite mondes existants : registres inchanges ⇒ chunks intacts ; NyI : BE crusher/furnace
legacy migrees au premier tick (log + /pala migratestats). Raison : les ids stables sont le contrat.
## 5. Milestones testables (criteres executables)
M0 Socle (config, SQLite, /pala reload, permissions, resource-pack gen + POG→composant, gates /nether).
OK si : ./server/start.sh → "Done" 0 erreur registry ; /pala reload → "config re chargee" ;
joueur sans metier /nether → refuse, mineur niv.3 → teleporte.
M1 Minerais+outils (reconciliation stats wiki, worldgen couches, 95 crafts, smelting).
OK si : give chaque minerai, craft epee/casque/marteau en table (screenshots), /give POG craft manuel OK,
prospect 1000 blocs : chaque minerai present dans ses couches (compteur /pala orestats).
M2 POG (XP, niveaux, OS, tooltip). OK si : 50 roches → niveau>1 ; OS neige au niv.11 ; non enchantable.
M3 Plantes+crusher (pousse, loot, cuisson, crusher jauges+fruits+mixed coals). OK si : cycle complet
graine→fruit→lingot via crusher uniquement aux mixed coals ; gate niveau 8 fermier.
M4 Machines batch 1 (palafurnace, palaforge, palamachine crafts releve JEI, grinder, cobblebreaker,
compressor-18, bow machine). OK si : chaque GUI s'ouvre, chaque recette input→output constatee.
M5 Metiers (XP 4 metiers, paliers, recompenses niv.2-5, gates, GUI). OK si : casser charbon → +4 XP ;
/jobs affiche ; recompense auto au passage de palier.
M6 Factions (create 100 lingots, claims, grades, regions, ELO, village niv.25, attaque 200k$).
OK si : 2 factions, claim protege (adversaire bloque), ELO evolue sur KOTH simule.
M7 Economie (market, trade, shop, money, black market rotation). OK si : mise en vente→achat→taxe 5 % ;
trade double-validation ; expiration 7 j.
M8 Donjons (1 donjon vertical slice Nimbria : structure+mobs+boss+recompenses+runes+sorts).
OK si : run complete en survie, boss loot table conforme.
M9 Events/quetes (KOTH, totem, boss timer, lucky 537 entrees chargees, palapass jour).
OK si : tirage lucky 100× distribution conforme aux poids (±10 %) ; palapass valide une quete.
M10 Migration+release (convertisseurs legacy, docs staff, perfs : tick < 2 ms hors events, DB WAL).
OK si : monde pala-0.2.1 charge : 0 bloc manquant, BE migrees, POG converties.
## 6. Risques + infaisable en pur serveur (sans mod client)
R1 Ghost blocks (apparence dynamique d'un autre bloc) : INFASABLE pur serveur — le client decide du
rendu par blockstate ; aucun paquet serveur ne change le modele d'un bloc arbitraire. Alternative :
retexturage statique (1 ghost = 1 faux bloc fixe) ou mod client. TODO:ghost : spec degeneree M4.
R2 Touche "F" faction / boutons HUD (Helios) : INFASABLE — le serveur ne capte pas de touches custom.
Alternative : /f GUI + raccourcis negociables (sneak+clic). Helios/blueprint/voice : cote client ou
mods existants (voice = ajouter Simple Voice Chat, non present).
R3 Modeles 3D/blocs vraiment nouveaux : le serveur ne pousse que des blockstates existants ; le visuel
custom passe par le resource pack (retexturage d'items/blocs vanilla + notes). Les 891 PNG sont
reutilises mais certains rendus MCreator (spikes multidirectionnels, portails animes) seront simplifies.
R4 537 lucky events +textures mensuelles : faisable, risque = volume de data entry (erreurs) → import
versionne + test distribution M9.
R5 Claims vs mods existants (waystones/rtp/veinminer/treeharvester) : bypass potentiels (miner a travers
claim via veinminer). Mitigation : API d'autorisation + patch compat ou desactivation ciblée.
R6 Perfs : 100+ joueurs + machines tickees + market. Mitigation : BE tick cap + batch DB (§2.3) ;
budget : < 2 ms/tick M10.
R7 OCR crafts : voir §0 (rejete). Risque residuel : patterns machines faux → releve JEI obligatoire
avant M4 (TODO:crafts-machines).
## 7. Compte-rendu de mission (fin = ce fichier + ce resume)
- Features wiki couvertes (entree CDC) : ~210 (§1.14). Portage existant : ~120 (assets+registres).
Logique specifiee restant a implementer : ~90. Couverture spec : 100 % des pages FR Java ingestionnees.
- TODO explicites : 15 nommes (pog-os CLOS par M2-complement : OS 11/21/26/31 implemente ;
grep : ocr-fiable, worldgen-couches, broadsword,
crafts-machines, chervil, commandes-exactes, ghost, luckperms, runclient, mcp-ocular, shebang,
java-home, craft-xp, smelt-xp, crusher-xp, market-expiry)
+ 4 points UNVERIFIED (seuils XP 6-100, durees sticks, valeurs armures wiki vs MCreator,
patterns machines images).
- Methode : WAVES (crawl 137 articles → stub JS isoles → re-crawl navigateur → verifie) + ETERNAL CODE
(tout chiffre du CDC cite sa preuve : fichier+ligne ou page wiki ; OCR teste et rejete au lieu d'etre cru).
- Prochaine action recommandee : M0 — ecrire tools/build_rp.py + socle config/SQLite/permissions, puis
releve JEI des crafts machines (TODO:crafts-machines) avant tout code machine.
## 8. Tests locaux (obligatoire)
- Serveur : nix-shell <repo>/shell.nix --run "bash <repo>/server/start.sh" (le shebang
#!/bin/bash est INEXECUTABLE sur hôte NixOS : /bin/bash n'existe pas — VERIFIE
"bad interpreter". Toujours invoquer via `bash start.sh`. TODO:shebang : passer en
#!/usr/bin/env bash. Raison : NixOS sans /bin/bash.)
Boot partiel VERIFIE : mods charges, "[SurviePote] Pala initialise." dans le log, arret
ensuite sur EULA (eula=false d'origine). EULA accepte en local pour le test
(eula.txt remis a false apres — git status doit rester clean).
Boot complet VERIFIE 2026-10-18 23:16 (log server/logs/latest.log) : "Done (0.391s)!",
pala 0.2.1 charge + "[SurviePote] Pala initialise.", 84 biome modifications, 0 erreur
registry/datapack, 0 exception. Seuls avertissements : 8 WARN mixin refmap tiers
(biomesoplenty/clumps/glitchcore/patpat, message standard "ignorer hors dev").
Serveur arrete apres test (port 25565 libere), eula.txt restaure a false,
server.properties/packetfixer.properties restaures (le boot ne change que leurs timestamps).
- M0 LIVRE 2026-10-18 23:35 (commit 86cc852, pousse) : package core (PalaConfig JSON rechargeable
22 entrees minerXp wiki, PalaStorage joueurs JSON + flush 5 s, PalaPermissions rangs+noeuds,
PalaJobs moteur XP/niveaux, PalaCore lifecycle), /pala reload|info|rank, /jobs,
/nether gate mineur niv.3, hook XP dans PalaToolEvents. VERIFIE : ./gradlew compileJava+build
SUCCESSFUL, boot serveur Done (0.355s), "Pala core charge (config creee par defaut)",
config/pala.json generee (6 cles), 0 ERROR/Exception au log. Deploys : server/mods/pala-0.2.1.jar.
- Build mod : TOUJOURS via shell racine avec JAVA_HOME redirige (le shell.nix positionne
JAVA_HOME sur un JDK 8, Gradle 9 exige 17+). Commande VERIFIE :
cd mods/pala && nix-shell <repo>/shell.nix --run "export JAVA_HOME=$(dirname $(dirname
$(readlink -f $(which java)))); ./gradlew build". TODO:java-home : fixer shell.nix racine
(export JAVA_HOME jdk25) pour ne plus bricoler. Raison : decouvert en testant (3 essais).
- M1 LIVRE 2026-10-18 23:40 : worldgen amethyste/titane calibre wiki (size 8, trapezoid 6-10,
placed deep 25-40 + injection 4 biomes deep ocean), /pala orestats (scan + histo Y).
VERIFIE : build SUCCESSFUL, boot Done (0.345s), config persistee ("config existante"),
0 ERROR/Exception/datapack au log. UNVERIFIED en jeu : /pala orestats (pas de joueur connecte
pendant le test) + distribution reelle sur nouveaux chunks.
- M2 LIVRE (partiel) 2026-10-18 23:45 : tooltip XP POG via composant LORE serveur
(ItemLore verifie au javap ; niveau + "XP : x / seuil" maj a chaque casse, ligne MAX au niv.25 ;
conserve sur evolution). Craft POG VERIFIE dans data (pattern "#AB/C/C" = 1 pioche amethyste +
1 paladium + 1 titane + 2 sticks paladium, conforme wiki). Seuils legacy conserves (25..3500).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED : affichage tooltip en jeu
(pas de joueur). TODO:pog-os CLOS (M2-OS livre : one-shot 11/21/26/31).
- M2-OS LIVRE (commit 5141e38) : one-shot POG aux paliers wiki 11/21/26/31 via
PlayerBlockBreakEvents.BEFORE (destroyBlock + annulation casse normale). ECART documente :
pas d'XP POG/mineur sur les blocs one-shottes. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
- M3 LIVRE 2026-10-18 23:51 : crusher fidele legacy (fruits->lingots : eggplant/amethyste,
okami/titane, kiwano/paladium, orange_blue/nugget endium ; fuels = 5 mixed coals, creative infini,
conso 20 %/tick, luck 1/128, auto-generate a 1.0 ; NE cuit plus de minerais) + furnace accepte
mixed coals + gate fermier 8 a l'ouverture du crusher. VERIFIE : build SUCCESSFUL, boot Done,
0 erreur. UNVERIFIED en jeu : cycle fruit->lingot avec joueur, gate a fermier<8.
- M5a LIVRE 2026-10-19 ~00:30 : XP chasseur (AFTER_DEATH verifie au javap : passifs 14@0, poulpe
10@0, creeper 40, wither 1000, gates niveau) + fermier (ble 1.5@0, patate 2@2, carotte 2.5@2,
pasteque 4@3, citrouille 5@6, eggplant 10@8, okami 20@12, kiwano 50@16 ; maturite age=max).
Config auto-complete les vieilles pala.json (detection par cles, pas par null : Gson
utilise le constructeur no-args donc les champs absents gardent leurs defauts — VERIFIE
au boot : 13 cles persistees). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu : gains XP avec joueur (pas de joueur connecte).
TODO:craft-xp, TODO:smelt-xp (aucun event serveur dans fabric-api, mixin requis),
TODO:crusher-xp (attribution : stocker UUID poseur, M5b).
- M4a LIVRE (meme vague) : recettes machines data-driven (crusherResults/Fuels, furnaceSmelts/Fuels
en config, /pala reload live, ids invalides ignores). VERIFIE : build SUCCESSFUL.
- M1-complement LIVRE 2026-10-19 ~01:55 : les 3 marteaux font du 3x3 (amethyst/titane oublies
par le portage, seul le paladium avait l'effet). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
- M-TEXFIX LIVRE 2026-10-19 ~02:20 : fix damier de TOUS les items (il manquait les definitions
assets/pala/items/*.json exigees en 26.3 par ClientItemInfoLoader — 162 generees, une par
models/item). VERIFIE en jeu : minerai texturé en main, epee orange complete, 0 damier
(visfix1/visfix2). Lecon : models/item seuls ne suffisent plus ; le missing est silencieux.
- M-cmd-util LIVRE 2026-10-19 ~02:45 : /craft (table portable via CraftingMenu+SimpleMenuProvider,
APIs verifiees javap) + /spawn (tp spawn overworld, delai wiki 3 s via END_SERVER_TICK, file
UUID). Refacto teleport() vers teleportPlayer(). VERIFIE : build SUCCESSFUL, boot Done,
0 erreur. UNVERIFIED en jeu (pas de joueur).
- M-tpa LIVRE 2026-10-19 ~02:50 : /tpa <joueur> (demande, expire 60 s) + /tpaccept (tp dans 3 s
wiki + invisibilite 15 s wiki via MobEffectInstance, APIs verifiees javap) + /tpdeny.
Bug corrige avant build : tp vers la cible (TpaTask), pas vers soi-meme. VERIFIE : build
SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (2 joueurs requis).
- M-msg LIVRE 2026-10-19 ~02:55 : /msg <joueur> <texte> + /r <texte> (wiki commandes, suivi
dernier expediteur par destinataire, double sens). VERIFIE : build SUCCESSFUL, boot Done,
0 erreur. UNVERIFIED en jeu (2 joueurs requis).
- M-infos LIVRE 2026-10-19 ~03:00 : /serv /wiki /discord /boutique /couleurs (wiki commandes,
liens en config : serverName/wikiUrl/discordUrl/shopUrl). VERIFIE : build SUCCESSFUL, boot
Done, 0 erreur, 17 cles config persistees (auto-complete +4). UNVERIFIED en jeu.
- M-eco LIVRE 2026-10-19 ~03:05 : /pay (transfert, fonds verifies) + /baltop (top 10 via
PalaStorage.topMoney) + /balance (solde). ECART : connectes uniquement (wiki suggere pseudo
offline). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (2 joueurs).
- M-grinder-1 LIVRE 2026-10-19 ~03:10 : 3 blocs (block/casing/frame, textures legacy cablees)
+ 9 items (5 patterns, socket, orb_knockback, broadsword +9, fastsword +7 ; vitesses
UNVERIFIED) + 30 assets (blockstates/models/definitions items/lang 221 cles) + 3 crafts
(pattern frame UNVERIFIED : positions titane non visibles wiki). GUI + multibloc + tetes +
modifieurs : M-grinder-2/3. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED
visuel (session client M-grinder-2).
- M-grinder-2 LIVRE 2026-10-19 ~03:15 : detecteur multibloc PalaGrinder (3x3x3, totaux wiki
20F/5C/1B/1L recalcules, 4 rotations, lave source exigee) + diagnostic clic droit sur le
coeur. GUI + tetes + modifieurs : M-grinder-3. VERIFIE : build SUCCESSFUL, boot Done,
0 erreur. UNVERIFIED : orientation exacte du B (tableau wiki ambigu).
- M-grinder-3 LIVRE 2026-10-19 ~03:20 : BE dediee PalaGrinderBlockEntity (inventaire 9 slots,
jauge fuel 0-100 en lingots paladium, persistee) + GUI DispenserMenu + coeur avec ticker +
diagnostic deplace dans useWithoutItem (le callback UseBlockCallback ne doit pas renvoyer
SUCCESS sinon la GUI ne s'ouvre pas — lecon). Tetes + modifieurs : M-grinder-4 (pas de
textures legacy pour les tetes). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu (GUI + fuel).
- M-grinder-4 LIVRE 2026-10-19 ~03:30 : page grinder relue en live (18 875 car., images des
crafts non OCRisables fiablement) : fix jauge 1 lingot = +1 (wiki "jusqu'a 100 lingots") +
3 recettes patterns shapeless (socket 4p+4t, hammer marteau+4p+4t, lingot 4p+5t ; positions
UNVERIFIED -> shapeless). ECART : patterns broadsword/fastsword non craftables (outils
titane inexistants), patterns pioche/hache/pelle/epee/bloc sans textures legacy. Tetes +
modifieurs + recyclage armure : M-grinder-5. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
- M-mixin LIVRE 2026-10-19 ~03:25 : pipeline mixin sans loom (pala.mixins.json + entree
fabric.mod.json, pas de refmap : noms Mojang non obfusques) + 2 mixins : craft (pain 1 XP
niv.1+, tarte citrouille 4 XP niv.11+, via ResultSlot.checkTakeAchievements) et cuisson
(4 viandes 10 XP chasseur niv.1+, lapin exclu, via FurnaceResultSlot.onTake). Signatures
verifiees au javap. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur mixin. UNVERIFIED en
jeu (prise en main : seul un craft/fourneau avec joueur le prouvera). TODO:craft-xp et
TODO:smelt-xp CLOS.
- M-recompenses LIVRE 2026-10-19 ~03:35 : lots de palier wiki (pages Recompenses relues en
live) : formule 5N pala + 5N trixium + 500N $ (niv.2-19), 105/105/10500 (niv.20) + extras
existants (findium, amethyste, graines, pepite endium). ECART : lots speciaux inexistants
(voidstone, excavatrices...) => TODO:recompenses-items. + furnaceSmelts completee (6 minerais).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (level-up joueur).
- M-xpcurve LIVRE 2026-10-19 ~03:40 : seuils XP wiki VERIFIES (table mineur niv.2-20 :
22 123 a 2 594 953, points = milliers) en config data-driven + formule au-dela ; XP
"broyer" fermier au crusher (3/4.5/6, attribution dernier ouvreur, ECART documente) ;
pas d'XP au toucher de soie (wiki) ; hammerArea confirme sans XP (destroyBlock direct).
TODO:crusher-xp CLOS. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur, 19 cles config.
UNVERIFIED en jeu.
- M-candy LIVRE 2026-10-19 ~03:45 : 7 bonbons (textures legacy 32x32) + effet clic droit :
vert = +50 000 XP fermier, rainbow = +50 000 aux 4 metiers (wiki VERIFIE). Autres couleurs
= mapping non publie (TODO:mapping-bonbons, sans effet). VERIFIE : build SUCCESSFUL, boot
Done, 0 erreur, lang 228 cles. UNVERIFIED en jeu.
- M-mineur-xp LIVRE 2026-10-19 ~03:55 : page Evolution mineur relue en live : gates niveau
par bloc (minerLevels, niv.1/5), XP cuisson (8 entrees : brique 0.1@1, charbon 6@1, fer
8@2, or 30@4, amethyste 35@5, titane 50@7, paladium 150@10, green 200@12, centralisees
dans PalaJobs.onSmeltTake), XP hammer par bloc (choix du chiffre 4,5 = 9x0,5, contradiction
wiki documentee), bonbon jaune = mineur (mapping partiel : restent bleu/rose). ECART :
"Particules" non identifie (non implemente). VERIFIE : build SUCCESSFUL, boot Done,
0 erreur. UNVERIFIED en jeu.
- M-rtp LIVRE 2026-10-19 ~04:00 : /rtp (wiki commandes : aleatoire overworld, cooldown 15 min,
position safe via heightmap, 16 essais, refuse ocean). ECART : pas de limite "3/version".
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur, 23 cles. UNVERIFIED en jeu.
- M-faction-1 LIVRE 2026-10-19 ~04:10 : socle factions (PalaFactions, pala-factions.json,
flush tick + arret) + /f create (100 lingots, GUI touche F infaisable — R2) / info /
invite / join / leave / disband. Lecon : Inventory.items prive en 26.3 -> getItem(i).
Grades/claims/elo : M-faction-2. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu (2 joueurs).
- M-faction-1b LIVRE 2026-10-19 ~04:15 : /f kick + /f transfer (trou bouche : le chef ne
pouvait pas partir). Lecon : compter les parentheses d'un chainage brigadier au python
(le patch fuzzy fusionne les lignes). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu.
- M-faction-1c LIVRE 2026-10-19 ~04:20 : /f desc (chef, 140 car., sans speciaux — wiki) +
/f help. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-2 LIVRE 2026-10-19 ~04:30 : claims de chunks (/f claim/unclaim/unclaimall,
quota factionMaxClaims=10 UNVERIFIED) + protection stricte (membres+OP, wilderness libre).
Lecon : ResourceKey.location() n'existe plus en 26.3 -> identifier(). Grades/regions/elo :
M-faction-3. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-2b LIVRE 2026-10-19 ~04:40 : commandes claim manquantes du commit precedent
(trou detecte a la relecture) + /f list + /fc (chat faction, membres connectes).
Lecon : chirurgie brigadier par indices de lignes + parencheck.py avant build.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-2c LIVRE 2026-10-19 ~04:50 : /f sethome (chef) + /f home (membres, multi-dim).
ECART : sans grades (GUI, M-faction-3), regle simplifiee chef/membres. Lecon : la ligne
/f list finissait par 4 parentheses (register inclus) — assert endswith trompeur.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-3a LIVRE 2026-10-19 ~05:05 : grades commandes (co-chefs viceLeaders,
/f promote/demote chef-uniquement, invite/kick/unclaim/sethome elargis aux co-chefs,
/f info + /f help maj). ECART : perms fines GUI en M-faction-3b. Lecon : bloc
argument-then = 5 parentheses finales (getPlayer inclus), verifier au parencheck.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-compress LIVRE 2026-10-19 ~05:10 : COMPRESSED_PALADIUM + COMPRESSED_TITANE (textures
legacy, pattern legacy 8 ore_blocks + 1 ingot comme amethyste, lang, onglets).
ECART : pas de recette decompression (legacy n'en a pas), guardian stone + broken
spawner absents du legacy (textures a extraire, TODO:M-spawner). VERIFIE : build
SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-3b LIVRE 2026-10-19 ~05:15 : elo PvP inter-factions (base 1000, +10/-10,
plancher 0, hook AFTER_DEATH existant) + /f top (tri elo) + elo dans /f info/help.
ECART : formule exacte Paladium non publiee (UNVERIFIED). VERIFIE : build SUCCESSFUL,
boot Done, 0 erreur. UNVERIFIED en jeu (2 joueurs).
- M-gadget-1 LIVRE 2026-10-19 ~05:20 : analyseur de chunk (ID 4975, durabilite 5 wiki,
texture legacy, scan 16x16 top 10 + total, sans craft ECART). Lecon : patch
chevauchant la fin d'une methode mange sa signature — verifier la queue du fichier.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-2 LIVRE 2026-10-19 ~05:25 : grappin (ID 4979, dura 255, raycast 32,
teleport TeleportTransition, cooldown 20t, texture legacy, sans craft ECART).
Lecon : teleportTo 6-args n'existe plus en 26.3 -> pattern maison TeleportTransition.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-3 LIVRE 2026-10-19 ~05:30 : bocal a poissons (ID 3100, slot casque =
cecite, texture fish legacy, sans craft ECART). ECART : poisson a l'ecran infaisable
sans mod client. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-4 LIVRE 2026-10-19 ~05:35 : bottes lestees (ID 4977, legacy VERIFIE :
explosion BREAK |vel_y| + 0.25, "EXPLOSION !!!!", usure 1, dura 16, ArmorItem existant
reutilise). ECART : puissance approximee h/8 + 1, annulee en claim d'autrui.
Lecons : ExplosionInteraction.BREAK n'existe plus en 26.3 -> TNT ; fallDistance est
double. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-5 LIVRE 2026-10-19 ~05:45 : pierre stats (ID 5021, connectes) + epee couac
(ID 5035, stats paladium, son a chaque coup via AFTER_DAMAGE). Lecons : Entity.
getServer() n'existe plus en 26.3 -> sl.getServer() ; CHICKEN_AMBIENT remplace par
variants -> CHICKEN_EGG. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu.
- M-gadget-6 LIVRE 2026-10-19 ~05:50 : sac de couchage (ID 5024, bloc + loot self +
clic droit = RespawnConfig via RespawnData.of, texture legacy). Sans craft ECART.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-7 LIVRE 2026-10-19 ~05:55 : banane (ID 5030, vitesse III 15 s via
Consumable+ApplyStatusEffects, nutrition ECART). Lecon : effets nourriture 26.3 =
food(FoodProperties, Consumable), plus de Builder.effect(). VERIFIE : build
SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-gadget-8 LIVRE 2026-10-19 ~06:05 : cagoule (ID 5023, 0 armure, UNBREAKABLE) +
masque de garde (ID 5029, stats fer, repair iron). ECART : textures portees absentes
(pas d'equipment JSON). Incident : 1er boot crash (Enchantment value 0 rejete en
26.3 -> enchant 1). Lecon : boot-systématique attrape ce que la compile ne voit pas.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-market-1 LIVRE 2026-10-19 ~06:15 : market commandes (/market sell/list/buy/cancel,
PalaMarket pala-market.json, money existante, hors-ligne credite, taxe 5 % §3.8 :
vendeur recoit 95 % floor au centime). ECART : pas de GUI, items sans NBT.
TODO:market-expiry : expiration 7 j + purge au flush (M7). Lecons : DefaultedRegistry
.get() -> Optional en 26.3 (getValue) ; Player.drop() supprime (-> ItemEntity) ;
ne jamais nommer une variable `net` (masque net.minecraft -> "double cannot be
dereferenced"). VERIFIE : build SUCCESSFUL, boot Done (0.381s), 0 ERROR/Exception
au log, jar md5 identique build/libs = server/mods = HEAD. UNVERIFIED en jeu
(2 joueurs : vente->achat->taxe).
- M-market-2 LIVRE 2026-10-19 ~06:20 : expiration annonces (createdAt, marketExpiryDays
=7 persistee VERIFIEE, purge au flush, rendu si vendeur connecte). TODO:market-expiry
CLOS. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (purge 7j).
- M-alchi-1 LIVRE 2026-10-19 ~06:30 : fiole (craft 5 verres, XP craft 0.2@1) + mixin
craft data-driven (alchemistCraft). Incident : 1er boot refuse (ingredients objets
{"item"} parses vide en 26.3 -> strings simples comme socketpattern). Lecon : boot
Datapack = validation JSON, toujours rebooter apres recette. ECART : positions craft
UNVERIFIED, extracteur sans texture (TODO:M-alchi-extracteur), arbes a seve absents
(TODO:M-trees). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-alchi-2 LIVRE 2026-10-19 ~06:35 : lightning potion (capture eclair via afterDamage
+ casque fer/or + fiole vanilla, 30 XP@1, degats conserves ; la boire = mort HARM 5).
Correction CDC : craft extracteur = sticks+core+planches+fake water (lingots+eau =
REPARATION palamachine). Lecon : variable locale `entity` masque le parametre (nommer
`drop`). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (orage).
- M-alchi-3 LIVRE 2026-10-19 ~06:40 : fondations chaudron (bloc ID 2931 + coeur 2933 +
support tank 2932, textures legacy, loot self, decoratifs v1). ECART : sans craft
(ressources non lisibles), logique crafts/rituels en TODO:M-alchi-cauldron.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-alchi-4 LIVRE 2026-10-19 ~06:45 : bibliotheque mystique (ID 2955, texture legacy,
loot self, decorative v1). ECART : nouveaux enchantements en TODO:M-enchants, sans
craft. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-enchants-1 LIVRE 2026-10-19 ~06:50 : voleur d'XP (datapack 4 niveaux + hook
afterDamage : 2 %/40, 4 %/50, 7 %/70, 10 %/90 XP vanilla). ECART : table moddee
absente (table vanilla/enclume//enchant). TODO:M-enchants partiel (bottler,
overwhelmed, unclaim, anti-chute infaisable serveur, fake-water sans degats).
VERIFIE : build SUCCESSFUL, boot Done (datapack accepte), 0 erreur. UNVERIFIED en jeu.
- M-enchants-2 LIVRE 2026-10-19 ~06:55 : Experience Bottler (datapack + redirection XP
minage vers fiole CUSTOM_DATA 1000 max + lore contenu + clic droit = recuperation).
ECART : mineur uniquement, outils de minage uniquement (pas armure/arme).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-enchants-3 LIVRE 2026-10-19 ~07:00 : amulette magma (ID 4849, incrustation main +
offhand sur plastron pala/endium, CUSTOM_DATA 1 slot, fire resistance au port).
ECART : moyen d'incrustation simplifie, Overwhelmed en TODO (slots +1).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-hunter-1 LIVRE 2026-10-19 ~07:05 : marteau des cavernes (niv.5, dura 256, craft
shapeless 1 marteau pala + 2 obsidiennes + 6 blocs titane ECART positions, texture
legacy). Usage ames : M-spawner (TODO). Sans gate craft (ECART).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-hunter-2 LIVRE 2026-10-19 ~07:10 : infernal knocker (niv.9, knockback x3 wither
tenu en main, sans degats/craft/durabilite ECART). Lecons : setDeltaMovement +
hurtMarked absents en 26.3 (-> push) ; EntityType.WITHER -> EntityTypes.WITHER.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu (wither).
- M-spawner-1 LIVRE 2026-10-19 ~07:15 : chercheur de spawners (ID 4795, scan 9 chunks
autour du joueur + coords, gate chasseur 10, dura 140 ECART, cooldown 5 s, texture
legacy). Lecon : onUseItem (clic air) n'a pas de pos -> sp.blockPosition().
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-pillage-1 LIVRE 2026-10-19 ~07:25 : dynamites (ID 4640/4641/4642, explosion
controlee raycast 32, exclusions obsidiennes+enclumes+claims, annulee eau, ninja
silencieuse). Menage : 3 DynamiteGunItem legacy supprimes (fireball non-wiki,
conflit id "dynamite"). ECART : chiffres degats, portes/trappes+hardened TODO,
sans craft. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-faction-4 LIVRE 2026-10-19 ~07:35 : /f map (carte ASCII 9x9 claims, permission
wiki "Voir les territoires", +help). Lecon : compter opens/closes PAR LIGNE au
script (jamais a l'oeil) ; le compteur cumule est biaise par les apostrophes
(strip quotes apres comments) ; javac = arbitre final. VERIFIE : build SUCCESSFUL,
boot Done, 0 erreur. UNVERIFIED en jeu.
- M-tp-zones LIVRE 2026-10-19 ~07:40 : /minage + /farmland (zones config "dim:x:y:z",
miningZone/farmlandZone persistee VERIFIEES, message si non configure). ECART :
positions = config admin (UNVERIFIED), bonus grades payants SKIP (boutique externe).
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-homes-1 LIVRE 2026-10-19 ~07:50 : homes personnels /sethome /home /delhome /homes
(persistes pala-players.json, limite maxHomesBase=3 ECART base non publiee, grades
payants SKIP). Lecon : markDirty() existait deja (doublon retire). VERIFIE : build
SUCCESSFUL, boot Done, 0 erreur, maxHomesBase persiste. UNVERIFIED en jeu.
- M-hunter-3 LIVRE 2026-10-19 ~08:05 : kills vanilla supplementaires (chevre 20@1,
perroquet 30@3, dauphin 35@5, tortue 40@8, panda 60@9 VERIFIES wiki ; moddees
SKIP M8, peche QTE SKIP). Fix structurel : mergeDefaults() fusionne les entrees
par defaut manquantes au chargement + resave (panda/goat VERIFIES en pala.json).
Lecon : killer l'ancien serveur AVANT de booter (session.lock orphelin), rm lock
si tue -9. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-hunter-4 LIVRE 2026-10-19 ~08:15 : knocker 4 degats EXACTS (attribut +3 mainhand
ADD_VALUE via DataComponents.ATTRIBUTE_MODIFIERS, core.component ; Item simple =
non enchantable wiki OK) + canne peche paladium (FishingRodItem dura 384 ECART
vitesse non quantifiee, craft shapeless connu, textures cast/uncast, overrides).
Lecon : shapeless 26.3 = ingredients STRINGS (objets {item} rejetes "List too
short", crash datapacks). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu (degats, peche, visuel cast).
- M-hunter-5 LIVRE 2026-10-19 ~08:25 : capture sword/stone (coup + pierre vide ->
capture type en CUSTOM_DATA vanilla, pattern POG ; pierre pleine -> relache
raycast + rend vide ; wither hors-overworld ECART "minages" ; epee sans craft
ECART ingrédients, pierre craftee 9 shapeless). Lecon : ENTITY_TYPE.getValue
retourne direct (@Nullable, pas Optional) ; CompoundTag.getString = Optional.
VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-hunter-6 LIVRE 2026-10-19 ~08:30 : etiquette endium (UseEntityCallback javap,
nom enclume, max 3 persistee, 4e retire surnom 1ere, sans craft ECART). Lecon :
sp.server prive -> sp.level().getServer(). VERIFIE : build SUCCESSFUL, boot
Done, 0 erreur. UNVERIFIED en jeu.
- M-fix-1 + M-roller-1 LIVRES 2026-10-19 ~08:35 : canne dura 384->128 (wiki outils
VERIFIE ; bonus loot sans chiffres SKIP TODO:chance-peche) + rollers (ArmorItem
pieds, tick transient +20% vitesse ECART + step 0.6->1.1, retrait auto, dura 128
ECART, sans craft). Decouverte : incrustation amulettes EXISTE (CUSTOM_DATA
plastron + magma fire ; hunter/voyance/combat/daemon/discretion = prochaine
vague, effets a lire). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu.
- M-hoes-1 + M-fix-2 LIVRES 2026-10-19 ~08:45 : 4 grandes houes (dura wiki
1999/4999/4999/4999, AoE 2x2/6x6/8x8/12x12 ancre ECART, gates fermier 2/12/16/20
FAIL, claims, -1 dura/bloc ECART, sans craft ; titane SKIP texture) + capture
sword +7 (attribut +6, comme knocker). Lecon : outils 26.3 = .hoe() sur
Properties (pas de HoeItem). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu.
- M-audit-1 LIVRE 2026-10-19 ~08:50 : stats outils pala corrigees wiki (pickaxe
2->8, shovel 11->7, axe 4.8->9, duras 3072->4999 x3 + hammer 16000->4999 ;
epee 8/4999 deja OK). Principe : wiki (public actuel) > legacy (prive ancien)
pour les chiffres. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur.
UNVERIFIED en jeu (degats effectifs).
- M-audit-2 LIVRE 2026-10-19 ~08:55 : 4 tiers restants (amethyst 5/4/6/1999,
titane 5.5/4.5/6.5/2999, green 8/7/9/4999, endium 9.3/10.3/4999 ; titane axe
dura deja 2999) + endium pick 3x3 (hammerArea) + gates usage endium (pick
@mineur20, axe @fermier20, BEFORE FAIL). ECART : treecapitator + no-break
endium TODO. VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED.
- M-palaforge LIVRE 2026-10-19 ~03:55 : bloc + BE partagee (3e mode) + gate mineur 14 +
table fonte wiki §3.2 (31 items, ratio floor usure) + fuels bois (+buche/planches/baton,
lave exclue : seau non rendu) + craft (1 findium + 4 furnaces + 4 briques, positions
UNVERIFIED) + assets/lang 231. ECART : green non fondu (pas de colonne wiki). VERIFIE :
build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu.
- M-afk LIVRE 2026-10-19 ~03:50 : kick AFK (wiki reglement/grades) : inactivite pos+rot
controlee chaque seconde, limite afkKickMinutes (defaut 10), bypass pala.afk.bypass.
Lecon : authlib absent du compileClasspath (getGameProfile inutilisable) -> PermissionSet
joueur (player.permissions()). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur, 20 cles.
UNVERIFIED en jeu (10 min d'inactivite requises).
- Client/mod : boucle visuelle OPERATIONNELLE 2026-10-19 (Xvfb :99 + runClient SDL x11 +
captures Java Robot + pilotage xdotool + vision_analyze) : menu, Select World, monde New World
(creatif, cheats), /give, pose de bloc, inventaire, F3+T. Screens : scratch vis*.png.
docs/screens/ : LIVRE (LOG.md + 8 screens : menu, monde, /give, pose, inventaire, pack test).
Lecon : SDL prend wayland par defaut (forcer SDL_VIDEODRIVER=x11), xdotool click --window
ignore par SDL (utiliser XTEST au pointeur), ffmpeg local sans x11grab (Robot a la place).
- MCP : aucun MCP externe installe ; navigateur integre + vision_analyze utilises (OCR rejete §0).
TODO:mcp-ocular : installer pour la boucle runClient quand l'environnement le permet.
+11 -5
View File
@@ -1,9 +1,15 @@
[Desktop Entry] [Desktop Entry]
Name=SurviePote Version=1.0
Comment=Launcher officiel SurviePote - Rejoins mcpv.ihorizon.org
Exec=/opt/SurviePote/SurviePote-setup-1.0.0.AppImage --no-sandbox %U
Icon=/opt/SurviePote/surviepote.png
Type=Application Type=Application
Categories=Game; Name=SurviePote
GenericName=Minecraft Launcher
Comment=Launcher officiel SurviePote — rejoins mcpv.ihorizon.org
TryExec=/home/kisakay/Applications/SurviePote-setup-1.0.0.AppImage
Exec=/home/kisakay/Applications/SurviePote-setup-1.0.0.AppImage --no-sandbox %U
Icon=surviepote
Terminal=false Terminal=false
Categories=Game;
Keywords=minecraft;surviepote;launcher;game;
StartupNotify=true
StartupWMClass=SurviePote StartupWMClass=SurviePote
SingleMainWindow=true
+386 -39
View File
@@ -10,7 +10,12 @@
"address": "mcpv.ihorizon.org:25565", "address": "mcpv.ihorizon.org:25565",
"minecraftVersion": "26.3", "minecraftVersion": "26.3",
"mainServer": true, "mainServer": true,
"autoconnect": true, "autoconnect": false,
"javaOptions": {
"distribution": "TEMURIN",
"supported": ">=25.x",
"suggestedMajor": 25
},
"modules": [ "modules": [
{ {
"id": "net.fabricmc:fabric-loader:0.20.0", "id": "net.fabricmc:fabric-loader:0.20.0",
@@ -94,6 +99,29 @@
} }
] ]
}, },
{
"id": "com.autovw:advancednetherite:2.4.4-26.3@jar",
"name": "Advanced Netherite",
"type": "FabricMod",
"artifact": {
"size": 568803,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/advancednetherite-fabric-2.4.4-26.3.jar",
"MD5": "ed5a33f1c9d190369eaaa9a757ad0678"
}
},
{
"id": "squeek.appleskin:appleskin:3.0.10+mc26.3@jar",
"name": "AppleSkin",
"type": "FabricMod",
"artifact": {
"size": 180194,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/appleskin-fabric-mc26.3-3.0.10.jar",
"MD5": "8e1c81bffa2830438600f52d15e251ed"
},
"required": {
"value": false
}
},
{ {
"id": "dev.architectury.utils:architectury:22.0.5@jar", "id": "dev.architectury.utils:architectury:22.0.5@jar",
"name": "Architectury", "name": "Architectury",
@@ -134,6 +162,26 @@
"MD5": "522b9df994d1a6111aa3d716d364073b" "MD5": "522b9df994d1a6111aa3d716d364073b"
} }
}, },
{
"id": "org.betterx:betternether:26.301.2@jar",
"name": "Better Nether",
"type": "FabricMod",
"artifact": {
"size": 27986160,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/better-nether-26.301.2.jar",
"MD5": "dae1d6ca17260a928bd2a685b169adde"
}
},
{
"id": "net.Pandarix:betterarcheology:26.3-1.3.9@jar",
"name": "Better Archeology",
"type": "FabricMod",
"artifact": {
"size": 3113287,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/betterarcheology-fabric-26.3-1.3.9.jar",
"MD5": "b80841a76199f6df1facc8e6a7df7a64"
}
},
{ {
"id": "net.bettercombat:bettercombat:3.2.2@jar", "id": "net.bettercombat:bettercombat:3.2.2@jar",
"name": "Better Combat", "name": "Better Combat",
@@ -154,6 +202,32 @@
"MD5": "53eb5271f71b948dff00ce05c33f39cc" "MD5": "53eb5271f71b948dff00ce05c33f39cc"
} }
}, },
{
"id": "me.cael:capes:1.5.10+26.3@jar",
"name": "Capes",
"type": "FabricMod",
"artifact": {
"size": 78113,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/capes-1.5.10+26.3.jar",
"MD5": "a0b66137b25dc76d25c4c29d7d66408a"
},
"required": {
"value": false
}
},
{
"id": "dzwdz.chat_heads:chat_heads:1.3.2@jar",
"name": "Chat Heads",
"type": "FabricMod",
"artifact": {
"size": 98686,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/chat_heads-1.3.2-fabric-26.3.jar",
"MD5": "a31f5e1b9f55f8321c49ff34a86be5d8"
},
"required": {
"value": false
}
},
{ {
"id": "me.shedaniel.autoconfig.example:cloth-config:26.3.159@jar", "id": "me.shedaniel.autoconfig.example:cloth-config:26.3.159@jar",
"name": "Cloth Config v26.3", "name": "Cloth Config v26.3",
@@ -184,6 +258,42 @@
"MD5": "19d0bb757fbc910514254261bb022e91" "MD5": "19d0bb757fbc910514254261bb022e91"
} }
}, },
{
"id": "dev.smto:constructionwand:2.1.3@jar",
"name": "Construction Wand (Fabric)",
"type": "FabricMod",
"artifact": {
"size": 306717,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/constructionwand-2.1.3+26.3.jar",
"MD5": "e7df688426cc24b62474386086be5dd7"
}
},
{
"id": "me.pepperbell:continuity:3.0.1+26.3@jar",
"name": "Continuity",
"type": "FabricMod",
"artifact": {
"size": 1002033,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/continuity-3.0.1+26.3.jar",
"MD5": "fc9ba23f0b9a2ff6ed2c8e906091a0ab"
},
"required": {
"value": false
}
},
{
"id": "net.lostluma.dynamic_fps.impl.fabric.compat:dynamic_fps:3.11.10@jar",
"name": "Dynamic FPS",
"type": "FabricMod",
"artifact": {
"size": 189474,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/dynamic-fps-3.11.10+minecraft-26.3.0-fabric.jar",
"MD5": "0b5600c4d6825890c9cc9a2b9727c5f9"
},
"required": {
"value": false
}
},
{ {
"id": "ivkond.mc.mods.eh:easy_homes:1.0.1+26.3@jar", "id": "ivkond.mc.mods.eh:easy_homes:1.0.1+26.3@jar",
"name": "Easy Homes", "name": "Easy Homes",
@@ -194,6 +304,16 @@
"MD5": "b59adc60f71cb3bf517b876b64689c91" "MD5": "b59adc60f71cb3bf517b876b64689c91"
} }
}, },
{
"id": "com.vsngarcia:elevatorid:26.3-1.16.6@jar",
"name": "ElevatorMod",
"type": "FabricMod",
"artifact": {
"size": 780593,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/elevatorid-fabric-26.3-1.16.6.jar",
"MD5": "8046d4dfc93e80121d47db42292fea5b"
}
},
{ {
"id": "generated.fabricmod:fabric-api:0.163.0+26.3@jar", "id": "generated.fabricmod:fabric-api:0.163.0+26.3@jar",
"name": "Fabric API", "name": "Fabric API",
@@ -214,6 +334,29 @@
"MD5": "08b201c7d0e80ba7a3d77cca0b2d3d3f" "MD5": "08b201c7d0e80ba7a3d77cca0b2d3d3f"
} }
}, },
{
"id": "me.fallenbreath:fastipping:1.0.12@jar",
"name": "Fast IP Ping",
"type": "FabricMod",
"artifact": {
"size": 11310,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/fast-ip-ping-v1.0.12-mc26.3.jar",
"MD5": "353e78168aa824daa0b442a572167ce8"
},
"required": {
"value": false
}
},
{
"id": "fuzs.forgeconfigapiport.fabric.impl:forgeconfigapiport:26.3.1@jar",
"name": "Forge Config API Port",
"type": "FabricMod",
"artifact": {
"size": 609574,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ForgeConfigAPIPort-v26.3.1-mc26.3.x-Fabric.jar",
"MD5": "a16f7b8b0f57d3f2fb2fb9d4adab070c"
}
},
{ {
"id": "glitchcore.fabric.core:glitchcore:26.3.0.0.5@jar", "id": "glitchcore.fabric.core:glitchcore:26.3.0.0.5@jar",
"name": "GlitchCore", "name": "GlitchCore",
@@ -225,13 +368,26 @@
} }
}, },
{ {
"id": "com.blocklegend001:immersiveores:26.3-0.1@jar", "id": "generated.fabricmod:ipo:7@jar",
"name": "ImmersiveOres", "name": "ImprovedPillagerOutpost",
"type": "FabricMod", "type": "FabricMod",
"artifact": { "artifact": {
"size": 543252, "size": 614666,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImmersiveOres-26.3-0.1.jar", "url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImprovedPillagerOutpost-v7.jar",
"MD5": "840a11c59c29eadd909cba3ce6f569bb" "MD5": "5806e860495c8a4a7294a5249b41f968"
}
},
{
"id": "generated.fabricmod:iris:1.11.7+mc26.3@jar",
"name": "Iris",
"type": "FabricMod",
"artifact": {
"size": 2850938,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/iris-fabric-1.11.7+mc26.3.jar",
"MD5": "6e390346ffb4f04e670d7f0ffc04887f"
},
"required": {
"value": false
} }
}, },
{ {
@@ -274,6 +430,42 @@
"MD5": "228595e27e2977ec10adfc05dce05489" "MD5": "228595e27e2977ec10adfc05dce05489"
} }
}, },
{
"id": "com.terraformersmc:modmenu:21.0.0@jar",
"name": "Mod Menu",
"type": "FabricMod",
"artifact": {
"size": 856779,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/modmenu-21.0.0.jar",
"MD5": "d2b9c21cb592b8723d3d443589aabc8a"
},
"required": {
"value": false
}
},
{
"id": "generated.fabricmod:morechathistory:2.0.0@jar",
"name": "More Chat History",
"type": "FabricMod",
"artifact": {
"size": 3773,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/morechathistory-2.0.0.jar",
"MD5": "89baaf10d39c96e70460765738afb201"
},
"required": {
"value": false
}
},
{
"id": "net.trique:mythicupgrades:5.1.1@jar",
"name": "Mythic Upgrades",
"type": "FabricMod",
"artifact": {
"size": 2375834,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/mythicupgrades-fabric-26.3-5.1.1.jar",
"MD5": "09e9e8eaf95febf0835780c073220f28"
}
},
{ {
"id": "com.chaosthedude:naturescompass:26.3-2.5.1-fabric@jar", "id": "com.chaosthedude:naturescompass:26.3-2.5.1-fabric@jar",
"name": "Nature's Compass", "name": "Nature's Compass",
@@ -284,6 +476,26 @@
"MD5": "ee0b1ce7458afa5c4701c02b42a9f475" "MD5": "ee0b1ce7458afa5c4701c02b42a9f475"
} }
}, },
{
"id": "com.aizistral:nochatreports:26.3-v2.21.0@jar",
"name": "No Chat Reports",
"type": "FabricMod",
"artifact": {
"size": 239561,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/NoChatReports-FABRIC-26.3-v2.21.0.jar",
"MD5": "8434b3b01bbad6e6deb5309d3e2c5800"
}
},
{
"id": "fr.surviepote:nosingleplayer:1.1.8@jar",
"name": "NoSingleplayer (SurviePote)",
"type": "FabricMod",
"artifact": {
"size": 3637392,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/nosingleplayer-1.1.8.jar",
"MD5": "dc69cd7ebc5d10ba4797c4e212e47e0c"
}
},
{ {
"id": "dev.tonimatas:packetfixer:3.3.6@jar", "id": "dev.tonimatas:packetfixer:3.3.6@jar",
"name": "Packet Fixer", "name": "Packet Fixer",
@@ -294,6 +506,16 @@
"MD5": "b15fd6703384c58c19d30624923460a5" "MD5": "b15fd6703384c58c19d30624923460a5"
} }
}, },
{
"id": "fr.surviepote:pala:0.2.1@jar",
"name": "Pala (SurviePote)",
"type": "FabricMod",
"artifact": {
"size": 512504,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/pala-0.2.1.jar",
"MD5": "5dd172a0d8ac14c924729cea9685174c"
}
},
{ {
"id": "net.lopymine.patpat.entrypoint.impl:patpat:1.3.2+26.3+fabric@jar", "id": "net.lopymine.patpat.entrypoint.impl:patpat:1.3.2+26.3+fabric@jar",
"name": "PatPat", "name": "PatPat",
@@ -324,6 +546,26 @@
"MD5": "e176ae70db08a26839282e6d373b5d97" "MD5": "e176ae70db08a26839282e6d373b5d97"
} }
}, },
{
"id": "fuzs.puzzleslib.fabric.impl:puzzleslib:26.3.15@jar",
"name": "Puzzles Lib",
"type": "FabricMod",
"artifact": {
"size": 1093312,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/puzzleslib-v26.3.15-mc26.3.x+fabric.jar",
"MD5": "51ceba62abb73e69212caa0f923eacff"
}
},
{
"id": "com.teamresourceful.resourcefulconfig.web.server:resourcefulconfig:6.0.2@jar",
"name": "Resourcefulconfig",
"type": "FabricMod",
"artifact": {
"size": 444092,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ResourcefulConfig-6.0.2.jar",
"MD5": "f9817fe0bf73467af9c00eeaee420550"
}
},
{ {
"id": "net.blay09.mods:shogi:26.3.0.3@jar", "id": "net.blay09.mods:shogi:26.3.0.3@jar",
"name": "Shogi", "name": "Shogi",
@@ -334,6 +576,52 @@
"MD5": "7d4488104a6d0714838b5c801f50407d" "MD5": "7d4488104a6d0714838b5c801f50407d"
} }
}, },
{
"id": "zy.swthb:simple-warp-tpa-home-back:26.3-1.3@jar",
"name": "Simple-warp-tpa-home-back",
"type": "FabricMod",
"artifact": {
"size": 67653,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/simple-warp-tpa-home-back-26.3-1.3.jar",
"MD5": "8e6235409b851c40691f65d729130e82"
}
},
{
"id": "dev.tr7zw.skinlayers:skinlayers3d:1.11.3@jar",
"name": "3d-Skin-Layers",
"type": "FabricMod",
"artifact": {
"size": 1890220,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/skinlayers3d-fabric-1.11.3-mc26.3.jar",
"MD5": "948669bcb13af2f322ffebc950a89f6c"
},
"required": {
"value": false
}
},
{
"id": "net.caffeinemc.mods:sodium:0.9.3-beta.1+mc26.3@jar",
"name": "Sodium",
"type": "FabricMod",
"artifact": {
"size": 1912496,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/sodium-fabric-0.9.3-beta.1+mc26.3.jar",
"MD5": "acc6ae644c0a71953832d35d9fb475e6"
},
"required": {
"value": false
}
},
{
"id": "generated.fabricmod:spatial:gui-1.8+26.3-fabric@jar",
"name": "Spatial",
"type": "FabricMod",
"artifact": {
"size": 276562,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/spatial-gui-1.8+26.3-fabric.jar",
"MD5": "80fcb9b1ed083b8515fa78e6a5cd86a4"
}
},
{ {
"id": "com.jaquadro.minecraft:storagedrawers:26.3.0.1@jar", "id": "com.jaquadro.minecraft:storagedrawers:26.3.0.1@jar",
"name": "Storage Drawers", "name": "Storage Drawers",
@@ -354,6 +642,16 @@
"MD5": "310a79696ae06b2049c29f1689c86db1" "MD5": "310a79696ae06b2049c29f1689c86db1"
} }
}, },
{
"id": "com.tiviacz:travelersbackpack:11.4.1@jar",
"name": "Traveler's Backpack",
"type": "FabricMod",
"artifact": {
"size": 1511186,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/travelersbackpack-fabric-26.3-11.4.1.jar",
"MD5": "a18139ae9428e3072db547749642cd5d"
}
},
{ {
"id": "com.serilum:treeharvester:10.3@jar", "id": "com.serilum:treeharvester:10.3@jar",
"name": "Tree Harvester", "name": "Tree Harvester",
@@ -364,6 +662,16 @@
"MD5": "63fe436df405aa30d6adf50ebf65737f" "MD5": "63fe436df405aa30d6adf50ebf65737f"
} }
}, },
{
"id": "fuzs.universalenchants:universalenchants:26.3.1@jar",
"name": "Universal Enchants",
"type": "FabricMod",
"artifact": {
"size": 198420,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/universalenchants-v26.3.1-mc26.3.x+fabric.jar",
"MD5": "d8ed00b6e2d334da7e469686f6b64b59"
}
},
{ {
"id": "net.execheinz:upgrader:1.2.1@jar", "id": "net.execheinz:upgrader:1.2.1@jar",
"name": "Upgrader", "name": "Upgrader",
@@ -374,6 +682,19 @@
"MD5": "1a0d1ad1d17ce8686e11cf081cf1acf4" "MD5": "1a0d1ad1d17ce8686e11cf081cf1acf4"
} }
}, },
{
"id": "net.loomiyaa:vanillarice:v0.1.8-mc26.3@jar",
"name": "Vanilla Rice",
"type": "FabricMod",
"artifact": {
"size": 53090,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/vanillarice-v0.1.8-mc26.3.jar",
"MD5": "d00d39fc19be957b2e0239b187e72399"
},
"required": {
"value": false
}
},
{ {
"id": "de.miraculixx:veinminer:2.12.3@jar", "id": "de.miraculixx:veinminer:2.12.3@jar",
"name": "Veinminer", "name": "Veinminer",
@@ -384,6 +705,19 @@
"MD5": "d18e6fe6b403a3a00f722317cf681b24" "MD5": "d18e6fe6b403a3a00f722317cf681b24"
} }
}, },
{
"id": "dev.tr7zw:waveycapes:1.11.3@jar",
"name": "WaveyCapes",
"type": "FabricMod",
"artifact": {
"size": 1764922,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/waveycapes-fabric-1.11.3-mc26.3.jar",
"MD5": "20470d32a938af01bd999ba04211e537"
},
"required": {
"value": false
}
},
{ {
"id": "net.blay09.mods:waystones:26.3.0.1@jar", "id": "net.blay09.mods:waystones:26.3.0.1@jar",
"name": "Waystones", "name": "Waystones",
@@ -404,6 +738,52 @@
"MD5": "defce770e3f9661853c1d343fc870f7f" "MD5": "defce770e3f9661853c1d343fc870f7f"
} }
}, },
{
"id": "xaero.minimap:xaerominimap:26.6.0@jar",
"name": "Xaero's Minimap",
"type": "FabricMod",
"artifact": {
"size": 2230141,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/xaerominimap-fabric-26.3-26.6.0.jar",
"MD5": "6861979f573b4c47566431a7e2a601c3"
},
"required": {
"value": false
}
},
{
"id": "xaero.map:xaeroworldmap:1.47.0@jar",
"name": "Xaero's World Map",
"type": "FabricMod",
"artifact": {
"size": 1485579,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/optionalon/xaeroworldmap-fabric-26.3-1.47.0.jar",
"MD5": "8c0ce6594b6b447403f266720b4b32fb"
},
"required": {
"value": false
}
},
{
"id": "dev.isxander.yacl3.platform:yet_another_config_lib_v3:3.9.7+26.3-fabric@jar",
"name": "YetAnotherConfigLib",
"type": "FabricMod",
"artifact": {
"size": 1125377,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/yet_another_config_lib_v3-3.9.7+26.3-fabric.jar",
"MD5": "967207b5cf360d9481fb318abacfe8eb"
}
},
{
"id": "com.yyz:yyzsbackpack:0.0.11-reborn+26.3@jar",
"name": "Yyz's Backpack",
"type": "FabricMod",
"artifact": {
"size": 357667,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/yyzsbackpack-0.0.11-reborn+26.3.jar",
"MD5": "67db85ac45cf311d9994349f51d00942"
}
},
{ {
"id": "settings.json", "id": "settings.json",
"name": "settings.json", "name": "settings.json",
@@ -558,39 +938,6 @@
"path": "config/easy_homes.json" "path": "config/easy_homes.json"
} }
}, },
{
"id": "enderium-common.toml",
"name": "enderium-common.toml",
"type": "File",
"artifact": {
"size": 3930,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/enderium-common.toml",
"MD5": "29c0e8dc797b264ff04944f44756ff16",
"path": "config/immersiveores/enderium-common.toml"
}
},
{
"id": "vibranium-common.toml",
"name": "vibranium-common.toml",
"type": "File",
"artifact": {
"size": 3531,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/vibranium-common.toml",
"MD5": "425488f8ca763dc9c5f1c406f79a7f53",
"path": "config/immersiveores/vibranium-common.toml"
}
},
{
"id": "vulpus-common.toml",
"name": "vulpus-common.toml",
"type": "File",
"artifact": {
"size": 3466,
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/files/config/immersiveores/vulpus-common.toml",
"MD5": "05cc5da3fcd2dab7314b62636043342f",
"path": "config/immersiveores/vulpus-common.toml"
}
},
{ {
"id": "jade.json", "id": "jade.json",
"name": "jade.json", "name": "jade.json",
@@ -1,68 +0,0 @@
# --- Enderium Armor Protection and Resistance ---
protectionValueEnderiumBoots=12 #Protection value of Enderium Boots | default: 12
protectionValueEnderiumLeggings=16 #Protection value of Enderium Leggings | default: 16
protectionValueEnderiumChestplate=20 #Protection value of Enderium Chestplate | default: 20
protectionValueEnderiumHelmet=12 #Protection value of Enderium Helmet | default: 12
toughnessValueEnderiumArmor=10 #Toughness value of Enderium Armor | default: 10
knockbackResistanceValueEnderiumArmor=0.4 #Knockback resistance of Enderium Armor | default: 0.4
enchantmentValueEnderiumArmor=65 #Enchantment value of Enderium Armor | default: 65
# --- Enderium Armor Special Effects and Abilities ---
speedIIIEnderiumArmor=true #Speed III effect for Enderium Armor | default: true
jumpIIIEnderiumArmor=true #Jump III effect for Enderium Armor | default: true
nightVisionEnderiumArmor=true #Night Vision effect for Enderium Armor | default: true
immuneToFallDamageEnderiumArmor=true #Immunity to fall damage for Enderium Armor | default: true
fireResistanceEnderiumArmor=true #Fire resistance for Enderium Armor | default: true
neverLoseHungerEnderiumArmor=true #Never lose hunger with Enderium Armor | default: true
canFlyEnderiumArmor=true #Ability to fly with Enderium Armor | default: true
canWalkOnPowderedSnowEnderium=true #Can walk on powdered snow with Enderium Armor | default: true
makesPiglinsNeutralEnderium=true #Makes Piglins neutral with Enderium Armor | default: true
endermanWillNotBeAngryWithYouEnderium=true #Endermen won't be angry with you with Enderium Armor | default: true
# --- Enderium Bow Settings ---
enchantmentValueEnderiumBow=5 #Enchantment value of Enderium Bow | default: 5
damageEnderiumBow=14 #Damage value of Enderium Bow | default: 14
arrowCountEnderiumBow=4 #Arrow count for Enderium Bow | default: 4
# --- Enderium Tool Tier Base Properties ---
speedEnderiumTier=50 #Speed value of Enderium tier | default: 50
enchantmentValueEnderiumTier=65 #Enchantment value of Enderium tier | default: 65
attackDamageBonusEnderiumTier=3 #Attack damage bonus of Enderium tier | default: 3
durabilityEnderium=5139 #Durability of Enderium tools | default: 5139
unbreakableEnderium=false #Whether Enderium tools are unbreakable | default: false
# --- Enderium Pickaxe Stats ---
attackDamageEnderiumPickaxe=15 #Attack damage of Enderium Pickaxe | default: 15
attackSpeedEnderiumPickaxe=1.0 #Attack speed of Enderium Pickaxe | default: 1.0
# --- Enderium Axe Stats ---
attackDamageEnderiumAxe=18 #Attack damage of Enderium Axe | default: 18
attackSpeedEnderiumAxe=1.0 #Attack speed of Enderium Axe | default: 1.0
# --- Enderium Shovel Stats ---
attackDamageEnderiumShovel=15 #Attack damage of Enderium Shovel | default: 15
attackSpeedEnderiumShovel=1.0 #Attack speed of Enderium Shovel | default: 1.0
# --- Enderium Sword Stats ---
attackDamageEnderiumSword=22 #Attack damage of Enderium Sword | default: 22
attackSpeedEnderiumSword=1.0 #Attack speed of Enderium Sword | default: 1.0
# --- Enderium Hoe Stats ---
attackDamageEnderiumHoe=15 #Attack damage of Enderium Hoe | default: 15
attackSpeedEnderiumHoe=1.0 #Attack speed of Enderium Hoe | default: 1.0
# --- Enderium Paxel Stats ---
attackDamageEnderiumPaxel=18 #Attack damage of Enderium Paxel | default: 18
attackSpeedEnderiumPaxel=1.0 #Attack speed of Enderium Paxel | default: 1.0
# --- Enderium Hammer Stats ---
attackDamageEnderiumHammer=16 #Attack damage of Enderium Hammer | default: 16
attackSpeedEnderiumHammer=1.0 #Attack speed of Enderium Hammer | default: 1.0
radiusEnderiumHammer=3 #Hammering radius for Enderium Hammer | default: 3
# --- Enderium Excavator Stats ---
attackDamageEnderiumExcavator=16 #Attack damage of Enderium Excavator | default: 16
attackSpeedEnderiumExcavator=1.0 #Attack speed of Enderium Excavator | default: 1.0
radiusEnderiumExcavator=3 #Excavation radius for Enderium Excavator | default: 3
@@ -1,62 +0,0 @@
# --- --- Vibranium Armor Protection and Resistance --- ---
protectionValueVibraniumBoots=6 #Protection value of Vibranium Boots | default: 6
protectionValueVibraniumLeggings=9 #Protection value of Vibranium Leggings | default: 9
protectionValueVibraniumChestplate=12 #Protection value of Vibranium Chestplate | default: 12
protectionValueVibraniumHelmet=6 #Protection value of Vibranium Helmet | default: 6
toughnessValueVibraniumArmor=3 #Toughness value of Vibranium Armor | default: 3
knockbackResistanceValueVibraniumArmor=0.2 #Knockback resistance of Vibranium Armor | default: 0.2
enchantmentValueVibraniumArmor=55 #Enchantment value of Vibranium Armor | default: 55
# --- --- Vibranium Armor Special Effects and Abilities --- ---
speedIVibraniumArmor=true #Speed I effect for Vibranium Armor | default: true
jumpIVibraniumArmor=true #Jump I effect for Vibranium Armor | default: true
nightVisionVibraniumArmor=true #Night Vision effect for Vibranium Armor | default: true
canWalkOnPowderedSnowVibranium=true #Can walk on powdered snow with Vibranium Armor | default: true
# --- --- Vibranium Bow Settings --- ---
enchantmentValueVibraniumBow=5 #Enchantment value of Vibranium Bow | default: 5
damageVibraniumBow=5 #Damage value of Vibranium Bow | default: 5
arrowCountVibraniumBow=2 #Arrow count for Vibranium Bow | default: 2
# --- --- Vibranium Tool Tier Base Properties --- ---
speedVibraniumTier=30 #Speed value of Vibranium tier | default: 30
enchantmentValueVibraniumTier=55 #Enchantment value of Vibranium tier | default: 55
attackDamageBonusVibraniumTier=1 #Attack damage bonus of Vibranium tier | default: 1
durabilityVibranium=2562 #Durability of Vibranium tools | default: 2562
unbreakableVibranium=false #Whether Vibranium tools are unbreakable | default: false
# --- --- Vibranium Pickaxe Stats --- ---
attackDamageVibraniumPickaxe=7 #Attack damage of Vibranium Pickaxe | default: 7
attackSpeedVibraniumPickaxe=0.5 #Attack speed of Vibranium Pickaxe | default: 0.5
# --- --- Vibranium Axe Stats --- ---
attackDamageVibraniumAxe=12 #Attack damage of Vibranium Axe | default: 12
attackSpeedVibraniumAxe=0.5 #Attack speed of Vibranium Axe | default: 0.5
# --- --- Vibranium Shovel Stats --- ---
attackDamageVibraniumShovel=7 #Attack damage of Vibranium Shovel | default: 7
attackSpeedVibraniumShovel=0.5 #Attack speed of Vibranium Shovel | default: 0.5
# --- --- Vibranium Sword Stats --- ---
attackDamageVibraniumSword=10 #Attack damage of Vibranium Sword | default: 10
attackSpeedVibraniumSword=0.5 #Attack speed of Vibranium Sword | default: 0.5
# --- --- Vibranium Hoe Stats --- ---
attackDamageVibraniumHoe=7 #Attack damage of Vibranium Hoe | default: 7
attackSpeedVibraniumHoe=0.5 #Attack speed of Vibranium Hoe | default: 0.5
# --- --- Vibranium Paxel Stats --- ---
attackDamageVibraniumPaxel=11 #Attack damage of Vibranium Paxel | default: 11
attackSpeedVibraniumPaxel=0.5 #Attack speed of Vibranium Paxel | default: 0.5
# --- --- Vibranium Hammer Stats --- ---
attackDamageVibraniumHammer=7 #Attack damage of Vibranium Hammer | default: 7
attackSpeedVibraniumHammer=0.5 #Attack speed of Vibranium Hammer | default: 0.5
radiusVibraniumHammer=1 #Hammering radius for Vibranium Hammer | default: 1
# --- --- Vibranium Excavator Stats --- ---
attackDamageVibraniumExcavator=7 #Attack damage of Vibranium Excavator | default: 7
attackSpeedVibraniumExcavator=0.5 #Attack speed of Vibranium Excavator | default: 0.5
radiusVibraniumExcavator=1 #Excavation radius for Vibranium Excavator | default: 1
@@ -1,66 +0,0 @@
# --- Vulpus Armor Protection and Resistance ---
protectionValueVulpusBoots=9 #Protection value of Vulpus Boots | default: 9
protectionValueVulpusLeggings=12 #Protection value of Vulpus Leggings | default: 12
protectionValueVulpusChestplate=16 #Protection value of Vulpus Chestplate | default: 16
protectionValueVulpusHelmet=9 #Protection value of Vulpus Helmet | default: 9
toughnessValueVulpusArmor=7 #Toughness value of Vulpus Armor | default: 7
enchantmentValueVulpusArmor=65 #Enchantment value of Vulpus Armor | default: 65
knockbackResistanceValueVulpusArmor=0.3 #Knockback resistance of Vulpus Armor | default: 0.3
# --- Vulpus Armor Special Effects and Abilities ---
speedIIVulpusArmor=true #Speed II effect for Vulpus Armor | default: true
jumpIIVulpusArmor=true #Jump II effect for Vulpus Armor | default: true
nightVisionVulpusArmor=true #Night Vision for Vulpus Armor | default: true
fireResistanceVulpusArmor=true #Fire Resistance for Vulpus Armor | default: true
immuneToFallDamageVulpusArmor=true #Fall Damage Immunity for Vulpus Armor | default: true
neverLoseHungerVulpusArmor=true #Prevention of Hunger Loss for Vulpus Armor | default: true
canWalkOnPowderedSnowVulpus=true #Walking on Powdered Snow with Vulpus Armor | default: true
makesPiglinsNeutralVulpus=true #Piglins become Neutral with Vulpus Armor | default: true
# --- Vulpus Bow ---
enchantmentValueVulpusBow=5 #Enchantment value of Vulpus Bow | default: 5
damageVulpusBow=10 #Damage of Vulpus Bow | default: 10
arrowCountVulpusBow=3 #Arrow count for Vulpus Bow | default: 3
# --- Vulpus Tool Tier Base ---
speedVulpusTier=40 #Mining speed of Vulpus tier | default: 40
enchantmentValueVulpusTier=65 #Enchantment value of Vulpus tier | default: 65
attackDamageBonusVulpusTier=3 #Attack damage bonus of Vulpus tier | default: 3
durabilityVulpus=3093 #Durability of Vulpus tools | default: 3093
unbreakableVulpus=false #Whether Vulpus tools are unbreakable | default: false
# --- Vulpus Pickaxe ---
attackDamageVulpusPickaxe=10 #Attack damage of Vulpus Pickaxe | default: 10
attackSpeedVulpusPickaxe=0.5 #Attack speed of Vulpus Pickaxe | default: 0.5
# --- Vulpus Axe ---
attackDamageVulpusAxe=15 #Attack damage of Vulpus Axe | default: 15
attackSpeedVulpusAxe=0.5 #Attack speed of Vulpus Axe | default: 0.5
# --- Vulpus Shovel ---
attackDamageVulpusShovel=10 #Attack damage of Vulpus Shovel | default: 10
attackSpeedVulpusShovel=0.5 #Attack speed of Vulpus Shovel | default: 0.5
# --- Vulpus Sword ---
attackDamageVulpusSword=14 #Attack damage of Vulpus Sword | default: 14
attackSpeedVulpusSword=0.5 #Attack speed of Vulpus Sword | default: 0.5
# --- Vulpus Hoe ---
attackDamageVulpusHoe=10 #Attack damage of Vulpus Hoe | default: 10
attackSpeedVulpusHoe=0.5 #Attack speed of Vulpus Hoe | default: 0.5
# --- Vulpus Paxel ---
attackDamageVulpusPaxel=15 #Attack damage of Vulpus Paxel | default: 15
attackSpeedVulpusPaxel=0.5 #Attack speed of Vulpus Paxel | default: 0.5
# --- Vulpus Hammer ---
attackDamageVulpusHammer=11 #Attack damage of Vulpus Hammer | default: 11
attackSpeedVulpusHammer=0.5 #Attack speed of Vulpus Hammer | default: 0.5
radiusVulpusHammer=2 #Hammering radius for Vulpus Hammer | default: 2
# --- Vulpus Excavator ---
attackDamageVulpusExcavator=11 #Attack damage of Vulpus Excavator | default: 11
attackSpeedVulpusExcavator=0.5 #Attack speed of Vulpus Excavator | default: 0.5
radiusVulpusExcavator=2 #Excavation radius for Vulpus Excavator | default: 2
@@ -7,7 +7,12 @@
"icon": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/SurviePote-26.3.png", "icon": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/SurviePote-26.3.png",
"address": "mcpv.ihorizon.org:25565", "address": "mcpv.ihorizon.org:25565",
"mainServer": true, "mainServer": true,
"autoconnect": true "autoconnect": false,
"javaOptions": {
"distribution": "TEMURIN",
"supported": ">=25.x",
"suggestedMajor": 25
}
}, },
"fabric": { "fabric": {
"version": "0.20.0" "version": "0.20.0"
+16
View File
@@ -0,0 +1,16 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=SurviePote (Dev)
GenericName=Minecraft Launcher
Comment=SurviePote en mode dev (electron ., sans build)
TryExec=/home/kisakay/Documents/Code/HomeLabGitea/mc/launcher/SurviePote.sh
Exec=/home/kisakay/Documents/Code/HomeLabGitea/mc/launcher/SurviePote.sh %U
Icon=/home/kisakay/Documents/Code/HomeLabGitea/mc/launcher/build/icon.png
Terminal=false
Categories=Game;
Keywords=minecraft;surviepote;launcher;game;
StartupNotify=true
StartupWMClass=SurviePote
SingleMainWindow=true
NoDisplay=false
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# SurviePote — lance le launcher (nix-shell : Java 25 + Node 22 + libs).
set -e
ROOT="/home/kisakay/Documents/Code/HomeLabGitea/mc"
exec nix-shell "$ROOT" --run "cd launcher && npx electron . --no-sandbox"
+1 -3
View File
@@ -30,7 +30,6 @@
<%- include('frame') %> <%- include('frame') %>
<div id="main"> <div id="main">
<%- include('welcome') %> <%- include('welcome') %>
<%- include('login') %>
<%- include('waiting') %> <%- include('waiting') %>
<%- include('loginOptions') %> <%- include('loginOptions') %>
<%- include('settings') %> <%- include('settings') %>
@@ -40,8 +39,7 @@
<div id="loadingContainer"> <div id="loadingContainer">
<div id="loadingContent"> <div id="loadingContent">
<div id="loadSpinnerContainer"> <div id="loadSpinnerContainer">
<img id="loadCenterImage" src="assets/images/LoadingSeal.png"> <img id="loadCenterImage" src="assets/images/SealCircle.png">
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
</div> </div>
</div> </div>
</div> </div>
+1 -119
View File
@@ -2858,9 +2858,7 @@ input:checked + .toggleSwitchSlider:before {
height: 70px; height: 70px;
width: auto; width: auto;
position: relative; position: relative;
border: 2px solid white;
box-sizing: border-box; box-sizing: border-box;
border-radius: 50%;
} }
/* Logo container styles. */ /* Logo container styles. */
@@ -2868,7 +2866,6 @@ input:checked + .toggleSwitchSlider:before {
position: relative; position: relative;
height: 70px; height: 70px;
width: 70px; width: 70px;
border-radius: 50%;
margin-top: 50px; margin-top: 50px;
} }
@@ -2885,7 +2882,6 @@ input:checked + .toggleSwitchSlider:before {
width: 100%; width: 100%;
top: 0%; top: 0%;
left: 0%; left: 0%;
border-radius: 50%;
box-shadow: 0 0 15px #43c628; box-shadow: 0 0 15px #43c628;
animation: glow-grow 4s ease-out infinite; animation: glow-grow 4s ease-out infinite;
background: rgba(0, 0, 0, 0.15); background: rgba(0, 0, 0, 0.15);
@@ -2970,8 +2966,6 @@ input:checked + .toggleSwitchSlider:before {
/* User profile avatar container. */ /* User profile avatar container. */
#avatarContainer { #avatarContainer {
border-radius: 50%;
border: 2px solid #cad7e1;
box-sizing: border-box; box-sizing: border-box;
background: rgba(1, 2, 1, 0.5); background: rgba(1, 2, 1, 0.5);
height: 70px; height: 70px;
@@ -3001,7 +2995,6 @@ input:checked + .toggleSwitchSlider:before {
cursor: pointer; cursor: pointer;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 50%;
} }
#avatarOverlay:hover, #avatarOverlay:hover,
#avatarOverlay:focus { #avatarOverlay:focus {
@@ -3215,109 +3208,6 @@ input:checked + .toggleSwitchSlider:before {
margin-left: 10px; margin-left: 10px;
} }
/* Wrapper container for the mojang status bar. */
#mojangStatusWrapper {
position: relative;
display: flex;
cursor: pointer;
}
/* Icon which displays the status of the mojang services. */
#mojang_status_icon {
font-size: 30px;
color: #848484;
margin-left: 15px;
font-family: 'sans-serif';
}
/* Tooltip which displays more details about the mojang statuses. */
#mojangStatusTooltip {
position: absolute;
visibility: hidden;
opacity: 0;
width: 145px;
min-height: 150px;
background-color: rgba(0, 0, 0, 0.75);
color: #fff;
border-radius: 4px;
padding: 5px 10px;
z-index: 1;
font-family: 'Avenir Medium';
font-size: 12px;
transition: visibility 0s linear 0.25s, opacity 0.25s ease;
bottom: calc(100% + 15px);
transform: translateX(-50%);
margin-left: 50%;
box-shadow: 0px 0px 20px rgb(0, 0, 0);
cursor: default;
}
#mojangStatusTooltip:after {
content: " ";
position: absolute;
left: 50%;
top: 100%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: rgba(0, 0, 0, 0.75) transparent transparent transparent;
}
#mojangStatusWrapper:hover #mojangStatusTooltip {
visibility: visible;
opacity: 1;
transition-delay: 0s;
}
/* Tooltip title for the mojang statuses. */
#mojangStatusTooltipTitle {
width: 100%;
text-align: center;
margin-bottom: 5px;
letter-spacing: 1px;
}
/* Wrapper container for the non essential services title. */
#mojangStatusNEContainer {
display: flex;
align-items: center;
margin: 10px 0px;
}
/* White bar which surrounds the non essential service title. */
.mojangStatusNEBar {
height: 1px;
width: 100%;
background: white;
}
/* Non essential service title text. */
#mojangStatusNETitle {
font-size: 10px;
padding: 0px 3px;
text-align: center;
letter-spacing: 1px;
}
/* Wrapper container for mojang service information. */
.mojangStatusContainer {
display: flex;
}
/* Displays the name of the mojang service. */
.mojangStatusName {
width: 100%;
font-size: 10px;
letter-spacing: 1px;
line-height: 12px;
padding: 6px 0px;
}
/* Displays the status of the mojang service. */
.mojangStatusIcon {
margin-right: 10px;
font-size: 18.5px;
color: #848484;
}
/* * * /* * *
* Landing View (Bottom Styles) | Center Content * Landing View (Bottom Styles) | Center Content
* * */ * * */
@@ -3940,18 +3830,10 @@ input:checked + .toggleSwitchSlider:before {
/* Stationary image for the spinner. */ /* Stationary image for the spinner. */
#loadCenterImage { #loadCenterImage {
position: absolute; width: 200px;
width: 277px;
height: auto; height: auto;
} }
/* Rotating image for the spinner. */
#loadSpinnerImage {
width: 280px;
height: auto;
z-index: 400;
}
/* Rotating animation for the spinner. */ /* Rotating animation for the spinner. */
@keyframes rotating { @keyframes rotating {
from { from {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

+12 -184
View File
@@ -1,10 +1,10 @@
/** /**
* AuthManager * AuthManager
* *
* This module aims to abstract login procedures. Results from Mojang's REST api * This module aims to abstract login procedures. Microsoft auth results
* are retrieved through our Mojang module. These results are processed and stored, * are retrieved through our Microsoft module. These results are processed and
* if applicable, in the config using the ConfigManager. All login procedures should * stored, if applicable, in the config using the ConfigManager. All login
* be made through this module. * procedures should be made through this module.
* *
* @module authmanager * @module authmanager
*/ */
@@ -12,7 +12,6 @@
const ConfigManager = require('./configmanager') const ConfigManager = require('./configmanager')
const { LoggerUtil } = require('helios-core') const { LoggerUtil } = require('helios-core')
const { RestResponseStatus } = require('helios-core/common') const { RestResponseStatus } = require('helios-core/common')
const { MojangRestAPI, MojangErrorCode } = require('helios-core/mojang')
const { MicrosoftAuth, MicrosoftErrorCode } = require('helios-core/microsoft') const { MicrosoftAuth, MicrosoftErrorCode } = require('helios-core/microsoft')
const { AZURE_CLIENT_ID } = require('./ipcconstants') const { AZURE_CLIENT_ID } = require('./ipcconstants')
const Lang = require('./langloader') const Lang = require('./langloader')
@@ -51,122 +50,8 @@ function microsoftErrorDisplayable(errorCode) {
} }
} }
function mojangErrorDisplayable(errorCode) {
switch(errorCode) {
case MojangErrorCode.ERROR_METHOD_NOT_ALLOWED:
return {
title: Lang.queryJS('auth.mojang.error.methodNotAllowedTitle'),
desc: Lang.queryJS('auth.mojang.error.methodNotAllowedDesc')
}
case MojangErrorCode.ERROR_NOT_FOUND:
return {
title: Lang.queryJS('auth.mojang.error.notFoundTitle'),
desc: Lang.queryJS('auth.mojang.error.notFoundDesc')
}
case MojangErrorCode.ERROR_USER_MIGRATED:
return {
title: Lang.queryJS('auth.mojang.error.accountMigratedTitle'),
desc: Lang.queryJS('auth.mojang.error.accountMigratedDesc')
}
case MojangErrorCode.ERROR_INVALID_CREDENTIALS:
return {
title: Lang.queryJS('auth.mojang.error.invalidCredentialsTitle'),
desc: Lang.queryJS('auth.mojang.error.invalidCredentialsDesc')
}
case MojangErrorCode.ERROR_RATELIMIT:
return {
title: Lang.queryJS('auth.mojang.error.tooManyAttemptsTitle'),
desc: Lang.queryJS('auth.mojang.error.tooManyAttemptsDesc')
}
case MojangErrorCode.ERROR_INVALID_TOKEN:
return {
title: Lang.queryJS('auth.mojang.error.invalidTokenTitle'),
desc: Lang.queryJS('auth.mojang.error.invalidTokenDesc')
}
case MojangErrorCode.ERROR_ACCESS_TOKEN_HAS_PROFILE:
return {
title: Lang.queryJS('auth.mojang.error.tokenHasProfileTitle'),
desc: Lang.queryJS('auth.mojang.error.tokenHasProfileDesc')
}
case MojangErrorCode.ERROR_CREDENTIALS_MISSING:
return {
title: Lang.queryJS('auth.mojang.error.credentialsMissingTitle'),
desc: Lang.queryJS('auth.mojang.error.credentialsMissingDesc')
}
case MojangErrorCode.ERROR_INVALID_SALT_VERSION:
return {
title: Lang.queryJS('auth.mojang.error.invalidSaltVersionTitle'),
desc: Lang.queryJS('auth.mojang.error.invalidSaltVersionDesc')
}
case MojangErrorCode.ERROR_UNSUPPORTED_MEDIA_TYPE:
return {
title: Lang.queryJS('auth.mojang.error.unsupportedMediaTypeTitle'),
desc: Lang.queryJS('auth.mojang.error.unsupportedMediaTypeDesc')
}
case MojangErrorCode.ERROR_GONE:
return {
title: Lang.queryJS('auth.mojang.error.accountGoneTitle'),
desc: Lang.queryJS('auth.mojang.error.accountGoneDesc')
}
case MojangErrorCode.ERROR_UNREACHABLE:
return {
title: Lang.queryJS('auth.mojang.error.unreachableTitle'),
desc: Lang.queryJS('auth.mojang.error.unreachableDesc')
}
case MojangErrorCode.ERROR_NOT_PAID:
return {
title: Lang.queryJS('auth.mojang.error.gameNotPurchasedTitle'),
desc: Lang.queryJS('auth.mojang.error.gameNotPurchasedDesc')
}
case MojangErrorCode.UNKNOWN:
return {
title: Lang.queryJS('auth.mojang.error.unknownErrorTitle'),
desc: Lang.queryJS('auth.mojang.error.unknownErrorDesc')
}
default:
throw new Error(`Unknown error code: ${errorCode}`)
}
}
// Functions // Functions
/**
* Add a Mojang account. This will authenticate the given credentials with Mojang's
* authserver. The resultant data will be stored as an auth account in the
* configuration database.
*
* @param {string} username The account username (email if migrated).
* @param {string} password The account password.
* @returns {Promise.<Object>} Promise which resolves the resolved authenticated account object.
*/
exports.addMojangAccount = async function(username, password) {
try {
const response = await MojangRestAPI.authenticate(username, password, ConfigManager.getClientToken())
console.log(response)
if(response.responseStatus === RestResponseStatus.SUCCESS) {
const session = response.data
if(session.selectedProfile != null){
const ret = ConfigManager.addMojangAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
if(ConfigManager.getClientToken() == null){
ConfigManager.setClientToken(session.clientToken)
}
ConfigManager.save()
return ret
} else {
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.ERROR_NOT_PAID))
}
} else {
return Promise.reject(mojangErrorDisplayable(response.mojangErrorCode))
}
} catch (err){
log.error(err)
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.UNKNOWN))
}
}
const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 } const AUTH_MODE = { FULL: 0, MS_REFRESH: 1, MC_REFRESH: 2 }
/** /**
@@ -239,7 +124,7 @@ function calculateExpiryDate(nowMs, epiresInS) {
} }
/** /**
* Add a Microsoft account. This will pass the provided auth code to Mojang's OAuth2.0 flow. * Add a Microsoft account. This will pass the provided auth code to Microsoft's OAuth2.0 flow.
* The resultant data will be stored as an auth account in the configuration database. * The resultant data will be stored as an auth account in the configuration database.
* *
* @param {string} authCode The authCode obtained from microsoft. * @param {string} authCode The authCode obtained from microsoft.
@@ -266,31 +151,6 @@ exports.addMicrosoftAccount = async function(authCode) {
return ret return ret
} }
/**
* Remove a Mojang account. This will invalidate the access token associated
* with the account and then remove it from the database.
*
* @param {string} uuid The UUID of the account to be removed.
* @returns {Promise.<void>} Promise which resolves to void when the action is complete.
*/
exports.removeMojangAccount = async function(uuid){
try {
const authAcc = ConfigManager.getAuthAccount(uuid)
const response = await MojangRestAPI.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
if(response.responseStatus === RestResponseStatus.SUCCESS) {
ConfigManager.removeAuthAccount(uuid)
ConfigManager.save()
return Promise.resolve()
} else {
log.error('Error while removing account', response.error)
return Promise.reject(response.error)
}
} catch (err){
log.error('Error while removing account', err)
return Promise.reject(err)
}
}
/** /**
* Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout * Remove a Microsoft account. It is expected that the caller will invoke the OAuth logout
* through the ipc renderer. * through the ipc renderer.
@@ -309,41 +169,6 @@ exports.removeMicrosoftAccount = async function(uuid){
} }
} }
/**
* Validate the selected account with Mojang's authserver. If the account is not valid,
* we will attempt to refresh the access token and update that value. If that fails, a
* new login will be required.
*
* @returns {Promise.<boolean>} Promise which resolves to true if the access token is valid,
* otherwise false.
*/
async function validateSelectedMojangAccount(){
const current = ConfigManager.getSelectedAccount()
const response = await MojangRestAPI.validate(current.accessToken, ConfigManager.getClientToken())
if(response.responseStatus === RestResponseStatus.SUCCESS) {
const isValid = response.data
if(!isValid){
const refreshResponse = await MojangRestAPI.refresh(current.accessToken, ConfigManager.getClientToken())
if(refreshResponse.responseStatus === RestResponseStatus.SUCCESS) {
const session = refreshResponse.data
ConfigManager.updateMojangAuthAccount(current.uuid, session.accessToken)
ConfigManager.save()
} else {
log.error('Error while validating selected profile:', refreshResponse.error)
log.info('Account access token is invalid.')
return false
}
log.info('Account access token validated.')
return true
} else {
log.info('Account access token validated.')
return true
}
}
}
/** /**
* Validate the selected account with Microsoft's authserver. If the account is not valid, * Validate the selected account with Microsoft's authserver. If the account is not valid,
* we will attempt to refresh the access token and update that value. If that fails, a * we will attempt to refresh the access token and update that value. If that fails, a
@@ -416,10 +241,13 @@ async function validateSelectedMicrosoftAccount(){
exports.validateSelected = async function(){ exports.validateSelected = async function(){
const current = ConfigManager.getSelectedAccount() const current = ConfigManager.getSelectedAccount()
if(current.type === 'microsoft') { // Auth Mojang supprimee (depreciee) : plus de sign-in Mojang au bootstrap.
return await validateSelectedMicrosoftAccount() // Seuls les comptes Microsoft sont valides ; un ancien compte Mojang est
} else { // considere invalide sans appel reseau (l'authserver Mojang est ferme).
return await validateSelectedMojangAccount() if(current.type !== 'microsoft') {
return false
} }
return await validateSelectedMicrosoftAccount()
} }
+4 -61
View File
@@ -81,7 +81,7 @@ const DEFAULT_CONFIG = {
resWidth: 1280, resWidth: 1280,
resHeight: 720, resHeight: 720,
fullscreen: false, fullscreen: false,
autoConnect: true, autoConnect: false,
launchDetached: true launchDetached: true
}, },
launcher: { launcher: {
@@ -94,7 +94,6 @@ const DEFAULT_CONFIG = {
content: null, content: null,
dismissed: false dismissed: false
}, },
clientToken: null,
selectedServer: null, // Resolved selectedServer: null, // Resolved
selectedAccount: null, selectedAccount: null,
authenticationDatabase: {}, authenticationDatabase: {},
@@ -255,25 +254,6 @@ exports.getInstanceDirectory = function(){
return path.join(exports.getDataDirectory(), 'instances') return path.join(exports.getDataDirectory(), 'instances')
} }
/**
* Retrieve the launcher's Client Token.
* There is no default client token.
*
* @returns {string} The launcher's Client Token.
*/
exports.getClientToken = function(){
return config.clientToken
}
/**
* Set the launcher's Client Token.
*
* @param {string} clientToken The launcher's new Client Token.
*/
exports.setClientToken = function(clientToken){
config.clientToken = clientToken
}
/** /**
* Retrieve the ID of the selected serverpack. * Retrieve the ID of the selected serverpack.
* *
@@ -281,7 +261,7 @@ exports.setClientToken = function(clientToken){
* @returns {string} The ID of the selected serverpack. * @returns {string} The ID of the selected serverpack.
*/ */
exports.getSelectedServer = function(def = false){ exports.getSelectedServer = function(def = false){
return !def ? config.selectedServer : DEFAULT_CONFIG.clientToken return !def ? config.selectedServer : DEFAULT_CONFIG.selectedServer
} }
/** /**
@@ -313,42 +293,6 @@ exports.getAuthAccount = function(uuid){
return config.authenticationDatabase[uuid] return config.authenticationDatabase[uuid]
} }
/**
* Update the access token of an authenticated mojang account.
*
* @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The new Access Token.
*
* @returns {Object} The authenticated account object created by this action.
*/
exports.updateMojangAuthAccount = function(uuid, accessToken){
config.authenticationDatabase[uuid].accessToken = accessToken
config.authenticationDatabase[uuid].type = 'mojang' // For gradual conversion.
return config.authenticationDatabase[uuid]
}
/**
* Adds an authenticated mojang account to the database to be stored.
*
* @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The accessToken of the authenticated account.
* @param {string} username The username (usually email) of the authenticated account.
* @param {string} displayName The in game name of the authenticated account.
*
* @returns {Object} The authenticated account object created by this action.
*/
exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName){
config.selectedAccount = uuid
config.authenticationDatabase[uuid] = {
type: 'mojang',
accessToken,
username: username.trim(),
uuid: uuid.trim(),
displayName: displayName.trim()
}
return config.authenticationDatabase[uuid]
}
/** /**
* Update the tokens of an authenticated microsoft account. * Update the tokens of an authenticated microsoft account.
* *
@@ -357,7 +301,7 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
* @param {string} msAccessToken The new Microsoft Access Token * @param {string} msAccessToken The new Microsoft Access Token
* @param {string} msRefreshToken The new Microsoft Refresh Token * @param {string} msRefreshToken The new Microsoft Refresh Token
* @param {date} msExpires The date when the microsoft access token expires * @param {date} msExpires The date when the microsoft access token expires
* @param {date} mcExpires The date when the mojang access token expires * @param {date} mcExpires The date when the Minecraft access token expires
* *
* @returns {Object} The authenticated account object created by this action. * @returns {Object} The authenticated account object created by this action.
*/ */
@@ -376,7 +320,7 @@ exports.updateMicrosoftAuthAccount = function(uuid, accessToken, msAccessToken,
* @param {string} uuid The uuid of the authenticated account. * @param {string} uuid The uuid of the authenticated account.
* @param {string} accessToken The accessToken of the authenticated account. * @param {string} accessToken The accessToken of the authenticated account.
* @param {string} name The in game name of the authenticated account. * @param {string} name The in game name of the authenticated account.
* @param {date} mcExpires The date when the mojang access token expires * @param {date} mcExpires The date when the Minecraft access token expires
* @param {string} msAccessToken The microsoft access token * @param {string} msAccessToken The microsoft access token
* @param {string} msRefreshToken The microsoft refresh token * @param {string} msRefreshToken The microsoft refresh token
* @param {date} msExpires The date when the microsoft access token expires * @param {date} msExpires The date when the microsoft access token expires
@@ -419,7 +363,6 @@ exports.removeAuthAccount = function(uuid){
config.selectedAccount = keys[0] config.selectedAccount = keys[0]
} else { } else {
config.selectedAccount = null config.selectedAccount = null
config.clientToken = null
} }
} }
return true return true
+65 -32
View File
@@ -13,17 +13,20 @@ const ConfigManager = require('./configmanager')
const logger = LoggerUtil.getLogger('ProcessBuilder') const logger = LoggerUtil.getLogger('ProcessBuilder')
/** /**
* SurviePote : dossiers système contenant libGL/libEGL/libvulkan (dispatch + drivers). * SurviePote : dossiers système contenant les libs natives dlopen par le jeu
* Nécessaire là où dlopen ne les voit pas (ex: NixOS hors wrapper, LD_LIBRARY_PATH vide). * (libGL/libEGL/libvulkan + drivers Mesa, et backends audio libasound/libpulse/libpipewire).
* Nécessaires là où dlopen ne les voit pas (ex: NixOS hors wrapper, LD_LIBRARY_PATH vide).
* Sans ça : "[LWJGL] libGL.so.1 not found in system paths" ou "Failed to open OpenAL device".
* Ne retourne que des dossiers existants. Ordre : dispatch d'abord, drivers ensuite. * Ne retourne que des dossiers existants. Ordre : dispatch d'abord, drivers ensuite.
* *
* @returns {Array.<string>} Dossiers à ajouter à LD_LIBRARY_PATH pour le jeu. * @returns {Array.<string>} Dossiers à ajouter à LD_LIBRARY_PATH pour le jeu.
*/ */
function resolveSystemGLDirs(){ function resolveSystemNativeDirs(){
const dirs = [] const dirs = []
const candidates = [ const candidates = [
'/run/opengl-driver/lib', '/run/opengl-driver/lib',
'/run/opengl-driver-32/lib', '/run/opengl-driver-32/lib',
'/run/current-system/sw/lib',
'/usr/lib/x86_64-linux-gnu', '/usr/lib/x86_64-linux-gnu',
'/usr/lib64', '/usr/lib64',
'/usr/lib' '/usr/lib'
@@ -35,11 +38,13 @@ function resolveSystemGLDirs(){
} }
} catch(_err) { /* ignore */ } } catch(_err) { /* ignore */ }
} }
// NixOS : libglvnd (dispatch libGL/libEGL) + vulkan-loader dans le store. // NixOS : libglvnd (dispatch GL), vulkan-loader, alsa/pulse/pipewire dans le store.
try { try {
const store = fs.readdirSync('/nix/store') const store = fs.readdirSync('/nix/store')
for(const entry of store){ for(const entry of store){
if(entry.includes('libglvnd') || entry.includes('vulkan-loader')){ if(entry.includes('libglvnd') || entry.includes('vulkan-loader')
|| entry.includes('alsa-lib') || entry.includes('pulseaudio')
|| (entry.includes('pipewire') && !entry.includes('python'))){
const lib = path.join('/nix/store', entry, 'lib') const lib = path.join('/nix/store', entry, 'lib')
if(fs.existsSync(lib) && !dirs.includes(lib)){ if(fs.existsSync(lib) && !dirs.includes(lib)){
dirs.push(lib) dirs.push(lib)
@@ -50,6 +55,41 @@ function resolveSystemGLDirs(){
return dirs return dirs
} }
/**
* SurviePote : détecte une session Wayland (socket wayland-* du runtime dir).
*
* @returns {boolean} True si un socket Wayland est visible.
*/
function hasWaylandSocket(){
try {
const runtime = process.env.XDG_RUNTIME_DIR || '/run/user/' + (process.getuid ? process.getuid() : 1000)
return fs.readdirSync(runtime).some(f => f.startsWith('wayland-') && !f.endsWith('.lock'))
} catch(_err) {
return false
}
}
/**
* SurviePote : dossier des plugins SPA PipeWire (routage audio).
* Sans lui, OpenAL voit PipeWire mais ne peut router aucun son.
*
* @returns {string|null} Chemin spa-0.2 ou null.
*/
function resolveSpaPluginDir(){
try {
const store = fs.readdirSync('/nix/store')
for(const entry of store){
if(entry.includes('pipewire') && !entry.includes('python')){
const spa = path.join('/nix/store', entry, 'lib', 'spa-0.2')
if(fs.existsSync(spa)){
return spa
}
}
}
} catch(_err) { /* ignore */ }
return null
}
/** /**
* Only forge and fabric are top level mod loaders. * Only forge and fabric are top level mod loaders.
@@ -120,11 +160,25 @@ class ProcessBuilder {
// Sans ça : "[LWJGL] libGL.so.1 not found in system paths" -> crash "no OpenGL context API". // Sans ça : "[LWJGL] libGL.so.1 not found in system paths" -> crash "no OpenGL context API".
const gameEnv = { ...process.env } const gameEnv = { ...process.env }
if(process.platform === 'linux'){ if(process.platform === 'linux'){
const glDirs = resolveSystemGLDirs() const nativeDirs = resolveSystemNativeDirs()
if(glDirs.length > 0){ if(nativeDirs.length > 0){
const prev = gameEnv.LD_LIBRARY_PATH ? gameEnv.LD_LIBRARY_PATH + ':' : '' const prev = gameEnv.LD_LIBRARY_PATH ? gameEnv.LD_LIBRARY_PATH + ':' : ''
gameEnv.LD_LIBRARY_PATH = prev + glDirs.join(':') gameEnv.LD_LIBRARY_PATH = prev + nativeDirs.join(':')
logger.info('Prepended GL driver dirs to LD_LIBRARY_PATH:', glDirs.join(':')) logger.info('Prepended system native dirs to LD_LIBRARY_PATH:', nativeDirs.join(':'))
}
if(!gameEnv.SPA_PLUGIN_DIR){
const spa = resolveSpaPluginDir()
if(spa){
gameEnv.SPA_PLUGIN_DIR = spa
logger.info('Set SPA_PLUGIN_DIR:', spa)
}
}
// SurviePote : sur Wayland, forcer SDL en natif plutôt que XWayland.
// XWayland rootless n'a pas de GLX direct -> backend OpenGL mort + Iris abort.
// En natif Wayland, EGL fonctionne et le backend OpenGL (requis par Iris) s'init.
if(!gameEnv.SDL_VIDEODRIVER && (gameEnv.WAYLAND_DISPLAY || hasWaylandSocket())){
gameEnv.SDL_VIDEODRIVER = 'wayland'
logger.info('Set SDL_VIDEODRIVER=wayland')
} }
} }
@@ -377,20 +431,6 @@ class ProcessBuilder {
} }
_processAutoConnectArg(args){
if(ConfigManager.getAutoConnect() && this.server.rawServer.autoconnect){
if(mcVersionAtLeast('1.20', this.server.rawServer.minecraftVersion)){
args.push('--quickPlayMultiplayer')
args.push(`${this.server.hostname}:${this.server.port}`)
} else {
args.push('--server')
args.push(this.server.hostname)
args.push('--port')
args.push(this.server.port)
}
}
}
/** /**
* Construct the argument array that will be passed to the JVM process. * Construct the argument array that will be passed to the JVM process.
* *
@@ -562,7 +602,7 @@ class ProcessBuilder {
val = this.authUser.accessToken val = this.authUser.accessToken
break break
case 'user_type': case 'user_type':
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang' val = 'msa'
break break
case 'version_type': case 'version_type':
val = this.vanillaManifest.type val = this.vanillaManifest.type
@@ -593,10 +633,6 @@ class ProcessBuilder {
} }
} }
// Autoconnect
this._processAutoConnectArg(args)
// Forge Specific Arguments // Forge Specific Arguments
args = args.concat(this.modManifest.arguments.game) args = args.concat(this.modManifest.arguments.game)
@@ -646,7 +682,7 @@ class ProcessBuilder {
val = this.authUser.accessToken val = this.authUser.accessToken
break break
case 'user_type': case 'user_type':
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang' val = 'msa'
break break
case 'user_properties': // 1.8.9 and below. case 'user_properties': // 1.8.9 and below.
val = '{}' val = '{}'
@@ -661,9 +697,6 @@ class ProcessBuilder {
} }
} }
// Autoconnect to the selected server.
this._processAutoConnectArg(mcArgs)
// Prepare game resolution // Prepare game resolution
if(ConfigManager.getFullscreen()){ if(ConfigManager.getFullscreen()){
mcArgs.push('--fullscreen') mcArgs.push('--fullscreen')
-63
View File
@@ -4,7 +4,6 @@
// Requirements // Requirements
const { URL } = require('url') const { URL } = require('url')
const { const {
MojangRestAPI,
getServerStatus getServerStatus
} = require('helios-core/mojang') } = require('helios-core/mojang')
const { const {
@@ -176,65 +175,6 @@ server_selection_button.onclick = async e => {
await toggleServerSelection(true) await toggleServerSelection(true)
} }
// Update Mojang Status Color
const refreshMojangStatuses = async function(){
loggerLanding.info('Refreshing Mojang Statuses..')
let status = 'grey'
let tooltipEssentialHTML = ''
let tooltipNonEssentialHTML = ''
const response = await MojangRestAPI.status()
let statuses
if(response.responseStatus === RestResponseStatus.SUCCESS) {
statuses = response.data
} else {
loggerLanding.warn('Unable to refresh Mojang service status.')
statuses = MojangRestAPI.getDefaultStatuses()
}
greenCount = 0
greyCount = 0
for(let i=0; i<statuses.length; i++){
const service = statuses[i]
const tooltipHTML = `<div class="mojangStatusContainer">
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">&#8226;</span>
<span class="mojangStatusName">${service.name}</span>
</div>`
if(service.essential){
tooltipEssentialHTML += tooltipHTML
} else {
tooltipNonEssentialHTML += tooltipHTML
}
if(service.status === 'yellow' && status !== 'red'){
status = 'yellow'
} else if(service.status === 'red'){
status = 'red'
} else {
if(service.status === 'grey'){
++greyCount
}
++greenCount
}
}
if(greenCount === statuses.length){
if(greyCount === statuses.length){
status = 'grey'
} else {
status = 'green'
}
}
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status)
}
const refreshServerStatus = async (fade = false) => { const refreshServerStatus = async (fade = false) => {
loggerLanding.info('Refreshing Server Status') loggerLanding.info('Refreshing Server Status')
const serv = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer()) const serv = (await DistroAPI.getDistribution()).getServerById(ConfigManager.getSelectedServer())
@@ -266,11 +206,8 @@ const refreshServerStatus = async (fade = false) => {
} }
refreshMojangStatuses()
// Server Status is refreshed in uibinder.js on distributionIndexDone. // Server Status is refreshed in uibinder.js on distributionIndexDone.
// Refresh statuses every hour. The status page itself refreshes every day so...
let mojangStatusListener = setInterval(() => refreshMojangStatuses(true), 60*60*1000)
// Set refresh rate to once every 5 minutes. // Set refresh rate to once every 5 minutes.
let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000) let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000)
-234
View File
@@ -1,234 +0,0 @@
/**
* Script for login.ejs
*/
// Validation Regexes.
const validUsername = /^[a-zA-Z0-9_]{1,16}$/
const basicEmail = /^\S+@\S+\.\S+$/
//const validEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
// Login Elements
const loginCancelContainer = document.getElementById('loginCancelContainer')
const loginCancelButton = document.getElementById('loginCancelButton')
const loginEmailError = document.getElementById('loginEmailError')
const loginUsername = document.getElementById('loginUsername')
const loginPasswordError = document.getElementById('loginPasswordError')
const loginPassword = document.getElementById('loginPassword')
const checkmarkContainer = document.getElementById('checkmarkContainer')
const loginRememberOption = document.getElementById('loginRememberOption')
const loginButton = document.getElementById('loginButton')
const loginForm = document.getElementById('loginForm')
// Control variables.
let lu = false, lp = false
/**
* Show a login error.
*
* @param {HTMLElement} element The element on which to display the error.
* @param {string} value The error text.
*/
function showError(element, value){
element.innerHTML = value
element.style.opacity = 1
}
/**
* Shake a login error to add emphasis.
*
* @param {HTMLElement} element The element to shake.
*/
function shakeError(element){
if(element.style.opacity == 1){
element.classList.remove('shake')
void element.offsetWidth
element.classList.add('shake')
}
}
/**
* Validate that an email field is neither empty nor invalid.
*
* @param {string} value The email value.
*/
function validateEmail(value){
if(value){
if(!basicEmail.test(value) && !validUsername.test(value)){
showError(loginEmailError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true)
lu = false
} else {
loginEmailError.style.opacity = 0
lu = true
if(lp){
loginDisabled(false)
}
}
} else {
lu = false
showError(loginEmailError, Lang.queryJS('login.error.requiredValue'))
loginDisabled(true)
}
}
/**
* Validate that the password field is not empty.
*
* @param {string} value The password value.
*/
function validatePassword(value){
if(value){
loginPasswordError.style.opacity = 0
lp = true
if(lu){
loginDisabled(false)
}
} else {
lp = false
showError(loginPasswordError, Lang.queryJS('login.error.invalidValue'))
loginDisabled(true)
}
}
// Emphasize errors with shake when focus is lost.
loginUsername.addEventListener('focusout', (e) => {
validateEmail(e.target.value)
shakeError(loginEmailError)
})
loginPassword.addEventListener('focusout', (e) => {
validatePassword(e.target.value)
shakeError(loginPasswordError)
})
// Validate input for each field.
loginUsername.addEventListener('input', (e) => {
validateEmail(e.target.value)
})
loginPassword.addEventListener('input', (e) => {
validatePassword(e.target.value)
})
/**
* Enable or disable the login button.
*
* @param {boolean} v True to enable, false to disable.
*/
function loginDisabled(v){
if(loginButton.disabled !== v){
loginButton.disabled = v
}
}
/**
* Enable or disable loading elements.
*
* @param {boolean} v True to enable, false to disable.
*/
function loginLoading(v){
if(v){
loginButton.setAttribute('loading', v)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.login'), Lang.queryJS('login.loggingIn'))
} else {
loginButton.removeAttribute('loading')
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.login'))
}
}
/**
* Enable or disable login form.
*
* @param {boolean} v True to enable, false to disable.
*/
function formDisabled(v){
loginDisabled(v)
loginCancelButton.disabled = v
loginUsername.disabled = v
loginPassword.disabled = v
if(v){
checkmarkContainer.setAttribute('disabled', v)
} else {
checkmarkContainer.removeAttribute('disabled')
}
loginRememberOption.disabled = v
}
let loginViewOnSuccess = VIEWS.landing
let loginViewOnCancel = VIEWS.settings
let loginViewCancelHandler
function loginCancelEnabled(val){
if(val){
$(loginCancelContainer).show()
} else {
$(loginCancelContainer).hide()
}
}
loginCancelButton.onclick = (e) => {
switchView(getCurrentView(), loginViewOnCancel, 500, 500, () => {
loginUsername.value = ''
loginPassword.value = ''
loginCancelEnabled(false)
if(loginViewCancelHandler != null){
loginViewCancelHandler()
loginViewCancelHandler = null
}
})
}
// Disable default form behavior.
loginForm.onsubmit = () => { return false }
// Bind login button behavior.
loginButton.addEventListener('click', () => {
// Disable form.
formDisabled(true)
// Show loading stuff.
loginLoading(true)
AuthManager.addMojangAccount(loginUsername.value, loginPassword.value).then((value) => {
updateSelectedAccount(value)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.loggingIn'), Lang.queryJS('login.success'))
$('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle()
setTimeout(() => {
switchView(VIEWS.login, loginViewOnSuccess, 500, 500, async () => {
// Temporary workaround
if(loginViewOnSuccess === VIEWS.settings){
await prepareSettings()
}
loginViewOnSuccess = VIEWS.landing // Reset this for good measure.
loginCancelEnabled(false) // Reset this for good measure.
loginViewCancelHandler = null // Reset this for good measure.
loginUsername.value = ''
loginPassword.value = ''
$('.circle-loader').toggleClass('load-complete')
$('.checkmark').toggle()
loginLoading(false)
loginButton.innerHTML = loginButton.innerHTML.replace(Lang.queryJS('login.success'), Lang.queryJS('login.login'))
formDisabled(false)
})
}, 1000)
}).catch((displayableError) => {
loginLoading(false)
let actualDisplayableError
if(isDisplayableError(displayableError)) {
msftLoginLogger.error('Error while logging in.', displayableError)
actualDisplayableError = displayableError
} else {
// Uh oh.
msftLoginLogger.error('Unhandled error during login.', displayableError)
actualDisplayableError = Lang.queryJS('login.error.unknown')
}
setOverlayContent(actualDisplayableError.title, actualDisplayableError.desc, Lang.queryJS('login.tryAgain'))
setOverlayHandler(() => {
formDisabled(false)
toggleOverlay(false)
})
toggleOverlay(true)
})
})
@@ -1,6 +1,5 @@
const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer') const loginOptionsCancelContainer = document.getElementById('loginOptionCancelContainer')
const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft') const loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft')
const loginOptionMojang = document.getElementById('loginOptionMojang')
const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton') const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton')
let loginOptionsCancellable = false let loginOptionsCancellable = false
@@ -28,20 +27,9 @@ loginOptionMicrosoft.onclick = (e) => {
}) })
} }
loginOptionMojang.onclick = (e) => {
switchView(getCurrentView(), VIEWS.login, 500, 500, () => {
loginViewOnSuccess = loginOptionsViewOnLoginSuccess
loginViewOnCancel = loginOptionsViewOnLoginCancel
loginCancelEnabled(true)
})
}
loginOptionsCancelButton.onclick = (e) => { loginOptionsCancelButton.onclick = (e) => {
switchView(getCurrentView(), loginOptionsViewOnCancel, 500, 500, () => { switchView(getCurrentView(), loginOptionsViewOnCancel, 500, 500, () => {
// Clear login values (Mojang login)
// No cleanup needed for Microsoft. // No cleanup needed for Microsoft.
loginUsername.value = ''
loginPassword.value = ''
if(loginOptionsViewCancelHandler != null){ if(loginOptionsViewCancelHandler != null){
loginOptionsViewCancelHandler() loginOptionsViewCancelHandler()
loginOptionsViewCancelHandler = null loginOptionsViewCancelHandler = null
+5 -38
View File
@@ -342,15 +342,6 @@ settingsNavDone.onclick = () => {
const msftLoginLogger = LoggerUtil.getLogger('Microsoft Login') const msftLoginLogger = LoggerUtil.getLogger('Microsoft Login')
const msftLogoutLogger = LoggerUtil.getLogger('Microsoft Logout') const msftLogoutLogger = LoggerUtil.getLogger('Microsoft Logout')
// Bind the add mojang account button.
document.getElementById('settingsAddMojangAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.login, 500, 500, () => {
loginViewOnCancel = VIEWS.settings
loginViewOnSuccess = VIEWS.settings
loginCancelEnabled(true)
})
}
// Bind the add microsoft account button. // Bind the add microsoft account button.
document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => { document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => {
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
@@ -511,32 +502,10 @@ let msAccDomElementCache
function processLogOut(val, isLastAccount){ function processLogOut(val, isLastAccount){
const parent = val.closest('.settingsAuthAccount') const parent = val.closest('.settingsAuthAccount')
const uuid = parent.getAttribute('uuid') const uuid = parent.getAttribute('uuid')
const prevSelAcc = ConfigManager.getSelectedAccount()
const targetAcc = ConfigManager.getAuthAccount(uuid)
if(targetAcc.type === 'microsoft') {
msAccDomElementCache = parent msAccDomElementCache = parent
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => { switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount) ipcRenderer.send(MSFT_OPCODE.OPEN_LOGOUT, uuid, isLastAccount)
}) })
} else {
AuthManager.removeMojangAccount(uuid).then(() => {
if(!isLastAccount && uuid === prevSelAcc.uuid){
const selAcc = ConfigManager.getSelectedAccount()
refreshAuthAccountSelected(selAcc.uuid)
updateSelectedAccount(selAcc)
validateSelectedAccount()
}
if(isLastAccount) {
loginOptionsCancelEnabled(false)
loginOptionsViewOnLoginSuccess = VIEWS.settings
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
switchView(getCurrentView(), VIEWS.loginOptions)
}
})
$(parent).fadeOut(250, () => {
parent.remove()
})
}
} }
// Bind reply for Microsoft Logout. // Bind reply for Microsoft Logout.
@@ -619,7 +588,6 @@ function refreshAuthAccountSelected(uuid){
} }
const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts') const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts')
const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts')
/** /**
* Add auth account elements for each one stored in the authentication database. * Add auth account elements for each one stored in the authentication database.
@@ -633,11 +601,15 @@ function populateAuthAccounts(){
const selectedUUID = ConfigManager.getSelectedAccount().uuid const selectedUUID = ConfigManager.getSelectedAccount().uuid
let microsoftAuthAccountStr = '' let microsoftAuthAccountStr = ''
let mojangAuthAccountStr = ''
authKeys.forEach((val) => { authKeys.forEach((val) => {
const acc = authAccounts[val] const acc = authAccounts[val]
// Les vieux comptes Mojang sont ignorés (auth Mojang supprimée).
if(acc.type !== 'microsoft') {
return
}
const accHtml = `<div class="settingsAuthAccount" uuid="${acc.uuid}"> const accHtml = `<div class="settingsAuthAccount" uuid="${acc.uuid}">
<div class="settingsAuthAccountLeft"> <div class="settingsAuthAccountLeft">
<img class="settingsAuthAccountImage" alt="${acc.displayName}" src="https://mc-heads.net/body/${acc.uuid}/60"> <img class="settingsAuthAccountImage" alt="${acc.displayName}" src="https://mc-heads.net/body/${acc.uuid}/60">
@@ -662,16 +634,11 @@ function populateAuthAccounts(){
</div> </div>
</div>` </div>`
if(acc.type === 'microsoft') {
microsoftAuthAccountStr += accHtml microsoftAuthAccountStr += accHtml
} else {
mojangAuthAccountStr += accHtml
}
}) })
settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr
settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr
} }
/** /**
+1 -19
View File
@@ -17,7 +17,6 @@ let fatalStartupError = false
const VIEWS = { const VIEWS = {
landing: '#landingContainer', landing: '#landingContainer',
loginOptions: '#loginOptionsContainer', loginOptions: '#loginOptionsContainer',
login: '#loginContainer',
settings: '#settingsContainer', settings: '#settingsContainer',
welcome: '#welcomeContainer', welcome: '#welcomeContainer',
waiting: '#waitingContainer' waiting: '#waitingContainer'
@@ -97,9 +96,7 @@ async function showMainUI(data){
} }
setTimeout(() => { setTimeout(() => {
$('#loadingContainer').fadeOut(500, () => { $('#loadingContainer').fadeOut(500)
$('#loadSpinnerImage').removeClass('rotating')
})
}, 250) }, 250)
}, 750) }, 750)
@@ -338,24 +335,12 @@ async function validateSelectedAccount(){
) )
setOverlayHandler(() => { setOverlayHandler(() => {
const isMicrosoft = selectedAcc.type === 'microsoft'
if(isMicrosoft) {
// Empty for now
} else {
// Mojang
// For convenience, pre-populate the username of the account.
document.getElementById('loginUsername').value = selectedAcc.username
validateEmail(selectedAcc.username)
}
loginOptionsViewOnLoginSuccess = getCurrentView() loginOptionsViewOnLoginSuccess = getCurrentView()
loginOptionsViewOnLoginCancel = VIEWS.loginOptions loginOptionsViewOnLoginCancel = VIEWS.loginOptions
if(accLen > 0) { if(accLen > 0) {
loginOptionsViewOnCancel = getCurrentView() loginOptionsViewOnCancel = getCurrentView()
loginOptionsViewCancelHandler = () => { loginOptionsViewCancelHandler = () => {
if(isMicrosoft) {
ConfigManager.addMicrosoftAuthAccount( ConfigManager.addMicrosoftAuthAccount(
selectedAcc.uuid, selectedAcc.uuid,
selectedAcc.accessToken, selectedAcc.accessToken,
@@ -365,9 +350,6 @@ async function validateSelectedAccount(){
selectedAcc.microsoft.refresh_token, selectedAcc.microsoft.refresh_token,
selectedAcc.microsoft.expires_at selectedAcc.microsoft.expires_at
) )
} else {
ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
}
ConfigManager.save() ConfigManager.save()
validateSelectedAccount() validateSelectedAccount()
} }
-19
View File
@@ -39,25 +39,6 @@
<span class="bot_label" id="landingPlayerLabel"><%- lang('landing.serverStatus') %></span> <span class="bot_label" id="landingPlayerLabel"><%- lang('landing.serverStatus') %></span>
<span id="player_count"><%- lang('landing.serverStatusPlaceholder') %></span> <span id="player_count"><%- lang('landing.serverStatusPlaceholder') %></span>
</div> </div>
<div class="bot_divider"></div>
<div id="mojangStatusWrapper">
<span class="bot_label"><%- lang('landing.mojangStatus') %></span>
<span id="mojang_status_icon">&#8226;</span>
<div id="mojangStatusTooltip">
<div id="mojangStatusTooltipTitle"><%- lang('landing.mojangStatusTooltipTitle') %></div>
<div id="mojangStatusEssentialContainer">
<!-- Essential Mojang services are populated here. -->
</div>
<div id="mojangStatusNEContainer">
<div class="mojangStatusNEBar"></div>
<div id="mojangStatusNETitle"><%- lang('landing.mojangStatusNETitle') %></div>
<div class="mojangStatusNEBar"></div>
</div>
<div id="mojangStatusNonEssentialContainer">
<!-- Non Essential Mojang services are populated here. -->
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
-65
View File
@@ -1,65 +0,0 @@
<div id="loginContainer" style="display: none;">
<div id="loginCancelContainer" style="display: none;">
<button id="loginCancelButton">
<div id="loginCancelIcon">X</div>
<span id="loginCancelText"><%- lang('login.loginCancelText') %></span>
</button>
</div>
<div id="loginContent">
<form id="loginForm">
<img id="loginImageSeal" src="assets/images/SealCircle.png"/>
<span id="loginSubheader"><%- lang('login.loginSubheader') %></span>
<div class="loginFieldContainer">
<svg id="profileSVG" class="loginSVG" viewBox="40 37 65.36 61.43">
<g>
<path d="M86.77,58.12A13.79,13.79,0,1,0,73,71.91,13.79,13.79,0,0,0,86.77,58.12M97,103.67a3.41,3.41,0,0,0,3.39-3.84,27.57,27.57,0,0,0-54.61,0,3.41,3.41,0,0,0,3.39,3.84Z"/>
</g>
</svg>
<span class="loginErrorSpan" id="loginEmailError"><%- lang('login.loginEmailError') %></span>
<input id="loginUsername" class="loginField" type="text" placeholder="<%- lang('login.loginEmailPlaceholder') %>"/>
</div>
<div class="loginFieldContainer">
<svg id="lockSVG" class="loginSVG" viewBox="40 32 60.36 70.43">
<g>
<path d="M86.16,54a16.38,16.38,0,1,0-32,0H44V102.7H96V54Zm-25.9-3.39a9.89,9.89,0,1,1,19.77,0A9.78,9.78,0,0,1,79.39,54H60.89A9.78,9.78,0,0,1,60.26,50.59ZM70,96.2a6.5,6.5,0,0,1-6.5-6.5,6.39,6.39,0,0,1,3.1-5.4V67h6.5V84.11a6.42,6.42,0,0,1,3.39,5.6A6.5,6.5,0,0,1,70,96.2Z"/>
</g>
</svg>
<span class="loginErrorSpan" id="loginPasswordError"><%- lang('login.loginPasswordError') %></span>
<input id="loginPassword" class="loginField" type="password" placeholder="<%- lang('login.loginPasswordPlaceholder') %>"/>
</div>
<div id="loginOptions">
<span class="loginSpanDim">
<a href="<%- lang('login.loginForgotPasswordLink') %>"><%- lang('login.loginForgotPasswordText') %></a>
</span>
<label id="checkmarkContainer">
<input id="loginRememberOption" type="checkbox" checked>
<span id="loginRememberText" class="loginSpanDim"><%- lang('login.loginRememberMeText') %></span>
<span class="loginCheckmark"></span>
</label>
</div>
<button id="loginButton" disabled>
<div id="loginButtonContent">
<%- lang('login.loginButtonText') %>
<svg id="loginSVG" viewBox="0 0 24.87 13.97">
<defs>
<style>.arrowLine{fill:none;stroke:#FFF;stroke-width:2px;transition: 0.25s ease;}</style>
</defs>
<polyline class="arrowLine" points="0.71 13.26 12.56 1.41 24.16 13.02"/>
</svg>
<div class="circle-loader">
<div class="checkmark draw"></div>
</div>
<!--<div class="spinningCircle" id="loginSpinner"></div>-->
</div>
</button>
<div id="loginDisclaimer">
<span class="loginSpanDim" id="loginRegisterSpan">
<a href="<%- lang('login.loginNeedAccountLink') %>"><%- lang('login.loginNeedAccountText') %></a>
</span>
<p class="loginDisclaimerText"><%- lang('login.loginPasswordDisclaimer1') %></p>
<p class="loginDisclaimerText"><%- lang('login.loginPasswordDisclaimer2', { appName: lang('app.title') }) %></p>
</div>
</form>
</div>
<script src="./assets/js/scripts/login.js"></script>
</div>
-10
View File
@@ -14,16 +14,6 @@
<span><%- lang('loginOptions.loginWithMicrosoft') %></span> <span><%- lang('loginOptions.loginWithMicrosoft') %></span>
</button> </button>
</div> </div>
<div class="loginOptionButtonContainer">
<button id="loginOptionMojang" class="loginOptionButton">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667">
<path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" />
<path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" />
<path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" />
</svg>
<span><%- lang('loginOptions.loginWithMojang') %></span>
</button>
</div>
</div> </div>
<div id="loginOptionCancelContainer" style="display: none;"> <div id="loginOptionCancelContainer" style="display: none;">
<button id="loginOptionCancelButton"><%- lang('loginOptions.cancelButton') %></button> <button id="loginOptionCancelButton"><%- lang('loginOptions.cancelButton') %></button>
-31
View File
@@ -48,26 +48,6 @@
<!-- Microsoft auth accounts populated here. --> <!-- Microsoft auth accounts populated here. -->
</div> </div>
</div> </div>
<div class="settingsAuthAccountTypeContainer">
<div class="settingsAuthAccountTypeHeader">
<div class="settingsAuthAccountTypeHeaderLeft">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 9.677 9.667">
<path d="M-26.332-12.098h2.715c-1.357.18-2.574 1.23-2.715 2.633z" fill="#fff" />
<path d="M2.598.022h7.07L9.665 7c-.003 1.334-1.113 2.46-2.402 2.654H0V2.542C.134 1.2 1.3.195 2.598.022z" fill="#db2331" />
<path d="M1.54 2.844c.314-.76 1.31-.46 1.954-.528.785-.083 1.503.272 2.1.758l.164-.9c.327.345.587.756.964 1.052.28.254.655-.342.86-.013.42.864.408 1.86.54 2.795l-.788-.373C6.9 4.17 5.126 3.052 3.656 3.685c-1.294.592-1.156 2.65.06 3.255 1.354.703 2.953.51 4.405.292-.07.42-.34.87-.834.816l-4.95.002c-.5.055-.886-.413-.838-.89l.04-4.315z" fill="#fff" />
</svg>
<span><%- lang('settings.mojangAccount') %></span>
</div>
<div class="settingsAuthAccountTypeHeaderRight">
<button class="settingsAddAuthAccount" id="settingsAddMojangAccount"><%- lang('settings.addMojangAccount') %></button>
</div>
</div>
<div class="settingsCurrentAccounts" id="settingsCurrentMojangAccounts">
<!-- Mojang auth accounts populated here. -->
</div>
</div>
</div> </div>
<div id="settingsTabMinecraft" class="settingsTab" style="display: none;"> <div id="settingsTabMinecraft" class="settingsTab" style="display: none;">
<div class="settingsTabHeader"> <div class="settingsTabHeader">
@@ -93,17 +73,6 @@
</label> </label>
</div> </div>
</div> </div>
<div class="settingsFieldContainer">
<div class="settingsFieldLeft">
<span class="settingsFieldTitle"><%- lang('settings.autoConnectTitle') %></span>
</div>
<div class="settingsFieldRight">
<label class="toggleSwitch">
<input type="checkbox" cValue="AutoConnect">
<span class="toggleSwitchSlider"></span>
</label>
</div>
</div>
<div class="settingsFieldContainer"> <div class="settingsFieldContainer">
<div class="settingsFieldLeft"> <div class="settingsFieldLeft">
<span class="settingsFieldTitle"><%- lang('settings.launchDetachedTitle') %></span> <span class="settingsFieldTitle"><%- lang('settings.launchDetachedTitle') %></span>
+1 -1
View File
@@ -239,7 +239,7 @@ function createWindow() {
remoteMain.enable(win.webContents) remoteMain.enable(win.webContents)
const data = { const data = {
bkid: Math.floor((Math.random() * fs.readdirSync(path.join(__dirname, 'app', 'assets', 'images', 'backgrounds')).length)), bkid: 0,
lang: (str, placeHolders) => LangLoader.queryEJS(str, placeHolders) lang: (str, placeHolders) => LangLoader.queryEJS(str, placeHolders)
} }
Object.entries(data).forEach(([key, val]) => ejse.data(key, val)) Object.entries(data).forEach(([key, val]) => ejse.data(key, val))
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "surviepote-launcher", "name": "surviepote-launcher",
"version": "1.0.0", "version": "1.1.0",
"productName": "SurviePote", "productName": "SurviePote",
"description": "Launcher officiel SurviePote - Rejoins mcpv.ihorizon.org", "description": "Launcher officiel SurviePote - Rejoins mcpv.ihorizon.org",
"author": "SurviePote", "author": "SurviePote",
+16
View File
@@ -0,0 +1,16 @@
# nosingleplayer (mod maison SurviePote)
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
(téléchargé au build) + loader/mixin en `compileOnly`. Les noms Mojang passent tels quels,
pas de refmap nécessaire.
Build : `cd mods/nosingleplayer && ./gradlew build` (dans nix-shell racine pour Java 25).
Jar : `build/libs/nosingleplayer-1.1.4.jar` → copié dans `atlas/.../fabricmods/required/`.
+163
View File
@@ -0,0 +1,163 @@
// SurviePote - nosingleplayer : build SANS loom.
// MC 26.3 n'est plus obfusqué (noms Mojang lisibles dans le jar vanilla) et Mojang ne
// publie plus de mappings (pas de client_mappings/server_mappings, pas de Yarn 26.x).
// On compile directement contre le jar client Mojang + loader/mixin en compileOnly.
plugins {
id 'java'
}
version = project.mod_version
group = project.maven_group
base {
archivesName = project.archives_base_name
}
def mcDir = layout.buildDirectory.dir('mc')
tasks.register('downloadMcLibs') {
def manifestUrl = 'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json'
def mcVersion = project.minecraft_version
def loaderVersion = project.loader_version
outputs.dir(mcDir)
doLast {
def outDir = mcDir.get().asFile
outDir.mkdirs()
def dl = { String url, File dest ->
if (!dest.exists()) {
println "Téléchargement ${dest.name} ..."
new URL(url).withInputStream { i -> dest.withOutputStream { it << i } }
}
}
def manifest = new groovy.json.JsonSlurper().parse(new URL(manifestUrl))
def entry = manifest.versions.find { it.id == mcVersion }
if (entry == null) throw new GradleException("Version MC introuvable : ${mcVersion}")
def versionJson = new groovy.json.JsonSlurper().parse(new URL(entry.url))
dl(versionJson.downloads.client.url, new File(outDir, 'client.jar'))
dl("https://maven.fabricmc.net/net/fabricmc/fabric-loader/${loaderVersion}/fabric-loader-${loaderVersion}.jar",
new File(outDir, 'fabric-loader.jar'))
dl('https://maven.fabricmc.net/net/fabricmc/sponge-mixin/0.17.5+mixin.0.8.7/sponge-mixin-0.17.5+mixin.0.8.7.jar',
new File(outDir, 'sponge-mixin.jar'))
dl('https://repo1.maven.org/maven2/org/slf4j/slf4j-api/2.0.17/slf4j-api-2.0.17.jar',
new File(outDir, 'slf4j-api.jar'))
dl('https://libraries.minecraft.net/com/mojang/brigadier/1.3.11/brigadier-1.3.11.jar',
new File(outDir, 'brigadier.jar'))
// Annotations JSpecify : referencées par les signatures de MC 26.3 (@Nullable...).
dl('https://repo1.maven.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar',
new File(outDir, 'jspecify.jar'))
}
}
dependencies {
compileOnly files({ tasks.named('downloadMcLibs').map { mcDir.get().asFile.listFiles().findAll { it.name.endsWith('.jar') } } })
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 25
it.dependsOn('downloadMcLibs')
}
processResources {
inputs.property 'version', project.version
filesMatching('fabric.mod.json') {
expand 'version': project.version
}
}
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'
}
+13
View File
@@ -0,0 +1,13 @@
# Fabric Properties (SurviePote - nosingleplayer)
minecraft_version=26.3
yarn_mappings=26.3
loader_version=0.20.0
loom_version=1.19.1
# Mod Properties
mod_version=1.1.8
maven_group=fr.surviepote
archives_base_name=nosingleplayer
# Dependencies (unused here, kept for template ref)
# fabric_version=0.163.0+26.3
Binary file not shown.
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+248
View File
@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+112
View File
@@ -0,0 +1,112 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@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 ##########################################################################
@rem
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
@rem Catch executions from older scripts and ensure they exit cleanly.
@rem This can be removed once we can be reasonably confident that few people
@rem will be migrating directly to this new wrapper.
goto afterSafetyNet
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto exitWithErrorLevel
:afterSafetyNet
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
1>&2 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.
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
1>&2 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.
"%COMSPEC%" /c exit 1
goto exitWithErrorLevel
:execute
@rem Setup the command line
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel
@rem This label must not be changed. We rely on old scripts being able to jump to this point.
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+5
View File
@@ -0,0 +1,5 @@
pluginManagement {
repositories {
gradlePluginPortal()
}
}
@@ -0,0 +1,69 @@
package fr.surviepote.nosingleplayer;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.renderpearl.api.textures.FilterMode;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.Identifier;
/**
* Chargement des textures du mod.
*
* <p>MC 26.3 n'expose plus les ressources des mods au ResourceManager : les packs ne
* sont lus que depuis des dossiers, pas depuis les jars du classpath. Une texture
* référencée par son Identifier est donc introuvable (placeholder magenta). On lit
* donc le PNG directement dans le jar du mod et on enregistre une DynamicTexture dans
* le TextureManager.
*/
public final class ModTextures {
private static final Map<Identifier, DynamicTexture> CACHE = new HashMap<>();
private ModTextures() {
}
/**
* Renvoie la texture (chargée depuis le jar du mod), ou null si le fichier est absent.
*
* @param id identifiant sous lequel la texture est enregistrée
* @param resourcePath chemin dans le jar, ex {@code /assets/nosingleplayer/...}
* @param name nom affiché dans les crash reports GPU
* @param linear true = filtrage linéaire (images agrandies), false = nearest (pixel art)
*/
public static DynamicTexture get(Identifier id, String resourcePath, String name, boolean linear) {
DynamicTexture texture = CACHE.get(id);
var textures = Minecraft.getInstance().getTextureManager();
if (texture != null && textures.getTexture(id) == texture) {
return texture; // déjà chargée et toujours enregistrée
}
try (InputStream in = ModTextures.class.getResourceAsStream(resourcePath)) {
if (in == null) {
NoSingleplayer.LOGGER.error("[SurviePote] Texture introuvable dans le jar : {}", resourcePath);
return null;
}
NativeImage image = NativeImage.read(in);
texture = linear
? new LinearTexture(() -> name, image)
: new DynamicTexture(() -> name, image);
CACHE.put(id, texture);
textures.register(id, texture);
return texture;
} catch (IOException e) {
NoSingleplayer.LOGGER.error("[SurviePote] Échec de chargement de la texture " + resourcePath, e);
return null;
}
}
/** DynamicTexture avec filtrage linéaire (utile quand l'image est réduite à l'écran). */
private static final class LinearTexture extends DynamicTexture {
LinearTexture(java.util.function.Supplier<String> name, NativeImage image) {
super(name, image);
this.sampler = RenderSystem.getSamplerCache().getClampToEdge(FilterMode.LINEAR);
}
}
}
@@ -0,0 +1,15 @@
package fr.surviepote.nosingleplayer;
import net.fabricmc.api.ClientModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NoSingleplayer implements ClientModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("nosingleplayer");
@Override
public void onInitializeClient() {
LOGGER.info("[SurviePote] Solo désactivé, direction le multi.");
}
}
@@ -0,0 +1,65 @@
package fr.surviepote.nosingleplayer.gui;
import fr.surviepote.nosingleplayer.ModTextures;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.input.InputWithModifiers;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
/**
* Bouton "Join Discord" : fond vanilla, logo Discord en pixel art et libellé.
* Ouvre l'invite Discord au clic.
*/
public class DiscordButton extends AbstractButton {
public static final Identifier TEXTURE = Identifier.fromNamespaceAndPath(
"nosingleplayer", "textures/gui/discord.png");
private static final String TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/discord.png";
private static final int ICON_SIZE = 16;
private static final int ICON_GAP = 5;
private static final int TEXT_COLOR = 0xFFFFFFFF;
private static final int TEXT_HEIGHT = 9;
private final Runnable action;
public DiscordButton(int x, int y, int width, int height, String label, Runnable action) {
super(x, y, width, height, Component.literal(label));
this.action = action;
// La texture doit être enregistrée avant le premier rendu.
ModTextures.get(TEXTURE, TEXTURE_PATH, "SurviePote Discord", false);
}
@Override
protected void extractContents(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
extractDefaultSprite(extractor); // fond standard (survol/clic inclus)
Font font = Minecraft.getInstance().font;
String label = getMessage().getString();
int textWidth = font.width(label);
int groupWidth = ICON_SIZE + ICON_GAP + textWidth;
int startX = getX() + (getWidth() - groupWidth) / 2;
int iconY = getY() + (getHeight() - ICON_SIZE) / 2;
int textY = getY() + (getHeight() - TEXT_HEIGHT) / 2;
extractor.blit(RenderPipelines.GUI_TEXTURED, TEXTURE,
startX, iconY, 0.0F, 0.0F, ICON_SIZE, ICON_SIZE, ICON_SIZE, ICON_SIZE, -1);
extractor.text(font, label, startX + ICON_SIZE + ICON_GAP, textY, TEXT_COLOR, true);
}
@Override
public void onPress(InputWithModifiers input) {
this.action.run();
}
@Override
protected void updateWidgetNarration(NarrationElementOutput output) {
defaultButtonNarrationText(output);
}
}
@@ -0,0 +1,54 @@
package fr.surviepote.nosingleplayer.gui;
import fr.surviepote.nosingleplayer.ModTextures;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.input.InputWithModifiers;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
/**
* Bouton icône vanilla (fond standard) avec une texture du mod, sans texte.
*/
public class IconButton extends AbstractButton {
public static final Identifier OPTIONS_TEXTURE = Identifier.fromNamespaceAndPath(
"nosingleplayer", "textures/gui/options.png");
public static final Identifier QUIT_TEXTURE = Identifier.fromNamespaceAndPath(
"nosingleplayer", "textures/gui/quit.png");
private static final int ICON_SIZE = 16;
private final Identifier texture;
private final Runnable action;
public IconButton(int x, int y, int width, int height, Component message,
Identifier texture, String resourcePath, Runnable action) {
super(x, y, width, height, message);
this.texture = texture;
this.action = action;
// La texture doit être enregistrée avant le premier rendu.
ModTextures.get(texture, resourcePath, message.getString(), false);
}
@Override
protected void extractContents(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
extractDefaultSprite(extractor); // fond standard (survol/clic inclus)
int iconX = getX() + (getWidth() - ICON_SIZE) / 2;
int iconY = getY() + (getHeight() - ICON_SIZE) / 2;
extractor.blit(RenderPipelines.GUI_TEXTURED, this.texture,
iconX, iconY, 0.0F, 0.0F, ICON_SIZE, ICON_SIZE, ICON_SIZE, ICON_SIZE, -1);
}
@Override
public void onPress(InputWithModifiers input) {
this.action.run();
}
@Override
protected void updateWidgetNarration(NarrationElementOutput output) {
defaultButtonNarrationText(output);
}
}
@@ -0,0 +1,127 @@
package fr.surviepote.nosingleplayer.gui;
import fr.surviepote.nosingleplayer.NoSingleplayer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.ServerStatusPinger;
import net.minecraft.network.chat.Component;
import net.minecraft.server.network.EventLoopGroupHolder;
/**
* Panneau "statut du serveur" affiché en haut à gauche du menu principal :
* état en ligne / hors ligne, nombre de joueurs et ping.
*
* <p>Note : dans ServerStatusPinger.pingServer, les deux Runnable passés ne sont pas
* des callbacks de succès/échec (le 1er ne fire que si le favicon change, le 2nd est
* le pong). On lit donc directement l'état dans ServerData : `status` contient le
* compteur de joueurs ("0/20") quand le statut est arrivé, et est vidé en cas d'échec.
*/
public class ServerStatusWidget extends AbstractWidget {
private static final String HOST = "mcpv.ihorizon.org:25565";
private static final long REFRESH_MS = 15000L;
private static final long TIMEOUT_MS = 8000L;
private static final int BG_COLOR = 0xB0000000;
private static final int ONLINE_COLOR = 0xFF4CD964;
private static final int OFFLINE_COLOR = 0xFFE04B4B;
private static final int PENDING_COLOR = 0xFFAAAAAA;
private static final int TEXT_COLOR = 0xFFFFFFFF;
private final ServerStatusPinger pinger = new ServerStatusPinger();
private final ServerData data = new ServerData("SurviePote", HOST, ServerData.Type.OTHER);
private volatile boolean resolved;
private volatile boolean online;
private volatile String playersText = "";
private volatile long ping = -1L;
private long nextRefresh;
private long pingStartedAt;
private boolean pending;
public ServerStatusWidget(int x, int y, int width, int height) {
super(x, y, width, height, Component.literal("Statut du serveur SurviePote"));
}
@Override
protected void extractWidgetRenderState(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick) {
long now = System.currentTimeMillis();
if (now >= this.nextRefresh) {
this.nextRefresh = now + REFRESH_MS;
refresh();
}
if (this.pending && now - this.pingStartedAt > TIMEOUT_MS) {
this.pending = false; // aucune réponse du serveur
}
this.pinger.tick();
readStatus();
Font font = Minecraft.getInstance().font;
boolean online = this.online;
boolean resolved = this.resolved;
String statusText = !resolved ? "Connexion..." : (online ? "En ligne" : "Hors ligne");
int statusColor = !resolved ? PENDING_COLOR : (online ? ONLINE_COLOR : OFFLINE_COLOR);
String playersText = online
? (this.playersText.matches("\\d+/\\d+") ? this.playersText + " joueurs" : this.playersText)
: (resolved ? "Joueurs : -" : "");
String pingText = online && this.ping > 0 ? this.ping + " ms" : "";
// Fond du panneau.
extractor.fill(getX(), getY(), getX() + getWidth(), getY() + getHeight(), BG_COLOR);
// Pastille d'état.
extractor.fill(getX() + 4, getY() + 4, getX() + 9, getY() + 9, statusColor);
// Textes.
extractor.text(font, statusText, getX() + 12, getY() + 3, statusColor, true);
if (!playersText.isEmpty()) {
extractor.text(font, playersText, getX() + 12, getY() + 13, TEXT_COLOR, true);
}
if (!pingText.isEmpty()) {
extractor.text(font, pingText,
getX() + getWidth() - font.width(pingText) - 4, getY() + 3, PENDING_COLOR, true);
}
}
/** Lit l'état réel du ping dans ServerData (mis à jour par le thread réseau). */
private void readStatus() {
Component status = this.data.status;
String text = status != null ? status.getString() : "";
if (!text.isEmpty()) {
this.online = true;
this.resolved = true;
this.playersText = text; // "0/20"
} else if (this.resolved) {
this.online = false; // le status a été vidé => échec du ping
} else if (!this.pending) {
this.resolved = true; // timeout : serveur injoignable
}
if (this.data.ping > 0) {
this.ping = this.data.ping;
}
}
private void refresh() {
try {
this.pingStartedAt = System.currentTimeMillis();
this.pending = true;
this.pinger.pingServer(this.data, () -> {
}, () -> {
}, EventLoopGroupHolder.remote(Minecraft.getInstance().options.useNativeTransport()));
} catch (Exception e) {
NoSingleplayer.LOGGER.warn("[SurviePote] Ping serveur impossible : {}", e.toString());
this.pending = false;
this.resolved = true;
this.online = false;
}
}
@Override
protected void updateWidgetNarration(NarrationElementOutput output) {
defaultButtonNarrationText(output);
}
}
@@ -0,0 +1,13 @@
package fr.surviepote.nosingleplayer.mixin;
import net.minecraft.client.gui.components.Button;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
/** Permet de récupérer l'action d'un bouton vanilla (pour la conserver). */
@Mixin(Button.class)
public interface ButtonAccessor {
@Accessor("onPress")
Button.OnPress surviepote$getOnPress();
}
@@ -0,0 +1,32 @@
package fr.surviepote.nosingleplayer.mixin;
import fr.surviepote.nosingleplayer.NoSingleplayer;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* Titre de la fenêtre : "Minecraft* 26.3" -> "SurviePote [26.3]".
* Minecraft.createTitle() est privé, appelé au démarrage (création de la fenêtre)
* et par updateTitle() (changement de monde, focus, resize...).
*/
@Mixin(Minecraft.class)
public abstract class MinecraftTitleMixin {
@Unique
private static boolean surviepote$titleLogged;
@Inject(method = "createTitle", at = @At("HEAD"), cancellable = true)
private void surviepote$customWindowTitle(CallbackInfoReturnable<String> cir) {
String title = "SurviePote [" + SharedConstants.getCurrentVersion().name() + "]";
cir.setReturnValue(title);
if (!surviepote$titleLogged) {
surviepote$titleLogged = true;
NoSingleplayer.LOGGER.info("[SurviePote] Titre de fenêtre : {}", title);
}
}
}
@@ -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);
}
}
@@ -0,0 +1,17 @@
package fr.surviepote.nosingleplayer.mixin;
import com.mojang.realmsclient.gui.screens.RealmsNotificationsScreen;
import net.minecraft.client.gui.GuiGraphicsExtractor;
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.callback.CallbackInfo;
@Mixin(RealmsNotificationsScreen.class)
public class RealmsNotificationsMixin {
@Inject(method = "extractRenderState", at = @At("HEAD"), cancellable = true)
private void surviepote$hideRealmsNotifications(GuiGraphicsExtractor extractor, int mouseX, int mouseY, float partialTick, CallbackInfo ci) {
ci.cancel();
}
}
@@ -0,0 +1,14 @@
package fr.surviepote.nosingleplayer.mixin;
import java.util.List;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.screens.Screen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Screen.class)
public interface ScreenAccessor {
@Accessor("renderables")
List<Renderable> surviepote$getRenderables();
}
@@ -0,0 +1,165 @@
package fr.surviepote.nosingleplayer.mixin;
import com.mojang.blaze3d.Blaze3D;
import fr.surviepote.nosingleplayer.gui.DiscordButton;
import fr.surviepote.nosingleplayer.gui.IconButton;
import fr.surviepote.nosingleplayer.gui.ServerStatusWidget;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.FriendsButton;
import net.minecraft.client.gui.components.PlainTextButton;
import net.minecraft.client.gui.components.SpriteIconButton;
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.resolver.ServerAddress;
import net.minecraft.network.chat.Component;
import fr.surviepote.nosingleplayer.NoSingleplayer;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public abstract class TitleScreenMixin {
private static final String SERVER_HOST = "mcpv.ihorizon.org:25565";
private static final String DISCORD_URL = "https://discord.gg/EpkgEPv5c";
private static final int ICON_SIZE = 20;
private static final String OPTIONS_TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/options.png";
private static final String QUIT_TEXTURE_PATH = "/assets/nosingleplayer/textures/gui/quit.png";
@Unique
private static void surviepote$removeWidget(TitleScreen self, GuiEventListener widget) {
self.children().remove(widget);
((ScreenAccessor) self).surviepote$getRenderables().remove(widget);
}
@Inject(method = "init", at = @At("TAIL"))
private void surviepote$customizeMenu(CallbackInfo ci) {
TitleScreen self = (TitleScreen) (Object) this;
Minecraft minecraft = Minecraft.getInstance();
String solo = Component.translatable("menu.singleplayer").getString();
String multi = Component.translatable("menu.multiplayer").getString();
String realms = Component.translatable("menu.online").getString();
String options = Component.translatable("menu.options").getString();
String quit = Component.translatable("menu.quit").getString();
int joinX = minecraft.getWindow().getGuiScaledWidth() / 2 - 100;
int joinY = minecraft.getWindow().getGuiScaledHeight() / 4 + 72;
boolean foundMulti = false;
List<GuiEventListener> toRemove = new ArrayList<>();
Button optionsButton = null;
Button quitButton = null;
for (GuiEventListener child : new ArrayList<>(self.children())) {
// ATTENTION : FriendsButton/SpriteIconButton/PlainTextButton héritent tous
// de Button. Il faut donc les tester AVANT "instanceof Button", sinon la
// première branche les avale et ils ne sont jamais retirés.
if (child instanceof FriendsButton) {
// Bouton "amis" (interactions sociales).
toRemove.add(child);
} else if (child instanceof SpriteIconButton) {
// Bas-gauche : langue, accessibilité, etc.
toRemove.add(child);
} else if (child instanceof PlainTextButton) {
// Copyright Mojang cliquable en bas.
toRemove.add(child);
} else if (child instanceof Button button) {
String msg = button.getMessage().getString();
if (msg.equals(solo)) {
// Solo retiré complètement (plutôt que grisé).
toRemove.add(button);
} else if (msg.equals(multi)) {
joinX = button.getX();
joinY = button.getY();
foundMulti = true;
toRemove.add(button);
} else if (msg.equals(realms)) {
toRemove.add(button);
} else if (msg.equals(options)) {
// Remplacé plus bas par un bouton icône (engrenage).
optionsButton = button;
toRemove.add(button);
} else if (msg.equals(quit)) {
// Remplacé plus bas par un bouton icône (power).
quitButton = button;
toRemove.add(button);
}
}
}
for (GuiEventListener widget : toRemove) {
surviepote$removeWidget(self, widget);
}
Button join = Button.builder(Component.literal("Rejoindre SurviePote"), pressed -> {
ServerData data = new ServerData("SurviePote", SERVER_HOST, ServerData.Type.OTHER);
// 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" })
List rawChildren = (List) self.children();
rawChildren.add(join);
// Bouton Discord (logo pixel art + texte) juste sous "Rejoindre SurviePote".
DiscordButton discord = new DiscordButton(joinX, joinY + 24, 200, 20, "Join Discord", () ->
Blaze3D.openUri(URI.create(DISCORD_URL)));
((ScreenAccessor) self).surviepote$getRenderables().add(discord);
rawChildren.add(discord);
// Options / Quit Game : boutons icônes (engrenage / power) sans texte.
// On garde l'action du bouton vanilla d'origine.
if (optionsButton != null) {
final Button vanillaOptions = optionsButton;
Button.OnPress press = ((ButtonAccessor) vanillaOptions).surviepote$getOnPress();
IconButton icon = new IconButton(
optionsButton.getX() + optionsButton.getWidth() / 2 - ICON_SIZE / 2,
optionsButton.getY(), ICON_SIZE, ICON_SIZE,
Component.literal("Options"),
IconButton.OPTIONS_TEXTURE, OPTIONS_TEXTURE_PATH,
() -> press.onPress(vanillaOptions));
((ScreenAccessor) self).surviepote$getRenderables().add(icon);
rawChildren.add(icon);
}
if (quitButton != null) {
final Button vanillaQuit = quitButton;
Button.OnPress press = ((ButtonAccessor) vanillaQuit).surviepote$getOnPress();
IconButton icon = new IconButton(
quitButton.getX() + quitButton.getWidth() / 2 - ICON_SIZE / 2,
quitButton.getY(), ICON_SIZE, ICON_SIZE,
Component.literal("Quit Game"),
IconButton.QUIT_TEXTURE, QUIT_TEXTURE_PATH,
() -> press.onPress(vanillaQuit));
((ScreenAccessor) self).surviepote$getRenderables().add(icon);
rawChildren.add(icon);
}
// Panneau de statut du serveur en haut à gauche (en ligne / joueurs / ping).
ServerStatusWidget status = new ServerStatusWidget(4, 4, 120, 24);
((ScreenAccessor) self).surviepote$getRenderables().add(status);
rawChildren.add(status);
if (join instanceof AbstractWidget w) {
w.visible = true;
w.active = true;
}
if (discord instanceof AbstractWidget w) {
w.visible = true;
w.active = true;
}
if (!foundMulti) {
NoSingleplayer.LOGGER.warn("[SurviePote] Bouton Multi introuvable, Join placé par défaut.");
}
}
}
@@ -0,0 +1,135 @@
package fr.surviepote.nosingleplayer.mixin;
import com.mojang.realmsclient.gui.screens.RealmsNotificationsScreen;
import fr.surviepote.nosingleplayer.ModTextures;
import fr.surviepote.nosingleplayer.NoSingleplayer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.LogoRenderer;
import net.minecraft.client.gui.components.SplashRenderer;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.Identifier;
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(TitleScreen.class)
public abstract class TitleScreenRenderMixin {
@Unique
private static final Identifier SURVIEPOTE_BACKGROUND = Identifier.fromNamespaceAndPath(
"nosingleplayer", "textures/gui/background.png");
// Le PNG est lu directement dans le jar du mod (voir surviepote$loadBackground).
@Unique
private static final String SURVIEPOTE_BACKGROUND_RESOURCE =
"/assets/nosingleplayer/textures/gui/background.png";
@Unique
private static DynamicTexture surviepote$background;
@Unique
private static int surviepote$backgroundWidth;
@Unique
private static int surviepote$backgroundHeight;
// Pas de panorama dynamique : fond statique custom à la place.
// Note : extractPanorama est déclarée dans Screen (héritée), on redirige donc
// l'APPEL dans TitleScreen plutôt que la méthode elle-même.
// Le fond couvre l'écran sans déformation : recadrage centré type "cover".
@Redirect(method = "extractRenderState",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/gui/screens/TitleScreen;extractPanorama(Lnet/minecraft/client/gui/GuiGraphicsExtractor;F)V"))
private void surviepote$customBackground(TitleScreen self, GuiGraphicsExtractor extractor, float partialTick) {
int w = Minecraft.getInstance().getWindow().getGuiScaledWidth();
int h = Minecraft.getInstance().getWindow().getGuiScaledHeight();
surviepote$loadBackground();
if (surviepote$background == null) {
return;
}
float screenAspect = (float) w / (float) h;
float texAspect = (float) surviepote$backgroundWidth / (float) surviepote$backgroundHeight;
int u, v, srcW, srcH;
if (screenAspect > texAspect) {
// Écran plus large que l'image : on rogne en haut/bas.
srcW = surviepote$backgroundWidth;
srcH = Math.round(surviepote$backgroundWidth / screenAspect);
u = 0;
v = (surviepote$backgroundHeight - srcH) / 2;
} else {
// Écran plus haut que l'image : on rogne à gauche/droite.
srcH = surviepote$backgroundHeight;
srcW = Math.round(surviepote$backgroundHeight * screenAspect);
u = (surviepote$backgroundWidth - srcW) / 2;
v = 0;
}
// Surcharge complète : (u, v) = origine dans la texture, (w, h) = taille à
// l'écran, (srcW, srcH) = taille de la région source dans la texture.
// L'overload court réutilise w/h comme région source (=> zoom sur un coin).
extractor.blit(RenderPipelines.GUI_TEXTURED, SURVIEPOTE_BACKGROUND,
0, 0, (float) u, (float) v,
w, h, srcW, srcH,
surviepote$backgroundWidth, surviepote$backgroundHeight, -1);
}
/**
* MC 26.3 n'expose plus les ressources des mods au ResourceManager : les packs
* ne sont lus que depuis des dossiers, pas depuis les jars du classpath. Une
* texture référencée par son Identifier est donc introuvable (placeholder
* magenta). Le PNG est donc lu dans le jar du mod (voir ModTextures).
*/
@Unique
private static void surviepote$loadBackground() {
DynamicTexture texture = ModTextures.get(SURVIEPOTE_BACKGROUND, SURVIEPOTE_BACKGROUND_RESOURCE,
"SurviePote Background", true);
if (texture == null) {
surviepote$background = null;
return;
}
if (surviepote$background != texture) {
surviepote$background = texture;
surviepote$backgroundWidth = texture.getPixels().getWidth();
surviepote$backgroundHeight = texture.getPixels().getHeight();
NoSingleplayer.LOGGER.info("[SurviePote] Fond chargé : {}x{}",
surviepote$backgroundWidth, surviepote$backgroundHeight);
}
}
// Pas de texte de version ("26.3 mc") en bas à gauche.
@Redirect(method = "extractRenderState",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;text(Lnet/minecraft/client/gui/Font;Ljava/lang/String;III)V"))
private void surviepote$skipVersionText(GuiGraphicsExtractor extractor, Font font, String text, int x, int y, int color) {
}
// Pas de texte de splash (message rotatif) sur le menu principal.
@Redirect(method = "extractRenderState",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/gui/components/SplashRenderer;extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;ILnet/minecraft/client/gui/Font;F)V"))
private void surviepote$skipSplash(SplashRenderer splash, GuiGraphicsExtractor extractor, int width, Font font, float fade) {
}
// Pas de logo Minecraft.
@Redirect(method = "extractRenderState",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/gui/components/LogoRenderer;extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IF)V"))
private void surviepote$skipLogo(LogoRenderer logo, GuiGraphicsExtractor extractor, int width, float fade) {
}
// Pas d'overlay Realms.
@Redirect(method = "extractRenderState",
at = @At(value = "INVOKE",
target = "Lcom/mojang/realmsclient/gui/screens/RealmsNotificationsScreen;extractRenderState(Lnet/minecraft/client/gui/GuiGraphicsExtractor;IIF)V"))
private void surviepote$skipRealmsOverlay(RealmsNotificationsScreen notifs, GuiGraphicsExtractor extractor,
int mouseX, int mouseY, float partialTick) {
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Some files were not shown because too many files have changed in this diff Show More