-- Hideously Smashed Together by Compilr, a Hideous Smash-Stuff-Togetherer, (c) 2014 oeed -- -- This file REALLLLLLLY isn't suitable to be used for anything other than being executed -- -- To extract all the files, run: " --extract" in the Shell -- local files = {["Views"]={["toolbar.view"]="{\ [\"Width\"]=\"100%\",\ [\"Height\"]=3,\ [\"Type\"]=\"View\",\ [\"BackgroundColour\"]=128,\ [\"Children\"]={\ [1]={\ [\"Y\"]=2,\ [\"X\"]=2,\ [\"Name\"]=\"GoStopButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\">\",\ [\"BackgroundColour\"]=1,\ [\"TextColour\"]=128\ },\ [2]={\ [\"Y\"]=2,\ [\"X\"]=\"100%,-23\",\ [\"Name\"]=\"LogButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Log\",\ [\"BackgroundColour\"]=1,\ [\"TextColour\"]=128,\ [\"Toggle\"]=false\ },\ [3]={\ [\"Y\"]=2,\ [\"X\"]=\"100%,-17\",\ [\"Name\"]=\"SettingsButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Settings\",\ [\"BackgroundColour\"]=1,\ [\"TextColour\"]=128,\ [\"Toggle\"]=false\ },\ [4]={\ [\"Y\"]=2,\ [\"X\"]=\"100%,-6\",\ [\"Name\"]=\"QuitButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Quit\",\ [\"BackgroundColour\"]=1,\ [\"TextColour\"]=128\ },\ [5]={\ [\"Y\"]=2,\ [\"X\"]=6,\ [\"Name\"]=\"StatusLabel\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Stopped\",\ [\"TextColour\"]=1\ },\ },\ }",["main.view"]="{\ [\"Children\"]={\ [1]={\ [\"Y\"]=1,\ [\"X\"]=1,\ [\"Name\"]=\"Toolbar\",\ [\"Type\"]=\"View\",\ [\"InheritView\"]=\"toolbar\"\ },\ [2]={\ [\"X\"]=1,\ [\"Y\"]=4,\ [\"Name\"]=\"SettingsView\",\ [\"Type\"]=\"SettingsView\",\ [\"Width\"]=\"100%\",\ [\"Height\"]=\"100%,-3\",\ [\"Visible\"]=false,\ [\"InheritView\"]=\"settings\"\ },\ [3]={\ [\"X\"]=1,\ [\"Y\"]=4,\ [\"Name\"]=\"LogView\",\ [\"Type\"]=\"LogView\",\ [\"Width\"]=\"100%\",\ [\"Height\"]=\"100%,-3\",\ [\"Visible\"]=false\ },\ },\ [\"BackgroundColour\"]=1,\ [\"ToolBarColour\"]=128,\ [\"ToolBarTextColour\"]=1\ }",["nomodem.view"]="{\ [\"Children\"]={\ [1]={\ [\"Y\"]=\"50%,-2\",\ [\"X\"]=1,\ [\"Width\"]=\"100%\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"No Modem Attached!\",\ [\"TextColour\"]=16384,\ [\"Align\"]=\"Center\"\ },\ [2]={\ [\"Y\"]=\"50%\",\ [\"X\"]=\"10%\",\ [\"Width\"]=\"80%\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Please attach a wireless modem to use Quest Server.\",\ [\"Align\"]=\"Center\"\ },\ [3]={\ [\"Y\"]=\"100%,-1\",\ [\"X\"]=\"100%,-6\",\ [\"Name\"]=\"QuitButton\",\ [\"Type\"]=\"Button\",\ [\"Text\"]=\"Quit\",\ },\ },\ [\"BackgroundColour\"]=1,\ [\"ToolBarColour\"]=128,\ [\"ToolBarTextColour\"]=1\ }",["settings.view"]="{\ [\"Children\"]={\ [1]={\ [\"Y\"]=2,\ [\"X\"]=3,\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Server Address\",\ [\"TextColour\"]=128\ },\ [2]={\ [\"Y\"]=2,\ [\"X\"]=19,\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"wifi://\",\ [\"TextColour\"]=256\ },\ [3]={\ [\"Y\"]=2,\ [\"X\"]=26,\ [\"Width\"]=20,\ [\"Name\"]=\"AddressTextBox\",\ [\"Type\"]=\"TextBox\",\ [\"Placeholder\"]=\"e.g. basesite\",\ },\ [4]={\ [\"Y\"]=5,\ [\"X\"]=3,\ [\"Width\"]=\"100%,-4\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Ok... so maybe there aren't that many settings. But hey, at least it's easy to use.\",\ [\"TextColour\"]=256\ },\ [5]={\ [\"Y\"]=9,\ [\"X\"]=3,\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Quest Server v1.0.0\",\ },\ [6]={\ [\"Y\"]=11,\ [\"X\"]=3,\ [\"Width\"]=\"100%,-4\",\ [\"Type\"]=\"Label\",\ [\"Text\"]=\"Quest and Quest Server were made by oeed using Bedrock, the source of all awesomeness. If you find a bug or have any questions give me a PM or post on the forum topic.\",\ [\"TextColour\"]=128\ },\ },\ }",},["README.md"]="Quest-Server\ ============\ \ A wireless modem based CCML server for Quest",["startup"]="local bedrockPath='' if OneOS then OneOS.LoadAPI('/System/API/Bedrock.lua', false)elseif fs.exists(bedrockPath..'/Bedrock')then os.loadAPI(bedrockPath..'/Bedrock')else if http then print('Downloading Bedrock...')local h=http.get('http://pastebin.com/raw.php?i=0MgKNqpN')if h then local f=fs.open(bedrockPath..'/Bedrock','w')f.write(h.readAll())f.close()h.close()os.loadAPI(bedrockPath..'/Bedrock')else error('Failed to download Bedrock. Is your internet working?') end else error('This program needs to download Bedrock to work. Please enable HTTP.') end end if Bedrock then Bedrock.BasePath = bedrockPath Bedrock.ProgramPath = shell.getRunningProgram() end\ \ local program = Bedrock:Initialise()\ \ os.loadAPI(program.ProgramPath .. '/APIs/Peripheral')\ os.loadAPI(program.ProgramPath .. '/APIs/Wireless')\ \ local serverRunning = false\ \ local messageLevel = {\ Info = 'Info',\ Success = 'Success',\ Warning = 'Warning',\ Error = 'Error',\ }\ \ local function logMsg(msg, level)\ level = level or messageLevel.Info\ program:GetObject('LogView'):AddItem('[' .. level .. '] '..msg, level)\ end\ \ local defaultSettings = {\ address = nil\ }\ \ local settings = {}\ \ local function saveSettings()\ logMsg('Saving settings.')\ local f = fs.open('.QuestServer.settings', 'w')\ settings.address = program:GetObject('AddressTextBox').Text\ if f then\ f.write(textutils.serialize(settings))\ f.close()\ end\ end\ \ local function loadSettings()\ logMsg('Loading settings.')\ local f = fs.open('.QuestServer.settings', 'r')\ if f then\ settings = textutils.unserialize(f.readAll())\ f.close()\ else\ logMsg('No settings file, using default.', messageLevel.Warning)\ settings = defaultSettings\ saveSettings()\ end\ \ program:GetObject('AddressTextBox').Text = settings.address or ''\ end\ \ local function switchView(name)\ local viewNames = {\ 'Settings',\ 'Log',\ }\ \ for i, v in ipairs(viewNames) do\ if name == v then\ program:GetObject(v .. 'View').Visible = true\ program:GetObject(v .. 'Button').Toggle = true\ else\ program:GetObject(v .. 'View').Visible = false\ program:GetObject(v .. 'Button').Toggle = false\ end\ end\ \ program:SetActiveObject()\ end\ \ local availableTimer = nil\ \ local startServer = nil\ local stopServer = nil\ \ local function checkNameAvailable(name)\ logMsg('Checking address clashes: '..name)\ if name:match(\"%W\") then\ logMsg('Invalid address!', messageLevel.Error)\ stopServer('Invalid Address')\ switchView('Settings')\ else\ Wireless.SendMessage(Wireless.Channels.QuestServerNameAvailable, name)\ availableTimer = program:StartTimer(function()\ if availableTimer and name == settings.address then\ logMsg('No address clashes found!', messageLevel.Success)\ availableTimer = nil\ startServer(true)\ end\ end, 1)\ end\ end\ \ function stopServer(reason)\ logMsg('Stopping server: ' .. reason or 'Stopped', messageLevel.Warning)\ serverRunning = false\ program:GetObject('GoStopButton').Text = '>'\ program:GetObject('StatusLabel').Text = reason or 'Stopped'\ end\ \ function startServer(available)\ logMsg('Starting server...')\ if settings.address and #settings.address > 0 then\ if available then\ logMsg('Server started!', messageLevel.Success)\ serverRunning = true\ program:GetObject('GoStopButton').Text = 'x'\ program:GetObject('StatusLabel').Text = 'Running'\ else\ program:GetObject('StatusLabel').Text = 'Checking Name'\ checkNameAvailable(settings.address)\ end\ else\ logMsg('Server could not start, address not set!', messageLevel.Error)\ stopServer('Address Not Set')\ switchView('Settings')\ end\ end\ \ program.OnKeyChar = function(self, event, keychar)\ if keychar == '\\\\' then\ os.reboot()\ end\ end\ \ program:RegisterEvent('modem_message', function(self, event, side, channel, replyChannel, message, distance)\ Wireless.HandleMessage(event, side, channel, replyChannel, message, distance)\ end)\ \ local function split(str, pat)\ local t = {}\ local fpat = \"(.-)\" .. pat\ local last_end = 1\ local s, e, cap = str:find(fpat, 1)\ while s do\ if s ~= 1 or cap ~= \"\" then\ table.insert(t,cap)\ end\ last_end = e+1\ s, e, cap = str:find(fpat, last_end)\ end\ if last_end <= #str then\ cap = str:sub(last_end)\ table.insert(t, cap)\ end\ return t\ end\ \ local function findLast(haystack, needle)\ local i=haystack:match(\".*\"..needle..\"()\")\ if i==nil then return nil else return i-1 end\ end\ \ local hex_to_char = function(x)\ return string.char(tonumber(x, 16))\ end\ \ local function urlUnencode( str )\ -- essentially reverses textutils.urlDecode\ if str then\ str = string.gsub(str, \"+\", \" \")\ str = string.gsub(str, \"\\r\\n\", \"\\n\")\ term.setTextColor(colors.black)\ str = str:gsub(\"%%(%x%x)\", hex_to_char)\ end\ return str \ end\ \ local function urlComponents(url)\ if url then\ urlUnencode(textutils.urlEncode(url))\ local components = {}\ local parts = split(url, '[\\\\/]+')\ if url:find('://') and parts[1]:sub(#parts[1]) == ':' then\ components.protocol = parts[1]:sub(1, #parts[1]-1)\ components.sansprotocol = url:sub(#components.protocol + 4)\ components.host = parts[2]\ components.fullhost = components.protocol .. '://' .. parts[2] .. '/'\ components.filename = fs.getName(url)\ components.filepath = url:sub(#components.fullhost)\ if components.filename == components.host then\ components.filename = ''\ end\ components.base = url:sub(1, findLast(url, '/'))\ components.get = {}\ components.filepathsansget = components.sansprotocol\ if url:find('?') then\ local start = url:find('?')\ components.filepathsansget = url:sub(#components.protocol + 4, start - 1)\ local getString = url:sub(start + 1)\ local values = split(getString, '&')\ for i, v in ipairs(values) do\ local keyvalue = split(v, '=')\ components.get[keyvalue[1]] = urlUnencode(keyvalue[2])\ end\ end\ return components\ end\ end\ end\ \ local function resolveFile(path)\ local parts = split(path, '[\\\\/]+')\ local realPath = '/Server Files'\ if #parts == 0 then\ parts = {''}\ end\ for i, v in ipairs(parts) do\ local tmpPath\ if #v == 0 then\ tmpPath = realPath\ else\ tmpPath = realPath .. '/' ..v\ end\ if fs.exists(tmpPath) then\ if fs.isDir(tmpPath) and i == #parts then\ local attempts = {\ tmpPath .. '/index.ccml',\ tmpPath .. '/index.html',\ }\ \ for i2, v2 in ipairs(attempts) do\ if fs.exists(v2) then\ return v2\ end\ end\ return nil\ end\ realPath = tmpPath\ else\ return nil\ end\ end\ return realPath\ end\ \ Wireless.Responder = function(event, side, channel, replyChannel, message, distance)\ if channel == Wireless.Channels.QuestServerRequest and serverRunning then\ if message.content:find('wifi://') == 1 then\ local parts = urlComponents(message.content)\ if parts.host and parts.host == settings.address then\ local path = resolveFile(parts.filepath)\ local content\ if path then\ local f = fs.open(path, 'r')\ if f then\ content = f.readAll()\ logMsg('File request successful: '..message.content, messageLevel.Success)\ end\ end\ if not content then\ logMsg('File request failed: '..message.content, messageLevel.Warning)\ end\ Wireless.SendMessage(replyChannel, {url = message.content, content = content}, nil, message.messageID)\ end\ end\ elseif channel == Wireless.Channels.QuestServerNameAvailable then\ if message.content == settings.address then\ logMsg('External address clash request clashed with this server: '..message.content, messageLevel.Warning)\ Wireless.SendMessage(replyChannel, 'IN_USE', nil, message.messageID)\ end\ elseif channel == Wireless.Channels.QuestServerNameAvailableReply and running then\ availableTimer = nil\ logMsg('Address clash request failed, address in use: '..message.content, messageLevel.Error)\ stopServer('Address In Use')\ switchView('Settings')\ end\ end\ \ local debounce = nil\ \ program.OnTimer = function(self, event, timer)\ if timer == debounce then\ saveSettings()\ end\ end\ \ program:Run(function()\ if Wireless.Present() then\ program:LoadView('main')\ \ if not fs.exists('/Server Files/') then\ fs.makeDir('/Server Files/')\ local f = fs.open('/Server Files/index.ccml', 'w')\ if f then\ f.write([[\ \ \ Welcome to your Quest Server Website!\ \ \ \
\ Welcome to your Quest Server Website!\
\
\

\ The files for this website are stored in the /Server Files/ folder on the server.\

\
\

\ If you haven't made a Quest web page before you should look for the CCML tutorial on the ComputerCraft forums.\

\
\ \ ]])\ f.close()\ end\ end\ \ loadSettings()\ Wireless.Initialise()\ \ switchView('Log')\ startServer()\ \ program:GetObject('SettingsButton').OnClick = function(self, event, side, x, y)\ switchView('Settings')\ end\ \ program:GetObject('LogButton').OnClick = function(self, event, side, x, y)\ switchView('Log')\ end\ \ program:GetObject('GoStopButton').OnClick = function(self, event, side, x, y)\ if serverRunning then\ stopServer()\ else\ startServer()\ end\ end\ \ program:GetObject('AddressTextBox').OnChange = function(self, event, keychar)\ if settings.address ~= program:GetObject('AddressTextBox').Text then\ stopServer('Address Changed')\ debounce = os.startTimer(1)\ end\ end\ \ else\ program:LoadView('nomodem')\ end\ \ program:GetObject('QuitButton').OnClick = function(self, event, side, x, y)\ term.setBackgroundColour(colours.black)\ term.setTextColor(colours.white)\ term.clear()\ term.setCursorPos(1, 1)\ print('Thanks for using Quest Server by oeed')\ program:Quit()\ end\ end)",["Objects"]={["SettingsView.lua"]="Inherit = 'View'",["LogView.lua"]="Inherit = 'View'\ Log = nil\ \ SaveLog = function(self)\ local str = table.concat(self.Log, '\\n')\ local f = fs.open('QuestServer.log', 'w')\ if f then\ f.write(str)\ f.close()\ end\ end\ \ AddItem = function(self, str, level)\ local messageColours = {\ Info = colours.blue,\ Success = colours.green,\ Warning = colours.orange,\ Error = colours.red,\ }\ table.insert(self.Log, str)\ \ local y = 1\ \ for i, v in ipairs(self.Children) do\ y = y + v.Height\ end\ \ self:AddObject({\ X = 1,\ Y = y,\ Width = \"100%\",\ Type = 'Label',\ Text = str,\ TextColour = messageColours[level]\ })\ \ self:SaveLog()\ end\ \ OnLoad = function(self)\ self.Log = {}\ end",},["APIs"]={["Peripheral"]="GetPeripheral = function(_type)\ for i, p in ipairs(GetPeripherals()) do\ if p.Type == _type then\ return p\ end\ end\ end\ \ Call = function(type, ...)\ local tArgs = {...}\ local p = GetPeripheral(type)\ peripheral.call(p.Side, unpack(tArgs))\ end\ \ local getNames = peripheral.getNames or function()\ local tResults = {}\ for n,sSide in ipairs( rs.getSides() ) do\ if peripheral.isPresent( sSide ) then\ table.insert( tResults, sSide )\ local isWireless = false\ if pcall(function()isWireless = peripheral.call(sSide, 'isWireless') end) then\ isWireless = true\ end \ if peripheral.getType( sSide ) == \"modem\" and not isWireless then\ local tRemote = peripheral.call( sSide, \"getNamesRemote\" )\ for n,sName in ipairs( tRemote ) do\ table.insert( tResults, sName )\ end\ end\ end\ end\ return tResults\ end\ \ GetPeripherals = function(filterType)\ local peripherals = {}\ for i, side in ipairs(getNames()) do\ local name = peripheral.getType(side):gsub(\"^%l\", string.upper)\ local code = string.upper(side:sub(1,1))\ if side:find('_') then\ code = side:sub(side:find('_')+1)\ end\ \ local dupe = false\ for i, v in ipairs(peripherals) do\ if v[1] == name .. ' ' .. code then\ dupe = true\ end\ end\ \ if not dupe then\ local _type = peripheral.getType(side)\ local formattedType = _type:sub(1, 1):upper() .. _type:sub(2, -1)\ local isWireless = false\ if _type == 'modem' then\ if not pcall(function()isWireless = peripheral.call(side, 'isWireless') end) then\ isWireless = true\ end \ if isWireless then\ _type = 'wireless_modem'\ formattedType = 'Wireless Modem'\ name = 'W '..name\ end\ end\ if not filterType or _type == filterType then\ table.insert(peripherals, {Name = name:sub(1,8) .. ' '..code, Fullname = name .. ' ('..side:sub(1, 1):upper() .. side:sub(2, -1)..')', Side = side, Type = _type, Wireless = isWireless, FormattedType = formattedType})\ end\ end\ end\ return peripherals\ end\ \ GetSide = function(side)\ for i, p in ipairs(GetPeripherals()) do\ if p.Side == side then\ return p\ end\ end\ end\ \ PresentNamed = function(name)\ return peripheral.isPresent(name)\ end\ \ CallType = function(type, ...)\ local tArgs = {...}\ local p = GetPeripheral(type)\ return peripheral.call(p.Side, unpack(tArgs))\ end\ \ CallNamed = function(name, ...)\ local tArgs = {...}\ return peripheral.call(name, unpack(tArgs))\ end\ \ GetInfo = function(p)\ local info = {}\ local buttons = {}\ if p.Type == 'computer' then\ local id = peripheral.call(p.Side:lower(),'getID')\ if id then\ info = {\ ID = tostring(id)\ }\ else\ info = {}\ end\ elseif p.Type == 'drive' then\ local discType = 'No Disc'\ local discID = nil\ local mountPath = nil\ local discLabel = nil\ local songName = nil\ if peripheral.call(p.Side:lower(), 'isDiskPresent') then\ if peripheral.call(p.Side:lower(), 'hasData') then\ discType = 'Data'\ discID = peripheral.call(p.Side:lower(), 'getDiskID')\ if discID then\ discID = tostring(discID)\ else\ discID = 'None'\ end\ mountPath = '/'..peripheral.call(p.Side:lower(), 'getMountPath')..'/'\ discLabel = peripheral.call(p.Side:lower(), 'getDiskLabel')\ else\ discType = 'Audio'\ songName = peripheral.call(p.Side:lower(), 'getAudioTitle')\ end\ end\ if mountPath then\ table.insert(buttons, {Text = 'View Files', OnClick = function(self, event, side, x, y)GoToPath(mountPath)end})\ elseif discType == 'Audio' then\ table.insert(buttons, {Text = 'Play', OnClick = function(self, event, side, x, y)\ if self.Text == 'Play' then\ disk.playAudio(p.Side:lower())\ self.Text = 'Stop'\ else\ disk.stopAudio(p.Side:lower())\ self.Text = 'Play'\ end\ end})\ else\ diskOpenButton = nil\ end\ if discType ~= 'No Disc' then\ table.insert(buttons, {Text = 'Eject', OnClick = function(self, event, side, x, y)disk.eject(p.Side:lower()) sleep(0) RefreshFiles() end})\ end\ \ info = {\ ['Disc Type'] = discType,\ ['Disc Label'] = discLabel,\ ['Song Title'] = songName,\ ['Disc ID'] = discID,\ ['Mount Path'] = mountPath\ }\ elseif p.Type == 'printer' then\ local pageSize = 'No Loaded Page'\ local _, err = pcall(function() return tostring(peripheral.call(p.Side:lower(), 'getPgaeSize')) end)\ if not err then\ pageSize = tostring(peripheral.call(p.Side:lower(), 'getPageSize'))\ end\ info = {\ ['Paper Level'] = tostring(peripheral.call(p.Side:lower(), 'getPaperLevel')),\ ['Paper Size'] = pageSize,\ ['Ink Level'] = tostring(peripheral.call(p.Side:lower(), 'getInkLevel'))\ }\ elseif p.Type == 'modem' then\ info = {\ ['Connected Peripherals'] = tostring(#peripheral.call(p.Side:lower(), 'getNamesRemote'))\ }\ elseif p.Type == 'monitor' then\ local w, h = peripheral.call(p.Side:lower(), 'getSize')\ local screenType = 'Black and White'\ if peripheral.call(p.Side:lower(), 'isColour') then\ screenType = 'Colour'\ end\ local buttonTitle = 'Use as Screen'\ if OneOS.Settings:GetValues()['Monitor'] == p.Side:lower() then\ buttonTitle = 'Use Computer Screen'\ end\ table.insert(buttons, {Text = buttonTitle, OnClick = function(self, event, side, x, y)\ self.Bedrock:DisplayAlertWindow('Reboot Required', \"To change screen you'll need to reboot your computer.\", {'Reboot', 'Cancel'}, function(value)\ if value == 'Reboot' then\ if buttonTitle == 'Use Computer Screen' then\ OneOS.Settings:SetValue('Monitor', nil)\ else\ OneOS.Settings:SetValue('Monitor', p.Side:lower())\ end\ OneOS.Reboot()\ end\ end)\ end\ })\ info = {\ ['Type'] = screenType,\ ['Width'] = tostring(w),\ ['Height'] = tostring(h),\ }\ end\ info.Buttons = buttons\ return info\ end",["Wireless"]="--This is just the OneOS Wireless API\ \ --OneOS uses channels between 4200 and 4300, avoid use where possible\ \ Channels = {\ Ignored = 4299,\ Ping = 4200,\ PingReply = 4201,\ QuestServerRequest = 4250,\ QuestServerRequestReply = 4251,\ QuestServerNameAvailable = 4252,\ QuestServerNameAvailableReply = 4253,\ }\ \ local function isOpen(channel)\ return Peripheral.CallType('wireless_modem', 'isOpen', channel)\ end\ \ local function open(channel)\ if not isOpen(channel) then\ Peripheral.CallType('wireless_modem', 'open', channel)\ end\ end\ \ Open = open\ \ local function close(channel)\ Peripheral.CallType('wireless_modem', 'close', channel)\ end\ \ local function closeAll()\ Peripheral.CallType('wireless_modem', 'closeAll')\ end\ \ local function transmit(channel, replyChannel, message)\ Peripheral.CallType('wireless_modem', 'transmit', channel, replyChannel, textutils.serialize(message))\ end\ \ function Present()\ if Peripheral.GetPeripheral('wireless_modem') == nil then\ return false\ else\ return true\ end\ end\ \ local function FormatMessage(message, messageID, destinationID)\ return {\ content = textutils.serialize(message),\ senderID = os.getComputerID(),\ senderName = os.getComputerLabel(),\ channel = channel,\ replyChannel = reply,\ messageID = messageID or math.random(10000),\ destinationID = destinationID\ }\ end\ \ local Timeout = function(func, time)\ time = time or 1\ parallel.waitForAny(func, function()\ sleep(time)\ --log('Timeout!'..time)\ end)\ end\ \ RecieveMessage = function(_channel, messageID, timeout)\ open(_channel)\ local done = false\ local event, side, channel, replyChannel, message = nil\ Timeout(function()\ while not done do\ event, side, channel, replyChannel, message = os.pullEvent('modem_message')\ if channel ~= _channel then\ event, side, channel, replyChannel, message = nil\ else\ message = textutils.unserialize(message)\ message.content = textutils.unserialize(message.content)\ if messageID and messageID ~= message.messageID or (message.destinationID ~= nil and message.destinationID ~= os.getComputerID()) then\ event, side, channel, replyChannel, message = nil\ else\ done = true\ end\ end\ end\ end,\ timeout)\ return event, side, channel, replyChannel, message\ end\ \ Initialise = function()\ if Present() then\ for i, c in pairs(Channels) do\ open(c)\ end\ end\ end\ \ HandleMessage = function(event, side, channel, replyChannel, message, distance)\ message = textutils.unserialize(message)\ message.content = textutils.unserialize(message.content)\ \ if channel == Channels.Ping then\ if message.content == 'Ping!' then\ SendMessage(replyChannel, 'Pong!', nil, message.messageID)\ end\ elseif message.destinationID ~= nil and message.destinationID ~= os.getComputerID() then\ elseif Wireless.Responder then\ Wireless.Responder(event, side, channel, replyChannel, message, distance)\ end\ end\ \ SendMessage = function(channel, message, reply, messageID, destinationID)\ reply = reply or channel + 1\ open(channel)\ open(reply)\ local _message = FormatMessage(message, messageID, destinationID)\ transmit(channel, reply, _message)\ return _message\ end\ \ Ping = function()\ local message = SendMessage(Channels.Ping, 'Ping!', Channels.PingReply)\ RecieveMessage(Channels.PingReply, message.messageID)\ end",},} local function run(tArgs) local fnFile, err = loadstring(files['startup'], 'startup') if err then error(err) end local function split(str, pat) local t = {} local fpat = "(.-)" .. pat local last_end = 1 local s, e, cap = str:find(fpat, 1) while s do if s ~= 1 or cap ~= "" then table.insert(t,cap) end last_end = e+1 s, e, cap = str:find(fpat, last_end) end if last_end <= #str then cap = str:sub(last_end) table.insert(t, cap) end return t end local function resolveTreeForPath(path, single) local _files = files local parts = split(path, '/') if parts then for i, v in ipairs(parts) do if #v > 0 then if _files[v] then _files = _files[v] else _files = nil break end end end elseif #path > 0 and path ~= '/' then _files = _files[path] end if not single or type(_files) == 'string' then return _files end end local oldFs = fs local env env = { fs = { list = function(path) local list = {} if fs.exists(path) then list = fs.list(path) end for k, v in pairs(resolveTreeForPath(path)) do if not fs.exists(path .. '/' ..k) then table.insert(list, k) end end return list end, exists = function(path) if fs.exists(path) then return true elseif resolveTreeForPath(path) then return true else return false end end, isDir = function(path) if fs.isDir(path) then return true else local tree = resolveTreeForPath(path) if tree and type(tree) == 'table' then return true else return false end end end, isReadOnly = function(path) if not fs.isReadOnly(path) then return false else return true end end, getName = fs.getName, getSize = fs.getSize, getFreespace = fs.getFreespace, makeDir = fs.makeDir, move = fs.move, copy = fs.copy, delete = fs.delete, combine = fs.combine, open = function(path, mode) if fs.exists(path) then return fs.open(path, mode) elseif type(resolveTreeForPath(path)) == 'string' then local handle = {close = function()end} if mode == 'r' then local content = resolveTreeForPath(path) handle.readAll = function() return content end local line = 1 local lines = split(content, '\n') handle.readLine = function() if line > #lines then return nil else return lines[line] end line = line + 1 end return handle else error('Cannot write to read-only file (compilr archived).') end else return fs.open(path, mode) end end }, loadfile = function( _sFile ) local file = env.fs.open( _sFile, "r" ) if file then local func, err = loadstring( file.readAll(), fs.getName( _sFile ) ) file.close() return func, err end return nil, "File not found: ".._sFile end, dofile = function( _sFile ) local fnFile, e = env.loadfile( _sFile ) if fnFile then setfenv( fnFile, getfenv(2) ) return fnFile() else error( e, 2 ) end end } setmetatable( env, { __index = _G } ) local tAPIsLoading = {} env.os.loadAPI = function( _sPath ) local sName = fs.getName( _sPath ) if tAPIsLoading[sName] == true then printError( "API "..sName.." is already being loaded" ) return false end tAPIsLoading[sName] = true local tEnv = {} setmetatable( tEnv, { __index = env } ) local fnAPI, err = env.loadfile( _sPath ) if fnAPI then setfenv( fnAPI, tEnv ) fnAPI() else printError( err ) tAPIsLoading[sName] = nil return false end local tAPI = {} for k,v in pairs( tEnv ) do tAPI[k] = v end env[sName] = tAPI tAPIsLoading[sName] = nil return true end env.shell = shell setfenv( fnFile, env ) fnFile(unpack(tArgs)) end local function extract() local function node(path, tree) if type(tree) == 'table' then fs.makeDir(path) for k, v in pairs(tree) do node(path .. '/' .. k, v) end else local f = fs.open(path, 'w') if f then f.write(tree) f.close() end end end node('', files) end local tArgs = {...} if #tArgs == 1 and tArgs[1] == '--extract' then extract() else run(tArgs) end