jake
2 years ago
1 changed files with 172 additions and 0 deletions
@ -0,0 +1,172 @@ |
|||
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 crate::common::serverstate::ClientId;
|
|||
use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
|
|||
use crate::entity::character::CharacterEntity;
|
|||
use crate::entity::item;
|
|||
|
|||
use crate::ship::ship::ShipError;
|
|||
use crate::ship::items;
|
|||
use crate::ship::map::MapArea;
|
|||
use crate::ship::shops::{WeaponShopItem, ToolShopItem, ArmorShopItem};
|
|||
|
|||
|
|||
#[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, ShipError>
|
|||
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_else(|| ShipError::ClientNotFound(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, ShipError>
|
|||
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_else(|| ShipError::ClientNotFound(*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, ShipError>
|
|||
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_else(|| ShipError::ClientNotFound(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;
|
|||
}
|
|||
}
|
|||
|
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue