第八批:雪球菜单入口接入SN系统(换载体转移SN)、邀请码一次性持久落库并按新规则发债券、代码层删除提成规则.txt改由邀请好友赢债券/规则.md控制
This commit is contained in:
@@ -639,6 +639,18 @@ public class ChatInputManager {
|
||||
reset(p);
|
||||
return;
|
||||
}
|
||||
// ★ 第八批(2026-10-12):邀请码一次性——已绑定过邀请人的
|
||||
// 禁止重复使用(invited_by 持久落库,重启后依然拦截)
|
||||
Object bound = db.getField(
|
||||
myName, "invited_by");
|
||||
if (bound != null
|
||||
&& !bound.toString().isEmpty()) {
|
||||
p.sendMessage(
|
||||
"§c你已绑定过邀请人(" + bound
|
||||
+ "),邀请码仅能使用一次");
|
||||
reset(p);
|
||||
return;
|
||||
}
|
||||
boolean found = false;
|
||||
for (Map<String, Object> u :
|
||||
db.getAllUsers()) {
|
||||
@@ -650,17 +662,32 @@ public class ChatInputManager {
|
||||
u.get("player_name");
|
||||
db.setField(myName,
|
||||
"invited_by", inv);
|
||||
db.addPoints(inv, 50);
|
||||
// ★ 第八批(2026-10-12):绑定奖励改按
|
||||
// 邀请好友赢债券/规则.md 发债券;
|
||||
// 旧 addPoints(inv,50) 积分随提成规则体系废弃
|
||||
int bondGive = mainPlugin
|
||||
.getCommission()
|
||||
.payInviteBond(inv, myName);
|
||||
p.sendMessage(
|
||||
"§a邀请成功!邀请人: "
|
||||
+ inv);
|
||||
Player ip =
|
||||
Bukkit.getPlayer(inv);
|
||||
Bukkit.getPlayerExact(inv);
|
||||
if (ip != null
|
||||
&& ip.isOnline())
|
||||
&& ip.isOnline()) {
|
||||
String extra = "";
|
||||
if (bondGive > 0) {
|
||||
extra = "§7,你获得 §e"
|
||||
+ bondGive
|
||||
+ " §a债券";
|
||||
} else if (bondGive == 0) {
|
||||
extra = "§7(今日邀请债券已达上限)";
|
||||
}
|
||||
ip.sendMessage("§a"
|
||||
+ myName
|
||||
+ " 使用了您的邀请码");
|
||||
+ " 使用了您的邀请码"
|
||||
+ extra);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 邀请好友赢债券奖励(配置:邀请好友赢债券/规则.md)。
|
||||
*
|
||||
* <p>★ 2026-10-12 第八批(用户决策):旧「提成规则.txt」(邀请人数:提成百分比)
|
||||
* 体系整体废弃——其 payCommission 因 Economy 移除早已是无调用死代码,
|
||||
* 签到链路奖励计算 (int)(1*rate) 恒为 0 从未生效(「邀请好友无效」根因)。
|
||||
* 代码层面删除旧 txt(启动时清理而非 SFTP 手删——否则 writeDefault
|
||||
* 会再次释放,写文件逻辑已一并移除),奖励改由新 md 规则控制:
|
||||
* 绑定邀请码成功时立即结算债券,受「单次邀请奖励上限」「单日邀请奖励上限」
|
||||
* 双重限制(当日累计按 bond_transaction type='invite_reward' 汇总,自然日重置)。</p>
|
||||
*/
|
||||
public class CommissionManager {
|
||||
|
||||
private final Main main;
|
||||
private final Logger logger;
|
||||
private final List<double[]> rules =
|
||||
new ArrayList<>();
|
||||
|
||||
/** 单次邀请奖励(债券数,绑定成功立即结算) */
|
||||
private volatile int perInviteReward = 10;
|
||||
/** 单日邀请奖励上限(债券数,自然日重置) */
|
||||
private volatile int dailyInviteCap = 100;
|
||||
|
||||
public CommissionManager(Main main) {
|
||||
this.main = main;
|
||||
@@ -21,189 +36,151 @@ public class CommissionManager {
|
||||
}
|
||||
|
||||
public void loadRules() {
|
||||
rules.clear();
|
||||
File f = new File(
|
||||
main.getDataFolder(),
|
||||
// ★ 代码层面删除旧提成规则.txt(手删会被再次释放,故写文件
|
||||
// 逻辑已移除,这里负责清理存量文件)
|
||||
File legacy = new File(main.getDataFolder(),
|
||||
"提成规则.txt");
|
||||
if (!f.exists()) {
|
||||
writeDefault(f);
|
||||
if (legacy.exists() && legacy.delete()) {
|
||||
logger.info("[Commission] 已删除旧版 提成规则.txt"
|
||||
+ "(改由 邀请好友赢债券/规则.md 控制)");
|
||||
}
|
||||
try (BufferedReader br =
|
||||
new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets
|
||||
.UTF_8))) {
|
||||
|
||||
File f = new File(main.getDataFolder(),
|
||||
"邀请好友赢债券/规则.md");
|
||||
if (!f.exists()) {
|
||||
try {
|
||||
main.saveResource(
|
||||
"邀请好友赢债券/规则.md", false);
|
||||
} catch (Exception e) {
|
||||
logger.warning("[Commission] 规则文件释放失败: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
if (!f.exists()) {
|
||||
logger.warning("[Commission] 规则文件缺失,使用默认值"
|
||||
+ " 单次=" + perInviteReward
|
||||
+ " 单日=" + dailyInviteCap);
|
||||
return;
|
||||
}
|
||||
boolean inCfg = false;
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty()
|
||||
|| line.startsWith("#"))
|
||||
String t = line.trim();
|
||||
if (t.startsWith("##") && t.contains("配置区")
|
||||
&& t.contains("开始")) {
|
||||
inCfg = true;
|
||||
continue;
|
||||
// 格式: 邀请人数:提成百分比
|
||||
// 如 "5:15" = 邀请5人提成15%
|
||||
String[] parts =
|
||||
line.split(":");
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
int count = Integer.parseInt(
|
||||
parts[0].trim());
|
||||
double rate = Double
|
||||
.parseDouble(
|
||||
parts[1]
|
||||
.trim()) / 100.0;
|
||||
rules.add(new double[]{
|
||||
count, rate});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (t.startsWith("##") && t.contains("配置区")
|
||||
&& t.contains("结束")) {
|
||||
break;
|
||||
}
|
||||
if (!inCfg || !t.startsWith("|")) continue;
|
||||
String[] cols = t.split("\\|", -1);
|
||||
if (cols.length < 4) continue; // | 键 | 解释 | 值 |
|
||||
String key = cols[1].trim();
|
||||
String val = cols[cols.length - 2].trim()
|
||||
.replace("`", "");
|
||||
if ("单次邀请奖励上限".equals(key)) {
|
||||
perInviteReward = parseIntSafe(val,
|
||||
perInviteReward);
|
||||
} else if ("单日邀请奖励上限".equals(key)) {
|
||||
dailyInviteCap = parseIntSafe(val,
|
||||
dailyInviteCap);
|
||||
}
|
||||
}
|
||||
// 按人数排序(从大到小)
|
||||
rules.sort((a, b) ->
|
||||
Double.compare(b[0], a[0]));
|
||||
logger.info(
|
||||
"[Commission] 加载 "
|
||||
+ rules.size() + " 条提成规则");
|
||||
logger.info("[Commission] 邀请债券规则: 单次="
|
||||
+ perInviteReward + " 单日=" + dailyInviteCap);
|
||||
} catch (Exception e) {
|
||||
logger.warning(
|
||||
"[Commission] 加载失败: "
|
||||
+ e.getMessage());
|
||||
logger.warning("[Commission] 规则解析失败: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDefault(File f) {
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.println("# 邀请提成规则");
|
||||
pw.println("# 格式: 邀请人数:提成百分比");
|
||||
pw.println("# 邀请10人提成20%");
|
||||
pw.println("10:20");
|
||||
pw.println("# 邀请5人提成15%");
|
||||
pw.println("5:15");
|
||||
pw.println("# 邀请1人提成10%");
|
||||
pw.println("1:10");
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取邀请人的提成比例
|
||||
* 根据已邀请人数匹配最高规则
|
||||
*/
|
||||
public double getCommissionRate(
|
||||
String inviter) {
|
||||
int inviteCount = getInviteCount(
|
||||
inviter);
|
||||
for (double[] rule : rules) {
|
||||
if (inviteCount >= (int) rule[0]) {
|
||||
return rule[1];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取邀请人数
|
||||
*/
|
||||
public int getInviteCount(
|
||||
String inviter) {
|
||||
private static int parseIntSafe(String s, int def) {
|
||||
try {
|
||||
Object val =
|
||||
main.getDb().getField(
|
||||
inviter,
|
||||
"monthly_invite_count");
|
||||
if (val instanceof Number) {
|
||||
return ((Number) val).intValue();
|
||||
}
|
||||
Object total =
|
||||
main.getDb().getField(
|
||||
inviter,
|
||||
"total_invite_count");
|
||||
if (total instanceof Number) {
|
||||
return ((Number) total).intValue();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
return (int) Double.parseDouble(s.trim());
|
||||
} catch (Exception e) {
|
||||
return def;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** 单次邀请奖励(债券) */
|
||||
public int getPerInviteReward() {
|
||||
return perInviteReward;
|
||||
}
|
||||
|
||||
/** 单日邀请奖励上限(债券) */
|
||||
public int getDailyInviteCap() {
|
||||
return dailyInviteCap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放提成
|
||||
* 被邀请人获得奖励时,邀请人获得提成
|
||||
* 邀请绑定成功 → 给邀请人发债券。
|
||||
* 规则见 邀请好友赢债券/规则.md:每次按单次上限结算,
|
||||
* 当日累计不超单日上限。
|
||||
*
|
||||
* @param inviter 邀请人(收款方)
|
||||
* @param invitee 被邀请人(绑定者)
|
||||
* @return >0 实发债券数;0 = 今日已达上限或规则为0;
|
||||
* -1 = 异常/冻结未发
|
||||
*/
|
||||
public void payCommission(
|
||||
String invitee,
|
||||
double originalAmount) {
|
||||
// 查找邀请人
|
||||
String inviter = getInviter(invitee);
|
||||
if (inviter == null
|
||||
|| inviter.isEmpty()) return;
|
||||
|
||||
double rate = getCommissionRate(
|
||||
inviter);
|
||||
if (rate <= 0) return;
|
||||
|
||||
double commission =
|
||||
originalAmount * rate;
|
||||
if (commission <= 0) return;
|
||||
|
||||
// Economy已移除,佣金提成功能不可用
|
||||
// 邀请人可通过债券系统获取奖励
|
||||
|
||||
// 通知邀请人
|
||||
Player inviterP =
|
||||
main.getServer()
|
||||
.getPlayer(inviter);
|
||||
if (inviterP != null
|
||||
&& inviterP.isOnline()) {
|
||||
inviterP.sendMessage(
|
||||
"§a§l[邀请提成] §f"
|
||||
+ invitee + " 完成任务,"
|
||||
+ "你获得 §e"
|
||||
+ String.format("%.2f",
|
||||
commission)
|
||||
+ " §f游戏币提成 ("
|
||||
+ (int)(rate * 100) + "%)");
|
||||
public int payInviteBond(String inviter, String invitee) {
|
||||
if (inviter == null || inviter.isEmpty()) return -1;
|
||||
if (perInviteReward <= 0) return 0;
|
||||
int used = inviteBondToday(inviter);
|
||||
int room = dailyInviteCap - used;
|
||||
if (room <= 0) {
|
||||
logger.info("[Commission] " + inviter
|
||||
+ " 今日邀请债券已达上限(" + dailyInviteCap + ")");
|
||||
return 0;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"[Commission] 提成: "
|
||||
+ inviter + " ← "
|
||||
+ invitee + " | "
|
||||
+ String.format("%.2f",
|
||||
originalAmount)
|
||||
+ " × "
|
||||
+ (int)(rate * 100) + "% = "
|
||||
+ String.format("%.2f",
|
||||
commission));
|
||||
int give = Math.min(perInviteReward, room);
|
||||
int after = main.getBonds().addBonds(inviter, give,
|
||||
"invite_reward", invitee, "system",
|
||||
"邀请好友绑定奖励(" + invitee + ")");
|
||||
if (after < 0) return -1; // 冻结期未入账
|
||||
logger.info("[Commission] 邀请债券: " + inviter + " ← "
|
||||
+ invitee + " | +" + give + "(今日累计 "
|
||||
+ (used + give) + "/" + dailyInviteCap + ")");
|
||||
return give;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取邀请人
|
||||
*/
|
||||
private String getInviter(String player) {
|
||||
/** 今日 0 点起该玩家已发放的邀请债券总数 */
|
||||
private int inviteBondToday(String inviter) {
|
||||
long dayStart = LocalDate.now()
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant().toEpochMilli();
|
||||
try {
|
||||
Object val =
|
||||
main.getDb().getField(
|
||||
player,
|
||||
"invited_by");
|
||||
if (val != null
|
||||
&& !val.toString()
|
||||
.isEmpty()) {
|
||||
return val.toString();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
PreparedStatement ps = main.getDb()
|
||||
.getConnection().prepareStatement(
|
||||
"SELECT COALESCE(SUM(amount),0) "
|
||||
+ "FROM bond_transaction "
|
||||
+ "WHERE player_name=? "
|
||||
+ "AND type='invite_reward' "
|
||||
+ "AND time>=?");
|
||||
ps.setString(1, inviter);
|
||||
ps.setLong(2, dayStart);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
int v = rs.next() ? rs.getInt(1) : 0;
|
||||
rs.close();
|
||||
ps.close();
|
||||
return v;
|
||||
} catch (Exception e) {
|
||||
logger.warning("[Commission] 查询当日邀请债券失败: "
|
||||
+ e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断金额是否包含经济内容
|
||||
*/
|
||||
public boolean hasEconomyContent(
|
||||
String text) {
|
||||
public boolean hasEconomyContent(String text) {
|
||||
String t = text.toLowerCase();
|
||||
return t.contains("游戏币")
|
||||
|| t.contains("金币")
|
||||
|
||||
@@ -1670,6 +1670,34 @@ public class GUIManager implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
// ★ 第八批(2026-10-12):图标槽接入 SN 系统(此前完全没拦)——
|
||||
// ① 背包已有自定义菜单物品 → 拒绝重复载体;
|
||||
// ② 读旧雪球 SN 作换载体复用(登记不动、不触发冷静期);
|
||||
// ③ 无旧 SN 则先过申领管控(冷静期/1人1个),拦在动背包之前。
|
||||
SnManager snm = plugin.getSnManager();
|
||||
for (ItemStack pre : p.getInventory().getContents()) {
|
||||
if (isCustomMenuTrigger(pre)) {
|
||||
p.sendMessage("§c[菜单] 你已拥有菜单物品,请先清除当前图标再更换");
|
||||
return;
|
||||
}
|
||||
}
|
||||
String reuseSn = null;
|
||||
if (snm != null) {
|
||||
for (ItemStack pre : p.getInventory().getContents()) {
|
||||
if (pre != null
|
||||
&& pre.getType() == Material.SNOWBALL
|
||||
&& isMenuTriggerItem(pre)) {
|
||||
reuseSn = snm.readSn(pre);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reuseSn == null && snm != null
|
||||
&& snm.isBlocked(p, SnManager.TYPE_MENU)) {
|
||||
p.sendMessage("§c[SN] 菜单物品管控中(冷静期或已有有效登记),暂不能更换图标");
|
||||
return;
|
||||
}
|
||||
|
||||
int amount = item.getAmount();
|
||||
removeSnowballFromInventory(p); //没收雪球菜单
|
||||
// 1. 从来源移除
|
||||
@@ -1740,6 +1768,23 @@ public class GUIManager implements Listener {
|
||||
menu.setItemMeta(mm);
|
||||
}
|
||||
|
||||
// 4.5 ★ 第八批:自定义菜单物品打 SN 副本——
|
||||
// 有旧雪球 SN 则转移复用;没有则新申领(入口已过管控,
|
||||
// 此处失败按 DB 异常放行不打标,与 Main.snTag 同款容错)
|
||||
String menuSn = reuseSn;
|
||||
if (menuSn == null && snm != null) {
|
||||
menuSn = snm.applySnQuiet(p,
|
||||
SnManager.TYPE_MENU, "自定义菜单图标");
|
||||
}
|
||||
if (menuSn != null && snm != null) {
|
||||
snm.writeSn(menu, menuSn, SnManager.TYPE_MENU);
|
||||
if (reuseSn != null) {
|
||||
snm.logSn(menuSn, SnManager.TYPE_MENU, "xfer",
|
||||
p.getName(),
|
||||
"菜单载体转换:雪球->自定义图标");
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 发放菜单物品
|
||||
int slot = p.getInventory()
|
||||
.firstEmpty();
|
||||
@@ -1766,6 +1811,18 @@ public class GUIManager implements Listener {
|
||||
String dbIcon = plugin.getDb()
|
||||
.getMenuIcon(p.getName());
|
||||
|
||||
// ★ 第八批:没收前先读自定义物品的 SN,发雪球时转移复用
|
||||
// (登记不动,避免旧登记把新雪球卡在「1人1个」/冷静期)
|
||||
String reuseSn = null;
|
||||
if (plugin.getSnManager() != null) {
|
||||
for (ItemStack it : p.getInventory().getContents()) {
|
||||
if (isCustomMenuTrigger(it)) {
|
||||
reuseSn = plugin.getSnManager().readSn(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 没收自定义菜单物品
|
||||
removeCustomMenuFromInventory(p);
|
||||
|
||||
@@ -1776,8 +1833,8 @@ public class GUIManager implements Listener {
|
||||
// 3. 强制清空slot 31
|
||||
topInv.setItem(31, null);
|
||||
|
||||
// 4. 发雪球
|
||||
plugin.giveMenuSnowball(p);
|
||||
// 4. 发雪球(复用旧 SN 转移登记)
|
||||
plugin.giveMenuSnowball(p, reuseSn);
|
||||
|
||||
// 5. 还原件
|
||||
if (dbIcon != null && !dbIcon.isEmpty()) {
|
||||
|
||||
@@ -47,29 +47,11 @@ public class InviteManager {
|
||||
invitedBy, "monthly_invite_count",
|
||||
count + 1);
|
||||
|
||||
double rate = 0.10;
|
||||
if (count + 1 > 15) {
|
||||
rate = 0.60;
|
||||
} else if (count + 1 > 10) {
|
||||
rate = 0.35;
|
||||
} else if (count + 1 > 5) {
|
||||
rate = 0.15;
|
||||
}
|
||||
|
||||
int bonus = (int) (1 * rate);
|
||||
if (bonus > 0) {
|
||||
plugin.getDb().addPoints(invitedBy, bonus);
|
||||
Player inviterPlayer = Bukkit
|
||||
.getPlayerExact(invitedBy);
|
||||
if (inviterPlayer != null
|
||||
&& inviterPlayer.isOnline()) {
|
||||
inviterPlayer.sendMessage(
|
||||
plugin.getConfig2()
|
||||
.msg("invite_referral_bonus")
|
||||
.replace("{points}",
|
||||
String.valueOf(bonus)));
|
||||
}
|
||||
}
|
||||
// ★ 第八批(2026-10-12):旧「签到给邀请人积分提成」废弃——
|
||||
// 原 bonus=(int)(1*rate) 恒为 0(rate 最大 0.60,截断后为 0),
|
||||
// 奖励从未实际发放,即用户反馈「邀请好友无效」的根因;
|
||||
// 邀请奖励统一在绑定邀请码时按 邀请好友赢债券/规则.md 发债券
|
||||
// (CommissionManager.payInviteBond)。此处只保留月度邀请计数。
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAllInviteData() {
|
||||
|
||||
@@ -3205,6 +3205,14 @@ public class Main extends JavaPlugin
|
||||
"sdf1_menu";
|
||||
|
||||
public void giveMenuSnowball(Player p) {
|
||||
giveMenuSnowball(p, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reuseSn 复用的旧 SN(自定义图标≉雪球互换时转移登记,
|
||||
* 不重新申领、不触发冷静期);null = 正常申领
|
||||
*/
|
||||
public void giveMenuSnowball(Player p, String reuseSn) {
|
||||
String name = p.getName();
|
||||
try {
|
||||
// getLogger().info("[菜单] ★调用 giveMenuSnowball: " + name);
|
||||
@@ -3231,7 +3239,8 @@ public class Main extends JavaPlugin
|
||||
}
|
||||
|
||||
// ★ SN 管控:1人同种类仅限1个,旧SN未注销/冷静期未过不再发放
|
||||
if (snManager != null
|
||||
// ★ 复用旧 SN 换载体时不拦——登记本来就是本人的
|
||||
if (reuseSn == null && snManager != null
|
||||
&& snManager.isBlocked(p, SnManager.TYPE_MENU)) {
|
||||
return;
|
||||
}
|
||||
@@ -3299,7 +3308,14 @@ public class Main extends JavaPlugin
|
||||
im.setLore(lore);
|
||||
snow.setItemMeta(im);
|
||||
}
|
||||
if (!snTag(p, snow, SnManager.TYPE_MENU, "雪球菜单")) {
|
||||
if (reuseSn != null && snManager != null) {
|
||||
// ★ 图标≉雪球互换:SN 转移到新载体,登记不动(不触发冷静期)
|
||||
snManager.writeSn(snow, reuseSn,
|
||||
SnManager.TYPE_MENU);
|
||||
snManager.logSn(reuseSn, SnManager.TYPE_MENU,
|
||||
"xfer", p.getName(),
|
||||
"菜单载体转换:自定义图标->雪球");
|
||||
} else if (!snTag(p, snow, SnManager.TYPE_MENU, "雪球菜单")) {
|
||||
return;
|
||||
}
|
||||
int slot = p.getInventory()
|
||||
|
||||
Reference in New Issue
Block a user