-- simple_file_client.lua local args = {...} local httpServer = args[1] or "http://192.168.2.200:8080" local roomId = args[2] local pollInterval = 1 local computerID = tostring(os.computerID() or "unknown") -- ========== 加载 JSON ========== local JsonUrl = "https://git.liulikeji.cn/GitHub/json.lua/raw/branch/master/json.lua" local JsonResp = http.get(JsonUrl) if not JsonResp then error("无法下载 Json 框架") end local json = load(JsonResp.readAll())() JsonResp.close() -- ========== 加载 Basalt ========== local basaltUrl = "https://git.liulikeji.cn/GitHub/Basalt/releases/download/v1.7/basalt.lua" local basaltResp = http.get(basaltUrl) if not basaltResp then error("无法下载 Basalt 框架") end local basalt = load(basaltResp.readAll())() basaltResp.close() local mainFrame = basalt.createFrame() -- ========== 工具函数 ========== local function log(msg) --basalt.debug("[FileClient] " .. tostring(msg)) end local function isBinaryFile(path) local extension = string.lower(string.match(path, "%.([^%.%s]+)$") or "") local binaryExtensions = { ["dfpwm"] = true, } if binaryExtensions[extension] then return true end -- 对于没有扩展名的文件,检查内容 local absPath = path if not fs.exists(absPath) then return false end local ok, handle = pcall(fs.open, absPath, "rb") if not ok or not handle then return false end local data = handle.read(math.min(1024, fs.getSize(absPath))) handle.close() if not data then return false end -- 检查是否存在控制字符(除常见的空白字符外) for i = 1, #data do local b = data:byte(i) -- 控制字符范围是 0-8, 11-12, 14-31, 127 if (b >= 0 and b <= 8) or (b == 11) or (b == 12) or (b >= 14 and b <= 31) or (b == 127) then -- 如果控制字符过多(超过5%),则认为是二进制文件 local controlCount = 0 for j = 1, #data do local byte = data:byte(j) if (byte >= 0 and byte <= 8) or (byte == 11) or (byte == 12) or (byte >= 14 and byte <= 31) or (byte == 127) then controlCount = controlCount + 1 end end if controlCount / #data > 0.05 then return true end end end return false end local function cleanPath(path) return (path:gsub("^computer/", ""):gsub("^computer\\", "")) end local function httpPost(path, data) local jsonData = json.encode(data) local url = httpServer .. path -- 使用长轮询 local response,err = http.post({ url = url, body = jsonData, method = "POST", headers = { ["Content-Type"] = "application/json" }, timeout = 60 }) if not response then return nil, "0 "..err end local responseBody = response.readAll() response.close() local ok, result = pcall(json.decode, responseBody) if ok then return result else return nil, "1无效的JSON响应: " .. responseBody end end -- ========== 文件系统操作 ========== local function getFiles(currentPath, result, prefix) local computerPrefix = "computer_" .. computerID local fullPrefix = currentPath == "" and prefix:sub(1, -2) or prefix .. currentPath local absPath = "/" .. (currentPath == "" and "" or currentPath) if fs.isDir(absPath) then result[fullPrefix] = { isFolder = true } for _, entry in ipairs(fs.list(absPath)) do if entry ~= "rom" then getFiles(currentPath == "" and entry or (currentPath .. "/" .. entry), result, prefix) end end else local content = "[binary]" if not isBinaryFile(absPath) then local ok, handle = pcall(fs.open, absPath, "r") if ok and handle then local data = handle.readAll() handle.close() content = data or "" end end result[fullPrefix] = { isFile = true, content = content, isBinary = isBinaryFile(absPath) } end end local function fetchFiles() local files = {} getFiles("", files, "computer_" .. computerID .. "/") return files end local function saveFile(path, content) path = cleanPath(path):gsub("^computer[" .. computerID .. "_]*/", "") local dir = fs.getDir(path) if not fs.exists(dir) then fs.makeDir(dir) end local f = fs.open(path, "w") f.write(content or "") f.close() end local function createFile(path) path = cleanPath(path):gsub("^computer[" .. computerID .. "_]*/", "") local dir = fs.getDir(path) if not fs.exists(dir) then fs.makeDir(dir) end if not fs.exists(path) then local f = fs.open(path, "w") f.close() end end local function createFolder(path) path = cleanPath(path):gsub("^computer[" .. computerID .. "_]*/", "") fs.makeDir(path) end local function renameFile(oldPath, newPath) oldPath = cleanPath(oldPath):gsub("^computer[" .. computerID .. "_]*/", "") newPath = cleanPath(newPath):gsub("^computer[" .. computerID .. "_]*/", "") fs.move(oldPath, newPath) end local function deleteFile(path) path = cleanPath(path):gsub("^computer[" .. computerID .. "_]*/", "") if fs.exists(path) then fs.delete(path) end end -- ========== 消息处理函数 ========== local function handleFetchFiles(reqId, sender) local success, result = pcall(fetchFiles) return { type = "file_operation_response", requestId = reqId, success = success, data = success and result or nil, error = success and nil or tostring(result), target_client_id = sender } end local function handleSaveFile(data, reqId, sender) local success, err = pcall(saveFile, data.path, data.content) return { type = "file_operation_response", requestId = reqId, success = success, error = success and nil or tostring(err), target_client_id = sender } end local function handleCreateFile(data, reqId, sender) local success, err = pcall(createFile, data.path) return { type = "file_operation_response", requestId = reqId, success = success, error = success and nil or tostring(err), target_client_id = sender } end local function handleCreateFolder(data, reqId, sender) local success, err = pcall(createFolder, data.path) return { type = "file_operation_response", requestId = reqId, success = success, error = success and nil or tostring(err), target_client_id = sender } end local function handleRename(data, reqId, sender) local success, err = pcall(renameFile, data.path, data.newPath) return { type = "file_operation_response", requestId = reqId, success = success, error = success and nil or tostring(err), target_client_id = sender } end local function handleDelete(data, reqId, sender) local success, err = pcall(deleteFile, data.path) return { type = "file_operation_response", requestId = reqId, success = success, error = success and nil or tostring(err), target_client_id = sender } end -- ========== 房间管理函数 ========== local function createRoom() local result, err = httpPost("/api/room", {}) if not result then error("无法创建房间: " .. tostring(err)) end return result.room_id end local function sendResponse(response) if response and roomId then local result, err = httpPost("/api/client/send", { room_id = roomId, message = response }) if not result then log("3 发送响应失败: " .. tostring(err)) end end end local function pollMessages() while true do if not roomId then sleep(pollInterval) break end local result, err = httpPost("/api/client/receive", { room_id = roomId }) if result and result.success then local msg = result.message log(msg) if msg then local msgType = msg.type if msgType == "file_operation" or msgType == "file_operation_request" then local op = msg.operation_type or msg.type local data = msg.data or {} local reqId = msg.requestId or msg.request_id local sender = msg.sender_id local response if op == "fetch_files" then response = handleFetchFiles(reqId, sender) elseif op == "create_or_save_file" then response = handleSaveFile(data, reqId, sender) elseif op == "new_file" then response = handleCreateFile(data, reqId, sender) elseif op == "new_folder" then response = handleCreateFolder(data, reqId, sender) elseif op == "rename" then response = handleRename(data, reqId, sender) elseif op == "delete_file" then response = handleDelete(data, reqId, sender) else response = { type = "file_operation_response", requestId = reqId, success = false, error = "Unknown operation: " .. tostring(op), target_client_id = sender } end sendResponse(response) end end elseif err then log("2 轮询错误: " .. tostring(err)) -- 如果是连接错误,稍后再试 sleep(5) end end end -- ========== 主函数 ========== local function main() if not roomId then roomId = createRoom() log("创建新房间: " .. roomId) else log("使用现有房间: " .. roomId) end mainFrame:addProgram():execute(function() shell.run("shell") end):setSize("parent.w","parent.h") os.queueEvent("mouse_click",1,1,1) -- 启动消息轮询 mainFrame:addThread():start(pollMessages) log("客户端已启动。房间ID: " .. roomId) log("计算机ID: " .. computerID) log("按 Q 退出") -- 主循环 while true do local event, param1 = os.pullEvent() if event == "key" and param1 == keys.q then log("用户按 Q 退出") break end end end -- 启动主逻辑和Basalt事件循环 parallel.waitForAll(basalt.autoUpdate, main)