old_sg_server.nim 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import
  2. sockets, times, streams, streams_enh, tables, json, os,
  3. sg_packets, sg_assets, md5, server_utils, client_helpers
  4. var
  5. dirServer: PServer
  6. thisZone = newScZoneRecord("local", "sup")
  7. thisZoneSettings: PZoneSettings
  8. dirServerConnected = false
  9. ## I was high.
  10. clients = initTable[TupAddress, PClient](16)
  11. alias2client = initTable[string, PClient](32)
  12. allClients: seq[PClient] = @[]
  13. zonePlayers: seq[PClient] = @[]
  14. const
  15. PubChatDelay = 100/1000 #100 ms
  16. import hashes
  17. proc hash*(x: uint16): THash {.inline.} =
  18. result = int32(x)
  19. proc findClient*(host: string; port: int16): PClient =
  20. let addy: TupAddress = (host, port)
  21. if clients.hasKey(addy):
  22. return clients[addy]
  23. result = newClient(addy)
  24. clients[addy] = result
  25. allClients.add(result)
  26. proc sendZoneList(client: PClient) =
  27. echo(">> zonelist ", client)
  28. #client.send(HZonelist, zonelist)
  29. proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) =
  30. var m = newScChat(CPriv, sender.alias, txt)
  31. rcv.send(HChat, m)
  32. proc sendChat(client: PClient; kind: ChatType; txt: string) =
  33. echo(">> chat ", client)
  34. var m = newScChat(kind, "", txt)
  35. client.send(HChat, m)
  36. var pubChatQueue = newStringStream("")
  37. pubChatQueue.flushImpl = proc(stream: PStream) =
  38. stream.setPosition(0)
  39. PStringStream(stream).data.setLen(0)
  40. proc queuePub(sender: string, msg: CsChat) =
  41. var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text)
  42. pubChatQueue.write(HChat)
  43. chat.pack(pubChatQueue)
  44. handlers[HHello] = (proc(client: PClient; stream: PStream) =
  45. var h = readCsHello(stream)
  46. if h.i == 14:
  47. var greet = newScHello("Well hello there")
  48. client.send(HHello, greet))
  49. handlers[HLogin] = proc(client: PClient; stream: PStream) =
  50. var loginInfo = readCsLogin(stream)
  51. echo("** login: alias = ", loginInfo.alias)
  52. if not dirServerConnected and client.loginPlayer(loginInfo):
  53. client.sendMessage("Welcome "& client.alias)
  54. alias2client[client.alias] = client
  55. client.sendZonelist()
  56. handlers[HZoneList] = proc(client: PClient; stream: PStream) =
  57. var pinfo = readCsZoneList(stream)
  58. echo("** zonelist req")
  59. handlers[HChat] = proc(client: PClient; stream: PStream) =
  60. var chat = readCsChat(stream)
  61. if not client.auth:
  62. client.sendError("You are not logged in.")
  63. return
  64. if chat.target != "": ##private
  65. if alias2client.hasKey(chat.target):
  66. alias2client[chat.target].forwardPrivate(client, chat.text)
  67. else:
  68. queuePub(client.alias, chat)
  69. handlers[HZoneQuery] = proc(client: PClient; stream: PStream) =
  70. echo("Got zone query")
  71. var q = readCsZoneQuery(stream)
  72. var resp = newScZoneQuery(zonePlayers.len.uint16)
  73. client.send(HZoneQuery, resp)
  74. handlers[HZoneJoinReq] = proc(client: PClient; stream: PStream) =
  75. var req = readCsZoneJoinReq(stream)
  76. echo "Join zone request from (",req.session.id,") ", req.session.alias
  77. if client.auth and client.kind == CPlayer:
  78. echo "Client is authenticated, verifying filez"
  79. client.startVerifyingFiles()
  80. elif dirServerConnected:
  81. echo "Dirserver is connected, verifying client"
  82. dirServer.send HVerifyClient, req.session
  83. else:
  84. echo "Dirserver is disconnected =("
  85. client.startVerifyingFiles()
  86. proc handlePkt(s: PClient; stream: PStream) =
  87. while not stream.atEnd:
  88. var typ = readChar(stream)
  89. if not handlers.hasKey(typ):
  90. break
  91. else:
  92. handlers[typ](s, stream)
  93. proc createServer(port: TPort) =
  94. if not server.isNil:
  95. server.close()
  96. server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false)
  97. server.bindAddr(port)
  98. var clientIndex = 0
  99. var incoming = newIncomingBuffer()
  100. proc poll*(timeout: int = 250) =
  101. if server.isNil: return
  102. var
  103. reads = @[server]
  104. writes = @[server]
  105. if select(reads, timeout) > 0:
  106. var
  107. addy = ""
  108. port: TPort
  109. let res = server.recvFromAsync(incoming.data, 512, addy, port, 0)
  110. if not res:
  111. echo("No recv")
  112. return
  113. else:
  114. var client = findClient(addy, port.int16)
  115. #echo("<< ", res, " ", client.alias, ": ", len(line.data), " ", repr(line.data))
  116. handlePkt(client, incoming)
  117. incoming.flush()
  118. if selectWrite(writes, timeout) > 0:
  119. let nclients = allClients.len
  120. if nclients == 0:
  121. return
  122. clientIndex = (clientIndex + 1) mod nclients
  123. var c = allClients[clientIndex]
  124. if c.outputBuf.getPosition > 0:
  125. let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data)
  126. echo("Write ", c, " result: ", res, " data: ", c.outputBuf.data)
  127. c.outputBuf.flush()
  128. when true:
  129. import parseopt, strutils
  130. var zoneCfgFile = "./server_settings.json"
  131. for kind, key, val in getOpt():
  132. case kind
  133. of cmdShortOption, cmdLongOption:
  134. case key
  135. of "f", "file":
  136. if fileExists(val):
  137. zoneCfgFile = val
  138. else:
  139. echo("File does not exist: ", val)
  140. else:
  141. echo("Unknown option: ", key," ", val)
  142. else:
  143. echo("Unknown option: ", key, " ", val)
  144. var jsonSettings = parseFile(zoneCfgFile)
  145. let
  146. host = jsonSettings["host"].str
  147. port = TPort(jsonSettings["port"].num)
  148. zoneFile = jsonSettings["settings"].str
  149. dirServerInfo = jsonSettings["dirserver"]
  150. var path = getAppDir()/../"data"/zoneFile
  151. if not fileExists(path):
  152. echo("Zone settings file does not exist: ../data/", zoneFile)
  153. echo(path)
  154. quit(1)
  155. ## Test file
  156. block:
  157. var
  158. TestFile: FileChallengePair
  159. contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
  160. testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
  161. testFile.file = checksumStr(contents)
  162. myAssets.add testFile
  163. setCurrentDir getAppDir().parentDir()
  164. block:
  165. let zonesettings = readFile(path)
  166. var
  167. errors: seq[string] = @[]
  168. if not loadSettings(zoneSettings, errors):
  169. echo("You have errors in your zone settings:")
  170. for e in errors: echo("**", e)
  171. quit(1)
  172. errors.setLen 0
  173. var pair: FileChallengePair
  174. pair.challenge.file = zoneFile
  175. pair.challenge.assetType = FZoneCfg
  176. pair.challenge.fullLen = zoneSettings.len.int32
  177. pair.file = checksumStr(zoneSettings)
  178. myAssets.add pair
  179. allAssets:
  180. if not load(asset):
  181. echo "Invalid or missing file ", file
  182. else:
  183. var pair: FileChallengePair
  184. pair.challenge.file = file
  185. pair.challenge.assetType = assetType
  186. pair.challenge.fullLen = getFileSize(
  187. expandPath(assetType, file)).int32
  188. pair.file = asset.contents
  189. myAssets.add pair
  190. echo "Zone has ", myAssets.len, " associated assets"
  191. dirServer = newServerConnection(dirServerInfo[0].str, dirServerInfo[1].num.TPort)
  192. dirServer.handlers[HDsMsg] = proc(serv: PServer; stream: PStream) =
  193. var m = readDsMsg(stream)
  194. echo("DirServer> ", m.msg)
  195. dirServer.handlers[HZoneLogin] = proc(serv: PServer; stream: PStream) =
  196. let loggedIn = readDsZoneLogin(stream).status
  197. if loggedIn:
  198. dirServerConnected = true
  199. dirServer.writePkt HZoneLogin, login
  200. thisZone.name = jsonSettings["name"].str
  201. thisZone.desc = jsonSettings["desc"].str
  202. thisZone.ip = "localhost"
  203. thisZone.port = port
  204. var login = newSdZoneLogin(
  205. dirServerInfo[2].str, dirServerInfo[3].str,
  206. thisZone)
  207. #echo "MY LOGIN: ", $login
  208. createServer(port)
  209. echo("Listening on port ", port, "...")
  210. var pubChatTimer = cpuTime()#newClock()
  211. while true:
  212. discard dirServer.pollServer(15)
  213. poll(15)
  214. ## TODO sort this type of thing VV into a queue api
  215. #let now = cpuTime()
  216. if cpuTime() - pubChatTimer > PubChatDelay: #.getElapsedTime.asMilliseconds > 100:
  217. pubChatTimer -= pubChatDelay #.restart()
  218. if pubChatQueue.getPosition > 0:
  219. var cn = 0
  220. let sizePubChat = pubChatQueue.data.len
  221. for c in allClients:
  222. c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
  223. pubChatQueue.flush()