pub struct AccountRecord {
pub nick: String,
pub timezone: String,
- pub access: Arc<dyn PlayerAccessSpec>,
pub tokens_revealed: HashMap<Html, TokenRevelation>,
+ access: Arc<dyn PlayerAccessSpec>,
}
#[derive(Copy,Clone,Debug,Ord,PartialOrd,Eq,PartialEq)]
#[throws(MgmtError)]
pub fn with_entry_mut<T, F>(account: &AccountName,
auth: Authorisation<AccountName>,
+ set_access: Option<Arc<dyn PlayerAccessSpec>>,
f: F)
-> Result<T, (InternalError, T)>
where F: FnOnce(&mut AccountRecord, AccountId) -> T
let (entry, acctid)
= AccountRecord::lookup_mut_caller_must_save(account, auth)
.ok_or(MgmtError::AccountNotFound)?;
- let old_access = entry.access.clone();
+
+ if let Some(new_access) = set_access() {
+ invalidate_all_tokens_for_account(acctid)?;
+ entry.access = new_access;
+ }
let output = f(&mut *entry, acctid);
- let mut ok = true;
- if ! Arc::ptr_eq(&old_access, &entry.access) {
- // xxx actually do this
-// invalidate_all_tokens_for_account(accid)
-// .dont_just_questionmark
- };
let ok = save_accounts_now();
match ok {
Ok(()) => Ok(output),
save_accounts_now()?;
}
+ #[throws(MgmtError)]
+ pub fn remove_entry(account: &AccountName,
+ _auth: Authorisation<AccountName>)
+ {
+ let accounts = ACCOUNTS.write();
+ let oe = if_chain! {
+ if let Some(accounts) = accounts.as_mut();
+ let entry = accounts.names.entry(account);
+ if let hash_map::Entry::Occupied(oe) = entry;
+ then { oe }
+ else { throw!(AccountNotFound) }
+ };
+ let acctid = *oe.key();
+ process_all_players_for_account(acctid, InstanceGuard::player_remove)?;
+ oe.remove();
+ accounts.records.remove(acctid);
+ save_accounts_now()?;
+ }
+
pub fn expire_tokens_revealed(&mut self) {
panic!("xxx")
}
#[derive(Debug,Clone)]
struct AccountSpecified {
- account: AccountName,
+ notional_account: AccountName, // might not exist
cooked: String, // account.to_string()
auth: Authorisation<AccountName>,
}
.map_err(|(e,_)|e) ?
}
+ DeleteAccount(account) => {
+ let auth = authorise_for_account(cs, &account)?;
+ AccountRecord::remove_entry(&account auth)?;
+ Fine
+ }
+
SetAccount(wanted_account) => {
let auth = authorise_scope_direct(cs, &wanted_account.scope)?;
cs.account = Some(AccountSpecified {
use MgmtGameResponse::*;
type Insn = MgmtGameInstruction;
type Resp = MgmtGameResponse;
- let who = &cs.who;
+ let who = &cs.who; // todo show player nick when it's a player
#[throws(MgmtError)]
fn readonly<'ig,
})?,
RemovePlayer(account) => {
- // todo let you remove yourself unconditionally
let ig = cs.check_acl(ig,
PCH::InstanceOrOnlyAffectedAccount(&account),
&[TP::RemovePlayer])?.0;
CreateAccont(AccountDetails),
UpdateAccont(AccountDetails),
- DeleteAccount(AccountDetails),
+ DeleteAccount(AccountName),
- SetAccount(AccountName),
+ SetAccount(AccountName), // success does not mean account exists
CreateGame {
game: InstanceName,
insns: Vec<MgmtGameInstruction>,
how: MgmtGameUpdateMode,
},
+ DestroyGame {
+ game: InstanceName,
+ },
LibraryListByGlob {
glob: shapelib::ItemSpec,
pub gen : Generation,
pub log : VecDeque<(Generation, Arc<CommittedLogEntry>)>,
pub max_z : ZCoord,
- pub players : DenseSlotMap<PlayerId, AccountName>,
+ pub players : DenseSlotMap<PlayerId, String /* nick */>,
}
#[derive(Debug,Serialize,Deserialize)]
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct PlayerState {
- pub account: AccountName,
- pub nick: String,
+ pub acctid: AccountId,
pub tz: Timezone,
}
#[derive(Default)]
pub struct Global {
- // lock hierarchy: InstanceContainer < games < {players, clients}
+ // lock hierarchy: games < InstanceContainer < {players, clients}
// (in order of criticality (perf impact); outermost first, innermost last)
games : RwLock<HashMap<Arc<InstanceName>,InstanceRef>>,
players : RwLock<TokenTable<PlayerId>>,
let a_savefile = savefilename(&g.name, "a-", "");
let mut gw = GLOBAL.games.write().unwrap();
+ // xxx lock order wrong, this function should unlock and then relock
let g_file = savefilename(&g.name, "g-", "");
fs::remove_file(&g_file).context("remove").context(g_file)?;
Ok((old_account, old_pst))
}
+ #[throws(InternalError)]
+ pub fn invalidate_tokens(&mut self, player: PlayerId) {
+ let old_tokens = self.tokens_players.get(player).clone();
+ xxx much of middle of the infallible bit of player_remove
+ }
+
#[throws(MgmtError)]
fn player_access_reset_redeliver(&mut self, player: PlayerId,
authorised: Authorisation<AccountName>,
token
}
+#[throws(E)]
+pub fn process_all_players_for_account<
+ E: Error,
+ F: FnMut(&mut InstanceGuard, player: PlayerId) -> Result<(),E>
+ >
+ (acctid: AccountId, f: F)
+{
+ let games = GLOBAL.games.write().unlock();
+ for gref in games.values() {
+ let ig = gref.lock();
+ let remove : Vec<_> = ig.iplayers.filter_map(|(player,pr)| {
+ if pr.pst.acctid == acctid { Some(player) } else { None }
+ }).collect();
+ for player in remove.drain(..) {
+ f(player)?;
+ }
+ }
+}
+
// ========== instance pieces data access ==========
impl PiecesLoaded {