|
|
use std::collections::HashMap;
use async_std::sync::{Arc, RwLock, RwLockReadGuard};
use futures::future::BoxFuture;
use libpso::packet::ship::*;
use libpso::packet::login::Session;
use networking::serverstate::ClientId;
use entity::account::{UserAccountEntity, UserSettingsEntity};
use entity::character::CharacterEntity;
use entity::item;
use maps::area::MapArea;
use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
#[derive(thiserror::Error, Debug)]
pub enum ClientError {
#[error("not found {0}")]
NotFound(ClientId),
}
#[derive(Clone, Default)]
pub struct Clients(Arc<RwLock<HashMap<ClientId, RwLock<ClientState>>>>);
impl Clients {
pub async fn add(&mut self, client_id: ClientId, client_state: ClientState) {
self.0
.write()
.await
.insert(client_id, RwLock::new(client_state));
}
pub async fn remove(&mut self, client_id: &ClientId) -> Option<ClientState> {
Some(self.0
.write()
.await
.remove(client_id)?
.into_inner())
}
pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
where
T: Send,
F: for<'b> FnOnce(&'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
{
let clients = self.0
.read()
.await;
let client = clients
.get(&client_id)
.ok_or(ClientError::NotFound(client_id))?
.read()
.await;
Ok(func(&client).await)
}
pub async fn with_many<'a, T, F, const N: usize>(&'a self, client_ids: [ClientId; N], func: F) -> Result<T, anyhow::Error>
where
T: Send,
F: for<'b> FnOnce([RwLockReadGuard<'b, ClientState>; N]) -> BoxFuture<'b, T> + Send + 'a,
{
let clients = self.0
.read()
.await;
let mut client_states: [std::mem::MaybeUninit<RwLockReadGuard<ClientState>>; N] = unsafe {
std::mem::MaybeUninit::uninit().assume_init()
};
for (cindex, client_id) in client_ids.iter().enumerate() {
let c = clients
.get(client_id)
.ok_or(ClientError::NotFound(*client_id))?
.read()
.await;
client_states[cindex].write(c);
}
let client_states = unsafe {
// TODO: this should just be a normal transmute but due to compiler limitations it
// does not yet work with const generics
// https://github.com/rust-lang/rust/issues/61956
std::mem::transmute_copy::<_, [RwLockReadGuard<ClientState>; N]>(&client_states)
};
Ok(func(client_states).await)
}
pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error>
where
T: Send,
F: for<'b> FnOnce(&'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
{
let clients = self.0
.read()
.await;
let mut client = clients
.get(&client_id)
.ok_or(ClientError::NotFound(client_id))?
.write()
.await;
Ok(func(&mut client).await)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ItemDropLocation {
pub map_area: MapArea,
pub x: f32,
pub z: f32,
pub item_id: items::ClientItemId,
}
pub struct LoadingQuest {
pub header_bin: Option<QuestHeader>,
pub header_dat: Option<QuestHeader>,
}
pub struct ClientState {
pub user: UserAccountEntity,
pub settings: UserSettingsEntity,
pub character: CharacterEntity,
_session: Session,
//guildcard: GuildCard,
pub block: usize,
pub item_drop_location: Option<ItemDropLocation>,
pub done_loading_quest: bool,
pub area: Option<MapArea>,
pub x: f32,
pub y: f32,
pub z: f32,
pub weapon_shop: Vec<WeaponShopItem>,
pub tool_shop: Vec<ToolShopItem>,
pub armor_shop: Vec<ArmorShopItem>,
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
pub character_playtime: chrono::Duration,
pub log_on_time: chrono::DateTime<chrono::Utc>,
}
impl ClientState {
pub fn new(user: UserAccountEntity, settings: UserSettingsEntity, character: CharacterEntity, session: Session) -> ClientState {
let character_playtime = chrono::Duration::seconds(character.playtime as i64);
ClientState {
user,
settings,
character,
_session: session,
block: 0,
item_drop_location: None,
done_loading_quest: false,
area: None,
x: 0.0,
y: 0.0,
z: 0.0,
weapon_shop: Vec::new(),
tool_shop: Vec::new(),
armor_shop: Vec::new(),
tek: None,
character_playtime,
log_on_time: chrono::Utc::now(),
}
}
pub fn update_playtime(&mut self) {
let additional_playtime = chrono::Utc::now() - self.log_on_time;
self.character.playtime = (self.character_playtime + additional_playtime).num_seconds() as u32;
}
}
|