diff --git a/src/dbmanager/database_manager.rs b/src/dbmanager/database_manager.rs index f56e198..c3a4394 100644 --- a/src/dbmanager/database_manager.rs +++ b/src/dbmanager/database_manager.rs @@ -1,3 +1,4 @@ +use num_traits::Signed; // Database Manager use std::collections::HashMap; @@ -98,25 +99,27 @@ trait Insertable: ActiveModelTrait E::Model: IntoActiveModel, E: EntityTrait, { - fn put(self, db: &DatabaseConnection, guid: i64) -> Result + fn put(self, db: &DatabaseConnection) -> Result { use std::str::FromStr; - E::insert_many(vec![self]).exec(db).wait()?; - let column = match E::Column::from_str("guid") { Ok(column) => column, Err(_) => panic!("GUID column not found!"), }; + let guid = self.get(column).unwrap(); + + E::insert(self).exec(db).wait()?; + let item = E::find().filter( Condition::all() - .add(column.eq(guid)) + .add(column.eq(guid.clone())) ).one(db).wait()?; match item { Some(item) => Ok(item), //Ok(item.into_active_model()), - None => Err(DbErr::Custom(format!("Failed to find inserted item: {}", guid))) + None => Err(DbErr::Custom(format!("Failed to find inserted item: {:?}", guid))) } } } @@ -128,6 +131,40 @@ impl Insertable for A E: EntityTrait, {} +/* + This is another hack to update all the fields of the record. + By default, Sea ORM only updates fields that are changed in ActiveModel. + As it is much more convenient to pass Model instead of ActiveModel around, we need this hack. + */ + +trait FullyUpdateable: ActiveModelTrait + where + A: ActiveModelTrait, + E::Model: IntoActiveModel, + E: EntityTrait, +{ + fn full_update(mut self, db: &DatabaseConnection) -> Result + where ::Model: sea_orm::IntoActiveModel + { + for col in <::Column>::iter() { + let val = self.get(col); + + self.set(col, val.unwrap()); + } + + let item: E::Model = E::update(self).exec(db).wait()?; + + Ok(item) + } +} + +impl FullyUpdateable for A + where + A: ActiveModelTrait, + E::Model: IntoActiveModel, + E: EntityTrait, +{} + /* Database manager itself */ @@ -649,6 +686,20 @@ impl DatabaseManager { return scene_info; } + pub fn update_player_scene_info(&self, scene_info: SceneInfo) { + let mut sc_info: super::scene_info::ActiveModel = scene_info.into(); + + /*for col in <::Column>::iter() { + let val = sc_info.get(col); + + sc_info.set(col, val.unwrap()); + } + + println!("Updating scene info: {:?}", sc_info);*/ + + let sc_info: SceneInfo = sc_info.full_update(&self.db).unwrap(); + } + pub fn get_player_teams(&self, uid: u32) -> Option> { /*let t1 = TeamInfo { uid: uid.clone(), @@ -759,7 +810,7 @@ impl DatabaseManager { promote_level: ActiveValue::Set(0), // TODO: 1? }; - let eq_info: EquipInfo = eq_info.put(&self.db, new_guid as i64).unwrap(); + let eq_info: EquipInfo = eq_info.put(&self.db).unwrap(); let it_info = super::item_info::ActiveModel { uid: ActiveValue::Set(uid), @@ -767,7 +818,7 @@ impl DatabaseManager { item_id: ActiveValue::Set(item_id), }; - let it_info: ItemInfo = it_info.put(&self.db, new_guid as i64).unwrap(); + let it_info: ItemInfo = it_info.put(&self.db).unwrap(); let detail = if self.jm.is_item_weapon(item_id) { let affixes: Vec<_> = self.jm.weapons[&item_id].skill_affix.iter() @@ -800,7 +851,7 @@ impl DatabaseManager { main_prop_id: ActiveValue::Set(main_stat), }; - let re_info: ReliquaryInfo = re_info.put(&self.db, new_guid as i64).unwrap(); + let re_info: ReliquaryInfo = re_info.put(&self.db).unwrap(); let sub_stats_v: Vec<_> = sub_stats.clone().into_iter() .map(|s| super::reliquary_prop::ActiveModel { @@ -854,7 +905,7 @@ impl DatabaseManager { item_id: ActiveValue::Set(item_id), }; - let it_info: ItemInfo = it_info.put(&self.db, new_guid as i64).unwrap(); + let it_info: ItemInfo = it_info.put(&self.db).unwrap(); let detail = if self.jm.is_item_material(item_id) { // Material @@ -865,7 +916,7 @@ impl DatabaseManager { // TODO: MaterialDeleteConfig! }; - let mt_info: MaterialInfo = mt_info.put(&self.db, new_guid as i64).unwrap(); + let mt_info: MaterialInfo = mt_info.put(&self.db).unwrap(); proto::item::Detail::Material(build!(Material { count: mt_info.count, @@ -880,7 +931,7 @@ impl DatabaseManager { count: ActiveValue::Set(count), }; - let fr_info: FurnitureInfo = fr_info.put(&self.db, new_guid as i64).unwrap(); + let fr_info: FurnitureInfo = fr_info.put(&self.db).unwrap(); proto::item::Detail::Furniture(build!(Furniture { count: fr_info.count, diff --git a/src/dbmanager/scene_info.rs b/src/dbmanager/scene_info.rs index 3c07ec0..aa3fbbc 100644 --- a/src/dbmanager/scene_info.rs +++ b/src/dbmanager/scene_info.rs @@ -5,7 +5,7 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "scene_info")] pub struct Model { - #[sea_orm(primary_key)] + #[sea_orm(primary_key, autoincrement = false)] pub uid: u32, pub scene_id: u32, pub scene_token: u32, diff --git a/src/subsystems/entity_subsystem/entities.rs b/src/entitymanager/entities.rs similarity index 100% rename from src/subsystems/entity_subsystem/entities.rs rename to src/entitymanager/entities.rs diff --git a/src/entitymanager/entity_manager.rs b/src/entitymanager/entity_manager.rs new file mode 100644 index 0000000..1cfbe89 --- /dev/null +++ b/src/entitymanager/entity_manager.rs @@ -0,0 +1,277 @@ +use std::sync::{mpsc::{self, Sender, Receiver}, Arc, Mutex}; +use std::thread; +use std::collections::{HashMap, HashSet}; +use std::collections::hash_map::Entry::{Occupied, Vacant}; + +use crate::server::IpcMessage; + +use prost::Message; + +use proto; +use proto::{PacketId, CombatTypeArgument, ForwardType, ProtEntityType}; + +use packet_processor_macro::*; +#[macro_use] +use packet_processor::*; +use serde_json::de::Read; +use crate::{DatabaseManager, JsonManager, LuaManager}; +use crate::utils::{IdManager, TimeManager}; + +use crate::luamanager::Vector; +use super::entities::Entity; + +#[derive(Debug, Clone)] +struct Player { + player_id: u32, + pos: Vector, + current_scene: u32, + current_block: u32, + entities: HashMap>, + lua_manager: Arc, + json_manager: Arc, + db_manager: Arc, + packets_to_send_tx: Sender, +} + +impl Player { + const DESPAWN_DISTANCE: f32 = 100.0; + const SPAWN_DISTANCE: f32 = Self::DESPAWN_DISTANCE * 0.8; + const RESPAWN_TIME: i32 = 10; // In seconds + + pub fn despawn_everything(&self) { + let entity_list: Vec = self.entities.iter().map(|(k, v)| *k).collect(); + + if entity_list.len() > 0 { + // TODO: HACK! + let player_id = self.player_id; + let metadata = &build!(PacketHead { + sent_ms: TimeManager::timestamp(), + client_sequence_id: 0, + }); + + build_and_send!(self, player_id, metadata, SceneEntityDisappearNotify { + entity_list: entity_list, + disappear_type: proto::VisionType::VisionMiss as i32, + }) + } + } + + pub fn position_changed(&mut self) { + // 1. Go through the list of spawned entities and despawn those that are too far from us + let despawn_list: Vec = self.entities.iter() + .filter(|(k, v)| v.pos().sub(&self.pos).len() > Self::DESPAWN_DISTANCE) + .map(|(k, v)| *k) + .collect(); + + if despawn_list.len() > 0 { + for k in despawn_list.iter() { + self.entities.remove(&k); + } + + // TODO: HACK! + let player_id = self.player_id; + let metadata = &build!(PacketHead { + sent_ms: TimeManager::timestamp(), + client_sequence_id: 0, + }); + + build_and_send!(self, player_id, metadata, SceneEntityDisappearNotify { + entity_list: despawn_list, + disappear_type: proto::VisionType::VisionMiss as i32, + }); + } + + // 2. Go through the list of available entities and spawn those that are close to us and their respawn timeout (in case of collectibles and monsters) is over + let spawned_list: HashSet = self.entities.iter().map(|(k, v)| *k).collect(); + + // TODO: do this once only on block change! + let scene = self.lua_manager.get_scene_by_id(self.current_scene).unwrap(); + let block = match scene.get_block_by_id(self.current_block) { // TODO: this is due to some blocks missing + Ok(block) => block, + Err(_) => return, + }; + + let spawn_list: Vec> = block.entities.iter() + .filter(|(entity_id, entity)| !spawned_list.contains(entity_id)) // If entity isn't spawned already... + .filter(|(entity_id, entity)| entity.pos().sub(&self.pos).len() < Self::SPAWN_DISTANCE) // ... and is close enough + .map(|(entity_id, entity)| (*entity).clone()) + .collect(); + + if spawn_list.len() > 0 { + // TODO: HACK! + let player_id = self.player_id; + let metadata = &build!(PacketHead { + sent_ms: TimeManager::timestamp(), + client_sequence_id: 0, + }); + + let world_level = self.db_manager.get_player_world_level(self.player_id).unwrap() as u32; + + build_and_send!(self, player_id, metadata, SceneEntityAppearNotify { + entity_list: spawn_list.iter().map(|e| e.convert(world_level, &self.json_manager, &self.db_manager)).collect(), + appear_type: proto::VisionType::VisionBorn as i32, + }); + + for entity in spawn_list.into_iter() { + self.entities.insert(entity.entity_id, entity.clone()); + } + } + } + + pub fn enter_scene(&self, enter_type: &proto::EnterType, token: u32) { + let world_level = self.db_manager.get_player_world_level(self.player_id).unwrap() as u32; + let player_id = self.player_id; + + let mut scene_info = self.db_manager.get_player_scene_info(player_id).unwrap(); + + scene_info.scene_id = self.current_scene; + scene_info.scene_token = token; + scene_info.pos_x = self.pos.x; + scene_info.pos_y = self.pos.y; + scene_info.pos_z = self.pos.z; + + self.db_manager.update_player_scene_info(scene_info); + + let metadata = &build!(PacketHead { + sent_ms: TimeManager::timestamp(), + client_sequence_id: 0, + }); + + build_and_send! (self, player_id, metadata, PlayerEnterSceneNotify { + scene_id: self.current_scene, + r#type: *enter_type as i32, + scene_begin_time: TimeManager::timestamp(), + pos: Some((&self.pos).into()), + target_uid: self.player_id, + world_level: world_level, + enter_scene_token: token, + //enter_reason: 1, + }); + } + + // Gatherable stuff is described in GatherExcelConfigData +} + +pub struct EntityManager { + packets_to_send_tx: Sender, + players: Arc>>, + players_moved: Sender, + lua_manager: Arc, + json_manager: Arc, + db_manager: Arc, +} + +impl EntityManager { + pub fn new(lua_manager: Arc, json_manager: Arc, db_manager: Arc, packets_to_send_tx: Sender) -> Self { + let (tx, rx): (Sender, Receiver) = mpsc::channel(); + + let mut es = Self { + packets_to_send_tx: packets_to_send_tx, + players_moved: tx, + players: Arc::new(Mutex::new(HashMap::new())), + lua_manager: lua_manager, + json_manager: json_manager, + db_manager: db_manager, + }; + + es.run(rx); + + return es; + } + + fn run(&self, mut rx: Receiver) { + let players = self.players.clone(); + let lua_manager = self.lua_manager.clone(); + + thread::spawn(move || { + loop { + let player_id = rx.recv().unwrap(); + + match players.lock() { + Ok(mut players) => { + let mut player = &mut players.get_mut(&player_id).unwrap(); + let scene = lua_manager.get_scene_by_id(player.current_scene).unwrap(); + let block = scene.get_block_by_pos(&player.pos); + + match block { + Ok(block) => + if player.current_block != block.block_id { + println!("Player {:?} moved to the block {:?}", player.player_id, block.block_id); + player.current_block = block.block_id; + }, + Err(_) => { + // TODO? + player.current_block = 0; + }, + }; + + player.position_changed(); + }, + Err(_) => panic!("Failed to grab player data!"), + }; + } + }); + } + + pub fn player_moved(&self, user_id: u32, pos: Vector) { + match self.players.lock() + { + Ok(mut players) => match players.entry(user_id) { + Occupied(mut player) => { + let mut player = player.get_mut(); + + // HACK: if player moved too far away, then he's probably teleported just now; don't change position, we're in the process of teleportation + if player.pos.sub(&pos).len() < 10.0 { + player.pos = pos; + } else { + println!("WARN: Teleport detected, hack applied!"); + } + }, + Vacant(entry) => { + panic!("Moving of nonexistent player: {}", user_id); + }, + }, + Err(_) => panic!("Failed to grab player data!"), + }; + + self.players_moved.send(user_id).unwrap(); + } + + pub fn player_teleported(&self, user_id: u32, pos: Vector, scene_id: u32, token: u32, reason: &proto::EnterType) { + match self.players.lock() + { + Ok(mut players) => match players.entry(user_id) { + Occupied(mut player) => { + let mut player = player.get_mut(); + + player.pos = pos; + + // TODO: check for scene_id change! + player.current_scene = scene_id; + + player.enter_scene(reason, token); + }, + Vacant(entry) => { + let player = Player { + player_id: user_id, + pos: pos, + current_block: 0, + current_scene: scene_id, + entities: HashMap::new(), + lua_manager: self.lua_manager.clone(), + json_manager: self.json_manager.clone(), + db_manager: self.db_manager.clone(), + packets_to_send_tx: self.packets_to_send_tx.clone(), + }; + + player.enter_scene(reason, token); + + entry.insert(player); + }, + }, + Err(_) => panic!("Failed to grab player data!"), + }; + + self.players_moved.send(user_id).unwrap(); + } +} \ No newline at end of file diff --git a/src/entitymanager/mod.rs b/src/entitymanager/mod.rs new file mode 100644 index 0000000..adae095 --- /dev/null +++ b/src/entitymanager/mod.rs @@ -0,0 +1,5 @@ +mod entity_manager; +mod entities; + +pub use self::entity_manager::EntityManager; +pub use self::entities::{Entity, EntityTrait}; \ No newline at end of file diff --git a/src/jsonmanager/json_manager.rs b/src/jsonmanager/json_manager.rs index 2cbfa89..dc16953 100644 --- a/src/jsonmanager/json_manager.rs +++ b/src/jsonmanager/json_manager.rs @@ -12,6 +12,7 @@ use crate::jsonmanager::material::Material; use crate::jsonmanager::reliquary::{Reliquary, ReliquaryAffix, ReliquaryMainProp}; use crate::jsonmanager::shop_goods::ShopGoods; use crate::jsonmanager::shop_rotate::ShopRotate; +use crate::jsonmanager::teleport_point::TeleportPoint; use crate::jsonmanager::weapon::Weapon; use super::avatar_skill_depot::AvatarSkillDepot; @@ -54,6 +55,8 @@ pub struct JsonManager { pub reliquary_affixes: HashMap>, pub materials: HashMap, + + pub teleport_points: HashMap>, } impl std::fmt::Debug for JsonManager { // TODO: fucking hack! @@ -66,23 +69,25 @@ impl JsonManager { pub fn new(directory: &str) -> JsonManager { let reader = JsonReader::new(directory); - let asd: Vec = reader.read_json_list("AvatarSkillDepot"); - let mc: Vec = reader.read_json_list("MonsterCurve"); - let monsters: Vec = reader.read_json_list("Monster"); - let world_levels: Vec = reader.read_json_list("WorldLevel"); - let gadget_props: Vec = reader.read_json_list("GadgetProp"); - let gc: Vec = reader.read_json_list("GadgetCurve"); - let gathers: Vec = reader.read_json_list("Gather"); - let shop_goods: Vec = reader.read_json_list("ShopGoods"); - let shop_rotate: Vec = reader.read_json_list("ShopRotate"); - let weapons: Vec = reader.read_json_list("Weapon"); + let asd: Vec = reader.read_json_list_game("AvatarSkillDepot"); + let mc: Vec = reader.read_json_list_game("MonsterCurve"); + let monsters: Vec = reader.read_json_list_game("Monster"); + let world_levels: Vec = reader.read_json_list_game("WorldLevel"); + let gadget_props: Vec = reader.read_json_list_game("GadgetProp"); + let gc: Vec = reader.read_json_list_game("GadgetCurve"); + let gathers: Vec = reader.read_json_list_game("Gather"); + let shop_goods: Vec = reader.read_json_list_game("ShopGoods"); + let shop_rotate: Vec = reader.read_json_list_game("ShopRotate"); + let weapons: Vec = reader.read_json_list_game("Weapon"); - let reliquaries: Vec = reader.read_json_list("Reliquary"); + let reliquaries: Vec = reader.read_json_list_game("Reliquary"); - let reliquary_main_prop_depot : Vec = reader.read_json_list("ReliquaryMainProp"); - let reliquary_affixes : Vec = reader.read_json_list("ReliquaryAffix"); + let reliquary_main_prop_depot : Vec = reader.read_json_list_game("ReliquaryMainProp"); + let reliquary_affixes : Vec = reader.read_json_list_game("ReliquaryAffix"); - let materials: Vec = reader.read_json_list("Material"); + let materials: Vec = reader.read_json_list_game("Material"); + + let teleport_points: Vec = reader.read_json_list_3rdparty("TeleportPoints"); return JsonManager { reader: reader, @@ -106,6 +111,10 @@ impl JsonManager { .collect(), // TODO: we're grouping by depot_id! materials: materials.into_iter().map(|m| (m.id, m)).collect(), + + teleport_points: group_nonconsec_by(teleport_points, |tp| tp.scene_id).into_iter() + .map(|(scene_id, tp_list)| (scene_id, tp_list.into_iter().map(|tp| (tp.point_id, tp)).collect())) + .collect(), }; } @@ -152,14 +161,26 @@ impl JsonReader { }; } - fn read_json_list(&self, name: &str) -> Vec + fn read_json_list(&self, name: &str, subpath: &str) -> Vec where T: DeserializeOwned { - let path = format!("{}/{}ExcelConfigData.json", self.base_path, name); + let path = format!("{}/{}/{}.json", self.base_path, subpath, name); let json_file_path = Path::new(&path); let json_file_str = read_to_string(json_file_path).unwrap_or_else(|_| panic!("File {} not found", path)); let data: Vec = serde_json::from_str(&json_file_str).expect(&format!("Error while reading json {}", name)); return data; } + + fn read_json_list_game(&self, name: &str) -> Vec + where T: DeserializeOwned + { + self.read_json_list(&format!("{}ExcelConfigData", name), "game") + } + + fn read_json_list_3rdparty(&self, name: &str) -> Vec + where T: DeserializeOwned + { + self.read_json_list(name, "thirdparty") + } } diff --git a/src/jsonmanager/mod.rs b/src/jsonmanager/mod.rs index e0ac2c3..0a03f33 100644 --- a/src/jsonmanager/mod.rs +++ b/src/jsonmanager/mod.rs @@ -13,6 +13,7 @@ mod shop_rotate; mod weapon; mod reliquary; mod material; +mod teleport_point; pub use entity_curve::{CurveInfo,EntityCurve}; pub use shop_goods::ShopGoods; \ No newline at end of file diff --git a/src/jsonmanager/teleport_point.rs b/src/jsonmanager/teleport_point.rs new file mode 100644 index 0000000..32cbb7b --- /dev/null +++ b/src/jsonmanager/teleport_point.rs @@ -0,0 +1,22 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct Vector { + #[serde(default)] + pub x: f32, + #[serde(default)] + pub y: f32, + #[serde(default)] + pub z: f32, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct TeleportPoint { + pub scene_id: u32, + pub point_id: u32, + #[serde(flatten)] + pub position: Vector, + pub rotation: Vector, +} \ No newline at end of file diff --git a/src/luamanager/lua_manager.rs b/src/luamanager/lua_manager.rs index d4767ad..68377eb 100644 --- a/src/luamanager/lua_manager.rs +++ b/src/luamanager/lua_manager.rs @@ -3,7 +3,7 @@ use std::result::Result; use std::sync::Arc; use crate::utils::IdManager; -use crate::subsystems::entity_subsystem::{Entity, EntityTrait}; +use crate::entitymanager::{Entity, EntityTrait}; use lua_serde::from_file; diff --git a/src/main.rs b/src/main.rs index b734a51..1e031d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod utils; mod dbmanager; mod jsonmanager; mod luamanager; +mod entitymanager; mod subsystems; @@ -21,6 +22,7 @@ use dbmanager::DatabaseManager; use jsonmanager::JsonManager; use luamanager::LuaManager; use subsystems::EntitySubsystem; +use entitymanager::EntityManager; fn main() { //pretty_env_logger::init(); diff --git a/src/server/game_server.rs b/src/server/game_server.rs index 2fe0a4b..28d376f 100644 --- a/src/server/game_server.rs +++ b/src/server/game_server.rs @@ -12,8 +12,9 @@ use crate::JsonManager; use crate::LuaManager; use crate::server::LoginManager; use std::sync::Arc; +use crate::entitymanager::EntityManager; use crate::subsystems::{InventorySubsystem, NpcSubsystem, ShopSubsystem}; -use crate::subsystems::misc::{PauseSubsystem, SceneSubsystem, SocialSubsystem}; +use crate::subsystems::misc::{PauseSubsystem, SceneSubsystem, SocialSubsystem, TeleportSubsystem}; pub struct GameServer { packets_to_process_rx: mpsc::Receiver, @@ -29,17 +30,19 @@ impl GameServer { pub fn new(packets_to_process_rx: mpsc::Receiver, packets_to_send_tx: mpsc::Sender) -> GameServer { let jm = Arc::new(JsonManager::new("./data/json")); let db = Arc::new(DatabaseManager::new("sqlite://./database.db3", jm.clone())); - let lm = LoginManager::new(db.clone(), jm.clone(), packets_to_send_tx.clone()); let lum = Arc::new(LuaManager::new("./data/lua")); + let em = Arc::new(EntityManager::new(lum.clone(),jm.clone(), db.clone(), packets_to_send_tx.clone())); + let lm = LoginManager::new(db.clone(), jm.clone(), em.clone(),packets_to_send_tx.clone()); let inv = Arc::new(InventorySubsystem::new(jm.clone(), db.clone(), packets_to_send_tx.clone())); - let em = EntitySubsystem::new(lum.clone(), jm.clone(), db.clone(), packets_to_send_tx.clone()); + let es = EntitySubsystem::new(lum.clone(), jm.clone(), db.clone(), em.clone(), packets_to_send_tx.clone()); let nt = NpcSubsystem::new(packets_to_send_tx.clone()); let ss = ShopSubsystem::new(jm.clone(), db.clone(), inv.clone(), packets_to_send_tx.clone()); let scs = SceneSubsystem::new(packets_to_send_tx.clone()); let ps = PauseSubsystem::new(packets_to_send_tx.clone()); let socs = SocialSubsystem::new(db.clone(), packets_to_send_tx.clone()); + let ts = TeleportSubsystem::new(jm.clone(), db.clone(), em.clone(), packets_to_send_tx.clone()); let gs = GameServer { packets_to_process_rx: packets_to_process_rx, @@ -48,7 +51,7 @@ impl GameServer { login_manager: lm, database_manager: db.clone(), json_manager: jm.clone(), - processors: vec![Box::new(em), Box::new(nt), Box::new(ss), Box::new(scs), Box::new(ps), Box::new(socs)], + processors: vec![Box::new(es), Box::new(nt), Box::new(ss), Box::new(scs), Box::new(ps), Box::new(socs), Box::new(ts)], }; return gs; diff --git a/src/server/login_manager.rs b/src/server/login_manager.rs index f40b710..b82c227 100644 --- a/src/server/login_manager.rs +++ b/src/server/login_manager.rs @@ -9,28 +9,31 @@ use packet_processor_macro::*; #[macro_use] use packet_processor::*; -use crate::DatabaseManager; +use crate::{DatabaseManager, luamanager}; use crate::JsonManager; use crate::utils::{AvatarBuilder, IdManager, Remapper}; use crate::utils::TimeManager; use crate::dbmanager::database_manager::AvatarInfo as DbAvatarInfo; +use crate::entitymanager::EntityManager; #[packet_processor(PlayerLoginReq)] pub struct LoginManager { packets_to_send_tx: mpsc::Sender, db: Arc, jm: Arc, + em: Arc, } impl LoginManager { - pub fn new(db: Arc, jm: Arc, packets_to_send_tx: mpsc::Sender) -> LoginManager { + pub fn new(db: Arc, jm: Arc, em: Arc, packets_to_send_tx: mpsc::Sender) -> LoginManager { let mut lm = LoginManager { packet_callbacks: HashMap::new(), packets_to_send_tx: packets_to_send_tx, - db: db.clone(), - jm: jm.clone(), + db: db, + jm: jm, + em: em, }; lm.register(); @@ -112,16 +115,9 @@ impl LoginManager { owned_flycloak_list: vec![140001], // TODO! }); - build_and_send! (self, user_id, metadata, PlayerEnterSceneNotify { - scene_id: scene_info.scene_id, - r#type: proto::EnterType::EnterSelf as i32, - scene_begin_time: TimeManager::timestamp(), - pos: Some(proto::Vector {x: scene_info.pos_x, y: scene_info.pos_y, z: scene_info.pos_z}), - target_uid: user_id, - world_level: world_level, - enter_scene_token: scene_info.scene_token, - //enter_reason: 1, - }); + let pos = luamanager::Vector {x: scene_info.pos_x, y: scene_info.pos_y, z: scene_info.pos_z}; + + self.em.player_teleported(user_id, pos, scene_info.scene_id, scene_info.scene_token, &proto::EnterType::EnterSelf); } fn retrieve_team_info(&self, user_id: u32) -> HashMap { diff --git a/src/subsystems/entity_subsystem/entity_subsystem.rs b/src/subsystems/entity_subsystem/entity_subsystem.rs index 1f777a2..b1cd3f0 100644 --- a/src/subsystems/entity_subsystem/entity_subsystem.rs +++ b/src/subsystems/entity_subsystem/entity_subsystem.rs @@ -15,178 +15,38 @@ use packet_processor_macro::*; use packet_processor::*; use serde_json::de::Read; use crate::{DatabaseManager, JsonManager, LuaManager}; +use crate::entitymanager::EntityManager; use crate::utils::{IdManager, TimeManager}; use crate::luamanager::Vector; -use super::entities::Entity; - -#[derive(Debug, Clone)] -struct Player { - player_id: u32, - pos: Vector, - current_scene: u32, - current_block: u32, - entities: HashMap>, - lua_manager: Arc, - json_manager: Arc, - db_manager: Arc, - packets_to_send_tx: Sender, -} - -impl Player { - const DESPAWN_DISTANCE: f32 = 100.0; - const SPAWN_DISTANCE: f32 = Self::DESPAWN_DISTANCE * 0.8; - const RESPAWN_TIME: i32 = 10; // In seconds - - pub fn despawn_everything(&self) { - let entity_list: Vec = self.entities.iter().map(|(k, v)| *k).collect(); - - if entity_list.len() > 0 { - // TODO: HACK! - let player_id = self.player_id; - let metadata = &build!(PacketHead { - sent_ms: TimeManager::timestamp(), - client_sequence_id: 0, - }); - - build_and_send!(self, player_id, metadata, SceneEntityDisappearNotify { - entity_list: entity_list, - disappear_type: proto::VisionType::VisionMiss as i32, - }) - } - } - - pub fn position_changed(&mut self) { - // 1. Go through the list of spawned entities and despawn those that are too far from us - let despawn_list: Vec = self.entities.iter() - .filter(|(k, v)| v.pos().sub(&self.pos).len() > Self::DESPAWN_DISTANCE) - .map(|(k, v)| *k) - .collect(); - - if despawn_list.len() > 0 { - for k in despawn_list.iter() { - self.entities.remove(&k); - } - - // TODO: HACK! - let player_id = self.player_id; - let metadata = &build!(PacketHead { - sent_ms: TimeManager::timestamp(), - client_sequence_id: 0, - }); - - build_and_send!(self, player_id, metadata, SceneEntityDisappearNotify { - entity_list: despawn_list, - disappear_type: proto::VisionType::VisionMiss as i32, - }); - } - - // 2. Go through the list of available entities and spawn those that are close to us and their respawn timeout (in case of collectibles and monsters) is over - let spawned_list: HashSet = self.entities.iter().map(|(k, v)| *k).collect(); - - // TODO: do this once only on block change! - let scene = self.lua_manager.get_scene_by_id(self.current_scene).unwrap(); - let block = match scene.get_block_by_id(self.current_block) { // TODO: this is due to some blocks missing - Ok(block) => block, - Err(_) => return, - }; - - let spawn_list: Vec> = block.entities.iter() - .filter(|(entity_id, entity)| !spawned_list.contains(entity_id)) // If entity isn't spawned already... - .filter(|(entity_id, entity)| entity.pos().sub(&self.pos).len() < Self::SPAWN_DISTANCE) // ... and is close enough - .map(|(entity_id, entity)| (*entity).clone()) - .collect(); - - if spawn_list.len() > 0 { - // TODO: HACK! - let player_id = self.player_id; - let metadata = &build!(PacketHead { - sent_ms: TimeManager::timestamp(), - client_sequence_id: 0, - }); - let world_level = self.db_manager.get_player_world_level(self.player_id).unwrap() as u32; // TODO: hardcoded value! - - build_and_send!(self, player_id, metadata, SceneEntityAppearNotify { - entity_list: spawn_list.iter().map(|e| e.convert(world_level, &self.json_manager, &self.db_manager)).collect(), - appear_type: proto::VisionType::VisionBorn as i32, - }); - - for entity in spawn_list.into_iter() { - self.entities.insert(entity.entity_id, entity.clone()); - } - } - } - - // Gatherable stuff is described in GatherExcelConfigData -} #[packet_processor( CombatInvocationsNotify, )] pub struct EntitySubsystem { packets_to_send_tx: Sender, - players: Arc>>, - players_moved: Sender, lua_manager: Arc, json_manager: Arc, db_manager: Arc, + entity_manager: Arc, } impl EntitySubsystem { - pub fn new(lua_manager: Arc, json_manager: Arc, db_manager: Arc, packets_to_send_tx: Sender) -> EntitySubsystem { - let (tx, rx): (Sender, Receiver) = mpsc::channel(); - + pub fn new(lua_manager: Arc, json_manager: Arc, db_manager: Arc, entity_manager: Arc, packets_to_send_tx: Sender) -> EntitySubsystem { let mut es = EntitySubsystem { packets_to_send_tx: packets_to_send_tx, packet_callbacks: HashMap::new(), - players_moved: tx, - players: Arc::new(Mutex::new(HashMap::new())), lua_manager: lua_manager, json_manager: json_manager, db_manager: db_manager, + entity_manager: entity_manager, }; es.register(); - es.run(rx); - return es; } - fn run(&self, mut rx: Receiver) { - let players = self.players.clone(); - let lua_manager = self.lua_manager.clone(); - - thread::spawn(move || { - loop { - let player_id = rx.recv().unwrap(); - - match players.lock() { - Ok(mut players) => { - let mut player = &mut players.get_mut(&player_id).unwrap(); - let scene = lua_manager.get_scene_by_id(player.current_scene).unwrap(); - let block = scene.get_block_by_pos(&player.pos); - - match block { - Ok(block) => - if player.current_block != block.block_id { - println!("Player {:?} moved to the block {:?}", player.player_id, block.block_id); - player.current_block = block.block_id; - }, - Err(_) => { - // TODO? - player.current_block = 0; - }, - }; - - player.position_changed(); - }, - Err(_) => panic!("Failed to grab player data!"), - }; - } - }); - } - fn process_combat_invocations(&self, user_id: u32, metadata: &proto::PacketHead, notify: &proto::CombatInvocationsNotify) { for invoke in notify.invoke_list.iter() { self.handle_invoke(user_id, metadata, invoke); @@ -236,33 +96,7 @@ impl EntitySubsystem { return; }; - match self.players.lock() - { - Ok(mut players) => match players.entry(user_id) { - Occupied(mut player) => { - player.into_mut().pos = pos; - }, - Vacant(entry) => { - // TODO: must panic!() here! - let player = Player { - player_id: user_id, - pos: pos, - current_block: 0, - current_scene: 3, - entities: HashMap::new(), - lua_manager: self.lua_manager.clone(), - json_manager: self.json_manager.clone(), - db_manager: self.db_manager.clone(), - packets_to_send_tx: self.packets_to_send_tx.clone(), - }; - - entry.insert(player); - }, - }, - Err(_) => panic!("Failed to grab player data!"), - }; - - self.players_moved.send(user_id).unwrap(); + self.entity_manager.player_moved(user_id, pos); } } diff --git a/src/subsystems/entity_subsystem/mod.rs b/src/subsystems/entity_subsystem/mod.rs index ef9fafa..b3301f2 100644 --- a/src/subsystems/entity_subsystem/mod.rs +++ b/src/subsystems/entity_subsystem/mod.rs @@ -1,5 +1,3 @@ mod entity_subsystem; -mod entities; -pub use self::entity_subsystem::EntitySubsystem; -pub use self::entities::{Entity, EntityTrait}; \ No newline at end of file +pub use self::entity_subsystem::EntitySubsystem; \ No newline at end of file diff --git a/src/subsystems/misc/mod.rs b/src/subsystems/misc/mod.rs index 5811469..ed4490e 100644 --- a/src/subsystems/misc/mod.rs +++ b/src/subsystems/misc/mod.rs @@ -3,9 +3,11 @@ mod shop; mod scene; mod pause; mod social; +mod teleport; pub use npc::NpcSubsystem; pub use shop::ShopSubsystem; pub use scene::SceneSubsystem; pub use pause::PauseSubsystem; -pub use social::SocialSubsystem; \ No newline at end of file +pub use social::SocialSubsystem; +pub use teleport::TeleportSubsystem; \ No newline at end of file diff --git a/src/subsystems/misc/teleport.rs b/src/subsystems/misc/teleport.rs new file mode 100644 index 0000000..e148421 --- /dev/null +++ b/src/subsystems/misc/teleport.rs @@ -0,0 +1,78 @@ +use std::sync::{mpsc::{self, Sender, Receiver}, Arc, Mutex}; +use std::thread; +use std::collections::{HashMap, HashSet}; +use std::collections::hash_map::Entry::{Occupied, Vacant}; + +use crate::server::IpcMessage; + +use prost::Message; + +use proto; +use proto::{PacketId, CombatTypeArgument, ForwardType, ProtEntityType}; + +use packet_processor_macro::*; +#[macro_use] +use packet_processor::*; +use serde_json::de::Read; +use crate::{DatabaseManager, JsonManager, LuaManager}; +use crate::entitymanager::EntityManager; +use crate::luamanager::Vector; +use crate::utils::{IdManager, TimeManager}; + +#[packet_processor( +SceneTransToPointReq, +)] +pub struct TeleportSubsystem { + packets_to_send_tx: Sender, + jm: Arc, + em: Arc, + db: Arc +} + +impl TeleportSubsystem { + pub fn new(jm: Arc, db: Arc, em: Arc, packets_to_send_tx: Sender) -> Self { + let mut nt = Self { + packets_to_send_tx: packets_to_send_tx, + packet_callbacks: HashMap::new(), + jm: jm, + em: em, + db: db, + }; + + nt.register(); + + return nt; + } + + fn process_scene_trans_to_point(&self, user_id: u32, metadata: &proto::PacketHead, req: &proto::SceneTransToPointReq, rsp: &mut proto::SceneTransToPointRsp) { + let s_id = req.scene_id; + let p_id = req.point_id; + + rsp.scene_id = s_id; + rsp.point_id = p_id; + + let pos = match self.jm.teleport_points.get(&s_id) { + None => None, + Some(scene) => match scene.get(&p_id) { + None => None, + Some(point) => Some(point.position.clone()), + }, + }; + + let pos = match pos { + Some(pos) => Vector {x: pos.x, y: pos.y, z: pos.z}, + None => { + println!("Warning: unknown TP point {}-{}, moving player to origin!", s_id, p_id); + Vector {x: 0.0, y: 500.0, z: 0.0} + } + }; + + // TODO: scene_token can probably be random? + let scene_info = match self.db.get_player_scene_info(user_id) { + Some(scene_info) => scene_info, + None => panic!("Scene info for user {} not found!", user_id), + }; + + self.em.player_teleported(user_id, pos, s_id, scene_info.scene_token, &proto::EnterType::EnterGoto); + } +} \ No newline at end of file