From: Ian Jackson Date: Sat, 11 Jul 2020 22:30:51 +0000 (+0100) Subject: much code motion etc. X-Git-Tag: otter-0.2.0~1385 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=53604514a32e5a157b5fd4a30220b3be94882db8;p=otter.git much code motion etc. --- diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 00000000..530ee58f --- /dev/null +++ b/src/api.rs @@ -0,0 +1,247 @@ + +use crate::imports::*; +use crate::http::*; + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPiece { + ctoken : String, + piece : VisiblePieceId, + gen : Generation, + cseq : ClientSequence, + op : O, +} +trait ApiPieceOp : Debug { + #[throws(GameError)] + fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, + lens: &dyn Lens /* used for LogEntry and PieceId but not Pos */) + -> (PieceUpdateOp<()>, Vec); +} + +trait Lens { + fn log_pri(&self, piece: PieceId, pc: &PieceRecord) + -> PieceRenderInstructions; + fn svg_pri(&self, piece: PieceId, pc: &PieceRecord, player: PlayerId) + -> PieceRenderInstructions; + fn massage_prep_piecestate(&self, ns : &mut PreparedPieceState); + fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, player: PlayerId) + -> PieceId; +} +struct TransparentLens { } +impl Lens for TransparentLens { + fn log_pri(&self, piece: PieceId, pc: &PieceRecord) + -> PieceRenderInstructions { + let kd : slotmap::KeyData = piece.into(); + let id = VisiblePieceId(kd); + PieceRenderInstructions { id, face : pc.face } + } + fn svg_pri(&self, piece: PieceId, pc: &PieceRecord, _player: PlayerId) + -> PieceRenderInstructions { + self.log_pri(piece, pc) + } + fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, _player: PlayerId) + -> PieceId { + let kd : slotmap::KeyData = vpiece.into(); + PieceId::from(kd) + } + fn massage_prep_piecestate(&self, _ns : &mut PreparedPieceState) { } +} + +#[throws(OE)] +fn api_piece_op(form : Json>) + -> impl response::Responder<'static> { +// thread::sleep(Duration::from_millis(2000)); + let iad = lookup_token(&form.ctoken)?; + let client = iad.ident; + let mut g = iad.g.lock()?; + let g = &mut *g; + let cl = &g.clients.byid(client)?; + // ^ can only fail if we raced + let player = cl.player; + let gs = &mut g.gs; + let _ = gs.players.byid(player)?; + let lens = TransparentLens { }; + let piece = lens.decode_visible_pieceid(form.piece, player); + + match (||{ + let pc = gs.pieces.byid_mut(piece)?; + + let q_gen = form.gen; + let u_gen = + if client == pc.lastclient { pc.gen_before_lastclient } + else { pc.gen }; + + eprintln!("Q_GEN={:?} U_GEN={:?}", u_gen, q_gen); + + if u_gen > q_gen { Err(GameError::Conflict)? } + if pc.held != None && pc.held != Some(player) { + Err(GameError::PieceHeld)? + }; + let (update, logents) = form.op.op(gs,player,piece,&lens)?; + Ok((update, logents)) + })() { + Err(err) => { + let err : GameError = err; + eprintln!("API {:?} => {:?}", &form, &err); + }, + Ok((update, logents)) => { + let pc = gs.pieces.byid_mut(piece).expect("piece deleted by op!"); + + gs.gen.increment(); + let gen = gs.gen; + if client != pc.lastclient { + pc.gen_before_lastclient = pc.gen; + pc.lastclient = client; + } + pc.gen = gen; + eprintln!("PC GEN_LC={:?} LC={:?}", pc.gen, pc.lastclient); + + let pri_for_all = lens.svg_pri(piece,pc,Default::default()); + + let update = update.map_new_state(|_|{ + let mut ns = pc.prep_piecestate(&pri_for_all); + lens.massage_prep_piecestate(&mut ns); + ns + }); + + let mut us = Vec::with_capacity(1 + logents.len()); + + us.push(PreparedUpdateEntry::Piece { + client, + sameclient_cseq : form.cseq, + piece : pri_for_all.id, + op : update, + }); + + for logentry in logents { + let logentry = Arc::new(logentry); + gs.log.push((gen, logentry.clone())); + us.push(PreparedUpdateEntry::Log(logentry)); + } + + let update = PreparedUpdate { gen, us, }; + let update = Arc::new(update); + eprintln!("UPDATE {:?}", &update); + + for (_tplayer, tplupdates) in &mut g.updates { + tplupdates.log.push_back(update.clone()); + tplupdates.cv.notify_all(); + } + eprintln!("API {:?} OK", &form); + } + } + "" +} + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPieceGrab { +} +#[post("/_/api/grab", format="json", data="
")] +#[throws(OE)] +fn api_grab(form : Json>) + -> impl response::Responder<'static> { + api_piece_op(form) +} +impl ApiPieceOp for ApiPieceGrab { + #[throws(GameError)] + fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, + lens: &dyn Lens) + -> (PieceUpdateOp<()>, Vec) { + let pl = gs.players.byid(player).unwrap(); + let pc = gs.pieces.byid_mut(piece).unwrap(); + + if pc.held.is_some() { Err(GameError::PieceHeld)? } + pc.held = Some(player); + + let update = PieceUpdateOp::Modify(()); + + let logent = LogEntry { + html : format!("{} grasped {}", + &htmlescape::encode_minimal(&pl.nick), + pc.describe_html(&lens.log_pri(piece, pc))), + }; + + (update, vec![logent]) + } +} + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPieceUngrab { +} +#[post("/_/api/ungrab", format="json", data="")] +#[throws(OE)] +fn api_ungrab(form : Json>) + -> impl response::Responder<'static> { + api_piece_op(form) +} +impl ApiPieceOp for ApiPieceUngrab { + #[throws(GameError)] + fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, + lens: &dyn Lens) + -> (PieceUpdateOp<()>, Vec) { + let pl = gs.players.byid(player).unwrap(); + let pc = gs.pieces.byid_mut(piece).unwrap(); + + if pc.held != Some(player) { Err(GameError::PieceHeld)? } + pc.held = None; + + let update = PieceUpdateOp::Modify(()); + + let logent = LogEntry { + html : format!("{} released {}", + &htmlescape::encode_minimal(&pl.nick), + pc.describe_html(&lens.log_pri(piece, pc))), + }; + + (update, vec![logent]) + } +} + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPieceRaise { + z : ZCoord, +} +#[post("/_/api/setz", format="json", data="")] +#[throws(OE)] +fn api_raise(form : Json>) + -> impl response::Responder<'static> { + api_piece_op(form) +} +impl ApiPieceOp for ApiPieceRaise { + #[throws(GameError)] + fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId, + _: &dyn Lens) + -> (PieceUpdateOp<()>, Vec) { + let pc = gs.pieces.byid_mut(piece).unwrap(); + pc.zlevel = ZLevel { z : self.z, zg : gs.gen }; + let update = PieceUpdateOp::SetZLevel(pc.zlevel); + (update, vec![]) + } +} + +#[derive(Debug,Serialize,Deserialize)] +struct ApiPieceMove (Pos); +#[post("/_/api/m", format="json", data="")] +#[throws(OE)] +fn api_move(form : Json>) -> impl response::Responder<'static> { + api_piece_op(form) +} +impl ApiPieceOp for ApiPieceMove { + #[throws(GameError)] + fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId, + _lens: &dyn Lens) + -> (PieceUpdateOp<()>, Vec) { + let pc = gs.pieces.byid_mut(piece).unwrap(); + + pc.pos = self.0; + let update = PieceUpdateOp::Move(self.0); + (update, vec![]) + } +} + +/* + api_grab, + api_ungrab, + api_raise, + api_move, +xxx +*/ diff --git a/src/bin/server.rs b/src/bin/server.rs index 95459c55..e0fb2967 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -5,9 +5,7 @@ #![feature(proc_macro_hygiene, decl_macro)] -use rocket::{get,post,routes}; -use rocket_contrib::json::Json; -//use rocket::{post}; +use rocket::{get,routes}; use game::imports::*; @@ -50,331 +48,6 @@ fn loading(ptoken : InstanceAccess) -> Template { Template::render("loading",&c) } -#[derive(Serialize,Debug)] -struct SessionRenderContext { - ctoken : String, - player : PlayerId, - gen : Generation, - uses : Vec, - defs : Vec<(VisiblePieceId,String)>, - nick : String, -} - -#[derive(Serialize,Debug)] -struct SessionPieceContext { - id: VisiblePieceId, - pos: Pos, - info: String, -} - -#[derive(Serialize,Debug)] -struct SessionPieceLoadJson<'r> { - held : &'r Option, - z : ZCoord, - zg : Generation, -} - -#[derive(Deserialize)] -struct SessionForm { - ptoken : String, -} -#[post("/_/session", format="json", data="")] -fn session(form : Json) -> Result { - // make session in this game, log a message to other players - let iad = lookup_token(&form.ptoken)?; - let player = iad.ident; - let c = { - let mut ig = iad.g.lock()?; - let ig = &mut *ig; - let pl = ig.gs.players.byid_mut(player)?; - let cl = Client { player }; - let client = ig.clients.insert(cl); - - let ciad = InstanceAccessDetails { - g : iad.g.clone(), - ident : client, - }; - let ctoken = record_token(ciad); - - let mut uses = vec![]; - let mut alldefs = vec![]; - - let mut pieces : Vec<_> = ig.gs.pieces.iter().collect(); - - pieces.sort_by_key(|(_,pr)| &pr.zlevel); - - for (gpid, pr) in pieces { - let pri = PieceRenderInstructions { - id : make_pieceid_visible(gpid), - face : pr.face, - }; - let defs = pr.make_defs(&pri); - alldefs.push((pri.id, defs)); - - let for_info = SessionPieceLoadJson { - held : &pr.held, - z : pr.zlevel.z, - zg : pr.zlevel.zg, - }; - - let for_piece = SessionPieceContext { - id: pri.id, - pos : pr.pos, - info : serde_json::to_string(&for_info)?, - }; - uses.push(for_piece); - } - - let src = SessionRenderContext { - ctoken : ctoken.0, - gen : ig.gs.gen, - player, - defs : alldefs, - uses, - nick : pl.nick.clone(), - }; - eprintln!("SRC {:?}", &src); - src - }; - Ok(Template::render("session",&c)) -} - -#[derive(Debug,Serialize,Deserialize)] -struct ApiPiece { - ctoken : String, - piece : VisiblePieceId, - gen : Generation, - cseq : ClientSequence, - op : O, -} -trait ApiPieceOp : Debug { - #[throws(GameError)] - fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, - lens: &dyn Lens /* used for LogEntry and PieceId but not Pos */) - -> (PieceUpdateOp<()>, Vec); -} - -trait Lens { - fn log_pri(&self, piece: PieceId, pc: &PieceRecord) - -> PieceRenderInstructions; - fn svg_pri(&self, piece: PieceId, pc: &PieceRecord, player: PlayerId) - -> PieceRenderInstructions; - fn massage_prep_piecestate(&self, ns : &mut PreparedPieceState); - fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, player: PlayerId) - -> PieceId; -} -struct TransparentLens { } -impl Lens for TransparentLens { - fn log_pri(&self, piece: PieceId, pc: &PieceRecord) - -> PieceRenderInstructions { - let kd : slotmap::KeyData = piece.into(); - let id = VisiblePieceId(kd); - PieceRenderInstructions { id, face : pc.face } - } - fn svg_pri(&self, piece: PieceId, pc: &PieceRecord, _player: PlayerId) - -> PieceRenderInstructions { - self.log_pri(piece, pc) - } - fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, _player: PlayerId) - -> PieceId { - let kd : slotmap::KeyData = vpiece.into(); - PieceId::from(kd) - } - fn massage_prep_piecestate(&self, _ns : &mut PreparedPieceState) { } -} - -#[throws(OE)] -fn api_piece_op(form : Json>) - -> impl response::Responder<'static> { -// thread::sleep(Duration::from_millis(2000)); - let iad = lookup_token(&form.ctoken)?; - let client = iad.ident; - let mut g = iad.g.lock()?; - let g = &mut *g; - let cl = &g.clients.byid(client)?; - // ^ can only fail if we raced - let player = cl.player; - let gs = &mut g.gs; - let _ = gs.players.byid(player)?; - let lens = TransparentLens { }; - let piece = lens.decode_visible_pieceid(form.piece, player); - - match (||{ - let pc = gs.pieces.byid_mut(piece)?; - - let q_gen = form.gen; - let u_gen = - if client == pc.lastclient { pc.gen_before_lastclient } - else { pc.gen }; - - eprintln!("Q_GEN={:?} U_GEN={:?}", u_gen, q_gen); - - if u_gen > q_gen { Err(GameError::Conflict)? } - if pc.held != None && pc.held != Some(player) { - Err(GameError::PieceHeld)? - }; - let (update, logents) = form.op.op(gs,player,piece,&lens)?; - Ok((update, logents)) - })() { - Err(err) => { - let err : GameError = err; - eprintln!("API {:?} => {:?}", &form, &err); - }, - Ok((update, logents)) => { - let pc = gs.pieces.byid_mut(piece).expect("piece deleted by op!"); - - gs.gen.increment(); - let gen = gs.gen; - if client != pc.lastclient { - pc.gen_before_lastclient = pc.gen; - pc.lastclient = client; - } - pc.gen = gen; - eprintln!("PC GEN_LC={:?} LC={:?}", pc.gen, pc.lastclient); - - let pri_for_all = lens.svg_pri(piece,pc,Default::default()); - - let update = update.map_new_state(|_|{ - let mut ns = pc.prep_piecestate(&pri_for_all); - lens.massage_prep_piecestate(&mut ns); - ns - }); - - let mut us = Vec::with_capacity(1 + logents.len()); - - us.push(PreparedUpdateEntry::Piece { - client, - sameclient_cseq : form.cseq, - piece : pri_for_all.id, - op : update, - }); - - for logentry in logents { - let logentry = Arc::new(logentry); - gs.log.push((gen, logentry.clone())); - us.push(PreparedUpdateEntry::Log(logentry)); - } - - let update = PreparedUpdate { gen, us, }; - let update = Arc::new(update); - eprintln!("UPDATE {:?}", &update); - - for (_tplayer, tplupdates) in &mut g.updates { - tplupdates.log.push_back(update.clone()); - tplupdates.cv.notify_all(); - } - eprintln!("API {:?} OK", &form); - } - } - "" -} - -#[derive(Debug,Serialize,Deserialize)] -struct ApiPieceGrab { -} -#[post("/_/api/grab", format="json", data="")] -#[throws(OE)] -fn api_grab(form : Json>) - -> impl response::Responder<'static> { - api_piece_op(form) -} -impl ApiPieceOp for ApiPieceGrab { - #[throws(GameError)] - fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, - lens: &dyn Lens) - -> (PieceUpdateOp<()>, Vec) { - let pl = gs.players.byid(player).unwrap(); - let pc = gs.pieces.byid_mut(piece).unwrap(); - - if pc.held.is_some() { Err(GameError::PieceHeld)? } - pc.held = Some(player); - - let update = PieceUpdateOp::Modify(()); - - let logent = LogEntry { - html : format!("{} grasped {}", - &htmlescape::encode_minimal(&pl.nick), - pc.describe_html(&lens.log_pri(piece, pc))), - }; - - (update, vec![logent]) - } -} - -#[derive(Debug,Serialize,Deserialize)] -struct ApiPieceUngrab { -} -#[post("/_/api/ungrab", format="json", data="")] -#[throws(OE)] -fn api_ungrab(form : Json>) - -> impl response::Responder<'static> { - api_piece_op(form) -} -impl ApiPieceOp for ApiPieceUngrab { - #[throws(GameError)] - fn op(&self, gs: &mut GameState, player: PlayerId, piece: PieceId, - lens: &dyn Lens) - -> (PieceUpdateOp<()>, Vec) { - let pl = gs.players.byid(player).unwrap(); - let pc = gs.pieces.byid_mut(piece).unwrap(); - - if pc.held != Some(player) { Err(GameError::PieceHeld)? } - pc.held = None; - - let update = PieceUpdateOp::Modify(()); - - let logent = LogEntry { - html : format!("{} released {}", - &htmlescape::encode_minimal(&pl.nick), - pc.describe_html(&lens.log_pri(piece, pc))), - }; - - (update, vec![logent]) - } -} - -#[derive(Debug,Serialize,Deserialize)] -struct ApiPieceRaise { - z : ZCoord, -} -#[post("/_/api/setz", format="json", data="")] -#[throws(OE)] -fn api_raise(form : Json>) - -> impl response::Responder<'static> { - api_piece_op(form) -} -impl ApiPieceOp for ApiPieceRaise { - #[throws(GameError)] - fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId, - _: &dyn Lens) - -> (PieceUpdateOp<()>, Vec) { - let pc = gs.pieces.byid_mut(piece).unwrap(); - pc.zlevel = ZLevel { z : self.z, zg : gs.gen }; - let update = PieceUpdateOp::SetZLevel(pc.zlevel); - (update, vec![]) - } -} - -#[derive(Debug,Serialize,Deserialize)] -struct ApiPieceMove (Pos); -#[post("/_/api/m", format="json", data="")] -#[throws(OE)] -fn api_move(form : Json>) -> impl response::Responder<'static> { - api_piece_op(form) -} -impl ApiPieceOp for ApiPieceMove { - #[throws(GameError)] - fn op(&self, gs: &mut GameState, _: PlayerId, piece: PieceId, - _lens: &dyn Lens) - -> (PieceUpdateOp<()>, Vec) { - let pc = gs.pieces.byid_mut(piece).unwrap(); - - pc.pos = self.0; - let update = PieceUpdateOp::Move(self.0); - (update, vec![]) - } -} - #[get("/_/updates//")] #[throws(OE)] fn updates(ctoken : InstanceAccess, gen: u64) @@ -403,19 +76,15 @@ fn main() { .enable(Frame::Deny) .enable(Referrer::NoReferrer); - rocket::ignite() + let mut r = rocket::ignite() .attach(helmet) .attach(Template::fairing()) .mount("/", routes![ index, loading, - session, resource, updates, - api_grab, - api_ungrab, - api_raise, - api_move, - ]) - .launch(); + ]); + game::session::mount(&mut r); + r.launch(); } diff --git a/src/gamestate.rs b/src/gamestate.rs index 29c85bdf..3478217c 100644 --- a/src/gamestate.rs +++ b/src/gamestate.rs @@ -98,15 +98,6 @@ pub struct PieceRecord { pub gen_before_lastclient : Generation, } -#[derive(Debug,Serialize)] -pub struct PreparedPieceState { - pub pos : Pos, - pub svg : String, - pub held : Option, - pub z : ZCoord, - pub zg : Generation, -} - impl PieceRecord { pub fn make_defs(&self, pri : &PieceRenderInstructions) -> String { let pr = self; diff --git a/src/global.rs b/src/global.rs index 39c45871..123d313c 100644 --- a/src/global.rs +++ b/src/global.rs @@ -18,98 +18,6 @@ pub struct Client { pub player : PlayerId, } -#[derive(Debug)] -pub struct PreparedUpdate { - pub gen : Generation, - pub us : Vec, -} -#[derive(Debug)] -pub enum PreparedUpdateEntry { - Piece { - client : ClientId, - sameclient_cseq : ClientSequence, - piece : VisiblePieceId, - op : PieceUpdateOp, - }, - Log (Arc), -} -impl PreparedUpdateEntry { - pub fn json_len(&self) -> usize { - use PreparedUpdateEntry::*; - match self { - Piece { ref op, .. } => { - 50 + - op.new_state().map(|x| x.svg.len()).unwrap_or(0) - }, - Log(logent) => { - logent.html.as_bytes().len() * 3 - } - } - } -} -impl PreparedUpdate { - pub fn json_len(&self) -> usize { - self.us.iter().map(|u| 20 + u.json_len()).sum() - } -} - -#[derive(Debug,Serialize)] -pub enum PieceUpdateOp { - Delete(), - Insert(NS), - Modify(NS), - Move(Pos), - SetZLevel(ZLevel), -} -impl PieceUpdateOp { - pub fn new_state(&self) -> Option<&NS> { - use PieceUpdateOp::*; - match self { - Delete() => None, - Insert(ns) => Some(ns), - Modify(ns) => Some(ns), - Move(_) => None, - SetZLevel(_) => None, - } - } - pub fn map_new_state NS2>(self, f:F) - -> PieceUpdateOp { - use PieceUpdateOp::*; - match self { - Delete() => Delete(), - Insert(ns) => Insert(f(ns)), - Modify(ns) => Modify(f(ns)), - Move(pos) => Move(pos), - SetZLevel(zl) => SetZLevel(zl), - } - } - pub fn new_z_generation(&self) -> Option { - use PieceUpdateOp::*; - match self { - Delete() => None, - Insert(_) => None, - Modify(_) => None, - Move(_) => None, - SetZLevel(ZLevel{zg,..}) => Some(*zg), - } - } -} - -#[derive(Debug)] -pub struct PlayerUpdates { - pub log : StableIndexVecDeque,sse::UpdateId>, - pub cv : Arc, -} - -const RECENT_BUFFER : usize = 50; - -impl Default for PlayerUpdates { - fn default() -> PlayerUpdates { PlayerUpdates { - log : StableIndexVecDeque::with_capacity(RECENT_BUFFER), - cv : Default::default(), - } } -} - #[derive(Debug)] pub struct Instance { pub gs : GameState, diff --git a/src/http.rs b/src/http.rs index 50f4b5c3..ec31f2a2 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,6 +1,9 @@ -use rocket::request::Request; -use rocket::response::{Response,Responder}; +pub use rocket::request::Request; +pub use rocket::response::{Response,Responder}; +pub use rocket::post; +pub use rocket_contrib::json::Json; +pub use rocket::http::Status; use crate::imports::*; diff --git a/src/imports.rs b/src/imports.rs index 80894365..a1fd14ab 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -28,8 +28,8 @@ pub use serde::Serializer; pub use rocket_contrib::helmet::*; pub use rocket_contrib::templates::Template; -pub use rocket::State; -pub use rocket::http::{Status,RawStr,ContentType}; +pub use rocket::{State,Rocket}; +pub use rocket::http::{RawStr,ContentType}; pub use rocket::request::{FromParam,FromRequest,FromFormValue,LenientForm}; pub use rocket::response::NamedFile; pub use rocket::response; diff --git a/src/lib.rs b/src/lib.rs index 3c365e95..13010239 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ +#![feature(proc_macro_hygiene, decl_macro)] + pub mod imports; pub mod global; pub mod pieces; @@ -8,3 +10,5 @@ pub mod updates; pub mod sse; pub mod error; pub mod http; +pub mod session; +pub mod api; diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 00000000..7b37d38a --- /dev/null +++ b/src/session.rs @@ -0,0 +1,96 @@ + +use crate::imports::*; +use crate::http::*; + +#[derive(Serialize,Debug)] +struct SessionRenderContext { + ctoken : String, + player : PlayerId, + gen : Generation, + uses : Vec, + defs : Vec<(VisiblePieceId,String)>, + nick : String, +} + +#[derive(Serialize,Debug)] +struct SessionPieceContext { + id: VisiblePieceId, + pos: Pos, + info: String, +} + +#[derive(Serialize,Debug)] +struct SessionPieceLoadJson<'r> { + held : &'r Option, + z : ZCoord, + zg : Generation, +} + +#[derive(Deserialize)] +struct SessionForm { + ptoken : String, +} +#[post("/_/session", format="json", data="")] +fn session(form : Json) -> Result { + // make session in this game, log a message to other players + let iad = lookup_token(&form.ptoken)?; + let player = iad.ident; + let c = { + let mut ig = iad.g.lock()?; + let ig = &mut *ig; + let pl = ig.gs.players.byid_mut(player)?; + let cl = Client { player }; + let client = ig.clients.insert(cl); + + let ciad = InstanceAccessDetails { + g : iad.g.clone(), + ident : client, + }; + let ctoken = record_token(ciad); + + let mut uses = vec![]; + let mut alldefs = vec![]; + + let mut pieces : Vec<_> = ig.gs.pieces.iter().collect(); + + pieces.sort_by_key(|(_,pr)| &pr.zlevel); + + for (gpid, pr) in pieces { + let pri = PieceRenderInstructions { + id : make_pieceid_visible(gpid), + face : pr.face, + }; + let defs = pr.make_defs(&pri); + alldefs.push((pri.id, defs)); + + let for_info = SessionPieceLoadJson { + held : &pr.held, + z : pr.zlevel.z, + zg : pr.zlevel.zg, + }; + + let for_piece = SessionPieceContext { + id: pri.id, + pos : pr.pos, + info : serde_json::to_string(&for_info)?, + }; + uses.push(for_piece); + } + + let src = SessionRenderContext { + ctoken : ctoken.0, + gen : ig.gs.gen, + player, + defs : alldefs, + uses, + nick : pl.nick.clone(), + }; + eprintln!("SRC {:?}", &src); + src + }; + Ok(Template::render("session",&c)) +} + +pub fn mount(_rocket_instance: &mut Rocket) { + //rocket_instance.mount(&session); xxx +} diff --git a/src/sse.rs b/src/sse.rs index fe2b43b3..0b566389 100644 --- a/src/sse.rs +++ b/src/sse.rs @@ -148,72 +148,6 @@ impl Read for UpdateReader { } } - /* - loop { - e - let send_from = (||{ - let l = self.updates.len(); - let last_probe = match updates.last() { - None => return None, - Some(&now) if self.last_sent > now.gen => return l+1, - _ => l, - }; - let (lo, hi /* half-open */) = loop { - let depth = l - last_probe; - depth *= 2; - if depth > l { break (0, last_probe) } - let probe = l - depth; - let here = updates[probe]; - if here.gen < l - - if let Some(&now) = { - if { return None } - } - let probe = inst.updates.len() - 1; - let (lo, hi) = loop { - if search == 0 { break } - search -= 1; - tu = inst.updates[search]; - if - - let lo = 0; - - }; - loop { - implement this! - } - for (tclient, tcl) in &mut g.clients { - if tclient == client { - tcl.transmit_update(&Update { - gen, - u : UpdatePayload::ClientSequence(piece, form.s), - }); - } else { - tcl.transmit_update(&update); - } - } - */ -/* - - thread::sleep(Duration::from_millis(500)); - let message = XUpdate::TestCounter { value : self.next }; - let data = serde_json::to_string(&message)?; - let data = format!("data: {}\n\n", &data); - // eprintln!("want to return into &[;{}] {:?}", buf.len(), &data); - self.next += 1; - buf[0..data.len()].copy_from_slice(data.as_bytes()); - Ok(buf.len()) - } -}*/ - -/* -#[derive(Deserialize)] -struct APIForm { - t : String, - c : ClientId, -} - */ - #[derive(Debug)] pub struct DebugReader(pub T); diff --git a/src/updates.rs b/src/updates.rs index 0d1b6d8a..20a94a2b 100644 --- a/src/updates.rs +++ b/src/updates.rs @@ -1,17 +1,109 @@ +// update messages from server to client + use crate::imports::*; #[derive(Debug,Copy,Clone,Eq,PartialEq,Deserialize,Serialize)] #[serde(transparent)] pub struct ClientSequence(u64); -/* -#[derive(Debug,Serialize)] -pub struct Update { + +#[derive(Debug)] +pub struct PreparedUpdate { pub gen : Generation, - pub u : UpdatePayload, + pub us : Vec, +} +#[derive(Debug)] +pub enum PreparedUpdateEntry { + Piece { + client : ClientId, + sameclient_cseq : ClientSequence, + piece : VisiblePieceId, + op : PieceUpdateOp, + }, + Log (Arc), +} +impl PreparedUpdateEntry { + pub fn json_len(&self) -> usize { + use PreparedUpdateEntry::*; + match self { + Piece { ref op, .. } => { + 50 + + op.new_state().map(|x| x.svg.len()).unwrap_or(0) + }, + Log(logent) => { + logent.html.as_bytes().len() * 3 + } + } + } +} +impl PreparedUpdate { + pub fn json_len(&self) -> usize { + self.us.iter().map(|u| 20 + u.json_len()).sum() + } +} + +#[derive(Debug,Serialize)] +pub enum PieceUpdateOp { + Delete(), + Insert(NS), + Modify(NS), + Move(Pos), + SetZLevel(ZLevel), +} +impl PieceUpdateOp { + pub fn new_state(&self) -> Option<&NS> { + use PieceUpdateOp::*; + match self { + Delete() => None, + Insert(ns) => Some(ns), + Modify(ns) => Some(ns), + Move(_) => None, + SetZLevel(_) => None, + } + } + pub fn map_new_state NS2>(self, f:F) + -> PieceUpdateOp { + use PieceUpdateOp::*; + match self { + Delete() => Delete(), + Insert(ns) => Insert(f(ns)), + Modify(ns) => Modify(f(ns)), + Move(pos) => Move(pos), + SetZLevel(zl) => SetZLevel(zl), + } + } + pub fn new_z_generation(&self) -> Option { + use PieceUpdateOp::*; + match self { + Delete() => None, + Insert(_) => None, + Modify(_) => None, + Move(_) => None, + SetZLevel(ZLevel{zg,..}) => Some(*zg), + } + } +} + +#[derive(Debug)] +pub struct PlayerUpdates { + pub log : StableIndexVecDeque,sse::UpdateId>, + pub cv : Arc, +} + +const RECENT_BUFFER : usize = 50; + +impl Default for PlayerUpdates { + fn default() -> PlayerUpdates { PlayerUpdates { + log : StableIndexVecDeque::with_capacity(RECENT_BUFFER), + cv : Default::default(), + } } } #[derive(Debug,Serialize)] -pub enum PieceUpdate { +pub struct PreparedPieceState { + pub pos : Pos, + pub svg : String, + pub held : Option, + pub z : ZCoord, + pub zg : Generation, } -*/