--- /dev/null
+
+use crate::imports::*;
+use crate::http::*;
+
+#[derive(Debug,Serialize,Deserialize)]
+struct ApiPiece<O : ApiPieceOp> {
+ 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<LogEntry>);
+}
+
+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<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
+ -> 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="<form>")]
+#[throws(OE)]
+fn api_grab(form : Json<ApiPiece<ApiPieceGrab>>)
+ -> 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<LogEntry>) {
+ 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="<form>")]
+#[throws(OE)]
+fn api_ungrab(form : Json<ApiPiece<ApiPieceUngrab>>)
+ -> 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<LogEntry>) {
+ 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="<form>")]
+#[throws(OE)]
+fn api_raise(form : Json<ApiPiece<ApiPieceRaise>>)
+ -> 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<LogEntry>) {
+ 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="<form>")]
+#[throws(OE)]
+fn api_move(form : Json<ApiPiece<ApiPieceMove>>) -> 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<LogEntry>) {
+ 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
+*/
#![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::*;
Template::render("loading",&c)
}
-#[derive(Serialize,Debug)]
-struct SessionRenderContext {
- ctoken : String,
- player : PlayerId,
- gen : Generation,
- uses : Vec<SessionPieceContext>,
- 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<PlayerId>,
- z : ZCoord,
- zg : Generation,
-}
-
-#[derive(Deserialize)]
-struct SessionForm {
- ptoken : String,
-}
-#[post("/_/session", format="json", data="<form>")]
-fn session(form : Json<SessionForm>) -> Result<Template,OE> {
- // 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<O : ApiPieceOp> {
- 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<LogEntry>);
-}
-
-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<O: ApiPieceOp>(form : Json<ApiPiece<O>>)
- -> 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="<form>")]
-#[throws(OE)]
-fn api_grab(form : Json<ApiPiece<ApiPieceGrab>>)
- -> 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<LogEntry>) {
- 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="<form>")]
-#[throws(OE)]
-fn api_ungrab(form : Json<ApiPiece<ApiPieceUngrab>>)
- -> 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<LogEntry>) {
- 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="<form>")]
-#[throws(OE)]
-fn api_raise(form : Json<ApiPiece<ApiPieceRaise>>)
- -> 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<LogEntry>) {
- 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="<form>")]
-#[throws(OE)]
-fn api_move(form : Json<ApiPiece<ApiPieceMove>>) -> 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<LogEntry>) {
- let pc = gs.pieces.byid_mut(piece).unwrap();
-
- pc.pos = self.0;
- let update = PieceUpdateOp::Move(self.0);
- (update, vec![])
- }
-}
-
#[get("/_/updates/<ctoken>/<gen>")]
#[throws(OE)]
fn updates(ctoken : InstanceAccess<ClientId>, gen: u64)
.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();
}
pub gen_before_lastclient : Generation,
}
-#[derive(Debug,Serialize)]
-pub struct PreparedPieceState {
- pub pos : Pos,
- pub svg : String,
- pub held : Option<PlayerId>,
- pub z : ZCoord,
- pub zg : Generation,
-}
-
impl PieceRecord {
pub fn make_defs(&self, pri : &PieceRenderInstructions) -> String {
let pr = self;
pub player : PlayerId,
}
-#[derive(Debug)]
-pub struct PreparedUpdate {
- pub gen : Generation,
- pub us : Vec<PreparedUpdateEntry>,
-}
-#[derive(Debug)]
-pub enum PreparedUpdateEntry {
- Piece {
- client : ClientId,
- sameclient_cseq : ClientSequence,
- piece : VisiblePieceId,
- op : PieceUpdateOp<PreparedPieceState>,
- },
- Log (Arc<LogEntry>),
-}
-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<NS> {
- Delete(),
- Insert(NS),
- Modify(NS),
- Move(Pos),
- SetZLevel(ZLevel),
-}
-impl<NS> PieceUpdateOp<NS> {
- 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,F: FnOnce(NS) -> NS2>(self, f:F)
- -> PieceUpdateOp<NS2> {
- 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<Generation> {
- 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<Arc<PreparedUpdate>,sse::UpdateId>,
- pub cv : Arc<Condvar>,
-}
-
-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,
-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::*;
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;
+#![feature(proc_macro_hygiene, decl_macro)]
+
pub mod imports;
pub mod global;
pub mod pieces;
pub mod sse;
pub mod error;
pub mod http;
+pub mod session;
+pub mod api;
--- /dev/null
+
+use crate::imports::*;
+use crate::http::*;
+
+#[derive(Serialize,Debug)]
+struct SessionRenderContext {
+ ctoken : String,
+ player : PlayerId,
+ gen : Generation,
+ uses : Vec<SessionPieceContext>,
+ 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<PlayerId>,
+ z : ZCoord,
+ zg : Generation,
+}
+
+#[derive(Deserialize)]
+struct SessionForm {
+ ptoken : String,
+}
+#[post("/_/session", format="json", data="<form>")]
+fn session(form : Json<SessionForm>) -> Result<Template,OE> {
+ // 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
+}
}
}
- /*
- 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<T : Read>(pub T);
+// 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<PreparedUpdateEntry>,
+}
+#[derive(Debug)]
+pub enum PreparedUpdateEntry {
+ Piece {
+ client : ClientId,
+ sameclient_cseq : ClientSequence,
+ piece : VisiblePieceId,
+ op : PieceUpdateOp<PreparedPieceState>,
+ },
+ Log (Arc<LogEntry>),
+}
+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<NS> {
+ Delete(),
+ Insert(NS),
+ Modify(NS),
+ Move(Pos),
+ SetZLevel(ZLevel),
+}
+impl<NS> PieceUpdateOp<NS> {
+ 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,F: FnOnce(NS) -> NS2>(self, f:F)
+ -> PieceUpdateOp<NS2> {
+ 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<Generation> {
+ 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<Arc<PreparedUpdate>,sse::UpdateId>,
+ pub cv : Arc<Condvar>,
+}
+
+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<PlayerId>,
+ pub z : ZCoord,
+ pub zg : Generation,
}
-*/