Updated how we handle client ids in the world service and logic service
Implemented the bidirectional comms stream between the world service and game logic service
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CharacterGraphics {
|
||||
pub(crate) struct CharacterGraphics {
|
||||
race: u8,
|
||||
hair: u8,
|
||||
face: u8,
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Client {
|
||||
pub client_id: u16,
|
||||
pub client_id: String,
|
||||
pub access_level: u16,
|
||||
pub session_id: String
|
||||
}
|
||||
|
||||
impl Default for Client {
|
||||
fn default() -> Self {
|
||||
Self { client_id: 0, access_level: 1, session_id: "".to_string() }
|
||||
Self { client_id: "".to_string(), access_level: 1, session_id: "".to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn get_client_id(&self) -> u16 {
|
||||
self.client_id
|
||||
pub fn get_client_id(&self) -> &str {
|
||||
&self.client_id
|
||||
}
|
||||
|
||||
pub fn get_access_level(&self) -> u16 {
|
||||
|
||||
@@ -4,7 +4,9 @@ use hecs::{Entity, World};
|
||||
use tracing::debug;
|
||||
use crate::components::basic_info::BasicInfo;
|
||||
use crate::components::destination::Destination;
|
||||
use crate::components::level::Level;
|
||||
use crate::components::position::Position;
|
||||
use crate::components::character_graphics::CharacterGraphics;
|
||||
use crate::components::markers::*;
|
||||
use crate::components::life::Life;
|
||||
use crate::spatial_grid::SpatialGrid;
|
||||
@@ -101,8 +103,8 @@ impl EntitySystem {
|
||||
*spatial_grid = temp_factory.spatial_grid;
|
||||
}
|
||||
|
||||
/// Get nearby objects for a specific client
|
||||
pub fn get_nearby_objects_for_client(&self, client_id: u16, x: f32, y: f32, z: f32, map_id: u16) -> Vec<EntityInfo> {
|
||||
/// Get nearby objects for a specific client (excluding the client itself)
|
||||
pub fn get_nearby_objects_for_client(&self, client_id: &str, x: f32, y: f32, z: f32, map_id: u16) -> Vec<EntityInfo> {
|
||||
let world = self.world.lock().unwrap();
|
||||
let spatial_grid = self.spatial_grid.lock().unwrap();
|
||||
|
||||
@@ -146,7 +148,7 @@ impl EntitySystem {
|
||||
entity_infos
|
||||
}
|
||||
|
||||
/// Get nearby objects at a specific position
|
||||
/// Get all nearby objects at a specific position
|
||||
pub fn get_nearby_objects_at_position(&self, x: f32, y: f32, z: f32, map_id: u16) -> Vec<EntityInfo> {
|
||||
let world = self.world.lock().unwrap();
|
||||
let spatial_grid = self.spatial_grid.lock().unwrap();
|
||||
@@ -208,6 +210,46 @@ impl EntitySystem {
|
||||
return None; // Skip entities without recognized types
|
||||
};
|
||||
|
||||
// get the extra entity data based on the type
|
||||
let extra_data = match entity_type {
|
||||
EntityType::Player => {
|
||||
let player = world.get::<&Player>(entity).ok()?;
|
||||
let level = world.get::<&Level>(entity).ok()?;
|
||||
let character_graphics = world.get::<&CharacterGraphics>(entity).ok()?;
|
||||
ExtraEntityData {
|
||||
player: Some(PlayerData {
|
||||
level: level.get_level() as u32,
|
||||
class: basic_info.job as u32,
|
||||
race: character_graphics.get_race() as u32,
|
||||
}),
|
||||
npc: None,
|
||||
mob: None,
|
||||
}
|
||||
},
|
||||
EntityType::Npc => {
|
||||
let npc = world.get::<&Npc>(entity).ok()?;
|
||||
ExtraEntityData {
|
||||
player: None,
|
||||
npc: Some(NpcData {
|
||||
quest_id: npc.quest_id,
|
||||
angle: npc.angle as u32,
|
||||
event_status: npc.event_status as u32,
|
||||
}),
|
||||
mob: None,
|
||||
}
|
||||
},
|
||||
EntityType::Mob => {
|
||||
let mob = world.get::<&Mob>(entity).ok()?;
|
||||
ExtraEntityData {
|
||||
player: None,
|
||||
npc: None,
|
||||
mob: Some(MobData {
|
||||
quest_id: mob.quest_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some(EntityInfo {
|
||||
id: basic_info.id as i32,
|
||||
entity_type,
|
||||
@@ -217,6 +259,7 @@ impl EntitySystem {
|
||||
name: basic_info.name.clone(),
|
||||
hp,
|
||||
max_hp,
|
||||
extra_data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -225,7 +268,7 @@ impl EntitySystem {
|
||||
self.world.clone()
|
||||
}
|
||||
|
||||
pub fn set_destination(&self, client_id: u16, x: f32, y: f32, z: f32) {
|
||||
pub fn set_destination(&self, client_id: &str, x: f32, y: f32, z: f32) {
|
||||
use crate::components::client::Client;
|
||||
|
||||
let mut world = self.world.lock().unwrap();
|
||||
@@ -252,7 +295,7 @@ impl EntitySystem {
|
||||
|
||||
|
||||
/// Update player position and check for nearby entity changes
|
||||
pub fn update_player_position(&self, client_id: u16, x: f32, y: f32, z: f32, map_id: u16) {
|
||||
pub fn update_player_position(&self, client_id: &str, x: f32, y: f32, z: f32, map_id: u16) {
|
||||
use crate::components::client::Client;
|
||||
|
||||
let mut world = self.world.lock().unwrap();
|
||||
@@ -324,6 +367,33 @@ pub struct EntityInfo {
|
||||
pub name: String,
|
||||
pub hp: i32,
|
||||
pub max_hp: i32,
|
||||
pub extra_data: ExtraEntityData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtraEntityData {
|
||||
pub player: Option<PlayerData>,
|
||||
pub npc: Option<NpcData>,
|
||||
pub mob: Option<MobData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PlayerData {
|
||||
pub level: u32,
|
||||
pub class: u32,
|
||||
pub race: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NpcData {
|
||||
pub quest_id: u32,
|
||||
pub angle: u32,
|
||||
pub event_status: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MobData {
|
||||
pub quest_id: u32,
|
||||
}
|
||||
|
||||
/// Type of entity
|
||||
|
||||
@@ -27,7 +27,7 @@ impl GameLogicService for MyGameLogicService {
|
||||
|
||||
// Get nearby entities from the entity system
|
||||
let entity_infos = self.entity_system.get_nearby_objects_for_client(
|
||||
req.client_id as u16,
|
||||
&req.client_id,
|
||||
req.x,
|
||||
req.y,
|
||||
req.z,
|
||||
@@ -35,6 +35,7 @@ impl GameLogicService for MyGameLogicService {
|
||||
);
|
||||
|
||||
// Convert EntityInfo to proto Object
|
||||
// TODO: we also need to grab the extra data from the entity and add it to the objects
|
||||
let objects: Vec<Object> = entity_infos
|
||||
.into_iter()
|
||||
.map(|info| Object {
|
||||
|
||||
@@ -63,6 +63,7 @@ impl WorldGameLogicService for WorldGameLogicServiceImpl {
|
||||
|
||||
let map_id = self.map_id;
|
||||
let entity_system = self.entity_system.clone();
|
||||
let outbound_tx_clone = outbound_tx.clone();
|
||||
|
||||
// Handle incoming events from world service
|
||||
tokio::spawn(async move {
|
||||
@@ -76,10 +77,38 @@ impl WorldGameLogicService for WorldGameLogicServiceImpl {
|
||||
Some(world::game_logic_event::Event::PlayerConnect(connect_event)) => {
|
||||
info!("Player {} connected to map {}", connect_event.session_id, map_id);
|
||||
// Handle player connection logic
|
||||
if let Some(entity_system) = &entity_system {
|
||||
|
||||
let world = entity_system.get_world();
|
||||
let mut world_lock = world.lock().unwrap();
|
||||
|
||||
//TODO: add an entity for the player if it doesn't exist
|
||||
// entity_system.spawn_player(crate::components::position::Position {
|
||||
// x: connect_event.x,
|
||||
// y: connect_event.y,
|
||||
// z: connect_event.z,
|
||||
// map_id: map_id as u16,
|
||||
// spawn_id: 0,
|
||||
// });
|
||||
|
||||
// Find the player entity and update session_id using UUID client_id
|
||||
let mut query = world_lock.query::<&mut crate::components::client::Client>();
|
||||
for (entity, mut client) in query.iter() {
|
||||
if client.client_id == connect_event.client_id {
|
||||
client.session_id = connect_event.session_id.clone();
|
||||
debug!("Updated session_id for client_id {} to {}", connect_event.client_id, connect_event.session_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(world::game_logic_event::Event::PlayerDisconnect(disconnect_event)) => {
|
||||
info!("Player {} disconnected from map {}", disconnect_event.session_id, map_id);
|
||||
// Handle player disconnection logic
|
||||
// Handle player disconnection logic - for now just log it
|
||||
// TODO: Remove player entity from the world when disconnect handling is implemented
|
||||
if let Some(entity_system) = &entity_system {
|
||||
// entity_system.remove_player(&disconnect_event.session_id);
|
||||
}
|
||||
}
|
||||
Some(world::game_logic_event::Event::PlayerMove(move_event)) => {
|
||||
debug!("Player {} moved to ({}, {}, {}) in map {}",
|
||||
@@ -87,18 +116,13 @@ impl WorldGameLogicService for WorldGameLogicServiceImpl {
|
||||
|
||||
// Handle player movement logic - update position and check for nearby changes
|
||||
if let Some(entity_system) = &entity_system {
|
||||
// Parse client_id from string to u16
|
||||
if let Ok(client_id) = move_event.client_id.parse::<u16>() {
|
||||
entity_system.update_player_position(
|
||||
client_id,
|
||||
move_event.x,
|
||||
move_event.y,
|
||||
move_event.z,
|
||||
map_id as u16
|
||||
);
|
||||
} else {
|
||||
debug!("Failed to parse client_id: {}", move_event.client_id);
|
||||
}
|
||||
entity_system.update_player_position(
|
||||
&move_event.client_id,
|
||||
move_event.x,
|
||||
move_event.y,
|
||||
move_event.z,
|
||||
map_id as u16
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -115,6 +139,35 @@ impl WorldGameLogicService for WorldGameLogicServiceImpl {
|
||||
info!("World service stream ended for map {}", map_id);
|
||||
});
|
||||
|
||||
// Send an initial connection acknowledgment event to keep the stream alive
|
||||
let initial_event = world::GameLogicEvent {
|
||||
client_ids: vec![],
|
||||
map_id: map_id as i32,
|
||||
event: None, // Empty event just to establish the stream
|
||||
};
|
||||
|
||||
if let Err(e) = outbound_tx.send(initial_event) {
|
||||
error!("Failed to send initial event for map {}: {}", map_id, e);
|
||||
}
|
||||
|
||||
// Spawn a task to periodically check for spawner events and send them
|
||||
let entity_system_spawner = self.entity_system.clone();
|
||||
let _outbound_tx_spawner = outbound_tx.clone();
|
||||
let _spawner_map_id = map_id;
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
if let Some(ref _entity_system) = entity_system_spawner {
|
||||
// Check for any spawner events that need to be sent
|
||||
// For now, this keeps the task alive and ready for future spawner events
|
||||
// Maybe we want to use a pub sub channel for spawner events in the future
|
||||
// See entity_factory.rs ln 285 for where we would send the events
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create outbound stream for sending events to world service
|
||||
let outbound_stream = tokio_stream::wrappers::UnboundedReceiverStream::new(outbound_rx)
|
||||
.map(|event| Ok(event));
|
||||
|
||||
Reference in New Issue
Block a user