// thread::sleep(Duration::from_millis(2000));
let iad = lookup_token(&form.ctoken)?;
let client = iad.ident;
- let mut g = iad.g.lock()?;
+ let mut g = iad.gref.lock()?;
let g = &mut *g;
let cl = &g.clients.byid(client)?;
// ^ can only fail if we raced
#![allow(dead_code)]
+pub use crate::from_instance_lock_error;
+
use crate::imports::*;
//use std::os::unix::prelude;
use MgmtError::*;
type ME = MgmtError;
+from_instance_lock_error!{MgmtError}
#[throws(CSE)]
fn decode_and_process(cs: &mut CommandStream, s: &str) {
scoped_name : name,
};
- let ami = Instance::new(name, gs)?;
- let g = ami.lock();
+ let gref = Instance::new(name, gs)?;
+ let mut ig = gref.lock()?;
- execute_for_game(cs, &ami, subcommands, MgmtGameUpdateMode::Bulk)
+ execute_for_game(cs, &mut ig, subcommands, MgmtGameUpdateMode::Bulk)
.map_err(|e|{
- Instance::destroy(ami);
+ Instance::destroy(ig);
e
})?;
}
}
+#[derive(Debug,Default)]
struct UpdateHandlerBulk {
pieces : SecondarySlotMap<PieceId, PieceUpdateOp<()>>,
}
+#[derive(Debug)]
enum UpdateHandler {
Bulk(UpdateHandlerBulk),
Online,
}
impl UpdateHandler {
+ fn from_how(how: MgmtGameUpdateMode) -> Self {
+ use UpdateHandler::*;
+ match how {
+ MgmtGameUpdateMode::Bulk => Bulk(Default::default()),
+ MgmtGameUpdateMode::Online => Online,
+ }
+ }
+
#[throws(SVGProcessingError)]
fn accumulate(&mut self, g: &mut Instance,
upieces: Vec<(PieceId,PieceUpdateOp<()>)>,
for (upiece, uuop) in upieces {
use PieceUpdateOp::*;
let ne = match (bulk.pieces.get(upiece), uuop) {
- ( None , e ) => e,
- ( Some( Insert(()) ) , Some( Delete(()) ) ) => None,
- ( Some( Insert(()) ) , _ ) => Some( Insert(()) ),
- ( Some( Delete(()) ) , _ ) => Some( Modify(()) ),
- ( _ , _ ) => Some( Modify(()) ),
+ ( None , e ) => Some( e ),
+ ( Some( Insert(()) ) , Delete() ) => None,
+ ( Some( Insert(()) ) , _ ) => Some( Insert(()) ),
+ ( Some( Delete( ) ) , _ ) => Some( Modify(()) ),
+ ( _ , _ ) => Some( Modify(()) ),
};
match ne {
- Some(ne) => bulk.pieces[upiece] = ne,
- None => bulk.pieces.remove(upiece),
+ Some(ne) => { bulk.pieces[upiece] = ne; },
+ None => { bulk.pieces.remove(upiece); },
};
}
let _logs = ulogs;
},
Online => {
let estimate = upieces.len() + ulogs.len();
- let buf = PrepareUpdatesBuffer::new(g, None, Some(estimate));
+ let mut buf = PrepareUpdatesBuffer::new(g, None, Some(estimate));
for (upiece, uuop) in upieces {
let lens = TransparentLens { };
buf.piece_update(upiece, uuop, &lens)?;
}
#[throws(SVGProcessingError)]
- fn complete(&mut self, g: &mut Instance) {
+ fn complete(self, _cs: &CommandStream, g: &mut InstanceGuard) {
use UpdateHandler::*;
match self {
Bulk(bulk) => {
- let buf = PrepareUpdatesBuffer::new(g, None, None);
- for (upiece, uuop) in self.pieces {
+ let mut buf = PrepareUpdatesBuffer::new(g, None, None);
+ for (upiece, uuop) in bulk.pieces {
let lens = TransparentLens { };
buf.piece_update(upiece, uuop, &lens)?;
}
- buf.log_updates(LogEntry {
+ buf.log_updates(vec![LogEntry {
html: "The facilitator (re)configured the game".to_owned(),
- })?;
+ // xxx use cs.desc
+ }])?;
},
Online => { },
}
}
#[throws(ME)]
-fn execute_for_game(cs: &CommandStream, amu: &InstanceRef,
- subcommands: Vec<MgmtGameUpdate>,
- how: MgmtGameUpdateMode) {
- let g = amu.lock()?;
- let mut subcommands = subcommands.drain.zip(0..);
+fn execute_for_game(cs: &CommandStream, ig: &mut InstanceGuard,
+ mut subcommands: Vec<MgmtGameUpdate>,
+ how: MgmtGameUpdateMode) -> MgmtResponse {
+ let mut subcommands = subcommands.drain(0..).zip(0..);
let mut uh = UpdateHandler::from_how(how);
let response = 'subcommands: loop {
Some(r) => r,
};
- let (upieces, ulogs) = match execute_game_update(&mut g.gs, subcommand) {
+ let (upieces, ulogs) = match execute_game_update(&mut ig.gs, subcommand) {
Err(e) => break 'subcommands ErrorAfter(index, e),
Ok(r) => r,
};
- uh.accumulate(upieces, ulogs)?;
+ uh.accumulate(ig, upieces, ulogs)?;
};
- uh.complete()?;
+ uh.complete(cs,ig)?;
+ response
}
#[throws(ME)]
-fn execute_game_update(gs: &mut GameState, update: MgmtGameUpdate)
+fn execute_game_update(_gs: &mut GameState, update: MgmtGameUpdate)
-> (Vec<(PieceId,PieceUpdateOp<()>)>, Vec<LogEntry>) {
+ use MgmtGameUpdate::*;
match update {
+ Noop { } => (vec![], vec![]),
}
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtCommand {
- Noop { },
+ Noop(),
SetScope(ManagementScope),
CreateGame(String, Vec<MgmtGameUpdate>),
// AddPiece(Box<dyn PieceSpec>),
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtGameUpdate {
- Noop { },
+ Noop(),
}
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtResponse {
- Fine { },
+ Fine(),
Error(MgmtError),
ErrorAfter(usize, MgmtError),
}
Bulk,
}
-#[derive(Debug,Error)]
+#[derive(Debug,Error,Serialize,Deserialize)]
pub enum MgmtError {
ParseFailed(String),
AuthorisationError,
NoScope,
AlreadyExists,
- XXXU(&'static str),
+ GameBeingDestroyed,
+ GameCorrupted,
+ SVGProcessingFailed(#[from] SVGProcessingError),
}
display_as_debug!{MgmtError}
use crate::imports::*;
-use std::sync::PoisonError;
-
#[derive(Error,Debug)]
#[error("operation error {:?}",self)]
pub enum GameError {
#[error("Server MessagePack decoding error (game load failed) {0:?}")]
ServerMessagePackDecodeFail(#[from] rmp_serde::decode::Error),
}
+from_instance_lock_error!{OnlineError}
pub type StartupError = anyhow::Error;
pub use OnlineError::{NoClient,NoPlayer};
-use OnlineError::*;
-
-impl<X> From<PoisonError<X>> for OnlineError {
- fn from(_: PoisonError<X>) -> OnlineError { GameCorrupted }
+#[derive(Error,Debug)]
+pub enum InstanceLockError {
+ GameCorrupted,
+ GameBeingDestroyed,
+}
+#[macro_export]
+macro_rules! from_instance_lock_error {
+ ($into:ident) => {
+ impl From<InstanceLockError> for $into {
+ fn from(e: InstanceLockError) -> $into {
+ use InstanceLockError::*;
+ match e {
+ GameCorrupted => $into::GameCorrupted,
+ GameBeingDestroyed => $into::GameBeingDestroyed,
+ }
+ }
+ }
+ }
}
pub trait ById {
use crate::imports::*;
use lazy_static::lazy_static;
+use std::sync::PoisonError;
+
#[allow(dead_code)]
const SAVE_DIRECTORY : &str = "save";
pub scope: ManagementScope,
pub scoped_name: String,
}
-pub type InstanceRef = Arc<Mutex<Instance>>;
+
+#[derive(Debug,Clone)]
+pub struct InstanceRef (Arc<Mutex<InstanceContainer>>);
+
+#[derive(Debug)]
+pub struct InstanceContainer {
+ live : bool,
+ g : Instance,
+}
#[derive(Debug)]
pub struct Instance {
- pub live : bool,
pub name : Arc<InstanceName>,
pub gs : GameState,
pub clients : DenseSlotMap<ClientId,Client>,
#[derive(Debug)]
pub struct InstanceGuard<'g> {
- ig : MutexGuard<'g,Instance>,
- amu : Arc<Mutex<Instance>>,
+ pub c : MutexGuard<'g,InstanceContainer>,
+ pub gref : InstanceRef,
}
#[derive(Default)]
tokens_players : Vec<(RawTokenStr, PlayerId)>,
}
+display_as_debug!{InstanceLockError}
+impl<X> From<PoisonError<X>> for InstanceLockError {
+ fn from(_: PoisonError<X>) -> Self { Self::GameCorrupted }
+}
+
// ---------- API ----------
#[derive(Clone,Debug)]
pub struct InstanceAccessDetails<Id> {
- pub g : Arc<Mutex<Instance>>,
+ pub gref : InstanceRef,
pub ident : Id,
}
// ---------- Player and instance API ----------
+impl InstanceRef {
+ #[throws(InstanceLockError)]
+ pub fn lock<'g>(&'g self) -> InstanceGuard<'g> {
+ let c = self.0.lock()?;
+ if !c.live { throw!(InstanceLockError::GameBeingDestroyed) }
+ InstanceGuard { c, gref: self.clone() }
+ }
+}
+
impl Instance {
/// Returns `None` if a game with this name already exists
#[throws(MgmtError)]
pub fn new(name: InstanceName, gs: GameState) -> InstanceRef {
let name = Arc::new(name);
- let inst = Instance {
- live : true,
+ let g = Instance {
name : name.clone(),
gs,
clients : Default::default(),
tokens_clients : Default::default(),
};
+ let cont = InstanceContainer {
+ live : true,
+ g,
+ };
+
+ let gref = InstanceRef(Arc::new(Mutex::new(cont)));
+
let mut games = GLOBAL.games.write().unwrap();
let entry = games.entry(name);
Occupied(_) => throw!(MgmtError::AlreadyExists),
};
- let ami = Arc::new(Mutex::new(inst));
- entry.insert(ami.clone());
- ami
- }
+ entry.insert(gref.clone());
- #[throws(OE)]
- pub fn lock<'g>(amu : &'g Arc<Mutex<Instance>>) -> InstanceGuard<'g> {
- let ig = amu.lock()?;
- if !ig.live { throw!(OnlineError::GameBeingDestroyed) }
- InstanceGuard { ig, amu: amu.clone() }
+ // xxx save, but first release the GLOBAL.games lock
+
+ gref
}
- pub fn destroy(g: InstanceGuard) {
- g.live = false;
+ pub fn destroy(mut g: InstanceGuard) {
+ g.c.live = false;
// remove the files
GLOBAL.games.write().unwrap().remove(&g.name);
InstanceGuard::forget_all_tokens(&mut g.tokens_players);
impl Deref for InstanceGuard<'_> {
type Target = Instance;
- fn deref(&self) -> &Instance { &self.ig }
+ fn deref(&self) -> &Instance { &self.c.g }
}
impl DerefMut for InstanceGuard<'_> {
- fn deref_mut(&mut self) -> &mut Instance { &mut self.ig }
+ fn deref_mut(&mut self) -> &mut Instance { &mut self.c.g }
}
impl InstanceGuard<'_> {
#[throws(OE)]
pub fn player_new(&mut self, newplayer: PlayerState) -> PlayerId {
- let player = self.ig.gs.players.insert(newplayer);
- self.ig.updates.insert(player, Default::default());
+ let player = self.c.g.gs.players.insert(newplayer);
+ self.c.g.updates.insert(player, Default::default());
self.save_game_now()?;
player
}
#[throws(OE)]
pub fn player_access_register(&mut self, token: RawToken, player: PlayerId) {
- let iad = InstanceAccessDetails { g : self.amu.clone(), ident : player };
+ let iad = InstanceAccessDetails { gref : self.gref.clone(), ident : player };
self.token_register(token, iad);
self.save_access_now()?;
}
token: RawToken,
iad: InstanceAccessDetails<Id>
) {
- Id::tokens_registry(&mut *self.ig, PRIVATE_Y).tr.insert(token.clone());
+ Id::tokens_registry(&mut self.c.g, PRIVATE_Y).tr.insert(token.clone());
Id::global_tokens(PRIVATE_Y).write().unwrap().insert(token, iad);
}
#[throws(OE)]
fn save_game_now(&mut self) {
self.save_something("g-", |s,w| {
- rmp_serde::encode::write_named(w, &s.ig.gs)
+ rmp_serde::encode::write_named(w, &s.c.g.gs)
})?;
}
self.save_something("a-", |s,w| {
let global_players = GLOBAL.players.read().unwrap();
let tokens_players : Vec<(&str, PlayerId)> =
- s.ig.tokens_players.tr
+ s.c.g.tokens_players.tr
.iter()
.map(|token|
global_players.get(token)
}
#[throws(OE)]
- pub fn load(name: InstanceName) -> Arc<Mutex<Instance>> {
+ pub fn load(name: InstanceName) -> InstanceRef {
// xxx scan on startup, rather than asking caller to specify names
// xxx should take a file lock on save area
let gs : GameState = Self::load_something(&name, "g-")?;
}
let name = Arc::new(name);
- let inst = Instance {
- live: true,
+ let g = Instance {
name, gs, updates,
clients : Default::default(),
tokens_clients : Default::default(),
tokens_players : Default::default(),
};
+ let cont = InstanceContainer {
+ live: true,
+ g,
+ };
// xxx record in GLOBAL.games
- let amu = Arc::new(Mutex::new(inst));
- let mut ig = amu.lock().unwrap();
+ let gref = InstanceRef(Arc::new(Mutex::new(cont)));
+ let mut g = gref.lock().unwrap();
for (token, _) in &al.tokens_players {
- ig.tokens_players.tr.insert(RawToken(token.clone()));
+ g.tokens_players.tr.insert(RawToken(token.clone()));
}
let mut global = GLOBAL.players.write().unwrap();
for (token, player) in al.tokens_players.drain(0..) {
let iad = InstanceAccessDetails {
- g : amu.clone(),
+ gref : gref.clone(),
ident : player,
};
global.insert(RawToken(token), iad);
}
drop(global);
- drop(ig);
- amu
+ drop(g);
+ gref
}
}
#[throws(OE)]
pub fn xxx_global_setup() {
let gs = xxx_gamestate_init();
- let ami = Instance::new(InstanceName {
+ let gref = Instance::new(InstanceName {
scope: ManagementScope::XXX,
scoped_name: "dummy".to_string()
}, gs).expect("xxx create dummy");
- let mut ig = Instance::lock(&ami)?;
+ let mut g = gref.lock()?;
for (token, nick) in XXX_PLAYERS_TOKENS {
- let player = ig.player_new(PlayerState {
+ let player = g.player_new(PlayerState {
nick : nick.to_string(),
})?;
- ig.player_access_register(RawToken(token.to_string()), player)?;
+ g.player_access_register(RawToken(token.to_string()), player)?;
}
}
| ServerMessagePackEncodeFail(_)
| ServerMessagePackDecodeFail(_)
=> Status::InternalServerError,
- NoClient | NoPlayer => Status::NotFound,
+ NoClient | NoPlayer | GameBeingDestroyed => Status::NotFound,
InvalidZCoord => Status::BadRequest,
};
let mut resp = Responder::respond_to(msg,req).unwrap();
const SELECT_SCALE : f64 = 1.1;
-#[derive(Copy,Clone,Debug,Error)]
+#[derive(Copy,Clone,Debug,Error,Serialize,Deserialize)]
pub enum SVGProcessingError {
UnknownOperator,
BadNumber,
let iad = lookup_token(&form.ptoken)?;
let player = iad.ident;
let c = {
- let mut ig = Instance::lock(&iad.g)?;
+ let mut ig = iad.gref.lock()?;
let cl = Client { player };
let client = ig.clients.insert(cl);
let ciad = InstanceAccessDetails {
- g : iad.g.clone(),
+ gref : iad.gref.clone(),
ident : client,
};
let ctoken = record_token(&mut ig, ciad)?;
init_confirmation_send : iter::Once<()>,
keepalives : Wrapping<u32>,
to_send : UpdateId,
- ami : Arc<Mutex<Instance>>,
+ gref : InstanceRef,
}
#[derive(Error,Debug)]
let em : fn(&'static str) -> io::Error =
|s| io::Error::new(io::ErrorKind::Other, anyhow!(s));
- let mut amig = self.ami.lock().map_err(|_| em("poison"))?;
+ let mut ig = self.gref.lock().map_err(|_| em("poison"))?;
let orig_wanted = orig_buf.len();
let mut buf = orig_buf.as_mut();
self.player, self.client, self.to_send)?;
}
- let pu = &mut amig.updates.get(self.player)
+ let pu = &mut ig.updates.get(self.player)
.ok_or_else(|| em("player gonee"))?;
let cv = pu.cv.clone();
// xxx this endless stream is a leak
// restart it occasionally
- amig = cv.wait_timeout(amig, UPDATE_KEEPALIVE)
+ ig.c = cv.wait_timeout(ig.c, UPDATE_KEEPALIVE)
.map_err(|_| em("poison"))?.0;
write!(buf, "event: commsworking\n\
let client = iad.ident;
let content = {
- let mut ig = iad.g.lock()?;
- let _g = &mut ig.gs;
- let cl = ig.clients.byid(client)?;
+ let mut g = iad.gref.lock()?;
+ let _g = &mut g.gs;
+ let cl = g.clients.byid(client)?;
let player = cl.player;
eprintln!("updates content iad={:?} player={:?} cl={:?} updates={:?}",
- &iad, &player, &cl, &ig.updates);
- let ami = iad.g.clone();
+ &iad, &player, &cl, &g.updates);
+ let gref = iad.gref.clone();
- let log = &ig.updates.byid(player)?.log;
+ let log = &g.updates.byid(player)?.log;
let to_send = match log.into_iter().rev()
.find(|(_,update)| update.gen <= gen) {
};
UpdateReader {
- player, client, to_send, ami,
+ player, client, to_send, gref,
need_flush : false,
keepalives : Wrapping(0),
init_confirmation_send : iter::once(()),