2022-01-16 21:31:07 +01:00
local secretToken = " $TOKEN "
2022-01-16 22:29:50 +01:00
local connectionUri = " $HOST "
2022-01-16 21:31:07 +01:00
local waitSeconds = 5
2022-01-18 10:43:15 +01:00
-- 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
2022-01-16 21:31:07 +01:00
local function chunkString ( value , chunkSize )
2022-01-18 10:43:15 +01:00
if not chunkSize then chunkSize = maxMessageSize end
2022-01-16 21:31:07 +01:00
local length = value : len ( )
local total = math.ceil ( length / chunkSize )
local chunks = { }
2022-01-17 15:24:04 +01:00
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
2022-01-16 21:31:07 +01:00
end
return total , chunks
end
local function sendJson ( socket , message )
return socket.send ( textutils.serializeJSON ( message ) )
end
local function sendResponse ( socket , id , result , success )
2022-01-17 15:24:04 +01:00
if success == nil then success = 0 end
2022-01-16 21:31:07 +01:00
local total , chunks = chunkString ( result )
for i , chunk in pairs ( chunks ) do
2022-01-17 16:22:15 +01:00
sendJson ( socket , { type = " reply " , id = id , result = chunk , chunk = i , total = total , success = success } )
2022-01-16 21:31:07 +01:00
end
end
-- error: no rs system
-- return rssystem rs
local function getPeripheral ( name )
local dev = peripheral.find ( name )
2022-01-17 15:24:04 +01:00
if not dev then error ( { message = " No peripheral ' " .. name .. " ' attached to the computer! " } ) end
2022-01-16 21:31:07 +01:00
return dev
end
2022-01-17 15:24:04 +01:00
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
2022-01-18 14:44:31 +01:00
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
2022-01-16 21:31:07 +01:00
-- 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 ) )
2022-01-16 21:51:37 +01:00
elseif parsed.method == " getitem " then
2022-01-17 15:24:04 +01:00
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 )
2022-01-18 14:44:31 +01:00
elseif parsed.method == " peripherals " then
return textutils.serializeJSON ( getPeripheralList ( ) )
2022-01-17 19:10:14 +01:00
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 )
2022-01-18 10:10:49 +01:00
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 "
2022-01-16 21:31:07 +01:00
end
2022-01-17 15:24:04 +01:00
error ( { message = " No message handler for method: " .. parsed.method .. " ! " } )
2022-01-16 21:31:07 +01:00
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 )
2022-01-17 15:24:04 +01:00
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
2022-01-16 21:31:07 +01:00
return true
end
printError ( " Invalid message type: " , parsed.type )
return false
end
2022-01-17 19:10:14 +01:00
local function responder ( socket )
2022-01-16 21:31:07 +01:00
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
2022-01-17 19:10:14 +01:00
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
2022-01-18 16:47:11 +01:00
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! " )
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
2022-01-17 19:10:14 +01:00
local function eventListeners ( socket )
parallel.waitForAny (
termWaiter ,
function ( ) chatEventListener ( socket ) end ,
2022-01-18 16:47:11 +01:00
function ( ) playerStatusEventListener ( socket ) end ,
2022-01-17 19:10:14 +01:00
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 ( )
2022-01-16 21:31:07 +01:00
end
local function main ( )
while true do
2022-01-17 19:10:14 +01:00
local status , error = pcall ( socketClient )
2022-01-16 21:31:07 +01:00
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