如何将存储Player与Integer的HashMap持久化到文件中
解决玩家计时器数据持久化问题
首先要明确:不能直接存储Player对象,因为它是服务器运行时的实例,无法被序列化保存,重启后会失效。我们需要用玩家的UUID作为唯一标识来存储数据,先修改你的Map:
// 把原来的Player键换成UUID public static Map<UUID, Integer> playerSeconds = new HashMap<>();
下面用Bukkit自带的YAML文件实现数据的保存与加载,无需额外依赖,适合新手:
1. 核心工具方法(写在插件主类中)
加载所有玩家数据
在插件启用时调用,从文件读取数据到Map:
public void loadPlayerTimes() { // 数据文件存放在插件的数据文件夹下,比如 plugins/你的插件名/player_times.yml File dataFile = new File(getDataFolder(), "player_times.yml"); // 如果文件夹或文件不存在,先创建 if (!dataFile.exists()) { try { getDataFolder().mkdirs(); // 创建插件数据目录 dataFile.createNewFile(); // 创建空文件 } catch (IOException e) { getLogger().severe("创建数据文件失败: " + e.getMessage()); e.printStackTrace(); } return; } // 加载YAML配置 YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile); // 遍历所有存储的UUID,转换后存入Map for (String uuidStr : config.getKeys(false)) { try { UUID uuid = UUID.fromString(uuidStr); int seconds = config.getInt(uuidStr); playerSeconds.put(uuid, seconds); } catch (IllegalArgumentException e) { getLogger().warning("无效的UUID格式: " + uuidStr); } } }
保存单个玩家数据
玩家退出时调用,保存该玩家的计时器数值:
public void savePlayerTime(Player player) { UUID uuid = player.getUniqueId(); Integer seconds = playerSeconds.get(uuid); if (seconds == null) return; // 无数据则跳过 File dataFile = new File(getDataFolder(), "player_times.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile); // 将UUID转为字符串作为键,存储数值 config.set(uuid.toString(), seconds); try { config.save(dataFile); } catch (IOException e) { getLogger().severe("保存玩家 " + player.getName() + " 的数据失败: " + e.getMessage()); e.printStackTrace(); } }
保存所有玩家数据
插件关闭时调用,防止遗漏未退出玩家的数据:
public void saveAllPlayerTimes() { File dataFile = new File(getDataFolder(), "player_times.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(dataFile); // 遍历Map,批量写入所有数据 for (Map.Entry<UUID, Integer> entry : playerSeconds.entrySet()) { config.set(entry.getKey().toString(), entry.getValue()); } try { config.save(dataFile); } catch (IOException e) { getLogger().severe("保存所有玩家数据失败: " + e.getMessage()); e.printStackTrace(); } }
2. 注册事件监听,处理玩家加入/退出
让你的插件主类实现Listener接口,然后注册事件并编写处理逻辑:
public class YourPlugin extends JavaPlugin implements Listener { public static Map<UUID, Integer> playerSeconds = new HashMap<>(); @Override public void onEnable() { // 插件启用时加载数据 loadPlayerTimes(); // 注册事件监听器 getServer().getPluginManager().registerEvents(this, this); getLogger().info("插件已启用,数据加载完成"); } @Override public void onDisable() { // 插件关闭时保存所有数据 saveAllPlayerTimes(); getLogger().info("插件已关闭,数据保存完成"); } // 玩家加入服务器时触发 @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); UUID uuid = player.getUniqueId(); // 如果玩家是第一次加入,默认设置计时器为0(可根据需求修改) playerSeconds.putIfAbsent(uuid, 0); // 可以给玩家发送提示 player.sendMessage("欢迎回来!你的计时器剩余 " + playerSeconds.get(uuid) + " 秒"); } // 玩家退出服务器时触发 @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); // 保存该玩家的数据 savePlayerTime(player); // 从Map中移除,节省内存 playerSeconds.remove(player.getUniqueId()); } // 上面的loadPlayerTimes、savePlayerTime、saveAllPlayerTimes方法放在这里 }
3. 替换原有代码中的Map使用方式
原来你操作seconds的地方,都要改成用UUID来访问:
// 示例:给玩家设置计时器数值 playerSeconds.put(player.getUniqueId(), 120); // 示例:获取玩家当前计时器数值 int currentTime = playerSeconds.getOrDefault(player.getUniqueId(), 0);
注意事项
- 确保你的插件主类继承了
JavaPlugin,事件方法上的@EventHandler注解不要忘记,否则事件不会触发。 - 数据文件会自动生成在
服务器目录/plugins/你的插件名/player_times.yml,可以直接打开查看或修改。
内容的提问来源于stack exchange,提问作者Chanlanfan
相关产品推荐
相关产品推荐

