Advertisement
Treyzania

RedMesh v1.0

Feb 17th, 2015
205
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.97 KB | None | 0 0
  1. -- RedMesh v1.0
  2. -- Author: Treyzania
  3.  
  4. -- CONFIG
  5.  
  6. local cacheTime = 0.25 -- 6 in-game hours, ~5 minutes
  7. local savePackets = false -- DON'T ENABLE.  NYI!
  8.  
  9. local configDir = "/redmesh.cfg/" -- Make sure to have the trailing '/'.
  10.  
  11. -- END CONFIG
  12.  
  13. local ident = "__noname"
  14. local rnProtocolName = "redmesh"
  15. local recv = nil
  16. local verbose = false
  17.  
  18. local recent = {}
  19. local messageHandlers = {}
  20. local modemSides = {}
  21.  
  22. local oldrednetreceive = nil
  23.  
  24. -- Some verbosity functions.
  25. function setVerbose(val)
  26.     verbose = val
  27.     pcall(function() hypercoro.verbose = val end)
  28. end
  29. function vprint(text) if verbose then print(text) end end
  30.  
  31. local function savePacket(pkt)
  32.    
  33.     local sig = pkt["contents"]["signature"]
  34.     local file = fs.open(configDir .. "pktlog/" .. tostring(sig) .. ".rmp", "w");
  35.    
  36.     file:write(textutils.serialize(pkt))
  37.     file:close()
  38.    
  39. end
  40.  
  41. function rawTime()
  42.     return os.day() + (os.time() / 24)
  43. end
  44.  
  45. local function msgSig(str, curTime)
  46.    
  47.     local t = curTime or rawTime()
  48.    
  49.     return sha1.sha1(str .. os.getComputerID() .. t)
  50.    
  51. end
  52.  
  53. local function cacheSignature(sig)
  54.    
  55.     recent[sig] = rawTime()
  56.    
  57. end
  58.  
  59. local function callHandlers(data)
  60.    
  61.     vprint("Calling handlers...")
  62.    
  63.     for i, v in ipairs(messageHandlers) do
  64.        
  65.         local r, err = pcall(function() v(data) end)
  66.        
  67.         -- This feels cheaty.
  68.         if r then
  69.             -- Everything looks all fine and dandy.
  70.         else
  71.             error("\nE: on " .. tostring(v) .. ": " .. tostring(err), 1)
  72.         end
  73.        
  74.     end
  75.    
  76. end
  77.  
  78. local function removeExpiredCaches()
  79.    
  80.     local t = rawTime()
  81.    
  82.     for k, v in pairs(recent) do
  83.        
  84.         if (t - v) > cacheTime then
  85.            
  86.             recent[k] = nil -- "Remove" it.
  87.            
  88.         end
  89.        
  90.     end
  91.    
  92. end
  93.  
  94. local function processable(sig)
  95.    
  96.     local s, err = pcall(removeExpiredCaches) -- Sometimes this errors.
  97.    
  98.     --print("Checking ", sig)
  99.    
  100.     local t = rawTime()
  101.    
  102.     local cacheResult = recent[sig] -- Nil if it's safe, something that's true if it's still cached.
  103.    
  104.     -- Update the cache.
  105.     recent[sig] = t
  106.    
  107.     -- If it's nil then we're golden!
  108.     return (type(cacheResult) == "nil")
  109.    
  110. end
  111.  
  112. local function pushDownwards(data)
  113.    
  114.     vprint("Pushing packet ", data, " to lower levels...")
  115.    
  116.     os.queueEvent("rednet_message", senderId, msg, mProto)
  117.     callHandlers(data)
  118.    
  119. end
  120.  
  121. local function makeEnvelope(contents)
  122.    
  123.     local env = {}
  124.    
  125.     env["sender_id"] = os.getComputerID()
  126.     env["type"] = contents["type"]
  127.     env["contents"] = contents
  128.    
  129.     return env
  130.    
  131. end
  132.  
  133. local function makePgtPacket(dest, message, mType, proto)
  134.    
  135.     local t = rawTime()
  136.    
  137.     -- Forms the body table of the request.
  138.     local body = {}
  139.     body["dest"] = dest -- "*" for broadcasts.
  140.     body["sender"] = ident
  141.     body["sender_id"] = os.getComputerID()
  142.     body["signature"] = msgSig(message, t)
  143.     body["time"] = t
  144.     body["type"] = mType
  145.     body["protocol"] = base64.to_base64(proto)
  146.     body["message"] = base64.to_base64(message)
  147.    
  148.     return body
  149.    
  150. end
  151.  
  152. local function repropegate(mType, data)
  153.    
  154.     -- Simple enough.  Should get it everywhere else.
  155.     rednet.broadcast(textutils.serialize(makeEnvelope(data)), rnProtocolName)
  156.    
  157. end
  158.  
  159. function send(dest, message, proto)
  160.    
  161.     if not proto then error("no protocol") end
  162.    
  163.     -- Hopefully gets the message everywhere.
  164.     rednet.broadcast(textutils.serialize(makeEnvelope(makePgtPacket(tostring(dest), message, "p2p", proto))), rnProtocolName)
  165.    
  166. end
  167.  
  168. function broadcast(message, proto)
  169.    
  170.     if not proto then error("no protocol") end
  171.    
  172.     local packet = makePgtPacket("*", message, "broadcast", proto)
  173.    
  174.     processable(packet["sig"])
  175.    
  176.     -- Hopefully gets it to everywhere, somehow.
  177.     rednet.broadcast(textutils.serialize(makeEnvelope(packet)), rnProtocolName)
  178.    
  179. end
  180.  
  181. local function recvLoop()
  182.    
  183.     print("RedMesh loop started!")
  184.    
  185.     while true do
  186.        
  187.         local sender, msg, proto = rednet.receive(rnProtocolName, 30)
  188.        
  189.         local env = {}
  190.        
  191.         if sender then
  192.            
  193.             local a, b = pcall(function() env = textutils.unserialize(msg) end) -- This was buggin me for HOURS!  It's not "deserialize!"
  194.            
  195.             if a then -- Tries to deserialize.
  196.                
  197.                 local mType = env["type"]
  198.                 local sender = env["sender_id"]
  199.                 local data = env["contents"]
  200.                
  201.                 local senderId = data["sender_id"]
  202.                 local mProto = base64.from_base64(data["protocol"])
  203.                 local msg = base64.from_base64(data["message"])
  204.                 local sig = data["signature"]
  205.                
  206.                 if savePackets then savePacket(env) end
  207.                
  208.                 if processable(sig) then
  209.                    
  210.                     if mType == "p2p" then
  211.                        
  212.                         local dest = data["dest"]
  213.                         --vprint("P2P packet...")
  214.                        
  215.                         if dest == ident then
  216.                             --vprint("...got it!")
  217.                             pushDownwards(data)
  218.                         else
  219.                             --vprint("...repropegating!")
  220.                             repropegate("p2p", data) -- Pass it along if it isn't ours.
  221.                         end
  222.                        
  223.                     end
  224.                    
  225.                     if mType == "broadcast" then
  226.                        
  227.                         --vprint("Broadcast packet!")
  228.                        
  229.                         pushDownwards(data)
  230.                         repropegate("broadcast", data)
  231.                        
  232.                     end
  233.                    
  234.                 end
  235.                    
  236.             else
  237.                 vprint("Error in de-serialization, sender is " .. tostring(sender) .. ", \"" .. b .. "\".")
  238.             end
  239.            
  240.         else
  241.             -- Nothing happens.
  242.         end
  243.        
  244.     end
  245.    
  246. end
  247.  
  248. local function run()
  249.    
  250.     -- This function is just to restart the main loop if some error happens.
  251.    
  252.     while true do
  253.        
  254.         local yn, err = pcall(recvLoop)
  255.         print("ERROR: ", err)
  256.        
  257.     end
  258.    
  259. end
  260.  
  261. -- Handlers called as "handler(data)", where data is env["contents"].
  262. function registerHandler(handler)
  263.    
  264.     vprint("Added handler " .. tostring(handler) .. ".")
  265.    
  266.     local index = #messageHandlers + 1;
  267.     messageHandlers[index] = handler;
  268.     return index;
  269.    
  270. end
  271.  
  272. local function newrednetreceive(protocol, timeout)
  273.    
  274.     if not protocol then error("no protocol; string expected, got " .. type(protocol or nil)) end
  275.     return oldrednetreceive(protocol, timeout) -- Should work as expected.
  276.    
  277. end
  278.  
  279. function init(id)
  280.    
  281.     vprint("Loading RedMesh APIs...")
  282.    
  283.     os.loadAPI("sha1")
  284.     os.loadAPI("base64")
  285.     os.loadAPI("hypercoro")
  286.    
  287.     hypercoro.init()
  288.    
  289.     vprint("Done!  Beginning internals...")
  290.    
  291.     textutils.deserailzie = textutils.unserialize
  292.    
  293.     if not fs.exists(configDir) then
  294.         fs.makeDir(configDir) end
  295.     if not fs.exists(configDir .. "pktlog/") then
  296.         fs.makeDir(configDir .. "pktlog/") end
  297.    
  298.     -- Replace the old rednet method with the correct, better one.
  299.     oldrednetreceive = rednet.receive
  300.     rednet.receive = newrednetreceive
  301.    
  302.     ident = tostring(id)
  303.    
  304.     recv = hypercoro.create("redmesh.recvloop", run)
  305.    
  306.     vprint("RedMesh init complete!")
  307.     vprint("READY TO ROCK AND ROLL BABY!")
  308.    
  309. end
  310.  
  311. function addSide(side, modemType)
  312.    
  313.     vprint("Adding modem on " .. side .. "(" .. modemType .. ").")
  314.     rednet.open(side)
  315.     modemSides[side] = modemType
  316.    
  317. end
  318.  
  319. -- END OF FILE
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement