You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.1 KiB

  1. use std::collections::HashMap;
  2. use async_std::sync::{Arc, RwLock, RwLockReadGuard};
  3. use futures::future::BoxFuture;
  4. use libpso::packet::ship::*;
  5. use libpso::packet::login::Session;
  6. use networking::serverstate::ClientId;
  7. use entity::account::{UserAccountEntity, UserSettingsEntity};
  8. use entity::character::CharacterEntity;
  9. use entity::item;
  10. use maps::area::MapArea;
  11. use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
  12. #[derive(thiserror::Error, Debug)]
  13. pub enum ClientError {
  14. #[error("not found {0}")]
  15. NotFound(ClientId),
  16. }
  17. #[derive(Clone, Default)]
  18. pub struct Clients(Arc<RwLock<HashMap<ClientId, RwLock<ClientState>>>>);
  19. impl Clients {
  20. pub async fn add(&mut self, client_id: ClientId, client_state: ClientState) {
  21. self.0
  22. .write()
  23. .await
  24. .insert(client_id, RwLock::new(client_state));
  25. }
  26. pub async fn remove(&mut self, client_id: &ClientId) -> Option<ClientState> {
  27. Some(self.0
  28. .write()
  29. .await
  30. .remove(client_id)?
  31. .into_inner())
  32. }
  33. pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
  34. where
  35. T: Send,
  36. F: for<'b> FnOnce(&'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
  37. {
  38. let clients = self.0
  39. .read()
  40. .await;
  41. let client = clients
  42. .get(&client_id)
  43. .ok_or(ClientError::NotFound(client_id))?
  44. .read()
  45. .await;
  46. Ok(func(&client).await)
  47. }
  48. pub async fn with_many<'a, T, F, const N: usize>(&'a self, client_ids: [ClientId; N], func: F) -> Result<T, anyhow::Error>
  49. where
  50. T: Send,
  51. F: for<'b> FnOnce([RwLockReadGuard<'b, ClientState>; N]) -> BoxFuture<'b, T> + Send + 'a,
  52. {
  53. let clients = self.0
  54. .read()
  55. .await;
  56. let mut client_states: [std::mem::MaybeUninit<RwLockReadGuard<ClientState>>; N] = unsafe {
  57. std::mem::MaybeUninit::uninit().assume_init()
  58. };
  59. for (cindex, client_id) in client_ids.iter().enumerate() {
  60. let c = clients
  61. .get(client_id)
  62. .ok_or(ClientError::NotFound(*client_id))?
  63. .read()
  64. .await;
  65. client_states[cindex].write(c);
  66. }
  67. let client_states = unsafe {
  68. // TODO: this should just be a normal transmute but due to compiler limitations it
  69. // does not yet work with const generics
  70. // https://github.com/rust-lang/rust/issues/61956
  71. std::mem::transmute_copy::<_, [RwLockReadGuard<ClientState>; N]>(&client_states)
  72. };
  73. Ok(func(client_states).await)
  74. }
  75. pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
  76. where
  77. T: Send,
  78. F: for<'b> FnOnce(&'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
  79. {
  80. let clients = self.0
  81. .read()
  82. .await;
  83. let mut client = clients
  84. .get(&client_id)
  85. .ok_or(ClientError::NotFound(client_id))?
  86. .write()
  87. .await;
  88. Ok(func(&mut client).await)
  89. }
  90. }
  91. #[derive(Debug, Clone, Copy)]
  92. pub struct ItemDropLocation {
  93. pub map_area: MapArea,
  94. pub x: f32,
  95. pub z: f32,
  96. pub item_id: items::ClientItemId,
  97. }
  98. pub struct LoadingQuest {
  99. pub header_bin: Option<QuestHeader>,
  100. pub header_dat: Option<QuestHeader>,
  101. }
  102. pub struct ClientState {
  103. pub user: UserAccountEntity,
  104. pub settings: UserSettingsEntity,
  105. pub character: CharacterEntity,
  106. _session: Session,
  107. //guildcard: GuildCard,
  108. pub block: usize,
  109. pub item_drop_location: Option<ItemDropLocation>,
  110. pub done_loading_quest: bool,
  111. pub area: Option<MapArea>,
  112. pub x: f32,
  113. pub y: f32,
  114. pub z: f32,
  115. pub weapon_shop: Vec<WeaponShopItem>,
  116. pub tool_shop: Vec<ToolShopItem>,
  117. pub armor_shop: Vec<ArmorShopItem>,
  118. pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
  119. pub character_playtime: chrono::Duration,
  120. pub log_on_time: chrono::DateTime<chrono::Utc>,
  121. }
  122. impl ClientState {
  123. pub fn new(user: UserAccountEntity, settings: UserSettingsEntity, character: CharacterEntity, session: Session) -> ClientState {
  124. let character_playtime = chrono::Duration::seconds(character.playtime as i64);
  125. ClientState {
  126. user,
  127. settings,
  128. character,
  129. _session: session,
  130. block: 0,
  131. item_drop_location: None,
  132. done_loading_quest: false,
  133. area: None,
  134. x: 0.0,
  135. y: 0.0,
  136. z: 0.0,
  137. weapon_shop: Vec::new(),
  138. tool_shop: Vec::new(),
  139. armor_shop: Vec::new(),
  140. tek: None,
  141. character_playtime,
  142. log_on_time: chrono::Utc::now(),
  143. }
  144. }
  145. pub fn update_playtime(&mut self) {
  146. let additional_playtime = chrono::Utc::now() - self.log_on_time;
  147. self.character.playtime = (self.character_playtime + additional_playtime).num_seconds() as u32;
  148. }
  149. }