diff --git a/character-service/src/character_db_client.rs b/character-service/src/character_db_client.rs index fa2c59c..d7b4062 100644 --- a/character-service/src/character_db_client.rs +++ b/character-service/src/character_db_client.rs @@ -12,12 +12,15 @@ pub struct CharacterDbClient { struct Item { item_id: i32, item_type: i32, - count: i32, - durability: f32, - slot: i32, + is_created: i32, gem_option: i32, + durability: f32, + life: f32, socket: i8, + is_appraised: i8, grade: u32, + count: i32, + slot: i32, } #[derive(Debug, Deserialize, Serialize)] @@ -25,6 +28,7 @@ struct Looks { race: i32, face: i32, hair: i32, + stone: i32, } #[derive(Debug, Deserialize, Serialize)] @@ -33,6 +37,7 @@ struct Position { x: f64, y: f64, z: f64, + spawn_id: i32, } #[derive(Debug, Deserialize, Serialize)] @@ -48,7 +53,7 @@ struct Stats { hp: i32, max_mp: i32, mp: i32, - xp: u32, + xp: u64, level: u16, head_size: u8, body_size: u8, @@ -93,32 +98,41 @@ impl CharacterDbClient { Item { item_id: 30, item_type: 3, - count: 1, - slot: 3, - durability: 45.0, + is_created: 0, gem_option: 0, + durability: 45.0, + life: 100.0, socket: 0, grade: 0, + is_appraised: 0, + count: 1, + slot: 3, }, Item { item_id: 1, item_type: 8, - count: 1, - slot: 7, - durability: 45.0, + is_created: 0, gem_option: 0, + durability: 45.0, + life: 100.0, socket: 0, grade: 0, + is_appraised: 0, + count: 1, + slot: 7, }, Item { item_id: hatid, item_type: 2, - count: 1, - slot: 12, - durability: 45.0, + is_created: 0, gem_option: 0, + durability: 45.0, + life: 100.0, socket: 0, grade: 0, + is_appraised: 0, + count: 1, + slot: 12, }, ]; @@ -146,8 +160,8 @@ impl CharacterDbClient { 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 looks = Looks {race, face, hair, stone}; + let position = Position {map_id: 20, x: 5200.00, y: 5200.00, z: 1.0, spawn_id: 1}; let request = tonic::Request::new(CreateCharacterRequest { user_id: user_id.parse().unwrap(), diff --git a/character-service/src/character_service.rs b/character-service/src/character_service.rs index 7c939d0..ea72e5c 100644 --- a/character-service/src/character_service.rs +++ b/character-service/src/character_service.rs @@ -4,8 +4,8 @@ use tonic::{Request, Response, Status}; 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::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest}; -use crate::character_service::character_common::{Character, Looks, Stats}; +use crate::character_service::character::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest, GetCharacterResponse}; +use crate::character_service::character_common::{Character, CharacterFull, Looks, Stats}; pub mod character_common { tonic::include_proto!("character_common"); @@ -71,9 +71,21 @@ impl CharacterService for MyCharacterService { Ok(Response::new(response)) } - async fn get_character(&self, request: Request) -> Result, Status> { + async fn get_character(&self, request: Request) -> Result, Status> { let req = request.into_inner(); debug!("{:?}", req); - Ok(Response::new(Empty{})) + let get_character_response = self.character_db_client.as_ref().clone().get_character(&req.user_id, &req.char_id).await.map_err(|_| Status::not_found("Character not found"))?; + + let character = CharacterFull { + character_id: get_character_response.id.to_string(), + name: get_character_response.name.to_string(), + position: serde_json::from_str(&get_character_response.position).unwrap(), + looks: serde_json::from_str(&get_character_response.looks).unwrap(), + stats: serde_json::from_str(&get_character_response.stats).unwrap(), + items: serde_json::from_str(&get_character_response.inventory).unwrap(), + }; + + let response = GetCharacterResponse { character: Some(character) }; + Ok(Response::new(response)) } } \ No newline at end of file diff --git a/packet-service/src/character_client.rs b/packet-service/src/character_client.rs index 5913419..9c57cf6 100644 --- a/packet-service/src/character_client.rs +++ b/packet-service/src/character_client.rs @@ -1,5 +1,5 @@ use crate::character::character_service_client::CharacterServiceClient; -use crate::character::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest}; +use crate::character::{CreateCharacterRequest, CreateCharacterResponse, DeleteCharacterRequest, DeleteCharacterResponse, Empty, GetCharacterListRequest, GetCharacterListResponse, GetCharacterRequest, GetCharacterResponse}; use tonic::transport::Channel; use utils::null_string::NullTerminatedString; @@ -48,9 +48,10 @@ impl CharacterClient { Ok(response.into_inner()) } - pub async fn get_character(&mut self, user_id: &str, char_id: u8) -> Result> { + pub async fn get_character(&mut self, user_id: &str, char_id: u8) -> Result> { let request = GetCharacterRequest { - user_id: char_id.to_string(), + user_id: user_id.to_string(), + char_id: char_id.to_string(), }; let response = self.client.get_character(request).await?; diff --git a/packet-service/src/handlers/character.rs b/packet-service/src/handlers/character.rs index 05acccb..c2f12e2 100644 --- a/packet-service/src/handlers/character.rs +++ b/packet-service/src/handlers/character.rs @@ -67,7 +67,7 @@ pub(crate) async fn handle_char_list_req(stream: &mut TcpStream, packet: Packet, }; } } - + let character_info = CharInfo { name: NullTerminatedString(character.name), race: character.looks.unwrap().race as u8, @@ -153,6 +153,9 @@ pub(crate) async fn handle_delete_char_req(stream: &mut TcpStream, packet: Packe pub(crate) async fn handle_select_char_req(stream: &mut TcpStream, packet: Packet, character_client: Arc>, connection_service: Arc, connection_id: String) -> Result<(), Box> { use crate::packets::srv_select_char_reply::*; + use crate::packets::srv_inventory_data::*; + use crate::packets::srv_quest_data::*; + use crate::packets::srv_billing_message::*; use crate::types::{HotbarItem, StatusEffect}; let request = CliSelectCharReq::decode(packet.payload.as_slice())?; debug!("{:?}", request); @@ -167,50 +170,88 @@ pub(crate) async fn handle_select_char_req(stream: &mut TcpStream, packet: Packe let mut character_client = character_client.lock().await; let character_data = character_client.get_character(&user_id.to_string(), character_id_list[request.char_id as usize]).await?; + + let character = character_data.character.unwrap_or_default(); + + let looks = character.looks.unwrap(); + let position = character.position.unwrap(); + let stats = character.stats.unwrap(); + let items = character.items; let mut equipped_item_list: [EquippedItem; (MAX_VISIBLE_ITEMS as usize)] = core::array::from_fn(|i| EquippedItem::default()); + let mut inventory: [srv_inventory_data::Item; (MAX_ITEMS as usize)] = core::array::from_fn(|i| srv_inventory_data::Item::default()); + + for item in items { + if item.slot < 10 { + let slot = convert_slot(item.slot) as usize; + equipped_item_list[slot] = EquippedItem { + id: item.item_id as u16, + gem_opt: item.gem_option as u16, + socket: item.socket as i8, + grade: item.grade as u8, + }; + } + + inventory[item.slot as usize] = srv_inventory_data::Item { + header: srv_inventory_data::Header { + type_: item.item_type as u8, + id: item.item_id as u16, + is_created:item.is_created as u8, + }, + data: srv_inventory_data::Data { + gem_opt: item.gem_option as u16, + life: item.life as u16, + durability: item.durability as u8, + has_socket: item.socket as u8, + is_appraised: item.is_appraised as u8, + refine: item.grade as u8, + count: item.count as u32, + }, + }; + } + 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 data = SrvSelectCharReply { - race: 0, - map: 0, - x: 0.0, - y: 0.0, - spawn: 0, - body_face: 0, - body_hair: 0, + race: looks.race as u8, + map: position.map_id as u16, + x: position.x, + y: position.y, + spawn: position.spawn_id as u16, + body_face: looks.face as u32, + body_hair: looks.hair as u32, equipped_items: equipped_item_list, - stone: 0, - face: 0, - hair: 0, - job: 0, + stone: looks.stone as u8, + face: looks.face as u8, + hair: looks.hair as u8, + job: stats.job as u16, faction_id: 0, faction_rank: 0, fame: 0, - str: 0, - dex: 0, - int: 0, - con: 0, - charm: 0, - sense: 0, - hp: 0, - mp: 0, - xp: 0, - level: 0, - stat_points: 0, - skill_points: 0, - body_size: 0, - head_size: 0, - penalty_xp: 0, + str: stats.str as u16, + dex: stats.dex as u16, + int: stats.int as u16, + con: stats.con as u16, + charm: stats.charm as u16, + sense: stats.sense as u16, + hp: stats.hp, + mp: stats.mp, + xp: stats.xp as u32, + level: stats.level as u16, + stat_points: stats.stat_points as u32, + skill_points: stats.skill_points as u32, + body_size: stats.body_size as u8, + head_size: stats.head_size as u8, + penalty_xp: stats.penalty_xp as u32, faction_fame: [0u16; 2], faction_points: [0u16; 10], guild_id: 0, guild_contribution: 0, guild_rank: 0, pk_flag: 0, - stamina: 0, + stamina: stats.stamina as u16, effects: effect_list, - pat_hp: 0, - pat_cooldown_time: 0, + pat_hp: stats.pat_hp as u16, + pat_cooldown_time: stats.pat_cooldown_time as u32, skills: [0u16; (MAX_SKILL_COUNT as usize)], hotbar: hotbar_list, tag: 0, @@ -218,5 +259,38 @@ pub(crate) async fn handle_select_char_req(stream: &mut TcpStream, packet: Packe }; let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?; send_packet(stream, &response_packet).await?; + + // here we build the inventory + let data = SrvInventoryData { + zuly: 0, + items: inventory, + }; + let response_packet = Packet::new(PacketType::PakwcInventoryData, &data)?; + send_packet(stream, &response_packet).await?; + + // Now we need to build the Quest data + let mut quests: [srv_quest_data::Quest; (MAX_QUESTS as usize)] = core::array::from_fn(|i| srv_quest_data::Quest::default()); + let mut wishlist: [srv_quest_data::Item; (MAX_WISHLIST as usize)] = core::array::from_fn(|i| srv_quest_data::Item::default()); + + let data = SrvQuestData { + episodes: [0; (MAX_CONDITIONS_EPISODE as usize)], + jobs: [0; (MAX_CONDITIONS_JOB as usize)], + planets: [0; (MAX_CONDITIONS_PLANET as usize)], + unions: [0; (MAX_CONDITIONS_UNION as usize)], + quests, + switches: [0; (MAX_SWITCHES as usize)], + wishlist, + }; + let response_packet = Packet::new(PacketType::PakwcInventoryData, &data)?; + send_packet(stream, &response_packet).await?; + + // Send the billing message (we don't actually use this so we just send the defaults to allow) + let data = SrvBillingMessage { + function_type: 0x1001, + pay_flag: 2, + }; + let response_packet = Packet::new(PacketType::PakwcBillingMessage, &data)?; + send_packet(stream, &response_packet).await?; + Ok(()) } \ No newline at end of file diff --git a/proto/character.proto b/proto/character.proto index c734713..27e7bfd 100644 --- a/proto/character.proto +++ b/proto/character.proto @@ -8,7 +8,7 @@ service CharacterService { rpc GetCharacterList(GetCharacterListRequest) returns (GetCharacterListResponse); rpc CreateCharacter(CreateCharacterRequest) returns (CreateCharacterResponse); rpc DeleteCharacter(DeleteCharacterRequest) returns (DeleteCharacterResponse); - rpc GetCharacter(GetCharacterRequest) returns (Empty); + rpc GetCharacter(GetCharacterRequest) returns (GetCharacterResponse); } message GetCharacterListRequest { @@ -45,6 +45,11 @@ message DeleteCharacterResponse { message GetCharacterRequest { string user_id = 1; + string char_id = 2; +} + +message GetCharacterResponse { + character_common.CharacterFull character = 1; } message Empty {} diff --git a/proto/character_common.proto b/proto/character_common.proto index d49e35e..53b8a37 100644 --- a/proto/character_common.proto +++ b/proto/character_common.proto @@ -5,12 +5,32 @@ package character_common; message Stats { int32 level = 1; int32 job = 2; + int32 str = 3; + int32 dex = 4; + int32 int = 5; + int32 con = 6; + int32 charm = 7; + int32 sense = 8; + int32 max_hp = 9; + int32 max_mp = 10; + int32 hp = 11; + int32 mp = 12; + int64 xp = 13; + int32 head_size = 14; + int32 body_size = 15; + int32 stat_points = 16; + int32 skill_points = 17; + int32 penalty_xp = 18; + int32 stamina = 19; + int32 pat_hp = 20; + int32 pat_cooldown_time = 21; } message Looks { int32 race = 1; int32 hair = 2; int32 face = 3; + int32 stone = 4; } message EquippedItem { @@ -21,6 +41,27 @@ message EquippedItem { int32 slot = 5; } +message Item { + int32 item_id = 1; + int32 item_type = 2; + int32 is_created = 3; + int32 gem_option = 4; + float durability = 5; + float life = 6; + int32 socket = 7; + int32 is_appraised = 8; + int32 grade = 9; + int32 count = 10; + int32 slot = 11; +} + +message Location { + int32 map_id = 1; + float x = 2; + float y = 3; + int32 spawn_id = 4; +} + message Character { string character_id = 1; // Unique ID for the character string name = 2; // Name of the character @@ -29,4 +70,13 @@ message Character { Stats stats = 5; // Character's stats Looks looks = 6; // Character's Looks repeated EquippedItem items = 7; +} + +message CharacterFull { + string character_id = 1; // Unique ID for the character + string name = 2; // Name of the character + Location position = 3; // Character's position + Looks looks = 4; // Character's Looks + Stats stats = 5; // Character's stats + repeated Item items = 6; // Character inventory } \ No newline at end of file