added manyloc test suite; --path now relative to project dir if not absolute

This commit is contained in:
Araq 2013-04-13 21:55:02 +02:00
commit 75b508032b
51 changed files with 9286 additions and 5 deletions

View file

@ -0,0 +1,7 @@
{
"network":"lamenet",
"port":2049,
"zones":[
{"name":"alphazone","key":"skittles"}
]
}

View file

@ -0,0 +1,6 @@
debugger = off
deadCodeElim = on
path = ".."
path = "../genpacket"
path = "../helpers"
define = NoSFML

View file

@ -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"

View file

@ -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)

View file

@ -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()

View file

@ -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()