pull/6/head
jakub 9 months ago
parent 0c14e81eac
commit d77cb08f53
  1. 11
      src/main/java/xyz/soukup/ecoCraftCore/EcoCraftCore.java
  2. 22
      src/main/java/xyz/soukup/ecoCraftCore/commands/MoneyCommand.java
  3. 3
      src/main/java/xyz/soukup/ecoCraftCore/commands/ShopCommand.java
  4. 33
      src/main/java/xyz/soukup/ecoCraftCore/commands/TransactionCommand.java
  5. 1
      src/main/java/xyz/soukup/ecoCraftCore/events/PreparePlayer.java
  6. 1
      src/main/java/xyz/soukup/ecoCraftCore/events/ShopLogic.java
  7. 8
      src/main/java/xyz/soukup/ecoCraftCore/events/VirtualChestLogic.java
  8. 95
      src/main/java/xyz/soukup/ecoCraftCore/objects/Account.java
  9. 11
      src/main/java/xyz/soukup/ecoCraftCore/objects/Shop.java
  10. 24
      src/main/java/xyz/soukup/ecoCraftCore/objects/Transaction.java
  11. 44
      src/main/java/xyz/soukup/ecoCraftCore/objects/VirtualChest.java
  12. 12
      src/main/resources/config.yml
  13. 29
      src/main/resources/messages.yml

@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull;
import xyz.soukup.ecoCraftCore.commands.MoneyCommand; import xyz.soukup.ecoCraftCore.commands.MoneyCommand;
import xyz.soukup.ecoCraftCore.commands.RulerCommand; import xyz.soukup.ecoCraftCore.commands.RulerCommand;
import xyz.soukup.ecoCraftCore.commands.ShopCommand; import xyz.soukup.ecoCraftCore.commands.ShopCommand;
import xyz.soukup.ecoCraftCore.commands.TransactionCommand;
import xyz.soukup.ecoCraftCore.events.PreparePlayer; import xyz.soukup.ecoCraftCore.events.PreparePlayer;
import xyz.soukup.ecoCraftCore.events.RulerMarking; import xyz.soukup.ecoCraftCore.events.RulerMarking;
import xyz.soukup.ecoCraftCore.events.ShopLogic; import xyz.soukup.ecoCraftCore.events.ShopLogic;
@ -25,6 +24,7 @@ import xyz.soukup.ecoCraftCore.objects.VirtualChest;
import xyz.soukup.ecoCraftCore.utilities.DaoRegistry; import xyz.soukup.ecoCraftCore.utilities.DaoRegistry;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -44,10 +44,18 @@ public final class EcoCraftCore extends JavaPlugin {
e.printStackTrace(); e.printStackTrace();
getLogger().severe("Failed to initialize database."); getLogger().severe("Failed to initialize database.");
} }
this.getServer().getScheduler().runTaskTimer(this, /* Lambda: */ task -> {
VirtualChest.saveCache();
} , 6000, 6000);
} }
@Override @Override
public void onDisable() { public void onDisable() {
VirtualChest.saveCache();
try { try {
if (connectionSource != null) { if (connectionSource != null) {
connectionSource.close(); connectionSource.close();
@ -78,7 +86,6 @@ public final class EcoCraftCore extends JavaPlugin {
private void registerCommands() { private void registerCommands() {
@NotNull LifecycleEventManager<@NotNull Plugin> lm = this.getLifecycleManager(); @NotNull LifecycleEventManager<@NotNull Plugin> lm = this.getLifecycleManager();
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(TransactionCommand.createCommand().build()));
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(ShopCommand.createCommand().build())); lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(ShopCommand.createCommand().build()));
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(RulerCommand.createCommand().build())); lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(RulerCommand.createCommand().build()));
lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MoneyCommand.createCommand().build())); lm.registerEventHandler(LifecycleEvents.COMMANDS, event -> event.registrar().register(MoneyCommand.createCommand().build()));

@ -4,9 +4,11 @@ import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.command.brigadier.argument.ArgumentTypes; import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import xyz.soukup.ecoCraftCore.objects.Account; import xyz.soukup.ecoCraftCore.objects.Account;
@ -57,9 +59,11 @@ public class MoneyCommand {
} }
private static int sendMoney(CommandContext<CommandSourceStack> context){ private static int sendMoney(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
PlayerSelectorArgumentResolver receiverResolver = context.getArgument("player", PlayerSelectorArgumentResolver.class);
Player receiver = receiverResolver.resolve(context.getSource()).getFirst();
Player sender = (Player) context.getSource().getSender(); Player sender = (Player) context.getSource().getSender();
Player receiver = context.getArgument("player", Player.class);
Float amount = context.getArgument("amount", Float.class); Float amount = context.getArgument("amount", Float.class);
Account senderAccount = Account.getOrCreate(sender); Account senderAccount = Account.getOrCreate(sender);
@ -75,9 +79,12 @@ public class MoneyCommand {
return 1; return 1;
} }
private static int giveMoneyPlayer(CommandContext<CommandSourceStack> context){ private static int giveMoneyPlayer(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
CommandSender commandSender = context.getSource().getSender(); CommandSender commandSender = context.getSource().getSender();
Player receiver = context.getArgument("player", Player.class);
PlayerSelectorArgumentResolver receiverResolver = context.getArgument("player", PlayerSelectorArgumentResolver.class);
Player receiver = receiverResolver.resolve(context.getSource()).getFirst();
Float amount = context.getArgument("amount", Float.class); Float amount = context.getArgument("amount", Float.class);
Transaction transaction = new Transaction(amount,"admin", commandSender.getName(), "player", receiver.getName(), "admin"); Transaction transaction = new Transaction(amount,"admin", commandSender.getName(), "player", receiver.getName(), "admin");
@ -105,9 +112,12 @@ public class MoneyCommand {
transaction.process(); transaction.process();
return 1; return 1;
} }
private static int removeMoneyPlayer(CommandContext<CommandSourceStack> context){ private static int removeMoneyPlayer(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
CommandSender commandSender = context.getSource().getSender(); CommandSender commandSender = context.getSource().getSender();
Player sender = context.getArgument("player", Player.class);
PlayerSelectorArgumentResolver senderResolver = context.getArgument("player", PlayerSelectorArgumentResolver.class);
Player sender = senderResolver.resolve(context.getSource()).getFirst();
Float amount = context.getArgument("amount", Float.class); Float amount = context.getArgument("amount", Float.class);
Transaction transaction = new Transaction(amount, "player", sender.getName(),"admin", commandSender.getName(), "admin"); Transaction transaction = new Transaction(amount, "player", sender.getName(),"admin", commandSender.getName(), "admin");

@ -5,7 +5,6 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import org.bukkit.Location;
import org.bukkit.block.Chest; import org.bukkit.block.Chest;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -82,7 +81,7 @@ public class ShopCommand {
VirtualChest virtualChest = VirtualChest.getOrCreate(chest); VirtualChest virtualChest = VirtualChest.getOrCreate(chest);
virtualChest.save(); virtualChest.databaseSave();
PDC.set(chest, "virtual", virtualChest.getId()); PDC.set(chest, "virtual", virtualChest.getId());

@ -1,33 +0,0 @@
package xyz.soukup.ecoCraftCore.commands;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import xyz.soukup.ecoCraftCore.objects.Transaction;
public class TransactionCommand {
public static LiteralArgumentBuilder<CommandSourceStack> createCommand() {
return Commands.literal("transaction")
.then(Commands.literal("simulate")
.then(Commands.argument("sender", StringArgumentType.word())
.then(Commands.argument("receiver", StringArgumentType.word())
.then(Commands.argument("amount", FloatArgumentType.floatArg(0.1F))
.executes(TransactionCommand::simulateTransaction)))));
}
private static int simulateTransaction(CommandContext<CommandSourceStack> ctx) {
String sender = StringArgumentType.getString(ctx, "sender");
String receiver = StringArgumentType.getString(ctx, "receiver");
Float amount = FloatArgumentType.getFloat(ctx, "amount");
Transaction tx = new Transaction(amount, "player", sender, "player", receiver, "command");
tx.save();
return 1;
}
}

@ -10,6 +10,5 @@ public class PreparePlayer implements Listener {
@EventHandler @EventHandler
public void preparePlayer(PlayerJoinEvent event){ public void preparePlayer(PlayerJoinEvent event){
Account account = Account.getOrCreate(event.getPlayer()); Account account = Account.getOrCreate(event.getPlayer());
account.save();
} }
} }

@ -226,7 +226,6 @@ public class ShopLogic implements Listener {
virtualChest.save(); virtualChest.save();
Transaction transaction = new Transaction(price, "player", shop.getOwner(), "player", player.getName(), "playerShop", shop.getItemName() ,Integer.toString(amount)); Transaction transaction = new Transaction(price, "player", shop.getOwner(), "player", player.getName(), "playerShop", shop.getItemName() ,Integer.toString(amount));
transaction.process(); transaction.process();
} }

@ -1,26 +1,20 @@
package xyz.soukup.ecoCraftCore.events; package xyz.soukup.ecoCraftCore.events;
import com.destroystokyo.paper.utils.PaperPluginLogger;
import com.j256.ormlite.stmt.query.In;
import org.bukkit.block.Chest; import org.bukkit.block.Chest;
import org.bukkit.block.DoubleChest; import org.bukkit.block.DoubleChest;
import org.bukkit.block.TileState; import org.bukkit.block.TileState;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.HopperInventorySearchEvent;
import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import xyz.soukup.ecoCraftCore.objects.VirtualChest; import xyz.soukup.ecoCraftCore.objects.VirtualChest;
import xyz.soukup.ecoCraftCore.utilities.PDC; import xyz.soukup.ecoCraftCore.utilities.PDC;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Objects;
public class VirtualChestLogic implements Listener { public class VirtualChestLogic implements Listener {
@ -106,7 +100,7 @@ public class VirtualChestLogic implements Listener {
return; return;
} }
virtualChest.setOpened(false);
virtualChest.setInventory(event.getInventory()); virtualChest.setInventory(event.getInventory());
virtualChest.save(); virtualChest.save();
} }

@ -1,7 +1,9 @@
package xyz.soukup.ecoCraftCore.objects; package xyz.soukup.ecoCraftCore.objects;
import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.misc.TransactionManager;
import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.UpdateBuilder;
import com.j256.ormlite.table.DatabaseTable; import com.j256.ormlite.table.DatabaseTable;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -9,6 +11,9 @@ import xyz.soukup.ecoCraftCore.EcoCraftCore;
import xyz.soukup.ecoCraftCore.utilities.DaoRegistry; import xyz.soukup.ecoCraftCore.utilities.DaoRegistry;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap;
import static xyz.soukup.ecoCraftCore.EcoCraftCore.connectionSource;
@DatabaseTable(tableName = "accounts") @DatabaseTable(tableName = "accounts")
public class Account { public class Account {
@ -24,6 +29,8 @@ public class Account {
@DatabaseField(canBeNull = false, defaultValue = "0") @DatabaseField(canBeNull = false, defaultValue = "0")
private float balance; private float balance;
private static final HashMap<String, Account> cache = new HashMap<>();
public Account(){ public Account(){
} }
@ -42,33 +49,90 @@ public class Account {
} }
public float getBalance() { public float getBalance() {
return balance; try {
Account fresh = DaoRegistry.getAccountDao().queryBuilder()
.selectColumns("balance")
.where().eq("id", this.id)
.queryForFirst();
return fresh.balance;
} catch (SQLException e) {
throw new RuntimeException("Failed to refresh balance from DB", e);
}
} }
public int getId() { public int getId() {
return id; return id;
} }
public void setBalance(float balance) {
this.balance = balance;
}
public void deposit(float amount){ public void deposit(float amount){
this.balance += amount; try {
TransactionManager.callInTransaction(connectionSource, () -> {
UpdateBuilder<Account, Integer> builder = DaoRegistry.getAccountDao().updateBuilder();
builder.updateColumnExpression("balance", "balance + " + amount);
builder.where().idEq(this.id);
int updated = builder.update();
return null;
});
} catch (SQLException e) {
throw new RuntimeException("Failed to deposit funds", e);
}
} }
public void withdraw(float amount){ public void withdraw(float amount){
this.balance -= amount; try {
TransactionManager.callInTransaction(connectionSource, () -> {
UpdateBuilder<Account, Integer> builder = DaoRegistry.getAccountDao().updateBuilder();
builder.updateColumnExpression("balance", "balance - " + amount);
builder.where().idEq(this.id);
return null;
});
} catch (SQLException e) {
throw new RuntimeException("Failed to deposit funds", e);
}
}
public static void transferFunds(Account from, Account to, float amount) {
try {
TransactionManager.callInTransaction(
connectionSource,
() -> {
UpdateBuilder<Account, Integer> withdrawBuilder = DaoRegistry.getAccountDao().updateBuilder();
withdrawBuilder.updateColumnExpression("balance", "balance - " + amount);
withdrawBuilder.where().eq("id", from.getId());
withdrawBuilder.update();
UpdateBuilder<Account, Integer> depositBuilder = DaoRegistry.getAccountDao().updateBuilder();
depositBuilder.updateColumnExpression("balance", "balance + " + amount);
depositBuilder.where().idEq(to.getId());
depositBuilder.update();
return null;
}
);
} catch (SQLException e) {
throw new RuntimeException("Fund transfer failed: " + e.getMessage(), e);
}
} }
public static Account getOrCreate(String type, String owner){ public static Account getOrCreate(String type, String owner){
Account account = cache.get(type + "." + owner);
if (account != null){
return account;
}
QueryBuilder<Account, Integer> queryBuilder = DaoRegistry.getAccountDao().queryBuilder(); QueryBuilder<Account, Integer> queryBuilder = DaoRegistry.getAccountDao().queryBuilder();
try { try {
queryBuilder.where() queryBuilder.where()
.eq("type", type) .eq("type", type)
.and() .and()
.eq("owner", owner); .eq("owner", owner);
Account account = DaoRegistry.getAccountDao().queryForFirst(queryBuilder.prepare()); account = DaoRegistry.getAccountDao().queryForFirst(queryBuilder.prepare());
if (account == null){ if (account == null){
account = new Account(owner, type); account = new Account(owner, type);
@ -90,6 +154,15 @@ public class Account {
if (account == null){ if (account == null){
account = new Account(player.getName(), "player"); account = new Account(player.getName(), "player");
try {
DaoRegistry.getAccountDao().createOrUpdate(account);
} catch (SQLException e) {
throw new RuntimeException(e);
}
cache.put(account.type + "." + account.owner, account);
} }
return account; return account;
} catch (SQLException e) { } catch (SQLException e) {
@ -97,13 +170,5 @@ public class Account {
} }
} }
public void save(){
try {
DaoRegistry.getAccountDao().createOrUpdate(this);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
} }

@ -14,6 +14,7 @@ import xyz.soukup.ecoCraftCore.utilities.PDC;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.HashMap;
@DatabaseTable(tableName = "shops") @DatabaseTable(tableName = "shops")
public class Shop { public class Shop {
@ -58,6 +59,8 @@ public class Shop {
@DatabaseField(columnName = "active_on_server") @DatabaseField(columnName = "active_on_server")
private String activeServer; private String activeServer;
private static final HashMap<Integer, Shop> cache = new HashMap<>();
public Shop() { public Shop() {
} }
@ -100,6 +103,12 @@ public class Shop {
} }
public static Shop findById(int id) { public static Shop findById(int id) {
Shop shop = cache.get(id);
if (shop != null){
return shop;
}
try { try {
return DaoRegistry.getShopDao().queryForId(id); return DaoRegistry.getShopDao().queryForId(id);
} catch (SQLException e) { } catch (SQLException e) {
@ -113,6 +122,8 @@ public class Shop {
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
cache.put(this.id, this);
} }
public ItemStack getItemStack() { public ItemStack getItemStack() {

@ -49,14 +49,6 @@ public class Transaction {
// ORMLite requires a no-arg constructor // ORMLite requires a no-arg constructor
} }
public void save(){
try {
DaoRegistry.getTransactionDao().createOrUpdate(this);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void process(){ public void process(){
if (processed){ if (processed){
@ -65,19 +57,13 @@ public class Transaction {
this.processed = true; this.processed = true;
if(!Objects.equals(this.senderType, "admin")){ Account.transferFunds(Account.getOrCreate(this.senderType, this.sender), Account.getOrCreate(this.receiverType, this.receiver), this.amount);
Account senderAccount = Account.getOrCreate(this.senderType, this.sender);
senderAccount.withdraw(this.amount);
senderAccount.save();
}
if (!Objects.equals(this.receiverType, "admin")){ try {
Account receiverAccount = Account.getOrCreate(this.receiverType, this.receiver); DaoRegistry.getTransactionDao().createOrUpdate(this);
receiverAccount.deposit(this.amount); } catch (SQLException e) {
receiverAccount.save(); throw new RuntimeException(e);
} }
save();
} }
public static Transaction findById(int id) { public static Transaction findById(int id) {

@ -12,6 +12,8 @@ import xyz.soukup.ecoCraftCore.utilities.InventoryUtils;
import xyz.soukup.ecoCraftCore.utilities.PDC; import xyz.soukup.ecoCraftCore.utilities.PDC;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
@DatabaseTable(tableName = "virtual_chests") @DatabaseTable(tableName = "virtual_chests")
public class VirtualChest { public class VirtualChest {
@ -25,11 +27,10 @@ public class VirtualChest {
@DatabaseField(columnName = "active_on_server") @DatabaseField(columnName = "active_on_server")
private String activeServer; private String activeServer;
@DatabaseField(columnName = "is_opened")
private Boolean isOpened = false;
private Inventory inventory; private Inventory inventory;
private static final HashMap<Integer, VirtualChest> cache = new HashMap<>();
private static final HashSet<Integer> cacheChanges = new HashSet<>();
public VirtualChest(){ public VirtualChest(){
@ -59,14 +60,38 @@ public class VirtualChest {
} }
public static VirtualChest findById(int id) { public static VirtualChest findById(int id) {
VirtualChest virtualChest = cache.get(id);
if (virtualChest != null){
return virtualChest;
}
try { try {
return DaoRegistry.getVirtualChestDao().queryForId(id); return DaoRegistry.getVirtualChestDao().queryForId(id);
} catch (SQLException e) { } catch (SQLException e) {
return null; return null;
} }
} }
public void save(){ public static void saveCache(){
cache.forEach((key, value) -> {
if (!cacheChanges.contains(key)) return;
value.databaseSave();
});
}
public void save() {
cache.put(this.id, this);
cacheChanges.add(this.id);
}
public void databaseSave(){
if (this.inventory != null){ if (this.inventory != null){
this.inventoryString = Converter.toString(this.inventory); this.inventoryString = Converter.toString(this.inventory);
} }
@ -75,6 +100,9 @@ public class VirtualChest {
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
cache.put(this.id, this);
cacheChanges.remove(this.id);
} }
public void setInventory(Inventory inventory) { public void setInventory(Inventory inventory) {
@ -110,18 +138,10 @@ public class VirtualChest {
return id; return id;
} }
public Boolean getOpened() {
return isOpened;
}
public String getActiveServer() { public String getActiveServer() {
return activeServer; return activeServer;
} }
public void setOpened(Boolean opened) {
isOpened = opened;
}
public void setActiveServer(String activeServer) { public void setActiveServer(String activeServer) {
this.activeServer = activeServer; this.activeServer = activeServer;
} }

@ -0,0 +1,12 @@
version: 1.0
server:
name: "test"
type: "island"
database:
host: localhost
port: 3306
user: "ecc"
password: "ecc"
database: "ecc"
cache:
save-interval: 6000

@ -0,0 +1,29 @@
generic:
error:
no-funds:
self: "<red>Nemáš dost pěněz"
shop: "<red>Obchod nemá dost peněz"
no-space:
self: "<red>Nemáš ve svém inventáři dostatek místa"
shop: "<red>V obchodě není dostatek místa"
no-item:
self: "<red>Nemáš dostatek itemů"
shop: "<red>Obchod nemá dostatek itemů"
shop:
buy: "<lime>Koupil jsi <count>x <item> za <price>"
sell: "<lime>Prodal jsi <count>x <item> za <price>"
create: "<lime>Obchod vytvořen."
money:
give:
self: "<lime>Dal sis <amount>"
player: "<lime>Dal jsi hráči <receiver> <amount>"
send:
player: "<lime>Poslal jsi <amount> hráči <receiver>"
receive:
player: "<lime>Obdržel jsi <amount> od hráče <sender>"
marker:
marked:
chest: "<lime>Truhla označena (<x>,<y>,<z>)"
sign: "<lime>Cedule označena (<x>,<y>,<z>)"
primary: "<lime>První pozice označena (<x>,<y>,<z>)"
secondary: "<lime>Druhá pozice označena (<x>,<y>,<z>)"
Loading…
Cancel
Save