运营级区域复制脚本『幻邃独家』

-- ==========================================
-- 区域复制 V2.2 by幻邃
-- 功能说明:支持多模式区域选择、旋转/镜像粘贴、方块过滤、撤销回溯、模板管理、权限管控
-- 适配环境:开发者2.0
-- ==========================================

-- 全局命名空间隔离,避免全局变量污染
local AreaCopy = {
    version = "2.2.0",
    -- 常量枚举定义,消除魔法数字,提升可维护性
    ENUM = {
        -- 区域记录模式
        RECORD_MODE = {
            CHAT_CMD = 1,
            BLOCK_PLACE = 2,
            ITEM_CLICK = 3
        },
        -- 方块过滤模式
        FILTER_MODE = {
            ALL = 0,
            WHITELIST = 1,
            BLACKLIST = 2
        },
        -- 旋转角度枚举
        ROTATION = {
            ANGLE_0 = 0,
            ANGLE_90 = 90,
            ANGLE_180 = 180,
            ANGLE_270 = 270
        },
        -- 变量库类型枚举(匹配官方API定义)
        VARTYPE = {
            POSITION = 1,
            AREA = 2,
            NUMBER = 3,
            STRING = 4,
            BOOLEAN = 5,
            NUMBER_GROUP = 17,
            STRING_GROUP = 18
        },
        -- 命令权限等级
        CMD_PERMISSION = {
            GUEST = 0,
            ADMIN = 1
        },
        -- 游戏对象类型枚举(匹配官方API定义)
        OBJ_TYPE = {
            PLAYER = 1,
            CREATURE = 2,
            DROP_ITEM = 3,
            PROJECTILE = 4
        },
        -- 方块朝向枚举(匹配官方API定义)
        FACE_DIR = {
            WEST = 0,
            EAST = 1,
            SOUTH = 2,
            NORTH = 3,
            DOWN = 4,
            UP = 5
        }
    },
    -- 模块容器
    Modules = {},
    -- 运行时数据
    Runtime = {
        isInited = false,
        playerData = {}, -- 玩家维度全量数据统一管理
        copyTaskLocks = {}, -- 复制任务并发锁
        config = {} -- 运行时配置
    }
}

-- ==========================================
-- 配置管理模块
-- 职责:配置的初始化、读写、合并、持久化、深拷贝
-- ==========================================
AreaCopy.Modules.Config = {
    -- 默认配置规约
    defaultConfig = {
        record = {
            mode = AreaCopy.ENUM.RECORD_MODE.CHAT_CMD,
            blockId = 101,
            itemId = 1001
        },
        copy = {
            enableRotate = true,
            enableMirror = true,
            defaultRotation = AreaCopy.ENUM.ROTATION.ANGLE_0,
            defaultMirror = false,
            copyDelay = 100, -- 单位:ms,适配引擎最小等待粒度
            batchSize = 64 -- 单批次方块放置数量,平衡性能与帧率
        },
        filter = {
            mode = AreaCopy.ENUM.FILTER_MODE.ALL,
            whitelist = {},
            blacklist = {},
            copyAir = false
        },
        permission = {
            enableCheck = true,
            ownerIsAdmin = true,
            admins = {},
            blacklist = {}
        },
        special = {
            enableUndo = true,
            undoMaxSteps = 10,
            enablePreview = true,
            previewBlockId = 95,
            enableTemplate = true,
            maxTemplates = 20
        },
        others = {
            showTips = true
        }
    },
    configKey = "AreaCopy_Config", -- 存档配置存储键名
    isInited = false
}

-- 深拷贝工具函数,避免引用类型数据污染
function AreaCopy.Modules.Config:DeepCopy(src)
    local copy = {}
    if type(src) ~= "table" then
        return src
    end
    for k, v in pairs(src) do
        copy[k] = self:DeepCopy(v)
    end
    return copy
end

-- 配置递归合并,严格校验类型匹配,避免配置结构损坏
function AreaCopy.Modules.Config:MergeConfig(defaultTbl, customTbl)
    if type(defaultTbl) ~= "table" or type(customTbl) ~= "table" then
        return
    end
    for k, v in pairs(customTbl) do
        local defaultVal = defaultTbl[k]
        if defaultVal == nil then
            goto continue
        end
        if type(defaultVal) == "table" and type(v) == "table" then
            self:MergeConfig(defaultVal, v)
        elseif type(defaultVal) == type(v) then
            defaultTbl[k] = v
        end
        ::continue::
    end
end

-- 模块初始化
function AreaCopy.Modules.Config:Init()
    if self.isInited then return end
    -- 加载默认配置
    AreaCopy.Runtime.config = self:DeepCopy(self.defaultConfig)
    -- 从存档读取持久化配置
    self:LoadFromArchive()
    self.isInited = true
    print(string.format("[AreaCopy] 配置模块初始化完成,配置版本:%s", AreaCopy.version))
end

-- 从存档加载配置
function AreaCopy.Modules.Config:LoadFromArchive()
    local result, saveData = VarLib2:getGlobalVarByName(AreaCopy.ENUM.VARTYPE.STRING, self.configKey)
    if result ~= 0 or not saveData then
        print("[AreaCopy] 无存档配置,使用默认配置")
        return
    end
    -- JSON解码异常捕获
    local decodeOk, loadData = pcall(JSON.decode, JSON, saveData)
    if not decodeOk or type(loadData) ~= "table" then
        print("[AreaCopy] 存档配置解析失败,已重置为默认配置")
        return
    end
    -- 合并配置
    self:MergeConfig(AreaCopy.Runtime.config, loadData)
    print("[AreaCopy] 存档配置加载成功")
end

-- 保存配置到存档
function AreaCopy.Modules.Config:SaveToArchive()
    local encodeOk, jsonData = pcall(JSON.encode, JSON, AreaCopy.Runtime.config)
    if not encodeOk then
        print("[AreaCopy] 配置序列化失败")
        return false
    end
    local result = VarLib2:setGlobalVarByName(AreaCopy.ENUM.VARTYPE.STRING, self.configKey, jsonData)
    return result == 0
end

-- 重置配置为默认值
function AreaCopy.Modules.Config:Reset()
    AreaCopy.Runtime.config = self:DeepCopy(self.defaultConfig)
    return self:SaveToArchive()
end

-- 获取配置项
function AreaCopy.Modules.Config:Get(module, key)
    local moduleTbl = AreaCopy.Runtime.config[module]
    if not moduleTbl then return nil end
    return moduleTbl[key]
end

-- 设置配置项
function AreaCopy.Modules.Config:Set(module, key, value)
    local moduleTbl = AreaCopy.Runtime.config[module]
    if not moduleTbl then
        AreaCopy.Runtime.config[module] = {}
        moduleTbl = AreaCopy.Runtime.config[module]
    end
    moduleTbl[key] = value
end

-- ==========================================
-- 权限管理模块
-- 职责:管理员/黑名单校验、权限操作、权限生命周期管理
-- ==========================================
AreaCopy.Modules.Permission = {
    isInited = false
}

function AreaCopy.Modules.Permission:Init()
    if self.isInited then return end
    self.isInited = true
    print("[AreaCopy] 权限模块初始化完成")
end

-- 玩家黑名单校验(优先级最高,无视权限开关)
function AreaCopy.Modules.Permission:IsInBlacklist(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin then return false end
    local blacklist = AreaCopy.Modules.Config:Get("permission", "blacklist") or {}
    for _, uin in ipairs(blacklist) do
        if tonumber(uin) == playerUin then
            return true
        end
    end
    return false
end

-- 玩家管理员身份校验
function AreaCopy.Modules.Permission:IsAdmin(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin then return false end
    -- 房主自动管理员校验
    if AreaCopy.Modules.Config:Get("permission", "ownerIsAdmin") then
        local result, hostUin = Player:getHostUin()
        if result == 0 and playerUin == tonumber(hostUin) then
            return true
        end
    end
    -- 管理员列表校验
    local adminList = AreaCopy.Modules.Config:Get("permission", "admins") or {}
    for _, uin in ipairs(adminList) do
        if tonumber(uin) == playerUin then
            return true
        end
    end
    return false
end

-- 命令执行权限校验
function AreaCopy.Modules.Permission:CheckCommandPermission(playerUin, requireAdmin)
    -- 黑名单拦截
    if self:IsInBlacklist(playerUin) then
        return false, "你已被加入黑名单,无法使用该命令"
    end
    -- 无需管理员权限,直接放行
    if not requireAdmin then
        return true
    end
    -- 权限开关校验
    if not AreaCopy.Modules.Config:Get("permission", "enableCheck") then
        return true
    end
    -- 管理员身份校验
    if self:IsAdmin(playerUin) then
        return true
    end
    return false, "你没有权限执行该命令"
end

-- 添加管理员
function AreaCopy.Modules.Permission:AddAdmin(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin or self:IsAdmin(playerUin) then
        return false
    end
    local adminList = AreaCopy.Modules.Config:Get("permission", "admins") or {}
    table.insert(adminList, playerUin)
    AreaCopy.Modules.Config:Set("permission", "admins", adminList)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 移除管理员
function AreaCopy.Modules.Permission:RemoveAdmin(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin then return false end
    -- 禁止移除房主管理员权限
    local result, hostUin = Player:getHostUin()
    if result == 0 and playerUin == tonumber(hostUin) then
        return false
    end
    local adminList = AreaCopy.Modules.Config:Get("permission", "admins") or {}
    local newAdminList = {}
    local isRemoved = false
    for _, uin in ipairs(adminList) do
        if tonumber(uin) ~= playerUin then
            table.insert(newAdminList, uin)
        else
            isRemoved = true
        end
    end
    if not isRemoved then return false end
    AreaCopy.Modules.Config:Set("permission", "admins", newAdminList)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取管理员列表
function AreaCopy.Modules.Permission:GetAdminList()
    return AreaCopy.Modules.Config:Get("permission", "admins") or {}
end

-- 添加玩家到黑名单
function AreaCopy.Modules.Permission:AddBlacklist(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin or self:IsInBlacklist(playerUin) then
        return false
    end
    -- 禁止拉黑房主
    local result, hostUin = Player:getHostUin()
    if result == 0 and playerUin == tonumber(hostUin) then
        return false
    end
    local blacklist = AreaCopy.Modules.Config:Get("permission", "blacklist") or {}
    table.insert(blacklist, playerUin)
    AreaCopy.Modules.Config:Set("permission", "blacklist", blacklist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 从黑名单移除玩家
function AreaCopy.Modules.Permission:RemoveBlacklist(playerUin)
    playerUin = tonumber(playerUin)
    if not playerUin then return false end
    local blacklist = AreaCopy.Modules.Config:Get("permission", "blacklist") or {}
    local newBlacklist = {}
    local isRemoved = false
    for _, uin in ipairs(blacklist) do
        if tonumber(uin) ~= playerUin then
            table.insert(newBlacklist, uin)
        else
            isRemoved = true
        end
    end
    if not isRemoved then return false end
    AreaCopy.Modules.Config:Set("permission", "blacklist", newBlacklist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取黑名单列表
function AreaCopy.Modules.Permission:GetBlacklist()
    return AreaCopy.Modules.Config:Get("permission", "blacklist") or {}
end

-- ==========================================
-- 区域记录模块
-- 职责:多模式区域坐标记录、区域数据计算、预览渲染、玩家数据生命周期管理
-- ==========================================
AreaCopy.Modules.Record = {
    isInited = false
}

-- 玩家数据初始化
function AreaCopy.Modules.Record:InitPlayerData(playerUin)
    if not AreaCopy.Runtime.playerData[playerUin] then
        AreaCopy.Runtime.playerData[playerUin] = {
            selection = {}, -- 区域选择坐标
            previewBlocks = {}, -- 预览方块备份数据
            undoQueue = {}, -- 撤销队列
            copyCache = {}, -- 复制缓存数据
            templates = {} -- 玩家模板库
        }
    end
end

-- 清理玩家全量数据(玩家离开时调用)
function AreaCopy.Modules.Record:ClearPlayerData(playerUin)
    if not AreaCopy.Runtime.playerData[playerUin] then return end
    -- 清除预览方块
    self:ClearPreview(playerUin)
    -- 释放内存
    AreaCopy.Runtime.playerData[playerUin] = nil
    AreaCopy.Runtime.copyTaskLocks[playerUin] = nil
    print(string.format("[AreaCopy] 玩家%d数据已清理", playerUin))
end

-- 模块初始化
function AreaCopy.Modules.Record:Init()
    if self.isInited then return end
    -- 注册玩家放置方块事件(修复原代码事件名错误)
    ScriptSupportEvent:registerEvent([=[Block.PlaceBy]=], function(e)
        self:OnBlockPlaceEvent(e)
    end)
    -- 注册玩家道具使用事件
    ScriptSupportEvent:registerEvent([=[Player.UseItem]=], function(e)
        self:OnItemUseEvent(e)
    end)
    -- 注册玩家离开游戏事件(统一数据清理入口)
    ScriptSupportEvent:registerEvent([=[Game.AnyPlayer.LeaveGame]=], function(e)
        self:ClearPlayerData(e.eventobjid)
    end)
    self.isInited = true
    print("[AreaCopy] 区域记录模块初始化完成")
end

-- 设置记录模式
function AreaCopy.Modules.Record:SetMode(mode)
    mode = tonumber(mode)
    if not mode or not AreaCopy.ENUM.RECORD_MODE[mode] then
        return false
    end
    AreaCopy.Modules.Config:Set("record", "mode", mode)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取当前记录模式
function AreaCopy.Modules.Record:GetMode()
    return AreaCopy.Modules.Config:Get("record", "mode") or AreaCopy.ENUM.RECORD_MODE.CHAT_CMD
end

-- 记录区域坐标点
function AreaCopy.Modules.Record:RecordPosition(playerUin, posIndex, x, y, z)
    self:InitPlayerData(playerUin)
    -- 坐标自动补全:未传入坐标则使用玩家当前位置
    if not x or not y or not z then
        local result, px, py, pz = Player:getPosition(playerUin)
        if result ~= 0 then
            return false, "无法获取玩家当前位置,请手动输入坐标"
        end
        x, y, z = math.floor(px), math.floor(py), math.floor(pz)
    else
        x, y, z = math.floor(x), math.floor(y), math.floor(z)
    end
    -- 坐标合法性校验
    if y < 0 or y > 256 then
        return false, "高度坐标超出合法范围(0-256)"
    end
    -- 记录坐标点
    local selection = AreaCopy.Runtime.playerData[playerUin].selection
    if posIndex == 1 then
        selection.pos1 = {x = x, y = y, z = z}
    else
        selection.pos2 = {x = x, y = y, z = z}
    end
    -- 双点记录完成,计算区域信息
    if selection.pos1 and selection.pos2 then
        local areaInfo = self:CalcPlayerArea(playerUin)
        local volume = areaInfo.sizeX * areaInfo.sizeY * areaInfo.sizeZ
        return true, string.format("区域选择完成,尺寸:%d×%d×%d,总计%d个方块",
            areaInfo.sizeX, areaInfo.sizeY, areaInfo.sizeZ, volume)
    end
    return true, string.format("已记录%d号坐标点:(%d, %d, %d)", posIndex, x, y, z)
end

-- 方块放置事件处理
function AreaCopy.Modules.Record:OnBlockPlaceEvent(e)
    local playerUin = e.eventobjid
    local currentMode = self:GetMode()
    -- 非方块放置模式直接拦截
    if currentMode ~= AreaCopy.ENUM.RECORD_MODE.BLOCK_PLACE then
        return
    end
    -- 校验记录方块ID
    local recordBlockId = AreaCopy.Modules.Config:Get("record", "blockId")
    if e.blockid ~= recordBlockId then
        return
    end
    -- 记录坐标
    local selection = AreaCopy.Runtime.playerData[playerUin] and AreaCopy.Runtime.playerData[playerUin].selection
    local posIndex = (not selection or not selection.pos1) and 1 or 2
    local success, msg = self:RecordPosition(playerUin, posIndex, e.x, e.y, e.z)
    if msg then
        Chat:sendSystemMsg(msg, playerUin)
    end
    -- 销毁记录用方块,不生成掉落物
    Block:destroyBlock(e.x, e.y, e.z, false)
end

-- 道具使用事件处理
function AreaCopy.Modules.Record:OnItemUseEvent(e)
    local playerUin = e.eventobjid
    local currentMode = self:GetMode()
    -- 非道具点击模式直接拦截
    if currentMode ~= AreaCopy.ENUM.RECORD_MODE.ITEM_CLICK then
        return
    end
    -- 校验记录道具ID
    local recordItemId = AreaCopy.Modules.Config:Get("record", "itemId")
    if e.itemid ~= recordItemId then
        return
    end
    -- 获取玩家准星指向坐标
    local result, x, y, z = Player:getAimPos(playerUin)
    if result ~= 0 then
        Chat:sendSystemMsg("无法获取准星指向坐标", playerUin)
        return
    end
    -- 记录坐标
    local selection = AreaCopy.Runtime.playerData[playerUin] and AreaCopy.Runtime.playerData[playerUin].selection
    local posIndex = (not selection or not selection.pos1) and 1 or 2
    local success, msg = self:RecordPosition(playerUin, posIndex, x, y, z)
    if msg then
        Chat:sendSystemMsg(msg, playerUin)
    end
end

-- 计算玩家选择的区域边界数据
function AreaCopy.Modules.Record:CalcPlayerArea(playerUin)
    local selection = AreaCopy.Runtime.playerData[playerUin] and AreaCopy.Runtime.playerData[playerUin].selection
    if not selection or not selection.pos1 or not selection.pos2 then
        return nil
    end
    -- 计算区域最小/最大边界
    local minX, maxX = math.min(selection.pos1.x, selection.pos2.x), math.max(selection.pos1.x, selection.pos2.x)
    local minY, maxY = math.min(selection.pos1.y, selection.pos2.y), math.max(selection.pos1.y, selection.pos2.y)
    local minZ, maxZ = math.min(selection.pos1.z, selection.pos2.z), math.max(selection.pos1.z, selection.pos2.z)
    -- 返回标准化区域数据
    return {
        minX = minX, maxX = maxX,
        minY = minY, maxY = maxY,
        minZ = minZ, maxZ = maxZ,
        sizeX = maxX - minX + 1,
        sizeY = maxY - minY + 1,
        sizeZ = maxZ - minZ + 1,
        centerX = (minX + maxX) / 2,
        centerZ = (minZ + maxZ) / 2
    }
end

-- 清除玩家区域选择数据
function AreaCopy.Modules.Record:ClearSelection(playerUin)
    if not AreaCopy.Runtime.playerData[playerUin] then return false end
    -- 先清除预览
    self:ClearPreview(playerUin)
    -- 重置选择数据
    AreaCopy.Runtime.playerData[playerUin].selection = {}
    return true
end

-- 绘制区域预览边框(仅空气位置渲染,避免覆盖原有方块)
function AreaCopy.Modules.Record:DrawPreview(playerUin)
    if not AreaCopy.Modules.Config:Get("special", "enablePreview") then
        return false, "预览功能已禁用"
    end
    local areaInfo = self:CalcPlayerArea(playerUin)
    if not areaInfo then
        return false, "请先完成区域双点选择"
    end
    -- 清除旧预览
    self:ClearPreview(playerUin)
    local previewBlockId = AreaCopy.Modules.Config:Get("special", "previewBlockId")
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    playerData.previewBlocks = {}
    -- 边框线段绘制函数
    local function drawLine(x1, y1, z1, x2, y2, z2)
        local dx = x2 - x1
        local dy = y2 - y1
        local dz = z2 - z1
        local steps = math.max(math.abs(dx), math.abs(dy), math.abs(dz))
        for i = 0, steps do
            local x = math.floor(x1 + dx * i / steps)
            local y = math.floor(y1 + dy * i / steps)
            local z = math.floor(z1 + dz * i / steps)
            -- 坐标合法性校验
            if y < 0 or y > 256 then
                goto continue
            end
            -- 仅在空气位置渲染预览方块,避免覆盖原有方块
            local result, blockId = Block:getBlockID(x, y, z)
            if result ~= 0 or blockId ~= 0 then
                goto continue
            end
            -- 记录原方块数据
            table.insert(playerData.previewBlocks, {
                x = x, y = y, z = z,
                oldId = blockId, oldData = 0
            })
            -- 放置预览方块
            Block:setBlockAll(x, y, z, previewBlockId, 0)
            ::continue::
        end
    end
    -- 绘制区域12条边框线
    drawLine(areaInfo.minX, areaInfo.minY, areaInfo.minZ, areaInfo.maxX, areaInfo.minY, areaInfo.minZ)
    drawLine(areaInfo.minX, areaInfo.maxY, areaInfo.minZ, areaInfo.maxX, areaInfo.maxY, areaInfo.minZ)
    drawLine(areaInfo.minX, areaInfo.minY, areaInfo.maxZ, areaInfo.maxX, areaInfo.minY, areaInfo.maxZ)
    drawLine(areaInfo.minX, areaInfo.maxY, areaInfo.maxZ, areaInfo.maxX, areaInfo.maxY, areaInfo.maxZ)
    
    drawLine(areaInfo.minX, areaInfo.minY, areaInfo.minZ, areaInfo.minX, areaInfo.maxY, areaInfo.minZ)
    drawLine(areaInfo.maxX, areaInfo.minY, areaInfo.minZ, areaInfo.maxX, areaInfo.maxY, areaInfo.minZ)
    drawLine(areaInfo.minX, areaInfo.minY, areaInfo.maxZ, areaInfo.minX, areaInfo.maxY, areaInfo.maxZ)
    drawLine(areaInfo.maxX, areaInfo.minY, areaInfo.maxZ, areaInfo.maxX, areaInfo.maxY, areaInfo.maxZ)
    
    drawLine(areaInfo.minX, areaInfo.minY, areaInfo.minZ, areaInfo.minX, areaInfo.minY, areaInfo.maxZ)
    drawLine(areaInfo.maxX, areaInfo.minY, areaInfo.minZ, areaInfo.maxX, areaInfo.minY, areaInfo.maxZ)
    drawLine(areaInfo.minX, areaInfo.maxY, areaInfo.minZ, areaInfo.minX, areaInfo.maxY, areaInfo.maxZ)
    drawLine(areaInfo.maxX, areaInfo.maxY, areaInfo.minZ, areaInfo.maxX, areaInfo.maxY, areaInfo.maxZ)
    return true, "区域预览边框已渲染"
end

-- 清除玩家预览方块,还原场景
function AreaCopy.Modules.Record:ClearPreview(playerUin)
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    if not playerData or not playerData.previewBlocks or #playerData.previewBlocks == 0 then
        return false
    end
    -- 还原原方块数据
    for _, block in ipairs(playerData.previewBlocks) do
        Block:setBlockAll(block.x, block.y, block.z, block.oldId, block.oldData)
    end
    -- 清空预览数据
    playerData.previewBlocks = {}
    return true
end

-- ==========================================
-- 方块过滤模块
-- 职责:方块过滤规则管理、过滤校验、预设管理
-- ==========================================
AreaCopy.Modules.Filter = {
    -- 常用方块预设组
    presetGroups = {
        建筑 = {1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 46, 47, 101},
        矿石 = {10, 11, 14, 15, 16, 41, 42, 43, 44, 45},
        电路 = {58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80},
        自然 = {2, 3, 4, 5, 6, 8, 9, 12, 13, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
        功能 = {53, 54, 55, 56, 57, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}
    },
    -- 方块ID-名称映射表
    blockNameMap = {
        [0] = "空气", [1] = "基岩", [2] = "草方块", [3] = "泥土", [4] = "圆石",
        [5] = "木板", [10] = "煤矿石", [11] = "铁矿石", [12] = "沙子", [13] = "沙砾",
        [14] = "金矿石", [15] = "钻石矿石", [16] = "青金石矿石", [41] = "金块",
        [42] = "铁块", [43] = "钻石块", [45] = "青金石块", [46] = "TNT", [47] = "蜘蛛网", [101] = "土块"
    },
    isInited = false
}

function AreaCopy.Modules.Filter:Init()
    if self.isInited then return end
    self.isInited = true
    print("[AreaCopy] 过滤模块初始化完成")
end

-- 设置过滤模式
function AreaCopy.Modules.Filter:SetMode(mode)
    mode = tonumber(mode)
    if not mode or not AreaCopy.ENUM.FILTER_MODE[mode] then
        return false
    end
    AreaCopy.Modules.Config:Set("filter", "mode", mode)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取当前过滤模式
function AreaCopy.Modules.Filter:GetMode()
    return AreaCopy.Modules.Config:Get("filter", "mode") or AreaCopy.ENUM.FILTER_MODE.ALL
end

-- 设置是否复制空气方块
function AreaCopy.Modules.Filter:SetCopyAir(enable)
    AreaCopy.Modules.Config:Set("filter", "copyAir", enable)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取是否复制空气方块
function AreaCopy.Modules.Filter:GetCopyAir()
    return AreaCopy.Modules.Config:Get("filter", "copyAir") or false
end

-- 方块过滤校验核心函数
function AreaCopy.Modules.Filter:CheckBlockValid(blockId)
    local filterMode = self:GetMode()
    local copyAir = self:GetCopyAir()
    -- 空气方块特殊处理
    if blockId == 0 then
        return copyAir
    end
    -- 全量复制模式
    if filterMode == AreaCopy.ENUM.FILTER_MODE.ALL then
        return true
    end
    -- 白名单模式
    if filterMode == AreaCopy.ENUM.FILTER_MODE.WHITELIST then
        local whitelist = AreaCopy.Modules.Config:Get("filter", "whitelist") or {}
        for _, id in ipairs(whitelist) do
            if id == blockId then
                return true
            end
        end
        return false
    end
    -- 黑名单模式
    if filterMode == AreaCopy.ENUM.FILTER_MODE.BLACKLIST then
        local blacklist = AreaCopy.Modules.Config:Get("filter", "blacklist") or {}
        for _, id in ipairs(blacklist) do
            if id == blockId then
                return false
            end
        end
        return true
    end
    return true
end

-- 添加方块到白名单
function AreaCopy.Modules.Filter:AddToWhitelist(blockId)
    blockId = math.floor(tonumber(blockId) or -1)
    if blockId < 0 then return false end
    local whitelist = AreaCopy.Modules.Config:Get("filter", "whitelist") or {}
    -- 去重校验
    for _, id in ipairs(whitelist) do
        if id == blockId then
            return false
        end
    end
    table.insert(whitelist, blockId)
    AreaCopy.Modules.Config:Set("filter", "whitelist", whitelist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 从白名单移除方块
function AreaCopy.Modules.Filter:RemoveFromWhitelist(blockId)
    blockId = math.floor(tonumber(blockId) or -1)
    if blockId < 0 then return false end
    local whitelist = AreaCopy.Modules.Config:Get("filter", "whitelist") or {}
    local newWhitelist = {}
    local isRemoved = false
    for _, id in ipairs(whitelist) do
        if id ~= blockId then
            table.insert(newWhitelist, id)
        else
            isRemoved = true
        end
    end
    if not isRemoved then return false end
    AreaCopy.Modules.Config:Set("filter", "whitelist", newWhitelist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 添加方块到黑名单
function AreaCopy.Modules.Filter:AddToBlacklist(blockId)
    blockId = math.floor(tonumber(blockId) or -1)
    if blockId < 0 then return false end
    local blacklist = AreaCopy.Modules.Config:Get("filter", "blacklist") or {}
    -- 去重校验
    for _, id in ipairs(blacklist) do
        if id == blockId then
            return false
        end
    end
    table.insert(blacklist, blockId)
    AreaCopy.Modules.Config:Set("filter", "blacklist", blacklist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 从黑名单移除方块
function AreaCopy.Modules.Filter:RemoveFromBlacklist(blockId)
    blockId = math.floor(tonumber(blockId) or -1)
    if blockId < 0 then return false end
    local blacklist = AreaCopy.Modules.Config:Get("filter", "blacklist") or {}
    local newBlacklist = {}
    local isRemoved = false
    for _, id in ipairs(blacklist) do
        if id ~= blockId then
            table.insert(newBlacklist, id)
        else
            isRemoved = true
        end
    end
    if not isRemoved then return false end
    AreaCopy.Modules.Config:Set("filter", "blacklist", newBlacklist)
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 添加预设方块组到过滤列表
function AreaCopy.Modules.Filter:AddPresetGroup(presetName, isWhitelist)
    local preset = self.presetGroups[presetName]
    if not preset then return 0 end
    local addCount = 0
    if isWhitelist then
        for _, blockId in ipairs(preset) do
            if self:AddToWhitelist(blockId) then
                addCount = addCount + 1
            end
        end
    else
        for _, blockId in ipairs(preset) do
            if self:AddToBlacklist(blockId) then
                addCount = addCount + 1
            end
        end
    end
    return addCount
end

-- 清空过滤列表
function AreaCopy.Modules.Filter:ClearList(isWhitelist)
    if isWhitelist then
        AreaCopy.Modules.Config:Set("filter", "whitelist", {})
    else
        AreaCopy.Modules.Config:Set("filter", "blacklist", {})
    end
    return AreaCopy.Modules.Config:SaveToArchive()
end

-- 获取方块名称
function AreaCopy.Modules.Filter:GetBlockName(blockId)
    return self.blockNameMap[blockId] or string.format("方块ID:%d", blockId)
end

-- 获取过滤状态详情
function AreaCopy.Modules.Filter:GetStatusInfo()
    local mode = self:GetMode()
    local modeNameMap = {[0] = "全量复制", [1] = "白名单模式", [2] = "黑名单模式"}
    local whitelist = AreaCopy.Modules.Config:Get("filter", "whitelist") or {}
    local blacklist = AreaCopy.Modules.Config:Get("filter", "blacklist") or {}
    return {
        modeName = modeNameMap[mode] or "未知模式",
        copyAir = self:GetCopyAir() and "开启" or "关闭",
        whitelistCount = #whitelist,
        blacklistCount = #blacklist
    }
end

-- ==========================================
-- 复制核心模块
-- 职责:区域方块读取、旋转/镜像计算、批量粘贴、撤销管理、模板管理
-- ==========================================
AreaCopy.Modules.Copy = {
    isInited = false
}

function AreaCopy.Modules.Copy:Init()
    if self.isInited then return end
    self.isInited = true
    print("[AreaCopy] 复制核心模块初始化完成")
end

-- 坐标旋转计算(基于区域中心的旋转矩阵)
function AreaCopy.Modules.Copy:RotateCoord(relX, relZ, rotation)
    local newX, newZ = relX, relZ
    if rotation == AreaCopy.ENUM.ROTATION.ANGLE_90 then
        newX = -relZ
        newZ = relX
    elseif rotation == AreaCopy.ENUM.ROTATION.ANGLE_180 then
        newX = -relX
        newZ = -relZ
    elseif rotation == AreaCopy.ENUM.ROTATION.ANGLE_270 then
        newX = relZ
        newZ = -relX
    end
    return newX, newZ
end

-- 坐标镜像计算
function AreaCopy.Modules.Copy:MirrorCoord(relX, enableMirror)
    if not enableMirror then
        return relX
    end
    return -relX
end

-- 读取区域方块数据(预过滤,优化内存占用)
function AreaCopy.Modules.Copy:ReadAreaData(areaInfo)
    local blockData = {}
    local minX, maxX = areaInfo.minX, areaInfo.maxX
    local minY, maxY = areaInfo.minY, areaInfo.maxY
    local minZ, maxZ = areaInfo.minZ, areaInfo.maxZ
    -- 优化遍历顺序:Y轴优先,减少区块加载切换开销
    for y = minY, maxY do
        for x = minX, maxX do
            for z = minZ, maxZ do
                local result, blockId = Block:getBlockID(x, y, z)
                local result2, blockDataVal = Block:getBlockData(x, y, z)
                -- 过滤无效方块
                if result ~= 0 or result2 ~= 0 then
                    goto continue
                end
                -- 应用过滤规则
                if not AreaCopy.Modules.Filter:CheckBlockValid(blockId) then
                    goto continue
                end
                -- 存储相对坐标,减少后续计算量
                table.insert(blockData, {
                    relX = x - minX,
                    relY = y - minY,
                    relZ = z - minZ,
                    blockId = blockId,
                    blockData = blockDataVal
                })
                ::continue::
            end
        end
    end
    return blockData
end

-- 执行区域复制粘贴
function AreaCopy.Modules.Copy:ExecuteCopy(playerUin, targetX, targetY, targetZ, rotation, mirror)
    -- 并发锁校验,防止重复执行
    if AreaCopy.Runtime.copyTaskLocks[playerUin] then
        return false, "正在执行复制操作,请等待当前任务完成"
    end
    -- 初始化玩家数据
    AreaCopy.Modules.Record:InitPlayerData(playerUin)
    -- 获取区域信息
    local areaInfo = AreaCopy.Modules.Record:CalcPlayerArea(playerUin)
    if not areaInfo then
        return false, "请先完成区域双点选择"
    end
    -- 参数标准化
    targetX, targetY, targetZ = math.floor(targetX), math.floor(targetY), math.floor(targetZ)
    rotation = tonumber(rotation) or AreaCopy.Modules.Config:Get("copy", "defaultRotation")
    mirror = mirror ~= nil and mirror or AreaCopy.Modules.Config:Get("copy", "defaultMirror")
    -- 旋转功能校验
    if not AreaCopy.Modules.Config:Get("copy", "enableRotate") and rotation ~= 0 then
        return false, "旋转功能已禁用"
    end
    -- 镜像功能校验
    if not AreaCopy.Modules.Config:Get("copy", "enableMirror") and mirror then
        return false, "镜像功能已禁用"
    end
    -- 读取源区域方块数据
    local blockData = self:ReadAreaData(areaInfo)
    if #blockData == 0 then
        return false, "所选区域内无有效方块"
    end
    -- 缓存复制数据
    AreaCopy.Runtime.playerData[playerUin].copyCache = {
        blockData = blockData,
        sourceArea = areaInfo,
        rotation = rotation,
        mirror = mirror
    }
    -- 加锁
    AreaCopy.Runtime.copyTaskLocks[playerUin] = true
    -- 批量放置配置
    local batchSize = AreaCopy.Modules.Config:Get("copy", "batchSize")
    local copyDelay = AreaCopy.Modules.Config:Get("copy", "copyDelay") / 1000
    local totalCount = #blockData
    local placedCount = 0
    local batchIndex = 0
    local undoData = {} -- 撤销数据记录
    -- 区域尺寸计算
    local sizeX = areaInfo.sizeX
    local sizeZ = areaInfo.sizeZ
    local centerOffsetX = (sizeX - 1) / 2
    local centerOffsetZ = (sizeZ - 1) / 2
    -- 分批放置协程函数
    local function batchPlaceTask()
        local endIndex = math.min((batchIndex + 1) * batchSize, totalCount)
        for i = batchIndex * batchSize + 1, endIndex do
            local data = blockData[i]
            -- 计算旋转后的相对坐标
            local relX = data.relX - centerOffsetX
            local relZ = data.relZ - centerOffsetZ
            local rotX, rotZ = self:RotateCoord(relX, relZ, rotation)
            -- 镜像处理
            rotX = self:MirrorCoord(rotX, mirror)
            -- 计算最终绝对坐标
            local finalX = math.floor(targetX + rotX + centerOffsetX)
            local finalY = math.floor(targetY + data.relY)
            local finalZ = math.floor(targetZ + rotZ + centerOffsetZ)
            -- 坐标合法性校验
            if finalY < 0 or finalY > 256 then
                goto continue
            end
            -- 记录原方块数据,用于撤销
            local result, oldBlockId = Block:getBlockID(finalX, finalY, finalZ)
            local result2, oldData = Block:getBlockData(finalX, finalY, finalZ)
            if result == 0 and result2 == 0 then
                table.insert(undoData, {
                    x = finalX, y = finalY, z = finalZ,
                    oldId = oldBlockId, oldData = oldData,
                    newId = data.blockId, newData = data.blockData
                })
            end
            -- 放置方块
            Block:setBlockAll(finalX, finalY, finalZ, data.blockId, data.blockData)
            placedCount = placedCount + 1
            ::continue::
        end
        batchIndex = batchIndex + 1
        -- 未完成则继续下一批次
        if batchIndex * batchSize < totalCount then
            threadpool:wait(copyDelay)
            batchPlaceTask()
        else
            -- 复制完成,释放锁
            AreaCopy.Runtime.copyTaskLocks[playerUin] = false
            -- 记录撤销数据
            self:AddUndoRecord(playerUin, undoData)
            Chat:sendSystemMsg(string.format("复制完成!成功放置%d个方块", placedCount), playerUin)
        end
    end
    -- 启动协程执行批量放置
    threadpool:new(batchPlaceTask)
    return true, string.format("开始复制区域,总计%d个有效方块,正在执行...", totalCount)
end

-- 添加撤销记录
function AreaCopy.Modules.Copy:AddUndoRecord(playerUin, undoData)
    if not AreaCopy.Modules.Config:Get("special", "enableUndo") then
        return
    end
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    if not playerData then return end
    local maxSteps = AreaCopy.Modules.Config:Get("special", "undoMaxSteps")
    -- 超出最大步数,移除最早的记录
    if #playerData.undoQueue >= maxSteps then
        table.remove(playerData.undoQueue, 1)
    end
    table.insert(playerData.undoQueue, undoData)
end

-- 执行撤销操作
function AreaCopy.Modules.Copy:ExecuteUndo(playerUin)
    if not AreaCopy.Modules.Config:Get("special", "enableUndo") then
        return false, "撤销功能已禁用"
    end
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    if not playerData or #playerData.undoQueue == 0 then
        return false, "无可用的撤销记录"
    end
    -- 取出最近一次操作记录
    local undoData = table.remove(playerData.undoQueue)
    local restoreCount = 0
    -- 还原方块数据
    for _, block in ipairs(undoData) do
        Block:setBlockAll(block.x, block.y, block.z, block.oldId, block.oldData)
        restoreCount = restoreCount + 1
    end
    return true, string.format("撤销成功!已还原%d个方块", restoreCount)
end

-- 保存区域为模板
function AreaCopy.Modules.Copy:SaveTemplate(playerUin, templateName)
    if not AreaCopy.Modules.Config:Get("special", "enableTemplate") then
        return false, "模板功能已禁用"
    end
    local areaInfo = AreaCopy.Modules.Record:CalcPlayerArea(playerUin)
    if not areaInfo then
        return false, "请先完成区域双点选择"
    end
    local blockData = self:ReadAreaData(areaInfo)
    if #blockData == 0 then
        return false, "所选区域内无有效方块"
    end
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    local maxTemplates = AreaCopy.Modules.Config:Get("special", "maxTemplates")
    if #playerData.templates >= maxTemplates then
        return false, string.format("模板数量已达上限%d个,请先删除旧模板", maxTemplates)
    end
    -- 保存模板
    table.insert(playerData.templates, {
        name = templateName,
        blockData = blockData,
        sourceArea = areaInfo,
        createTime = os.time()
    })
    return true, string.format("模板「%s」保存成功!当前模板数量:%d", templateName, #playerData.templates)
end

-- 获取玩家模板列表
function AreaCopy.Modules.Copy:GetTemplateList(playerUin)
    local playerData = AreaCopy.Runtime.playerData[playerUin]
    if not playerData or #playerData.templates == 0 then
        return nil
    end
    return playerData.templates
end

-- 使用模板粘贴
function AreaCopy.Modules.Copy:UseTemplate(playerUin, templateIndex, targetX, targetY, targetZ, rotation, mirror)
    if AreaCopy.Runtime.copyTaskLocks[playerUin] then
        return false, "正在执行复制操作,请等待当前任务完成"
    end
    local templates = self:GetTemplateList(playerUin)
    if not templates then
        return false, "你还没有保存任何模板"
    end
    templateIndex = tonumber(templateIndex)
    if not templateIndex or templateIndex < 1 or templateIndex > #templates then
        return false, "无效的模板索引"
    end
    -- 参数标准化
    targetX, targetY, targetZ = math.floor(targetX), math.floor(targetY), math.floor(targetZ)
    rotation = tonumber(rotation) or AreaCopy.Modules.Config:Get("copy", "defaultRotation")
    mirror = mirror ~= nil and mirror or AreaCopy.Modules.Config:Get("copy", "defaultMirror")
    -- 旋转/镜像功能校验
    if not AreaCopy.Modules.Config:Get("copy", "enableRotate") and rotation ~= 0 then
        return false, "旋转功能已禁用"
    end
    if not AreaCopy.Modules.Config:Get("copy", "enableMirror") and mirror then
        return false, "镜像功能已禁用"
    end
    -- 获取模板数据
    local template = templates[templateIndex]
    local blockData = template.blockData
    local areaInfo = template.sourceArea
    -- 加锁
    AreaCopy.Runtime.copyTaskLocks[playerUin] = true
    -- 批量放置配置
    local batchSize = AreaCopy.Modules.Config:Get("copy", "batchSize")
    local copyDelay = AreaCopy.Modules.Config:Get("copy", "copyDelay") / 1000
    local totalCount = #blockData
    local placedCount = 0
    local batchIndex = 0
    local undoData = {}
    -- 区域尺寸计算
    local sizeX = areaInfo.sizeX
    local sizeZ = areaInfo.sizeZ
    local centerOffsetX = (sizeX - 1) / 2
    local centerOffsetZ = (sizeZ - 1) / 2
    -- 分批放置协程函数
    local function templatePlaceTask()
        local endIndex = math.min((batchIndex + 1) * batchSize, totalCount)
        for i = batchIndex * batchSize + 1, endIndex do
            local data = blockData[i]
            -- 坐标变换计算
            local relX = data.relX - centerOffsetX
            local relZ = data.relZ - centerOffsetZ
            local rotX, rotZ = self:RotateCoord(relX, relZ, rotation)
            rotX = self:MirrorCoord(rotX, mirror)
            -- 最终坐标
            local finalX = math.floor(targetX + rotX + centerOffsetX)
            local finalY = math.floor(targetY + data.relY)
            local finalZ = math.floor(targetZ + rotZ + centerOffsetZ)
            if finalY < 0 or finalY > 256 then
                goto continue
            end
            -- 记录撤销数据
            local result, oldBlockId = Block:getBlockID(finalX, finalY, finalZ)
            local result2, oldData = Block:getBlockData(finalX, finalY, finalZ)
            if result == 0 and result2 == 0 then
                table.insert(undoData, {
                    x = finalX, y = finalY, z = finalZ,
                    oldId = oldBlockId, oldData = oldData,
                    newId = data.blockId, newData = data.blockData
                })
            end
            -- 放置方块
            Block:setBlockAll(finalX, finalY, finalZ, data.blockId, data.blockData)
            placedCount = placedCount + 1
            ::continue::
        end
        batchIndex = batchIndex + 1
        if batchIndex * batchSize < totalCount then
            threadpool:wait(copyDelay)
            templatePlaceTask()
        else
            AreaCopy.Runtime.copyTaskLocks[playerUin] = false
            self:AddUndoRecord(playerUin, undoData)
            Chat:sendSystemMsg(string.format("模板「%s」使用完成!成功放置%d个方块", template.name, placedCount), playerUin)
        end
    end
    -- 启动协程
    threadpool:new(templatePlaceTask)
    return true, string.format("开始使用模板「%s」,总计%d个有效方块,正在执行...", template.name, totalCount)
end

-- 删除模板
function AreaCopy.Modules.Copy:DeleteTemplate(playerUin, templateIndex)
    local templates = self:GetTemplateList(playerUin)
    if not templates then
        return false, "你还没有保存任何模板"
    end
    templateIndex = tonumber(templateIndex)
    if not templateIndex or templateIndex < 1 or templateIndex > #templates then
        return false, "无效的模板索引"
    end
    local templateName = templates[templateIndex].name
    table.remove(templates, templateIndex)
    return true, string.format("模板「%s」已删除", templateName)
end

-- ==========================================
-- 命令管理模块
-- 职责:命令注册、消息解析、权限校验、命令执行
-- ==========================================
AreaCopy.Modules.Command = {
    cmdPrefix = "/copy",
    cmdHandlers = {},
    cmdAliases = {},
    isInited = false
}

-- 注册命令
function AreaCopy.Modules.Command:RegisterCmd(name, handler, requireAdmin)
    self.cmdHandlers[name] = {
        handler = handler,
        requireAdmin = requireAdmin or false
    }
end

-- 注册命令别名
function AreaCopy.Modules.Command:RegisterAlias(alias, targetCmd)
    self.cmdAliases[alias] = targetCmd
end

-- 模块初始化
function AreaCopy.Modules.Command:Init()
    if self.isInited then return end
    -- 注册基础命令
    self:RegisterBaseCmds()
    -- 注册聊天消息监听事件
    ScriptSupportEvent:registerEvent([=[Player.NewInputContent]=], function(e)
        self:OnChatMessageEvent(e)
    end)
    self.isInited = true
    print("[AreaCopy] 命令管理模块初始化完成")
end

-- 注册所有基础命令
function AreaCopy.Modules.Command:RegisterBaseCmds()
    -- 帮助命令
    self:RegisterCmd("help", function(playerUin, args)
        self:ShowHelpInfo(playerUin)
    end, false)
    self:RegisterAlias("?", "help")

    -- 记录模式切换
    self:RegisterCmd("mode", function(playerUin, args)
        local mode = tonumber(args[1])
        if not mode or not AreaCopy.ENUM.RECORD_MODE[mode] then
            Chat:sendSystemMsg("用法:/copy mode <1|2|3> (1=聊天命令 2=方块放置 3=道具点击)", playerUin)
            return
        end
        if AreaCopy.Modules.Record:SetMode(mode) then
            local modeNameMap = {[1] = "聊天命令", [2] = "方块放置", [3] = "道具点击"}
            Chat:sendSystemMsg(string.format("已切换为%s记录模式", modeNameMap[mode]), playerUin)
        else
            Chat:sendSystemMsg("模式切换失败", playerUin)
        end
    end, true)

    -- 坐标点记录
    self:RegisterCmd("pos1", function(playerUin, args)
        local x, y, z = tonumber(args[1]), tonumber(args[2]), tonumber(args[3])
        local success, msg = AreaCopy.Modules.Record:RecordPosition(playerUin, 1, x, y, z)
        if msg then
            Chat:sendSystemMsg(msg, playerUin)
        end
    end, false)

    self:RegisterCmd("pos2", function(playerUin, args)
        local x, y, z = tonumber(args[1]), tonumber(args[2]), tonumber(args[3])
        local success, msg = AreaCopy.Modules.Record:RecordPosition(playerUin, 2, x, y, z)
        if msg then
            Chat:sendSystemMsg(msg, playerUin)
        end
    end, false)

    -- 清除选择
    self:RegisterCmd("clear", function(playerUin, args)
        if AreaCopy.Modules.Record:ClearSelection(playerUin) then
            Chat:sendSystemMsg("已清除当前区域选择数据", playerUin)
        end
    end, false)

    -- 粘贴命令
    self:RegisterCmd("paste", function(playerUin, args)
        local x, y, z = tonumber(args[1]), tonumber(args[2]), tonumber(args[3])
        -- 未传入坐标则使用玩家当前位置
        if not x or not y or not z then
            local result, px, py, pz = Player:getPosition(playerUin)
            if result ~= 0 then
                Chat:sendSystemMsg("无法获取当前位置,请手动输入目标坐标", playerUin)
                return
            end
            x, y, z = math.floor(px), math.floor(py - 1), math.floor(pz)
        end
        -- 获取旋转/镜像配置
        local rotation = AreaCopy.Modules.Config:Get("copy", "defaultRotation")
        local mirror = AreaCopy.Modules.Config:Get("copy", "defaultMirror")
        local success, msg = AreaCopy.Modules.Copy:ExecuteCopy(playerUin, x, y, z, rotation, mirror)
        if msg then
            Chat:sendSystemMsg(msg, playerUin)
        end
    end, false)

    -- 旋转设置
    self:RegisterCmd("rotate", function(playerUin, args)
        local rotation = tonumber(args[1])
        local validRotations = {0, 90, 180, 270}
        local isValid = false
        for _, v in ipairs(validRotations) do
            if rotation == v then
                isValid = true
                break
            end
        end
        if not isValid then
            Chat:sendSystemMsg("用法:/copy rotate <0|90|180|270>", playerUin)
            return
        end
        AreaCopy.Modules.Config:Set("copy", "defaultRotation", rotation)
        AreaCopy.Modules.Config:SaveToArchive()
        Chat:sendSystemMsg(string.format("默认旋转角度已设置为%d°", rotation), playerUin)
    end, false)

    -- 镜像设置
    self:RegisterCmd("mirror", function(playerUin, args)
        local option = args[1]
        if not option or (option ~= "on" and option ~= "off") then
            Chat:sendSystemMsg("用法:/copy mirror <on|off>", playerUin)
            return
        end
        local mirror = (option == "on")
        AreaCopy.Modules.Config:Set("copy", "defaultMirror", mirror)
        AreaCopy.Modules.Config:SaveToArchive()
        Chat:sendSystemMsg(string.format("镜像模式已%s", mirror and "开启" or "关闭"), playerUin)
    end, false)

    -- 过滤模式设置
    self:RegisterCmd("filter", function(playerUin, args)
        local mode = args[1]
        local modeMap = {all = 0, white = 1, black = 2}
        if not mode or modeMap[mode] == nil then
            Chat:sendSystemMsg("用法:/copy filter <all|white|black>", playerUin)
            return
        end
        AreaCopy.Modules.Filter:SetMode(modeMap[mode])
        local modeNameMap = {all = "全量复制", white = "白名单", black = "黑名单"}
        Chat:sendSystemMsg(string.format("过滤模式已设置为%s", modeNameMap[mode]), playerUin)
    end, true)

    -- 添加方块到过滤列表
    self:RegisterCmd("addblock", function(playerUin, args)
        local blockId = tonumber(args[1])
        if not blockId then
            Chat:sendSystemMsg("用法:/copy addblock <方块ID>", playerUin)
            return
        end
        local filterMode = AreaCopy.Modules.Filter:GetMode()
        if filterMode == AreaCopy.ENUM.FILTER_MODE.ALL then
            Chat:sendSystemMsg("当前为全量复制模式,无需设置过滤规则", playerUin)
            return
        end
        local success = false
        local blockName = AreaCopy.Modules.Filter:GetBlockName(blockId)
        if filterMode == AreaCopy.ENUM.FILTER_MODE.WHITELIST then
            success = AreaCopy.Modules.Filter:AddToWhitelist(blockId)
            if success then
                Chat:sendSystemMsg(string.format("已将%s(ID:%d)添加到白名单", blockName, blockId), playerUin)
            else
                Chat:sendSystemMsg("该方块已在白名单中", playerUin)
            end
        else
            success = AreaCopy.Modules.Filter:AddToBlacklist(blockId)
            if success then
                Chat:sendSystemMsg(string.format("已将%s(ID:%d)添加到黑名单", blockName, blockId), playerUin)
            else
                Chat:sendSystemMsg("该方块已在黑名单中", playerUin)
            end
        end
    end, true)

    -- 从过滤列表移除方块
    self:RegisterCmd("removeblock", function(playerUin, args)
        local blockId = tonumber(args[1])
        if not blockId then
            Chat:sendSystemMsg("用法:/copy removeblock <方块ID>", playerUin)
            return
        end
        local filterMode = AreaCopy.Modules.Filter:GetMode()
        if filterMode == AreaCopy.ENUM.FILTER_MODE.ALL then
            Chat:sendSystemMsg("当前为全量复制模式,无需设置过滤规则", playerUin)
            return
        end
        local success = false
        if filterMode == AreaCopy.ENUM.FILTER_MODE.WHITELIST then
            success = AreaCopy.Modules.Filter:RemoveFromWhitelist(blockId)
            if success then
                Chat:sendSystemMsg(string.format("已从白名单中移除方块ID:%d", blockId), playerUin)
            else
                Chat:sendSystemMsg("该方块不在白名单中", playerUin)
            end
        else
            success = AreaCopy.Modules.Filter:RemoveFromBlacklist(blockId)
            if success then
                Chat:sendSystemMsg(string.format("已从黑名单中移除方块ID:%d", blockId), playerUin)
            else
                Chat:sendSystemMsg("该方块不在黑名单中", playerUin)
            end
        end
    end, true)

    -- 空气复制设置
    self:RegisterCmd("air", function(playerUin, args)
        local option = args[1]
        if not option or (option ~= "on" and option ~= "off") then
            Chat:sendSystemMsg("用法:/copy air <on|off>", playerUin)
            return
        end
        local copyAir = (option == "on")
        AreaCopy.Modules.Filter:SetCopyAir(copyAir)
        Chat:sendSystemMsg(string.format("空气方块复制已%s", copyAir and "开启" or "关闭"), playerUin)
    end, true)

    -- 管理员管理
    self:RegisterCmd("admin", function(playerUin, args)
        local action = args[1]
        local targetUin = tonumber(args[2])
        if not action then
            Chat:sendSystemMsg("用法:/copy admin <add|remove|list> [玩家迷你号]", playerUin)
            return
        end
        if action == "add" then
            if not targetUin then
                Chat:sendSystemMsg("请输入有效的玩家迷你号", playerUin)
                return
            end
            if AreaCopy.Modules.Permission:AddAdmin(targetUin) then
                Chat:sendSystemMsg(string.format("已将玩家%d添加为管理员", targetUin), playerUin)
            else
                Chat:sendSystemMsg("添加失败,该玩家已是管理员", playerUin)
            end
        elseif action == "remove" then
            if not targetUin then
                Chat:sendSystemMsg("请输入有效的玩家迷你号", playerUin)
                return
            end
            if AreaCopy.Modules.Permission:RemoveAdmin(targetUin) then
                Chat:sendSystemMsg(string.format("已移除管理员%d", targetUin), playerUin)
            else
                Chat:sendSystemMsg("移除失败,该玩家不是管理员或无法移除房主", playerUin)
            end
        elseif action == "list" then
            local adminList = AreaCopy.Modules.Permission:GetAdminList()
            Chat:sendSystemMsg("===== 管理员列表 =====", playerUin)
            for i, uin in ipairs(adminList) do
                local result, name = Player:getNickname(uin)
                name = name or tostring(uin)
                Chat:sendSystemMsg(string.format("%d. %s (%d)", i, name, uin), playerUin)
            end
        else
            Chat:sendSystemMsg("未知操作:" .. action, playerUin)
        end
    end, true)

    -- 黑名单管理
    self:RegisterCmd("blacklist", function(playerUin, args)
        local action = args[1]
        local targetUin = tonumber(args[2])
        if not action then
            Chat:sendSystemMsg("用法:/copy blacklist <add|remove|list> [玩家迷你号]", playerUin)
            return
        end
        if action == "add" then
            if not targetUin then
                Chat:sendSystemMsg("请输入有效的玩家迷你号", playerUin)
                return
            end
            if AreaCopy.Modules.Permission:AddBlacklist(targetUin) then
                Chat:sendSystemMsg(string.format("已将玩家%d添加到黑名单", targetUin), playerUin)
            else
                Chat:sendSystemMsg("添加失败,该玩家已在黑名单中", playerUin)
            end
        elseif action == "remove" then
            if not targetUin then
                Chat:sendSystemMsg("请输入有效的玩家迷你号", playerUin)
                return
            end
            if AreaCopy.Modules.Permission:RemoveBlacklist(targetUin) then
                Chat:sendSystemMsg(string.format("已从黑名单中移除玩家%d", targetUin), playerUin)
            else
                Chat:sendSystemMsg("移除失败,该玩家不在黑名单中", playerUin)
            end
        elseif action == "list" then
            local blacklist = AreaCopy.Modules.Permission:GetBlacklist()
            Chat:sendSystemMsg("===== 黑名单列表 =====", playerUin)
            if #blacklist == 0 then
                Chat:sendSystemMsg("黑名单为空", playerUin)
            else
                for i, uin in ipairs(blacklist) do
                    local result, name = Player:getNickname(uin)
                    name = name or tostring(uin)
                    Chat:sendSystemMsg(string.format("%d. %s (%d)", i, name, uin), playerUin)
                end
            end
        else
            Chat:sendSystemMsg("未知操作:" .. action, playerUin)
        end
    end, true)

    -- 配置管理
    self:RegisterCmd("config", function(playerUin, args)
        local action = args[1]
        if not action then
            Chat:sendSystemMsg("用法:/copy config <show|save|reset>", playerUin)
            return
        end
        if action == "show" then
            self:ShowConfigInfo(playerUin)
        elseif action == "save" then
            if AreaCopy.Modules.Config:SaveToArchive() then
                Chat:sendSystemMsg("当前配置已持久化到存档", playerUin)
            else
                Chat:sendSystemMsg("配置保存失败", playerUin)
            end
        elseif action == "reset" then
            if AreaCopy.Modules.Config:Reset() then
                Chat:sendSystemMsg("配置已重置为默认值", playerUin)
            else
                Chat:sendSystemMsg("配置重置失败", playerUin)
            end
        else
            Chat:sendSystemMsg("未知操作:" .. action, playerUin)
        end
    end, true)

    -- 撤销操作
    self:RegisterCmd("undo", function(playerUin, args)
        local success, msg = AreaCopy.Modules.Copy:ExecuteUndo(playerUin)
        if msg then
            Chat:sendSystemMsg(msg, playerUin)
        end
    end, false)

    -- 预览管理
    self:RegisterCmd("preview", function(playerUin, args)
        local success, msg = AreaCopy.Modules.Record:DrawPreview(playerUin)
        if msg then
            Chat:sendSystemMsg(msg, playerUin)
        end
    end, false)

    self:RegisterCmd("nopreview", function(playerUin, args)
        if AreaCopy.Modules.Record:ClearPreview(playerUin) then
            Chat:sendSystemMsg("已清除区域预览", playerUin)
        end
    end, false)

    -- 模板管理
    self:RegisterCmd("template", function(playerUin, args)
        if not AreaCopy.Modules.Config:Get("special", "enableTemplate") then
            Chat:sendSystemMsg("模板功能已禁用", playerUin)
            return
        end
        local action = args[1]
        if not action then
            Chat:sendSystemMsg("用法:/copy template <save|list|use|delete> [参数]", playerUin)
            return
        end
        if action == "save" then
            local templateName = args[2]
            if not templateName then
                Chat:sendSystemMsg("用法:/copy template save <模板名称>", playerUin)
                return
            end
            local success, msg = AreaCopy.Modules.Copy:SaveTemplate(playerUin, templateName)
            if msg then
                Chat:sendSystemMsg(msg, playerUin)
            end
        elseif action == "list" then
            local templates = AreaCopy.Modules.Copy:GetTemplateList(playerUin)
            if not templates then
                Chat:sendSystemMsg("你还没有保存任何模板", playerUin)
                return
            end
            Chat:sendSystemMsg("===== 我的模板列表 =====", playerUin)
            for i, template in ipairs(templates) do
                local size = string.format("%d×%d×%d", template.sourceArea.sizeX, template.sourceArea.sizeY, template.sourceArea.sizeZ)
                Chat:sendSystemMsg(string.format("%d. %s (%s)", i, template.name, size), playerUin)
            end
        elseif action == "use" then
            local templateIndex = tonumber(args[2])
            if not templateIndex then
                Chat:sendSystemMsg("用法:/copy template use <模板索引> [x y z]", playerUin)
                return
            end
            local x, y, z = tonumber(args[3]), tonumber(args[4]), tonumber(args[5])
            -- 未传入坐标则使用玩家当前位置
            if not x or not y or not z then
                local result, px, py, pz = Player:getPosition(playerUin)
                if result ~= 0 then
                    Chat:sendSystemMsg("无法获取当前位置,请手动输入目标坐标", playerUin)
                    return
                end
                x, y, z = math.floor(px), math.floor(py - 1), math.floor(pz)
            end
            local rotation = AreaCopy.Modules.Config:Get("copy", "defaultRotation")
            local mirror = AreaCopy.Modules.Config:Get("copy", "defaultMirror")
            local success, msg = AreaCopy.Modules.Copy:UseTemplate(playerUin, templateIndex, x, y, z, rotation, mirror)
            if msg then
                Chat:sendSystemMsg(msg, playerUin)
            end
        elseif action == "delete" then
            local templateIndex = tonumber(args[2])
            if not templateIndex then
                Chat:sendSystemMsg("用法:/copy template delete <模板索引>", playerUin)
                return
            end
            local success, msg = AreaCopy.Modules.Copy:DeleteTemplate(playerUin, templateIndex)
            if msg then
                Chat:sendSystemMsg(msg, playerUin)
            end
        else
            Chat:sendSystemMsg("未知操作:" .. action, playerUin)
        end
    end, false)
end

-- 聊天消息事件处理
function AreaCopy.Modules.Command:OnChatMessageEvent(e)
    local message = e.content
    local playerUin = e.eventobjid
    -- 匹配命令前缀
    if not string.find(message, "^" .. self.cmdPrefix) then
        return
    end
    -- 解析命令参数
    local args = {}
    for part in string.gmatch(message, "%S+") do
        table.insert(args, part)
    end
    -- 提取命令名
    local cmdName = args[2] or "help"
    -- 别名解析
    if self.cmdAliases[cmdName] then
        cmdName = self.cmdAliases[cmdName]
    end
    -- 校验命令是否存在
    local cmdInfo = self.cmdHandlers[cmdName]
    if not cmdInfo then
        Chat:sendSystemMsg(string.format("未知命令:%s,输入 /copy help 查看帮助文档", cmdName), playerUin)
        return
    end
    -- 权限校验
    local hasPermission, errMsg = AreaCopy.Modules.Permission:CheckCommandPermission(playerUin, cmdInfo.requireAdmin)
    if not hasPermission then
        Chat:sendSystemMsg(errMsg, playerUin)
        return
    end
    -- 提取命令参数
    local cmdArgs = {}
    for i = 3, #args do
        table.insert(cmdArgs, args[i])
    end
    -- 执行命令处理函数
    cmdInfo.handler(playerUin, cmdArgs)
end

-- 显示帮助信息
function AreaCopy.Modules.Command:ShowHelpInfo(playerUin)
    Chat:sendSystemMsg("===== 区域复制脚本 V2.2 帮助 =====", playerUin)
    Chat:sendSystemMsg("【基础命令】", playerUin)
    Chat:sendSystemMsg("/copy help - 显示帮助文档", playerUin)
    Chat:sendSystemMsg("/copy mode <1|2|3> - 切换区域记录模式", playerUin)
    Chat:sendSystemMsg("/copy pos1 [x y z] - 记录区域起点坐标", playerUin)
    Chat:sendSystemMsg("/copy pos2 [x y z] - 记录区域终点坐标", playerUin)
    Chat:sendSystemMsg("/copy clear - 清除当前区域选择", playerUin)
    Chat:sendSystemMsg("/copy paste [x y z] - 粘贴区域到指定位置", playerUin)
    Chat:sendSystemMsg("【高级配置】", playerUin)
    Chat:sendSystemMsg("/copy rotate <0|90|180|270> - 设置默认旋转角度", playerUin)
    Chat:sendSystemMsg("/copy mirror <on|off> - 开启/关闭镜像粘贴", playerUin)
    Chat:sendSystemMsg("/copy filter <all|white|black> - 设置方块过滤模式", playerUin)
    Chat:sendSystemMsg("/copy addblock <方块ID> - 添加方块到过滤列表", playerUin)
    Chat:sendSystemMsg("/copy removeblock <方块ID> - 从过滤列表移除方块", playerUin)
    Chat:sendSystemMsg("/copy air <on|off> - 开启/关闭空气方块复制", playerUin)
    -- 仅管理员显示管理命令
    if AreaCopy.Modules.Permission:IsAdmin(playerUin) then
        Chat:sendSystemMsg("【管理命令】", playerUin)
        Chat:sendSystemMsg("/copy admin <add|remove|list> - 管理员管理", playerUin)
        Chat:sendSystemMsg("/copy blacklist <add|remove|list> - 黑名单管理", playerUin)
        Chat:sendSystemMsg("/copy config <show|save|reset> - 配置管理", playerUin)
    end
    Chat:sendSystemMsg("【功能扩展】", playerUin)
    Chat:sendSystemMsg("/copy undo - 撤销上一次粘贴操作", playerUin)
    Chat:sendSystemMsg("/copy preview - 渲染区域预览边框", playerUin)
    Chat:sendSystemMsg("/copy nopreview - 清除区域预览", playerUin)
    Chat:sendSystemMsg("/copy template <save|list|use|delete> - 模板管理", playerUin)
end

-- 显示当前配置信息
function AreaCopy.Modules.Command:ShowConfigInfo(playerUin)
    local config = AreaCopy.Runtime.config
    local filterStatus = AreaCopy.Modules.Filter:GetStatusInfo()
    Chat:sendSystemMsg("===== 当前运行配置 =====", playerUin)
    Chat:sendSystemMsg(string.format("记录模式:%d", config.record.mode), playerUin)
    Chat:sendSystemMsg(string.format("默认旋转:%d°", config.copy.defaultRotation), playerUin)
    Chat:sendSystemMsg(string.format("默认镜像:%s", config.copy.defaultMirror and "开启" or "关闭"), playerUin)
    Chat:sendSystemMsg(string.format("过滤模式:%s", filterStatus.modeName), playerUin)
    Chat:sendSystemMsg(string.format("空气复制:%s", filterStatus.copyAir), playerUin)
    Chat:sendSystemMsg(string.format("白名单数量:%d", filterStatus.whitelistCount), playerUin)
    Chat:sendSystemMsg(string.format("黑名单数量:%d", filterStatus.blacklistCount), playerUin)
    Chat:sendSystemMsg(string.format("权限校验:%s", config.permission.enableCheck and "开启" or "关闭"), playerUin)
    Chat:sendSystemMsg(string.format("撤销功能:%s", config.special.enableUndo and "开启" or "关闭"), playerUin)
    Chat:sendSystemMsg(string.format("预览功能:%s", config.special.enablePreview and "开启" or "关闭"), playerUin)
    Chat:sendSystemMsg(string.format("模板功能:%s", config.special.enableTemplate and "开启" or "关闭"), playerUin)
end

-- ==========================================
-- 主程序生命周期管理
-- ==========================================
-- 脚本初始化入口
function AreaCopy:Init()
    if self.Runtime.isInited then return end
    print(string.format("[AreaCopy] 区域复制脚本 V%s 开始初始化", self.version))
    -- 按依赖顺序初始化各模块
    self.Modules.Config:Init()
    self.Modules.Permission:Init()
    self.Modules.Filter:Init()
    self.Modules.Record:Init()
    self.Modules.Copy:Init()
    self.Modules.Command:Init()
    -- 注册游戏启动事件
    ScriptSupportEvent:registerEvent([=[Game.Start]=], function(e)
        self:OnGameStartEvent(e)
    end)
    -- 注册玩家进入事件
    ScriptSupportEvent:registerEvent([=[Game.AnyPlayer.EnterGame]=], function(e)
        self:OnPlayerEnterEvent(e)
    end)
    self.Runtime.isInited = true
    print(string.format("[AreaCopy] 脚本初始化完成,版本:%s", self.version))
end

-- 游戏启动事件处理
function AreaCopy:OnGameStartEvent(e)
    local showTips = self.Modules.Config:Get("others", "showTips")
    if showTips then
        Chat:sendSystemMsg(string.format("区域复制脚本 V%s 加载完成,输入 /copy help 查看帮助", self.version))
    end
    -- 房主自动添加为管理员
    local result, hostUin = Player:getHostUin()
    if result == 0 and self.Modules.Config:Get("permission", "ownerIsAdmin") then
        self.Modules.Permission:AddAdmin(hostUin)
    end
end

-- 玩家进入游戏事件处理
function AreaCopy:OnPlayerEnterEvent(e)
    local playerUin = e.eventobjid
    -- 初始化玩家数据
    self.Modules.Record:InitPlayerData(playerUin)
    -- 欢迎提示
    local showTips = self.Modules.Config:Get("others", "showTips")
    if showTips then
        Chat:sendSystemMsg("欢迎使用区域复制脚本,输入 /copy help 查看帮助文档", playerUin)
    end
    -- 房主权限校验
    local result, hostUin = Player:getHostUin()
    if playerUin == hostUin and self.Modules.Config:Get("permission", "ownerIsAdmin") then
        self.Modules.Permission:AddAdmin(playerUin)
    end
end

-- 启动脚本
AreaCopy:Init()

-- 全局导出
_G.AreaCopy = AreaCopy

一、快速上手流程

1. 基础操作三步法

  • 第一步:记录区域

选择以下任意一种方式记录区域的两个对角点:

– 聊天命令模式(默认):输入  /copy pos1  记录起点(默认当前位置),输入  /copy pos2  记录终点(可手动添加坐标,格式: /copy pos2 x y z )

– 方块放置模式:输入  /copy mode 2  切换模式,放置指定记录方块(默认ID:101)记录两点,方块自动销毁

– 道具点击模式:输入  /copy mode 3  切换模式,用指定道具(默认ID:1001)点击方块表面记录两点 

  • 第二步:配置复制参数(可选)

 – 旋转设置: /copy rotate 90 (支持0/90/180/270度,默认0度)​

– 镜像设置: /copy mirror on (开启镜像, off  关闭,默认关闭)

– 过滤设置: /copy filter white (仅复制白名单方块,可选all/white/black)

  •  第三步:执行粘贴

输入  /copy paste  粘贴到当前位置(或手动指定坐标: /copy paste x y z ),脚本会自动批量放置方块并显示进度

2. 常用功能速查

  • 功能需求 执行命令 备注 
  • 查看帮助  /copy help  显示完整命令列表 
  • 清除选择  /copy clear  重置已记录的区域坐标 
  • 显示预览  /copy preview  渲染区域边框,避免粘贴偏差 
  • 撤销操作  /copy undo  恢复上一次粘贴前的状态 
  • 保存模板  /copy template save 模板名  保存当前区域为模板 
  • 使用模板  /copy template use 索引  粘贴已保存的模板(索引通过列表查看) 
  • 查看模板  /copy template list  显示所有已保存的模板及尺寸

二、高级功能详解

 1. 方块过滤精细化配置

 基础设置

  • – 切换过滤模式: /copy filter black (黑名单模式,排除指定方块)
  • ​- 添加方块到列表: /copy addblock 101 (将土块ID101添加到当前过滤列表)
  • ​- 移除方块: /copy removeblock 101 (从过滤列表移除指定方块)
  • ​- 空气复制开关: /copy air on (开启后复制空气方块,默认关闭)

 预设组快速配置

 脚本内置常用方块预设组,可快速添加过滤规则:

  •  – 建筑类方块:自动包含木板、石头、砖块等基础建材
  • ​- 矿石类方块:包含各类矿石及矿石块
  • ​- 电路类方块:包含导线、开关、发射器等
  • – 自然类方块:包含草方块、沙子、树叶等自然方块
  • ​- 功能类方块:包含箱子、熔炉、工作台等交互方块

 2. 模板管理高效复用

 模板操作流程

  •  1. 记录目标区域(pos1+pos2)
  • ​2. 保存模板: /copy template save 城墙组件 (命名建议简洁明了)
  • ​3. 查看模板: /copy template list (获取模板索引)
  • ​4. 快速粘贴: /copy template use 1 x y z (使用索引1的模板,指定粘贴坐标)
  • ​5. 删除模板: /copy template delete 1 (删除无需保留的模板)

 模板使用技巧

  •  – 模板支持旋转/镜像粘贴,粘贴前可配置参数
  • ​- 建议将常用组件(如窗户、门框、装饰花纹)保存为模板
  • ​- 模板数量上限可通过配置调整(默认20个)

 3. 权限管控多人协作

 管理员操作

  •  – 添加管理员: /copy admin add 玩家迷你号 (赋予其他玩家管理权限)
  • – 移除管理员: /copy admin remove 玩家迷你号
  • – 查看管理员列表: /copy admin list 

 黑名单管理

  •  – 添加黑名单: /copy blacklist add 玩家迷你号 (禁止该玩家使用脚本)
  • ​- 移除黑名单: /copy blacklist remove 玩家迷你号 
  • ​- 查看黑名单: /copy blacklist list 

 权限说明

  •  – 房主默认拥有管理员权限,可配置是否自动继承
  • ​- 普通玩家仅可使用基础复制粘贴功能,无法修改配置
  • ​- 黑名单玩家无法使用脚本任何功能

 4. 配置自定义优化

 查看当前配置

  •  输入  /copy config show  查看所有运行参数,包括记录模式、过滤规则、功能开关等

 常用配置调整

  •  – 保存当前配置: /copy config save (持久化到存档,下次启动自动加载)
  • ​- 重置默认配置: /copy config reset (恢复初始设置)
  • ​- 调整批量大小:需修改脚本内 batchSize 参数(默认64,设备性能差可改小)
  • ​- 预览方块修改:修改 previewBlockId 参数(默认95,可替换为其他方块ID)

 三、常见问题排查

 1. 区域记录失败

  •  – 检查坐标是否超出合法范围(Y轴0-256)
  • ​- 确认记录模式与操作方式匹配(如方块模式需使用指定方块)
  • ​- 检查是否已记录两个完整坐标点(pos1和pos2均需记录)

 2. 复制粘贴卡顿

  •  – 减少单次复制区域大小(建议单区域不超过10000个方块)
  • ​- 关闭空气复制功能( /copy air off )
  • ​- 降低批量放置大小(修改脚本 batchSize 为32)

 3. 权限不足提示

  •  – 确认是否为管理员或房主身份
  • – 联系管理员添加权限( /copy admin add 你的迷你号 )
  • – 检查是否被加入黑名单

 4. 撤销功能无效

  •  – 确认撤销功能已开启( config.special.enableUndo=true )
  • ​- 撤销步数有上限(默认10步,可通过配置调整)
  • ​- 仅粘贴操作支持撤销,记录和模板操作无法撤销

 四、使用技巧进阶

  •  1. 精准粘贴:粘贴前使用 /copy preview 查看区域边框,调整坐标后再执行粘贴
  • 2. 组件组合:将复杂建筑拆分为多个小模板,按需组合粘贴,提升灵活性
  • 3. 批量部署:配合旋转/镜像功能,可快速生成对称建筑(如宫殿、城堡)
  • ​4. 备份重要操作:关键建筑复制前建议保存模板,避免误操作无法恢复
  • ​5. 性能优化:复制大型区域时关闭游戏内特效和光影,减少资源占用

🍥辛苦了,吃颗糖吧,本文已经没有了哦~

请登录后发表评论

    没有回复内容