Implement basic entity (de)spawning (gadgets, mobs, npcs)

This commit is contained in:
Nobody 2022-02-09 00:41:04 +05:00
parent 8b6f33421c
commit b83363fd6d
4 changed files with 268 additions and 40 deletions

View File

@ -1,13 +1,22 @@
use std::collections::HashMap;
use std::result::Result;
use std::sync::Arc;
use lua_serde::from_file;
#[macro_use]
use packet_processor::*;
use crate::utils::IdManager;
use super::scene_config;
use super::scene_config::Group;
use super::scene_config::Block;
use super::scene_config::Scene;
pub use super::scene_config::Group;
pub use super::scene_config::Block;
pub use super::scene_config::Scene;
pub use super::scene_config::Vector;
pub use super::scene_config::Monster;
pub use super::scene_config::Npc;
pub use super::scene_config::Gadget;
#[derive(Debug)]
pub struct InternalSceneData {
@ -22,6 +31,7 @@ pub struct InternalBlockData {
pub block_id: u32,
pub block: Block,
pub groups: HashMap<u32,InternalGroupData>,
pub entities: HashMap<u32, Arc<Entity>>,
}
#[derive(Debug)]
@ -33,9 +43,25 @@ pub struct InternalGroupData {
// No extra data here
}
#[derive(Debug,Clone)]
pub enum EntityType {
Monster(Monster),
Npc(Npc),
Gadget(Gadget),
None,
}
#[derive(Debug,Clone)]
pub struct Entity {
pub entity_id: u32,
pub group_id: u32,
pub block_id: u32,
pub entity: EntityType,
}
/// Implementation of utility functions
impl InternalSceneData {
pub fn get_block_by_pos(&self, pos: &proto::Vector) -> Result<&InternalBlockData, String> {
pub fn get_block_by_pos(&self, pos: &Vector) -> Result<&InternalBlockData, String> {
for (key, value) in self.scene.block_rects.iter() {
if value.contains(pos.x, pos.z) {
let id = self.scene.blocks[&key];
@ -45,6 +71,96 @@ impl InternalSceneData {
return Err(format!("Block in coords {}, {} not found!", pos.x, pos.z));
}
pub fn get_block_by_id(&self, block_id: u32) -> Result<&InternalBlockData, String> {
match self.blocks.get(&block_id) {
Some(block) => Ok(block),
None => Err(format!("Block with ID {} not found!", block_id)),
}
}
}
impl Entity {
pub fn pos(&self) -> Vector {
match &self.entity {
EntityType::Monster(m) => m.pos.clone(),
EntityType::Npc(n) => n.pos.clone(),
EntityType::Gadget(g) => g.pos.clone(),
_ => panic!("Unsupported entity type!"),
}
}
pub fn rot(&self) -> Vector {
match &self.entity {
EntityType::Monster(m) => m.rot.clone(),
EntityType::Npc(n) => n.rot.clone(),
EntityType::Gadget(g) => g.rot.clone(),
_ => panic!("Unsupported entity type!"),
}
}
pub fn speed(&self) -> Vector {
// TODO!
Vector { x: 0.0, y: 0.0, z: 0.0 }
}
pub fn etype(&self) -> i32 {
(match &self.entity {
EntityType::Monster(_) => proto::ProtEntityType::ProtEntityMonster,
EntityType::Npc(n) => proto::ProtEntityType::ProtEntityNpc,
EntityType::Gadget(g) => proto::ProtEntityType::ProtEntityGadget,
_ => panic!("Unsupported entity type!"),
}) as i32
}
pub fn convert(&self) -> proto::SceneEntityInfo {
let mut sei = build!(SceneEntityInfo {
entity_id: self.entity_id,
entity_type: self.etype(),
motion_info: Some(build!(MotionInfo {
pos: Some((&self.pos()).into()),
rot: Some((&self.rot()).into()),
speed: Some((&self.speed()).into()),
})),
animator_para_list: vec![],
entity_client_data: Some(build!(EntityClientData {})),
entity_authority_info: Some(build!(EntityAuthorityInfo {
renderer_changed_info: Some(build!(EntityRendererChangedInfo{})),
ai_info: Some(build!(SceneEntityAiInfo {
is_ai_open: true, // TODO!
born_pos: Some((&self.pos()).into()),
})),
born_pos: Some((&self.pos()).into()),
})),
});
sei.entity = Some(match &self.entity {
EntityType::Monster(m) => proto::scene_entity_info::Entity::Monster(build!(SceneMonsterInfo {
monster_id: m.monster_id,
group_id: self.group_id,
config_id: m.config_id,
authority_peer_id: 1, // TODO: hardcoded value!
born_type: proto::MonsterBornType::MonsterBornDefault as i32, // TODO: hardcoded value!
block_id: self.block_id,
// TODO: level scaling, weapons!
})),
EntityType::Npc(n) => proto::scene_entity_info::Entity::Npc(build!(SceneNpcInfo {
npc_id: n.npc_id,
block_id: self.block_id,
})),
EntityType::Gadget(g) => proto::scene_entity_info::Entity::Gadget(build!(SceneGadgetInfo {
gadget_id: g.gadget_id,
group_id: self.group_id,
config_id: g.config_id,
authority_peer_id: 1, // TODO: hardcoded value!
is_enable_interact: true,
// TODO: collectibles!
})),
_ => panic!("Unsupported entity type!"),
});
sei
}
}
#[derive(Debug)]
@ -84,13 +200,15 @@ impl LuaManager {
}
fn load_scene(directory: &str, scene_id: u32) -> InternalSceneData {
let mut entity_id_counter: u32 = 1;
let filename = format!(scene_name!(), directory, scene_id, scene_id);
let scene: Scene = from_file(&filename).unwrap(); // TODO: error handling!
let blocks = scene.blocks
.iter()
.map(|(key, block_id)| (*block_id, Self::load_block(directory, scene_id, *block_id)))
.map(|(key, block_id)| (*block_id, Self::load_block(directory, scene_id, *block_id, &mut entity_id_counter)))
.collect();
InternalSceneData {
@ -100,11 +218,11 @@ impl LuaManager {
}
}
fn load_block(directory: &str, scene_id: u32, block_id: u32) -> InternalBlockData {
fn load_block(directory: &str, scene_id: u32, block_id: u32, entity_id_counter: &mut u32) -> InternalBlockData {
let filename = format!(block_name!(), directory, scene_id, scene_id, block_id);
let block: Block = from_file(&filename).unwrap(); // TODO: error handling!
let groups = if false
let groups: HashMap<u32,InternalGroupData> = if false
{
// TODO: should be this! But some groups are missing
block.groups
@ -124,15 +242,57 @@ impl LuaManager {
groups
};
// TODO: figure out more casual way!
let mut entities = HashMap::new();
for (group_id, igd) in groups.iter() {
for (npc_id, npc) in igd.group.npcs.iter() {
let entity_id = IdManager::get_entity_id_by_type_and_sub_id(&proto::ProtEntityType::ProtEntityNpc, *entity_id_counter);
*entity_id_counter = *entity_id_counter + 1;
entities.insert(entity_id, Arc::new(Entity {
entity_id: entity_id,
group_id: *group_id,
block_id: block_id,
entity: EntityType::Npc(npc.clone()), // TODO: very fucking inefficient!
}));
}
for (monster_id, monster) in igd.group.monsters.iter() {
let entity_id = IdManager::get_entity_id_by_type_and_sub_id(&proto::ProtEntityType::ProtEntityMonster, *entity_id_counter);
*entity_id_counter = *entity_id_counter + 1;
entities.insert(entity_id, Arc::new(Entity {
entity_id: entity_id,
group_id: *group_id,
block_id: block_id,
entity: EntityType::Monster(monster.clone()), // TODO: very fucking inefficient!
}));
}
for (gadget_id, gadget) in igd.group.gadgets.iter() {
let entity_id = IdManager::get_entity_id_by_type_and_sub_id(&proto::ProtEntityType::ProtEntityGadget, *entity_id_counter);
*entity_id_counter = *entity_id_counter + 1;
entities.insert(entity_id, Arc::new(Entity {
entity_id: entity_id,
group_id: *group_id,
block_id: block_id,
entity: EntityType::Gadget(gadget.clone()), // TODO: very fucking inefficient!
}));
}
}
InternalBlockData {
scene_id,
block_id,
block,
groups,
entities,
}
}
fn load_group(directory: &str, scene_id: u32, block_id: u32, group_id: u32) -> Result<InternalGroupData, std::io::Error> {
fn load_group(directory: &str, scene_id: u32, block_id: u32, group_id: u32) -> Result<(InternalGroupData), std::io::Error> {
let filename = format!(group_name!(), directory, scene_id, scene_id, group_id);
//let group: Group = from_file(&filename).unwrap(); // TODO: error handling!
let group: Group = from_file(&filename)?;

View File

@ -1,4 +1,6 @@
mod lua_manager;
mod scene_config;
pub use self::lua_manager::LuaManager;
pub use self::lua_manager::LuaManager;
pub use self::scene_config::Vector;
pub use self::lua_manager::Entity;

View File

@ -5,7 +5,7 @@ use serde::Deserialize;
// sceneX.lua
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Vector {
#[serde(default)]
pub x: f32,
@ -49,7 +49,19 @@ impl Vector {
}
}
#[derive(Deserialize, PartialEq, Debug)]
impl From<&Vector> for proto::Vector {
fn from(v: &Vector) -> proto::Vector {
proto::Vector { x: v.x, y: v.y, z: v.z }
}
}
impl From<&proto::Vector> for Vector {
fn from(v: &proto::Vector) -> Vector {
Vector { x: v.x, y: v.y, z: v.z }
}
}
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct BlockRect {
pub min: Vector,
pub max: Vector,
@ -64,7 +76,7 @@ impl BlockRect {
}
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct SceneConfig {
pub born_rot: Vector,
pub born_pos: Vector,
@ -74,7 +86,7 @@ pub struct SceneConfig {
pub die_y: f32,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Scene {
pub blocks: HashMap<u32,u32>,
#[serde(default)]
@ -88,12 +100,12 @@ pub struct Scene {
// sceneX_blockY.lua
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Block {
pub groups: HashMap<u32,GroupInfo>,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct GroupInfo {
pub is_replaceable: Option<ComplicatedBool>,
#[serde(default)]
@ -104,21 +116,21 @@ pub struct GroupInfo {
pub business: Option<Business>,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct ComplicatedBool {
pub version: u32,
pub value: bool,
pub new_bin_only: bool,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Business {
pub r#type: u32,
}
// sceneX_groupZ.lua
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Group {
pub init_config: Option<GroupInitConfig>,
#[serde(default)]
@ -140,7 +152,7 @@ pub struct Group {
// MovePlatform - Function???
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Suite {
pub rand_weight: u32,
@ -157,7 +169,7 @@ pub struct Suite {
pub monsters: HashMap<u32,u32>,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct GroupInitConfig {
pub end_suite: Option<u32>,
//#[serde(default)]
@ -165,14 +177,14 @@ pub struct GroupInitConfig {
pub suite: u32,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Variable {
pub name: String,
pub value: u32,
pub no_refresh: bool,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Monster {
pub rot: Vector,
pub pos: Vector,
@ -181,7 +193,7 @@ pub struct Monster {
pub monster_id: u32,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Npc {
pub rot: Vector,
pub pos: Vector,
@ -192,7 +204,7 @@ pub struct Npc {
pub room: Option<u32>,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct Gadget {
pub rot: Vector,
pub pos: Vector,
@ -215,7 +227,7 @@ pub struct Gadget {
pub is_use_point_array: bool,
}
#[derive(Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub struct ExploreInfo {
pub exp: u32,
pub name: String,

View File

@ -1,6 +1,6 @@
use std::sync::{mpsc::{self, Sender, Receiver}, Arc, Mutex};
use std::thread;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use crate::server::IpcMessage;
@ -17,20 +17,23 @@ use serde_json::de::Read;
use crate::LuaManager;
use crate::utils::{IdManager, TimeManager};
use crate::luamanager::Vector;
use crate::luamanager::Entity;
#[derive(Debug, Clone)]
struct Player {
player_id: u32,
pos: proto::Vector,
pos: Vector,
current_scene: u32,
current_block: u32,
entities: HashMap<u32,Entity>,
entities: HashMap<u32, Arc<Entity>>,
lua_manager: Arc<LuaManager>,
packets_to_send_tx: Sender<IpcMessage>,
}
impl Player {
const DESPAWN_DISTANCE: f32 = 10.0;
const SPAWN_DISTANCE: f32 = 8.0;
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) {
@ -53,19 +56,67 @@ impl Player {
pub fn position_changed(&mut self) {
// 1. Go through the list of spawned entities and despawn those that are too far from us
// 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 despawn_list: Vec<u32> = 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<u32> = 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<Arc<Entity>> = block.entities.iter()
.filter(|(entity_id, entity)| !spawned_list.contains(entity_id))
.filter(|(entity_id, entity)| entity.pos().sub(&self.pos).len() < Self::SPAWN_DISTANCE)
.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,
});
build_and_send!(self, player_id, metadata, SceneEntityAppearNotify {
entity_list: spawn_list.iter().map(|e| e.convert()).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
}
#[derive(Debug,Clone)]
pub struct Entity {
entity_id: u32,
health: i32,
}
#[packet_processor(
CombatInvocationsNotify,
)]
@ -112,10 +163,13 @@ impl EntitySubsystem {
match block {
Ok(block) =>
if player.current_block != block.block_id {
println!("Player {:?} moved to the block {:?}", player, block.block_id);
println!("Player {:?} moved to the block {:?}", player.player_id, block.block_id);
player.current_block = block.block_id;
},
Err(_) => {/* TODO? */},
Err(_) => {
// TODO?
player.current_block = 0;
},
};
player.position_changed();
@ -167,7 +221,7 @@ impl EntitySubsystem {
// Avatar moved => update player's position
let pos = if let Some(motion_info) = invoke.motion_info.as_ref() {
if let Some(pos) = motion_info.pos.as_ref() {
pos.clone()
pos.into()
} else {
return;
}