Files
Sdf1_login/api/land_api.php
T
ypsdf1 8edc94b848 v12: PHP密码验证安全修复 + land_api GET参数支持
安全修复:
- sync.php: 删除verify_web_password端点,禁止PHP自验证密码
- core.php: validateWebAccess大额交易密码验证改为检查web_login_verified(Java验证结果)
- 密码验证强制走web_login_request流程

其他:
- land_api.php: handleAckAdminChanges支持GET参数ids(避免POST Content-Type问题)
2026-06-25 14:19:08 +08:00

1108 lines
40 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 领地系统API - 接收Java端领地数据 + 管理面板 + 玩家端
* POST: Java端推送领地数据
* GET: admin/玩家面板读取领地数据
*/
header('Content-Type: application/json; charset=utf-8');
error_reporting(E_ERROR | E_PARSE);
try {
// ★ 加载core.php(与sync.php相同的加载方式)
$coreFile = dirname(__DIR__) . '/core.php';
if (!file_exists($coreFile)) {
$coreFile = dirname(__DIR__) . '/plugin/core.php';
}
require_once $coreFile;
$action = $_GET['action'] ?? $_POST['action'] ?? '';
$secret = $_GET['secret'] ?? $_POST['secret'] ?? '';
$method = $_SERVER['REQUEST_METHOD'];
// ===== 验证 =====
// 三种认证方式:
// 1. Java端推送: POST + secret
// 2. 管理面板: GET/POST + admin_token (admin.php已登录的session)
// 3. 玩家端: GET + player_token
$playerName = '';
$isAdmin = false;
// 同步类action只接受secret验证(Java端推送)
$syncActions = ['sync_lands', 'sync_shop', 'sync_permissions'];
// 管理面板action:支持admin_token或secret
$adminActions = ['list_lands', 'list_shop', 'get_config', 'update_config', 'delete_land', 'update_land_owner', 'delete_shop_item'];
// 玩家端action:需要token
$playerActions = ['my_lands', 'land_detail', 'add_visitor', 'remove_visitor', 'list_visitors', 'land_shop', 'buy_permission'];
// ★ 玩家端权限操作action
$playerPermActions = ['update_visitor_perm', 'get_visitor_perm'];
// ★ 成员独立权限操作action(领地所有者编辑成员权限)
$memberPermActions = ['get_member_perms', 'update_member_perm', 'clear_member_perm'];
// ★ Java端轮询PHP管理员变更
$syncFromPhpActions = ['poll_admin_changes', 'ack_admin_changes'];
if (in_array($action, $syncActions) || in_array($action, $syncFromPhpActions)) {
// 同步类:必须有secret
if (!validateSecret($secret)) {
echo json_encode(['success' => false, 'error' => 'invalid secret']);
exit;
}
} elseif (in_array($action, $adminActions)) {
// 管理面板:验证admin_token或secret
$adminToken = $_GET['admin_token'] ?? $_POST['admin_token'] ?? '';
if (!empty($adminToken)) {
// 用admin_token验证(admin.php的登录态)
$adminInfo = validateTokenSilent($adminToken);
if (!$adminInfo) {
echo json_encode(['success' => false, 'error' => '管理员认证失败', 'needLogin' => true]);
exit;
}
$isAdmin = true;
} elseif (!validateSecret($secret)) {
// 没有admin_token,尝试secret
echo json_encode(['success' => false, 'error' => '需要管理员认证']);
exit;
}
} elseif (in_array($action, $playerActions) || in_array($action, $playerPermActions) || in_array($action, $memberPermActions)) {
// 玩家端:需要token
$token = $_GET['token'] ?? '';
if (empty($token)) {
echo json_encode(['success' => false, 'error' => '需要登录', 'needLogin' => true]);
exit;
}
$tokenInfo = validateTokenSilent($token);
if (!$tokenInfo) {
echo json_encode(['success' => false, 'error' => 'token无效或已过期', 'needLogin' => true]);
exit;
}
$playerName = is_array($tokenInfo) ? $tokenInfo['player'] : $tokenInfo;
} else {
// 未知action:默认放行(部分公开GET读取)
if ($method === 'POST' && !validateSecret($secret)) {
echo json_encode(['success' => false, 'error' => 'invalid secret']);
exit;
}
}
$db = getDB();
// 建表
initLandTables($db);
switch ($action) {
// ===== Java端推送领地数据 =====
case 'sync_lands':
handleSyncLands($db, $_POST);
break;
// ===== Java端推送权限商店数据 =====
case 'sync_shop':
handleSyncShop($db, $_POST);
break;
// ===== Java端推送访客权限数据 =====
case 'sync_permissions':
handleSyncPermissions($db, $_POST);
break;
// ===== 管理面板:获取所有领地 =====
case 'list_lands':
if ($method !== 'GET') {
echo json_encode(['success' => false, 'error' => 'GET only']);
exit;
}
handleListLands($db);
break;
// ===== 管理面板:获取权限商店 =====
case 'list_shop':
if ($method !== 'GET') {
echo json_encode(['success' => false, 'error' => 'GET only']);
exit;
}
handleListShop($db);
break;
// ===== 管理面板:获取配置 =====
case 'get_config':
handleGetConfig($db);
break;
// ===== 管理面板:更新配置 =====
case 'update_config':
handleUpdateConfig($db, $_POST);
break;
// ===== 管理面板:删除领地 =====
case 'delete_land':
handleDeleteLand($db, $_POST);
break;
// ===== 管理面板:更新领地所有者 =====
case 'update_land_owner':
handleUpdateLandOwner($db, $_POST);
break;
// ===== 管理面板:删除权限商品 =====
case 'delete_shop_item':
handleDeleteShopItem($db, $_POST);
break;
// ===== 玩家端:我拥有的领地 =====
case 'my_lands':
handleMyLands($db, $playerName);
break;
// ===== 玩家端:领地详情(含访客列表)=====
case 'land_detail':
handleLandDetail($db, $playerName, $_GET['name'] ?? '');
break;
// ===== 玩家端:添加访客 =====
case 'add_visitor':
if ($method !== 'GET' && $method !== 'POST') {
echo json_encode(['success' => false, 'error' => 'GET/POST only']);
exit;
}
$req = $method === 'POST' ? $_POST : $_GET;
handleAddVisitor($db, $playerName, $req);
break;
// ===== 玩家端:移除访客 =====
case 'remove_visitor':
if ($method !== 'GET' && $method !== 'POST') {
echo json_encode(['success' => false, 'error' => 'GET/POST only']);
exit;
}
$req = $method === 'POST' ? $_POST : $_GET;
handleRemoveVisitor($db, $playerName, $req);
break;
// ===== 玩家端:访客列表 =====
case 'list_visitors':
handleListVisitors($db, $_GET['name'] ?? '');
break;
// ===== 玩家端:领地商店(购买访客权限)=====
case 'land_shop':
handleLandShop($db, $_GET['name'] ?? '');
break;
// ===== 玩家端:获取访客权限 =====
case 'get_visitor_perm':
handleGetVisitorPerm($db, $playerName, $_GET['land'] ?? '');
break;
// ===== 玩家端:更新访客权限 =====
case 'update_visitor_perm':
if ($method !== 'POST') {
echo json_encode(['success' => false, 'error' => 'POST only']);
exit;
}
handleUpdateVisitorPerm($db, $playerName, $_POST);
break;
// ===== 成员独立权限:获取成员权限列表 =====
case 'get_member_perms':
handleGetMemberPerms($db, $playerName, $_GET['land'] ?? '');
break;
// ===== 成员独立权限:更新单个权限 =====
case 'update_member_perm':
if ($method !== 'POST') {
echo json_encode(['success' => false, 'error' => 'POST only']);
exit;
}
handleUpdateMemberPerm($db, $playerName, $_POST);
break;
// ===== 成员独立权限:清除所有自定义权限 =====
case 'clear_member_perm':
if ($method !== 'POST') {
echo json_encode(['success' => false, 'error' => 'POST only']);
exit;
}
handleClearMemberPerm($db, $playerName, $_POST);
break;
// ===== Java端轮询PHP管理员变更 =====
case 'poll_admin_changes':
handlePollAdminChanges($db, $_GET);
break;
// ===== Java端确认已处理变更 =====
case 'ack_admin_changes':
handleAckAdminChanges($db, $_POST);
break;
default:
echo json_encode(['success' => false, 'error' => 'unknown action']);
}
} catch (\Throwable $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
// ==================== 函数 ====================
function initLandTables($db) {
$db->exec("CREATE TABLE IF NOT EXISTS web_area_lands (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
owner TEXT DEFAULT '',
world TEXT DEFAULT '',
x1 INTEGER DEFAULT 0, z1 INTEGER DEFAULT 0,
x2 INTEGER DEFAULT 0, z2 INTEGER DEFAULT 0,
y_min INTEGER DEFAULT 0, y_max INTEGER DEFAULT 255,
area_size INTEGER DEFAULT 0,
created_at INTEGER DEFAULT 0,
synced_at INTEGER DEFAULT 0
)");
$db->exec("CREATE TABLE IF NOT EXISTS web_area_shop (
id INTEGER PRIMARY KEY,
land_id INTEGER NOT NULL,
land_name TEXT DEFAULT '',
seller TEXT NOT NULL,
permission TEXT DEFAULT 'visitor',
price INTEGER DEFAULT 0,
duration INTEGER DEFAULT 86400,
status TEXT DEFAULT 'active',
buyer TEXT DEFAULT '',
bought_at INTEGER DEFAULT 0,
created_at INTEGER DEFAULT 0,
synced_at INTEGER DEFAULT 0
)");
$db->exec("CREATE TABLE IF NOT EXISTS web_area_config (
key TEXT PRIMARY KEY,
value TEXT DEFAULT ''
)");
// ★ 访客权限表(Java端同步过来)
$db->exec("CREATE TABLE IF NOT EXISTS web_area_permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
land_id INTEGER NOT NULL,
land_name TEXT DEFAULT '',
player_name TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'visitor',
permissions TEXT DEFAULT '',
granted_at INTEGER DEFAULT 0,
expires_at INTEGER DEFAULT 0,
synced_at INTEGER DEFAULT 0,
UNIQUE(land_id, player_name)
)");
// ★ 管理员变更队列表(PHP→Java同步)
$db->exec("CREATE TABLE IF NOT EXISTS web_admin_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
change_type TEXT NOT NULL,
target_id TEXT NOT NULL,
target_name TEXT DEFAULT '',
change_data TEXT DEFAULT '',
created_at INTEGER DEFAULT 0,
acknowledged INTEGER DEFAULT 0,
acked_at INTEGER DEFAULT 0
)");
// ★ 领地所有者变更表(记录PHP端修改的所有者变更)
$db->exec("CREATE TABLE IF NOT EXISTS web_land_owner_changes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
land_id INTEGER NOT NULL,
land_name TEXT NOT NULL,
old_owner TEXT DEFAULT '',
new_owner TEXT NOT NULL,
created_at INTEGER DEFAULT 0,
synced INTEGER DEFAULT 0
)");
}
function handleSyncLands($db, $post) {
$lands = json_decode($post['lands'] ?? '[]', true);
if (!is_array($lands)) {
echo json_encode(['success' => false, 'error' => 'invalid lands data']);
return;
}
$now = time();
$stmt = $db->prepare("INSERT OR REPLACE INTO web_area_lands
(id, name, owner, world, x1, z1, x2, z2, y_min, y_max, area_size, created_at, synced_at)
VALUES (:id, :name, :owner, :world, :x1, :z1, :x2, :z2, :ymin, :ymax, :size, :created, :synced)");
foreach ($lands as $land) {
$stmt->bindValue(':id', (int)($land['id'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':name', $land['name'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':owner', $land['owner'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':world', $land['world'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':x1', (int)($land['x1'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':z1', (int)($land['z1'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':x2', (int)($land['x2'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':z2', (int)($land['z2'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':ymin', (int)($land['y_min'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':ymax', (int)($land['y_max'] ?? 255), SQLITE3_INTEGER);
$stmt->bindValue(':size', (int)($land['area_size'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':created', (int)($land['created_at'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':synced', $now, SQLITE3_INTEGER);
$stmt->execute();
}
echo json_encode(['success' => true, 'count' => count($lands)]);
}
function handleSyncShop($db, $post) {
$items = json_decode($post['items'] ?? '[]', true);
if (!is_array($items)) {
echo json_encode(['success' => false, 'error' => 'invalid shop data']);
return;
}
$now = time();
$stmt = $db->prepare("INSERT OR REPLACE INTO web_area_shop
(id, land_id, land_name, seller, permission, price, duration, status, buyer, bought_at, created_at, synced_at)
VALUES (:id, :land_id, :land_name, :seller, :perm, :price, :dur, :status, :buyer, :bought, :created, :synced)");
foreach ($items as $item) {
$stmt->bindValue(':id', (int)($item['id'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':land_id', (int)($item['land_id'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':land_name', $item['land_name'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':seller', $item['seller'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':perm', $item['permission'] ?? 'visitor', SQLITE3_TEXT);
$stmt->bindValue(':price', (int)($item['price'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':dur', (int)($item['duration'] ?? 86400), SQLITE3_INTEGER);
$stmt->bindValue(':status', $item['status'] ?? 'active', SQLITE3_TEXT);
$stmt->bindValue(':buyer', $item['buyer'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':bought', (int)($item['bought_at'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':created', (int)($item['created_at'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':synced', $now, SQLITE3_INTEGER);
$stmt->execute();
}
echo json_encode(['success' => true, 'count' => count($items)]);
}
function handleListLands($db) {
$result = $db->query("SELECT * FROM web_area_lands ORDER BY synced_at DESC");
$lands = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$lands[] = $row;
}
echo json_encode(['success' => true, 'lands' => $lands]);
}
function handleListShop($db) {
$result = $db->query("SELECT * FROM web_area_shop ORDER BY created_at DESC LIMIT 100");
$items = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$items[] = $row;
}
echo json_encode(['success' => true, 'items' => $items]);
}
function handleGetConfig($db) {
$config = [];
$result = $db->query("SELECT * FROM web_area_config");
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$config[$row['key']] = $row['value'];
}
echo json_encode(['success' => true, 'config' => $config]);
}
function handleUpdateConfig($db, $post) {
$key = $post['key'] ?? '';
$value = $post['value'] ?? '';
if (empty($key)) {
echo json_encode(['success' => false, 'error' => 'missing key']);
return;
}
$stmt = $db->prepare("INSERT OR REPLACE INTO web_area_config (key, value) VALUES (:key, :val)");
$stmt->bindValue(':key', $key, SQLITE3_TEXT);
$stmt->bindValue(':val', $value, SQLITE3_TEXT);
$stmt->execute();
echo json_encode(['success' => true]);
}
function handleDeleteLand($db, $post) {
$name = $post['name'] ?? '';
if (empty($name)) {
echo json_encode(['success' => false, 'error' => 'missing name']);
return;
}
$stmt = $db->prepare("DELETE FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
$stmt->execute();
echo json_encode(['success' => true]);
}
function handleUpdateLandOwner($db, $post) {
$name = $post['name'] ?? '';
$owner = $post['owner'] ?? '';
if (empty($name)) {
echo json_encode(['success' => false, 'error' => 'missing name']);
return;
}
// 获取旧所有者
$stmt = $db->prepare("SELECT owner FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$oldOwner = $row ? ($row['owner'] ?? '') : '';
// 更新所有者
$stmt2 = $db->prepare("UPDATE web_area_lands SET owner = :owner WHERE name = :name");
$stmt2->bindValue(':owner', $owner, SQLITE3_TEXT);
$stmt2->bindValue(':name', $name, SQLITE3_TEXT);
$stmt2->execute();
// 记录变更(用于同步到Java)
if ($oldOwner !== $owner) {
$landId = (int)($post['id'] ?? 0);
$stmt3 = $db->prepare("INSERT INTO web_land_owner_changes (land_id, land_name, old_owner, new_owner, created_at) VALUES (:lid, :name, :old, :new, :now)");
$stmt3->bindValue(':lid', $landId, SQLITE3_INTEGER);
$stmt3->bindValue(':name', $name, SQLITE3_TEXT);
$stmt3->bindValue(':old', $oldOwner, SQLITE3_TEXT);
$stmt3->bindValue(':new', $owner, SQLITE3_TEXT);
$stmt3->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt3->execute();
// 同时添加到变更队列
$stmt4 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) VALUES ('owner_change', :id, :name, :data, :now)");
$stmt4->bindValue(':id', (int)($post['id'] ?? 0), SQLITE3_INTEGER);
$stmt4->bindValue(':name', $name, SQLITE3_TEXT);
$stmt4->bindValue(':data', json_encode(['old_owner' => $oldOwner, 'new_owner' => $owner]), SQLITE3_TEXT);
$stmt4->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt4->execute();
}
echo json_encode(['success' => true]);
}
function handleDeleteShopItem($db, $post) {
$id = (int)($post['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'error' => 'invalid id']);
return;
}
$stmt = $db->prepare("UPDATE web_area_shop SET status = 'removed' WHERE id = :id");
$stmt->bindValue(':id', $id, SQLITE3_INTEGER);
$stmt->execute();
echo json_encode(['success' => true]);
}
// ==================== Java端同步:访客权限 ====================
function handleSyncPermissions($db, $post) {
$perms = json_decode($post['permissions'] ?? '[]', true);
if (!is_array($perms)) {
echo json_encode(['success' => false, 'error' => 'invalid permissions data']);
return;
}
$now = time();
$stmt = $db->prepare("INSERT OR REPLACE INTO web_area_permissions
(land_id, land_name, player_name, role, permissions, granted_at, expires_at, synced_at)
VALUES (:land_id, :land_name, :player, :role, :perms, :granted, :expires, :synced)");
foreach ($perms as $p) {
$stmt->bindValue(':land_id', (int)($p['land_id'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':land_name', $p['land_name'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':player', $p['player_name'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':role', $p['role'] ?? 'visitor', SQLITE3_TEXT);
$stmt->bindValue(':perms', $p['permissions'] ?? '', SQLITE3_TEXT);
$stmt->bindValue(':granted', (int)($p['granted_at'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':expires', (int)($p['expires_at'] ?? 0), SQLITE3_INTEGER);
$stmt->bindValue(':synced', $now, SQLITE3_INTEGER);
$stmt->execute();
}
echo json_encode(['success' => true, 'count' => count($perms)]);
}
// ==================== 玩家端:我拥有的领地 ====================
function handleMyLands($db, $playerName) {
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE owner = :owner ORDER BY created_at DESC");
$stmt->bindValue(':owner', $playerName, SQLITE3_TEXT);
$result = $stmt->execute();
$lands = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$lands[] = $row;
}
echo json_encode(['success' => true, 'lands' => $lands, 'player' => $playerName]);
}
// ==================== 玩家端:领地详情(含访客) ====================
function handleLandDetail($db, $playerName, $landName) {
if (empty($landName)) {
echo json_encode(['success' => false, 'error' => '缺少领地名称']);
return;
}
// 获取领地信息
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 权限检查:只有所有者或管理员能查看详情
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '你不是此领地的所有者']);
return;
}
// 获取访客列表
$stmt2 = $db->prepare("SELECT * FROM web_area_permissions WHERE land_id = :land_id ORDER BY granted_at DESC");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$result2 = $stmt2->execute();
$visitors = [];
while ($row = $result2->fetchArray(SQLITE3_ASSOC)) {
$visitors[] = $row;
}
echo json_encode(['success' => true, 'land' => $land, 'visitors' => $visitors]);
}
// ==================== 玩家端:添加访客 ====================
function handleAddVisitor($db, $playerName, $req) {
$landName = $req['name'] ?? $req['land'] ?? '';
$visitor = $req['visitor'] ?? $req['player'] ?? '';
if (empty($landName) || empty($visitor)) {
echo json_encode(['success' => false, 'error' => '缺少领地名或玩家名']);
return;
}
// ★ 校验玩家名格式(至少3字符,只含字母数字下划线)
if (!preg_match('/^[a-zA-Z0-9_]{3,16}$/', $visitor)) {
echo json_encode(['success' => false, 'error' => '无效的玩家名: ' . $visitor]);
return;
}
// ★ 校验玩家是否存在于数据库(login.db或web用户表)
if (!playerExists($db, $visitor)) {
echo json_encode(['success' => false, 'error' => '玩家不存在: ' . $visitor . ',请确认玩家已注册']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能添加访客']);
return;
}
// 插入访客
$stmt2 = $db->prepare("INSERT OR REPLACE INTO web_area_permissions
(land_id, land_name, player_name, role, permissions, granted_at, expires_at, synced_at)
VALUES (:land_id, :land_name, :player, 'visitor', '', :now, 0, :now)");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':land_name', $landName, SQLITE3_TEXT);
$stmt2->bindValue(':player', $visitor, SQLITE3_TEXT);
$stmt2->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt2->execute();
echo json_encode(['success' => true, 'message' => "已添加访客: $visitor"]);
}
/**
* ★ 检查玩家是否存在于数据库
*/
function playerExists($db, $playerName) {
// 检查web用户表
try {
$stmt = $db->prepare("SELECT COUNT(*) as cnt FROM weblogin_tokens WHERE player = :player");
$stmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row && $row['cnt'] > 0) return true;
} catch (\Throwable $e) {
// 表可能不存在,忽略
}
// 检查users表
try {
$stmt = $db->prepare("SELECT COUNT(*) as cnt FROM users WHERE player_name = :player");
$stmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row && $row['cnt'] > 0) return true;
} catch (\Throwable $e) {
// 表可能不存在,忽略
}
// 检查tokens表
try {
$stmt = $db->prepare("SELECT COUNT(*) as cnt FROM tokens WHERE player = :player");
$stmt->bindValue(':player', $playerName, SQLITE3_TEXT);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row && $row['cnt'] > 0) return true;
} catch (\Throwable $e) {
// 表可能不存在,忽略
}
return false;
}
// ==================== 玩家端:移除访客 ====================
function handleRemoveVisitor($db, $playerName, $req) {
$landName = $req['name'] ?? $req['land'] ?? '';
$visitor = $req['visitor'] ?? $req['player'] ?? '';
if (empty($landName) || empty($visitor)) {
echo json_encode(['success' => false, 'error' => '缺少领地名或玩家名']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能移除访客']);
return;
}
$stmt2 = $db->prepare("DELETE FROM web_area_permissions WHERE land_id = :land_id AND player_name = :player");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':player', $visitor, SQLITE3_TEXT);
$stmt2->execute();
echo json_encode(['success' => true, 'message' => "已移除访客: $visitor"]);
}
// ==================== 玩家端:访客列表 ====================
function handleListVisitors($db, $landName) {
if (empty($landName)) {
echo json_encode(['success' => false, 'error' => '缺少领地名称']);
return;
}
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
$stmt2 = $db->prepare("SELECT * FROM web_area_permissions WHERE land_id = :land_id ORDER BY granted_at DESC");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$result2 = $stmt2->execute();
$visitors = [];
while ($row = $result2->fetchArray(SQLITE3_ASSOC)) {
$visitors[] = $row;
}
echo json_encode(['success' => true, 'visitors' => $visitors]);
}
// ==================== 玩家端:领地商店 ====================
function handleLandShop($db, $landName) {
$sql = "SELECT * FROM web_area_shop WHERE status = 'active'";
$params = [];
if (!empty($landName)) {
$sql .= " AND land_name = :name";
$params[':name'] = $landName;
}
$sql .= " ORDER BY created_at DESC LIMIT 50";
$stmt = $db->prepare($sql);
foreach ($params as $k => $v) {
$stmt->bindValue($k, $v, SQLITE3_TEXT);
}
$result = $stmt->execute();
$items = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$items[] = $row;
}
echo json_encode(['success' => true, 'items' => $items]);
}
// ==================== 玩家端:获取访客权限 ====================
function handleGetVisitorPerm($db, $playerName, $landName) {
if (empty($landName)) {
echo json_encode(['success' => false, 'error' => '缺少领地名称']);
return;
}
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 获取该玩家的权限
$stmt2 = $db->prepare("SELECT * FROM web_area_permissions WHERE land_id = :land_id AND player_name = :player");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':player', $playerName, SQLITE3_TEXT);
$result2 = $stmt2->execute();
$perm = $result2->fetchArray(SQLITE3_ASSOC);
echo json_encode(['success' => true, 'permission' => $perm]);
}
// ==================== 玩家端:更新访客权限 ====================
function handleUpdateVisitorPerm($db, $playerName, $post) {
$landName = $post['land'] ?? '';
$visitor = $post['visitor'] ?? '';
$permissions = $post['permissions'] ?? '';
$role = $post['role'] ?? 'visitor';
if (empty($landName) || empty($visitor)) {
echo json_encode(['success' => false, 'error' => '缺少领地名或玩家名']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 权限检查:只有所有者能更新权限
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能更新权限']);
return;
}
// 更新权限
$stmt2 = $db->prepare("INSERT OR REPLACE INTO web_area_permissions
(land_id, land_name, player_name, role, permissions, granted_at, expires_at, synced_at)
VALUES (:land_id, :land_name, :player, :role, :perms, :now, 0, :now)");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':land_name', $landName, SQLITE3_TEXT);
$stmt2->bindValue(':player', $visitor, SQLITE3_TEXT);
$stmt2->bindValue(':role', $role, SQLITE3_TEXT);
$stmt2->bindValue(':perms', $permissions, SQLITE3_TEXT);
$stmt2->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt2->execute();
// ★ 写入web_admin_changes以便Java同步
$stmt4 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) VALUES ('perm_change', :id, :name, :data, :now)");
$stmt4->bindValue(':id', (int)$land['id'], SQLITE3_INTEGER);
$stmt4->bindValue(':name', $visitor, SQLITE3_TEXT);
$stmt4->bindValue(':data', json_encode(['land_name' => $landName, 'permissions' => $permissions, 'role' => $role]), SQLITE3_TEXT);
$stmt4->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt4->execute();
echo json_encode(['success' => true, 'message' => "已更新 $visitor 的权限"]);
}
// ==================== 成员独立权限操作 ====================
/**
* 29种权限类型定义
*/
$PERM_TYPES = [
'denyMove' => '移动',
'denyBlockPlace' => '放置方块',
'denyBlockBreak' => '破坏方块',
'denyPVP' => 'PVP战斗',
'denyMount' => '骑乘',
'denyEnderPearl' => '末影珍珠',
'denyThrownProjectiles' => '投掷物',
'denyRaid' => '袭击',
'denyBow' => '弓箭',
'denyPotion' => '药水',
'denyFire' => '点火',
'denyFireSpread' => '火焰蔓延',
'denyPickup' => '拾取物品',
'denyDrop' => '丢弃物品',
'denyExplosion' => '爆炸',
'denyFallDamage' => '摔落伤害',
'denyHunger' => '饥饿',
'denyAllDamage' => '所有伤害',
'denyAllEffects' => '所有效果',
'denyItemFrame' => '物品展示框',
'denyRedstoneInteraction' => '红石交互',
'denyDoorInteraction' => '门禁交互',
'denyNoteblockJukebox' => '音符盒/唱片机',
'denyLead' => '拴绳',
'denyCropHarvest' => '收割作物',
'denyWoolShear' => '剪羊毛',
'denyAnimalFeeding' => '投喂动物',
'denyGlowing' => '发光效果',
'denyPeaceMode' => '和平模式'
];
function handleGetMemberPerms($db, $playerName, $landName) {
global $PERM_TYPES;
if (empty($landName)) {
echo json_encode(['success' => false, 'error' => '缺少领地名称']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 权限检查:只有所有者能编辑成员权限
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能编辑成员权限']);
return;
}
// 获取所有成员及其权限
$stmt2 = $db->prepare("SELECT * FROM web_area_permissions WHERE land_id = :land_id ORDER BY player_name");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$result2 = $stmt2->execute();
$members = [];
while ($row = $result2->fetchArray(SQLITE3_ASSOC)) {
// 解析权限JSON
$permJson = $row['permissions'] ?? '';
$perms = [];
if (!empty($permJson)) {
$decoded = json_decode($permJson, true);
if (is_array($decoded)) {
$perms = $decoded;
}
}
$row['perm_map'] = $perms;
$members[] = $row;
}
echo json_encode([
'success' => true,
'land' => $land,
'members' => $members,
'perm_types' => $PERM_TYPES
]);
}
function handleUpdateMemberPerm($db, $playerName, $post) {
$landName = $post['land'] ?? '';
$targetPlayer = $post['player'] ?? '';
$permKey = $post['perm'] ?? '';
$enabled = isset($post['enabled']) ? (bool)$post['enabled'] : false;
if (empty($landName) || empty($targetPlayer) || empty($permKey)) {
echo json_encode(['success' => false, 'error' => '缺少参数']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 权限检查
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能编辑成员权限']);
return;
}
// 获取当前权限
$stmt2 = $db->prepare("SELECT * FROM web_area_permissions WHERE land_id = :land_id AND player_name = :player");
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':player', $targetPlayer, SQLITE3_TEXT);
$result2 = $stmt2->execute();
$perm = $result2->fetchArray(SQLITE3_ASSOC);
if (!$perm) {
echo json_encode(['success' => false, 'error' => '该玩家不是领地成员']);
return;
}
// 解析现有权限
$permMap = [];
$permJson = $perm['permissions'] ?? '';
if (!empty($permJson)) {
$decoded = json_decode($permJson, true);
if (is_array($decoded)) {
$permMap = $decoded;
}
}
// 更新权限
if ($enabled) {
$permMap[$permKey] = true;
} else {
unset($permMap[$permKey]);
}
// 写回数据库
$newJson = json_encode($permMap, JSON_UNESCAPED_UNICODE);
$stmt3 = $db->prepare("UPDATE web_area_permissions SET permissions = :perms, synced_at = :now WHERE land_id = :land_id AND player_name = :player");
$stmt3->bindValue(':perms', $newJson, SQLITE3_TEXT);
$stmt3->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt3->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt3->bindValue(':player', $targetPlayer, SQLITE3_TEXT);
$stmt3->execute();
echo json_encode(['success' => true, 'message' => "已更新 $targetPlayer 的 $permKey 权限"]);
}
function handleClearMemberPerm($db, $playerName, $post) {
$landName = $post['land'] ?? '';
$targetPlayer = $post['player'] ?? '';
if (empty($landName) || empty($targetPlayer)) {
echo json_encode(['success' => false, 'error' => '缺少参数']);
return;
}
// 获取领地
$stmt = $db->prepare("SELECT * FROM web_area_lands WHERE name = :name");
$stmt->bindValue(':name', $landName, SQLITE3_TEXT);
$result = $stmt->execute();
$land = $result->fetchArray(SQLITE3_ASSOC);
if (!$land) {
echo json_encode(['success' => false, 'error' => '领地不存在']);
return;
}
// 权限检查
if ($land['owner'] !== $playerName) {
echo json_encode(['success' => false, 'error' => '只有领地所有者才能清除成员权限']);
return;
}
// 获取旧权限
$stmt_old = $db->prepare("SELECT permissions FROM web_area_permissions WHERE land_id = :land_id AND player_name = :player");
$stmt_old->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt_old->bindValue(':player', $targetPlayer, SQLITE3_TEXT);
$result_old = $stmt_old->execute();
$old_perm = $result_old->fetchArray(SQLITE3_ASSOC);
$old_perms = $old_perm ? ($old_perm['permissions'] ?? '') : '';
// 清除权限
$stmt2 = $db->prepare("UPDATE web_area_permissions SET permissions = '', synced_at = :now WHERE land_id = :land_id AND player_name = :player");
$stmt2->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt2->bindValue(':land_id', (int)$land['id'], SQLITE3_INTEGER);
$stmt2->bindValue(':player', $targetPlayer, SQLITE3_TEXT);
$stmt2->execute();
// 记录变更
if (!empty($old_perms)) {
$stmt3 = $db->prepare("INSERT INTO web_admin_changes (change_type, target_id, target_name, change_data, created_at) VALUES ('perm_clear', :id, :name, :data, :now)");
$stmt3->bindValue(':id', (int)$land['id'], SQLITE3_INTEGER);
$stmt3->bindValue(':name', $targetPlayer, SQLITE3_TEXT);
$stmt3->bindValue(':data', json_encode(['land_name' => $landName, 'old_permissions' => $old_perms]), SQLITE3_TEXT);
$stmt3->bindValue(':now', time(), SQLITE3_INTEGER);
$stmt3->execute();
}
echo json_encode(['success' => true, 'message' => "已清除 $targetPlayer 的所有自定义权限"]);
}
// ==================== Java端轮询PHP管理员变更 ====================
function handlePollAdminChanges($db, $get) {
$lastId = (int)($get['last_id'] ?? 0);
$limit = min((int)($get['limit'] ?? 50), 100);
// 获取未确认的变更
$stmt = $db->prepare("SELECT * FROM web_admin_changes WHERE id > :last_id AND acknowledged = 0 ORDER BY id ASC LIMIT :limit");
$stmt->bindValue(':last_id', $lastId, SQLITE3_INTEGER);
$stmt->bindValue(':limit', $limit, SQLITE3_INTEGER);
$result = $stmt->execute();
$changes = [];
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$changes[] = $row;
}
echo json_encode(['success' => true, 'changes' => $changes, 'count' => count($changes)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
function handleAckAdminChanges($db, $post) {
$ids = $post['ids'] ?? ($_GET['ids'] ?? '');
if (empty($ids)) {
echo json_encode(['success' => false, 'error' => 'missing ids']);
return;
}
$idList = array_filter(array_map('intval', explode(',', $ids)));
if (empty($idList)) {
echo json_encode(['success' => false, 'error' => 'invalid ids']);
return;
}
$now = time();
$placeholders = implode(',', array_fill(0, count($idList), '?'));
$stmt = $db->prepare("UPDATE web_admin_changes SET acknowledged = 1, acked_at = :now WHERE id IN ($placeholders)");
$stmt->bindValue(':now', $now, SQLITE3_INTEGER);
$i = 1;
foreach ($idList as $id) {
$stmt->bindValue($i++, $id, SQLITE3_INTEGER);
}
$stmt->execute();
echo json_encode(['success' => true, 'acked' => count($idList)]);
}
function validateSecret($secret) {
if (empty($secret)) return false;
$validSecrets = [
'sdf1_web_comm_2026_ypshidifu'
];
return in_array($secret, $validSecrets);
}