266 lines
12 KiB
Rust
266 lines
12 KiB
Rust
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, ClientItemId, TriggerCreateItem, FloorItem};
|
|
use crate::entity::gateway::EntityGateway;
|
|
use libpso::utf8_to_utf16_array;
|
|
use crate::ship::packet::builder;
|
|
|
|
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)
|
|
-> 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)
|
|
}
|
|
|
|
pub async 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)
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
where
|
|
EG: EntityGateway
|
|
{
|
|
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 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)| async {
|
|
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.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
|
|
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
|
|
|
// I am not able to manually specify a closure return type when also using the async keyword
|
|
let result: Result<(ClientId, SendShipPacket), ShipError> = Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
|
result
|
|
})
|
|
.map(|item_drop_pkt| async {
|
|
item_drop_pkt.await
|
|
});
|
|
|
|
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
|
|
.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))
|
|
*/
|
|
|
|
let client_and_drop = 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)
|
|
})
|
|
});
|
|
|
|
let mut item_drop_packets = Vec::new();
|
|
for (area_client, item_drop) in client_and_drop {
|
|
let item_drop = ItemDrop {
|
|
map_area: monster.map_area,
|
|
x: request_item.x,
|
|
y: request_item.y,
|
|
z: request_item.z,
|
|
item: item_drop,
|
|
};
|
|
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
|
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
|
|
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
|
|
|
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
|
|
}
|
|
|
|
Ok(Box::new(item_drop_packets.into_iter()))
|
|
}
|
|
|
|
pub async fn pickup_item<EG>(id: ClientId,
|
|
pickup_item: &PickupItem,
|
|
entity_gateway: &mut EG,
|
|
client_location: &ClientLocation,
|
|
clients: &mut Clients,
|
|
item_manager: &mut ItemManager)
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
where
|
|
EG: EntityGateway
|
|
{
|
|
let 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 clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
|
|
let item = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?;
|
|
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
|
|
let create_item = match item {
|
|
FloorItem::Meseta(_) => None,
|
|
_ => Some(builder::message::create_item(area_client, &item)?),
|
|
};
|
|
|
|
match item_manager.character_picks_up_item(entity_gateway, &mut client.character, item).await {
|
|
Ok(trigger_create_item) => {
|
|
Ok(Box::new(Vec::new().into_iter()
|
|
.chain(clients_in_area.clone().into_iter()
|
|
.map(move |c| {
|
|
(c.client, SendShipPacket::Message(Message::new(GameMessage::RemoveItemFromFloor(remove_item.clone()))))
|
|
}))
|
|
.chain(clients_in_area.into_iter().
|
|
filter_map(move |c| {
|
|
match trigger_create_item {
|
|
TriggerCreateItem::Yes => create_item.clone().map(|ci| (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(ci))))),
|
|
_ => None
|
|
}
|
|
}
|
|
)))
|
|
)
|
|
},
|
|
Err(err) => {
|
|
warn!("character {:?} could not pick up item: {:?}", client.character.id, err);
|
|
Ok(Box::new(None.into_iter()))
|
|
},
|
|
}
|
|
}
|
|
|
|
pub async fn request_box_item<EG>(id: ClientId,
|
|
box_drop_request: &BoxDropRequest,
|
|
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
|
|
{
|
|
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 box_object = room.maps.object_by_id(box_drop_request.object_id as usize)?;
|
|
if box_object.dropped_item {
|
|
return Err(ShipError::BoxAlreadyDroppedItem(id, box_drop_request.object_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_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
|
warn!("drop is? {:?}", item_drop_type);
|
|
(area_client, item_drop_type)
|
|
})
|
|
})
|
|
.map(async move |(area_client, item_drop_type)| -> Result<_, ShipError> {
|
|
let item_drop = ItemDrop {
|
|
map_area: box_object.map,
|
|
x: box_drop_request.x,
|
|
y: 0.0,
|
|
z: box_drop_request.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.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
|
|
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
|
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?*/
|
|
;
|
|
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
|
|
.filter_map(|item_drop_pkt| {
|
|
// TODO: log errors here
|
|
item_drop_pkt.ok()
|
|
});
|
|
|
|
Ok(Box::new(item_drop_packets))
|
|
*/
|
|
|
|
let client_and_drop = clients_in_area.into_iter()
|
|
.filter_map(|area_client| {
|
|
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
|
|
warn!("drop is? {:?}", item_drop_type);
|
|
(area_client, item_drop_type)
|
|
})
|
|
});
|
|
|
|
let mut item_drop_packets = Vec::new();
|
|
for (area_client, item_drop) in client_and_drop {
|
|
let item_drop = ItemDrop {
|
|
map_area: box_object.map,
|
|
x: box_drop_request.x,
|
|
y: 0.0,
|
|
z: box_drop_request.z,
|
|
item: item_drop,
|
|
};
|
|
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
|
|
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await?; // TODO: unwrap
|
|
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
|
|
item_drop_packets.push((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
|
|
}
|
|
|
|
Ok(Box::new(item_drop_packets.into_iter()))
|
|
}
|