Advertisement
Chaos_Cash

serverHelper.lua

Mar 15th, 2025 (edited)
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 96.72 KB | None | 0 0
  1. chatFunctions = {}
  2. droneFunctions = {}
  3. aboutFunctions = {}
  4. subFunctions = {}
  5. subFunctions.about = {}
  6. subFunctions.mail = {}
  7. subFunctions.computer = {}
  8. subFunctions.editWarp = {}
  9.  
  10. -- startup functions -------------
  11.  
  12. function startup()
  13. systemName = "server helper"
  14.  
  15. patchNotes = {{name = "1.2", notes = "\n- @about system now shows the pastebin link for the program.\n- With @about ranks you can now see information about the diffrent ranks.\n- Added a notification you get when you rankup.\n- Now there is an \"$\" before every insertion of chat commands to hide the message."},
  16.               {name = "1.2.1", notes = "\n- Fixed a bug that would crash the program every time someon used the @pay command while not entering a number as the second parameter."},
  17.               {name = "1.3", notes = "\n- Added the following chat commands: \n  - @warp\n  - @setWarp\n  - @delWarp\n\n- Renamed the following chat commands:\n  - @balanceScoreboard ---> @balTop\n  - @playtimeScoreboard ---> @timeTop"},
  18.               {name = "1.4", notes = "\n- Now you dont lose all your data when you change your minecraft name.\n- Added the subcommand @about player.\n- Added the @mail chat command."},
  19.               {name = "1.4.1", notes = "\nFixed the @about player subcommand, before you could only get information about yourself. Now you can get information about every other player as well."},
  20.               {name = "1.5", notes = '\n- Chat commands and player names are not case-sensitive anymore.\n- Fixed a bug that caused all teleportation commands to not work.\n- With the "@tp" chat command you can now also teleport to relative coordinates like with the vanilla "/tp" command.\n- Now you can insert the "@about player" command directly form the scoreboard.\n- With the "@about player" subcommand you can now also see the placement of the player in all scoreboards.'},
  21.               {name = "1.6", notes = '\n- Added the "@computer" chat command, this is used together with the server API\n- Added the server API, this API is used for interacting with the server helper with your own program. You can find a link to the api in "@about system"'},
  22.               {name = "1.7", notes = '\n- "~" now works the same as "~0" (As relative coordinates when using "@tp").\n- Added the "@computer unregister" subcommand.\n- Removed the "@setWarp" and "@delWarp" chat commands.\n- Added the "@editWarp" command with the following subcommands: \n    - create: Creates a warp with the given name.\n    - delete: Deletes the warp with the given name.\n    - setPublic: Makes the warp with the given name public wich means other players can use it as well.\n    - setPrivate: Makes the warp with the given name private wich means other players cant use the warp.'},
  23.               {name = "1.7.1", notes = 'Just a small update that adds the "@mail clear" subcommand wich deletes all read mails.'}
  24.              }
  25. curVersion = patchNotes[table.maxn(patchNotes)]["name"]
  26.  
  27.  
  28.  
  29.     for k,name in pairs(peripheral.getNames()) do
  30.         if peripheral.getType(name) == "modem" then
  31.             if peripheral.wrap(name).isWireless() then
  32.             rednet.open(name)
  33.             end
  34.         end
  35.     end
  36.  
  37.  
  38.  
  39. createRanks()
  40. createSettings()   
  41.          
  42. standardPlayerData = {
  43. ["rank"] = standardRank,
  44. ["settings"] = standardSettings,
  45. ["latestVersion"] = curVersion,
  46. ["warps"] = {},
  47. ["receivedMails"] = {},
  48. ["stats"] = {
  49.     ["playtime"] = 0,
  50.     ["balance"] = 250,
  51.     ["reputation"] = 0
  52.     },
  53. ["computerCount"] = 0,
  54. ["publicWarpCount"] = 0
  55. }
  56.  
  57.  
  58.  
  59.  
  60. mainProgramLink = "qQERbqsa"
  61. messageSenderLink = "FpXUacvK"
  62. serverAPILink = "EJ9S67hC"
  63.  
  64.  
  65.     if fs.exists(systemName .. "/refuelCoords") then
  66.     local file = fs.open(systemName .. "/refuelCoords","r")
  67.     refuelCoords = {["x"] = tonumber(file.readLine()) ,["y"] = tonumber(file.readLine()), ["z"] = tonumber(file.readLine())}
  68.     file.close()
  69.     else
  70.     term.clear()
  71.     print("x:")
  72.     local x = tonumber(read())
  73.     print("y:")
  74.     local y = tonumber(read())
  75.     print("z:")
  76.     local z = tonumber(read())
  77.     local file = fs.open(systemName .. "/refuelCoords","w")
  78.     file.writeLine(x)
  79.     file.writeLine(y)
  80.     file.writeLine(z)
  81.     file.flush()
  82.     file.close()
  83.     refuelCoords = {["x"] = x ,["y"] = y, ["z"] = z}
  84.     end
  85.  
  86.     if fs.exists(systemName .. "/firstStartupDate") then
  87.     local file = fs.open(systemName .. "/firstStartupDate","r")
  88.     firstStartupDate = file.readLine()
  89.     file.close()
  90.     else
  91.     firstStartupDate = os.date()
  92.     local file = fs.open(systemName .. "/firstStartupDate","w")
  93.     file.write(firstStartupDate)
  94.     file.flush()
  95.     file.close()
  96.     end
  97.  
  98. createFiles()
  99. getPeripherals()
  100.  
  101. onlinePlayers = playerDetector.getOnlinePlayers()
  102.  
  103. scrollmenuLen={}
  104. scrollmenuInfos={}
  105.  
  106. dropdown = {}
  107. dropdown["loaded"] = {}
  108. dropdown["loaded"]["len"] = 0
  109.  
  110.  
  111. requestedKeys = readFolder(programFolderName .. "/requestedKeys")
  112. registeredComputers = readFolder(programFolderName .. "/registeredComputers")
  113. publicWarps = readFolder(programFolderName .. "/publicWarps")
  114.  
  115.  
  116. playerData = {}
  117.  
  118.  
  119. sendTheseMessages = {}
  120.  
  121.  
  122. checkTeleportTimers = {}
  123.  
  124. createChatCommands()
  125.  
  126. loadPlayerData()
  127. createRankups()
  128.  
  129.  
  130. createScoreboard("playtime")
  131. createScoreboard("balance")
  132. getDroneData()
  133. createDroneActions()
  134. createRankInformationMessage()
  135. createSubcommands()
  136.  
  137.  
  138. addTimeTimer = os.startTimer(60)
  139. checkJoinedPlayersTimer = os.startTimer(10)
  140. end
  141.  
  142.  
  143.  
  144. function createDroneActions() -- Creates all drone actions (a drone action is a list of functions that the drone will perform one after the other)
  145. createDroneAction("refuel",{"teleport","standby"})
  146. createDroneAction("teleportPlayer",{"importPlayer","teleport","exportEntity"})
  147. end
  148.  
  149.  
  150.  
  151. function getPeripherals() -- checks if all peripherals that are needed are connected to the computer and connects them
  152. chatBoxes = {}
  153. droneNames = {}
  154.  
  155.     for k,peripheralName in pairs(peripheral.getNames()) do
  156.    
  157.         if string.find(peripheralName,"playerDetector") then
  158.         playerDetector = peripheral.wrap(peripheralName)
  159.         end
  160.        
  161.         if peripheral.getType(peripheralName) == "modem" then
  162.         rednet.open(peripheralName)
  163.         end
  164.        
  165.         if string.find(peripheralName,"chatBox") then
  166.         table.insert(chatBoxes, {peripheral = peripheral.wrap(peripheralName), cooldownTime = 0})
  167.         end
  168.        
  169.         if string.find(peripheralName,"drone_interface") then
  170.         table.insert(droneNames, peripheralName)
  171.         end
  172.        
  173.         if peripheral.getType(peripheralName) == "modem" then
  174.             if peripheral.wrap(peripheralName).isWireless() then
  175.             rednet.open(peripheralName)
  176.             end
  177.         end
  178.    
  179.     end
  180.    
  181.    
  182.     if not playerDetector then
  183.     error("No Player Detector found")
  184.     end
  185.    
  186.     if table.maxn(chatBoxes) < 1 then
  187.     error("No Chat Box found")
  188.     end
  189.  
  190. end
  191.  
  192.  
  193.  
  194. function createFiles() -- Creates all for the program necessary files/folders
  195. programFolderName = systemName
  196.  
  197.     if not fs.exists(programFolderName .. "/playerData") then
  198.     fs.makeDir(programFolderName .. "/playerData")
  199.     end
  200.  
  201. end
  202.  
  203.  
  204.  
  205. function createRanks() -- Creates all player ranks
  206. playerRanks = {}
  207. rankNames = {}
  208. standardRank = "Beginner"
  209.  
  210.  
  211. createRank("Owner",6,{["canChangeOtherPlayersSettings"] = true}, {["maxWarps"] = 100, ["maxComputers"]=100, ["maxPublicWarps"] = 100}, "They own the " .. systemName .. ".")
  212. createRank("Admin",5,{["canChangeOtherPlayersSettings"] = true}, {["maxWarps"] = 50, ["maxComputers"]=30, ["maxPublicWarps"] = 25}, "Same as a Supervisor, but can do more Stuff.")
  213. createRank("Supervisor",4,{}, {["maxWarps"] = 35, ["maxComputers"] = 15, ["maxPublicWarps"] = 10}, "A supervisor is there to, you guessed it, supervise! They look after stuff like drones and Bugs. You can only become a Supervisor by being granted this Rank by an Admin or Owner.")
  214. createRank("Elder",3,{}, {["maxWarps"] = 25, ["maxComputers"] = 9, ["maxPublicWarps"] = 5}, "To reach the Elder rank you need to play 3000 minutes on the server.")
  215. createRank("Member",2,{}, {["maxWarps"] = 10, ["maxComputers"] = 3, ["maxPublicWarps"] = 2}, "To reach the Member rank you need to play 500 minutes on the server.")
  216. createRank("Beginner",1,{}, {["maxWarps"] = 3, ["maxComputers"] = 1, ["maxPublicWarps"] = 0}, "Everyone starts out as a Beginner. As a Beginner you only have basic permissions, to get more you need to rank up.")
  217. createRank("Banned",0,{["canChangeOtherPlayersSettings"] = false}, {["maxWarps"] = 0, ["maxComputers"] = 0, ["maxPublicWarps"] = 0}, "You are Banned you cant do anything.")
  218. end
  219.  
  220.  
  221.  
  222. function createChatCommands() -- creates all chat commands (a chat command is a function that can be called by typing its name together with an @ in the chat)
  223. chatCommands = {}
  224. chatCommandNames = {}
  225. lowerToExactChatCommands = {}
  226.  
  227.  
  228. createChatCommand("help",1, "Lists all chat commands. As the first parameter either enter a number to change the help page or specify the name of a chat command to only get info about that command.")
  229. createChatCommand("about",1, "If you enter it without any parameters it gives you a list of what you can get information about.")
  230. createChatCommand("giveFeedback",1, "You can use this chat command to report bugs/problems or also to give ideas about new chat commands or things like settings.")
  231. createChatCommand("tp",1, "You can either enter a player as the first parameter or you can enter the x, y and z coordinates of your destination. If you enter \"Back\" as the first parameter you will be teleported to your last teleport position. The chunk you teleport to needs to be loaded.")
  232. createChatCommand("warp",1, "With this command you can enter the name of a warp you created with \"@editWarp create\" to teleport to that warp. You can also use \"@tp back\" to teleport back to where you came from.")
  233. createChatCommand("editWarp",1, "Lets you delete and create warps. You can also use this command to make your warps public.")
  234. createChatCommand("mail",1, "lets you send and read mails.")
  235. createChatCommand("listSettings",1, "Lists all your settings.")
  236. createChatCommand("changeSetting",1, "The first parameter either needs to be the name of a player or the name of a setting. If you enter the name of a player, then the setting of that player will be changed either your setting will be changed. After that just enter the value you want the setting to be changed to.")
  237. createChatCommand("balance",1, "Shows your balance. If you enter a player as the first parameter then it shows the balance of that player.")
  238. createChatCommand("pay",1, "Pays the player that you enter as the first parameter the amount of credits that you specified as the second parameter.")
  239. createChatCommand("timeTop",1, "Shows the playtime scoreboard. If the first parameter is blank, then it shows your position, if it is a number, then it shows the page with that number.")
  240. createChatCommand("balTop",1, "Shows the balance scoreboard. If the first parameter is blank, then it shows your position, if it is a number, then it shows the page with that number.")
  241. createChatCommand("computer",2, "Lets you do like things like requesting payment from other players with your own program.")
  242. createChatCommand("setRank",5, "Sets the rank of the player specified in the first parameter to the rank specified in the second parameter.")
  243. createChatCommand("ban",5, "Bans the player specified in the first parameter.")
  244. createChatCommand("unban",5, "Unbans the player specified in the first parameter.")
  245. end
  246.  
  247.  
  248.  
  249. function createSubcommands() -- creates all subcommands
  250. createSubcommand("about","system",1,"Gives information about the system.")
  251. createSubcommand("about","ranks",1,"Gives information about all player ranks.")
  252. createSubcommand("about","player",1,"Gives information about the player stated as the first parameter.")
  253.  
  254. createSubcommand("mail","send",1,"Lets you send mails. The first parameter is the receiving player, after that you just type in the text you want to send.")
  255. createSubcommand("mail","list",1,"Lists all your mails. You can also enter a page as the first parameter.")
  256. createSubcommand("mail","read",1,"Lets you read the mail you entered the id (number) of.")
  257. createSubcommand("mail","respond",1,"Sends a mail as a response to the mail you entered the id (number) of.")
  258. createSubcommand("mail","delete",1,"Deletes the mail you entered the id (number) of.")
  259. createSubcommand("mail","clear",1,"Deletes all read mails.")
  260.  
  261. createSubcommand("computer","requestKey",2,'Sends you a key you need to be able to use the server api (you can find the link to the server api with "@about system").')
  262. createSubcommand("computer","clearKeys",2,"Deletes all the keys that you requested that you didnt use to register with a computer.")
  263. createSubcommand("computer","list",2,"Lists all your registered computers.")
  264. createSubcommand("computer","pay",2,"Pays the computer with the id specifed in the first parameter the amount specifed in the second parameter.")
  265. createSubcommand("computer","unregister",2,"Unregisters the computer with the id given as the first parameter.")
  266.  
  267. createSubcommand("editWarp","create",1,"Creates a warp with the given name.")
  268. createSubcommand("editWarp","delete",1,"Deletes a warp with the given name.")
  269. createSubcommand("editWarp","list",1,"Lists all of your private and public warps.")
  270. createSubcommand("editWarp","setPublic",2,"Sets the given warp to be public, wich means other players can use it as well.")
  271. createSubcommand("editWarp","setPrivate",2,"Sets the given warp to be private, wich means other players cant it.")
  272. end
  273.  
  274.  
  275.  
  276. function createSettings() -- creates all player settings
  277. settings = {}
  278. standardSettings = {}
  279.  
  280. createSetting("disableJoinMessage",false,0, "Disables the joining message.")
  281. createSetting("disablePatchMessage",false,0, "Disables the patch notes notifications.")
  282. createSetting("disableMailNotifications",false,0, "Disables all mail notifications.")
  283. createSetting("disableAllMessages",false,0, "Disables all system messages (not recommended, if you set this to true a lot of essential functions will be disabled for you).")
  284. createSetting("canChangeOtherPlayersSettings",false,5, "Allows you, to change other players settings.")
  285. end
  286.  
  287.  
  288.  
  289. function createRankups() -- creates all rankup conditions (such as how long you need to play to get a new rank)
  290. createRankup("Member", "playtime", 500)
  291. createRankup("Elder", "playtime", 3000)
  292. end
  293.  
  294.  
  295.  
  296. function loadPlayerData() -- loads the data of all registered players
  297. playerNames = fs.list(programFolderName .. "/playerData")
  298. lowerToExactPlayerNames = {}
  299.     for k,playerName in pairs(playerNames) do
  300.     lowerToExactPlayerNames[string.lower(playerName)] = playerName
  301.     getPlayerData(playerName)
  302.     updatePlayerData(playerName)
  303.     end
  304. end
  305.  
  306.  
  307.  
  308. function updatePlayerData(playerName) -- checks if the settings of any player are outdated and changes them if necessary
  309.  
  310.     for dataName, dataValue in pairs(standardPlayerData) do
  311.         if not playerData[playerName][dataName] then
  312.         playerData[playerName][dataName] = dataValue
  313.         end
  314.     end
  315.  
  316.  
  317.  
  318.     for settingName, settingValue in pairs(standardSettings) do
  319.         if not playerData[playerName]["settings"][settingName] then
  320.         playerData[playerName]["settings"][settingName] = settingValue
  321.         end
  322.     end
  323.    
  324.     for settingName, settingValue in pairs(playerData[playerName]["settings"]) do
  325.         if not settings[settingName] then
  326.         playerData[playerName]["settings"][settingName] = nil
  327.         end
  328.     end
  329.  
  330. savePlayerData(playerName)
  331. end
  332.  
  333.  
  334.  
  335. function createRankInformationMessage() -- creates the message about ranks that will be sent with the aboutFunctions.ranks function
  336. rankInformationMessage = {}
  337.  
  338.     for k,rankName in ipairs(rankNames) do
  339.     local rankTable = playerRanks[rankName]
  340.     table.insert(rankInformationMessage, {text = "\n" .. rankName, color = "green", hoverEvent = hoverMessage(rankTable["info"])})
  341.     table.insert(rankInformationMessage, {text = " - Permission level: ", hoverEvent = hoverMessage(rankTable["info"])})
  342.     table.insert(rankInformationMessage, {text = rankTable["permissionLevel"], color = "green", hoverEvent = hoverMessage(rankTable["info"])})
  343.     end
  344. end
  345. -------------------------------------
  346.  
  347.  
  348.  
  349.  
  350.  
  351. -- main functions -------------------
  352.  
  353. function main() -- the main function that runs the whole time and listens to events
  354.  
  355.     while true do
  356.     os.startTimer(0.25)
  357.     info1,info2,info3,info4 = os.pullEvent()
  358.     performDroneActions()
  359.        
  360.         if info1 == "rednet_message" then
  361.             if registeredComputers[info2] and info4 == "server" then
  362.             local message = getMessageTable(info3,registeredComputers[info2]["key"])
  363.            
  364.            
  365.                 if message["action"] == "requestPayment" then
  366.                 message["amount"] = tonumber(message["amount"])
  367.                     if type(message["amount"]) ~= "number" then
  368.                     computerError(info2,"The amount you request a player to pay needs to be a number.")
  369.                     goto skipMessage
  370.                     end
  371.                    
  372.                     if message["amount"] <= 0 then
  373.                     computerError(info2,"The amount you request a player to pay needs to be a positive number.")
  374.                     goto skipMessage
  375.                     end
  376.                    
  377.                     if type(message["text"]) ~= "string" then
  378.                     computerError(info2,"The text of the requestPayment function needs to a string")
  379.                     goto skipMessage
  380.                     end
  381.                    
  382.                     if not playerData[exactName(message["player"])] then
  383.                     computerError(info2,"The player you request a payment from needs to be a registered player.")
  384.                     goto skipMessage
  385.                     end
  386.                    
  387.                 sendMessage({{text = message["text"], hoverEvent = hoverMessage("Shift + click to pay " .. message["amount"] .. "c"), insertion = "$@computer pay " .. info2 .. " " .. message["amount"]}}, message["player"])
  388.                 end
  389.  
  390.  
  391.                 if message["action"] == "pay" then
  392.                 message["amount"] = tonumber(message["amount"])
  393.                     if type(message["amount"]) ~= "number" then
  394.                     computerError(info2,"The amount you pay a player needs to be a number.")
  395.                     goto skipMessage
  396.                     end
  397.                    
  398.                     if message["amount"] <= 0 then
  399.                     computerError(info2,"The amount you pay a player needs to be a positive number.")
  400.                     goto skipMessage
  401.                     end
  402.                    
  403.                     if message["amount"] > registeredComputers[info2]["balance"] then
  404.                     computerError(info2,"The Computer dosnt have enaugh credits to pay this amount.")
  405.                     goto skipMessage
  406.                     end
  407.                    
  408.                     if not playerData[exactName(message["player"])] then
  409.                     computerError(info2,"The player you pay needs to be a registered player.")
  410.                     goto skipMessage
  411.                     end
  412.                    
  413.                 payPlayerFromComputer(info2,message["player"],message["amount"])
  414.                    
  415.                     if type(message["text"]) == "string" then
  416.                     sendMessage(message["text"], message["player"])
  417.                     end
  418.                 end
  419.  
  420.  
  421.                 if message["action"] == "getBalance" then
  422.                 sendRednet(info2,{["action"] = "sendingBalance", ["balance"] = registeredComputers[info2]["balance"]})
  423.                 end
  424.  
  425.  
  426.                 if message["action"] == "getOwner" then
  427.                 sendRednet(info2,{["action"] = "sendingOwner", ["owner"] = registeredComputers[info2]["owner"]})
  428.                 end
  429.  
  430.        
  431.             else
  432.             registerComputer(info2,info3)
  433.             end
  434.        
  435.         ::skipMessage::
  436.         end
  437.        
  438.        
  439.        
  440.         if info1 == "chat" and not isMessageDouble() then
  441.         local uuid = info4
  442.         local uuidName = getPlayerNameByuuid(uuid)
  443.             if not playerData[info2] then -- checks if player is registered
  444.                 if uuidName then
  445.                 playerData[info2] = playerData[uuidName]
  446.                 playerData[info2]["uuid"] = uuid
  447.                 savePlayerData(info2)
  448.                 fs.delete(programFolderName .. "/playerData/" .. uuidName)
  449.                 sendMessage("It seems like you renamed yourself, your data from your old name has been succesfully transferred to your new one.", info2, "green")
  450.                 else
  451.                 getPlayerData(info2)
  452.                 end
  453.             end
  454.            
  455.             if not playerData[info2]["uuid"] then
  456.             playerData[info2]["uuid"] = uuid
  457.             savePlayerData(info2)
  458.             end
  459.            
  460.         returnMessage,params = getChatCommand(info2,info3)
  461.             if returnMessage then -- checks if the message was a chat command
  462.             executingPlayer = info2
  463.                
  464.                
  465.                 if type(returnMessage) == "function" then -- checks if the chat command is valid
  466.                 local chatErrorMessage = returnMessage(params)
  467.                     if chatErrorMessage then
  468.                     sendMessage(chatErrorMessage,executingPlayer,"red")
  469.                     end
  470.                 else
  471.                 sendMessage(returnMessage,executingPlayer,"red") -- sends an error message
  472.                 end
  473.             end
  474.         end
  475.        
  476.        
  477.        
  478.         if info1 == "timer" then
  479.        
  480.             if info2 == addTimeTimer then
  481.             addTime()
  482.             checkRankups()
  483.             giveMoney()
  484.             end
  485.            
  486.            
  487.             if info2 == checkJoinedPlayersTimer then -- checks if a player joined the server
  488.             sendJoinMessages()
  489.             sendPatchNotes()
  490.             checkJoinedPlayersTimer = os.startTimer(10)
  491.             end
  492.        
  493.         end
  494.        
  495.     end
  496.    
  497. end
  498.  
  499. ------------------------------------
  500.  
  501.  
  502.  
  503.  
  504.  
  505. -- external computer functions ------------------------------
  506.  
  507. function payPlayerFromComputer(computerId,playerName,amount)
  508.     if registeredComputers[computerId]["balance"] < amount then
  509.     error("amount is higher then balance")
  510.     end
  511.    
  512.     if 0 >= amount then
  513.     error("amount needs to be positive")
  514.     end
  515.  
  516. registeredComputers[computerId]["balance"] = registeredComputers[computerId]["balance"] - amount
  517. saveComputerData(computerId)
  518.  
  519. playerData[playerName]["stats"]["balance"] = playerData[playerName]["stats"]["balance"] + amount
  520. savePlayerData(playerName,"stats")
  521.  
  522. updateScoreboard("balance",playerName)
  523.  
  524. sendRednet(info2,{["action"] = "paymentConfirmation", ["player"] = playerName, ["amount"] = amount})
  525. end
  526.  
  527.  
  528.  
  529. function computerError(id,errorMessage)
  530.     if not registeredComputers[id] then
  531.     error("This is not a registered computer.")
  532.     end
  533.  
  534. sendMail(registeredComputers[id]["owner"], "Your computer caused an error.", errorMessage)
  535. end
  536.  
  537.  
  538.  
  539. function generateKey()
  540. local usableChars = {"1","2","3","4","5","6","7","8","9","0","!","\"","§","$","%","&","/","(",")","?","\\","}","]","[","{","@","q","w","e","r","t","z","u","i","o","p","ü","+","a","s","d","f","g","h","j","k","l","ö","ä","#","y","x","c","v","b","n","m",".","-","<",">","Y","X","C","V","B","N","M",";",":","_","'","L","K","J","H","G","F","D","S","A","*","P","O","I","U","Z","T","R","E","W","Q","|","~"}local usableCount = #usableChars
  541. local key = ""
  542.  
  543.     for i = 1,16 do
  544.     key = key .. usableChars[math.random(1, usableCount)]
  545.     end
  546.    
  547. return key
  548. end
  549.  
  550.  
  551.  
  552. function encrypt(text,key)
  553. local result = ""
  554. local keyLength = #key
  555.  
  556.     for i = 1, #text do
  557.     local textByte = text:byte(i)
  558.     local keyByte = key:byte((i - 1) % keyLength + 1)
  559.     result = result .. string.char(bit.bxor(textByte, keyByte))
  560.     end
  561. return result
  562. end
  563.  
  564.  
  565.  
  566. function decrypt(encryptedText,key)
  567. return encrypt(encryptedText,key)
  568. end
  569.  
  570.  
  571.  
  572. function createMessageString(messageTable,key)
  573. local outTable = {}
  574. local returnString = "@@"
  575. local i = 1
  576.     for k,v in pairs(messageTable) do
  577.     outTable[i] = {["key"] = k, ["value"] = v}
  578.     i=i+1
  579.     end
  580.  
  581.     for i = 1, #outTable do
  582.     local i = math.random(1,#outTable)
  583.     returnString = returnString .. outTable[i]["key"] .. "=" .. outTable[i]["value"] .. ","
  584.     table.remove(outTable,i)
  585.     end
  586.  
  587.  
  588. returnString = encrypt(returnString,key)
  589.  
  590. return returnString
  591. end
  592.  
  593.  
  594.  
  595. function getMessageTable(messageString,key)
  596. local messageString = decrypt(messageString,key)
  597.  
  598.     if string.sub(messageString,1,2) == "@@" then
  599.     messageString = string.sub(messageString,3,-1)
  600.     else
  601.     return {}
  602.     end
  603.  
  604.  
  605. local returnTable = {}
  606.  
  607. local curPos = 1
  608.     while true do
  609.     local equalPos = string.find(messageString,"=",curPos)
  610.         if not equalPos then
  611.         break
  612.         end
  613.     local curKey = string.sub(messageString,curPos,equalPos-1)
  614.     curPos = equalPos+1
  615.     local commaPos = string.find(messageString,",",curPos)
  616.         if not commaPos then
  617.         break
  618.         end
  619.     local curValue = string.sub(messageString,curPos,commaPos-1)
  620.     curPos = commaPos+1
  621.     returnTable[curKey] = curValue
  622.     end
  623.    
  624. return returnTable
  625. end
  626.  
  627.  
  628.  
  629. function sendRednet(id,messageTable)
  630.     if not registeredComputers[id] then
  631.     error("This is not a registered computer.")
  632.     end
  633.  
  634. local messageString = createMessageString(messageTable,registeredComputers[id]["key"])
  635. rednet.send(id,messageString,"serverResponse")
  636. end
  637.  
  638.  
  639.  
  640. function registerComputer(id, messageString)
  641.  
  642.     for k, requestedKey in pairs(requestedKeys) do
  643.     print(decrypt(messageString,requestedKey["key"]))
  644.    
  645.     local curMessage = getMessageTable(messageString,requestedKey["key"])
  646.         if curMessage["action"] == "register" then
  647.         registeredComputers[id] = {}
  648.         registeredComputers[id]["key"] = requestedKey["key"]
  649.         registeredComputers[id]["balance"] =  0
  650.         registeredComputers[id]["owner"] = requestedKey["owner"]
  651.         saveComputerData(id)
  652.        
  653.         table.remove(requestedKeys,k)
  654.         saveTable(programFolderName .. "/requestedKeys", requestedKeys)
  655.        
  656.         sendRednet(id, {["action"]="registrationResponse",["registrationWorked"]="true"})
  657.         return
  658.         end
  659.     end
  660. print("")
  661. end
  662.  
  663.  
  664.  
  665. function getComputerData(id)
  666.     if not registeredComputers[id] then
  667.     error("This is not a registered computer.")
  668.     end
  669.    
  670.     if id == "all" then
  671.     registeredComputers = readFolder(programFolderName .. "/registeredComputers")
  672.     return
  673.     end
  674.    
  675. registeredComputers[id] = readFolder(programFolderName .. "/registeredComputers/" .. id)
  676. end
  677.  
  678.  
  679.  
  680. function saveComputerData(id)
  681.     if not registeredComputers[id] then
  682.     error("This is not a registered computer.")
  683.     end
  684.    
  685. saveTable(programFolderName .. "/registeredComputers/" .. id,registeredComputers[id])
  686. end
  687.  
  688.  
  689.  
  690. function unregisterComputer(id)
  691. local owner = registeredComputers[id]["owner"]
  692.  
  693. playerData[owner]["computerCount"] = playerData[owner]["computerCount"]-1
  694. playerData[owner]["stats"]["balance"] = playerData[owner]["stats"]["balance"] + registeredComputers[id]["balance"]
  695. savePlayerData(owner)
  696.  
  697. updateScoreboard("balance",owner)
  698.  
  699. registeredComputers[id] = nil
  700. fs.delete(programFolderName .. "/registeredComputers/" .. id)
  701.  
  702. sendMessage("The computer with the ID " .. id .. " has been unregistered.", executingPlayer, "green")
  703. end
  704.  
  705. -------------------------------------------------
  706.  
  707.  
  708.  
  709.  
  710.  
  711. -- Drone functions ------------------------------
  712.  
  713. function getDroneData(droneName) -- loads all drone data from the files
  714.  
  715.     if not droneName then
  716.     droneData = readFolder(programFolderName .. "/droneData")
  717.  
  718.         for k,droneName in pairs(droneNames) do
  719.             if not droneData[droneName] then
  720.             registerDrone(droneName)
  721.             else
  722.             droneData[droneName]["peripheral"] = peripheral.wrap(droneName)
  723.             end
  724.         end
  725.     else
  726.    
  727.     droneData[droneName] = readFolder(programFolderName .. "/droneData/" .. droneName)
  728.         if not droneData[droneName] then
  729.         registerDrone(droneName)
  730.         else
  731.         droneData[droneName]["peripheral"] = peripheral.wrap(droneName)
  732.         end
  733.     end
  734.  
  735. end
  736.  
  737.  
  738.  
  739. function registerDrone(droneName) -- registers a new drone (creates all the files and stuff)
  740. local dronePeripheral = peripheral.wrap(droneName)
  741.     if not dronePeripheral then
  742.     error("Not a valid drone interface")
  743.     end
  744.    
  745. droneData[droneName] ={["hasWork"] = false, ["peripheral"] = dronePeripheral}
  746. refuelDrone(droneName)
  747. saveDroneData(droneName)
  748. end
  749.  
  750.  
  751.  
  752. function saveDroneData(droneName) -- saves the drone data in a file
  753.  
  754.     if droneName == "all" or (not droneData) then
  755.     saveTable(programFolderName .. "/droneData",droneData)
  756.     return
  757.     end
  758.  
  759.     if not droneData[droneName] then
  760.     registerDrone(droneName)
  761.     return
  762.     end
  763.    
  764. saveTable(programFolderName .. "/droneData/" .. droneName, droneData[droneName])
  765. end
  766.  
  767.  
  768.  
  769. function setDroneAction(droneName,action,params,controllingPlayer) -- sets the action of a drone
  770.     if not droneData[droneName] then
  771.     error("This drone does not exist.")
  772.     end
  773.    
  774.     if controllingPlayer then
  775.         if playerData[controllingPlayer]["lostDrone"] and action ~= "refuel" then
  776.         sendMessage("Because you have lost a drone, you currently cant use drones. Please try again later.", controllingPlayer, "red")
  777.         return false
  778.         end
  779.     end
  780.  
  781. droneData[droneName]["hasWork"] = true
  782. droneData[droneName]["actionName"] = action
  783. droneData[droneName]["startedFunctions"] = 0
  784. droneData[droneName]["controllingPlayer"] = controllingPlayer
  785. droneData[droneName]["params"] = params or {}
  786. droneData[droneName]["startTime"] = os.epoch("local")
  787. return true
  788. end
  789.  
  790.  
  791.  
  792. function createDroneAction(name, functions)
  793.     if type(name) ~= "string" then
  794.     error("name needs to be a string got " .. type(name))
  795.     end
  796.    
  797.     if type(functions) ~= "table" then
  798.     error("functions needs to be a table got " .. type(name))
  799.     end
  800.  
  801.  
  802. droneActions = droneActions or {}
  803.  
  804. local actionFunctions = {}
  805.     for k,v in pairs(functions) do
  806.     table.insert(actionFunctions,_ENV["droneFunctions"][v])
  807.     end
  808.  
  809. droneActions[name] = actionFunctions
  810. end
  811.  
  812.  
  813.  
  814. function disconnectDrone(droneName) -- disconnects the drone (this drone will not be used anymore till its reconnected)
  815.     if not droneData[droneName] then
  816.     error("A drone named \"" .. droneName .. "\" does not exist." )
  817.     end
  818.  
  819. local controller = droneData[droneName]["controllingPlayer"]
  820.  
  821. local file = fs.open(programFolderName .. "/disconnectedDrones", "a")
  822.     if controller then
  823.     file.write(os.date("local") .. ": The player \"" .. controller .. "\" has disconnected the drone named \"" .. droneName ..  "\"")
  824.     sendMessage("Your reputation decreased by 50, because you havent recovered the drone in time. To recover some of your lost reputation you can recover the drone at a later time.", controller, "red")
  825.     playerData[controller]["lostDrone"] = nil
  826.     giveReputation(controller, -50)
  827.     else
  828.     file.writeLine(os.date() .. ":  The drone named \"" .. droneName ..  "\" has been disconnected.")
  829.     end
  830.        
  831. file.flush()
  832. file.close()
  833.  
  834. droneData[droneName] = {["isDisconnected"] = true, ["hasWork"] = false, ["peripheral"] = droneData[droneName]["peripheral"]}
  835. saveDroneData(droneName)
  836. end
  837.  
  838.  
  839.  
  840. function performDroneActions() -- performs the drone actions of all drones
  841.     for droneName,drone in pairs(droneData) do
  842.     checkDroneStatus(droneName)
  843.         if drone["hasWork"] then
  844.         performDroneAction(droneName)
  845.         end
  846.     end
  847. end
  848.  
  849.  
  850.  
  851. function performDroneAction(droneName) -- performs the drone action of the specified drone
  852. local dronePeriph = droneData[droneName]["peripheral"]
  853.  
  854.     if not dronePeriph.isConnectedToDrone() then -- checks if the drone is connected
  855.     return "error"
  856.     end
  857.  
  858.     if os.epoch("local") - droneData[droneName]["startTime"] > 120000 then -- Checks if the drone takes too long to perform its action
  859.     finishAction(droneName)
  860.     end
  861.  
  862.     if droneData[droneName]["startedFunctions"] == 0 then -- Starts the first function of the drone
  863.     droneData[droneName]["startedFunctions"] = droneData[droneName]["startedFunctions"] + 1
  864.     droneActions[droneData[droneName]["actionName"]][droneData[droneName]["startedFunctions"]](droneName, droneData[droneName]["params"][droneData[droneName]["startedFunctions"]])
  865.     end
  866.    
  867.     if (not pcall(dronePeriph.isActionDone)) and droneData[droneName]["actionName"] then -- restarts the drone function if it got cancelled because of something like a server restart
  868.     droneActions[droneData[droneName]["actionName"]][droneData[droneName]["startedFunctions"]](droneName, droneData[droneName]["params"][droneData[droneName]["startedFunctions"]])
  869.     end
  870.    
  871.    
  872.     if dronePeriph.isActionDone() then -- starts next function or completes the action
  873.         if droneData[droneName]["startedFunctions"] == table.maxn(droneActions[droneData[droneName]["actionName"]]) then
  874.         finishAction(droneName)
  875.         return
  876.         end
  877.     droneData[droneName]["startedFunctions"] = droneData[droneName]["startedFunctions"] + 1
  878.     droneActions[droneData[droneName]["actionName"]][droneData[droneName]["startedFunctions"]](droneName, droneData[droneName]["params"][droneData[droneName]["startedFunctions"]])
  879.     saveDroneData(droneName)
  880.     end
  881.  
  882. end
  883.  
  884.  
  885.  
  886. function finishAction(droneName, errorMessage) -- cancels/finishes the action of the drone
  887.  
  888.     if droneData[droneName]["controllingPlayer"] and droneData[droneName]["actionName"] ~= "refuel" then
  889.         if errorMessage then
  890.         sendMessage(errorMessage, droneData[droneName]["controllingPlayer"] ,"red")
  891.         elseif checkPlayerTpFailure(droneName) then
  892.         sendMessage("Teleportation failed, please only teleport to loaded chunks", droneData[droneName]["controllingPlayer"], "red")
  893.         else
  894.         sendMessage("The drone named \"" .. droneName .. "\" has finished its task.", droneData[droneName]["controllingPlayer"], "green")
  895.         end
  896.     end
  897.  
  898.     if droneData[droneName]["actionName"] == "refuel" then
  899.     droneData[droneName]["startedFunctions"] = nil
  900.     droneData[droneName]["hasWork"] = false
  901.     droneData[droneName]["params"] = nil
  902.     droneData[droneName]["actionName"] = nil
  903.     droneData[droneName]["controllingPlayer"] = nil
  904.     droneData[droneName]["actionName"] = nil
  905.     droneData[droneName]["startTime"] = nil
  906.     else
  907.     refuelDrone(droneName, droneData[droneName]["controllingPlayer"])
  908.     end
  909.    
  910. saveDroneData(droneName)
  911. end
  912.  
  913.  
  914.  
  915. function checkPlayerTpFailure(droneName) -- checks if the player that was teleported has reached his destination
  916. local playerName = droneData[droneName]["controllingPlayer"]
  917.  
  918.     if droneData[droneName]["actionName"] ~= "teleportPlayer" or not table.contains(playerDetector.getOnlinePlayers(), playerName) then
  919.     return false
  920.     end
  921.  
  922. local x = droneData[droneName]["params"][droneData[droneName]["startedFunctions"]-1]["x"]
  923. local y = droneData[droneName]["params"][droneData[droneName]["startedFunctions"]-1]["y"]
  924. local z = droneData[droneName]["params"][droneData[droneName]["startedFunctions"]-1]["z"]
  925. local playerPos = playerDetector.getPlayerPos(playerName)
  926. local pX = playerPos["x"]
  927. local pY = playerPos["y"]
  928. local pZ = playerPos["z"]
  929.  
  930.     if not (pX > x-10 and pX < x+10 and pY > y-10 and pY < y+10 and pZ > z-10 and pZ < z+10) then
  931.     droneFunctions.exportEntity(droneName)
  932.     return true
  933.     end
  934. end
  935.  
  936.  
  937.  
  938. function checkDroneStatus(droneName) -- checks if the drone was disconnected/reconnected etc.
  939. checkDroneDisconnection(droneName)
  940. checkCancelledDroneDisconnection(droneName)
  941. checkDroneReconnection(droneName)
  942. end
  943.  
  944.  
  945.  
  946. function checkDroneDisconnection(droneName) -- checks if the drone was disconnected
  947.     if (not droneData[droneName]["peripheral"].isConnectedToDrone()) and (not droneData[droneName]["isDisconnected"]) then
  948.         if not droneData[droneName]["disconnectTime"] and droneData[droneName]["controllingPlayer"] then
  949.         sendMessage("The drone named \"" .. droneName .. "\" you were using has been disconnected, please recover the drone (replace it in the world) within 60 seconds, if you dont, and this happens too often, you will get banned.", droneData[droneName]["controllingPlayer"], "red")
  950.         playerData[droneData[droneName]["controllingPlayer"]]["lostDrone"] = true
  951.         savePlayerData(droneData[droneName]["controllingPlayer"])
  952.         end
  953.        
  954.         if not droneData[droneName]["disconnectTime"] then
  955.         droneData[droneName]["disconnectTime"] = os.epoch("local")
  956.         saveDroneData(droneName)
  957.         elseif os.epoch("local") - droneData[droneName]["disconnectTime"] > 60000 then
  958.         disconnectDrone(droneName)
  959.         end
  960.     end
  961. end
  962.  
  963.  
  964.  
  965. function checkCancelledDroneDisconnection(droneName) -- checks if the drone disconnection was cancelled
  966.     if droneData[droneName]["disconnectTime"] and droneData[droneName]["peripheral"].isConnectedToDrone() then
  967.     droneData[droneName]["disconnectTime"] = nil
  968.     refuelDrone(droneName, droneData[droneName]["peripheral"].getOwnerName())
  969.         if droneData[droneName]["controllingPlayer"] then
  970.         sendMessage("The drone named \"" .. droneName .. "\" has been reconnected.", droneData[droneName]["controllingPlayer"], "green")
  971.         playerData[droneData[droneName]["controllingPlayer"]]["lostDrone"] = nil
  972.         savePlayerData(droneData[droneName]["controllingPlayer"])
  973.         end
  974.     end
  975. end
  976.  
  977.  
  978.  
  979. function checkDroneReconnection(droneName) -- checks if the drone was reconnected
  980.     if droneData[droneName]["isDisconnected"] and droneData[droneName]["peripheral"].isConnectedToDrone() then
  981.     droneData[droneName]["isDisconnected"] = nil
  982.     droneData[droneName]["disconnectTime"] = nil
  983.     saveDroneData(droneName)
  984.     refuelDrone(droneName, droneData[droneName]["peripheral"].getOwnerName())
  985.     giveReputation(droneData[droneName]["peripheral"].getOwnerName(), 25)
  986.     sendMessage("You have gained 25 reputation for reconnecting the drone named \"" .. droneName .. "\".", droneData[droneName]["peripheral"].getOwnerName(), "green")
  987.     end
  988. end
  989.  
  990.  
  991.  
  992. function refuelDrone(droneName,player) -- refuels the drone
  993.     if not droneData[droneName] then
  994.     error("The drone named " .. droneName .. " does not exist.")
  995.     end
  996. setDroneAction(droneName,"refuel",{refuelCoords},player)
  997. end
  998.  
  999.  
  1000.  
  1001. function getFreeDrone() -- gets the first free drone (a drone that currently does not perform an action)
  1002.  
  1003.     for droneName,drone in pairs(droneData) do
  1004.         if not drone["hasWork"] and not drone["isDisconnected"] then
  1005.         return droneName
  1006.         end
  1007.     end
  1008.  
  1009. end
  1010.  
  1011.  
  1012.  
  1013. function getConnectedDroneCount() -- gets the count of connected drones
  1014. local count = 0
  1015.     for k,v in pairs(droneData) do
  1016.         if not v["isDisconnected"] then
  1017.         count = count + 1
  1018.         end
  1019.     end
  1020. return count
  1021. end
  1022.  
  1023.  
  1024.  
  1025. function droneFunctions.teleport(droneName,params) -- teleports the drone to the specified coordinates
  1026.  
  1027.     if not params then
  1028.     finishAction(droneName, "The teleportation was stopped because of an unexpected error.")
  1029.     return
  1030.     end
  1031.    
  1032. local dronePeriph = droneData[droneName]["peripheral"]
  1033. local x = params["x"]
  1034. local y = params["y"]
  1035. local z = params["z"]
  1036.  
  1037. dronePeriph.clearArea()
  1038. dronePeriph.addArea(x,y,z)
  1039.  
  1040. dronePeriph.abortAction()
  1041. dronePeriph.setAction("goto")
  1042. end
  1043.  
  1044.  
  1045.  
  1046. function droneFunctions.standby(droneName) -- sets the drone to be on standby
  1047. droneData[droneName]["peripheral"].abortAction()
  1048. droneData[droneName]["peripheral"].setAction("standby")
  1049. end
  1050.  
  1051.  
  1052.  
  1053. function droneFunctions.importPlayer(droneName,params) -- picks up the player that is controlling the drone
  1054.  
  1055.     if not params then
  1056.     finishAction(droneName, "The player import was stopped because of an unexpected error.")
  1057.     return
  1058.     end
  1059.  
  1060. local x1 = params["x"] - 3
  1061. local y1 = params["y"] - 3
  1062. local z1 = params["z"] - 3
  1063. local x2 = params["x"] + 3
  1064. local y2 = params["y"] + 3
  1065. local z2 = params["z"] + 3
  1066.  
  1067. local dronePeriph = droneData[droneName]["peripheral"]
  1068.  
  1069.  
  1070. dronePeriph.clearArea()
  1071. dronePeriph.addArea(x1,y1,z1,x2,y2,z2,"Filled")
  1072.  
  1073. dronePeriph.clearWhitelistText()
  1074. dronePeriph.clearBlacklistText()
  1075. dronePeriph.addWhitelistText("player")
  1076.  
  1077. dronePeriph.abortAction()
  1078. dronePeriph.setAction("entity_import")
  1079. end
  1080.  
  1081.  
  1082.  
  1083. function droneFunctions.exportEntity(droneName) -- exports the entity that the drone is currently carrying
  1084. local dronePeriph = droneData[droneName]["peripheral"]
  1085. dronePeriph.clearWhitelistText()
  1086. dronePeriph.clearBlacklistText()
  1087. dronePeriph.clearArea()
  1088. dronePeriph.addArea(dronePeriph.getDronePosition())
  1089. dronePeriph.abortAction()
  1090. dronePeriph.setAction("entity_export")
  1091. end
  1092.  
  1093. -------------------------------------------------
  1094.  
  1095.  
  1096.  
  1097.  
  1098. -- Settings and data functions -------------------
  1099.  
  1100. function getRankVariable(variable,player)
  1101. return playerRanks[playerData[player]["rank"]]["rankVariables"][variable]
  1102. end
  1103.  
  1104.  
  1105.  
  1106. function getPlayerNameByuuid(uuid)
  1107.     for name,data in pairs(playerData) do
  1108.         if data["uuid"] == uuid then
  1109.         return name
  1110.         end
  1111.     end
  1112. end
  1113.  
  1114.  
  1115.  
  1116. function getPlayerSetting(setting,player) -- get the value of the specified setting of the specified player
  1117.     if not playerData[player] then
  1118.     error(player .. " is not a registered player.")
  1119.     end
  1120.    
  1121.     if not settings[setting] then
  1122.     error(setting .. " is not a setting")
  1123.     end
  1124.  
  1125. return playerData[player]["settings"][setting] or settings[setting]["standardValue"]
  1126. end
  1127.  
  1128.  
  1129.  
  1130. function createSetting(name,standardValue,requiredPermLevl,info) -- creates a player setting
  1131. settings[name] = {}
  1132. settings[name]["standardValue"] = standardValue
  1133. settings[name]["requiredPermLevl"] = requiredPermLevl
  1134. settings[name]["type"] = type(standardValue)
  1135. settings[name]["info"] = info
  1136.  
  1137. settingNames = settingNames or {}
  1138. table.insert(settingNames,name)
  1139.  
  1140. standardSettings[name] = standardValue
  1141. end
  1142.  
  1143.  
  1144.  
  1145. function changeSetting(player,setting,value) -- changes the setting of a player
  1146.  
  1147.     if not table.contains(playerNames,player) then
  1148.     error("not a viable player")
  1149.     end
  1150.    
  1151.    
  1152.     if not settings[setting] then
  1153.     error("not a viable setting")
  1154.     end
  1155.  
  1156.  
  1157.     if type(value) ~= settings[setting]["type"] then
  1158.     error("bad argument #3 (value supposed to have the type " .. settings[setting]["type"] .. " but has the type" .. type(value) .. ")")
  1159.     end
  1160.    
  1161. playerData[player]["settings"][setting] = value
  1162. saveTable(programFolderName .. "/playerData/" .. player .. "/settings", playerData[player]["settings"])
  1163. end
  1164.  
  1165.  
  1166.  
  1167. function addTime() -- adds playtime to all players
  1168.     for k,player in pairs(playerDetector.getOnlinePlayers()) do
  1169.         if not playerData[player] then -- registers the player if they are new
  1170.         getPlayerData(player)
  1171.         end
  1172.     playerData[player]["stats"]["playtime"] = playerData[player]["stats"]["playtime"] + 1
  1173.         if playerData[player]["stats"]["playtime"] % 60 == 0 then
  1174.         giveMoney(player, 150)
  1175.         end
  1176.     savePlayerData(player, "stats")
  1177.     updateScoreboard("playtime", player)
  1178.     end
  1179. addTimeTimer = os.startTimer(60)
  1180. end
  1181.  
  1182.  
  1183.  
  1184. function updateScoreboard(data, player) -- updates position of the specified player in the specified scoreboard
  1185.     if not scoreboards[data] then
  1186.     error("This scoreboard does not exist")
  1187.     end
  1188.  
  1189. local score = playerData[player]["stats"][data]
  1190. local position = getScoreboardPosition(data, player)
  1191.  
  1192.     if not position then -- adds the player to the end of the scoreboard if they dont have a position yet
  1193.     table.insert(scoreboards[data], {["playerName"] = player, ["score"] = score})
  1194.     position = table.maxn(scoreboards[data])
  1195.     end
  1196.  
  1197.  
  1198. scoreboards[data][position]["score"] = score
  1199.  
  1200.  
  1201.     if position ~= 1 then
  1202.         while score > scoreboards[data][position-1]["score"] do -- ckecks if the score of the player is higher than the players in the spots over them
  1203.         table.remove(scoreboards[data], position)
  1204.         position = position - 1
  1205.         table.insert(scoreboards[data], position, {["playerName"] = player, ["score"] = score})
  1206.             if position == 1 then
  1207.             return
  1208.             end
  1209.         end
  1210.     end
  1211.    
  1212.     if scoreboards[data][position+1] then
  1213.         while score < scoreboards[data][position+1]["score"] do -- ckecks if the score of the player is lower than the players in the spots under them
  1214.         table.remove(scoreboards[data], position)
  1215.         position = position + 1
  1216.         table.insert(scoreboards[data], position, {["playerName"] = player, ["score"] = score})
  1217.             if not scoreboards[data][position+1] then
  1218.             return
  1219.             end
  1220.         end
  1221.     end
  1222.    
  1223. end
  1224.  
  1225.  
  1226.  
  1227. function getScoreboardPosition(data, player) -- gets the position of a specified player in a specified scoreboard
  1228.     if not scoreboards[data] then
  1229.     error("This scoreboard does not exist")
  1230.     end
  1231.  
  1232.     for k,v in pairs(scoreboards[data]) do
  1233.         if v["playerName"] == player then
  1234.         return k
  1235.         end
  1236.     end
  1237. end
  1238.  
  1239.  
  1240.  
  1241. function createScoreboard(data) -- creates a scoreboard (the variable data needs to be a index of the playerData[playerName]["stats"] table)
  1242. scoreboards = scoreboards or {}
  1243. local returnTable = {}
  1244. local scores = {}
  1245. local temporaryTable = {}
  1246.  
  1247.     for k,v in pairs(playerData) do
  1248.     temporaryTable[v["stats"][data]] = temporaryTable[v["stats"][data]] or {}
  1249.     table.insert(temporaryTable[v["stats"][data]], k)
  1250.         if not table.contains(scores, v["stats"][data]) then
  1251.         table.insert(scores,v["stats"][data])
  1252.         end
  1253.     end
  1254.  
  1255.  
  1256. scores = sortScores(scores)
  1257.  
  1258.    
  1259. local maxn = table.maxn(scores)
  1260.     for k,v in pairs(scores) do
  1261.         for k2,v2 in pairs(temporaryTable[v]) do
  1262.         table.insert(returnTable, table.maxn(returnTable)+1, {playerName = v2, score = v})
  1263.         end
  1264.     end
  1265.  
  1266. scoreboards[data] = returnTable
  1267. end
  1268.  
  1269.  
  1270.  
  1271. function sortScores(scores) -- sorts the scores of a scoreboard in the right order
  1272. local returnTable = {[1] = scores[table.maxn(scores)]}
  1273. scores[table.maxn(scores)] = nil
  1274.     for k,v in pairs(scores) do
  1275.     local worked = false
  1276.         for k2,v2 in pairs(returnTable) do
  1277.             if v > v2 then
  1278.             table.insert(returnTable,k2 , v)
  1279.             worked = true
  1280.             break
  1281.             end
  1282.         end
  1283.        
  1284.         if not worked then
  1285.         table.insert(returnTable, v)
  1286.         end
  1287.     end
  1288.    
  1289. return returnTable
  1290. end
  1291.  
  1292. ----------------------------------------
  1293.  
  1294.  
  1295.  
  1296.  
  1297. -- player functions ---------------
  1298.  
  1299. function getComputersOfPlayer(name)
  1300. local returnTable = {}
  1301.     for id,computer in pairs(registeredComputers) do
  1302.         if computer["owner"] == name then
  1303.         returnTable[id] = computer
  1304.         end
  1305.     end
  1306. return returnTable
  1307. end
  1308.  
  1309.  
  1310.  
  1311. function getMaxComputers(name)
  1312. return playerRanks[playerData[name]["rank"]]["rankVariables"]["maxComputers"]
  1313. end
  1314.  
  1315.  
  1316.  
  1317. function exactName(name) -- returns the exact player name (makes player names not case-sensitive)
  1318. return lowerToExactPlayerNames[string.lower(name or "")]
  1319. end
  1320.  
  1321.  
  1322.  
  1323. function sendMail(receiver,subject,text,sender) -- sends a mail to the receiver
  1324.     if not playerData[receiver] then
  1325.     error(receiver .. " is not a registered player.")
  1326.     end
  1327.    
  1328. table.insert(playerData[receiver]["receivedMails"], 1, {["subject"] = subject, ["text"] = text, ["sender"] = sender or systemName, ["date"] = os.date("%A %d %B %Y")})
  1329. playerData[receiver]["receivedMails"][51] = nil
  1330. savePlayerData(receiver,"receivedMails")
  1331.  
  1332. sendMessage({{text = "You have received a new mail.", hoverEvent = hoverMessage("Shift + click to disable this message."), insertion = "$@changeSetting disableMailNotifications true"}}, receiver, "green")
  1333. end
  1334.  
  1335.  
  1336.  
  1337. function getWarpCount(player) -- returns the count of warps the player currently has
  1338. local warpCount = 0
  1339.     for k,v in pairs(playerData[player]["warps"]) do
  1340.     warpCount = warpCount + 1
  1341.     end
  1342. return warpCount
  1343. end
  1344.  
  1345.  
  1346.  
  1347. function getMaxWarps(player) -- returns the count of warps the player can have
  1348. return playerRanks[playerData[player]["rank"]]["rankVariables"]["maxWarps"]
  1349. end
  1350.  
  1351.  
  1352.  
  1353. function getJoinedPlayers() -- checks if a player joined the server
  1354. local returnTable = {}
  1355.  
  1356.     for k, playerName in pairs(playerDetector.getOnlinePlayers()) do
  1357.         if not playerData[playerName] then
  1358.         registerNewPlayer(playerName)
  1359.         end
  1360.        
  1361.         if not table.contains(onlinePlayers, playerName) then
  1362.         table.insert(returnTable, playerName)
  1363.         end
  1364.     end
  1365.  
  1366. onlinePlayers = playerDetector.getOnlinePlayers()
  1367.  
  1368. return returnTable
  1369. end
  1370.  
  1371.  
  1372.  
  1373. function isPlayerOnline(player) -- checks if the player is online
  1374. return table.contains(playerDetector.getOnlinePlayers(),player)
  1375. end
  1376.  
  1377.  
  1378.  
  1379. function giveReputation(player, amount) -- gives reputation to the player
  1380.  
  1381.     if not playerData[player]["stats"]["reputation"] then
  1382.     playerData[player]["stats"]["reputation"] = amount
  1383.     else
  1384.     playerData[player]["stats"]["reputation"] = playerData[player]["stats"]["reputation"] + amount
  1385.     end
  1386.    
  1387.     if playerData[player]["stats"]["reputation"] <= -100 and playerData[player]["rank"] ~= "Banned" then -- checks if the player should be banned
  1388.     banPlayer(player)
  1389.     elseif playerData[player]["stats"]["reputation"] > -100 and playerData[player]["rank"] == "Banned" then -- chekcs if the player should be unbanned
  1390.     unbanPlayer(player)
  1391.     end
  1392.    
  1393. savePlayerData(player)
  1394. end
  1395.  
  1396.  
  1397.  
  1398. function unbanPlayer(player, unbanningPlayer) -- unbanns the specified player
  1399.  
  1400.     if unbanningPlayer then
  1401.     sendMessage("You have been unbanned from the " .. systemName .. " by " .. unbanningPlayer .. ".", player, "green")
  1402.     sendMessage("You have succesfully unbanned the player " .. player .. ".", unbanningPlayer, "green")
  1403.     setPlayerRank(player, getRankByPlaytime(player))
  1404.     else
  1405.     sendMessage("You have been unbanned from the " .. systemName .. ".", player, "green")
  1406.     setPlayerRank(player, getRankByPlaytime(player))
  1407.     end
  1408.  
  1409. savePlayerData(player)
  1410. end
  1411.  
  1412.  
  1413.  
  1414. function banPlayer(player, banningPlayer) -- banns the specified player
  1415.  
  1416.     if banningPlayer then
  1417.         if getPermissionLevel(banningPlayer) > getPermissionLevel(player) then
  1418.         sendMessage("You have been banned from the " .. systemName .. " by " .. banningPlayer .. ". If you think this is unfair, please message someon of the rank of supervisor or higher.", player, "red")
  1419.         sendMessage("You have succesfully banned the player " .. player .. ".", banningPlayer, "green")
  1420.         setPlayerRank(player, "Banned")
  1421.         else
  1422.         sendMessage("Your permission level isnt high enaugh to ban this player.", banningPlayer, "red")
  1423.         end
  1424.     else
  1425.     sendMessage("You have been banned from the " .. systemName .. ". If you think this is unfair, please message someon of the rank of supervisor or higher.", player, "red")
  1426.     setPlayerRank(player, "Banned")
  1427.     end
  1428.  
  1429. savePlayerData(player)
  1430. end
  1431.  
  1432.  
  1433.  
  1434. function getRankByPlaytime(playerName) -- checks what rank the player would have based on their playtime (used for unbannig)
  1435. local curHighestRank = "Beginner"
  1436.     for rankupName,rankup in pairs(rankups) do
  1437.         if playerData[playerName]["stats"][rankup["statName"]] >= rankup["condition"] and playerRanks[curHighestRank]["permissionLevel"] < playerRanks[rankupName]["permissionLevel"] then
  1438.         curHighestRank = rankupName
  1439.         end
  1440.     end
  1441. return curHighestRank
  1442. end
  1443.  
  1444.  
  1445.  
  1446. function giveMoney(player,amount) -- gives money to the specified player
  1447.    
  1448.     if not table.contains(playerNames,player) then
  1449.     return
  1450.     end
  1451.    
  1452.     if playerData[player]["stats"]["balance"] then
  1453.     playerData[player]["stats"]["balance"] = playerData[player]["stats"]["balance"] + amount
  1454.     else
  1455.     playerData[player]["stats"]["balance"] = amount
  1456.     end
  1457.  
  1458. updateScoreboard("balance", player)
  1459. sendMessage({"You have received ", {text = amount .. "c", color = "green"}, ".\nBalance: ", {text = playerData[player]["stats"]["balance"], color = "green"}},player)
  1460. end
  1461.  
  1462.  
  1463.  
  1464. function getPlayerData(player) -- gets the data of a player
  1465. playerData[player] = {}
  1466.  
  1467.     if fs.exists(programFolderName .. "/playerData/" .. player) then
  1468.     playerData[player] = readFolder(programFolderName .. "/playerData/" .. player)
  1469.     else
  1470.     registerNewPlayer(player)
  1471.     end
  1472.  
  1473. end
  1474.  
  1475.  
  1476.  
  1477. function registerNewPlayer(player) -- registers a new player
  1478. lowerToExactPlayerNames[string.lower(player)] = player
  1479.  
  1480. playerData[player] = standardPlayerData
  1481. playerData[player]["lastLogin"] = os.date("%A %d %B %Y")
  1482. savePlayerData(player)
  1483.  
  1484. table.insert(playerNames,player)
  1485.  
  1486.  
  1487. updateScoreboard("playtime", player)
  1488. updateScoreboard("balance", player)
  1489.  
  1490.  
  1491. sendMessage("Congratulations, you have been registered to the " .. systemName .. ", you can use it for things like teleporting, paying other players or getting stat ranklists. Type \"@help\" for more info.", player, "green")
  1492. end
  1493.  
  1494.  
  1495.  
  1496. function savePlayerData(player,saveWhat) -- saves the data of a player (if player is "all" then it saves the data of all players, if saveWhat is nil it saves the whole playerData)
  1497.    
  1498.     if player == "all" then
  1499.         for k,v in pairs(playerData) do
  1500.         savePlayerData(k, saveWhat)
  1501.         end
  1502.    
  1503.     else
  1504.         if saveWhat then
  1505.             if not playerData[player][saveWhat] then
  1506.             error(saveWhat .. "is not a existing playerData")
  1507.             end
  1508.         saveTable(programFolderName .. "/playerData/" .. player .. "/" .. saveWhat, playerData[player][saveWhat])
  1509.         else
  1510.         saveTable(programFolderName .. "/playerData/" .. player, playerData[player])
  1511.         end
  1512.     end
  1513.  
  1514. end
  1515.  
  1516.  
  1517.  
  1518. function setPlayerRank(player,rank) -- sets the rank of the specified player
  1519.  
  1520.     if not table.contains(playerNames,player) then
  1521.     error("bad argument #1 (" .. player .. " is not a viable player)")
  1522.     elseif not table.contains(rankNames,rank) then
  1523.     error("bad argument #1 (" .. rank .. " is not a viable rank)")
  1524.     end
  1525.    
  1526. playerData[player]["rank"] = rank
  1527.  
  1528.     for k,v in pairs(playerRanks[rank]["settings"]) do
  1529.     playerData[player]["settings"][k] = v
  1530.     end
  1531.  
  1532. savePlayerData(player)
  1533. end
  1534.  
  1535.  
  1536.  
  1537. function createRank(name,permissionLevel,rankSettings,rankVariables,info) -- creates a player rank (the rank settings are all the settings that will be changed when a player reaches that rank. its a table in the format {[settingName] = settingValue})
  1538.  
  1539.     if type(name) ~= "string" then
  1540.     error("bad argument #1 (name is supposed to be a string)")
  1541.     elseif type(permissionLevel) ~= "number" then
  1542.     error("bad argument #2 (permissionLevel is supposed to be a number)")
  1543.     end
  1544.  
  1545. playerRanks[name] = {
  1546. ["allowedChatCommands"] = allowedChatCommands,
  1547. ["permissionLevel"] = permissionLevel,
  1548. ["settings"] = rankSettings or {},
  1549. ["info"] = info,
  1550. ["rankVariables"] = rankVariables
  1551. }
  1552.  
  1553.  
  1554. table.insert(rankNames,name)
  1555. end
  1556.  
  1557.  
  1558.  
  1559. function getPermissionLevel(player) -- gets the permission level of a player based on their rank
  1560. return playerRanks[playerData[player]["rank"]]["permissionLevel"] or 0
  1561. end
  1562.  
  1563.  
  1564.  
  1565. function isPermLevelHigher(playerOne,playerTwo) -- checks if the permission level of playerOne is higher than that of player two
  1566. return getPermissionLevel(playerOne) > getPermissionLevel(playerTwo)
  1567. end
  1568.  
  1569.  
  1570.  
  1571. function createRankup(rank, statName, condition) -- creates a rankup, players will automatically rank up to that rank if the player has the specified value of the data specified in statName
  1572. rankups = rankups or {}
  1573.  
  1574. rankups[rank] =
  1575. {["statName"] = statName,
  1576. ["condition"] = condition
  1577. }
  1578. end
  1579.  
  1580.  
  1581.  
  1582. function checkRankups() -- checks if any of the online players are supposed to rank up
  1583.  
  1584.     for k,player in pairs(playerDetector.getOnlinePlayers()) do
  1585.     checkRankup(player)
  1586.     end
  1587.  
  1588. end
  1589.  
  1590.  
  1591.  
  1592. function checkRankup(player) -- checks if the specified player is supposed to rank up (if so they will get ranked up)
  1593.  
  1594.     for rankupName,rankup in pairs(rankups) do
  1595.         if playerData[player]["stats"][rankup["statName"]] == rankup["condition"] and playerRanks[rankupName]["permissionLevel"] > playerRanks[playerData[player]["rank"]]["permissionLevel"] then
  1596.         setPlayerRank(player, rankupName)
  1597.         sendMessage("Congratulations, your rank is now " .. rankupName .. ".")
  1598.         return
  1599.         end
  1600.     end
  1601.  
  1602. end
  1603.  
  1604. -------------------------
  1605.  
  1606.  
  1607.  
  1608.  
  1609.  
  1610. -- chat functions -------------
  1611.  
  1612. function listWarps(player,sendErrorMessage)
  1613.     if getWarpCount(player) == 0 then
  1614.     sendMessage("You dont have any warps.", player, "red")
  1615.     return
  1616.     end
  1617.  
  1618.  
  1619. local message = {{text = "\nPrivate warps:", color = "green"}}
  1620.    
  1621.     if sendErrorMessage then
  1622.     table.insert(message,1,{text = "\nYou dont have a warp with this name.\n", color = "red"})
  1623.     end
  1624.    
  1625.    
  1626.     for warpName, warpData in pairs(playerData[player]["warps"]) do
  1627.     table.insert(message, {text = "\n  - " .. warpName, hoverEvent = hoverMessage("isPublic = " .. tostring(warpData["isPublic"] or false) .. "\nX = " .. warpData["x"] .. "\nY = " .. warpData["y"] .. "\nZ = " .. warpData["z"] .. "\nshift + click to warp"), insertion = "$@warp " .. warpName})
  1628.     end
  1629.    
  1630.  
  1631. table.insert(message, {text = "\n\nPublic warps:", color = "green"})
  1632.     for warpName, warpData in pairs(publicWarps) do
  1633.         if not (warpData["owner"] == executingPlayer) then
  1634.         table.insert(message, {text = "\n  - " .. warpName, hoverEvent = hoverMessage("Owner = " .. warpData["owner"] .. "\nX = " .. warpData["x"] .. "\nY = " .. warpData["y"] .. "\nZ = " .. warpData["z"] .. "\nshift + click to warp"), insertion = "$@warp " .. warpName})
  1635.         end
  1636.     end
  1637.  
  1638.  
  1639. sendMessage(message, player)
  1640. end
  1641.  
  1642.  
  1643.  
  1644. function sendJoinMessages() -- sends a welcome message to all newly joined players
  1645.     for k,playerName in pairs(getJoinedPlayers()) do
  1646.         if playerData[playerName] then
  1647.         local message = {}
  1648.             if not getPlayerSetting("disableJoinMessage", playerName) then
  1649.             sendMessage({{text = "\nWelcome back to the server. For more info on the " .. systemName .. " enter @help.", hoverEvent = hoverMessage("shift + click to disable this message."), insertion = "$@changeSetting disableJoinMessage true"}}, playerName, "green")
  1650.             end
  1651.            
  1652.             if not getPlayerSetting("disableMailNotifications", playerName) then
  1653.             local newMailCount = 0
  1654.                 for mailID,mail in pairs(playerData[playerName]["receivedMails"]) do
  1655.                     if not mail["isRead"] then
  1656.                     newMailCount = newMailCount + 1
  1657.                     end
  1658.                 end
  1659.            
  1660.                 if newMailCount == 1 then
  1661.                 sendMessage({{text = "\nYou have an unread mail.", hoverEvent = hoverMessage("shift + click to disable this message."), insertion = "$@changeSetting disableMailNotifications true"}}, playerName, "green")
  1662.                 elseif newMailCount > 1 then
  1663.                 sendMessage({{text = "\nYou have " .. newMailCount .. " unread mails.", hoverEvent = hoverMessage("shift + click to disable this message."), insertion = "$@changeSetting disableMailNotifications true"}}, playerName, "green")
  1664.                 end
  1665.             end
  1666.         playerData[playerName]["lastLogin"] = os.date("%A %d %B %Y")
  1667.         savePlayerData(playerName)
  1668.         end
  1669.     end
  1670. end
  1671.  
  1672.  
  1673.  
  1674. function sendPatchNotes() -- sends the patchnotes if the player joined the current version for the first time
  1675.  
  1676.     for k,playerName in pairs(playerDetector.getOnlinePlayers()) do
  1677.         if playerData[playerName]["latestVersion"] ~= curVersion then
  1678.             if not getPlayerSetting("disablePatchMessage", playerName) then
  1679.             local pastLatestVersion = false
  1680.                 for version,v in pairs(patchNotes) do
  1681.                     if pastLatestVersion then
  1682.                     sendMessage({{text = "\nPatch notes " .. version .. ":\n", hoverEvent = hoverMessage("shift + click to disable this message."), insertion = "$@changeSetting disablePatchMessage true", color = "green"},{text = v["notes"], hoverEvent = hoverMessage("shift + click to disable this message."), insertion = "$@changeSetting disablePatchMessage true" }}, playerName)
  1683.                     end
  1684.                
  1685.                     if v["name"] == playerData[playerName]["latestVersion"] then
  1686.                     pastLatestVersion = true
  1687.                     end
  1688.                 end
  1689.             end
  1690.            
  1691.         playerData[playerName]["latestVersion"] = curVersion
  1692.         savePlayerData(playerName) 
  1693.         end
  1694.     end
  1695. end
  1696.  
  1697.  
  1698.  
  1699. function isMessageDouble() -- checks if the chat event is double (if multiple chatboxes are connected to the same computer there will be multiple events for each message)
  1700.  
  1701.     if os.epoch("local") - (lastMessageTime or 0) < 100 and lastMessagePlayer == info2 then
  1702.     return true
  1703.     end
  1704.    
  1705. lastMessageTime = os.epoch("local")
  1706. lastMessagePlayer = info2
  1707.  
  1708. end
  1709.  
  1710.  
  1711.  
  1712. function createChatCommand(name,requiredPermLevl,info) -- creats a chat command, if someon types @[name] in the chat the function named chatFunctions.[name] will be executed
  1713.    
  1714.     if type(name) ~= "string" then
  1715.     error("bad argument #1 (name is supposed to be a string)")
  1716.     elseif type(requiredPermLevl) ~= "number" then
  1717.     error("bad argument #2 (requiredPermLevl is supposed to be a number)")
  1718.     elseif type(info) ~= "string" then
  1719.     error("bad argument #3 (info is supposed to be a string)")
  1720.     end
  1721.    
  1722. chatCommands[name] = {
  1723. ["requiredPermLevl"] = requiredPermLevl,
  1724. ["function"] = _ENV["chatFunctions"][name],
  1725. ["info"] = info
  1726. }
  1727.  
  1728. table.insert(chatCommandNames,name)
  1729. lowerToExactChatCommands[string.lower(name)] = name
  1730. end
  1731.  
  1732.  
  1733.  
  1734. function getChatCommand(player,message) -- checks if the chat message was a chat command and also returns the parameters written into the chat if the message was a chat command
  1735.     if string.sub(message,1,1) == "@" then
  1736.     chatCommand = string.sub(message,2,(string.find(message," ") or 0) -1)
  1737.     chatCommand = lowerToExactChatCommands[string.lower(chatCommand)]
  1738.     else
  1739.     return
  1740.     end
  1741.    
  1742.     if not chatCommands[chatCommand] then
  1743.     return "This command does not exist."
  1744.     end    
  1745.    
  1746.     if getPermissionLevel(player) < chatCommands[chatCommand]["requiredPermLevl"] then
  1747.     return "You do not have permission to use this command."
  1748.     end
  1749.    
  1750.    
  1751. messageRest = string.sub(message,string.len(chatCommand)+3)
  1752. local params = {}
  1753.  
  1754. i = 1
  1755.     while string.len(messageRest) ~= 0 do
  1756.     params[i] = string.sub(messageRest,1,(string.find(messageRest," ") or 0) -1)
  1757.     messageRest = string.sub(messageRest,string.len(params[i])+2)
  1758.     i = i+1
  1759.     end
  1760.    
  1761.     if not subcommands[chatCommand] then
  1762.     return chatCommands[chatCommand]["function"], params
  1763.     end
  1764.    
  1765.    
  1766. local subcommandName = params[1]
  1767.  
  1768.     if not subcommands[chatCommand][subcommandName] then
  1769.     sendAvailableSubcommands(player,chatCommand)
  1770.     return
  1771.     end
  1772. table.remove(params,1)
  1773.  
  1774.     if getPermissionLevel(player) < subcommands[chatCommand][subcommandName]["requiredPermLevl"] then
  1775.     return "You do not have permission to use this subcommand."
  1776.     end
  1777.    
  1778. return _ENV["subFunctions"][chatCommand][subcommandName], params
  1779. end
  1780.  
  1781.  
  1782.  
  1783. function sendMessage(message,player,color) -- sends a message to the specified player, the message can be a normal string or a table. For more info go to https://docs.advanced-peripherals.de/latest/peripherals/chat_box/#sendformattedmessage
  1784.     if not isPlayerOnline(player) then
  1785.     return false
  1786.     end
  1787.  
  1788.     if getPlayerSetting("disableAllMessages", player) then
  1789.     return false
  1790.     end
  1791.    
  1792. table.insert(sendTheseMessages, {message = message, player = player, color = color or "white"})
  1793. os.queueEvent("sendMessage")
  1794. end
  1795.  
  1796.  
  1797.  
  1798. function hoverMessage(message) -- returns the table used for a hoverEvent in the sendMessage function for more info also check https://docs.advanced-peripherals.de/latest/peripherals/chat_box/#sendformattedmessage
  1799. return {action = "show_text",value = message}
  1800. end
  1801.  
  1802.  
  1803.  
  1804. function showScoreboard(data, player, page, extraText) -- shows the specified scoreboard to the specified player (in form of a chat message)
  1805.     if not scoreboards[data] then
  1806.     error("This scoreboard does not exist")
  1807.     return
  1808.     end
  1809.    
  1810. local page = page or math.ceil(getScoreboardPosition(data, player)/10) -- sets the page to the page the executing player is on if the page isnt specified
  1811.  
  1812.     if not scoreboards[data][page*10-9] then
  1813.     sendMessage("This page does not exist", player, "red")
  1814.     return
  1815.     end
  1816.    
  1817.    
  1818. local message = {"\n"}
  1819.     for i = 1,10 do
  1820.     position = (page-1)*10+i
  1821.         if not scoreboards[data][position] then
  1822.         break
  1823.         end
  1824.  
  1825.     table.insert(message, {text = "#" .. position .. " ", color = "green", hoverEvent = hoverMessage("Shift + click to get information about this player"), insertion = "$@about player " .. scoreboards[data][position]["playerName"]})
  1826.    
  1827.     local color = "white"
  1828.         if scoreboards[data][position]["playerName"] == player then
  1829.         color = "green"
  1830.         elseif position == 1 then
  1831.         color = "#ffe400"
  1832.         elseif position == 2 then
  1833.         color = "#c0cbe4"
  1834.         elseif position == 3 then
  1835.         color = "#c46403"
  1836.         end
  1837.     table.insert(message, {text = scoreboards[data][position]["playerName"] .. ": " .. scoreboards[data][position]["score"] .. (extraText or "") .. "\n", color = color, hoverEvent = hoverMessage("Shift + click to get information about this player"), insertion = "$@about player " .. scoreboards[data][position]["playerName"]})
  1838.     end
  1839.  
  1840. sendMessage(message, player)
  1841. end
  1842.  
  1843.  
  1844.  
  1845. function combineFormattedMessages(messages) -- a function used in the help chat command to format the message because im to lazy to do it properly
  1846. local returnTable = {}
  1847.  
  1848.     for k,message in pairs(messages) do
  1849.         for k2, v in pairs(message) do
  1850.         table.insert(returnTable, v)
  1851.         end
  1852.     end
  1853.  
  1854. return returnTable
  1855. end
  1856.  
  1857.  
  1858.  
  1859. function getOwnersText() -- returns a text about the owner*s of the system (used for the about sytem command)
  1860. local owners = ""
  1861. local ownerCount = 0
  1862.     for name,data in pairs(playerData) do
  1863.         if data["rank"] == "Owner" then
  1864.         ownerCount = ownerCount + 1
  1865.             if ownerCount == 1 then
  1866.             owners = name
  1867.             else
  1868.             owners = owners .. ", " .. name
  1869.             end
  1870.         end
  1871.     end
  1872.  
  1873. local ownersText = {}
  1874.  
  1875.     if ownerCount == 1 then
  1876.     table.insert(ownersText, {text = "Owner: ", color = "green"})
  1877.     else
  1878.     table.insert(ownersText, {text = "Owners: ", color = "green"})
  1879.     end
  1880.  
  1881.     if ownerCount == 0 then
  1882.     table.insert(ownersText, "None")
  1883.     else
  1884.     table.insert(ownersText, owners)
  1885.     end
  1886.    
  1887. return ownersText[1], ownersText[2]
  1888. end
  1889.  
  1890.  
  1891.  
  1892. function chatFunctions.setRank(params) -- chat function to set the rank of a player
  1893. local player = exactName(params[1])
  1894. local rank = params[2]
  1895.  
  1896.     if not table.contains(playerNames,player) then
  1897.     return "param #1 is supposed to be a registered player"
  1898.     end
  1899.    
  1900.     if not playerRanks[rank] then
  1901.     return "param #2 is not a viable rank"
  1902.     end
  1903.    
  1904.     if not (isPermLevelHigher(executingPlayer,player) or executingPlayer == player) then
  1905.     return "Your permission level is not high enough to change the rank of this player"
  1906.     end
  1907.    
  1908.     if playerData[player]["rank"] == rank then
  1909.     return "This player already has this rank"
  1910.     end
  1911.    
  1912.     if getPermissionLevel(executingPlayer) <= playerRanks[rank]["permissionLevel"] then
  1913.     return "Your permission level is not high enough to grant this rank"
  1914.     end
  1915.    
  1916.    
  1917. setPlayerRank(player,rank)
  1918. sendMessage("Rank succesfully changed to " .. rank, executingPlayer, "green")
  1919. end
  1920.  
  1921.  
  1922.  
  1923. function chatFunctions.changeSetting(params) -- chat function to change the setting of a player
  1924.  
  1925.     if playerData[exactName(params[1])] then
  1926.     player = exactName(params[1])
  1927.     setting = params[2]
  1928.     value = params[3] or ""
  1929.     else
  1930.     player = executingPlayer
  1931.     setting = params[1]
  1932.     value = params[2] or ""
  1933.     end
  1934.  
  1935.  
  1936.     if not playerData[player] then
  1937.     return "This is not a registered player."
  1938.     end
  1939.    
  1940.     if not settings[setting] then
  1941.         if not getPlayerSetting("canChangeOtherPlayersSettings", player) then
  1942.             if playerData[player] then
  1943.             return "You do not have permission to change other players settings."
  1944.             else
  1945.             return "This setting does not exist."
  1946.             end
  1947.         end
  1948.     end
  1949.    
  1950.     if getPermissionLevel(player) < settings[setting]["requiredPermLevl"]  then
  1951.     return "Your permission level is not high enough to change this setting."
  1952.     end
  1953.    
  1954.     if player ~= executingPlayer and getPermissionLevel(player) >= getPermissionLevel(executingPlayer) then
  1955.     return "Your permission level is not high enaugh to change this players settings."
  1956.     end
  1957.    
  1958.    
  1959.     if settings[setting]["type"] == "boolean" then
  1960.         if string.lower(value) == "true" then
  1961.         value = true
  1962.         elseif string.lower(value) == "false" then
  1963.         value = false
  1964.         end
  1965.     elseif settings[setting]["type"] == "number" then
  1966.     value = tonumber(value)
  1967.     end
  1968.    
  1969.     if type(value) ~= settings[setting]["type"] then
  1970.     return "bad argument (value has the wrong type, expected type: ".. settings[setting]["type"] ..")"
  1971.     end
  1972.    
  1973.    
  1974.     if executingPlayer == player or (isPermLevelHigher(executingPlayer,player) and getPlayerSetting("canChangeOtherPlayersSettings")) then
  1975.     changeSetting(player,setting,value)
  1976.     sendMessage("The setting was succesfully changed.", executingPlayer, "green")
  1977.     end
  1978.  
  1979. end
  1980.  
  1981.  
  1982.  
  1983. function chatFunctions.help(params) -- chat function that gives the executing player information about the chat commands
  1984. local returnMessage = nil
  1985. local getHelpOnWhat = params[1]
  1986.  
  1987.     if tonumber(getHelpOnWhat) or not getHelpOnWhat then -- checks if a chat command to get information on was specified
  1988.     local sendThisMessage = {}
  1989.     local page = tonumber(getHelpOnWhat) or 1
  1990.     local numberOfCommands = 0
  1991.    
  1992.         if (page-1)*10+1 > table.maxn(chatCommandNames) then
  1993.         sendMessage("This page does not exist.", executingPlayer, "red")
  1994.         return
  1995.         end
  1996.    
  1997.        
  1998.         if page > 1 then
  1999.         table.insert(sendThisMessage, {{text = "\n<<<< Last page", color = "blue", hoverEvent = hoverMessage("shift + click to insert"), insertion = "$@help " .. page-1}})
  2000.         end
  2001.        
  2002.         for k,commandName in pairs(chatCommandNames) do
  2003.             if numberOfCommands >= (page-1)*10 and numberOfCommands < page*10 then
  2004.             local info = chatCommands[commandName]["info"]
  2005.                 if getPermissionLevel(executingPlayer) >= chatCommands[commandName]["requiredPermLevl"] then
  2006.                 table.insert(sendThisMessage,
  2007.                 {"\n",
  2008.                 {text = "@" .. commandName, color = "green", insertion = "$@" .. commandName, hoverEvent = hoverMessage(info .. "\nshift + click to insert")},
  2009.                 {text = " - Required permission level: ", insertion = "$@" .. commandName, hoverEvent = hoverMessage(info .. "\nshift + click to insert")},
  2010.                 {text = chatCommands[commandName]["requiredPermLevl"], color = "green", insertion = "$@" .. commandName, hoverEvent = hoverMessage(info .. "\nshift + click to insert")}})
  2011.                 else
  2012.                 table.insert(sendThisMessage,
  2013.                 {"\n",
  2014.                 {text = "@" .. commandName, color = "red", hoverEvent = hoverMessage("You dont have permission to use this command.")},
  2015.                 {text = " - Required permission level: ", color = "red",hoverEvent = hoverMessage("You dont have permission to use this command.")},
  2016.                 {text = chatCommands[commandName]["requiredPermLevl"], color = "red", hoverEvent = hoverMessage("You dont have permission to use this command.")}})
  2017.                 end
  2018.             end
  2019.         numberOfCommands = numberOfCommands + 1
  2020.         end
  2021.        
  2022.         if (page)*10 < table.maxn(chatCommandNames) then
  2023.         table.insert(sendThisMessage, {{text = "\nNext page >>>>", color = "blue", hoverEvent = hoverMessage("shift + click to insert"), insertion = "$@help " .. page+1}})
  2024.         end
  2025.     sendMessage(combineFormattedMessages(sendThisMessage), executingPlayer)
  2026.     return
  2027.     end
  2028.    
  2029.    
  2030.     if chatCommands[getHelpOnWhat] then
  2031.     sendMessage({"\n", {text = "@" .. getHelpOnWhat .. ": ", color = "green", insertion = "$@" .. getHelpOnWhat, hoverEvent = {action = "show_text",value = "shift + click to insert"}}, {text = chatCommands[getHelpOnWhat]["info"] .. " ", insertion = "$@" .. getHelpOnWhat, hoverEvent = {action = "show_text",value = "shift + click to insert"} }, {text = "Required permission level: " .. chatCommands[getHelpOnWhat]["requiredPermLevl"], color = "green", insertion = "$@" .. getHelpOnWhat, hoverEvent = {action = "show_text",value = "shift + click to insert"}}}, executingPlayer)
  2032.     else
  2033.     return nil
  2034.     end
  2035.        
  2036.    
  2037. end
  2038.  
  2039.  
  2040.  
  2041. function chatFunctions.timeTop(params) -- shows the executing player the playtime scoreboard
  2042. showScoreboard("playtime", executingPlayer, params[1], " minutes")
  2043. end
  2044.  
  2045.  
  2046.  
  2047. function chatFunctions.listSettings() -- lists the settings of the executing player
  2048. local message = {}
  2049. local greenOrRed = {[true] = "green", [false] = "red"}
  2050.     for k,settingName in pairs(settingNames) do
  2051.     local settingValue = getPlayerSetting(settingName,executingPlayer)
  2052.     table.insert(message, "\n")
  2053.         if getPermissionLevel(executingPlayer) >= settings[settingName]["requiredPermLevl"] then
  2054.         table.insert(message, {text = settingName .. ": ", color = "green", insertion = "$@changeSetting " .. settingName .. " ", hoverEvent = hoverMessage(settings[settingName]["info"] .. "\nshift + click to change")})
  2055.         table.insert(message, {text = settingValue, insertion = "$@changeSetting " .. settingName .. " ", hoverEvent = hoverMessage(settings[settingName]["info"] .. "\nshift + click to change")})
  2056.         else
  2057.         table.insert(message, {text = settingName .. ": ", color = "red", hoverEvent = hoverMessage(settings[settingName]["info"] .. "\nYou dont have permission to change this setting.")})
  2058.         table.insert(message, {text = settingValue, hoverEvent = hoverMessage(settings[settingName]["info"] .. "\nYou dont have permission to change this setting.")})
  2059.         end
  2060.     end
  2061.  
  2062. sendMessage(message, executingPlayer)
  2063. end
  2064.  
  2065.  
  2066.  
  2067. function chatFunctions.balance(params) -- tells the executing player what their balance is
  2068. player = exactName(params[1] or executingPlayer)
  2069.  
  2070.     if not playerData[player] then
  2071.     return "This is not a registered player."
  2072.     end
  2073. sendMessage({"Balance: ", {text = (playerData[player]["stats"]["balance"] or 0) .. "c", color = "green"}}, executingPlayer)
  2074. end
  2075.  
  2076.  
  2077.  
  2078. function chatFunctions.pay(params) -- pays the receiving player the specified amount and removes that amount from the balance of the executing player
  2079. local receivingPlayer = exactName(params[1])
  2080. local amount = tonumber(params[2])
  2081.  
  2082.     if not amount then
  2083.     return "You cant pay \"" .. (params[2] or "nothing") .. "\". The second parameter needs to be a number."
  2084.     end
  2085.  
  2086.     if not playerData[receivingPlayer] then
  2087.     return "This is not a registered player."
  2088.     end
  2089.    
  2090.     if receivingPlayer == executingPlayer then
  2091.     return "You cant pay yourself."
  2092.     end
  2093.  
  2094.     if amount > playerData[executingPlayer]["stats"]["balance"] then
  2095.     return "Your balance is too low."
  2096.     end
  2097.    
  2098.     if amount < 0 then
  2099.     return "You cant pay someon less then zero credits."
  2100.     end
  2101.    
  2102. playerData[receivingPlayer]["stats"]["balance"] = playerData[receivingPlayer]["stats"]["balance"] + amount
  2103. playerData[executingPlayer]["stats"]["balance"] = playerData[executingPlayer]["stats"]["balance"] - amount
  2104. savePlayerData(receivingPlayer, "stats")
  2105. savePlayerData(executingPlayer, "stats")
  2106.  
  2107. updateScoreboard("balance", receivingPlayer)
  2108. updateScoreboard("balance", executingPlayer)
  2109.  
  2110. sendMessage({"You succesfully paid ", receivingPlayer, " ", {text = amount .. "c", color = "green"}, "\n Balance: ", {text = playerData[executingPlayer]["stats"]["balance"] .. "c", color = "green"}}, executingPlayer)
  2111. end
  2112.  
  2113.  
  2114.  
  2115. function chatFunctions.balTop(params) -- shows the executing player the balance scoreboard
  2116. showScoreboard("balance", executingPlayer, params[1], "c")
  2117. end
  2118.  
  2119.  
  2120.  
  2121. function chatFunctions.tp(params) -- teleports the executing player to the specified coordinates
  2122.  
  2123.     if not params[1] then
  2124.     return "The first parameter needs to be either a player or a number."
  2125.     end
  2126.  
  2127.  
  2128. local x,y,z = 0,0,0
  2129. local executingPlayerPos = playerDetector.getPlayerPos(executingPlayer)
  2130. local eX = executingPlayerPos["x"]
  2131. local eY = executingPlayerPos["y"]
  2132. local eZ = executingPlayerPos["z"]
  2133.  
  2134.     if table.contains(playerDetector.getOnlinePlayers(),exactName(params[1])) then
  2135.         if getPermissionLevel(executingPlayer) > 1 then
  2136.         local playerPos = playerDetector.getPlayerPos(exactName(params[1]))
  2137.         x = playerPos["x"]
  2138.         y = playerPos["y"]
  2139.         z = playerPos["z"]
  2140.         else
  2141.         return "You can only tp to other players at Member rank or higher."
  2142.         end
  2143.     elseif string.lower(params[1]) == "back" then
  2144.         if not playerData[executingPlayer]["lastTpPos"] then
  2145.         return "You dont have a saved last teleport position."
  2146.         end
  2147.     x = playerData[executingPlayer]["lastTpPos"]["x"]
  2148.     y = playerData[executingPlayer]["lastTpPos"]["y"]
  2149.     z = playerData[executingPlayer]["lastTpPos"]["z"]
  2150.     elseif params[1] and params[2] and params[3] then
  2151.         if string.sub(params[1],1,1) == "~" then
  2152.         x = (tonumber(string.sub(params[1],2)) or 0) + eX
  2153.         else
  2154.         x = tonumber(params[1])
  2155.         end
  2156.        
  2157.         if string.sub(params[2],1,1) == "~" then
  2158.         y = (tonumber(string.sub(params[2],2)) or 0) + eY
  2159.         else
  2160.         y = tonumber(params[2])
  2161.         end
  2162.    
  2163.         if string.sub(params[3],1,1) == "~" then
  2164.         z = (tonumber(string.sub(params[3],2)) or 0) + eZ
  2165.         else
  2166.         z = tonumber(params[3])
  2167.         end
  2168.     else
  2169.     return 'Either you enter "back" or a player name as the first parameter, else all three parameters need to be numbers (coordinates).'
  2170.     end
  2171.  
  2172.     if type(x) ~= "number" then
  2173.     return "The first parameter needs to be either a player or a number."
  2174.     end
  2175.    
  2176.     if type(y) ~= "number" then
  2177.     return "The second parameter needs to be a number."
  2178.     end
  2179.    
  2180.     if type(z) ~= "number" then
  2181.     return "The third parameter needs to be a number."
  2182.     end
  2183.  
  2184.  
  2185.  
  2186. local droneName = getFreeDrone()
  2187.  
  2188.     if not droneName then
  2189.     sendMessage("There is currently no free drone available.", executingPlayer, "red")
  2190.     return
  2191.     elseif setDroneAction(droneName,"teleportPlayer",{{x=eX,y=eY,z=eZ},{x=x,y=y,z=z}},executingPlayer) then
  2192.     sendMessage("Please stand still until the teleportation is complete.", executingPlayer, "green")
  2193.     playerData[executingPlayer]["lastTpPos"] = {["x"]=eX,["y"]=eY,["z"]=eZ}
  2194.     savePlayerData(executingPlayer,"lastTpPos")
  2195.     end
  2196. end
  2197.  
  2198.  
  2199.  
  2200. function chatFunctions.warp(params)
  2201. local warpName = ""
  2202.     for k,v in pairs(params) do
  2203.     warpName = warpName .. v
  2204.     end
  2205. warpName = string.lower(warpName)
  2206.  
  2207.  
  2208.     if not (playerData[executingPlayer]["warps"][warpName] or publicWarps[warpName]) then
  2209.     listWarps(executingPlayer,true)
  2210.     return
  2211.     end
  2212.    
  2213.  
  2214. local executingPlayerPos = playerDetector.getPlayerPos(executingPlayer)
  2215. local eX = executingPlayerPos["x"]
  2216. local eY = executingPlayerPos["y"]
  2217. local eZ = executingPlayerPos["z"]
  2218. local warpPos = playerData[executingPlayer]["warps"][warpName] or publicWarps[warpName]
  2219. local droneName = getFreeDrone()
  2220.  
  2221.     if not droneName then
  2222.     sendMessage("There is currently no free drone available.", executingPlayer, "red")
  2223.     return
  2224.     elseif setDroneAction(droneName,"teleportPlayer",{{x=eX,y=eY,z=eZ},{x=warpPos["x"],y=warpPos["y"],z=warpPos["z"]}},executingPlayer) then
  2225.     sendMessage("Please stand still until the teleportation is complete.", executingPlayer, "green")
  2226.     playerData[executingPlayer]["lastTpPos"] = {["x"]=eX,["y"]=eY,["z"]=eZ}
  2227.     savePlayerData(executingPlayer,"lastTpPos")
  2228.     end
  2229. end
  2230.  
  2231.  
  2232.  
  2233. function chatFunctions.ban(params) -- bans the specified player
  2234. local player = exactName(params[1])
  2235.     if not playerData[player] then
  2236.     return "This is not a registered player."
  2237.     end
  2238.    
  2239.     if playerData[player]["rank"] == "Banned" then
  2240.     return "This player is already banned."
  2241.     end
  2242.  
  2243. banPlayer(player, executingPlayer)
  2244. end
  2245.  
  2246.  
  2247.  
  2248. function chatFunctions.unban(params) -- unbans the specifed player
  2249. local player = exactName(params[1])
  2250.     if not playerData[player] then
  2251.     return "This is not a registered player."
  2252.     end
  2253.  
  2254.     if playerData[player]["rank"] ~= "Banned" then
  2255.     return "This player is not banned."
  2256.     end
  2257.  
  2258. unbanPlayer(player, executingPlayer)
  2259. end
  2260.  
  2261.  
  2262.  
  2263. function chatFunctions.giveFeedback(params) -- lets the executing player five feedback about the system, the feedback will be sent to all owners as a mail
  2264. local feedback = ""
  2265.     for k,v in pairs(params) do
  2266.     feedback = feedback .. v .. " "
  2267.     end
  2268.  
  2269.     for playerName, data in pairs(playerData) do
  2270.         if data["rank"] == "Owner" then
  2271.         sendMail(playerName,"Feedback from " .. executingPlayer,feedback,executingPlayer)
  2272.         end
  2273.     end
  2274.  
  2275. sendMessage("Thank you for giving feedback.", executingPlayer, "green")
  2276. end
  2277.  
  2278. ------------------------------------------------
  2279.  
  2280.  
  2281.  
  2282.  
  2283. -- subFunctions
  2284.  
  2285. function sendAvailableSubcommands(player,chatCommand) -- sends the given player a message that contains all subcommands of the given chat command
  2286. local availableSubcommands = subcommands[chatCommand]
  2287.  
  2288. local message = {"Available subcommands:"}
  2289.     for subName,subData in pairs(availableSubcommands) do
  2290.     table.insert(message, {text = "\n@"  .. chatCommand .. " ", insertion = "$@" .. chatCommand .. " " .. subName, hoverEvent = hoverMessage(subData["info"] .. "\nshift + click to insert")})
  2291.     table.insert(message, {text = subName, insertion = "$@" .. chatCommand .. " " .. subName, hoverEvent = hoverMessage(subData["info"] .. "\nshift + click to insert"), color = "green"})
  2292.     end
  2293. sendMessage(message, player)
  2294. end
  2295.  
  2296.  
  2297.  
  2298. function createSubcommand(mainCommand,name,requiredPermLevl,info) -- creates a sub command (for example: @about system)
  2299. subcommands = subcommands or {}
  2300. subcommands[mainCommand] = subcommands[mainCommand] or {}
  2301. subcommands[mainCommand][name] = {["info"] = info, ["requiredPermLevl"] = requiredPermLevl}
  2302. end
  2303.  
  2304.  
  2305.  
  2306. function subFunctions.about.system() -- Gives the executingPlayer player information about the system.
  2307. local ownerOrOwners, ownerNames = getOwnersText()
  2308.  
  2309.  
  2310. sendMessage({
  2311. {text = "\nFirst startup date: ", color = "green"},
  2312. firstStartupDate .. "\n",
  2313. ownerOrOwners,
  2314. ownerNames,
  2315. {text = "\nRegistered player count: ", color = "green"},
  2316. table.maxn(playerNames),
  2317. {text = "\nConnected drone count: ", color = "green"},
  2318. getConnectedDroneCount(),
  2319. {text = "\nMain program pastebin link: ", color = "green", clickEvent = {action = "open_url", value = "https://pastebin.com/" .. mainProgramLink}, hoverEvent = hoverMessage("Click to open link.")},
  2320. {text = mainProgramLink, clickEvent = {action = "open_url", value = "https://pastebin.com/" .. mainProgramLink}, hoverEvent = hoverMessage("Click to open link.")},
  2321. {text = "\nMessage sender pastebin link: ", color = "green", clickEvent = {action = "open_url", value = "https://pastebin.com/" .. messageSenderLink}, hoverEvent = hoverMessage("Click to open link.")},
  2322. {text = messageSenderLink, clickEvent = {action = "open_url", value = "https://pastebin.com/" .. messageSenderLink}, hoverEvent = hoverMessage("Click to open link.")},
  2323. {text = "\nServer API pastebin link: ", color = "green", clickEvent = {action = "open_url", value = "https://pastebin.com/" .. serverAPILink}, hoverEvent = hoverMessage("Click to open link.")},
  2324. {text = serverAPILink, clickEvent = {action = "open_url", value = "https://pastebin.com/" .. serverAPILink}, hoverEvent = hoverMessage("Click to open link.")}
  2325.  
  2326. }, executingPlayer)
  2327. end
  2328.  
  2329.  
  2330.  
  2331. function subFunctions.about.ranks() -- Gives the executingPlayer player information about all ranks.
  2332. sendMessage(rankInformationMessage, executingPlayer)
  2333. end
  2334.  
  2335.  
  2336.  
  2337. function subFunctions.about.player(params) -- Gives the executingPlayer player information the stated player.
  2338. local playerName = exactName(params[1])
  2339.     if not playerData[playerName] then
  2340.     return "This is not a registered player."
  2341.     end
  2342.  
  2343.  
  2344. local message = {}
  2345. table.insert(message, {text = playerName .. "'s information:", color = "green"})
  2346.  
  2347. table.insert(message, "\nRank: ")
  2348. table.insert(message, {text = playerData[playerName]["rank"], color = "green"})
  2349.  
  2350. table.insert(message, "\nlast login date: ")
  2351. table.insert(message, {text = playerData[playerName]["lastLogin"] or "Unknown", color = "green"})
  2352.  
  2353. table.insert(message, "\n\nStats: ")
  2354.     for statName,stat in pairs(playerData[playerName]["stats"]) do
  2355.     table.insert(message, "\n  - " .. statName .. ": ")
  2356.     table.insert(message, {text = stat, color = "green"})
  2357.     end
  2358.    
  2359. table.insert(message, "\n\nScoreboards: ")
  2360.     for statName,stat in pairs(scoreboards) do
  2361.     table.insert(message, "\n  - " .. statName .. " scoreboard" .. ": ")
  2362.     table.insert(message, {text = getScoreboardPosition(statName, playerName) .. ". Place", color = "green"})
  2363.     end
  2364.    
  2365.    
  2366. sendMessage(message, executingPlayer)
  2367. end
  2368.  
  2369.  
  2370.  
  2371. function subFunctions.mail.send(params) -- sends a mail to the player stated as the first parameter
  2372. local receiver = params[1]
  2373.     if not playerData[receiver] then
  2374.     return "The first parameter needs to be a registered player."
  2375.     end
  2376.    
  2377. local text = ""
  2378. params[1] = nil
  2379.     for k,v in pairs(params) do
  2380.     text = text .. v .. " "
  2381.     end
  2382.    
  2383.     if string.len(text) < 2 then
  2384.     return "Your message is too short."
  2385.     end
  2386. sendMail(receiver, "Mail from " .. executingPlayer, text, executingPlayer)
  2387. sendMessage("Your mail was succesfully sent.", executingPlayer, "green")
  2388. end
  2389.  
  2390.  
  2391.  
  2392. function subFunctions.mail.list(params) -- lists all mails of the executing player
  2393. local page = tonumber(params[1]) or 1
  2394. local mailsPerPage = 10
  2395. local mails = playerData[executingPlayer]["receivedMails"]
  2396.  
  2397.     if not mails[1] then
  2398.     return "You dont have any Mails."
  2399.     end
  2400.  
  2401.     if not mails[page*mailsPerPage-mailsPerPage+1] then
  2402.     return "This page does not exist."
  2403.     end
  2404.    
  2405. local message = {}
  2406.     if page > 1 then
  2407.     message[1] = {text = "\n<<<< Last page", color = "blue", hoverEvent = hoverMessage("shift + click to insert"), insertion = "$@mail list " .. page-1}
  2408.     end
  2409.  
  2410.     for i = 1, mailsPerPage do
  2411.     local mailID = page*mailsPerPage-mailsPerPage+i
  2412.     local curMail = mails[mailID]
  2413.         if not curMail then
  2414.         break
  2415.         end
  2416.    
  2417.         if curMail["isRead"] then
  2418.         table.insert(message, {text = "\n" .. curMail["subject"], hoverEvent = hoverMessage("Shift + click to read"), insertion = "$@mail read " .. mailID, color = "gray"})
  2419.         else
  2420.         table.insert(message, {text = "\n" .. curMail["subject"], hoverEvent = hoverMessage("Shift + click to read"), insertion = "$@mail read " .. mailID, color = "green"})
  2421.         end
  2422.     end
  2423.    
  2424.     if mails[(page+1)*mailsPerPage-mailsPerPage+1] then
  2425.     table.insert(message, {text = "\nNext page >>>>", color = "blue", hoverEvent = hoverMessage("shift + click to insert"), insertion = "$@mail list " .. page+1})
  2426.     end
  2427.  
  2428.  
  2429. sendMessage(message, executingPlayer)
  2430. end
  2431.  
  2432.  
  2433.  
  2434. function subFunctions.mail.read(params) -- lets the executing player read the mail stated as the first parameter
  2435. local mailID = tonumber(params[1])
  2436. local mail = playerData[executingPlayer]["receivedMails"][mailID]
  2437.  
  2438.     if not mail then
  2439.     return "The first parameter needs to be the ID (number) of one of your mails."
  2440.     end
  2441.    
  2442.  
  2443. local message = {}
  2444. message[1] = {text = "\nSender: "}
  2445. message[2] = {text = mail["sender"], color = "green"}
  2446.  
  2447. message[3] = {text = "\nDate: ",}
  2448. message[4] = {text = mail["date"], color = "green"}
  2449.  
  2450. message[5] = {text = "\nSubject: "}
  2451. message[6] = {text = mail["subject"], color = "green"}
  2452.  
  2453. message[7] = {text = "\n\n" .. mail["text"] .. "\n\n"}
  2454.  
  2455.  
  2456.     for k,v in pairs(message) do -- doing this cause if i dont, for some reason the player info shows up on every part of the message
  2457.     message[k]["hoverEvent"] = hoverMessage("")
  2458.     message[k]["insertion"] = ""
  2459.     end
  2460.  
  2461.  
  2462.     if playerData[mail["sender"]] then
  2463.     message[1]["hoverEvent"] = hoverMessage("Shift + click to get information about this player.")
  2464.     message[1]["insertion"] = "$@about player " .. mail["sender"]
  2465.     message[2]["hoverEvent"] = hoverMessage("Shift + click to get information about this player.")
  2466.     message[2]["insertion"] = "$@about player " .. mail["sender"]
  2467.     message[8] = {text = "[Respond] ", color = "green", hoverEvent = hoverMessage("Shift + click to respond"), insertion = "$@mail respond " .. mailID .. " "}
  2468.     message[9] = {text = "[Delete] ", color = "red", hoverEvent = hoverMessage("Shift + click to delete"), insertion = "$@mail delete " .. mailID}
  2469.         if playerData[executingPlayer]["receivedMails"][mailID+1] then
  2470.         message[10] = {text = "[Read next]", color = "blue", hoverEvent = hoverMessage("Shift + click to read next"), insertion = "$@mail read " .. mailID+1}
  2471.         end
  2472.     else
  2473.     message[8] = {text = "[Delete] ", color = "red", hoverEvent = hoverMessage("Shift + click to delete"), insertion = "$@mail delete " .. mailID}
  2474.         if playerData[executingPlayer]["receivedMails"][mailID+1] then
  2475.         message[9] = {text = "[Read next]", color = "blue", hoverEvent = hoverMessage("Shift + click to read next"), insertion = "$@mail read " .. mailID+1}
  2476.         end
  2477.     end
  2478.    
  2479.  
  2480. sendMessage(message, executingPlayer)
  2481.  
  2482. mail["isRead"] = true
  2483. savePlayerData(executingPlayer,"receivedMails")
  2484. end
  2485.  
  2486.  
  2487.  
  2488. function subFunctions.mail.delete(params) -- deletes the mail with the given id
  2489. local mailID = tonumber(params[1])
  2490.  
  2491.     if not playerData[executingPlayer]["receivedMails"][mailID] then
  2492.     return "The first parameter needs to be the ID (number) of one of your mails."
  2493.     end
  2494.    
  2495. table.remove(playerData[executingPlayer]["receivedMails"], mailID)
  2496. savePlayerData(executingPlayer, "receivedMails")
  2497.  
  2498. sendMessage("Your mail was succesfully deleted.", executingPlayer, "green")
  2499. end
  2500.  
  2501.  
  2502.  
  2503. function subFunctions.mail.respond(params) -- sends a mail as a response to the mail with the given id
  2504. local mailID = tonumber(params[1])
  2505. local mail = playerData[executingPlayer]["receivedMails"][mailID]
  2506.  
  2507.     if not mail then
  2508.     return "The first parameter needs to be the ID (number) of one of your mails."
  2509.     end
  2510.    
  2511. local text = ""
  2512. params[1] = nil
  2513.     for k,v in pairs(params) do
  2514.     text = text .. v .. " "
  2515.     end
  2516.    
  2517.     if string.len(text) < 2 then
  2518.     return "Your message is too short."
  2519.     end
  2520. sendMail(mail["sender"], "Response from " .. executingPlayer, text, executingPlayer)
  2521. sendMessage("Your response was succesfully sent.", executingPlayer, "green")
  2522. end
  2523.  
  2524.  
  2525.  
  2526. function subFunctions.mail.clear(params) -- deletes all the read mails of a player
  2527.     if not playerData[executingPlayer]["receivedMails"][1] then
  2528.     return "You dont have any Mails."
  2529.     end
  2530.    
  2531.    
  2532. local clearCount = 0
  2533.     repeat
  2534.     local deletedOne = false
  2535.         for mailID,mail in pairs(playerData[executingPlayer]["receivedMails"]) do
  2536.             if mail["isRead"] then
  2537.             clearCount = clearCount + 1
  2538.             table.remove(playerData[executingPlayer]["receivedMails"],mailID)
  2539.             deletedOne = true
  2540.             break
  2541.             end
  2542.         end
  2543.     until not deletedOne
  2544.    
  2545.     if clearCount == 0 then
  2546.     return "You dont have any read mails"
  2547.     elseif clearCount == 1 then
  2548.     savePlayerData(executingPlayer,"receivedMails")
  2549.     sendMessage("Deleted 1 read mail.",executingPlayer,"green")
  2550.     else
  2551.     savePlayerData(executingPlayer,"receivedMails")
  2552.     sendMessage("Deleted " .. clearCount .. " read mails.",executingPlayer,"green")
  2553.     end
  2554.  
  2555. end
  2556.  
  2557.  
  2558.  
  2559. function subFunctions.computer.requestKey() -- sends the executing player a message with a key that they can use to use the server api
  2560.     if not (playerData[executingPlayer]["computerCount"] < getMaxComputers(executingPlayer)) then
  2561.     return "You already have your maximum computer keys, to get more you need to rank up."
  2562.     end
  2563.  
  2564.  
  2565. local key = generateKey()
  2566. table.insert(requestedKeys,{["key"] = key, ["owner"] = executingPlayer})
  2567. saveTable(programFolderName .. "/requestedKeys", requestedKeys)
  2568.  
  2569. playerData[executingPlayer]["computerCount"] = playerData[executingPlayer]["computerCount"] + 1
  2570. savePlayerData(executingPlayer)
  2571.  
  2572.  
  2573. local message = {}
  2574. table.insert(message,{text = "Your key is [", clickEvent = {action = "copy_to_clipboard", value = key}, hoverEvent = hoverMessage("Click to copy to clipboard")})
  2575. table.insert(message,{text = key, color = "green", clickEvent = {action = "copy_to_clipboard", value = key}, hoverEvent = hoverMessage("Click to copy to clipboard")})
  2576. table.insert(message,{text = "] please dont share this code.", clickEvent = {action = "copy_to_clipboard", value = key}, hoverEvent = hoverMessage("Click to copy to clipboard")})
  2577.  
  2578. sendMessage(message, executingPlayer)
  2579. end
  2580.  
  2581.  
  2582.  
  2583. function subFunctions.computer.clearKeys() -- deletes all unused requested keys of the executing player
  2584.     if playerData[executingPlayer]["computerCount"] == 0 then
  2585.     return "You dont have any requested Keys"
  2586.     end
  2587.    
  2588. local keyCount = 0
  2589.     for k,requestedKey in pairs(requestedKeys) do
  2590.         if requestedKey["owner"] == executingPlayer then
  2591.         requestedKeys[k] = nil
  2592.         keyCount = keyCount + 1
  2593.         end
  2594.     end
  2595.    
  2596.     if keyCount == 0 then
  2597.     return "You dont have any unused requested Keys"
  2598.     else
  2599.     playerData[executingPlayer]["computerCount"] = playerData[executingPlayer]["computerCount"] - keyCount
  2600.     savePlayerData(executingPlayer)
  2601.     saveTable(programFolderName .. "/requestedKeys", requestedKeys)
  2602.     sendMessage(keyCount .. " requested key(s) have been deleted.", executingPlayer, "green")
  2603.     end
  2604. end
  2605.  
  2606.  
  2607.  
  2608. function subFunctions.computer.list() -- lists all registered computers of a player
  2609.     if playerData[executingPlayer]["computerCount"] == 0 then
  2610.     return "You dont have any computers"
  2611.     end
  2612.    
  2613. local message = {}
  2614. local hasComputers = false
  2615.     for id, computer in pairs(getComputersOfPlayer(executingPlayer)) do
  2616.     hasComputers = true
  2617.     table.insert(message,{text = "\n- Computer ", hoverEvent = hoverMessage("Key = " .. computer["key"] .. "\nBalance = " .. computer["balance"] .. "\nShift + click to unregister"), insertion = "$@computer unregister " .. id})
  2618.     table.insert(message,{text = id, color = "green", hoverEvent = hoverMessage("Key = " .. computer["key"] .. "\nBalance = " .. computer["balance"] .. "\nShift + click to unregister"), insertion = "$@computer unregister " .. id})
  2619.     end
  2620.  
  2621.     if hasComputers then
  2622.     sendMessage(message, executingPlayer)
  2623.     else
  2624.     return "You dont have any registered computers"
  2625.     end
  2626. end
  2627.  
  2628.  
  2629.  
  2630. function subFunctions.computer.pay(params) -- lets the executing player pay the specifed amount to the specified computer
  2631. local id = tonumber(params[1])
  2632. local amount = tonumber(params[2])
  2633.  
  2634.     if not registeredComputers[id] then
  2635.     return "The first parameter needs to be a registered computer"
  2636.     end
  2637.    
  2638.     if not amount then
  2639.     return "The second parameter needs to be the amount of credits you want to pay the computer."
  2640.     end
  2641.    
  2642.     if amount <= 0 then
  2643.     return "The amount of credits you want to pay the computer needs to be positive."
  2644.     end
  2645.    
  2646.     if amount > playerData[executingPlayer]["stats"]["balance"] then
  2647.     return "Your balance is not high enaugh to pay this amount."
  2648.     end
  2649.    
  2650. playerData[executingPlayer]["stats"]["balance"] = playerData[executingPlayer]["stats"]["balance"] - amount
  2651. savePlayerData(executingPlayer,"stats")
  2652.  
  2653. registeredComputers[id]["balance"] = registeredComputers[id]["balance"] + amount
  2654. saveComputerData(id)
  2655.  
  2656. sendMessage("Payment succesfull.", executingPlayer, "green")
  2657. sendRednet(id,{["action"] = "receivedPayment", ["amount"] = amount, ["player"] = player})
  2658. end
  2659.  
  2660.  
  2661.  
  2662. function subFunctions.computer.unregister(params) -- unregisters the computer with the given id
  2663. local id = tonumber(params[1])
  2664.  
  2665.     if not registeredComputers[id] then
  2666.     return "The first parameter needs to be the id of an registered computer."
  2667.     end
  2668.    
  2669.     if registeredComputers[id]["owner"] ~= executingPlayer then
  2670.     return "You need to be the owner of the computer you want to unregister."
  2671.     end
  2672.  
  2673. unregisterComputer(id)
  2674. end
  2675.  
  2676.  
  2677.  
  2678. function subFunctions.editWarp.create(params) -- creates a warp with the given name
  2679. local warpName = ""
  2680.     for k,v in pairs(params) do
  2681.     warpName = warpName .. v
  2682.     end
  2683. warpName = string.lower(warpName)
  2684.  
  2685.  
  2686.     if playerData[executingPlayer]["warps"][warpName] then
  2687.     return "You already have a warp with this name."
  2688.     end
  2689.  
  2690.     if getMaxWarps(executingPlayer) <= getWarpCount(executingPlayer) then
  2691.     return "At your current rank you cant have more warps."
  2692.     end
  2693.    
  2694.    
  2695. local playerPos = playerDetector.getPlayerPos(executingPlayer)
  2696. playerData[executingPlayer]["warps"][warpName] = {["x"] = playerPos["x"], ["y"] = playerPos["y"], ["z"] = playerPos["z"]}
  2697. savePlayerData(executingPlayer,"warps")
  2698. sendMessage("Successfully created the warp named " .. warpName .. ".", executingPlayer, "green")
  2699. end
  2700.  
  2701.  
  2702.  
  2703. function subFunctions.editWarp.delete(params) -- deletes the warp with the given name
  2704. local warpName = ""
  2705.     for k,v in pairs(params) do
  2706.     warpName = warpName .. v
  2707.     end
  2708. warpName = string.lower(warpName)
  2709.  
  2710.     if not playerData[executingPlayer]["warps"][warpName] then
  2711.     return "You dont have a warp with this name."
  2712.     end
  2713.    
  2714.    
  2715.     if publicWarps[warpName] then
  2716.         if publicWarps[warpName]["owner"] == executingPlayer then
  2717.         publicWarps[warpName] = nil
  2718.         fs.delete(programFolderName .. "/publicWarps/" .. warpName)
  2719.         playerData[executingPlayer]["publicWarpCount"] = playerData[executingPlayer]["publicWarpCount"] - 1
  2720.         end
  2721.     end
  2722.    
  2723. playerData[executingPlayer]["warps"][warpName] = nil
  2724. savePlayerData(executingPlayer,"warps")
  2725. sendMessage("Successfully deleted the warp named " .. warpName .. ".", executingPlayer, "green")
  2726. end
  2727.  
  2728.  
  2729.  
  2730. function subFunctions.editWarp.setPublic(params) -- Sets the warp with the given name to be public
  2731. local warpName = ""
  2732.     for k,v in pairs(params) do
  2733.     warpName = warpName .. v
  2734.     end
  2735. warpName = string.lower(warpName)
  2736.  
  2737. local warp = playerData[executingPlayer]["warps"][warpName]
  2738.  
  2739.  
  2740.     if getRankVariable("maxPublicWarps",executingPlayer) <= playerData[executingPlayer]["publicWarpCount"] then
  2741.     return "At your current rank you cant have more public warps."
  2742.     end
  2743.    
  2744.     if not warp then
  2745.     return "You dont have a warp with this name."
  2746.     end
  2747.    
  2748.     if warp["isPublic"] then
  2749.     return "This warp is already public."
  2750.     end
  2751.  
  2752.     if publicWarps[warpName] then
  2753.     return "A public warp with this name already exists."
  2754.     end
  2755.    
  2756.  
  2757. publicWarps[warpName] = {["x"] = warp["x"], ["y"] = warp["y"], ["z"] = warp["z"], ["owner"] = executingPlayer}
  2758. saveTable(programFolderName .. "/publicWarps/" .. warpName, publicWarps[warpName])
  2759.  
  2760. playerData[executingPlayer]["publicWarpCount"] = playerData[executingPlayer]["publicWarpCount"] + 1
  2761. playerData[executingPlayer]["warps"][warpName]["isPublic"] = true
  2762. savePlayerData(executingPlayer)
  2763.  
  2764. sendMessage("Successfully set the warp named " .. warpName .. " to be public.", executingPlayer, "green")
  2765. end
  2766.  
  2767.  
  2768.  
  2769. function subFunctions.editWarp.setPrivate(params) -- Sets the warp with the given name to be private
  2770. local warpName = ""
  2771.     for k,v in pairs(params) do
  2772.     warpName = warpName .. v
  2773.     end
  2774. warpName = string.lower(warpName)
  2775.  
  2776. local warp = playerData[executingPlayer]["warps"][warpName]
  2777.    
  2778.     if not warp then
  2779.     return "You dont have a warp with this name."
  2780.     end
  2781.    
  2782.     if not warp["isPublic"] then
  2783.     return "This warp is already private."
  2784.     end
  2785.  
  2786.  
  2787.  
  2788. publicWarps[warpName] = nil
  2789. fs.delete(programFolderName .. "/publicWarps/" .. warpName)
  2790.  
  2791. playerData[executingPlayer]["publicWarpCount"] = playerData[executingPlayer]["publicWarpCount"] - 1
  2792. playerData[executingPlayer]["warps"][warpName]["isPublic"] = false
  2793. savePlayerData(executingPlayer)
  2794.  
  2795. sendMessage("Successfully set the warp named " .. warpName .. " to be private.", executingPlayer, "green")
  2796. end
  2797.  
  2798.  
  2799.  
  2800. function subFunctions.editWarp.list() -- lists all warps of the executing player
  2801. listWarps(executingPlayer)
  2802. end
  2803.  
  2804. ------------------------------------------------
  2805.  
  2806.  
  2807.  
  2808.  
  2809.  
  2810. -- Other stuff
  2811.  
  2812. function table.contains(inputTable,value) -- checks if the specified table contains a value
  2813.     for k,v in pairs(inputTable) do
  2814.         if v == value then
  2815.         return true
  2816.         end
  2817.     end
  2818. return false
  2819. end
  2820.  
  2821.  
  2822.  
  2823. function saveTable(folderName,inputTable) -- saves the specifed table in files inside of the specified folder
  2824. fs.delete(folderName)
  2825. fs.makeDir(folderName)
  2826. local file = false
  2827.  
  2828.     for k,v in pairs(inputTable) do
  2829.         if type(v) ~= "table" and type(v) ~= "function" then
  2830.             if not file then
  2831.             file = fs.open(folderName .. "/variables","w")
  2832.             end
  2833.            
  2834.             vType = type(v)
  2835.                 if vType == "boolean" then
  2836.                     if v then
  2837.                     v = "true"
  2838.                     else
  2839.                     v = "false"
  2840.                     end
  2841.                 end
  2842.        
  2843.         file.writeLine(k .. string.sub(type(k),1,1) .. "=" .. string.sub(vType,1,1) .. v)
  2844.         elseif type(v) == "table" then
  2845.         saveTable(folderName .. "/" .. k,v)
  2846.         end
  2847.     end
  2848.  
  2849.     if file then
  2850.     file.flush()
  2851.     file.close()
  2852.     end
  2853. end
  2854.  
  2855.  
  2856.  
  2857. function readFile(fileName) -- reads a file created by the save table function and returns the saved table
  2858. local file = fs.open(fileName,"r")
  2859. local returnTable = {}
  2860.  
  2861.     if not file then
  2862.     return {}
  2863.     end
  2864.    
  2865.    
  2866.     repeat
  2867.     local curLine = file.readLine()
  2868.     local equalPos = string.find(curLine or "","=")
  2869.    
  2870.    
  2871.         if equalPos then
  2872.         local indexType = string.sub(curLine,equalPos-1,equalPos-1)
  2873.         local valueType = string.sub(curLine,equalPos+1,equalPos+1)
  2874.         local index = string.sub(curLine,1,equalPos-2)
  2875.        
  2876.             if indexType == "n" then
  2877.             index = tonumber(index)
  2878.             elseif indexType == "b" then
  2879.                 if index == "true" then
  2880.                 index = true
  2881.                 else
  2882.                 index = false
  2883.                 end
  2884.             end
  2885.            
  2886.         local value = string.sub(curLine,equalPos+2)
  2887.             if valueType == "n" then
  2888.             value = tonumber(value)
  2889.             elseif valueType == "b" then
  2890.                 if value == "true" then
  2891.                 value = true
  2892.                 else
  2893.                 value = false
  2894.                 end
  2895.             end
  2896.         returnTable[index] = value
  2897.         end
  2898.        
  2899.     until not curLine
  2900. file.close()
  2901.    
  2902.    
  2903. return returnTable
  2904. end
  2905.  
  2906.  
  2907.  
  2908. function readFolder(folderName)-- reads a folder created by the save table function and returns the saved table
  2909. if not fs.isDir(folderName) then
  2910. return {}
  2911. end
  2912.  
  2913.  
  2914. local returnTable = {}
  2915.  
  2916.     for k,v in pairs(fs.list(folderName)) do
  2917.         if fs.isDir(folderName .. "/" .. v) then
  2918.             if tonumber(v) then
  2919.             key = tonumber(v)
  2920.             elseif v == "true" or v == "false" then
  2921.             key = v == "true"
  2922.             else
  2923.             key = v
  2924.             end
  2925.            
  2926.         returnTable[key] = readFolder(folderName .. "/" .. v)
  2927.         else
  2928.             for k2,v2 in pairs(readFile(folderName .. "/" .. v)) do
  2929.             returnTable[k2] = v2
  2930.             end
  2931.         end
  2932.     end
  2933.    
  2934.  
  2935. return returnTable
  2936. end
  2937.  
  2938. -------------------------------------------------
  2939.  
  2940. noError, errorMessage = pcall(startup)
  2941.  
  2942.     if noError then
  2943.     noError, errorMessage = pcall(main)
  2944.     end
  2945.    
  2946.     if errorMessage ~= "Terminated" then
  2947.    
  2948.     print(noError)
  2949.     print(errorMessage)
  2950.     local curDate = string.gsub(os.date(),":",".")
  2951.     local file = fs.open("crashReports/" .. curDate,"w")
  2952.     file.write(errorMessage)
  2953.     file.flush()
  2954.     file.close()
  2955.  
  2956.     term.setTextColor(colors.red)
  2957.     print("REBOOTING")
  2958.    
  2959.     sendMessage("It looks like the program crashed while executing your command. The program will take 10 seconds to restart, after that please report what you did with \"@giveFeedback\".", executingPlayer, "red")
  2960.    
  2961.     sleep(10)
  2962.     os.reboot()
  2963.     end
  2964.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement