scoped_name : name,
};
- let gref = Instance::new(name, gs, Default::default())?;
+ let gref = Instance::new(name, gs)?;
let mut ig = gref.lock()?;
execute_for_game(cs, &mut ig, insns, MgmtGameUpdateMode::Bulk)
}
DeletePiece(piece) => {
- let p = ig.pieces.remove(piece).ok_or(ME::PieceNotFound)?;
+ let modperm = ig.modify_pieces();
+ let p = ig.pieces.as_mut(modperm)
+ .remove(piece).ok_or(ME::PieceNotFound)?;
let gs = &mut ig.gs;
let pc = gs.pieces.remove(piece);
let desc_html = p.describe_html(Some(Default::default()));
},
AddPieces(PiecesSpec{ pos,posd,count,face,info }) => {
+ let modperm = ig.modify_pieces();
let ig = &mut **ig;
let gs = &mut ig.gs;
let count = count.unwrap_or(1);
throw!(SpecError::PosOffTable);
}
let piece = gs.pieces.insert(pc);
- ig.pieces.insert(piece, p);
+ ig.pieces.as_mut(modperm).insert(piece, p);
updates.push((piece, PieceUpdateOp::Insert(())));
pos[0] += posd[0];
pos[1] += posd[1];
#[repr(transparent)]
pub struct RawTokenVal(str);
-pub type PiecesLoaded = SecondarySlotMap<PieceId,Box<dyn Piece>>;
-
// ---------- public data structure ----------
#[derive(Debug,Serialize,Deserialize)]
pub tokens_clients : TokenRegistry<ClientId>,
}
+#[derive(Debug,Serialize,Deserialize)]
+#[serde(transparent)]
+pub struct PiecesLoaded (ActualPiecesLoaded);
+pub type ActualPiecesLoaded = SecondarySlotMap<PieceId,Box<dyn Piece>>;
+#[derive(Copy,Clone,Debug)]
+pub struct ModifyingPieces(());
+
#[derive(Debug,Clone,Deserialize,Serialize)]
#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
pub enum ManagementScope {
pub struct InstanceContainer {
live : bool,
game_dirty : bool,
+ access_dirty : bool,
g : Instance,
}
/// Returns `None` if a game with this name already exists
#[allow(clippy::new_ret_no_self)]
#[throws(MgmtError)]
- pub fn new(name: InstanceName, gs: GameState, pieces: PiecesLoaded)
+ pub fn new(name: InstanceName, gs: GameState)
-> InstanceRef {
let name = Arc::new(name);
let g = Instance {
name : name.clone(),
- gs, pieces,
+ gs,
+ pieces : PiecesLoaded(Default::default()),
clients : Default::default(),
updates : Default::default(),
tokens_players : Default::default(),
let cont = InstanceContainer {
live : true,
game_dirty : false,
+ access_dirty : false,
g,
};
out
}
+ pub fn modify_pieces(&mut self) -> ModifyingPieces {
+ self.save_game_and_access_later();
+ // want this to be borrowed from self, so that we tie it properly
+ // to the same game. But in practice we don't expect to write
+ // bugs where we get different games mixed up. Borrowing self
+ // from the caller's pov is troublesome beczuse ultimately the
+ // caller will need to manipulate various fields of Instance (so
+ // we mustn't have a borrow of it).
+ ModifyingPieces(())
+ }
+
fn token_register<Id:AccessId>(
&mut self,
token: RawToken,
!remove
});
}
+
}
// ---------- save/load ----------
#[throws(InternalError)]
pub fn save_game_now(&mut self) {
+ if self.c.access_dirty {
+ self.save_access_now()?;
+ }
self.save_something("g-", |s,w| {
rmp_serde::encode::write_named(w, &s.c.g.gs)
})?;
let isa = InstanceSaveAccesses { pieces, tokens_players };
rmp_serde::encode::write_named(w, &isa)
})?;
+ self.c.access_dirty = false;
info!("saved accesses for {:?}", &self.name);
}
})().context(lockfile).context("lock global save area")?);
}
}
- let InstanceSaveAccesses::<String,PiecesLoaded>
+ let InstanceSaveAccesses::<String,ActualPiecesLoaded>
{ mut tokens_players, mut pieces } = Self::load_something(&name, "a-")
.or_else(|e| {
if let InternalError::Anyhow(ae) = &e {
);
let g = Instance {
- gs, pieces, updates,
+ gs, updates,
+ pieces: PiecesLoaded(pieces),
name: name.clone(),
clients : Default::default(),
tokens_clients : Default::default(),
let cont = InstanceContainer {
live: true,
game_dirty: false,
+ access_dirty: false,
g,
};
let gref = InstanceRef(Arc::new(Mutex::new(cont)));
token
}
+// ========== instance pieces data access ==========
+
+impl PiecesLoaded {
+ pub fn get(&self, piece: PieceId) -> Option<&Box<dyn Piece>> {
+ self.0.get(piece)
+ }
+
+ pub fn as_mut(&mut self, _: ModifyingPieces) -> &mut ActualPiecesLoaded {
+ &mut self.0
+ }
+}
+
// ========== background maintenance ==========
// ---------- delayed game save ----------
GLOBAL.dirty.lock().unwrap().push_back(self.gref.clone());
self.c.game_dirty = true;
}
+
+ pub fn save_game_and_access_later(&mut self) {
+ if self.c.access_dirty { return }
+ self.save_game_later();
+ self.c.access_dirty = true;
+ }
}
pub fn game_flush_task() {