UIPosts

UIPosts.java v2.2
多功能美化发帖/查询/删除,优化GUI/书本和格式的社区插件
作者: ScriptIrc Engine

命令列表

  • uiposts主命令:支持post/del/info/list/chest/book/help

权限列表

  • uiposts.post允许发帖
  • uiposts.reply允许留言
  • uiposts.del允许删除帖子
  • uiposts.admin管理及高级操作
package com.uiposts;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.InputStreamReader;
import java.io.BufferedReader;

/**
 * @pluginName UIPosts
 * @author ScriptIrc Engine
 * @version 2.2
 * @description 多功能美化发帖/查询/删除,优化GUI/书本和格式的社区插件
 * [command]uiposts|主命令:支持post/del/info/list/chest/book/help[/command]
 * [Permission]uiposts.post|允许发帖[/Permission]
 * [Permission]uiposts.reply|允许留言[/Permission]
 * [Permission]uiposts.del|允许删除帖子[/Permission]
 * [Permission]uiposts.admin|管理及高级操作[/Permission]
 */
public class UIPosts extends JavaPlugin implements Listener, TabCompleter {
    private static final String DB_FILE = "uiposts.db";
    private static final String TIME_API = "http://worldtimeapi.org/api/timezone/Asia/Shanghai";
    private static final int POST_LIMIT = 100, REPLY_LIMIT = 50;
    private static final ChatColor PRIMARY = ChatColor.AQUA, SECONDARY = ChatColor.YELLOW, SUCCESS = ChatColor.GREEN, ERROR = ChatColor.RED, INFO = ChatColor.GRAY;
    private Connection connection;
    private Map<String, Object> guiConfig;
    private final Map<UUID, Boolean> awaitingPostInput = new HashMap<>();
    private final Map<UUID, Integer> awaitingReply = new HashMap<>();
    private final Map<UUID, Long> lastPostTime = new HashMap<>();

    @Override
    public void onEnable() {
        Bukkit.getPluginManager().registerEvents(this, this);
        getCommand("uiposts").setTabCompleter(this);
        initDatabase();
        loadConfig();
        getLogger().info("UIPosts v2.2 美化社区系统启动!");
    }
    @Override
    public void onDisable() {
        try { if (connection != null && !connection.isClosed()) connection.close(); } catch (Exception ignored) {}
        getLogger().info("UIPosts 插件关闭.");
    }

    // ======= 配置与数据库相关 =======
    private void loadConfig() {
        guiConfig = new HashMap<>();
        Map<String, Object> chestGUI = new HashMap<>();
        // 更大的GUI(6行54格)美化布局
        chestGUI.put("title", PRIMARY+"§l帖子广场 §f[Chest]{sep}{page}/{pages}");
        chestGUI.put("size", 54);
        // 发帖槽位大幅提升,可容纳更多帖子展示
        chestGUI.put("post_slot", Arrays.asList(10, 11, 12, 13, 14, 19, 20, 21, 22, 23, 28, 29, 30, 31, 32, 37, 38, 39, 40, 41));
        chestGUI.put("next_slot", 53);
        chestGUI.put("prev_slot", 45);
        chestGUI.put("home_slot", 49);
        chestGUI.put("stat_slot", 48);
        chestGUI.put("post_btn", 4);
        chestGUI.put("theme", ChatColor.AQUA.toString());
        chestGUI.put("post_button", SUCCESS+"§l➕ 我要发帖");
        chestGUI.put("jump_book", SECONDARY+"§l📖 切换到书本模式");
        chestGUI.put("refresh", PRIMARY+"§l🔄 刷新/回首页");
        guiConfig.put("chest", chestGUI);
        Map<String, Object> bookGUI = new HashMap<>();
        bookGUI.put("title", PRIMARY+"§l帖子广场 §f[书本]{sep}{page}页 共{pages}页");
        bookGUI.put("lines_per_page", 3); // 一页3条,便于阅读
        guiConfig.put("book", bookGUI);
    }
    private void initDatabase() {
        try {
            File dbFile = new File(getDataFolder(), DB_FILE);
            if (!getDataFolder().exists()) getDataFolder().mkdirs();
            boolean init = false;
            if (!dbFile.exists()) {
                dbFile.createNewFile();
                init = true;
            }
            connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getPath());
            if (init) {
                Statement st = connection.createStatement();
                st.executeUpdate("CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, player VARCHAR, content TEXT, time VARCHAR)");
                st.executeUpdate("CREATE TABLE IF NOT EXISTS replies (id INTEGER PRIMARY KEY AUTOINCREMENT, post_id INTEGER, player VARCHAR, content TEXT, time VARCHAR)");
                st.close();
            }
        } catch (Exception e) { e.printStackTrace(); }
    }

    // ============ 命令与补全 =============
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if (args.length == 0) {
            if (sender instanceof Player) {
                openChestGUI((Player)sender, 1);
            } else {
                sender.sendMessage(INFO+"请加参数,或用/uiposts help 查看帮助。");
            }
            return true;
        }
        String sub = args[0].toLowerCase();
        switch (sub) {
            case "help": case "?":
                sendHelp(sender);
                break;
            case "chest":
                if (sender instanceof Player) openChestGUI((Player)sender, 1);
                else sender.sendMessage(ERROR+"仅玩家有效");
                break;
            case "book":
                if (sender instanceof Player) openBookGUI((Player)sender, 1);
                else sender.sendMessage(ERROR+"仅玩家有效");
                break;
            case "post":
                if (!(sender instanceof Player)) { sender.sendMessage(ERROR+"仅玩家可发帖"); return true; }
                Player p = (Player)sender;
                if (!p.hasPermission("uiposts.post")) { p.sendMessage(ERROR+"无权限 uiposts.post"); return true;}
                String msg = joinArgs(args, 1);
                if (msg.length() == 0) { p.sendMessage(INFO+"请输入帖子内容: /uiposts post <内容>"); return true;}
                if (msg.length() > POST_LIMIT) { p.sendMessage(ERROR+"已截断内容为"+POST_LIMIT+"字"); msg = msg.substring(0, POST_LIMIT); }
                long now = System.currentTimeMillis();
                if(lastPostTime.containsKey(p.getUniqueId()) && (now-lastPostTime.get(p.getUniqueId())<5000)) {
                    p.sendMessage(ERROR+"冷却中,5秒后再试!");
                    return true;
                }
                savePost(p, msg);
                lastPostTime.put(p.getUniqueId(), now);
                p.sendMessage(SUCCESS+"发帖成功!");
                break;
            case "del":
                if (args.length < 2) { sender.sendMessage(INFO+"用法: /uiposts del <帖子ID>"); return true;}
                int delId = safeInt(args[1], -1);
                if (delId < 1) { sender.sendMessage(ERROR+"帖子ID无效"); return true;}
                if (!sender.hasPermission("uiposts.del") && !(sender.hasPermission("uiposts.admin"))) {
                    sender.sendMessage(ERROR+"无权删除帖子(需要uiposts.del)");
                    return true;
                }
                Post delp = getPostById(delId);
                if (delp == null) { sender.sendMessage(ERROR+"未找到帖子#"+delId); return true; }
                boolean isOwner = (sender instanceof Player) && delp.player.equalsIgnoreCase(sender.getName());
                if (!isOwner && !sender.hasPermission("uiposts.admin")) {
                    sender.sendMessage(ERROR+"只能删除自己帖子,或需要uiposts.admin权限");
                    return true;
                }
                deletePost(delId);
                sender.sendMessage(SUCCESS+"已删除帖子 #"+delId);
                break;
            case "info":
                if (args.length < 2) { sender.sendMessage(INFO+"/uiposts info <帖子ID>"); return true;}
                int infoId = safeInt(args[1], -1);
                if (infoId < 1) { sender.sendMessage(ERROR+"帖子ID无效"); return true;}
                Post post = getPostById(infoId);
                if (post == null) { sender.sendMessage(ERROR+"未找到帖子 #"+infoId); return true;}
                sender.sendMessage(primaryTitle("帖子 #"+infoId));
                sender.sendMessage(SECONDARY+""+"作者:"+ChatColor.WHITE+post.player+"  时间:"+post.time);
                sender.sendMessage(INFO+"内容:"+ChatColor.RESET+post.content);
                List<Reply> replies = getReplies(infoId);
                if (replies.isEmpty()) sender.sendMessage(INFO+"暂无留言");
                else {
                    sender.sendMessage(SECONDARY+"留言(共"+replies.size()+"条):");
                    int idx=1;
                    for (Reply r : replies) {
                        sender.sendMessage(INFO+" "+idx+". "+r.player+": "+ChatColor.WHITE+r.content);
                        idx++;
                    }
                }
                break;
            case "list":
                int page = args.length>=2?safeInt(args[1], 1):1;
                if (page<1) page=1;
                int perPage = 10, total = countPosts(), totalPages = Math.max(1, (total+perPage-1)/perPage);
                List<Post> posts = getPostsByPage(page, perPage);
                sender.sendMessage(primaryTitle("帖子列表 第"+page+"/"+totalPages+"页(共"+total+"条)"));
                if (posts.isEmpty()) sender.sendMessage(INFO+"暂无帖子!");
                else
                    for (Post pp:posts) sender.sendMessage(
                            ChatColor.GREEN+"#"+pp.id+ChatColor.WHITE+" ["+pp.player+"] "+ChatColor.GRAY+shorten(pp.content, 30)
                                    +ChatColor.DARK_GRAY+"("+pp.time+")"
                    );
                sender.sendMessage(INFO+"用 /uiposts info <id> 查询详情, /uiposts del <id> 删除, /uiposts post <内容> 发新帖");
                break;
            default:
                sendHelp(sender);
        }
        return true;
    }

    /**
     * 帮助信息输出(修复遗漏方法)
     * @param sender 接收命令的实体
     */
    private void sendHelp(CommandSender sender) {
        sender.sendMessage(PRIMARY+""+ChatColor.BOLD+"[UIPosts 帮助]");
        sender.sendMessage(SECONDARY+"/uiposts chest"+INFO+" - 打开帖子广场 (格子美化版)");
        sender.sendMessage(SECONDARY+"/uiposts book"+INFO+" - 打开帖子广场 (书本模式)");
        sender.sendMessage(SECONDARY+"/uiposts post <内容>"+INFO+" - 新发布一条帖子");
        sender.sendMessage(SECONDARY+"/uiposts del <帖子ID>"+INFO+" - 删除一条帖子(需要权限)");
        sender.sendMessage(SECONDARY+"/uiposts info <帖子ID>"+INFO+" - 查看帖子与评论");
        sender.sendMessage(SECONDARY+"/uiposts list [页码]"+INFO+" - 列出帖子列表(分页)");
        sender.sendMessage(SECONDARY+"/uiposts help"+INFO+" - 查看本帮助");
        sender.sendMessage(ChatColor.GRAY+"你也可以点击GUI、书本内留言互动哦~");
        sender.sendMessage(ChatColor.GRAY+"权限:"+ChatColor.YELLOW+"uiposts.post uiposts.reply uiposts.del uiposts.admin");
    }

    @Override
    public List<String> onTabComplete(CommandSender s, Command c, String l, String[] a) {
        List<String> res = new ArrayList<>();
        if (a.length == 1)
            res = Arrays.asList("chest","book","help","post","del","info","list");
        else if (a.length == 2 && Arrays.asList("del", "info").contains(a[0].toLowerCase())) {
            List<Post> posts = getPostsByPage(1, 50);
            for(Post p:posts)
                res.add(String.valueOf(p.id));
        }
        return res;
    }
    private int safeInt(String x, int def) { try{return Integer.parseInt(x);}catch(Exception e){return def;} }
    private String joinArgs(String[] arr, int idx) {
        if (idx>=arr.length) return "";
        StringBuilder sb=new StringBuilder();
        for(int i=idx;i<arr.length;i++) {
            if(i>idx) sb.append(" ");
            sb.append(arr[i]);
        }
        return sb.toString().trim();
    }
    private String shorten(String str, int n) { return str.length()>n?str.substring(0,n)+"...":str; }
    private String primaryTitle(String s){return PRIMARY+""+ChatColor.BOLD+"§l"+s;}
    private String parse(String raw, Player p, int page, int pages) {
        return raw.replace("{player}", p.getName()).replace("{page}", String.valueOf(page)).replace("{pages}", String.valueOf(pages));
    }
    // ========= 美化后的GUI =========
    private void openChestGUI(Player p, int page) {
        Map<String, Object> cfg = (Map<String, Object>) guiConfig.get("chest");
        int size = (int) cfg.get("size");
        String title = ((String) cfg.get("title")).replace("{sep}", ChatColor.GRAY+" | ");
        List<Integer> postSlots = (List<Integer>) cfg.get("post_slot");
        int totalPages = getTotalPages(postSlots.size());
        page = Math.max(1, Math.min(page, totalPages));
        Inventory inv = Bukkit.createInventory(null, size, parse(title, p, page, totalPages));
        List<Post> posts = getPostsByPage(page, postSlots.size());
        // 背景填色
        ItemStack bg = new ItemStack(Material.BLUE_STAINED_GLASS_PANE);
        ItemMeta bgm = bg.getItemMeta(); bgm.setDisplayName(" "); bg.setItemMeta(bgm);
        for(int i=0;i<size;i++) inv.setItem(i, bg);
        // 帖子展示(info美化)
        int i = 0;
        for (; i < posts.size() && i < postSlots.size(); i++) {
            Post post = posts.get(i);
            ItemStack item = new ItemStack(Material.WRITABLE_BOOK);
            ItemMeta im = item.getItemMeta();
            List<String> lore = new ArrayList<>();
            im.setDisplayName(PRIMARY+"§l#"+post.id+" 【"+post.player+"】");
            // 修正:ChatColor类型拼接都加 "" 转为字符串
            lore.add(SECONDARY+""+ChatColor.ITALIC+wrapLines(post.content,24));
            int replyCount = getReplies(post.id).size();
            lore.add((replyCount>0? ChatColor.LIGHT_PURPLE+""+"留言 "+replyCount+"条": ChatColor.DARK_GRAY+""+"暂无留言"));
            lore.add(SECONDARY+""+"时间: "+ChatColor.WHITE+post.time);
            lore.add(ChatColor.GRAY+""+"点击查看/留言");
            im.setLore(lore);
            item.setItemMeta(im);
            inv.setItem(postSlots.get(i), item);
        }
        // 发帖、切换、刷新按钮等美化
        ItemStack postBtn = new ItemStack(Material.ANVIL);
        ItemMeta pm = postBtn.getItemMeta();
        pm.setDisplayName((String) cfg.get("post_button"));
        pm.setLore(Arrays.asList(
                ChatColor.GRAY+""+"你可以每隔5秒发一次帖",
                ChatColor.YELLOW+""+"长度上限 "+POST_LIMIT+" 字节"));
        postBtn.setItemMeta(pm);
        inv.setItem( (int)cfg.get("post_btn"), postBtn);

        ItemStack jumpBtn = new ItemStack(Material.BOOK);
        ItemMeta jm = jumpBtn.getItemMeta();
        jm.setDisplayName((String) cfg.get("jump_book"));
        jm.setLore(Arrays.asList(ChatColor.GRAY+""+"以书本分布分页浏览"));
        jumpBtn.setItemMeta(jm);
        inv.setItem((int) cfg.get("home_slot"), jumpBtn);

        ItemStack refreshBtn = new ItemStack(Material.SUNFLOWER);
        ItemMeta ref = refreshBtn.getItemMeta();
        ref.setDisplayName(cfg.get("refresh").toString());
        ref.setLore(Arrays.asList(ChatColor.GRAY+""+"点击返回首页,刷新帖子"));
        refreshBtn.setItemMeta(ref);
        inv.setItem(size/2, refreshBtn);

        // 统计按钮
        ItemStack statBtn = new ItemStack(Material.PAPER);
        ItemMeta stat = statBtn.getItemMeta();
        int mycnt=countPlayerPosts(p.getName());
        int totalcnt=countPosts();
        stat.setDisplayName(ChatColor.BOLD+""+"帖子统计信息");
        stat.setLore(Arrays.asList(ChatColor.GRAY+""+"总帖数: "+ChatColor.GREEN+""+totalcnt,ChatColor.GRAY+""+"你发帖: "+ChatColor.YELLOW+""+mycnt));
        statBtn.setItemMeta(stat);
        inv.setItem((int)cfg.get("stat_slot"),statBtn);

        // 下一页
        ItemStack nextBtn = new ItemStack(Material.ARROW);
        ItemMeta nm = nextBtn.getItemMeta();
        nm.setDisplayName(ChatColor.YELLOW+""+"§l下一页 »");
        nm.setLore(Arrays.asList(ChatColor.GRAY+""+"第 "+(page+1)+" 页"+(page>=totalPages?ChatColor.DARK_GRAY+""+"(无更多)":"")));
        nextBtn.setItemMeta(nm);
        if (page<totalPages) inv.setItem((int) cfg.get("next_slot"), nextBtn);

        // 上一页
        ItemStack prevBtn = new ItemStack(Material.ARROW);
        ItemMeta pmv = prevBtn.getItemMeta();
        pmv.setDisplayName(ChatColor.YELLOW+""+"« 上一页");
        pmv.setLore(Arrays.asList(ChatColor.GRAY+""+"第 "+(page-1)+" 页"+(page<=1?ChatColor.DARK_GRAY+""+"(最前)":"")));
        prevBtn.setItemMeta(pmv);
        if (page>1) inv.setItem((int) cfg.get("prev_slot"), prevBtn);

        p.openInventory(inv);
        p.setMetadata("uiposts_gui_page", new FixedMetadataValue(this, page));
        p.setMetadata("uiposts_gui_type", new FixedMetadataValue(this, "chest"));
    }
    // ====== 书本GUI优化 ======
    private void openBookGUI(Player p, int page) {
        Map<String, Object> cfg = (Map<String, Object>) guiConfig.get("book");
        int lines = (int) cfg.get("lines_per_page");
        List<Post> posts = getPostsByPage(page, lines);
        int totalPages = getTotalPages(lines);
        String title = ((String) cfg.get("title")).replace("{sep}", ChatColor.GRAY+" | ");
        ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) book.getItemMeta();
        List<String> bPages = new ArrayList<>();
        int cnt=0;
        for (Post post : posts) {
            StringBuilder sb = new StringBuilder();
            sb.append(PRIMARY+"§l帖子 #"+post.id+"\n");
            sb.append(ChatColor.YELLOW+""+"作者:").append(ChatColor.WHITE+post.player+" ")
                    .append(ChatColor.YELLOW+""+"时间:").append(ChatColor.WHITE+post.time+"\n");
            sb.append(ChatColor.GRAY+""+wrapLines(post.content,30)).append("\n---\n");
            List<Reply> replies = getReplies(post.id);
            if (replies.isEmpty()) sb.append(ChatColor.DARK_GRAY+""+"暂无留言\n");
            else {
                int idx=1;
                for (Reply r : replies) {
                    sb.append(ChatColor.LIGHT_PURPLE+""+"["+idx+"] ")
                            .append(ChatColor.GRAY + r.player +": ")
                            .append(ChatColor.WHITE + wrapLines(r.content,20)).append("\n"); idx++;
                }
            }
            bPages.add(sb.toString());
            cnt++;
        }
        if (bPages.isEmpty()) bPages.add(ChatColor.GRAY+"暂无帖子, "+SUCCESS+"去发第一条吧!");
        // 页脚提示
        String foot = ChatColor.DARK_GRAY+"- 第"+page+"/"+totalPages+"页 - /uiposts chest返回";
        List<String> realPages = new ArrayList<>();
        for(String b:bPages) realPages.add(b+"\n"+foot);
        meta.setAuthor("UIPosts");
        meta.setTitle(parse(title,p,page,totalPages));
        meta.setPages(realPages);
        book.setItemMeta(meta);
        int oldSlot = p.getInventory().getHeldItemSlot();
        p.getInventory().setItem(oldSlot, book);
        new BukkitRunnable(){public void run(){p.openBook(book);}}.runTaskLater(this,2L);
        p.setMetadata("uiposts_gui_page", new FixedMetadataValue(this, page));
        p.setMetadata("uiposts_gui_type", new FixedMetadataValue(this, "book"));
    }
    private void showBookForPost(Player p, Post post) {
        ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) book.getItemMeta();
        StringBuilder sb = new StringBuilder();
        sb.append(PRIMARY+"§l帖子 #"+post.id+"\n");
        sb.append(ChatColor.YELLOW+""+"作者:").append(ChatColor.WHITE+post.player+"  ").append(ChatColor.YELLOW+""+"时间:").append(ChatColor.WHITE+post.time+"\n");
        sb.append(ChatColor.GRAY+""+wrapLines(post.content,30)).append("\n---\n");
        List<Reply> replies = getReplies(post.id);
        if (replies.isEmpty()) sb.append(ChatColor.DARK_GRAY+""+"暂无留言\n");
        else {
            int idx=1;
            for (Reply r : replies) sb.append(ChatColor.LIGHT_PURPLE+""+"["+idx+"] ")
                    .append(ChatColor.GRAY+""+r.player+": "+ChatColor.WHITE+wrapLines(r.content,20)+"\n");
        }
        sb.append("\n").append(SECONDARY+""+"输入留言内容(聊天发送),'取消'退出");
        meta.setAuthor("UIPosts");
        meta.setTitle("回复帖子");
        meta.setPages(Collections.singletonList(sb.toString()));
        book.setItemMeta(meta);
        p.getInventory().setItem(p.getInventory().getHeldItemSlot(), book);
        new BukkitRunnable(){public void run(){p.openBook(book);}}.runTaskLater(this,2L);
        askForReplyInput(p, post.id);
    }

    @EventHandler
    public void onInvClick(InventoryClickEvent e) {
        Player p = (Player) e.getWhoClicked();
        if (!p.hasMetadata("uiposts_gui_type") || !p.hasMetadata("uiposts_gui_page")) return;
        String title = e.getView().getTitle();
        if (!title.contains("帖子广场")) return;
        e.setCancelled(true);
        int page = p.getMetadata("uiposts_gui_page").get(0).asInt();
        String type = p.getMetadata("uiposts_gui_type").get(0).asString();
        ItemStack current = e.getCurrentItem();
        if (current == null || !current.hasItemMeta()) return;
        String name = ChatColor.stripColor(current.getItemMeta().getDisplayName());
        if (name.contains("发帖")) {
            if (!p.hasPermission("uiposts.post")) {p.sendMessage(ERROR+"无权发帖: 需要权限 uiposts.post");return;}
            long now=System.currentTimeMillis();
            if(lastPostTime.containsKey(p.getUniqueId())&&(now-lastPostTime.get(p.getUniqueId())<5000)){
                p.sendMessage(ERROR+"冷却中!每5秒可发一次~"); return;
            }
            p.closeInventory(); askForPostInput(p); return;
        }
        if (name.contains("切换到书本")) { p.closeInventory(); openBookGUI(p, 1); return; }
        if (name.contains("刷新")||name.contains("首页")) { p.closeInventory(); openChestGUI(p,1); return; }
        if (name.contains("下一页")) { openChestGUI(p, page+1); return; }
        if (name.contains("上一页")) { openChestGUI(p,Math.max(1,page-1)); return; }
        if (current.getType()==Material.WRITABLE_BOOK) {
            List<Integer> postSlots = (List<Integer>) ((Map<String, Object>) guiConfig.get("chest")).get("post_slot");
            int idx = postSlots.indexOf(e.getSlot());
            List<Post> posts = getPostsByPage(page, postSlots.size());
            if (idx<0 || idx>=posts.size()) return;
            showBookForPost(p, posts.get(idx));
        }
    }
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent e) {
        Player p = e.getPlayer();
        if (p.hasMetadata("uiposts_gui_type") && "book".equals(p.getMetadata("uiposts_gui_type").get(0).asString())) {
            openChestGUI(p, 1);
        }
    }
    // 聊天监听用于发帖、留言输入(GUI模式)
    @EventHandler
    public void onPlayerChat(org.bukkit.event.player.AsyncPlayerChatEvent e) {
        UUID uid = e.getPlayer().getUniqueId();
        Player player = e.getPlayer();
        if (awaitingPostInput.containsKey(uid)) {
            e.setCancelled(true);
            String msg = e.getMessage();
            if ("取消".equals(msg)) { awaitingPostInput.remove(uid); player.sendMessage(ERROR+"已取消发帖"); return; }
            if (msg.trim().length()==0) {player.sendMessage(ERROR+"内容不能为空");return;}
            if (msg.length()>POST_LIMIT) {
                player.sendMessage(ERROR+"内容超出最大长度,将自动截断。"+SECONDARY+""+msg.substring(0,POST_LIMIT));
                msg=msg.substring(0,POST_LIMIT);
            }
            savePost(player, msg);
            awaitingPostInput.remove(uid);
            lastPostTime.put(uid,System.currentTimeMillis());
            player.sendMessage(SUCCESS+"发帖成功!");
            Bukkit.getScheduler().runTask(this, () -> {
                openChestGUI(player, 1);
            });
            return;
        }
        if (awaitingReply.containsKey(uid)) {
            e.setCancelled(true);
            String msg = e.getMessage();
            if ("取消".equals(msg)) { awaitingReply.remove(uid); player.sendMessage(ERROR+"已取消留言"); return; }
            if (!player.hasPermission("uiposts.reply")) { player.sendMessage(ERROR+"无权留言:uiposts.reply");return; }
            if (msg.trim().length()==0) {player.sendMessage(ERROR+"留言不能为空");return;}
            if (msg.length()>REPLY_LIMIT) { player.sendMessage(ERROR+"内容超出限制,已截断。"); msg=msg.substring(0,REPLY_LIMIT); }
            saveReply(player, awaitingReply.get(uid), msg);
            awaitingReply.remove(uid);
            player.sendMessage(SUCCESS+"留言成功!");
            Bukkit.getScheduler().runTask(this, () -> {
                openChestGUI(player, 1);
            });
            return;
        }
    }
    // ===== 数据库/结构方法略 =====
    private void askForPostInput(Player p) {
        p.sendMessage(SECONDARY+"请输入帖子内容,限"+POST_LIMIT+"字内,输入'取消'退出:");
        awaitingPostInput.put(p.getUniqueId(), true);
    }
    private void askForReplyInput(Player p, int postId) {
        p.sendMessage(SECONDARY+"请输入你的留言(限"+REPLY_LIMIT+"字),输入'取消'退出:");
        awaitingReply.put(p.getUniqueId(), postId);
    }
    private String getInternetTime() {
        try {
            URL url = new URL(TIME_API);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(1500);
            conn.setReadTimeout(2000);
            conn.setRequestMethod("GET");
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuilder json = new StringBuilder(); String line; while ((line = in.readLine()) != null) json.append(line); in.close();
            String txt = json.toString(); int idx = txt.indexOf("\"datetime\":\""); if (idx != -1) { int start = idx + 12; int end = txt.indexOf("\"", start); String val = txt.substring(start, end); if (val.contains("T")) val = val.replace("T", " "); int dot = val.indexOf('.'); if (dot > 0) val = val.substring(0, dot); int plus = val.indexOf('+'); if (plus > 0) val = val.substring(0, plus); return val.trim(); }
        } catch (Exception e) {}
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
    }
    private static String wrapLines(String str,int lineLen){if(str==null)return"";StringBuilder sb=new StringBuilder();int i=0;while(i<str.length()){int e=Math.min(i+lineLen,str.length());sb.append(str.substring(i,e));if(e<str.length())sb.append("\n");i=e;}return sb.toString();}
    private int getTotalPages(int pageSize) {
        try {
            PreparedStatement ps = connection.prepareStatement("SELECT COUNT(*) FROM posts");
            ResultSet rs = ps.executeQuery();
            int count = rs.next() ? rs.getInt(1) : 0;
            rs.close();ps.close();
            return Math.max(1, (count + pageSize - 1) / pageSize);
        } catch (SQLException e) {}
        return 1;
    }
    private int countPosts(){
        int count=0;
        try{
            PreparedStatement ps=connection.prepareStatement("SELECT count(*) FROM posts");
            ResultSet rs=ps.executeQuery();
            count=rs.next()?rs.getInt(1):0;
            rs.close();ps.close();
        }catch(Exception e){}
        return count;
    }
    private int countPlayerPosts(String name){
        int count=0;
        try{
            PreparedStatement ps=connection.prepareStatement("SELECT count(*) FROM posts WHERE player=?");
            ps.setString(1, name);
            ResultSet rs=ps.executeQuery();
            count=rs.next()?rs.getInt(1):0;
            rs.close();ps.close();
        }catch(Exception e){}
        return count;
    }
    private void savePost(Player p, String content) {
        String time = getInternetTime();
        try {
            PreparedStatement ps = connection.prepareStatement("INSERT INTO posts(player,content,time) VALUES(?, ?, ?)");
            ps.setString(1, p.getName());
            ps.setString(2, content);
            ps.setString(3, time);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException e) {}
    }
    private void saveReply(Player p, int postId, String content) {
        String time = getInternetTime();
        try {
            PreparedStatement ps = connection.prepareStatement("INSERT INTO replies(post_id,player,content,time) VALUES(?, ?, ?, ?)");
            ps.setInt(1, postId);
            ps.setString(2, p.getName());
            ps.setString(3, content);
            ps.setString(4, time);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException e) {}
    }
    private List<Post> getPostsByPage(int page, int size) {
        List<Post> res = new ArrayList<>();
        try {
            PreparedStatement ps = connection.prepareStatement("SELECT id,player,content,time FROM posts ORDER BY id DESC LIMIT ? OFFSET ?");
            ps.setInt(1, size);
            ps.setInt(2, (page - 1) * size);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                res.add(new Post(
                        rs.getInt("id"),
                        rs.getString("player"),
                        rs.getString("content"),
                        rs.getString("time")
                ));
            }
            rs.close();ps.close();
        } catch (SQLException e) {}
        return res;
    }
    private Post getPostById(int pid) {
        try {
            PreparedStatement ps = connection.prepareStatement("SELECT id,player,content,time FROM posts WHERE id=?");
            ps.setInt(1, pid);
            ResultSet rs = ps.executeQuery();
            if(rs.next()) {
                Post p = new Post(rs.getInt("id"), rs.getString("player"), rs.getString("content"), rs.getString("time"));
                rs.close();ps.close(); return p;
            }
            rs.close();ps.close();
        } catch (SQLException e) {}
        return null;
    }
    private void deletePost(int pid) {
        try {
            PreparedStatement del1 = connection.prepareStatement("DELETE FROM posts WHERE id=?");
            del1.setInt(1, pid);
            del1.executeUpdate();
            del1.close();
            PreparedStatement del2 = connection.prepareStatement("DELETE FROM replies WHERE post_id=?");
            del2.setInt(1, pid);
            del2.executeUpdate();
            del2.close();
        } catch (SQLException e) {}
    }
    private List<Reply> getReplies(int postId) {
        List<Reply> res = new ArrayList<>();
        try {
            PreparedStatement ps = connection.prepareStatement("SELECT player,content,time FROM replies WHERE post_id=? ORDER BY id ASC");
            ps.setInt(1, postId);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                res.add(new Reply(
                        rs.getString("player"), rs.getString("content"), rs.getString("time")
                ));
            }
            rs.close();ps.close();
        } catch (SQLException e) {}
        return res;
    }
    private static class Post {
        int id; String player; String content; String time;
        Post(int id, String player, String content, String time) { this.id = id; this.player = player; this.content = content; this.time = time; }
    }
    private static class Reply { String player; String content; String time; Reply(String pl, String c, String t) { player = pl; content = c; time = t; } }
}
上一篇: UIPosts下一篇: UIPosts

举报内容

意见反馈