Added movement updates Updated how entities are checked Events sending between packet service all the way to the logic service
196 lines
6.9 KiB
Rust
196 lines
6.9 KiB
Rust
pub mod components;
|
|
mod entity_factory;
|
|
mod entity_system;
|
|
mod id_manager;
|
|
mod loader;
|
|
mod random;
|
|
mod game_logic_service;
|
|
mod game_service;
|
|
mod world_client;
|
|
mod spatial_grid;
|
|
|
|
use dotenv::dotenv;
|
|
use std::env;
|
|
use std::sync::Arc;
|
|
use hecs::World;
|
|
use tokio::time::{timeout, Duration};
|
|
use tokio::sync::oneshot;
|
|
use tonic::transport::Server;
|
|
use tracing::{debug, error, info, warn};
|
|
use utils::service_discovery::{get_kube_service_endpoints_by_dns, get_service_endpoints_by_dns};
|
|
use utils::{health_check, logging};
|
|
|
|
use loader::load_zone_for_map;
|
|
use crate::components::position::Position;
|
|
use crate::entity_factory::EntityFactory;
|
|
use crate::entity_system::EntitySystem;
|
|
use crate::world_client::{WorldGameLogicServiceImpl, WorldGameLogicServiceServer};
|
|
use crate::game_logic_service::{MyGameLogicService, game_logic::game_logic_service_server::GameLogicServiceServer};
|
|
use crate::game_service::{MyGameService};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
dotenv().ok();
|
|
let app_name = env!("CARGO_PKG_NAME");
|
|
logging::setup_logging(app_name, &["game_logic_service", "health_check"]);
|
|
|
|
|
|
let map_id = env::var("MAP_ID").unwrap_or_else(|_| "20".to_string()).parse::<u32>()?;
|
|
let file_path = "/opt/data/zone_data.json";
|
|
|
|
// // Set the gRPC server address
|
|
let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0".to_string());
|
|
let port = env::var("SERVICE_PORT").unwrap_or_else(|_| "50056".to_string());
|
|
let db_url = format!(
|
|
"http://{}",
|
|
get_kube_service_endpoints_by_dns("database-service", "tcp", "database-service")
|
|
.await?
|
|
.get(0)
|
|
.unwrap()
|
|
);
|
|
let chat_service = format!(
|
|
"http://{}",
|
|
get_kube_service_endpoints_by_dns("chat-service", "tcp", "chat-service")
|
|
.await?
|
|
.get(0)
|
|
.unwrap()
|
|
);
|
|
|
|
// Create world service first
|
|
let world_game_logic_service_impl = WorldGameLogicServiceImpl::new(map_id);
|
|
let world_game_logic_service_arc = Arc::new(world_game_logic_service_impl);
|
|
|
|
// Create shared entity system with world service
|
|
let entity_system = Arc::new(EntitySystem::new().with_world_service(world_game_logic_service_arc.clone()));
|
|
|
|
// Create world service with entity system for the gRPC server
|
|
let world_game_logic_service = WorldGameLogicServiceImpl::new(map_id).with_entity_system(entity_system.clone());
|
|
|
|
// Create gRPC services
|
|
let game_logic_service = MyGameLogicService {
|
|
map_id,
|
|
entity_system: entity_system.clone(),
|
|
};
|
|
let game_service = MyGameService {};
|
|
|
|
// Start gRPC server with graceful shutdown support
|
|
let grpc_addr = format!("{}:{}", addr, port).parse()?;
|
|
info!("Starting Game Logic Service gRPC server on {}", grpc_addr);
|
|
|
|
// Create shutdown signal channels
|
|
let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
|
|
let (game_logic_shutdown_tx, mut game_logic_shutdown_rx) = oneshot::channel::<()>();
|
|
|
|
let server_task = tokio::spawn(async move {
|
|
let server = Server::builder()
|
|
.add_service(GameLogicServiceServer::new(game_logic_service))
|
|
.add_service(game_service.into_service())
|
|
.add_service(WorldGameLogicServiceServer::new(world_game_logic_service))
|
|
.serve_with_shutdown(grpc_addr, async {
|
|
shutdown_rx.await.ok();
|
|
info!("gRPC server shutdown signal received");
|
|
});
|
|
|
|
if let Err(e) = server.await {
|
|
error!("gRPC server error: {}", e);
|
|
} else {
|
|
info!("gRPC server shut down gracefully");
|
|
}
|
|
});
|
|
|
|
let entity_system_clone = entity_system.clone();
|
|
let game_logic_task = tokio::spawn(async move {
|
|
let mut loading = true;
|
|
|
|
loop {
|
|
// Check for shutdown signal
|
|
if let Ok(_) = game_logic_shutdown_rx.try_recv() {
|
|
info!("Game logic task received shutdown signal");
|
|
break;
|
|
}
|
|
|
|
// Load the map
|
|
if loading {
|
|
match load_zone_for_map(file_path, map_id) {
|
|
Ok(Some(zone)) => {
|
|
info!("Zone with Map Id {} found:", map_id);
|
|
entity_system_clone.load_map(zone);
|
|
loading = false;
|
|
}
|
|
Ok(None) => {
|
|
error!("No zone found for Map Id {}", map_id);
|
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
}
|
|
Err(e) => {
|
|
error!("Error loading zone: {}", e);
|
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the world
|
|
if !loading {
|
|
entity_system_clone.run();
|
|
}
|
|
std::thread::sleep(std::time::Duration::from_millis(1000/30));
|
|
}
|
|
info!("Game logic task shut down gracefully");
|
|
});
|
|
|
|
// Register service with Consul
|
|
// health_check::start_health_check(addr.as_str()).await?;
|
|
|
|
// Wait for shutdown signal
|
|
info!("Game Logic service is running. Waiting for shutdown signal...");
|
|
utils::signal_handler::wait_for_signal().await;
|
|
|
|
info!("Shutdown signal received. Beginning graceful shutdown...");
|
|
|
|
// Step 1: Signal the game logic task to stop
|
|
if let Err(_) = game_logic_shutdown_tx.send(()) {
|
|
warn!("Failed to send shutdown signal to game logic task (receiver may have been dropped)");
|
|
}
|
|
|
|
// Step 2: Signal the gRPC server to stop accepting new connections
|
|
if let Err(_) = shutdown_tx.send(()) {
|
|
warn!("Failed to send shutdown signal to gRPC server (receiver may have been dropped)");
|
|
}
|
|
|
|
// Step 3: Wait for both tasks to finish with timeouts
|
|
let mut shutdown_errors = Vec::new();
|
|
|
|
match timeout(Duration::from_secs(10), game_logic_task).await {
|
|
Ok(result) => {
|
|
if let Err(e) = result {
|
|
error!("Game logic task failed: {}", e);
|
|
shutdown_errors.push(format!("Game logic task: {}", e));
|
|
}
|
|
}
|
|
Err(_) => {
|
|
error!("Game logic task shutdown timed out after 10 seconds");
|
|
shutdown_errors.push("Game logic task: timeout".to_string());
|
|
}
|
|
}
|
|
|
|
match timeout(Duration::from_secs(30), server_task).await {
|
|
Ok(result) => {
|
|
if let Err(e) = result {
|
|
error!("gRPC server task failed: {}", e);
|
|
shutdown_errors.push(format!("gRPC server task: {}", e));
|
|
}
|
|
}
|
|
Err(_) => {
|
|
error!("gRPC server shutdown timed out after 30 seconds");
|
|
shutdown_errors.push("gRPC server task: timeout".to_string());
|
|
}
|
|
}
|
|
|
|
if shutdown_errors.is_empty() {
|
|
info!("All components shut down successfully");
|
|
} else {
|
|
warn!("Some components failed to shut down cleanly: {:?}", shutdown_errors);
|
|
}
|
|
|
|
Ok(())
|
|
}
|