Compare commits
2 Commits
9f63a5fd3a
...
cb6ee657f0
| Author | SHA256 | Date | |
|---|---|---|---|
|
cb6ee657f0
|
|||
|
c6c502fd8a
|
@@ -13,6 +13,8 @@ tracing-subscriber = "0.3.18"
|
|||||||
tonic = "0.12.3"
|
tonic = "0.12.3"
|
||||||
prost = "0.13.4"
|
prost = "0.13.4"
|
||||||
warp = "0.3.7"
|
warp = "0.3.7"
|
||||||
|
async-trait = "0.1.83"
|
||||||
|
serde_json = "1.0.133"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12.3"
|
tonic-build = "0.12.3"
|
||||||
@@ -4,13 +4,13 @@ fn main() {
|
|||||||
.build_server(true) // Generate gRPC server code
|
.build_server(true) // Generate gRPC server code
|
||||||
.compile_well_known_types(true)
|
.compile_well_known_types(true)
|
||||||
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
.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));
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
|
||||||
// gRPC Client code
|
// gRPC Client code
|
||||||
tonic_build::configure()
|
tonic_build::configure()
|
||||||
.build_server(false) // Generate gRPC client code
|
.build_server(false) // Generate gRPC client code
|
||||||
.compile_well_known_types(true)
|
.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));
|
.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 tracing::debug;
|
||||||
use tonic::{Request, Response, Status};
|
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_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 {
|
pub mod character {
|
||||||
|
|
||||||
tonic::include_proto!("character");
|
tonic::include_proto!("character");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
pub struct MyCharacterService {
|
||||||
pub struct MyCharacterService {}
|
pub character_db_client: Arc<CharacterDbClient>,
|
||||||
|
}
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl CharacterService for MyCharacterService {
|
impl CharacterService for MyCharacterService {
|
||||||
@@ -19,30 +27,41 @@ impl CharacterService for MyCharacterService {
|
|||||||
let user_id = req.user_id;
|
let user_id = req.user_id;
|
||||||
debug!("Character list for User ID: {}", 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
|
// Simulated database fetch for characters
|
||||||
let characters = vec![
|
let characters = vec![
|
||||||
Character {
|
Character {
|
||||||
character_id: "1".to_string(),
|
character_id: "1".to_string(),
|
||||||
name: "Warrior123".to_string(),
|
name: "Warrior123".to_string(),
|
||||||
level: 1,
|
last_played: 1633017600,
|
||||||
race: 1,
|
|
||||||
job: 111,
|
|
||||||
last_played: 1633017600, // Example timestamp
|
|
||||||
delete_time: 0,
|
delete_time: 0,
|
||||||
face: 1,
|
stats: Option::from(Stats {
|
||||||
hair: 0,
|
job: 0,
|
||||||
|
level: 0,
|
||||||
|
}),
|
||||||
|
looks: Option::from(Looks {
|
||||||
|
race: 0,
|
||||||
|
hair: 0,
|
||||||
|
face: 0,
|
||||||
|
}),
|
||||||
items: vec![],
|
items: vec![],
|
||||||
},
|
},
|
||||||
Character {
|
Character {
|
||||||
character_id: "2".to_string(),
|
character_id: "2".to_string(),
|
||||||
name: "Mage123".to_string(),
|
name: "Mage123".to_string(),
|
||||||
level: 20,
|
last_played: 1633017600,
|
||||||
race: 0,
|
|
||||||
job: 211,
|
|
||||||
last_played: 1633017600, // Example timestamp
|
|
||||||
delete_time: 0,
|
delete_time: 0,
|
||||||
face: 1,
|
stats: Option::from(Stats {
|
||||||
hair: 0,
|
job: 211,
|
||||||
|
level: 20,
|
||||||
|
}),
|
||||||
|
looks: Option::from(Looks {
|
||||||
|
race: 1,
|
||||||
|
hair: 0,
|
||||||
|
face: 1,
|
||||||
|
}),
|
||||||
items: vec![],
|
items: vec![],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -51,15 +70,30 @@ impl CharacterService for MyCharacterService {
|
|||||||
Ok(Response::new(response))
|
Ok(Response::new(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_character(&self, request: Request<CreateCharacterRequest>) -> Result<Response<Empty>, Status> {
|
async fn create_character(&self, request: Request<CreateCharacterRequest>) -> Result<Response<CreateCharacterResponse>, Status> {
|
||||||
todo!()
|
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> {
|
async fn delete_character(&self, request: Request<DeleteCharacterRequest>) -> Result<Response<DeleteCharacterResponse>, Status> {
|
||||||
todo!()
|
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> {
|
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_service;
|
||||||
|
mod character_db_client;
|
||||||
|
pub mod database {
|
||||||
|
tonic::include_proto!("character_db_api");
|
||||||
|
}
|
||||||
|
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::{select, signal};
|
use tokio::{select, signal};
|
||||||
use tracing::{info, Level};
|
use tracing::{info, Level};
|
||||||
use utils::consul_registration;
|
use utils::consul_registration;
|
||||||
use utils::service_discovery::get_service_address;
|
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::character::character_service_server::CharacterServiceServer;
|
||||||
use crate::character_service::MyCharacterService;
|
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 address = full_addr.parse().expect("Invalid address");
|
||||||
let db_address = db_nodes.get(0).unwrap();
|
let db_address = db_nodes.get(0).unwrap();
|
||||||
let db_url = format!("http://{}:{}", db_address.ServiceAddress, db_address.ServicePort);
|
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()
|
tonic::transport::Server::builder()
|
||||||
.add_service(CharacterServiceServer::new(character_service))
|
.add_service(CharacterServiceServer::new(character_service))
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl CharacterRepository {
|
|||||||
|
|
||||||
// Fetch from database
|
// Fetch from database
|
||||||
let character = sqlx::query_as::<_, Character>(
|
let character = sqlx::query_as::<_, Character>(
|
||||||
"SELECT id, user_id, name, level, experience, inventory, stats, looks, position, \
|
"SELECT id, user_id, name, inventory, stats, looks, position, \
|
||||||
created_at, updated_at, deleted_at, is_active \
|
created_at, updated_at, deleted_at, is_active \
|
||||||
FROM characters WHERE id = $1 AND is_active = true",
|
FROM characters WHERE id = $1 AND is_active = true",
|
||||||
)
|
)
|
||||||
@@ -56,7 +56,7 @@ impl CharacterRepository {
|
|||||||
|
|
||||||
pub async fn create_character(&self, user_id: i32, name: &str, inventory: serde_json::Value, stats: serde_json::Value, looks: serde_json::Value, position: serde_json::Value) -> Result<i32, sqlx::Error> {
|
pub async fn create_character(&self, user_id: i32, name: &str, inventory: serde_json::Value, stats: serde_json::Value, looks: serde_json::Value, position: serde_json::Value) -> Result<i32, sqlx::Error> {
|
||||||
let result = sqlx::query(
|
let result = sqlx::query(
|
||||||
"INSERT INTO characters (user_id, name, level, experience, inventory, stats, looks, position, created_at, updated_at, is_active) \
|
"INSERT INTO characters (user_id, name, inventory, stats, looks, position, created_at, updated_at, is_active) \
|
||||||
VALUES ($1, $2, 1, 0, $3, $4, $5, $6, NOW(), NOW(), true) RETURNING id",
|
VALUES ($1, $2, 1, 0, $3, $4, $5, $6, NOW(), NOW(), true) RETURNING id",
|
||||||
)
|
)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
@@ -95,7 +95,7 @@ impl CharacterRepository {
|
|||||||
|
|
||||||
// Fetch from database
|
// Fetch from database
|
||||||
let characters = sqlx::query_as::<_, Character>(
|
let characters = sqlx::query_as::<_, Character>(
|
||||||
"SELECT id, user_id, name, level, experience, inventory, stats, looks, position, \
|
"SELECT id, user_id, name, inventory, stats, looks, position, \
|
||||||
created_at, updated_at, deleted_at, is_active \
|
created_at, updated_at, deleted_at, is_active \
|
||||||
FROM characters WHERE user_id = $1 AND is_active = true",
|
FROM characters WHERE user_id = $1 AND is_active = true",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::grpc::{Character, CharacterRequest, CharacterListRequest, CharacterListResponse, CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, Empty};
|
use crate::grpc::{Character, CharacterRequest, CharacterListRequest, CharacterListResponse, CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty};
|
||||||
use crate::grpc::character_service_server::CharacterService;
|
use crate::grpc::character_service_server::CharacterService;
|
||||||
use crate::grpc::database_service::MyDatabaseService;
|
use crate::grpc::database_service::MyDatabaseService;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
@@ -101,6 +101,7 @@ impl CharacterService for MyDatabaseService {
|
|||||||
.map_err(|_| Status::not_found("Character not found"))?;
|
.map_err(|_| Status::not_found("Character not found"))?;
|
||||||
|
|
||||||
let response = CreateCharacterResponse {
|
let response = CreateCharacterResponse {
|
||||||
|
result: 0,
|
||||||
character_id: character.id,
|
character_id: character.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ impl CharacterService for MyDatabaseService {
|
|||||||
async fn delete_character(
|
async fn delete_character(
|
||||||
&self,
|
&self,
|
||||||
request: Request<DeleteCharacterRequest>,
|
request: Request<DeleteCharacterRequest>,
|
||||||
) -> Result<Response<Empty>, Status> {
|
) -> Result<Response<DeleteCharacterResponse>, Status> {
|
||||||
let req = request.into_inner();
|
let req = request.into_inner();
|
||||||
let repo = &self.db.character_repo;
|
let repo = &self.db.character_repo;
|
||||||
|
|
||||||
@@ -118,6 +119,10 @@ impl CharacterService for MyDatabaseService {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| Status::internal("Failed to delete character"))?;
|
.map_err(|_| Status::internal("Failed to delete character"))?;
|
||||||
|
|
||||||
Ok(Response::new(Empty {}))
|
let response = DeleteCharacterResponse {
|
||||||
|
remaining_time: 0,
|
||||||
|
name: "".to_string(),
|
||||||
|
};
|
||||||
|
Ok(Response::new(response))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ fn main() {
|
|||||||
.build_server(false) // Generate gRPC server code?
|
.build_server(false) // Generate gRPC server code?
|
||||||
.compile_well_known_types(true)
|
.compile_well_known_types(true)
|
||||||
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||||
.compile_protos(&["../proto/auth.proto", "../proto/character.proto"], &["../proto"])
|
.compile_protos(&["../proto/auth.proto", "../proto/character.proto", "../proto/character_common.proto"], &["../proto"])
|
||||||
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::char::character_service_client::CharacterServiceClient;
|
use crate::character::character_service_client::CharacterServiceClient;
|
||||||
use crate::char::{GetCharacterRequest, GetCharacterListRequest, GetCharacterListResponse, Character, EquippedItem, DeleteCharacterRequest, Empty};
|
use crate::character::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest};
|
||||||
use tonic::transport::Channel;
|
use tonic::transport::Channel;
|
||||||
|
use utils::null_string::NullTerminatedString;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CharacterClient {
|
pub struct CharacterClient {
|
||||||
@@ -22,16 +23,21 @@ impl CharacterClient {
|
|||||||
Ok(response.into_inner())
|
Ok(response.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_character(&mut self, user_id: &str) -> Result<GetCharacterListResponse, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn create_character(&mut self, user_id: &str, name: NullTerminatedString, race: u8, face: u8, hair: u8, stone: u8) -> Result<CreateCharacterResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let request = GetCharacterListRequest {
|
let request = CreateCharacterRequest {
|
||||||
user_id: user_id.to_string(),
|
user_id: user_id.to_string(),
|
||||||
|
name: name.0,
|
||||||
|
race: race as i32,
|
||||||
|
face: face as i32,
|
||||||
|
hair: hair as i32,
|
||||||
|
stone: stone as i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.client.get_character_list(request).await?;
|
let response = self.client.create_character(request).await?;
|
||||||
Ok(response.into_inner())
|
Ok(response.into_inner())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_character(&mut self, user_id: &str, char_id: &str) -> Result<Empty, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn delete_character(&mut self, user_id: &str, char_id: &str) -> Result<DeleteCharacterResponse, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let request = DeleteCharacterRequest {
|
let request = DeleteCharacterRequest {
|
||||||
user_id: user_id.to_string(),
|
user_id: user_id.to_string(),
|
||||||
char_id: char_id.to_string(),
|
char_id: char_id.to_string(),
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ pub(crate) async fn handle_char_list_req(stream: &mut TcpStream, packet: Packet,
|
|||||||
|
|
||||||
let character_info = CharInfo {
|
let character_info = CharInfo {
|
||||||
name: NullTerminatedString(character.name),
|
name: NullTerminatedString(character.name),
|
||||||
race: character.race as u8,
|
race: character.looks.unwrap().race as u8,
|
||||||
level: character.level as u16,
|
level: character.stats.unwrap().level as u16,
|
||||||
job: character.job as u16,
|
job: character.stats.unwrap().job as u16,
|
||||||
remain_secs_until_delete: character.delete_time as u32,
|
remain_secs_until_delete: character.delete_time as u32,
|
||||||
platinium: 0,
|
platinium: 0,
|
||||||
face: character.face as u32,
|
face: character.looks.unwrap().face as u32,
|
||||||
hair: character.hair as u32,
|
hair: character.looks.unwrap().hair as u32,
|
||||||
items: item_list,
|
items: item_list,
|
||||||
};
|
};
|
||||||
characters.push(character_info);
|
characters.push(character_info);
|
||||||
@@ -89,9 +89,19 @@ pub(crate) async fn handle_create_char_req(stream: &mut TcpStream, packet: Packe
|
|||||||
|
|
||||||
// send the data to the character service to create the character
|
// send the data to the character service to create the character
|
||||||
let mut character_client = character_client.lock().await;
|
let mut character_client = character_client.lock().await;
|
||||||
character_client.create_character(&user_id.to_string()).await?;
|
let create_character_response = character_client.create_character(&user_id.to_string(), request.name, request.race, request.face, request.hair, request.stone).await?;
|
||||||
|
let result = match create_character_response.result
|
||||||
let data = SrvCreateCharReply { result: srv_create_char_reply::Result::Ok, platininum: 0 };
|
{
|
||||||
|
0 => srv_create_char_reply::Result::Ok,
|
||||||
|
1 => srv_create_char_reply::Result::Failed,
|
||||||
|
2 => srv_create_char_reply::Result::NameTaken,
|
||||||
|
3 => srv_create_char_reply::Result::InvalidValue,
|
||||||
|
4 => srv_create_char_reply::Result::TooManyChars,
|
||||||
|
5 => srv_create_char_reply::Result::Blocked,
|
||||||
|
_ => srv_create_char_reply::Result::Failed
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = SrvCreateCharReply { result, platininum: 0 };
|
||||||
let response_packet = Packet::new(PacketType::PakccCreateCharReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakccCreateCharReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
send_packet(stream, &response_packet).await?;
|
||||||
|
|
||||||
@@ -110,10 +120,10 @@ pub(crate) async fn handle_delete_char_req(stream: &mut TcpStream, packet: Packe
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut character_client = character_client.lock().await;
|
let mut character_client = character_client.lock().await;
|
||||||
character_client.delete_character(&user_id.to_string(), &request.char_id.to_string());
|
let delete_response = character_client.delete_character(&user_id.to_string(), &request.char_id.to_string()).await?;
|
||||||
|
|
||||||
let character_name = request.name;
|
let character_name = request.name;
|
||||||
let data = SrvDeleteCharReply { remaining_time: 0, name: character_name };
|
let data = SrvDeleteCharReply { remaining_time: delete_response.remaining_time as u32, name: character_name };
|
||||||
let response_packet = Packet::new(PacketType::PakccDeleteCharReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakccDeleteCharReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
send_packet(stream, &response_packet).await?;
|
||||||
|
|
||||||
@@ -133,7 +143,7 @@ pub(crate) async fn handle_select_char_req(stream: &mut TcpStream, packet: Packe
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut character_client = character_client.lock().await;
|
let mut character_client = character_client.lock().await;
|
||||||
character_client.get_character(&user_id.to_string(), request.char_id);
|
let character_data = character_client.get_character(&user_id.to_string(), request.char_id).await?;
|
||||||
let mut equipped_item_list: [EquippedItem; (MAX_VISIBLE_ITEMS as usize)] = core::array::from_fn(|i| EquippedItem::default());
|
let mut equipped_item_list: [EquippedItem; (MAX_VISIBLE_ITEMS as usize)] = core::array::from_fn(|i| EquippedItem::default());
|
||||||
let mut effect_list: [StatusEffect; (MAX_STATUS_EFFECTS as usize)] = core::array::from_fn(|i| StatusEffect::default());
|
let mut effect_list: [StatusEffect; (MAX_STATUS_EFFECTS as usize)] = core::array::from_fn(|i| StatusEffect::default());
|
||||||
let mut hotbar_list: [HotbarItem; (MAX_HOTBAR_ITEMS as usize)] = core::array::from_fn(|i| HotbarItem::default());
|
let mut hotbar_list: [HotbarItem; (MAX_HOTBAR_ITEMS as usize)] = core::array::from_fn(|i| HotbarItem::default());
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ mod connection_service;
|
|||||||
pub mod auth {
|
pub mod auth {
|
||||||
tonic::include_proto!("auth"); // Path matches the package name in auth.proto
|
tonic::include_proto!("auth"); // Path matches the package name in auth.proto
|
||||||
}
|
}
|
||||||
pub mod char {
|
pub mod character_common {
|
||||||
|
tonic::include_proto!("character_common"); // Path matches the package name in auth.proto
|
||||||
|
}
|
||||||
|
pub mod character {
|
||||||
tonic::include_proto!("character"); // Path matches the package name in auth.proto
|
tonic::include_proto!("character"); // Path matches the package name in auth.proto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package character;
|
package character;
|
||||||
|
|
||||||
|
import "character_common.proto";
|
||||||
|
|
||||||
service CharacterService {
|
service CharacterService {
|
||||||
rpc GetCharacterList(GetCharacterListRequest) returns (GetCharacterListResponse);
|
rpc GetCharacterList(GetCharacterListRequest) returns (GetCharacterListResponse);
|
||||||
rpc CreateCharacter(CreateCharacterRequest) returns (Empty);
|
rpc CreateCharacter(CreateCharacterRequest) returns (CreateCharacterResponse);
|
||||||
rpc DeleteCharacter(DeleteCharacterRequest) returns (Empty);
|
rpc DeleteCharacter(DeleteCharacterRequest) returns (DeleteCharacterResponse);
|
||||||
rpc GetCharacter(GetCharacterRequest) returns (Empty);
|
rpc GetCharacter(GetCharacterRequest) returns (Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,11 +16,20 @@ message GetCharacterListRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message GetCharacterListResponse {
|
message GetCharacterListResponse {
|
||||||
repeated Character characters = 1;
|
repeated character_common.Character characters = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateCharacterRequest {
|
message CreateCharacterRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
|
string name = 2;
|
||||||
|
int32 race = 3;
|
||||||
|
int32 face = 4;
|
||||||
|
int32 hair = 5;
|
||||||
|
int32 stone = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateCharacterResponse {
|
||||||
|
int32 result = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DeleteCharacterRequest {
|
message DeleteCharacterRequest {
|
||||||
@@ -26,28 +37,13 @@ message DeleteCharacterRequest {
|
|||||||
string char_id = 2;
|
string char_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message DeleteCharacterResponse {
|
||||||
|
int64 remaining_time = 1;
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message GetCharacterRequest {
|
message GetCharacterRequest {
|
||||||
string user_id = 1;
|
string user_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Character {
|
|
||||||
string character_id = 1; // Unique ID for the character
|
|
||||||
string name = 2; // Name of the character
|
|
||||||
int32 level = 3; // Character's level
|
|
||||||
int32 race = 4; // Character's level
|
|
||||||
int32 job = 5; // Character's class or type
|
|
||||||
int64 last_played = 6; // Last played timestamp (Unix time)
|
|
||||||
int64 delete_time = 7; // Time until character deletion (seconds)
|
|
||||||
int32 face = 8; // Character's face
|
|
||||||
int32 hair = 9; // Character's hair
|
|
||||||
repeated EquippedItem items = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
message EquippedItem {
|
|
||||||
int32 id = 1;
|
|
||||||
int32 gem_option = 2;
|
|
||||||
int32 socket = 3;
|
|
||||||
int32 grade = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Empty {}
|
message Empty {}
|
||||||
|
|||||||
31
proto/character_common.proto
Normal file
31
proto/character_common.proto
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package character_common;
|
||||||
|
|
||||||
|
message Stats {
|
||||||
|
int32 level = 1;
|
||||||
|
int32 job = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Looks {
|
||||||
|
int32 race = 1;
|
||||||
|
int32 hair = 2;
|
||||||
|
int32 face = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EquippedItem {
|
||||||
|
int32 id = 1;
|
||||||
|
int32 gem_option = 2;
|
||||||
|
int32 socket = 3;
|
||||||
|
int32 grade = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Character {
|
||||||
|
string character_id = 1; // Unique ID for the character
|
||||||
|
string name = 2; // Name of the character
|
||||||
|
int64 last_played = 3; // Last played timestamp (Unix time)
|
||||||
|
int64 delete_time = 4; // Time until character deletion (seconds)
|
||||||
|
Stats stats = 5; // Character's stats
|
||||||
|
Looks looks = 6; // Character's Looks
|
||||||
|
repeated EquippedItem items = 7;
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ service CharacterService {
|
|||||||
rpc GetCharacter (CharacterRequest) returns (Character);
|
rpc GetCharacter (CharacterRequest) returns (Character);
|
||||||
rpc GetCharacterList (CharacterListRequest) returns (CharacterListResponse);
|
rpc GetCharacterList (CharacterListRequest) returns (CharacterListResponse);
|
||||||
rpc CreateCharacter (CreateCharacterRequest) returns (CreateCharacterResponse);
|
rpc CreateCharacter (CreateCharacterRequest) returns (CreateCharacterResponse);
|
||||||
rpc DeleteCharacter (DeleteCharacterRequest) returns (Empty);
|
rpc DeleteCharacter (DeleteCharacterRequest) returns (DeleteCharacterResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message CharacterRequest {
|
message CharacterRequest {
|
||||||
@@ -31,11 +31,18 @@ message CreateCharacterRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message CreateCharacterResponse {
|
message CreateCharacterResponse {
|
||||||
int32 character_id = 1;
|
int32 result = 1;
|
||||||
|
int32 character_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DeleteCharacterRequest {
|
message DeleteCharacterRequest {
|
||||||
int32 character_id = 1;
|
int32 user_id = 1;
|
||||||
|
int32 character_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteCharacterResponse {
|
||||||
|
int64 remaining_time = 1;
|
||||||
|
string name = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Character {
|
message Character {
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ create table characters
|
|||||||
on delete cascade,
|
on delete cascade,
|
||||||
is_active boolean default true,
|
is_active boolean default true,
|
||||||
name varchar(50) not null,
|
name varchar(50) not null,
|
||||||
level smallint default 1,
|
|
||||||
experience bigint default 0,
|
|
||||||
inventory jsonb default '{}'::jsonb,
|
inventory jsonb default '{}'::jsonb,
|
||||||
stats jsonb default '{}'::jsonb,
|
stats jsonb default '{}'::jsonb,
|
||||||
looks jsonb default '{}'::jsonb,
|
looks jsonb default '{}'::jsonb,
|
||||||
|
|||||||
Reference in New Issue
Block a user