Michael Chen
bbdd5ce586
Added build script to solution files Added address to configure client connection destination (if proxied) Added automatic semantic versioning build
269 lines
8.2 KiB
Lua
269 lines
8.2 KiB
Lua
local secretToken = "$TOKEN"
|
|
local connectionUri = "$HOST"
|
|
local waitSeconds = 5
|
|
-- https://github.com/cc-tweaked/CC-Tweaked/blob/9cf70b10effeeed23e0e9c537bbbe0b2ff0d1a0f/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java#L29
|
|
-- Chunk size must be less than packet size (type reply, success, chunkids, content: chunk) 16 kb for buffer
|
|
local maxMessageSize = (128 - 16) * 1024
|
|
|
|
local function chunkString(value, chunkSize)
|
|
if not chunkSize then chunkSize = maxMessageSize end
|
|
local length = value:len()
|
|
local total = math.ceil(length / chunkSize)
|
|
local chunks = {}
|
|
if length == 0 then
|
|
total = 1
|
|
chunks[1] = ""
|
|
else
|
|
local i = 1
|
|
for i=1,total do
|
|
local pos = 1 + ((i - 1) * chunkSize)
|
|
chunks[i] = value:sub(pos, pos + chunkSize - 1)
|
|
end
|
|
end
|
|
return total, chunks
|
|
end
|
|
|
|
local function sendJson(socket, message)
|
|
return socket.send(textutils.serializeJSON(message))
|
|
end
|
|
|
|
local function sendResponse(socket, id, result, success)
|
|
if success == nil then success = 0 end
|
|
|
|
local total, chunks = chunkString(result)
|
|
for i, chunk in pairs(chunks) do
|
|
sendJson(socket, { type = "reply", id = id, result = chunk, chunk = i, total = total, success = success })
|
|
end
|
|
end
|
|
|
|
-- error: no rs system
|
|
-- return rssystem rs
|
|
local function getPeripheral(name)
|
|
local dev = peripheral.find(name)
|
|
if not dev then error({message = "No peripheral '"..name.."' attached to the computer!"}) end
|
|
return dev
|
|
end
|
|
|
|
local function runRsCommand(params)
|
|
local script, reason = loadstring("local rs = peripheral.find(\"rsBridge\") if not rs then error({message = \"RS Bridge is not attached!\"}) end return rs."..params.command)
|
|
if not script then error({message = "Invalid command: "..reason.."!"}) end
|
|
local result = table.pack(pcall(script))
|
|
local success = result[1]
|
|
if not success then error({message = "Command execution failed: "..result[2].."!"}) end
|
|
|
|
local retvals = {}
|
|
retvals.n = result.n - 1
|
|
for i=1,retvals.n do retvals[tostring(i)] = result[i + 1] end
|
|
return textutils.serializeJSON(retvals)
|
|
end
|
|
|
|
local function getPeripheralInfo(side)
|
|
return {type = peripheral.getType(side), methods = peripheral.getMethods(side), side = side}
|
|
end
|
|
|
|
local function getPeripheralList()
|
|
local pers = {}
|
|
for i,side in pairs(peripheral.getNames()) do pers[side] = getPeripheralInfo(side) end
|
|
return pers
|
|
end
|
|
|
|
-- error: any error during execution
|
|
-- return string result
|
|
local function getResponse(parsed)
|
|
if parsed.method == "energyusage" then
|
|
return tostring(getPeripheral("rsBridge").getEnergyUsage())
|
|
elseif parsed.method == "energystorage" then
|
|
return tostring(getPeripheral("rsBridge").getEnergyStorage())
|
|
elseif parsed.method == "listitems" then
|
|
return textutils.serializeJSON(getPeripheral("rsBridge").listItems())
|
|
elseif parsed.method == "listfluids" then
|
|
return textutils.serializeJSON(getPeripheral("rsBridge").listFluids())
|
|
elseif parsed.method == "craft" then
|
|
return tostring(getPeripheral("rsBridge").craftItem(parsed.params))
|
|
elseif parsed.method == "getitem" then
|
|
local item = getPeripheral("rsBridge").getItem(parsed.params)
|
|
if not item then error({message = "Requested item not found!"}) end
|
|
return textutils.serializeJSON(item)
|
|
elseif parsed.method == "command" then
|
|
return runRsCommand(parsed.params)
|
|
elseif parsed.method == "peripherals" then
|
|
return textutils.serializeJSON(getPeripheralList())
|
|
elseif parsed.method == "getonline" then
|
|
return textutils.serializeJSON(getPeripheral("playerDetector").getOnlinePlayers())
|
|
elseif parsed.method == "whereis" then
|
|
local pos = getPeripheral("playerDetector").getPlayerPos(parsed.params.username)
|
|
if not pos then return "null" end
|
|
return textutils.serializeJSON(pos)
|
|
elseif parsed.method == "send" then
|
|
if not parsed.params.username then
|
|
getPeripheral("chatBox").sendMessage(parsed.params.message, parsed.params.prefix)
|
|
else
|
|
getPeripheral("chatBox").sendMessageToPlayer(parsed.params.message, parsed.params.username, parsed.params.prefix)
|
|
end
|
|
return "true"
|
|
end
|
|
|
|
error({message = "No message handler for method: "..parsed.method.."!"})
|
|
end
|
|
|
|
local function logJSON(json, prefix)
|
|
if not prefix then prefix = "" end
|
|
for k,v in pairs(json) do
|
|
local key = prefix..k
|
|
if type(v) == "table" then
|
|
logJSON(v, key..".")
|
|
else
|
|
print(key, "=", textutils.serializeJSON(v))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- return bool success
|
|
local function handleMessage(socket, message)
|
|
local parsed, reason = textutils.unserializeJSON(message)
|
|
if not parsed then
|
|
print("Received message:", message)
|
|
printError("Message could not be parsed:", reason)
|
|
return false
|
|
end
|
|
|
|
pcall(function() print("Received JSON:") logJSON(parsed) end)
|
|
|
|
if parsed.type == "request" then
|
|
local success, result = pcall(function() return getResponse(parsed) end)
|
|
if not success then
|
|
if not result.message then
|
|
sendResponse(socket, parsed.id, result, 2)
|
|
else
|
|
sendResponse(socket, parsed.id, result.message, 1)
|
|
end
|
|
else
|
|
sendResponse(socket, parsed.id, result, 0)
|
|
end
|
|
return true
|
|
end
|
|
|
|
printError("Invalid message type:", parsed.type)
|
|
return false
|
|
end
|
|
|
|
local function responder(socket)
|
|
while true do
|
|
local message, binary = socket.receive()
|
|
if not not message and not binary then
|
|
if message == "outdated" then
|
|
printError("Current script is outdated! Please update from the host!")
|
|
return
|
|
end
|
|
handleMessage(socket, message)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function termWaiter()
|
|
os.pullEvent("terminate")
|
|
end
|
|
|
|
local function chatEventListener(socket)
|
|
while true do
|
|
event, username, message, uuid, hidden = os.pullEvent("chat")
|
|
sendJson(socket, {type = "chat", username = username, message = message, uuid = uuid, hidden = hidden})
|
|
print("Chat event relayed!")
|
|
end
|
|
end
|
|
|
|
local function peripheralDetachEventListener(socket)
|
|
while true do
|
|
event, side = os.pullEvent("peripheral_detach")
|
|
sendJson(socket, {type = "peripheral_detach", side = side})
|
|
print("Peripheral was detached!")
|
|
end
|
|
end
|
|
|
|
local function peripheralAttachEventListener(socket)
|
|
while true do
|
|
event, side = os.pullEvent("peripheral")
|
|
sendJson(socket, {type = "peripheral", peripheral = getPeripheralInfo(side) })
|
|
print("Peripheral was attached!")
|
|
end
|
|
end
|
|
|
|
local function listAsSet(list)
|
|
local asSet = {}
|
|
for i,elem in pairs(list) do asSet[elem] = true end
|
|
return asSet
|
|
end
|
|
|
|
local function joinSets(a, b)
|
|
local joined = {}
|
|
for elem,exists in pairs(a) do if exists then joined[elem] = true end end
|
|
for elem,exists in pairs(b) do if exists then joined[elem] = true end end
|
|
return joined
|
|
end
|
|
|
|
local function playerStatusEventListener(socket)
|
|
local players = {}
|
|
while true do
|
|
local pd = peripheral.find("playerDetector")
|
|
if not not pd then
|
|
players = listAsSet(pd.getOnlinePlayers())
|
|
break
|
|
end
|
|
printError("playerDetector not connected!")
|
|
sleep(5)
|
|
end
|
|
while true do
|
|
local pd = peripheral.find("playerDetector")
|
|
if not not pd then
|
|
local newPlayers = listAsSet(pd.getOnlinePlayers())
|
|
for player,_ in pairs(joinSets(players, newPlayers)) do
|
|
if players[player] and (not newPlayers[player]) then
|
|
sendJson(socket, {type = "playerstatus", player = player, status = false})
|
|
elseif (not players[player]) and newPlayers[player] then
|
|
sendJson(socket, {type = "playerstatus", player = player, status = true})
|
|
end
|
|
end
|
|
players = newPlayers
|
|
end
|
|
sleep(1)
|
|
end
|
|
end
|
|
|
|
local function eventListeners(socket)
|
|
parallel.waitForAny(
|
|
termWaiter,
|
|
function() chatEventListener(socket) end,
|
|
function() playerStatusEventListener(socket) end,
|
|
function() peripheralDetachEventListener(socket) end,
|
|
function() peripheralAttachEventListener(socket) end
|
|
)
|
|
end
|
|
|
|
local function socketClient()
|
|
print("Connecting to the socket server at "..connectionUri.."...")
|
|
local socket, reason = http.websocket(connectionUri)
|
|
if not socket then error("Socket server could not be reached: "..reason) end
|
|
print("Connection successful!")
|
|
|
|
socket.send("login="..secretToken)
|
|
parallel.waitForAny(
|
|
function() responder(socket) end,
|
|
function() eventListeners(socket) end
|
|
)
|
|
socket.close()
|
|
end
|
|
|
|
local function main()
|
|
while true do
|
|
local status, error = pcall(socketClient)
|
|
if status then break end
|
|
printError("An uncaught exception was raised:", error)
|
|
printError("Restarting in", waitSeconds, "seconds...")
|
|
sleep(waitSeconds)
|
|
end
|
|
end
|
|
|
|
local oldPullEvent = os.pullEvent
|
|
os.pullEvent = os.pullEventRaw
|
|
pcall(main)
|
|
os.pullEvent = oldPullEvent |