- 版权类型
- 原创
- 插件中文名称
- 动态市场
- 插件英文名称
- MuuMD
- 原帖地址
- #
- 支持的核心(服务端)
- Spigot
- Paper
- Purpur
- 其他核心
- 语言支持
- 中文(简体)
- 前置组件
- Vault 必需 经济系统核心依赖
PlaceholderAPI 可选 提供占位符变量支持
WarZSafeBox 可选 保险箱物品价值统计 https://www.minebbs.com/resources/warzsafebox-mc.11399/
- 适配版本(Java)
- 1.21
- 1.20
- 1.19
- 1.18
动态定价引擎商品价格随供需实时波动,购买推高价格,出售压低价格,设有上下限保护机制防止极端波动。
库存与补货系统独立库存追踪,支持每日自动补货,可配置库存上限与补充数量。
税收调控买卖双向税收,有效回收服务器货币,控制经济通胀。
可视化界面分类导航、分页浏览、实时价格与库存显示,操作简洁直观。
数据持久化SQLite 本地存储,完整交易日志记录,配置热重载。
零外部依赖仅需 Vault,无需 MySQL 等外部服务,开箱即用。
高度可配置价格波动率、税率、库存、音效等参数均可自定义调整。
轻量高效模块化架构,SQLite 存储,资源占用低,性能稳定。
UI图标为mod图标可以自行修改图标
配置界面
可手动控制涨跌
提供的API大佬们来制作附属包!!!
库存与补货系统独立库存追踪,支持每日自动补货,可配置库存上限与补充数量。
税收调控买卖双向税收,有效回收服务器货币,控制经济通胀。
可视化界面分类导航、分页浏览、实时价格与库存显示,操作简洁直观。
数据持久化SQLite 本地存储,完整交易日志记录,配置热重载。
为什么选择我们?
真实经济体验供需定价机制让市场更具策略性,区别于传统固定价格商店。零外部依赖仅需 Vault,无需 MySQL 等外部服务,开箱即用。
高度可配置价格波动率、税率、库存、音效等参数均可自定义调整。
轻量高效模块化架构,SQLite 存储,资源占用低,性能稳定。
| 指令 | 说明 |
|---|---|
/muudm category <名称> | 创建分类 (需手持物品作为图标) |
/muudm delcategory <名称> | 删除分类 |
/muudm add <价格> <是否可购买> [分类] [权重] [每日库存] | 添加手持物品到市场 |
/muudm setprice <价格> | 修改手持物品价格 |
/muudm setstock <库存> | 修改手持物品库存 |
/muudm remove | 从市场移除手持物品 |
/muudm reload | 重载配置文件 |
/muudm restock | 手动补充所有物品库存 |
/muudm list | 查看所有物品ID和材质名 |
/muudm export | 导出分类排序到配置 |
/muudm config | 打开物品配置GUI |
配置界面
可手动控制涨跌
代码:
# MuuDm 动态市场配置文件
# 税收设置
tax:
buy: 0.1 # 购买税率 10%
sell: 0.1 # 出售税率 10%
# 价格设置
price:
buy-change-rate: 0.001 # 购买时价格上涨率 0.1%
sell-change-rate: 0.002 # 出售时价格下跌率 0.2%
# 价格波动限制(基于原始价格)
max-rate: 1.2 # 价格上限:原始价格的 120%
min-rate: 0.5 # 价格下限:原始价格的 50%
# 管理员手动调价幅度(用于GUI配置界面)
adjust-step: 5 # 每次调整的百分比,如 5 表示上涨/下跌 5%
# 每日价格上涨设置
daily-increase:
enabled: true # 是否启用每日价格上涨
rate: 0.01 # 每日上涨率 1%
hour: 0 # 每天几点执行(0-23,0表示凌晨0点)
# 音效设置
sound:
enabled: true # 是否启用音效
buy-success:
sound: minecraft:entity.experience_orb.pickup # 购买成功音效
volume: 1.0
pitch: 1.2
buy-fail:
sound: minecraft:entity.villager.no # 购买失败音效
volume: 1.0
pitch: 1.0
sell-success:
sound: minecraft:block.note_block.bell # 出售成功音效
volume: 1.0
pitch: 1.0
sell-fail:
sound: minecraft:entity.villager.no # 出售失败音效
volume: 1.0
pitch: 1.0
click:
sound: minecraft:ui.button.click # 界面点击音效
volume: 0.5
pitch: 1.0
# GUI图标设置
# 格式: minecraft:item_name 或 modid:item_name
# 也支持旧格式: MATERIAL_NAME (如 EMERALD)
gui-icons:
# 主菜单
main-menu:
buy: minecraft:emerald # 购买按钮图标
sell: minecraft:gold_ingot # 出售按钮图标
info: minecraft:book # 信息按钮图标
# 购买界面
buy-gui:
all-category: minecraft:nether_star # 全部分类按钮
category-page: minecraft:book # 分类翻页按钮
balance: minecraft:gold_nugget # 余额显示图标
switch-sell: minecraft:gold_ingot # 切换出售按钮
prev-page: minecraft:arrow # 上一页按钮
next-page: minecraft:arrow # 下一页按钮
close: minecraft:barrier # 关闭按钮
divider: minecraft:black_stained_glass_pane # 分割栏
filler: minecraft:gray_stained_glass_pane # 填充玻璃
# 出售界面
sell-gui:
confirm: minecraft:emerald_block # 确认出售按钮
back: minecraft:barrier # 返回按钮
filler: minecraft:gray_stained_glass_pane # 填充玻璃
可用变量 (PlaceholderAPI)
背包相关
| 变量 | 说明 |
|---|---|
%muudm_inventory_value% | 背包物品总价值 (扣税后,中文单位) |
%muudm_inventory_value_raw% | 背包物品总价值 (扣税后,原始数字) |
%muudm_inventory_value_notax% | 背包物品总价值 (不含税,中文单位) |
%muudm_inventory_value_notax_raw% | 背包物品总价值 (不含税,原始数字) |
%muudm_inventory_count% | 背包中可出售物品数量 |
%muudm_inventory_types% | 背包中可出售物品种类数 |
保险箱相关 (需安装 WarZSafeBox) https://www.minebbs.com/resources/warzsafebox-mc.11399/
| 变量 | 说明 |
|---|---|
%muudm_safebox_value% | 保险箱物品总价值 (不含税,中文单位) |
%muudm_safebox_value_raw% | 保险箱物品总价值 (不含税,原始数字) |
%muudm_safebox_value_tax% | 保险箱物品总价值 (扣税后,中文单位) |
%muudm_safebox_value_tax_raw% | 保险箱物品总价值 (扣税后,原始数字) |
总价值 (背包 + 保险箱)
| 变量 | 说明 |
|---|---|
%muudm_total_value% | 总价值 (不含税,中文单位) |
%muudm_total_value_raw% | 总价值 (不含税,原始数字) |
%muudm_total_value_tax% | 总价值 (扣税后,中文单位) |
%muudm_total_value_tax_raw% | 总价值 (扣税后,原始数字) |
MuuDm-Q—— 市场交易排行榜/趋势分析 附属插件
代码:
#### Maven
```xml
<repositories>
<repository>
<id>local-repo</id>
<url>file://${project.basedir}/libs</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.muumuui.muudm</groupId>
<artifactId>MuuDm</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
```
#### Gradle
```groovy
dependencies {
compileOnly files('libs/MuuDm.jar')
}
```
### plugin.yml配置
```yaml
depend: [MuuDm]
# 或软依赖
softdepend: [MuuDm]
```
### 基本使用
```java
import com.muumuuimuuDm.api.MuuDmAPI;
public class MyPlugin extends JavaPlugin {
private MuuDmAPI marketAPI;
@Override
public void onEnable() {
// 检查MuuDm是否已加载
if (!MuuDmAPI.isInitialized()) {
getLogger().severe("MuuDm未安装或未启动!");
getServer().getPluginManager().disablePlugin(this);
return;
}
// 获取API实例
marketAPI = MuuDmAPI.getInstance();
// 使用API
getLogger().info("市场中有 " + marketAPI.getTotalItemCount() + " 个物品");
}
}
```
---
## 核心API
### MuuDmAPI 类
#### 获取API实例
```java
MuuDmAPI api = MuuDmAPI.getInstance();
```
#### 物品查询
```java
// 根据ID获取物品
MarketItem item = api.getItemById(1);
// 获取所有物品
Collection<MarketItem> allItems = api.getAllItems();
// 获取可购买的物品
List<MarketItem> purchasable = api.getPurchasableItems();
// 根据分类获取物品
List<MarketItem> items = api.getItemsByCategory("矿物");
// 通过ItemStack查找相似物品
ItemStack diamond = new ItemStack(Material.DIAMOND);
MarketItem marketDiamond = api.findSimilarItem(diamond);
```
#### 价格管理
```java
// 设置物品价格
api.setItemPrice(item, 100.0);
// 按百分比调整价格(+5%)
api.adjustItemPrice(item, 5.0);
// 调整所有物品价格(-10%)
api.adjustAllPrices(-10.0);
// 重置所有价格为原价
api.resetAllPrices();
```
#### 库存管理
```java
// 设置库存
api.setItemStock(item, 1000);
// 设置每日补货量
api.setItemDailyRestock(item, 100);
// 设置最大库存
api.setItemMaxStock(item, 5000);
// 设置单次最大购买量
api.setItemMaxBuyAmount(item, 64);
// 手动补充所有库存
api.restockAll();
```
#### 分类管理
```java
// 获取所有分类
Set<String> categories = api.getCategories();
// 创建新分类
ItemStack icon = new ItemStack(Material.DIAMOND_BLOCK);
api.createCategory("珍稀物品", icon);
// 删除分类
api.removeCategory("珍稀物品");
// 获取分类中的物品数量
int count = api.getItemCountByCategory("矿物");
```
#### 物品管理
```java
// 添加物品到市场
ItemStack item = new ItemStack(Material.DIAMOND);
api.addItem(
item, // 物品
"矿物", // 分类
100.0, // 价格
10, // 权重
50, // 每日补货
true // 是否可购买
);
// 移除物品
api.removeItem(marketItem);
// 重新加载所有物品
api.loadItems();
```
#### 统计查询
```java
// 获取市场总物品数
int total = api.getTotalItemCount();
// 获取市场总价值
double value = api.getTotalMarketValue();
// 获取最贵的10个物品
List<MarketItem> expensive = api.getMostExpensiveItems(10);
// 获取最便宜的10个物品
List<MarketItem> cheap = api.getCheapestItems(10);
// 获取库存最少的10个物品
List<MarketItem> lowStock = api.getLowestStockItems(10);
// 获取涨幅最大的10个物品
List<MarketItem> inflated = api.getMostInflatedItems(10);
// 获取跌幅最大的10个物品
List<MarketItem> deflated = api.getMostDeflatedItems(10);
```
#### 异步API
```java
// 异步获取所有物品
api.getAllItemsAsync().thenAccept(items -> {
// 在主线程中执行
Bukkit.getScheduler().runTask(plugin, () -> {
getLogger().info("加载了 " + items.size() + " 个物品");
});
});
// 异步获取物品价格历史
api.getItemPriceHistoryAsync(1, 7).thenAccept(history -> {
double currentPrice = (double) history.get("currentPrice");
getLogger().info("当前价格: " + currentPrice);
});
// 异步批量调整价格
api.adjustAllPricesAsync(5.0).thenAccept(count -> {
getLogger().info("调整了 " + count + " 个物品的价格");
});
```
---
## 事件系统
### 可用事件
#### MarketBuyEvent
玩家购买物品时触发
```java
@EventHandler
public void onMarketBuy(MarketBuyEvent event) {
Player player = event.getPlayer();
MarketItem item = event.getItem();
int amount = event.getAmount();
double totalPrice = event.getTotalPrice();
double priceChange = event.getPriceChange();
player.sendMessage("你购买了 " + amount + " 个物品");
player.sendMessage("价格上涨了 " + priceChange);
// 取消购买
if (player.getLevel() < 10) {
event.setCancelled(true);
player.sendMessage("等级不足10级,无法购买!");
}
}
```
#### MarketSellEvent
玩家出售物品时触发
```java
@EventHandler
public void onMarketSell(MarketSellEvent event) {
Player player = event.getPlayer();
MarketItem item = event.getItem();
int amount = event.getAmount();
double totalPrice = event.getTotalPrice();
player.sendMessage("你出售了 " + amount + " 个物品,获得 " + totalPrice);
// 取消出售
if (item.getStock() > 10000) {
event.setCancelled(true);
player.sendMessage("该物品库存已满,暂时无法出售!");
}
}
```
#### MarketPriceChangeEvent
物品价格变化时触发
```java
@EventHandler
public void onPriceChange(MarketPriceChangeEvent event) {
MarketItem item = event.getItem();
double oldPrice = event.getOldPrice();
double newPrice = event.getNewPrice();
PriceChangeReason reason = event.getReason();
// 阻止价格下跌
if (newPrice < oldPrice) {
event.setCancelled(true);
return;
}
// 修改价格
if (newPrice > oldPrice * 2) {
event.setNewPrice(oldPrice * 2); // 最多涨到2倍
}
// 根据原因执行不同操作
switch (reason) {
case BUY:
// 玩家购买导致涨价
break;
case SELL:
// 玩家出售导致降价
break;
case DAILY_INCREASE:
// 每日自动涨价
break;
case ADMIN_ADJUST:
// 管理员手动调整
break;
}
}
```
#### MarketItemAddEvent
物品被添加到市场时触发
```java
@EventHandler
public void onItemAdd(MarketItemAddEvent event) {
MarketItem item = event.getItem();
CommandSender adder = event.getAdder();
// 阻止添加某些物品
if (item.getItem().getType() == Material.BEDROCK) {
event.setCancelled(true);
adder.sendMessage("不能添加基岩到市场!");
}
}
```
#### MarketItemRemoveEvent
物品从市场移除时触发
```java
@EventHandler
public void onItemRemove(MarketItemRemoveEvent event) {
MarketItem item = event.getItem();
CommandSender remover = event.getRemover();
// 记录日志
plugin.getLogger().info(remover.getName() + " 移除了物品: " + item.getId());
}
```
#### MarketRestockEvent
市场执行每日补货时触发
```java
@EventHandler
public void onRestock(MarketRestockEvent event) {
int count = event.getRestockedItems();
Bukkit.broadcastMessage("市场已补货 " + count + " 个物品!");
}
```
---
## 回调系统
### PreTradeCallback
交易前回调,可以阻止交易
```java
MuuDmAPI api = MuuDmAPI.getInstance();
api.registerPreTradeCallback((player, item, amount, isBuying) -> {
// 检查玩家等级
if (player.getLevel() < 10) {
player.sendMessage("等级不足10级,无法交易!");
return false; // 阻止交易
}
// 检查物品类型
if (item.getItem().getType() == Material.DIAMOND && isBuying) {
if (amount > 10) {
player.sendMessage("钻石每次最多只能购买10个!");
return false;
}
}
return true; // 允许交易
});
```
### PostTradeCallback
交易后回调
```java
api.registerPostTradeCallback((player, item, amount, totalPrice, isBuying) -> {
// 交易后奖励积分
if (isBuying) {
// 购买后给予积分
givePoints(player, amount);
} else {
// 出售后给予额外奖励
if (totalPrice > 1000) {
player.sendMessage("大额交易奖励!获得额外100金币");
api.getEconomy().depositPlayer(player, 100);
}
}
// 统计玩家交易次数
incrementTradeCount(player);
});
```
### PriceCalculationCallback
自定义价格计算
```java
api.setPriceCalculationCallback((item, currentPrice, amount, isBuying) -> {
// 自定义价格计算逻辑
// 例如:根据交易量动态调整涨跌幅
double baseRate = isBuying ? 0.001 : 0.002;
double multiplier = Math.log(amount + 1) / Math.log(2); // 对数缩放
double rate = baseRate * multiplier;
double newPrice;
if (isBuying) {
newPrice = currentPrice * (1 + rate);
} else {
newPrice = currentPrice * (1 - rate);
}
// 限制价格范围
double minPrice = item.getOriginalPrice() * 0.5;
double maxPrice = item.getOriginalPrice() * 2.0;
return Math.max(minPrice, Math.min(maxPrice, newPrice));
});
```
### RestockCallback
自定义补货逻辑
```java
api.setRestockCallback((item, currentStock, defaultRestock) -> {
// 根据当前库存动态调整补货量
int maxStock = item.getMaxStock() > 0 ? item.getMaxStock() : 1000;
if (currentStock < maxStock * 0.2) {
// 库存低于20%,双倍补货
return defaultRestock * 2;
} else if (currentStock > maxStock * 0.8) {
// 库存高于80%,减半补货
return defaultRestock / 2;
}
return defaultRestock;
});
```
---
## Hook框架
### 创建自定义Hook
```java
import com.muumuuimuuDm.api.hook.PluginHook;
public class MyPluginHook extends PluginHook {
private Method someMethod;
public MyPluginHook(Logger logger) {
super("TargetPluginName", logger);
}
@Override
protected boolean onHook() {
try {
// 获取目标插件的类
Class<?> targetClass = getClass("com.example.targetplugin.TargetClass");
if (targetClass == null) return false;
// 获取方法
someMethod = getMethod(targetClass, "someMethod", Player.class);
return true;
} catch (Exception e) {
logger.warning("Hook失败: " + e.getMessage());
return false;
}
}
// 提供公开方法供外部调用
public Object callSomeMethod(Player player) {
if (!isEnabled()) return null;
try {
Object instance = hookedPlugin; // 或其他实例
return invokeMethod(instance, someMethod, player);
} catch (Exception e) {
return null;
}
}
}
```
### 使用Hook管理器
```java
import com.muumuuimuuDm.api.hook.HookManager;
// 在插件主类中
HookManager hookManager = new HookManager(this);
// 注册Hook
MyPluginHook myHook = new MyPluginHook(getLogger());
hookManager.registerHook(myHook);
// 使用Hook
if (hookManager.isHookEnabled("TargetPluginName")) {
Optional<MyPluginHook> hook = hookManager.getHook("TargetPluginName", MyPluginHook.class);
hook.ifPresent(h -> {
Object result = h.callSomeMethod(player);
});
}
// 注销时清理
@Override
public void onDisable() {
hookManager.unregisterAll();
}
```
### 内置Hook
#### CitizensHook
```java
import com.muumuuimuuDm.api.hook.CitizensHook;
CitizensHook citizens = new CitizensHook(getLogger());
if (citizens.isEnabled()) {
// 检查实体是否为NPC
if (citizens.isNPC(entity)) {
// 处理NPC交互
}
}
```
#### PlayerPointsHook
```java
import com.muumuuimuuDm.api.hook.PlayerPointsHook;
PlayerPointsHook points = new PlayerPointsHook(getLogger());
if (points.isEnabled()) {
// 获取玩家点券
int balance = points.getPoints(player);
// 扣除点券
if (points.takePoints(player, 100)) {
player.sendMessage("消费100点券");
}
}
```
#### EssentialsHook
```java
import com.muumuuimuuDm.api.hook.EssentialsHook;
EssentialsHook ess = new EssentialsHook(getLogger());
if (ess.isEnabled()) {
// 检查玩家是否AFK
if (ess.isAfk(player)) {
player.sendMessage("你现在是AFK状态,无法交易");
return;
}
}
```
---
## 数据统计API
### MarketDataAPI 类
```java
import com.muumuuimuuDm.api.data.MarketDataAPI;
MarketDataAPI dataAPI = new MarketDataAPI(MuuDm.getInstance());
```
### 交易记录查询
```java
// 获取玩家的交易记录
List<TradeRecord> records = dataAPI.getPlayerTrades(player.getUniqueId().toString(), 50);
for (TradeRecord record : records) {
System.out.println(record.playerName + " " +
record.tradeType + " " +
record.amount + "个物品 " +
"价格: " + record.price);
}
// 异步获取
dataAPI.getPlayerTradesAsync(uuid, 50).thenAccept(records -> {
// 处理记录
});
// 获取物品的交易记录
List<TradeRecord> itemTrades = dataAPI.getItemTrades(itemId, 100);
// 获取最近的交易
List<TradeRecord> recentTrades = dataAPI.getRecentTrades(20);
```
### 玩家统计
```java
// 获取玩家统计
PlayerStats stats = dataAPI.getPlayerStats(player.getUniqueId().toString());
if (stats != null) {
player.sendMessage("总交易次数: " + stats.totalTrades);
player.sendMessage("总消费: " + stats.totalSpent);
player.sendMessage("总收入: " + stats.totalEarned);
player.sendMessage("总税收: " + stats.totalTax);
}
```
### 排行榜
```java
// 消费排行榜(前10名)
List<PlayerStats> topSpenders = dataAPI.getTopSpenders(10);
for (int i = 0; i < topSpenders.size(); i++) {
PlayerStats stats = topSpenders.get(i);
System.out.println((i + 1) + ". " + stats.playerName +
" 消费: " + stats.totalSpent);
}
// 收入排行榜
List<PlayerStats> topSellers = dataAPI.getTopSellers(10);
// 交易次数排行榜
List<PlayerStats> topTraders = dataAPI.getTopTraders(10);
```
### 物品统计
```java
// 获取物品统计
ItemStats itemStats = dataAPI.getItemStats(itemId);
if (itemStats != null) {
System.out.println("总购买量: " + itemStats.totalBought);
System.out.println("总出售量: " + itemStats.totalSold);
System.out.println("总收益: " + itemStats.totalRevenue);
}
// 获取热门商品
List<ItemStats> hotItems = dataAPI.getHotItems(10);
```
### 服务器统计
```java
// 总交易次数
int totalTrades = dataAPI.getTotalTradeCount();
// 总交易额
double totalVolume = dataAPI.getTotalTradeVolume();
// 总税收
double totalTax = dataAPI.getTotalTaxCollected();
System.out.println("市场总交易次数: " + totalTrades);
System.out.println("市场总交易额: " + totalVolume);
System.out.println("市场总税收: " + totalTax);
```
---
## GUI扩展
### 创建自定义GUI
#### 方法1: 实现CustomGUI接口
```java
import com.muumuuimuuDm.api.gui.CustomGUI;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
public class MyMarketGUI implements CustomGUI {
private Inventory inventory;
@Override
public String getIdentifier() {
return "my_custom_market";
}
@Override
public String getTitle() {
return "§6我的自定义市场";
}
@Override
public int getSize() {
return 54; // 6行
}
@Override
public void open(Player player) {
inventory = Bukkit.createInventory(null, getSize(), getTitle());
// 设置GUI内容
setupInventory(player);
player.openInventory(inventory);
}
@Override
public boolean handleClick(Player player, int slot, boolean isShiftClick, boolean isRightClick) {
// 处理点击事件
// 返回true取消事件,false允许事件
if (slot == 0) {
player.sendMessage("你点击了第一个槽位!");
return true; // 取消事件
}
return true;
}
@Override
public Inventory getInventory() {
return inventory;
}
private void setupInventory(Player player) {
// 设置物品
// ...
}
}
```
#### 方法2: 继承AbstractCustomGUI
```java
import com.muumuuimuuDm.api.gui.AbstractCustomGUI;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class SimpleGUI extends AbstractCustomGUI {
public SimpleGUI() {
super("simple_gui", "§a简单GUI", 27); // 3行
}
@Override
protected void setupGUI(Player player) {
// 设置GUI内容
inventory.setItem(13, new ItemStack(Material.DIAMOND));
}
@Override
public boolean handleClick(Player player, int slot, boolean isShiftClick, boolean isRightClick) {
if (slot == 13) {
player.sendMessage("你点击了钻石!");
}
return true;
}
}
```
### 注册和使用GUI
```java
import com.muumuuimuuDm.api.gui.GUIRegistry;
// 注册GUI
MyMarketGUI gui = new MyMarketGUI();
GUIRegistry.register(gui);
// 打开GUI
GUIRegistry.openGUI("my_custom_market", player);
// 检查GUI是否已注册
if (GUIRegistry.isRegistered("my_custom_market")) {
// ...
}
// 获取GUI实例
Optional<CustomGUI> optionalGUI = GUIRegistry.getGUI("my_custom_market");
optionalGUI.ifPresent(g -> g.open(player));
// 注销GUI
GUIRegistry.unregister("my_custom_market");
```
### GUI点击处理
需要在GUIListener中处理自定义GUI的点击:
```java
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (event.getInventory().getHolder() instanceof CustomGUI) {
CustomGUI gui = (CustomGUI) event.getInventory().getHolder();
event.setCancelled(true);
Player player = (Player) event.getWhoClicked();
int slot = event.getRawSlot();
boolean shift = event.getClick().isShiftClick();
boolean right = event.getClick().isRightClick();
gui.handleClick(player, slot, shift, right);
}
}
```
---
## PlaceholderAPI变量
### 背包物品价值
| 变量 | 说明 |
|------|------|
| `%muudm_inventory_value%` | 背包物品总价值(扣税后,中文单位) |
| `%muudm_inventory_value_raw%` | 背包物品总价值(扣税后,原始数字) |
| `%muudm_inventory_value_notax%` | 背包物品总价值(不含税,中文单位) |
| `%muudm_inventory_value_notax_raw%` | 背包物品总价值(不含税,原始数字) |
| `%muudm_inventory_count%` | 背包可出售物品数量 |
| `%muudm_inventory_types%` | 背包可出售物品种类数 |
### 保险箱物品价值
| 变量 | 说明 |
|------|------|
| `%muudm_safebox_value%` | 保险箱物品总价值(不含税,中文单位) |
| `%muudm_safebox_value_raw%` | 保险箱物品总价值(不含税,原始数字) |
| `%muudm_safebox_value_tax%` | 保险箱物品总价值(扣税后,中文单位) |
| `%muudm_safebox_value_tax_raw%` | 保险箱物品总价值(扣税后,原始数字) |
### 总价值
| 变量 | 说明 |
|------|------|
| `%muudm_total_value%` | 总价值(背包+保险箱,不含税,中文单位) |
| `%muudm_total_value_raw%` | 总价值(背包+保险箱,不含税,原始数字) |
| `%muudm_total_value_tax%` | 总价值(背包+保险箱,扣税后,中文单位) |
| `%muudm_total_value_tax_raw%` | 总价值(背包+保险箱,扣税后,原始数字) |
### 物品信息(支持动态ID)
| 变量格式 | 说明 | 示例 |
|---------|------|------|
| `%muudm_item_price_<id>%` | 物品当前价格 | `%muudm_item_price_1%` |
| `%muudm_item_stock_<id>%` | 物品库存 | `%muudm_item_stock_1%` |
| `%muudm_item_name_<id>%` | 物品名称 | `%muudm_item_name_1%` |
### 市场信息
| 变量 | 说明 |
|------|------|
| `%muudm_market_items%` | 市场物品总数 |
| `%muudm_market_categories%` | 市场分类总数 |
| `%muudm_market_value%` | 市场总价值(中文单位) |
| `%muudm_market_value_raw%` | 市场总价值(原始数字) |
### 玩家信息
| 变量 | 说明 |
|------|------|
| `%muudm_balance%` | 玩家余额(中文单位) |
| `%muudm_balance_raw%` | 玩家余额(原始数字) |
### 配置信息
| 变量 | 说明 |
|------|------|
| `%muudm_tax_buy%` | 购买税率(百分比) |
| `%muudm_tax_sell%` | 出售税率(百分比) |
---
## 完整示例
### 示例1: 创建市场扩展插件
```java
package com.example.marketaddon;
import com.muumuuimuuDm.api.MuuDmAPI;
import com.muumuuimuuDm.event.MarketBuyEvent;
import com.muumuuimuuDm.model.MarketItem;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
public class MarketAddon extends JavaPlugin implements Listener {
private MuuDmAPI api;
@Override
public void onEnable() {
// 检查MuuDm
if (!MuuDmAPI.isInitialized()) {
getLogger().severe("MuuDm未安装!");
getServer().getPluginManager().disablePlugin(this);
return;
}
api = MuuDmAPI.getInstance();
getServer().getPluginManager().registerEvents(this, this);
// 注册交易前回调
api.registerPreTradeCallback(this::onPreTrade);
// 注册交易后回调
api.registerPostTradeCallback(this::onPostTrade);
getLogger().info("市场扩展插件已启用!");
}
private boolean onPreTrade(Player player, MarketItem item, int amount, boolean isBuying) {
// VIP玩家免税
if (player.hasPermission("addon.vip")) {
player.sendMessage("§aVIP免税优惠!");
return true;
}
// 限制购买数量
if (isBuying && amount > 64) {
player.sendMessage("§c单次最多只能购买64个!");
return false;
}
return true;
}
private void onPostTrade(Player player, MarketItem item, int amount, double totalPrice, boolean isBuying) {
// 大额交易奖励
if (totalPrice > 10000) {
player.sendMessage("§6§l大额交易奖励!§e获得100积分");
// 给予积分...
}
}
@EventHandler
public void onMarketBuy(MarketBuyEvent event) {
Player player = event.getPlayer();
// 显示价格变化
double change = event.getPriceChangePercent();
player.sendMessage(String.format("§e价格上涨了 §c%.2f%%", change));
}
}
```
### 示例2: 自定义价格算法
```java
package com.example.pricecontroller;
import com.muumuuimuuDm.api.MuuDmAPI;
import org.bukkit.plugin.java.JavaPlugin;
public class PriceController extends JavaPlugin {
@Override
public void onEnable() {
MuuDmAPI api = MuuDmAPI.getInstance();
// 设置自定义价格计算
api.setPriceCalculationCallback((item, currentPrice, amount, isBuying) -> {
// 基于供需的非线性价格模型
double elasticity = 0.5; // 价格弹性系数
double stockRatio = (double) item.getStock() / item.getMaxStock();
// 库存越少,价格变化越大
double impact = elasticity * (1 - stockRatio);
double newPrice;
if (isBuying) {
// 购买:库存减少,价格上涨
double rate = 0.001 * (1 + impact);
newPrice = currentPrice * (1 + rate * amount);
} else {
// 出售:库存增加,价格下跌
double rate = 0.002 * (1 + impact);
newPrice = currentPrice * (1 - rate * amount);
}
// 限制价格范围
double minPrice = item.getOriginalPrice() * 0.3;
double maxPrice = item.getOriginalPrice() * 3.0;
return Math.max(minPrice, Math.min(maxPrice, newPrice));
});
getLogger().info("自定义价格算法已启用!");
}
}
```
### 示例3: 市场统计看板
```java
package com.example.marketstats;
import com.muumuuimuuDm.api.MuuDmAPI;
import com.muumuuimuuDm.api.data.MarketDataAPI;
import com.muumuuimuuDm.api.data.MarketDataAPI.PlayerStats;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
public class MarketStats extends JavaPlugin {
private MarketDataAPI dataAPI;
@Override
public void onEnable() {
dataAPI = new MarketDataAPI(com.muumuuimuuDm.MuuDm.getInstance());
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (command.getName().equalsIgnoreCase("marketstats")) {
if (sender instanceof Player) {
showStats((Player) sender);
}
return true;
}
return false;
}
private void showStats(Player player) {
player.sendMessage("§6§l=== 市场统计 ===");
// 服务器统计
int totalTrades = dataAPI.getTotalTradeCount();
double totalVolume = dataAPI.getTotalTradeVolume();
double totalTax = dataAPI.getTotalTaxCollected();
player.sendMessage("§e总交易次数: §f" + totalTrades);
player.sendMessage("§e总交易额: §f" + String.format("%.2f", totalVolume));
player.sendMessage("§e总税收: §f" + String.format("%.2f", totalTax));
player.sendMessage("");
player.sendMessage("§6§l=== 消费排行榜 TOP 5 ===");
// 消费排行榜
List<PlayerStats> topSpenders = dataAPI.getTopSpenders(5);
for (int i = 0; i < topSpenders.size(); i++) {
PlayerStats stats = topSpenders.get(i);
player.sendMessage(String.format("§e%d. §f%s §7- §6%.2f",
i + 1, stats.playerName, stats.totalSpent));
}
// 个人统计
player.sendMessage("");
player.sendMessage("§6§l=== 你的统计 ===");
PlayerStats myStats = dataAPI.getPlayerStats(player.getUniqueId().toString());
if (myStats != null) {
player.sendMessage("§e交易次数: §f" + myStats.totalTrades);
player.sendMessage("§e总消费: §c" + String.format("%.2f", myStats.totalSpent));
player.sendMessage("§e总收入: §a" + String.format("%.2f", myStats.totalEarned));
player.sendMessage("§e总税收: §7" + String.format("%.2f", myStats.totalTax));
} else {
player.sendMessage("§7还没有交易记录");
}
}
}
```
### 示例4: 自定义GUI
```java
package com.example.customgui;
import com.muumuuimuuDm.api.MuuDmAPI;
import com.muumuuimuuDm.api.gui.AbstractCustomGUI;
import com.muumuuimuuDm.api.gui.GUIRegistry;
import com.muumuuimuuDm.model.MarketItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
import java.util.List;
public class CustomGUIPlugin extends JavaPlugin {
@Override
public void onEnable() {
// 注册自定义GUI
GUIRegistry.register(new HotItemsGUI());
}
// 热门商品GUI
static class HotItemsGUI extends AbstractCustomGUI {
public HotItemsGUI() {
super("hot_items", "§c§l热门商品", 54);
}
@Override
protected void setupGUI(Player player) {
MuuDmAPI api = MuuDmAPI.getInstance();
// 获取最贵的物品
List<MarketItem> expensive = api.getMostExpensiveItems(9);
for (int i = 0; i < Math.min(expensive.size(), 9); i++) {
MarketItem item = expensive.get(i);
ItemStack display = item.getItem().clone();
ItemMeta meta = display.getItemMeta();
if (meta != null) {
meta.setDisplayName("§6" + item.getItem().getType().toString());
meta.setLore(Arrays.asList(
"§e价格: §f" + String.format("%.2f", item.getCurrentPrice()),
"§e库存: §f" + item.getStock(),
"§a左键购买 1个",
"§aShift+左键购买 64个"
));
display.setItemMeta(meta);
}
inventory.setItem(i, display);
}
// 添加关闭按钮
ItemStack close = new ItemStack(Material.BARRIER);
ItemMeta closeMeta = close.getItemMeta();
if (closeMeta != null) {
closeMeta.setDisplayName("§c关闭");
close.setItemMeta(closeMeta);
}
inventory.setItem(49, close);
}
@Override
public boolean handleClick(Player player, int slot, boolean isShiftClick, boolean isRightClick) {
if (slot == 49) {
player.closeInventory();
return true;
}
if (slot < 9) {
MuuDmAPI api = MuuDmAPI.getInstance();
List<MarketItem> expensive = api.getMostExpensiveItems(9);
if (slot < expensive.size()) {
MarketItem item = expensive.get(slot);
int amount = isShiftClick ? 64 : 1;
// 尝试购买
if (api.buyItem(item.getId(), amount)) {
player.sendMessage("§a购买成功!");
reload(player); // 刷新GUI
} else {
player.sendMessage("§c购买失败!");
}
}
}
return true;
}
}
}
```
---
## 结语
MuuDm API提供了丰富的扩展功能,让你能够:
- ✅ 监听和控制市场交易事件
- ✅ 自定义价格计算和补货逻辑
- ✅ 集成其他插件功能
- ✅ 创建自定义GUI界面
- ✅ 查询详细的交易统计数据
- ✅ 使用PlaceholderAPI变量
如有问题或建议,欢迎反馈!