pala M-faction-1: socle factions + /f create/info/invite/join/leave/disband

This commit is contained in:
Anaïs Saraiva
2026-10-19 04:06:55 +02:00
parent 061f19d4ee
commit d5a88d4516
5 changed files with 354 additions and 0 deletions
+5
View File
@@ -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
@@ -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).
@@ -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());
});
@@ -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<String> members = new ArrayList<>();
public String description = "";
public List<String> invites = new ArrayList<>();
}
private final Map<String, Faction> 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());
}
}
}
Binary file not shown.