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,228 @@
import enet, strutils,
sfml, sfml_colors, sg_gui, input_helpers,
math_helpers, sg_packets, estreams, tables,
json, sg_assets, client_helpers
if enetInit() != 0:
quit "Could not initialize ENet"
type
TClientSettings = object
resolution*: TVideoMode
offlineFile: string
dirserver: tuple[host: string, port: int16]
website*: string
var
clientSettings: TClientSettings
event: enet.TEvent
bConnected = false
runServer = true
gui = newGuiContainer()
zonelist = newGuiContainer()
kc = newKeyClient(setActive = true)
clock = newClock()
chatBox: PMessageArea
chatInput: PTextEntry
loginBtn, playBtn: PButton
fpsText = newText("", guiFont, 18)
connectionButtons: seq[PButton]
connectButton: PButton
u_alias, u_passwd: PTextEntry
dirServer: PServer
zone: PServer
showZoneList = false
myCreds = newScLogin(0, "", "") ##my session token
proc handleChat(server: PServer; buf: PBuffer) =
let msg = readScChat(buf)
chatBox.add msg
proc handlePlayerLogin(server: PServer; buf: PBuffer) =
let login = readScLogin(buf)
myCreds = login
echo("I am ", $myCreds)
kc.registerHandler MouseLeft, down, proc() =
gui.click(input_helpers.getMousePos())
block:
var pos = vec2f(15, 550)
chatBox = gui.newMessageArea(pos)
pos.y += 20
chatInput = gui.newTextEntry("...", pos, proc() =
sendPubChat dirServer, chatInput.getText()
chatInput.clearText())
gui.setActive(chatInput)
proc dispMessage(args: varargs[string, `$`]) =
var s = ""
for it in items(args):
s.add it
chatbox.add(s)
proc dispMessage(text: string) {.inline.} =
chatbox.add(text)
proc dispError(text: string) {.inline.} =
chatBox.add(newScChat(kind = CError, text = text))
proc updateButtons() =
let conn = dirServer.connected
for b in connectionButtons: setEnabled(b, conn)
if conn:
connectButton.setString "Disconnect"
else:
connectButton.setString "Connect"
proc poll(serv: PServer; timeout: cuint = 30) =
if serv.isNil or serv.host.isNil: return
if serv.connected:
while serv.host.hostService(event, timeout) > 0:
case event.kind
of EvtReceive:
var buf = newBuffer(event.packet)
serv.handlePackets(buf)
event.packet.destroy()
of EvtDisconnect:
dispMessage "Disconnected"
serv.connected = false
event.peer.data = nil
updateButtons()
of EvtNone: discard
else:
echo repr(event)
else:
if serv.host.hostService(event, timeout) > 0 and event.kind == EvtConnect:
dispMessage "Connected"
serv.connected = true
if serv.peer != event.peer:
serv.peer = event.peer
event.peer.data = serv
updateButtons()
proc tryLogin*(b: PButton) =
var login = newCsLogin(
alias = u_alias.getText(),
passwd = u_passwd.getText())
dirServer.send HLogin, login
proc tryTransition*(b: PButton) =
#zone.writePkt HZoneJoinReq, myCreds
proc tryConnect*(b: PButton) =
if not dirServer.connected:
var error: string
if not dirServer.connect(
clientSettings.dirServer.host,
clientSettings.dirServer.port,
error):
dispError(error)
else:
dirServer.peer.disconnect(1)
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: dispError(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.int16
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 = @[]
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
pos.y += 20
connectButton = gui.newButton(
text = "Connect",
position = pos,
onClick = tryConnect)
pos.y += 20
gui.newButton("Test Files", position = pos, onClick = proc(b: PButton) =
var req = newCsZoneJoinReq(myCreds)
dirServer.send HZoneJoinReq, req)
pos.y += 20
connectionButtons.add(gui.newButton(
text = "Test Chat",
position = pos,
onClick = (proc(b: PButton) =
var pkt = newCsChat(text = "ohai")
dirServer.send HChat, pkt),
startEnabled = false))
pos.y += 20
downloadProgress.setPosition(pos)
downloadProgress.bg.setFillColor(color(34, 139, 34))
downloadProgress.bg.setSize(vec2f(0, 0))
gui.add(downloadProgress)
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)
discard """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))"""
dirServer = newServer()
dirServer.addHandler HChat, handleChat
dirServer.addHandler HLogin, handlePlayerLogin
dirServer.addHandler HFileTransfer, client_helpers.handleFilePartRecv
dirServer.addHandler HChallengeResult, client_helpers.handleFileChallengeResult
dirServer.addHandler HFileChallenge, client_helpers.handleFileChallenge
proc lobbyReady*() =
kc.setActive()
gui.setActive(u_alias)
var i = 0
proc lobbyUpdate*(dt: float) =
dirServer.poll()
#let res = disp.poll()
gui.update(dt)
i = (i + 1) mod 60
if i == 0:
fpsText.setString("FPS: "& ff(1.0/dt))
proc lobbyDraw*(window: PRenderWindow) =
window.clear(Black)
window.draw chatBox
window.draw gui
window.draw fpsText
if showZonelist: window.draw zonelist
window.display()

View file

@ -0,0 +1,294 @@
import enet, strutils, idgen, tables, math_helpers,
estreams, sg_packets, server_utils, sg_assets, client_helpers
when appType == "gui":
import sfml, sfml_colors, sg_gui,
input_helpers, sfml_stuff
else:
import times
type
TCallback = proc(client: PClient; buffer: PBuffer)
var
server: PHost
dirServer: PServer
standAloneMode = true
event: enet.TEvent
clientID = newIDGen[int32]()
clients = initTable[int32, PClient](64)
handlers = initTable[char, TCallback](32)
when appType == "gui":
var
gui = newGuiContainer()
chatBox = gui.newMessageArea(vec2f(15, 550))
window = newRenderWindow(videoMode(800, 600, 32), "Sup yo", sfDefaultSTyle)
mousepos = newText("", guiFont, 16)
fpsText = mousePos.copy()
inputClient = newKeyClient(setActive = true)
chatBox.sizeVisible = 30
mousePos.setColor(Green)
fpsText.setposition(vec2f(0, 20))
inputClient.registerHandler MouseLeft, down, proc() =
gui.click(input_helpers.getMousePos())
inputClient.registerHandler MouseMiddle, down, proc() =
let pos = input_helpers.getMousePos()
mousePos.setString("($1,$2)".format(ff(pos.x), ff(pos.y)))
mousePos.setPosition(pos)
proc dispMessage(args: varargs[string, `$`]) =
var s = ""
for it in items(args):
s.add it
chatbox.add(s)
proc dispError(args: varargs[string, `$`]) =
var s = ""
for it in items(args): s.add(it)
chatBox.add(newScChat(kind = CError, text = s))
else:
proc dispMessage(args: varargs[string, `$`]) =
var m = ""
for it in items(args): m.add(it)
echo "<msg> ", m
proc dispError(args: varargs[string, `$`]) =
var m = ""
for it in items(args): m.add(it)
echo "**", m
var pubChatQueue = newBuffer(1024)
proc queuePub(sender: PClient, msg: CsChat) =
var chat = newScChat(kind = CPub, fromPlayer = sender.alias, text = msg.text)
pubChatQueue.write(HChat)
pubChatQueue.pack(chat)
proc flushPubChat() =
if pubChatQueue.isDirty:
let packet = pubChatQueue.toPacket(FlagReliable)
for id, client in pairs(clients):
discard client.peer.send(0.cuchar, packet)
pubChatQueue.flush()
handlers[HChat] = proc(client: PClient; buffer: PBuffer) =
var chat = readCsChat(buffer)
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:
dispmessage("<", client.alias, "> ", chat.text)
queuePub(client, chat)
handlers[HLogin] = proc(client: PClient; buffer: PBuffer) =
var info = readCsLogin(buffer)
if client.auth:
client.sendError "You are already logged in."
return
client.alias = info.alias
client.auth = true
var resp = newScLogin(client.id, client.alias, "sessionkeylulz")
client.send HLogin, resp
client.sendMessage "welcome"
dispMessage("Client logged in: ", client)
handlers[HFileTransfer] = server_utils.handleFilePartAck
handlers[HFileChallenge] = server_utils.handleFileChallengeResp
handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) =
var info = readCsZoneJoinReq(buffer)
dispmessage "Got zone join request"
client.startVerifyingFiles()
when isMainModule:
import parseopt, matchers, os, json
if enetInit() != 0:
quit "Could not initialize ENet"
var address: enet.TAddress
block:
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
port = uint16(jsonSettings["port"].num)
zoneFile = jsonSettings["settings"].str
dirServerInfo = jsonSettings["dirserver"]
address.host = EnetHostAny
address.port = port
var path = getAppDir()/../"data"/zoneFile
if not existsFile(path):
echo("Zone settings file does not exist: ../data/", zoneFile)
echo(path)
quit(1)
discard """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()
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 = newServer()
dirServer.addHandler HDsMsg, proc(serv: PServer; buffer: PBuffer) =
var m = readDsMsg(buffer)
dispMessage("<DirServer> ", m.msg)
dirServer.addHandler HZoneLogin, proc(serv: PServer; buffer: PBuffer) =
let loggedIn = readDsZoneLogin(buffer).status
if loggedIn:
#dirServerConnected = true
if dirServerInfo.kind == JArray:
var error: string
if not dirServer.connect(dirServerInfo[0].str, dirServerInfo[1].num.int16, error):
dispError("<DirServer> "&error)
server = enet.createHost(address, 32, 2, 0, 0)
if server == nil:
quit "Could not create the server!"
dispMessage("Listening on port ", address.port)
var
serverRunning = true
when appType == "gui":
var frameRate = newClock()
var pubChatDelay = newClock()
else:
var frameRate = epochTime()
var pubChatDelay = frameRate
while serverRunning:
when appType == "gui":
let dt = frameRate.restart.asMilliseconds().float / 1000.0
for event in window.filterEvents():
case event.kind
of sfml.EvtClosed:
window.close()
serverRunning = false
else:
discard
else:
let dt = epochTime() - frameRate ##is this right? probably not
frameRate = epochTime()
while server.hostService(event, 10) > 0:
case event.kind
of EvtConnect:
var client = newClient()
clients[client.id] = client
event.peer.data = addr client.id
client.peer = event.peer
dispMessage("New client connected ", client)
var
msg = "hello"
resp = createPacket(cstring(msg), msg.len + 1, FlagReliable)
if event.peer.send(0.cuchar, resp) < 0:
echo "FAILED"
else:
echo "Replied"
of EvtReceive:
let client = clients[cast[ptr int32](event.peer.data)[]]
var buf = newBuffer(event.packet)
let k = buf.readChar()
if handlers.hasKey(k):
handlers[k](client, buf)
else:
dispError("Unknown packet from ", client)
destroy(event.packet)
of EvtDisconnect:
var
id = cast[ptr int32](event.peer.data)[]
client = clients[id]
if client.isNil:
disperror("CLIENT IS NIL!")
dispmessage(event.peer.data.isNil)
else:
dispMessage(clients[id], " disconnected")
GCUnref(clients[id])
clients.del id
event.peer.data = nil
else:
discard
when appType == "gui":
fpsText.setString(ff(1.0/dt))
if pubChatDelay.getElapsedTime.asSeconds > 0.25:
pubChatDelay.restart()
flushPubChat()
else:
pubChatDelay -= dt
if frameRate - pubChatDelay > 0.25:
flushPubChat()
when appType == "gui":
window.clear(Black)
window.draw(GUI)
window.draw chatbox
window.draw mousePos
window.draw fpstext
window.display()
server.destroy()
enetDeinit()

View file

@ -0,0 +1,13 @@
import nake
nakeimports
const
ServerDefines = "-d:NoSFML --forceBuild"
task "server", "build the server":
if shell("nimrod", ServerDefines, "-r", "compile", "enet_server") != 0:
quit "Failed to build"
task "gui", "build the server GUI mode":
if shell("nimrod", "--app:gui", ServerDefines, "-r", "compile", "enet_server") != 0:
quit "Failed to build"

View file

@ -0,0 +1,9 @@
path = ".."
path = "../dependencies/sfml"
path = "../dependencies/enet"
path = "../dependencies/nake"
path = "../dependencies/genpacket"
path = "../lib"
define = "noChipmunk"
define = "noSFML"

View file

@ -0,0 +1,8 @@
{
"name": "Alpha Zone",
"desc": "Beta Testing",
"host": "localhost",
"port": 8024,
"settings": "alphazone.json",
"dirserver":["localhost",2049,"alphazone","skittles"]
}

View file

@ -0,0 +1,120 @@
import enet, sg_packets, estreams, md5, zlib_helpers, client_helpers, strutils,
idgen, sg_assets, tables, os
type
PClient* = ref object
id*: int32
auth*: bool
alias*: string
peer*: PPeer
FileChallengePair* = tuple[challenge: ScFileChallenge; file: TChecksumFile]
PFileChallengeSequence* = ref TFileChallengeSequence
TFileChallengeSequence = object
index: int #which file is active
transfer: ScFileTransfer
file: ptr FileChallengePair
var
clientID = newIdGen[int32]()
myAssets*: seq[FileChallengePair] = @[]
fileChallenges = initTable[int32, PFileChallengeSequence](32)
const FileChunkSize = 256
proc free(client: PClient) =
if client.id != 0:
fileChallenges.del client.id
clientID.del client.id
proc newClient*(): PClient =
new(result, free)
result.id = clientID.next()
result.alias = "billy"
proc `$`*(client: PClient): string =
result = "$1:$2".format(client.id, client.alias)
proc send*[T](client: PClient; pktType: char; pkt: var T) =
var buf = newBuffer(128)
buf.write pktType
buf.pack pkt
discard client.peer.send(0.cuchar, buf, flagReliable)
proc sendMessage*(client: PClient; txt: string) =
var m = newScChat(CSystem, text = txt)
client.send HChat, m
proc sendError*(client: PClient; error: string) =
var m = newScChat(CError, text = error)
client.send HChat, m
proc next*(challenge: PFileChallengeSequence, client: PClient)
proc sendChunk*(challenge: PFileChallengeSequence, client: PClient)
proc startVerifyingFiles*(client: PClient) =
var fcs: PFileChallengeSequence
new(fcs)
fcs.index = -1
fileChallenges[client.id] = fcs
next(fcs, client)
proc next*(challenge: PFileChallengeSequence, client: PClient) =
inc(challenge.index)
if challenge.index >= myAssets.len:
client.sendMessage "You are cleared to enter"
fileChallenges.del client.id
return
else:
echo myAssets.len, "assets"
challenge.file = addr myAssets[challenge.index]
client.send HFileChallenge, challenge.file.challenge # :rolleyes:
echo "sent challenge"
proc sendChunk*(challenge: PFileChallengeSequence, client: PClient) =
let size = min(FileChunkSize, challenge.transfer.fileSize - challenge.transfer.pos)
challenge.transfer.data.setLen size
copyMem(
addr challenge.transfer.data[0],
addr challenge.file.file.compressed[challenge.transfer.pos],
size)
client.send HFileTransfer, challenge.transfer
echo "chunk sent"
proc startSend*(challenge: PFileChallengeSequence, client: PClient) =
challenge.transfer.fileSize = challenge.file.file.compressed.len().int32
challenge.transfer.pos = 0
challenge.transfer.data = ""
challenge.transfer.data.setLen FileChunkSize
challenge.sendChunk(client)
echo "starting xfer"
## HFileTransfer
proc handleFilePartAck*(client: PClient; buffer: PBuffer) =
echo "got filepartack"
var
ftrans = readCsFilepartAck(buffer)
fcSeq = fileChallenges[client.id]
fcSeq.transfer.pos = ftrans.lastPos
fcSeq.sendChunk client
## HFileCHallenge
proc handleFileChallengeResp*(client: PClient; buffer: PBuffer) =
echo "got file challenge resp"
var
fcResp = readCsFileChallenge(buffer)
fcSeq = fileChallenges[client.id]
let index = $(fcSeq.index + 1) / $(myAssets.len)
if fcResp.needFile:
client.sendMessage "Sending file... "&index
fcSeq.startSend(client)
else:
var resp = newScChallengeResult(false)
if fcResp.checksum == fcSeq.file.file.sum: ##client is good
client.sendMessage "Checksum is good. "&index
resp.status = true
client.send HChallengeResult, resp
fcSeq.next(client)
else:
client.sendMessage "Checksum is bad, sending file... "&index
client.send HChallengeResult, resp
fcSeq.startSend(client)