├── .gitignore ├── .examples ├── userdata │ ├── uuid.json │ ├── database.sql │ └── uuid.yml └── depositories │ ├── images │ ├── farmer.png │ ├── fishman.png │ ├── hunter.png │ └── miner.png │ └── README.md ├── .documentation ├── images │ ├── sell-gui.png │ ├── item-in-gui.png │ ├── sell-message.png │ └── collect-message.jpg ├── JAVADOC-README.md ├── README.md └── develop │ └── use-custome-storage.md ├── renovate.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_issues.md │ └── bugs_report.md └── workflows │ ├── maven.yml │ ├── codeql-analysis.yml │ └── deploy.yml ├── src ├── test │ └── java │ │ ├── MoneyTest.java │ │ ├── ReleasesTest.java │ │ ├── PluginNameTest.java │ │ └── GsonMapTest.java └── main │ ├── resources │ ├── PLUGIN_INFO │ ├── plugin.yml │ ├── depositories │ │ ├── fishman.yml │ │ ├── .example-depository.yml │ │ ├── hunter.yml │ │ ├── farmer.yml │ │ └── miner.yml │ ├── i18n │ │ ├── en_US │ │ │ ├── messages.yml │ │ │ ├── .example-depository.yml │ │ │ └── config.yml │ │ └── ko_KR │ │ │ ├── messages.yml │ │ │ ├── .example-depository.yml │ │ │ └── config.yml │ └── config.yml │ └── java │ └── cc │ └── carm │ └── plugin │ └── ultradepository │ ├── event │ ├── UltraDepositoryEvent.java │ ├── DepositorySellItemEvent.java │ └── DepositoryCollectItemEvent.java │ ├── util │ ├── DateIntUtil.java │ ├── DatabaseTable.java │ ├── JarUtil.java │ └── JarResourceUtils.java │ ├── hooker │ ├── UpdateChecker.java │ ├── GHUpdateChecker.java │ ├── VaultHooker.java │ └── PAPIExpansion.java │ ├── UltraDepository.java │ ├── storage │ ├── impl │ │ ├── CustomStorage.java │ │ ├── YAMLStorage.java │ │ ├── MySQLStorage.java │ │ └── JSONStorage.java │ ├── DataStorage.java │ └── StorageMethod.java │ ├── data │ ├── DepositoryItemData.java │ ├── DepositoryData.java │ └── UserData.java │ ├── configuration │ ├── depository │ │ ├── DepositoryCapacity.java │ │ ├── Depository.java │ │ └── DepositoryItem.java │ ├── PluginMessages.java │ └── PluginConfig.java │ ├── listener │ ├── UserListener.java │ └── CollectListener.java │ ├── manager │ ├── ConfigManager.java │ ├── EconomyManager.java │ ├── UserManager.java │ └── DepositoryManager.java │ ├── ui │ ├── DepositoryGUI.java │ └── SellItemGUI.java │ ├── Main.java │ └── command │ └── DepositoryCommand.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /target/ 3 | ./*.iml 4 | *.iml 5 | asset/ -------------------------------------------------------------------------------- /.examples/userdata/uuid.json: -------------------------------------------------------------------------------- 1 | {"date":20220103,"depositories":{"miner":{"DIAMOND:0":{"sold":102,"amount":399}}}} -------------------------------------------------------------------------------- /.documentation/images/sell-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.documentation/images/sell-gui.png -------------------------------------------------------------------------------- /.documentation/images/item-in-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.documentation/images/item-in-gui.png -------------------------------------------------------------------------------- /.documentation/images/sell-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.documentation/images/sell-message.png -------------------------------------------------------------------------------- /.documentation/images/collect-message.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.documentation/images/collect-message.jpg -------------------------------------------------------------------------------- /.examples/depositories/images/farmer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.examples/depositories/images/farmer.png -------------------------------------------------------------------------------- /.examples/depositories/images/fishman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.examples/depositories/images/fishman.png -------------------------------------------------------------------------------- /.examples/depositories/images/hunter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.examples/depositories/images/hunter.png -------------------------------------------------------------------------------- /.examples/depositories/images/miner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carm-outsource/UltraDepository/HEAD/.examples/depositories/images/miner.png -------------------------------------------------------------------------------- /.examples/userdata/database.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `ub_data`(`uuid`, `data`, `day`) 2 | VALUES ('', '{"date":20220103,"depositories":{"miner":{"DIAMOND:0":{"sold":102,"amount":399}}}}', '20220103'); -------------------------------------------------------------------------------- /.examples/userdata/uuid.yml: -------------------------------------------------------------------------------- 1 | date: 20211230 # 用于判断售出数据的所在日期 2 | 3 | depositories: 4 | hunter: 5 | RABBIT_HIDE:0: 6 | amount: 1 7 | miner: 8 | GOLD_INGOT:0: 9 | amount: 52 10 | DIAMOND:0: 11 | sold: 30 12 | amount: 351 -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchUpdateTypes": ["minor", "patch"], 9 | "matchCurrentVersion": "!/^0/", 10 | "automerge": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 功能需求 3 | about: 希望我们提供更多的功能。 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **功能简述** 11 | 12 | 13 | **需求来源** 14 | 15 | 16 | **功能参考**(可选) 17 | 18 | 19 | **附加内容** 20 | 21 | -------------------------------------------------------------------------------- /.documentation/JAVADOC-README.md: -------------------------------------------------------------------------------- 1 | # UltraDepository Javadoc 2 | 3 | 基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/UltraDepository) 。 4 | 5 | ## 如何实现? 6 | 7 | 若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions) 8 | 自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/) , 9 | 可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bugs_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 问题提交 3 | about: 提交并描述问题,帮助我们对其进行检查与修复。 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **问题简述** 11 | 12 | 13 | **问题来源** 14 | 20 | 21 | **预期结果**(可选) 22 | 23 | 24 | **问题截图/问题报错** 25 | 26 | 27 | **操作环境** 28 | 29 | 30 | 31 | **其他补充** 32 | 33 | -------------------------------------------------------------------------------- /src/test/java/MoneyTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.RoundingMode; 5 | 6 | public class MoneyTest { 7 | 8 | @Test 9 | public void test() { 10 | System.out.println(get(1.2, 100)); 11 | System.out.println(get(0.55, 10)); 12 | System.out.println(get(0.21, 5)); 13 | } 14 | 15 | 16 | public double get(double price, int amount) { 17 | BigDecimal money = BigDecimal.valueOf(price * amount).setScale(2, RoundingMode.DOWN); 18 | return money.doubleValue(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/ReleasesTest.java: -------------------------------------------------------------------------------- 1 | import cc.carm.lib.githubreleases4j.GithubRelease; 2 | import cc.carm.lib.githubreleases4j.GithubReleases4J; 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | public class ReleasesTest { 8 | 9 | @Test 10 | public void onTest() { 11 | 12 | List releases = GithubReleases4J.listReleases("CarmJos", "UltraDepository"); 13 | 14 | for (GithubRelease release : releases) { 15 | System.out.println("#" + release.getID() + " (:" + release.getTagName() + ")" + " " + release.getName()); 16 | System.out.println("- " + release.getHTMLUrl()); 17 | } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/PLUGIN_INFO: -------------------------------------------------------------------------------- 1 | &6 _ _ _ _ &e _____ _ _ 2 | &6| | | | | | &e| __ \ (_) | 3 | &6| | | | | |_ _ __ __ _&e| | | | ___ _ __ ___ ___ _| |_ ___ _ __ _ _ 4 | &6| | | | | __| '__/ _` &e| | | |/ _ \ '_ \ / _ \/ __| | __/ _ \| '__| | | | 5 | &6| |__| | | |_| | | (_| &e| |__| | __/ |_) | (_) \__ \ | || (_) | | | |_| | 6 | &6 \____/|_|\__|_| \__,_&e|_____/ \___| .__/ \___/|___/_|\__\___/|_| \__, | 7 | &6 &e | | __/ | 8 | &6 &e |_| |___/ 9 | &f请访问项目主页查看详细插件介绍 &8/ &fView GitHub to get more information 10 | &8-> &6${project.url} -------------------------------------------------------------------------------- /.examples/depositories/README.md: -------------------------------------------------------------------------------- 1 | # UltraDepository 预设仓库配置 2 | 3 | ## 详细示例 4 | 5 | 您可以 [点击这里](../../src/main/resources/depositories/.example-depository.yml) 查看一份详细的示例。 6 | 7 | ## 使用须知 8 | 9 | 预设配置基于 MineCraft 1.16 实现,理论上支持更高版本使用。 10 | 11 | ## 如何使用? 12 | 13 | 1. 下载并安装 UltraDepository 插件。 14 | 2. 启动服务器,配置 `config.yml` 中的一些基础设置。 15 | 3. 下载你想要的示例仓库配置文件,并放入 `插件配置目录/depositories` 下。 16 | 4. 重启服务器,即可令对应的配置文件生效! 17 | 18 | ## 预设配置截图 19 | 20 | ### 渔夫仓库 ([fishman.yml](../../src/main/resources/depositories/fishman.yml)) 21 | 22 | ![fishman](images/fishman.png) 23 | 24 | ### 矿工仓库 ([miner.yml](../../src/main/resources/depositories/miner.yml)) 25 | 26 | ![miner](images/miner.png) 27 | 28 | ### 农夫仓库 ([farmer.yml](../../src/main/resources/depositories/farmer.yml)) 29 | 30 | ![farmer](images/farmer.png) 31 | 32 | ### 猎人仓库 ([hunter.yml](../../src/main/resources/depositories/hunter.yml)) 33 | 34 | ![hunter](images/hunter.png) -------------------------------------------------------------------------------- /src/test/java/PluginNameTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | public class PluginNameTest { 4 | 5 | 6 | @Test 7 | public void test() { 8 | outputPlugin(); 9 | } 10 | 11 | 12 | public static void outputPlugin() { 13 | log(" _ _ _ _ _____ _ _ "); 14 | log("| | | | | | | __ \\ (_) | "); 15 | log("| | | | | |_ _ __ __ _| | | | ___ _ __ ___ ___ _| |_ ___ _ __ _ _ "); 16 | log("| | | | | __| '__/ _` | | | |/ _ \\ '_ \\ / _ \\/ __| | __/ _ \\| '__| | | |"); 17 | log("| |__| | | |_| | | (_| | |__| | __/ |_) | (_) \\__ \\ | || (_) | | | |_| |"); 18 | log(" \\____/|_|\\__|_| \\__,_|_____/ \\___| .__/ \\___/|___/_|\\__\\___/|_| \\__, |"); 19 | log(" | | __/ |"); 20 | log(" |_| |___/ "); 21 | } 22 | 23 | private static void log(String s) { 24 | System.out.println(s); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/event/UltraDepositoryEvent.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.event; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 5 | import cc.carm.plugin.ultradepository.data.UserData; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.Event; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public abstract class UltraDepositoryEvent extends Event { 11 | 12 | 13 | @NotNull Player player; 14 | @NotNull Depository depository; 15 | 16 | public UltraDepositoryEvent(@NotNull Player player, @NotNull Depository depository) { 17 | this.player = player; 18 | this.depository = depository; 19 | } 20 | 21 | public @NotNull UserData getUserData() { 22 | return UltraDepository.getUserManager().getData(getPlayer()); 23 | } 24 | 25 | public @NotNull Player getPlayer() { 26 | return player; 27 | } 28 | 29 | public @NotNull Depository getDepository() { 30 | return depository; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/util/DateIntUtil.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.util; 2 | 3 | import java.sql.Date; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | 7 | public class DateIntUtil { 8 | 9 | private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyyMMdd"); 10 | 11 | 12 | public static SimpleDateFormat getFormat() { 13 | return FORMAT; 14 | } 15 | 16 | public static int getCurrentDate() { 17 | return getDateInt(new Date(System.currentTimeMillis())); 18 | } 19 | 20 | public static int getDateInt(Date date) { 21 | return Integer.parseInt(getFormat().format(date)); 22 | } 23 | 24 | public static long getDateMillis(int dateInt) { 25 | return getDate(dateInt).getTime(); 26 | } 27 | 28 | public static Date getDate(int dateInt) { 29 | try { 30 | long millis = getFormat().parse(Integer.toString(dateInt)).getTime(); 31 | return new java.sql.Date(millis); 32 | } catch (ParseException | NumberFormatException e) { 33 | return new Date(System.currentTimeMillis()); 34 | } 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/hooker/UpdateChecker.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.hooker; 2 | 3 | import cc.carm.lib.githubreleases4j.GithubReleases4J; 4 | import cc.carm.plugin.ultradepository.Main; 5 | import cc.carm.plugin.ultradepository.UltraDepository; 6 | 7 | public class UpdateChecker { 8 | 9 | public static void checkUpdate(Main plugin) { 10 | plugin.getScheduler().runAsync(() -> { 11 | 12 | Integer behindVersions = GithubReleases4J.getVersionBehind( 13 | "CarmJos", "UltraDepository", 14 | plugin.getDescription().getVersion() 15 | ); 16 | 17 | String downloadURL = GithubReleases4J.getReleasesURL("CarmJos", "UltraDepository"); 18 | 19 | if (behindVersions == null) { 20 | plugin.error("检查更新失败,请您定期查看插件是否更新,避免安全问题。"); 21 | plugin.error("下载地址 " + downloadURL); 22 | } else if (behindVersions == 0) { 23 | plugin.log("检查完成,当前已是最新版本。"); 24 | } else if (behindVersions > 0) { 25 | plugin.log("发现新版本! 目前已落后 " + behindVersions + " 个版本。"); 26 | plugin.log("最新版下载地址 " + downloadURL); 27 | } else { 28 | plugin.error("检查更新失败! 当前版本未知,请您使用原生版本以避免安全问题。"); 29 | plugin.error("最新版下载地址 " + downloadURL); 30 | } 31 | 32 | }); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/UltraDepository.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository; 2 | 3 | import cc.carm.plugin.ultradepository.manager.ConfigManager; 4 | import cc.carm.plugin.ultradepository.manager.DepositoryManager; 5 | import cc.carm.plugin.ultradepository.manager.EconomyManager; 6 | import cc.carm.plugin.ultradepository.manager.UserManager; 7 | import cc.carm.plugin.ultradepository.storage.DataStorage; 8 | 9 | public class UltraDepository { 10 | 11 | public static DataStorage getStorage() { 12 | return Main.getInstance().getStorage(); 13 | } 14 | 15 | public static Main getInstance() { 16 | return Main.getInstance(); 17 | } 18 | 19 | public static UserManager getUserManager() { 20 | return Main.getInstance().getUserManager(); 21 | } 22 | 23 | public static EconomyManager getEconomyManager() { 24 | return Main.getInstance().getEconomyManager(); 25 | } 26 | 27 | public static DepositoryManager getDepositoryManager() { 28 | return Main.getInstance().getDepositoryManager(); 29 | } 30 | 31 | public static ConfigManager getConfigManager() { 32 | return Main.getInstance().getConfigManager(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/event/DepositorySellItemEvent.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.event; 2 | 3 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.HandlerList; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class DepositorySellItemEvent extends UltraDepositoryEvent { 9 | 10 | public static HandlerList handlers = new HandlerList(); 11 | 12 | int beforeAmount; 13 | int afterAmount; 14 | double earnedMoney; 15 | 16 | public DepositorySellItemEvent(@NotNull Player player, @NotNull DepositoryItem depositoryItem, 17 | int beforeAmount, int afterAmount, double earnedMoney) { 18 | super(player, depositoryItem.getDepository()); 19 | this.beforeAmount = beforeAmount; 20 | this.afterAmount = afterAmount; 21 | this.earnedMoney = earnedMoney; 22 | } 23 | 24 | public int getBeforeAmount() { 25 | return beforeAmount; 26 | } 27 | 28 | public int getAfterAmount() { 29 | return afterAmount; 30 | } 31 | 32 | public double getEarnedMoney() { 33 | return earnedMoney; 34 | } 35 | 36 | @NotNull 37 | @Override 38 | public HandlerList getHandlers() { 39 | return handlers; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | main: cc.carm.plugin.ultradepository.Main 2 | name: UltraDepository 3 | 4 | version: ${project.version} 5 | description: ${project.description} 6 | website: ${project.url} 7 | 8 | authors: 9 | - CarmJos 10 | - Zimrs 11 | 12 | api-version: 1.16 13 | 14 | softdepend: 15 | - PlaceholderAPI 16 | - Vault 17 | 18 | libraries: 19 | - cc.carm.lib:easysql-beecp:${easysql.version} 20 | - cc.carm.lib:githubreleases4j:${ghreleases.version} 21 | 22 | commands: 23 | "UltraDepository": 24 | description: "超级仓库的主指令" 25 | permission: "UltraDepository.use" 26 | aliases: 27 | - ud 28 | - depository 29 | - depositories 30 | 31 | permissions: 32 | 33 | "UltraDepository.use": 34 | description: "超级仓库的基本使用权限" 35 | default: true 36 | 37 | "UltraDepository.silent": 38 | description: "超级仓库的安静模式权限,拥有该权限将不再接收到放入背包的提示。" 39 | default: false 40 | 41 | "UltraDepository.auto": 42 | description: "超级仓库的自动收集权限" 43 | default: op 44 | 45 | "UltraDepository.auto.enable": 46 | description: "用于判断是否启用了自动收集功能" 47 | 48 | "UltraDepository.admin": 49 | description: "超级仓库的管理权限" 50 | default: op 51 | 52 | "UltraDepository.Command.Sell": 53 | description: "超级仓库的出售指令权限" 54 | 55 | "UltraDepository.Command.SellAll": 56 | description: "超级仓库的出售全部指令权限" -------------------------------------------------------------------------------- /src/main/resources/depositories/fishman.yml: -------------------------------------------------------------------------------- 1 | name: "&b&l渔夫仓库" 2 | 3 | capacity: 4 | default: 500 # 若为0则默认不可以使用该仓库 5 | permissions: 6 | - "ud.fishman.vip:1000" 7 | 8 | gui: 9 | title: "&b&l渔夫仓库" 10 | lines: 4 11 | items: 12 | "INFO": 13 | material: CHEST 14 | data: 0 15 | slot: 31 16 | name: "&9&l背包信息" 17 | lore: 18 | - " " 19 | - "&f仓库最大容量&b %UltraDepository_capacity_fishman%" 20 | - "&f仓库已用容量&b %UltraDepository_used_fishman%" 21 | - "&f仓库剩余容量&b %UltraDepository_usable_fishman%" 22 | - " " 23 | 24 | items: 25 | "INK_SAC": 26 | slot: 11 27 | price: 0.1 28 | limit: 500 29 | name: "&8&l墨囊" 30 | lore: 31 | - " " 32 | - "&f抓住墨鱼!" 33 | "TROPICAL_FISH": 34 | slot: 12 35 | price: 0.1 36 | limit: 500 37 | name: "&(#EE7942)&l小丑鱼" 38 | lore: 39 | - " " 40 | - "&f不是小丑!" 41 | "SALMON": 42 | slot: 13 43 | price: 0.1 44 | limit: 500 45 | name: "&c&l鲑鱼" 46 | lore: 47 | - " " 48 | - "&f肉质鲜美!" 49 | "COD": 50 | slot: 14 51 | price: 0.1 52 | limit: 500 53 | name: "&f&l鳕鱼" 54 | lore: 55 | - " " 56 | - "&f十分美味!" 57 | "PUFFERFISH": 58 | slot: 15 59 | price: 0.1 60 | limit: 500 61 | name: "&6&L河豚" 62 | lore: 63 | - " " 64 | - "&f小心有毒!" -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Build & Tests 5 | 6 | on: 7 | # 支持手动触发构建 8 | workflow_dispatch: 9 | push: 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v6 18 | - name: "Set up JDK" 19 | uses: actions/setup-java@v5 20 | with: 21 | java-version: '11' 22 | distribution: 'adopt' 23 | cache: maven 24 | server-id: github 25 | server-username: MAVEN_USERNAME 26 | server-password: MAVEN_TOKEN 27 | - name: "Package" 28 | run: mvn -B package --file pom.xml -Dmaven.javadoc.skip=true 29 | env: 30 | MAVEN_USERNAME: ${{ github.repository_owner }} 31 | MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}} 32 | 33 | - name: "Upload artifact" 34 | uses: actions/upload-artifact@v5 35 | with: 36 | name: artifact 37 | path: target 38 | 39 | - name: "Upload assets" 40 | uses: actions/upload-artifact@v5 41 | with: 42 | name: assets 43 | path: asset -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/impl/CustomStorage.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage.impl; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.data.UserData; 5 | import cc.carm.plugin.ultradepository.storage.DataStorage; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.jetbrains.annotations.TestOnly; 9 | 10 | import java.util.UUID; 11 | 12 | public class CustomStorage implements DataStorage { 13 | 14 | @Override 15 | @TestOnly 16 | public boolean initialize() { 17 | UltraDepository.getInstance().error("您选择使用自定义存储,但并没有应用成功。"); 18 | UltraDepository.getInstance().error("请访问 https://github.com/CarmJos/UltraDepository/blob/master/.documentation 获取相关帮助!"); 19 | UltraDepository.getInstance().error("You are using CustomStorage, but not overwrite the methods."); 20 | UltraDepository.getInstance().error("Please view https://github.com/CarmJos/UltraDepository/blob/master/.documentation to get more information."); 21 | return false; 22 | } 23 | 24 | @Override 25 | @TestOnly 26 | public void shutdown() { 27 | 28 | } 29 | 30 | @Override 31 | @TestOnly 32 | public @Nullable UserData loadData(@NotNull UUID uuid) { 33 | return null; 34 | } 35 | 36 | @Override 37 | @TestOnly 38 | public void saveUserData(@NotNull UserData data) { 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/hooker/GHUpdateChecker.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.hooker; 2 | 3 | import cc.carm.lib.githubreleases4j.GithubReleases4J; 4 | 5 | import java.util.logging.Logger; 6 | 7 | 8 | public class GHUpdateChecker { 9 | 10 | private final Logger logger; 11 | private final String owner; 12 | private final String repo; 13 | 14 | public GHUpdateChecker(Logger logger, String owner, String repo) { 15 | this.logger = logger; 16 | this.owner = owner; 17 | this.repo = repo; 18 | } 19 | 20 | public void checkUpdate(String currentVersion) { 21 | Integer behindVersions = GithubReleases4J.getVersionBehind(owner, repo, currentVersion); 22 | String downloadURL = GithubReleases4J.getReleasesURL(owner, repo); 23 | if (behindVersions == null) { 24 | logger.severe("检查更新失败,请您定期查看插件是否更新,避免安全问题。"); 25 | logger.severe("下载地址 " + downloadURL); 26 | } else if (behindVersions == 0) { 27 | logger.info("检查完成,当前已是最新版本。"); 28 | } else if (behindVersions > 0) { 29 | logger.info("发现新版本! 目前已落后 " + behindVersions + " 个版本。"); 30 | logger.info("最新版下载地址 " + downloadURL); 31 | } else { 32 | logger.severe("检查更新失败! 当前版本未知,请您使用原生版本以避免安全问题。"); 33 | logger.severe("最新版下载地址 " + downloadURL); 34 | } 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/event/DepositoryCollectItemEvent.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.event; 2 | 3 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 4 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.Cancellable; 7 | import org.bukkit.event.HandlerList; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class DepositoryCollectItemEvent extends UltraDepositoryEvent implements Cancellable { 11 | 12 | public static HandlerList handlers = new HandlerList(); 13 | 14 | boolean cancelled; 15 | 16 | private final DepositoryItem depositoryItem; 17 | int itemAmount; 18 | 19 | public DepositoryCollectItemEvent(@NotNull Player player, @NotNull Depository depository, 20 | @NotNull DepositoryItem depositoryItem, int itemAmount) { 21 | super(player, depository); 22 | this.depositoryItem = depositoryItem; 23 | this.itemAmount = itemAmount; 24 | } 25 | 26 | 27 | public void setItemAmount(int itemAmount) { 28 | this.itemAmount = itemAmount; 29 | } 30 | 31 | public int getItemAmount() { 32 | return itemAmount; 33 | } 34 | 35 | public DepositoryItem getDepositoryItem() { 36 | return depositoryItem; 37 | } 38 | 39 | @Override 40 | public @NotNull HandlerList getHandlers() { 41 | return handlers; 42 | } 43 | 44 | @Override 45 | public boolean isCancelled() { 46 | return cancelled; 47 | } 48 | 49 | @Override 50 | public void setCancelled(boolean cancel) { 51 | this.cancelled = cancel; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/resources/i18n/en_US/messages.yml: -------------------------------------------------------------------------------- 1 | help: 2 | console: 3 | - '&6&l超级仓库 &f后台指令帮助' 4 | - '&8#&f info &6<玩家> &e[仓库ID] &e[物品ID]' 5 | - '&8-&7 得到玩家的相关物品信息。' 6 | - '&8#&f add &6<玩家> &6<仓库ID> &6<物品ID> &6<数量>' 7 | - '&8-&7 为玩家添加对应仓库中对于物品的数量。' 8 | - '&8#&f remove &6<玩家> &6<仓库ID> &6<物品ID> &e[数量]' 9 | - '&8-&7 为玩家减少对应仓库中对于物品的数量。' 10 | - '&8-&7 若不填写数量,则清空对应仓库的对应物品。' 11 | - '&8#&f sell &6<玩家> &e[仓库ID] &e[物品ID] &e[数量]' 12 | - '&8-&7 为玩家售出相关物品。' 13 | - '&8-&7 若不填写数量,则售出所有对应仓库的对应物品。' 14 | - '&8-&7 若不填写物品,则售出对应仓库内所有物品。' 15 | - '&8-&7 若不填写仓库,则售出所有仓库内所有物品。' 16 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 17 | player: 18 | - '&6&l超级仓库 &f玩家指令帮助' 19 | - '&8#&f open &e[仓库ID]' 20 | - '&8-&7 打开对应仓库的界面。' 21 | - '&8#&f sell &6<仓库ID> &6<物品ID> &6<数量>' 22 | - '&8-&7 售出对应数量的对应物品。' 23 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 24 | - '&8#&f sellAll &e[仓库ID] &e[物品ID]' 25 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 26 | item-sold: 27 | - '&f您出售了 &r%(item)&7x%(amount) &f,共赚取 &6%(money) &f元。' 28 | item-sold-limit: 29 | - '&f该物品今日剩余可出售额度为 &a%(amount)&8/%(limit) &f个。' 30 | item-pickup: 31 | - '&f您拾取了 &r%(item)&7x%(amount) &f,已自动放入到您的仓库中。' 32 | item-takeout: 33 | - '&f您从仓库中拿取了 &r%(item)&7x%(amount) &f放入到您的背包中。' 34 | item-collect: 35 | - '&f您收集了 &r%(item)&7x%(amount) &f,已自动放入到您的 &6%(depository) &f中。' 36 | item-collect-actionbar: '&r%(item)&7x%(amount) &f-> &6%(depository)' 37 | no-space: 38 | - '&f您背包内没有足够的空间取出物品!' 39 | no-economy: 40 | - '&f本服务器暂未启用出售功能。' 41 | no-depository: 42 | - '&f不存在该仓库,请检查仓库ID是否正确。' 43 | no-item: 44 | - '&f仓库中不存在该物品,请检查物品ID是否正确。' 45 | no-enough-item: 46 | - '&f仓库中不存在足够的物品。' 47 | wrong-number: 48 | - '&f数目输入错误,请输入正确的数字!' 49 | -------------------------------------------------------------------------------- /src/main/resources/i18n/ko_KR/messages.yml: -------------------------------------------------------------------------------- 1 | help: 2 | console: 3 | - '&6&l超级仓库 &f后台指令帮助' 4 | - '&8#&f info &6<玩家> &e[仓库ID] &e[物品ID]' 5 | - '&8-&7 得到玩家的相关物品信息。' 6 | - '&8#&f add &6<玩家> &6<仓库ID> &6<物品ID> &6<数量>' 7 | - '&8-&7 为玩家添加对应仓库中对于物品的数量。' 8 | - '&8#&f remove &6<玩家> &6<仓库ID> &6<物品ID> &e[数量]' 9 | - '&8-&7 为玩家减少对应仓库中对于物品的数量。' 10 | - '&8-&7 若不填写数量,则清空对应仓库的对应物品。' 11 | - '&8#&f sell &6<玩家> &e[仓库ID] &e[物品ID] &e[数量]' 12 | - '&8-&7 为玩家售出相关物品。' 13 | - '&8-&7 若不填写数量,则售出所有对应仓库的对应物品。' 14 | - '&8-&7 若不填写物品,则售出对应仓库内所有物品。' 15 | - '&8-&7 若不填写仓库,则售出所有仓库内所有物品。' 16 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 17 | player: 18 | - '&6&l超级仓库 &f玩家指令帮助' 19 | - '&8#&f open &e[仓库ID]' 20 | - '&8-&7 打开对应仓库的界面。' 21 | - '&8#&f sell &6<仓库ID> &6<物品ID> &6<数量>' 22 | - '&8-&7 售出对应数量的对应物品。' 23 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 24 | - '&8#&f sellAll &e[仓库ID] &e[物品ID]' 25 | - '&8-&7 该指令受到玩家每日售出数量的限制。' 26 | item-sold: 27 | - '&f您出售了 &r%(item)&7x%(amount) &f,共赚取 &6%(money) &f元。' 28 | item-sold-limit: 29 | - '&f该物品今日剩余可出售额度为 &a%(amount)&8/%(limit) &f个。' 30 | item-pickup: 31 | - '&f您拾取了 &r%(item)&7x%(amount) &f,已自动放入到您的仓库中。' 32 | item-takeout: 33 | - '&f您从仓库中拿取了 &r%(item)&7x%(amount) &f放入到您的背包中。' 34 | item-collect: 35 | - '&f您收集了 &r%(item)&7x%(amount) &f,已自动放入到您的 &6%(depository) &f中。' 36 | item-collect-actionbar: '&r%(item)&7x%(amount) &f-> &6%(depository)' 37 | no-space: 38 | - '&f您背包内没有足够的空间取出物品!' 39 | no-economy: 40 | - '&f本服务器暂未启用出售功能。' 41 | no-depository: 42 | - '&f不存在该仓库,请检查仓库ID是否正确。' 43 | no-item: 44 | - '&f仓库中不存在该物品,请检查物品ID是否正确。' 45 | no-enough-item: 46 | - '&f仓库中不存在足够的物品。' 47 | wrong-number: 48 | - '&f数目输入错误,请输入正确的数字!' 49 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/data/DepositoryItemData.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.data; 2 | 3 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 4 | 5 | public class DepositoryItemData { 6 | 7 | final DepositoryItem source; 8 | final DepositoryData owner; 9 | 10 | int amount; 11 | int sold; 12 | 13 | public DepositoryItemData(DepositoryItem source, DepositoryData owner, 14 | int amount, int sold) { 15 | this.owner = owner; 16 | this.source = source; 17 | this.amount = amount; 18 | this.sold = sold; 19 | } 20 | 21 | public DepositoryData getOwner() { 22 | return owner; 23 | } 24 | 25 | public DepositoryItem getSource() { 26 | return source; 27 | } 28 | 29 | public int getAmount() { 30 | return amount; 31 | } 32 | 33 | public int getSold() { 34 | return sold; 35 | } 36 | 37 | public void setAmount(int amount) { 38 | this.amount = Math.max(0, amount); 39 | } 40 | 41 | public void setSold(int sold) { 42 | this.sold = Math.max(0, sold); 43 | } 44 | 45 | public int[] applyChanges(int amountChanges, int soldChanges) { 46 | setAmount(getAmount() + amountChanges); 47 | setSold(getSold() + soldChanges); 48 | return new int[]{getAmount(), getSold()}; 49 | } 50 | 51 | public void clearSold() { 52 | this.sold = 0; 53 | } 54 | 55 | public static DepositoryItemData emptyItemData(DepositoryItem source, DepositoryData owner) { 56 | return new DepositoryItemData(source, owner, 0, 0); 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return "UBItemData{" + 62 | "amount=" + amount + 63 | ", sold=" + sold + 64 | '}'; 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/resources/i18n/ko_KR/.example-depository.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "&b&l示例仓库" # 仓库名,用于消息显示 3 | 4 | capacity: # 容量配置 5 | default: 500 # 若为0则默认不可以使用该仓库 6 | permissions: # 特殊权限对应的仓库容量,格式为 "权限:容量 7 | - "UltraDepository.vip:1000" 8 | - "UltraDepository.mvp:1500" 9 | 10 | gui: # GUI额外配置 11 | title: "&b&l示例仓库 &7| 界面" #示例仓库的GUI标题 12 | lines: 4 # GUI的行数,支持 1-6行。 13 | items: 14 | "TEST": 15 | material: CHEST # 物品图标的类型 16 | data: 0 # 物品图标的数据值 17 | slot: 31 # 在GUI中显示的格子 18 | name: "&9&l测试图标" 19 | lore: 20 | # 支持使用变量 21 | - "你好 %player_name% !" 22 | actions: # 物品点击操作 23 | - "[CHAT] Hello!" #以玩家身份发送Hello,支持PlaceholderAPI变量 24 | - "[CHAT] /help" #若内容以"/"开头,则会以玩家身份执行指令,支持PlaceholderAPI变量 25 | - "[CONSOLE] say HELLO WORLD" #以后台身份执行指令,不需要加"/",支持PlaceholderAPI变量 26 | - "[MESSAGE] &(#FFBBBBB)Test %player_name%" # 向玩家发送消息,支持PlaceholderAPI变量和RGB颜色 27 | - "[SOUND] ENTITY_EXPERIENCE_ORB_PICKUP:0.5" # 向玩家发送声音,可以规定音量大小和音调,格式为 <声音>:[音量]:[音调] 28 | - "[CLOSE]" # 为玩家关闭界面 29 | 30 | - "[LEFT:CLOSE]" #限制只有 鼠标左键 才触发CLOSE 31 | - "[SHIFT_LEFT:CLOSE]" #限制只有 按住Shift+鼠标左键 才触发CLOSE 32 | - "[RIGHT:CLOSE]" #限制只有 鼠标右键 才触发CLOSE 33 | - "[SHIFT_RIGHT:CLOSE]" #限制只有 按住Shift+鼠标右键 才触发CLOSE 34 | - "[MIDDLE:CLOSE]" #限制只有 鼠标中键 才触发CLOSE 35 | - "[DROP:CLOSE]" #限制只有 丢弃建 才触发CLOSE 36 | - "[CONTROL_DROP:CLOSE]" #限制只有 按住Ctrl+丢弃键 才触发CLOSE 37 | - "[DOUBLE_CLICK:CLOSE]" #限制只有 鼠标双击物品 才触发CLOSE 38 | - "[NUMBER_KEY:CLOSE]" #限制只有 数字键切换 才触发CLOSE 39 | 40 | items: 41 | "INK_SAC": #物品ID,若需要限制数据ID则可以加“:”,如 "INK_SANK:4" 42 | slot: 11 # 物品在GUI中显示的槽位 43 | price: 0.1 # 物品单价 44 | limit: 500 # 物品每日售出限制 45 | name: "&8&l墨囊" # 物品显示的名字 46 | lore: # 物品的lore 47 | - " " 48 | - "&f抓住墨鱼!" -------------------------------------------------------------------------------- /src/main/resources/depositories/.example-depository.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "&b&l示例仓库" # 仓库名,用于消息显示 3 | 4 | capacity: # 容量配置 5 | default: 500 # 若为0则默认不可以使用该仓库 6 | permissions: # 特殊权限对应的仓库容量,格式为 "权限:容量 7 | - "UltraDepository.vip:1000" 8 | - "UltraDepository.mvp:1500" 9 | 10 | gui: # GUI额外配置 11 | title: "&b&l示例仓库 &7| 界面" #示例仓库的GUI标题 12 | lines: 4 # GUI的行数,支持 1-6行。 13 | items: 14 | "TEST": 15 | material: CHEST # 物品图标的类型 16 | data: 0 # 物品图标的数据值 17 | slot: 31 # 在GUI中显示的格子 18 | name: "&9&l测试图标" 19 | lore: 20 | # 支持使用变量 21 | - "你好 %player_name% !" 22 | actions: # 物品点击操作 23 | - "[CHAT] Hello!" #以玩家身份发送Hello,支持PlaceholderAPI变量 24 | - "[CHAT] /help" #若内容以"/"开头,则会以玩家身份执行指令,支持PlaceholderAPI变量 25 | - "[CONSOLE] say HELLO WORLD" #以后台身份执行指令,不需要加"/",支持PlaceholderAPI变量 26 | - "[MESSAGE] &(#FFBBBBB)Test %player_name%" # 向玩家发送消息,支持PlaceholderAPI变量和RGB颜色 27 | - "[SOUND] ENTITY_EXPERIENCE_ORB_PICKUP:0.5" # 向玩家发送声音,可以规定音量大小和音调,格式为 <声音>:[音量]:[音调] 28 | - "[CLOSE]" # 为玩家关闭界面 29 | 30 | - "[LEFT:CLOSE]" #限制只有 鼠标左键 才触发CLOSE 31 | - "[SHIFT_LEFT:CLOSE]" #限制只有 按住Shift+鼠标左键 才触发CLOSE 32 | - "[RIGHT:CLOSE]" #限制只有 鼠标右键 才触发CLOSE 33 | - "[SHIFT_RIGHT:CLOSE]" #限制只有 按住Shift+鼠标右键 才触发CLOSE 34 | - "[MIDDLE:CLOSE]" #限制只有 鼠标中键 才触发CLOSE 35 | - "[DROP:CLOSE]" #限制只有 丢弃建 才触发CLOSE 36 | - "[CONTROL_DROP:CLOSE]" #限制只有 按住Ctrl+丢弃键 才触发CLOSE 37 | - "[DOUBLE_CLICK:CLOSE]" #限制只有 鼠标双击物品 才触发CLOSE 38 | - "[NUMBER_KEY:CLOSE]" #限制只有 数字键切换 才触发CLOSE 39 | 40 | items: 41 | "INK_SAC": #物品ID,若需要限制数据ID则可以加“:”,如 "INK_SANK:4" 42 | slot: 11 # 物品在GUI中显示的槽位 43 | price: 0.1 # 物品单价 44 | limit: 500 # 物品每日售出限制 45 | name: "&8&l墨囊" # 物品显示的名字 46 | lore: # 物品的lore 47 | - " " 48 | - "&f抓住墨鱼!" -------------------------------------------------------------------------------- /src/main/resources/i18n/en_US/.example-depository.yml: -------------------------------------------------------------------------------- 1 | name: "&b&lExample Depository" # 仓库名,用于消息显示 2 | 3 | capacity: # 容量配置 4 | default: 500 # 若为0则默认不可以使用该仓库 5 | permissions: # 特殊权限对应的仓库容量,格式为 "权限:容量 6 | - "UltraDepository.vip:1000" 7 | - "UltraDepository.mvp:1500" 8 | 9 | gui: # GUI额外配置 10 | title: "&b&l示例仓库 &7| 界面" #示例仓库的GUI标题 11 | lines: 4 # GUI的行数,支持 1-6行。 12 | items: 13 | "TEST": 14 | material: CHEST # 物品图标的类型 15 | data: 0 # 物品图标的数据值 16 | slot: 31 # 在GUI中显示的格子 17 | name: "&9&l测试图标" 18 | lore: 19 | # 支持使用变量 20 | - "你好 %player_name% !" 21 | actions: # 物品点击操作 22 | - "[CHAT] Hello!" #以玩家身份发送Hello,支持PlaceholderAPI变量 23 | - "[CHAT] /help" #若内容以"/"开头,则会以玩家身份执行指令,支持PlaceholderAPI变量 24 | - "[CONSOLE] say HELLO WORLD" #以后台身份执行指令,不需要加"/",支持PlaceholderAPI变量 25 | - "[MESSAGE] &(#FFBBBBB)Test %player_name%" # 向玩家发送消息,支持PlaceholderAPI变量和RGB颜色 26 | - "[SOUND] ENTITY_EXPERIENCE_ORB_PICKUP:0.5" # 向玩家发送声音,可以规定音量大小和音调,格式为 <声音>:[音量]:[音调] 27 | - "[CLOSE]" # 为玩家关闭界面 28 | 29 | - "[LEFT:CLOSE]" #限制只有 鼠标左键 才触发CLOSE 30 | - "[SHIFT_LEFT:CLOSE]" #限制只有 按住Shift+鼠标左键 才触发CLOSE 31 | - "[RIGHT:CLOSE]" #限制只有 鼠标右键 才触发CLOSE 32 | - "[SHIFT_RIGHT:CLOSE]" #限制只有 按住Shift+鼠标右键 才触发CLOSE 33 | - "[MIDDLE:CLOSE]" #限制只有 鼠标中键 才触发CLOSE 34 | - "[DROP:CLOSE]" #限制只有 丢弃建 才触发CLOSE 35 | - "[CONTROL_DROP:CLOSE]" #限制只有 按住Ctrl+丢弃键 才触发CLOSE 36 | - "[DOUBLE_CLICK:CLOSE]" #限制只有 鼠标双击物品 才触发CLOSE 37 | - "[NUMBER_KEY:CLOSE]" #限制只有 数字键切换 才触发CLOSE 38 | 39 | items: 40 | "INK_SAC": #物品ID,若需要限制数据ID则可以加“:”,如 "INK_SANK:4" 41 | slot: 11 # 物品在GUI中显示的槽位 42 | price: 0.1 # 物品单价 43 | limit: 500 # 物品每日售出限制 44 | name: "&8&l墨囊" # 物品显示的名字 45 | lore: # 物品的lore 46 | - " " 47 | - "&f抓住墨鱼!" -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/DataStorage.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage; 2 | 3 | import cc.carm.plugin.ultradepository.data.UserData; 4 | import cc.carm.plugin.ultradepository.manager.UserManager; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.UUID; 9 | 10 | public interface DataStorage { 11 | 12 | /** 13 | * 在插件加载存储源时执行。 14 | * 15 | * @return 是否初始化成功 16 | */ 17 | boolean initialize(); 18 | 19 | /** 20 | * 在插件被卸载时执行。 21 | */ 22 | void shutdown(); 23 | 24 | /** 25 | * 用于加载用户数据的方法。该方法将会被异步运行! 26 | *
该方法一般无需自行执行,见 {@link UserManager#loadData(UUID)} 27 | *
28 | *
若不存在该用户的数据,请返回 null 。 29 | *
若加载出现任何错误,请抛出异常。 30 | * 31 | * @param uuid 用户UUID 32 | * @throws Exception 当出现任何错误时抛出 33 | */ 34 | @Nullable 35 | UserData loadData(@NotNull UUID uuid) throws Exception; 36 | 37 | /** 38 | * 用于保存用户数据的方法。 该方法将会被异步运行! 39 | *
该方法一般无需自行执行,见 {@link UserManager#saveData(UserData)} 40 | * 41 | * @param data 用户数据 42 | * @throws Exception 当出现任何错误时抛出 43 | */ 44 | void saveUserData(@NotNull UserData data) throws Exception; 45 | 46 | /** 47 | * Support old data which is forced to use data value. 48 | * 老数据强制给typeID加上了data值(包括0),然而1.13后每个物品都有对应的Material。 49 | * 自插件v1.1.6版本开始不再强制,但需要为此额外做出支持,避免玩家数据丢失。 50 | * 51 | * @param typeID ID源数据 52 | * @return 正确ID数据 53 | * @since v1.1.6 54 | */ 55 | static String getFixedTypeID(String typeID) { 56 | String trueID = typeID; 57 | if (typeID.contains(":")) { 58 | try { 59 | String[] args = trueID.split(":"); 60 | if (Integer.parseInt(args[1]) == 0) { 61 | 62 | trueID = args[0]; 63 | } 64 | } catch (Exception ignore) { 65 | } 66 | } 67 | return trueID; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/configuration/depository/DepositoryCapacity.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.configuration.depository; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class DepositoryCapacity { 11 | 12 | int defaultCapacity; 13 | Map permissions; 14 | 15 | public DepositoryCapacity(int defaultCapacity, List permissionStrings) { 16 | this.defaultCapacity = defaultCapacity; 17 | Map permissions = new HashMap<>(); 18 | permissionStrings.stream() 19 | .filter(s -> s.contains(":")) 20 | .map(s -> s.split(":", 1)) 21 | .forEach(args -> { 22 | try { 23 | permissions.put(args[0], Integer.parseInt(args[1])); 24 | } catch (Exception ignored) { 25 | } 26 | }); 27 | this.permissions = permissions; 28 | } 29 | 30 | 31 | public DepositoryCapacity(int defaultCapacity, Map permissions) { 32 | this.defaultCapacity = defaultCapacity; 33 | this.permissions = permissions; 34 | } 35 | 36 | public int getDefault() { 37 | return defaultCapacity; 38 | } 39 | 40 | public @NotNull Map getPermissions() { 41 | return permissions; 42 | } 43 | 44 | public int getPlayerCapacity(Player player) { 45 | if (defaultCapacity == -1) return -1; 46 | 47 | int capacity = defaultCapacity; 48 | for (Map.Entry entry : getPermissions().entrySet()) { 49 | if (player.hasPermission(entry.getKey())) { 50 | int value = entry.getValue(); 51 | if (value == -1) { 52 | return -1; 53 | } else if (value > capacity) { 54 | capacity = value; 55 | } 56 | } 57 | } 58 | 59 | return capacity; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/hooker/VaultHooker.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.hooker; 2 | 3 | import net.milkbowl.vault.economy.Economy; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.OfflinePlayer; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.plugin.RegisteredServiceProvider; 8 | 9 | public class VaultHooker { 10 | 11 | private Economy econ = null; 12 | 13 | public static boolean hasVault() { 14 | return Bukkit.getServer().getPluginManager().getPlugin("Vault") != null; 15 | } 16 | 17 | public boolean setupEconomy() { 18 | 19 | if (!hasVault()) return false; 20 | RegisteredServiceProvider rsp = Bukkit.getServer().getServicesManager().getRegistration(Economy.class); 21 | if (rsp == null) return false; 22 | 23 | this.econ = rsp.getProvider(); 24 | return true; 25 | } 26 | 27 | public Economy getEconomy() { 28 | return econ; 29 | } 30 | 31 | public double getMoney(Player player) { 32 | if (player != null) { 33 | try { 34 | return getEconomy().getBalance(player); 35 | } catch (NullPointerException ignore) { 36 | } 37 | } 38 | return 0L; 39 | } 40 | 41 | public double getMoney(OfflinePlayer player) { 42 | if (player != null) { 43 | try { 44 | return getEconomy().getBalance(player); 45 | } catch (NullPointerException ignore) { 46 | } 47 | } 48 | return 0L; 49 | } 50 | 51 | public void removeMoney(Player player, double amount) { 52 | getEconomy().withdrawPlayer(player, amount); 53 | } 54 | 55 | public void removeMoney(OfflinePlayer player, double amount) { 56 | getEconomy().withdrawPlayer(player, amount); 57 | } 58 | 59 | public void addMoney(Player player, double amount) { 60 | getEconomy().depositPlayer(player, amount); 61 | } 62 | 63 | public void addMoney(OfflinePlayer player, double amount) { 64 | getEconomy().depositPlayer(player, amount); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/data/DepositoryData.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.data; 2 | 3 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 4 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class DepositoryData { 12 | 13 | final UserData owner; 14 | final Depository source; 15 | private final Map<@NotNull String, @NotNull DepositoryItemData> contents; 16 | 17 | public DepositoryData(@NotNull Depository source, @NotNull UserData owner, 18 | Map<@NotNull String, @NotNull DepositoryItemData> contents) { 19 | this.owner = owner; 20 | this.source = source; 21 | this.contents = contents; 22 | } 23 | 24 | public String getIdentifier() { 25 | return getSource().getIdentifier(); 26 | } 27 | 28 | public Depository getSource() { 29 | return source; 30 | } 31 | 32 | public @NotNull Map getContents() { 33 | return this.contents; 34 | } 35 | 36 | public @Nullable DepositoryItemData getItemData(@NotNull String itemType) { 37 | DepositoryItem item = getSource().getItems().get(itemType); 38 | if (item != null) { 39 | return getItemData(item); 40 | } else { 41 | return null; 42 | } 43 | } 44 | 45 | public @NotNull DepositoryItemData getItemData(@NotNull DepositoryItem item) { 46 | getContents().putIfAbsent(item.getTypeID(), DepositoryItemData.emptyItemData(item, this)); 47 | return getContents().get(item.getTypeID()); 48 | } 49 | 50 | public int getUsedCapacity() { 51 | return getContents().values().stream().mapToInt(DepositoryItemData::getAmount).sum(); 52 | } 53 | 54 | public static DepositoryData emptyContents(Depository source, UserData owner) { 55 | return new DepositoryData(source, owner, new HashMap<>()); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/listener/UserListener.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.listener; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 5 | import cc.carm.plugin.ultradepository.data.UserData; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.AsyncPlayerPreLoginEvent; 11 | import org.bukkit.event.player.PlayerLoginEvent; 12 | import org.bukkit.event.player.PlayerQuitEvent; 13 | 14 | import java.util.UUID; 15 | 16 | public class UserListener implements Listener { 17 | 18 | @EventHandler(priority = EventPriority.HIGH) 19 | public void onPreLogin(AsyncPlayerPreLoginEvent event) { 20 | if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { 21 | return; 22 | } 23 | UltraDepository.getUserManager().loadDataCache(event.getUniqueId()); 24 | } 25 | 26 | @EventHandler(priority = EventPriority.MONITOR) 27 | public void onPreLoginMonitor(AsyncPlayerPreLoginEvent event) { 28 | if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { 29 | UltraDepository.getUserManager().removeDataCache(event.getUniqueId()); 30 | } 31 | } 32 | 33 | @EventHandler(priority = EventPriority.LOWEST) 34 | public void onPlayerLogin(PlayerLoginEvent e) { 35 | UserData data = UltraDepository.getUserManager().getData(e.getPlayer().getUniqueId()); 36 | if (data == null) { 37 | e.setResult(PlayerLoginEvent.Result.KICK_OTHER); 38 | e.setKickMessage(PluginMessages.LOAD_FAILED.get(e.getPlayer())); 39 | } 40 | } 41 | 42 | @EventHandler(priority = EventPriority.MONITOR) 43 | public void onQuit(PlayerQuitEvent event) { 44 | Player player = event.getPlayer(); 45 | UUID playerUUID = player.getUniqueId(); 46 | UltraDepository.getInstance().getScheduler() 47 | .runAsync(() -> UltraDepository.getUserManager().unloadData(playerUUID, true)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/StorageMethod.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage; 2 | 3 | import cc.carm.plugin.ultradepository.storage.impl.CustomStorage; 4 | import cc.carm.plugin.ultradepository.storage.impl.JSONStorage; 5 | import cc.carm.plugin.ultradepository.storage.impl.MySQLStorage; 6 | import cc.carm.plugin.ultradepository.storage.impl.YAMLStorage; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.Arrays; 11 | import java.util.Optional; 12 | import java.util.function.Supplier; 13 | 14 | public enum StorageMethod { 15 | 16 | CUSTOM(0, CustomStorage::new), 17 | YAML(1, YAMLStorage::new), 18 | JSON(2, JSONStorage::new), 19 | MYSQL(3, MySQLStorage::new); 20 | 21 | private final int id; 22 | private @NotNull Supplier<@NotNull DataStorage> storageSupplier; 23 | 24 | StorageMethod(int id, @NotNull Supplier<@NotNull DataStorage> storageSupplier) { 25 | this.id = id; 26 | this.storageSupplier = storageSupplier; 27 | } 28 | 29 | public int getID() { 30 | return id; 31 | } 32 | 33 | public @NotNull Supplier<@NotNull DataStorage> getStorageSupplier() { 34 | return storageSupplier; 35 | } 36 | 37 | public void setStorageSupplier(@NotNull Supplier<@NotNull DataStorage> storageSupplier) { 38 | this.storageSupplier = storageSupplier; 39 | } 40 | 41 | public @NotNull DataStorage createStorage() { 42 | return getStorageSupplier().get(); 43 | } 44 | 45 | public static @NotNull StorageMethod read(String s) { 46 | StorageMethod byName = readByName(s); 47 | if (byName != null) return byName; 48 | try { 49 | return Optional.ofNullable(readByID(Integer.parseInt(s))).orElse(YAML); 50 | } catch (Exception ex) { 51 | return YAML; 52 | } 53 | } 54 | 55 | 56 | public static @Nullable StorageMethod readByName(String name) { 57 | return Arrays.stream(values()).filter(value -> value.name().equalsIgnoreCase(name)).findFirst().orElse(null); 58 | } 59 | 60 | 61 | public static @Nullable StorageMethod readByID(int id) { 62 | return Arrays.stream(values()).filter(value -> value.getID() == id).findFirst().orElse(null); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.documentation/README.md: -------------------------------------------------------------------------------- 1 | ```text 2 | _ _ _ _ _____ _ _ 3 | | | | | | | | __ \ (_) | 4 | | | | | | |_ _ __ __ _| | | | ___ _ __ ___ ___ _| |_ ___ _ __ _ _ 5 | | | | | | __| '__/ _` | | | |/ _ \ '_ \ / _ \/ __| | __/ _ \| '__| | | | 6 | | |__| | | |_| | | (_| | |__| | __/ |_) | (_) \__ \ | || (_) | | | |_| | 7 | \____/|_|\__|_| \__,_|_____/ \___| .__/ \___/|___/_|\__\___/|_| \__, | 8 | | | __/ | 9 | |_| |___/ 10 | ``` 11 | 12 | # UltraDepository 帮助介绍文档 13 | 14 | ## 插件介绍目录 15 | 16 | - 使用示例 17 | - [仓库配置文件预设示例](../.examples/depositories) 18 | - [用户数据示例](../.examples/userdata) 19 | - [YAML格式](../.examples/userdata/uuid.yml) 20 | - [JSON格式](../.examples/userdata/uuid.json) 21 | - [MySQL格式](../.examples/userdata/database.sql) 22 | - [开发](develop) 23 | - [自定义存储源](develop/use-custome-storage.md) 24 | 25 | 26 | ## [开发文档](JAVADOC-README.md) 27 | 28 | 基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/UltraDepository) 。 29 | 30 | ## 依赖方式 31 | 32 | ### Maven 依赖 33 | 34 | ```xml 35 | 36 | 37 | 38 | 39 | 40 | 41 | UltraDepository 42 | GitHub Packages 43 | https://maven.pkg.github.com/CarmJos/UltraDepository 44 | 45 | 46 | 47 | 48 | carm-repo 49 | Carm's Repo 50 | https://repo.carm.cc/repository/maven-public/ 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | cc.carm.plugin 59 | ultradepository 60 | [LATEST RELEASE] 61 | provided 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | ### Gradle 依赖 69 | 70 | ```groovy 71 | repositories { 72 | // 采用github依赖库,安全稳定,但需要配置 (推荐) 73 | maven { url 'https://maven.pkg.github.com/CarmJos/EasyPlugin' } 74 | 75 | // 采用我的私人依赖库,简单方便,但可能因为变故而无法使用 76 | maven { url 'https://repo.carm.cc/repository/maven-public/' } 77 | } 78 | 79 | dependencies { 80 | compileOnly "cc.carm.plugin:ultradepository:[LATEST RELEASE]" 81 | } 82 | ``` -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL Analysis" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '45 12 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'java' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v6 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v4 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v4 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v4 71 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/util/DatabaseTable.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.util; 2 | 3 | import cc.carm.lib.easysql.api.SQLManager; 4 | import cc.carm.lib.easysql.api.action.PreparedSQLUpdateAction; 5 | import cc.carm.lib.easysql.api.action.PreparedSQLUpdateBatchAction; 6 | import cc.carm.lib.easysql.api.builder.*; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.sql.SQLException; 11 | 12 | public class DatabaseTable { 13 | 14 | private final @NotNull String tableName; 15 | private final @NotNull String[] columns; 16 | 17 | @Nullable String tableSettings; 18 | 19 | public DatabaseTable(@NotNull String tableName, @NotNull String[] columns) { 20 | this(tableName, columns, null); 21 | } 22 | 23 | public DatabaseTable(@NotNull String tableName, @NotNull String[] columns, 24 | @Nullable String tableSettings) { 25 | this.tableName = tableName; 26 | this.columns = columns; 27 | this.tableSettings = tableSettings; 28 | } 29 | 30 | public @NotNull String getTableName() { 31 | return tableName; 32 | } 33 | 34 | public @NotNull String[] getColumns() { 35 | return columns; 36 | } 37 | 38 | public @Nullable String getTableSettings() { 39 | return tableSettings; 40 | } 41 | 42 | public int createTable(SQLManager sqlManager) throws SQLException { 43 | TableCreateBuilder createAction = sqlManager.createTable(getTableName()); 44 | createAction.setColumns(getColumns()); 45 | if (getTableSettings() != null) createAction.setTableSettings(getTableSettings()); 46 | return createAction.build().execute(); 47 | } 48 | 49 | public TableQueryBuilder createQuery(SQLManager sqlManager) { 50 | return sqlManager.createQuery().inTable(getTableName()); 51 | } 52 | 53 | public DeleteBuilder createDelete(SQLManager sqlManager) { 54 | return sqlManager.createDelete(getTableName()); 55 | } 56 | 57 | public UpdateBuilder createUpdate(SQLManager sqlManager) { 58 | return sqlManager.createUpdate(getTableName()); 59 | } 60 | 61 | public InsertBuilder createInsert(SQLManager sqlManager) { 62 | return sqlManager.createInsert(getTableName()); 63 | } 64 | 65 | public InsertBuilder createInsertBatch(SQLManager sqlManager) { 66 | return sqlManager.createInsertBatch(getTableName()); 67 | } 68 | 69 | public ReplaceBuilder createReplace(SQLManager sqlManager) { 70 | return sqlManager.createReplace(getTableName()); 71 | } 72 | 73 | public ReplaceBuilder createReplaceBatch(SQLManager sqlManager) { 74 | return sqlManager.createReplaceBatch(getTableName()); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/util/JarUtil.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.util; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipInputStream; 11 | 12 | @SuppressWarnings("ResultOfMethodCallIgnored") 13 | public class JarUtil { 14 | public static final char JAR_SEPARATOR = '/'; 15 | 16 | public static void copyFolderFromJar(String folderName, File destFolder, CopyOption option) 17 | throws IOException { 18 | copyFolderFromJar(folderName, destFolder, option, null); 19 | } 20 | 21 | public static void copyFolderFromJar(String folderName, File destFolder, 22 | CopyOption option, PathTrimmer trimmer) throws IOException { 23 | if (!destFolder.exists()) 24 | destFolder.mkdirs(); 25 | 26 | byte[] buffer = new byte[1024]; 27 | 28 | File fullPath; 29 | String path = JarUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 30 | if (trimmer != null) 31 | path = trimmer.trim(path); 32 | try { 33 | if (!path.startsWith("file")) 34 | path = "file://" + path; 35 | 36 | fullPath = new File(new URI(path)); 37 | } catch (URISyntaxException e) { 38 | e.printStackTrace(); 39 | return; 40 | } 41 | 42 | ZipInputStream zis = new ZipInputStream(new FileInputStream(fullPath)); 43 | 44 | ZipEntry entry; 45 | while ((entry = zis.getNextEntry()) != null) { 46 | if (!entry.getName().startsWith(folderName + JAR_SEPARATOR)) 47 | continue; 48 | 49 | String fileName = entry.getName(); 50 | 51 | if (fileName.charAt(fileName.length() - 1) == JAR_SEPARATOR) { 52 | File file = new File(destFolder + File.separator + fileName); 53 | if (file.isFile()) { 54 | file.delete(); 55 | } 56 | file.mkdirs(); 57 | continue; 58 | } 59 | 60 | File file = new File(destFolder + File.separator + fileName); 61 | if (option == CopyOption.COPY_IF_NOT_EXIST && file.exists()) 62 | continue; 63 | 64 | if (!file.getParentFile().exists()) 65 | file.getParentFile().mkdirs(); 66 | 67 | if (!file.exists()) 68 | file.createNewFile(); 69 | FileOutputStream fos = new FileOutputStream(file); 70 | 71 | int len; 72 | while ((len = zis.read(buffer)) > 0) { 73 | fos.write(buffer, 0, len); 74 | } 75 | fos.close(); 76 | } 77 | 78 | zis.closeEntry(); 79 | zis.close(); 80 | } 81 | 82 | public enum CopyOption { 83 | COPY_IF_NOT_EXIST, REPLACE_IF_EXIST 84 | } 85 | 86 | @FunctionalInterface 87 | public interface PathTrimmer { 88 | String trim(String original); 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/manager/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.manager; 2 | 3 | 4 | import cc.carm.lib.easyplugin.configuration.file.FileConfig; 5 | import cc.carm.lib.easyplugin.configuration.language.MessagesConfig; 6 | import cc.carm.lib.easyplugin.configuration.language.MessagesInitializer; 7 | import cc.carm.plugin.ultradepository.UltraDepository; 8 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 9 | import cc.carm.plugin.ultradepository.util.JarUtil; 10 | import org.bukkit.plugin.java.JavaPlugin; 11 | 12 | import java.io.File; 13 | 14 | public class ConfigManager { 15 | 16 | private final JavaPlugin plugin; 17 | private FileConfig pluginConfiguration; 18 | private MessagesConfig messageConfiguration; 19 | 20 | public ConfigManager(JavaPlugin plugin) { 21 | this.plugin = plugin; 22 | } 23 | 24 | public boolean initialize() { 25 | 26 | try { 27 | File configFile = new File(plugin.getDataFolder(), "config.yml"); 28 | if (!configFile.exists()) { 29 | //没找到配置文件,可能是第一次加载此插件 30 | //把一些英文版的东西复制出来,方便英文用户使用。 31 | UltraDepository.getInstance().log(" 未找到配置文件,生成默认配置..."); 32 | JarUtil.copyFolderFromJar( 33 | "i18n", plugin.getDataFolder(), 34 | JarUtil.CopyOption.COPY_IF_NOT_EXIST 35 | ); 36 | 37 | } 38 | 39 | pluginConfiguration = new FileConfig(plugin, "config.yml"); 40 | messageConfiguration = new MessagesConfig(plugin, "messages.yml"); 41 | 42 | FileConfig.pluginConfiguration = () -> pluginConfiguration; 43 | FileConfig.messageConfiguration = () -> messageConfiguration; 44 | 45 | MessagesInitializer.initialize(messageConfiguration, PluginMessages.class); 46 | 47 | return true; 48 | } catch (Exception ex) { 49 | ex.printStackTrace(); 50 | return false; 51 | } 52 | } 53 | 54 | public FileConfig getPluginConfig() { 55 | return pluginConfiguration; 56 | } 57 | 58 | public MessagesConfig getMessageConfig() { 59 | return messageConfiguration; 60 | } 61 | 62 | public void reload() { 63 | try { 64 | getPluginConfig().reload(); 65 | getMessageConfig().reload(); 66 | } catch (Exception ex) { 67 | ex.printStackTrace(); 68 | } 69 | } 70 | 71 | public void saveConfig() { 72 | try { 73 | getPluginConfig().save(); 74 | getMessageConfig().save(); 75 | } catch (Exception ex) { 76 | ex.printStackTrace(); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/resources/depositories/hunter.yml: -------------------------------------------------------------------------------- 1 | name: "&5&l猎人仓库" 2 | 3 | capacity: 4 | default: 500 # 若为0则默认不可以使用该仓库 5 | permissions: 6 | - "ud.hunter.vip:1000" 7 | 8 | gui: 9 | title: "&5&l猎人仓库" 10 | lines: 6 11 | items: 12 | "INFO": 13 | material: CHEST 14 | data: 0 15 | slot: 49 16 | name: "&5&l背包信息" 17 | lore: 18 | - " " 19 | - "&f仓库最大容量&d %UltraDepository_capacity_hunter%" 20 | - "&f仓库已用容量&d %UltraDepository_used_hunter%" 21 | - "&f仓库剩余容量&d %UltraDepository_usable_hunter%" 22 | - " " 23 | 24 | 25 | items: 26 | "CHICKEN": 27 | slot: 10 28 | price: 0.1 29 | limit: 500 30 | name: "&6&l鸡肉" 31 | "PORKCHOP": 32 | slot: 11 33 | price: 0.1 34 | limit: 500 35 | name: "&c&l猪肉" 36 | "BEEF": 37 | slot: 12 38 | price: 0.1 39 | limit: 500 40 | name: "&c&l牛肉" 41 | "MUTTON": 42 | slot: 13 43 | price: 0.1 44 | limit: 500 45 | name: "&6&l羊肉" 46 | "RABBIT": 47 | slot: 14 48 | price: 0.1 49 | limit: 500 50 | name: "&f&l兔肉" 51 | "RABBIT_HIDE": 52 | slot: 15 53 | price: 0.1 54 | limit: 500 55 | name: "&f&l兔子皮" 56 | "LEATHER": 57 | slot: 16 58 | price: 0.1 59 | limit: 500 60 | name: "&c&l皮革" 61 | "EGG": 62 | slot: 19 63 | price: 0.1 64 | limit: 500 65 | name: "&f&l鸡蛋" 66 | "RABBIT_FOOT": 67 | slot: 20 68 | price: 0.1 69 | limit: 500 70 | name: "&f&l兔子脚" 71 | "FEATHER": 72 | slot: 21 73 | price: 0.1 74 | limit: 500 75 | name: "&f&l羽毛" 76 | "GOLD_NUGGET": 77 | slot: 22 78 | price: 0.1 79 | limit: 500 80 | name: "&e&l金粒" 81 | "GHAST_TEAR": 82 | slot: 23 83 | price: 0.1 84 | limit: 500 85 | name: "&7&l恶魂之泪" 86 | "BLAZE_ROD": 87 | slot: 24 88 | price: 0.1 89 | limit: 500 90 | name: "&e&l烈焰棒" 91 | "MAGMA_CREAM": 92 | slot: 25 93 | price: 0.1 94 | limit: 500 95 | name: "&c&l岩浆球" 96 | "SLIME_BALL": 97 | slot: 28 98 | price: 0.1 99 | limit: 500 100 | name: "&a&l粘液球" 101 | "BONE": 102 | slot: 29 103 | price: 0.1 104 | limit: 500 105 | name: "&f&l骨头" 106 | "SPIDER_EYE": 107 | slot: 30 108 | price: 0.1 109 | limit: 500 110 | name: "&4&l蜘蛛眼" 111 | "GUNPOWDER": 112 | slot: 31 113 | price: 0.1 114 | limit: 500 115 | name: "&7&l火药" 116 | "ROTTEN_FLESH": 117 | slot: 32 118 | price: 0.1 119 | limit: 500 120 | name: "&2&l腐肉" 121 | "STRING": 122 | slot: 33 123 | price: 0.1 124 | limit: 500 125 | name: "&f&l线" 126 | "ENDER_PEARL": 127 | slot: 34 128 | price: 0.1 129 | limit: 500 130 | name: "&5&l末影珍珠" -------------------------------------------------------------------------------- /src/main/resources/depositories/farmer.yml: -------------------------------------------------------------------------------- 1 | name: "&a&l农夫仓库" 2 | 3 | capacity: 4 | default: 500 # 若为0则默认不可以使用该仓库 5 | permissions: 6 | - "ud.farmer.vip:1000" 7 | 8 | gui: 9 | title: "&a&l农夫仓库" 10 | lines: 6 11 | items: 12 | "INFO": 13 | material: CHEST 14 | data: 0 15 | slot: 49 16 | name: "&2&l背包信息" 17 | lore: 18 | - " " 19 | - "&f仓库最大容量&a %UltraDepository_capacity_farmer%" 20 | - "&f仓库已用容量&a %UltraDepository_used_farmer%" 21 | - "&f仓库剩余容量&a %UltraDepository_usable_farmer%" 22 | - " " 23 | 24 | items: 25 | "WHEAT_SEEDS": 26 | slot: 10 27 | price: 0.1 28 | limit: 500 29 | name: "&a&l小麦种子" 30 | "MELON_SEEDS": 31 | slot: 11 32 | price: 0.1 33 | limit: 500 34 | name: "&8&l西瓜种子" 35 | "PUMPKIN_SEEDS": 36 | slot: 12 37 | price: 0.1 38 | limit: 500 39 | name: "&f&l南瓜种子" 40 | "BEETROOT_SEEDS": 41 | slot: 13 42 | price: 0.1 43 | limit: 500 44 | name: "&7&l甜菜种子" 45 | "NETHER_WART": 46 | slot: 14 47 | price: 0.1 48 | limit: 500 49 | name: "&c&l地狱疣" 50 | "COCOA_BEANS": 51 | slot: 15 52 | price: 0.1 53 | limit: 500 54 | name: "&6&l可可豆" 55 | "SWEET_BERRIES": 56 | slot: 16 57 | price: 0.1 58 | limit: 500 59 | name: "&c&l树莓" 60 | "BEETROOT": 61 | slot: 19 62 | price: 0.1 63 | limit: 500 64 | name: "&c&l甜菜根" 65 | "CARROT": 66 | slot: 20 67 | price: 0.1 68 | limit: 500 69 | name: "&6&l胡萝卜" 70 | "POTATO": 71 | slot: 21 72 | price: 0.1 73 | limit: 500 74 | name: "&e&l土豆" 75 | "WHEAT": 76 | slot: 22 77 | price: 0.1 78 | limit: 500 79 | name: "&e&l小麦" 80 | "MELON_SLICE": 81 | slot: 23 82 | price: 0.1 83 | limit: 500 84 | name: "&c&l西瓜片" 85 | "PUMPKIN": 86 | slot: 24 87 | price: 0.1 88 | limit: 500 89 | name: "&6&l南瓜" 90 | "CACTUS": 91 | slot: 25 92 | price: 0.1 93 | limit: 500 94 | name: "&2&l仙人掌" 95 | "POISONOUS_POTATO": 96 | slot: 28 97 | price: 0.1 98 | limit: 500 99 | name: "&a&l毒土豆" 100 | "KELP": 101 | slot: 29 102 | price: 0.1 103 | limit: 500 104 | name: "&2&l海带" 105 | "SUGAR_CANE": 106 | slot: 30 107 | price: 0.1 108 | limit: 500 109 | name: "&a&l甘蔗" 110 | "BAMBOO": 111 | slot: 31 112 | price: 0.1 113 | limit: 500 114 | name: "&a&l竹子" 115 | "VINE": 116 | slot: 32 117 | price: 0.1 118 | limit: 500 119 | name: "&2&l藤蔓" 120 | "CHORUS_FRUIT": 121 | slot: 33 122 | price: 0.1 123 | limit: 500 124 | name: "&d&l紫颂果" 125 | "CHORUS_PLANT": 126 | slot: 34 127 | price: 0.1 128 | limit: 500 129 | name: "&5&l紫颂植株" -------------------------------------------------------------------------------- /src/main/resources/depositories/miner.yml: -------------------------------------------------------------------------------- 1 | name: "&e&l矿工仓库" 2 | 3 | capacity: 4 | default: 5000 # 若为0则默认不可以使用该仓库 5 | permissions: 6 | - "ud.miner.vip:10000" 7 | 8 | gui: 9 | title: "&e&l矿工仓库" 10 | lines: 6 11 | items: 12 | "INFO": 13 | material: CHEST 14 | data: 0 15 | slot: 49 16 | name: "&e&l背包信息" 17 | lore: 18 | - " " 19 | - "&f仓库最大容量&6 %UltraDepository_capacity_miner%" 20 | - "&f仓库已用容量&6 %UltraDepository_used_miner%" 21 | - "&f仓库剩余容量&6 %UltraDepository_usable_miner%" 22 | - " " 23 | 24 | items: 25 | "COBBLESTONE": 26 | slot: 11 27 | price: 0.1 28 | limit: 500 29 | name: "&f&l圆石" 30 | lore: 31 | - " " 32 | - "&f方块!" 33 | "IRON_ORE": 34 | slot: 12 35 | price: 0.1 36 | limit: 500 37 | name: "&f&l铁矿" 38 | lore: 39 | - " " 40 | - "&f升级!" 41 | "GOLD_ORE": 42 | slot: 13 43 | price: 0.1 44 | limit: 500 45 | name: "&e&l金矿" 46 | lore: 47 | - " " 48 | - "&f财富!" 49 | "COAL": 50 | slot: 14 51 | price: 0.1 52 | limit: 500 53 | name: "&8&l煤炭" 54 | lore: 55 | - " " 56 | - "&f燃料!" 57 | "FLINT": 58 | slot: 15 59 | price: 0.1 60 | limit: 500 61 | name: "&7&l燧石" 62 | lore: 63 | - " " 64 | - "&f点火!" 65 | "IRON_INGOT": 66 | slot: 20 67 | price: 0.1 68 | limit: 500 69 | name: "&f&l铁锭" 70 | lore: 71 | - " " 72 | - "&f制造!" 73 | "GOLD_INGOT": 74 | slot: 21 75 | price: 0.1 76 | limit: 500 77 | name: "&e&l金锭" 78 | lore: 79 | - " " 80 | - "&f高贵!" 81 | "LAPIS_LAZULI": 82 | slot: 22 83 | price: 0.1 84 | limit: 500 85 | name: "&9&l青金石" 86 | lore: 87 | - " " 88 | - "&f附魔!" 89 | "DIAMOND": 90 | slot: 23 91 | price: 10 92 | limit: 1500 93 | name: "&b&l钻石" 94 | lore: 95 | - " " 96 | - "&f钻石!" 97 | "EMERALD": 98 | slot: 24 99 | price: 10 100 | limit: 1500 101 | name: "&a&l绿宝石" 102 | lore: 103 | - " " 104 | - "&f交易!" 105 | "CLAY_BALL": 106 | slot: 30 107 | price: 0.1 108 | limit: 500 109 | name: "&7&l黏土" 110 | lore: 111 | - " " 112 | - "&f塑形!" 113 | "REDSTONE": 114 | slot: 29 115 | price: 0.1 116 | limit: 500 117 | name: "&c&l红石" 118 | lore: 119 | - " " 120 | - "&f电路!" 121 | "GLOWSTONE_DUST": 122 | slot: 32 123 | price: 0.5 124 | limit: 1500 125 | name: "&6&l萤石" 126 | lore: 127 | - " " 128 | - "&f发光!" 129 | "QUARTZ": 130 | slot: 33 131 | price: 0.5 132 | limit: 1500 133 | name: "&f&l石英" 134 | lore: 135 | - " " 136 | - "&f美丽!" -------------------------------------------------------------------------------- /.documentation/develop/use-custome-storage.md: -------------------------------------------------------------------------------- 1 | # 开发 - 自定义存储源 2 | 3 | 在某些情况下,插件提供的几种存储方式并不能满足您的需求,此时您可以选择在您自己的插件中自定义本插件的存储源。 4 | 5 | ## 1. 修改 plugin.yml 6 | 7 | 您需要在您自己的插件中声明依赖了本插件,即在 `plugin.yml` 中添加以下内容: 8 | 9 | ```yaml 10 | softdepend: 11 | - UltraDepository 12 | ``` 13 | 14 | 添加后,Bukkit会让您的插件在本插件之后加载,此时您就可以应用您的存储源。 15 | 16 | ## 2. 依赖本插件 17 | 18 | 请依据 [开发指南](../README.md) 中的依赖介绍部分完成对本插件的依赖。 19 | 20 | ## 3. 实现 DataStorage 21 | 22 | 您需要在您的插件中实现 DataStorage 类,并实现其中的功能,他看起来像是这样的: 23 | 24 | ```java 25 | 26 | import cc.carm.plugin.ultradepository.data.UserData; 27 | import cc.carm.plugin.ultradepository.storage.DataStorage; 28 | import org.jetbrains.annotations.NotNull; 29 | import org.jetbrains.annotations.Nullable; 30 | import org.jetbrains.annotations.TestOnly; 31 | 32 | import java.util.UUID; 33 | 34 | public class CustomStorage implements DataStorage { 35 | 36 | @Override 37 | public boolean initialize() { 38 | //初始化存储,在这里可进行连接数据库、创建表等操作。 39 | 40 | return true; //返回true代表初始化成功,若失败则插件将不再加载 41 | } 42 | 43 | @Override 44 | public void shutdown() { 45 | // 插件卸载时触发,一般用于释放连接池。 46 | } 47 | 48 | @Override 49 | public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { 50 | // 加载玩家数据部分 51 | // 若抛出错误,则视为加载出错,会采用临时玩家数据的形式保证插件继续运行,同时在后台提示检查。 52 | // 返回空则代表暂无该玩家数据,会自动视作新数据 53 | return null; 54 | } 55 | 56 | @Override 57 | public void saveUserData(@NotNull UserData data) throws Exception { 58 | // 保存玩家数据部分 59 | // 若抛出错误,则视为保存出错,将在后台提示检查。 60 | } 61 | 62 | } 63 | ``` 64 | 65 | 您也可以 [点击这里](../../src/main/java/cc/carm/plugin/ultradepository/storage/impl) 参考本插件提供的其他已实现的存储方式,并在此基础上开发您的自定义存储。 66 | 67 | > 若您需要JSON格式存储,可以直接继承 [`JSONStorage`](../../src/main/java/cc/carm/plugin/ultradepository/storage/impl/JSONStorage.java) ,并重写相关方法。 68 | 69 | ## 4. 应用您的存储 70 | 71 | 您需要在插件加载(`onLoad()`)时,应用您的自定义存储。 72 | 73 | ```java 74 | 75 | import cc.carm.plugin.ultradepository.storage.DataStorage; 76 | import cc.carm.plugin.ultradepository.storage.StorageMethod; 77 | import cc.carm.plugin.ultradepository.storage.impl.CustomStorage; 78 | import org.bukkit.plugin.java.JavaPlugin; 79 | 80 | import java.util.function.Supplier; 81 | 82 | public class YourPlugin extends JavaPlugin { 83 | 84 | @Override 85 | public void onLoad() { 86 | 87 | // 应用您的存储方式 88 | StorageMethod.CUSTOM.setStorageSupplier(new Supplier() { 89 | @Override 90 | public DataStorage get() { 91 | return new CustomStorage(); 92 | } 93 | }); 94 | 95 | // 简化后大概长这样(lamda) 96 | StorageMethod.CUSTOM.setStorageSupplier(CustomStorage::new); 97 | } 98 | } 99 | ``` 100 | 101 | ## 5. 修改本插件的 config.yml 102 | 103 | 您需要修改本插件的 `config.yml` 中的 `storage.method` 为 **CUSTOM** 。 104 | 105 | 修改完成后,在插件下次启动时就将应用您实现的 **DataStorage** 从而完成自定义存储源。 -------------------------------------------------------------------------------- /src/test/java/GsonMapTest.java: -------------------------------------------------------------------------------- 1 | import com.google.gson.Gson; 2 | import com.google.gson.JsonObject; 3 | 4 | import java.lang.reflect.Method; 5 | import java.util.*; 6 | 7 | public class GsonMapTest { 8 | 9 | private static final Gson GSON = new Gson(); 10 | 11 | @org.junit.Test 12 | public void test() { 13 | System.out.println(this.getClass().getSimpleName()); 14 | 15 | 16 | List tests = new ArrayList<>(); 17 | tests.add(new Test1()); 18 | tests.add(new Test2()); 19 | tests.add(new Test3()); 20 | tests.stream().map(test -> test.getClass().getSimpleName() + " : " + test.isOverride("load")).forEach(System.out::println); 21 | 22 | Map>> values = new LinkedHashMap<>(); 23 | 24 | 25 | for (int u = 0; u < 3; u++) { 26 | Map> depositoryDataMap = new LinkedHashMap<>(); 27 | for (int i = 0; i < 5; i++) { 28 | Map itemDataMap = new HashMap<>(); 29 | int amount = Math.max(0, new Random().nextInt(15)); 30 | int sold = Math.max(0, new Random().nextInt(15)); 31 | if (amount > 0) itemDataMap.put("amount", amount); 32 | if (sold > 0) itemDataMap.put("sold", sold); 33 | if (!itemDataMap.isEmpty()) { 34 | depositoryDataMap.put(UUID.randomUUID().toString().substring(0, 4), itemDataMap); 35 | } 36 | } 37 | if (!depositoryDataMap.isEmpty()) { 38 | values.put(UUID.randomUUID().toString().substring(0, 8), depositoryDataMap); 39 | } 40 | } 41 | 42 | System.out.println(values.size()); 43 | 44 | String jsonValues = GSON.toJson(values); 45 | System.out.println(jsonValues); 46 | 47 | JsonObject dataObject = new JsonObject(); 48 | dataObject.addProperty("date", 20201011); 49 | dataObject.add("depositories", GSON.toJsonTree(values)); 50 | 51 | System.out.println(GSON.toJson(dataObject)); 52 | } 53 | 54 | public interface Test { 55 | 56 | void load(); 57 | 58 | default boolean isOverride(String methodName) { 59 | Map methodMap = new HashMap<>(); 60 | Arrays.stream(Test.class.getDeclaredMethods()) 61 | .filter(method -> method.getName().equals(methodName)) 62 | .forEach(method -> Arrays.stream(getClass().getDeclaredMethods()) 63 | .filter(extend -> extend.getName().equals(methodName)) 64 | .filter(extend -> extend.getReturnType().equals(method.getReturnType())) 65 | .filter(extend -> extend.getParameterTypes().length == method.getParameterTypes().length) 66 | .findFirst().ifPresent(extendMethod -> methodMap.put(method, extendMethod)) 67 | ); 68 | return !methodMap.isEmpty(); 69 | } 70 | 71 | } 72 | 73 | public static class Test1 implements Test { 74 | 75 | 76 | @Override 77 | public void load() { 78 | System.out.println("test1"); 79 | } 80 | } 81 | 82 | public static class Test2 extends Test1 { 83 | 84 | } 85 | 86 | public static class Test3 extends Test2 { 87 | 88 | @Override 89 | public void load() { 90 | } 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/listener/CollectListener.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.listener; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 5 | import org.bukkit.entity.Item; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.block.BlockDropItemEvent; 11 | import org.bukkit.event.entity.EntityDeathEvent; 12 | import org.bukkit.event.entity.EntityPickupItemEvent; 13 | import org.bukkit.inventory.ItemStack; 14 | 15 | import java.util.Collection; 16 | import java.util.List; 17 | import java.util.UUID; 18 | 19 | public class CollectListener implements Listener { 20 | 21 | @EventHandler(priority = EventPriority.HIGH) 22 | public void onBreak(BlockDropItemEvent event) { 23 | if (event.isCancelled() || !PluginConfig.Collect.BREAK.get()) return; 24 | 25 | Player player = event.getPlayer(); 26 | if (!UltraDepository.getUserManager().isCollectEnabled(player)) return; 27 | if (event.getBlock().getType().isOccluding()) return; 28 | 29 | List droppedItems = event.getItems(); 30 | if (droppedItems.isEmpty()) return; 31 | 32 | for (Item drop : droppedItems) { 33 | UltraDepository.getInstance().debug("Dropped " + drop.getType().name() + " " + drop.getItemStack().getAmount()); 34 | } 35 | 36 | event.getItems().removeIf(item -> UltraDepository.getDepositoryManager().collectItem(player, item.getItemStack())); 37 | 38 | } 39 | 40 | @EventHandler(priority = EventPriority.HIGH) 41 | public void onDeath(EntityDeathEvent event) { 42 | if (!PluginConfig.Collect.KILL.get()) return; 43 | 44 | Player player = event.getEntity().getKiller(); 45 | if (player == null) return; 46 | if (!UltraDepository.getUserManager().isCollectEnabled(player)) return; 47 | 48 | Collection finalDrops = UltraDepository.getDepositoryManager().collectItem(player, event.getDrops()); 49 | event.getDrops().clear(); 50 | event.getDrops().addAll(finalDrops); 51 | } 52 | 53 | @EventHandler(priority = EventPriority.HIGH) 54 | public void onPickup(EntityPickupItemEvent event) { 55 | if (event.isCancelled() || !PluginConfig.Collect.PICKUP.get()) return; 56 | if (!(event.getEntity() instanceof Player)) return; 57 | 58 | Player player = (Player) event.getEntity(); 59 | if (!UltraDepository.getUserManager().isCollectEnabled(player)) return; 60 | 61 | // 自己扔出去的东西不计入背包 62 | UUID thrower = event.getItem().getThrower(); 63 | if (thrower != null && thrower.equals(player.getUniqueId())) return; 64 | 65 | ItemStack item = event.getItem().getItemStack(); 66 | UltraDepository.getInstance().debug("Picked up " + item.getType().name() + " " + item.getAmount()); 67 | if (UltraDepository.getDepositoryManager().collectItem(player, item)) { 68 | event.setCancelled(true); 69 | event.getItem().remove(); 70 | } 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | # ${project.name} - ${project.description} 2 | # 项目地址: ${project.url} 3 | # 下载地址: ${project.distributionManagement.downloadUrl} 4 | 5 | version: ${project.version} 6 | 7 | debug: false 8 | 9 | # 统计数据设定 10 | # 该选项用于帮助开发者统计插件版本与使用情况,且绝不会影响性能与使用体验。 11 | # 当然,您也可以选择在这里关闭,或在plugins/bStats下的配置文件中关闭。 12 | metrics: true 13 | 14 | # 检查更新设定 15 | # 该选项用于插件判断是否要检查更新,若您不希望插件检查更新并提示您,可以选择关闭。 16 | # 检查更新为异步操作,绝不会影响性能与使用体验。 17 | check-update: true 18 | 19 | # 存储相关配置 20 | # 注意:存储配置不会通过重载指令生效,如有修改请重新启动服务器。 21 | storage: 22 | 23 | # 存储方式,可选 [ yaml | json | mysql(推荐) ] 24 | method: yaml 25 | 26 | # 选择 yaml/json 存储方式时的存储路径 27 | # 默认为相对路径,相对于插件生成的配置文件夹下的路径 28 | # 支持绝对路径,如 “/var/data/ud/"(linux) 或 "D:\data\ud\"(windows) 29 | # 使用绝对路径时请注意权限问题 30 | file-path: data 31 | 32 | # 选择 database 存储方式时的数据库配置 33 | mysql: 34 | # 数据库驱动路径 35 | driver: "com.mysql.cj.jdbc.Driver" 36 | url: "jdbc:mysql://127.0.0.1:3306/" 37 | table: "ud_data" # 插件表名,允许自定义 38 | username: "username" 39 | password: "password" 40 | 41 | 42 | # 玩家收集配置 43 | # 用于决定玩家在哪些情况下的物品会自动放入背包 44 | collect: 45 | pickup: true # 捡取物品 46 | kill: true #杀死动物 47 | break: true #破坏方块 48 | 49 | 50 | sounds: 51 | collect: "ENTITY_EXPERIENCE_ORB_PICKUP:0.5" 52 | takeout: "ENTITY_HORSE_ARMOR:0.5" 53 | sell-success: "ENTITY_VILLAGER_CELEBRATE" 54 | sell-fail: "ENTITY_VILLAGER_NO" 55 | gui-click: "UI_BUTTON_CLICK" 56 | 57 | # 通用配置 58 | general: 59 | 60 | # 针对可出售物品的额外介绍 61 | # 将添加到背包界面内的物品上,避免重复配置 62 | additional-lore: 63 | available-for-sale: 64 | # 可出售物品的介绍 65 | - " " 66 | - "&f仓库内数量 &a%(amount)" 67 | - "&f该物品单价 &a%(price)" 68 | - "&f今日可出售 &a%(remain)&8/%(limit)" 69 | not-for-sale: 70 | # 针对不可出售的物品的额外介绍 71 | # (当 未安装经济插件 或 每日可售出数量<=0 或 单价<=0 时判断为不可出售) 72 | - " " 73 | - "&f仓库内数量 &a%(amount)" 74 | 75 | 76 | # 提示玩家点击行为的介绍 77 | # 将添加到背包界面内的物品上,避免重复配置 78 | click-lore: 79 | available-for-sale: 80 | - " " 81 | - "&a&l左键点击 &8| &f按量售出该物品" 82 | - "&a&l右键点击 &8| &f取出一组该物品" 83 | not-for-sale: 84 | # 针对不可出售的物品的额外介绍 85 | # (当 未安装经济插件 或 每日可售出数量<=0 或 单价<=0 时判断为不可出售) 86 | - " " 87 | - "&a&l右键点击 &8| &f取出一组该物品" 88 | 89 | # 售出界面的配置 90 | sell-gui: 91 | title: "&8出售 %(item_name)" 92 | items: 93 | add: 94 | type: GREEN_STAINED_GLASS_PANE 95 | name: "&a增加 %(amount) 个" 96 | remove: 97 | type: RED_STAINED_GLASS_PANE 98 | name: "&c减少 %(amount) 个" 99 | confirm: 100 | type: EMERALD 101 | name: "&a确认售出" 102 | lore: 103 | - " " 104 | - "&7您将售出 &r%(item_name) &8x &f%(amount)" 105 | - "&7共计获得 &e%(money) &7元作为回报。" 106 | - " " 107 | - "&a&l点击确认售出" 108 | cancel: 109 | type: REDSTONE 110 | name: "&c取消售出" 111 | lore: 112 | - " " 113 | - "&c&l点击取消售出" -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Deploy & Upload 5 | 6 | on: 7 | # 支持手动触发构建 8 | workflow_dispatch: 9 | release: 10 | # 创建release的时候触发 11 | types: [ published ] 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v6 20 | - name: "Set up JDK" 21 | uses: actions/setup-java@v5 22 | with: 23 | java-version: '11' 24 | distribution: 'adopt' 25 | cache: maven 26 | server-id: github 27 | server-username: MAVEN_USERNAME 28 | server-password: MAVEN_TOKEN 29 | 30 | - name: "Maven Deploy" 31 | run: mvn -B deploy --file pom.xml -DskipTests 32 | env: 33 | MAVEN_USERNAME: ${{ github.repository_owner }} 34 | MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}} 35 | 36 | - name: "Release Asset Upload" 37 | id: upload-release-asset 38 | uses: shogo82148/actions-upload-release-asset@v1 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | with: 42 | upload_url: ${{ github.event.release.upload_url }} 43 | asset_path: asset/*.jar 44 | asset_content_type: application/java-archive 45 | 46 | - name: "Javadoc Deploy Staging" 47 | run: | 48 | rm -rf docs 49 | mkdir -vp docs 50 | cp -vrf target/apidocs/* docs/ 51 | cp -vrf .documentation/JAVADOC-README.md docs/README.md 52 | 53 | - name: "Generate the Javadoc sitemap" 54 | id: sitemap 55 | uses: cicirello/generate-sitemap@v1 56 | with: 57 | base-url-path: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }} 58 | path-to-root: docs 59 | 60 | - name: "Output Javadoc stats" 61 | run: | 62 | echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}" 63 | echo "url-count = ${{ steps.sitemap.outputs.url-count }}" 64 | echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}" 65 | 66 | - name: "Configure Git" 67 | env: 68 | DEPLOY_PRI: ${{secrets.DEPLOY_PRI}} 69 | run: | 70 | sudo timedatectl set-timezone "Asia/Shanghai" 71 | mkdir -p ~/.ssh/ 72 | echo "$DEPLOY_PRI" > ~/.ssh/id_rsa 73 | chmod 600 ~/.ssh/id_rsa 74 | ssh-keyscan github.com >> ~/.ssh/known_hosts 75 | git config --global user.name 'CarmJos' 76 | git config --global user.email 'carm@carm.cc' 77 | 78 | - name: "Commit Javadocs" 79 | run: | 80 | cd docs 81 | git init 82 | git remote add origin git@github.com:${{ github.repository }}.git 83 | git checkout -b gh-pages 84 | git add -A 85 | git commit -m "API Document generated." 86 | 87 | - name: "Push javadocs" 88 | run: | 89 | cd docs 90 | git push origin HEAD:gh-pages --force -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/configuration/depository/Depository.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.configuration.depository; 2 | 3 | import cc.carm.lib.easyplugin.gui.configuration.GUIConfiguration; 4 | import org.bukkit.configuration.ConfigurationSection; 5 | import org.bukkit.configuration.file.FileConfiguration; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.Objects; 11 | 12 | public class Depository { 13 | 14 | private final @NotNull String identifier; 15 | 16 | private final @NotNull String name; 17 | private final @NotNull GUIConfiguration guiConfiguration; 18 | private final @NotNull DepositoryCapacity capacity; 19 | 20 | private Map items; 21 | 22 | public Depository(@NotNull String identifier, @NotNull String name, 23 | @NotNull GUIConfiguration guiConfiguration, 24 | @NotNull DepositoryCapacity capacity) { 25 | this(identifier, name, guiConfiguration, capacity, new HashMap<>()); 26 | } 27 | 28 | public Depository(@NotNull String identifier, @NotNull String name, 29 | @NotNull GUIConfiguration guiConfiguration, 30 | @NotNull DepositoryCapacity capacity, 31 | Map items) { 32 | this.identifier = identifier; 33 | this.name = name; 34 | this.guiConfiguration = guiConfiguration; 35 | this.capacity = capacity; 36 | this.items = items; 37 | } 38 | 39 | public @NotNull String getIdentifier() { 40 | return this.identifier; 41 | } 42 | 43 | public @NotNull String getName() { 44 | return this.name; 45 | } 46 | 47 | public @NotNull GUIConfiguration getGUIConfiguration() { 48 | return this.guiConfiguration; 49 | } 50 | 51 | public @NotNull DepositoryCapacity getCapacity() { 52 | return this.capacity; 53 | } 54 | 55 | public @NotNull Map getItems() { 56 | return this.items; 57 | } 58 | 59 | 60 | @Override 61 | public boolean equals(Object o) { 62 | if (this == o) return true; 63 | if (o == null || getClass() != o.getClass()) return false; 64 | Depository that = (Depository) o; 65 | return identifier.equals(that.identifier); 66 | } 67 | 68 | @Override 69 | public int hashCode() { 70 | return Objects.hash(identifier); 71 | } 72 | 73 | public static Depository loadFrom(String identifier, FileConfiguration configuration) { 74 | Depository depository = new Depository(identifier, configuration.getString("name", identifier), 75 | GUIConfiguration.readConfiguration(configuration.getConfigurationSection("gui")), 76 | new DepositoryCapacity( 77 | configuration.getInt("capacity.default", 0), 78 | configuration.getStringList("capacity.permissions") 79 | ) 80 | ); 81 | depository.items = readItems(depository, configuration.getConfigurationSection("items")); 82 | return depository; 83 | } 84 | 85 | private static Map readItems(Depository depository, ConfigurationSection section) { 86 | if (section == null) return new HashMap<>(); 87 | Map items = new HashMap<>(); 88 | for (String key : section.getKeys(false)) { 89 | ConfigurationSection itemSection = section.getConfigurationSection(key); 90 | if (itemSection != null) { 91 | DepositoryItem item = DepositoryItem.readFrom(depository, key, itemSection); 92 | if (item != null) items.put(item.getTypeID(), item); 93 | } 94 | } 95 | return items; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/resources/i18n/ko_KR/config.yml: -------------------------------------------------------------------------------- 1 | # ${project.name} - ${project.description} 2 | # Source url: ${project.url} 3 | # Download URL: ${project.distributionManagement.downloadUrl} 4 | 5 | version: ${project.version} 6 | 7 | debug: false 8 | 9 | # bStats Metrics 10 | # This option is used to help developers analysis plugin stats, 11 | # and will never affect performance and user experience. 12 | # You can choose to turn it off here. 13 | metrics: true 14 | 15 | # Storage Configuration 16 | storage: 17 | 18 | # Storage method 19 | # You can choose [ yaml | json | mysql ] 20 | method: yaml 21 | 22 | # The storage file path when choosing "yaml"/"json" method. 23 | # Support absolute paths, e.g. “/var/data/ud/"(linux) or "D:\data\ud\"(windows) 24 | # **Be aware of permission issues when using absolute paths!** 25 | file-path: data 26 | 27 | # The Database configuration when choosing "mysql" method. 28 | mysql: 29 | driver: "com.mysql.jdbc.Driver" 30 | url: "jdbc:mysql://127.0.0.1:3306/" 31 | table: "ud_data" # Plugin data table name, allowing customization 32 | username: "username" 33 | password: "password" 34 | 35 | 36 | # Player Collect Configuration 37 | # Used to determine when put the player's item into the backpack automatically 38 | collect: 39 | pickup: true # Pickup Items 40 | kill: true # Kill Entities (Animals/Monsters) 41 | break: true # Break Blocks 42 | 43 | sounds: 44 | collect: "ENTITY_EXPERIENCE_ORB_PICKUP:0.5" 45 | sell-success: "ENTITY_VILLAGER_CELEBRATE" 46 | sell-fail: "ENTITY_VILLAGER_NO" 47 | gui-click: "UI_BUTTON_CLICK" 48 | 49 | # General Configuration 50 | general: 51 | 52 | # Hints lore for the item's information 53 | # Will add to items in BackpackGUI and SellGUI. 54 | additional-lore: 55 | available-for-sale: 56 | - " " 57 | - "&fAmount &a%(amount)" 58 | - "&fPrice &a%(price)" 59 | - "&fSold &a%(remain)&8/%(limit)" 60 | not-for-sale: 61 | # Display when : 62 | # 1. Vault not installed 63 | # 2. No economy plugins 64 | # 3. daily sell limit <= 0 65 | # 4. item price <=0 66 | - " " 67 | - "&fAmount &a%(amount)" 68 | 69 | # Hints lore for the player's click 70 | # Will add to items in BackpackGUI. 71 | click-lore: 72 | available-for-sale: 73 | - " " 74 | - "&a&lLEFT-CLICK &8| &fSell items" 75 | - "&a&lRIGHT-CLICK &8| &fTake one Stack" 76 | not-for-sale: 77 | # Display when : 78 | # 1. Vault not installed 79 | # 2. No economy plugins 80 | # 3. daily sell limit <= 0 81 | # 4. item price <=0 82 | - " " 83 | - "&a&lRIGHT-CLICK &8| &fTake one Stack" 84 | 85 | # Configuration of the SellGUI 86 | sell-gui: 87 | title: "&8Selling %(item_name)" 88 | items: 89 | add: 90 | type: GREEN_STAINED_GLASS_PANE 91 | name: "&aAdd %(amount)" 92 | remove: 93 | type: RED_STAINED_GLASS_PANE 94 | name: "&cReduce %(amount)" 95 | confirm: 96 | type: EMERALD 97 | name: "&a&lConfirm" 98 | lore: 99 | - " " 100 | - "&7You will sell &r%(item_name) &8x &f%(amount)" 101 | - "&7and will get $&e%(money) &7." 102 | - " " 103 | - "&a&lClick to confirm" 104 | cancel: 105 | type: REDSTONE 106 | name: "&c&lCancel" 107 | lore: 108 | - " " 109 | - "&cClick to cancel" -------------------------------------------------------------------------------- /src/main/resources/i18n/en_US/config.yml: -------------------------------------------------------------------------------- 1 | # ${project.name} - ${project.description} 2 | # Source url: ${project.url} 3 | # Download URL: ${project.distributionManagement.downloadUrl} 4 | 5 | version: ${project.version} 6 | 7 | debug: false 8 | 9 | # bStats Metrics 10 | # This option is used to help developers analysis plugin stats, 11 | # and will never affect performance and user experience. 12 | # You can choose to turn it off here. 13 | metrics: true 14 | 15 | # Storage Configuration 16 | storage: 17 | 18 | # Storage method 19 | # You can choose [ yaml | json | mysql ] 20 | method: yaml 21 | 22 | # The storage file path when choosing "yaml"/"json" method. 23 | # Support absolute paths, e.g. “/var/data/ud/"(linux) or "D:\data\ud\"(windows) 24 | # **Be aware of permission issues when using absolute paths!** 25 | file-path: data 26 | 27 | # The Database configuration when choosing "mysql" method. 28 | mysql: 29 | driver: "com.mysql.jdbc.Driver" 30 | url: "jdbc:mysql://127.0.0.1:3306/" 31 | table: "ud_data" # Plugin data table name, allowing customization 32 | username: "username" 33 | password: "password" 34 | 35 | 36 | # Player Collect Configuration 37 | # Used to determine when put the player's item into the backpack automatically 38 | collect: 39 | pickup: true # Pickup Items 40 | kill: true # Kill Entities (Animals/Monsters) 41 | break: true # Break Blocks 42 | 43 | sounds: 44 | collect: "ENTITY_EXPERIENCE_ORB_PICKUP:0.5" 45 | sell-success: "ENTITY_VILLAGER_CELEBRATE" 46 | sell-fail: "ENTITY_VILLAGER_NO" 47 | gui-click: "UI_BUTTON_CLICK" 48 | 49 | # General Configuration 50 | general: 51 | 52 | # Hints lore for the item's information 53 | # Will add to items in BackpackGUI and SellGUI. 54 | additional-lore: 55 | available-for-sale: 56 | - " " 57 | - "&fAmount &a%(amount)" 58 | - "&fPrice &a%(price)" 59 | - "&fSold &a%(remain)&8/%(limit)" 60 | not-for-sale: 61 | # Display when : 62 | # 1. Vault not installed 63 | # 2. No economy plugins 64 | # 3. daily sell limit <= 0 65 | # 4. item price <=0 66 | - " " 67 | - "&fAmount &a%(amount)" 68 | 69 | # Hints lore for the player's click 70 | # Will add to items in BackpackGUI. 71 | click-lore: 72 | available-for-sale: 73 | - " " 74 | - "&a&lLEFT-CLICK &8| &fSell items" 75 | - "&a&lRIGHT-CLICK &8| &fTake one Stack" 76 | not-for-sale: 77 | # Display when : 78 | # 1. Vault not installed 79 | # 2. No economy plugins 80 | # 3. daily sell limit <= 0 81 | # 4. item price <=0 82 | - " " 83 | - "&a&lRIGHT-CLICK &8| &fTake one Stack" 84 | 85 | 86 | # Configuration of the SellGUI 87 | sell-gui: 88 | title: "&8Selling %(item_name)" 89 | items: 90 | add: 91 | type: GREEN_STAINED_GLASS_PANE 92 | name: "&aAdd %(amount)" 93 | remove: 94 | type: RED_STAINED_GLASS_PANE 95 | name: "&cReduce %(amount)" 96 | confirm: 97 | type: EMERALD 98 | name: "&a&lConfirm" 99 | lore: 100 | - " " 101 | - "&7You will sell &r%(item_name) &8x &f%(amount)" 102 | - "&7and will get $&e%(money) &7." 103 | - " " 104 | - "&a&lClick to confirm" 105 | cancel: 106 | type: REDSTONE 107 | name: "&c&lCancel" 108 | lore: 109 | - " " 110 | - "&cClick to cancel" -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/configuration/PluginMessages.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.configuration; 2 | 3 | 4 | import cc.carm.lib.easyplugin.configuration.language.EasyMessage; 5 | import cc.carm.lib.easyplugin.configuration.language.EasyMessageList; 6 | import cc.carm.lib.easyplugin.configuration.language.MessagesRoot; 7 | import cc.carm.lib.easyplugin.configuration.language.MessagesSection; 8 | 9 | public class PluginMessages extends MessagesRoot { 10 | 11 | @MessagesSection("help") 12 | public static class Usages { 13 | 14 | public static final EasyMessageList CONSOLE = new EasyMessageList( 15 | "&6&l超级仓库 &f后台指令帮助", 16 | "&8#&f info &6<玩家> &e[仓库ID] &e[物品ID]", 17 | "&8-&7 得到玩家的相关物品信息。", 18 | "&8#&f add &6<玩家> &6<仓库ID> &6<物品ID> &6<数量>", 19 | "&8-&7 为玩家添加对应仓库中对于物品的数量。", 20 | "&8#&f remove &6<玩家> &6<仓库ID> &6<物品ID> &e[数量]", 21 | "&8-&7 为玩家减少对应仓库中对于物品的数量。", 22 | "&8-&7 若不填写数量,则清空对应仓库的对应物品。", 23 | "&8#&f sell &6<玩家> &e[仓库ID] &e[物品ID] &e[数量]", 24 | "&8-&7 为玩家售出相关物品。", 25 | "&8-&7 若不填写数量,则售出所有对应仓库的对应物品。", 26 | "&8-&7 若不填写物品,则售出对应仓库内所有物品。", 27 | "&8-&7 若不填写仓库,则售出所有仓库内所有物品。", 28 | "&8-&7 该指令受到玩家每日售出数量的限制。" 29 | ); 30 | 31 | public static final EasyMessageList PLAYER = new EasyMessageList( 32 | "&6&l超级仓库 &f玩家指令帮助", 33 | "&8#&f open &e[仓库ID]", 34 | "&8-&7 打开对应仓库的界面。", 35 | "&8#&f sell &6<仓库ID> &6<物品ID> &6<数量>", 36 | "&8-&7 售出对应数量的对应物品。", 37 | "&8-&7 该指令受到玩家每日售出数量的限制。", 38 | "&8#&f sellAll &e[仓库ID] &e[物品ID]", 39 | "&8-&7 该指令受到玩家每日售出数量的限制。" 40 | ); 41 | 42 | } 43 | 44 | public static final EasyMessageList ITEM_SOLD = new EasyMessageList( 45 | new String[]{"&f您出售了 &r%(item)&7x%(amount) &f,共赚取 &6%(money) &f元。"}, 46 | new String[]{"%(item)", "%(amount)", "%(money)"} 47 | ); 48 | 49 | public static final EasyMessageList ITEM_SOLD_LIMIT = new EasyMessageList( 50 | new String[]{"&f该物品今日剩余可出售额度为 &a%(amount)&8/%(limit) &f个。"}, 51 | new String[]{"%(amount)", "%(limit)"} 52 | ); 53 | 54 | public static final EasyMessageList ITEM_PICKUP = new EasyMessageList( 55 | new String[]{"&f您拾取了 &r%(item)&7x%(amount) &f,已自动放入到您的仓库中。"}, 56 | new String[]{"%(item)", "%(amount)"} 57 | ); 58 | 59 | public static final EasyMessageList ITEM_TAKEOUT = new EasyMessageList( 60 | new String[]{"&f您从仓库中拿取了 &r%(item)&7x%(amount) &f放入到您的背包中。"}, 61 | new String[]{"%(item)", "%(amount)"} 62 | ); 63 | 64 | public static final EasyMessageList ITEM_COLLECT = new EasyMessageList( 65 | new String[]{"&f您收集了 &r%(item)&7x%(amount) &f,已自动放入到您的 &6%(depository) &f中。"}, 66 | new String[]{"%(item)", "%(amount)", "%(depository)"} 67 | ); 68 | 69 | public static final EasyMessage ITEM_COLLECT_ACTIONBAR = new EasyMessage( 70 | "&r%(item)&7x%(amount) &f-> &6%(depository)", 71 | new String[]{"%(item)", "%(amount)", "%(depository)"} 72 | ); 73 | 74 | public static final EasyMessageList NO_SPACE = new EasyMessageList( 75 | "&f您背包内没有足够的空间取出物品!" 76 | ); 77 | 78 | public static final EasyMessageList NO_ECONOMY = new EasyMessageList( 79 | "&f本服务器暂未启用出售功能。" 80 | ); 81 | 82 | public static final EasyMessageList NO_DEPOSITORY = new EasyMessageList( 83 | "&f不存在该仓库,请检查仓库ID是否正确。" 84 | ); 85 | 86 | public static final EasyMessageList NO_ITEM = new EasyMessageList( 87 | "&f仓库中不存在该物品,请检查物品ID是否正确。" 88 | ); 89 | 90 | public static final EasyMessageList NO_ENOUGH_ITEM = new EasyMessageList( 91 | "&f仓库中不存在足够的物品。" 92 | ); 93 | 94 | public static final EasyMessageList WRONG_NUMBER = new EasyMessageList( 95 | "&f数目输入错误,请输入正确的数字!" 96 | ); 97 | 98 | public static final EasyMessage LOAD_FAILED = new EasyMessage( 99 | "&c您的背包数据未被正确加载,请重新进入!" 100 | ); 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/configuration/depository/DepositoryItem.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.configuration.depository; 2 | 3 | import cc.carm.lib.easyplugin.utils.ItemStackFactory; 4 | import cc.carm.plugin.ultradepository.UltraDepository; 5 | import org.bukkit.Material; 6 | import org.bukkit.configuration.ConfigurationSection; 7 | import org.bukkit.inventory.ItemStack; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | public class DepositoryItem { 15 | 16 | final Depository depository; 17 | 18 | final @NotNull Material material; 19 | final int data; 20 | 21 | int slot; 22 | 23 | double price; 24 | int limit; 25 | 26 | @Nullable String name; 27 | @Nullable List lore; 28 | 29 | public DepositoryItem(@NotNull Depository depository, 30 | @NotNull Material material, int data, 31 | int slot, double price, int limit, 32 | @Nullable String name, @Nullable List lore) { 33 | this.depository = depository; 34 | this.material = material; 35 | this.data = data; 36 | this.slot = slot; 37 | this.price = price; 38 | this.limit = limit; 39 | this.name = name; 40 | this.lore = lore; 41 | } 42 | 43 | public Depository getDepository() { 44 | return depository; 45 | } 46 | 47 | public @NotNull String getTypeID() { 48 | return UltraDepository.getDepositoryManager().getItemTypeID(getMaterial(), getData()); 49 | } 50 | 51 | public @NotNull Material getMaterial() { 52 | return material; 53 | } 54 | 55 | public int getData() { 56 | return data; 57 | } 58 | 59 | public int getSlot() { 60 | return slot; 61 | } 62 | 63 | public double getPrice() { 64 | return price; 65 | } 66 | 67 | public int getLimit() { 68 | return limit; 69 | } 70 | 71 | public @Nullable String getName() { 72 | return name; 73 | } 74 | 75 | public @Nullable List getLore() { 76 | return lore; 77 | } 78 | 79 | public ItemStack getRawItem(int amount) { 80 | return new ItemStack(getMaterial(), amount, (short) getData()); 81 | } 82 | 83 | public ItemStack getDisplayItem() { 84 | ItemStackFactory factory = new ItemStackFactory(getRawItem(1)); 85 | if (getName() != null) factory.setDisplayName(getName()); 86 | if (getLore() != null) factory.setLore(getLore()); 87 | return factory.toItemStack(); 88 | } 89 | 90 | @Override 91 | public boolean equals(Object o) { 92 | if (this == o) return true; 93 | if (o == null || getClass() != o.getClass()) return false; 94 | DepositoryItem that = (DepositoryItem) o; 95 | return data == that.data && material == that.material; 96 | } 97 | 98 | @Override 99 | public int hashCode() { 100 | return Objects.hash(material, data); 101 | } 102 | 103 | public static DepositoryItem readFrom(Depository depository, String typeID, ConfigurationSection section) { 104 | try { 105 | Material material; 106 | int data = 0; 107 | if (typeID.contains(":")) { 108 | String[] args = typeID.split(":"); 109 | material = Material.matchMaterial(args[0]); 110 | data = Integer.parseInt(args[1]); 111 | } else { 112 | material = Material.matchMaterial(typeID); 113 | } 114 | 115 | if (material == null) throw new NullPointerException(typeID); 116 | return new DepositoryItem( 117 | depository, material, data, 118 | section.getInt("slot", 0), 119 | section.getDouble("price", -1), 120 | section.getInt("limit", -1), 121 | section.getString("name", material.name()), 122 | section.getStringList("lore") 123 | ); 124 | 125 | } catch (Exception ex) { 126 | UltraDepository.getInstance().error("没有与 " + typeID + " 匹配的物品!"); 127 | UltraDepository.getInstance().error("No match material of " + typeID + " !"); 128 | return null; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/util/JarResourceUtils.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.util; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.io.*; 6 | import java.net.URI; 7 | import java.net.URISyntaxException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Scanner; 12 | import java.util.zip.ZipEntry; 13 | import java.util.zip.ZipInputStream; 14 | 15 | @SuppressWarnings("ResultOfMethodCallIgnored") 16 | public class JarResourceUtils { 17 | public static final char JAR_SEPARATOR = '/'; 18 | 19 | public static @Nullable String[] readResource(@Nullable InputStream resourceStream) { 20 | if (resourceStream == null) return null; 21 | try (Scanner scanner = new Scanner(resourceStream, StandardCharsets.UTF_8.name())) { 22 | List contents = new ArrayList<>(); 23 | while (scanner.hasNextLine()) { 24 | contents.add(scanner.nextLine()); 25 | } 26 | return contents.toArray(new String[0]); 27 | } catch (Exception e) { 28 | return null; 29 | } 30 | } 31 | 32 | public static void copyFolderFromJar(String folderName, File destFolder, CopyOption option) 33 | throws IOException { 34 | copyFolderFromJar(folderName, destFolder, option, null); 35 | } 36 | 37 | public static void copyFolderFromJar(String folderName, File destFolder, 38 | CopyOption option, PathTrimmer trimmer) throws IOException { 39 | if (!destFolder.exists()) 40 | destFolder.mkdirs(); 41 | 42 | byte[] buffer = new byte[1024]; 43 | 44 | File fullPath; 45 | String path = JarResourceUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 46 | if (trimmer != null) 47 | path = trimmer.trim(path); 48 | try { 49 | if (!path.startsWith("file")) 50 | path = "file://" + path; 51 | 52 | fullPath = new File(new URI(path)); 53 | } catch (URISyntaxException e) { 54 | e.printStackTrace(); 55 | return; 56 | } 57 | 58 | ZipInputStream zis = new ZipInputStream(new FileInputStream(fullPath)); 59 | 60 | ZipEntry entry; 61 | while ((entry = zis.getNextEntry()) != null) { 62 | if (!entry.getName().startsWith(folderName + JAR_SEPARATOR)) 63 | continue; 64 | 65 | String fileName = entry.getName(); 66 | 67 | if (fileName.charAt(fileName.length() - 1) == JAR_SEPARATOR) { 68 | File file = new File(destFolder + File.separator + fileName); 69 | if (file.isFile()) { 70 | file.delete(); 71 | } 72 | file.mkdirs(); 73 | continue; 74 | } 75 | 76 | File file = new File(destFolder + File.separator + fileName); 77 | if (option == CopyOption.COPY_IF_NOT_EXIST && file.exists()) 78 | continue; 79 | 80 | if (!file.getParentFile().exists()) 81 | file.getParentFile().mkdirs(); 82 | 83 | if (!file.exists()) 84 | file.createNewFile(); 85 | FileOutputStream fos = new FileOutputStream(file); 86 | 87 | int len; 88 | while ((len = zis.read(buffer)) > 0) { 89 | fos.write(buffer, 0, len); 90 | } 91 | fos.close(); 92 | } 93 | 94 | zis.closeEntry(); 95 | zis.close(); 96 | } 97 | 98 | public enum CopyOption { 99 | COPY_IF_NOT_EXIST, REPLACE_IF_EXIST 100 | } 101 | 102 | @FunctionalInterface 103 | public interface PathTrimmer { 104 | String trim(String original); 105 | } 106 | } -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/manager/EconomyManager.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.manager; 2 | 3 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 4 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 5 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 6 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 7 | import cc.carm.plugin.ultradepository.data.DepositoryData; 8 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 9 | import cc.carm.plugin.ultradepository.data.UserData; 10 | import cc.carm.plugin.ultradepository.event.DepositorySellItemEvent; 11 | import cc.carm.plugin.ultradepository.hooker.VaultHooker; 12 | import org.bukkit.Bukkit; 13 | import org.bukkit.entity.Player; 14 | 15 | import java.math.BigDecimal; 16 | import java.math.RoundingMode; 17 | 18 | public class EconomyManager { 19 | 20 | VaultHooker hooker; 21 | boolean initialized; 22 | 23 | public EconomyManager() { 24 | this.hooker = new VaultHooker(); 25 | } 26 | 27 | public boolean initialize() { 28 | return initialized = this.hooker.setupEconomy(); 29 | } 30 | 31 | public boolean isInitialized() { 32 | return initialized; 33 | } 34 | 35 | public VaultHooker getHooker() { 36 | return hooker; 37 | } 38 | 39 | public double sell(Player player, double price, int amount) { 40 | if (!isInitialized()) return 0D; 41 | BigDecimal money = BigDecimal.valueOf(price * amount).setScale(2, RoundingMode.DOWN); 42 | getHooker().addMoney(player, money.doubleValue()); 43 | return money.doubleValue(); 44 | } 45 | 46 | public void sellAllItem(Player player, UserData userData) { 47 | sellAllItem(player, userData, true); 48 | } 49 | 50 | public void sellAllItem(Player player, UserData userData, boolean playSound) { 51 | userData.getDepositories().values().stream().map(DepositoryData::getSource) 52 | .forEach(depositoryData -> sellAllItem(player, userData, depositoryData, false)); 53 | if (playSound) PluginConfig.Sounds.SELL_SUCCESS.play(player); 54 | } 55 | 56 | 57 | public void sellAllItem(Player player, UserData userData, Depository depository) { 58 | sellAllItem(player, userData, depository, true); 59 | } 60 | 61 | public void sellAllItem(Player player, UserData userData, Depository depository, boolean playSound) { 62 | depository.getItems().values().forEach(value -> sellAllItem(player, userData, value, false)); 63 | if (playSound) PluginConfig.Sounds.SELL_SUCCESS.play(player); 64 | } 65 | 66 | 67 | public void sellAllItem(Player player, UserData userData, DepositoryItem depositoryItem) { 68 | sellAllItem(player, userData, depositoryItem, true); 69 | } 70 | 71 | public void sellAllItem(Player player, UserData userData, DepositoryItem depositoryItem, boolean playSound) { 72 | DepositoryItemData itemData = userData.getItemData(depositoryItem); 73 | int amount = itemData.getAmount(); 74 | int sold = itemData.getSold(); 75 | int limit = depositoryItem.getLimit(); 76 | int finalAmount = Math.min(amount, (limit - sold)); 77 | if (finalAmount > 0) sellItem(player, userData, depositoryItem, finalAmount, false); 78 | if (playSound) PluginConfig.Sounds.SELL_SUCCESS.play(player); 79 | } 80 | 81 | public void sellItem(Player player, UserData userData, DepositoryItem depositoryItem, int amount) { 82 | sellItem(player, userData, depositoryItem, amount, true); 83 | } 84 | 85 | public void sellItem(Player player, UserData userData, DepositoryItem depositoryItem, int amount, boolean playSound) { 86 | int[] changes = userData.getItemData(depositoryItem).applyChanges(-amount, amount); 87 | double money = sell(player, depositoryItem.getPrice(), amount); 88 | Bukkit.getPluginManager().callEvent(new DepositorySellItemEvent( 89 | player, depositoryItem, changes[0] + amount, changes[0], money 90 | )); 91 | 92 | PluginMessages.ITEM_SOLD.send(player, new Object[]{depositoryItem.getName(), amount, money}); 93 | if (playSound) PluginConfig.Sounds.SELL_SUCCESS.play(player); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/impl/YAMLStorage.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage.impl; 2 | 3 | import cc.carm.lib.easyplugin.configuration.values.ConfigValue; 4 | import cc.carm.plugin.ultradepository.UltraDepository; 5 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 6 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 7 | import cc.carm.plugin.ultradepository.data.DepositoryData; 8 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 9 | import cc.carm.plugin.ultradepository.data.UserData; 10 | import cc.carm.plugin.ultradepository.storage.DataStorage; 11 | import cc.carm.plugin.ultradepository.util.DateIntUtil; 12 | import org.bukkit.configuration.ConfigurationSection; 13 | import org.bukkit.configuration.file.YamlConfiguration; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.UUID; 21 | 22 | public class YAMLStorage implements DataStorage { 23 | 24 | private static final ConfigValue FILE_PATH = new ConfigValue<>( 25 | "storage.file-path", String.class, "data" 26 | ); 27 | 28 | private File dataContainer; 29 | 30 | @Override 31 | public boolean initialize() { 32 | dataContainer = new File(UltraDepository.getInstance().getDataFolder(), FILE_PATH.get()); 33 | if (!dataContainer.exists()) { 34 | return dataContainer.mkdir(); 35 | } else { 36 | return dataContainer.isDirectory(); 37 | } 38 | } 39 | 40 | @Override 41 | public void shutdown() { 42 | // 似乎没什么需要做的? 43 | dataContainer = null; 44 | } 45 | 46 | public File getDataContainer() { 47 | return dataContainer; 48 | } 49 | 50 | @Override 51 | public @Nullable UserData loadData(@NotNull UUID uuid) { 52 | File userDataFile = new File(getDataContainer(), uuid + ".yml"); 53 | if (!userDataFile.exists()) { 54 | UltraDepository.getInstance().debug("当前文件夾内不存在玩家 " + uuid + " 的数据,视作新档。"); 55 | return null; 56 | } 57 | 58 | YamlConfiguration userDataConfig = YamlConfiguration.loadConfiguration(userDataFile); 59 | int dateInt = userDataConfig.getInt("date", DateIntUtil.getCurrentDate()); 60 | UserData userData = new UserData(uuid, new HashMap<>(), dateInt); 61 | 62 | ConfigurationSection depositoriesSection = userDataConfig.getConfigurationSection("depositories"); 63 | if (depositoriesSection != null) { 64 | for (String depositoryID : depositoriesSection.getKeys(false)) { 65 | 66 | Depository depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 67 | if (depository == null) continue; 68 | 69 | ConfigurationSection depositorySection = depositoriesSection.getConfigurationSection(depositoryID); 70 | if (depositorySection == null) continue; 71 | 72 | DepositoryData depositoryData = DepositoryData.emptyContents(depository, userData); 73 | 74 | for (String itemTypeID : depositorySection.getKeys(false)) { 75 | 76 | DepositoryItem item = depository.getItems().get(DataStorage.getFixedTypeID(itemTypeID)); 77 | if (item == null) continue; 78 | 79 | ConfigurationSection itemSection = depositorySection.getConfigurationSection(itemTypeID); 80 | if (itemSection == null) continue; 81 | 82 | depositoryData.getContents().put(item.getTypeID(), new DepositoryItemData( 83 | item, depositoryData, 84 | itemSection.getInt("amount", 0), 85 | itemSection.getInt("sold", 0) 86 | )); 87 | 88 | } 89 | 90 | if (!depositoryData.getContents().isEmpty()) userData.setDepository(depositoryData); 91 | } 92 | } 93 | return userData; 94 | } 95 | 96 | @Override 97 | public void saveUserData(@NotNull UserData data) throws IOException { 98 | YamlConfiguration userDataConfig = new YamlConfiguration(); 99 | userDataConfig.set("date", data.getDateInt()); 100 | userDataConfig.createSection("depositories", data.serializeToMap()); 101 | userDataConfig.save(new File(getDataContainer(), data.getUserUUID() + ".yml")); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/manager/UserManager.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.manager; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.data.UserData; 5 | import cc.carm.plugin.ultradepository.storage.DataStorage; 6 | import cc.carm.plugin.ultradepository.util.DateIntUtil; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.HashMap; 13 | import java.util.UUID; 14 | 15 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") 16 | public class UserManager { 17 | 18 | private final HashMap dataCache = new HashMap<>(); 19 | 20 | public HashMap getDataCache() { 21 | return dataCache; 22 | } 23 | 24 | public void loadPlayersData() { 25 | if (Bukkit.getOnlinePlayers().size() < 1) return; 26 | 27 | UltraDepository.getInstance().log("加载当前在线玩家数据..."); 28 | // 用于热加载时重载玩家数据。 29 | Bukkit.getOnlinePlayers().forEach(player -> loadDataCache(player.getUniqueId())); 30 | } 31 | 32 | public @NotNull UserData loadDataCache(@NotNull UUID userUUID) { 33 | UserData data = loadData(userUUID); 34 | UltraDepository.getUserManager().getDataCache().put(userUUID, data); 35 | return data; 36 | } 37 | 38 | 39 | public boolean removeDataCache(@NotNull UUID userUUID) { 40 | boolean contains = getDataCache().containsKey(userUUID); 41 | getDataCache().remove(userUUID); 42 | return contains; 43 | } 44 | 45 | public @Nullable UserData getData(@NotNull UUID userUUID) { 46 | return getDataCache().get(userUUID); 47 | } 48 | 49 | public @NotNull UserData getData(@NotNull Player player) { 50 | return getDataCache().get(player.getUniqueId()); 51 | } 52 | 53 | public @NotNull UserData loadData(@NotNull UUID userUUID) { 54 | try { 55 | long start = System.currentTimeMillis(); 56 | DataStorage storage = UltraDepository.getStorage(); 57 | UltraDepository.getInstance().debug("正通过 " + storage.getClass().getSimpleName() + " 加载 " + userUUID + " 的用户数据...(" + System.currentTimeMillis() + ")"); 58 | UserData data = UltraDepository.getStorage().loadData(userUUID); 59 | 60 | if (data == null) { 61 | UltraDepository.getInstance().debug("当前还不存在玩家 " + userUUID + " 的数据,视作新档。"); 62 | return new UserData(userUUID, new HashMap<>(), DateIntUtil.getCurrentDate()); 63 | } 64 | 65 | UltraDepository.getInstance().debug("通过 " + storage.getClass().getSimpleName() + "加载 " + userUUID + " 的用户数据完成," 66 | + "耗时 " + (System.currentTimeMillis() - start) + "ms。"); 67 | 68 | return data; 69 | } catch (Exception e) { 70 | UltraDepository.getInstance().error("无法正常加载玩家数据,玩家操作将不会被保存,请检查数据配置!"); 71 | UltraDepository.getInstance().error("Could not load user's data, please check the data configuration!"); 72 | e.printStackTrace(); 73 | return new UserData(userUUID, new HashMap<>(), DateIntUtil.getCurrentDate()); 74 | } 75 | } 76 | 77 | public void unloadData(UUID uuid, boolean save) { 78 | UserData data = getData(uuid); 79 | if (data == null) return; 80 | if (save) saveData(data); 81 | removeDataCache(uuid); 82 | } 83 | 84 | public void saveData(UserData data) { 85 | try { 86 | long start = System.currentTimeMillis(); 87 | DataStorage storage = UltraDepository.getStorage(); 88 | 89 | UltraDepository.getInstance().debug("正通过 " + storage.getClass().getSimpleName() + " 保存 " + data.getUserUUID() + " 的用户数据...(" + System.currentTimeMillis() + ")"); 90 | storage.saveUserData(data); 91 | 92 | UltraDepository.getInstance().debug("通过 " + storage.getClass().getSimpleName() + " 保存 " + data.getUserUUID() + " 的用户数据完成," + 93 | "耗时 " + (System.currentTimeMillis() - start) + "ms。"); 94 | 95 | } catch (Exception e) { 96 | UltraDepository.getInstance().error("无法正常保存玩家数据,请检查数据配置!"); 97 | UltraDepository.getInstance().error("Could not save user's data, please check the data configuration!"); 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | public void saveAll() { 103 | dataCache.values().forEach(this::saveData); 104 | } 105 | 106 | public boolean isCollectEnabled(Player player) { 107 | return player.hasPermission("UltraDepository.use") && 108 | player.hasPermission("UltraDepository.auto") && 109 | player.hasPermission("UltraDepository.auto.enable"); 110 | } 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/impl/MySQLStorage.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage.impl; 2 | 3 | import cc.carm.lib.easyplugin.configuration.values.ConfigValue; 4 | import cc.carm.lib.easysql.EasySQL; 5 | import cc.carm.lib.easysql.api.SQLManager; 6 | import cc.carm.lib.easysql.api.action.query.PreparedQueryAction; 7 | import cc.carm.plugin.ultradepository.UltraDepository; 8 | import cc.carm.plugin.ultradepository.data.UserData; 9 | import cc.carm.plugin.ultradepository.util.DatabaseTable; 10 | import cc.carm.plugin.ultradepository.util.DateIntUtil; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.sql.Date; 15 | import java.sql.ResultSet; 16 | import java.sql.SQLException; 17 | import java.util.HashMap; 18 | import java.util.UUID; 19 | 20 | public class MySQLStorage extends JSONStorage { 21 | 22 | private static final ConfigValue DRIVER_NAME = new ConfigValue<>( 23 | "storage.mysql.driver", String.class, 24 | "com.mysql.jdbc.Driver" 25 | ); 26 | 27 | private static final ConfigValue URL = new ConfigValue<>( 28 | "storage.mysql.url", String.class, 29 | "jdbc:mysql://127.0.0.1:3306/minecraft" 30 | ); 31 | 32 | private static final ConfigValue USERNAME = new ConfigValue<>( 33 | "storage.mysql.username", String.class, 34 | "root" 35 | ); 36 | 37 | private static final ConfigValue PASSWORD = new ConfigValue<>( 38 | "storage.mysql.password", String.class, 39 | "password" 40 | ); 41 | 42 | private static final ConfigValue TABLE_NAME = new ConfigValue<>( 43 | "storage.mysql.table", String.class, 44 | "ud_data" 45 | ); 46 | 47 | SQLManager sqlManager; 48 | DatabaseTable userDataTable; 49 | 50 | @Override 51 | public boolean initialize() { 52 | 53 | try { 54 | UltraDepository.getInstance().log(" 尝试连接到数据库..."); 55 | this.sqlManager = EasySQL.createManager(DRIVER_NAME.get(), URL.get(), USERNAME.get(), PASSWORD.get()); 56 | this.sqlManager.setDebugMode(UltraDepository.getInstance().isDebugging()); 57 | } catch (Exception exception) { 58 | UltraDepository.getInstance().error("无法连接到数据库,请检查配置文件。"); 59 | UltraDepository.getInstance().error("Could not connect to the database, please check the configuration."); 60 | exception.printStackTrace(); 61 | return false; 62 | } 63 | 64 | try { 65 | UltraDepository.getInstance().log(" 创建插件所需表..."); 66 | 67 | this.userDataTable = new DatabaseTable( 68 | TABLE_NAME.getOptional().orElse("ud_data"), 69 | new String[]{ 70 | "`uuid` VARCHAR(36) NOT NULL PRIMARY KEY", // 用户的UUID 71 | "`data` MEDIUMTEXT NOT NULL",// 背包内具体物品 72 | "`day` DATE NOT NULL", // 记录卖出数量的所在天 73 | }); 74 | 75 | getUserDataTable().createTable(sqlManager); 76 | 77 | } catch (SQLException exception) { 78 | UltraDepository.getInstance().error("无法创建插件所需的表,请检查数据库权限。"); 79 | UltraDepository.getInstance().error("Could not create necessary tables, please check the database privileges."); 80 | exception.printStackTrace(); 81 | return false; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | @Override 88 | public void shutdown() { 89 | UltraDepository.getInstance().log(" 关闭数据库连接..."); 90 | EasySQL.shutdownManager(getSQLManager()); 91 | } 92 | 93 | @Override 94 | public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { 95 | return createAction(uuid).executeFunction((query) -> { 96 | ResultSet resultSet = query.getResultSet(); 97 | 98 | if (resultSet == null || !resultSet.next()) return null; 99 | 100 | Date date = resultSet.getDate("day"); 101 | UserData data = new UserData(uuid, new HashMap<>(), DateIntUtil.getDateInt(date)); 102 | 103 | loadDepositoriesInto(data, PARSER.parse(resultSet.getString("data"))); 104 | 105 | return data; 106 | }); 107 | } 108 | 109 | @Override 110 | public void saveUserData(@NotNull UserData data) throws Exception { 111 | getSQLManager().createReplace(getUserDataTable().getTableName()) 112 | .setColumnNames("uuid", "data", "day") 113 | .setParams(data.getUserUUID(), serializeDepositories(data), data.getDate()) 114 | .execute(); 115 | } 116 | 117 | private SQLManager getSQLManager() { 118 | return sqlManager; 119 | } 120 | 121 | public DatabaseTable getUserDataTable() { 122 | return userDataTable; 123 | } 124 | 125 | private PreparedQueryAction createAction(UUID uuid) { 126 | return getUserDataTable().createQuery(getSQLManager()) 127 | .addCondition("uuid", uuid.toString()) 128 | .setLimit(1).build(); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/hooker/PAPIExpansion.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.hooker; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 5 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 6 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 7 | import cc.carm.plugin.ultradepository.data.UserData; 8 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.plugin.java.JavaPlugin; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | public class PAPIExpansion extends PlaceholderExpansion { 17 | 18 | private static final List PLACEHOLDERS = Arrays.asList( 19 | "%UltraDepository_amount__%", 20 | "%UltraDepository_sold__%", 21 | "%UltraDepository_price__%", 22 | "%UltraDepository_remain__%", 23 | "%UltraDepository_capacity_%", 24 | "%UltraDepository_used_%", 25 | "%UltraDepository_usable_%" 26 | ); 27 | 28 | private final JavaPlugin plugin; 29 | 30 | public PAPIExpansion(JavaPlugin plugin) { 31 | this.plugin = plugin; 32 | } 33 | 34 | @Override 35 | public @NotNull List getPlaceholders() { 36 | return PLACEHOLDERS; 37 | } 38 | 39 | @Override 40 | public boolean canRegister() { 41 | return true; 42 | } 43 | 44 | @Override 45 | public @NotNull String getAuthor() { 46 | return plugin.getDescription().getAuthors().toString(); 47 | } 48 | 49 | @Override 50 | public @NotNull String getIdentifier() { 51 | return plugin.getDescription().getName(); 52 | } 53 | 54 | @Override 55 | public @NotNull String getVersion() { 56 | return plugin.getDescription().getVersion(); 57 | } 58 | 59 | @Override 60 | public String onPlaceholderRequest(Player player, @NotNull String identifier) { 61 | if (player == null) return "加载中..."; 62 | String[] args = identifier.split("_"); 63 | 64 | if (args.length < 1) { 65 | return "Error Params"; 66 | } 67 | 68 | UserData data = UltraDepository.getUserManager().getData(player); 69 | 70 | switch (args[0].toLowerCase()) { 71 | case "amount": { 72 | if (args.length < 3) return "Error Params"; 73 | Integer amount = data.getItemAmount(args[1], args[2]); 74 | if (amount == null) return "Depository or Item not exists"; 75 | else return amount.toString(); 76 | } 77 | case "sold": { 78 | if (args.length < 3) return "Error Params"; 79 | Integer sold = data.getItemSold(args[1], args[2]); 80 | if (sold == null) return "Depository or Item not exists"; 81 | else return sold.toString(); 82 | } 83 | case "remain": { 84 | if (args.length < 2) return "Error Params"; 85 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 86 | if (depository == null) return "Depository not exists"; 87 | DepositoryItem item = depository.getItems().get(args[2]); 88 | if (item == null) return "Depository Item not exists"; 89 | int limit = item.getLimit(); 90 | DepositoryItemData itemData = data.getItemData(item); 91 | return Integer.toString(limit - itemData.getSold()); 92 | } 93 | case "limit": { 94 | if (args.length < 3) return "Error Params"; 95 | Integer limit = UltraDepository.getDepositoryManager().getItemSellLimit(args[1], args[2]); 96 | if (limit == null) return "Depository or Item not exists"; 97 | else return limit.toString(); 98 | } 99 | case "price": { 100 | if (args.length < 3) return "Error Params"; 101 | Double price = UltraDepository.getDepositoryManager().getItemPrice(args[1], args[2]); 102 | if (price == null) return "Depository or Item not exists"; 103 | else return price.toString(); 104 | } 105 | case "capacity": { 106 | if (args.length < 2) return "Error Params"; 107 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 108 | if (depository == null) return "Depository not exists"; 109 | int capacity = depository.getCapacity().getPlayerCapacity(player); 110 | return capacity < 0 ? "∞" : Integer.toString(capacity); 111 | } 112 | case "used": { 113 | if (args.length < 2) return "Error Params"; 114 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 115 | if (depository == null) return "Depository not exists"; 116 | return Integer.toString(data.getDepositoryData(depository).getUsedCapacity()); 117 | } 118 | case "usable": { 119 | if (args.length < 2) return "Error Params"; 120 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 121 | if (depository == null) return "Depository not exists"; 122 | int max = depository.getCapacity().getPlayerCapacity(player); 123 | int used = data.getDepositoryData(depository).getUsedCapacity(); 124 | return max < 0 ? "∞" : Integer.toString(max - used); 125 | } 126 | case "version": { 127 | return getVersion(); 128 | } 129 | default: { 130 | return "参数错误"; 131 | } 132 | } 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/storage/impl/JSONStorage.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.storage.impl; 2 | 3 | import cc.carm.lib.easyplugin.configuration.values.ConfigValue; 4 | import cc.carm.plugin.ultradepository.UltraDepository; 5 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 6 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 7 | import cc.carm.plugin.ultradepository.data.DepositoryData; 8 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 9 | import cc.carm.plugin.ultradepository.data.UserData; 10 | import cc.carm.plugin.ultradepository.storage.DataStorage; 11 | import com.google.gson.Gson; 12 | import com.google.gson.JsonElement; 13 | import com.google.gson.JsonObject; 14 | import com.google.gson.JsonParser; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.io.File; 19 | import java.io.FileReader; 20 | import java.io.FileWriter; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | import java.util.UUID; 24 | 25 | public class JSONStorage implements DataStorage { 26 | 27 | private static final ConfigValue FILE_PATH = new ConfigValue<>( 28 | "storage.file-path", String.class, "data" 29 | ); 30 | 31 | private File dataContainer; 32 | 33 | protected static final Gson GSON = new Gson(); 34 | protected static final JsonParser PARSER = new JsonParser(); 35 | 36 | @Override 37 | public boolean initialize() { 38 | dataContainer = new File(UltraDepository.getInstance().getDataFolder(), FILE_PATH.get()); 39 | if (!dataContainer.exists()) { 40 | return dataContainer.mkdir(); 41 | } else { 42 | return dataContainer.isDirectory(); 43 | } 44 | } 45 | 46 | @Override 47 | public void shutdown() { 48 | // 似乎没什么需要做的? 49 | dataContainer = null; 50 | } 51 | 52 | public File getDataContainer() { 53 | return dataContainer; 54 | } 55 | 56 | @Override 57 | public @Nullable UserData loadData(@NotNull UUID uuid) throws Exception { 58 | File userDataFile = new File(getDataContainer(), uuid + ".json"); 59 | if (!userDataFile.exists()) { 60 | UltraDepository.getInstance().debug("当前文件夾内不存在玩家 " + uuid + " 的数据,视作新档。"); 61 | return null; 62 | } 63 | 64 | JsonElement dataElement = PARSER.parse(new FileReader(userDataFile)); 65 | if (!dataElement.isJsonObject()) throw new NullPointerException(userDataFile.getName()); 66 | JsonObject dataObject = dataElement.getAsJsonObject(); 67 | 68 | int dateInt = dataObject.get("date").getAsInt(); 69 | UserData userData = new UserData(uuid, new HashMap<>(), dateInt); 70 | 71 | loadDepositoriesInto(userData, dataObject.getAsJsonObject("depositories")); 72 | 73 | return userData; 74 | } 75 | 76 | @Override 77 | public void saveUserData(@NotNull UserData data) throws Exception { 78 | JsonObject dataObject = new JsonObject(); 79 | dataObject.addProperty("date", data.getDateInt()); 80 | dataObject.add("depositories", saveDepositoriesToJson(data)); 81 | 82 | FileWriter writer = new FileWriter(new File(getDataContainer(), data.getUserUUID() + ".json")); 83 | writer.write(GSON.toJson(dataObject)); 84 | writer.flush(); 85 | writer.close(); 86 | } 87 | 88 | public static JsonElement saveDepositoriesToJson(UserData data) { 89 | return GSON.toJsonTree(data.serializeToMap()); 90 | } 91 | 92 | public static String serializeDepositories(UserData data) { 93 | return GSON.toJson(saveDepositoriesToJson(data)); 94 | } 95 | 96 | public static void loadDepositoriesInto(UserData data, JsonElement depositoriesElement) { 97 | if (depositoriesElement == null || !depositoriesElement.isJsonObject()) return; 98 | 99 | for (Map.Entry entry : depositoriesElement.getAsJsonObject().entrySet()) { 100 | Depository depository = UltraDepository.getDepositoryManager().getDepository(entry.getKey()); 101 | if (depository == null) continue; 102 | 103 | DepositoryData contentsData = parseContentsData(depository, data, entry.getValue()); 104 | if (contentsData != null) data.setDepository(contentsData); 105 | 106 | } 107 | } 108 | 109 | public static DepositoryData parseContentsData(@NotNull Depository source, 110 | @NotNull UserData owner, 111 | @NotNull JsonElement contentsElement) { 112 | if (!contentsElement.isJsonObject()) return null; 113 | JsonObject contentsObject = contentsElement.getAsJsonObject(); 114 | 115 | DepositoryData data = DepositoryData.emptyContents(source, owner); 116 | for (Map.Entry entry : contentsObject.entrySet()) { 117 | DepositoryItem item = source.getItems().get(DataStorage.getFixedTypeID(entry.getKey())); 118 | if (item == null) continue; 119 | 120 | DepositoryItemData itemData = parseItemData(item, data, entry.getValue()); 121 | if (itemData != null) data.getContents().put(item.getTypeID(), itemData); 122 | 123 | } 124 | return data; 125 | } 126 | 127 | 128 | public static DepositoryItemData parseItemData(@NotNull DepositoryItem source, 129 | @NotNull DepositoryData owner, 130 | @NotNull JsonElement itemElement) { 131 | if (!itemElement.isJsonObject()) return null; 132 | JsonObject itemObject = itemElement.getAsJsonObject(); 133 | 134 | int amount = itemObject.has("amount") ? itemObject.get("amount").getAsInt() : 0; 135 | int sold = itemObject.has("sold") ? itemObject.get("sold").getAsInt() : 0; 136 | if (amount == 0 && sold == 0) return null; 137 | 138 | return new DepositoryItemData(source, owner, amount, sold); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/ui/DepositoryGUI.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.ui; 2 | 3 | import cc.carm.lib.easyplugin.gui.GUI; 4 | import cc.carm.lib.easyplugin.gui.GUIItem; 5 | import cc.carm.lib.easyplugin.utils.ItemStackFactory; 6 | import cc.carm.plugin.ultradepository.UltraDepository; 7 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 8 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 9 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 10 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 11 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 12 | import cc.carm.plugin.ultradepository.data.UserData; 13 | import org.bukkit.Material; 14 | import org.bukkit.entity.Player; 15 | import org.bukkit.event.inventory.ClickType; 16 | import org.bukkit.inventory.ItemStack; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.stream.IntStream; 22 | 23 | public class DepositoryGUI extends GUI { 24 | 25 | Player player; 26 | UserData userData; 27 | Depository depository; 28 | 29 | private DepositoryGUI(Player player, Depository depository) { 30 | super(depository.getGUIConfiguration().getGUIType(), depository.getGUIConfiguration().getTitle()); 31 | 32 | this.player = player; 33 | this.userData = UltraDepository.getUserManager().getData(player); 34 | this.depository = depository; 35 | 36 | setupItems(); 37 | } 38 | 39 | public void setupItems() { 40 | loadConfigItems(); 41 | loadDepositoryItems(); 42 | } 43 | 44 | public void loadConfigItems() { 45 | depository.getGUIConfiguration().setupItems(player, this); 46 | } 47 | 48 | public void loadDepositoryItems() { 49 | depository.getItems().values().forEach(depositoryItem -> setItem(depositoryItem.getSlot(), createGUIItem(depositoryItem))); 50 | } 51 | 52 | private GUIItem createGUIItem(DepositoryItem item) { 53 | return new GUIItem(getItemIcon(player, userData, item)) { 54 | @Override 55 | public void onClick(ClickType type) { 56 | DepositoryItemData itemData = userData.getItemData(item); 57 | if (itemData.getAmount() < 1) { 58 | PluginConfig.Sounds.SELL_FAIL.play(player); 59 | PluginMessages.NO_ENOUGH_ITEM.send(player); 60 | return; 61 | } 62 | 63 | if (canSell(item) && type == ClickType.LEFT) { 64 | int sellableAmount = item.getLimit() - itemData.getSold(); 65 | if (sellableAmount >= 1) { 66 | SellItemGUI.open(player, userData, itemData, depository, item); 67 | } else { 68 | PluginConfig.Sounds.SELL_FAIL.play(player); 69 | PluginMessages.ITEM_SOLD_LIMIT.send(player, new Object[]{sellableAmount, item.getLimit()}); 70 | player.closeInventory(); 71 | } 72 | } else if (type == ClickType.RIGHT) { 73 | 74 | if (hasEmptySlot(player)) { 75 | int takeoutAmount = Math.min(itemData.getAmount(), item.getMaterial().getMaxStackSize()); 76 | userData.removeItemAmount( 77 | item.getDepository().getIdentifier(), item.getTypeID(), takeoutAmount 78 | ); 79 | player.getInventory().addItem(item.getRawItem(takeoutAmount)); 80 | PluginMessages.ITEM_TAKEOUT.send(player, new Object[]{ 81 | item.getName(), takeoutAmount 82 | }); 83 | PluginConfig.Sounds.TAKEOUT.play(player); 84 | 85 | setDisplay(getItemIcon(player, userData, item)); // 刷新物品显示 86 | loadConfigItems(); // 更新配置中的其他物品 87 | updateView(); 88 | } else { 89 | PluginMessages.NO_SPACE.send(player); 90 | player.closeInventory(); 91 | } 92 | } 93 | } 94 | }; 95 | } 96 | 97 | public static boolean hasEmptySlot(Player player) { 98 | return IntStream.range(0, 36) 99 | .mapToObj(i -> player.getInventory().getItem(i)) 100 | .anyMatch(i -> i == null || i.getType() == Material.AIR); 101 | } 102 | 103 | public static ItemStack getItemIcon(@NotNull Player player, 104 | @NotNull UserData userData, 105 | @NotNull DepositoryItem item) { 106 | DepositoryItemData itemData = userData.getItemData(item); 107 | ItemStackFactory factory = new ItemStackFactory(item.getDisplayItem()); 108 | getExtraLore(player, itemData).forEach(factory::addLore); 109 | return factory.toItemStack(); 110 | } 111 | 112 | public static List getExtraLore(@NotNull Player player, 113 | @NotNull DepositoryItemData itemData) { 114 | DepositoryItem item = itemData.getSource(); 115 | int canSell = item.getLimit() - itemData.getSold(); 116 | 117 | List lore = new ArrayList<>(); 118 | if (canSell(item)) { 119 | lore.addAll(PluginConfig.General.AdditionalLore.AVAILABLE_FOR_SALE.get(player, new Object[]{ 120 | item.getName(), itemData.getAmount(), item.getPrice(), 121 | itemData.getSold(), canSell, item.getLimit() 122 | })); 123 | lore.addAll(PluginConfig.General.ClickLore.AVAILABLE_FOR_SALE.get(player, new Object[]{ 124 | item.getName(), itemData.getAmount(), item.getPrice() 125 | })); 126 | } else { 127 | lore.addAll(PluginConfig.General.AdditionalLore.NOT_FOR_SALE.get(player, new Object[]{ 128 | item.getName(), itemData.getAmount() 129 | })); 130 | lore.addAll(PluginConfig.General.ClickLore.NOT_FOR_SALE.get(player, new Object[]{ 131 | item.getName(), itemData.getAmount() 132 | })); 133 | } 134 | return lore; 135 | } 136 | 137 | public static boolean canSell(DepositoryItem item) { 138 | return UltraDepository.getEconomyManager().isInitialized() 139 | && item.getLimit() > 0 && item.getPrice() > 0; 140 | } 141 | 142 | public static void open(@NotNull Player player, @NotNull Depository depository) { 143 | player.closeInventory(); 144 | DepositoryGUI gui = new DepositoryGUI(player, depository); 145 | gui.openGUI(player); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/data/UserData.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.data; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 5 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 6 | import cc.carm.plugin.ultradepository.util.DateIntUtil; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.sql.Date; 11 | import java.util.HashMap; 12 | import java.util.LinkedHashMap; 13 | import java.util.Map; 14 | import java.util.UUID; 15 | 16 | public class UserData { 17 | 18 | public final UUID userUUID; 19 | 20 | Map depositories; 21 | 22 | int date; 23 | 24 | public UserData(UUID userUUID, 25 | Map depositories, int date) { 26 | this.userUUID = userUUID; 27 | this.depositories = depositories; 28 | this.date = date; 29 | } 30 | 31 | 32 | public @NotNull UUID getUserUUID() { 33 | return this.userUUID; 34 | } 35 | 36 | public @NotNull Map getDepositories() { 37 | return this.depositories; 38 | } 39 | 40 | public void setDepository(DepositoryData data) { 41 | this.depositories.put(data.getIdentifier(), data); 42 | } 43 | 44 | public @Nullable DepositoryData getDepositoryData(String depositoryID) { 45 | Depository depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 46 | if (depository == null) return null; 47 | return getDepositoryData(depository); 48 | } 49 | 50 | public @NotNull DepositoryData getDepositoryData(Depository depository) { 51 | getDepositories().putIfAbsent(depository.getIdentifier(), DepositoryData.emptyContents(depository, this)); 52 | return getDepositories().get(depository.getIdentifier()); 53 | } 54 | 55 | public @Nullable DepositoryItemData getItemData(@NotNull String depositoryID, @NotNull String typeID) { 56 | DepositoryData data = getDepositoryData(depositoryID); 57 | if (data == null) return null; 58 | return data.getItemData(typeID); 59 | } 60 | 61 | public @NotNull DepositoryItemData getItemData(@NotNull DepositoryItem itemType) { 62 | checkoutDate(); 63 | return getDepositoryData(itemType.getDepository()).getItemData(itemType); 64 | } 65 | 66 | public @Nullable Integer getItemAmount(@NotNull String depositoryID, @NotNull String typeID) { 67 | DepositoryItemData data = getItemData(depositoryID, typeID); 68 | if (data == null) return null; 69 | return data.getAmount(); 70 | } 71 | 72 | public @Nullable Integer getItemSold(@NotNull String depositoryID, @NotNull String typeID) { 73 | DepositoryItemData data = getItemData(depositoryID, typeID); 74 | if (data == null) return null; 75 | return data.getSold(); 76 | } 77 | 78 | 79 | public @Nullable Integer setItemAmount(@NotNull String depositoryID, @NotNull String typeID, int amount) { 80 | DepositoryItemData data = getItemData(depositoryID, typeID); 81 | if (data == null) return null; 82 | data.setAmount(Math.max(0, amount)); // 最小为0 83 | return data.getAmount(); 84 | } 85 | 86 | 87 | public @Nullable Integer setItemSold(@NotNull String depositoryID, @NotNull String typeID, int soldAmount) { 88 | DepositoryItemData data = getItemData(depositoryID, typeID); 89 | if (data == null) return null; 90 | data.setSold(Math.max(0, soldAmount)); 91 | return data.getSold(); 92 | } 93 | 94 | 95 | public @Nullable Integer addItemAmount(@NotNull String depositoryID, @NotNull String typeID, int amount) { 96 | Integer current = getItemAmount(depositoryID, typeID); 97 | if (current == null) return null; 98 | return setItemAmount(depositoryID, typeID, current + amount); 99 | } 100 | 101 | 102 | public @Nullable Integer addItemSold(@NotNull String depositoryID, @NotNull String typeID, int amount) { 103 | Integer current = getItemSold(depositoryID, typeID); 104 | if (current == null) return null; 105 | return setItemSold(depositoryID, typeID, current + amount); 106 | } 107 | 108 | 109 | public @Nullable Integer removeItemAmount(@NotNull String depositoryID, @NotNull String typeID, int amount) { 110 | return addItemAmount(depositoryID, typeID, -amount); 111 | } 112 | 113 | 114 | public @Nullable Integer removeItemSold(@NotNull String depositoryID, @NotNull String typeID, int amount) { 115 | return addItemSold(depositoryID, typeID, -amount); 116 | } 117 | 118 | 119 | public Date getDate() { 120 | return DateIntUtil.getDate(getDateInt()); 121 | } 122 | 123 | public int getDateInt() { 124 | return this.date; 125 | } 126 | 127 | 128 | public boolean isCurrentDay() { 129 | return this.date == DateIntUtil.getCurrentDate(); 130 | } 131 | 132 | 133 | public void checkoutDate() { 134 | if (isCurrentDay()) return; 135 | 136 | this.date = DateIntUtil.getCurrentDate(); //更新日期 137 | getDepositories().values().stream() 138 | .flatMap(value -> value.getContents().values().stream()) 139 | .forEach(DepositoryItemData::clearSold); 140 | } 141 | 142 | public Map>> serializeToMap() { 143 | Map>> values = new LinkedHashMap<>(); 144 | 145 | getDepositories().forEach((depositoryID, depositoryData) -> { 146 | Map> depositoryDataMap = new LinkedHashMap<>(); 147 | depositoryData.getContents().forEach((itemType, itemData) -> { 148 | Map itemDataMap = new HashMap<>(); 149 | if (itemData.getAmount() > 0) itemDataMap.put("amount", itemData.getAmount()); 150 | if (itemData.getSold() > 0) itemDataMap.put("sold", itemData.getSold()); 151 | if (!itemDataMap.isEmpty()) depositoryDataMap.put(itemType, itemDataMap); 152 | }); 153 | if (!depositoryDataMap.isEmpty()) values.put(depositoryID, depositoryDataMap); 154 | }); 155 | 156 | return values; 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/ui/SellItemGUI.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.ui; 2 | 3 | import cc.carm.lib.easyplugin.gui.GUI; 4 | import cc.carm.lib.easyplugin.gui.GUIItem; 5 | import cc.carm.lib.easyplugin.gui.GUIType; 6 | import cc.carm.lib.easyplugin.utils.ItemStackFactory; 7 | import cc.carm.plugin.ultradepository.UltraDepository; 8 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 9 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 10 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 11 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 12 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 13 | import cc.carm.plugin.ultradepository.data.UserData; 14 | import org.bukkit.entity.Player; 15 | import org.bukkit.event.inventory.ClickType; 16 | import org.bukkit.inventory.ItemStack; 17 | 18 | import java.math.BigDecimal; 19 | import java.math.RoundingMode; 20 | import java.util.List; 21 | 22 | import static cc.carm.plugin.ultradepository.configuration.PluginConfig.General.SellGUI.Items.*; 23 | 24 | public class SellItemGUI extends GUI { 25 | 26 | final Player player; 27 | final UserData userData; 28 | final DepositoryItemData itemData; 29 | final Depository configuration; 30 | final DepositoryItem item; 31 | 32 | ItemStack itemDisplay; 33 | 34 | int currentAmount; 35 | 36 | public SellItemGUI(Player player, UserData userData, DepositoryItemData itemData, 37 | Depository configuration, DepositoryItem item) { 38 | 39 | super(GUIType.FOUR_BY_NINE, PluginConfig.General.SellGUI.TITLE.get(player, new String[]{ 40 | configuration.getName(), item.getName() 41 | })); 42 | 43 | this.player = player; 44 | this.userData = userData; 45 | this.itemData = itemData; 46 | this.configuration = configuration; 47 | this.item = item; 48 | this.itemDisplay = item.getDisplayItem(); 49 | 50 | load(1); 51 | } 52 | 53 | private void load(int amount) { 54 | this.currentAmount = Math.max(1, amount); // 不可小于1 55 | ItemStackFactory factory = new ItemStackFactory(this.itemDisplay); 56 | List additionalLore = PluginConfig.General.AdditionalLore.AVAILABLE_FOR_SALE 57 | .get(player, new Object[]{ 58 | getItemName(), getDepositoryAmount(), getItemPrice(), 59 | getSoldAmount(), (getSellLimit() - getSoldAmount()), getSellLimit() 60 | }); 61 | additionalLore.forEach(factory::addLore); 62 | 63 | setItem(9, getCurrentAmount() > 1000 ? getRemoveItem(1000) : null); 64 | setItem(10, getCurrentAmount() > 100 ? getRemoveItem(100) : null); 65 | setItem(11, getCurrentAmount() > 10 ? getRemoveItem(10) : null); 66 | setItem(12, getCurrentAmount() > 1 ? getRemoveItem(1) : null); 67 | setItem(13, new GUIItem(factory.toItemStack())); 68 | setItem(14, getAddableAmount() >= 1 ? getAddItem(1) : null); 69 | setItem(15, getAddableAmount() >= 10 ? getAddItem(10) : null); 70 | setItem(16, getAddableAmount() >= 100 ? getAddItem(100) : null); 71 | setItem(17, getAddableAmount() >= 1000 ? getAddItem(1000) : null); 72 | 73 | setItem(getCurrentAmount() >= 1 ? getConfirmItem() : null, 27, 28, 29, 30); 74 | setItem(getCancelItem(), 32, 33, 34, 35); 75 | 76 | } 77 | 78 | private GUIItem getAddItem(int amount) { 79 | return new GUIItem(ADD.getItem(player, new Object[]{ 80 | getItemName(), amount 81 | })) { 82 | @Override 83 | public void onClick(ClickType type) { 84 | PluginConfig.Sounds.GUI_CLICK.play(player); 85 | load(getCurrentAmount() + amount); 86 | updateView(); 87 | } 88 | }; 89 | } 90 | 91 | private GUIItem getRemoveItem(int amount) { 92 | return new GUIItem(REMOVE.getItem(player, new Object[]{ 93 | getItemName(), amount 94 | })) { 95 | @Override 96 | public void onClick(ClickType type) { 97 | PluginConfig.Sounds.GUI_CLICK.play(player); 98 | load(getCurrentAmount() - amount); 99 | updateView(); 100 | } 101 | }; 102 | } 103 | 104 | private GUIItem getConfirmItem() { 105 | return new GUIItem(CONFIRM.getItem(player, new Object[]{ 106 | getItemName(), getCurrentAmount(), getTotalMoney() 107 | })) { 108 | @Override 109 | public void onClick(ClickType type) { 110 | int amount = Math.min(getCurrentAmount(), Math.min(getDepositoryAmount(), getSellLimit() - getSoldAmount())); 111 | if (amount > 0) UltraDepository.getEconomyManager().sellItem(player, userData, item, amount); 112 | player.closeInventory(); 113 | } 114 | }; 115 | } 116 | 117 | private GUIItem getCancelItem() { 118 | return new GUIItem(CANCEL.getItem(player)) { 119 | @Override 120 | public void onClick(ClickType type) { 121 | PluginConfig.Sounds.SELL_FAIL.play(player); 122 | player.closeInventory(); 123 | } 124 | }; 125 | } 126 | 127 | private String getItemName() { 128 | return this.item.getName(); 129 | } 130 | 131 | public int getCurrentAmount() { 132 | return currentAmount; 133 | } 134 | 135 | private double getItemPrice() { 136 | return this.item.getPrice(); 137 | } 138 | 139 | private int getSellLimit() { 140 | return this.item.getLimit(); 141 | } 142 | 143 | private double getTotalMoney() { 144 | BigDecimal money = BigDecimal.valueOf(getCurrentAmount() * getItemPrice()).setScale(2, RoundingMode.DOWN); 145 | return money.doubleValue(); 146 | } 147 | 148 | private int getDepositoryAmount() { 149 | return userData.getItemData(this.item).getAmount(); 150 | } 151 | 152 | private int getSoldAmount() { 153 | return userData.getItemData(this.item).getSold(); 154 | } 155 | 156 | private int getAddableAmount() { 157 | return Math.min(getDepositoryAmount(), getSellLimit() - getSoldAmount()) - getCurrentAmount(); 158 | } 159 | 160 | public static void open(Player player, UserData userData, DepositoryItemData itemData, 161 | Depository configuration, DepositoryItem item) { 162 | player.closeInventory(); 163 | if (!UltraDepository.getEconomyManager().isInitialized()) { 164 | PluginMessages.NO_ECONOMY.send(player); 165 | return; 166 | } 167 | 168 | SellItemGUI gui = new SellItemGUI(player, userData, itemData, configuration, item); 169 | gui.openGUI(player); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/Main.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository; 2 | 3 | import cc.carm.lib.easyplugin.EasyPlugin; 4 | import cc.carm.lib.easyplugin.gui.GUI; 5 | import cc.carm.lib.easyplugin.i18n.EasyPluginMessageProvider; 6 | import cc.carm.lib.easyplugin.utils.MessageUtils; 7 | import cc.carm.plugin.ultradepository.command.DepositoryCommand; 8 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 9 | import cc.carm.plugin.ultradepository.hooker.GHUpdateChecker; 10 | import cc.carm.plugin.ultradepository.hooker.PAPIExpansion; 11 | import cc.carm.plugin.ultradepository.listener.CollectListener; 12 | import cc.carm.plugin.ultradepository.listener.UserListener; 13 | import cc.carm.plugin.ultradepository.manager.ConfigManager; 14 | import cc.carm.plugin.ultradepository.manager.DepositoryManager; 15 | import cc.carm.plugin.ultradepository.manager.EconomyManager; 16 | import cc.carm.plugin.ultradepository.manager.UserManager; 17 | import cc.carm.plugin.ultradepository.storage.DataStorage; 18 | import cc.carm.plugin.ultradepository.storage.StorageMethod; 19 | import cc.carm.plugin.ultradepository.util.JarResourceUtils; 20 | import org.bstats.bukkit.Metrics; 21 | import org.bstats.charts.SimplePie; 22 | import org.bstats.charts.SingleLineChart; 23 | import org.bukkit.Bukkit; 24 | import org.bukkit.plugin.Plugin; 25 | 26 | import java.util.Optional; 27 | 28 | public class Main extends EasyPlugin { 29 | private static Main instance; 30 | 31 | private DataStorage storage; 32 | 33 | 34 | private ConfigManager configManager; 35 | private UserManager userManager; 36 | private EconomyManager economyManager; 37 | private DepositoryManager depositoryManager; 38 | 39 | public Main() { 40 | super(new EasyPluginMessageProvider.zh_CN()); 41 | } 42 | 43 | @Override 44 | protected void load() { 45 | instance = this; 46 | } 47 | 48 | @Override 49 | protected boolean initialize() { 50 | 51 | log("加载配置文件..."); 52 | this.configManager = new ConfigManager(this); 53 | if (!configManager.initialize()) { 54 | log("初始化配置文件失败,放弃加载。"); 55 | return false; 56 | } 57 | 58 | log("初始化存储方式..."); 59 | StorageMethod storageMethod = PluginConfig.STORAGE_METHOD.getOptional().orElse(StorageMethod.YAML); 60 | log(" 正在使用 " + storageMethod.name() + " 进行数据存储"); 61 | 62 | this.storage = storageMethod.createStorage(); 63 | if (!storage.initialize()) { 64 | error("初始化存储失败,请检查配置文件。"); 65 | storage.shutdown(); 66 | return false; 67 | } 68 | 69 | log("加载经济系统..."); 70 | if (Bukkit.getPluginManager().getPlugin("Vault") != null) { 71 | this.economyManager = new EconomyManager(); 72 | if (!economyManager.initialize()) { 73 | error("经济系统初始化失败,关闭出售功能。"); 74 | } 75 | } else { 76 | log(" &7[-] 检测到未安装Vault,关闭出售功能。"); 77 | } 78 | 79 | log("加载仓库管理器..."); 80 | this.depositoryManager = new DepositoryManager(); 81 | getDepositoryManager().loadDepositories(); 82 | 83 | log("加载用户系统..."); 84 | this.userManager = new UserManager(); 85 | 86 | 87 | log("注册监听器..."); 88 | regListener(new UserListener()); 89 | regListener(new CollectListener()); 90 | GUI.initialize(this); 91 | 92 | log("注册指令..."); 93 | registerCommand("UltraDepository", new DepositoryCommand()); 94 | 95 | if (MessageUtils.hasPlaceholderAPI()) { 96 | log("注册变量..."); 97 | new PAPIExpansion(this).register(); 98 | } else { 99 | log("检测到未安装PlaceholderAPI,跳过变量注册。"); 100 | } 101 | 102 | if (PluginConfig.METRICS.get()) { 103 | log("启用统计数据..."); 104 | Metrics metrics = new Metrics(this, 13777); 105 | metrics.addCustomChart(new SingleLineChart( 106 | "active_depositories", 107 | () -> getDepositoryManager().getDepositories().size()) 108 | ); 109 | metrics.addCustomChart(new SimplePie("storage_method", storageMethod::name)); 110 | metrics.addCustomChart(new SimplePie("economy_enabled", () -> economyManager.isInitialized() ? "YES" : "NO")); 111 | metrics.addCustomChart(new SimplePie("papi_version", () -> { 112 | Plugin plugin = Bukkit.getPluginManager().getPlugin("PlaceholderAPI"); 113 | if (plugin == null) return "Not installed"; 114 | else return plugin.getDescription().getVersion(); 115 | })); 116 | } 117 | 118 | if (PluginConfig.CHECK_UPDATE.get()) { 119 | log("开始检查更新..."); 120 | GHUpdateChecker checker = new GHUpdateChecker(getLogger(), "CarmJos", "UltraDepository"); 121 | getScheduler().runAsync(() -> checker.checkUpdate(getDescription().getVersion())); 122 | } else { 123 | log("已禁用检查更新,跳过。"); 124 | } 125 | 126 | getUserManager().loadPlayersData(); 127 | return true; 128 | } 129 | 130 | @Override 131 | protected void shutdown() { 132 | if (!isInitialized()) return; 133 | 134 | log("保存现有用户数据..."); 135 | getUserManager().saveAll(); 136 | getUserManager().getDataCache().clear(); 137 | 138 | log("释放存储源..."); 139 | getStorage().shutdown(); 140 | 141 | log("卸载监听器..."); 142 | Bukkit.getServicesManager().unregisterAll(this); 143 | 144 | } 145 | 146 | protected DataStorage getStorage() { 147 | return storage; 148 | } 149 | 150 | public static Main getInstance() { 151 | return instance; 152 | } 153 | 154 | protected UserManager getUserManager() { 155 | return userManager; 156 | } 157 | 158 | protected EconomyManager getEconomyManager() { 159 | return economyManager; 160 | } 161 | 162 | protected DepositoryManager getDepositoryManager() { 163 | return depositoryManager; 164 | } 165 | 166 | protected ConfigManager getConfigManager() { 167 | return configManager; 168 | } 169 | 170 | @Override 171 | public boolean isDebugging() { 172 | return PluginConfig.DEBUG.get(); 173 | } 174 | 175 | @Override 176 | public void outputInfo() { 177 | Optional.ofNullable(JarResourceUtils.readResource(this.getResource("PLUGIN_INFO"))).ifPresent(this::log); 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/configuration/PluginConfig.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.configuration; 2 | 3 | import cc.carm.lib.easyplugin.configuration.cast.ConfigStringCast; 4 | import cc.carm.lib.easyplugin.configuration.impl.ConfigItem; 5 | import cc.carm.lib.easyplugin.configuration.impl.ConfigSound; 6 | import cc.carm.lib.easyplugin.configuration.message.ConfigMessage; 7 | import cc.carm.lib.easyplugin.configuration.message.ConfigMessageList; 8 | import cc.carm.lib.easyplugin.configuration.values.ConfigValue; 9 | import cc.carm.plugin.ultradepository.UltraDepository; 10 | import cc.carm.plugin.ultradepository.storage.StorageMethod; 11 | import org.bukkit.Material; 12 | import org.bukkit.Sound; 13 | 14 | public class PluginConfig { 15 | 16 | public static final ConfigValue DEBUG = new ConfigValue<>( 17 | "debug", Boolean.class, false 18 | ); 19 | 20 | public static final ConfigValue METRICS = new ConfigValue<>( 21 | "metrics", Boolean.class, true 22 | ); 23 | 24 | public static final ConfigValue CHECK_UPDATE = new ConfigValue<>( 25 | "check-update", Boolean.class, true 26 | ); 27 | 28 | public static final ConfigStringCast STORAGE_METHOD = new ConfigStringCast<>( 29 | "storage.method", StorageMethod::read, StorageMethod.YAML 30 | ); 31 | 32 | /** 33 | * 收集配置 34 | */ 35 | public static class Collect { 36 | 37 | public static final ConfigValue PICKUP = new ConfigValue<>( 38 | "collect.pickup", Boolean.class, true 39 | ); 40 | 41 | public static final ConfigValue KILL = new ConfigValue<>( 42 | "collect.kill", Boolean.class, true 43 | ); 44 | 45 | public static final ConfigValue BREAK = new ConfigValue<>( 46 | "collect.break", Boolean.class, true 47 | ); 48 | 49 | } 50 | 51 | public static class Sounds { 52 | 53 | public static final ConfigSound COLLECT = new ConfigSound("sounds.collect", Sound.ENTITY_VILLAGER_CELEBRATE); 54 | public static final ConfigSound TAKEOUT = new ConfigSound("sounds.collect"); 55 | public static final ConfigSound SELL_SUCCESS = new ConfigSound("sounds.sell-success"); 56 | public static final ConfigSound SELL_FAIL = new ConfigSound("sounds.sell-fail"); 57 | public static final ConfigSound GUI_CLICK = new ConfigSound("sounds.gui-click"); 58 | } 59 | 60 | /** 61 | * 通用配置 62 | */ 63 | public static class General { 64 | 65 | /** 66 | * 针对每一件物品的额外介绍 67 | * 将添加到背包界面内的物品上,避免重复配置 68 | */ 69 | public static class AdditionalLore { 70 | 71 | public static final ConfigMessageList AVAILABLE_FOR_SALE = new ConfigMessageList( 72 | () -> UltraDepository.getConfigManager().getPluginConfig(), 73 | "general.additional-lore.available-for-sale", 74 | new String[]{}, 75 | new String[]{ 76 | "%(item_name)", "%(amount)", "%(price)", "%(sold)", "%(remain)", "%(limit)" 77 | }); 78 | 79 | public static final ConfigMessageList NOT_FOR_SALE = new ConfigMessageList( 80 | () -> UltraDepository.getConfigManager().getPluginConfig(), 81 | "general.additional-lore.not-for-sale", 82 | new String[]{}, new String[]{ 83 | "%(item_name)", "%(amount)" 84 | }); 85 | 86 | } 87 | 88 | /** 89 | * 提示玩家点击行为的介绍 90 | * 将添加到背包界面内的物品上,避免重复配置 91 | */ 92 | public static class ClickLore { 93 | public static final ConfigMessageList AVAILABLE_FOR_SALE = new ConfigMessageList( 94 | () -> UltraDepository.getConfigManager().getPluginConfig(), 95 | "general.click-lore.available-for-sale", 96 | new String[]{}, new String[]{ 97 | "%(item_name)", "%(amount)", "%(price)" 98 | }); 99 | 100 | public static final ConfigMessageList NOT_FOR_SALE = new ConfigMessageList( 101 | () -> UltraDepository.getConfigManager().getPluginConfig(), 102 | "general.click-lore.not-for-sale", 103 | new String[]{}, 104 | new String[]{ 105 | "%(item_name)", "%(amount)" 106 | }); 107 | } 108 | 109 | 110 | public static final ConfigMessageList CLICK_LORE = new ConfigMessageList( 111 | () -> UltraDepository.getConfigManager().getPluginConfig(), 112 | "general.click-lore", 113 | new String[]{}, new String[]{ 114 | "%(item_name)", "%(amount)", "%(price)" 115 | }); 116 | 117 | /** 118 | * 售出界面的配置 119 | */ 120 | public static class SellGUI { 121 | 122 | 123 | public static final ConfigMessage TITLE = new ConfigMessage( 124 | () -> UltraDepository.getConfigManager().getPluginConfig(), 125 | "general.sell-gui.title", 126 | "&a&l出售", new String[]{ 127 | "%(item_name)", "%(backpack_name)" 128 | } 129 | ); 130 | 131 | public static class Items { 132 | 133 | public static final ConfigItem ADD = new ConfigItem( 134 | "general.sell-gui.items.add", 135 | new String[]{"%(item_name)", "%(amount)"}, 136 | ConfigItem.create(Material.GREEN_STAINED_GLASS_PANE, "&a添加物品 %(amount) 个") 137 | ); 138 | 139 | public static final ConfigItem REMOVE = new ConfigItem( 140 | "general.sell-gui.items.remove", 141 | new String[]{"%(item_name)", "%(amount)"}, 142 | ConfigItem.create(Material.RED_STAINED_GLASS_PANE, "&c減少物品 %(amount) 个") 143 | ); 144 | 145 | public static final ConfigItem CONFIRM = new ConfigItem( 146 | "general.sell-gui.items.confirm", 147 | new String[]{"%(item_name)", "%(amount)", "%(money)"}, 148 | ConfigItem.create(Material.EMERALD, "&2确认售出") 149 | ); 150 | 151 | public static final ConfigItem CANCEL = new ConfigItem( 152 | "general.sell-gui.items.cancel", 153 | ConfigItem.create(Material.REDSTONE, "&4取消售出") 154 | ); 155 | 156 | } 157 | 158 | } 159 | 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ```text 2 | _ _ _ _ _____ _ _ 3 | | | | | | | | __ \ (_) | 4 | | | | | | |_ _ __ __ _| | | | ___ _ __ ___ ___ _| |_ ___ _ __ _ _ 5 | | | | | | __| '__/ _` | | | |/ _ \ '_ \ / _ \/ __| | __/ _ \| '__| | | | 6 | | |__| | | |_| | | (_| | |__| | __/ |_) | (_) \__ \ | || (_) | | | |_| | 7 | \____/|_|\__|_| \__,_|_____/ \___| .__/ \___/|___/_|\__\___/|_| \__, | 8 | | | __/ | 9 | |_| |___/ 10 | ``` 11 | 12 | # UltraDepository 13 | 14 | [![version](https://img.shields.io/github/v/release/CarmJos/UltraDepository)](https://github.com/CarmJos/UltraDepository/releases) 15 | [![License](https://img.shields.io/github/license/CarmJos/UltraDepository)](https://opensource.org/licenses/GPL-3.0) 16 | [![workflow](https://github.com/CarmJos/UltraDepository/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/UltraDepository/actions/workflows/maven.yml) 17 | ![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/UltraDepository) 18 | ![Support](https://img.shields.io/badge/Minecraft-Java%201.16--Latest-green) 19 | ![](https://visitor-badge.glitch.me/badge?page_id=UltraDepository.readme) 20 | 21 | 超级仓库插件,支持设定不同物品的存储仓库。 22 | 23 | 本插件基于 Spigot(1.16.3) 实现,**理论上支持1.16后的全部版本**。 24 | 25 | 本插件由 [墨豆Mordo](https://www.mordo.cn)、[子墨Zimrs](https://www.zimrs.cn) 资助本人开发,经过授权后开源。 26 | 27 | > 本插件已发布于 [MCBBS](https://www.mcbbs.net/forum.php?mod=viewthread&tid=1292631) 和 [SpigotMC]() 。 28 | 29 | ## 功能介绍 30 | 31 | 本插件允许配置多个不同功能的仓库,玩家通过 击杀生物/挖掘方块/捡起收集 获得的原版物品可以自动被放入仓库中。 32 | 33 | 进入仓库后的物品玩家可以选择拿出或直接按量出售,且每日的出售数量上限和每件物品的价格可以自定义。 34 | 35 | 插件支持针对不同的权限配置仓库的容量,由此可以制作付费享用的”作物仓库“、”药剂师仓库“、”伐木仓库“... 36 | 37 | 综上,该插件不但提供了一种功能特权,对其合理配置之后也将大大为玩家带来便利。 38 | 39 | ## 效果预览 40 | 41 |
42 | 收集物品 43 | 44 | ![collect](.documentation/images/collect-message.jpg) 45 | 46 |
47 | 48 |
49 | 仓库界面 (可自定义配置) 50 | 51 | ![item-gui](.documentation/images/item-in-gui.png) 52 | 53 |
54 | 55 |
56 | 出售界面 57 | 58 | ![sell-gui](.documentation/images/sell-gui.png) 59 | 60 | ![sell-message](.documentation/images/sell-message.png) 61 |
62 | 63 | ## 插件依赖 64 | 65 | - **[必须]** 插件本体基于 [Spigot-API](https://hub.spigotmc.org/stash/projects/SPIGOT) 、 [BukkitAPI](http://bukkit.org/) 实现。 66 | - **[自带]** 插件功能基于 [EasyPlugin](https://github.com/CarmJos/EasyPlugin) 实现。 67 | - **[自带]** 数据部分基于 [EasySQL](https://github.com/CarmJos/EasySQL) 实现。 68 | - 本插件连接池使用 [BeeCP](https://github.com/Chris2018998/BeeCP) ,更轻量、快速。 69 | - **[推荐]** 变量部分基于 [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) 实现。 70 | - **[推荐]** 经济部分基于 [VaultAPI](https://github.com/MilkBowl/VaultAPI) 实现。 71 | 72 | 详细依赖列表可见 [Dependencies](https://github.com/CarmJos/UltraDepository/network/dependencies) 。 73 | 74 | ## 特殊优势 75 | 76 | - 详细的变量与指令! 77 | - 支持多种存储方式! 78 | - 可自定义程度高,配置任何想要的仓库! 79 | - 仓库容量权限配置,可给予特殊用户更大的仓库容量! 80 | - 仓库自动收集物品,提供API操作玩家数据! 81 | - ... 82 | 83 | ## 插件指令 84 | 85 | 指令主指令为 /UltraDepository (/ud | /Depository) 86 | 87 |
88 | 展开查看所有玩家指令 89 | 90 | ```text 91 | # open [仓库ID] 92 | @ 玩家指令 (UltraDepository.use) 93 | - 打开对应仓库的界面。 94 | 95 | # sell <仓库ID> <物品ID> <数量> 96 | @ 玩家指令 (UltraDepository.Command.Sell) 97 | - 售出对应数量的对应物品。 98 | - 该指令受到玩家每日售出数量的限制。 99 | 100 | # sellAll [仓库ID] [物品ID] 101 | @ 玩家指令 (UltraDepository.Command.SellAll) 102 | - 售出所有相关物品。 103 | - 该指令受到玩家每日售出数量的限制。 104 | ``` 105 | 106 |
107 | 108 |
109 | 展开查看所有后台指令 110 | 111 | 以下指令**只有后台**才可以使用,可用于搭配变量自制玩家管理GUI。 112 | 113 | ```text 114 | # info <玩家> [仓库ID] [物品ID] 115 | - 得到玩家的相关物品信息。 116 | 117 | # add <玩家> <仓库ID> <物品ID> <数量> 118 | - 为玩家添加对应仓库中对于物品的数量。 119 | 120 | # remove <玩家> <仓库ID> <物品ID> [数量] 121 | - 为玩家减少对应仓库中对于物品的数量。 122 | - 若不填写数量,则清空对应仓库的对应物品。 123 | 124 | # sell <玩家> [仓库ID] [物品ID] [数量] 125 | - 为玩家售出相关物品。 126 | - 若不填写数量,则售出所有对应仓库的对应物品。 127 | - 若不填写物品,则售出对应仓库内所有物品。 128 | - 若不填写仓库,则售出所有仓库内所有物品。 129 | - 该指令受到玩家每日售出数量的限制。 130 | ``` 131 | 132 |
133 | 134 | ## 插件变量 ([PlaceholderAPI](https://www.spigotmc.org/resources/6245/)) 135 | 136 | 变量部分基于 [PlaceholderAPI](https://www.spigotmc.org/resources/6245/) 实现,如需使用变量请安装其插件。 137 | 138 |
139 | 展开查看所有变量 140 | 141 | ```text 142 | # %UltraDepository_amount_<仓库ID>_<物品ID>% 143 | - 得到对应仓库内对应物品的数量 144 | 145 | # %UltraDepository_price_<仓库ID>_<物品ID>% 146 | - 得到对应仓库内对应物品的价格 147 | 148 | # %UltraDepository_sold_<仓库ID>_<物品ID>% 149 | - 得到对应仓库内对应物品的今日售出数量 150 | 151 | # %UltraDepository_limit_<仓库ID>_<物品ID>% 152 | - 得到对应仓库内对应物品的每日售出限制 153 | 154 | # %UltraDepository_remain_<仓库ID>_<物品ID>% 155 | - 得到对应仓库内对应物品的剩余可售出数量 156 | - $剩余可售出数量 = $每日售出限制 - $今日售出数量 157 | 158 | # %UltraDepository_capacity_<仓库ID>% 159 | - 得到对应仓库的容量 160 | 161 | # %UltraDepository_used_<仓库ID>% 162 | - 得到已使用的仓库容量 163 | 164 | # %UltraDepository_usable_<仓库ID>% 165 | - 得到剩余可使用的仓库容量 166 | 167 | ``` 168 | 169 |
170 | 171 | ## 插件权限 172 | 173 |
174 | 展开查看所有权限 175 | 176 | ```text 177 | # UltraDepository.use 178 | - 超级仓库的基本使用权限 (默认所有人都有) 179 | 180 | # UltraDepository.Command.Sell 181 | - 玩家使用Sell指令的权限 182 | 183 | # UltraDepository.Command.SellAll 184 | - 玩家使用SellAll指令的权限 185 | 186 | # UltraDepository.auto 187 | - 超级仓库的自动收集权限 188 | 189 | # UltraDepository.auto.enable 190 | - 用于判断是否启用了自动收集功能 191 | - 若玩家有"UltraDepository.auto"权限,且玩家有该权限,则会开始为玩家自动收集物品。 192 | - 若玩家缺失该权限或“UltraDepository.auto”权限,则自动收集物品功能不会启用。 193 | - 您可以自己使用GUI创建一个按钮,后通过给玩家添加/删除该权限决定玩家是否开启自动收集。 194 | 195 | # UltraDepository.silent 196 | - 拥有该权限将不再接收到放入背包的提示。 197 | - 您可以自己使用GUI创建一个按钮,后通过给玩家添加/删除该权限决定玩家是否开启收集提示。 198 | 199 | # UltraDepository.admin 200 | - "超级仓库的管理权限" 201 | ``` 202 | 203 |
204 | 205 | ## 配置文件 206 | 207 | ### 插件配置文件 ([config.yml](src/main/resources/config.yml)) 208 | 209 | 详见源文件。 210 | 211 | ### 消息配置文件 ([messages.yml](src/main/java/cc/carm/plugin/ultradepository/configuration/PluginMessages.java)) 212 | 213 | 详见代码文件中默认值,相关文件将在首次运行时创建。 214 | 215 | ### 仓库配置文件 ([depositories/<仓库ID>.yml](src/main/resources/depositories/.example-depository.yml)) 216 | 217 | 所有仓库配置均为单独的配置文件,存放于 `插件配置目录/depositories` 下,便于管理。 218 | 219 | 文件名即仓库的ID,**强烈推荐使用纯英文**。以`.`开头的仓库配置不会被加载。部分符号可能会影响正常读取,请避免使用。 220 | 221 | 随本项目预设了几个常用的仓库类型,可以 [在这里](.examples/depositories) 直接下载您需要的仓库配置加以修改后使用。 222 | 223 | 您也可以 [点击这里](src/main/resources/depositories/.example-depository.yml) 查看一份*详细的仓库配置示例*,以制作您自己的仓库。 224 | 225 | ## 开发 226 | 227 | 详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release) 请 [点击这里](https://carmjos.github.io/UltraDepository) 。 228 | 229 | ## 使用统计 230 | 231 | [![bStats](https://bstats.org/signatures/bukkit/UltraDepository.svg)](https://bstats.org/plugin/bukkit/UltraDepository/13777) 232 | 233 | ## 支持与捐赠 234 | 235 | 若您觉得本插件做的不错,您可以捐赠支持我! 236 | 237 | 238 | 239 | ## 开源协议 240 | 241 | 本项目源码采用 [GNU General Public License v3.0](https://opensource.org/licenses/GPL-3.0) 开源协议。 242 | 243 |
244 | 关于 GPL 协议 245 | 246 | > GNU General Public Licence (GPL) 有可能是开源界最常用的许可模式。GPL 保证了所有开发者的权利,同时为使用者提供了足够的复制,分发,修改的权利: 247 | > 248 | > #### 可自由复制 249 | > 你可以将软件复制到你的电脑,你客户的电脑,或者任何地方。复制份数没有任何限制。 250 | > #### 可自由分发 251 | > 在你的网站提供下载,拷贝到U盘送人,或者将源代码打印出来从窗户扔出去(环保起见,请别这样做)。 252 | > #### 可以用来盈利 253 | > 你可以在分发软件的时候收费,但你必须在收费前向你的客户提供该软件的 GNU GPL 许可协议,以便让他们知道,他们可以从别的渠道免费得到这份软件,以及你收费的理由。 254 | > #### 可自由修改 255 | > 如果你想添加或删除某个功能,没问题,如果你想在别的项目中使用部分代码,也没问题,唯一的要求是,使用了这段代码的项目也必须使用 GPL 协议。 256 | > 257 | > 需要注意的是,分发的时候,需要明确提供源代码和二进制文件,另外,用于某些程序的某些协议有一些问题和限制,你可以看一下 @PierreJoye 写的 Practical Guide to GPL Compliance 一文。使用 GPL 协议,你必须在源代码代码中包含相应信息,以及协议本身。 258 | > 259 | > *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。* 260 |
261 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/manager/DepositoryManager.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.manager; 2 | 3 | import cc.carm.plugin.ultradepository.UltraDepository; 4 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 5 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 6 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 7 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 8 | import cc.carm.plugin.ultradepository.event.DepositoryCollectItemEvent; 9 | import cc.carm.plugin.ultradepository.util.JarUtil; 10 | import com.google.common.collect.HashMultimap; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.Material; 13 | import org.bukkit.configuration.file.FileConfiguration; 14 | import org.bukkit.configuration.file.YamlConfiguration; 15 | import org.bukkit.entity.Player; 16 | import org.bukkit.inventory.ItemStack; 17 | import org.bukkit.inventory.meta.ItemMeta; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.util.*; 24 | import java.util.stream.Collectors; 25 | 26 | @SuppressWarnings("ResultOfMethodCallIgnored") 27 | public class DepositoryManager { 28 | 29 | /** 30 | * 记录仓库ID对应的仓库实例 31 | */ 32 | public HashMap<@NotNull String, @NotNull Depository> depositories; 33 | 34 | /** 35 | * 用于记录储存每个物品ID所对应的背包ID 36 | */ 37 | public HashMultimap<@NotNull String, @NotNull String> itemMap; 38 | 39 | public DepositoryManager() { 40 | this.depositories = new HashMap<>(); 41 | this.itemMap = HashMultimap.create(); 42 | } 43 | 44 | public void loadDepositories() { 45 | long start = System.currentTimeMillis(); 46 | UltraDepository.getInstance().log(" 开始加载仓库配置..."); 47 | File folder = new File(UltraDepository.getInstance().getDataFolder(), "depositories"); 48 | if (!folder.exists()) { 49 | folder.mkdir(); 50 | 51 | try { 52 | JarUtil.copyFolderFromJar( 53 | "depositories", UltraDepository.getInstance().getDataFolder(), 54 | JarUtil.CopyOption.COPY_IF_NOT_EXIST 55 | ); 56 | } catch (IOException ignore) { 57 | } 58 | 59 | } else if (folder.isDirectory()) { 60 | folder.delete(); 61 | folder.mkdir(); 62 | } 63 | 64 | File[] files = folder.listFiles(); 65 | if (files == null) return; 66 | HashMultimap<@NotNull String, @NotNull String> items = HashMultimap.create(); 67 | HashMap<@NotNull String, @NotNull Depository> data = new HashMap<>(); 68 | for (File file : files) { 69 | String fileName = file.getName(); 70 | if (!file.isFile() || fileName.startsWith(".")) continue; 71 | if (!fileName.toLowerCase().endsWith(".yml")) continue; 72 | String identifier = fileName.substring(0, fileName.lastIndexOf(".")); 73 | FileConfiguration configuration = YamlConfiguration.loadConfiguration(file); 74 | Depository depository = Depository.loadFrom(identifier, configuration); 75 | if (depository.getItems().size() > 0) { 76 | depository.getItems().values().forEach(value -> items.put(value.getTypeID(), depository.getIdentifier())); 77 | data.put(identifier, depository); 78 | } else { 79 | UltraDepository.getInstance().error(" 仓库 " + depository.getName() + " 未配置任何物品,请检查相关配置!"); 80 | } 81 | } 82 | for (Map.Entry> entry : items.asMap().entrySet()) { 83 | UltraDepository.getInstance().debug("# " + entry.getKey()); 84 | for (String depositoryID : entry.getValue()) { 85 | UltraDepository.getInstance().debug("- " + depositoryID); 86 | } 87 | } 88 | 89 | this.depositories = data; 90 | this.itemMap = items; 91 | UltraDepository.getInstance().log(" 仓库配置加载完成,共加载 " + data.size() + " 个仓库,耗时 " + (System.currentTimeMillis() - start) + "ms 。"); 92 | } 93 | 94 | public @NotNull HashMap<@NotNull String, @NotNull Depository> getDepositories() { 95 | return depositories; 96 | } 97 | 98 | public boolean hasDepository(@NotNull String depositoryID) { 99 | return getDepositories().containsKey(depositoryID); 100 | } 101 | 102 | public boolean hasItem(@NotNull String depositoryID, @NotNull String itemTypeID) { 103 | Depository configuration = getDepository(depositoryID); 104 | if (configuration == null) return false; 105 | return hasItem(configuration, itemTypeID); 106 | } 107 | 108 | public boolean hasItem(@NotNull Depository depository, @NotNull String itemTypeID) { 109 | return depository.getItems().containsKey(itemTypeID); 110 | } 111 | 112 | public @Nullable Depository getDepository(@NotNull String depositoryID) { 113 | return getDepositories().get(depositoryID); 114 | } 115 | 116 | @SuppressWarnings("deprecation") 117 | public Set getItemDepositories(ItemStack itemStack) { 118 | return getItemDepositories(itemStack.getType(), itemStack.getDurability()); 119 | } 120 | 121 | public @Nullable Set getItemDepositories(Material material, int data) { 122 | return Optional.ofNullable(itemMap.get(getItemTypeID(material, data))) 123 | .map(set -> set.stream().map(this::getDepository).collect(Collectors.toSet())) 124 | .orElse(null); 125 | } 126 | 127 | public Set getPlayerUsableDepository(Player player, ItemStack itemStack) { 128 | return getItemDepositories(itemStack).stream().filter(configuration -> { 129 | int used = UltraDepository.getUserManager().getData(player).getDepositoryData(configuration).getUsedCapacity(); 130 | int max = configuration.getCapacity().getPlayerCapacity(player); 131 | return max < 0 || used + itemStack.getAmount() <= max; 132 | }).collect(Collectors.toSet()); 133 | } 134 | 135 | public @NotNull String getItemTypeID(Material material, int data) { 136 | if (data == 0) return material.name(); 137 | else return material.name() + ":" + data; 138 | } 139 | 140 | @SuppressWarnings("deprecation") 141 | public @NotNull String getItemTypeID(ItemStack itemStack) { 142 | return getItemTypeID(itemStack.getType(), itemStack.getDurability()); 143 | } 144 | 145 | public Collection collectItem(Player player, Collection items) { 146 | if (!UltraDepository.getUserManager().isCollectEnabled(player)) { 147 | UltraDepository.getInstance().debug("player " + player.getName() + " disabled collect, skipped."); 148 | return items; 149 | } else return items.stream().filter(item -> !collectItem(player, item)).collect(Collectors.toList()); 150 | } 151 | 152 | public boolean collectItem(Player player, ItemStack item) { 153 | String typeID = getItemTypeID(item); 154 | UltraDepository.getInstance().debug("Checking item " + typeID + " ..."); 155 | if (!UltraDepository.getUserManager().isCollectEnabled(player)) { 156 | UltraDepository.getInstance().debug("Player " + player.getName() + " disabled collect, skipped."); 157 | return false; 158 | } 159 | ItemMeta meta = item.getItemMeta(); 160 | if (meta != null && (meta.hasLore() || meta.hasDisplayName() || meta.hasEnchants())) { 161 | // 不收集有特殊属性的物品 162 | UltraDepository.getInstance().debug("Item has special meta, skipped."); 163 | return false; 164 | } 165 | Set usableDepositories = getPlayerUsableDepository(player, item); 166 | if (usableDepositories.size() < 1) { 167 | UltraDepository.getInstance().debug("Item doesn't has any depository, skipped."); 168 | return false; 169 | } 170 | 171 | Depository depository = usableDepositories.stream().findFirst().orElse(null); 172 | DepositoryItem depositoryItem = depository.getItems().get(typeID); 173 | int itemAmount = item.getAmount(); 174 | 175 | DepositoryCollectItemEvent collectItemEvent = new DepositoryCollectItemEvent(player, depository, depositoryItem, itemAmount); 176 | Bukkit.getPluginManager().callEvent(collectItemEvent); 177 | 178 | if (collectItemEvent.isCancelled()) return false; 179 | int finalAmount = collectItemEvent.getItemAmount(); 180 | 181 | collectItemEvent.getUserData().addItemAmount(depository.getIdentifier(), typeID, finalAmount); 182 | if (!player.hasPermission("UltraDepository.silent")) { 183 | PluginMessages.ITEM_COLLECT.send(player, new Object[]{ 184 | depository.getItems().get(typeID).getName(), 185 | finalAmount, depository.getName() 186 | }); 187 | PluginConfig.Sounds.COLLECT.play(player); 188 | 189 | PluginMessages.ITEM_COLLECT_ACTIONBAR.sendBar(player, new Object[]{ 190 | depository.getItems().get(typeID).getName(), 191 | finalAmount, depository.getName() 192 | }); // Support action bar 193 | 194 | } 195 | UltraDepository.getInstance().debug("Item collected successfully."); 196 | return true; 197 | } 198 | 199 | /** 200 | * 获得某背包配置中的某件物品单价,最低为0。 201 | * 202 | * @param depositoryID 背包ID 203 | * @param itemTypeID 物品ID 204 | * @return 若为空,则该背包或该物品不存在。 205 | */ 206 | public @Nullable Double getItemPrice(@NotNull String depositoryID, @NotNull String itemTypeID) { 207 | Depository configuration = getDepository(depositoryID); 208 | if (configuration == null) return null; 209 | DepositoryItem item = configuration.getItems().get(itemTypeID); 210 | if (item == null) return null; 211 | return item.getPrice(); 212 | } 213 | 214 | /** 215 | * 获得某背包配置中的某件物品每日售出限制,最低为0。 216 | * 217 | * @param depositoryID 背包ID 218 | * @param itemTypeID 物品ID 219 | * @return 若为空,则该背包或该物品不存在。 220 | */ 221 | public @Nullable Integer getItemSellLimit(@NotNull String depositoryID, @NotNull String itemTypeID) { 222 | Depository configuration = getDepository(depositoryID); 223 | if (configuration == null) return null; 224 | DepositoryItem item = configuration.getItems().get(itemTypeID); 225 | if (item == null) return null; 226 | return item.getLimit(); 227 | } 228 | 229 | /** 230 | * 获得某背包配置中的某件物品每日售出限制,最低为0。 231 | * 232 | * @param depository 背包 233 | * @param itemTypeID 物品ID 234 | * @return 若为空,则该背包或该物品不存在。 235 | */ 236 | public @Nullable Integer getItemSellLimit(@NotNull Depository depository, @NotNull String itemTypeID) { 237 | DepositoryItem item = depository.getItems().get(itemTypeID); 238 | if (item == null) return null; 239 | return item.getLimit(); 240 | } 241 | 242 | 243 | } 244 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 11 9 | ${java.version} 10 | ${java.version} 11 | UTF-8 12 | UTF-8 13 | 1.5.14 14 | 0.4.7 15 | 1.3.2 16 | 17 | 18 | cc.carm.plugin 19 | ultradepository 20 | jar 21 | 1.3.9 22 | 23 | UltraDepository 24 | 超级仓库插件,支持设定不同物品的存储仓库。 25 | https://github.com/CarmJos/UltraDepository 26 | 27 | 28 | 29 | CarmJos 30 | Carm Jos 31 | carm@carm.cc 32 | https://www.carm.cc 33 | 34 | Main Developer 35 | Designer 36 | 37 | 38 | 39 | zimrs 40 | Zimrs 41 | zimrs@kar.red 42 | https://www.zimrs.cn 43 | 44 | Product Manager 45 | 46 | 47 | 48 | 49 | 50 | 51 | GNU General Public License v3.0 52 | https://opensource.org/licenses/GPL-3.0 53 | 54 | 55 | 56 | 57 | GitHub Issues 58 | https://github.com/CarmJos/UltraDepository/issues 59 | 60 | 61 | 62 | GitHub Actions 63 | https://github.com/CarmJos/UltraDepository/actions/workflows/maven.yml 64 | 65 | 66 | 67 | https://github.com/CarmJos/UltraDepository/releases 68 | 69 | github 70 | GitHub Packages 71 | https://maven.pkg.github.com/CarmJos/UltraDepository 72 | 73 | 74 | 75 | 76 | 77 | 78 | central 79 | https://repo1.maven.org/maven2/ 80 | 81 | 82 | 83 | carm-repo 84 | Carm's Repo 85 | https://repo.carm.cc/repository/maven-public/ 86 | 87 | 88 | 89 | github 90 | GitHub Packages 91 | https://maven.pkg.github.com/CarmJos/* 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | cc.carm.lib 100 | easyplugin-main 101 | true 102 | 103 | 104 | 105 | cc.carm.lib 106 | easyplugin-configuration 107 | true 108 | 109 | 110 | 111 | cc.carm.lib 112 | easyplugin-gui 113 | true 114 | 115 | 116 | 117 | cc.carm.lib 118 | easysql-beecp 119 | ${easysql.version} 120 | provided 121 | 122 | 123 | 124 | cc.carm.lib 125 | githubreleases4j 126 | ${ghreleases.version} 127 | provided 128 | 129 | 130 | 131 | org.bstats 132 | bstats-bukkit 133 | 3.1.0 134 | compile 135 | 136 | 137 | 138 | org.spigotmc 139 | spigot-api 140 | 1.19-R0.1-20220725.090125-47 141 | provided 142 | 143 | 144 | 145 | me.clip 146 | placeholderapi 147 | 2.11.7 148 | provided 149 | 150 | 151 | 152 | com.github.MilkBowl 153 | VaultAPI 154 | 1.7.1 155 | provided 156 | 157 | 158 | 159 | junit 160 | junit 161 | 4.13.2 162 | test 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | cc.carm.lib 172 | easyplugin-bom 173 | ${easyplugin.version} 174 | pom 175 | import 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-surefire-plugin 186 | 3.5.4 187 | 188 | false 189 | 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-javadoc-plugin 194 | 3.12.0 195 | 196 | javadoc 197 | 198 | https://javadoc.io/doc/org.jetbrains/annotations/ 199 | 200 | false 201 | UTF-8 202 | UTF-8 203 | UTF-8 204 | zh_CN 205 | 206 | 207 | 208 | attach-javadocs 209 | 210 | jar 211 | 212 | 213 | 214 | 215 | 216 | org.apache.maven.plugins 217 | maven-compiler-plugin 218 | 3.14.1 219 | 220 | ${java.version} 221 | ${java.version} 222 | UTF-8 223 | -parameters 224 | 225 | 226 | 227 | org.apache.maven.plugins 228 | maven-jar-plugin 229 | 3.5.0 230 | 231 | 232 | org.apache.maven.plugins 233 | maven-source-plugin 234 | 3.4.0 235 | 236 | 237 | package 238 | 239 | jar-no-fork 240 | 241 | 242 | 243 | 244 | 245 | org.apache.maven.plugins 246 | maven-shade-plugin 247 | 3.6.1 248 | 249 | 250 | package 251 | 252 | shade 253 | 254 | 255 | 256 | 257 | 258 | ${project.name}-${project.version} 259 | ${project.basedir}/asset/ 260 | false 261 | 262 | 263 | org.bstats 264 | cc.carm.plugin.ultradepository.lib.bstats 265 | 266 | 267 | cc.carm.lib.easyplugin 268 | cc.carm.plugin.ultradepository.lib.easyplugin 269 | 270 | 271 | 272 | 273 | *:* 274 | 275 | META-INF/MANIFEST.MF 276 | META-INF/*.txt 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | src/main/resources 286 | true 287 | 288 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /src/main/java/cc/carm/plugin/ultradepository/command/DepositoryCommand.java: -------------------------------------------------------------------------------- 1 | package cc.carm.plugin.ultradepository.command; 2 | 3 | import cc.carm.lib.easyplugin.utils.ColorParser; 4 | import cc.carm.lib.easyplugin.utils.MessageUtils; 5 | import cc.carm.plugin.ultradepository.UltraDepository; 6 | import cc.carm.plugin.ultradepository.configuration.PluginConfig; 7 | import cc.carm.plugin.ultradepository.configuration.PluginMessages; 8 | import cc.carm.plugin.ultradepository.configuration.depository.Depository; 9 | import cc.carm.plugin.ultradepository.configuration.depository.DepositoryItem; 10 | import cc.carm.plugin.ultradepository.data.DepositoryData; 11 | import cc.carm.plugin.ultradepository.data.DepositoryItemData; 12 | import cc.carm.plugin.ultradepository.data.UserData; 13 | import cc.carm.plugin.ultradepository.ui.DepositoryGUI; 14 | import org.bukkit.Bukkit; 15 | import org.bukkit.command.Command; 16 | import org.bukkit.command.CommandExecutor; 17 | import org.bukkit.command.CommandSender; 18 | import org.bukkit.command.TabCompleter; 19 | import org.bukkit.entity.HumanEntity; 20 | import org.bukkit.entity.Player; 21 | import org.bukkit.util.StringUtil; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | public class DepositoryCommand implements CommandExecutor, TabCompleter { 30 | 31 | 32 | private boolean helpConsole(CommandSender sender) { 33 | PluginMessages.Usages.CONSOLE.send(sender); 34 | return true; 35 | } 36 | 37 | private boolean helpPlayer(Player player) { 38 | PluginMessages.Usages.PLAYER.send(player); 39 | return true; 40 | } 41 | 42 | @Override 43 | public boolean onCommand( 44 | @NotNull CommandSender sender, @NotNull Command command, 45 | @NotNull String alias, @NotNull String[] args) { 46 | if (sender instanceof Player) { 47 | Player player = (Player) sender; 48 | if (args.length < 1) return helpPlayer(player); 49 | switch (args[0].toLowerCase()) { 50 | case "open": { 51 | if (!player.hasPermission("UltraDepository.use")) { 52 | return false; 53 | } 54 | if (args.length < 2) return helpPlayer(player); 55 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 56 | if (depository == null) { 57 | PluginMessages.NO_DEPOSITORY.send(player); 58 | return true; 59 | } 60 | DepositoryGUI.open((Player) sender, depository); 61 | return true; 62 | } 63 | case "sell": { 64 | if (!player.hasPermission("UltraDepository.Command.Sell")) { 65 | return false; 66 | } 67 | if (!UltraDepository.getEconomyManager().isInitialized()) { 68 | PluginConfig.Sounds.SELL_FAIL.play(player); 69 | PluginMessages.NO_ECONOMY.send(player); 70 | return true; 71 | } 72 | if (args.length < 4) return helpPlayer(player); 73 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[1]); 74 | if (depository == null) { 75 | PluginConfig.Sounds.SELL_FAIL.play(player); 76 | PluginMessages.NO_DEPOSITORY.send(player); 77 | return true; 78 | } 79 | 80 | DepositoryItem item = depository.getItems().get(args[2]); 81 | if (item == null) { 82 | PluginConfig.Sounds.SELL_FAIL.play(player); 83 | PluginMessages.NO_ITEM.send(player); 84 | return true; 85 | } 86 | 87 | int amount = -1; 88 | try { 89 | amount = Integer.parseInt(args[3]); 90 | } catch (Exception ignore) { 91 | } 92 | if (amount <= 0) { 93 | PluginConfig.Sounds.SELL_FAIL.play(player); 94 | PluginMessages.WRONG_NUMBER.send(player); 95 | return true; 96 | } 97 | 98 | UserData userData = UltraDepository.getUserManager().getData(player); 99 | DepositoryItemData itemData = userData.getItemData(item); 100 | int limit = item.getLimit(); 101 | int sold = itemData.getSold(); 102 | int currentAmount = itemData.getAmount(); 103 | 104 | if (currentAmount < amount) { 105 | PluginConfig.Sounds.SELL_FAIL.play(player); 106 | PluginMessages.NO_ENOUGH_ITEM.send(player); 107 | return true; 108 | } 109 | 110 | if (amount > (limit - sold)) { 111 | PluginConfig.Sounds.SELL_FAIL.play(player); 112 | PluginMessages.ITEM_SOLD_LIMIT.send(player, new Object[]{(limit - sold), limit}); 113 | return true; 114 | } 115 | 116 | UltraDepository.getEconomyManager().sellItem(player, userData, item, amount); 117 | return true; 118 | } 119 | case "sellall": { 120 | if (!player.hasPermission("UltraDepository.Command.SellAll")) { 121 | return false; 122 | } 123 | if (!UltraDepository.getEconomyManager().isInitialized()) { 124 | PluginConfig.Sounds.SELL_FAIL.play(player); 125 | PluginMessages.NO_ECONOMY.send(player); 126 | return true; 127 | } 128 | UserData userData = UltraDepository.getUserManager().getData(player); 129 | 130 | String depositoryID = args.length >= 2 ? args[1] : null; 131 | String itemID = args.length >= 3 ? args[2] : null; 132 | 133 | Depository depository = null; 134 | if (depositoryID != null) { 135 | depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 136 | if (depository == null) { 137 | PluginConfig.Sounds.SELL_FAIL.play(player); 138 | PluginMessages.NO_DEPOSITORY.send(player); 139 | return true; 140 | } 141 | } 142 | 143 | if (depository == null) { 144 | UltraDepository.getEconomyManager().sellAllItem(player, userData); 145 | sender.sendMessage("Success! " + player.getName() + "'s items had been sold."); 146 | return true; 147 | } 148 | 149 | DepositoryItem item = null; 150 | if (itemID != null) { 151 | item = depository.getItems().get(itemID); 152 | if (item == null) { 153 | PluginConfig.Sounds.SELL_FAIL.play(player); 154 | PluginMessages.NO_ITEM.send(player); 155 | return true; 156 | } 157 | } 158 | 159 | if (item == null) { 160 | UltraDepository.getEconomyManager().sellAllItem(player, userData, depository); 161 | return true; 162 | } 163 | 164 | UltraDepository.getEconomyManager().sellAllItem(player, userData, item); 165 | return true; 166 | } 167 | default: 168 | return helpPlayer(player); 169 | } 170 | } else { 171 | if (args.length < 1) return helpConsole(sender); 172 | switch (args[0].toLowerCase()) { 173 | case "info": { 174 | if (args.length < 2) return helpConsole(sender); 175 | Player player = Bukkit.getPlayer(args[1]); 176 | if (player == null) { 177 | sender.sendMessage("Player does not exist."); 178 | return false; 179 | } 180 | UserData userData = UltraDepository.getUserManager().getData(player); 181 | 182 | String depositoryID = args.length >= 3 ? args[2] : null; 183 | String itemID = args.length >= 4 ? args[3] : null; 184 | 185 | Depository depository = null; 186 | if (depositoryID != null) { 187 | depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 188 | if (depository == null) { 189 | PluginMessages.NO_DEPOSITORY.send(player); 190 | return true; 191 | } 192 | } 193 | sender.sendMessage(ColorParser.parse("&fInfo &6" + player.getName() + " &f:")); 194 | if (depository == null) { 195 | userData.getDepositories().values().forEach(depositoryData -> { 196 | MessageUtils.send(sender, "&8# &e" + depositoryData.getIdentifier()); 197 | depositoryData.getContents().values().forEach(itemData -> { 198 | String typeID = itemData.getSource().getTypeID(); 199 | int amount = itemData.getAmount(); 200 | int sold = itemData.getSold(); 201 | MessageUtils.send(sender, "&8- &f" + typeID + " &7[&f " + amount + "&8|&f " + sold + "&7]"); 202 | }); 203 | }); 204 | return true; 205 | } 206 | 207 | DepositoryItem item = null; 208 | if (itemID != null) { 209 | item = depository.getItems().get(itemID); 210 | if (item == null) { 211 | PluginMessages.NO_ITEM.send(player); 212 | return true; 213 | } 214 | } 215 | 216 | if (item == null) { 217 | DepositoryData depositoryData = userData.getDepositoryData(depository); 218 | MessageUtils.send(sender, "&8# &e" + depositoryData.getIdentifier()); 219 | depositoryData.getContents().values().forEach(itemData -> { 220 | String typeID = itemData.getSource().getTypeID(); 221 | int amount = itemData.getAmount(); 222 | int sold = itemData.getSold(); 223 | MessageUtils.send(sender, "&8- &f" + typeID + " &7[&f " + amount + "&8|&f " + sold + "&7]"); 224 | }); 225 | return true; 226 | } 227 | 228 | DepositoryItemData itemData = userData.getItemData(item); 229 | String typeID = itemData.getSource().getTypeID(); 230 | int amount = itemData.getAmount(); 231 | int sold = itemData.getSold(); 232 | 233 | MessageUtils.send(sender, "&8# &e" + depository.getIdentifier()); 234 | MessageUtils.send(sender, "&8- &f" + typeID + " &7[&f " + amount + "&8|&f " + sold + "&7]"); 235 | return true; 236 | } 237 | case "add": { 238 | if (args.length < 5) return true; 239 | Player player = Bukkit.getPlayer(args[1]); 240 | if (player == null) { 241 | sender.sendMessage("Player does not exist."); 242 | return false; 243 | } 244 | 245 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[2]); 246 | if (depository == null) { 247 | PluginMessages.NO_DEPOSITORY.send(sender); 248 | return true; 249 | } 250 | 251 | DepositoryItem item = depository.getItems().get(args[3]); 252 | if (item == null) { 253 | PluginMessages.NO_ITEM.send(sender); 254 | return true; 255 | } 256 | 257 | int amount = -1; 258 | try { 259 | amount = Integer.parseInt(args[4]); 260 | } catch (Exception ignore) { 261 | } 262 | if (amount <= 0) { 263 | PluginMessages.WRONG_NUMBER.send(sender); 264 | return true; 265 | } 266 | 267 | Integer after = UltraDepository.getUserManager().getData(player) 268 | .addItemAmount(depository.getIdentifier(), item.getTypeID(), amount); 269 | 270 | if (after != null) { 271 | sender.sendMessage("Success! Now " + player.getName() + "'s " + item.getTypeID() + " is " + after + " ."); 272 | } else { 273 | sender.sendMessage("Failed!"); 274 | } 275 | 276 | return true; 277 | } 278 | case "remove": { 279 | if (args.length < 4) return true; 280 | Player player = Bukkit.getPlayer(args[1]); 281 | if (player == null) { 282 | sender.sendMessage("Player does not exist."); 283 | return false; 284 | } 285 | 286 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[2]); 287 | if (depository == null) { 288 | PluginMessages.NO_DEPOSITORY.send(sender); 289 | return true; 290 | } 291 | 292 | DepositoryItem item = depository.getItems().get(args[3]); 293 | if (item == null) { 294 | PluginMessages.NO_ITEM.send(sender); 295 | return true; 296 | } 297 | 298 | Integer amount = null; 299 | try { 300 | amount = Integer.parseInt(args[4]); 301 | } catch (Exception ignore) { 302 | } 303 | if (amount != null && amount < 0) { 304 | PluginMessages.WRONG_NUMBER.send(sender); 305 | return true; 306 | } 307 | 308 | UserData userData = UltraDepository.getUserManager().getData(player); 309 | if (amount != null) { 310 | Integer after = userData.removeItemAmount(depository.getIdentifier(), item.getTypeID(), amount); 311 | if (after != null) { 312 | sender.sendMessage("Success! Now " + player.getName() + "'s " + item.getTypeID() + " is " + after + " ."); 313 | } else { 314 | sender.sendMessage("Failed!"); 315 | } 316 | } else { 317 | userData.setItemAmount(depository.getIdentifier(), item.getTypeID(), 0); 318 | sender.sendMessage("Success! Cleared " + player.getName() + "'s " + item.getTypeID() + " ."); 319 | } 320 | return true; 321 | } 322 | case "sell": { 323 | if (args.length < 2) return true; 324 | Player player = Bukkit.getPlayer(args[1]); 325 | if (player == null) { 326 | sender.sendMessage("Player does not exist."); 327 | return false; 328 | } 329 | String depositoryID = args.length >= 3 ? args[2] : null; 330 | String itemID = args.length >= 4 ? args[3] : null; 331 | 332 | Depository depository = null; 333 | if (depositoryID != null) { 334 | depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 335 | if (depository == null) { 336 | PluginMessages.NO_DEPOSITORY.send(sender); 337 | return true; 338 | } 339 | } 340 | 341 | UserData userData = UltraDepository.getUserManager().getData(player); 342 | 343 | if (depository == null) { 344 | UltraDepository.getEconomyManager().sellAllItem(player, userData); 345 | sender.sendMessage("Success! " + player.getName() + "'s items had been sold."); 346 | return true; 347 | } 348 | 349 | DepositoryItem item = null; 350 | if (itemID != null) { 351 | item = depository.getItems().get(itemID); 352 | if (item == null) { 353 | PluginMessages.NO_ITEM.send(player); 354 | return true; 355 | } 356 | } 357 | if (item == null) { 358 | UltraDepository.getEconomyManager().sellAllItem(player, userData, depository); 359 | sender.sendMessage("Success! " + player.getName() + "'s " + depository.getIdentifier() + " had been sold."); 360 | return true; 361 | } 362 | 363 | Integer amount = null; 364 | if (args.length >= 5) { 365 | try { 366 | amount = Integer.parseInt(args[4]); 367 | } catch (Exception ignore) { 368 | } 369 | } 370 | 371 | if (amount != null && amount < 0) { 372 | PluginMessages.WRONG_NUMBER.send(sender); 373 | return true; 374 | } 375 | 376 | if (amount == null) { 377 | UltraDepository.getEconomyManager().sellAllItem(player, userData, item); 378 | sender.sendMessage("Success! " + player.getName() + "'s " + item.getTypeID() + " had been sold."); 379 | return true; 380 | } 381 | 382 | DepositoryItemData itemData = userData.getItemData(item); 383 | 384 | int limit = item.getLimit(); 385 | int sold = itemData.getSold(); 386 | int currentAmount = itemData.getAmount(); 387 | 388 | if (currentAmount < amount) { 389 | PluginMessages.NO_ENOUGH_ITEM.send(sender); 390 | return true; 391 | } 392 | 393 | if (currentAmount > (limit - sold)) { 394 | PluginMessages.ITEM_SOLD_LIMIT.send(sender, new Object[]{(limit - sold), limit}); 395 | return true; 396 | } 397 | 398 | UltraDepository.getEconomyManager().sellItem(player, userData, item, amount); 399 | sender.sendMessage("Success! " + player.getName() + "'s " + amount + " " + item.getTypeID() + " had been sold."); 400 | return true; 401 | } 402 | default: 403 | return helpConsole(sender); 404 | } 405 | } 406 | } 407 | 408 | 409 | @Nullable 410 | @Override 411 | public List onTabComplete( 412 | @NotNull CommandSender sender, @NotNull Command command, 413 | @NotNull String alias, @NotNull String[] args) { 414 | List allCompletes = new ArrayList<>(); 415 | if (sender instanceof Player) { 416 | // 玩家指令部分 417 | Player player = (Player) sender; 418 | if (player.hasPermission("UltraDepository.use")) { 419 | switch (args.length) { 420 | case 1: { 421 | allCompletes.add("open"); 422 | if (player.hasPermission("UltraDepository.Command.Sell")) allCompletes.add("sell"); 423 | if (player.hasPermission("UltraDepository.Command.SellAll")) allCompletes.add("sellAll"); 424 | break; 425 | } 426 | case 2: { 427 | String aim = args[0]; 428 | if (aim.equalsIgnoreCase("open") 429 | || (aim.equalsIgnoreCase("sell") 430 | && player.hasPermission("UltraDepository.Command.Sell")) 431 | || (aim.equalsIgnoreCase("sellAll") 432 | && player.hasPermission("UltraDepository.Command.SellAll"))) { 433 | allCompletes.addAll(UltraDepository.getDepositoryManager().getDepositories().keySet()); 434 | } 435 | break; 436 | } 437 | case 3: { 438 | String aim = args[0]; 439 | String depositoryID = args[1]; 440 | if ((aim.equalsIgnoreCase("sell") 441 | && player.hasPermission("UltraDepository.Command.Sell")) 442 | || (aim.equalsIgnoreCase("sellAll") 443 | && player.hasPermission("UltraDepository.Command.SellAll"))) { 444 | Depository depository = UltraDepository.getDepositoryManager().getDepository(depositoryID); 445 | if (depository != null) { 446 | allCompletes.addAll(depository.getItems().keySet()); 447 | } 448 | } 449 | break; 450 | } 451 | } 452 | } 453 | } else { 454 | //后台指令部分 455 | switch (args.length) { 456 | case 1: { 457 | allCompletes.add("info"); 458 | allCompletes.add("add"); 459 | allCompletes.add("remove"); 460 | allCompletes.add("sell"); 461 | break; 462 | } 463 | case 2: { 464 | allCompletes = Bukkit.getOnlinePlayers().stream().map(HumanEntity::getName).collect(Collectors.toList()); 465 | break; 466 | } 467 | case 3: { 468 | allCompletes.addAll(UltraDepository.getDepositoryManager().getDepositories().keySet()); 469 | break; 470 | } 471 | case 4: { 472 | Depository depository = UltraDepository.getDepositoryManager().getDepository(args[2]); 473 | if (depository != null) { 474 | allCompletes.addAll(depository.getItems().keySet()); 475 | } 476 | break; 477 | } 478 | } 479 | } 480 | 481 | return allCompletes.stream() 482 | .filter(s -> StringUtil.startsWithIgnoreCase(s, args[args.length - 1])) 483 | .limit(10).collect(Collectors.toList()); 484 | } 485 | } 486 | --------------------------------------------------------------------------------