- add: Character service can now actually create a character correctly in the database
- add: character db client to allow the character service to talk to the database service - update: character.proto to make character data shared
This commit is contained in:
@@ -13,6 +13,8 @@ tracing-subscriber = "0.3.18"
|
||||
tonic = "0.12.3"
|
||||
prost = "0.13.4"
|
||||
warp = "0.3.7"
|
||||
async-trait = "0.1.83"
|
||||
serde_json = "1.0.133"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.12.3"
|
||||
@@ -4,13 +4,13 @@ fn main() {
|
||||
.build_server(true) // Generate gRPC server code
|
||||
.compile_well_known_types(true)
|
||||
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||
.compile_protos(&["../proto/character.proto"], &["../proto"])
|
||||
.compile_protos(&["../proto/character_common.proto", "../proto/character.proto"], &["../proto"])
|
||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||
|
||||
// gRPC Client code
|
||||
tonic_build::configure()
|
||||
.build_server(false) // Generate gRPC client code
|
||||
.compile_well_known_types(true)
|
||||
.compile_protos(&["../proto/user_db_api.proto", "../proto/character_db_api.proto", "../proto/auth.proto"], &["../proto"])
|
||||
.compile_protos(&["../proto/character_db_api.proto", "../proto/auth.proto"], &["../proto"])
|
||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||
}
|
||||
|
||||
143
character-service/src/character_db_client.rs
Normal file
143
character-service/src/character_db_client.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use tonic::transport::Channel;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::database::character_service_client::CharacterServiceClient;
|
||||
use crate::database::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CharacterDbClient {
|
||||
client: CharacterServiceClient<Channel>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Item {
|
||||
item_id: i32,
|
||||
item_type: i32,
|
||||
count: i32,
|
||||
durability: f32,
|
||||
slot: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Looks {
|
||||
race: i32,
|
||||
face: i32,
|
||||
hair: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Position {
|
||||
map_id: i32,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Stats {
|
||||
job: u16,
|
||||
str: u16,
|
||||
dex: u16,
|
||||
int: u16,
|
||||
con: u16,
|
||||
charm: u16,
|
||||
sense: u16,
|
||||
max_hp: i32,
|
||||
hp: i32,
|
||||
max_mp: i32,
|
||||
mp: i32,
|
||||
xp: u32,
|
||||
level: u16,
|
||||
head_size: u8,
|
||||
body_size: u8,
|
||||
stat_points: u32,
|
||||
skill_points: u32,
|
||||
penalty_xp: u32,
|
||||
stamina: u16,
|
||||
pat_hp: u16,
|
||||
pat_cooldown_time: u32,
|
||||
}
|
||||
|
||||
impl CharacterDbClient {
|
||||
pub async fn connect(endpoint: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let client = CharacterServiceClient::connect(endpoint.to_string()).await?;
|
||||
Ok(Self { client })
|
||||
}
|
||||
|
||||
pub async fn create_character(&mut self, user_id: &str, name: &str, race: i32, face: i32, hair: i32, stone: i32) -> Result<CreateCharacterResponse, Box<dyn std::error::Error>> {
|
||||
let mut hatid = 221;
|
||||
if 0 == race {
|
||||
hatid = 222;
|
||||
}
|
||||
|
||||
let inventory = vec![
|
||||
Item {
|
||||
item_id: 30,
|
||||
item_type: 3,
|
||||
count: 1,
|
||||
slot: 3,
|
||||
durability: 45.0,
|
||||
},
|
||||
Item {
|
||||
item_id: 1,
|
||||
item_type: 8,
|
||||
count: 1,
|
||||
slot: 7,
|
||||
durability: 45.0,
|
||||
},
|
||||
Item {
|
||||
item_id: hatid,
|
||||
item_type: 2,
|
||||
count: 1,
|
||||
slot: 12,
|
||||
durability: 45.0,
|
||||
},
|
||||
];
|
||||
|
||||
let stats = Stats {
|
||||
job: 0,
|
||||
str: 10,
|
||||
dex: 10,
|
||||
int: 10,
|
||||
con: 10,
|
||||
charm: 10,
|
||||
sense: 10,
|
||||
max_hp: 81,
|
||||
hp: 81,
|
||||
max_mp: 55,
|
||||
mp: 55,
|
||||
xp: 0,
|
||||
level: 1,
|
||||
head_size: 1,
|
||||
body_size: 1,
|
||||
skill_points: 0,
|
||||
stat_points: 0,
|
||||
penalty_xp: 0,
|
||||
stamina: 0,
|
||||
pat_hp: 0,
|
||||
pat_cooldown_time: 0,
|
||||
};
|
||||
|
||||
let looks = Looks {race, face, hair};
|
||||
let position = Position {map_id: 20, x: 5200.00, y: 5200.00, z: 1.0};
|
||||
|
||||
let request = tonic::Request::new(CreateCharacterRequest {
|
||||
user_id: user_id.parse().unwrap(),
|
||||
name: name.to_string(),
|
||||
inventory: serde_json::to_value(inventory)?.to_string(),
|
||||
stats: serde_json::to_value(stats)?.to_string(),
|
||||
looks: serde_json::to_value(looks)?.to_string(),
|
||||
position: serde_json::to_value(position)?.to_string(),
|
||||
});
|
||||
let response = self.client.create_character(request).await?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
pub async fn delete_character(&mut self, user_id: &str, char_id: &str) -> Result<DeleteCharacterResponse, Box<dyn std::error::Error>> {
|
||||
let request = tonic::Request::new(DeleteCharacterRequest {
|
||||
user_id: user_id.parse().unwrap(),
|
||||
character_id: char_id.parse().unwrap(),
|
||||
});
|
||||
let response = self.client.delete_character(request).await?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,23 @@
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
use tonic::{Request, Response, Status};
|
||||
use tracing::field::debug;
|
||||
use utils::null_string::NullTerminatedString;
|
||||
use crate::character_db_client::CharacterDbClient;
|
||||
use crate::character_service::character::character_service_server::CharacterService;
|
||||
use crate::character_service::character::{Character, CreateCharacterRequest, DeleteCharacterRequest, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest};
|
||||
use crate::character_service::character::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest};
|
||||
use crate::character_service::character_common::{Character, Looks, Stats};
|
||||
|
||||
pub mod character_common {
|
||||
tonic::include_proto!("character_common");
|
||||
}
|
||||
pub mod character {
|
||||
|
||||
tonic::include_proto!("character");
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MyCharacterService {}
|
||||
pub struct MyCharacterService {
|
||||
pub character_db_client: Arc<CharacterDbClient>,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl CharacterService for MyCharacterService {
|
||||
@@ -19,30 +27,41 @@ impl CharacterService for MyCharacterService {
|
||||
let user_id = req.user_id;
|
||||
debug!("Character list for User ID: {}", user_id);
|
||||
|
||||
// todo: get the data from the database
|
||||
// self.character_db_client.as_ref();
|
||||
|
||||
// Simulated database fetch for characters
|
||||
let characters = vec![
|
||||
Character {
|
||||
character_id: "1".to_string(),
|
||||
name: "Warrior123".to_string(),
|
||||
level: 1,
|
||||
race: 1,
|
||||
job: 111,
|
||||
last_played: 1633017600, // Example timestamp
|
||||
last_played: 1633017600,
|
||||
delete_time: 0,
|
||||
face: 1,
|
||||
hair: 0,
|
||||
stats: Option::from(Stats {
|
||||
job: 0,
|
||||
level: 0,
|
||||
}),
|
||||
looks: Option::from(Looks {
|
||||
race: 0,
|
||||
hair: 0,
|
||||
face: 0,
|
||||
}),
|
||||
items: vec![],
|
||||
},
|
||||
Character {
|
||||
character_id: "2".to_string(),
|
||||
name: "Mage123".to_string(),
|
||||
level: 20,
|
||||
race: 0,
|
||||
job: 211,
|
||||
last_played: 1633017600, // Example timestamp
|
||||
last_played: 1633017600,
|
||||
delete_time: 0,
|
||||
face: 1,
|
||||
hair: 0,
|
||||
stats: Option::from(Stats {
|
||||
job: 211,
|
||||
level: 20,
|
||||
}),
|
||||
looks: Option::from(Looks {
|
||||
race: 1,
|
||||
hair: 0,
|
||||
face: 1,
|
||||
}),
|
||||
items: vec![],
|
||||
},
|
||||
];
|
||||
@@ -51,15 +70,30 @@ impl CharacterService for MyCharacterService {
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn create_character(&self, request: Request<CreateCharacterRequest>) -> Result<Response<Empty>, Status> {
|
||||
todo!()
|
||||
async fn create_character(&self, request: Request<CreateCharacterRequest>) -> Result<Response<CreateCharacterResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
debug!("{:?}", req);
|
||||
|
||||
let create_character_response = self.character_db_client.as_ref().clone().create_character(&req.user_id, &req.name, req.race, req.face, req.hair, req.stone)
|
||||
.await
|
||||
.map_err(|_| Status::aborted("Unable to create character"))?;
|
||||
|
||||
let response = CreateCharacterResponse { result: create_character_response.result };
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn delete_character(&self, request: Request<DeleteCharacterRequest>) -> Result<Response<Empty>, Status> {
|
||||
todo!()
|
||||
async fn delete_character(&self, request: Request<DeleteCharacterRequest>) -> Result<Response<DeleteCharacterResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
debug!("{:?}", req);
|
||||
|
||||
let delete_character_response = self.character_db_client.as_ref().clone().delete_character(&req.user_id, &req.char_id).await.map_err(|_| Status::not_found("Character not found"))?;
|
||||
let response = DeleteCharacterResponse { remaining_time: delete_character_response.remaining_time, name: delete_character_response.name };
|
||||
Ok(Response::new(response))
|
||||
}
|
||||
|
||||
async fn get_character(&self, request: Request<GetCharacterRequest>) -> Result<Response<Empty>, Status> {
|
||||
todo!()
|
||||
let req = request.into_inner();
|
||||
debug!("{:?}", req);
|
||||
Ok(Response::new(Empty{}))
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
mod character_service;
|
||||
mod character_db_client;
|
||||
pub mod database {
|
||||
tonic::include_proto!("character_db_api");
|
||||
}
|
||||
|
||||
use dotenv::dotenv;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::{select, signal};
|
||||
use tracing::{info, Level};
|
||||
use utils::consul_registration;
|
||||
use utils::service_discovery::get_service_address;
|
||||
use crate::character_db_client::CharacterDbClient;
|
||||
use crate::character_service::character::character_service_server::CharacterServiceServer;
|
||||
use crate::character_service::MyCharacterService;
|
||||
|
||||
@@ -55,7 +61,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let address = full_addr.parse().expect("Invalid address");
|
||||
let db_address = db_nodes.get(0).unwrap();
|
||||
let db_url = format!("http://{}:{}", db_address.ServiceAddress, db_address.ServicePort);
|
||||
let character_service = MyCharacterService::default();
|
||||
let character_db_client = Arc::new(CharacterDbClient::connect(&db_url).await?);
|
||||
let character_service = MyCharacterService {
|
||||
character_db_client
|
||||
};
|
||||
|
||||
tonic::transport::Server::builder()
|
||||
.add_service(CharacterServiceServer::new(character_service))
|
||||
|
||||
Reference in New Issue
Block a user