Quadrant System beta test started

This commit is contained in:
xavo95 2024-11-03 20:44:02 +01:00
parent 46e57fe2dc
commit 90179f1001
Signed by: xavo95
GPG key ID: CBF8ADED6DEBB783
15 changed files with 538 additions and 60 deletions

20
Cargo.lock generated
View file

@ -402,7 +402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"hashbrown", "hashbrown 0.14.5",
"lock_api", "lock_api",
"once_cell", "once_cell",
"parking_lot_core", "parking_lot_core",
@ -416,7 +416,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"hashbrown", "hashbrown 0.14.5",
"lock_api", "lock_api",
"once_cell", "once_cell",
"parking_lot_core", "parking_lot_core",
@ -724,13 +724,19 @@ dependencies = [
"allocator-api2", "allocator-api2",
] ]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]] [[package]]
name = "hashlink" name = "hashlink"
version = "0.9.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [ dependencies = [
"hashbrown", "hashbrown 0.14.5",
] ]
[[package]] [[package]]
@ -890,12 +896,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.4.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.15.0",
] ]
[[package]] [[package]]
@ -1927,7 +1933,7 @@ dependencies = [
"futures-intrusive", "futures-intrusive",
"futures-io", "futures-io",
"futures-util", "futures-util",
"hashbrown", "hashbrown 0.14.5",
"hashlink", "hashlink",
"hex", "hex",
"indexmap", "indexmap",

View file

@ -1,15 +1,19 @@
use shorekeeper_protocol::EntityConfigType; use shorekeeper_protocol::{EEntityType, EntityConfigType, EntityState};
use crate::logic::ecs::component::Component; use crate::logic::ecs::component::Component;
pub struct EntityConfig { pub struct EntityConfig {
pub config_id: i32, pub config_id: i32,
pub config_type: EntityConfigType, pub config_type: EntityConfigType,
pub entity_type: EEntityType,
pub entity_state: EntityState
} }
impl Component for EntityConfig { impl Component for EntityConfig {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) { fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.config_id = self.config_id; pb.config_id = self.config_id;
pb.config_type = self.config_type.into(); pb.config_type = self.config_type.into();
pb.entity_type = self.entity_type.into();
pb.entity_state = self.entity_state.into();
} }
} }

View file

@ -0,0 +1,25 @@
use crate::logic::ecs::component::Component;
use shorekeeper_protocol::entity_component_pb::ComponentPb;
use shorekeeper_protocol::{DFsm, DFsmBlackBoard, EntityComponentPb, EntityFsmComponentPb, FsmCustomBlackboardDatas};
pub struct Fsm {
pub fsms: Vec<DFsm>,
pub hash_code: i32,
pub common_hash_code: i32,
pub black_board: Vec<DFsmBlackBoard>,
pub fsm_custom_blackboard_datas: Option<FsmCustomBlackboardDatas>,
}
impl Component for Fsm {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.component_pbs.push(EntityComponentPb {
component_pb: Some(ComponentPb::EntityFsmComponentPb(EntityFsmComponentPb {
fsms: self.fsms.clone(),
hash_code: self.hash_code,
common_hash_code: self.common_hash_code,
black_board: self.black_board.clone(),
fsm_custom_blackboard_datas: self.fsm_custom_blackboard_datas.clone(),
})),
})
}
}

View file

@ -7,6 +7,8 @@ mod player_entity_marker;
mod position; mod position;
mod visibility; mod visibility;
mod vision_skill; mod vision_skill;
mod monster_ai;
mod fsm;
pub use attribute::Attribute; pub use attribute::Attribute;
pub use entity_config::EntityConfig; pub use entity_config::EntityConfig;
@ -17,3 +19,5 @@ pub use player_entity_marker::PlayerEntityMarker;
pub use position::Position; pub use position::Position;
pub use visibility::Visibility; pub use visibility::Visibility;
pub use vision_skill::VisionSkill; pub use vision_skill::VisionSkill;
pub use monster_ai::MonsterAi;
pub use fsm::Fsm;

View file

@ -0,0 +1,23 @@
use crate::logic::ecs::component::Component;
use shorekeeper_protocol::entity_component_pb::ComponentPb;
use shorekeeper_protocol::{EntityComponentPb, MonsterAiComponentPb};
pub struct MonsterAi {
pub weapon_id: i32,
pub hatred_group_id: i64,
pub ai_team_init_id: i32,
pub combat_message_id: i64,
}
impl Component for MonsterAi {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.component_pbs.push(EntityComponentPb {
component_pb: Some(ComponentPb::MonsterAiComponentPb(MonsterAiComponentPb {
weapon_id: self.weapon_id,
hatred_group_id: self.hatred_group_id,
ai_team_init_id: self.ai_team_init_id,
combat_message_id: self.combat_message_id,
})),
})
}
}

View file

@ -32,6 +32,8 @@ impl_component_container! {
Movement; Movement;
Equip; Equip;
VisionSkill; VisionSkill;
MonsterAi;
Fsm;
} }
pub trait Component { pub trait Component {

View file

@ -1,10 +1,9 @@
use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components}; use shorekeeper_protocol::{EntityActiveRequest, EntityActiveResponse, EntityLoadCompleteRequest,
use shorekeeper_protocol::entity_component_pb::ComponentPb; EntityLoadCompleteResponse, EntityOnLandedRequest,
use shorekeeper_protocol::{ EntityOnLandedResponse, EntityPb, EntityPositionRequest,
EntityActiveRequest, EntityActiveResponse, EntityComponentPb, EntityLoadCompleteRequest, EntityPositionResponse, ErrorCode, MovePackagePush};
EntityLoadCompleteResponse, EntityOnLandedRequest, EntityOnLandedResponse,
EntityPositionRequest, EntityPositionResponse, ErrorCode, MovePackagePush, use crate::{logic, logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
};
pub fn on_entity_active_request( pub fn on_entity_active_request(
player: &Player, player: &Player,
@ -23,18 +22,27 @@ pub fn on_entity_active_request(
return; return;
}; };
if let (Some(position), Some(attribute)) = let component_pbs = {
let mut pb = EntityPb {
id: request.entity_id,
..Default::default()
};
world.get_entity_components(request.entity_id as i32)
.into_iter()
.for_each(|comp| comp.set_pb_data(&mut pb));
pb.component_pbs
};
// TODO: Remove attribute
if let (Some(position), Some(_attribute)) =
query_components!(world, request.entity_id, Position, Attribute) query_components!(world, request.entity_id, Position, Attribute)
{ {
response.is_visible = true; response.is_visible = true;
response.pos = Some(position.0.get_position_protobuf()); response.pos = Some(position.0.get_position_protobuf());
response.rot = Some(position.0.get_rotation_protobuf()); response.rot = Some(position.0.get_rotation_protobuf());
response.component_pbs.push(EntityComponentPb { response.component_pbs.extend_from_slice(&component_pbs);
component_pb: Some(ComponentPb::AttributeComponent(
attribute.build_entity_attribute(),
)),
});
response.error_code = ErrorCode::Success.into(); response.error_code = ErrorCode::Success.into();
} else { } else {
@ -85,29 +93,47 @@ pub fn on_entity_load_complete_request(
} }
pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) { pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) {
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
for moving_entity in push.moving_entities { for moving_entity in push.moving_entities {
if !world.is_in_all_world_map(moving_entity.entity_id as i32) { // Query components borrows world component so lets wrap it
tracing::debug!( {
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
if !world.is_in_all_world_map(moving_entity.entity_id as i32) {
tracing::debug!(
"MovePackage: entity with id {} doesn't exist", "MovePackage: entity with id {} doesn't exist",
moving_entity.entity_id moving_entity.entity_id
); );
continue; continue;
} }
let Some(mut movement) = query_components!(world, moving_entity.entity_id, Movement).0 let Some(mut movement) = query_components!(world, moving_entity.entity_id, Movement).0
else { else {
tracing::warn!( tracing::warn!(
"MovePackage: entity {} doesn't have movement component", "MovePackage: entity {} doesn't have movement component",
moving_entity.entity_id moving_entity.entity_id
); );
continue; continue;
}; };
movement movement
.pending_movement_vec .pending_movement_vec
.extend(moving_entity.move_infos); .extend(moving_entity.move_infos);
}
// TODO: review instance id vs map id in world
let map = logic::utils::quadrant_util::get_map(player.location.instance_id);
let quadrant_id = map.get_quadrant_id(
player.location.position.position.x * 100.0,
player.location.position.position.y * 100.0,
);
// TODO: This may require some changes for Co-Op
if quadrant_id != player.quadrant_id {
let (entities_to_remove, entities_to_add) = map.get_update_entities(player.quadrant_id, quadrant_id);
player.quadrant_id = quadrant_id;
logic::utils::world_util::remove_entities(player, &entities_to_remove);
logic::utils::world_util::add_entities(player, &entities_to_add);
}
} }
} }

View file

@ -1,3 +1,4 @@
use shorekeeper_data::RawVectorData;
use shorekeeper_protocol::{Rotator, TransformData}; use shorekeeper_protocol::{Rotator, TransformData};
use super::Vector3f; use super::Vector3f;
@ -47,3 +48,12 @@ impl Transform {
} }
} }
} }
impl From<&[RawVectorData]> for Transform {
fn from(transform: &[RawVectorData]) -> Self {
Self {
position: Vector3f::from(&transform[0]),
..Default::default()
}
}
}

View file

@ -1,3 +1,4 @@
use shorekeeper_data::RawVectorData;
use shorekeeper_protocol::{Vector, VectorData}; use shorekeeper_protocol::{Vector, VectorData};
#[derive(Default, Clone, PartialEq, Debug)] #[derive(Default, Clone, PartialEq, Debug)]
@ -40,3 +41,13 @@ impl Vector3f {
} }
} }
} }
impl From<&RawVectorData> for Vector3f {
fn from(transform: &RawVectorData) -> Self {
Self {
x: transform.x / 100.0,
y: transform.y / 100.0,
z: transform.z / 100.0,
}
}
}

View file

@ -1,6 +1,6 @@
use common::time_util; use common::time_util;
use shorekeeper_protocol::{ use shorekeeper_protocol::{
EEntityType, ERemoveEntityType, EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo, EEntityType, EntityState, ERemoveEntityType, EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo,
EntityRemoveNotify, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FormationRoleInfo, EntityRemoveNotify, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FormationRoleInfo,
GroupFormation, ItemPkgOpenNotify, LivingStatus, PbGetRoleListNotify, PlayerBasicData, GroupFormation, ItemPkgOpenNotify, LivingStatus, PbGetRoleListNotify, PlayerBasicData,
PlayerFightFormations, PlayerRoleData, PlayerSaveData, ProtocolUnit, UpdateFormationNotify, PlayerFightFormations, PlayerRoleData, PlayerSaveData, ProtocolUnit, UpdateFormationNotify,
@ -55,6 +55,7 @@ pub struct Player {
// Runtime // Runtime
pub world: Rc<RefCell<World>>, pub world: Rc<RefCell<World>>,
pub last_save_time: u64, pub last_save_time: u64,
pub quadrant_id: u64,
} }
impl Player { impl Player {
@ -312,6 +313,7 @@ impl Player {
.unwrap_or_default(), .unwrap_or_default(),
world: Rc::new(RefCell::new(World::new())), world: Rc::new(RefCell::new(World::new())),
last_save_time: time_util::unix_timestamp(), last_save_time: time_util::unix_timestamp(),
quadrant_id: 0,
} }
} }

View file

@ -19,10 +19,7 @@ use std::{
use super::{ecs::world::World, player::Player, utils::world_util}; use super::{ecs::world::World, player::Player, utils::world_util};
use crate::logic::ecs::world::WorldEntity; use crate::logic::ecs::world::WorldEntity;
use crate::{ use crate::{logic, player_save_task::{self, PlayerSaveReason}, session::Session};
player_save_task::{self, PlayerSaveReason},
session::Session,
};
const WATER_MASK: &str = include_str!("../../watermask-rr.js"); const WATER_MASK: &str = include_str!("../../watermask-rr.js");
const UID_FIX: &str = include_str!("../../uidfix.js"); const UID_FIX: &str = include_str!("../../uidfix.js");
@ -200,6 +197,16 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
content: CENSORSHIP_FIX.to_string(), content: CENSORSHIP_FIX.to_string(),
}); });
let map = logic::utils::quadrant_util::get_map(player.location.instance_id);
let quadrant_id = map.get_quadrant_id(
player.location.position.position.x * 100.0,
player.location.position.position.y * 100.0,
);
player.quadrant_id = quadrant_id;
let entities = map.get_initial_entities(quadrant_id);
world_util::add_entities(&player, &entities);
drop(player); drop(player);
state state
@ -224,6 +231,7 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
let _ = state.worlds.remove(&player_id); let _ = state.worlds.remove(&player_id);
// TODO: kick co-op players from removed world // TODO: kick co-op players from removed world
// TODO: Remove all entitie
player_save_task::push( player_save_task::push(
player_id, player_id,

View file

@ -1,3 +1,4 @@
pub mod entity_serializer; pub mod entity_serializer;
pub mod load_role_info; pub mod load_role_info;
pub mod world_util; pub mod world_util;
pub mod quadrant_util;

View file

@ -0,0 +1,230 @@
use std::collections::HashMap;
use std::sync::OnceLock;
use shorekeeper_data::LevelEntityConfigData;
#[derive(Clone)]
struct MapBounds {
x_max: f32,
x_min: f32,
x_translate: f32,
y_max: f32,
y_min: f32,
y_translate: f32,
}
#[derive(Default)]
struct Quadrant {
entities: HashMap<i64, &'static LevelEntityConfigData>,
}
pub struct Map {
bounds: MapBounds,
width: u64,
height: u64,
quadrants: HashMap<u64, Quadrant>,
}
// TODO: Make it configurable?
const EDGE_SIZE: f32 = 1000000f32;
const EDGE_CHECK: f32 = EDGE_SIZE * 3.0f32;
pub(crate) static MAP_TABLE: OnceLock<HashMap<i32, Map>> = OnceLock::new();
impl MapBounds {
fn find_max_min(slice: &[&LevelEntityConfigData]) -> (Self, bool) {
let mut x_max = 0f32;
let mut x_min = 0f32;
let mut y_max = 0f32;
let mut y_min = 0f32;
// Find max and min coordinates
for entity in slice.iter() {
if entity.transform[0].x < x_min { x_min = entity.transform[0].x }
if entity.transform[0].x > x_max { x_max = entity.transform[0].x }
if entity.transform[0].y < y_min { y_min = entity.transform[0].y }
if entity.transform[0].y > y_max { y_max = entity.transform[0].y }
}
if (f32::abs(x_max - x_min) < EDGE_CHECK) || (f32::abs(y_max - y_min) < EDGE_CHECK) {
// TODO: Handle this special case, since all entities fit, no need for quadrant
// Move everything to positive coordinates to prevent corner cases
let (x_max, x_min, x_translate) = recenter_map(x_max, x_min);
let (y_max, y_min, y_translate) = recenter_map(y_max, y_min);
(MapBounds { x_max, x_min, x_translate, y_max, y_min, y_translate }, false)
} else {
// Round to edge
x_max = round_max_coordinate(x_max, EDGE_SIZE);
x_min = round_min_coordinate(x_min, EDGE_SIZE);
y_max = round_max_coordinate(y_max, EDGE_SIZE);
y_min = round_min_coordinate(y_min, EDGE_SIZE);
// Adding bounds to prevent OOB when moving
x_max += EDGE_SIZE;
x_min -= EDGE_SIZE;
y_max += EDGE_SIZE;
y_min -= EDGE_SIZE;
// Move everything to positive coordinates to prevent corner cases
let (x_max, x_min, x_translate) = recenter_map(x_max, x_min);
let (y_max, y_min, y_translate) = recenter_map(y_max, y_min);
(MapBounds { x_max, x_min, x_translate, y_max, y_min, y_translate }, true)
}
}
}
impl Quadrant {
fn insert_entity(&mut self, entity_id: i64, entity: &'static LevelEntityConfigData) {
self.entities.insert(entity_id, entity);
}
fn get_entities(&self) -> Vec<&LevelEntityConfigData> {
self.entities
.iter()
.map(|(_, v)| *v)
.collect()
}
}
impl Map {
fn insert_entity(&mut self, entity: &'static LevelEntityConfigData) {
let index = self.get_quadrant_id(entity.transform[0].x, entity.transform[0].y);
self.quadrants.entry(index).or_default().insert_entity(entity.entity_id, entity)
}
fn get_neighbour_cells(&self, quadrant_id: u64) -> [u64; 9] {
let x = quadrant_id % self.width;
let y = (quadrant_id - x) / self.width;
return [
(self.width * (y - 1)) + (x - 1),
(self.width * (y - 1)) + (x),
(self.width * (y - 1)) + (x + 1),
(self.width * (y)) + (x - 1),
(self.width * (y)) + (x),
(self.width * (y)) + (x + 1),
(self.width * (y + 1)) + (x - 1),
(self.width * (y + 1)) + (x),
(self.width * (y + 1)) + (x + 1),
];
}
fn collect_quadrant_differences(&self, discriminant: [u64; 9], discriminator: [u64; 9]) -> Vec<&LevelEntityConfigData> {
let mut output = Vec::new();
for quadrant in discriminant {
if !discriminator.contains(&quadrant) {
if let Some(quadrant) = &self.quadrants.get(&quadrant) {
output.extend_from_slice(&quadrant.get_entities())
}
}
}
output
}
pub fn get_quadrant_id(&self, x: f32, y: f32) -> u64 {
let width: u64 = unsafe {
f32::to_int_unchecked(
f32::trunc(
(self.bounds.x_max + self.bounds.x_translate - x) / EDGE_SIZE
)
)
};
let height: u64 = unsafe {
f32::to_int_unchecked(
f32::trunc(
(self.bounds.y_max + self.bounds.y_translate - y) / EDGE_SIZE
)
)
};
(self.width * height) + width
}
pub fn get_initial_entities(&self, quadrant_id: u64) -> Vec<&LevelEntityConfigData> {
let quadrants = self.get_neighbour_cells(quadrant_id);
let mut output = Vec::new();
for quadrant in quadrants {
if let Some(quadrant) = &self.quadrants.get(&quadrant) {
output.extend_from_slice(&quadrant.get_entities())
}
}
output
}
pub fn get_update_entities(&self, old_quadrant_id: u64, new_quadrant_id: u64) -> (Vec<&LevelEntityConfigData>, Vec<&LevelEntityConfigData>) {
let old_quadrants = self.get_neighbour_cells(old_quadrant_id);
let new_quadrants = self.get_neighbour_cells(new_quadrant_id);
let entities_to_remove = self.collect_quadrant_differences(old_quadrants, new_quadrants);
let entities_to_add = self.collect_quadrant_differences(new_quadrants, old_quadrants);
(entities_to_remove, entities_to_add)
}
}
pub fn maps_iter() -> std::collections::hash_map::Iter<'static, i32, Map> {
MAP_TABLE.get().unwrap().iter()
}
pub fn initialize_quadrant_system() {
let mut map_grouped_entities: HashMap<i32, Vec<&LevelEntityConfigData>> = HashMap::new();
for (_, entity) in shorekeeper_data::level_entity_config_data::iter() {
map_grouped_entities.entry(entity.map_id).or_default().push(entity);
}
let mut maps: HashMap<i32, Map> = HashMap::new();
for (map_id, entities) in map_grouped_entities {
let (bounds, _quadrant_enabled) = MapBounds::find_max_min(&entities[..]);
let width = unsafe { f32::to_int_unchecked((bounds.x_max - bounds.x_min) / EDGE_SIZE) };
let height = unsafe { f32::to_int_unchecked((bounds.y_max - bounds.y_min) / EDGE_SIZE) };
let map = maps.entry(map_id).or_insert(
Map {
bounds: bounds.clone(),
width,
height,
quadrants: HashMap::new(),
}
);
for entity in entities {
map.insert_entity(entity);
}
}
let _ = MAP_TABLE.set(maps);
}
pub fn get_map(map_id: i32) -> &'static Map {
// TODO: Error check for map id
MAP_TABLE.get().unwrap().get(&map_id).unwrap()
}
fn recenter_map(max: f32, min: f32) -> (f32, f32, f32) {
match min < 0.0 {
true => (max + f32::abs(min), 0.0, min),
false => (max, min, 0.0)
}
}
fn round_max_coordinate(coordinate: f32, round: f32) -> f32 {
let rounded = f32::round(coordinate);
let remainder = rounded % round;
if remainder != 0f32 {
rounded + (if rounded > 0.0 { round } else { 0.0 } - remainder)
} else {
rounded
}
}
fn round_min_coordinate(coordinate: f32, round: f32) -> f32 {
let rounded = f32::round(coordinate);
let remainder = rounded % round;
if remainder != 0f32 {
rounded + (remainder.signum() * (if rounded > 0.0 { 0.0 } else { round } - f32::abs(remainder)))
} else {
rounded
}
}

View file

@ -1,6 +1,11 @@
use crate::logic::ecs::world::World; use std::cell::{BorrowMutError, RefMut};
use crate::logic::player::Player;
use crate::logic::utils::entity_serializer; use shorekeeper_data::{base_property_data, LevelEntityConfigData};
use shorekeeper_protocol::{DFsm, EEntityType, EntityAddNotify, EntityConfigType, EntityPb,
EntityRemoveInfo, EntityRemoveNotify, EntityState, FightRoleInfo,
FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
ScenePlayerInformation, SceneTimeInfo};
use crate::logic::{ use crate::logic::{
components::{ components::{
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position, Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
@ -8,12 +13,13 @@ use crate::logic::{
}, },
ecs::component::ComponentContainer, ecs::component::ComponentContainer,
}; };
use crate::logic::components::{Fsm, MonsterAi};
use crate::logic::ecs::entity::Entity;
use crate::logic::ecs::world::{World, WorldEntity};
use crate::logic::math::Transform;
use crate::logic::player::Player;
use crate::logic::utils::entity_serializer;
use crate::query_with; use crate::query_with;
use shorekeeper_data::base_property_data;
use shorekeeper_protocol::{
EEntityType, EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation,
SceneMode, ScenePlayerInformation, SceneTimeInfo,
};
#[macro_export] #[macro_export]
macro_rules! create_player_entity_pb { macro_rules! create_player_entity_pb {
@ -32,6 +38,8 @@ macro_rules! create_player_entity_pb {
.with(ComponentContainer::EntityConfig(EntityConfig { .with(ComponentContainer::EntityConfig(EntityConfig {
config_id: role_id, config_id: role_id,
config_type: EntityConfigType::Character, config_type: EntityConfigType::Character,
entity_type: EEntityType::Player.into(),
entity_state: EntityState::Default
})) }))
.with(ComponentContainer::OwnerPlayer(OwnerPlayer($player_id))) .with(ComponentContainer::OwnerPlayer(OwnerPlayer($player_id)))
.with(ComponentContainer::Position(Position($position))) .with(ComponentContainer::Position(Position($position)))
@ -95,6 +103,8 @@ pub fn add_player_entities(player: &Player) {
.with(ComponentContainer::EntityConfig(EntityConfig { .with(ComponentContainer::EntityConfig(EntityConfig {
config_id: role.role_id, config_id: role.role_id,
config_type: EntityConfigType::Character, config_type: EntityConfigType::Character,
entity_type: EEntityType::Player.into(),
entity_state: EntityState::Default,
})) }))
.with(ComponentContainer::OwnerPlayer(OwnerPlayer( .with(ComponentContainer::OwnerPlayer(OwnerPlayer(
player.basic_info.id, player.basic_info.id,
@ -163,15 +173,15 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
Position, Position,
Equip Equip
) )
.into_iter() .into_iter()
.find_map(|(_, _, owner, visibility, conf, pos, equip)| { .find_map(|(_, _, owner, visibility, conf, pos, equip)| {
(sp.player_id == owner.0 && visibility.0).then_some(( (sp.player_id == owner.0 && visibility.0).then_some((
conf.config_id, conf.config_id,
pos.0.clone(), pos.0.clone(),
equip.weapon_id, equip.weapon_id,
)) ))
}) })
.unwrap_or_default(); .unwrap_or_default();
let active_characters = query_with!( let active_characters = query_with!(
world.get_world_entity(), world.get_world_entity(),
@ -179,8 +189,8 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
OwnerPlayer, OwnerPlayer,
EntityConfig EntityConfig
) )
.into_iter() .into_iter()
.filter(|(_, _, owner, _)| owner.0 == sp.player_id); .filter(|(_, _, owner, _)| owner.0 == sp.player_id);
ScenePlayerInformation { ScenePlayerInformation {
cur_role: cur_role_id, cur_role: cur_role_id,
@ -209,3 +219,118 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
}) })
.collect() .collect()
} }
pub fn build_monster_entity(world: &mut WorldEntity, config_id: i32, map_id: i32, transform: Transform) -> Entity {
// TODO: Check for more components, AI and so
world.create_entity(config_id, EEntityType::Monster.into(), map_id)
.with(ComponentContainer::EntityConfig(EntityConfig {
config_id,
config_type: EntityConfigType::Level,
entity_type: EEntityType::Monster.into(),
entity_state: EntityState::Born,
}))
.with(ComponentContainer::Position(Position(transform)))
.with(ComponentContainer::Visibility(Visibility(true)))
.with(ComponentContainer::Attribute(Attribute::from_data(
base_property_data::iter()
.find(|d| d.id == 600000100) // TODO: Implement monster stats
.unwrap(),
)))
.with(ComponentContainer::MonsterAi(MonsterAi {
weapon_id: 0,
hatred_group_id: 0,
ai_team_init_id: 100,
combat_message_id: 0,
}))
.with(ComponentContainer::Fsm(Fsm {
fsms: vec![
DFsm {
fsm_id: 10007,
current_state: 10013,
flag: 0,
k_ts: 0,
},
DFsm {
fsm_id: 10007,
current_state: 10015,
flag: 0,
k_ts: 0,
},
DFsm {
fsm_id: 10007,
current_state: 10012,
flag: 0,
k_ts: 0,
},
],
hash_code: 0,
common_hash_code: 0,
black_board: vec![],
fsm_custom_blackboard_datas: None,
}))
.with(ComponentContainer::Movement(Movement::default()))
.build()
}
pub fn remove_entities(player: &Player, entities: &[&LevelEntityConfigData]) {
let mut removed_entities = Vec::with_capacity(entities.len());
// Enclose to drop borrow mut ASAP
{
let mut world_ref = player.world.borrow_mut();
let world = world_ref.get_mut_world_entity();
for entity in entities {
let entity_id = entity.entity_id as i32; // TODO: Should be i64
if world.remove_entity(entity_id) {
removed_entities.push(world.get_entity_id(entity_id));
}
}
}
for entity_id in removed_entities {
player.notify(EntityRemoveNotify {
remove_infos: vec![EntityRemoveInfo { entity_id, r#type: 0 }],
is_remove: true,
});
}
}
pub fn add_entities(player: &Player, entities: &[&LevelEntityConfigData]) {
let mut added_entities = Vec::with_capacity(entities.len());
// Enclose to drop borrow mut ASAP
{
let mut world_ref = player.world.borrow_mut();
let world = world_ref.get_mut_world_entity();
for entity in entities {
// TODO: review other types
tracing::debug!("Entity to be added of type: {}", entity.blueprint_type);
if entity.blueprint_type.contains("Monster") {
added_entities.push(build_monster_entity(
world,
entity.entity_id as i32, // TODO: Should be i64
entity.map_id,
Transform::from(&entity.transform[..]),
));
}
}
}
let mut world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
// Since kuro has issues, we can only send one
for entity in added_entities {
let mut pb = EntityPb {
id: entity.entity_id as i64, // TODO: Should be i64
..Default::default()
};
world.get_entity_components(entity.entity_id)
.into_iter()
.for_each(|comp| comp.set_pb_data(&mut pb));
player.notify(EntityAddNotify {
entity_pbs: vec![pb],
is_add: true,
});
}
}

View file

@ -21,6 +21,7 @@ async fn main() -> Result<()> {
::common::splash::print_splash(); ::common::splash::print_splash();
::common::logging::init(::tracing::Level::DEBUG); ::common::logging::init(::tracing::Level::DEBUG);
shorekeeper_data::load_all_json_data("assets/logic/BinData")?; shorekeeper_data::load_all_json_data("assets/logic/BinData")?;
logic::utils::quadrant_util::initialize_quadrant_system();
let database = Arc::new(shorekeeper_database::connect_to(&CONFIG.database).await?); let database = Arc::new(shorekeeper_database::connect_to(&CONFIG.database).await?);
shorekeeper_database::run_migrations(database.as_ref()).await?; shorekeeper_database::run_migrations(database.as_ref()).await?;