More work.
Added chat service Updated packet service to pass the tcp stream around in a Arc type. Updated character position data to not require multiplying the coords Added more debug logs Added an interceptor for gRPC comms with the chat server Updated build and push script for the chat server changes
This commit is contained in:
@@ -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