session handling for players and hosts
This commit is contained in:
@@ -3,3 +3,4 @@
|
||||
/build
|
||||
erl_crash.dump
|
||||
config.toml
|
||||
/store
|
||||
|
||||
@@ -7,6 +7,7 @@ import gleam/erlang/process
|
||||
import gleam/float
|
||||
import gleam/http/request.{type Request}
|
||||
import gleam/http/response.{type Response}
|
||||
import gleam/int
|
||||
import gleam/io
|
||||
import gleam/json
|
||||
import gleam/list
|
||||
@@ -15,6 +16,10 @@ import gleam/otp/actor
|
||||
import gleam/result
|
||||
import gleam/string
|
||||
import mist.{type Connection, type ResponseData}
|
||||
import player
|
||||
import player_session
|
||||
import session
|
||||
import storail
|
||||
|
||||
pub fn handle_request(
|
||||
req: Request(Connection),
|
||||
@@ -122,8 +127,42 @@ fn handle_ws_message(state, conn, message) {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_user(
|
||||
ctx: web.Context,
|
||||
username: String,
|
||||
gamecode: String,
|
||||
) -> #(String, Int) {
|
||||
let secret = crypto.strong_random_bytes(16)
|
||||
let token =
|
||||
crypto.new_hasher(crypto.Sha512)
|
||||
|> crypto.hash_chunk(secret)
|
||||
|> crypto.hash_chunk(bit_array.from_string(username))
|
||||
|> crypto.hash_chunk(bit_array.from_string(gamecode))
|
||||
|> crypto.digest
|
||||
|
||||
let token_hash =
|
||||
crypto.new_hasher(crypto.Sha512)
|
||||
|> crypto.hash_chunk(token)
|
||||
|> crypto.digest
|
||||
|> bit_array.base64_encode(True)
|
||||
|
||||
let assert <<user_id:int-size(64)>> = crypto.strong_random_bytes(8)
|
||||
|
||||
let key = storail.key(ctx.player_sessions, int.to_base16(user_id))
|
||||
|
||||
let assert Ok(Nil) =
|
||||
storail.write(
|
||||
key,
|
||||
player_session.PlayerSession(id: user_id, token_hash:, username:),
|
||||
)
|
||||
|
||||
let token = bit_array.base64_encode(token, True)
|
||||
|
||||
#(token, user_id)
|
||||
}
|
||||
|
||||
fn handle_client_msg(
|
||||
_ctx: web.Context,
|
||||
ctx: web.Context,
|
||||
req: web.ClientRequest,
|
||||
) -> web.GameResponse {
|
||||
case req.msg {
|
||||
@@ -131,25 +170,30 @@ fn handle_client_msg(
|
||||
io.println("Got buzz in @ " <> float.to_string(time))
|
||||
web.AckBuzzer
|
||||
}
|
||||
web.Register(username, gamecode) -> {
|
||||
let secret = crypto.strong_random_bytes(16)
|
||||
let token =
|
||||
crypto.new_hasher(crypto.Sha512)
|
||||
|> crypto.hash_chunk(secret)
|
||||
|> crypto.hash_chunk(bit_array.from_string(username))
|
||||
|> crypto.hash_chunk(bit_array.from_string(gamecode))
|
||||
|> crypto.digest
|
||||
web.Register(gamecode, username) -> {
|
||||
let #(token, user_id) = register_user(ctx, username, gamecode)
|
||||
let key = storail.key(ctx.sessions, gamecode)
|
||||
let assert Ok(session) = storail.read(key)
|
||||
|
||||
let token_hash =
|
||||
crypto.new_hasher(crypto.Sha512)
|
||||
|> crypto.hash_chunk(token)
|
||||
|> crypto.digest
|
||||
let player = player.Player(name: username, id: user_id, score: 0)
|
||||
let session =
|
||||
session.Session(
|
||||
..session,
|
||||
players: list.append(session.players, [player]),
|
||||
)
|
||||
|
||||
io.println("New token: " <> bit_array.base64_encode(token_hash, True))
|
||||
|
||||
let token = bit_array.base64_encode(token, True)
|
||||
let assert Ok(Nil) = storail.write(key, session)
|
||||
|
||||
web.JoinResponse(username, token)
|
||||
}
|
||||
web.CreateRoom(gamecode, username) -> {
|
||||
let #(token, user_id) = register_user(ctx, username, gamecode)
|
||||
|
||||
let key = storail.key(ctx.sessions, gamecode)
|
||||
let session = session.Session(gamecode, user_id, [])
|
||||
let assert Ok(Nil) = storail.write(key, session)
|
||||
|
||||
web.HostResponse(username, token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,39 @@ import config
|
||||
import gleam/dynamic/decode
|
||||
import gleam/erlang/process
|
||||
import gleam/json
|
||||
import player_session
|
||||
import session
|
||||
import storail
|
||||
|
||||
pub type ClientMessage {
|
||||
BuzzIn(time: Float)
|
||||
Register(game_code: String, username: String)
|
||||
CreateRoom(game_code: String, username: String)
|
||||
}
|
||||
|
||||
fn client_message_decoder() -> decode.Decoder(ClientMessage) {
|
||||
pub fn encode_client_message(client_message: ClientMessage) -> json.Json {
|
||||
case client_message {
|
||||
BuzzIn(..) ->
|
||||
json.object([
|
||||
#("type", json.string("buzz_in")),
|
||||
#("time", json.float(client_message.time)),
|
||||
])
|
||||
Register(..) ->
|
||||
json.object([
|
||||
#("type", json.string("register")),
|
||||
#("game_code", json.string(client_message.game_code)),
|
||||
#("username", json.string(client_message.username)),
|
||||
])
|
||||
CreateRoom(..) ->
|
||||
json.object([
|
||||
#("type", json.string("create_room")),
|
||||
#("game_code", json.string(client_message.game_code)),
|
||||
#("username", json.string(client_message.username)),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_message_decoder() -> decode.Decoder(ClientMessage) {
|
||||
use variant <- decode.field("type", decode.string)
|
||||
case variant {
|
||||
"BuzzIn" -> {
|
||||
@@ -22,7 +46,12 @@ fn client_message_decoder() -> decode.Decoder(ClientMessage) {
|
||||
use username <- decode.field("username", decode.string)
|
||||
decode.success(Register(game_code:, username:))
|
||||
}
|
||||
_ -> decode.failure(BuzzIn(time: 0.0), "ClientMessage")
|
||||
"CreateRoom" -> {
|
||||
use game_code <- decode.field("game_code", decode.string)
|
||||
use username <- decode.field("username", decode.string)
|
||||
decode.success(CreateRoom(game_code:, username:))
|
||||
}
|
||||
_ -> decode.failure(BuzzIn(0.0), "ClientMessage")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +67,7 @@ pub fn client_request_decoder() -> decode.Decoder(ClientRequest) {
|
||||
|
||||
pub type GameResponse {
|
||||
JoinResponse(username: String, token: String)
|
||||
HostResponse(username: String, token: String)
|
||||
AckBuzzer
|
||||
ResetBuzzer
|
||||
UpdatePointTotal(score: Int)
|
||||
@@ -51,6 +81,12 @@ pub fn encode_game_response(game_response: GameResponse) -> json.Json {
|
||||
#("username", json.string(game_response.username)),
|
||||
#("token", json.string(game_response.token)),
|
||||
])
|
||||
HostResponse(..) ->
|
||||
json.object([
|
||||
#("type", json.string("HostResponse")),
|
||||
#("username", json.string(game_response.username)),
|
||||
#("token", json.string(game_response.token)),
|
||||
])
|
||||
AckBuzzer -> json.object([#("type", json.string("AckBuzzer"))])
|
||||
ResetBuzzer -> json.object([#("type", json.string("ResetBuzzer"))])
|
||||
UpdatePointTotal(..) ->
|
||||
@@ -65,6 +101,7 @@ pub type Context {
|
||||
Context(
|
||||
config: config.Config,
|
||||
sessions: storail.Collection(session.Session),
|
||||
player_sessions: storail.Collection(player_session.PlayerSession),
|
||||
selector: process.Selector(ClientRequest),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import gleam/erlang/process
|
||||
import gleam/result
|
||||
import glint
|
||||
import mist
|
||||
import player_session
|
||||
import session
|
||||
import storail
|
||||
|
||||
@@ -30,6 +31,17 @@ pub fn setup_session_collection(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn setup_player_session_collection(
|
||||
config: storail.Config,
|
||||
) -> storail.Collection(player_session.PlayerSession) {
|
||||
storail.Collection(
|
||||
name: "PlayerSessions",
|
||||
to_json: player_session.encode_player_session,
|
||||
decoder: player_session.player_session_decoder(),
|
||||
config:,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn run_server() -> glint.Command(Result(Nil, Error)) {
|
||||
use <- glint.command_help("Runs the backend of Play of the Game")
|
||||
use _, args, _ <- glint.command()
|
||||
@@ -52,6 +64,7 @@ pub fn run_server() -> glint.Command(Result(Nil, Error)) {
|
||||
web.Context(
|
||||
config: cfg,
|
||||
sessions: setup_session_collection(storail_cfg),
|
||||
player_sessions: setup_player_session_collection(storail_cfg),
|
||||
selector: process.new_selector(),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user