summary refs log tree commit diff stats
path: root/tests/manyloc/keineschweine/server
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manyloc/keineschweine/server')
-rw-r--r--tests/manyloc/keineschweine/server/dirserver_settings.json7
-rw-r--r--tests/manyloc/keineschweine/server/nimrod.cfg6
-rw-r--r--tests/manyloc/keineschweine/server/old_dirserver.nim201
-rw-r--r--tests/manyloc/keineschweine/server/old_server_utils.nim98
-rw-r--r--tests/manyloc/keineschweine/server/old_sg_server.nim254
-rw-r--r--tests/manyloc/keineschweine/server/sg_lobby.nim267
6 files changed, 833 insertions, 0 deletions
diff --git a/tests/manyloc/keineschweine/server/dirserver_settings.json b/tests/manyloc/keineschweine/server/dirserver_settings.json
new file mode 100644
index 000000000..18c15fb46
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/dirserver_settings.json
@@ -0,0 +1,7 @@
+{
+ "network":"lamenet",
+ "port":2049,
+ "zones":[
+  {"name":"alphazone","key":"skittles"} 
+ ]
+}
\ No newline at end of file
diff --git a/tests/manyloc/keineschweine/server/nimrod.cfg b/tests/manyloc/keineschweine/server/nimrod.cfg
new file mode 100644
index 000000000..fdc45a8e1
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/nimrod.cfg
@@ -0,0 +1,6 @@
+debugger = off
+deadCodeElim = on
+path = ".."
+path = "../genpacket"
+path = "../helpers"
+define = NoSFML
diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim
new file mode 100644
index 000000000..897fc7d32
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/old_dirserver.nim
@@ -0,0 +1,201 @@
+## directory server
+## handles client authorization and assets
+
+import
+  sockets, times, streams, streams_enh, tables, json, os,
+  sg_packets, sg_assets, md5, server_utils, map_filter
+type
+  THandler = proc(client: PCLient; stream: PStream)
+var
+  server: TSocket
+  handlers = initTable[char, THandler](16)
+  thisZone = newScZoneRecord("local", "sup")
+  zoneList = newScZoneList()
+  thisZoneSettings: string
+  zoneSlots: seq[tuple[name: string; key: string]] = @[]
+  zones: seq[PClient] = @[]
+  ## I was high.
+  clients = initTable[TupAddress, PClient](16)
+  alias2client = initTable[string, PClient](32)
+  allClients: seq[PClient] = @[] 
+
+proc findClient*(host: string; port: int16): PClient =
+  let addy: TupAddress = (host, port)
+  if clients.hasKey(addy):
+    return clients[addy]
+  result = newClient(addy)
+  clients[addy] = result
+  allClients.add(result)
+
+proc loginZone(client: PClient; login: SdZoneLogin): bool =
+  if not client.auth:
+    for s in zoneSlots.items:
+      if s.name == login.name and s.key == login.key:
+        client.auth = true
+        client.kind = CServer
+        client.record = login.record
+        result = true
+        break
+
+proc sendZoneList(client: PClient) = 
+  echo(">> zonelist ", client, ' ', HZoneList)
+  client.send(HZonelist, zonelist)
+proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) =
+  var m = newScChat(CPriv, sender.alias, txt)
+  rcv.send(HChat, m)
+proc sendChat(client: PClient; kind: ChatType; txt: string) =
+  echo(">> chat ", client)
+  var m = newScChat(kind, "", txt)
+  client.send(HChat, m)
+
+
+
+var pubChatQueue = newIncomingBuffer()
+proc queuePub(sender: string, msg: CsChat) =
+  var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text)
+  pubChatQueue.write(HChat)
+  chat.pack(pubChatQueue)
+
+handlers[HHello] = (proc(client: PClient; stream: PStream) =
+  var h = readCsHello(stream)
+  if h.i == 14:
+    var greet = newScHello("Well hello there")
+    client.send(HHello, greet))
+handlers[HLogin] = proc(client: PClient; stream: PStream) =
+  var loginInfo = readCsLogin(stream)
+  echo("** login: alias = ", loginInfo.alias)
+  if alias2client.hasKey(loginInfo.alias):
+    client.sendError("Alias in use.")
+    return
+  if client.loginPlayer(loginInfo):
+    alias2client[client.alias] = client
+    client.sendMessage("Welcome "& client.alias)
+    var session = newScLogin(client.id, client.alias, client.session)
+    client.send HLogin, session
+    client.sendZonelist()
+
+handlers[HZoneList] = proc(client: PClient; stream: PStream) =
+  var pinfo = readCsZoneList(stream)
+  echo("** zonelist req")
+  sendZoneList client
+handlers[HChat] = proc(client: PClient; stream: PStream) =
+  var chat = readCsChat(stream)
+  if not client.auth:
+    client.sendError("You are not logged in.")
+    return
+  if chat.target != "": ##private
+    if alias2client.hasKey(chat.target):
+      alias2client[chat.target].forwardPrivate(client, chat.text)
+  else:
+    queuePub(client.alias, chat)
+
+proc sendServMsg(client: PClient; msg: string) =
+  var m = newDsMsg(msg)
+  client.send HDsMsg, m
+handlers[HZoneLogin] = proc(client: PClient; stream: PStream) =
+  var 
+    login = readSdZoneLogin(stream)
+  if not client.loginZone(login):
+    client.sendServMsg "Invalid login"
+  else:
+    client.sendServMsg "Welcome to the servers"
+    echo "** Zone logged in: ", login
+    zones.add client
+    zonelist.zones.add client.record
+
+
+handlers[HFileChallenge] = proc(client: PClient; stream: PStream) =
+  if client.auth:
+    if client.kind == CServer:
+      var chg = readScFileChallenge(stream)
+
+proc handlePkt(s: PClient; stream: PStream) =
+  while not stream.atEnd:  
+    var typ = readChar(stream)
+    if not handlers.hasKey(typ):
+      break
+    else:
+      handlers[typ](s, stream)
+
+proc createServer(port: TPort) =
+  if not server.isNil:
+    server.close()
+  server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false)
+  server.bindAddr(port)
+
+
+var clientIndex = 0
+var incoming = newIncomingBuffer()
+proc poll*(timeout: int = 250) =
+  if server.isNil: return
+  var 
+    reads = @[server]
+    writes = @[server]
+  if select(reads, timeout) > 0:
+    var
+      addy = ""
+      port: TPort
+    incoming.data.setLen 512
+    let res = server.recvFromAsync(incoming.data, 512, addy, port, 0)
+    if not res:
+      echo("No recv")
+      return
+    else:
+      var client = findClient(addy, port.int16)
+      echo "<< ", res, " ", client, ": ", len(incoming.data), " ", repr(incoming.data)
+      handlePkt(client, incoming)
+    incoming.flush()
+  if selectWrite(writes, timeout) > 0:
+    let nclients = allClients.len
+    if nclients == 0:
+      return
+    clientIndex = (clientIndex + 1) mod nclients
+    var c = allClients[clientIndex]
+    if c.outputBuf.getPosition > 0:
+      let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data)
+      echo("Write ", c, " result: ", res, " data: ", repr(c.outputBuf.data))
+      c.outputBuf.flush()
+
+when isMainModule:
+  import parseopt, matchers, strutils
+  var cfgFile = "dirserver_settings.json"
+  for kind, key, val in getOpt():
+    case kind
+    of cmdShortOption, cmdLongOption:
+      case key
+      of "f", "file": 
+        if existsFile(val):
+          cfgFile = val
+        else:
+          echo("File does not exist: ", val)
+      else:
+        echo("Unknown option: ", key," ", val)
+    else:
+      echo("Unknown option: ", key, " ", val)
+  var jsonSettings = parseFile(cfgFile)
+  let port = TPort(jsonSettings["port"].num)
+  zonelist.network = jsonSettings["network"].str
+  for slot in jsonSettings["zones"].items:
+    zoneSlots.add((slot["name"].str, slot["key"].str))
+  
+  createServer(port)
+  echo("Listening on port ", port, "...")
+  var pubChatTimer = cpuTime() #newClock()
+  const PubChatDelay = 1000/1000
+  while true:
+    poll(15)
+    ## TODO sort this type of thing VV into a queue api 
+    if cpuTime() - pubChatTimer > PubChatDelay:       #.getElapsedTime.asMilliseconds > 100:
+      pubChatTimer -= pubChatDelay
+      if pubChatQueue.getPosition > 0:
+        var cn = 0
+        let sizePubChat = pubChatQueue.data.len
+        var sent = 0
+        filterIt2(allClients, it.auth == true and it.kind == CPlayer):
+          it.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
+          sent += 1
+        #for c in allClients:
+        #  c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
+        pubChatQueue.flush()
+        echo "pubChatQueue flushed to ", sent, "clients"
+
diff --git a/tests/manyloc/keineschweine/server/old_server_utils.nim b/tests/manyloc/keineschweine/server/old_server_utils.nim
new file mode 100644
index 000000000..af9a1b01e
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/old_server_utils.nim
@@ -0,0 +1,98 @@
+import 
+  streams, md5, sockets, unsigned,
+  sg_packets, zlib_helpers, idgen
+type
+  TClientType* = enum
+    CServer = 0'i8, CPlayer, CUnknown
+  PClient* = ref TClient
+  TClient* = object of TObject
+    id*: int32
+    addy*: TupAddress
+    clientID*: uint16
+    auth*: bool
+    outputBuf*: PStringStream
+    case kind*: TClientType
+    of CPlayer:
+      alias*: string
+      session*: string
+      lastPing*: float
+      failedPings*: int
+    of CServer:
+      record*: ScZoneRecord
+      cfg*: TChecksumFile
+    of CUnknown: nil
+  TChecksumFile* = object
+    unpackedSize*: int
+    sum*: MD5Digest
+    compressed*: string
+  TupAddress* = tuple[host: string, port: int16]
+  PIDGen*[T: Ordinal] = ref TIDGen[T]
+  TIDGen[T: Ordinal] = object
+    max: T
+    freeIDs: seq[T]
+var cliID = newIdGen[int32]()
+
+proc sendMessage*(client: PClient; txt: string)
+proc sendError*(client: PClient; txt: string)
+proc `$`*(client: PClient): string
+
+proc newIncomingBuffer*(size = 1024): PStringStream =
+  result = newStringStream("")
+  result.data.setLen size
+  result.data.setLen 0
+  result.flushImpl = proc(stream: PStream) =
+    stream.setPosition(0)
+    PStringStream(stream).data.setLen(0)
+
+
+proc free*(c: PClient) =
+  echo "Client freed: ", c
+  cliID.del c.id
+  c.outputBuf.flush()
+  c.outputBuf = nil
+proc newClient*(addy: TupAddress): PClient =
+  new(result, free)
+  result.addy = addy
+  result.outputBuf = newStringStream("")
+  result.outputBuf.flushImpl = proc(stream: PStream) = 
+    stream.setPosition 0
+    PStringStream(stream).data.setLen 0
+
+proc loginPlayer*(client: PClient; login: CsLogin): bool =
+  if client.auth:
+    client.sendError("You are already logged in.")
+    return
+  client.id = cliID.next()
+  client.auth = true
+  client.kind = CPlayer
+  client.alias = login.alias
+  client.session = getMD5(client.alias & $rand(10000))
+  result = true
+
+proc `$`*(client: PClient): string =
+  if not client.auth: return $client.addy
+  case client.kind
+  of CPlayer: result = client.alias
+  of CServer: result = client.record.name
+  else: result = $client.addy
+proc send*[T](client: PClient; pktType: char; pkt: var T) =
+  client.outputBuf.write(pktType)
+  pkt.pack(client.outputBuf)
+
+proc sendMessage*(client: PClient; txt: string) =
+  var m = newScChat(CSystem, text = txt)
+  client.send HChat, m
+proc sendError*(client: PClient; txt: string) =
+  var m = newScChat(CError, text = txt)
+  client.send HChat, m
+
+proc checksumFile*(filename: string): TChecksumFile =
+  let fullText = readFile(filename)
+  result.unpackedSize = fullText.len
+  result.sum = toMD5(fullText)
+  result.compressed = compress(fullText)
+proc checksumStr*(str: string): TChecksumFile =
+  result.unpackedSize = str.len
+  result.sum = toMD5(str)
+  result.compressed = compress(str)
+
diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim
new file mode 100644
index 000000000..ac85cbf62
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/old_sg_server.nim
@@ -0,0 +1,254 @@
+import
+  sockets, times, streams, streams_enh, tables, json, os, unsigned,
+  sg_packets, sg_assets, md5, server_utils, client_helpers
+var
+  dirServer: PServer
+  thisZone = newScZoneRecord("local", "sup")
+  thisZoneSettings: PZoneSettings
+  dirServerConnected = false
+  ## I was high.
+  clients = initTable[TupAddress, PClient](16)
+  alias2client = initTable[string, PClient](32)
+  allClients: seq[PClient] = @[] 
+  zonePlayers: seq[PClient] = @[] 
+const
+  PubChatDelay = 100/1000 #100 ms
+
+import hashes
+proc hash*(x: uint16): THash {.inline.} = 
+  result = int32(x)
+
+proc findClient*(host: string; port: int16): PClient =
+  let addy: TupAddress = (host, port)
+  if clients.hasKey(addy):
+    return clients[addy]
+  result = newClient(addy)
+  clients[addy] = result
+  allClients.add(result)
+
+
+proc sendZoneList(client: PClient) = 
+  echo(">> zonelist ", client)
+  #client.send(HZonelist, zonelist)
+
+proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) =
+  var m = newScChat(CPriv, sender.alias, txt)
+  rcv.send(HChat, m)
+proc sendChat(client: PClient; kind: ChatType; txt: string) =
+  echo(">> chat ", client)
+  var m = newScChat(kind, "", txt)
+  client.send(HChat, m)
+
+var pubChatQueue = newStringStream("")
+pubChatQueue.flushImpl = proc(stream: PStream) =
+  stream.setPosition(0)
+  PStringStream(stream).data.setLen(0)
+proc queuePub(sender: string, msg: CsChat) =
+  var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text)
+  pubChatQueue.write(HChat)
+  chat.pack(pubChatQueue)
+
+handlers[HHello] = (proc(client: PClient; stream: PStream) =
+  var h = readCsHello(stream)
+  if h.i == 14:
+    var greet = newScHello("Well hello there")
+    client.send(HHello, greet))
+handlers[HLogin] = proc(client: PClient; stream: PStream) =
+  var loginInfo = readCsLogin(stream)
+  echo("** login: alias = ", loginInfo.alias)
+  if not dirServerConnected and client.loginPlayer(loginInfo):
+    client.sendMessage("Welcome "& client.alias)
+    alias2client[client.alias] = client
+    client.sendZonelist()
+handlers[HZoneList] = proc(client: PClient; stream: PStream) =
+  var pinfo = readCsZoneList(stream)
+  echo("** zonelist req")
+handlers[HChat] = proc(client: PClient; stream: PStream) =
+  var chat = readCsChat(stream)
+  if not client.auth:
+    client.sendError("You are not logged in.")
+    return
+  if chat.target != "": ##private
+    if alias2client.hasKey(chat.target):
+      alias2client[chat.target].forwardPrivate(client, chat.text)
+  else:
+    queuePub(client.alias, chat)
+handlers[HZoneQuery] = proc(client: PClient; stream: PStream) =
+  echo("Got zone query")
+  var q = readCsZoneQuery(stream)
+  var resp = newScZoneQuery(zonePlayers.len.uint16)
+  client.send(HZoneQuery, resp)
+
+
+
+handlers[HZoneJoinReq] = proc(client: PClient; stream: PStream) =
+  var req = readCsZoneJoinReq(stream)
+  echo "Join zone request from (",req.session.id,") ", req.session.alias 
+  if client.auth and client.kind == CPlayer:
+    echo "Client is authenticated, verifying filez"
+    client.startVerifyingFiles()
+  elif dirServerConnected:
+    echo "Dirserver is connected, verifying client"
+    dirServer.send HVerifyClient, req.session
+  else:
+    echo "Dirserver is disconnected =("
+    client.startVerifyingFiles()
+
+
+
+proc handlePkt(s: PClient; stream: PStream) =
+  while not stream.atEnd:  
+    var typ = readChar(stream)
+    if not handlers.hasKey(typ):
+      break
+    else:
+      handlers[typ](s, stream)
+
+proc createServer(port: TPort) =
+  if not server.isNil:
+    server.close()
+  server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false)
+  server.bindAddr(port)
+
+var clientIndex = 0
+var incoming = newIncomingBuffer()
+proc poll*(timeout: int = 250) =
+  if server.isNil: return
+  var 
+    reads = @[server]
+    writes = @[server]
+  if select(reads, timeout) > 0:
+    var
+      addy = ""
+      port: TPort
+    let res = server.recvFromAsync(incoming.data, 512, addy, port, 0)
+    if not res:
+      echo("No recv")
+      return
+    else:
+      var client = findClient(addy, port.int16)
+      #echo("<< ", res, " ", client.alias, ": ", len(line.data), " ", repr(line.data))
+      handlePkt(client, incoming)
+    incoming.flush()
+  if selectWrite(writes, timeout) > 0:
+    let nclients = allClients.len
+    if nclients == 0:
+      return
+    clientIndex = (clientIndex + 1) mod nclients
+    var c = allClients[clientIndex]
+    if c.outputBuf.getPosition > 0:
+      let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data)
+      echo("Write ", c, " result: ", res, " data: ", c.outputBuf.data)
+      c.outputBuf.flush()
+
+when isMainModule:
+  import parseopt, matchers, strutils
+  var zoneCfgFile = "./server_settings.json"
+  for kind, key, val in getOpt():
+    case kind
+    of cmdShortOption, cmdLongOption:
+      case key
+      of "f", "file": 
+        if existsFile(val):
+          zoneCfgFile = val
+        else:
+          echo("File does not exist: ", val)
+      else:
+        echo("Unknown option: ", key," ", val)
+    else:
+      echo("Unknown option: ", key, " ", val)
+  var jsonSettings = parseFile(zoneCfgFile)
+  let 
+    host = jsonSettings["host"].str
+    port = TPort(jsonSettings["port"].num)
+    zoneFile = jsonSettings["settings"].str
+    dirServerInfo = jsonSettings["dirserver"]
+  
+  var path = getAppDir()/../"data"/zoneFile
+  if not existsFile(path):
+    echo("Zone settings file does not exist: ../data/", zoneFile)
+    echo(path)
+    quit(1)
+  
+  ## Test file
+  block:
+    var 
+      TestFile: FileChallengePair
+      contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz")
+    testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) 
+    testFile.file = checksumStr(contents)
+    myAssets.add testFile
+  
+  setCurrentDir getAppDir().parentDir()
+  block:
+    let zonesettings = readFile(path)
+    var 
+      errors: seq[string] = @[]
+    if not loadSettings(zoneSettings, errors):
+      echo("You have errors in your zone settings:")
+      for e in errors: echo("**", e)
+      quit(1)
+    errors.setLen 0
+    
+    var pair: FileChallengePair
+    pair.challenge.file = zoneFile
+    pair.challenge.assetType = FZoneCfg
+    pair.challenge.fullLen = zoneSettings.len.int32
+    pair.file = checksumStr(zoneSettings)
+    myAssets.add pair
+    
+    allAssets:
+      if not load(asset):
+        echo "Invalid or missing file ", file
+      else:
+        var pair: FileChallengePair
+        pair.challenge.file = file
+        pair.challenge.assetType = assetType
+        pair.challenge.fullLen = getFileSize(
+          expandPath(assetType, file)).int32
+        pair.file = asset.contents
+        myAssets.add pair
+        
+    echo "Zone has ", myAssets.len, " associated assets"
+    
+      
+    dirServer = newServerConnection(dirServerInfo[0].str, dirServerInfo[1].num.TPort)
+    dirServer.handlers[HDsMsg] = proc(serv: PServer; stream: PStream) =
+      var m = readDsMsg(stream)
+      echo("DirServer> ", m.msg)
+    dirServer.handlers[HZoneLogin] = proc(serv: PServer; stream: PStream) =
+      let loggedIn = readDsZoneLogin(stream).status
+      if loggedIn:
+        dirServerConnected = true
+    dirServer.writePkt HZoneLogin, login
+  
+  thisZone.name = jsonSettings["name"].str
+  thisZone.desc = jsonSettings["desc"].str
+  thisZone.ip = "localhost"
+  thisZone.port = port
+  var login = newSdZoneLogin(
+    dirServerInfo[2].str, dirServerInfo[3].str,
+    thisZone)  
+  #echo "MY LOGIN: ", $login
+  
+  
+  
+  createServer(port)
+  echo("Listening on port ", port, "...")
+  var pubChatTimer = cpuTime()#newClock()
+  while true:
+    discard dirServer.pollServer(15)
+    poll(15)
+    ## TODO sort this type of thing VV into a queue api
+    #let now = cpuTime() 
+    if cpuTime() - pubChatTimer > PubChatDelay:       #.getElapsedTime.asMilliseconds > 100:
+      pubChatTimer -= pubChatDelay #.restart()
+      if pubChatQueue.getPosition > 0:
+        var cn = 0
+        let sizePubChat = pubChatQueue.data.len
+        for c in allClients:
+          c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
+        pubChatQueue.flush()
+
+  
+  
\ No newline at end of file
diff --git a/tests/manyloc/keineschweine/server/sg_lobby.nim b/tests/manyloc/keineschweine/server/sg_lobby.nim
new file mode 100644
index 000000000..042d72337
--- /dev/null
+++ b/tests/manyloc/keineschweine/server/sg_lobby.nim
@@ -0,0 +1,267 @@
+
+import
+  sockets, streams, tables, times, math, strutils, json, os, md5, 
+  sfml, sfml_vector, sfml_colors, 
+  streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui
+type
+  TClientSettings = object
+    resolution*: TVideoMode
+    offlineFile: string
+    dirserver: tuple[host: string, port: TPort]
+    website*: string
+var
+  clientSettings: TClientSettings
+  gui = newGuiContainer()
+  zonelist = newGuiContainer()
+  u_alias, u_passwd: PTextEntry
+  activeInput = 0
+  aliasText, passwdText: PText
+  fpsTimer: PButton
+  loginBtn: PButton
+  playBtn: PButton
+  keyClient = newKeyClient("lobby")
+  showZonelist = false
+  chatInput*: PTextEntry
+  messageArea*: PMessageArea
+  mySession*: ScLogin
+var
+  dirServer: PServer
+  zone*: PServer
+  activeServer: PServer
+  bConnected = false
+  outgoing = newStringStream("")
+  downloadProgress: PButton
+  connectionButtons: seq[PButton] #buttons that depend on connection to function
+
+template dispmessage(m: expr): stmt = 
+  messageArea.add(m)
+proc connectZone(host: string; port: TPort)
+proc connectToDirserv()
+
+proc writePkt[T](pid: PacketID; p: var T) =
+  if activeServer.isNil: return
+  activeServer.writePkt pid, p
+
+proc setConnected(state: bool) =
+  if state:
+    bConnected = true
+    for b in connectionButtons: enable(b)
+  else:
+    bConnected = false
+    for b in connectionButtons: disable(b)
+
+proc setActiveZone(ind: int; zone: ScZoneRecord) =
+  #hilight it or something
+  dispmessage("Selected " & zone.name)
+  connectZone(zone.ip, zone.port)
+  playBtn.enable()
+
+proc handleChat(serv: PServer; s: PStream) =
+  var msg = readScChat(s)
+  messageArea.add(msg)
+
+proc connectToDirserv() =
+  if dirServer.isNil:
+    dirServer = newServerConnection(clientSettings.dirserver.host, clientSettings.dirserver.port)
+    dirServer.handlers[HHello] = proc(serv: PServer; s: PStream) = 
+      let msg = readScHello(s)
+      dispMessage(msg.resp)
+      setConnected(true)
+    dirServer.handlers[HLogin] = proc(serv: PServer; s: PStream) =
+      mySession = readScLogin(s)
+      ##do something here
+    dirServer.handlers[HZonelist] = proc(serv: PServer; s: PStream) =
+      var 
+        info = readScZonelist(s)
+        zones = info.zones
+      if zones.len > 0:
+        zonelist.clearButtons()
+        var pos = vec2f(0.0, 0.0)
+        zonelist.newButton(
+          text = "Zonelist - "& info.network,
+          position = pos,
+          onClick = proc(b: PButton) =
+            dispmessage("Click on header"))
+        pos.y += 20
+        for i in 0..zones.len - 1:
+          var z = zones[i]
+          zonelist.newButton(
+            text = z.name, position = pos,
+            onClick = proc(b: PButton) = 
+              setActiveZone(i, z))
+          pos.y += 20
+        showZonelist = true
+    dirServer.handlers[HPoing] = proc(serv: PServer; s: PStream) = 
+      var ping = readPoing(s)
+      dispmessage("Ping: "& $ping.time)
+      ping.time = epochTime().float32
+      serv.writePkt HPoing, ping
+    dirServer.handlers[HChat] = handleChat
+    dirServer.handlers[HFileChallenge] = handleFileChallenge
+  var hello = newCsHello()
+  dirServer.writePkt HHello, hello
+  activeServer = dirServer
+
+
+proc zoneListReq() =
+  var pkt = newCsZonelist("sup")
+  writePkt HZonelist, pkt
+
+##key handlers
+keyClient.registerHandler(MouseMiddle, down, proc() = 
+  gui.setPosition(getMousePos()))
+
+keyClient.registerHandler(KeyO, down, proc() = 
+  if keyPressed(KeyRShift): echo(repr(outgoing)))
+keyClient.registerHandler(KeyTab, down, proc() =
+  activeInput = (activeInput + 1) mod 2) #does this work?
+keyClient.registerHandler(MouseLeft, down, proc() = 
+  let p = getMousePos()
+  gui.click(p)
+  if showZonelist: zonelist.click(p))
+var mptext = newText("", guiFont, 16)
+keyClient.registerHandler(MouseRight, down, proc() = 
+  let p = getMousePos()
+  mptext.setPosition(p)
+  mptext.setString("($1,$2)"%[$p.x.int,$p.y.int]))
+
+
+proc connectZone(host: string, port: TPort) =
+  echo "Connecting to zone at ", host, ':', port
+  if zone.isNil:
+    zone = newServerConnection(host, port)
+    zone.handlers[HFileChallenge] = handleFileChallenge
+    zone.handlers[HChallengeResult] = handleFileChallengeResult
+    zone.handlers[HFileTransfer] = handleFileTransfer
+    zone.handlers[HChat] = handleChat 
+  else:
+    zone.sock.connect(host, port)
+  var hello = newCsHello()
+  zone.writePkt HHello, hello
+  
+
+
+proc lobbyReady*() = 
+  keyClient.setActive()
+  gui.setActive(u_alias)
+
+proc tryConnect*(b: PButton) =
+  connectToDirserv()
+proc tryLogin*(b: PButton) =
+  var login = newCsLogin(
+    alias = u_alias.getText(),
+    passwd = u_passwd.getText())
+  writePkt HLogin, login
+proc tryTransition*(b: PButton) =
+  ##check if we're logged in
+  #<implementation censored by the church>
+  #var joinReq = newCsJ
+  zone.writePkt HZoneJoinReq, mySession
+  #var errors: seq[string] = @[]
+  #if loadSettings("", errors):
+  #  transition()
+  #else:
+  #  for e in errors: dispmessage(e)
+proc playOffline*(b: PButton) =
+  var errors: seq[string] = @[]
+  if loadSettingsFromFile(clientSettings.offlineFile, errors):
+    transition()
+  else:
+    dispmessage("Errors reading the file ("& clientSettings.offlineFile &"):")
+    for e in errors: dispmessage(e)
+
+proc getClientSettings*(): TClientSettings =
+  result = clientSettings
+
+proc lobbyInit*() =
+  var s = json.parseFile("./client_settings.json")
+  clientSettings.offlineFile = "data/"
+  clientSettings.offlineFile.add s["default-file"].str
+  let dirserv = s["directory-server"]
+  clientSettings.dirserver.host = dirserv["host"].str
+  clientSettings.dirserver.port = dirserv["port"].num.TPort
+  clientSettings.resolution.width = s["resolution"][0].num.cint
+  clientSettings.resolution.height= s["resolution"][1].num.cint
+  clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint
+  clientSettings.website = s["website"].str
+  zonelist.setPosition(vec2f(200.0, 100.0))
+  connectionButtons = @[]
+  
+  downloadProgress = gui.newButton(
+    text = "", position = vec2f(10, 130), onClick = nil) 
+  downloadProgress.bg.setFillColor(color(34, 139, 34))
+  downloadProgress.bg.setSize(vec2f(0, 0))
+  
+  var pos = vec2f(10, 10)
+  u_alias = gui.newTextEntry(
+    if s.existsKey("alias"): s["alias"].str else: "alias", 
+    pos)
+  pos.y += 20
+  u_passwd = gui.newTextEntry("buzz", pos)
+  pos.y += 20
+  connectionButtons.add(gui.newButton(
+    text = "Login", 
+    position = pos,
+    onClick = tryLogin,
+    startEnabled = false))
+  pos.y += 20
+  fpsText.setPosition(pos)
+  
+  playBtn = gui.newButton(
+    text = "Play",
+    position = vec2f(680.0, 8.0),
+    onClick = tryTransition,
+    startEnabled = false)
+  gui.newButton(
+    text = "Play Offline",
+    position = vec2f(680.0, 28.0),
+    onClick = playOffline)
+  fpsTimer = gui.newButton(
+    text = "FPS: ",
+    position = vec2f(10.0, 70.0),
+    onClick = proc(b: PButton) = nil)
+  gui.newButton(
+    text = "Connect",
+    position = vec2f(10.0, 90.0),
+    onClick = tryConnect)
+  connectionButtons.add(gui.newButton(
+    text = "Test Chat",
+    position = vec2f(10.0, 110.0),
+    onClick = (proc(b: PButton) = 
+      var pkt = newCsChat(text = "ohai")
+      writePkt HChat, pkt),
+    startEnabled = false))
+  chatInput = gui.newTextEntry("...", vec2f(10.0, 575.0), proc() =
+    sendChat dirServer, chatInput.getText()
+    chatInput.clearText())
+  messageArea = gui.newMessageArea(vec2f(10.0, 575.0 - 20.0))
+  messageArea.sizeVisible = 25
+  gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) =
+    messageArea.scrollBack += 1
+    update(messageArea))
+  gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) = 
+    messageArea.scrollBack -= 1
+    update(messageArea))
+  gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
+    for i in 0.. <30: 
+      dispMessage($i))
+
+var i = 0
+proc lobbyUpdate*(dt: float) = 
+  #let res = disp.poll()
+  gui.update(dt)
+  i = (i + 1) mod 60
+  if i == 0:
+    fpsTimer.setString("FPS: "& $round(1.0/dt))
+  if not pollServer(dirServer, 5) and bConnected:
+    setConnected(false)
+    echo("Lost connection")
+  discard pollServer(zone, 5)
+
+proc lobbyDraw*(window: PRenderWindow) =
+  window.clear(Black)
+  window.draw messageArea
+  window.draw mptext
+  window.draw gui
+  if showZonelist: window.draw zonelist
+  window.display()