diff --git a/PALAMOD_CDC.md b/PALAMOD_CDC.md index 1fb2df2..6476711 100644 --- a/PALAMOD_CDC.md +++ b/PALAMOD_CDC.md @@ -474,6 +474,17 @@ avant M4 (TODO:crafts-machines). (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 a suivre) : 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 /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). - Client/mod : ./gradlew runClient dans mods/pala/ : NON LANCE (session : pas d'affichage ; screenshots ocular impossibles ici). TODO:runclient : 1 screenshot/feature dans mods/pala/docs/screens/ avant M10. Raison : environnement headless, a faire sur poste avec X. diff --git a/mods/pala/src/main/java/fr/surviepote/pala/Pala.java b/mods/pala/src/main/java/fr/surviepote/pala/Pala.java index 6c9f556..0facfbe 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/Pala.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/Pala.java @@ -11,6 +11,7 @@ public class Pala implements ModInitializer { @Override public void onInitialize() { + fr.surviepote.pala.core.PalaCore.register(); PalaRegistries.registerAll(); fr.surviepote.pala.events.PalaEvents.registerAll(); LOGGER.info("[SurviePote] Pala initialisé."); 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 e2785aa..b407f58 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java @@ -1,10 +1,13 @@ package fr.surviepote.pala; import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; @@ -15,7 +18,13 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.phys.Vec3; -// Commandes Pala : /end /nether /overworld (permission niveau 4, legacy). +import fr.surviepote.pala.core.PalaConfig; +import fr.surviepote.pala.core.PalaJobs; +import fr.surviepote.pala.core.PalaPermissions; +import fr.surviepote.pala.core.PalaStorage; + +// Commandes Pala : /end /overworld (permission niveau 4, legacy), /nether (gate metier +// mineur niv.3, M0), /pala reload|info|rank (admin), /jobs (metiers). Voir CDC §3.8. // Origine : CommandEnd/Nether/Overworld + procedures *CommandExecuted qui // teleportaient le joueur au spawn de la dimension visee. Ici via // TeleportTransition (API 26.x), sans le Teleporter custom du legacy. @@ -30,14 +39,96 @@ public final class PalaCommands { .requires(source -> Commands.LEVEL_OWNERS.check(source.permissions())) .executes(command -> teleport(command.getSource(), Level.END))); dispatcher.register(Commands.literal("nether") - .requires(source -> Commands.LEVEL_OWNERS.check(source.permissions())) - .executes(command -> teleport(command.getSource(), Level.NETHER))); + .executes(command -> nether(command.getSource()))); dispatcher.register(Commands.literal("overworld") .requires(source -> Commands.LEVEL_OWNERS.check(source.permissions())) .executes(command -> teleport(command.getSource(), Level.OVERWORLD))); + com.mojang.brigadier.builder.ArgumentBuilder rankArg = + Commands.argument("rang", StringArgumentType.word()) + .executes(command -> rank(command.getSource(), + EntityArgument.getPlayer(command, "joueur"), + StringArgumentType.getString(command, "rang"))); + com.mojang.brigadier.builder.ArgumentBuilder joueurArg = + Commands.argument("joueur", EntityArgument.player()).then(rankArg); + dispatcher.register(Commands.literal("pala") + .then(Commands.literal("reload") + .requires(source -> isAdmin(source)) + .executes(command -> reload(command.getSource()))) + .then(Commands.literal("info") + .requires(source -> isAdmin(source)) + .executes(command -> info(command.getSource()))) + .then(Commands.literal("rank") + .requires(source -> isAdmin(source)) + .then(joueurArg))); + dispatcher.register(Commands.literal("jobs") + .executes(command -> jobs(command.getSource()))); }); } + private static boolean isAdmin(CommandSourceStack source) { + ServerPlayer player = source.getPlayer(); + if (player == null) { + return true; + } + return PalaPermissions.has(source, player, "pala.admin"); + } + + private static int reload(CommandSourceStack source) { + PalaConfig.load(); + source.sendSuccess(() -> Component.literal( + "§6[Pala] §7Config rechargee (" + PalaStorage.get().playerCount() + " joueurs connus)."), + false); + return Command.SINGLE_SUCCESS; + } + + private static int info(CommandSourceStack source) { + source.sendSuccess(() -> Component.literal("§6[Pala] §7Joueurs connus : " + + PalaStorage.get().playerCount()), false); + return Command.SINGLE_SUCCESS; + } + + private static int rank(CommandSourceStack source, ServerPlayer target, String rank) { + if (!PalaConfig.get().rankNodes.containsKey(rank)) { + source.sendFailure(Component.literal("§cRang inconnu : " + rank)); + return 0; + } + PalaPermissions.setRank(target, rank); + source.sendSuccess(() -> Component.literal("§6[Pala] §7" + target.getScoreboardName() + + " -> rang " + rank + "."), false); + return Command.SINGLE_SUCCESS; + } + + private static int jobs(CommandSourceStack source) { + try { + ServerPlayer player = source.getPlayerOrException(); + StringBuilder lines = new StringBuilder("§6[Metiers]"); + for (String job : PalaJobs.JOBS) { + lines.append("\n§e").append(job).append(" §7niv. §a") + .append(PalaJobs.levelOf(player, job)).append(" §8(") + .append((long) PalaJobs.xpOf(player, job)).append(" xp)"); + } + source.sendSuccess(() -> Component.literal(lines.toString()), false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + // /nether : deblocage metier mineur niv.3 (wiki recompenses mineur, VERIFIE), OP bypass. + private static int nether(CommandSourceStack source) { + ServerPlayer player = source.getPlayer(); + if (player != null) { + int required = PalaConfig.get().netherMinerLevel; + if (!Commands.LEVEL_ADMINS.check(source.permissions()) + && PalaJobs.levelOf(player, "mineur") < required) { + player.sendSystemMessage(Component.literal("§cNether verrouille : mineur niveau " + + required + " requis (actuel : " + PalaJobs.levelOf(player, "mineur") + ").")); + return 0; + } + } + return teleport(source, Level.NETHER); + } + private static int teleport(CommandSourceStack source, ResourceKey dimension) { try { ServerPlayer player = source.getPlayerOrException(); diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java new file mode 100644 index 0000000..8e1fa19 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaConfig.java @@ -0,0 +1,124 @@ +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.HashMap; +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; + +// Config rechargeable du mod (M0). JSON via Gson (fourni par Mojang au runtime). +// +// Choix JSON plutot que YAML en 3 lignes : +// 1. Zero dependance (pas de snakeyaml a embarquer/patcher au build) ; +// 2. Gson est deja au classpath compile (gson-2.14.0.jar) et au runtime Mojang ; +// 3. /pala reload re-parse le fichier sans restart, valeurs par defaut si cles absentes. +// Les valeurs wiki VERIFIEES sont en Maka ; le reste est UNVERIFIED (balance staff, M5). +public final class PalaConfig { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + // XP metier mineur par bloc casse (wiki "Evolution du metier Mineur", VERIFIE). + // Cles = ids de registres (vanilla ou pala:). Map.ofEntries : Map.of limite a 10 paires. + public Map minerXp = new HashMap<>(Map.ofEntries( + Map.entry("minecraft:stone", 0.5), + Map.entry("minecraft:diorite", 3.0), Map.entry("minecraft:andesite", 3.0), + Map.entry("minecraft:coal_ore", 4.0), Map.entry("minecraft:deepslate_coal_ore", 4.0), + Map.entry("minecraft:quartz_ore", 6.0), Map.entry("minecraft:obsidian", 6.0), + Map.entry("minecraft:lapis_ore", 15.0), Map.entry("minecraft:redstone_ore", 15.0), + Map.entry("minecraft:emerald_ore", 50.0), + Map.entry("minecraft:iron_ore", 2.0), Map.entry("minecraft:deepslate_iron_ore", 2.0), + Map.entry("minecraft:gold_ore", 4.0), Map.entry("minecraft:deepslate_gold_ore", 4.0), + Map.entry("minecraft:diamond_ore", 8.0), Map.entry("minecraft:deepslate_diamond_ore", 8.0), + Map.entry("pala:amethyst_ore", 12.0), Map.entry("pala:titane_ore", 16.0), + Map.entry("pala:paladium_ore", 20.0), + Map.entry("pala:paladium_green_ore", 20.0), Map.entry("pala:trixium_ore", 10.0), + Map.entry("pala:findium_ore", 10.0))); + + // Courbe XP : cout du niveau n. UNVERIFIED (formule placeholder, a caler M5). + public double xpBase = 100.0; + public double xpExponent = 1.5; + + // Niveau mineur requis pour /nether (wiki recompenses mineur niv.3, VERIFIE). + public int netherMinerLevel = 3; + + // Cooldowns sticks PvP en secondes. UNVERIFIED (durees balance staff, M5). + public Map stickCooldowns = new HashMap<>(Map.of( + "heal_stick", 60, "speed_stick", 30, "hyper_jump_stick", 30, + "strenght_stick", 120, "stick_of_god", 600)); + + // Rangs -> noeuds de permission (extrait page "par grade", partiel UNVERIFIED). + public Map> rankNodes = new HashMap<>(Map.of( + "default", java.util.List.of("pala.job", "pala.trade", "pala.market"), + "heros", java.util.List.of("pala.*"))); + + private static PalaConfig instance = new PalaConfig(); + + private PalaConfig() { + } + + public static PalaConfig get() { + return instance; + } + + public static Path file() { + return FabricLoader.getInstance().getConfigDir().resolve("pala.json"); + } + + /** Charge (ou cree avec defauts). Retourne true si un fichier existant a ete lu. */ + public static boolean load() { + Path path = file(); + if (!Files.exists(path)) { + save(); + return false; + } + try (Reader reader = Files.newBufferedReader(path)) { + PalaConfig loaded = GSON.fromJson(reader, PalaConfig.class); + if (loaded != null) { + if (loaded.minerXp != null && !loaded.minerXp.isEmpty()) { + instance.minerXp = loaded.minerXp; + } + if (loaded.xpBase > 0) { + instance.xpBase = loaded.xpBase; + } + if (loaded.xpExponent > 0) { + instance.xpExponent = loaded.xpExponent; + } + if (loaded.netherMinerLevel >= 0) { + instance.netherMinerLevel = loaded.netherMinerLevel; + } + if (loaded.stickCooldowns != null && !loaded.stickCooldowns.isEmpty()) { + instance.stickCooldowns = loaded.stickCooldowns; + } + if (loaded.rankNodes != null && !loaded.rankNodes.isEmpty()) { + instance.rankNodes = loaded.rankNodes; + } + } + return true; + } catch (IOException | com.google.gson.JsonParseException e) { + Pala.LOGGER.warn("[SurviePote] Config pala.json illisible, defauts gardes : {}", e.toString()); + return false; + } + } + + public static void save() { + try (Writer writer = Files.newBufferedWriter(file())) { + GSON.toJson(instance, writer); + } catch (IOException e) { + Pala.LOGGER.warn("[SurviePote] Ecriture pala.json impossible : {}", e.toString()); + } + } + + /** Cout XP total pour atteindre le niveau n (n >= 1). */ + public long xpForLevel(int level) { + return (long) (xpBase * Math.pow(level, xpExponent)); + } +} 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 new file mode 100644 index 0000000..115ea09 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaCore.java @@ -0,0 +1,37 @@ +package fr.surviepote.pala.core; + +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; + +import fr.surviepote.pala.Pala; + +// Cycle de vie du socle M0 : config + storage au boot, flush periodique, save a l'arret. +// Le compteur modulo 100 sur END_SERVER_TICK = ~5 s (20 ticks/s) : zero I/O si pas dirty. +public final class PalaCore { + + private static int tickCounter = 0; + + private PalaCore() { + } + + public static void register() { + boolean hadFile = PalaConfig.load(); + PalaStorage.get().load(); + Pala.LOGGER.info("[SurviePote] Pala core charge (config {}).", + hadFile ? "existante" : "creee par defaut"); + + ServerTickEvents.END_SERVER_TICK.register(server -> { + tickCounter++; + if (tickCounter >= 100) { + tickCounter = 0; + PalaStorage.get().saveIfDirty(); + } + }); + + ServerLifecycleEvents.SERVER_STOPPING.register(server -> { + PalaStorage.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/PalaJobs.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java new file mode 100644 index 0000000..f6128fc --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaJobs.java @@ -0,0 +1,64 @@ +package fr.surviepote.pala.core; + +import java.util.List; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.block.state.BlockState; + +// Metiers : XP et niveaux (M0 = moteur minimal, tables completes en M5). +// Wiki VERIFIE : 4 metiers (mineur, fermier, chasseur, alchimiste), ~100 niveaux, +// table XP mineur par bloc (PalaConfig.minerXp). Courbe et recompenses : UNVERIFIED (M5). +public final class PalaJobs { + + public static final List JOBS = List.of("mineur", "fermier", "chasseur", "alchimiste"); + + private PalaJobs() { + } + + public static int levelOf(ServerPlayer player, String job) { + return PalaStorage.get().data(player.getUUID()).jobs + .getOrDefault(job, new PalaStorage.JobData()).level; + } + + public static double xpOf(ServerPlayer player, String job) { + return PalaStorage.get().data(player.getUUID()).jobs + .getOrDefault(job, new PalaStorage.JobData()).xp; + } + + /** Ajoute de l'XP, monte les niveaux franchis, annonce. Retourne le niveau final. */ + public static int addXp(ServerPlayer player, String job, double amount) { + if (amount <= 0) { + return levelOf(player, job); + } + PalaStorage.JobData data = PalaStorage.get().data(player.getUUID()).jobs + .computeIfAbsent(job, k -> new PalaStorage.JobData()); + data.xp += amount; + PalaConfig config = PalaConfig.get(); + while (data.level < 100 && data.xp >= config.xpForLevel(data.level + 1)) { + data.level++; + player.sendSystemMessage(Component.literal( + "§6[Metier] §e" + job + " §7niveau §a" + data.level + " §7atteint !")); + // TODO:recompenses-m5 : lots par palier (voidstone niv.2, /nether niv.3...). Raison : M5. + } + PalaStorage.get().markDirty(); + return data.level; + } + + /** Hook casse-bloc : XP mineur selon la table config. Appele depuis PalaToolEvents. */ + public static void onBlockBreak(ServerPlayer player, ServerLevel level, BlockPos pos, BlockState state) { + Identifier id = level.registryAccess() + .lookupOrThrow(net.minecraft.core.registries.Registries.BLOCK) + .getKey(state.getBlock()); + if (id == null) { + return; + } + Double xp = PalaConfig.get().minerXp.get(id.toString()); + if (xp != null) { + addXp(player, "mineur", xp); + } + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaPermissions.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaPermissions.java new file mode 100644 index 0000000..d016a8f --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaPermissions.java @@ -0,0 +1,47 @@ +package fr.surviepote.pala.core; + +import java.util.List; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; + +// Permissions internes (M0) : rangs + noeuds pala.*. +// +// Choix systeme interne plutot que LuckPerms en 3 lignes : +// 1. Zero dependance : le serveur actuel n'a pas LuckPerms, M0 doit booter tel quel ; +// 2. Les grades boutique se resolvent en 2 rangs + noeuds explicites, suffisant pour M0-M6 ; +// 3. Si LuckPerms est ajoute plus tard, cette classe devient un adaptateur (1 seul point de contact). +// TODO:luckperms : re-evaluer en M7 (CDC §3.8). Raison : reconciliation avec les grades boutique. +public final class PalaPermissions { + + private PalaPermissions() { + } + + public static String rankOf(ServerPlayer player) { + return PalaStorage.get().data(player.getUUID()).rank; + } + + public static void setRank(ServerPlayer player, String rank) { + PalaStorage.PlayerData data = PalaStorage.get().data(player.getUUID()); + data.rank = rank; + PalaStorage.get().markDirty(); + } + + /** OP niveau 2+ (LEVEL_ADMINS) bypass tout ; sinon noeud exact ou wildcard pala.* du rang. + * Prend la source (PermissionSet vanilla 26.3, verifie au javap) + le joueur (rang interne). */ + public static boolean has(CommandSourceStack source, ServerPlayer player, String node) { + if (net.minecraft.commands.Commands.LEVEL_ADMINS.check(source.permissions())) { + return true; + } + List nodes = PalaConfig.get().rankNodes.getOrDefault(rankOf(player), List.of()); + if (nodes.contains(node) || nodes.contains("pala.*")) { + return true; + } + // Wildcard partiel : pala.job.* couvre pala.job.mineur, etc. + int dot = node.lastIndexOf('.'); + if (dot > 0 && nodes.contains(node.substring(0, dot) + ".*")) { + return true; + } + return false; + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/core/PalaStorage.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaStorage.java new file mode 100644 index 0000000..cf4a140 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaStorage.java @@ -0,0 +1,110 @@ +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.HashMap; +import java.util.Map; +import java.util.UUID; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import net.fabricmc.loader.api.FabricLoader; + +import fr.surviepote.pala.Pala; + +// Donnees joueurs persistantes (M0) : jobs XP/niveaux, rang, monnaie. +// +// Choix JSON fichier unique en 3 lignes : +// 1. M0 n'a besoin que de lectures/ecritures simples (pas de jointures) ; +// 2. Flush differe hors tick (dirty + save 5 s) = zero I/O sur le thread tick ; +// 3. SQLite arrive en M7 avec l'economie (listings, historique) — le schema est prevu +// pour migrer tel quel (1 ligne joueur = 1 document JSON aujourd'hui). +// TODO:sqlite : migrer vers pala.db (JDBC) en M7. Raison : requetes market/ELO hors de portee JSON. +public final class PalaStorage { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final class JobData { + public double xp = 0.0; + public int level = 0; + } + + public static final class PlayerData { + public Map jobs = new HashMap<>(); + public String rank = "default"; + public double money = 0.0; + } + + private final Map players = new HashMap<>(); + private boolean dirty = false; + + private static final PalaStorage INSTANCE = new PalaStorage(); + + private PalaStorage() { + } + + public static PalaStorage get() { + return INSTANCE; + } + + public static Path file() { + return FabricLoader.getInstance().getConfigDir().resolve("pala-players.json"); + } + + public PlayerData data(UUID uuid) { + return players.computeIfAbsent(uuid.toString(), k -> new PlayerData()); + } + + public void markDirty() { + dirty = true; + } + + 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()) { + PlayerData data = GSON.fromJson(GSON.toJson(entry.getValue()), PlayerData.class); + if (data != null) { + if (data.jobs == null) { + data.jobs = new HashMap<>(); + } + players.put(String.valueOf(entry.getKey()), data); + } + } + } catch (IOException | com.google.gson.JsonParseException e) { + Pala.LOGGER.warn("[SurviePote] pala-players.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(players, writer); + } catch (IOException e) { + Pala.LOGGER.warn("[SurviePote] Ecriture pala-players.json impossible : {}", e.toString()); + } + } + + /** Nombre de joueurs connus (debug /pala). */ + public int playerCount() { + return players.size(); + } +} diff --git a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java index 86d093a..b638ad3 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/events/PalaToolEvents.java @@ -25,6 +25,7 @@ import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import fr.surviepote.pala.PalaBlocks; import fr.surviepote.pala.PalaItems; +import fr.surviepote.pala.core.PalaJobs; // Portage des procedures "bloc detruit avec l'outil / par le joueur" MCreator : // - ProcedurePickaxeOfTheGodBlockDestroyedWithTool : compteur "level" en CustomData, @@ -104,6 +105,9 @@ public final class PalaToolEvents { ItemStack hand = sp.getMainHandItem(); Item held = hand.isEmpty() ? null : hand.getItem(); + // --- Metiers : XP mineur (M0) --- + PalaJobs.onBlockBreak(sp, sl, pos, state); + // --- Pioche divine : compteur + evolution --- if (held != null && GOD_PICKAXES.contains(held)) { if (held == PalaItems.PICKAXE_OF_THE_GOD_LEVEL25) { diff --git a/server/mods/pala-0.2.1.jar b/server/mods/pala-0.2.1.jar index 2965f53..55b184b 100644 Binary files a/server/mods/pala-0.2.1.jar and b/server/mods/pala-0.2.1.jar differ