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