Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d02bb4b0ac | ||
|
|
49e429ce1b | ||
|
|
4f95e82812 | ||
|
|
89e6b6f680 | ||
|
|
fc80845f5c | ||
|
|
8053411565 | ||
|
|
c92059db26 | ||
|
|
fdc7d867ec | ||
|
|
b8a283a177 | ||
|
|
3a2b403acc | ||
|
|
37fcdfb277 | ||
|
|
20330310fe | ||
|
|
f4372ab8a4 | ||
|
|
7a1a4306b1 | ||
|
|
058c18c4cb | ||
|
|
3f389f65f2 | ||
|
|
19f0e05c8e | ||
|
|
722802c001 | ||
|
|
4a48d2c721 | ||
|
|
159a594b2c | ||
|
|
6dc8e02278 | ||
|
|
bd22c3c4e6 | ||
|
|
72978469aa | ||
|
|
7dac46275c | ||
|
|
16e980c133 | ||
|
|
b91cf69e6f | ||
|
|
dcbf327a57 | ||
|
|
4da821104d | ||
|
|
d24d863a64 | ||
|
|
8c3ae8d90e | ||
|
|
44c172021c | ||
|
|
89ea9fe43a | ||
|
|
ca01bd796c | ||
|
|
90615db6ff | ||
|
|
b3c9e8e207 | ||
|
|
65dbaaec22 | ||
|
|
1c6cd2bff6 | ||
|
|
9222e9fa95 | ||
|
|
1c4efce590 | ||
|
|
b30d3128b1 | ||
|
|
bb44266215 | ||
|
|
aecf8fa031 | ||
|
|
4195574341 | ||
|
|
1e29bfef67 | ||
|
|
53c7416aae | ||
|
|
ee42ccca7c | ||
|
|
5bfebd92ae | ||
|
|
48e73d2a18 | ||
|
|
ab2467c65d | ||
|
|
8fdd7fb1e1 | ||
|
|
66e0705505 | ||
|
|
6900627536 | ||
|
|
32c4e4069a | ||
|
|
d9f1f2af1d | ||
|
|
efc7267306 | ||
|
|
101384e4bf | ||
|
|
9a5b5acb03 | ||
|
|
e8bfd0d99c | ||
|
|
7878f006bd | ||
|
|
76cc105f19 | ||
|
|
7da06c6581 | ||
|
|
4379f63e8a | ||
|
|
a57b614de7 | ||
|
|
d5a88d4516 | ||
|
|
061f19d4ee | ||
|
|
8c4a6748ef | ||
|
|
588e516dbe | ||
|
|
77fbb485d7 | ||
|
|
1720463a87 | ||
|
|
0e97130479 | ||
|
|
52c3229684 | ||
|
|
8e280e751f | ||
|
|
58441d5048 | ||
|
|
baf29d5c11 | ||
|
|
41f4ff8f0a | ||
|
|
be6c0306e3 | ||
|
|
fdff425a48 | ||
|
|
da60aa2228 | ||
|
|
9efa6c5548 | ||
|
|
ba78a3b1e2 | ||
|
|
e3b4500bd6 | ||
|
|
6e0bed343b | ||
|
|
cbc742046d | ||
|
|
c984c9ae9c | ||
|
|
17b5b92fa5 | ||
|
|
0e2284a26b | ||
|
|
5141e38ea2 | ||
|
|
c76d67a2fd | ||
|
|
610dc597fb | ||
|
|
3330b5bd04 | ||
|
|
cc7a5f7c77 | ||
|
|
86cc852c81 | ||
|
|
a1c86f8be9 | ||
|
|
ba73b2080f | ||
|
|
dab383966d | ||
|
|
2e9223f117 | ||
|
|
cd5965a022 | ||
|
|
7169310cdc | ||
|
|
bc45c619a5 | ||
|
|
07986c3883 | ||
|
|
4eb9f850ca | ||
|
|
4575154fc8 | ||
|
|
45fb11c3ef | ||
|
|
156a23397c | ||
|
|
7e2b5011ee | ||
|
|
e800fde2ca | ||
|
|
3be064ad25 | ||
|
|
37c955d67a | ||
|
|
5782acc08a | ||
|
|
3b5e8e7a13 | ||
|
|
3a2dfeb21e | ||
|
|
cedc5c50c0 | ||
|
|
e8e99fdb9d | ||
|
|
bae95baed3 | ||
|
|
f278884cfa | ||
|
|
11f5976dde | ||
|
|
d666a65dac | ||
|
|
d7102cf7af | ||
|
|
50e1030442 | ||
|
|
3143443a8d | ||
|
|
101698b75b |
@@ -22,16 +22,11 @@ server/crash-reports/
|
||||
server/versions/
|
||||
server/*.log
|
||||
server/*.json
|
||||
server/config/
|
||||
server/fabricloader.log
|
||||
server/fabric-server-launcher.properties
|
||||
quarantine/*.jar
|
||||
|
||||
# server runtime
|
||||
server/world/
|
||||
server/logs/
|
||||
server/crash-reports/
|
||||
server/*.log
|
||||
|
||||
# OS / IDE
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -43,4 +38,10 @@ Thumbs.db
|
||||
.env
|
||||
atlas/.env
|
||||
server/.fabric/
|
||||
server/config/**/update/
|
||||
mods/*/.gradle/
|
||||
mods/*/build/
|
||||
mods/*/logs/
|
||||
.gradle/
|
||||
.direnv/
|
||||
mods/pala/.classpath
|
||||
mods/pala/.project
|
||||
|
||||
@@ -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é).
|
||||
@@ -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.
|
||||
@@ -1,9 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Name=SurviePote
|
||||
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
|
||||
Version=1.0
|
||||
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
|
||||
Categories=Game;
|
||||
Keywords=minecraft;surviepote;launcher;game;
|
||||
StartupNotify=true
|
||||
StartupWMClass=SurviePote
|
||||
SingleMainWindow=true
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
"address": "mcpv.ihorizon.org:25565",
|
||||
"minecraftVersion": "26.3",
|
||||
"mainServer": true,
|
||||
"autoconnect": true,
|
||||
"autoconnect": false,
|
||||
"javaOptions": {
|
||||
"distribution": "TEMURIN",
|
||||
"supported": ">=25.x",
|
||||
"suggestedMajor": 25
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"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",
|
||||
"name": "Architectury",
|
||||
@@ -134,6 +162,26 @@
|
||||
"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",
|
||||
"name": "Better Combat",
|
||||
@@ -154,6 +202,32 @@
|
||||
"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",
|
||||
"name": "Cloth Config v26.3",
|
||||
@@ -184,6 +258,42 @@
|
||||
"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",
|
||||
"name": "Easy Homes",
|
||||
@@ -194,6 +304,16 @@
|
||||
"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",
|
||||
"name": "Fabric API",
|
||||
@@ -214,6 +334,29 @@
|
||||
"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",
|
||||
"name": "GlitchCore",
|
||||
@@ -225,13 +368,26 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "com.blocklegend001:immersiveores:26.3-0.1@jar",
|
||||
"name": "ImmersiveOres",
|
||||
"id": "generated.fabricmod:ipo:7@jar",
|
||||
"name": "ImprovedPillagerOutpost",
|
||||
"type": "FabricMod",
|
||||
"artifact": {
|
||||
"size": 543252,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImmersiveOres-26.3-0.1.jar",
|
||||
"MD5": "840a11c59c29eadd909cba3ce6f569bb"
|
||||
"size": 614666,
|
||||
"url": "https://git.kisakay.com/k/mc/raw/branch/main/atlas/root/servers/SurviePote-26.3/fabricmods/required/ImprovedPillagerOutpost-v7.jar",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"name": "Nature's Compass",
|
||||
@@ -284,6 +476,26 @@
|
||||
"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",
|
||||
"name": "Packet Fixer",
|
||||
@@ -294,6 +506,16 @@
|
||||
"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",
|
||||
"name": "PatPat",
|
||||
@@ -324,6 +546,26 @@
|
||||
"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",
|
||||
"name": "Shogi",
|
||||
@@ -334,6 +576,52 @@
|
||||
"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",
|
||||
"name": "Storage Drawers",
|
||||
@@ -354,6 +642,16 @@
|
||||
"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",
|
||||
"name": "Tree Harvester",
|
||||
@@ -364,6 +662,16 @@
|
||||
"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",
|
||||
"name": "Upgrader",
|
||||
@@ -374,6 +682,19 @@
|
||||
"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",
|
||||
"name": "Veinminer",
|
||||
@@ -384,6 +705,19 @@
|
||||
"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",
|
||||
"name": "Waystones",
|
||||
@@ -404,6 +738,52 @@
|
||||
"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",
|
||||
"name": "settings.json",
|
||||
@@ -558,39 +938,6 @@
|
||||
"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",
|
||||
"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",
|
||||
"address": "mcpv.ihorizon.org:25565",
|
||||
"mainServer": true,
|
||||
"autoconnect": true
|
||||
"autoconnect": false,
|
||||
"javaOptions": {
|
||||
"distribution": "TEMURIN",
|
||||
"supported": ">=25.x",
|
||||
"suggestedMajor": 25
|
||||
}
|
||||
},
|
||||
"fabric": {
|
||||
"version": "0.20.0"
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -30,7 +30,6 @@
|
||||
<%- include('frame') %>
|
||||
<div id="main">
|
||||
<%- include('welcome') %>
|
||||
<%- include('login') %>
|
||||
<%- include('waiting') %>
|
||||
<%- include('loginOptions') %>
|
||||
<%- include('settings') %>
|
||||
@@ -40,8 +39,7 @@
|
||||
<div id="loadingContainer">
|
||||
<div id="loadingContent">
|
||||
<div id="loadSpinnerContainer">
|
||||
<img id="loadCenterImage" src="assets/images/LoadingSeal.png">
|
||||
<img id="loadSpinnerImage" class="rotating" src="assets/images/LoadingText.png">
|
||||
<img id="loadCenterImage" src="assets/images/SealCircle.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2858,9 +2858,7 @@ input:checked + .toggleSwitchSlider:before {
|
||||
height: 70px;
|
||||
width: auto;
|
||||
position: relative;
|
||||
border: 2px solid white;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Logo container styles. */
|
||||
@@ -2868,7 +2866,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
position: relative;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
border-radius: 50%;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
@@ -2885,7 +2882,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
width: 100%;
|
||||
top: 0%;
|
||||
left: 0%;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 15px #43c628;
|
||||
animation: glow-grow 4s ease-out infinite;
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
@@ -2970,8 +2966,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
|
||||
/* User profile avatar container. */
|
||||
#avatarContainer {
|
||||
border-radius: 50%;
|
||||
border: 2px solid #cad7e1;
|
||||
box-sizing: border-box;
|
||||
background: rgba(1, 2, 1, 0.5);
|
||||
height: 70px;
|
||||
@@ -3001,7 +2995,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
#avatarOverlay:hover,
|
||||
#avatarOverlay:focus {
|
||||
@@ -3215,109 +3208,6 @@ input:checked + .toggleSwitchSlider:before {
|
||||
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
|
||||
* * */
|
||||
@@ -3940,18 +3830,10 @@ input:checked + .toggleSwitchSlider:before {
|
||||
|
||||
/* Stationary image for the spinner. */
|
||||
#loadCenterImage {
|
||||
position: absolute;
|
||||
width: 277px;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Rotating image for the spinner. */
|
||||
#loadSpinnerImage {
|
||||
width: 280px;
|
||||
height: auto;
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
/* Rotating animation for the spinner. */
|
||||
@keyframes rotating {
|
||||
from {
|
||||
|
||||
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 502 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 268 KiB |
|
Before Width: | Height: | Size: 456 KiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 5.0 MiB |
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* AuthManager
|
||||
*
|
||||
* This module aims to abstract login procedures. Results from Mojang's REST api
|
||||
* are retrieved through our Mojang module. These results are processed and stored,
|
||||
* if applicable, in the config using the ConfigManager. All login procedures should
|
||||
* be made through this module.
|
||||
* This module aims to abstract login procedures. Microsoft auth results
|
||||
* are retrieved through our Microsoft module. These results are processed and
|
||||
* stored, if applicable, in the config using the ConfigManager. All login
|
||||
* procedures should be made through this module.
|
||||
*
|
||||
* @module authmanager
|
||||
*/
|
||||
@@ -12,7 +12,6 @@
|
||||
const ConfigManager = require('./configmanager')
|
||||
const { LoggerUtil } = require('helios-core')
|
||||
const { RestResponseStatus } = require('helios-core/common')
|
||||
const { MojangRestAPI, MojangErrorCode } = require('helios-core/mojang')
|
||||
const { MicrosoftAuth, MicrosoftErrorCode } = require('helios-core/microsoft')
|
||||
const { AZURE_CLIENT_ID } = require('./ipcconstants')
|
||||
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
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
|
||||
/**
|
||||
@@ -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.
|
||||
*
|
||||
* @param {string} authCode The authCode obtained from microsoft.
|
||||
@@ -266,31 +151,6 @@ exports.addMicrosoftAccount = async function(authCode) {
|
||||
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
|
||||
* 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,
|
||||
* 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(){
|
||||
const current = ConfigManager.getSelectedAccount()
|
||||
|
||||
if(current.type === 'microsoft') {
|
||||
return await validateSelectedMicrosoftAccount()
|
||||
} else {
|
||||
return await validateSelectedMojangAccount()
|
||||
// Auth Mojang supprimee (depreciee) : plus de sign-in Mojang au bootstrap.
|
||||
// Seuls les comptes Microsoft sont valides ; un ancien compte Mojang est
|
||||
// considere invalide sans appel reseau (l'authserver Mojang est ferme).
|
||||
if(current.type !== 'microsoft') {
|
||||
return false
|
||||
}
|
||||
|
||||
return await validateSelectedMicrosoftAccount()
|
||||
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const DEFAULT_CONFIG = {
|
||||
resWidth: 1280,
|
||||
resHeight: 720,
|
||||
fullscreen: false,
|
||||
autoConnect: true,
|
||||
autoConnect: false,
|
||||
launchDetached: true
|
||||
},
|
||||
launcher: {
|
||||
@@ -94,7 +94,6 @@ const DEFAULT_CONFIG = {
|
||||
content: null,
|
||||
dismissed: false
|
||||
},
|
||||
clientToken: null,
|
||||
selectedServer: null, // Resolved
|
||||
selectedAccount: null,
|
||||
authenticationDatabase: {},
|
||||
@@ -255,25 +254,6 @@ exports.getInstanceDirectory = function(){
|
||||
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.
|
||||
*
|
||||
@@ -281,7 +261,7 @@ exports.setClientToken = function(clientToken){
|
||||
* @returns {string} The ID of the selected serverpack.
|
||||
*/
|
||||
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]
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -357,7 +301,7 @@ exports.addMojangAuthAccount = function(uuid, accessToken, username, displayName
|
||||
* @param {string} msAccessToken The new Microsoft Access Token
|
||||
* @param {string} msRefreshToken The new Microsoft Refresh Token
|
||||
* @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.
|
||||
*/
|
||||
@@ -376,7 +320,7 @@ exports.updateMicrosoftAuthAccount = function(uuid, accessToken, msAccessToken,
|
||||
* @param {string} uuid The uuid 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 {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} msRefreshToken The microsoft refresh token
|
||||
* @param {date} msExpires The date when the microsoft access token expires
|
||||
@@ -419,7 +363,6 @@ exports.removeAuthAccount = function(uuid){
|
||||
config.selectedAccount = keys[0]
|
||||
} else {
|
||||
config.selectedAccount = null
|
||||
config.clientToken = null
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -13,17 +13,20 @@ const ConfigManager = require('./configmanager')
|
||||
const logger = LoggerUtil.getLogger('ProcessBuilder')
|
||||
|
||||
/**
|
||||
* SurviePote : dossiers système contenant libGL/libEGL/libvulkan (dispatch + drivers).
|
||||
* Nécessaire là où dlopen ne les voit pas (ex: NixOS hors wrapper, LD_LIBRARY_PATH vide).
|
||||
* SurviePote : dossiers système contenant les libs natives dlopen par le jeu
|
||||
* (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.
|
||||
*
|
||||
* @returns {Array.<string>} Dossiers à ajouter à LD_LIBRARY_PATH pour le jeu.
|
||||
*/
|
||||
function resolveSystemGLDirs(){
|
||||
function resolveSystemNativeDirs(){
|
||||
const dirs = []
|
||||
const candidates = [
|
||||
'/run/opengl-driver/lib',
|
||||
'/run/opengl-driver-32/lib',
|
||||
'/run/current-system/sw/lib',
|
||||
'/usr/lib/x86_64-linux-gnu',
|
||||
'/usr/lib64',
|
||||
'/usr/lib'
|
||||
@@ -35,11 +38,13 @@ function resolveSystemGLDirs(){
|
||||
}
|
||||
} 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 {
|
||||
const store = fs.readdirSync('/nix/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')
|
||||
if(fs.existsSync(lib) && !dirs.includes(lib)){
|
||||
dirs.push(lib)
|
||||
@@ -50,6 +55,41 @@ function resolveSystemGLDirs(){
|
||||
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.
|
||||
@@ -120,11 +160,25 @@ class ProcessBuilder {
|
||||
// Sans ça : "[LWJGL] libGL.so.1 not found in system paths" -> crash "no OpenGL context API".
|
||||
const gameEnv = { ...process.env }
|
||||
if(process.platform === 'linux'){
|
||||
const glDirs = resolveSystemGLDirs()
|
||||
if(glDirs.length > 0){
|
||||
const nativeDirs = resolveSystemNativeDirs()
|
||||
if(nativeDirs.length > 0){
|
||||
const prev = gameEnv.LD_LIBRARY_PATH ? gameEnv.LD_LIBRARY_PATH + ':' : ''
|
||||
gameEnv.LD_LIBRARY_PATH = prev + glDirs.join(':')
|
||||
logger.info('Prepended GL driver dirs to LD_LIBRARY_PATH:', glDirs.join(':'))
|
||||
gameEnv.LD_LIBRARY_PATH = prev + nativeDirs.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.
|
||||
*
|
||||
@@ -562,7 +602,7 @@ class ProcessBuilder {
|
||||
val = this.authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang'
|
||||
val = 'msa'
|
||||
break
|
||||
case 'version_type':
|
||||
val = this.vanillaManifest.type
|
||||
@@ -593,10 +633,6 @@ class ProcessBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Autoconnect
|
||||
this._processAutoConnectArg(args)
|
||||
|
||||
|
||||
// Forge Specific Arguments
|
||||
args = args.concat(this.modManifest.arguments.game)
|
||||
|
||||
@@ -646,7 +682,7 @@ class ProcessBuilder {
|
||||
val = this.authUser.accessToken
|
||||
break
|
||||
case 'user_type':
|
||||
val = this.authUser.type === 'microsoft' ? 'msa' : 'mojang'
|
||||
val = 'msa'
|
||||
break
|
||||
case 'user_properties': // 1.8.9 and below.
|
||||
val = '{}'
|
||||
@@ -661,9 +697,6 @@ class ProcessBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// Autoconnect to the selected server.
|
||||
this._processAutoConnectArg(mcArgs)
|
||||
|
||||
// Prepare game resolution
|
||||
if(ConfigManager.getFullscreen()){
|
||||
mcArgs.push('--fullscreen')
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// Requirements
|
||||
const { URL } = require('url')
|
||||
const {
|
||||
MojangRestAPI,
|
||||
getServerStatus
|
||||
} = require('helios-core/mojang')
|
||||
const {
|
||||
@@ -176,65 +175,6 @@ server_selection_button.onclick = async e => {
|
||||
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)};">•</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) => {
|
||||
loggerLanding.info('Refreshing Server Status')
|
||||
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.
|
||||
|
||||
// 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.
|
||||
let serverStatusListener = setInterval(() => refreshServerStatus(true), 300000)
|
||||
|
||||
|
||||
@@ -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 loginOptionMicrosoft = document.getElementById('loginOptionMicrosoft')
|
||||
const loginOptionMojang = document.getElementById('loginOptionMojang')
|
||||
const loginOptionsCancelButton = document.getElementById('loginOptionCancelButton')
|
||||
|
||||
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) => {
|
||||
switchView(getCurrentView(), loginOptionsViewOnCancel, 500, 500, () => {
|
||||
// Clear login values (Mojang login)
|
||||
// No cleanup needed for Microsoft.
|
||||
loginUsername.value = ''
|
||||
loginPassword.value = ''
|
||||
if(loginOptionsViewCancelHandler != null){
|
||||
loginOptionsViewCancelHandler()
|
||||
loginOptionsViewCancelHandler = null
|
||||
|
||||
@@ -342,15 +342,6 @@ settingsNavDone.onclick = () => {
|
||||
const msftLoginLogger = LoggerUtil.getLogger('Microsoft Login')
|
||||
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.
|
||||
document.getElementById('settingsAddMicrosoftAccount').onclick = (e) => {
|
||||
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
|
||||
@@ -511,32 +502,10 @@ let msAccDomElementCache
|
||||
function processLogOut(val, isLastAccount){
|
||||
const parent = val.closest('.settingsAuthAccount')
|
||||
const uuid = parent.getAttribute('uuid')
|
||||
const prevSelAcc = ConfigManager.getSelectedAccount()
|
||||
const targetAcc = ConfigManager.getAuthAccount(uuid)
|
||||
if(targetAcc.type === 'microsoft') {
|
||||
msAccDomElementCache = parent
|
||||
switchView(getCurrentView(), VIEWS.waiting, 500, 500, () => {
|
||||
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.
|
||||
@@ -619,7 +588,6 @@ function refreshAuthAccountSelected(uuid){
|
||||
}
|
||||
|
||||
const settingsCurrentMicrosoftAccounts = document.getElementById('settingsCurrentMicrosoftAccounts')
|
||||
const settingsCurrentMojangAccounts = document.getElementById('settingsCurrentMojangAccounts')
|
||||
|
||||
/**
|
||||
* Add auth account elements for each one stored in the authentication database.
|
||||
@@ -633,11 +601,15 @@ function populateAuthAccounts(){
|
||||
const selectedUUID = ConfigManager.getSelectedAccount().uuid
|
||||
|
||||
let microsoftAuthAccountStr = ''
|
||||
let mojangAuthAccountStr = ''
|
||||
|
||||
authKeys.forEach((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}">
|
||||
<div class="settingsAuthAccountLeft">
|
||||
<img class="settingsAuthAccountImage" alt="${acc.displayName}" src="https://mc-heads.net/body/${acc.uuid}/60">
|
||||
@@ -662,16 +634,11 @@ function populateAuthAccounts(){
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
if(acc.type === 'microsoft') {
|
||||
microsoftAuthAccountStr += accHtml
|
||||
} else {
|
||||
mojangAuthAccountStr += accHtml
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
settingsCurrentMicrosoftAccounts.innerHTML = microsoftAuthAccountStr
|
||||
settingsCurrentMojangAccounts.innerHTML = mojangAuthAccountStr
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@ let fatalStartupError = false
|
||||
const VIEWS = {
|
||||
landing: '#landingContainer',
|
||||
loginOptions: '#loginOptionsContainer',
|
||||
login: '#loginContainer',
|
||||
settings: '#settingsContainer',
|
||||
welcome: '#welcomeContainer',
|
||||
waiting: '#waitingContainer'
|
||||
@@ -97,9 +96,7 @@ async function showMainUI(data){
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
$('#loadingContainer').fadeOut(500, () => {
|
||||
$('#loadSpinnerImage').removeClass('rotating')
|
||||
})
|
||||
$('#loadingContainer').fadeOut(500)
|
||||
}, 250)
|
||||
|
||||
}, 750)
|
||||
@@ -338,24 +335,12 @@ async function validateSelectedAccount(){
|
||||
)
|
||||
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()
|
||||
loginOptionsViewOnLoginCancel = VIEWS.loginOptions
|
||||
|
||||
if(accLen > 0) {
|
||||
loginOptionsViewOnCancel = getCurrentView()
|
||||
loginOptionsViewCancelHandler = () => {
|
||||
if(isMicrosoft) {
|
||||
ConfigManager.addMicrosoftAuthAccount(
|
||||
selectedAcc.uuid,
|
||||
selectedAcc.accessToken,
|
||||
@@ -365,9 +350,6 @@ async function validateSelectedAccount(){
|
||||
selectedAcc.microsoft.refresh_token,
|
||||
selectedAcc.microsoft.expires_at
|
||||
)
|
||||
} else {
|
||||
ConfigManager.addMojangAuthAccount(selectedAcc.uuid, selectedAcc.accessToken, selectedAcc.username, selectedAcc.displayName)
|
||||
}
|
||||
ConfigManager.save()
|
||||
validateSelectedAccount()
|
||||
}
|
||||
|
||||
@@ -39,25 +39,6 @@
|
||||
<span class="bot_label" id="landingPlayerLabel"><%- lang('landing.serverStatus') %></span>
|
||||
<span id="player_count"><%- lang('landing.serverStatusPlaceholder') %></span>
|
||||
</div>
|
||||
<div class="bot_divider"></div>
|
||||
<div id="mojangStatusWrapper">
|
||||
<span class="bot_label"><%- lang('landing.mojangStatus') %></span>
|
||||
<span id="mojang_status_icon">•</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>
|
||||
|
||||
@@ -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>
|
||||
@@ -14,16 +14,6 @@
|
||||
<span><%- lang('loginOptions.loginWithMicrosoft') %></span>
|
||||
</button>
|
||||
</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 id="loginOptionCancelContainer" style="display: none;">
|
||||
<button id="loginOptionCancelButton"><%- lang('loginOptions.cancelButton') %></button>
|
||||
|
||||
@@ -48,26 +48,6 @@
|
||||
<!-- Microsoft auth accounts populated here. -->
|
||||
</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 id="settingsTabMinecraft" class="settingsTab" style="display: none;">
|
||||
<div class="settingsTabHeader">
|
||||
@@ -93,17 +73,6 @@
|
||||
</label>
|
||||
</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="settingsFieldLeft">
|
||||
<span class="settingsFieldTitle"><%- lang('settings.launchDetachedTitle') %></span>
|
||||
|
||||
@@ -239,7 +239,7 @@ function createWindow() {
|
||||
remoteMain.enable(win.webContents)
|
||||
|
||||
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)
|
||||
}
|
||||
Object.entries(data).forEach(([key, val]) => ejse.data(key, val))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "surviepote-launcher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"productName": "SurviePote",
|
||||
"description": "Launcher officiel SurviePote - Rejoins mcpv.ihorizon.org",
|
||||
"author": "SurviePote",
|
||||
|
||||
@@ -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/`.
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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" "$@"
|
||||
@@ -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%
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 98 B |
|
After Width: | Height: | Size: 98 B |
|
After Width: | Height: | Size: 1.9 MiB |