diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..67283fc --- /dev/null +++ b/README.MD @@ -0,0 +1,61 @@ +# Imyvm Villager Shop +*** +**注意,该库仍在开发状态中,目前仅完成了部分指令的注册以及数据的存取,如有任何想法欢迎提issue,有能力者可以帮忙完善并提出pr。** +## 一、配置文件 +文件位置:`config/imyvm_villagershop.conf` +```yaml +core { + # 指定显示语言 + language="en_us" + # 玩家补货时的税率 + "tax_rate"=0.01 + # 数据库地址,数据库目前仅支持Postgresql + "database_url"="jdbc:postgresql://localhost:12346/imyvmvillagershop" + # 数据库用户名 + "database_username"=root + # 数据库密码 + "database_password"="1145141919810" +} +``` +管理员可以使用`/villagershop config reload`来重载配置,使用`/villagershop taxrate set `来设置税率。 +## 二、商店创建 +### 1.玩家商店 +玩家可以使用指令`/villagershop create shop `创建商店[^1],并扣除一定的货币。 +货币的花费会随着玩家拥有商店数量的增加而增加,函数表达式为: +$$ +f(x) +\begin{cases} +40, &x\ \le\ 3\\ +10\times2^{x-1}, &x\ \gt\ 3 +\end{cases} +$$ +**注意** +- 商店刚创建或商品刚添加时商品的库存为0,你需要在创建完成后额外使用`/villagershop item addstock [count]`来进行“补货”,`count`是一个可选参数,若不填则默认消耗背包内所有特定物品。 +同时,补货需要消耗一定的货币,计算公式如下: +$$f(x) = p \times c \times t$$ +> $p$为设置价格,$c$为补货数量,$t$为税率,默认为0.1% +- 同一玩家创建的商店**不允许**名称相同,当个商店的商品**数量上限为7个物品**,价格下限**不低于**服务器设置的物价或系统商店物价。 +- 创建商店时的`count`参数代表用`price`的钱可以买到`count`个数的物品 +举个栗子,比如我使用`/villagershop create shop air 0 0 0 minecraft:air 2 100`创建了一个出售`air`的商店。那么,我可以使用`100`货币买到`2`个`air`。而不是商店中`air`的库存为`2`。 +- 所有商店都有一个唯一的`shopnumber`标识符,并且永远不可能重复。 +### 2.系统商店 +管理员可以使用指令`/villagershop create adminshop : : ... :`来创建系统商店。 +系统商店库存**无限**,但是可以根据情况限制单个玩家的购买数量。 +### 3.商店管理 +##### ①货物设置 +在商店创建成功后,玩家可以使用`/villagershop item add `来添加商品,使用`/villagershop item change `来修改**已有**商品的价格以及出售数量,使用`/villagershop item delete `来删除已有的商品[^1] +##### ②商店设置 +玩家可以使用`/villagershop manager info set {shopnamenew,x y z}`来修改商店的名称或者位置。当你不想要该商店时,可以使用`/villagershop delete {shopname,shopnumber}`[^1]来删除该商店,管理员可以使用`shopnumber`删除任意不符合规定的商店,无需提前通知拥有者。删除商店会返还所有的库存,但是并不会返还创建时消耗的货币。 +同时,管理员可以使用`/villagershop manager search {shopname:shopname, location:location, owner:owner}`,owner参数暂时只允许使用玩家的uuid,可以使用`/villagershop manager inquire `获取特定商店的详细信息。 +管理员在征得玩家以及管理组同意的情况下,可以使用`/villagershop setAdmin `[^1]来将该商店转化为系统商店。 +### 4.收购相关 +服务器在特定时间(如每半小时)就系统商店中已在出售的一种物品发起一次收购,拥有物品的玩家可以使用`/villagershop sell `出售指定数量的收购物品,达到收购数量后服务器停止收购。 +收购价格为系统商店在售价格的**80%** + +TODO: + - [ ] 商店创建时即消耗背包内的物品补货 + - [ ] 补货时可以从玩家身上的潜影盒中获取所需物品 + - [ ] 支持范围搜索商店,以及通过玩家名称搜索商店,搜索结果添加所满足条件,不输出重复结果。 + - [ ] 完成收购指令以及商店的实体创建。 + +[^1]: 对于这种指令,你需要使用`/villagershop confirm`来确定执行。 \ No newline at end of file diff --git a/src/main/kotlin/com/imyvm/villagerShop/VillagerShopMain.kt b/src/main/kotlin/com/imyvm/villagerShop/VillagerShopMain.kt index 5688e98..6a3ec69 100644 --- a/src/main/kotlin/com/imyvm/villagerShop/VillagerShopMain.kt +++ b/src/main/kotlin/com/imyvm/villagerShop/VillagerShopMain.kt @@ -8,17 +8,11 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory class VillagerShopMain : ModInitializer { - // This logger is used to write text to the console and the log file. - // It is considered best practice to use your mod id as the logger's name. - // That way, it's clear which mod wrote info, warnings, and errors. override fun onInitialize() { CONFIG.loadAndSave() CommandRegistrationCallback.EVENT.register { dispatcher, commandRegistryAccess, registrationEnvironment -> register(dispatcher, commandRegistryAccess, registrationEnvironment) } - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. LOGGER.info("Imyvm Villager Shop initialized") } companion object { diff --git a/src/main/kotlin/com/imyvm/villagerShop/apis/DataBase.kt b/src/main/kotlin/com/imyvm/villagerShop/apis/DataBase.kt index 4d8ff30..43ec44a 100644 --- a/src/main/kotlin/com/imyvm/villagerShop/apis/DataBase.kt +++ b/src/main/kotlin/com/imyvm/villagerShop/apis/DataBase.kt @@ -18,6 +18,12 @@ import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction +enum class ItemOperation { + ADD, DELETE, CHANGE +} +enum class DataSaveOperation { + SHOPNAME,ITEM,ADMIN,POS +} @Serializable data class Items(val item: String, val count: Int, val price: Int, val stock: Int = 0) @@ -118,7 +124,9 @@ class DataBase { } } - dataBaseChange(Shops.shopname, shopname, sellItemListNew, playerUUID = playerUUID) + if (dataBaseChange(Shops.shopname, shopname, sellItemListNew, playerUUID = playerUUID, operation = DataSaveOperation.ITEM)==-1) { + return "commands.shops.none" + } return when (operation) { ItemOperation.ADD -> "commands.shop.item.add.success" @@ -127,10 +135,6 @@ class DataBase { } } - enum class ItemOperation { - ADD, DELETE, CHANGE - } - private fun dataBaseSave(shopname: String, pos: String, items: String, owner: String, admin: Int = 0) { transaction(DbSettings.db) { SchemaUtils.create(Shops) @@ -175,38 +179,33 @@ class DataBase { targetValue: String = "", sellItemListNew: MutableList = mutableListOf(), shopNameNew: String = "", - targetInt: Column> = Shops.id, targetValueInt: Int = -1, + targetInt: Column> = Shops.id, + targetValueInt: Int = -1, playerUUID: String = "", - blockPos: BlockPos = BlockPos(0,0,0)) { - if (shopNameNew != ""){ - transaction(DbSettings.db) { - SchemaUtils.create(Shops) - Shops.update ({ target eq targetValue and (Shops.owner eq playerUUID) }) { - it[shopname] = shopNameNew - } - } - }else if (sellItemListNew.isNotEmpty()){ - transaction(DbSettings.db) { - SchemaUtils.create(Shops) - Shops.update ({ target eq targetValue and (Shops.owner eq playerUUID) }) { - it[items] = Json.encodeToString(serializer,sellItemListNew) - } - } - } else if (targetValueInt != -1) { - transaction(DbSettings.db) { - SchemaUtils.create(Shops) - Shops.update ({ targetInt eq targetValueInt }) { - it[admin] = 1 - } - } - } else { - transaction(DbSettings.db) { - SchemaUtils.create(Shops) - Shops.update ({ Shops.pos eq blockPosToString(blockPos) and (Shops.owner eq playerUUID) }){ - it[pos] = blockPosToString(blockPos) + blockPos: BlockPos = BlockPos(0, 0, 0), + operation: DataSaveOperation + ): Int { + val condition: Op = when { + targetInt == Shops.id -> { targetInt eq targetValueInt } + blockPos != BlockPos(0, 0, 0) -> { Shops.pos eq blockPosToString(blockPos) and (Shops.owner eq playerUUID) } + else -> { target eq targetValue and (Shops.owner eq playerUUID) } + } + + val numberOfRowsUpdated = transaction(DbSettings.db) { + SchemaUtils.create(Shops) + Shops.update({condition}) { + when (operation) { + DataSaveOperation.SHOPNAME -> it[shopname] = shopNameNew + DataSaveOperation.ITEM -> it[items] = Json.encodeToString(serializer, sellItemListNew) + DataSaveOperation.ADMIN -> it[admin] = 1 + DataSaveOperation.POS -> it[pos] = blockPosToString(blockPos) } } } + return when (numberOfRowsUpdated) { + 0 -> -1 + else -> 1 + } } fun dataBaseDelete( @@ -223,7 +222,7 @@ class DataBase { if (data == uuid){ Shops.deleteWhere { targetString eq targetValueString and (owner eq data) } } else { - returnMessage = "commands.deleteshop.none" + returnMessage = "commands.shops.none" } } } diff --git a/src/main/kotlin/com/imyvm/villagerShop/commands/ItemManager.kt b/src/main/kotlin/com/imyvm/villagerShop/commands/ItemManager.kt index 85cdcfc..2216ac9 100644 --- a/src/main/kotlin/com/imyvm/villagerShop/commands/ItemManager.kt +++ b/src/main/kotlin/com/imyvm/villagerShop/commands/ItemManager.kt @@ -3,6 +3,7 @@ package com.imyvm.villagerShop.commands import com.imyvm.economy.EconomyMod import com.imyvm.villagerShop.apis.Translator.tr import com.imyvm.villagerShop.apis.DataBase +import com.imyvm.villagerShop.apis.ItemOperation import com.imyvm.villagerShop.apis.ModConfig import com.mojang.brigadier.Command import com.mojang.brigadier.context.CommandContext @@ -18,14 +19,14 @@ fun handleItemOperation( item: ItemStackArgument? = null, count: Int = 0, price: Int = 0, - operation: DataBase.ItemOperation + operation: ItemOperation ): Int { val player = context.source.player val playerUUID = player!!.uuidAsString val message = DataBase().modifyItems(shopname, item, count, price, playerUUID = playerUUID, operation = operation) player.sendMessage(tr(message)) - return if (operation == DataBase.ItemOperation.DELETE) 0 else if (message == "commands.playershop.create.limit") -1 else Command.SINGLE_SUCCESS + return if (operation == ItemOperation.DELETE) 0 else if (message == "commands.playershop.create.limit") -1 else Command.SINGLE_SUCCESS } fun itemAdd( @@ -34,14 +35,14 @@ fun itemAdd( item: ItemStackArgument, count: Int, price: Int -) :Int = handleItemOperation(context, shopname, item, count, price, DataBase.ItemOperation.ADD) +) :Int = handleItemOperation(context, shopname, item, count, price, ItemOperation.ADD) fun itemDelete( context: CommandContext, shopname: String, item: ItemStackArgument ) { - handleItemOperation(context, shopname, item, operation = DataBase.ItemOperation.DELETE) + handleItemOperation(context, shopname, item, operation = ItemOperation.DELETE) } fun itemChange( @@ -50,7 +51,7 @@ fun itemChange( item: ItemStackArgument, count: Int, price: Int -) :Int = handleItemOperation(context, shopname, item, count, price, DataBase.ItemOperation.CHANGE) +) :Int = handleItemOperation(context, shopname, item, count, price, ItemOperation.CHANGE) fun itemQuantityAdd( context: CommandContext, @@ -91,7 +92,7 @@ fun itemQuantityAdd( DataBase().modifyItems( shopname, playerUUID = player.uuidAsString, - operation = DataBase.ItemOperation.CHANGE, + operation = ItemOperation.CHANGE, stock = amountToConsume + itemStock, item = item, price = itemPrice, diff --git a/src/main/kotlin/com/imyvm/villagerShop/commands/ShopManage.kt b/src/main/kotlin/com/imyvm/villagerShop/commands/ShopManage.kt index 16d6983..b61cc2b 100644 --- a/src/main/kotlin/com/imyvm/villagerShop/commands/ShopManage.kt +++ b/src/main/kotlin/com/imyvm/villagerShop/commands/ShopManage.kt @@ -3,6 +3,7 @@ package com.imyvm.villagerShop.commands import com.imyvm.economy.EconomyMod import com.imyvm.economy.Translator import com.imyvm.villagerShop.apis.DataBase +import com.imyvm.villagerShop.apis.DataSaveOperation import com.imyvm.villagerShop.apis.Translator.tr import com.imyvm.villagerShop.apis.checkParameterLegality import com.mojang.brigadier.Command @@ -155,8 +156,11 @@ fun shopSetAdmin( number: Int ){ val player = context.source.player!! - DataBase().dataBaseChange(targetValueInt = number) - player.sendMessage(tr("commands.setadmin.ok")) + if (DataBase().dataBaseChange(targetValueInt = number, operation = DataSaveOperation.ADMIN) == 1){ + context.source.sendFeedback(tr("commands.setadmin.ok"),true) + } else { + context.source.sendFeedback(tr("commands.shops.none"),true) + } } fun shopInfoChange( @@ -166,10 +170,17 @@ fun shopInfoChange( shopname: String = "", blockPos: BlockPos = BlockPos(0,0,0) ) :Int { - val playerUUID = context.source.player!!.uuidAsString - when (infoname){ - "shopname" -> DataBase().dataBaseChange(targetValue = shopname, shopNameNew = shopnameNew, playerUUID = playerUUID) - "pos" -> DataBase().dataBaseChange(targetValue = shopname, blockPos = blockPos, playerUUID = playerUUID) + val player = context.source.player!! + val playerUUID = player.uuidAsString + val result = when (infoname){ + "shopname" -> DataBase().dataBaseChange(targetValue = shopname, shopNameNew = shopnameNew, playerUUID = playerUUID, operation = DataSaveOperation.SHOPNAME) + "pos" -> DataBase().dataBaseChange(targetValue = shopname, blockPos = blockPos, playerUUID = playerUUID, operation = DataSaveOperation.POS) + else -> 114514 + } + if (result == -1){ + player.sendMessage(tr("commands.shops.none")) + } else { + player.sendMessage(tr("commands.shopinfo.change.success")) } return Command.SINGLE_SUCCESS } \ No newline at end of file diff --git a/src/main/resources/assets/imyvm-villagershop/lang/en_us.json b/src/main/resources/assets/imyvm-villagershop/lang/en_us.json index 1524060..92d8d1c 100644 --- a/src/main/resources/assets/imyvm-villagershop/lang/en_us.json +++ b/src/main/resources/assets/imyvm-villagershop/lang/en_us.json @@ -6,6 +6,7 @@ "commands.shop.create.failed.duplicate_name": "Duplicate shop name", "commands.shop.create.success": "Store created successfully", "commands.shop.item.add.success": "Item add successfully", + "commands.shops.none": "The shop does not exist", "commands.playershop.add.repeat": "Duplicate shop items", "commands.shop.item.change.success": "Item modified successfully", "commands.shop.item.delete.success": "Item delete successfully", @@ -25,9 +26,9 @@ "commands.shopinfo.pos": "Shop location : ({0})", "commands.shopinfo.owner": "Shop owner: {0}", "commands.shopinfo.items": "Shop items :{0} , Count : {1} , Price : {2} , Stock : {3}", + "commands.shopinfo.change.success": "Shop information modified successfully", "commands.setadmin.ok": "Shop type modified successfully", "commands.deleteshop.ok": "Shop deleted successfully", - "commands.deleteshop.none": "The shop does not exist", "commands.stock.add.ok": "Successful replenishment.", "commands.search.none": "There are no eligible shops" } \ No newline at end of file