sendgc #103

Closed
andy wants to merge 5 commits from sendgc into master
  1. 2
      Cargo.lock
  2. 2
      Cargo.toml
  3. 27
      src/entity/account.rs
  4. 4
      src/entity/gateway/entitygateway.rs
  5. 23
      src/entity/gateway/inmemory.rs
  6. 3
      src/entity/gateway/postgres/postgres.rs
  7. 4
      src/login/character.rs
  8. 14
      src/ship/packet/handler/communication.rs
  9. 5
      src/ship/ship.rs
  10. 55
      tests/test_communication.rs

2
Cargo.lock

@ -1001,7 +1001,6 @@ checksum = "739e9d7726dc32173fed2d69d17eef3c54682169e4e20ff1d0a45dcd37063cef"
[[package]] [[package]]
name = "libpso" name = "libpso"
version = "0.1.0" version = "0.1.0"
source = "git+http://git.sharnoth.com/jake/libpso#892d2ed220369f0ff7b7530fa734e722c2b21c2c"
dependencies = [ dependencies = [
"chrono", "chrono",
"psopacket", "psopacket",
@ -1401,7 +1400,6 @@ dependencies = [
[[package]] [[package]]
name = "psopacket" name = "psopacket"
version = "1.0.0" version = "1.0.0"
source = "git+http://git.sharnoth.com/jake/libpso#892d2ed220369f0ff7b7530fa734e722c2b21c2c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

2
Cargo.toml

@ -5,7 +5,7 @@ authors = ["Jake Probst <jake.probst@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
libpso = { git = "http://git.sharnoth.com/jake/libpso", branch = "sendgc" }
Review

merge this in libpso

merge this in libpso
async-std = { version = "1.9.0", features = ["unstable", "attributes"] } async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
futures = "0.3.5" futures = "0.3.5"
rand = "0.7.3" rand = "0.7.3"

27
src/entity/account.rs

@ -1,6 +1,7 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use libpso::character::settings; use libpso::character::settings;
use libpso::character::guildcard; use libpso::character::guildcard;
use libpso::packet::ship::{GuildcardAccept};
pub const USERFLAG_NEWCHAR: u32 = 0x00000001; pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002; pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
@ -9,9 +10,13 @@ pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
pub struct UserAccountId(pub u32); pub struct UserAccountId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UserSettingsId(pub u32); pub struct UserSettingsId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GuildCardDataId(pub u32);
#[derive(Debug)]
Review

TODO: remove TODO

TODO: remove TODO
pub enum GuildcardError {
GuildcardAlreadyFriend(UserAccountId),
GuildcardAlreadyBlocked(UserAccountId),
GuildcardListFull,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NewUserAccountEntity { pub struct NewUserAccountEntity {
@ -124,20 +129,26 @@ impl NewGuildCardDataEntity {
} }
} }
// TODO: implement this properly
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct GuildCardDataEntity { pub struct GuildCardDataEntity {
pub id: GuildCardDataId,
pub user_id: UserAccountId, pub user_id: UserAccountId,
pub guildcard: guildcard::GuildCardData,
pub guildcard_data: Box<guildcard::GuildCardData>,
} }
impl GuildCardDataEntity { impl GuildCardDataEntity {
pub fn new(user_id: UserAccountId) -> GuildCardDataEntity { pub fn new(user_id: UserAccountId) -> GuildCardDataEntity {
GuildCardDataEntity { GuildCardDataEntity {
id: GuildCardDataId(0),
user_id, user_id,
guildcard: guildcard::GuildCardData::default(),
guildcard_data: Box::new(guildcard::GuildCardData::default()),
} }
} }
pub fn add_friend(&mut self, new_friend: &GuildcardAccept) -> Result<(), GuildcardError> {
let next_open_spot = self.guildcard_data.friends
.iter()
.position(|&g| g.id == 0)
.ok_or(GuildcardError::GuildcardListFull)?;
self.guildcard_data.friends[next_open_spot] = guildcard::GuildCard::from(new_friend);
Ok(()) // TODO: implement a real error
}
} }

4
src/entity/gateway/entitygateway.rs

@ -61,6 +61,10 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn set_guild_card(&mut self, _id: UserAccountId, _gc_data: GuildCardDataEntity) -> Result<(), GatewayError> {
unimplemented!();
}
async fn create_item(&mut self, _item: NewItemEntity) -> Result<ItemEntity, GatewayError> { async fn create_item(&mut self, _item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
unimplemented!(); unimplemented!();
} }

23
src/entity/gateway/inmemory.rs

@ -21,6 +21,7 @@ pub struct InMemoryGateway {
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>, equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>, mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>, weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
guildcard_entities: Arc<Mutex<BTreeMap<UserAccountId, GuildCardDataEntity>>>,
} }
impl Default for InMemoryGateway { impl Default for InMemoryGateway {
@ -37,6 +38,7 @@ impl Default for InMemoryGateway {
equips: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
guildcard_entities: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -101,6 +103,7 @@ impl InMemoryGateway {
impl EntityGateway for InMemoryGateway { impl EntityGateway for InMemoryGateway {
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> { async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
let mut users = self.users.lock().unwrap(); let mut users = self.users.lock().unwrap();
let mut guildcards = self.guildcard_entities.lock().unwrap();
let id = users let id = users
.iter() .iter()
.fold(0, |sum, (i, _)| std::cmp::max(sum, i.0)) .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
@ -109,7 +112,7 @@ impl EntityGateway for InMemoryGateway {
id: UserAccountId(id), id: UserAccountId(id),
username: user.username, username: user.username,
password: user.password, password: user.password,
guildcard: user.guildcard,
guildcard: id,
team_id: user.team_id, team_id: user.team_id,
banned_until: user.banned_until, banned_until: user.banned_until,
muted_until: user.muted_until, muted_until: user.muted_until,
@ -120,7 +123,11 @@ impl EntityGateway for InMemoryGateway {
at_character: false, at_character: false,
at_ship: false, at_ship: false,
}; };
let guildcard = GuildCardDataEntity::new(UserAccountId(id)); // TODO: NewGuildcardDataEntity ?
users.insert(user.id, user.clone()); users.insert(user.id, user.clone());
guildcards.insert(user.id, guildcard);
Ok(user) Ok(user)
} }
@ -213,8 +220,14 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
// TODO: ok_or a real error ?
async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> { async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
Ok(GuildCardDataEntity::new(user.id))
let guildcards = self.guildcard_entities.lock().unwrap();
guildcards
.iter()
.find(|(_, g)| g.user_id == user.id)
.map(|(_, g)| g.clone())
.ok_or(GatewayError::Error)
} }
async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> { async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
@ -347,4 +360,10 @@ impl EntityGateway for InMemoryGateway {
Err(GatewayError::Error) Err(GatewayError::Error)
} }
} }
async fn set_guild_card(&mut self, id: UserAccountId, gc_data: GuildCardDataEntity) -> Result<(), GatewayError> {
let mut guildcard = self.guildcard_entities.lock().unwrap();
guildcard.insert(id, gc_data);
Ok(())
}
} }

3
src/entity/gateway/postgres/postgres.rs

@ -278,9 +278,8 @@ impl EntityGateway for PostgresGateway {
async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> { async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
Ok(GuildCardDataEntity { Ok(GuildCardDataEntity {
id: GuildCardDataId(0),
user_id: user.id, user_id: user.id,
guildcard: guildcard::GuildCardData::default(),
guildcard_data: Box::new(guildcard::GuildCardData::default()),
}) })
} }

4
src/login/character.rs

@ -441,12 +441,10 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
async fn guildcard_data_header(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, anyhow::Error> { async fn guildcard_data_header(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, anyhow::Error> {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
let guildcard_data = self.entity_gateway.get_guild_card_data_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadGuildcard)?; let guildcard_data = self.entity_gateway.get_guild_card_data_by_user(client.user.as_ref().unwrap()).await.map_err(|_| CharacterError::CouldNotLoadGuildcard)?;
let bytes = guildcard_data.guildcard.as_bytes();
let bytes = guildcard_data.guildcard_data.as_bytes();
let mut crc = crc32::Digest::new(crc32::IEEE); let mut crc = crc32::Digest::new(crc32::IEEE);
crc.write(&bytes[..]); crc.write(&bytes[..]);
client.guildcard_data_buffer = Some(bytes.to_vec()); client.guildcard_data_buffer = Some(bytes.to_vec());
Ok(vec![SendCharacterPacket::GuildcardDataHeader(GuildcardDataHeader::new(bytes.len(), crc.sum32()))]) Ok(vec![SendCharacterPacket::GuildcardDataHeader(GuildcardDataHeader::new(bytes.len(), crc.sum32()))])
} }

14
src/ship/packet/handler/communication.rs

@ -45,3 +45,17 @@ pub async fn write_infoboard<EG: EntityGateway>(id: ClientId,
entity_gateway.save_character(&client.character).await.unwrap(); entity_gateway.save_character(&client.character).await.unwrap();
Box::new(None.into_iter()) Box::new(None.into_iter())
} }
// TODO: return Result<Box<...>> so ship can do await? and catch errors?
pub async fn accept_guildcard<EG: EntityGateway>(id: ClientId,
accepted_card: &GuildcardAccept,
clients: &mut Clients,
entity_gateway: &mut EG)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
let mut gc_data = entity_gateway.get_guild_card_data_by_user(&client.user).await.unwrap();
gc_data.add_friend(accepted_card).unwrap();
entity_gateway.set_guild_card(client.user.id, gc_data).await.unwrap();
Box::new(None.into_iter()) // TODO: does the server need to return anything to any client? everything seems to work fine like this...
}

5
src/ship/ship.rs

@ -114,6 +114,7 @@ pub enum RecvShipPacket {
RequestShipBlockList(RequestShipBlockList), RequestShipBlockList(RequestShipBlockList),
ItemsToTrade(ItemsToTrade), ItemsToTrade(ItemsToTrade),
TradeConfirmed(TradeConfirmed), TradeConfirmed(TradeConfirmed),
GuildcardAccept(GuildcardAccept),
} }
impl RecvServerPacket for RecvShipPacket { impl RecvServerPacket for RecvShipPacket {
@ -155,6 +156,7 @@ impl RecvServerPacket for RecvShipPacket {
0xD2 => Ok(RecvShipPacket::TradeConfirmed(TradeConfirmed::from_bytes(data)?)), 0xD2 => Ok(RecvShipPacket::TradeConfirmed(TradeConfirmed::from_bytes(data)?)),
0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))), 0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::from_bytes(data)?))),
0x1ED => Ok(RecvShipPacket::SaveOptions(SaveOptions::from_bytes(data)?)), 0x1ED => Ok(RecvShipPacket::SaveOptions(SaveOptions::from_bytes(data)?)),
0x4E8 => Ok(RecvShipPacket::GuildcardAccept(GuildcardAccept::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec())) _ => Err(PacketParseError::WrongPacketForServerType(u16::from_le_bytes([data[2], data[3]]), data.to_vec()))
} }
} }
@ -743,6 +745,9 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
let block = self.blocks.with_client(id, &self.clients)?; let block = self.blocks.with_client(id, &self.clients)?;
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await? handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
}, },
RecvShipPacket::GuildcardAccept(guildcard_accept) => {
handler::communication::accept_guildcard(id, guildcard_accept, &mut self.clients, &mut self.entity_gateway).await
},
}) })
} }

55
tests/test_communication.rs

@ -0,0 +1,55 @@
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use libpso::packet::ship::*;
#[path = "common.rs"]
mod common;
use common::*;
#[async_std::test]
async fn test_guildcard_add_friend() {
let mut entity_gateway = InMemoryGateway::default();
let (user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
// Accept friend request from "Test Char 2"
ship.handle(ClientId(1), &RecvShipPacket::GuildcardAccept(GuildcardAccept {
id: 2,
name: [84, 101, 115, 116, 32, 67, 104, 97, 114, 32, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
team: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
desc: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
one: 1,
language: 0,
section_id: 0,
class: 0,
})).await.unwrap().for_each(drop);
let friendlist = entity_gateway.get_guild_card_data_by_user(&user1).await.unwrap();
assert!(friendlist.guildcard_data.friends[0].name == [84, 101, 115, 116, 32, 67, 104, 97, 114, 32, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
}
/*
TODO: actually write these tests at some point. also add a test for transmute/repr(C)?
#[async_std::test]
async fn test_guildcard_block_rival() {}
#[async_std::test]
async fn test_guildcard_write_comment() {}
#[async_std::test]
async fn test_player_chat() {}
#[async_std::test]
async fn test_update_infoboard() {}
*/
Loading…
Cancel
Save