feat: 告示牌放置/编辑监控记录

1. land_block_log表新增detail列(已有数据库ALTER兼容)
2. DatabaseManager新增recordLandBlockWithDetail方法
3. LandRecordManager新增recordSignEdit方法,记录sign_place/sign_edit动作+内容
4. AreaProtection.onSignChange()延迟1tick记录(等事件处理完)
5. 回声碎片显示告示牌内容: 玩家XXX在HH:MM放置/编辑告示牌 [内容]
This commit is contained in:
2026-07-14 16:47:57 +08:00
parent 9ccda3f541
commit d2180cec1c
3 changed files with 92 additions and 5 deletions
+13 -1
View File
@@ -4994,13 +4994,25 @@ public class AreaProtection implements Listener {
@EventHandler
public void onSignChange(SignChangeEvent e) {
Player p = e.getPlayer();
if (p.hasPermission("sdf1.admin")) return; // 管理员跳过
AreaConfig ac = getArea(
p.getWorld().getName(),
e.getBlock().getX(),
e.getBlock().getY(),
e.getBlock().getZ());
if (ac == null) return;
// ★ 记录告示牌放置/编辑(权限检查前记录,被取消也要记录尝试)
// 检测是编辑还是放置:如果该位置已有告示牌方块数据
boolean isEdit = e.getBlock().getState() instanceof org.bukkit.block.Sign;
String[] lines = e.getLines();
// 延迟记录(等事件处理完后,内容已写入)
final boolean editFinal = isEdit;
plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
plugin.landRecordManager.recordSignEdit(ac, p, e.getBlock(),
lines, editFinal);
}, 1L);
if (p.hasPermission("sdf1.admin")) return; // 管理员跳过
if (p.getName().equalsIgnoreCase(ac.owner)) return; // 领地主免检
if (isAreaAdmin(p)) return; // 插件管理员免检
// ★ 授权成员(白名单内玩家)也豁免denySignEdit
+37 -1
View File
@@ -317,9 +317,15 @@ public class DatabaseManager {
+ "player_name TEXT,"
+ "action TEXT,"
+ "material TEXT,"
+ "detail TEXT,"
+ "time INTEGER"
+ ")");
// ★ 新增detail列(已有数据库兼容)
try {
st.executeUpdate("ALTER TABLE land_block_log ADD COLUMN detail TEXT");
} catch (SQLException ignored) {}
// 领地容器记录表:记录领地内容器的打开/拿取(7天滚动)
st.execute("CREATE TABLE IF NOT EXISTS "
+ "land_container_log ("
@@ -2247,6 +2253,35 @@ public class DatabaseManager {
}
}
/** ★ 记录领地方块(含detail,用于告示牌内容等) */
public void recordLandBlockWithDetail(String landName, int landId,
String world, int x, int y, int z,
String playerName, String action,
String material, String detail) {
try {
PreparedStatement ps = db.prepareStatement(
"INSERT INTO land_block_log"
+ " (land_name, land_id, world, x, y, z,"
+ " player_name, action, material, detail, time)"
+ " VALUES (?,?,?,?,?,?,?,?,?,?,?)");
ps.setString(1, landName);
ps.setInt(2, landId);
ps.setString(3, world);
ps.setInt(4, x);
ps.setInt(5, y);
ps.setInt(6, z);
ps.setString(7, playerName);
ps.setString(8, action);
ps.setString(9, material);
ps.setString(10, detail);
ps.setLong(11, System.currentTimeMillis());
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/** 记录领地容器打开/拿取 */
public void recordLandContainer(String landName, int landId,
String world, int x, int y, int z,
@@ -2285,7 +2320,7 @@ public class DatabaseManager {
List<Map<String, Object>> list = new ArrayList<>();
try {
PreparedStatement ps = db.prepareStatement(
"SELECT time, player_name, action, material"
"SELECT time, player_name, action, material, detail"
+ " FROM land_block_log"
+ " WHERE world=? AND x=? AND y=? AND z=?"
+ " ORDER BY time DESC LIMIT ?");
@@ -2303,6 +2338,7 @@ public class DatabaseManager {
rs.getString("player_name"));
row.put("action", rs.getString("action"));
row.put("material", rs.getString("material"));
row.put("detail", rs.getString("detail"));
list.add(row);
}
rs.close();
@@ -133,6 +133,32 @@ public class LandRecordManager implements Listener {
recordBlock(ac, p, block, "state_change");
}
/** ★ 记录告示牌放置/编辑(含内容) */
public void recordSignEdit(AreaConfig ac, Player p, Block block,
String[] lines, boolean isEdit) {
try {
int landId = plugin.areaProtection.getLandIdFromDb(ac.name);
// 拼接4行内容,空行跳过
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (line != null && !line.isEmpty()) {
if (sb.length() > 0) sb.append(" | ");
sb.append(line);
}
}
String detail = sb.toString();
String action = isEdit ? "sign_edit" : "sign_place";
plugin.getDb().recordLandBlockWithDetail(
ac.name, landId,
block.getWorld().getName(),
block.getX(), block.getY(), block.getZ(),
p.getName(), action,
block.getType().name(), detail);
} catch (Exception e) {
e.printStackTrace();
}
}
private void recordBlock(AreaConfig ac, Player p, Block block,
String action) {
try {
@@ -328,14 +354,27 @@ public class LandRecordManager implements Listener {
act = "§a放置";
} else if ("state_change".equals(action)) {
act = "§e状态变化";
} else if ("sign_place".equals(action)) {
act = "§a放置告示牌";
} else if ("sign_edit".equals(action)) {
act = "§b编辑告示牌";
} else {
act = "§7" + action;
}
String mat = (String) r.get("material");
String zh = translations.getOrDefault(mat, MaterialTranslator.toReadable(mat));
p.sendMessage(" §7" + fmt((Long) r.get("time"))
+ " §f" + r.get("player_name")
+ " " + act + " §e" + mat + " §a" + zh);
StringBuilder msg = new StringBuilder();
msg.append(" §7").append(fmt((Long) r.get("time")))
.append(" §f").append(r.get("player_name"))
.append(" ").append(act)
.append(" §e").append(mat).append(" §a").append(zh);
// ★ 告示牌内容显示
String detail = (String) r.get("detail");
if (detail != null && !detail.isEmpty()
&& ("sign_place".equals(action) || "sign_edit".equals(action))) {
msg.append(" §7[§f").append(detail).append("§7]");
}
p.sendMessage(msg.toString());
}
}