v1.0.1 changes #11

Manually merged
Raven merged 54 commits from betterauth into main 2025-03-21 23:36:08 -04:00
13 changed files with 151 additions and 79 deletions
Showing only changes of commit 8ba8fce20b - Show all commits

View File

@@ -36,7 +36,7 @@ impl AuthService for MyAuthService {
&req.username,
&req.password,
)
.await
.await
{
let user_id = user.user_id.to_string();
let session_id = uuid::Uuid::new_v4().to_string();
@@ -146,12 +146,13 @@ impl AuthService for MyAuthService {
match response {
Ok(res) => {
debug!("Session valid: {:?}", res.into_inner());
Ok(Response::new(ValidateSessionResponse { valid: true }))
let res = res.into_inner();
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(_) => {
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 {
Ok(res) => {
debug!("Session valid: {:?}", res.into_inner());
Ok(Response::new(ValidateSessionResponse { valid: true }))
let res = res.into_inner();
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(_) => {
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() }))
}
}
}

View File

@@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Row};
use std::sync::Arc;
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)]
pub struct Character {
@@ -74,14 +75,16 @@ impl CharacterRepository {
looks: serde_json::Value,
position: serde_json::Value,
) -> 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(
"INSERT INTO characters (user_id, name, inventory, stats, looks, position, created_at, updated_at, is_active) \
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW(), true) RETURNING id",
"INSERT INTO characters (user_id, name, inventory, stats, skills, looks, position, created_at, updated_at, is_active) \
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW(), true) RETURNING id",
)
.bind(user_id)
.bind(name)
.bind(inventory)
.bind(stats)
.bind(default_skills)
.bind(looks)
.bind(position)
.fetch_one(&self.pool)
@@ -150,7 +153,7 @@ impl CharacterRepository {
// Fetch from database
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)
.fetch_all(&self.pool)

View File

@@ -1,7 +1,7 @@
import React, {useEffect, useState} from "react";
import axios from "axios";
import {getServiceAddress} from "../utils/consul";
import { Link } from "react-router-dom";
import {Link} from "react-router-dom";
const LoginPage = () => {
const [apiUrl, setApiUrl] = useState(null);
@@ -13,7 +13,7 @@ const LoginPage = () => {
// Fetch the API address from Consul
const fetchApiUrl = async () => {
try {
const { ServiceAddress, ServicePort } = await getServiceAddress("api-service");
const {ServiceAddress, ServicePort} = await getServiceAddress("api-service");
setApiUrl(`http://${ServiceAddress}:${ServicePort}/api/login`);
} catch (error) {
setMessage("Failed to retrieve API information.");
@@ -31,15 +31,15 @@ const LoginPage = () => {
}
try {
const response = await axios.post(apiUrl, { username, password });
const response = await axios.post(apiUrl, {username, password});
// Extract token and server info from response
const { token, session_id } = response.data;
const {token, session_id} = response.data;
setMessage("Login successful! Launching game...");
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)}`;
const {ServiceAddress, ServicePort} = await getServiceAddress("packet-service");
window.location.href = `osirose-launcher://launch?session=${encodeURIComponent(session_id)}&ip=${encodeURIComponent(ServiceAddress)}&port=${encodeURIComponent(ServicePort)}&username=${encodeURIComponent(username)}`;
} catch (error) {
setMessage("Login failed: " + error.response?.data?.error || error.message);
}

View File

@@ -1,5 +1,8 @@
use crate::format_shell_command;
use crate::wait_for_keypress;
use std::borrow::Cow;
use std::env;
use std::process::exit;
use std::process::{Command, Stdio};
use tracing::{debug, error, info, warn};
use url::Url;
@@ -15,7 +18,7 @@ fn create_command() -> Command {
exit(1);
}
let mut command = Command::new("./TRose.exe");
let command = Command::new("./TRose.exe");
command
}
@@ -56,7 +59,8 @@ pub(crate) fn launch_game(url: String) {
command.arg("_direct").arg("_otp").arg(value.to_string());
}
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") => {
command.arg("_userid").arg(value.to_string());

View File

@@ -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
EXPOSE 4000
EXPOSE 29000
CMD ["packet-service"]

View File

@@ -119,6 +119,29 @@ pub(crate) enum ItemType {
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 {
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@@ -140,7 +140,7 @@ pub(crate) async fn handle_login_req(
debug!("{:?}", data);
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) => {
if response.valid == false {
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) {
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 =

View File

@@ -3,6 +3,7 @@ use crate::character_client::CharacterClient;
use crate::connection_service::ConnectionService;
use crate::dataconsts::*;
use crate::enums;
use crate::enums::ItemType;
use crate::packet::{send_packet, Packet, PacketPayload};
use crate::packet_type::PacketType;
use crate::packets::*;
@@ -16,7 +17,7 @@ use tonic::{Code, Status};
use tracing::{debug, error, info, warn};
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) {
Some(enums::EquippedPosition::Goggles) => srv_char_list_reply::EquippedPosition::Googles,
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(
stream: &mut TcpStream,
packet: Packet,
@@ -64,7 +93,7 @@ pub(crate) async fn handle_char_list_req(
for item in character.items {
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 {
id: item.item_id 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 name = NullTerminatedString(character.name.clone());
let looks = character.looks.unwrap();
let position = character.position.unwrap();
let stats = character.stats.unwrap();
@@ -253,22 +283,23 @@ pub(crate) async fn handle_select_char_req(
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());
let mut skill_list: [u16; (MAX_SKILL_COUNT as usize)] = core::array::from_fn(|i| {
if i < skills.len() {
return skills[i] as u16;
}
0
});
let mut skill_list: [u16; (MAX_SKILL_COUNT as usize)] = [0u16; MAX_SKILL_COUNT as usize];
for index in 0..skills.len() {
skill_list[index] = skills[index].id as u16;
}
for item in items {
if item.slot < MAX_VISIBLE_ITEMS as i32 {
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,
};
let slot = convert_type_to_body_part(item.slot) as usize - 2;
if slot >= 0 {
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 {
@@ -337,8 +368,9 @@ pub(crate) async fn handle_select_char_req(
skills: skill_list,
hotbar: hotbar_list,
tag: user_id as u32,
name: request.name,
name,
};
debug!("{:?}", data);
let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?;
send_packet(stream, &response_packet).await?;

View File

@@ -14,14 +14,18 @@ pub struct Packet {
}
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();
Ok(bincode::encode_to_vec(self, config)?)
}
fn decode(data: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>
where
Self: Sized, Self: Decode<()>
Self: Sized,
Self: Decode<()>,
{
let config = bincode::config::standard().with_fixed_int_encoding();
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> {
let data = packet.to_raw();
debug!("Sending data: {:?}", data);
debug!("Sending '{:#X}' bytes of data. {:?}", data.len(), data);
stream.write_all(&data).await?;
Ok(())
}

View File

@@ -1,5 +1,4 @@
use bincode::{Decode, Encode};
use std::time::Duration;
// `HotbarItem` structure converted to Rust.
#[derive(Debug, Clone, Copy, Encode, Decode, Default)]
@@ -18,8 +17,7 @@ pub(crate) struct Skill {
// `StatusEffect` structure converted to Rust.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Default)]
pub(crate) struct StatusEffect {
expired: Duration,
expired: u32,
value: u16,
unknown: u16,
dt: Duration,
}

View File

@@ -5,74 +5,76 @@ package auth;
import "common.proto";
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
rpc Logout(LogoutRequest) returns (common.Empty);
rpc ValidateToken(ValidateTokenRequest) returns (ValidateTokenResponse);
rpc ValidateSession(ValidateSessionRequest) returns (ValidateSessionResponse);
rpc RefreshSession(ValidateSessionRequest) returns (ValidateSessionResponse);
rpc Register (RegisterRequest) returns (RegisterResponse);
rpc RequestPasswordReset (PasswordResetRequest) returns (PasswordResetResponse);
rpc ResetPassword (ResetPasswordRequest) returns (ResetPasswordResponse);
rpc Login(LoginRequest) returns (LoginResponse);
rpc Logout(LogoutRequest) returns (common.Empty);
rpc ValidateToken(ValidateTokenRequest) returns (ValidateTokenResponse);
rpc ValidateSession(ValidateSessionRequest) returns (ValidateSessionResponse);
rpc RefreshSession(ValidateSessionRequest) returns (ValidateSessionResponse);
rpc Register (RegisterRequest) returns (RegisterResponse);
rpc RequestPasswordReset (PasswordResetRequest) returns (PasswordResetResponse);
rpc ResetPassword (ResetPasswordRequest) returns (ResetPasswordResponse);
}
message LoginRequest {
string username = 1;
string password = 2;
string ip_address = 3;
string username = 1;
string password = 2;
string ip_address = 3;
}
message LoginResponse {
string token = 1;
string user_id = 2;
string session_id = 3;
string token = 1;
string user_id = 2;
string session_id = 3;
}
message LogoutRequest {
string session_id = 1;
string session_id = 1;
}
message ValidateTokenRequest {
string token = 1;
string token = 1;
}
message ValidateTokenResponse {
bool valid = 1;
string user_id = 2;
string session_id = 3;
bool valid = 1;
string user_id = 2;
string session_id = 3;
}
message ValidateSessionRequest {
string session_id = 1;
string session_id = 1;
}
message ValidateSessionResponse {
bool valid = 1;
bool valid = 1;
string session_id = 2;
string user_id = 3;
}
message RegisterRequest {
string username = 1;
string email = 2;
string password = 3;
string username = 1;
string email = 2;
string password = 3;
}
message RegisterResponse {
int32 user_id = 1;
string message = 2;
int32 user_id = 1;
string message = 2;
}
message PasswordResetRequest {
string email = 1;
string email = 1;
}
message PasswordResetResponse {
string message = 1;
string message = 1;
}
message ResetPasswordRequest {
string reset_token = 1;
string new_password = 2;
string reset_token = 1;
string new_password = 2;
}
message ResetPasswordResponse {
string message = 1;
string message = 1;
}

View File

@@ -62,6 +62,10 @@ message Location {
int32 spawn_id = 4;
}
message Skill {
int32 id = 1;
}
message Character {
string character_id = 1; // Unique ID for the character
string name = 2; // Name of the character
@@ -78,6 +82,6 @@ message CharacterFull {
Location position = 3; // Character's position
Looks looks = 4; // Character's Looks
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
}

View File

@@ -37,11 +37,11 @@ create table characters
user_id integer not null
references users
on delete cascade,
is_active boolean default true,
is_active boolean default true,
name varchar(50) not null,
inventory jsonb default '{}'::jsonb,
inventory jsonb default '[]'::jsonb,
stats jsonb default '{}'::jsonb,
skills jsonb default '{}'::jsonb,
skills jsonb default '[]'::jsonb,
looks jsonb default '{}'::jsonb,
position jsonb default '{}'::jsonb,
created_at timestamp default CURRENT_TIMESTAMP,