- update: schema now sets the skills column to prevent a crash
- update: frontend to only pass the session id - update: launcher to pass the session correctly - update: validate session response now returns the session id and user id to the requester - update: auth client based on session id instead of a jwt token
This commit is contained in:
@@ -146,12 +146,13 @@ impl AuthService for MyAuthService {
|
|||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
debug!("Session valid: {:?}", res.into_inner());
|
let res = res.into_inner();
|
||||||
Ok(Response::new(ValidateSessionResponse { valid: true }))
|
debug!("Session valid: {:?}", res);
|
||||||
|
Ok(Response::new(ValidateSessionResponse { valid: true, session_id: res.session_id.to_string(), user_id: res.user_id.to_string() }))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("Session invalid or not found");
|
debug!("Session invalid or not found");
|
||||||
Ok(Response::new(ValidateSessionResponse { valid: false }))
|
Ok(Response::new(ValidateSessionResponse { valid: false, session_id: "".to_string(), user_id: "".to_string() }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,12 +173,13 @@ impl AuthService for MyAuthService {
|
|||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
debug!("Session valid: {:?}", res.into_inner());
|
let res = res.into_inner();
|
||||||
Ok(Response::new(ValidateSessionResponse { valid: true }))
|
debug!("Session valid: {:?}", res);
|
||||||
|
Ok(Response::new(ValidateSessionResponse { valid: true, session_id: res.session_id.to_string(), user_id: res.user_id.to_string() }))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("Session invalid or not found");
|
debug!("Session invalid or not found");
|
||||||
Ok(Response::new(ValidateSessionResponse { valid: false }))
|
Ok(Response::new(ValidateSessionResponse { valid: false, session_id: "".to_string(), user_id: "".to_string() }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sqlx::{FromRow, Row};
|
use sqlx::{FromRow, Row};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use utils::redis_cache::{Cache, RedisCache}; // Import RedisCache
|
use utils::redis_cache::{Cache, RedisCache};
|
||||||
|
// Import RedisCache
|
||||||
|
|
||||||
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||||
pub struct Character {
|
pub struct Character {
|
||||||
@@ -74,14 +75,16 @@ impl CharacterRepository {
|
|||||||
looks: serde_json::Value,
|
looks: serde_json::Value,
|
||||||
position: serde_json::Value,
|
position: serde_json::Value,
|
||||||
) -> Result<i32, sqlx::Error> {
|
) -> Result<i32, sqlx::Error> {
|
||||||
|
let default_skills = "[{\"id\": 11, \"level\": 1}, {\"id\": 12, \"level\": 1}, {\"id\": 16, \"level\": 1}, {\"id\": 19, \"level\": 1}, {\"id\": 20, \"level\": 1}, {\"id\": 21, \"level\": 1}, {\"id\": 26, \"level\": 1}, {\"id\": 41, \"level\": 1}, {\"id\": 42, \"level\": 1}, {\"id\": 43, \"level\": 1}, {\"id\": 46, \"level\": 1}, {\"id\": 47, \"level\": 1}, {\"id\": 48, \"level\": 1}, {\"id\": 49, \"level\": 1}, {\"id\": 50, \"level\": 1}]";
|
||||||
let result = sqlx::query(
|
let result = sqlx::query(
|
||||||
"INSERT INTO characters (user_id, name, inventory, stats, looks, position, created_at, updated_at, is_active) \
|
"INSERT INTO characters (user_id, name, inventory, stats, skills, looks, position, created_at, updated_at, is_active) \
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW(), true) RETURNING id",
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW(), true) RETURNING id",
|
||||||
)
|
)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
.bind(name)
|
.bind(name)
|
||||||
.bind(inventory)
|
.bind(inventory)
|
||||||
.bind(stats)
|
.bind(stats)
|
||||||
|
.bind(default_skills)
|
||||||
.bind(looks)
|
.bind(looks)
|
||||||
.bind(position)
|
.bind(position)
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&self.pool)
|
||||||
@@ -150,7 +153,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, inventory, stats, looks, position, created_at, updated_at, extract(epoch from (deleted_at - now()))::BIGINT as deleted_at, is_active FROM characters WHERE user_id = $1 AND is_active = true",
|
"SELECT id, user_id, name, inventory, stats, skills, looks, position, created_at, updated_at, extract(epoch from (deleted_at - now()))::BIGINT as deleted_at, is_active FROM characters WHERE user_id = $1 AND is_active = true",
|
||||||
)
|
)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
.fetch_all(&self.pool)
|
.fetch_all(&self.pool)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const LoginPage = () => {
|
|||||||
setMessage("Login successful! Launching game...");
|
setMessage("Login successful! Launching game...");
|
||||||
|
|
||||||
const {ServiceAddress, ServicePort} = await getServiceAddress("packet-service");
|
const {ServiceAddress, ServicePort} = await getServiceAddress("packet-service");
|
||||||
window.location.href = `osirose-launcher://launch?otp=${encodeURIComponent(token)}&session=${encodeURIComponent(session_id)}&ip=${encodeURIComponent(ServiceAddress)}&port=${encodeURIComponent(ServicePort)}&username=${encodeURIComponent(username)}`;
|
window.location.href = `osirose-launcher://launch?session=${encodeURIComponent(session_id)}&ip=${encodeURIComponent(ServiceAddress)}&port=${encodeURIComponent(ServicePort)}&username=${encodeURIComponent(username)}`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setMessage("Login failed: " + error.response?.data?.error || error.message);
|
setMessage("Login failed: " + error.response?.data?.error || error.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use crate::format_shell_command;
|
use crate::format_shell_command;
|
||||||
|
use crate::wait_for_keypress;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::env;
|
||||||
|
use std::process::exit;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -15,7 +18,7 @@ fn create_command() -> Command {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command = Command::new("./TRose.exe");
|
let command = Command::new("./TRose.exe");
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +59,8 @@ pub(crate) fn launch_game(url: String) {
|
|||||||
command.arg("_direct").arg("_otp").arg(value.to_string());
|
command.arg("_direct").arg("_otp").arg(value.to_string());
|
||||||
}
|
}
|
||||||
Cow::Borrowed("session") => {
|
Cow::Borrowed("session") => {
|
||||||
command.arg("_session").arg(value.to_string());
|
is_direct = true;
|
||||||
|
command.arg("_direct").arg("_session").arg(value.to_string());
|
||||||
}
|
}
|
||||||
Cow::Borrowed("username") => {
|
Cow::Borrowed("username") => {
|
||||||
command.arg("_userid").arg(value.to_string());
|
command.arg("_userid").arg(value.to_string());
|
||||||
|
|||||||
@@ -20,6 +20,6 @@ RUN apk add --no-cache libssl3 libgcc
|
|||||||
|
|
||||||
COPY --from=builder /usr/src/packet-service/target/release/packet-service /usr/local/bin/packet-service
|
COPY --from=builder /usr/src/packet-service/target/release/packet-service /usr/local/bin/packet-service
|
||||||
|
|
||||||
EXPOSE 4000
|
EXPOSE 29000
|
||||||
|
|
||||||
CMD ["packet-service"]
|
CMD ["packet-service"]
|
||||||
@@ -119,6 +119,29 @@ pub(crate) enum ItemType {
|
|||||||
Zuly = 0x1F,
|
Zuly = 0x1F,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ItemType {
|
||||||
|
pub(crate) fn from_i32(value: i32) -> Option<ItemType> {
|
||||||
|
match value {
|
||||||
|
1 => Some(ItemType::ItemGoggles),
|
||||||
|
2 => Some(ItemType::ItemHelmet),
|
||||||
|
3 => Some(ItemType::ItemArmor),
|
||||||
|
4 => Some(ItemType::ItemGauntlet),
|
||||||
|
5 => Some(ItemType::ItemBoots),
|
||||||
|
6 => Some(ItemType::ItemBackpack),
|
||||||
|
7 => Some(ItemType::ItemRing),
|
||||||
|
8 => Some(ItemType::ItemWeaponR),
|
||||||
|
9 => Some(ItemType::ItemWeaponL),
|
||||||
|
10 => Some(ItemType::ItemConsumable),
|
||||||
|
11 => Some(ItemType::ItemEtcGem),
|
||||||
|
12 => Some(ItemType::ItemEtc),
|
||||||
|
13 => Some(ItemType::ItemEtc2),
|
||||||
|
14 => Some(ItemType::ItemRiding),
|
||||||
|
0x1F => Some(ItemType::Zuly),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod party_req {
|
mod party_req {
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ pub(crate) async fn handle_login_req(
|
|||||||
debug!("{:?}", data);
|
debug!("{:?}", data);
|
||||||
|
|
||||||
let mut auth_client = auth_client.lock().await;
|
let mut auth_client = auth_client.lock().await;
|
||||||
match auth_client.login_token(&data.token.0).await {
|
match auth_client.validate_session(&data.token.0).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
if response.valid == false {
|
if response.valid == false {
|
||||||
info!("Login failed: Invalid credentials");
|
info!("Login failed: Invalid credentials");
|
||||||
@@ -158,7 +158,7 @@ pub(crate) async fn handle_login_req(
|
|||||||
|
|
||||||
if let Some(mut state) = connection_service.get_connection_mut(&connection_id) {
|
if let Some(mut state) = connection_service.get_connection_mut(&connection_id) {
|
||||||
state.user_id = Some(response.user_id.parse().unwrap());
|
state.user_id = Some(response.user_id.parse().unwrap());
|
||||||
state.session_id = Some(response.session_id);
|
state.session_id = Some(response.session_id.parse().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
let consul_url =
|
let consul_url =
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::character_client::CharacterClient;
|
|||||||
use crate::connection_service::ConnectionService;
|
use crate::connection_service::ConnectionService;
|
||||||
use crate::dataconsts::*;
|
use crate::dataconsts::*;
|
||||||
use crate::enums;
|
use crate::enums;
|
||||||
|
use crate::enums::ItemType;
|
||||||
use crate::packet::{send_packet, Packet, PacketPayload};
|
use crate::packet::{send_packet, Packet, PacketPayload};
|
||||||
use crate::packet_type::PacketType;
|
use crate::packet_type::PacketType;
|
||||||
use crate::packets::*;
|
use crate::packets::*;
|
||||||
@@ -16,7 +17,7 @@ use tonic::{Code, Status};
|
|||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use utils::null_string::NullTerminatedString;
|
use utils::null_string::NullTerminatedString;
|
||||||
|
|
||||||
pub(crate) fn convert_slot(slot: i32) -> srv_char_list_reply::EquippedPosition {
|
pub(crate) fn convert_slot_to_equip_slot(slot: i32) -> srv_char_list_reply::EquippedPosition {
|
||||||
match enums::EquippedPosition::from_i32(slot) {
|
match enums::EquippedPosition::from_i32(slot) {
|
||||||
Some(enums::EquippedPosition::Goggles) => srv_char_list_reply::EquippedPosition::Googles,
|
Some(enums::EquippedPosition::Goggles) => srv_char_list_reply::EquippedPosition::Googles,
|
||||||
Some(enums::EquippedPosition::Helmet) => srv_char_list_reply::EquippedPosition::Helmet,
|
Some(enums::EquippedPosition::Helmet) => srv_char_list_reply::EquippedPosition::Helmet,
|
||||||
@@ -30,6 +31,34 @@ pub(crate) fn convert_slot(slot: i32) -> srv_char_list_reply::EquippedPosition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn convert_slot_to_body_part(slot: i32) -> usize {
|
||||||
|
match enums::EquippedPosition::from_i32(slot) {
|
||||||
|
Some(enums::EquippedPosition::Goggles) => 6,
|
||||||
|
Some(enums::EquippedPosition::Helmet) => 2,
|
||||||
|
Some(enums::EquippedPosition::Armor) => 3,
|
||||||
|
Some(enums::EquippedPosition::Backpack) => 7,
|
||||||
|
Some(enums::EquippedPosition::Gauntlet) => 4,
|
||||||
|
Some(enums::EquippedPosition::Boots) => 5,
|
||||||
|
Some(enums::EquippedPosition::WeaponR) => 8,
|
||||||
|
Some(enums::EquippedPosition::WeaponL) => 9,
|
||||||
|
_ => 12,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn convert_type_to_body_part(slot: i32) -> ItemType {
|
||||||
|
match enums::EquippedPosition::from_i32(slot) {
|
||||||
|
Some(enums::EquippedPosition::Goggles) => ItemType::ItemGoggles,
|
||||||
|
Some(enums::EquippedPosition::Helmet) => ItemType::ItemHelmet,
|
||||||
|
Some(enums::EquippedPosition::Armor) => ItemType::ItemArmor,
|
||||||
|
Some(enums::EquippedPosition::Backpack) => ItemType::ItemBackpack,
|
||||||
|
Some(enums::EquippedPosition::Gauntlet) => ItemType::ItemGauntlet,
|
||||||
|
Some(enums::EquippedPosition::Boots) => ItemType::ItemBoots,
|
||||||
|
Some(enums::EquippedPosition::WeaponR) => ItemType::ItemWeaponR,
|
||||||
|
Some(enums::EquippedPosition::WeaponL) => ItemType::ItemWeaponL,
|
||||||
|
_ => ItemType::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_char_list_req(
|
pub(crate) async fn handle_char_list_req(
|
||||||
stream: &mut TcpStream,
|
stream: &mut TcpStream,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
@@ -64,7 +93,7 @@ pub(crate) async fn handle_char_list_req(
|
|||||||
|
|
||||||
for item in character.items {
|
for item in character.items {
|
||||||
if item.slot < MAX_VISIBLE_ITEMS as i32 {
|
if item.slot < MAX_VISIBLE_ITEMS as i32 {
|
||||||
let slot = convert_slot(item.slot) as usize;
|
let slot = convert_slot_to_equip_slot(item.slot) as usize;
|
||||||
item_list[slot] = EquippedItem {
|
item_list[slot] = EquippedItem {
|
||||||
id: item.item_id as u16,
|
id: item.item_id as u16,
|
||||||
gem_opt: item.gem_option as u16,
|
gem_opt: item.gem_option as u16,
|
||||||
@@ -244,6 +273,7 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
|
|
||||||
let character = character_data.character.unwrap_or_default();
|
let character = character_data.character.unwrap_or_default();
|
||||||
|
|
||||||
|
let name = NullTerminatedString(character.name.clone());
|
||||||
let looks = character.looks.unwrap();
|
let looks = character.looks.unwrap();
|
||||||
let position = character.position.unwrap();
|
let position = character.position.unwrap();
|
||||||
let stats = character.stats.unwrap();
|
let stats = character.stats.unwrap();
|
||||||
@@ -253,16 +283,16 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
core::array::from_fn(|i| EquippedItem::default());
|
core::array::from_fn(|i| EquippedItem::default());
|
||||||
let mut inventory: [srv_inventory_data::Item; (MAX_ITEMS as usize)] =
|
let mut inventory: [srv_inventory_data::Item; (MAX_ITEMS as usize)] =
|
||||||
core::array::from_fn(|i| srv_inventory_data::Item::default());
|
core::array::from_fn(|i| srv_inventory_data::Item::default());
|
||||||
let mut skill_list: [u16; (MAX_SKILL_COUNT as usize)] = core::array::from_fn(|i| {
|
let mut skill_list: [u16; (MAX_SKILL_COUNT as usize)] = [0u16; MAX_SKILL_COUNT as usize];
|
||||||
if i < skills.len() {
|
|
||||||
return skills[i] as u16;
|
for index in 0..skills.len() {
|
||||||
|
skill_list[index] = skills[index].id as u16;
|
||||||
}
|
}
|
||||||
0
|
|
||||||
});
|
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
if item.slot < MAX_VISIBLE_ITEMS as i32 {
|
if item.slot < MAX_VISIBLE_ITEMS as i32 {
|
||||||
let slot = convert_slot(item.slot) as usize;
|
let slot = convert_type_to_body_part(item.slot) as usize - 2;
|
||||||
|
if slot >= 0 {
|
||||||
equipped_item_list[slot] = EquippedItem {
|
equipped_item_list[slot] = EquippedItem {
|
||||||
id: item.item_id as u16,
|
id: item.item_id as u16,
|
||||||
gem_opt: item.gem_option as u16,
|
gem_opt: item.gem_option as u16,
|
||||||
@@ -270,6 +300,7 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
grade: item.grade as u8,
|
grade: item.grade as u8,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inventory[item.slot as usize] = srv_inventory_data::Item {
|
inventory[item.slot as usize] = srv_inventory_data::Item {
|
||||||
header: srv_inventory_data::Header {
|
header: srv_inventory_data::Header {
|
||||||
@@ -337,8 +368,9 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
skills: skill_list,
|
skills: skill_list,
|
||||||
hotbar: hotbar_list,
|
hotbar: hotbar_list,
|
||||||
tag: user_id as u32,
|
tag: user_id as u32,
|
||||||
name: request.name,
|
name,
|
||||||
};
|
};
|
||||||
|
debug!("{:?}", data);
|
||||||
let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
send_packet(stream, &response_packet).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,18 @@ pub struct Packet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait PacketPayload {
|
pub trait PacketPayload {
|
||||||
fn encode(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> where Self: Encode {
|
fn encode(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
|
where
|
||||||
|
Self: Encode,
|
||||||
|
{
|
||||||
let config = bincode::config::standard().with_fixed_int_encoding();
|
let config = bincode::config::standard().with_fixed_int_encoding();
|
||||||
Ok(bincode::encode_to_vec(self, config)?)
|
Ok(bincode::encode_to_vec(self, config)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode(data: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>
|
fn decode(data: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>
|
||||||
where
|
where
|
||||||
Self: Sized, Self: Decode<()>
|
Self: Sized,
|
||||||
|
Self: Decode<()>,
|
||||||
{
|
{
|
||||||
let config = bincode::config::standard().with_fixed_int_encoding();
|
let config = bincode::config::standard().with_fixed_int_encoding();
|
||||||
Ok(bincode::decode_from_slice(data, config)?.0)
|
Ok(bincode::decode_from_slice(data, config)?.0)
|
||||||
@@ -107,7 +111,7 @@ impl Packet {
|
|||||||
|
|
||||||
pub async fn send_packet(stream: &mut TcpStream, packet: &Packet) -> Result<(), std::io::Error> {
|
pub async fn send_packet(stream: &mut TcpStream, packet: &Packet) -> Result<(), std::io::Error> {
|
||||||
let data = packet.to_raw();
|
let data = packet.to_raw();
|
||||||
debug!("Sending data: {:?}", data);
|
debug!("Sending '{:#X}' bytes of data. {:?}", data.len(), data);
|
||||||
stream.write_all(&data).await?;
|
stream.write_all(&data).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use bincode::{Decode, Encode};
|
use bincode::{Decode, Encode};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
// `HotbarItem` structure converted to Rust.
|
// `HotbarItem` structure converted to Rust.
|
||||||
#[derive(Debug, Clone, Copy, Encode, Decode, Default)]
|
#[derive(Debug, Clone, Copy, Encode, Decode, Default)]
|
||||||
@@ -18,8 +17,7 @@ pub(crate) struct Skill {
|
|||||||
// `StatusEffect` structure converted to Rust.
|
// `StatusEffect` structure converted to Rust.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Default)]
|
||||||
pub(crate) struct StatusEffect {
|
pub(crate) struct StatusEffect {
|
||||||
expired: Duration,
|
expired: u32,
|
||||||
value: u16,
|
value: u16,
|
||||||
unknown: u16,
|
unknown: u16,
|
||||||
dt: Duration,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ message ValidateSessionRequest {
|
|||||||
|
|
||||||
message ValidateSessionResponse {
|
message ValidateSessionResponse {
|
||||||
bool valid = 1;
|
bool valid = 1;
|
||||||
|
string session_id = 2;
|
||||||
|
string user_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RegisterRequest {
|
message RegisterRequest {
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ message Location {
|
|||||||
int32 spawn_id = 4;
|
int32 spawn_id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Skill {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message Character {
|
message Character {
|
||||||
string character_id = 1; // Unique ID for the character
|
string character_id = 1; // Unique ID for the character
|
||||||
string name = 2; // Name of the character
|
string name = 2; // Name of the character
|
||||||
@@ -78,6 +82,6 @@ message CharacterFull {
|
|||||||
Location position = 3; // Character's position
|
Location position = 3; // Character's position
|
||||||
Looks looks = 4; // Character's Looks
|
Looks looks = 4; // Character's Looks
|
||||||
Stats stats = 5; // Character's stats
|
Stats stats = 5; // Character's stats
|
||||||
repeated int32 skills = 6; // Character's skills
|
repeated Skill skills = 6; // Character's skills
|
||||||
repeated Item items = 7; // Character inventory
|
repeated Item items = 7; // Character inventory
|
||||||
}
|
}
|
||||||
@@ -39,9 +39,9 @@ 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,
|
||||||
inventory jsonb default '{}'::jsonb,
|
inventory jsonb default '[]'::jsonb,
|
||||||
stats jsonb default '{}'::jsonb,
|
stats jsonb default '{}'::jsonb,
|
||||||
skills jsonb default '{}'::jsonb,
|
skills jsonb default '[]'::jsonb,
|
||||||
looks jsonb default '{}'::jsonb,
|
looks jsonb default '{}'::jsonb,
|
||||||
position jsonb default '{}'::jsonb,
|
position jsonb default '{}'::jsonb,
|
||||||
created_at timestamp default CURRENT_TIMESTAMP,
|
created_at timestamp default CURRENT_TIMESTAMP,
|
||||||
|
|||||||
Reference in New Issue
Block a user