mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-18 17:08:32 +00:00
202 lines
6.3 KiB
Nim
202 lines
6.3 KiB
Nim
## 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"
|
|
|