diff --git a/sable_network/src/network/network/account_state.rs b/sable_network/src/network/network/account_state.rs index 5d06f7b..6acd79a 100644 --- a/sable_network/src/network/network/account_state.rs +++ b/sable_network/src/network/network/account_state.rs @@ -115,6 +115,7 @@ impl Network { self.historic_users.update_account( user, + event.timestamp, new_account .as_ref() .and_then(|id| accounts.get(id).map(|a| a.name)), diff --git a/sable_network/src/network/network/user_history.rs b/sable_network/src/network/network/user_history.rs index 0a8814c..d4c6626 100644 --- a/sable_network/src/network/network/user_history.rs +++ b/sable_network/src/network/network/user_history.rs @@ -17,9 +17,26 @@ impl HistoricUserStore { /// Add a user to the store. /// - /// This should be called by the [`Network`] for every change to a client-protocol-visible attribute - /// of the user object. - pub fn add(&mut self, user: &mut state::User, nickname: Nickname, account: Option) { + /// This (or one of the update variants) should be called by the [`Network`] for every change + /// to a client-protocol-visible attribute of the user object. + /// + /// The provided timestamp will be applied to the previous historic user corresponding to this + /// user object, to indicate when it ceased to be relevant, and the user object's serial number + /// will be incremented. + pub fn add( + &mut self, + user: &mut state::User, + timestamp: i64, + nickname: Nickname, + account: Option, + ) { + if let Some(existing) = self + .users + .get_mut(&HistoricUserId::new(user.id, user.serial)) + { + existing.timestamp = Some(timestamp); + } + user.serial += 1; let historic_user = HistoricUser { @@ -31,6 +48,7 @@ impl HistoricUserStore { realname: user.realname, away_reason: user.away_reason, account, + timestamp: None, }; let new_id = HistoricUserId::new(user.id, user.serial); @@ -39,7 +57,7 @@ impl HistoricUserStore { } /// Update the details of a user that's already in the store, reusing the existing nickname and account - pub fn update(&mut self, user: &mut state::User) -> HistoricUserId { + pub fn update(&mut self, user: &mut state::User, timestamp: i64) -> HistoricUserId { let old_id = HistoricUserId::new(user.id, user.serial); let Some(existing) = self.get_user(&user) else { @@ -49,12 +67,17 @@ impl HistoricUserStore { let nickname = existing.nickname; let account = existing.account; - self.add(user, nickname, account); + self.add(user, timestamp, nickname, account); old_id } /// Update the details of a user that's already in the store, reusing the existing account - pub fn update_nick(&mut self, user: &mut state::User, nickname: Nickname) -> HistoricUserId { + pub fn update_nick( + &mut self, + user: &mut state::User, + timestamp: i64, + nickname: Nickname, + ) -> HistoricUserId { let old_id = HistoricUserId::new(user.id, user.serial); let Some(existing) = self.get_user(&user) else { @@ -63,7 +86,7 @@ impl HistoricUserStore { let account = existing.account; - self.add(user, nickname, account); + self.add(user, timestamp, nickname, account); old_id } @@ -71,6 +94,7 @@ impl HistoricUserStore { pub fn update_account( &mut self, user: &mut state::User, + timestamp: i64, account: Option, ) -> HistoricUserId { let old_id = HistoricUserId::new(user.id, user.serial); @@ -81,7 +105,7 @@ impl HistoricUserStore { let nickname = existing.nickname; - self.add(user, nickname, account); + self.add(user, timestamp, nickname, account); old_id } diff --git a/sable_network/src/network/network/user_state.rs b/sable_network/src/network/network/user_state.rs index 2ecabc3..461ee33 100644 --- a/sable_network/src/network/network/user_state.rs +++ b/sable_network/src/network/network/user_state.rs @@ -100,7 +100,9 @@ impl Network { let new_binding = state::NickBinding::new(new_nick, user_id, trigger.timestamp, trigger.id); - let prev_historic_id = self.historic_users.update_nick(user, new_nick); + let prev_historic_id = + self.historic_users + .update_nick(user, trigger.timestamp, new_nick); // Let translate_historic_user do the work of mapping the account name, then manually fill in // the old (collided) nick @@ -154,7 +156,9 @@ impl Network { let new_nick = new_binding.nick; self.nick_bindings.insert(new_nick, new_binding); - let prev_historic_id = self.historic_users.update_nick(user_object, new_nick); + let prev_historic_id = + self.historic_users + .update_nick(user_object, event.timestamp, new_nick); // Emit UserNickChange update if a nick change happens as a result of this rebinding. if old_nick.value() != new_nick.value() { @@ -209,6 +213,7 @@ impl Network { // get_mut later on self.historic_users.add( &mut user, + event.timestamp, detail.nickname, detail .account @@ -259,7 +264,7 @@ impl Network { let mut old_reason = new_reason; std::mem::swap(&mut user.away_reason, &mut old_reason); - self.historic_users.update(user); + self.historic_users.update(user, event.timestamp); let update_user = user.clone(); diff --git a/sable_network/src/network/state/historic.rs b/sable_network/src/network/state/historic.rs index a9d2628..84d5734 100644 --- a/sable_network/src/network/state/historic.rs +++ b/sable_network/src/network/state/historic.rs @@ -11,6 +11,10 @@ pub struct HistoricUser { pub realname: Realname, pub away_reason: Option, pub account: Option, + + /// The time until which this historic user state was accurate - if None + /// then this data is current. + pub timestamp: Option, } impl wrapper::WrappedUser for HistoricUser { @@ -70,6 +74,7 @@ impl HistoricUser { visible_host: user.visible_host, realname: user.realname, away_reason: user.away_reason, + timestamp: None, } } }