Files
osirose-new/game-logic-service/src/main.rs
raven d906cd8d64 Tons of fixes
Added movement updates
Updated how entities are checked
Events sending between packet service all the way to the logic service
2025-07-02 15:37:10 -04:00

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(())
}