package Sdf1_login; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.scheduler.BukkitRunnable; import java.io.*; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.sql.*; import java.sql.Date; import java.text.SimpleDateFormat; import java.util.*; public class GarbageManager { private final Main plugin; private Connection db; private int cleanupInterval = 1800; private int maxRounds = 1; private boolean enabled = true; private int currentRound = 0; private final Map recyclePages = new HashMap<>(); public GarbageManager(Main plugin) { this.plugin = plugin; } public void init() { try { Class.forName("org.sqlite.JDBC"); File dbFile = new File( plugin.getDataFolder(), "garbage.db"); db = DriverManager.getConnection( "jdbc:sqlite:" + dbFile.getAbsolutePath()); Statement st = db.createStatement(); st.execute("PRAGMA journal_mode=WAL"); st.execute("CREATE TABLE IF NOT EXISTS " + "items (" + "id INTEGER PRIMARY KEY " + "AUTOINCREMENT," + "item_data TEXT NOT NULL," + "amount INTEGER DEFAULT 1," + "round INTEGER DEFAULT 0," + "create_time INTEGER NOT NULL" + ")"); st.execute("CREATE TABLE IF NOT EXISTS " + "config (" + "key TEXT PRIMARY KEY," + "value TEXT NOT NULL" + ")"); st.close(); } catch (Exception e) { plugin.getLogger().severe( "[Garbage] DB失败: " + e.getMessage()); } } public void loadConfig() { // 从 ConfigManager 读取 cleanupInterval = plugin.getConfig2() .garbageInterval; enabled = plugin.getConfig2() .garbageEnabled; maxRounds = plugin.getConfig2() .garbageMaxRounds; // 从数据库读取当前轮次 try { String val = getConfigValue("round"); if (val != null) currentRound = Integer.parseInt(val); } catch (Exception ignored) { } plugin.getLogger().info( "[Garbage] 间隔: " + cleanupInterval + "秒, " + "轮次保留: " + maxRounds + "轮, " + "启用: " + enabled); } private String getConfigValue(String key) { try { PreparedStatement ps = db.prepareStatement( "SELECT value FROM config " + "WHERE key=?"); ps.setString(1, key); ResultSet rs = ps.executeQuery(); String val = rs.next() ? rs.getString("value") : null; rs.close(); ps.close(); return val; } catch (Exception e) { return null; } } private void setConfigValue(String key, String value) { try { PreparedStatement ps = db.prepareStatement( "INSERT OR REPLACE INTO config " + "(key, value) " + "VALUES (?, ?)"); ps.setString(1, key); ps.setString(2, value); ps.executeUpdate(); ps.close(); } catch (Exception e) { e.printStackTrace(); } } public void startAutoCleanup() { if (!enabled) return; new BukkitRunnable() { int countdown = 0; @Override public void run() { // 在线人数≤0时暂停 if (Bukkit.getOnlinePlayers() .isEmpty()) { countdown = 0; return; } // 重新读取配置(支持热更) cleanupInterval = plugin.getConfig2() .garbageInterval; int remain = cleanupInterval - countdown; if (remain == 300) broadcast("§e§l[垃圾站] §f距离清理还有 §e5分钟"); else if (remain == 180) broadcast("§e§l[垃圾站] §f距离清理还有 §e3分钟"); else if (remain == 60) broadcast("§e§l[垃圾站] §f距离清理还有 §e1分钟"); else if (remain == 30) broadcast("§e§l[垃圾站] §f距离清理还有 §e30秒"); else if (remain == 10) broadcast("§e§l[垃圾站] §f距离清理还有 §e10秒"); else if (remain <= 5 && remain > 0) broadcast("§e§l[垃圾站] §c" + remain + "§f..."); else if (remain == 0) { doCleanup(); countdown = 0; return; } countdown++; } }.runTaskTimer(plugin, 20L, 20L); } private void doCleanup() { // 删除过期轮次的物品 int deleteRound = currentRound - maxRounds; if (deleteRound >= 0) { int deleted = deleteOldItems( deleteRound); if (deleted > 0) { broadcast("§7[垃圾站] §f永久清理了 §e" + deleted + " §f个过期物品"); } } // 递增轮次 currentRound++; setConfigValue("round", String.valueOf(currentRound)); // 收集当前掉落物 int count = collectItems(); broadcast("§a§l[垃圾站] §f清理完成," + "共清理 §e" + count + " §f个掉落物,存入回收站"); // 打印下次清理时间 long nextMs = System.currentTimeMillis() + (long) cleanupInterval * 1000L; String nextTime = new SimpleDateFormat("HH:mm:ss") .format(new Date(nextMs)); broadcast("§e§l[垃圾站] §f下次清理时间: §a" + nextTime); } public int collectItems() { int count = 0; for (org.bukkit.World w : Bukkit.getWorlds()) { Collection items = w.getEntitiesByClass(Item.class); for (Item item : items) { ItemStack stack = item.getItemStack(); if (stack == null || stack.getType() == Material.AIR) continue; saveItem(stack); item.remove(); count++; } } return count; } public void saveItem(ItemStack stack) { { try { String data = stackToBase64(stack); if (data.isEmpty()) return; PreparedStatement ps = db.prepareStatement( "INSERT INTO items " + "(item_data, amount, " + "round, create_time) " + "VALUES (?,?,?,?)"); ps.setString(1, data); ps.setInt(2, stack.getAmount()); ps.setInt(3, currentRound); ps.setLong(4, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); } catch (Exception e) { e.printStackTrace(); } }} private int deleteOldItems(int beforeRound) { try { PreparedStatement ps = db.prepareStatement( "DELETE FROM items " + "WHERE round <= ?"); ps.setInt(1, beforeRound); int rows = ps.executeUpdate(); ps.close(); return rows; } catch (Exception e) { return 0; } } // ========== 序列化 ========== private String stackToBase64(ItemStack stack) { try { Map map = stack.serialize(); String json = new Gson().toJson(map); return Base64.getEncoder() .encodeToString( json.getBytes( StandardCharsets.UTF_8)); } catch (Exception e) { return ""; } } private ItemStack base64ToStack(String base64) { try { byte[] bytes = Base64.getDecoder() .decode(base64); String json = new String(bytes, StandardCharsets.UTF_8); Type type = new TypeToken< Map>() {} .getType(); Map map = new Gson().fromJson(json, type); map = fixNumberTypes(map); return ItemStack.deserialize(map); } catch (Exception e) { return null; } } @SuppressWarnings("unchecked") private Map fixNumberTypes( Map map) { Map result = new LinkedHashMap<>(); for (Map.Entry e : map.entrySet()) { Object v = e.getValue(); if (v instanceof Double) { double d = (Double) v; if (d == (long) d) { result.put(e.getKey(), (long) d); } else { result.put(e.getKey(), d); } } else if (v instanceof Map) { result.put(e.getKey(), fixNumberTypes( (Map) v)); } else if (v instanceof List) { List list = new ArrayList<>(); for (Object item : (List) v) { if (item instanceof Map) { list.add(fixNumberTypes( (Map) item)); } else { list.add(item); } } result.put(e.getKey(), list); } else { result.put(e.getKey(), v); } } return result; } // ========== 回收站GUI ========== public void openRecycle(Player p) { openRecyclePage(p, 0); } public void openRecyclePage(Player p, int page) { Inventory g = Bukkit.createInventory( null, 54, "§6§l垃圾回收站"); // 获取全部存储物品 List> allItems = getStoredItems(999); int total = allItems.size(); int pageSize = 40; int totalPages = Math.max(1, (int) Math.ceil( (double) total / pageSize)); // 修正页码 if (page < 0) page = 0; if (page >= totalPages) page = totalPages - 1; recyclePages.put(p.getName(), page); // 填充物品(0-39 格 = 第1页,40-79 = 第2页...) int start = page * pageSize; int end = Math.min(start + pageSize, total); int slot = 0; for (int i = start; i < end; i++) { if (slot >= 40) break; Map item = allItems.get(i); int id = ((Number) item.get("id")) .intValue(); String data = (String) item .get("item_data"); int amount = ((Number) item .get("amount")).intValue(); ItemStack stack = base64ToStack(data); if (stack != null) { stack.setAmount(amount); ItemMeta im = stack.getItemMeta(); if (im != null) { List lore = new ArrayList<>(); lore.add("§7ID: #" + id); lore.add("§a点击取出"); im.setLore(lore); stack.setItemMeta(im); } g.setItem(slot, stack); } slot++; } // 45-48 格:额外投放区(空位) // 52-53 格:额外投放区(空位) // 49 格:翻页-上一页 if (page > 0) { g.setItem(49, mk(Material.ARROW, "§e上一页 §7(" + page + "/" + totalPages + ")")); } else { g.setItem(49, mk(Material.ARROW, "§e第一页 §7(" + "1/" + totalPages + ")")); } // 50 格:统计信息 g.setItem(50, mk(Material.CHEST, "§e回收站统计", "§7总存量: §f" + total + " 个", "§7当前页: §f" + (page + 1) + "/" + totalPages)); // 51 格:翻页-下一页 boolean canNext = total >= pageSize && page < totalPages - 1; if (canNext) { g.setItem(51, mk(Material.ARROW, "§e下一页 §7(" + (page + 2) + "/" + totalPages + ")")); } else { g.setItem(51, mk(Material.ARROW, "§7已是最后一页")); } p.openInventory(g); } public int getRecyclePage(String name) { return recyclePages .getOrDefault(name, 0); } public boolean handleClick(Player p, String title, int slot) { if (!title.equals("§6§l垃圾回收站")) return false; // 49:上一页 if (slot == 49) { int page = recyclePages .getOrDefault(p.getName(), 0); if (page > 0) openRecyclePage(p, page - 1); return true; } // 51:下一页 if (slot == 51) { int page = recyclePages .getOrDefault(p.getName(), 0); openRecyclePage(p, page + 1); return true; } // 50/52/53:不处理 if (slot >= 49 && slot <= 53) return true; return true; } private List> getStoredItems(int limit) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT id, item_data, " + "amount " + "FROM items " + "ORDER BY " + "create_time " + "DESC LIMIT ?"); ps.setInt(1, limit); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); row.put("id", rs.getInt("id")); row.put("item_data", rs.getString("item_data")); row.put("amount", rs.getInt("amount")); list.add(row); } rs.close(); ps.close(); } catch (Exception e) { e.printStackTrace(); } return list; } public boolean removeItem(int id) { try { PreparedStatement ps = db.prepareStatement( "DELETE FROM items " + "WHERE id=?"); ps.setInt(1, id); int rows = ps.executeUpdate(); ps.close(); return rows > 0; } catch (Exception e) { return false; } } public int getItemCount() { try { Statement st = db.createStatement(); ResultSet rs = st.executeQuery( "SELECT COUNT(*) FROM items"); int count = 0; if (rs.next()) count = rs.getInt(1); rs.close(); st.close(); return count; } catch (Exception e) { return 0; } } private void broadcast(String msg) { // 同步打印到控制台 plugin.getLogger().info( msg.replaceAll("§[0-9a-fk-orA-FK-OR]", "")); // 同步打印到聊天栏 Bukkit.broadcastMessage(msg); } private ItemStack mk(Material mat, String name, String... lore) { ItemStack it = new ItemStack(mat); ItemMeta im = it.getItemMeta(); if (im != null) { im.setDisplayName(name); if (lore.length > 0) { List list = new ArrayList<>(); for (String l : lore) if (!l.isEmpty()) list.add(l); if (!list.isEmpty()) im.setLore(list); } it.setItemMeta(im); } return it; } public void close() { try { if (db != null && !db.isClosed()) db.close(); } catch (Exception ignored) { } } }