Compare commits
3 Commits
4046f56191
...
launcher_v
| Author | SHA256 | Date | |
|---|---|---|---|
|
50cc39c6a2
|
|||
|
d47d5f44b1
|
|||
|
9e984d2aa8
|
@@ -7,7 +7,7 @@ use crate::auth::{
|
|||||||
use crate::common::Empty;
|
use crate::common::Empty;
|
||||||
use crate::database_client::{DatabaseClient, DatabaseClientTrait};
|
use crate::database_client::{DatabaseClient, DatabaseClientTrait};
|
||||||
use crate::session::session_service_client::SessionServiceClient;
|
use crate::session::session_service_client::SessionServiceClient;
|
||||||
use crate::session::{GetSessionRequest};
|
use crate::session::{GetSessionRequest, RefreshSessionRequest};
|
||||||
use crate::users::{hash_password, verify_user};
|
use crate::users::{hash_password, verify_user};
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -78,7 +78,7 @@ impl AuthService for MyAuthService {
|
|||||||
.session_client
|
.session_client
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.clone()
|
.clone()
|
||||||
.get_session(GetSessionRequest {
|
.refresh_session(RefreshSessionRequest {
|
||||||
session_id: req.session_id,
|
session_id: req.session_id,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
@@ -87,7 +87,7 @@ impl AuthService for MyAuthService {
|
|||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let res = res.into_inner();
|
let res = res.into_inner();
|
||||||
debug!("Session valid: {:?}", res);
|
debug!("Session valid: {:?}", res);
|
||||||
Ok(Response::new(RefreshSessionResponse { valid: true }))
|
Ok(Response::new(RefreshSessionResponse { valid: res.valid }))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!("Unable to refresh session");
|
debug!("Unable to refresh session");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::session::{session_service_client::SessionServiceClient, GetSessionRequest, GetSessionResponse};
|
use crate::session::{session_service_client::SessionServiceClient, GetSessionRequest, GetSessionResponse, RefreshSessionRequest, RefreshSessionResponse};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@@ -11,6 +11,10 @@ pub trait SessionClientTrait: Sized {
|
|||||||
&mut self,
|
&mut self,
|
||||||
session_id: String,
|
session_id: String,
|
||||||
) -> Result<GetSessionResponse, Box<dyn std::error::Error>>;
|
) -> Result<GetSessionResponse, Box<dyn std::error::Error>>;
|
||||||
|
async fn refresh_session(
|
||||||
|
&mut self,
|
||||||
|
session_id: String,
|
||||||
|
) -> Result<RefreshSessionResponse, Box<dyn std::error::Error>>;
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SessionClient {
|
pub struct SessionClient {
|
||||||
@@ -31,4 +35,11 @@ impl SessionClientTrait for SessionClient {
|
|||||||
let response = self.client.get_session(request).await?;
|
let response = self.client.get_session(request).await?;
|
||||||
Ok(response.into_inner())
|
Ok(response.into_inner())
|
||||||
}
|
}
|
||||||
|
async fn refresh_session(&mut self, session_id: String) -> Result<RefreshSessionResponse, Box<dyn Error>> {
|
||||||
|
let request = tonic::Request::new(RefreshSessionRequest {
|
||||||
|
session_id,
|
||||||
|
});
|
||||||
|
let response = self.client.refresh_session(request).await?;
|
||||||
|
Ok(response.into_inner())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::grpc::database_service::MyDatabaseService;
|
|||||||
use crate::grpc::session_service_server::SessionService;
|
use crate::grpc::session_service_server::SessionService;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use crate::grpc::{GetSessionRequest, GetSessionResponse};
|
use crate::grpc::{GetSessionRequest, GetSessionResponse, RefreshSessionRequest, RefreshSessionResponse};
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl SessionService for MyDatabaseService {
|
impl SessionService for MyDatabaseService {
|
||||||
@@ -22,4 +22,19 @@ impl SessionService for MyDatabaseService {
|
|||||||
user_id: session.user_id,
|
user_id: session.user_id,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn refresh_session(&self, request: Request<RefreshSessionRequest>) -> Result<Response<RefreshSessionResponse>, Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
debug!("get_session: {:?}", req);
|
||||||
|
|
||||||
|
let session = self.db.session_repo.refresh_session(&req.session_id).await
|
||||||
|
.map_err(|_| Status::not_found("Session not found"))?;
|
||||||
|
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
debug!("session: {:?}", session);
|
||||||
|
Ok(Response::new(RefreshSessionResponse {
|
||||||
|
valid
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,34 @@ impl SessionRepository {
|
|||||||
debug!("session: {:?}", session);
|
debug!("session: {:?}", session);
|
||||||
|
|
||||||
self.cache.lock().await
|
self.cache.lock().await
|
||||||
.set(&cache_key, &session, 0).await
|
.set(&cache_key, &session, 300).await
|
||||||
|
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||||
|
Ok(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn refresh_session(&self, session_id: &str) -> Result<Session, sqlx::Error> {
|
||||||
|
let cache_key = format!("session:{}", session_id);
|
||||||
|
|
||||||
|
if let Some(session) = self.cache.lock().await
|
||||||
|
.get::<Session>(&cache_key).await
|
||||||
|
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||||
|
{
|
||||||
|
self.cache.lock().await
|
||||||
|
.refresh(&cache_key, 300).await
|
||||||
|
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||||
|
return Ok(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure the session is still valid
|
||||||
|
let session = sqlx::query_as::<_, Session>(
|
||||||
|
"SELECT id, \"userId\" as user_id FROM session WHERE id = $1",
|
||||||
|
)
|
||||||
|
.bind(session_id)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.cache.lock().await
|
||||||
|
.set(&cache_key, &session, 300).await
|
||||||
.map_err(|_| sqlx::Error::RowNotFound)?;
|
.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||||
Ok(session)
|
Ok(session)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "launcher"
|
name = "launcher"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
shell-escape = "0.1.5"
|
shell-escape = "0.1.5"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "chrono"] }
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
clap = { version = "4.5.23", features = ["derive"] }
|
clap = { version = "4.5.23", features = ["derive"] }
|
||||||
|
reqwest = { version = "0.12.15", features = ["json"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
mod launcher;
|
mod launcher;
|
||||||
|
mod version;
|
||||||
|
|
||||||
|
use crate::version::version_check;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
use tracing::{debug, error, info, Level};
|
use tracing::{debug, error, info, Level};
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
Launch,
|
Launch,
|
||||||
Update,
|
Update,
|
||||||
|
Check,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let verbose = 0;
|
let verbose = 0;
|
||||||
let verbose = match verbose {
|
let verbose = match verbose {
|
||||||
0 => Level::INFO,
|
0 => Level::INFO,
|
||||||
@@ -20,7 +25,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Set our logging level
|
// Set our logging level
|
||||||
tracing_subscriber::fmt().with_max_level(verbose).init();
|
let filter = EnvFilter::try_new(format!("launcher=debug"))
|
||||||
|
.unwrap_or_else(|_| EnvFilter::new(format!("launcher=info")));
|
||||||
|
tracing_subscriber::fmt().with_env_filter(filter).init();
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
@@ -42,6 +49,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let action = match method {
|
let action = match method {
|
||||||
"launch" => Commands::Launch,
|
"launch" => Commands::Launch,
|
||||||
"update" => Commands::Update,
|
"update" => Commands::Update,
|
||||||
|
"version" => Commands::Check,
|
||||||
other => return Err(format!("Unknown method: {}", other).into()),
|
other => return Err(format!("Unknown method: {}", other).into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,6 +62,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
error!("Update action not implemented");
|
error!("Update action not implemented");
|
||||||
return Err("Update action not implemented".into());
|
return Err("Update action not implemented".into());
|
||||||
}
|
}
|
||||||
|
Commands::Check => {
|
||||||
|
version_check(uri.to_string()).await;
|
||||||
|
wait_for_keypress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
87
launcher/src/version.rs
Normal file
87
launcher/src/version.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
use crate::format_shell_command;
|
||||||
|
use crate::wait_for_keypress;
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
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;
|
||||||
|
|
||||||
|
static APP_USER_AGENT: &str = concat!(
|
||||||
|
"osirose-",
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
"/",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn send_version_to_browser(callback_url: Option<String>, client_version: &str) {
|
||||||
|
if let Some(url) = callback_url {
|
||||||
|
let payload = json!({
|
||||||
|
"version": client_version,
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = reqwest::Client::builder()
|
||||||
|
.user_agent(APP_USER_AGENT)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if (client.is_err()) {
|
||||||
|
error!("Unable to build info client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match client.unwrap().post(url.clone()).json(&payload).send().await {
|
||||||
|
Ok(response) => {
|
||||||
|
if response.status().is_success() {
|
||||||
|
info!("Successfully sent version info to {url} URL!");
|
||||||
|
} else {
|
||||||
|
error!("Failed to send data. Status: {}", response.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error sending POST request: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error!("Missing callback URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_url(url: &str) -> Option<String> {
|
||||||
|
if let parsed_url = Url::parse(url).ok()? {
|
||||||
|
return Some(parsed_url.to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_client_version() -> String {
|
||||||
|
// This is temp until I can get an actual version check
|
||||||
|
"1.0.0".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn version_check(url: String) {
|
||||||
|
match Url::parse(&url) {
|
||||||
|
Ok(parsed_url) => {
|
||||||
|
let params = parsed_url.query_pairs();
|
||||||
|
|
||||||
|
let mut callback_url = None;
|
||||||
|
for (key, value) in params {
|
||||||
|
debug!("Query pairs: [{}, {}]", key, value);
|
||||||
|
|
||||||
|
match key {
|
||||||
|
Cow::Borrowed("callback") => {
|
||||||
|
callback_url = parse_url(&value);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!("Unexpected parameter: [{}, {}]", key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send_version_to_browser(callback_url, &check_client_version()).await;
|
||||||
|
}
|
||||||
|
Err(e) => error!("Failed to parse URL: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,6 @@ 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::*;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::env;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
@@ -16,6 +14,17 @@ use tokio::sync::Mutex;
|
|||||||
use tonic::{Code, Status};
|
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;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
|
|
||||||
|
fn string_to_u32(s: &str) -> u32 {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
s.hash(&mut hasher);
|
||||||
|
// Convert the 64-bit hash to a 32-bit number.
|
||||||
|
// Note: This cast might lead to collisions if there are a lot of objects,
|
||||||
|
hasher.finish() as u32
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn convert_slot_to_equip_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) {
|
||||||
@@ -368,10 +377,9 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
pat_cooldown_time: stats.pat_cooldown_time as u32,
|
pat_cooldown_time: stats.pat_cooldown_time as u32,
|
||||||
skills: skill_list,
|
skills: skill_list,
|
||||||
hotbar: hotbar_list,
|
hotbar: hotbar_list,
|
||||||
tag: 100,
|
tag: string_to_u32(&user_id),
|
||||||
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?;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package session_db_api;
|
|||||||
|
|
||||||
service SessionService {
|
service SessionService {
|
||||||
rpc GetSession(GetSessionRequest) returns (GetSessionResponse);
|
rpc GetSession(GetSessionRequest) returns (GetSessionResponse);
|
||||||
|
rpc RefreshSession(RefreshSessionRequest) returns (RefreshSessionResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetSessionRequest {
|
message GetSessionRequest {
|
||||||
@@ -14,3 +15,11 @@ message GetSessionResponse {
|
|||||||
string session_id = 1;
|
string session_id = 1;
|
||||||
string user_id = 2;
|
string user_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RefreshSessionRequest {
|
||||||
|
string session_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RefreshSessionResponse {
|
||||||
|
bool valid = 1;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user