sg_lobby.nim 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import
  2. sockets, streams, tables, times, math, strutils, json, os, md5,
  3. sfml, sfml_vector, sfml_colors,
  4. streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui
  5. type
  6. TClientSettings = object
  7. resolution*: TVideoMode
  8. offlineFile: string
  9. dirserver: tuple[host: string, port: TPort]
  10. website*: string
  11. var
  12. clientSettings: TClientSettings
  13. gui = newGuiContainer()
  14. zonelist = newGuiContainer()
  15. u_alias, u_passwd: PTextEntry
  16. activeInput = 0
  17. aliasText, passwdText: PText
  18. fpsTimer: PButton
  19. loginBtn: PButton
  20. playBtn: PButton
  21. keyClient = newKeyClient("lobby")
  22. showZonelist = false
  23. chatInput*: PTextEntry
  24. messageArea*: PMessageArea
  25. mySession*: ScLogin
  26. var
  27. dirServer: PServer
  28. zone*: PServer
  29. activeServer: PServer
  30. bConnected = false
  31. outgoing = newStringStream("")
  32. downloadProgress: PButton
  33. connectionButtons: seq[PButton] #buttons that depend on connection to function
  34. template dispmessage(m: expr): stmt =
  35. messageArea.add(m)
  36. proc connectZone(host: string; port: TPort)
  37. proc connectToDirserv()
  38. proc writePkt[T](pid: PacketID; p: var T) =
  39. if activeServer.isNil: return
  40. activeServer.writePkt pid, p
  41. proc setConnected(state: bool) =
  42. if state:
  43. bConnected = true
  44. for b in connectionButtons: enable(b)
  45. else:
  46. bConnected = false
  47. for b in connectionButtons: disable(b)
  48. proc setActiveZone(ind: int; zone: ScZoneRecord) =
  49. #highlight it or something
  50. dispmessage("Selected " & zone.name)
  51. connectZone(zone.ip, zone.port)
  52. playBtn.enable()
  53. proc handleChat(serv: PServer; s: PStream) =
  54. var msg = readScChat(s)
  55. messageArea.add(msg)
  56. proc connectToDirserv() =
  57. if dirServer.isNil:
  58. dirServer = newServerConnection(clientSettings.dirserver.host, clientSettings.dirserver.port)
  59. dirServer.handlers[HHello] = proc(serv: PServer; s: PStream) =
  60. let msg = readScHello(s)
  61. dispMessage(msg.resp)
  62. setConnected(true)
  63. dirServer.handlers[HLogin] = proc(serv: PServer; s: PStream) =
  64. mySession = readScLogin(s)
  65. ##do something here
  66. dirServer.handlers[HZonelist] = proc(serv: PServer; s: PStream) =
  67. var
  68. info = readScZonelist(s)
  69. zones = info.zones
  70. if zones.len > 0:
  71. zonelist.clearButtons()
  72. var pos = vec2f(0.0, 0.0)
  73. zonelist.newButton(
  74. text = "Zonelist - "& info.network,
  75. position = pos,
  76. onClick = proc(b: PButton) =
  77. dispmessage("Click on header"))
  78. pos.y += 20
  79. for i in 0..zones.len - 1:
  80. var z = zones[i]
  81. zonelist.newButton(
  82. text = z.name, position = pos,
  83. onClick = proc(b: PButton) =
  84. setActiveZone(i, z))
  85. pos.y += 20
  86. showZonelist = true
  87. dirServer.handlers[HPoing] = proc(serv: PServer; s: PStream) =
  88. var ping = readPoing(s)
  89. dispmessage("Ping: "& $ping.time)
  90. ping.time = epochTime().float32
  91. serv.writePkt HPoing, ping
  92. dirServer.handlers[HChat] = handleChat
  93. dirServer.handlers[HFileChallenge] = handleFileChallenge
  94. var hello = newCsHello()
  95. dirServer.writePkt HHello, hello
  96. activeServer = dirServer
  97. proc zoneListReq() =
  98. var pkt = newCsZonelist("sup")
  99. writePkt HZonelist, pkt
  100. ##key handlers
  101. keyClient.registerHandler(MouseMiddle, down, proc() =
  102. gui.setPosition(getMousePos()))
  103. keyClient.registerHandler(KeyO, down, proc() =
  104. if keyPressed(KeyRShift): echo(repr(outgoing)))
  105. keyClient.registerHandler(KeyTab, down, proc() =
  106. activeInput = (activeInput + 1) mod 2) #does this work?
  107. keyClient.registerHandler(MouseLeft, down, proc() =
  108. let p = getMousePos()
  109. gui.click(p)
  110. if showZonelist: zonelist.click(p))
  111. var mptext = newText("", guiFont, 16)
  112. keyClient.registerHandler(MouseRight, down, proc() =
  113. let p = getMousePos()
  114. mptext.setPosition(p)
  115. mptext.setString("($1,$2)"%[$p.x.int,$p.y.int]))
  116. proc connectZone(host: string, port: TPort) =
  117. echo "Connecting to zone at ", host, ':', port
  118. if zone.isNil:
  119. zone = newServerConnection(host, port)
  120. zone.handlers[HFileChallenge] = handleFileChallenge
  121. zone.handlers[HChallengeResult] = handleFileChallengeResult
  122. zone.handlers[HFileTransfer] = handleFileTransfer
  123. zone.handlers[HChat] = handleChat
  124. else:
  125. zone.sock.connect(host, port)
  126. var hello = newCsHello()
  127. zone.writePkt HHello, hello
  128. proc lobbyReady*() =
  129. keyClient.setActive()
  130. gui.setActive(u_alias)
  131. proc tryConnect*(b: PButton) =
  132. connectToDirserv()
  133. proc tryLogin*(b: PButton) =
  134. var login = newCsLogin(
  135. alias = u_alias.getText(),
  136. passwd = u_passwd.getText())
  137. writePkt HLogin, login
  138. proc tryTransition*(b: PButton) =
  139. ##check if we're logged in
  140. #<implementation censored by the church>
  141. #var joinReq = newCsJ
  142. zone.writePkt HZoneJoinReq, mySession
  143. #var errors: seq[string] = @[]
  144. #if loadSettings("", errors):
  145. # transition()
  146. #else:
  147. # for e in errors: dispmessage(e)
  148. proc playOffline*(b: PButton) =
  149. var errors: seq[string] = @[]
  150. if loadSettingsFromFile(clientSettings.offlineFile, errors):
  151. transition()
  152. else:
  153. dispmessage("Errors reading the file ("& clientSettings.offlineFile &"):")
  154. for e in errors: dispmessage(e)
  155. proc getClientSettings*(): TClientSettings =
  156. result = clientSettings
  157. proc lobbyInit*() =
  158. var s = json.parseFile("./client_settings.json")
  159. clientSettings.offlineFile = "data/"
  160. clientSettings.offlineFile.add s["default-file"].str
  161. let dirserv = s["directory-server"]
  162. clientSettings.dirserver.host = dirserv["host"].str
  163. clientSettings.dirserver.port = dirserv["port"].num.TPort
  164. clientSettings.resolution.width = s["resolution"][0].num.cint
  165. clientSettings.resolution.height= s["resolution"][1].num.cint
  166. clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint
  167. clientSettings.website = s["website"].str
  168. zonelist.setPosition(vec2f(200.0, 100.0))
  169. connectionButtons = @[]
  170. downloadProgress = gui.newButton(
  171. text = "", position = vec2f(10, 130), onClick = nil)
  172. downloadProgress.bg.setFillColor(color(34, 139, 34))
  173. downloadProgress.bg.setSize(vec2f(0, 0))
  174. var pos = vec2f(10, 10)
  175. u_alias = gui.newTextEntry(
  176. if s.existsKey("alias"): s["alias"].str else: "alias",
  177. pos)
  178. pos.y += 20
  179. u_passwd = gui.newTextEntry("buzz", pos)
  180. pos.y += 20
  181. connectionButtons.add(gui.newButton(
  182. text = "Login",
  183. position = pos,
  184. onClick = tryLogin,
  185. startEnabled = false))
  186. pos.y += 20
  187. fpsText.setPosition(pos)
  188. playBtn = gui.newButton(
  189. text = "Play",
  190. position = vec2f(680.0, 8.0),
  191. onClick = tryTransition,
  192. startEnabled = false)
  193. gui.newButton(
  194. text = "Play Offline",
  195. position = vec2f(680.0, 28.0),
  196. onClick = playOffline)
  197. fpsTimer = gui.newButton(
  198. text = "FPS: ",
  199. position = vec2f(10.0, 70.0),
  200. onClick = proc(b: PButton) = nil)
  201. gui.newButton(
  202. text = "Connect",
  203. position = vec2f(10.0, 90.0),
  204. onClick = tryConnect)
  205. connectionButtons.add(gui.newButton(
  206. text = "Test Chat",
  207. position = vec2f(10.0, 110.0),
  208. onClick = (proc(b: PButton) =
  209. var pkt = newCsChat(text = "ohai")
  210. writePkt HChat, pkt),
  211. startEnabled = false))
  212. chatInput = gui.newTextEntry("...", vec2f(10.0, 575.0), proc() =
  213. sendChat dirServer, chatInput.getText()
  214. chatInput.clearText())
  215. messageArea = gui.newMessageArea(vec2f(10.0, 575.0 - 20.0))
  216. messageArea.sizeVisible = 25
  217. gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) =
  218. messageArea.scrollBack += 1
  219. update(messageArea))
  220. gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) =
  221. messageArea.scrollBack -= 1
  222. update(messageArea))
  223. gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
  224. for i in 0..< 30:
  225. dispMessage($i))
  226. var i = 0
  227. proc lobbyUpdate*(dt: float) =
  228. #let res = disp.poll()
  229. gui.update(dt)
  230. i = (i + 1) mod 60
  231. if i == 0:
  232. fpsTimer.setString("FPS: "& $round(1.0/dt))
  233. if not pollServer(dirServer, 5) and bConnected:
  234. setConnected(false)
  235. echo("Lost connection")
  236. discard pollServer(zone, 5)
  237. proc lobbyDraw*(window: PRenderWindow) =
  238. window.clear(Black)
  239. window.draw messageArea
  240. window.draw mptext
  241. window.draw gui
  242. if showZonelist: window.draw zonelist
  243. window.display()