SHARE
TWEET

Untitled

a guest Nov 16th, 2019 81 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. stem = {}
  2.  
  3. -- Constants
  4. --------------------------------------------------------------------
  5.  
  6. local PORT = 5733
  7. local Package = {
  8.     MESSAGE = 0,
  9.     SUBSCRIBE = 1,
  10.     UNSUBSCRIBE = 2,
  11.     PING = 3,
  12.     PONG = 4
  13. }
  14. local PING_TIMEOUT = 5
  15.  
  16. -- Server level API
  17. --------------------------------------------------------------------
  18.  
  19. local server_api = {
  20.     __pullSignal = computer.pullSignal,
  21.     __address = nil,
  22.     __port = nil,
  23.     __socket = nil,
  24.     __channels = {}, -- list of channels this server is subscribed to
  25.     __stream = "", -- the string which plays the role of bytearray for incoming data
  26.  
  27.     __build_package = function(type, id, message)
  28.         local package = string.char(type)
  29.         if type == Package.PING or type == Package.PONG then
  30.             -- ping/pong content takes place of the `id` argument here
  31.             package = package .. id
  32.         else
  33.             package = package .. string.char(#id) .. id
  34.             if message ~= nil then
  35.                 package = package .. message
  36.             end
  37.         end
  38.         local len = #package
  39.         package = string.char(math.floor(len / 256), len % 256) .. package
  40.         return package
  41.     end,
  42.  
  43.     isSubscribed = function(self, id)
  44.         return self.__channels[id]
  45.     end,
  46.  
  47.     send = function(self, id, message)
  48.         if self:isConnected() then
  49.             self.__socket.write(self.__build_package(Package.MESSAGE, id, message))
  50.             return true
  51.         else
  52.             return nil, "not connected"
  53.         end
  54.     end,
  55.  
  56.     subscribe = function(self, id)
  57.         if self:isConnected() then
  58.             self.__socket.write(self.__build_package(Package.SUBSCRIBE, id))
  59.             self.__channels[id] = true
  60.             return true
  61.         else
  62.             return nil, "not connected"
  63.         end
  64.     end,
  65.  
  66.     unsubscribe = function(self, id)
  67.         if self:isConnected() then
  68.             self.__socket.write(self.__build_package(Package.UNSUBSCRIBE, id))
  69.             self.__channels[id] = false
  70.             return true
  71.         else
  72.             return nil, "not connected"
  73.         end
  74.     end,
  75.  
  76.     ping = function(self, content, timeout)
  77.         -- send ping request
  78.         self.__socket.write(self.__build_package(Package.PING, content))
  79.         -- wait for response
  80.         local time = os.time()
  81.         local duration = timeout or PING_TIMEOUT
  82.         while true do
  83.             local name, data = computer.pullSignal(duration)
  84.             if name == "stem_pong" then
  85.                 return data == content
  86.             else
  87.                 local passed = os.time() - time
  88.                 if passed >= duration * 20 then
  89.                     return false
  90.                 else
  91.                     duration = (timeout or PING_TIMEOUT) - (passed / 20)
  92.                 end
  93.             end
  94.         end
  95.     end,
  96.  
  97.     isConnected = function(self)
  98.         if self.__socket == nil then
  99.             return nil, "there were no connection attempts"
  100.         else
  101.             return self.__socket.finishConnect()
  102.         end
  103.     end,
  104.  
  105.     reconnect = function(self)
  106.         if self:isConnected() then
  107.             self:disconnect()
  108.         end
  109.         self.__socket = internet.connect(self.__address, self.__port or PORT)
  110.         -- check connection until there will be some useful information
  111.         -- also this serves to kick off internet_ready events generation
  112.         while true do
  113.             local result, error = self.__socket.finishConnect()
  114.             if result then
  115.                 return self
  116.             elseif result == nil then
  117.                 self:disconnect()
  118.                 return nil, error
  119.             end
  120.         end
  121.     end,
  122.  
  123.     disconnect = function(self)
  124.         self.__socket.close()
  125.         self.__channels = {}
  126.         self.__stream = ""
  127.         computer.pullSignal = self.pullSignal
  128.     end,
  129.  
  130.     __incoming = function(self, socket_id)
  131.         -- check if the message belongs to the current connection
  132.         if self.__socket.id() == socket_id then
  133.             -- read all contents of the socket
  134.             while true do
  135.                 local chunk = self.__socket.read()
  136.                 if chunk ~= nil and #chunk > 0 then
  137.                     self.__stream = self.__stream .. chunk
  138.                 else
  139.                     break
  140.                 end
  141.             end
  142.             -- read all packages that may be already downloaded
  143.             while true do
  144.                 -- calculate the next package size, if necessary
  145.                 if self.len == nil and #self.__stream >= 2 then
  146.                     local a, b = self.__stream:byte(1, 2)
  147.                     self.len = a * 256 + b
  148.                 end
  149.                 -- check if the stream contains enough bytes for the package to be retrieved
  150.                 if self.len ~= nil and #self.__stream >= self.len + 2 then
  151.                     -- determine the package type
  152.                     local type = self.__stream:byte(3)
  153.                     local package = { type = type }
  154.                     if type == Package.PING or type == Package.PONG then
  155.                         -- read content
  156.                         package.content = self.__stream:sub(4, self.len + 2)
  157.                     else
  158.                         -- read channel ID
  159.                         local id_len = self.__stream:byte(4)
  160.                         local id = self.__stream:sub(5, id_len + 4)
  161.                         package.id = id
  162.                         -- read a message
  163.                         if type == Package.MESSAGE then
  164.                             package.message = self.__stream:sub(id_len + 5, self.len + 2)
  165.                         end
  166.                     end
  167.                     -- handle the package to processor
  168.                     self:__process(package)
  169.                     -- trim the stream
  170.                     self.__stream = self.__stream:sub(self.len + 3)
  171.                     self.len = nil
  172.                 else
  173.                     break
  174.                 end
  175.             end
  176.         end
  177.     end,
  178.  
  179.     __process = function(self, package)
  180.         if package.type == Package.MESSAGE then
  181.             computer.pushSignal("stem_message", package.id, package.message)
  182.         elseif package.type == Package.PING then
  183.             self.__socket.write(self.__build_package(Package.PONG, package.content))
  184.         elseif package.type == Package.PONG then
  185.             computer.pushSignal("stem_pong", package.content)
  186.         end
  187.     end
  188. }
  189. server_api.__index = server_api
  190.  
  191. -- Library level functions
  192. --------------------------------------------------------------------
  193.  
  194. stem.connect = function(address, port)
  195.     local server = {
  196.         __address = address,
  197.         __port = port,
  198.         __socket = socket
  199.     }
  200.     setmetatable(server, server_api)
  201.     computer.pullSignal = function(...)
  202.         local signal = {server.__pullSignal(...)}
  203.  
  204.         if signal[1] == "internet_ready" then
  205.             server:__incoming(signal[3])
  206.         end
  207.     end
  208.  
  209.     return server:reconnect()
  210. end
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top