Advertisement
melzneni

Turti Lib Network

Sep 17th, 2023 (edited)
365
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.38 KB | None | 0 0
  1. local storage
  2. local save
  3. local PROTOCOL = "TURTI_NETWORK_PROTOCOL"
  4. local CONFIRMATION_TIMEOUT = 5
  5. local EVENT_START_SERVER = PROTOCOL .. "_EVENT_START_SERVER"
  6. local EVENT_DISCOVERY_CONFIRMATION = PROTOCOL .. "_EVENT_DISCOVERY_CONFIRMATION"
  7. local EVENT_CONFIRMATION = PROTOCOL .. "_EVENT_CONFIRMATION"
  8. local EVENT_RESPONSE = PROTOCOL .. "_EVENT_RESPONSE"
  9.  
  10. local function openRednet()
  11. ThreadManager.blockExecutionOfOtherThreads()
  12. local side
  13. peripheral.find("modem", function(name, modem)
  14. if modem.isWireless() then
  15. side = name
  16. end
  17. end)
  18. if not rednet.isOpen(side) then
  19. rednet.open(side)
  20. end
  21. ThreadManager.unblockExecutionOfOtherThreads()
  22. end
  23.  
  24. local function listenerThread()
  25. while true do
  26. openRednet()
  27. local id, rawMessage = rednet.receive(PROTOCOL, 1)
  28. if id then
  29. local message = getTableFromSaveText(rawMessage)
  30. local protocol = message.protocol
  31. local handle = message.handle
  32. local arguments = message.arguments
  33.  
  34. if handle and protocol and message.type and message.type == "call" and message.callId then
  35. yield()
  36. if not arguments then
  37. arguments = {}
  38. end
  39. local networkAPI = storage.activeNetworks[protocol]
  40. if networkAPI then
  41. ThreadManager.startThread(function()
  42. local result = networkAPI._class_prototype.privateMethods.executeHandle(networkAPI, networkAPI._class_context, id, message.callId, handle, arguments)
  43. local throw = result.throw
  44. result.throw = nil
  45. result.protocol = protocol
  46. openRednet()
  47. rednet.send(id, getTableSaveText(result), PROTOCOL)
  48.  
  49. if result.responseType == "error" and throw then
  50. error(tostring(throw))
  51. end
  52. end, true, "execute handle for network api " .. getClassContext(networkAPI).protocol)
  53. end
  54. elseif protocol and message.type and message.type == "discover" and message.callId then
  55. local networkAPI = storage.activeNetworks[protocol]
  56. if networkAPI then
  57. rednet.send(id, getTableSaveText({
  58. protocol = protocol,
  59. type = "discoveryConfirmation",
  60. callId = message.callId,
  61. serverId = os.getComputerID()
  62. }), PROTOCOL)
  63. end
  64. elseif protocol and message.type and message.type == "discoveryConfirmation" and message.callId and message.serverId then
  65. print(message.callId, message.serverId)
  66. os.queueEvent(EVENT_DISCOVERY_CONFIRMATION, message.callId, message.serverId)
  67. elseif protocol and message.type and message.type == "response" and message.callId then
  68. os.queueEvent(EVENT_RESPONSE, message.callId, rawMessage)
  69. elseif protocol and message.type == "startServer" then
  70. os.queueEvent(EVENT_START_SERVER, protocol)
  71. end
  72. end
  73. end
  74. end
  75.  
  76. local TYPE_HANDLE_SLIM = "TYPE_HANDLE_SLIM"
  77. local TYPE_HANDLE_WITH_CONTEXT = "TYPE_HANDLE_WITH_CONTEXT"
  78.  
  79. local NETWORK_SERVER_CLASS_PROTOTYPE = {
  80. key = "NetworkServer",
  81. methods = {
  82. register = function(api, context, method, hookName)
  83. if not hookName then
  84. hookName = method.name
  85. end
  86. context.handles[hookName] = {
  87. method = method,
  88. type = TYPE_HANDLE_SLIM
  89. }
  90. end,
  91. registerWithContext = function(api, context, method, hookName)
  92. if not hookName then
  93. hookName = method.name
  94. end
  95. context.handles[hookName] = {
  96. method = method,
  97. type = TYPE_HANDLE_WITH_CONTEXT
  98. }
  99. end
  100. },
  101. privateMethods = {
  102. executeHandle = function(api, context, callerId, callId, handleName, arguments)
  103. local handle = context.handles[handleName]
  104. if not handle then
  105. return {
  106. type = "response",
  107. callId = callId,
  108. responseType = "error",
  109. msg = "unknown handle: " .. handleName
  110. }
  111. end
  112. local status, ret
  113. if handle.type == TYPE_HANDLE_SLIM then
  114. status, ret = xpcall(api._class_prototype.privateMethods.executeHandleInternal, debug.traceback, handle.method, arguments)
  115. else
  116. table.insert(arguments, 1, toTurtiTable({
  117. id = callerId
  118. }))
  119. status, ret = xpcall(api._class_prototype.privateMethods.executeHandleInternal, debug.traceback, handle.method, arguments)
  120. end
  121.  
  122. if status then
  123. return {
  124. type = "response",
  125. callId = callId,
  126. responseType = "return",
  127. returnValue = ret,
  128. callerId = callerId
  129. }
  130. else
  131. return {
  132. type = "response",
  133. callId = callId,
  134. responseType = "error",
  135. msg = "handle error (" .. handleName .. ")",
  136. throw = ret,
  137. callerId = callerId
  138. }
  139. end
  140. end,
  141. executeHandleInternal = function(handle, arguments)
  142. return invokeHook(handle.name, table.unpack(arguments))
  143. end
  144. },
  145. init = function()
  146. return { handles = {} }
  147. end,
  148. construct = function(api, context, protocol)
  149. context.protocol = protocol
  150. if storage.activeNetworks[protocol] then
  151. error("api with protocol " .. protocol .. " is already active")
  152. end
  153. storage.activeNetworks[protocol] = api
  154. save()
  155. openRednet()
  156. rednet.broadcast(getTableSaveText({
  157. protocol = context.protocol,
  158. type = "startServer"
  159. }), PROTOCOL)
  160. end
  161. }
  162.  
  163. local NETWORK_CLIENT_CLASS_PROTOTYPE = {
  164. key = "NetworkClient",
  165. methods = {
  166. call = function(api, context, handleName, ...)
  167. local _, ret = api._class_prototype.privateMethods.call(api, context, nil, handleName, ...)
  168. return ret
  169. end,
  170. callWithId = function(api, context, id, handleName, ...)
  171. local _, ret = api._class_prototype.privateMethods.call(api, context, id, handleName, ...)
  172. return ret
  173. end,
  174. callGetId = function(api, context, handleName, ...)
  175. local responseId, ret = api._class_prototype.privateMethods.call(api, context, nil, handleName, ...)
  176. return responseId, ret
  177. end,
  178. callNoWait = function(api, context, handleName, ...)
  179. local callId = api._class_prototype.privateMethods.newCallId()
  180. local serverId = api._class_prototype.privateMethods.discoverServer(api, context, nil, callId)
  181. api._class_prototype.privateMethods.placeCall(api, context, serverId, callId, handleName, ...)
  182. end,
  183. broadcast = function(api, context, handleName, ...)
  184. local callId = api._class_prototype.privateMethods.newCallId()
  185. api._class_prototype.privateMethods.placeCall(api, context, nil, callId, handleName, ...)
  186. end,
  187. callWithIdNoWait = function(api, context, id, handleName, ...)
  188. local callId = api._class_prototype.privateMethods.newCallId()
  189. local serverId = api._class_prototype.privateMethods.discoverServer(api, context, id, callId)
  190. api._class_prototype.privateMethods.placeCall(api, context, serverId, callId, handleName, ...)
  191. end
  192. },
  193. privateMethods = {
  194. call = function(api, context, id, handleName, ...)
  195. local callId = api._class_prototype.privateMethods.newCallId()
  196. local serverId = api._class_prototype.privateMethods.discoverServer(api, context, id, callId)
  197.  
  198. while true do
  199. api._class_prototype.privateMethods.placeCall(api, context, serverId, callId, handleName, ...)
  200. local success, idRet, ret = api._class_prototype.privateMethods.waitForResponse(context, callId)
  201. if success then
  202. return idRet, ret
  203. end
  204. print("recalling")
  205. end
  206. end,
  207. discoverServer = function(api, context, id, callId)
  208. local message = getTableSaveText({
  209. protocol = context.protocol,
  210. type = "discover",
  211. callId = callId
  212. })
  213. while true do
  214. if id == nil then
  215. rednet.broadcast(message, PROTOCOL)
  216. else
  217. rednet.send(id, message, PROTOCOL)
  218. end
  219. local serverId = api._class_prototype.privateMethods.waitForDiscoveryConfirmation(api, context, callId)
  220. if serverId then
  221. return serverId
  222. end
  223. end
  224. end,
  225. waitForDiscoveryConfirmation = function(api, context, callId)
  226. local timer = os.startTimer(storage.confirmationTimeout)
  227. while true do
  228. local event, arg1, arg2 = os.pullEvent()
  229. if event == "timer" then
  230. if arg1 == timer then
  231. return nil
  232. end
  233. elseif event == EVENT_DISCOVERY_CONFIRMATION then
  234. if arg1 == callId then
  235. return arg2
  236. end
  237. elseif event == EVENT_START_SERVER then
  238. if arg1 == context.protocol then
  239. return nil
  240. end
  241. end
  242. end
  243. end,
  244. newCallId = function()
  245. local callId = storage.currentCallId
  246. storage.currentCallId = storage.currentCallId + 1
  247. save()
  248. return callId
  249. end,
  250. placeCall = function(api, context, serverId, callId, handleName, ...)
  251. local message = getTableSaveText({
  252. protocol = context.protocol,
  253. handle = handleName,
  254. type = "call",
  255. callId = callId,
  256. arguments = { ... }
  257. })
  258.  
  259. openRednet()
  260. if serverId==nil then
  261. rednet.broadcast(message, PROTOCOL)
  262. else
  263. rednet.send(serverId, message, PROTOCOL)
  264. end
  265.  
  266. return callId
  267. end,
  268. waitForConfirmation = function(context, callId)
  269. local timer = os.startTimer(storage.confirmationTimeout)
  270. while true do
  271. local event, arg1 = os.pullEvent()
  272. if event == "timer" then
  273. if arg1 == timer then
  274. return false
  275. end
  276. elseif event == EVENT_CONFIRMATION then
  277. if arg1 == callId then
  278. return true
  279. end
  280. elseif event == EVENT_START_SERVER then
  281. if arg1 == context.protocol then
  282. return false
  283. end
  284. end
  285. end
  286. end,
  287. waitForResponse = function(context, callId)
  288. while true do
  289. local event, arg1, arg2 = os.pullEvent()
  290. if event == EVENT_START_SERVER then
  291. if arg1 == context.protocol then
  292. return false, nil
  293. end
  294. elseif event == EVENT_RESPONSE then
  295. if arg1 == callId then
  296. local response = getTableFromSaveText(arg2)
  297. if response.responseType == "error" then
  298. error(response.msg)
  299. elseif response.responseType == "return" then
  300. return true, response.callerId, response.returnValue
  301. else
  302. error("unknown response type: " .. response.responseType)
  303. end
  304. end
  305. end
  306. end
  307. end
  308. },
  309. construct = function(api, context, protocol)
  310. context.protocol = protocol
  311. end
  312. }
  313.  
  314. local api = {
  315. setConfirmationTimeout = function(timeout)
  316. storage.confirmationTimeout = timeout
  317. save()
  318. end
  319. }
  320.  
  321. return {
  322. name = "network",
  323. onSetup = function()
  324. ThreadManager.startThread(
  325. listenerThread, true, "network listener"
  326. )
  327. end,
  328. onInitStorage = function(_storage, _save)
  329. storage = _storage
  330. save = _save
  331. if not storage.activeNetworks then
  332. storage.activeNetworks = {}
  333. storage.currentCallId = 0
  334. storage.confirmationTimeout = CONFIRMATION_TIMEOUT
  335. end
  336. end,
  337. api = api,
  338. classes = {
  339. NETWORK_SERVER_CLASS_PROTOTYPE,
  340. NETWORK_CLIENT_CLASS_PROTOTYPE
  341. }
  342. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement