diff --git a/gradle.properties b/gradle.properties index 54bb45d..ff14496 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.21.1 yarn_mappings=1.21.1+build.3 loader_version=0.16.2 # Mod Properties -mod_version=1.0 +mod_version=1.2 maven_group=systems.brn archives_base_name=servershop # Dependencies diff --git a/src/main/java/systems/brn/servershop/ItemPrice.java b/src/main/java/systems/brn/servershop/ItemPrice.java index 0fa163e..9233860 100644 --- a/src/main/java/systems/brn/servershop/ItemPrice.java +++ b/src/main/java/systems/brn/servershop/ItemPrice.java @@ -1,6 +1,37 @@ package systems.brn.servershop; -public record ItemPrice( - int buyPrice, int sellPrice -) { +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.RegistryWrapper; + +import java.util.Optional; + +public record ItemPrice(int buyPrice, int sellPrice, ItemStack stack) { + + + public NbtCompound toNbt(RegistryWrapper.WrapperLookup wrapperLookup) { + NbtCompound nbt = new NbtCompound(); + nbt.putInt("BuyPrice", this.buyPrice); + nbt.putInt("SellPrice", this.sellPrice); + + // Serialize the ItemStack to NBT and add it to the compound + NbtElement stackNbt = stack.encode(wrapperLookup); + nbt.put("ItemStack", stackNbt); // Adds the ItemStack's NBT data to the main NBT compound + + return nbt; + } + + public static ItemPrice fromNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup wrapperLookup) { + int buyPrice = nbt.getInt("BuyPrice"); + int sellPrice = nbt.getInt("SellPrice"); + + // Deserialize the ItemStack from the NBT + NbtElement stackElement = nbt.get("ItemStack"); + + Optional stack = ItemStack.fromNbt(wrapperLookup, stackElement); + return stack.map(itemStack -> new ItemPrice(buyPrice, sellPrice, itemStack)).orElse(null); + + } + } diff --git a/src/main/java/systems/brn/servershop/ServerShop.java b/src/main/java/systems/brn/servershop/ServerShop.java index 716cb52..f3cbeb9 100644 --- a/src/main/java/systems/brn/servershop/ServerShop.java +++ b/src/main/java/systems/brn/servershop/ServerShop.java @@ -47,7 +47,7 @@ public class ServerShop implements ModInitializer { private void onPlayerJoin(ServerPlayNetworkHandler serverPlayNetworkHandler, PacketSender packetSender, MinecraftServer server) { if (packetSender instanceof ServerPlayerEntity player) { - balanceManager.onJoin(player); + balanceManager.setBalance(player, 0L); } } @@ -94,6 +94,13 @@ public class ServerShop implements ModInitializer { ) ) ) + .then(literal("setHand") + .then(argument("buyprice", IntegerArgumentType.integer()) + .then(argument("sellprice", IntegerArgumentType.integer()) + .executes(ShopPricesCommand::setHand) + ) + ) + ) ); dispatcher.register(literal("buy") @@ -110,6 +117,12 @@ public class ServerShop implements ModInitializer { .executes(StoreCommands::sellOne) ) ); + dispatcher.register(literal("price") + .then(argument("item", ItemStackArgumentType.itemStack(commandRegistryAccess)) + .executes(PriceCommand::run) + ) + .executes(PriceCommand::runHand) + ); } @@ -119,6 +132,6 @@ public class ServerShop implements ModInitializer { } private void onServerStopped(MinecraftServer server) { - balanceManager.saveBalance(); + balanceManager.saveBalances(); } } diff --git a/src/main/java/systems/brn/servershop/commands/PriceCommand.java b/src/main/java/systems/brn/servershop/commands/PriceCommand.java new file mode 100644 index 0000000..2a425a7 --- /dev/null +++ b/src/main/java/systems/brn/servershop/commands/PriceCommand.java @@ -0,0 +1,55 @@ +package systems.brn.servershop.commands; + +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.command.argument.ItemStackArgumentType; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; +import systems.brn.servershop.ItemPrice; + +import static systems.brn.servershop.ServerShop.priceStorage; + +public class PriceCommand { + public static int run(CommandContext ctx) { + Item item = ItemStackArgumentType.getItemStackArgument(ctx, "item").getItem(); + ItemStack stack = item.getDefaultStack(); + return runStack(ctx, stack); + } + + private static int runStack(CommandContext ctx, ItemStack stack) { + ItemPrice itemPrice = priceStorage.getPrices(stack); + ServerCommandSource src = ctx.getSource(); + int buyPrice = itemPrice.buyPrice(); + int sellPrice = itemPrice.sellPrice(); + String itemName = stack.getItem().toString(); + Text text = getText(buyPrice, sellPrice, itemName); + src.sendFeedback(() -> text, false); + return 0; + } + + public static int runHand(CommandContext ctx) { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + if (player != null) { + ItemStack stack = player.getMainHandStack(); + return runStack(ctx, stack); + } + return 1; + } + + private static @NotNull Text getText(int buyPrice, int sellPrice, String itemName) { + Text text; + if (buyPrice > 0 && sellPrice > 0) { + text = Text.translatable("message.servershop.price.both", itemName, buyPrice, sellPrice); + } else if (buyPrice > 0) { + text = Text.translatable("message.servershop.price.buy", itemName, buyPrice); + } else if (sellPrice > 0) { + text = Text.translatable("message.servershop.price.sell", itemName, sellPrice); + } else { + text = Text.translatable("message.servershop.price.neither", itemName); + } + return text; + } +} diff --git a/src/main/java/systems/brn/servershop/commands/ShopPricesCommand.java b/src/main/java/systems/brn/servershop/commands/ShopPricesCommand.java index 5a9879f..dcd0f96 100644 --- a/src/main/java/systems/brn/servershop/commands/ShopPricesCommand.java +++ b/src/main/java/systems/brn/servershop/commands/ShopPricesCommand.java @@ -3,8 +3,11 @@ package systems.brn.servershop.commands; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; import net.minecraft.command.argument.ItemStackArgumentType; -import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import systems.brn.servershop.ServerShop; @@ -15,6 +18,12 @@ public class ShopPricesCommand { return 1; } + public static int loadLegacy(CommandContext ctx) { + ServerShop.priceStorage.loadLegacy(); + ctx.getSource().sendFeedback(() -> Text.translatable("message.servershop.storage.load"), false); + return 1; + } + public static int save(CommandContext ctx) { boolean success = ServerShop.priceStorage.save(); ctx.getSource().sendFeedback(() -> @@ -29,17 +38,28 @@ public class ShopPricesCommand { } public static int set(CommandContext ctx) { - Item item = ItemStackArgumentType.getItemStackArgument(ctx, "item").getItem(); + ItemStack stack = ItemStackArgumentType.getItemStackArgument(ctx, "item").getItem().getDefaultStack(); + return setStack(ctx, stack); + } + + public static int setHand(CommandContext ctx) { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + if (player != null) { + ItemStack stack = player.getMainHandStack().copy(); + if (!stack.isEmpty()) { + return setStack(ctx, stack); + } + + } + return 1; + } + + private static int setStack(CommandContext ctx, ItemStack stack) { + String itemName = stack.getItem().toString(); int buyPrice = IntegerArgumentType.getInteger(ctx, "buyprice"); int sellPrice = IntegerArgumentType.getInteger(ctx, "sellprice"); - String itemName = item.toString(); - boolean success = ServerShop.priceStorage.setPrices(item, buyPrice, sellPrice); - if (success) { - ctx.getSource().sendFeedback(() -> Text.translatable("message.servershop.storage.set", itemName, buyPrice, sellPrice), false); - return 1; - } else { - ctx.getSource().sendFeedback(() -> Text.translatable("message.servershop.storage.set_fail", itemName), false); - return 0; - } + ServerShop.priceStorage.setPrices(stack, buyPrice, sellPrice); + ctx.getSource().sendFeedback(() -> Text.translatable("message.servershop.storage.set", itemName, buyPrice, sellPrice), false); + return 0; } } diff --git a/src/main/java/systems/brn/servershop/commands/StoreCommands.java b/src/main/java/systems/brn/servershop/commands/StoreCommands.java index 1e9dc11..f9c5161 100644 --- a/src/main/java/systems/brn/servershop/commands/StoreCommands.java +++ b/src/main/java/systems/brn/servershop/commands/StoreCommands.java @@ -18,7 +18,7 @@ public class StoreCommands { ItemStack itemStack = new ItemStack(item, count); ServerPlayerEntity player = ctx.getSource().getPlayer(); if (player != null) { - buy(itemStack, player); + buy(itemStack, player, false); return 0; } return 1; @@ -29,7 +29,7 @@ public class StoreCommands { ItemStack itemStack = new ItemStack(item); ServerPlayerEntity player = ctx.getSource().getPlayer(); if (player != null) { - buy(itemStack, player); + buy(itemStack, player, false); return 0; } return 1; @@ -41,7 +41,7 @@ public class StoreCommands { ItemStack itemStack = new ItemStack(item, count); ServerPlayerEntity player = ctx.getSource().getPlayer(); if (player != null) { - sell(itemStack, player); + sell(itemStack, player, false); return 0; } return 1; @@ -52,7 +52,7 @@ public class StoreCommands { ItemStack itemStack = new ItemStack(item); ServerPlayerEntity player = ctx.getSource().getPlayer(); if (player != null) { - sell(itemStack, player); + sell(itemStack, player, false); return 0; } return 1; diff --git a/src/main/java/systems/brn/servershop/lib/BalanceManager.java b/src/main/java/systems/brn/servershop/lib/BalanceManager.java index 6a0a09a..101b409 100644 --- a/src/main/java/systems/brn/servershop/lib/BalanceManager.java +++ b/src/main/java/systems/brn/servershop/lib/BalanceManager.java @@ -1,36 +1,35 @@ package systems.brn.servershop.lib; +import net.minecraft.nbt.*; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; +import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.util.HashMap; +import java.util.Optional; import java.util.Scanner; import java.util.UUID; import java.util.concurrent.locks.ReentrantLock; public class BalanceManager { - final private HashMap balances = new HashMap<>(); + private final HashMap balances = new HashMap<>(); public final MinecraftServer server; - public final File balanceStorageFile; + private final File balanceStorageFile; + private final File balanceStorageCSVFile; private static final ReentrantLock lock = new ReentrantLock(); // Lock for in-memory operations public BalanceManager(MinecraftServer server) { this.server = server; - balanceStorageFile = server.getSavePath(WorldSavePath.ROOT).resolve("balances.csv").toFile(); - loadBalance(); + balanceStorageFile = server.getSavePath(WorldSavePath.ROOT).resolve("balances.dat").toFile(); + balanceStorageCSVFile = server.getSavePath(WorldSavePath.ROOT).resolve("balances.csv").toFile(); + loadBalances(); } public long getBalance(UUID uuid) { - if (balances.containsKey(uuid)) { - return balances.get(uuid); - } - return 0L; + return balances.getOrDefault(uuid, 0L); } public long getBalance(ServerPlayerEntity player) { @@ -38,8 +37,8 @@ public class BalanceManager { } public void addBalance(UUID uuid, long amount) { - balances.put(uuid, amount + getBalance(uuid)); - saveBalance(); + balances.put(uuid, getBalance(uuid) + amount); + saveBalances(); } public void addBalance(ServerPlayerEntity player, long amount) { @@ -49,11 +48,12 @@ public class BalanceManager { public void removeBalance(UUID uuid, long amount) { balances.put(uuid, getBalance(uuid) - amount); - saveBalance(); + saveBalances(); } public void setBalance(UUID uuid, long amount) { - balances.put(uuid, getBalance(uuid) + amount); + balances.put(uuid, amount); + saveBalances(); } public void setBalance(ServerPlayerEntity player, long amount) { @@ -66,37 +66,63 @@ public class BalanceManager { announceBalance(player, true); } - public boolean saveBalance() { + public void saveBalances() { lock.lock(); try { - FileWriter fileWriter = new FileWriter(balanceStorageFile, false); + NbtList nbtList = new NbtList(); for (UUID uuid : balances.keySet()) { - long balance = getBalance(uuid); - fileWriter.write(uuid.toString() + "," + balance + "\n"); + NbtCompound nbtCompound = new NbtCompound(); + nbtCompound.putUuid("UUID", uuid); + nbtCompound.putLong("Balance", balances.get(uuid)); + nbtList.add(nbtCompound); } - fileWriter.close(); - lock.unlock(); - } catch (IOException e) { - lock.unlock(); - return false; - } - return true; - } - public void onJoin(ServerPlayerEntity player) { - UUID uuid = player.getUuid(); - if (!balances.containsKey(uuid)) { - balances.put(uuid, 0L); + NbtCompound root = new NbtCompound(); + root.put("Balances", nbtList); + + try (FileOutputStream fos = new FileOutputStream(balanceStorageFile); + DataOutputStream dos = new DataOutputStream(fos)) { + NbtIo.write(root, dos); + } + } catch (IOException ignored) { + } finally { + lock.unlock(); } } - public void loadBalance() { + public void loadBalances() { + lock.lock(); + if (balanceStorageFile.exists()) { + try { + NbtCompound root = NbtIo.read(balanceStorageFile.toPath()); + if (root != null) { + NbtList nbtList = root.getList("Balances", 10); + + for (NbtElement element : nbtList) { + if (element instanceof NbtCompound nbt) { + UUID uuid = nbt.getUuid("UUID"); + long balance = nbt.getLong("Balance"); + balances.put(uuid, balance); + } + } + } + } catch ( + IOException ignored) { + } finally { + lock.unlock(); + } + } else { + loadLegacy(); + } + } + + public void loadLegacy() { lock.lock(); if (!balances.isEmpty()) { balances.clear(); } try { - Scanner scanner = new Scanner(balanceStorageFile); + Scanner scanner = new Scanner(balanceStorageCSVFile); while (scanner.hasNextLine()) { String line = scanner.nextLine(); String[] lineParts = line.split(","); @@ -106,9 +132,10 @@ public class BalanceManager { balances.put(uuid, amount); } } + saveBalances(); } catch (FileNotFoundException ignored) { - saveBalance(); + saveBalances(); } lock.unlock(); } diff --git a/src/main/java/systems/brn/servershop/lib/ItemPriceStorage.java b/src/main/java/systems/brn/servershop/lib/ItemPriceStorage.java new file mode 100644 index 0000000..9c24da3 --- /dev/null +++ b/src/main/java/systems/brn/servershop/lib/ItemPriceStorage.java @@ -0,0 +1,5 @@ +package systems.brn.servershop.lib; + +public class ItemPriceStorage { + +} diff --git a/src/main/java/systems/brn/servershop/lib/PriceStorage.java b/src/main/java/systems/brn/servershop/lib/PriceStorage.java index 6c8ee6a..d031f15 100644 --- a/src/main/java/systems/brn/servershop/lib/PriceStorage.java +++ b/src/main/java/systems/brn/servershop/lib/PriceStorage.java @@ -1,57 +1,148 @@ package systems.brn.servershop.lib; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.*; import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryWrapper; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; import net.minecraft.util.WorldSavePath; import systems.brn.servershop.ItemPrice; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; +import java.io.*; import java.util.HashMap; +import java.util.Optional; import java.util.Scanner; public class PriceStorage { public final MinecraftServer server; public final File priceStorageFile; + public final File priceStorageCSVFile; + public RegistryWrapper.WrapperLookup wrapperLookup; public PriceStorage(MinecraftServer server) { this.server = server; - priceStorageFile = server.getSavePath(WorldSavePath.ROOT).resolve("prices.csv").toFile(); + priceStorageFile = server.getSavePath(WorldSavePath.ROOT).resolve("prices.dat").toFile(); + priceStorageCSVFile = server.getSavePath(WorldSavePath.ROOT).resolve("prices.csv").toFile(); + wrapperLookup = null; + for (ServerWorld world : server.getWorlds()) { + wrapperLookup = world.getRegistryManager(); + break; + } + load(); } - public final HashMap prices = new HashMap<>(); + public final HashMap prices = new HashMap<>(); public void generateEmpty() { if (!prices.isEmpty()) { prices.clear(); } for (Item item : Registries.ITEM) { - ItemPrice itemPrice = new ItemPrice(0, 0); - prices.put(item, itemPrice); + ItemPrice itemPrice = new ItemPrice(0, 0, item.getDefaultStack()); + prices.put(item.getDefaultStack(), itemPrice); } } - public boolean setPrices(Item item, int buyPrice, int sellPrice) { - if (prices.containsKey(item)) { - prices.remove(item); - prices.put(item, new ItemPrice(buyPrice, sellPrice)); - return true; + public void setPrices(ItemStack inStack, int buyPrice, int sellPrice) { + boolean found = false; + for (ItemStack priceStack : prices.keySet()) { + if (ItemStack.areItemsAndComponentsEqual(inStack, priceStack)) { + prices.put(priceStack, new ItemPrice(buyPrice, sellPrice, priceStack)); + found = true; + break; + } } - return false; + if (!found) { + prices.put(inStack, new ItemPrice(buyPrice, sellPrice, inStack)); + } + } + + public ItemPrice getPrices(ItemStack inStack) { + for (ItemStack priceStack : prices.keySet()) { + if (ItemStack.areItemsAndComponentsEqual(inStack, priceStack)) { + return prices.get(priceStack); + } + } + return new ItemPrice(0, 0, inStack.copy()); } public void load() { + if (wrapperLookup == null) { + return; + } + prices.clear(); + + try { + NbtCompound nbtCompound = NbtIo.read(priceStorageFile.toPath()); + if (nbtCompound != null && nbtCompound.contains("Prices") && priceStorageFile.exists()) { + NbtList nbtList = nbtCompound.getList("Prices", 10); // 10 is the type ID for NbtCompound + for (NbtElement element : nbtList) { + if (element instanceof NbtCompound nbt) { + // Deserialize the ItemStack from the NbtCompound + NbtCompound stackNbt = nbt.getCompound("ItemStack"); + Optional stackTemp = ItemStack.fromNbt(wrapperLookup, stackNbt); + if (stackTemp.isPresent()) { + ItemStack stack = stackTemp.get(); + // Retrieve the buy and sell prices + int buyPrice = nbt.getInt("BuyPrice"); + int sellPrice = nbt.getInt("SellPrice"); + if(sellPrice > buyPrice && buyPrice != 0) { + buyPrice = sellPrice; + } + // Add the deserialized ItemStack and ItemPrice to the prices map + prices.put(stack, new ItemPrice(buyPrice, sellPrice, stack)); + } + } + } + } else { + loadLegacy(); + save(); + } + } catch (IOException e) { + generateEmpty(); + save(); + } + } + + public boolean save() { + if (wrapperLookup == null) { + return false; + } + NbtList nbtList = new NbtList(); + + for (ItemStack stack : prices.keySet()) { + ItemPrice itemPrice = prices.get(stack); + if (!stack.isEmpty()) { + nbtList.add(itemPrice.toNbt(wrapperLookup)); + } + } + + NbtCompound nbtCompound = new NbtCompound(); + nbtCompound.put("Prices", nbtList); + + // Write the NbtList to a file + try (FileOutputStream fos = new FileOutputStream(priceStorageFile)) { + DataOutputStream dos = new DataOutputStream(fos); + NbtIo.write(nbtCompound, dos); + } catch (IOException e) { + return false; + } + + return true; + } + + + public void loadLegacy() { if (!prices.isEmpty()) { prices.clear(); } try { - Scanner scanner = new Scanner(priceStorageFile); + Scanner scanner = new Scanner(priceStorageCSVFile); while (scanner.hasNextLine()) { String line = scanner.nextLine(); String[] lineParts = line.split(","); @@ -63,32 +154,13 @@ public class PriceStorage { int buyPrice = Integer.parseInt(lineParts[1]); int sellPrice = Integer.parseInt(lineParts[2]); Item item = Registries.ITEM.get(itemIdentifier); - prices.put(item, new ItemPrice(buyPrice, sellPrice)); + prices.put(item.getDefaultStack(), new ItemPrice(buyPrice, sellPrice, item.getDefaultStack())); } } } } catch (FileNotFoundException ignored) { generateEmpty(); - save(); } } - - public boolean save() { - try { - FileWriter fileWriter = new FileWriter(priceStorageFile, false); - for (Item item : prices.keySet()) { - ItemPrice itemPrice = prices.get(item); - String itemName = item.toString(); - int buyPrice = itemPrice.buyPrice(); - int sellPrice = itemPrice.sellPrice(); - fileWriter.write(itemName + "," + buyPrice + "," + sellPrice + "\n"); - } - fileWriter.close(); - } catch (IOException e) { - return false; - } - return true; - } - } diff --git a/src/main/java/systems/brn/servershop/lib/ShopFunctions.java b/src/main/java/systems/brn/servershop/lib/ShopFunctions.java index 892a6f6..aa4be2e 100644 --- a/src/main/java/systems/brn/servershop/lib/ShopFunctions.java +++ b/src/main/java/systems/brn/servershop/lib/ShopFunctions.java @@ -1,6 +1,5 @@ package systems.brn.servershop.lib; -import net.minecraft.command.argument.ItemStackArgumentType; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -13,10 +12,9 @@ import static systems.brn.servershop.ServerShop.priceStorage; import static systems.brn.servershop.lib.Util.*; public class ShopFunctions { - public static void buy(ItemStack itemStack, ServerPlayerEntity player) { - Item item = itemStack.getItem(); + public static void buy(ItemStack itemStack, ServerPlayerEntity player, boolean overlay) { PlayerInventory playerInventory = player.getInventory(); - ItemPrice price = priceStorage.prices.getOrDefault(item, new ItemPrice(0, 0)); + ItemPrice price = priceStorage.getPrices(itemStack); int buyPrice = price.buyPrice() * itemStack.getCount(); long playerBalance = balanceManager.getBalance(player); if (buyPrice > 0) { @@ -26,34 +24,36 @@ public class ShopFunctions { int toDeduce = buyPrice - (price.buyPrice() * remaining.getCount()); int boughtCount = itemStack.getCount() - remaining.getCount(); balanceManager.removeBalance(player, toDeduce); - player.sendMessage(Text.translatable("message.servershop.buy.success", boughtCount, itemStack.getName(), toDeduce), true); + playerBalance = balanceManager.getBalance(player); + player.sendMessage(Text.translatable("message.servershop.buy.success", boughtCount, itemStack.getName(), toDeduce, playerBalance), overlay); } else { - player.sendMessage(Text.translatable("message.servershop.buy.inventory"), true); + player.sendMessage(Text.translatable("message.servershop.buy.inventory"), overlay); } } else { - player.sendMessage(Text.translatable("message.servershop.buy.not_enough", buyPrice, playerBalance, buyPrice - playerBalance), true); + player.sendMessage(Text.translatable("message.servershop.buy.not_enough", buyPrice, playerBalance, buyPrice - playerBalance), overlay); } } else { - player.sendMessage(Text.translatable("message.servershop.buy.not_available", itemStack.getName()), true); + player.sendMessage(Text.translatable("message.servershop.buy.not_available", itemStack.getName()), overlay); } } - public static void sell(ItemStack itemStack, ServerPlayerEntity player) { - Item item = itemStack.getItem(); + public static void sell(ItemStack itemStack, ServerPlayerEntity player, boolean overlay) { PlayerInventory playerInventory = player.getInventory(); - ItemPrice price = priceStorage.prices.getOrDefault(item, new ItemPrice(0, 0)); + ItemPrice price = priceStorage.getPrices(itemStack); int sellPrice = price.sellPrice(); if (sellPrice > 0) { - int removed = removeFromInventory(playerInventory, itemStack.copy(), itemStack.getCount()); - if (removed == 0) { - player.sendMessage(Text.translatable("message.servershop.sell.not_enough"), true); - } - int toAdd = sellPrice * (itemStack.getCount() - removed); - int soldCount = itemStack.getCount() - removed; + int remaining = removeFromInventory(playerInventory, itemStack.copy(), itemStack.getCount()); + int toAdd = sellPrice * (itemStack.getCount() - remaining); + int soldCount = itemStack.getCount() - remaining; balanceManager.addBalance(player, toAdd); - player.sendMessage(Text.translatable("message.servershop.sell.success", soldCount, itemStack.getName(), toAdd), true); + if (soldCount == 0) { + player.sendMessage(Text.translatable("message.servershop.sell.not_enough"), overlay); + } else { + long playerBalance = balanceManager.getBalance(player); + player.sendMessage(Text.translatable("message.servershop.sell.success", soldCount, itemStack.getName(), toAdd, playerBalance), overlay); + } } else { - player.sendMessage(Text.translatable("message.servershop.sell.not_available", itemStack.getName()), true); + player.sendMessage(Text.translatable("message.servershop.sell.not_available", itemStack.getName()), overlay); } } } diff --git a/src/main/java/systems/brn/servershop/lib/Util.java b/src/main/java/systems/brn/servershop/lib/Util.java index c36cac3..4554238 100644 --- a/src/main/java/systems/brn/servershop/lib/Util.java +++ b/src/main/java/systems/brn/servershop/lib/Util.java @@ -1,11 +1,24 @@ package systems.brn.servershop.lib; +import eu.pb4.polymer.core.api.other.PolymerComponent; +import net.minecraft.component.ComponentType; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.text.*; +import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; +import systems.brn.servershop.ItemPrice; import systems.brn.servershop.ServerShop; +import java.util.ArrayList; +import java.util.List; +import java.util.function.UnaryOperator; + public class Util { public static Identifier id(String path) { @@ -99,4 +112,68 @@ public class Util { return stack; } + + public static ItemStack removePrices(ItemStack stack) { + LoreComponent oldLore = stack.get(DataComponentTypes.LORE); + + if (oldLore != null) { + ItemStack newStack = stack.copy(); + ArrayList filteredLines = new ArrayList<>(); + for (Text text : oldLore.styledLines()) { + if (text instanceof MutableText mutableText) { + TextContent textContent = mutableText.getContent(); + if (textContent instanceof TranslatableTextContent translatableTextContent) { + String key = translatableTextContent.getKey(); + if (!key.equals("gui.servershop.item.buyprice") && !key.equals("gui.servershop.item.sellprice")) { + filteredLines.add(text); + } + } + } + } + LoreComponent newLore = new LoreComponent(filteredLines); + + newStack.set(DataComponentTypes.LORE, newLore); + return newStack; + } else { + return stack; + } + } + + public static ItemStack addPrices(ItemPrice price) { + ItemStack stack = price.stack(); + int buyPrice = price.buyPrice(); + int sellPrice = price.sellPrice(); + int count = stack.getCount(); + if (count > 0) { + ItemStack newStack = stack.copy(); + LoreComponent lore = stack.get(DataComponentTypes.LORE); + Text buyLine = Text.translatable("gui.servershop.item.buyprice", buyPrice).setStyle(Style.EMPTY.withColor(Formatting.DARK_GREEN).withItalic(true)); + Text sellLine = Text.translatable("gui.servershop.item.sellprice", sellPrice).setStyle(Style.EMPTY.withColor(Formatting.AQUA).withItalic(true)); + + LoreComponent newLore; + if (lore == null) { + List loreList = new ArrayList<>(); + if (buyPrice > 0) { + loreList.addFirst(buyLine); + } + if (sellPrice > 0) { + loreList.addFirst(sellLine); + } + newLore = new LoreComponent(loreList); + } else { + List newLines = new ArrayList<>(lore.lines()); + if (buyPrice > 0) { + newLines.addFirst(buyLine); + } + if (sellPrice > 0) { + newLines.addFirst(sellLine); + } + newLore = new LoreComponent(newLines); + } + newStack.set(DataComponentTypes.LORE, newLore); + return newStack; + } else { + return stack; + } + } } diff --git a/src/main/java/systems/brn/servershop/screens/ShopScreen.java b/src/main/java/systems/brn/servershop/screens/ShopScreen.java index e318f59..42aa301 100644 --- a/src/main/java/systems/brn/servershop/screens/ShopScreen.java +++ b/src/main/java/systems/brn/servershop/screens/ShopScreen.java @@ -3,10 +3,6 @@ package systems.brn.servershop.screens; import eu.pb4.sgui.api.ClickType; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.elements.GuiElementInterface; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.LoreComponent; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.screen.slot.SlotActionType; @@ -16,11 +12,9 @@ import net.minecraft.util.Formatting; import systems.brn.servershop.ItemPrice; import systems.brn.servershop.ServerShop; import systems.brn.servershop.lib.PagedGui; -import systems.brn.servershop.lib.ShopFunctions; import java.util.*; -import static systems.brn.servershop.ServerShop.balanceManager; import static systems.brn.servershop.lib.ShopFunctions.buy; import static systems.brn.servershop.lib.ShopFunctions.sell; import static systems.brn.servershop.lib.Util.*; @@ -29,7 +23,7 @@ public class ShopScreen extends PagedGui { public String searchQuery = ""; - final public HashMap filteredPrices = new HashMap<>(); + final public HashMap filteredPrices = new HashMap<>(); public ShopScreen(ServerPlayerEntity player) { super(player, null); @@ -50,12 +44,12 @@ public class ShopScreen extends PagedGui { public void filterPrices() { filteredPrices.clear(); - for (Item item : ServerShop.priceStorage.prices.keySet()) { - String itemName = item.toString(); + for (ItemStack stack : ServerShop.priceStorage.prices.keySet()) { + String itemName = stack.getItem().toString(); if (itemName.contains(searchQuery)) { - ItemPrice price = ServerShop.priceStorage.prices.get(item); + ItemPrice price = ServerShop.priceStorage.getPrices(stack); if (price.buyPrice() > 0 || price.sellPrice() > 0) { - filteredPrices.put(item, price); + filteredPrices.put(stack, price); } } } @@ -74,45 +68,36 @@ public class ShopScreen extends PagedGui { @Override public boolean onClick(int index, ClickType type, SlotActionType action, GuiElementInterface element) { - Item item = element.getItemStack().getItem(); - ItemStack itemStack = item.getDefaultStack(); - if (type.shift) { - itemStack.setCount(itemStack.getMaxCount()); - } - if (type.isLeft) { //buy - buy(itemStack, player); + ItemStack cursorStack = getPlayer().currentScreenHandler.getCursorStack(); + if (!cursorStack.isEmpty()) { + sell(cursorStack, player, true); + } else { + ItemStack itemStack = removePrices(element.getItemStack()); + if (type.shift) { + itemStack.setCount(itemStack.getMaxCount()); + } + if (type.isLeft) { //buy + buy(itemStack.copy(), player, true); - } else if (type.isRight) { //sell - sell(itemStack, player); + } else if (type.isRight) { //sell + sell(itemStack.copy(), player, true); + } } return false; } - public static ItemStack getItemStack(Map.Entry price) { - ItemPrice itemPrice = price.getValue(); - Item item = price.getKey(); - int buyPrice = itemPrice.buyPrice(); - int sellPrice = itemPrice.sellPrice(); - ItemStack stack = item.getDefaultStack(); - LoreComponent originalLore = stack.getOrDefault(DataComponentTypes.LORE, new LoreComponent(List.of())); - - ArrayList lore = new ArrayList<>(originalLore.lines()); - if (buyPrice > 0) { - lore.add(Text.translatable("gui.servershop.item.buyprice", buyPrice)); - } - if (sellPrice > 0) { - lore.add(Text.translatable("gui.servershop.item.sellprice", sellPrice)); - } - stack.set(DataComponentTypes.LORE, new LoreComponent(lore)); - return stack; + @Override + public boolean insertItem(ItemStack stack, int startIndex, int endIndex, boolean fromLast) { + sell(stack, player, true); + return super.insertItem(stack, startIndex, endIndex, fromLast); } @Override protected DisplayElement getElement(int id) { - List> list = new ArrayList<>(filteredPrices.entrySet()); + List> list = new ArrayList<>(filteredPrices.entrySet()); if (id < list.size()) { - Map.Entry itemPriceEntry = list.get(id); - ItemStack stack = getItemStack(itemPriceEntry); + Map.Entry itemPriceEntry = list.get(id); + ItemStack stack = addPrices(itemPriceEntry.getValue()); GuiElementBuilder elementBuilder = new GuiElementBuilder(stack); return DisplayElement.of(elementBuilder); diff --git a/src/main/resources/assets/servershop/lang/en_us.json b/src/main/resources/assets/servershop/lang/en_us.json index 58e6c71..09db1c8 100644 --- a/src/main/resources/assets/servershop/lang/en_us.json +++ b/src/main/resources/assets/servershop/lang/en_us.json @@ -16,9 +16,13 @@ "message.servershop.balance.set_other": "%s now has %d on his account", "message.servershop.buy.not_enough": "You don't have enough money (%d>%d), you need %d more", "message.servershop.buy.inventory": "You don't have enough inventory space", - "message.servershop.buy.success": "You bought %d %s for %d", + "message.servershop.buy.success": "You bought %d %s for %d, now you have %d", "message.servershop.buy.not_available": "This item (%s) is not available for buying", "message.servershop.sell.not_enough": "You don't have this item", - "message.servershop.sell.success": "You sold %d %s for %d", - "message.servershop.sell.not_available": "This item (%s) is not available for sale" + "message.servershop.sell.success": "You sold %d %s for %d, now you have %d", + "message.servershop.sell.not_available": "This item (%s) is not available for sale", + "message.servershop.price.both" : "This item (%s) can be bought for %d and sold for %d", + "message.servershop.price.buy" : "This item (%s) can be bought for %d and can't be sold", + "message.servershop.price.sell" : "This item (%s) can be sold for %d and can't bought", + "message.servershop.price.neither" : "This item (%s) can't be bought or sold" } \ No newline at end of file diff --git a/src/main/resources/assets/servershop/textures/icon.png b/src/main/resources/assets/servershop/textures/icon.png new file mode 100644 index 0000000..ca9ae88 Binary files /dev/null and b/src/main/resources/assets/servershop/textures/icon.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index ad3d243..653f43f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -7,7 +7,7 @@ "authors": [], "contact": {}, "license": "MIT", - "icon": "assets/servershop/icon.png", + "icon": "assets/servershop/textures/icon.png", "environment": "*", "entrypoints": { "main": [