sendgc #103
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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" }
|
||||||
|
|||||||
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"
|
||||||
|
@ -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)]
|
||||||
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NewUserAccountEntity {
|
pub struct NewUserAccountEntity {
|
||||||
@ -124,20 +129,26 @@ impl NewGuildCardDataEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement this properly
|
#[derive(Clone, Debug)]
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct GuildCardDataEntity {
|
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 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 {
|
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)
|
||||||
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!();
|
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!();
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_data.as_bytes();
|
||||||
let bytes = guildcard_data.guildcard.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()))])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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...
|
||||||
|
}
|
@ -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
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