elseware/src/ship/packet/handler/direct_message.rs

179 lines
7.8 KiB
Rust
Raw Normal View History

2020-04-22 07:14:59 -06:00
use log::warn;
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms};
use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::drops::ItemDrop;
use crate::ship::items::{ItemManager, ItemManagerError};
2020-04-22 07:14:59 -06:00
use crate::entity::gateway::EntityGateway;
use libpso::utf8_to_utf16_array;
2020-04-26 22:01:05 -06:00
use crate::ship::packet::builder;
2020-04-22 07:14:59 -06:00
fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
.filter(move |client| client.local_client.id() == target)
.map(move |client| {
(client.client, SendShipPacket::DirectMessage(msg.clone()))
}))
}
pub fn guildcard_send(id: ClientId,
guildcard_send: &GuildcardSend,
target: u32,
client_location: &ClientLocation,
clients: &Clients)
2020-04-22 07:14:59 -06:00
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
let client = clients.get(&id).unwrap();
let msg = DirectMessage{
flag: target,
msg: GameMessage::GuildcardRecv(GuildcardRecv {
client: guildcard_send.client,
target: guildcard_send.target,
guildcard: client.user.id.0,
name: utf8_to_utf16_array!(client.character.name, 0x18),
team: [0; 0x10], // TODO: teams not yet implemented
desc: utf8_to_utf16_array!(client.character.guildcard.description, 0x58),
one: 1,
language: 0, // TODO: add language flag to character
section_id: client.character.section_id.into(),
class: client.character.char_class.into(),
}),
};
send_to_client(id, target as u8, msg, &client_location)
}
2020-04-26 22:01:05 -06:00
pub fn request_item<EG>(id: ClientId,
request_item: &RequestItem,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
rooms: &mut Rooms,
item_manager: &mut ItemManager)
2020-04-26 22:01:05 -06:00
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
where
EG: EntityGateway
{
2020-04-26 22:01:05 -06:00
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
let room = rooms.get_mut(room_id.0)
2020-04-26 22:01:05 -06:00
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
.as_mut()
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
let monster = room.maps.enemy_by_id(request_item.enemy_id as usize)?;
if monster.dropped_item {
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id))
}
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
let item_drop_packets = clients_in_area.into_iter()
.filter_map(|area_client| {
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
warn!("drop is? {:?}", item_drop_type);
(area_client, item_drop_type)
})
})
.map(|(area_client, item_drop_type)| -> Result<_, ShipError> {
let item_drop = ItemDrop {
map_area: monster.map_area,
x: request_item.x,
y: request_item.y,
z: request_item.z,
item: item_drop_type,
};
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
let floor_item = item_manager.drop_item_on_local_floor(entity_gateway, &client.character, item_drop).unwrap(); // TODO: unwrap
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
client.floor_items.push(floor_item);
2020-04-26 22:01:05 -06:00
Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
})
.filter_map(|item_drop_pkt| {
// TODO: log errors here
item_drop_pkt.ok()
})
.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?
Ok(Box::new(item_drop_packets.into_iter()))
}
pub fn pickup_item<EG>(id: ClientId,
pickup_item: &PickupItem,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
rooms: &mut Rooms,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
where
EG: EntityGateway
{
#[derive(Copy, Clone)]
enum ItemFloor {
Local,
Shared
}
let mut client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
let room = rooms.get_mut(room_id.0)
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?
.as_mut()
.ok_or_else(|| ShipError::InvalidRoom(room_id.0 as u32))?;
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
let (item_index, (_, floor)) = client.floor_items.iter()
.zip(std::iter::repeat(ItemFloor::Local))
.enumerate()
.filter(|(_, (item, _))| item.id.0 == pickup_item.item_id)
.next()
.or_else(|| {
room.floor_items.iter()
.zip(std::iter::repeat(ItemFloor::Shared))
.enumerate()
.filter(|(_, (item, _))| item.id.0 == pickup_item.item_id)
.next()
})
.ok_or(ShipError::PickUpInvalidItemId(pickup_item.item_id))?;
let item = match floor {
ItemFloor::Local => client.floor_items.remove(item_index),
ItemFloor::Shared => room.floor_items.remove(item_index),
};
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
let create_item = builder::message::create_item(area_client, &item)?;
match item_manager.move_item_from_floor_to_inventory(entity_gateway, &mut client, item) {
Ok(_) => {
Ok(Box::new(Vec::new().into_iter()
.chain(clients_in_area.clone().into_iter()
.filter(move |c| {
match floor {
ItemFloor::Local => c.client == id,
ItemFloor::Shared => true,
}
})
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
}))
.chain(clients_in_area.into_iter().map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item.clone()))))
})))
)
},
Err(err) => {
// inventory full, probably
if let ItemManagerError::CouldNotAddToInventory(item) = err {
match floor {
ItemFloor::Local => client.floor_items.push(item),
ItemFloor::Shared => room.floor_items.push(item),
}
}
Ok(Box::new(None.into_iter()))
}
}
}