HandieAndy

Railcraft Server

Jan 20th, 2017
370
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.55 KB | None | 0 0
  1. --Server Program:
  2. --  Tracks trains across network.
  3. --  Handles requests by load balancing.
  4. --  Keeps a list of stations.
  5. --Constants:
  6. rednet.open("top")
  7. --Files to store permanent or sensitive data.
  8. local TRAINFILE = "trains"
  9. local STATIONFILE = "stations"
  10. --Protocols:
  11. local METADATA_PROT = "md"
  12. local METADATA_RQ = "rq"
  13. local UPDATE_PROT = "ud"
  14. local UPDATE_RQ = "rq"
  15. local DISPATCH_PROT = "dp"
  16. local REQUEST_PROT = "rq"
  17. local STATION_LIST_PROT = "sl"
  18. local STATION_LIST_RQ = "rq"
  19. local REBOOT_PROT = "rb"
  20. local REBOOT_RQ = "rb"
  21. --Stations List:
  22. local stations = {}
  23. --Trains List:
  24. local trains = {}
  25.  
  26. --Returns the number of items in a table.
  27. function tableSize(tbl)
  28.     local count = 0
  29.     for i in pairs(tbl) do count = count + 1 end
  30.     return count
  31. end
  32.  
  33. --Loads the trainslist into memory.
  34. function loadTrainsFromFile()
  35.     if (not fs.exists(TRAINFILE)) then
  36.         local f = fs.open(TRAINFILE, "w")
  37.         f.write("{}")
  38.         f.close()
  39.     end
  40.     local f = fs.open(TRAINFILE, "r")
  41.     trains = textutils.unserialize(f.readAll())
  42.     f.close()
  43. end
  44.  
  45. --Saves the trainslist into the file.
  46. function saveTrainsToFile()
  47.     local f = fs.open(TRAINFILE, "w")
  48.     f.write(textutils.serialise(trains))
  49.     f.close()
  50. end
  51.  
  52. --Loads the stations list from the file.
  53. function loadStationsFromFile()
  54.     if (not fs.exists(STATIONFILE)) then
  55.         local f = fs.open(STATIONFILE, "w")
  56.         f.write("{}")
  57.         f.close()
  58.     end
  59.     local f = fs.open(STATIONFILE, "r")
  60.     stations = textutils.unserialize(f.readAll())
  61.     f.close()
  62. end
  63.  
  64. --Saves the stations list to the file.
  65. function saveStationsToFile()
  66.     local f = fs.open(STATIONFILE, "w")
  67.     f.write(textutils.serialize(stations))
  68.     f.close()
  69. end
  70.  
  71. --Gets a station id from the dest.
  72. function getStationId(dest)
  73.     for i in pairs(stations) do
  74.         if (stations[i].dest == dest) then
  75.             return stations[i].id
  76.         end
  77.     end
  78. end
  79.  
  80. --Returns a table of all trains at, or approaching a station.
  81. function getTrainsAtStation(stationId)
  82.     local trainsAtStation = {}
  83.     for i in pairs(trains) do
  84.         if (trains[i].loc == stations[stationId].dest or trains[i].dest == stations[stationId].dest) then
  85.             table.insert(trainsAtStation, trains[i])
  86.         end
  87.     end
  88.     return trainsAtStation
  89. end
  90.  
  91. --Returns the list of trains parked at a station.
  92. function getTrainsParkedAtStation(stationId)
  93.     local trainsParked = {}
  94.     local trainsAtStation = getTrainsAtStation(stationId)
  95.     for i in pairs(trainsAtStation) do
  96.         if (trainsAtStation[i].loc == stations[stationId].dest) then
  97.             table.insert(trainsParked, trainsAtStation[i])
  98.         end
  99.     end
  100.     return trainsParked
  101. end
  102.  
  103. --Broadcasts that all stations respond with metadata.
  104. function sendMetadataRequest()
  105.     term.setTextColor(colors.blue)
  106.     rednet.broadcast(METADATA_RQ, METADATA_PROT)
  107.     print("[M]Sent Metadata request.")
  108. end
  109.  
  110. --Received update from a station containing all trains at that station.
  111. function onUpdateReceived(msg, stationId)
  112.     if (stations[stationId] == nil) then
  113.         rednet.send(stationId, METADATA_RQ, METADATA_PROT)
  114.         term.setTextColor(colors.blue)
  115.         print("[M]Sent Metadata request.")
  116.         return
  117.     end
  118.     term.setTextColor(colors.lightBlue)
  119.     print("[U]Update received from station: "..stations[stationId].dest)
  120.     for k in pairs(msg) do
  121.         trains[msg[k]] = {id = msg[k], loc = stations[stationId].dest, dest = ""}
  122.         saveTrainsToFile()
  123.     end
  124. end
  125.  
  126. --Received metadata from a station.
  127. function onMetadataReceived(msg)
  128.     term.setTextColor(colors.blue)
  129.     print("[M]Metadata received from: "..msg.dest)
  130.     stations[msg.id] = msg
  131.     saveStationsToFile()
  132. end
  133.  
  134. --Prints all trains and their destinations.
  135. function printTrains()
  136.     term.setTextColor(colors.orange)
  137.     print("Trains: ")
  138.     for i in pairs(trains) do
  139.         print("  Train "..i..": Dest: "..trains[i].dest.." Loc: "..trains[i].loc)
  140.     end
  141. end
  142.  
  143. --Ensure trains are spread out as much as possible.
  144. function balanceTrains()
  145.     term.setTextColor(colors.pink)
  146.     print("[BAL]Balancing start {")
  147.     for i in pairs(stations) do
  148.         local trainsAtStation = getTrainsAtStation(stations[i].id)
  149.         if (tableSize(trainsAtStation) == 0) then
  150.         --No trains at this station, so find an eligible train to send to it.
  151.             term.setTextColor(colors.magenta)
  152.             print("*[BAL]No trains at: "..stations[i].dest)
  153.             for k in pairs(stations) do
  154.                 local trainsParked = getTrainsParkedAtStation(stations[k].id)
  155.                 if (tableSize(trainsParked) > 1) then
  156.                 --This station has a train to spare, so send it to the needy station.
  157.                     sendTrainTo(trainsParked[1].id, stations[i].dest)
  158.                     break
  159.                 end
  160.             end
  161.         elseif (tableSize(trainsAtStation) > stations[i].trackCount) then
  162.         --Station is over-saturated, so find a station that can take a train.
  163.             term.setTextColor(colors.magenta)
  164.             print("*[BAL]Station "..stations[i].dest.." is saturated.")
  165.             for k in pairs(stations) do
  166.                 local trainsAtPotentialStation = getTrainsAtStation(stations[k].id)
  167.                 if (tableSize(trainsAtPotentialStation) < stations[k].trackCount) then
  168.                     --Station is below capacity, so it can take a train.
  169.                     local trainsParked = getTrainsParkedAtStation(stations[i].id)
  170.                     if (tableSize(trainsParked) > 0) then
  171.                     --Check that there is at least one train parked at the saturated station.
  172.                         sendTrainTo(trainsParked[1].id, stations[k].dest)
  173.                     end
  174.                 end
  175.             end
  176.         end
  177.     end
  178.     term.setTextColor(colors.pink)
  179.     print("[BAL]Balancing done.")
  180. end
  181.  
  182. --Attempts to send a train from its current station to a new one.
  183. function sendTrainTo(trainId, dest)
  184.     rednet.send(getStationId(trains[trainId].loc), {dest, trainId}, DISPATCH_PROT)
  185.     trains[trainId].loc = ""
  186.     trains[trainId].dest = dest
  187.     saveTrainsToFile()
  188.     term.setTextColor(colors.lime)
  189.     print("  Sent train "..trainId.." to "..dest)
  190. end
  191.  
  192. --Initialize the server.
  193. function init()
  194.     term.clear()
  195.     term.setCursorPos(1,1)
  196.     term.setTextColor(colors.yellow)
  197.     print("Initializing server. ID: "..os.getComputerID())
  198.     loadStationsFromFile()
  199.     loadTrainsFromFile()
  200.     sendMetadataRequest()
  201.     broadcastStationList()
  202. end
  203.  
  204. --Deals with station-initiated requests for train dispatch.
  205. function onRequestReceived(msg, stationId)
  206.     local parkedTrains = getTrainsParkedAtStation(stationId)
  207.     if (tableSize(parkedTrains) < 1) then
  208.         return
  209.     else
  210.         sendTrainTo(parkedTrains[1].id, msg)
  211.         balanceTrains()
  212.     end
  213. end
  214.  
  215. --Sends the requester a copy of the stations list.
  216. function onStationListRequestReceived(stationId)
  217.     local stationList = {}
  218.     for i in pairs(stations) do
  219.         table.insert(stationList, {dest = stations[i].dest, name = stations[i].name})
  220.     end
  221.     rednet.send(stationId, stationList, STATION_LIST_PROT)
  222. end
  223.  
  224. --Broadcasts the stations list to all listening stations.
  225. function broadcastStationList()
  226.     local stationList = {}
  227.     for i in pairs(stations) do
  228.         table.insert(stationList, {dest = stations[i].dest, name = stations[i].name})
  229.     end
  230.     rednet.broadcast(stationList, STATION_LIST_PROT)
  231. end
  232.  
  233. --Forces a reboot of all stations.
  234. function broadcastRebootRequest()
  235.     rednet.broadcast(REBOOT_RQ, REBOOT_PROT)
  236.     term.setTextColor(colors.red)
  237.     print("Sent reboot request.")
  238.     os.sleep(2)
  239. end
  240.  
  241. --Main event handling function.
  242. function eventHandler()
  243.     local event,p1,p2,p3,p4,p5 = os.pullEvent()
  244.     if (event == "rednet_message") then
  245.         if (p3 == UPDATE_PROT) then
  246.             onUpdateReceived(p2, p1)
  247.         elseif (p3 == METADATA_PROT) then
  248.             onMetadataReceived(p2)
  249.         elseif (p3 == REQUEST_PROT) then
  250.             onRequestReceived(p2, p1)
  251.         elseif (p3 == STATION_LIST_PROT and p2 == STATION_LIST_RQ) then
  252.             onStationListRequestReceived(p1)
  253.         end
  254.     elseif (event == "key") then
  255.         if (p1 == keys.r) then
  256.             --Send reboot.
  257.             broadcastRebootRequest()
  258.         end
  259.     end
  260. end
  261.  
  262. --Mainloop:
  263. init()
  264. while (true) do
  265.     eventHandler()
  266. end
Add Comment
Please, Sign In to add comment