diff --git a/PALAMOD_CDC.md b/PALAMOD_CDC.md index 35fc62d..6ab7a24 100644 --- a/PALAMOD_CDC.md +++ b/PALAMOD_CDC.md @@ -601,6 +601,11 @@ avant M4 (TODO:crafts-machines). - 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-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 diff --git a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java index 79ddfec..83e8f1f 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java @@ -145,6 +145,34 @@ public final class PalaCommands { .executes(command -> balance(command.getSource()))); dispatcher.register(Commands.literal("rtp") .executes(command -> rtp(command.getSource()))); + dispatcher.register(Commands.literal("f") + .then(Commands.literal("create") + .then(Commands.argument("nom", + com.mojang.brigadier.arguments.StringArgumentType.word()) + .executes(command -> fCreate(command.getSource(), + com.mojang.brigadier.arguments.StringArgumentType + .getString(command, "nom"))))) + .then(Commands.literal("info") + .executes(command -> fInfo(command.getSource(), null)) + .then(Commands.argument("faction", + com.mojang.brigadier.arguments.StringArgumentType.word()) + .executes(command -> fInfo(command.getSource(), + com.mojang.brigadier.arguments.StringArgumentType + .getString(command, "faction"))))) + .then(Commands.literal("invite") + .then(Commands.argument("cible", EntityArgument.player()) + .executes(command -> fInvite(command.getSource(), + EntityArgument.getPlayer(command, "cible"))))) + .then(Commands.literal("join") + .then(Commands.argument("faction", + com.mojang.brigadier.arguments.StringArgumentType.word()) + .executes(command -> fJoin(command.getSource(), + com.mojang.brigadier.arguments.StringArgumentType + .getString(command, "faction"))))) + .then(Commands.literal("leave") + .executes(command -> fLeave(command.getSource()))) + .then(Commands.literal("disband") + .executes(command -> fDisband(command.getSource())))); net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents.END_SERVER_TICK .register(PalaCommands::tickSpawnQueue); net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents.END_SERVER_TICK @@ -289,6 +317,199 @@ public final class PalaCommands { } } + // M-faction-1 (wiki Gestion faction) : create (100 lingots paladium, la GUI touche F + // etant infaisable pur serveur — R2), info, invite, join, leave, disband. + // Nom : 3-16 [A-Za-z0-9_] (UNVERIFIED, wiki sans spec). Grades/claims/elo : M-faction-2. + private static int fCreate(CommandSourceStack source, String nom) { + try { + ServerPlayer player = source.getPlayerOrException(); + if (!nom.matches("[A-Za-z0-9_]{3,16}")) { + source.sendFailure(Component.literal("§cNom invalide (3-16 caracteres, lettres/chiffres/_).")); + return 0; + } + if (fr.surviepote.pala.core.PalaFactions.get().byMember(player.getUUID().toString()) != null) { + source.sendFailure(Component.literal("§cVous etes deja dans une faction.")); + return 0; + } + if (fr.surviepote.pala.core.PalaFactions.get().byName(nom) != null) { + source.sendFailure(Component.literal("§cCe nom est deja pris.")); + return 0; + } + if (!takeItems(player, fr.surviepote.pala.PalaItems.PALADIUM_INGOT, 100)) { + source.sendFailure(Component.literal("§cCreation : 100 lingots de paladium requis.")); + return 0; + } + fr.surviepote.pala.core.PalaFactions.Faction faction = + new fr.surviepote.pala.core.PalaFactions.Faction(); + faction.name = nom; + faction.owner = player.getUUID().toString(); + faction.members.add(player.getUUID().toString()); + fr.surviepote.pala.core.PalaFactions.get().put(faction); + source.getServer().getPlayerList().broadcastSystemMessage( + Component.literal("§6[Faction] §e" + player.getScoreboardName() + + " §7a cree la faction §e" + nom + " §7!"), + false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static boolean takeItems(ServerPlayer player, net.minecraft.world.item.Item item, int count) { + net.minecraft.world.entity.player.Inventory inventory = player.getInventory(); + int total = 0; + for (int i = 0; i < inventory.getContainerSize(); i++) { + net.minecraft.world.item.ItemStack stack = inventory.getItem(i); + if (!stack.isEmpty() && stack.is(item)) { + total += stack.getCount(); + } + } + if (total < count) { + return false; + } + int rest = count; + for (int i = 0; i < inventory.getContainerSize() && rest > 0; i++) { + net.minecraft.world.item.ItemStack stack = inventory.getItem(i); + if (!stack.isEmpty() && stack.is(item)) { + int take = Math.min(rest, stack.getCount()); + stack.shrink(take); + rest -= take; + } + } + return true; + } + + private static int fInfo(CommandSourceStack source, String nom) { + fr.surviepote.pala.core.PalaFactions.Faction faction; + if (nom == null) { + try { + ServerPlayer player = source.getPlayerOrException(); + faction = fr.surviepote.pala.core.PalaFactions.get() + .byMember(player.getUUID().toString()); + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } else { + faction = fr.surviepote.pala.core.PalaFactions.get().byName(nom); + } + if (faction == null) { + source.sendFailure(Component.literal("§cFaction inconnue (ou vous n'en avez pas).")); + return 0; + } + source.sendSuccess(() -> Component.literal("§6[Faction " + faction.name + "] §7" + + faction.members.size() + " membres" + + (faction.description.isEmpty() ? "" : " : " + faction.description)), false); + return Command.SINGLE_SUCCESS; + } + + private static int fInvite(CommandSourceStack source, ServerPlayer cible) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaFactions.Faction faction = + fr.surviepote.pala.core.PalaFactions.get() + .byMember(player.getUUID().toString()); + if (faction == null) { + source.sendFailure(Component.literal("§cVous n'etes dans aucune faction.")); + return 0; + } + if (!faction.owner.equals(player.getUUID().toString())) { + source.sendFailure(Component.literal("§cSeul le chef peut inviter.")); + return 0; + } + if (fr.surviepote.pala.core.PalaFactions.get() + .byMember(cible.getUUID().toString()) != null) { + source.sendFailure(Component.literal("§cCe joueur est deja dans une faction.")); + return 0; + } + if (!faction.invites.contains(cible.getUUID().toString())) { + faction.invites.add(cible.getUUID().toString()); + fr.surviepote.pala.core.PalaFactions.get().markDirty(); + } + cible.sendSystemMessage(Component.literal("§6[Faction] §7" + player.getScoreboardName() + + " vous invite dans " + faction.name + " : §a/f join " + faction.name)); + source.sendSuccess(() -> Component.literal( + "§6[Faction] §7Invitation envoyee a " + cible.getScoreboardName() + "."), false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static int fJoin(CommandSourceStack source, String nom) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaFactions.Faction faction = + fr.surviepote.pala.core.PalaFactions.get().byName(nom); + if (faction == null) { + source.sendFailure(Component.literal("§cFaction inconnue.")); + return 0; + } + if (fr.surviepote.pala.core.PalaFactions.get() + .byMember(player.getUUID().toString()) != null) { + source.sendFailure(Component.literal("§cVous etes deja dans une faction.")); + return 0; + } + if (!faction.invites.remove(player.getUUID().toString())) { + source.sendFailure(Component.literal("§cPas d'invitation pour cette faction.")); + return 0; + } + faction.members.add(player.getUUID().toString()); + fr.surviepote.pala.core.PalaFactions.get().markDirty(); + source.getServer().getPlayerList().broadcastSystemMessage( + Component.literal("§6[Faction] §e" + player.getScoreboardName() + + " §7a rejoint §e" + faction.name + " §7!"), + false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static int fLeave(CommandSourceStack source) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaFactions.Faction faction = + fr.surviepote.pala.core.PalaFactions.get() + .byMember(player.getUUID().toString()); + if (faction == null) { + source.sendFailure(Component.literal("§cVous n'etes dans aucune faction.")); + return 0; + } + if (faction.owner.equals(player.getUUID().toString())) { + source.sendFailure( + Component.literal("§cLe chef doit dissoudre (/f disband) ou transferer.")); + return 0; + } + faction.members.remove(player.getUUID().toString()); + fr.surviepote.pala.core.PalaFactions.get().markDirty(); + source.sendSuccess(() -> Component.literal( + "§6[Faction] §7Vous avez quitte " + faction.name + "."), false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static int fDisband(CommandSourceStack source) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaFactions.Faction faction = + fr.surviepote.pala.core.PalaFactions.get() + .byMember(player.getUUID().toString()); + if (faction == null || !faction.owner.equals(player.getUUID().toString())) { + source.sendFailure(Component.literal("§cSeul le chef peut dissoudre.")); + return 0; + } + fr.surviepote.pala.core.PalaFactions.get().remove(faction.name); + source.getServer().getPlayerList().broadcastSystemMessage( + Component.literal("§6[Faction] §e" + faction.name + " §7a ete dissoute."), + false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + // M1 : prospecte les minerais pala dans un cube de rayon N autour du joueur. // Affiche comptes par minerai + histogramme Y (8 bandes de 16) pour amethyste/titane. // Test distribution worldgen (couches 6-10 + deep ocean). Rayon capé à 32 (65^3 lectures). diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaCore.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaCore.java index 115ea09..d795a22 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaCore.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaCore.java @@ -17,6 +17,7 @@ public final class PalaCore { public static void register() { boolean hadFile = PalaConfig.load(); PalaStorage.get().load(); + PalaFactions.get().load(); Pala.LOGGER.info("[SurviePote] Pala core charge (config {}).", hadFile ? "existante" : "creee par defaut"); @@ -25,11 +26,13 @@ public final class PalaCore { if (tickCounter >= 100) { tickCounter = 0; PalaStorage.get().saveIfDirty(); + PalaFactions.get().saveIfDirty(); } }); ServerLifecycleEvents.SERVER_STOPPING.register(server -> { PalaStorage.get().save(); + PalaFactions.get().save(); Pala.LOGGER.info("[SurviePote] Pala core sauvegarde ({} joueurs).", PalaStorage.get().playerCount()); }); diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaFactions.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaFactions.java new file mode 100644 index 0000000..b5bb50d --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaFactions.java @@ -0,0 +1,125 @@ +package fr.surviepote.pala.core; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.fabricmc.loader.api.FabricLoader; + +import fr.surviepote.pala.Pala; + +// M-faction-1 (wiki Gestion faction) : donnees factions persistantes (pala-factions.json). +// Creation via commande (100 lingots paladium, GUI touche F infaisable pur serveur — R2). +// V1 : nom (unique, casse preservee, cle minuscule), owner, membres, description, invites. +public final class PalaFactions { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final class Faction { + public String name = ""; + public String owner = ""; + public List members = new ArrayList<>(); + public String description = ""; + public List invites = new ArrayList<>(); + } + + private final Map factions = new HashMap<>(); + private boolean dirty = false; + + private static final PalaFactions INSTANCE = new PalaFactions(); + + private PalaFactions() { + } + + public static PalaFactions get() { + return INSTANCE; + } + + public static Path file() { + return FabricLoader.getInstance().getConfigDir().resolve("pala-factions.json"); + } + + public Faction byName(String name) { + return factions.get(name.toLowerCase(java.util.Locale.ROOT)); + } + + public Faction byMember(String uuid) { + for (Faction faction : factions.values()) { + if (faction.members.contains(uuid)) { + return faction; + } + } + return null; + } + + public void put(Faction faction) { + factions.put(faction.name.toLowerCase(java.util.Locale.ROOT), faction); + dirty = true; + } + + public void remove(String name) { + if (factions.remove(name.toLowerCase(java.util.Locale.ROOT)) != null) { + dirty = true; + } + } + + public void markDirty() { + dirty = true; + } + + public int count() { + return factions.size(); + } + + public void load() { + Path path = file(); + if (!Files.exists(path)) { + return; + } + try (Reader reader = Files.newBufferedReader(path)) { + Map raw = GSON.fromJson(reader, Map.class); + if (raw == null) { + return; + } + for (Map.Entry entry : raw.entrySet()) { + Faction faction = GSON.fromJson(GSON.toJson(entry.getValue()), Faction.class); + if (faction != null && faction.name != null && !faction.name.isEmpty()) { + if (faction.members == null) { + faction.members = new ArrayList<>(); + } + if (faction.invites == null) { + faction.invites = new ArrayList<>(); + } + factions.put(String.valueOf(entry.getKey()), faction); + } + } + } catch (IOException | com.google.gson.JsonParseException e) { + Pala.LOGGER.warn("[SurviePote] pala-factions.json illisible, demarrage vide : {}", e.toString()); + } + } + + public void saveIfDirty() { + if (!dirty) { + return; + } + dirty = false; + save(); + } + + public void save() { + try (Writer writer = Files.newBufferedWriter(file())) { + GSON.toJson(factions, writer); + } catch (IOException e) { + Pala.LOGGER.warn("[SurviePote] Ecriture pala-factions.json impossible : {}", e.toString()); + } + } +} diff --git a/server/mods/pala-0.2.1.jar b/server/mods/pala-0.2.1.jar index f84d23d..dfb6350 100644 Binary files a/server/mods/pala-0.2.1.jar and b/server/mods/pala-0.2.1.jar differ