sendgc #103
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1001,7 +1001,6 @@ checksum = "739e9d7726dc32173fed2d69d17eef3c54682169e4e20ff1d0a45dcd37063cef"
|
||||
[[package]]
|
||||
name = "libpso"
|
||||
version = "0.1.0"
|
||||
source = "git+http://git.sharnoth.com/jake/libpso#892d2ed220369f0ff7b7530fa734e722c2b21c2c"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"psopacket",
|
||||
@ -1401,7 +1400,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "psopacket"
|
||||
version = "1.0.0"
|
||||
source = "git+http://git.sharnoth.com/jake/libpso#892d2ed220369f0ff7b7530fa734e722c2b21c2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -5,7 +5,7 @@ authors = ["Jake Probst <jake.probst@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||
libpso = { git = "http://git.sharnoth.com/jake/libpso", branch = "sendgc" }
|
||||
|
||||
async-std = { version = "1.9.0", features = ["unstable", "attributes"] }
|
||||
futures = "0.3.5"
|
||||
rand = "0.7.3"
|
||||
|
@ -1,6 +1,7 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use libpso::character::settings;
|
||||
use libpso::character::guildcard;
|
||||
use libpso::packet::ship::{GuildcardAccept};
|
||||
|
||||
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
|
||||
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
|
||||
@ -9,9 +10,13 @@ pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
|
||||
pub struct UserAccountId(pub u32);
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UserSettingsId(pub u32);
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct GuildCardDataId(pub u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
jake
commented
TODO: remove TODO TODO: remove TODO
|
||||
pub enum GuildcardError {
|
||||
GuildcardAlreadyFriend(UserAccountId),
|
||||
GuildcardAlreadyBlocked(UserAccountId),
|
||||
jake
commented
should be should be `UserAccountId` not `u32` (or whatever proper not-number type is expected)
|
||||
GuildcardListFull,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NewUserAccountEntity {
|
||||
@ -124,20 +129,26 @@ impl NewGuildCardDataEntity {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement this properly
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GuildCardDataEntity {
|
||||
jake
commented
TODO: determine if this was implemented properly TODO: determine if this was implemented properly
|
||||
pub id: GuildCardDataId,
|
||||
pub user_id: UserAccountId,
|
||||
pub guildcard: guildcard::GuildCardData,
|
||||
pub guildcard_data: Box<guildcard::GuildCardData>,
|
||||
}
|
||||
|
||||
jake
commented
why is this boxed? why is this boxed?
andy
commented
`GuildCardData` is >50kb cus it contains the friendlist + blocklist.
|
||||
impl GuildCardDataEntity {
|
||||
pub fn new(user_id: UserAccountId) -> GuildCardDataEntity {
|
||||
GuildCardDataEntity {
|
||||
id: GuildCardDataId(0),
|
||||
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)
|
||||
jake
commented
`.iter().position()` might be preferable to `.enumerate().find()`?
|
||||
.ok_or(GuildcardError::GuildcardListFull)?;
|
||||
self.guildcard_data.friends[next_open_spot] = guildcard::GuildCard::from(new_friend);
|
||||
Ok(()) // TODO: implement a real error
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,10 @@ pub trait EntityGateway: Send + Sync + Clone {
|
||||
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> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ pub struct InMemoryGateway {
|
||||
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
|
||||
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
|
||||
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
|
||||
guildcard_entities: Arc<Mutex<BTreeMap<UserAccountId, GuildCardDataEntity>>>,
|
||||
}
|
||||
|
||||
impl Default for InMemoryGateway {
|
||||
@ -37,6 +38,7 @@ impl Default for InMemoryGateway {
|
||||
equips: Arc::new(Mutex::new(BTreeMap::new())),
|
||||
mag_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 {
|
||||
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
|
||||
let mut users = self.users.lock().unwrap();
|
||||
let mut guildcards = self.guildcard_entities.lock().unwrap();
|
||||
let id = users
|
||||
.iter()
|
||||
.fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
|
||||
@ -109,7 +112,7 @@ impl EntityGateway for InMemoryGateway {
|
||||
id: UserAccountId(id),
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
guildcard: user.guildcard,
|
||||
guildcard: id,
|
||||
team_id: user.team_id,
|
||||
banned_until: user.banned_until,
|
||||
muted_until: user.muted_until,
|
||||
@ -120,7 +123,11 @@ impl EntityGateway for InMemoryGateway {
|
||||
at_character: false,
|
||||
at_ship: false,
|
||||
};
|
||||
|
||||
let guildcard = GuildCardDataEntity::new(UserAccountId(id)); // TODO: NewGuildcardDataEntity ?
|
||||
users.insert(user.id, user.clone());
|
||||
guildcards.insert(user.id, guildcard);
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
@ -213,8 +220,14 @@ impl EntityGateway for InMemoryGateway {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: ok_or a real error ?
|
||||
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> {
|
||||
@ -347,4 +360,10 @@ impl EntityGateway for InMemoryGateway {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -278,9 +278,8 @@ impl EntityGateway for PostgresGateway {
|
||||
|
||||
async fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> Result<GuildCardDataEntity, GatewayError> {
|
||||
Ok(GuildCardDataEntity {
|
||||
id: GuildCardDataId(0),
|
||||
user_id: user.id,
|
||||
guildcard: guildcard::GuildCardData::default(),
|
||||
guildcard_data: Box::new(guildcard::GuildCardData::default()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -441,12 +441,10 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
||||
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 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);
|
||||
crc.write(&bytes[..]);
|
||||
client.guildcard_data_buffer = Some(bytes.to_vec());
|
||||
|
||||
Ok(vec![SendCharacterPacket::GuildcardDataHeader(GuildcardDataHeader::new(bytes.len(), crc.sum32()))])
|
||||
}
|
||||
|
||||
|
@ -45,3 +45,17 @@ pub async fn write_infoboard<EG: EntityGateway>(id: ClientId,
|
||||
entity_gateway.save_character(&client.character).await.unwrap();
|
||||
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...
|
||||
}
|
@ -114,6 +114,7 @@ pub enum RecvShipPacket {
|
||||
RequestShipBlockList(RequestShipBlockList),
|
||||
ItemsToTrade(ItemsToTrade),
|
||||
TradeConfirmed(TradeConfirmed),
|
||||
GuildcardAccept(GuildcardAccept),
|
||||
}
|
||||
|
||||
impl RecvServerPacket for RecvShipPacket {
|
||||
@ -155,6 +156,7 @@ impl RecvServerPacket for RecvShipPacket {
|
||||
0xD2 => Ok(RecvShipPacket::TradeConfirmed(TradeConfirmed::from_bytes(data)?)),
|
||||
0xE7 => Ok(RecvShipPacket::FullCharacterData(Box::new(FullCharacterData::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()))
|
||||
}
|
||||
}
|
||||
@ -743,6 +745,9 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
||||
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?
|
||||
},
|
||||
RecvShipPacket::GuildcardAccept(guildcard_accept) => {
|
||||
handler::communication::accept_guildcard(id, guildcard_accept, &mut self.clients, &mut self.entity_gateway).await
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
55
tests/test_communication.rs
Normal file
55
tests/test_communication.rs
Normal file
@ -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…
x
Reference in New Issue
Block a user
merge this in libpso