documentation, unit-tests, chat service #12
@@ -2,6 +2,7 @@
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"auth-service",
|
"auth-service",
|
||||||
|
"chat-service",
|
||||||
"character-service",
|
"character-service",
|
||||||
"database-service",
|
"database-service",
|
||||||
"packet-service",
|
"packet-service",
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ impl CharacterDbClient {
|
|||||||
};
|
};
|
||||||
let position = Position {
|
let position = Position {
|
||||||
map_id: 20,
|
map_id: 20,
|
||||||
x: 5200.00,
|
x: 520000.00,
|
||||||
y: 5200.00,
|
y: 520000.00,
|
||||||
z: 1.0,
|
z: 1.0,
|
||||||
spawn_id: 1,
|
spawn_id: 1,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ autoscaling:
|
|||||||
|
|
||||||
global:
|
global:
|
||||||
env:
|
env:
|
||||||
LOG_LEVEL: "debug"
|
LOG_LEVEL: "info"
|
||||||
APP_ENV: "dev"
|
APP_ENV: "dev"
|
||||||
DATABASE_URL: "" # This is a placeholder. Will be dynamically constructed
|
DATABASE_URL: "" # This is a placeholder. Will be dynamically constructed
|
||||||
REDIS_URL: "redis://valkey:6379/0"
|
REDIS_URL: "redis://valkey:6379/0"
|
||||||
@@ -26,6 +26,20 @@ services:
|
|||||||
targetPort: 50051
|
targetPort: 50051
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|
||||||
|
- name: chat-service
|
||||||
|
replicas: 1
|
||||||
|
serviceAccount: azgstudio-serviceaccount
|
||||||
|
image: chat-service:latest
|
||||||
|
port: 50055
|
||||||
|
env:
|
||||||
|
SERVICE_PORT: 50055
|
||||||
|
LOG_LEVEL: "debug"
|
||||||
|
service:
|
||||||
|
portName: chat-service
|
||||||
|
port: 50055
|
||||||
|
targetPort: 50055
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
- name: character-service
|
- name: character-service
|
||||||
replicas: 1
|
replicas: 1
|
||||||
serviceAccount: azgstudio-serviceaccount
|
serviceAccount: azgstudio-serviceaccount
|
||||||
@@ -61,6 +75,7 @@ services:
|
|||||||
port: 29000
|
port: 29000
|
||||||
env:
|
env:
|
||||||
SERVICE_PORT: 29000
|
SERVICE_PORT: 29000
|
||||||
|
LOG_LEVEL: "debug"
|
||||||
service:
|
service:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
portName: game-packet-service
|
portName: game-packet-service
|
||||||
@@ -75,6 +90,7 @@ services:
|
|||||||
port: 50054
|
port: 50054
|
||||||
env:
|
env:
|
||||||
SERVICE_PORT: 50054
|
SERVICE_PORT: 50054
|
||||||
|
LOG_LEVEL: "debug"
|
||||||
service:
|
service:
|
||||||
annotations:
|
annotations:
|
||||||
name: "Athena"
|
name: "Athena"
|
||||||
|
|||||||
22
chat-service/Cargo.toml
Normal file
22
chat-service/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "chat-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
utils = { path = "../utils" }
|
||||||
|
dotenv = "0.15"
|
||||||
|
tokio = { version = "1.41.1", features = ["full"] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "chrono"] }
|
||||||
|
tonic = "0.12.3"
|
||||||
|
prost = "0.13.4"
|
||||||
|
warp = "0.3.7"
|
||||||
|
tonic-health = "0.12.3"
|
||||||
|
futures = "0.3.31"
|
||||||
|
uuid = "1.15.1"
|
||||||
|
tokio-stream = "0.1.17"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tonic-build = "0.12.3"
|
||||||
25
chat-service/Dockerfile
Normal file
25
chat-service/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
FROM rust:alpine AS builder
|
||||||
|
LABEL authors="raven"
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev libressl-dev protobuf-dev
|
||||||
|
|
||||||
|
WORKDIR /usr/src/utils
|
||||||
|
COPY ./utils .
|
||||||
|
|
||||||
|
WORKDIR /usr/src/proto
|
||||||
|
COPY ./proto .
|
||||||
|
|
||||||
|
WORKDIR /usr/src/chat-service
|
||||||
|
COPY ./chat-service .
|
||||||
|
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
|
FROM alpine:3
|
||||||
|
|
||||||
|
RUN apk add --no-cache libssl3 libgcc
|
||||||
|
|
||||||
|
COPY --from=builder /usr/src/chat-service/target/release/chat-service /usr/local/bin/chat-service
|
||||||
|
|
||||||
|
EXPOSE 50054
|
||||||
|
|
||||||
|
CMD ["chat-service"]
|
||||||
16
chat-service/build.rs
Normal file
16
chat-service/build.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
fn main() {
|
||||||
|
// gRPC Server code
|
||||||
|
tonic_build::configure()
|
||||||
|
.build_server(true) // Generate gRPC server code
|
||||||
|
.compile_well_known_types(true)
|
||||||
|
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||||
|
.compile_protos(&["../proto/chat.proto"], &["../proto"])
|
||||||
|
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
|
||||||
|
// gRPC Client code
|
||||||
|
// tonic_build::configure()
|
||||||
|
// .build_server(false) // Generate gRPC client code
|
||||||
|
// .compile_well_known_types(true)
|
||||||
|
// .compile_protos(&["../proto/user_db_api.proto", "../proto/auth.proto"], &["../proto"])
|
||||||
|
// .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
|
||||||
|
}
|
||||||
22
chat-service/src/chat_channels/guild_chat.rs
Normal file
22
chat-service/src/chat_channels/guild_chat.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use crate::chat_channels::ChatChannel;
|
||||||
|
use crate::chat_service::Clients;
|
||||||
|
use crate::chat_service::chat::ChatMessage;
|
||||||
|
|
||||||
|
/// A dedicated module for guild chat.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GuildChat;
|
||||||
|
|
||||||
|
impl ChatChannel for GuildChat {
|
||||||
|
fn handle_message(&self, message: ChatMessage, sender_id: &str, clients: &Clients) {
|
||||||
|
// This is a placeholder. In a real implementation, verify
|
||||||
|
// guild membership by consulting your Character or Guild service.
|
||||||
|
let clients_lock = clients.lock().unwrap();
|
||||||
|
for (id, tx) in clients_lock.iter() {
|
||||||
|
// For demonstration, send only to clients whose IDs contain
|
||||||
|
// "guild". Replace this logic with your actual membership check.
|
||||||
|
if id != sender_id && id.contains("guild") {
|
||||||
|
let _ = tx.try_send(message.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
chat-service/src/chat_channels/local_chat.rs
Normal file
22
chat-service/src/chat_channels/local_chat.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use tracing::debug;
|
||||||
|
use crate::chat_channels::ChatChannel;
|
||||||
|
use crate::chat_service::Clients;
|
||||||
|
use crate::chat_service::chat::ChatMessage;
|
||||||
|
|
||||||
|
/// A dedicated module for local chat.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LocalChat;
|
||||||
|
|
||||||
|
impl ChatChannel for LocalChat {
|
||||||
|
fn handle_message(&self, message: ChatMessage, sender_id: &str, clients: &Clients) {
|
||||||
|
// In a full implementation, you might query for nearby clients.
|
||||||
|
// For demo purposes, we simply broadcast to all clients except the sender.
|
||||||
|
debug!("LocalChat::handle_message: {:?}", message);
|
||||||
|
let clients_lock = clients.lock().unwrap();
|
||||||
|
for (id, tx) in clients_lock.iter() {
|
||||||
|
// if id != sender_id {
|
||||||
|
let _ = tx.try_send(message.clone());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
chat-service/src/chat_channels/mod.rs
Normal file
12
chat-service/src/chat_channels/mod.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
pub mod local_chat;
|
||||||
|
pub mod shout_chat;
|
||||||
|
pub mod guild_chat;
|
||||||
|
|
||||||
|
/// Define a common trait for all dedicated chat channels.
|
||||||
|
use crate::chat_service::Clients;
|
||||||
|
use crate::chat_service::chat::ChatMessage;
|
||||||
|
|
||||||
|
pub trait ChatChannel: Send + Sync {
|
||||||
|
/// Process and route the chat message.
|
||||||
|
fn handle_message(&self, message: ChatMessage, sender_id: &str, clients: &Clients);
|
||||||
|
}
|
||||||
20
chat-service/src/chat_channels/shout_chat.rs
Normal file
20
chat-service/src/chat_channels/shout_chat.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use crate::chat_channels::ChatChannel;
|
||||||
|
use crate::chat_service::Clients;
|
||||||
|
use crate::chat_service::chat::ChatMessage;
|
||||||
|
|
||||||
|
/// A dedicated module for shout chat.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ShoutChat;
|
||||||
|
|
||||||
|
impl ChatChannel for ShoutChat {
|
||||||
|
fn handle_message(&self, message: ChatMessage, sender_id: &str, clients: &Clients) {
|
||||||
|
// For demo purposes, we simply broadcast to all clients except the sender.
|
||||||
|
// TODO: make sure the clients are on the same map
|
||||||
|
let clients_lock = clients.lock().unwrap();
|
||||||
|
for (id, tx) in clients_lock.iter() {
|
||||||
|
if id != sender_id {
|
||||||
|
let _ = tx.try_send(message.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
chat-service/src/chat_service.rs
Normal file
130
chat-service/src/chat_service.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
use futures::{Stream, StreamExt};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use tonic::{Request, Response, Status};
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
pub mod common {
|
||||||
|
tonic::include_proto!("common");
|
||||||
|
}
|
||||||
|
pub mod chat {
|
||||||
|
tonic::include_proto!("chat");
|
||||||
|
}
|
||||||
|
|
||||||
|
use chat::chat_service_server::{ChatService, ChatServiceServer};
|
||||||
|
use chat::{ChatMessage, MessageType};
|
||||||
|
|
||||||
|
/// Type alias for storing client connections.
|
||||||
|
pub type Clients =
|
||||||
|
Arc<Mutex<HashMap<String, tokio::sync::mpsc::Sender<ChatMessage>>>>;
|
||||||
|
|
||||||
|
use crate::chat_channels::ChatChannel;
|
||||||
|
|
||||||
|
/// Our chat service struct.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MyChatService {
|
||||||
|
pub clients: Clients,
|
||||||
|
pub local_channel: Arc<dyn ChatChannel>,
|
||||||
|
pub shout_channel: Arc<dyn ChatChannel>,
|
||||||
|
pub guild_channel: Arc<dyn ChatChannel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyChatService {
|
||||||
|
/// Wrap our service as a gRPC service.
|
||||||
|
pub fn into_service(self) -> ChatServiceServer<Self> {
|
||||||
|
ChatServiceServer::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tonic::async_trait]
|
||||||
|
impl ChatService for MyChatService {
|
||||||
|
type ChatStreamStream =
|
||||||
|
Pin<Box<dyn Stream<Item = Result<ChatMessage, Status>> + Send + Sync + 'static>>;
|
||||||
|
|
||||||
|
async fn chat_stream(
|
||||||
|
&self,
|
||||||
|
request: Request<tonic::Streaming<ChatMessage>>,
|
||||||
|
) -> Result<Response<Self::ChatStreamStream>, Status> {
|
||||||
|
debug!("New chat client connected");
|
||||||
|
debug!("request: {:?}", request);
|
||||||
|
|
||||||
|
let mut inbound = request.into_inner();
|
||||||
|
|
||||||
|
// Create a new client ID. In production, use authenticated IDs.
|
||||||
|
let client_id = format!("client-{}", uuid::Uuid::new_v4());
|
||||||
|
|
||||||
|
// Create a channel for sending outbound messages to this client.
|
||||||
|
let (tx, rx) = tokio::sync::mpsc::channel(32);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut clients = self.clients.lock().unwrap();
|
||||||
|
clients.insert(client_id.clone(), tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone shared resources for the spawned task.
|
||||||
|
let clients_clone = self.clients.clone();
|
||||||
|
let local_channel = self.local_channel.clone();
|
||||||
|
let shout_channel = self.shout_channel.clone();
|
||||||
|
let guild_channel = self.guild_channel.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(result) = inbound.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(message) => {
|
||||||
|
debug!("message: {:?}", message);
|
||||||
|
// Dispatch based on the chat type.
|
||||||
|
match TryFrom::try_from(message.r#type)
|
||||||
|
.unwrap_or(MessageType::Normal)
|
||||||
|
{
|
||||||
|
MessageType::Normal => {
|
||||||
|
local_channel.handle_message(
|
||||||
|
message,
|
||||||
|
&client_id,
|
||||||
|
&clients_clone,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MessageType::Shout => {
|
||||||
|
shout_channel.handle_message(
|
||||||
|
message,
|
||||||
|
&client_id,
|
||||||
|
&clients_clone,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MessageType::Clan => {
|
||||||
|
guild_channel.handle_message(
|
||||||
|
message,
|
||||||
|
&client_id,
|
||||||
|
&clients_clone,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// For other types, we simply broadcast as default.
|
||||||
|
_ => {
|
||||||
|
let clients_lock = clients_clone.lock().unwrap();
|
||||||
|
for (id, tx) in clients_lock.iter() {
|
||||||
|
if id != &client_id {
|
||||||
|
let _ = tx.try_send(message.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error receiving message from {}: {:?}", client_id, e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the client when the stream ends.
|
||||||
|
let mut clients = clients_clone.lock().unwrap();
|
||||||
|
clients.remove(&client_id);
|
||||||
|
println!("Client {} disconnected", client_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert the rx half into a stream for the response.
|
||||||
|
let out_stream = tokio_stream::wrappers::ReceiverStream::new(rx)
|
||||||
|
.map(|msg| Ok(msg));
|
||||||
|
Ok(Response::new(Box::pin(out_stream) as Self::ChatStreamStream))
|
||||||
|
}
|
||||||
|
}
|
||||||
51
chat-service/src/main.rs
Normal file
51
chat-service/src/main.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
mod chat_service;
|
||||||
|
mod chat_channels;
|
||||||
|
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::env;
|
||||||
|
use utils::service_discovery::{get_kube_service_endpoints_by_dns, get_service_endpoints_by_dns};
|
||||||
|
use utils::{health_check, logging};
|
||||||
|
use chat_service::MyChatService;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use tonic::transport::Server;
|
||||||
|
use crate::chat_service::chat::chat_service_server::ChatServiceServer;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
dotenv().ok();
|
||||||
|
let app_name = env!("CARGO_PKG_NAME");
|
||||||
|
logging::setup_logging(app_name, &["chat_service", "health_check"]);
|
||||||
|
|
||||||
|
// Set the gRPC server address
|
||||||
|
let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "0.0.0.0".to_string());
|
||||||
|
let port = env::var("SERVICE_PORT").unwrap_or_else(|_| "50055".to_string());
|
||||||
|
|
||||||
|
let clients = Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
let chat_service = MyChatService {
|
||||||
|
clients: clients.clone(),
|
||||||
|
local_channel: Arc::new(chat_channels::local_chat::LocalChat),
|
||||||
|
shout_channel: Arc::new(chat_channels::shout_chat::ShoutChat),
|
||||||
|
guild_channel: Arc::new(chat_channels::guild_chat::GuildChat),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register service with Consul
|
||||||
|
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
|
||||||
|
health_reporter
|
||||||
|
.set_serving::<ChatServiceServer<MyChatService>>()
|
||||||
|
.await;
|
||||||
|
let address = SocketAddr::new(addr.parse()?, port.parse()?);
|
||||||
|
tokio::spawn(
|
||||||
|
Server::builder()
|
||||||
|
.add_service(chat_service.into_service())
|
||||||
|
.add_service(health_service)
|
||||||
|
.serve(address),
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Chat Service listening on {}", address);
|
||||||
|
|
||||||
|
utils::signal_handler::wait_for_signal().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ serde_json = "1.0.133"
|
|||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
utils = { path = "../utils" }
|
utils = { path = "../utils" }
|
||||||
tonic-health = "0.12.3"
|
tonic-health = "0.12.3"
|
||||||
|
log = "0.4.26"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12.3"
|
tonic-build = "0.12.3"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ 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 tracing::debug;
|
use log::debug;
|
||||||
use utils::redis_cache::{Cache, RedisCache};
|
use utils::redis_cache::{Cache, RedisCache};
|
||||||
|
|
||||||
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||||
@@ -24,6 +24,8 @@ impl SessionRepository {
|
|||||||
pub async fn get_session(&self, session_id: &str) -> Result<Session, sqlx::Error> {
|
pub async fn get_session(&self, session_id: &str) -> Result<Session, sqlx::Error> {
|
||||||
let cache_key = format!("session:{}", session_id);
|
let cache_key = format!("session:{}", session_id);
|
||||||
|
|
||||||
|
debug!("get_session: {:?}", session_id);
|
||||||
|
|
||||||
if let Some(session) = self
|
if let Some(session) = self
|
||||||
.cache
|
.cache
|
||||||
.lock()
|
.lock()
|
||||||
@@ -32,10 +34,13 @@ impl SessionRepository {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| sqlx::Error::RowNotFound)?
|
.map_err(|_| sqlx::Error::RowNotFound)?
|
||||||
{
|
{
|
||||||
|
debug!("Found session in cache: {:?}", session);
|
||||||
return Ok(session);
|
return Ok(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch from database
|
debug!("Session not found in cache, fetching from database");
|
||||||
|
|
||||||
|
// Fetch from the database
|
||||||
let session = sqlx::query_as::<_, Session>("SELECT id, \"userId\" as user_id FROM session WHERE id = $1")
|
let session = sqlx::query_as::<_, Session>("SELECT id, \"userId\" as user_id FROM session WHERE id = $1")
|
||||||
.bind(session_id)
|
.bind(session_id)
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&self.pool)
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ dashmap = "6.1.0"
|
|||||||
uuid = { version = "1.11.0", features = ["v4"] }
|
uuid = { version = "1.11.0", features = ["v4"] }
|
||||||
chrono = "0.4.39"
|
chrono = "0.4.39"
|
||||||
prometheus_exporter = "0.8.5"
|
prometheus_exporter = "0.8.5"
|
||||||
|
futures = "0.3.31"
|
||||||
|
tokio-stream = "0.1.17"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12.3"
|
tonic-build = "0.12.3"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ fn main() {
|
|||||||
.compile_protos(
|
.compile_protos(
|
||||||
&[
|
&[
|
||||||
"../proto/auth.proto",
|
"../proto/auth.proto",
|
||||||
|
"../proto/chat.proto",
|
||||||
"../proto/character.proto",
|
"../proto/character.proto",
|
||||||
"../proto/character_common.proto",
|
"../proto/character_common.proto",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
use crate::handlers::chat_client::ChatClientHandler;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ConnectionState {
|
pub struct ConnectionState {
|
||||||
pub user_id: Option<String>,
|
pub user_id: Option<String>,
|
||||||
pub session_id: Option<String>,
|
pub session_id: Option<String>,
|
||||||
pub character_id: Option<i8>,
|
pub character_id: Option<i8>,
|
||||||
pub character_list: Option<Vec<u32>>,
|
pub character_list: Option<Vec<u32>>,
|
||||||
pub additional_data: HashMap<String, String>, // Flexible data storage
|
pub additional_data: HashMap<String, String>, // Flexible data storage
|
||||||
|
pub chat_handler: Option<Arc<ChatClientHandler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionState {
|
impl ConnectionState {
|
||||||
@@ -17,6 +22,20 @@ impl ConnectionState {
|
|||||||
character_id: None,
|
character_id: None,
|
||||||
character_list: None,
|
character_list: None,
|
||||||
additional_data: HashMap::new(),
|
additional_data: HashMap::new(),
|
||||||
|
chat_handler: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ConnectionState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("ConnectionState")
|
||||||
|
.field("user_id", &self.user_id)
|
||||||
|
.field("session_id", &self.session_id)
|
||||||
|
.field("character_id", &self.character_id)
|
||||||
|
.field("character_list", &self.character_list)
|
||||||
|
.field("additional_data", &self.additional_data)
|
||||||
|
.field("chat_handler", &self.chat_handler.as_ref().map(|_| "<chat handler>"))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,6 @@ use crate::auth_client::AuthClient;
|
|||||||
use crate::connection_service::ConnectionService;
|
use crate::connection_service::ConnectionService;
|
||||||
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::cli_channel_list_req::CliChannelListReq;
|
|
||||||
use crate::packets::cli_join_server_token_req::CliJoinServerTokenReq;
|
|
||||||
use crate::packets::cli_login_token_req::CliLoginTokenReq;
|
|
||||||
use crate::packets::cli_srv_select_req::CliSrvSelectReq;
|
|
||||||
use crate::packets::srv_accept_reply::SrvAcceptReply;
|
|
||||||
use crate::packets::srv_channel_list_reply::{ChannelInfo, SrvChannelListReply};
|
|
||||||
use crate::packets::srv_join_server_reply::SrvJoinServerReply;
|
|
||||||
use crate::packets::srv_login_reply::{ServerInfo, SrvLoginReply};
|
|
||||||
use crate::packets::srv_logout_reply::SrvLogoutReply;
|
|
||||||
use crate::packets::srv_srv_select_reply::SrvSrvSelectReply;
|
|
||||||
use crate::packets::*;
|
use crate::packets::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -26,9 +16,11 @@ use tracing::{debug, error, info, warn};
|
|||||||
use utils::null_string::NullTerminatedString;
|
use utils::null_string::NullTerminatedString;
|
||||||
use utils::service_discovery;
|
use utils::service_discovery;
|
||||||
use utils::service_discovery::{get_kube_service_endpoints_by_dns, get_service_info};
|
use utils::service_discovery::{get_kube_service_endpoints_by_dns, get_service_info};
|
||||||
|
use crate::handlers::chat::create_chat_client_handler;
|
||||||
|
use crate::handlers::chat_client::ChatClientHandler;
|
||||||
|
|
||||||
pub(crate) async fn handle_alive_req(
|
pub(crate) async fn handle_alive_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
auth_client: Arc<Mutex<AuthClient>>,
|
auth_client: Arc<Mutex<AuthClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -50,26 +42,30 @@ pub(crate) async fn handle_alive_req(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_accept_req(
|
pub(crate) async fn handle_accept_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::srv_accept_reply::SrvAcceptReply;
|
||||||
let data = SrvAcceptReply {
|
let data = SrvAcceptReply {
|
||||||
result: srv_accept_reply::Result::Accepted,
|
result: srv_accept_reply::Result::Accepted,
|
||||||
rand_value: 0,
|
rand_value: 0,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakssAcceptReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakssAcceptReply, &data)?;
|
||||||
|
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_join_server_req(
|
pub(crate) async fn handle_join_server_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
auth_client: Arc<Mutex<AuthClient>>,
|
auth_client: Arc<Mutex<AuthClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::cli_join_server_token_req::CliJoinServerTokenReq;
|
||||||
|
use crate::packets::srv_join_server_reply::SrvJoinServerReply;
|
||||||
let request = CliJoinServerTokenReq::decode(packet.payload.as_slice());
|
let request = CliJoinServerTokenReq::decode(packet.payload.as_slice());
|
||||||
debug!("{:?}", request);
|
debug!("{:?}", request);
|
||||||
|
|
||||||
@@ -86,7 +82,8 @@ pub(crate) async fn handle_join_server_req(
|
|||||||
pay_flag: 0,
|
pay_flag: 0,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakscJoinServerReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakscJoinServerReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
return Err("Session not valid".into());
|
return Err("Session not valid".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +93,8 @@ pub(crate) async fn handle_join_server_req(
|
|||||||
pay_flag: 0,
|
pay_flag: 0,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakscJoinServerReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakscJoinServerReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("Unable to find connection state".into())
|
Err("Unable to find connection state".into())
|
||||||
@@ -104,12 +102,13 @@ pub(crate) async fn handle_join_server_req(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_logout_req(
|
pub(crate) async fn handle_logout_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
auth_client: Arc<Mutex<AuthClient>>,
|
auth_client: Arc<Mutex<AuthClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::srv_logout_reply::SrvLogoutReply;
|
||||||
if let Some(mut state) = connection_service.get_connection(&connection_id) {
|
if let Some(mut state) = connection_service.get_connection(&connection_id) {
|
||||||
let session_id = state.session_id.clone().unwrap();
|
let session_id = state.session_id.clone().unwrap();
|
||||||
let mut auth_client = auth_client.lock().await;
|
let mut auth_client = auth_client.lock().await;
|
||||||
@@ -117,8 +116,9 @@ pub(crate) async fn handle_logout_req(
|
|||||||
|
|
||||||
let data = SrvLogoutReply { wait_time: 1 };
|
let data = SrvLogoutReply { wait_time: 1 };
|
||||||
let response_packet = Packet::new(PacketType::PakwcLogoutReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcLogoutReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
stream.shutdown().await?;
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
locked_stream.shutdown().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("Unable to find connection state".into())
|
Err("Unable to find connection state".into())
|
||||||
@@ -126,13 +126,14 @@ pub(crate) async fn handle_logout_req(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_login_req(
|
pub(crate) async fn handle_login_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
auth_client: Arc<Mutex<AuthClient>>,
|
auth_client: Arc<Mutex<AuthClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
addr: SocketAddr,
|
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::cli_login_token_req::CliLoginTokenReq;
|
||||||
|
use crate::packets::srv_login_reply::{ServerInfo, SrvLoginReply};
|
||||||
debug!("decoding packet payload of size {}", packet.payload.as_slice().len());
|
debug!("decoding packet payload of size {}", packet.payload.as_slice().len());
|
||||||
let data = CliLoginTokenReq::decode(packet.payload.as_slice())?;
|
let data = CliLoginTokenReq::decode(packet.payload.as_slice())?;
|
||||||
debug!("{:?}", data);
|
debug!("{:?}", data);
|
||||||
@@ -140,6 +141,7 @@ pub(crate) async fn handle_login_req(
|
|||||||
let mut auth_client = auth_client.lock().await;
|
let mut auth_client = auth_client.lock().await;
|
||||||
match auth_client.validate_session(&data.token.0).await {
|
match auth_client.validate_session(&data.token.0).await {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
|
debug!("Response: {:?}", response);
|
||||||
if response.valid == false {
|
if response.valid == false {
|
||||||
info!("Login failed: Invalid credentials");
|
info!("Login failed: Invalid credentials");
|
||||||
|
|
||||||
@@ -150,14 +152,32 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: Vec::new(),
|
servers_info: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
} else {
|
} else {
|
||||||
debug!("Successfully logged in");
|
debug!("Successfully logged in");
|
||||||
|
|
||||||
if let Some(mut state) = connection_service.get_connection_mut(&connection_id) {
|
if let Some(mut state) = connection_service.get_connection_mut(&connection_id) {
|
||||||
debug!("Response: {:?}", response);
|
|
||||||
state.user_id = Some(response.user_id);
|
state.user_id = Some(response.user_id);
|
||||||
state.session_id = Some(response.session_id);
|
state.session_id = Some(response.session_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let chat_url = format!(
|
||||||
|
"http://{}",
|
||||||
|
get_kube_service_endpoints_by_dns("chat-service", "tcp", "chat-service")
|
||||||
|
.await
|
||||||
|
.expect("Failed to get chat service endpoints")
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
let handler = ChatClientHandler::new(chat_url, connection_id.clone(), response.session_id.clone()).await?;
|
||||||
|
let chat_handler = Arc::new(handler);
|
||||||
|
|
||||||
|
create_chat_client_handler(stream.clone(), chat_handler.clone()).await?;
|
||||||
|
|
||||||
|
if let Some(mut state) = connection_service.get_connection_mut(&connection_id) {
|
||||||
|
state.chat_handler = Some(chat_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut id = 0;
|
let mut id = 0;
|
||||||
@@ -196,7 +216,8 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: server_info,
|
servers_info: server_info,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -208,7 +229,8 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: Vec::new(),
|
servers_info: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +249,8 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: Vec::new(),
|
servers_info: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
}
|
}
|
||||||
Code::Unavailable => {
|
Code::Unavailable => {
|
||||||
warn!("Login failed: Service is unavailable");
|
warn!("Login failed: Service is unavailable");
|
||||||
@@ -238,7 +261,8 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: Vec::new(),
|
servers_info: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Unexpected error: {}", tonic_status.message());
|
error!("Unexpected error: {}", tonic_status.message());
|
||||||
@@ -249,7 +273,8 @@ pub(crate) async fn handle_login_req(
|
|||||||
servers_info: Vec::new(),
|
servers_info: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,11 +285,13 @@ pub(crate) async fn handle_login_req(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_server_select_req(
|
pub(crate) async fn handle_server_select_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::cli_srv_select_req::CliSrvSelectReq;
|
||||||
|
use crate::packets::srv_srv_select_reply::SrvSrvSelectReply;
|
||||||
let request = CliSrvSelectReq::decode(packet.payload.as_slice())?;
|
let request = CliSrvSelectReq::decode(packet.payload.as_slice())?;
|
||||||
debug!("{:?}", request);
|
debug!("{:?}", request);
|
||||||
|
|
||||||
@@ -286,14 +313,17 @@ pub(crate) async fn handle_server_select_req(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let response_packet = Packet::new(PacketType::PaklcSrvSelectReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcSrvSelectReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_channel_list_req(
|
pub(crate) async fn handle_channel_list_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::cli_channel_list_req::CliChannelListReq;
|
||||||
|
use crate::packets::srv_channel_list_reply::{ChannelInfo, SrvChannelListReply};
|
||||||
let request = CliChannelListReq::decode(packet.payload.as_slice());
|
let request = CliChannelListReq::decode(packet.payload.as_slice());
|
||||||
debug!("{:?}", request);
|
debug!("{:?}", request);
|
||||||
|
|
||||||
@@ -326,7 +356,8 @@ pub(crate) async fn handle_channel_list_req(
|
|||||||
channels: channel_info,
|
channels: channel_info,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcChannelListReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcChannelListReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -335,7 +366,8 @@ pub(crate) async fn handle_channel_list_req(
|
|||||||
channels: Vec::new(),
|
channels: Vec::new(),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PaklcChannelListReply, &data)?;
|
let response_packet = Packet::new(PacketType::PaklcChannelListReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,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;
|
||||||
|
|
||||||
fn string_to_u32(s: &str) -> u32 {
|
pub(crate) fn string_to_u32(s: &str) -> u32 {
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
s.hash(&mut hasher);
|
s.hash(&mut hasher);
|
||||||
// Convert the 64-bit hash to a 32-bit number.
|
// Convert the 64-bit hash to a 32-bit number.
|
||||||
@@ -68,7 +68,7 @@ pub(crate) fn convert_type_to_body_part(slot: i32) -> ItemType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_char_list_req(
|
pub(crate) async fn handle_char_list_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
character_client: Arc<Mutex<CharacterClient>>,
|
character_client: Arc<Mutex<CharacterClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -91,6 +91,8 @@ pub(crate) async fn handle_char_list_req(
|
|||||||
let character_list = character_client.get_character_list(&user_id).await?;
|
let character_list = character_client.get_character_list(&user_id).await?;
|
||||||
let mut characters = vec![];
|
let mut characters = vec![];
|
||||||
let mut character_id_list: Vec<u32> = Vec::new();
|
let mut character_id_list: Vec<u32> = Vec::new();
|
||||||
|
|
||||||
|
// Build the visible inventory
|
||||||
for character in character_list.characters {
|
for character in character_list.characters {
|
||||||
let mut item_list: [EquippedItem; (MAX_VISIBLE_ITEMS as usize)] =
|
let mut item_list: [EquippedItem; (MAX_VISIBLE_ITEMS as usize)] =
|
||||||
core::array::from_fn(|i| EquippedItem::default());
|
core::array::from_fn(|i| EquippedItem::default());
|
||||||
@@ -128,13 +130,14 @@ pub(crate) async fn handle_char_list_req(
|
|||||||
|
|
||||||
let data = SrvCharListReply { characters };
|
let data = SrvCharListReply { characters };
|
||||||
let response_packet = Packet::new(PacketType::PakccCharListReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakccCharListReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_create_char_req(
|
pub(crate) async fn handle_create_char_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
character_client: Arc<Mutex<CharacterClient>>,
|
character_client: Arc<Mutex<CharacterClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -176,13 +179,14 @@ pub(crate) async fn handle_create_char_req(
|
|||||||
|
|
||||||
let data = SrvCreateCharReply { result, platininum: 0 };
|
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?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_delete_char_req(
|
pub(crate) async fn handle_delete_char_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
character_client: Arc<Mutex<CharacterClient>>,
|
character_client: Arc<Mutex<CharacterClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -218,13 +222,14 @@ pub(crate) async fn handle_delete_char_req(
|
|||||||
name: character_name,
|
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?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_select_char_req(
|
pub(crate) async fn handle_select_char_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
character_client: Arc<Mutex<CharacterClient>>,
|
character_client: Arc<Mutex<CharacterClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -255,7 +260,10 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
ip: NullTerminatedString("".to_string()),
|
ip: NullTerminatedString("".to_string()),
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakccSwitchServer, &data)?;
|
let response_packet = Packet::new(PacketType::PakccSwitchServer, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
{
|
||||||
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut character_client = character_client.lock().await;
|
let mut character_client = character_client.lock().await;
|
||||||
let character_data = character_client
|
let character_data = character_client
|
||||||
@@ -275,12 +283,14 @@ 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)] = [0u16; MAX_SKILL_COUNT as usize];
|
|
||||||
|
|
||||||
|
// Build the character learned skill list
|
||||||
|
let mut skill_list: [u16; (MAX_SKILL_COUNT as usize)] = [0u16; MAX_SKILL_COUNT as usize];
|
||||||
for index in 0..skills.len() {
|
for index in 0..skills.len() {
|
||||||
skill_list[index] = skills[index].id as u16;
|
skill_list[index] = skills[index].id as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the character inventory list
|
||||||
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_type_to_body_part(item.slot) as isize - 2;
|
let slot = convert_type_to_body_part(item.slot) as isize - 2;
|
||||||
@@ -318,8 +328,8 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
let data = SrvSelectCharReply {
|
let data = SrvSelectCharReply {
|
||||||
race: looks.race as u8,
|
race: looks.race as u8,
|
||||||
map: position.map_id as u16,
|
map: position.map_id as u16,
|
||||||
x: position.x * 100.0,
|
x: position.x,
|
||||||
y: position.y * 100.0,
|
y: position.y,
|
||||||
spawn: position.spawn_id as u16,
|
spawn: position.spawn_id as u16,
|
||||||
body_face: looks.face as u32,
|
body_face: looks.face as u32,
|
||||||
body_hair: looks.hair as u32,
|
body_hair: looks.hair as u32,
|
||||||
@@ -362,7 +372,10 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
name,
|
name,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcSelectCharReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
{
|
||||||
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
}
|
||||||
|
|
||||||
// here we build the inventory
|
// here we build the inventory
|
||||||
let data = SrvInventoryData {
|
let data = SrvInventoryData {
|
||||||
@@ -370,7 +383,10 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
items: inventory,
|
items: inventory,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcInventoryData, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcInventoryData, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
{
|
||||||
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
}
|
||||||
|
|
||||||
// Now we need to build the Quest data
|
// Now we need to build the Quest data
|
||||||
let mut quests: [srv_quest_data::Quest; (MAX_QUESTS as usize)] =
|
let mut quests: [srv_quest_data::Quest; (MAX_QUESTS as usize)] =
|
||||||
@@ -388,15 +404,21 @@ pub(crate) async fn handle_select_char_req(
|
|||||||
wishlist,
|
wishlist,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcQuestData, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcQuestData, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
{
|
||||||
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
}
|
||||||
|
|
||||||
// Send the billing message (we don't actually use this so we just send the defaults to allow)
|
// Send the billing message (we don't use this, so we just send the defaults to allow)
|
||||||
let data = SrvBillingMessage {
|
let data = SrvBillingMessage {
|
||||||
function_type: 0x1001,
|
function_type: 0x1001,
|
||||||
pay_flag: 2,
|
pay_flag: 2,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcBillingMessage, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcBillingMessage, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
{
|
||||||
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
136
packet-service/src/handlers/chat.rs
Normal file
136
packet-service/src/handlers/chat.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use crate::character_client::CharacterClient;
|
||||||
|
use crate::connection_service::ConnectionService;
|
||||||
|
use crate::packet::{send_packet, Packet, PacketPayload};
|
||||||
|
use crate::packet_type::PacketType;
|
||||||
|
use chrono::{Local, Timelike};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tonic::transport::Channel;
|
||||||
|
use tracing::{debug, error};
|
||||||
|
use utils::null_string::NullTerminatedString;
|
||||||
|
use utils::service_discovery::get_kube_service_endpoints_by_dns;
|
||||||
|
use crate::handlers::chat_client::chat::{ChatMessage, MessageType};
|
||||||
|
use crate::handlers::chat_client::ChatClientHandler;
|
||||||
|
|
||||||
|
pub async fn create_chat_client_handler(
|
||||||
|
stream_for_task: Arc<Mutex<TcpStream>>,
|
||||||
|
task_chat_handler: Arc<ChatClientHandler>,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::srv_normal_chat::SrvNormalChat;
|
||||||
|
use crate::packets::srv_shout_chat::SrvShoutChat;
|
||||||
|
use crate::packets::srv_party_chat::SrvPartyChat;
|
||||||
|
use crate::packets::srv_whisper_chat::SrvWhisperChat;
|
||||||
|
use crate::packets::srv_clan_chat::SrvClanChat;
|
||||||
|
use crate::packets::srv_allied_chat::SrvAlliedChat;
|
||||||
|
tokio::spawn({
|
||||||
|
async move {
|
||||||
|
debug!("Spawning chat handler task");
|
||||||
|
loop {
|
||||||
|
let mut rx = task_chat_handler.inbound_rx.lock().await;
|
||||||
|
while let Some(chat_msg) = rx.recv().await {
|
||||||
|
debug!("Packet-Service received chat message: {} (client_id: {}, type {})", chat_msg.message, chat_msg.client_id, chat_msg.r#type);
|
||||||
|
|
||||||
|
debug!("Locking stream");
|
||||||
|
let mut locked_stream = stream_for_task.lock().await;
|
||||||
|
debug!("Locked stream");
|
||||||
|
match chat_msg.r#type {
|
||||||
|
1 => {
|
||||||
|
// Normal Chat
|
||||||
|
let data = SrvNormalChat {
|
||||||
|
char_id: chat_msg.client_id.parse().unwrap_or(696),
|
||||||
|
message: NullTerminatedString(chat_msg.message),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the packet to the client
|
||||||
|
let response_packet = Packet::new(PacketType::PakwcNormalChat, &data);
|
||||||
|
debug!("Attempting to send normal chat to client");
|
||||||
|
if let Err(e) = send_packet(&mut locked_stream, &response_packet.unwrap()).await
|
||||||
|
{
|
||||||
|
error!("unable to send normal chat: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// Shout Chat
|
||||||
|
let data = SrvShoutChat {
|
||||||
|
sender: Default::default(),
|
||||||
|
message: NullTerminatedString(chat_msg.message),
|
||||||
|
};
|
||||||
|
let response_packet = Packet::new(PacketType::PakwcShoutChat, &data);
|
||||||
|
debug!("Attempting to send shout chat to client");
|
||||||
|
if let Err(e) = send_packet(&mut locked_stream, &response_packet.unwrap()).await
|
||||||
|
{
|
||||||
|
error!("unable to send shout chat: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
// Party Chat
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
// Whisper Chat
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
// Clan Chat
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
// Allied Chat
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Normal Chat
|
||||||
|
let data = SrvNormalChat {
|
||||||
|
char_id: 0,
|
||||||
|
message: NullTerminatedString(chat_msg.message),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the packet to the client
|
||||||
|
let response_packet = Packet::new(PacketType::PakwcNormalChat, &data);
|
||||||
|
if let Err(e) = send_packet(&mut locked_stream, &response_packet.unwrap()).await
|
||||||
|
{
|
||||||
|
error!("unable to send normal chat: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!("Chat handler task exiting");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn handle_normal_chat(
|
||||||
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
|
packet: Packet,
|
||||||
|
connection_service: Arc<ConnectionService>,
|
||||||
|
connection_id: String,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
use crate::packets::cli_normal_chat::*;
|
||||||
|
use crate::packets::srv_normal_chat::*;
|
||||||
|
let request = CliNormalChat::decode(packet.payload.as_slice())?;
|
||||||
|
debug!("{:?}", request);
|
||||||
|
|
||||||
|
if let Some(mut state) = connection_service.get_connection(&connection_id) {
|
||||||
|
let user_id = state.user_id.clone().expect("Missing user id in connection state");
|
||||||
|
let message = ChatMessage {
|
||||||
|
client_id: crate::handlers::character::string_to_u32(&user_id).to_string(),
|
||||||
|
r#type: MessageType::Normal as i32,
|
||||||
|
message: request.message.clone().0,
|
||||||
|
target_id: "".to_string(),
|
||||||
|
};
|
||||||
|
state.chat_handler.unwrap().send_message(message).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// We're not sending here because we should get a message back from the chat service
|
||||||
|
// let data = SrvNormalChat {
|
||||||
|
// char_id: 0,
|
||||||
|
// message: request.message,
|
||||||
|
// };
|
||||||
|
// let response_packet = Packet::new(PacketType::PakwcNormalChat, &data)?;
|
||||||
|
// let mut locked_stream = stream.lock().await;
|
||||||
|
// send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
91
packet-service/src/handlers/chat_client.rs
Normal file
91
packet-service/src/handlers/chat_client.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use tonic::{Request, transport::Channel};
|
||||||
|
use futures::StreamExt;
|
||||||
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub mod chat {
|
||||||
|
tonic::include_proto!("chat");
|
||||||
|
}
|
||||||
|
|
||||||
|
use chat::chat_service_client::ChatServiceClient;
|
||||||
|
use chat::ChatMessage;
|
||||||
|
use crate::interceptors::auth_interceptor::AuthInterceptor;
|
||||||
|
|
||||||
|
/// ChatClientHandler encapsulates the bidirectional chat stream.
|
||||||
|
/// In addition to providing an API to send messages, it also spawns a
|
||||||
|
/// background task which forwards incoming chat messages through an inbound channel.
|
||||||
|
pub struct ChatClientHandler {
|
||||||
|
outbound_tx: mpsc::Sender<ChatMessage>,
|
||||||
|
/// Inbound messages from the chat service are sent here.
|
||||||
|
pub inbound_rx: Mutex<mpsc::Receiver<ChatMessage>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatClientHandler {
|
||||||
|
/// Creates and returns a new ChatClientHandler.
|
||||||
|
///
|
||||||
|
/// * `chat_url` - Full URL of the Chat Service (for example, "http://127.0.0.1:50051")
|
||||||
|
/// * `client_id` - The authenticated client ID to be injected into each request.
|
||||||
|
/// * `session_id` - The authenticated session token to be injected into each request.
|
||||||
|
pub async fn new(
|
||||||
|
chat_url: String,
|
||||||
|
client_id: String,
|
||||||
|
session_id: String,
|
||||||
|
) -> Result<Self, Box<dyn Error + Send + Sync>> {
|
||||||
|
// Create a channel to the Chat Service.
|
||||||
|
let channel = Channel::from_shared(chat_url)?.connect().await
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)?;
|
||||||
|
let interceptor = AuthInterceptor { client_id, session_id };
|
||||||
|
|
||||||
|
// Create ChatService client with interceptor.
|
||||||
|
let mut chat_client = ChatServiceClient::with_interceptor(channel, interceptor);
|
||||||
|
|
||||||
|
// Create an mpsc channel for outbound messages.
|
||||||
|
let (out_tx, out_rx) = mpsc::channel(32);
|
||||||
|
let outbound_stream = ReceiverStream::new(out_rx);
|
||||||
|
|
||||||
|
// This channel will be used to forward inbound messages to the packet-service.
|
||||||
|
let (in_tx, in_rx) = mpsc::channel(32);
|
||||||
|
|
||||||
|
// Establish the bidirectional chat stream.
|
||||||
|
let request = Request::new(outbound_stream);
|
||||||
|
let mut response = chat_client.chat_stream(request).await
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)?.into_inner();
|
||||||
|
|
||||||
|
// Spawn a task to continuously receive messages from the Chat Service.
|
||||||
|
// Each received message is sent through the 'in_tx' channel.
|
||||||
|
tokio::spawn(async move {
|
||||||
|
while let Some(result) = response.next().await {
|
||||||
|
match result {
|
||||||
|
Ok(chat_msg) => {
|
||||||
|
// You might translate or process the chat_msg here,
|
||||||
|
// then forward it to your packet-service logic.
|
||||||
|
if let Err(e) = in_tx.send(chat_msg).await {
|
||||||
|
eprintln!("Failed to forward chat message: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error receiving chat stream message: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Chat inbound stream closed");
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
outbound_tx: out_tx,
|
||||||
|
inbound_rx: Mutex::new(in_rx),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a chat message to the Chat Service.
|
||||||
|
pub async fn send_message(
|
||||||
|
&self,
|
||||||
|
message: ChatMessage,
|
||||||
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
|
self.outbound_tx.send(message).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod character;
|
pub mod character;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
pub mod chat;
|
||||||
|
pub mod chat_client;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use std::error::Error;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
use tonic::transport::Channel;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
use utils::service_discovery::get_kube_service_endpoints_by_dns;
|
||||||
|
|
||||||
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> u16 {
|
fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> u16 {
|
||||||
let dist = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();
|
let dist = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();
|
||||||
@@ -15,7 +17,7 @@ fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> u16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_change_map_req(
|
pub(crate) async fn handle_change_map_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
character_client: Arc<Mutex<CharacterClient>>,
|
character_client: Arc<Mutex<CharacterClient>>,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
@@ -64,13 +66,14 @@ pub(crate) async fn handle_change_map_req(
|
|||||||
team_number: 10,
|
team_number: 10,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcChangeMapReply, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcChangeMapReply, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_mouse_cmd_req(
|
pub(crate) async fn handle_mouse_cmd_req(
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
connection_service: Arc<ConnectionService>,
|
connection_service: Arc<ConnectionService>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
@@ -96,6 +99,7 @@ pub(crate) async fn handle_mouse_cmd_req(
|
|||||||
z: request.z,
|
z: request.z,
|
||||||
};
|
};
|
||||||
let response_packet = Packet::new(PacketType::PakwcMouseCmd, &data)?;
|
let response_packet = Packet::new(PacketType::PakwcMouseCmd, &data)?;
|
||||||
send_packet(stream, &response_packet).await?;
|
let mut locked_stream = stream.lock().await;
|
||||||
|
send_packet(&mut locked_stream, &response_packet).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
20
packet-service/src/interceptors/auth_interceptor.rs
Normal file
20
packet-service/src/interceptors/auth_interceptor.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use tonic::{Request, Status, service::Interceptor};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AuthInterceptor {
|
||||||
|
pub client_id: String,
|
||||||
|
pub session_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interceptor for AuthInterceptor {
|
||||||
|
fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
|
||||||
|
// Attach the authenticated client ID into the metadata.
|
||||||
|
request
|
||||||
|
.metadata_mut()
|
||||||
|
.insert("x-client-id", self.client_id.parse().unwrap());
|
||||||
|
request
|
||||||
|
.metadata_mut()
|
||||||
|
.insert("x-session-id", self.session_id.parse().unwrap());
|
||||||
|
Ok(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packet-service/src/interceptors/mod.rs
Normal file
1
packet-service/src/interceptors/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod auth_interceptor;
|
||||||
@@ -5,3 +5,7 @@ pub mod connection_state;
|
|||||||
pub mod metrics;
|
pub mod metrics;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod packet_type;
|
pub mod packet_type;
|
||||||
|
pub mod handlers {
|
||||||
|
pub mod chat_client;
|
||||||
|
}
|
||||||
|
pub mod interceptors;
|
||||||
|
|||||||
@@ -39,18 +39,19 @@ mod packet_type;
|
|||||||
mod packets;
|
mod packets;
|
||||||
mod router;
|
mod router;
|
||||||
mod types;
|
mod types;
|
||||||
|
mod interceptors;
|
||||||
|
|
||||||
pub mod common {
|
pub mod common {
|
||||||
tonic::include_proto!("common");
|
tonic::include_proto!("common");
|
||||||
}
|
}
|
||||||
pub mod auth {
|
pub mod auth {
|
||||||
tonic::include_proto!("auth"); // Path matches the package name in auth.proto
|
tonic::include_proto!("auth");
|
||||||
}
|
}
|
||||||
pub mod character_common {
|
pub mod character_common {
|
||||||
tonic::include_proto!("character_common"); // Path matches the package name in auth.proto
|
tonic::include_proto!("character_common");
|
||||||
}
|
}
|
||||||
pub mod character {
|
pub mod character {
|
||||||
tonic::include_proto!("character"); // Path matches the package name in auth.proto
|
tonic::include_proto!("character");
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUFFER_POOL_SIZE: usize = 1000;
|
const BUFFER_POOL_SIZE: usize = 1000;
|
||||||
@@ -110,13 +111,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let pool = buffer_pool.clone();
|
let pool = buffer_pool.clone();
|
||||||
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
||||||
|
let stream = Arc::new(Mutex::new(socket));
|
||||||
|
|
||||||
// Spawn a new task for each connection
|
// Spawn a new task for each connection
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _permit = permit;
|
let _permit = permit;
|
||||||
let connection_id = packet_router.connection_service.add_connection();
|
let connection_id = packet_router.connection_service.add_connection();
|
||||||
if let Err(e) = packet_router
|
if let Err(e) = packet_router
|
||||||
.handle_connection(&mut socket, pool, connection_id.clone())
|
.handle_connection(stream, pool, connection_id.clone())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
error!("Error handling connection: {}", e);
|
error!("Error handling connection: {}", e);
|
||||||
|
|||||||
@@ -23,24 +23,30 @@ pub struct PacketRouter {
|
|||||||
impl PacketRouter {
|
impl PacketRouter {
|
||||||
pub async fn handle_connection(
|
pub async fn handle_connection(
|
||||||
&self,
|
&self,
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
pool: Arc<BufferPool>,
|
pool: Arc<BufferPool>,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
ACTIVE_CONNECTIONS.inc();
|
ACTIVE_CONNECTIONS.inc();
|
||||||
while let Some(mut buffer) = pool.acquire().await {
|
while let Some(mut buffer) = pool.acquire().await {
|
||||||
// Read data into the buffer
|
// Read data into the buffer
|
||||||
let mut header_handle = stream.take(6);
|
let packet_size: usize;
|
||||||
let n = header_handle.read(&mut buffer).await?;
|
{
|
||||||
if n == 0 {
|
let mut locked_stream = stream.lock().await;
|
||||||
break; // Connection closed
|
locked_stream.read_exact(&mut buffer[..6]).await?;
|
||||||
}
|
// let mut header_handle = locked_stream.take(6);
|
||||||
let packet_size = u16::from_le_bytes(buffer[0..2].try_into()?) as usize;
|
// let n = header_handle.read(&mut buffer).await?;
|
||||||
|
// if n == 0 {
|
||||||
|
// break; // Connection closed
|
||||||
|
// }
|
||||||
|
packet_size = u16::from_le_bytes(buffer[0..2].try_into()?) as usize;
|
||||||
if packet_size > 6 {
|
if packet_size > 6 {
|
||||||
let mut body_handle = stream.take((packet_size - 6) as u64);
|
locked_stream.read_exact(&mut buffer[6..packet_size]).await?;
|
||||||
let n = body_handle.read(&mut buffer[6..]).await?;
|
// let mut body_handle = locked_stream.take((packet_size - 6) as u64);
|
||||||
if n == 0 {
|
// let n = body_handle.read(&mut buffer[6..]).await?;
|
||||||
break; // Connection closed
|
// if n == 0 {
|
||||||
|
// break; // Connection closed
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +58,7 @@ impl PacketRouter {
|
|||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
debug!("Parsed Packet: {:?}", packet);
|
debug!("Parsed Packet: {:?}", packet);
|
||||||
// Handle the parsed packet (route it, process it, etc.)
|
// Handle the parsed packet (route it, process it, etc.)
|
||||||
self.route_packet(stream, packet, connection_id.clone()).await?;
|
self.route_packet(stream.clone(), packet, connection_id.clone()).await?;
|
||||||
}
|
}
|
||||||
Err(e) => warn!("Failed to parse packet: {}", e),
|
Err(e) => warn!("Failed to parse packet: {}", e),
|
||||||
}
|
}
|
||||||
@@ -67,7 +73,8 @@ impl PacketRouter {
|
|||||||
let mut auth_client = self.auth_client.lock().await;
|
let mut auth_client = self.auth_client.lock().await;
|
||||||
auth_client.logout(&session_id).await?;
|
auth_client.logout(&session_id).await?;
|
||||||
} else {
|
} else {
|
||||||
warn!("No session found for {}", stream.peer_addr()?);
|
let mut locked_stream = stream.lock().await;
|
||||||
|
warn!("No session found for {}", locked_stream.peer_addr()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ACTIVE_CONNECTIONS.dec();
|
ACTIVE_CONNECTIONS.dec();
|
||||||
@@ -77,7 +84,7 @@ impl PacketRouter {
|
|||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub async fn route_packet(
|
pub async fn route_packet(
|
||||||
&self,
|
&self,
|
||||||
stream: &mut TcpStream,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
@@ -88,7 +95,7 @@ impl PacketRouter {
|
|||||||
PacketType::PakcsAcceptReq => auth::handle_accept_req(stream, packet).await,
|
PacketType::PakcsAcceptReq => auth::handle_accept_req(stream, packet).await,
|
||||||
PacketType::PakcsJoinServerTokenReq => auth::handle_join_server_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id).await,
|
PacketType::PakcsJoinServerTokenReq => auth::handle_join_server_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id).await,
|
||||||
// Login Packets
|
// Login Packets
|
||||||
PacketType::PakcsLoginTokenReq => auth::handle_login_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id, stream.peer_addr()?).await,
|
PacketType::PakcsLoginTokenReq => auth::handle_login_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id).await,
|
||||||
PacketType::PakcsLogoutReq => auth::handle_logout_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id).await,
|
PacketType::PakcsLogoutReq => auth::handle_logout_req(stream, packet, self.auth_client.clone(), self.connection_service.clone(), connection_id).await,
|
||||||
PacketType::PakcsSrvSelectReq => auth::handle_server_select_req(stream, packet, self.connection_service.clone(), connection_id).await,
|
PacketType::PakcsSrvSelectReq => auth::handle_server_select_req(stream, packet, self.connection_service.clone(), connection_id).await,
|
||||||
PacketType::PakcsChannelListReq => auth::handle_channel_list_req(stream, packet).await,
|
PacketType::PakcsChannelListReq => auth::handle_channel_list_req(stream, packet).await,
|
||||||
@@ -103,6 +110,8 @@ impl PacketRouter {
|
|||||||
PacketType::PakcsChangeMapReq => world::handle_change_map_req(stream, packet, self.character_client.clone(), self.connection_service.clone(), connection_id).await,
|
PacketType::PakcsChangeMapReq => world::handle_change_map_req(stream, packet, self.character_client.clone(), self.connection_service.clone(), connection_id).await,
|
||||||
PacketType::PakcsMouseCmd => world::handle_mouse_cmd_req(stream, packet, self.connection_service.clone(), connection_id).await,
|
PacketType::PakcsMouseCmd => world::handle_mouse_cmd_req(stream, packet, self.connection_service.clone(), connection_id).await,
|
||||||
|
|
||||||
|
// Chat Packets
|
||||||
|
PacketType::PakcsNormalChat => chat::handle_normal_chat(stream, packet, self.connection_service.clone(), connection_id).await,
|
||||||
// 1 => chat::handle_chat(packet).await?,
|
// 1 => chat::handle_chat(packet).await?,
|
||||||
// 2 => movement::handle_movement(packet).await?,
|
// 2 => movement::handle_movement(packet).await?,
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package chat;
|
|||||||
import "common.proto";
|
import "common.proto";
|
||||||
|
|
||||||
service ChatService {
|
service ChatService {
|
||||||
rpc SendMessage(ChatMessage) returns (common.Empty);
|
rpc ChatStream(stream ChatMessage) returns (stream ChatMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
@@ -19,7 +19,8 @@ enum MessageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message ChatMessage {
|
message ChatMessage {
|
||||||
MessageType type = 1;
|
string client_id = 1;
|
||||||
string message = 2;
|
MessageType type = 2;
|
||||||
string target = 3;
|
string message = 3;
|
||||||
|
string target_id = 4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,19 @@ import os
|
|||||||
|
|
||||||
# Define your images, tags, and Dockerfile paths
|
# Define your images, tags, and Dockerfile paths
|
||||||
images = [
|
images = [
|
||||||
# "api-service",
|
|
||||||
"auth-service",
|
"auth-service",
|
||||||
|
"chat-service",
|
||||||
"character-service",
|
"character-service",
|
||||||
"database-service",
|
"database-service",
|
||||||
"packet-service",
|
"packet-service",
|
||||||
# "session-service",
|
|
||||||
"world-service"
|
"world-service"
|
||||||
]
|
]
|
||||||
dockerfile_paths = [
|
dockerfile_paths = [
|
||||||
# "../api-service/Dockerfile",
|
|
||||||
"../auth-service/Dockerfile",
|
"../auth-service/Dockerfile",
|
||||||
|
"../chat-service/Dockerfile",
|
||||||
"../character-service/Dockerfile",
|
"../character-service/Dockerfile",
|
||||||
"../database-service/Dockerfile",
|
"../database-service/Dockerfile",
|
||||||
"../packet-service/Dockerfile",
|
"../packet-service/Dockerfile",
|
||||||
# "../session-service/Dockerfile",
|
|
||||||
"../world-service/Dockerfile",
|
"../world-service/Dockerfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub async fn get_kube_service_endpoints_by_dns(
|
|||||||
service_protocol: &str,
|
service_protocol: &str,
|
||||||
service_name: &str,
|
service_name: &str,
|
||||||
) -> Result<Vec<SocketAddr>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<SocketAddr>, Box<dyn std::error::Error>> {
|
||||||
|
debug!("Looking up service '{}'", service_name);
|
||||||
let (config, options) = read_system_conf()?;
|
let (config, options) = read_system_conf()?;
|
||||||
let resolver = TokioAsyncResolver::tokio(config, options);
|
let resolver = TokioAsyncResolver::tokio(config, options);
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ pub async fn get_kube_service_endpoints_by_dns(
|
|||||||
))?);
|
))?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debug!("Got endpoints: {:?}", endpoints);
|
||||||
Ok(endpoints)
|
Ok(endpoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.get(0)
|
.get(0)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
let chat_service = format!(
|
||||||
|
"http://{}",
|
||||||
|
get_kube_service_endpoints_by_dns("chat-service", "tcp", "chat-service")
|
||||||
|
.await?
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
// Register service with Consul
|
// Register service with Consul
|
||||||
health_check::start_health_check(addr.as_str()).await?;
|
health_check::start_health_check(addr.as_str()).await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user