4 Commits

Author SHA1 Message Date
xiaobai
f899540449 1.0.1:Fix bug:[21:01:10 INFO]: [PBL] §a========================================
[21:01:10 INFO]: [PBL] §ePlayerBlockLife v1.0.1-1.20.4 已启用
              [21:01:10 INFO]: [PBL] §e作者: [YourName]
              [21:01:10 INFO]: [PBL] §a========================================
2026-02-13 21:02:28 +08:00
xiaobai
1fbd92ec72 1.0.1:Fix error:[DirectoryProviderSource] Error loading plugin: name is not defined 2026-02-13 20:59:33 +08:00
xiaobai
c15b30f666 1.0.1:Fix error:[DirectoryProviderSource] Error loading plugin: name is not defined 2026-02-13 20:58:03 +08:00
xiaobai
8d8cdcb244 1.0.1:Fix error:[DirectoryProviderSource] Error loading plugin: name is not defined 2026-02-13 20:56:03 +08:00
6 changed files with 303 additions and 356 deletions

View File

@@ -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;
}

View File

@@ -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("&", "§");
}

View File

@@ -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() {

View File

@@ -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
# 消息配置(可选,插件会使用默认消息)
messages:
commands:
setlifeblocks:
success: "&a已为你生成 {blocks} 个生命方块!"
already_has: "&c你已经有生命方块了使用 /checklifeblocks 查看位置"
game:
block_destroyed:
owner: "&c⚠ 警告!你的生命方块被 {breaker} 破坏了!"
breaker: "&a你破坏了 {owner} 的生命方块!"

View File

@@ -4,38 +4,72 @@ main: com.playerblocklife.PlayerBlockLife
api-version: 1.20
author: xiaobai
description: 玩家生命方块系统 - 方块被挖光则死亡
website: https://github.com/yourname/PlayerBlockLife
prefix: PBL
commands:
setlifeblocks:
description: 设置你的生命方块(使用你的皮肤)
usage: /<command>
usage: |
/<command> - 设置你的生命方块
/<command> help - 显示帮助
/<command> reset - 重置生命方块位置
/<command> other <玩家> - 为其他玩家设置(管理员)
aliases: [sbl, lifeblocks, setblocks]
permission: playerblocklife.set
permission-message: "§c你没有权限使用此命令"
checklifeblocks:
description: 查看你的生命方块位置
usage: /<command>
aliases: [cbl, checklife, myblocks]
permission: playerblocklife.check
permission-message: "§c你没有权限使用此命令"
pblreload:
description: 重载插件配置
usage: /<command>
aliases: [pblr, pblreload]
aliases: [pblr]
permission: playerblocklife.admin
permission-message: "§c你没有权限使用此命令"
pbldelete:
description: 删除指定玩家的生命方块
usage: /<command> <玩家>
aliases: [pbldel, deleteblocks]
permission: playerblocklife.admin
permission-message: "§c你没有权限使用此命令"
pblrevive:
description: 复活被淘汰的玩家
usage: /<command> [玩家]
aliases: [revive]
permission: playerblocklife.admin
permission-message: "§c你没有权限使用此命令"
pblstats:
description: 查看插件统计信息
usage: /<command>
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