ProgramCrafter

thlib-v0.0.1

Mar 17th, 2021 (edited)
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.28 KB | None | 0 0
  1. -- ThunderNet main library; needs th_interfaces and th_inform.lua files.
  2. -- (c) ProgramCrafter
  3.  
  4. -- Last patch: security of thunderlib.run_server (06.04.2021)
  5.  
  6. local evt = require('event')
  7. local com = require('component')
  8. local cmp = require('computer')
  9. local ser = require('serialization')
  10.  
  11. package.loaded['th_interfaces'] = nil
  12. local interfaces = require('th_interfaces')
  13.  
  14. os.execute('th_inform')
  15. local uid = _G.digest
  16. local PORT = 777
  17. _G.digest = nil
  18.  
  19. local connections = {}
  20. local global = interfaces.combine(connections)
  21.  
  22. local NULLADDR = interfaces.null(nil).addr
  23. connections[NULLADDR] = interfaces.null(nil)
  24. for addr in com.list() do
  25.   local proxy = com.proxy(addr)
  26.   if interfaces[proxy.type] then
  27.     connections[addr] = interfaces[proxy.type](proxy, uid)
  28.   end
  29.   if proxy.type == 'modem' then proxy.open(PORT) end
  30. end
  31.  
  32. local function reverseTable(tableToReverse)
  33.   local reversedTable, i = {}, 0
  34.   for i = #tableToReverse, 1, -1 do
  35.     table.insert(reversedTable, tableToReverse[i])
  36.   end
  37.   return reversedTable
  38. end
  39.  
  40. --[[
  41. Network info: it's a tree of infinite depth with root server on ocelot.fomalhaut.me
  42.  
  43. Connecting:
  44.   WTC [uid]
  45.   CAC [uid] [lev]
  46.   AC  [uid]
  47.   UCA [uid] [connected uid]
  48.  
  49. Disconnecting:
  50.   DC  [uid]
  51.  
  52. Sending message:
  53.   RM [msg] [addr]
  54.   -- the node stores the first segment of route to underlying nodes
  55.   -- if the route is not stored, message is retranslated up
  56.   -- and so on, until the root of the tree is reached
  57. ]]
  58.  
  59. local server_runs = false
  60. local parent_node = nil
  61. local self_level = nil
  62. local connts = {[uid] = {connections[NULLADDR], NULLADDR}} -- groups {connection, real recv addr}
  63.  
  64. local thunderlib = {['IMPORTANT'] = 'All methods are synchronous!'}
  65. thunderlib.hook = function() end
  66.  
  67. function thunderlib.connect(is_timeout)
  68.   -- This method is SYNCHRONOUS == it won't return until the is_timeout function returns 1
  69.   if parent_node then return false, 'already connected' end
  70.   thunderlib.hook('Integrating into ThunderNet with connections', connections)
  71.  
  72.   is_timeout = is_timeout or function() os.sleep(0) end
  73.   global.broadcast(PORT, ser.serialize {'WTC', uid})
  74.   while not is_timeout() do
  75.     local connection, data = global.recv(is_timeout)
  76.     if connection then
  77.       local success, pack = pcall(ser.unserialize, data)
  78.       if success and pack[1] == 'CAC' and pack[2] and pack[3] then
  79.         connts[pack[2]] = connection
  80.         parent_node = pack[2]
  81.         self_level = (tonumber(pack[3]) or 0) + 1
  82.         return
  83.       end
  84.     end
  85.   end
  86. end
  87. function thunderlib.disconnect()
  88.   -- This method is SYNCHRONOUS
  89.   if not parent_node then return false, 'not connected' end
  90.   thunderlib.hook('Disconnecting from ThunderNet')
  91.  
  92.   global.broadcast(PORT, ser.serialize {'DC', uid})
  93.  
  94.   parent_node = nil
  95.   self_level = nil
  96.   for k in pairs(connts) do
  97.     if k ~= uid then connts[k] = nil end
  98.   end
  99. end
  100. function thunderlib.stop()
  101.   -- This method is SYNCHRONOUS
  102.   thunderlib.disconnect()
  103.  
  104.   for k, conn in next, connections do
  105.     if conn.stop then
  106.       thunderlib.hook('Stopping connection', conn)
  107.       conn.stop()
  108.     end
  109.   end
  110. end
  111. function thunderlib.send(addr, data)
  112.   -- This method is SYNCHRONOUS == it won't return until the data is sent
  113.   os.sleep(0)
  114.   thunderlib.hook('Sending message', addr, data)
  115.  
  116.   if connts[addr] then
  117.     thunderlib.hook('Route to target is known')
  118.     return global.send(connts, addr, PORT, ser.serialize {'RM', data, addr})
  119.   elseif parent_node then
  120.     thunderlib.hook('Target is unknown, sending message to parent', parent_node)
  121.     return global.send(connts, parent_node, PORT, ser.serialize {'RM', data, addr})
  122.   else
  123.     thunderlib.hook('Target is unknown, message lost')
  124.     return false
  125.   end
  126. end
  127. function thunderlib.run_server(is_timeout, allow_connections)
  128.   -- This method is SYNCHRONOUS == it blocks the executor thread forever
  129.   thunderlib.run_server = function() error('Server cannot be run twice') end
  130.   -- That's not enough secure, as runServer method can be saved and then run twice.
  131.   -- If that becomes a problem, pls add check on local variable server_runs (l33).
  132.  
  133.   is_timeout = is_timeout or function() os.sleep(0) end
  134.   local hook = thunderlib.hook
  135.   hook('Server started')
  136.   while not is_timeout() do
  137.     local connection, data = global.recv(is_timeout)
  138.     hook('Got incoming data:', data)
  139.    
  140.     if not connection then goto continue_rs end
  141.    
  142.     local success, pack = pcall(ser.unserialize, data)
  143.     if not success or not pack then goto continue_rs end
  144.    
  145.     if pack[1] == 'WTC' and pack[2] and allow_connections then
  146.       hook('Got a connection attempt', pack)
  147.       connts[pack[2]] = connection
  148.       global.send(connts, pack[2], PORT, ser.serialize {'CAC', uid, self_level or 0})
  149.     elseif pack[1] == 'AC' and pack[2] and allow_connections then
  150.       hook('Connection established', pack)
  151.       if parent_node then
  152.         global.send(connts, parent_node, PORT, ser.serialize {'UCA', uid, pack[2]})
  153.       end
  154.     elseif pack[1] == 'UCA' and pack[2] and pack[3] then
  155.       hook('Underlying connection accepted', pack)
  156.       connts[pack[3]] = connection
  157.       if parent_node then
  158.         global.send(connts, parent_node, PORT, ser.serialize {'UCA', uid, pack[2]})
  159.       end
  160.     elseif pack[1] == 'DC' and pack[2] then
  161.       hook('Node nearby is disconnected', pack)
  162.       local test_conn = connts[pack[2]]
  163.       if not test_conn then
  164.         hook('That node was unknown till now')
  165.       else
  166.         for k, cur_conn in pairs(connts) do
  167.           if cur_conn[1] == test_conn[1] and cur_conn[2] == test_conn[2] then
  168.             connts[k] = nil
  169.           end
  170.         end
  171.       end
  172.     elseif pack[1] == 'RM' and pack[2] and pack[3] then
  173.       if pack[3] == uid then
  174.         hook('Incoming message:', pack[2])
  175.         cmp.pushSignal('thunderlib_incoming', pack[2])
  176.       else
  177.         local d = connts[pack[3]] and pack[3] or parent_node
  178.         if d then
  179.           hook('Passing message:', pack[2])
  180.           global.send(connts, d, PORT, data)
  181.         else
  182.           hook('Message lost', pack)
  183.         end
  184.       end
  185.     end
  186.    
  187.     ::continue_rs::
  188.   end
  189.  
  190.   server_runs = false
  191. end
  192. function thunderlib.uid()
  193.   return uid
  194. end
  195. function thunderlib.parent()
  196.   return parent_node
  197. end
  198.  
  199. _G.t = thunderlib
  200. return thunderlib
Add Comment
Please, Sign In to add comment