Advertisement
Guest User

Untitled

a guest
Nov 16th, 2019
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.97 KB | None | 0 0
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement