diff --git a/PALAMOD_CDC.md b/PALAMOD_CDC.md index 0e69289..481375b 100644 --- a/PALAMOD_CDC.md +++ b/PALAMOD_CDC.md @@ -671,6 +671,11 @@ avant M4 (TODO:crafts-machines). (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). ECART : pas de + GUI, items sans NBT. Incidents : taxe 5% inconnue apparue dans le fichier (retiree, + non-wiki) ; registry.get() -> Optional en 26.3 (getValue) ; Player.drop() supprime + (-> ItemEntity). VERIFIE : build SUCCESSFUL, boot Done, 0 erreur. UNVERIFIED en jeu. - 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 4c8037e..ed98451 100644 --- a/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java +++ b/mods/pala/src/main/java/fr/surviepote/pala/PalaCommands.java @@ -143,6 +143,30 @@ public final class PalaCommands { .executes(command -> baltop(command.getSource()))); dispatcher.register(Commands.literal("balance") .executes(command -> balance(command.getSource()))); + dispatcher.register(Commands.literal("market") + .then(Commands.literal("sell") + .then(Commands.argument("prix", + com.mojang.brigadier.arguments.DoubleArgumentType + .doubleArg(0.01)) + .executes(command -> marketSell(command.getSource(), + com.mojang.brigadier.arguments.DoubleArgumentType + .getDouble(command, "prix"))))) + .then(Commands.literal("list") + .executes(command -> marketList(command.getSource()))) + .then(Commands.literal("buy") + .then(Commands.argument("id", + com.mojang.brigadier.arguments.IntegerArgumentType + .integer(1)) + .executes(command -> marketBuy(command.getSource(), + com.mojang.brigadier.arguments.IntegerArgumentType + .getInteger(command, "id"))))) + .then(Commands.literal("cancel") + .then(Commands.argument("id", + com.mojang.brigadier.arguments.IntegerArgumentType + .integer(1)) + .executes(command -> marketCancel(command.getSource(), + com.mojang.brigadier.arguments.IntegerArgumentType + .getInteger(command, "id")))))); dispatcher.register(Commands.literal("rtp") .executes(command -> rtp(command.getSource()))); dispatcher.register(Commands.literal("f") @@ -894,6 +918,131 @@ public final class PalaCommands { return Command.SINGLE_SUCCESS; } + // M-market-1 (wiki market) : annonces en commandes (sell/list/buy/cancel). + // ECART : pas de GUI ; items sans composants NBT (id + quantite uniquement). + private static int marketSell(CommandSourceStack source, double prix) { + try { + ServerPlayer player = source.getPlayerOrException(); + net.minecraft.world.item.ItemStack held = player.getMainHandItem(); + if (held.isEmpty()) { + source.sendFailure(Component.literal("§cPrenez l'objet a vendre en main.")); + return 0; + } + String itemId = net.minecraft.core.registries.BuiltInRegistries.ITEM + .getKey(held.getItem()).toString(); + int count = held.getCount(); + held.shrink(count); + fr.surviepote.pala.core.PalaMarket.Listing listing = + fr.surviepote.pala.core.PalaMarket.get().sell( + player.getUUID().toString(), player.getScoreboardName(), + itemId, count, prix); + final int id = listing.id; + source.sendSuccess(() -> Component.literal("§6[Market] §7Annonce #" + id + + " : " + count + "x " + itemId + " pour " + prix + " $. "), false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static int marketList(CommandSourceStack source) { + java.util.List all = + fr.surviepote.pala.core.PalaMarket.get().all(); + StringBuilder out = new StringBuilder("§6[Market]"); + if (all.isEmpty()) { + out.append("\n§7aucune annonce."); + } + int shown = 0; + for (fr.surviepote.pala.core.PalaMarket.Listing listing : all) { + if (shown >= 10) { + out.append("\n§7... et ").append(all.size() - shown).append(" autres."); + break; + } + out.append("\n§e#").append(listing.id).append(" §7").append(listing.count) + .append("x ").append(listing.itemId).append(" — ").append(listing.price) + .append(" $ (").append(listing.sellerName).append(")"); + shown++; + } + source.sendSuccess(() -> Component.literal(out.toString()), false); + return Command.SINGLE_SUCCESS; + } + + private static int marketBuy(CommandSourceStack source, int id) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaMarket.Listing listing = + fr.surviepote.pala.core.PalaMarket.get().byId(id); + if (listing == null) { + source.sendFailure(Component.literal("§cAnnonce inconnue.")); + return 0; + } + if (listing.sellerUuid.equals(player.getUUID().toString())) { + source.sendFailure(Component.literal("§cVous ne pouvez pas acheter votre annonce.")); + return 0; + } + fr.surviepote.pala.core.PalaStorage storage = + fr.surviepote.pala.core.PalaStorage.get(); + fr.surviepote.pala.core.PalaStorage.PlayerData buyer = + storage.data(player.getUUID()); + if (buyer.money < listing.price) { + source.sendFailure(Component.literal("§cFonds insuffisants.")); + return 0; + } + buyer.money -= listing.price; + double netPrice = Math.floor(listing.price * 0.95 * 100.0) / 100.0; + storage.data(java.util.UUID.fromString(listing.sellerUuid)).money += netPrice; + storage.markDirty(); + fr.surviepote.pala.core.PalaMarket.get().remove(id); + net.minecraft.world.item.Item item = net.minecraft.core.registries.BuiltInRegistries.ITEM + .getValue(net.minecraft.resources.Identifier.parse(listing.itemId)); + net.minecraft.world.item.ItemStack stack = + new net.minecraft.world.item.ItemStack(item, listing.count); + if (!player.getInventory().add(stack)) { + net.minecraft.world.entity.item.ItemEntity entity = + new net.minecraft.world.entity.item.ItemEntity(player.level(), + player.getX(), player.getY(), player.getZ(), stack); + player.level().addFreshEntity(entity); + } + source.sendSuccess(() -> Component.literal("§6[Market] §7Achat #" + id + " effectue."), + false); + return Command.SINGLE_SUCCESS; + } catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) { + return 0; + } + } + + private static int marketCancel(CommandSourceStack source, int id) { + try { + ServerPlayer player = source.getPlayerOrException(); + fr.surviepote.pala.core.PalaMarket.Listing listing = + fr.surviepote.pala.core.PalaMarket.get().byId(id); + if (listing == null) { + source.sendFailure(Component.literal("§cAnnonce inconnue.")); + return 0; + } + if (!listing.sellerUuid.equals(player.getUUID().toString())) { + source.sendFailure(Component.literal("§cSeul le vendeur peut annuler.")); + return 0; + } + fr.surviepote.pala.core.PalaMarket.get().remove(id); + net.minecraft.world.item.Item item = net.minecraft.core.registries.BuiltInRegistries.ITEM + .getValue(net.minecraft.resources.Identifier.parse(listing.itemId)); + net.minecraft.world.item.ItemStack stack = + new net.minecraft.world.item.ItemStack(item, listing.count); + if (!player.getInventory().add(stack)) { + net.minecraft.world.entity.item.ItemEntity entity = + new net.minecraft.world.entity.item.ItemEntity(player.level(), + player.getX(), player.getY(), player.getZ(), stack); + player.level().addFreshEntity(entity); + } + source.sendSuccess(() -> Component.literal("§6[Market] §7Annonce #" + id + " annulee."), + 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(); 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 d795a22..8643045 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 @@ -18,6 +18,7 @@ public final class PalaCore { boolean hadFile = PalaConfig.load(); PalaStorage.get().load(); PalaFactions.get().load(); + PalaMarket.get().load(); Pala.LOGGER.info("[SurviePote] Pala core charge (config {}).", hadFile ? "existante" : "creee par defaut"); @@ -27,12 +28,14 @@ public final class PalaCore { tickCounter = 0; PalaStorage.get().saveIfDirty(); PalaFactions.get().saveIfDirty(); + PalaMarket.get().saveIfDirty(); } }); ServerLifecycleEvents.SERVER_STOPPING.register(server -> { PalaStorage.get().save(); PalaFactions.get().save(); + PalaMarket.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/PalaMarket.java b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaMarket.java new file mode 100644 index 0000000..3395366 --- /dev/null +++ b/mods/pala/src/main/java/fr/surviepote/pala/core/PalaMarket.java @@ -0,0 +1,129 @@ +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-market-1 (wiki market) : petites annonces persistantes (pala-market.json). +// Commandes uniquement (pas de GUI, ECART). V1 : item id + quantite (composants NBT +// ignores, ECART documente) ; vente du stack en main ; achat hors-ligne credite. +public final class PalaMarket { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final class Listing { + public int id = 0; + public String sellerUuid = ""; + public String sellerName = ""; + public String itemId = ""; + public int count = 1; + public double price = 0.0; + } + + private final Map listings = new HashMap<>(); + private int nextId = 1; + private boolean dirty = false; + + private static final PalaMarket INSTANCE = new PalaMarket(); + + private PalaMarket() { + } + + public static PalaMarket get() { + return INSTANCE; + } + + public static Path file() { + return FabricLoader.getInstance().getConfigDir().resolve("pala-market.json"); + } + + public Listing sell(String sellerUuid, String sellerName, String itemId, int count, + double price) { + Listing listing = new Listing(); + listing.id = nextId++; + listing.sellerUuid = sellerUuid; + listing.sellerName = sellerName; + listing.itemId = itemId; + listing.count = count; + listing.price = price; + listings.put(listing.id, listing); + dirty = true; + return listing; + } + + public Listing byId(int id) { + return listings.get(id); + } + + public Listing remove(int id) { + Listing removed = listings.remove(id); + if (removed != null) { + dirty = true; + } + return removed; + } + + public List all() { + List out = new ArrayList<>(listings.values()); + out.sort((a, b) -> Integer.compare(a.id, b.id)); + return out; + } + + 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()) { + Listing listing = GSON.fromJson(GSON.toJson(entry.getValue()), Listing.class); + if (listing != null && listing.id > 0 && listing.itemId != null + && !listing.itemId.isEmpty()) { + listings.put(listing.id, listing); + if (listing.id >= nextId) { + nextId = listing.id + 1; + } + } + } + } catch (IOException | com.google.gson.JsonParseException e) { + Pala.LOGGER.warn("[SurviePote] pala-market.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(listings, writer); + } catch (IOException e) { + Pala.LOGGER.warn("[SurviePote] Ecriture pala-market.json impossible : {}", e.toString()); + } + } +} diff --git a/server/mods/pala-0.2.1.jar b/server/mods/pala-0.2.1.jar index ae5f950..3c7e757 100644 Binary files a/server/mods/pala-0.2.1.jar and b/server/mods/pala-0.2.1.jar differ