Advertisement
77wisher77

startUp

May 31st, 2021 (edited)
850
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.00 KB | None | 0 0
  1. local PROGRAM_VERSION = [["1.1"]]
  2. local PROGRAM_VERSION_CONVERTED = "local PROGRAM_VERSION = ".."[["..PROGRAM_VERSION.."]]"
  3. local PROGRAM_LINK = "W1sXgzy6"
  4. -- downloads and installs all Libraries and APIs, downloads any programs that the computer should have based on its label, also updates this very file - how nifty
  5. -- try `wget` shell command, apparently can save a url to file, is shell.wget(url, filename), works, for pastebin use raw links
  6. -- TODO: migrate all ["link"]'s to gitlab links, make repo public, this will avoid silly restrictions on pastebin
  7. --    or use pipelines to push live files to gitlab pages and collect them there via `shell.run("wget", url, filename)`
  8. -- TODO: change over checkVersion() & checkSelf() to wget so files can be collected from gitlab pages
  9. -- TODO: if api is already loaded then should reload it, as it may have been updated
  10. -- TODO: adds checks for remaining disk space (also check type of computer to get total diskspace, or is there a total disk space command?)
  11. -- TODO: add check to see if computer has a label, if not exit and ask user to label computer
  12.  
  13. -- a list of all dependencies (libraries/apis) that wishers programs use
  14.  
  15. local DEPENDENCIES = {
  16.   [1] = {
  17.     ["file"] = "wLibMining",
  18.     ["link"] = "rJjmGPwk",
  19.     ["path"] = "wisher/"
  20.   },
  21.   [2] = {
  22.     ["file"] = "wLibVariables",
  23.     ["link"] = "P1xXXYKC",
  24.     ["path"] = "wisher/"
  25.   }
  26. }
  27.  
  28. -- list of all programs
  29. -- programs are nested again to allow downloading multiple programs
  30. -- these programs should sit in the base directory unlike library/apis which sit in the wisher/ directory
  31. local COMPUTER_PROGRAMS = {
  32.   [1] = {
  33.     ["label"] = "stripMine",
  34.     [1] = {
  35.       ["file"] = "stripMine",
  36.       ["link"] = "nVAXKrBz",
  37.       ["autorun"] = false
  38.     },
  39.     [2] = {
  40.       ["file"] = "exhaust",
  41.       ["link"] = "HcBLDp4r",
  42.       ["autorun"] = false
  43.     }
  44.   },
  45.   [2] = {
  46.     ["label"] = "tester",
  47.     [1] = {
  48.       ["file"] = "exhaust",
  49.       ["link"] = "nVAXKrBz",
  50.       ["autorun"] = false
  51.     }
  52.   },
  53.   [3] = {
  54.     ["label"] = "exhaust",
  55.     [1] = {
  56.       ["file"] = "exhaust",
  57.       ["link"] = "nVAXKrBz",
  58.       ["autorun"] = true
  59.     }
  60.   }
  61. }
  62.  
  63. -- table which stores what happened to each file during startup, used for verbose, could be used for debugging or other useful things too
  64. local fileStatus = {
  65.   [1] = {
  66.     ["type"] = "Dependencies",
  67.     [1] = {
  68.       ["status"] = "Fresh Download: "
  69.     },
  70.     [2] = {
  71.     ["status"] = "Loaded: "
  72.     },
  73.     [3] = { -- in-case startup is manually executed after actual startup for some reason
  74.     ["status"] = "Already Loaded: "
  75.     },
  76.     [4] = {
  77.     ["status"] = "Updated: "
  78.     }
  79.   },
  80.   [2] = { -- Programs
  81.     ["type"] = "Programs",
  82.     [1] = {
  83.       ["status"] = "Fresh Download: "
  84.     },
  85.     [2] = {
  86.     ["status"] = "Loaded: "
  87.     },
  88.     [3] = { -- in-case startup is manually executed after actual startup for some reason
  89.     ["status"] = "Already Loaded: "
  90.     },
  91.     [4] = {
  92.     ["status"] = "Updated: "
  93.     }
  94.   }
  95. }
  96. -- fileStatusIndexType - used to determin whether for Dependency or Programs, range 1-2 || MANUALLY SET TO VALUE REQUIRED IN FUNCTION WHICH UTILIZES
  97. -- fileStatusIndexStatus - used to determin whether for Downloaded, Loaded, Already Loaded or updated files range 1-4 in order
  98. -- fileStatusIndexFilename - needs to be logged locally for each IndexStatus
  99.  
  100. -- set this to true if any file fails something it should succeed, e.g. loading api/lib, freshly downloading program, updating program etc.
  101. -- if this is true then print a message to check a log file. and open another window (unfocused) with log?? how do windows work on these computers
  102. -- this is set true inside logFailed() as that is only called when something fails
  103. local fileStatusFailed = false
  104.  
  105. -- used to delete the log on each boot
  106. local logIndex = 0
  107.  
  108. -- helper function to get a particular type of application state, and persist the default if it's not set
  109. function getState(type)
  110.   local DEFAULT_STATE = 0
  111.  
  112.   local filepath = "wisher/_states/_" .. type
  113.  
  114.   if (fs.exists(filepath)) then
  115.     local handle = fs.open(filepath, "r")
  116.     local state = tonumber(handle.read())
  117.  
  118.     handle.close()
  119.  
  120.     return state
  121.   else
  122.     -- persist the default if no state has been persisted already
  123.     return setState(type, DEFAULT_STATE)
  124.   end
  125. end
  126.  
  127. -- helper function to persist a particular type of application state
  128. function setState(type, state)
  129.   local filepath = "wisher/_states/_" .. type
  130.   local handle = fs.open(filepath, "w")
  131.  
  132.   handle.write(state)
  133.   handle.close()
  134.  
  135.   return state
  136. end
  137.  
  138. -- returns the state, can be used for persistence accross file being closed (ie self update) or computer shutting down (chunk unloaded, server restarted etc.)
  139. function getStartupState()
  140.   return getState("startup")
  141. end
  142.  
  143. -- sets the state, used for file persistence
  144. function setStartupState(state)
  145.   setState("startup", state)
  146. end
  147.  
  148. -- used by checkSelf(), start() to determin what to do, 1=upToDate, 2=needs updating, 3=error(file not downloaded/copied), 4=Updated this boot
  149. function getStartupUpdate()
  150.   return getState("startupUpdate")
  151. end
  152.  
  153. -- sets the update status
  154. function setStartupUpdate(state)
  155.   setState("startupUpdate", state)
  156. end
  157.  
  158. -- returns the file PROGRAM_VERSION as a string for a specified file, this should be the first line in any live file
  159. function getVersion(filepath)
  160.   local handle = fs.open(filepath, "r")
  161.   local version = tostring(handle.readLine())
  162.   handle.close()
  163.   return version
  164. end
  165.  
  166. -- Appends the filenames of any failed files to a log with the server system date at the top of the file ie
  167. -- "programName: ".."status".." ".."failed"
  168. -- will mention whether it failed to download, update etc.
  169. -- sets fileStatusFailed to true which is used to print a message to read the log
  170. function logFailed(programName, programStatus)
  171.   local filepath = "wisher/_log/_startup"
  172.   logIndex = logIndex + 1
  173.  
  174.   if (logIndex == 1) then -- if this is the first file to be logged this boot then delete the previous log file to save storage space
  175.     fs.delete(filepath)
  176.   end
  177.  
  178.   local handle = fs.open(filepath, "a")
  179.  
  180.   if (logIndex == 1) then handle.writeLine(os.date()) end -- top line should be date for clarification
  181.  
  182.   handle.writeLine(programName .. ": " .. programStatus .. "Failed")
  183.   handle.close()
  184.   fileStatusFailed = true
  185. end
  186.  
  187. --  checks if a file needs updating by downloading and comparing the version number
  188. --  logs updates into fileStatus{}, logs fails to logFailed()
  189. --    we can always assume the 2nd fileStatus index is 4 since 4 = updated
  190. function checkVersion(fileCurrent, fileLink, verboseIndexType, verboseIndexFilename, ...)
  191.   --make sure to pass file path and file name if library for fileCurrent
  192.   local fileBasename = fileCurrent
  193.   local isUpdate = false
  194.  
  195.   if (#arg == 5) then
  196.     fileBasename = arg[5] -- used for checkDependecies where the fileCurrent includes path, this is to neaten verbose
  197.   end
  198.  
  199.   local remoteFileName = fileCurrent .. "VerCheck"
  200.   local localFileVersion = getVersion(fileCurrent)
  201.   shell.run("pastebin", "get", fileLink, remoteFileName)
  202.  
  203.   if (not fs.exists(remoteFileName)) then
  204.     -- if the file failed to download then log updated failed
  205.     logFailed(fileBasename, fileStatus[verboseIndexType][4])
  206.   end
  207.  
  208.   if (fs.exists(remoteFileName)) then
  209.     -- if the file successfully downloaded then proceed with logic and log the fileStatus
  210.     local remoteFileVersion = getVersion(remoteFileName)
  211.  
  212.     if (not (localFileVersion == remoteFileVersion)) then -- if the remoteFile version is different then delete current and rename new
  213.       fs.delete(fileCurrent)
  214.       fs.move(remoteFileName, fileCurrent)
  215.       fileStatus[verboseIndexType][4][verboseIndexFilename] = fileBasename
  216.       isUpdate = true
  217.     end
  218.  
  219.     if (localFileVersion == remoteFileVersion) then -- if file versions match then delete the comparison file
  220.       fs.delete(remoteFileName)
  221.     end
  222.   end
  223.  
  224.   return isUpdate
  225. end
  226.  
  227. -- Checks to see if this very program needs updating
  228. -- sets a value in a file to used persistently to determin whether the file needs updating
  229. -- refer to getStartupUpdate() comments to know what the numbers mean
  230. function checkSelf()
  231.   local fileCurrent = "startup"
  232.   local remoteFileName = fileCurrent .. "VerCheck"
  233.   shell.run("pastebin", "get", PROGRAM_LINK, remoteFileName)
  234.  
  235.   if (not fs.exists(remoteFileName)) then
  236.     setStartupUpdate(3)
  237.     return
  238.   end
  239.  
  240.   if (fs.exists(remoteFileName)) then
  241.     local remoteFileVersion = getVersion(remoteFileName)
  242.  
  243.     if (not (PROGRAM_VERSION_CONVERTED == remoteFileVersion)) then
  244.       fs.copy(fileCurrent, fileCurrent .. "Updater")
  245.  
  246.       if (fs.exists(fileCurrent .. "Updater")) then
  247.         setStartupUpdate(2)
  248.         return
  249.       end
  250.  
  251.       if (not (fs.exists(fileCurrent .. "Updater"))) then
  252.         setStartupUpdate(3)
  253.         return
  254.       end
  255.     end
  256.  
  257.     if (PROGRAM_VERSION_CONVERTED == remoteFileVersion) then
  258.         fs.delete(remoteFileName)
  259.         setStartupUpdate(1)
  260.         return
  261.     end
  262.   end
  263. end
  264.  
  265. -- Ensures all Libraries/Apis are downloaded, logs successes to fileStatus{}, logs failures to logFailed()
  266. function checkDependencies()
  267.   local libList = {} -- for libs physically located on the robot
  268.   local apiExist = false
  269.   local fileStatusIndexUpdate = 1 -- needs to be logged locally for each IndexStatus, index for the Filename, starts at 1 since we pass this into checkVersion() args, update if returns true, therefore first run must be 1 not 0
  270.   local fileStatusIndexDownload = 0
  271.  
  272.   for i, v in ipairs(DEPENDENCIES) do
  273.     local libName = DEPENDENCIES[i]["path"] .. DEPENDENCIES[i]["file"]
  274.     local libLink = DEPENDENCIES[i]["link"]
  275.     libList[i] = shell.resolveProgram(libName) -- attempts to add the filename of dependency[i] to libList to prove it exists
  276.  
  277.     if (libList[i] == libName) then --if the lib exists then check its version
  278.       apiExist = true
  279.  
  280.         if (checkVersion(libName, libLink, 1, fileStatusIndexUpdate, DEPENDENCIES[i]["file"])) then -- only update index if checkVersion actually updates
  281.           fileStatusIndexUpdate = fileStatusIndexUpdate + 1
  282.         end
  283.     end
  284.  
  285.     if (apiExist == false) then -- if the lib doesnt exist, download it by using the link in dependancy[i]
  286.       shell.run("pastebin", "get", libLink, libName)
  287.  
  288.       if (fs.exists(libName)) then
  289.         fileStatusIndexDownload = fileStatusIndexDownload + 1
  290.         fileStatus[1][1][fileStatusIndexDownload] = DEPENDENCIES[i]["file"]
  291.       end
  292.  
  293.       if (not fs.exists(libName)) then
  294.           -- if the file did not download then log this
  295.           logFailed(DEPENDENCIES[i]["file"], fileStatus[1][1])
  296.       end
  297.     end
  298.     apiExist = false
  299.   end
  300. end
  301.  
  302. -- Dynamically initializes DEPENDENCIES globally, only if one with the same name doesnt already exist, logs successes to fileStatus{}, failures to logFailed()
  303. function initializeDependencies()
  304.   local fileStatusIndexAlready = 0 -- needs to be logged locally for each IndexStatus
  305.   local fileStatusIndexLoaded = 0
  306.  
  307.   for keyLibIndex, valueLibName in ipairs(DEPENDENCIES) do
  308.     local libraryName = DEPENDENCIES[keyLibIndex]["file"]
  309.     local libraryPath = DEPENDENCIES[keyLibIndex]["path"] .. DEPENDENCIES[keyLibIndex]["file"]
  310.  
  311.     if (_G[libraryName]) then
  312.       fileStatusIndexAlready = fileStatusIndexAlready + 1
  313.       fileStatus[1][3][fileStatusIndexAlready] = libraryName
  314.     end
  315.  
  316.     if (not _G[libraryName]) then
  317.       _G[libraryName] = require(libraryPath)
  318.       fileStatusIndexLoaded = fileStatusIndexLoaded + 1
  319.       fileStatus[1][2][fileStatusIndexLoaded] = libraryName
  320.  
  321.       if (not _G[libraryName]) then
  322.           -- if the file still isn't loaded then log it didnt load, it should have loaded by now
  323.           logFailed(libraryName, fileStatus[1][2])
  324.       end
  325.     end
  326.   end
  327. end
  328.  
  329. -- Checks label of computer and downloads appropriate program/s, logs successes to fileStatus{}, logs failures to logFailed()
  330. function checkPrograms()
  331.   local computerType = os.getComputerLabel()
  332.   computerType = string.lower(computerType)
  333.   local programList = {}
  334.   local programListIndex = 0 -- need to manually index this since our loops are inside loops and the numbers would get funky if we used their iterator
  335.   local programExist = false
  336.   local fileStatusIndexUpdate = 1 -- needs to start at 1 as checkVersion() parses this, only update if true is received back
  337.   local fileStatusIndexDownload = 0
  338.  
  339.   for iOne, vOne in ipairs(COMPUTER_PROGRAMS) do
  340.     local computerLabel = COMPUTER_PROGRAMS[iOne]["label"]
  341.     computerLabel = string.lower(computerLabel)
  342.  
  343.     for iTwo, vTwo in ipairs(COMPUTER_PROGRAMS[iOne]) do
  344.       programListIndex = programListIndex + 1
  345.       local programName = COMPUTER_PROGRAMS[iOne][iTwo]["file"]
  346.       local programLink = COMPUTER_PROGRAMS[iOne][iTwo]["link"]
  347.       programList[programListIndex] = shell.resolveProgram(programName)
  348.  
  349.       if (string.find(computerType, computerLabel)) then -- only download programs if the computer label matches the table label
  350.         if (programList[programListIndex] == programName) then -- checks if file already exists
  351.           programExist = true
  352.  
  353.           if checkVersion(programName, programLink, 2, fileStatusIndexUpdate) then
  354.             fileStatusIndexUpdate = fileStatusIndexUpdate + 1
  355.           end
  356.         end
  357.  
  358.         if (programExist == false) then -- if file doesnt exist, download
  359.           shell.run("pastebin", "get", programLink, programName)
  360.  
  361.           if (fs.exists(programName)) then
  362.             fileStatusIndexDownload = fileStatusIndexDownload + 1
  363.             fileStatus[2][1][fileStatusIndexDownload] = programName
  364.           end
  365.  
  366.           if (not fs.exists(programName)) then
  367.               logFailed(programName, fileStatus[2][1])
  368.           end
  369.         end
  370.       end
  371.       programExist = false
  372.     end
  373.   end
  374. end
  375.  
  376. -- cleans the screen then prints how each file was handled, ie Downloaded, Loaded, Already Loaded, Updated, also informs startup version
  377. function verboseFileStatus()
  378.   -- need 3 loops? Type loop, Status loop, filename loop
  379.   shell.run("clear")
  380.  
  381.   for iType, vType in ipairs(fileStatus) do
  382.     print(fileStatus[iType]["type"])
  383.  
  384.       for iStatus, vStatus in ipairs(fileStatus[iType]) do
  385.         write(fileStatus[iType][iStatus]["status"])
  386.  
  387.         for iFilename, vFilename in ipairs(fileStatus[iType][iStatus]) do
  388.         write(vFilename .. " ")
  389.         end
  390.  
  391.         write("\n")
  392.       end
  393.   end
  394.  
  395.   print("Startup File Version: " .. PROGRAM_VERSION)
  396. end
  397.  
  398. --  returns the program name to run, or false if none.
  399. --  only returns program name if ONLY 1 program is set to autorun, if more than 1 progam should be run automatically than handle within THAT program
  400. function shouldInitializeProgram()
  401.   local computerLabel = os.getComputerLabel()
  402.   local programName
  403.   local isAutorun = false
  404.   local autorunCount = 0
  405.  
  406.   for i, v in pairs(COMPUTER_PROGRAMS) do
  407.     if (computerLabel == COMPUTER_PROGRAMS[i]["label"]) then
  408.         for ia, va in pairs(COMPUTER_PROGRAMS[i]) do
  409.           if (COMPUTER_PROGRAMS[i][ia]["autorun"]) then
  410.               programName = COMPUTER_PROGRAMS[i][ia]["file"]
  411.               isAutorun = true
  412.               autorunCount = autorunCount + 1
  413.           end
  414.         end
  415.     end
  416.   end
  417.  
  418.   if (isAutorun) and (autorunCount == 1) then
  419.     return programName
  420.   end
  421.  
  422.   if (autorunCount > 1) then
  423.     isAutorun = false
  424.     return isAutorun
  425.   end
  426.  
  427.   if (not isAutorun) then
  428.     return isAutorun
  429.   end
  430. end
  431.  
  432. -- Main function, first checks if it needs to update itself, then updates dependencies, initializes dependencies, updates programs, verboses what happened to all prior, then initializes autorun program if there is ONE
  433. function start()
  434.   local fileCurrent = "startup"
  435.   local fileUpdater = fileCurrent .. "Updater"
  436.   local fileRemote = fileCurrent .. "VerCheck"
  437.  
  438.   -- state list: 0=start of file, 1=open copy to update self, 2=close original update original,3=open original and close updater and delete files, 4=Check/Init Dependencies - Check programs - verbose - autorun
  439.   if (getStartupState() == 0) then -- if just booted
  440.     checkSelf()
  441.  
  442.     if (getStartupUpdate() ~= 2) then
  443.       -- if startup is upToDate OR failed to check properly, then do everything else
  444.       setStartupState(4)
  445.     end
  446.  
  447.     if (getStartupUpdate() == 2) then
  448.       -- if startup needs updating then begin that process
  449.       setStartupState(1)
  450.       shell.run(fileUpdater)
  451.       return
  452.     end
  453.   end
  454.  
  455.   if (getStartupState() == 1) then -- if needs updating then
  456.     --rename updated file to startup, delete original startup, launch startup again
  457.     fs.delete(fileCurrent)
  458.     if (not fs.exists(fileCurrent)) then fs.move(fileRemote, fileCurrent) end
  459.  
  460.     setStartupState(2)
  461.     shell.run(fileCurrent)
  462.     return
  463.   end
  464.  
  465.   if (getStartupState() == 2) then --if updated then
  466.     -- delete updater, then continue with normal startup
  467.     fs.delete(fileUpdater)
  468.     setStartupState(3)
  469.     os.reboot()
  470.   end
  471.  
  472.   if (getStartupState() == 3) then -- if finished updating & rebooted then set that value and finish remaining startup processes
  473.     setStartupUpdate(4)
  474.     setStartupState(4)
  475.   end
  476.  
  477.   if (getStartupState() == 4) then -- normal startup stuff
  478.     checkDependencies()
  479.     initializeDependencies()
  480.     checkPrograms()
  481.     verboseFileStatus()
  482.  
  483.     if (getStartupUpdate() == 4) then print("Startup was updated!") end
  484.  
  485.     if (getStartupUpdate() == 3) then -- if self-update failed, log to logFailed()
  486.       logFailed("Startup", "Updated: ")
  487.     end
  488.  
  489.     if (fileStatusFailed) then
  490.       print("Something went wrong, check log for details")
  491.     end
  492.   end
  493.  
  494.   setStartupState(0) -- so the next boot sequence or run of startup will continue from the top of start()
  495.   fs.delete("wisher/_states/_startupUpdate") -- regenerate startupUpdate information each completed boot sequence
  496.  
  497.   if (shouldInitializeProgram() ~= false) then
  498.     -- if shouldInitializeProgram returns any value that ISNT false
  499.     sleep(5) -- time to read screen verbose before executing autorun
  500.     shell.run(shouldInitializeProgram())
  501.     return
  502.   end
  503. end
  504.  
  505. start()
  506.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement