enet_server.nim 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import enet, strutils, idgen, tables, math_helpers,
  2. estreams, sg_packets, server_utils, sg_assets, client_helpers
  3. when appType == "gui":
  4. import sfml, sfml_colors, sg_gui,
  5. input_helpers, sfml_stuff
  6. else:
  7. import times
  8. type
  9. TCallback = proc(client: PClient; buffer: PBuffer)
  10. var
  11. server: PHost
  12. dirServer: PServer
  13. standAloneMode = true
  14. event: enet.TEvent
  15. clientID = newIDGen[int32]()
  16. clients = initTable[int32, PClient](64)
  17. handlers = initTable[char, TCallback](32)
  18. when appType == "gui":
  19. var
  20. gui = newGuiContainer()
  21. chatBox = gui.newMessageArea(vec2f(15, 550))
  22. window = newRenderWindow(videoMode(800, 600, 32), "Sup yo", sfDefaultSTyle)
  23. mousepos = newText("", guiFont, 16)
  24. fpsText = mousePos.copy()
  25. inputClient = newKeyClient(setActive = true)
  26. chatBox.sizeVisible = 30
  27. mousePos.setColor(Green)
  28. fpsText.setposition(vec2f(0, 20))
  29. inputClient.registerHandler MouseLeft, down, proc() =
  30. gui.click(input_helpers.getMousePos())
  31. inputClient.registerHandler MouseMiddle, down, proc() =
  32. let pos = input_helpers.getMousePos()
  33. mousePos.setString("($1,$2)".format(ff(pos.x), ff(pos.y)))
  34. mousePos.setPosition(pos)
  35. proc dispMessage(args: varargs[string, `$`]) =
  36. var s = ""
  37. for it in items(args):
  38. s.add it
  39. chatbox.add(s)
  40. proc dispError(args: varargs[string, `$`]) =
  41. var s = ""
  42. for it in items(args): s.add(it)
  43. chatBox.add(newScChat(kind = CError, text = s))
  44. else:
  45. proc dispMessage(args: varargs[string, `$`]) =
  46. var m = ""
  47. for it in items(args): m.add(it)
  48. echo "<msg> ", m
  49. proc dispError(args: varargs[string, `$`]) =
  50. var m = ""
  51. for it in items(args): m.add(it)
  52. echo "**", m
  53. var pubChatQueue = newBuffer(1024)
  54. proc queuePub(sender: PClient, msg: CsChat) =
  55. var chat = newScChat(kind = CPub, fromPlayer = sender.alias, text = msg.text)
  56. pubChatQueue.write(HChat)
  57. pubChatQueue.pack(chat)
  58. proc flushPubChat() =
  59. if pubChatQueue.isDirty:
  60. let packet = pubChatQueue.toPacket(FlagReliable)
  61. for id, client in pairs(clients):
  62. discard client.peer.send(0.cuchar, packet)
  63. pubChatQueue.flush()
  64. handlers[HChat] = proc(client: PClient; buffer: PBuffer) =
  65. var chat = readCsChat(buffer)
  66. if not client.auth:
  67. client.sendError("You are not logged in.")
  68. return
  69. #if chat.target != "": ##private
  70. # if alias2client.hasKey(chat.target):
  71. # alias2client[chat.target].forwardPrivate(client, chat.text)
  72. #else:
  73. dispmessage("<", client.alias, "> ", chat.text)
  74. queuePub(client, chat)
  75. handlers[HLogin] = proc(client: PClient; buffer: PBuffer) =
  76. var info = readCsLogin(buffer)
  77. if client.auth:
  78. client.sendError "You are already logged in."
  79. return
  80. client.alias = info.alias
  81. client.auth = true
  82. var resp = newScLogin(client.id, client.alias, "sessionkeylulz")
  83. client.send HLogin, resp
  84. client.sendMessage "welcome"
  85. dispMessage("Client logged in: ", client)
  86. handlers[HFileTransfer] = server_utils.handleFilePartAck
  87. handlers[HFileChallenge] = server_utils.handleFileChallengeResp
  88. handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) =
  89. var info = readCsZoneJoinReq(buffer)
  90. dispmessage "Got zone join request"
  91. client.startVerifyingFiles()
  92. when true:
  93. import parseopt, os, json
  94. if enetInit() != 0:
  95. quit "Could not initialize ENet"
  96. var address: enet.TAddress
  97. block:
  98. var zoneCfgFile = "./server_settings.json"
  99. for kind, key, val in getOpt():
  100. case kind
  101. of cmdShortOption, cmdLongOption:
  102. case key
  103. of "f", "file":
  104. if fileExists(val):
  105. zoneCfgFile = val
  106. else:
  107. echo("File does not exist: ", val)
  108. else:
  109. echo("Unknown option: ", key," ", val)
  110. else:
  111. echo("Unknown option: ", key, " ", val)
  112. var jsonSettings = parseFile(zoneCfgFile)
  113. let
  114. port = uint16(jsonSettings["port"].num)
  115. zoneFile = jsonSettings["settings"].str
  116. dirServerInfo = jsonSettings["dirserver"]
  117. address.host = EnetHostAny
  118. address.port = port
  119. var path = getAppDir()/../"data"/zoneFile
  120. if not fileExists(path):
  121. echo("Zone settings file does not exist: ../data/", zoneFile)
  122. echo(path)
  123. quit(1)
  124. discard """block:
  125. var
  126. TestFile: FileChallengePair
  127. contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
  128. testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
  129. testFile.file = checksumStr(contents)
  130. myAssets.add testFile"""
  131. setCurrentDir getAppDir().parentDir()
  132. let zonesettings = readFile(path)
  133. var
  134. errors: seq[string] = @[]
  135. if not loadSettings(zoneSettings, errors):
  136. echo("You have errors in your zone settings:")
  137. for e in errors: echo("**", e)
  138. quit(1)
  139. errors.setLen 0
  140. var pair: FileChallengePair
  141. pair.challenge.file = zoneFile
  142. pair.challenge.assetType = FZoneCfg
  143. pair.challenge.fullLen = zoneSettings.len.int32
  144. pair.file = checksumStr(zoneSettings)
  145. myAssets.add pair
  146. allAssets:
  147. if not load(asset):
  148. echo "Invalid or missing file ", file
  149. else:
  150. var pair: FileChallengePair
  151. pair.challenge.file = file
  152. pair.challenge.assetType = assetType
  153. pair.challenge.fullLen = getFileSize(
  154. expandPath(assetType, file)).int32
  155. pair.file = asset.contents
  156. myAssets.add pair
  157. echo "Zone has ", myAssets.len, " associated assets"
  158. dirServer = newServer()
  159. dirServer.addHandler HDsMsg, proc(serv: PServer; buffer: PBuffer) =
  160. var m = readDsMsg(buffer)
  161. dispMessage("<DirServer> ", m.msg)
  162. dirServer.addHandler HZoneLogin, proc(serv: PServer; buffer: PBuffer) =
  163. let loggedIn = readDsZoneLogin(buffer).status
  164. if loggedIn:
  165. #dirServerConnected = true
  166. if dirServerInfo.kind == JArray:
  167. var error: string
  168. if not dirServer.connect(dirServerInfo[0].str, dirServerInfo[1].num.int16, error):
  169. dispError("<DirServer> "&error)
  170. server = enet.createHost(address, 32, 2, 0, 0)
  171. if server == nil:
  172. quit "Could not create the server!"
  173. dispMessage("Listening on port ", address.port)
  174. var
  175. serverRunning = true
  176. when appType == "gui":
  177. var frameRate = newClock()
  178. var pubChatDelay = newClock()
  179. else:
  180. var frameRate = epochTime()
  181. var pubChatDelay = frameRate
  182. while serverRunning:
  183. when appType == "gui":
  184. let dt = frameRate.restart.asMilliseconds().float / 1000.0
  185. for event in window.filterEvents():
  186. case event.kind
  187. of sfml.EvtClosed:
  188. window.close()
  189. serverRunning = false
  190. else:
  191. discard
  192. else:
  193. let dt = epochTime() - frameRate ##is this right? probably not
  194. frameRate = epochTime()
  195. while server.hostService(event, 10) > 0:
  196. case event.kind
  197. of EvtConnect:
  198. var client = newClient()
  199. clients[client.id] = client
  200. event.peer.data = addr client.id
  201. client.peer = event.peer
  202. dispMessage("New client connected ", client)
  203. var
  204. msg = "hello"
  205. resp = createPacket(cstring(msg), msg.len + 1, FlagReliable)
  206. if event.peer.send(0.cuchar, resp) < 0:
  207. echo "FAILED"
  208. else:
  209. echo "Replied"
  210. of EvtReceive:
  211. let client = clients[cast[ptr int32](event.peer.data)[]]
  212. var buf = newBuffer(event.packet)
  213. let k = buf.readChar()
  214. if handlers.hasKey(k):
  215. handlers[k](client, buf)
  216. else:
  217. dispError("Unknown packet from ", client)
  218. destroy(event.packet)
  219. of EvtDisconnect:
  220. var
  221. id = cast[ptr int32](event.peer.data)[]
  222. client = clients[id]
  223. if client.isNil:
  224. disperror("CLIENT IS NIL!")
  225. dispmessage(event.peer.data.isNil)
  226. else:
  227. dispMessage(clients[id], " disconnected")
  228. GCUnref(clients[id])
  229. clients.del id
  230. event.peer.data = nil
  231. else:
  232. discard
  233. when appType == "gui":
  234. fpsText.setString(ff(1.0/dt))
  235. if pubChatDelay.getElapsedTime.asSeconds > 0.25:
  236. pubChatDelay.restart()
  237. flushPubChat()
  238. else:
  239. pubChatDelay -= dt
  240. if frameRate - pubChatDelay > 0.25:
  241. flushPubChat()
  242. when appType == "gui":
  243. window.clear(Black)
  244. window.draw(GUI)
  245. window.draw chatbox
  246. window.draw mousePos
  247. window.draw fpstext
  248. window.display()
  249. server.destroy()
  250. enetDeinit()