Browse Source

add create team

teams
jake 2 years ago
parent
commit
f04ae8113f
  1. 1
      src/bin/main.rs
  2. 2
      src/common/interserver.rs
  3. 9
      src/entity/account.rs
  4. 22
      src/entity/gateway/entitygateway.rs
  5. 66
      src/entity/gateway/inmemory.rs
  6. 1
      src/entity/mod.rs
  7. 21
      src/login/character.rs
  8. 30
      src/ship/client.rs
  9. 1
      src/ship/mod.rs
  10. 1
      src/ship/packet/builder/mod.rs
  11. 3
      src/ship/packet/handler/auth.rs
  12. 1
      src/ship/packet/handler/mod.rs
  13. 65
      src/ship/ship.rs

1
src/bin/main.rs

@ -60,7 +60,6 @@ fn main() {
username: if i == 0 { "hi".to_string() } else { format!("hi{}", i+1) },
password: bcrypt::hash("qwer", 5).unwrap(),
guildcard: i + 1,
team_id: None,
banned_until: None,
muted_until: None,
flags: 0,

2
src/common/interserver.rs

@ -31,6 +31,7 @@ pub enum LoginMessage {
ships: Vec<Ship>,
},
RequestUsers,
CreatedTeam(UserAccountId, TeamEntityId),
}
#[derive(Debug, Serialize, Deserialize)]
@ -45,6 +46,7 @@ pub enum ShipMessage {
RequestShipList,
AddUser(UserAccountId),
RemoveUser(UserAccountId),
CreateTeam(UserAccountId, String),
}
pub enum InterserverMessage<S, C> {

9
src/entity/account.rs

@ -1,6 +1,7 @@
use serde::{Serialize, Deserialize};
use libpso::character::settings;
use libpso::character::guildcard;
use super::team::TeamEntityId;
const GUILDCARD_OFFSET: u32 = 2300000;
@ -20,12 +21,11 @@ pub struct NewUserAccountEntity {
pub email: String,
pub username: String,
pub password: String,
pub guildcard: u32,
pub team_id: Option<u32>,
pub guildcard: u32, // TODO: remove this, guildcard is based on account id
pub banned_until: Option<chrono::DateTime<chrono::Utc>>,
pub muted_until: Option<chrono::DateTime<chrono::Utc>>,
pub flags: u32,
pub activated: bool
pub activated: bool,
}
impl Default for NewUserAccountEntity {
@ -35,7 +35,6 @@ impl Default for NewUserAccountEntity {
username: "".into(),
password: "".into(),
guildcard: 0xFFFFFFFF,
team_id: None,
banned_until: None,
muted_until: None,
flags: 0,
@ -50,7 +49,7 @@ pub struct UserAccountEntity {
pub username: String,
pub password: String,
pub guildcard: u32,
pub team_id: Option<u32>,
pub team_id: Option<TeamEntityId>,
pub banned_until: Option<chrono::DateTime<chrono::Utc>>,
pub muted_until: Option<chrono::DateTime<chrono::Utc>>,
pub created_at: chrono::DateTime<chrono::Utc>,

22
src/entity/gateway/entitygateway.rs

@ -5,6 +5,7 @@ use futures::Future;
use crate::entity::account::*;
use crate::entity::character::*;
use crate::entity::item::*;
use crate::entity::team::*;
// TODO: better granularity?
@ -13,6 +14,7 @@ use crate::entity::item::*;
#[error("")]
pub enum GatewayError {
Error,
NotFound,
PgError(#[from] sqlx::Error)
}
@ -148,6 +150,26 @@ pub trait EntityGateway: Send + Sync {
async fn set_character_playtime(&mut self, _char_id: &CharacterEntityId, _playtime: u32) -> Result<(), GatewayError> {
unimplemented!();
}
async fn create_team(&mut self, _team_entity: NewTeamEntity) -> Result<TeamEntity, GatewayError> {
unimplemented!();
}
async fn get_team(&mut self, _team_entity: &TeamEntityId) -> Result<TeamEntity, GatewayError> {
unimplemented!();
}
async fn add_user_to_team(&mut self, _user: &UserAccountId, _team: &TeamEntityId) -> Result<(), GatewayError> {
unimplemented!();
}
async fn remove_user_from_team(&mut self, _user: &UserAccountId) -> Result<(), GatewayError> {
unimplemented!();
}
async fn team_point_item(&mut self, _user: &UserAccountId, _team: &TeamEntityId) -> Result<(), GatewayError> {
unimplemented!();
}
}

66
src/entity/gateway/inmemory.rs

@ -4,6 +4,7 @@ use futures::Future;
use crate::entity::account::*;
use crate::entity::character::*;
use crate::entity::team::*;
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction, GatewayError};
use crate::entity::item::*;
@ -223,6 +224,7 @@ pub struct InMemoryGateway {
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
trades: Arc<Mutex<Vec<TradeEntity>>>,
teams: Arc<Mutex<BTreeMap<TeamEntityId, TeamEntity>>>,
}
impl Default for InMemoryGateway {
@ -240,6 +242,7 @@ impl Default for InMemoryGateway {
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
trades: Arc::new(Mutex::new(Vec::new())),
teams: Arc::new(Mutex::new(BTreeMap::new())),
}
}
}
@ -323,6 +326,7 @@ impl EntityGateway for InMemoryGateway {
let mag_modifiers = self.mag_modifiers.lock().await.clone();
let weapon_modifiers = self.weapon_modifiers.lock().await.clone();
let trades = self.trades.lock().await.clone();
let teams = self.teams.lock().await.clone();
let working_gateway = InMemoryGateway {
users: Arc::new(Mutex::new(users)),
@ -337,6 +341,7 @@ impl EntityGateway for InMemoryGateway {
mag_modifiers: Arc::new(Mutex::new(mag_modifiers)),
weapon_modifiers: Arc::new(Mutex::new(weapon_modifiers)),
trades: Arc::new(Mutex::new(trades)),
teams: Arc::new(Mutex::new(teams)),
};
let transaction = InMemoryGatewayTransaction {
@ -361,7 +366,7 @@ impl EntityGateway for InMemoryGateway {
username: user.username,
password: user.password,
guildcard: user.guildcard,
team_id: user.team_id,
team_id: None,
banned_until: user.banned_until,
muted_until: user.muted_until,
created_at: chrono::Utc::now(),
@ -627,4 +632,63 @@ impl EntityGateway for InMemoryGateway {
Err(GatewayError::Error)
}
}
async fn create_team(&mut self, team_entity: NewTeamEntity) -> Result<TeamEntity, GatewayError> {
let new_team = {
let mut teams = self.teams.lock().await;
let id = teams
.iter()
.fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
+ 1;
let new_team = TeamEntity {
id: TeamEntityId(id),
owner: team_entity.created_by,
name: team_entity.name,
flag: [0; 2048],
};
teams.insert(new_team.id, new_team.clone());
new_team
};
self.add_user_to_team(&team_entity.created_by, &new_team.id).await.unwrap();
Ok(new_team)
}
async fn get_team(&mut self, team_id: &TeamEntityId) -> Result<TeamEntity, GatewayError> {
let teams = self.teams.lock().await;
if let Some(team) = teams.get(team_id) {
Ok(team.clone())
}
else {
Err(GatewayError::Error)
}
}
async fn add_user_to_team(&mut self, user_id: &UserAccountId, team: &TeamEntityId) -> Result<(), GatewayError> {
let mut users = self.users.lock().await;
if let Some(user) = users.get_mut(user_id) {
user.team_id = Some(*team);
Ok(())
}
else {
Err(GatewayError::Error)
}
}
async fn remove_user_from_team(&mut self, user_id: &UserAccountId) -> Result<(), GatewayError> {
let mut users = self.users.lock().await;
if let Some(user) = users.get_mut(user_id) {
user.team_id = None;
Ok(())
}
else {
Err(GatewayError::Error)
}
}
async fn team_point_item(&mut self, _user: &UserAccountId, _team: &TeamEntityId) -> Result<(), GatewayError> {
unimplemented!();
}
}

1
src/entity/mod.rs

@ -2,3 +2,4 @@ pub mod gateway;
pub mod account;
pub mod character;
pub mod item;
pub mod team;

21
src/login/character.rs

@ -12,27 +12,27 @@ use libpso::packet::login::*;
use libpso::packet::ship::{MenuDetail, SmallLeftDialog};
use libpso::{PacketParseError, PSOPacket};
use libpso::crypto::bb::PSOBBCipher;
use crate::entity::item;
use libpso::character::character;
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship};
use crate::common::interserver::{ServerId, InterserverActor, LoginMessage, ShipMessage, Ship, AuthToken, InterserverMessage};
use crate::common::leveltable::LEVEL_TABLE;
use libpso::{utf8_to_array, utf8_to_utf16_array};
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::item;
use crate::entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity, Meseta};
use crate::entity::item::weapon::Weapon;
use crate::entity::item::armor::Armor;
use crate::entity::item::tech::Technique;
use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag;
use crate::entity::team::{TeamEntityId, NewTeamEntity};
use crate::entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel};
use crate::login::login::{get_login_status};
use crate::common::interserver::AuthToken;
pub const CHARACTER_PORT: u16 = 12001;
pub const SHIP_MENU_ID: u32 = 1;
@ -343,7 +343,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
response.guildcard = user.guildcard;
response.team_id = user.team_id.map_or(0, |ti| ti);
response.team_id = user.team_id.map_or(0, |ti| ti.0);
let mut client = self.clients.write().await;
let client = client.get_mut(&id).ok_or_else(|| CharacterError::ClientNotFound(id))?;
@ -427,7 +427,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
client.session.action = SessionAction::SelectCharacter;
client.session.character_slot = select.slot as u8;
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard,
user.team_id.unwrap_or(1),
user.team_id.unwrap_or(TeamEntityId(1)).0, // TODO: why is this 1?
client.session)),
SendCharacterPacket::CharAck(CharAck {
slot: select.slot,
@ -520,7 +520,7 @@ impl<EG: EntityGateway + Clone> CharacterServerState<EG> {
user.flags = 0;
self.entity_gateway.save_user(user).await.unwrap();
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard,
user.team_id.unwrap_or(1),
user.team_id.unwrap_or(TeamEntityId(1)).0, // TODO: why is this 1?
client.session)),
SendCharacterPacket::CharAck(CharAck {
slot: preview.slot,
@ -723,6 +723,15 @@ impl<EG: EntityGateway + Clone> InterserverActor for CharacterServerState<EG> {
ShipMessage::SendMail{..} => {
Ok(Vec::new())
},
ShipMessage::CreateTeam(user_id, team_name) => {
let team = self.entity_gateway.create_team(NewTeamEntity {
created_by: user_id,
name: team_name.clone(),
}).await.unwrap(); // TODO: unwrap
self.entity_gateway.add_user_to_team(&user_id, &team.id).await.unwrap(); // TODO: unwrap
Ok(vec![InterserverMessage::Server(id, LoginMessage::CreatedTeam(user_id, team.id))])
},
}
}

30
src/ship/client.rs

@ -2,6 +2,7 @@ use std::collections::HashMap;
use async_std::sync::{Arc, RwLock, RwLockReadGuard};
use futures::future::BoxFuture;
use futures::stream::{FuturesOrdered, Stream, StreamExt};
use libpso::packet::ship::*;
use libpso::packet::login::Session;
@ -16,7 +17,6 @@ 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>>>>);
@ -101,6 +101,34 @@ impl Clients {
Ok(func(&mut client).await)
}
pub async fn with_match<'a, P, F, T>(&'a self, pred: P, func: F) -> Result<T, ShipError>
where
P: Fn(&ClientState) -> bool,
F: for<'b> FnOnce(ClientId, &'b ClientState) -> BoxFuture<'b, T> + Send + 'a,
{
for (id, client) in self.0.read().await.iter() {
let client = client.read().await;
if pred(&*client) {
return Ok(func(*id, &*client).await)
}
}
Err(ShipError::ClientNotFound(ClientId(0xFFFFFFFF)))
}
pub async fn with_match_mut<'a, P, F, T>(&'a self, pred: P, func: F) -> Result<T, ShipError>
where
P: Fn(&ClientState) -> bool,
F: for<'b> FnOnce(ClientId, &'b mut ClientState) -> BoxFuture<'b, T> + Send + 'a,
{
for (id, client) in self.0.read().await.iter() {
let mut client = client.write().await;
if pred(&*client) {
return Ok(func(*id, &mut *client).await)
}
}
Err(ShipError::ClientNotFound(ClientId(0xFFFFFFFF)))
}
}

1
src/ship/mod.rs

@ -13,3 +13,4 @@ pub mod packet;
pub mod quests;
pub mod shops;
pub mod trade;
pub mod teams;

1
src/ship/packet/builder/mod.rs

@ -3,6 +3,7 @@ pub mod message;
pub mod room;
pub mod quest;
pub mod ship;
pub mod team;
use libpso::character::character::Inventory;
use libpso::packet::ship::{PlayerHeader, PlayerInfo};

3
src/ship/packet/handler/auth.rs

@ -1,3 +1,4 @@
use async_std::sync::{Arc, RwLock};
use libpso::packet::login::{Login, LoginResponse, AccountStatus, Session};
use libpso::packet::ship::*;
use crate::common::serverstate::ClientId;
@ -24,7 +25,7 @@ where
Ok(user) => {
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
response.guildcard = user.id.0;
response.team_id = user.team_id.map_or(31, |ti| ti);
response.team_id = user.team_id.map_or(31, |ti| ti.0); // TODO: why is this 31?
let characters = entity_gateway.get_characters_by_user(&user).await?;
let character = characters
.get(pkt.session.character_slot as usize)

1
src/ship/packet/handler/mod.rs

@ -7,4 +7,5 @@ pub mod room;
pub mod settings;
pub mod quest;
pub mod ship;
pub mod teams;
pub mod trade;

65
src/ship/ship.rs

@ -34,6 +34,7 @@ use crate::ship::map::{MapsError, MapAreaError};
use crate::ship::packet::handler;
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop};
use crate::ship::trade::TradeState;
use crate::ship::teams::Teams;
// TODO: remove once stuff settles down
pub use crate::ship::client::*;
@ -160,6 +161,8 @@ pub enum ShipError {
RoomCreationError(#[from] room::RoomCreationError),
#[error("channel send error {0}")]
SendError(#[from] async_std::channel::SendError<ShipMessage>),
#[error("team {0}")]
TeamError(#[from] crate::ship::teams::TeamError),
}
impl<I: Into<ClientLocationError>> From<I> for ShipError {
@ -204,6 +207,7 @@ pub enum RecvShipPacket {
KeyboardConfig(KeyboardConfig),
GamepadConfig(GamepadConfig),
UpdateConfig(UpdateConfig),
CreateTeam(CreateTeam),
}
impl RecvServerPacket for RecvShipPacket {
@ -247,6 +251,7 @@ impl RecvServerPacket for RecvShipPacket {
0x4ED => Ok(RecvShipPacket::KeyboardConfig(KeyboardConfig::from_bytes(data)?)),
0x5ED => Ok(RecvShipPacket::GamepadConfig(GamepadConfig::from_bytes(data)?)),
0x7ED => Ok(RecvShipPacket::UpdateConfig(UpdateConfig::from_bytes(data)?)),
0x1EA => Ok(RecvShipPacket::CreateTeam(CreateTeam::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec()))
}
}
@ -267,6 +272,7 @@ pub enum SendShipPacket {
PlayerChat(PlayerChat),
SmallDialog(SmallDialog),
SmallLeftDialog(SmallLeftDialog),
LargeDialog(LargeDialog),
JoinRoom(JoinRoom),
AddToRoom(AddToRoom),
LeaveLobby(LeaveLobby),
@ -292,6 +298,10 @@ pub enum SendShipPacket {
CancelTrade(CancelTrade),
TradeSuccessful(TradeSuccessful),
LobbyEvent(LobbyEvent),
//TeamActionResponse(TeamActionResponse),
CreateTeamResponse(CreateTeamResponse),
ClientTeamStateChanged(ClientTeamStateChanged),
TeamInfo(Box<TeamInfo>),
}
impl SendServerPacket for SendShipPacket {
@ -310,6 +320,7 @@ impl SendServerPacket for SendShipPacket {
SendShipPacket::PlayerChat(pkt) => pkt.as_bytes(),
SendShipPacket::SmallDialog(pkt) => pkt.as_bytes(),
SendShipPacket::SmallLeftDialog(pkt) => pkt.as_bytes(),
SendShipPacket::LargeDialog(pkt) => pkt.as_bytes(),
SendShipPacket::JoinRoom(pkt) => pkt.as_bytes(),
SendShipPacket::AddToRoom(pkt) => pkt.as_bytes(),
SendShipPacket::LeaveLobby(pkt) => pkt.as_bytes(),
@ -335,6 +346,9 @@ impl SendServerPacket for SendShipPacket {
SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(),
SendShipPacket::TradeSuccessful(pkt) => pkt.as_bytes(),
SendShipPacket::LobbyEvent(pkt) => pkt.as_bytes(),
SendShipPacket::CreateTeamResponse(pkt) => pkt.as_bytes(),
SendShipPacket::ClientTeamStateChanged(pkt) => pkt.as_bytes(),
SendShipPacket::TeamInfo(pkt) => pkt.as_bytes(),
}
}
}
@ -437,9 +451,11 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
pub fn build(self) -> ShipServerState<EG> {
let blocks = std::iter::repeat_with(Block::default).take(self.num_blocks).collect(); // Block doesn't have a Clone impl which limits the easy ways to init this
let entity_gateway = self.entity_gateway.unwrap();
let clients = Clients::default();
ShipServerState {
entity_gateway: self.entity_gateway.unwrap(),
clients: Clients::default(),
entity_gateway: entity_gateway.clone(),
clients: clients.clone(),
name: self.name.unwrap_or_else(|| "NAMENOTSET".into()),
item_state: items::state::ItemState::default(),
ip: self.ip.unwrap_or_else(|| Ipv4Addr::new(127,0,0,1)),
@ -447,6 +463,7 @@ impl<EG: EntityGateway + Clone + 'static> ShipServerStateBuilder<EG> {
shops: ItemShops::default(),
blocks: Blocks(blocks),
event: self.event.unwrap_or(ShipEvent::None),
teams: Teams::new(entity_gateway, clients),
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
ship_list: Arc::new(RwLock::new(Vec::new())),
@ -487,6 +504,7 @@ pub struct ShipServerState<EG: EntityGateway + Clone + 'static> {
shops: ItemShops,
pub blocks: Blocks,
event: ShipEvent,
teams: Teams<EG>,
ip: Ipv4Addr,
port: u16,
@ -803,6 +821,9 @@ impl<EG: EntityGateway + Clone> ServerState for ShipServerState<EG> {
RecvShipPacket::GamepadConfig(gamepad_config) => {
handler::settings::gamepad_config(id, gamepad_config, &self.clients, &mut self.entity_gateway).await?
},
RecvShipPacket::CreateTeam(create_team) => {
handler::teams::create_team(id, create_team, &mut self.entity_gateway, &self.clients, &mut self.shipgate_sender).await?
},
})
}
@ -884,7 +905,43 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
*/
// TODO
Ok(Vec::new())
}
},
LoginMessage::CreatedTeam(user_id, team_id) => {
let client_id = self.clients.with_match_mut(|c| c.user.id == user_id, |client_id, client| Box::pin(async move {
client.user.team_id = Some(team_id);
client_id
})).await?;
let team_pkts = self.clients.with(client_id, |client| {
let mut teams = self.teams.clone();
Box::pin(async move {
let team_pkts = teams.get_team(client_id).await
.and_then(|team| {
let team = team.ok_or_else(|| super::teams::TeamError::ClientHasNoTeam(client_id))?;
Ok((
crate::ship::packet::builder::team::team_info(client_id, client, &team),
crate::ship::packet::builder::team::client_team_state_changed(client_id, client, &team),
))
});
team_pkts
})
}).await?;
match team_pkts {
Ok((team_info, client_team_state_changed)) => {
Ok(vec![
InterserverMessage::Client(client_id, SendShipPacket::CreateTeamResponse(CreateTeamResponse::success())),
InterserverMessage::Client(client_id, SendShipPacket::TeamInfo(Box::new(team_info))), // TODO: send to neighbors
InterserverMessage::Client(client_id, SendShipPacket::ClientTeamStateChanged(client_team_state_changed)),
])
},
Err(err) => {
Ok(vec![
InterserverMessage::Client(client_id, SendShipPacket::CreateTeamResponse(CreateTeamResponse::failure())),
//InterserverMessage::Client(client_id, SendShipPacket::LargeDialog(LargeDialog::new(format!("failed to create team: {:?}", err)))),
])
}
}
},
}
}
@ -893,8 +950,6 @@ impl<EG: EntityGateway + Clone> InterserverActor for ShipServerState<EG> {
}
async fn set_sender(&mut self, _server_id: ServerId, sender: channel::Sender<Self::SendServerMessage>) {
dbg!("setting sender!");
//self.shipgate_sender = Arc::new(Some(sender));
*self.shipgate_sender.write().await = Some(sender);
}
}
Loading…
Cancel
Save