DorianH935

Untitled

Nov 15th, 2025
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.25 KB | None | 0 0
  1. -- lemonphone_server.lua
  2. -- Dedicated Lemonphone server node
  3. -- Run on any CC computer with a modem. Saves registry to /lemonphone_registry
  4.  
  5. local REG_PATH = "/lemonphone_registry"
  6. local GOSSIP_INTERVAL = 10
  7. local FORWARD_TIMEOUT = 2
  8.  
  9. -- load/save registry
  10. local registry = {} -- phone -> {server = id, lastSeen = os.time()}
  11. local function saveRegistry()
  12. local f = fs.open(REG_PATH, "w")
  13. if f then
  14. f.write(textutils.serialize(registry))
  15. f.close()
  16. end
  17. end
  18. local function loadRegistry()
  19. if fs.exists(REG_PATH) then
  20. local f = fs.open(REG_PATH, "r")
  21. local data = f.readAll()
  22. f.close()
  23. local ok, t = pcall(textutils.unserialize, data)
  24. if ok and type(t) == "table" then registry = t end
  25. end
  26. end
  27. loadRegistry()
  28.  
  29. -- host service so clients can discover this server
  30. rednet.host("lemonphone_server", tostring(os.getComputerID()))
  31.  
  32. -- knownServers: map serverID -> lastHeard
  33. local knownServers = {}
  34. local function markServer(id)
  35. knownServers[id] = os.time()
  36. end
  37.  
  38. -- gossip: periodically broadcast our registry summary (not full content, just phone -> owner mappings)
  39. local function gossipLoop()
  40. while true do
  41. local summary = {}
  42. for phone, info in pairs(registry) do
  43. summary[phone] = info.server
  44. end
  45. rednet.broadcast(summary, "lemonphone_gossip")
  46. sleep(GOSSIP_INTERVAL)
  47. end
  48. end
  49.  
  50. -- helper: assign phone number
  51. local function genHex4()
  52. local n = math.random(0, 0xFFFF)
  53. return string.format("%04X", n)
  54. end
  55. local function makePhone(serviceType)
  56. serviceType = tostring(serviceType or 2)
  57. return serviceType .. "-" .. genHex4() .. "-" .. genHex4()
  58. end
  59.  
  60. -- handle registration requests
  61. local function handleRegister(id, msg)
  62. -- msg = { type="register_request", desired_service = 2, requested_id = maybe }
  63. local serviceType = tonumber(msg.desired_service) or 2
  64. local phone
  65. repeat
  66. phone = makePhone(serviceType)
  67. until not registry[phone]
  68. registry[phone] = { server = os.getComputerID(), lastSeen = os.time(), ownerClient = msg.clientID } -- ownerClient optional
  69. saveRegistry()
  70. markServer(os.getComputerID())
  71. rednet.send(id, { type = "register_response", ok = true, phone = phone }, "lemonphone_control")
  72. end
  73.  
  74. -- forward message to owner server
  75. local function forwardToOwner(ownerServer, payload)
  76. if ownerServer == os.getComputerID() then
  77. return false, "owner local" -- shouldn't call this; use local delivery
  78. end
  79. local ok, err = pcall(function()
  80. rednet.send(ownerServer, payload, "lemonphone_control")
  81. end)
  82. if not ok then
  83. return false, err
  84. end
  85. return true
  86. end
  87.  
  88. -- local delivery attempt (store messages for later if not reachable)
  89. local stored = {} -- phone -> list of payloads
  90. local function deliverLocally(phone, payload)
  91. -- if ownerClient present and online, rednet.send directly to that client
  92. local info = registry[phone]
  93. if not info then
  94. table.insert(stored, payload)
  95. return false
  96. end
  97. local ownerClient = info.ownerClient
  98. if ownerClient and type(ownerClient) == "number" then
  99. local ok = pcall(function() rednet.send(ownerClient, payload, "lemonphone_dm") end)
  100. if ok then return true end
  101. end
  102. -- else store
  103. stored[phone] = stored[phone] or {}
  104. table.insert(stored[phone], payload)
  105. return false
  106. end
  107.  
  108. -- server main listener: handles control messages, forwards, and gossip
  109. local function serverListener()
  110. while true do
  111. local sender, msg, proto = rednet.receive()
  112. if proto == "lemonphone_control" then
  113. if msg and msg.type == "register_request" then
  114. handleRegister(sender, msg)
  115. elseif msg and msg.type == "server_forward" then
  116. -- forward to local owner
  117. local target = msg.dest_phone
  118. if registry[target] and registry[target].server == os.getComputerID() then
  119. -- deliver to local ownerClient if known
  120. deliverLocally(target, { type="dm_incoming", from_phone = msg.from_phone, text = msg.text })
  121. else
  122. -- target unknown locally: maybe store
  123. stored[target] = stored[target] or {}
  124. table.insert(stored[target], msg)
  125. end
  126. end
  127. elseif proto == "lemonphone_gossip" then
  128. -- msg is summary table phone -> ownerServer
  129. for phone, owner in pairs(msg or {}) do
  130. if not registry[phone] then
  131. registry[phone] = { server = owner, lastSeen = os.time() }
  132. else
  133. -- keep more recent mapping
  134. registry[phone].server = owner
  135. registry[phone].lastSeen = os.time()
  136. end
  137. end
  138. saveRegistry()
  139. elseif proto == "lemonphone_global" then
  140. -- global chat relay: rebroadcast to local clients and other servers
  141. -- we simply rebroadcast the message locally (other servers will pick up via rednet.broadcast too)
  142. rednet.broadcast(msg, "lemonphone_global")
  143. end
  144. -- mark server if sender is server
  145. if sender and tostring(sender) and tostring(sender) ~= tostring(os.getComputerID()) then
  146. -- heuristics: if the sender hosts lemonphone_server, mark it
  147. local ok, serv = pcall(rednet.lookup, "lemonphone_server")
  148. if ok and type(serv) == "table" then
  149. markServer(sender)
  150. end
  151. end
  152. end
  153. end
  154.  
  155. -- periodic cleanup of stale registry entries
  156. local function cleanupLoop()
  157. while true do
  158. local now = os.time()
  159. for phone, info in pairs(registry) do
  160. if now - (info.lastSeen or now) > 60 * 60 * 24 then
  161. registry[phone] = nil
  162. end
  163. end
  164. saveRegistry()
  165. sleep(600)
  166. end
  167. end
  168.  
  169. -- small HTTP-ish GET endpoint simulation is omitted for simplicity; communication is via rednet only
  170.  
  171. -- run main loops in parallel
  172. print("Lemonphone server starting, id = " .. os.getComputerID())
  173. parallel.waitForAny(gossipLoop, serverListener, cleanupLoop)
  174.  
Advertisement
Add Comment
Please, Sign In to add comment