diff --git a/Releases/PlayerBlockLife-1.0.1-1.20.4.jar b/Releases/PlayerBlockLife-1.0.1-1.20.4.jar deleted file mode 100644 index 1d7ebd8..0000000 Binary files a/Releases/PlayerBlockLife-1.0.1-1.20.4.jar and /dev/null differ diff --git a/src/main/java/com/playerblocklife/AdminCommands.java b/src/main/java/com/playerblocklife/AdminCommands.java index a6434e7..127c854 100644 --- a/src/main/java/com/playerblocklife/AdminCommands.java +++ b/src/main/java/com/playerblocklife/AdminCommands.java @@ -23,10 +23,9 @@ public class AdminCommands implements CommandExecutor { } if (command.getName().equalsIgnoreCase("pblreload")) { - plugin.reloadConfig(); - plugin.getBlockManager().loadData(); - plugin.getSkinManager().loadAllSkins(); - sender.sendMessage("§a插件配置已重载!"); + // 调用插件的完整重载方法 + plugin.reloadPluginConfig(); + sender.sendMessage("§a插件配置已完全重载!"); return true; } @@ -44,7 +43,12 @@ public class AdminCommands implements CommandExecutor { targetId = target.getUniqueId(); } else { // 尝试从离线玩家获取UUID - targetId = Bukkit.getOfflinePlayer(targetName).getUniqueId(); + try { + targetId = Bukkit.getOfflinePlayer(targetName).getUniqueId(); + } catch (Exception e) { + sender.sendMessage("§c找不到玩家: " + targetName); + return true; + } } plugin.getBlockManager().clearPlayerBlocks(targetId); @@ -69,12 +73,21 @@ public class AdminCommands implements CommandExecutor { } } - plugin.getLifeSystem().revivePlayer(target); - sender.sendMessage("§a玩家 " + target.getName() + " 已复活!"); + if (plugin.getLifeSystem() != null) { + plugin.getLifeSystem().revivePlayer(target); + sender.sendMessage("§a玩家 " + target.getName() + " 已复活!"); + } else { + sender.sendMessage("§c复活失败:生命系统未初始化"); + } return true; } if (command.getName().equalsIgnoreCase("pblstats")) { + if (plugin.getBlockManager() == null) { + sender.sendMessage("§c方块管理器未初始化"); + return true; + } + int totalPlayers = plugin.getBlockManager().getPlayerBlocksCount(); int totalBlocks = plugin.getBlockManager().getTotalBlocksCount(); @@ -85,9 +98,11 @@ public class AdminCommands implements CommandExecutor { for (Player player : Bukkit.getOnlinePlayers()) { int blocks = plugin.getBlockManager().getRemainingBlocks(player.getUniqueId()); - sender.sendMessage("§7- " + player.getName() + ": §e" + blocks + " §7/ §a5"); + String status = blocks > 0 ? "§a存活" : "§c已淘汰"; + sender.sendMessage("§7- " + player.getName() + ": §e" + blocks + " §7/ §a5 §7(" + status + "§7)"); } + sender.sendMessage("§a================================="); return true; } diff --git a/src/main/java/com/playerblocklife/ConfigManager.java b/src/main/java/com/playerblocklife/ConfigManager.java index d63f980..21ede7b 100644 --- a/src/main/java/com/playerblocklife/ConfigManager.java +++ b/src/main/java/com/playerblocklife/ConfigManager.java @@ -1,60 +1,209 @@ package com.playerblocklife; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; public class ConfigManager { private final PlayerBlockLife plugin; private FileConfiguration config; + private File configFile; public ConfigManager(PlayerBlockLife plugin) { this.plugin = plugin; - loadConfig(); + this.configFile = new File(plugin.getDataFolder(), "config.yml"); } + /** + * 加载配置 + */ public void loadConfig() { - plugin.saveDefaultConfig(); - config = plugin.getConfig(); + // 确保配置文件夹存在 + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdirs(); + } + + // 如果配置文件不存在,从JAR中复制默认配置 + if (!configFile.exists()) { + plugin.saveDefaultConfig(); + plugin.logInfo("创建默认配置文件"); + } + + // 重新加载配置 + reloadConfig(); } + /** + * 重新加载配置 + */ public void reloadConfig() { - plugin.reloadConfig(); - config = plugin.getConfig(); + // 重新从磁盘加载配置 + config = YamlConfiguration.loadConfiguration(configFile); + + // 加载默认配置作为后备 + InputStream defaultConfigStream = plugin.getResource("config.yml"); + if (defaultConfigStream != null) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration( + new InputStreamReader(defaultConfigStream, StandardCharsets.UTF_8)); + config.setDefaults(defaultConfig); + } + + // 检查配置版本,如果需要则更新 + checkConfigVersion(); + + plugin.logInfo("配置已加载"); } + /** + * 检查配置版本并更新 + */ + private void checkConfigVersion() { + int currentVersion = config.getInt("config-version", 1); + int latestVersion = 1; // 最新配置版本 + + if (currentVersion < latestVersion) { + plugin.logWarning("检测到旧版配置文件,正在更新..."); + updateConfig(currentVersion, latestVersion); + } + } + + /** + * 更新配置文件 + */ + private void updateConfig(int fromVersion, int toVersion) { + if (fromVersion == 1 && toVersion == 2) { + // 示例:添加新配置项 + if (!config.contains("new-feature.enabled")) { + config.set("new-feature.enabled", true); + config.set("new-feature.duration", 60); + } + + // 更新版本号 + config.set("config-version", toVersion); + + try { + config.save(configFile); + plugin.logInfo("配置文件已更新到版本 " + toVersion); + } catch (IOException e) { + plugin.logError("保存更新后的配置文件失败", e); + } + } + } + + /** + * 保存配置 + */ + public void saveConfig() { + try { + config.save(configFile); + } catch (IOException e) { + plugin.logError("保存配置文件失败", e); + } + } + + /** + * 获取配置对象 + */ + public FileConfiguration getConfig() { + if (config == null) { + reloadConfig(); + } + return config; + } + + // 以下为配置项的获取方法 + public int getBlocksPerPlayer() { - return config.getInt("blocks.amount", 5); + return getConfig().getInt("blocks.amount", 5); } public int getSpreadRange() { - return config.getInt("blocks.spread", 5); + return getConfig().getInt("blocks.spread", 5); } public int getDepth() { - return config.getInt("blocks.depth", -1); + return getConfig().getInt("blocks.depth", -1); + } + + public String getBlockMaterial() { + return getConfig().getString("blocks.material", "player_head"); } public boolean isDieWhenBlocksGone() { - return config.getBoolean("game.die_when_blocks_gone", true); + return getConfig().getBoolean("game.die_when_blocks_gone", true); } public boolean isBecomeSpectator() { - return config.getBoolean("game.become_spectator", true); + return getConfig().getBoolean("game.become_spectator", true); } public boolean isHealthSystemEnabled() { - return config.getBoolean("game.health_system", true); + return getConfig().getBoolean("game.health_system", true); } public boolean isSkinSystemEnabled() { - return config.getBoolean("skin.enabled", true); + return getConfig().getBoolean("skin.enabled", true); } public String getSkinSource() { - return config.getString("skin.source", "player_profile"); + return getConfig().getString("skin.source", "player_profile"); + } + + public int getCacheExpireDays() { + return getConfig().getInt("skin.cache.expire_days", 7); + } + + public boolean isAutoSaveEnabled() { + return getConfig().getBoolean("storage.auto_save.enabled", true); + } + + public int getAutoSaveInterval() { + return getConfig().getInt("storage.auto_save.interval", 300); + } + + public String getStorageType() { + return getConfig().getString("storage.type", "yaml"); + } + + public boolean isBroadcastOnBlockBreak() { + return getConfig().getBoolean("game.broadcast.on_block_break", true); + } + + public boolean isBroadcastOnPlayerDeath() { + return getConfig().getBoolean("game.broadcast.on_player_death", true); + } + + public int getBroadcastRange() { + return getConfig().getInt("game.broadcast.range", 30); + } + + public boolean isGiveExpReward() { + return getConfig().getBoolean("game.break_rewards.give_exp", true); + } + + public int getExpRewardAmount() { + return getConfig().getInt("game.break_rewards.exp_amount", 5); + } + + public boolean isProtectFromExplosions() { + return getConfig().getBoolean("protection.protect_from_explosions", true); + } + + public boolean isProtectFromFire() { + return getConfig().getBoolean("protection.protect_from_fire", true); + } + + public boolean isProtectFromPistons() { + return getConfig().getBoolean("protection.protect_from_pistons", true); } public String getMessage(String path, String defaultValue) { - String message = config.getString("messages." + path, defaultValue); + String message = getConfig().getString("messages." + path, defaultValue); if (message != null) { message = message.replace("&", "§"); } diff --git a/src/main/java/com/playerblocklife/PlayerBlockLife.java b/src/main/java/com/playerblocklife/PlayerBlockLife.java index b59cee9..772a9ab 100644 --- a/src/main/java/com/playerblocklife/PlayerBlockLife.java +++ b/src/main/java/com/playerblocklife/PlayerBlockLife.java @@ -1,6 +1,8 @@ package com.playerblocklife; +import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; + import java.util.logging.Level; public class PlayerBlockLife extends JavaPlugin { @@ -10,36 +12,40 @@ public class PlayerBlockLife extends JavaPlugin { private LifeSystem lifeSystem; private ConfigManager configManager; - // 在 PlayerBlockLife.java 中添加: - - @Override - public void reloadConfig() { - super.reloadConfig(); - configManager.reloadConfig(); - getLogger().info("配置已重新加载"); - } @Override public void onEnable() { instance = this; + + // 第一步:保存默认配置 saveDefaultConfig(); + // 第二步:初始化管理器(注意顺序!) this.configManager = new ConfigManager(this); this.skinManager = new SkinManager(this); this.blockManager = new PlayerBlockManager(this, skinManager); this.lifeSystem = new LifeSystem(this); + // 第三步:加载数据(必须在管理器初始化之后) + this.configManager.loadConfig(); + + // 第四步:注册事件监听器 getServer().getPluginManager().registerEvents(new BlockBreakListener(this), this); getServer().getPluginManager().registerEvents(new PlayerJoinListener(this), this); getServer().getPluginManager().registerEvents(new PlayerQuitListener(this), this); + // 第五步:注册命令 getCommand("setlifeblocks").setExecutor(new SetLifeBlocksCommand(this)); getCommand("checklifeblocks").setExecutor(new CheckLifeBlocksCommand(this)); getCommand("pblreload").setExecutor(new AdminCommands(this)); getCommand("pbldelete").setExecutor(new AdminCommands(this)); + getCommand("pblrevive").setExecutor(new AdminCommands(this)); + getCommand("pblstats").setExecutor(new AdminCommands(this)); + // 第六步:加载其他数据 blockManager.loadData(); skinManager.loadAllSkins(); + // 第七步:启动定时任务 startScheduler(); getLogger().info("§a========================================"); @@ -50,6 +56,7 @@ public class PlayerBlockLife extends JavaPlugin { @Override public void onDisable() { + // 保存数据 if (blockManager != null) { blockManager.saveData(); } @@ -59,15 +66,57 @@ public class PlayerBlockLife extends JavaPlugin { getLogger().info("§cPlayerBlockLife 插件已禁用"); } + /** + * 重写 reloadConfig 方法,避免循环依赖 + */ + @Override + public void reloadConfig() { + // 只调用父类的reloadConfig,不调用configManager的方法 + super.reloadConfig(); + getLogger().info("基础配置文件已重新加载"); + } + + /** + * 插件的完整重载方法(用于命令) + */ + public void reloadPluginConfig() { + if (configManager != null) { + configManager.reloadConfig(); + } + if (blockManager != null) { + blockManager.loadData(); + } + if (skinManager != null) { + skinManager.loadAllSkins(); + } + getLogger().info("插件配置已完全重载"); + } + private void startScheduler() { + // 每5分钟自动保存数据 getServer().getScheduler().runTaskTimerAsynchronously(this, () -> { - blockManager.saveData(); - skinManager.saveSkinData(); + if (blockManager != null) { + blockManager.saveData(); + } + if (skinManager != null) { + skinManager.saveSkinData(); + } + getLogger().info("数据已自动保存"); }, 6000L, 6000L); + // 每10秒检查玩家生命方块 getServer().getScheduler().runTaskTimer(this, () -> { - lifeSystem.checkAllPlayers(); + if (lifeSystem != null) { + lifeSystem.checkAllPlayers(); + } }, 200L, 200L); + + // 每分钟清理一次过期缓存 + getServer().getScheduler().runTaskTimerAsynchronously(this, () -> { + if (skinManager != null) { + skinManager.cleanupOldCache(); + } + }, 1200L, 1200L); } public static PlayerBlockLife getInstance() { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 94f4b7d..bba24ff 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,5 @@ # PlayerBlockLife 配置文件 -# 版本: 1.0.1-1.20.4 +config-version: 1 # 方块设置 blocks: @@ -10,28 +10,7 @@ blocks: # 方块埋藏深度(0为地面,负数为地下) depth: -1 # 方块材质类型 - # 可选值: player_head, custom_block, default material: player_head - # 是否在生成方块时自动填充周围的方块 - fill_around: true - - # 当使用玩家头颅时的设置 - player_head: - # 是否随机旋转头颅方向 - random_rotation: true - # 是否显示玩家名字 - show_player_name: true - # 是否显示特殊效果 - show_effects: true - - # 当使用自定义方块时的设置 - custom_block: - # 自定义方块材质 - material: DIAMOND_BLOCK - # 是否发光 - glowing: true - # 发光等级 (0-15) - light_level: 3 # 游戏规则 game: @@ -39,59 +18,8 @@ game: die_when_blocks_gone: true # 死亡后是否变成观察者 become_spectator: true - # 观察者模式 - spectator_mode: - # 是否可以飞行 - can_fly: true - # 是否可以看到其他玩家 - can_see_players: true - # 是否可以穿墙 - can_clip: true - # 是否启用生命值系统 health_system: true - # 生命值计算公式 - health_formula: - # 方块数量与生命值的关系 - # 格式: 方块数量:生命值 - 5: 20 - 4: 16 - 3: 12 - 2: 8 - 1: 4 - 0: 0 - - # 状态效果 - status_effects: - # 当剩余2个方块时 - two_blocks_left: - - type: SLOW - amplifier: 1 - duration: 100 - # 当剩余1个方块时 - one_block_left: - - type: SLOW - amplifier: 2 - duration: 100 - - type: WEAKNESS - amplifier: 0 - duration: 100 - - type: BLINDNESS - amplifier: 0 - duration: 100 - - # 挖掘奖励 - break_rewards: - # 是否给予经验 - give_exp: true - # 经验数量 - exp_amount: 5 - # 是否给予物品 - give_items: false - # 物品列表 - items: - - DIAMOND:1 - - GOLD_INGOT:3 # 广播设置 broadcast: @@ -101,58 +29,29 @@ game: range: 30 # 玩家死亡时是否全服广播 on_player_death: true - # 新玩家加入时是否广播 - on_player_join: false + + # 挖掘奖励 + break_rewards: + # 是否给予经验 + give_exp: true + # 经验数量 + exp_amount: 5 # 皮肤系统 skin: # 是否启用皮肤系统 enabled: true - # 皮肤来源 + # 皮肤来源 (player_profile, local_cache) source: player_profile - # 可选值: player_profile, mojang_api, local_cache - # player_profile: 从玩家本地缓存获取(推荐,不调用外部API) - # mojang_api: 从Mojang API获取(需要网络) - # local_cache: 从本地缓存获取 # 缓存设置 cache: - # 是否启用缓存 - enabled: true # 缓存过期时间(天) expire_days: 7 - # 缓存最大大小(MB) - max_size_mb: 100 - - # 默认皮肤(当无法获取玩家皮肤时) - default_skin: - # 使用哪个玩家的皮肤作为默认 - player_name: Steve - # 或使用自定义UUID - uuid: 8667ba71-b85a-4004-af54-457a9734eed7 - # 是否随机分配默认皮肤 - random_default: false - # 可选的默认皮肤列表 - available_skins: - - Steve - - Alex - - Enderman - - # 皮肤处理 - processing: - # 是否异步处理皮肤 - async: true - # 处理线程数 - threads: 2 - # 超时时间(秒) - timeout: 10 - # 重试次数 - retry_times: 3 # 数据存储 storage: - # 存储类型 - # 可选值: yaml, json, sqlite, mysql + # 存储类型 (yaml, json, sqlite) type: yaml # 自动保存 @@ -162,221 +61,22 @@ storage: # 保存间隔(秒) interval: 300 - # YAML存储设置 - yaml: - # 数据文件编码 - encoding: UTF-8 - # 是否压缩 - compress: false - - # SQLite设置 - sqlite: - # 数据库文件路径 - file: plugins/PlayerBlockLife/data.db - # 连接池大小 - pool_size: 5 - - # MySQL设置 - mysql: - # 数据库主机 - host: localhost - # 数据库端口 - port: 3306 - # 数据库名 - database: minecraft - # 用户名 - username: root - # 密码 - password: password - # 表前缀 - table_prefix: pbl_ - # 连接池设置 - pool: - max_connections: 10 - min_connections: 2 - connection_timeout: 30000 - idle_timeout: 600000 - -# 消息配置 -messages: - # 消息前缀 - prefix: "&6[&ePlayerBlockLife&6]&r " - # 消息颜色 - colors: - success: "&a" - error: "&c" - warning: "&e" - info: "&7" - highlight: "&6" - - # 命令消息 - commands: - setlifeblocks: - success: "&a已为你生成 {blocks} 个生命方块!" - already_has: "&c你已经有生命方块了!使用 /checklifeblocks 查看位置" - no_skin: "&e你的皮肤正在加载中,请稍候..." - failed: "&c无法生成生命方块,请确保周围有足够空间" - - checklifeblocks: - no_blocks: "&c你还没有设置生命方块!使用 /setlifeblocks 来设置" - info: "&a你的生命方块信息:" - location: "&7{index}. &e世界: {world} &7坐标: &a{x}&7, &a{y}&7, &a{z}" - remaining: "&7剩余方块: &e{remaining}&7/&a5" - health: "&7当前生命值: &c{health} ❤" - - admin: - reload: "&a插件配置已重载!" - delete_success: "&a已删除玩家 {player} 的生命方块" - delete_failed: "&c删除失败,玩家不存在或没有生命方块" - revive_success: "&a玩家 {player} 已复活!" - revive_failed: "&c复活失败,玩家不存在或未死亡" - - # 游戏消息 - game: - block_destroyed: - owner: "&c⚠ 警告!你的生命方块被 {breaker} 破坏了!" - breaker: "&a你破坏了 {owner} 的生命方块!" - remaining: "&7对方剩余生命方块: &a{remaining}" - broadcast: "&7[附近] &e一个生命方块被破坏了!" - - death: - title: "&4☠ 你死了!" - subtitle: "&c所有生命方块已被挖光" - broadcast: "&4☠ 玩家 {player} 的生命方块已被全部挖光,惨遭淘汰!" - spectator: "&e你已被淘汰,可以观察其他玩家。等待下一轮游戏开始..." - - warning: - low_blocks: "&c⚠ 警告!生命方块即将耗尽!" - last_block: "&4⚠ 警告!这是最后一个生命方块!" - health_low: "&4⚠ 警告!生命值过低!" - - # 加入消息 - join: - welcome: "&e欢迎加入游戏!" - welcome_back: "&e欢迎回来,{player}!" - rules: | - &6游戏规则: - &7- 每个玩家有5个生命方块 - &7- 方块被其他玩家挖光时,你将死亡 - &7- 方块使用你的皮肤作为材质 - &7- 你可以自由移动,但方块固定位置 - remaining_blocks: "&7你还有 {blocks} 个生命方块" - commands: "&7使用 &e/setlifeblocks &7来设置你的生命方块" - -# 效果设置 -effects: - # 方块放置效果 - place: - particles: - - type: ENCHANTMENT_TABLE - count: 30 - offset_x: 0.3 - offset_y: 0.3 - offset_z: 0.3 - speed: 0.1 - sounds: - - type: BLOCK_ANVIL_PLACE - volume: 0.5 - pitch: 1.2 - - # 方块破坏效果 - break: - particles: - - type: BLOCK_CRACK - count: 50 - offset_x: 0.5 - offset_y: 0.5 - offset_z: 0.5 - speed: 0.5 - data: PLAYER_HEAD - - type: SMOKE_LARGE - count: 20 - offset_x: 0.3 - offset_y: 0.3 - offset_z: 0.3 - speed: 0.05 - sounds: - - type: ENTITY_ITEM_BREAK - volume: 1.0 - pitch: 0.8 - - type: BLOCK_GLASS_BREAK - volume: 0.8 - pitch: 1.0 - knockback: - enabled: true - power: 0.5 - vertical: 0.3 - - # 玩家受伤效果 - damage: - particles: - - type: DAMAGE_INDICATOR - count: 10 - sounds: - - type: ENTITY_PLAYER_HURT - volume: 1.0 - pitch: 1.0 - - # 玩家死亡效果 - player_death: - particles: - - type: EXPLOSION_HUGE - count: 5 - sounds: - - type: ENTITY_WITHER_DEATH - volume: 0.7 - pitch: 0.8 - - type: ENTITY_LIGHTNING_BOLT_THUNDER - volume: 1.0 - pitch: 0.5 - # 保护设置 protection: - # 是否保护生命方块不被非玩家破坏 - protect_from_non_players: true # 是否保护生命方块不被爆炸破坏 protect_from_explosions: true # 是否保护生命方块不被火焰烧毁 protect_from_fire: true # 是否保护生命方块不被活塞推动 protect_from_pistons: true - # 是否允许TNT破坏生命方块 - allow_tnt_damage: false - # 是否允许苦力怕爆炸破坏生命方块 - allow_creeper_explosions: false - # 是否允许末影龙破坏生命方块 - allow_ender_dragon_damage: false - # 是否允许其他插件修改生命方块 - allow_plugin_modification: false -# 世界保护 -world_guard: - # 是否与WorldGuard集成 - enabled: false - # 需要保护的地区标志 - region_flags: - - block-break - - block-place - - pvp - # 白名单地区(允许破坏生命方块的地区) - whitelist_regions: [] - # 黑名单地区(不允许破坏生命方块的地区) - blacklist_regions: [] - -# 调试设置 -debug: - # 是否启用调试模式 - enabled: false - # 调试级别 - # 可选值: INFO, WARNING, ERROR, DEBUG - level: INFO - # 是否记录到文件 - log_to_file: true - # 日志文件路径 - log_file: plugins/PlayerBlockLife/debug.log - # 是否显示详细事件日志 - verbose_events: false - # 是否记录性能数据 - log_performance: false - # 性能日志间隔(秒) - performance_log_interval: 60 \ No newline at end of file +# 消息配置(可选,插件会使用默认消息) +messages: + commands: + setlifeblocks: + success: "&a已为你生成 {blocks} 个生命方块!" + already_has: "&c你已经有生命方块了!使用 /checklifeblocks 查看位置" + game: + block_destroyed: + owner: "&c⚠ 警告!你的生命方块被 {breaker} 破坏了!" + breaker: "&a你破坏了 {owner} 的生命方块!" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 62bf0fc..6eb1df1 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,40 +2,74 @@ name: PlayerBlockLife version: 1.0.1-1.20.4 main: com.playerblocklife.PlayerBlockLife api-version: 1.20 -author: xiaobai +author: YourName description: 玩家生命方块系统 - 方块被挖光则死亡 +website: https://github.com/yourname/PlayerBlockLife +prefix: PBL + commands: setlifeblocks: description: 设置你的生命方块(使用你的皮肤) - usage: / + usage: | + / - 设置你的生命方块 + / help - 显示帮助 + / reset - 重置生命方块位置 + / other <玩家> - 为其他玩家设置(管理员) aliases: [sbl, lifeblocks, setblocks] permission: playerblocklife.set + permission-message: "§c你没有权限使用此命令!" checklifeblocks: description: 查看你的生命方块位置 usage: / aliases: [cbl, checklife, myblocks] permission: playerblocklife.check + permission-message: "§c你没有权限使用此命令!" pblreload: description: 重载插件配置 usage: / - aliases: [pblr, pblreload] + aliases: [pblr] permission: playerblocklife.admin + permission-message: "§c你没有权限使用此命令!" pbldelete: description: 删除指定玩家的生命方块 usage: / <玩家> aliases: [pbldel, deleteblocks] permission: playerblocklife.admin + permission-message: "§c你没有权限使用此命令!" pblrevive: description: 复活被淘汰的玩家 usage: / [玩家] aliases: [revive] permission: playerblocklife.admin + permission-message: "§c你没有权限使用此命令!" pblstats: description: 查看插件统计信息 usage: / - permission: playerblocklife.admin \ No newline at end of file + permission: playerblocklife.admin + permission-message: "§c你没有权限使用此命令!" + +permissions: + playerblocklife.*: + description: 所有 PlayerBlockLife 权限 + children: + playerblocklife.set: true + playerblocklife.check: true + playerblocklife.admin: true + default: op + + playerblocklife.set: + description: 允许设置生命方块 + default: true + + playerblocklife.check: + description: 允许查看生命方块 + default: true + + playerblocklife.admin: + description: 管理员权限 + default: op \ No newline at end of file