- add: utils library

- add: packet-service to handle game client packets
- fix: health check for database-service
- fix: health check for auth-service
This commit is contained in:
2024-12-09 23:10:26 -05:00
parent 20e86dd117
commit e5c961d1b4
25 changed files with 1176 additions and 120 deletions

View File

@@ -3,5 +3,6 @@ members = [
"auth-service",
"database-service",
"packet-service",
"utils",
]
resolver = "2"

View File

@@ -16,7 +16,7 @@ serde = { version = "1.0", features = ["derive"] }
dotenv = "0.15"
tracing = "0.1"
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "chrono"] }
prost = "0.13.3"
prost = "0.13.4"
prost-types = "0.13.3"
chrono = { version = "0.4.38", features = ["serde"] }
async-trait = "0.1.83"
@@ -24,6 +24,7 @@ mockall = "0.13.1"
rand = "0.8.5"
warp = "0.3.7"
reqwest = { version = "0.12.9", features = ["json"] }
utils = { path = "../utils" }
[build-dependencies]
tonic-build = "0.12.3"

View File

@@ -1,4 +1,3 @@
use crate::service_discovery::get_service_address;
use auth_service::auth::auth_service_server::AuthServiceServer;
use auth_service::database_client::DatabaseClient;
use auth_service::database_client::DatabaseClientTrait;
@@ -11,9 +10,8 @@ use tokio::{select, signal};
use tonic::transport::Server;
use tracing::{info, Level};
use warp::Filter;
mod consul_registration;
mod service_discovery;
use utils::consul_registration;
use utils::service_discovery::get_service_address;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -27,13 +25,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set the gRPC server address
let addr = env::var("AUTH_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
let port = env::var("AUTH_SERVICE_PORT").unwrap_or_else(|_| "50051".to_string());
let health_port = env::var("HEALTH_CHECK_PORT").unwrap_or_else(|_| "8081".to_string());
let consul_url = env::var("CONSUL_URL").unwrap_or_else(|_| "http://127.0.0.1:8500".to_string());
let service_name = env::var("SERVICE_NAME").unwrap_or_else(|_| "auth-service".to_string());
let service_address = addr.as_str();
let service_port = port.clone();
let health_check_url = format!("http://{}:{}/health", service_address, service_port);
let health_check_endpoint_addr = format!("{}:8081", service_address);
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
let health_check_endpoint_addr = format!("{}:{}", service_address, health_port);
let db_address = get_service_address(&consul_url, "database-service").await?;
// Register service with Consul

View File

@@ -24,6 +24,7 @@ async-trait = "0.1.83"
mockall = "0.13.1"
reqwest = { version = "0.12.9", features = ["json"] }
warp = "0.3.7"
utils = { path = "../utils" }
[build-dependencies]
tonic-build = "0.12.3"

View File

@@ -1,59 +0,0 @@
use reqwest::Client;
use serde::Serialize;
#[derive(Serialize)]
struct ConsulRegistration {
name: String,
address: String,
port: u16,
check: ConsulCheck,
}
#[derive(Serialize)]
struct ConsulCheck {
http: String,
interval: String,
}
pub async fn register_service(
consul_url: &str,
service_name: &str,
service_address: &str,
service_port: u16,
health_check_url: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let registration = ConsulRegistration {
name: service_name.to_string(),
address: service_address.to_string(),
port: service_port,
check: ConsulCheck {
http: health_check_url.to_string(),
interval: "10s".to_string(), // Health check interval
},
};
let client = Client::new();
let consul_register_url = format!("{}/v1/agent/service/register", consul_url);
client
.put(&consul_register_url)
.json(&registration)
.send()
.await?
.error_for_status()?; // Ensure response is successful
Ok(())
}
pub async fn deregister_service(consul_url: &str, service_name: &str) -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let consul_deregister_url = format!("{}/v1/agent/service/deregister/{}", consul_url, service_name);
client
.put(&consul_deregister_url)
.send()
.await?
.error_for_status()?; // Ensure response is successful
Ok(())
}

View File

@@ -9,12 +9,11 @@ use std::env;
use std::net::ToSocketAddrs;
use std::str::FromStr;
use std::sync::Arc;
use tokio::{select, signal};
use tokio::{select, signal};
use tonic::transport::Server;
use tracing::{info, Level};
use warp::Filter;
mod consul_registration;
use utils::consul_registration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -25,6 +24,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = env::var("DATABASE_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
let port = env::var("DATABASE_SERVICE_PORT").unwrap_or_else(|_| "50052".to_string());
let health_port = env::var("HEALTH_CHECK_PORT").unwrap_or_else(|_| "8080".to_string());
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let redis_url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379".to_string());
@@ -32,8 +33,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let service_name = env::var("SERVICE_NAME").unwrap_or_else(|_| "database-service".to_string());
let service_address = addr.as_str();
let service_port = port.clone();
let health_check_url = format!("http://{}:{}/health", service_address, service_port);
let health_check_endpoint_addr = format!("{}:8080", service_address);
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
let health_check_endpoint_addr = format!("{}:{}", service_address, health_port);
// Register service with Consul
consul_registration::register_service(

View File

@@ -14,4 +14,15 @@ serde = { version = "1.0", features = ["derive"] }
bytes = { version = "1.8.0", features = ["std", "serde"] }
tracing = "0.1"
tracing-subscriber = "0.3.18"
bincode = { version = "2.0.0-rc.3", features = ["derive", "serde"] }
thiserror = "2.0.3"
lazy_static = "1.5.0"
prometheus = "0.13.4"
hyper = { version = "1.5.1", features = ["server"] }
tonic = "0.12.3"
prost = "0.13.4"
utils = { path = "../utils" }
warp = "0.3.7"
[build-dependencies]
tonic-build = "0.12.3"

9
packet-service/build.rs Normal file
View File

@@ -0,0 +1,9 @@
fn main() {
// gRPC Client code
tonic_build::configure()
.build_server(false) // Generate gRPC client code
.compile_well_known_types(true)
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
.compile_protos(&["../proto/auth.proto"], &["../proto"])
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}

View File

@@ -0,0 +1,24 @@
use crate::auth::auth_service_client::AuthServiceClient;
use crate::auth::{LoginRequest, LoginResponse};
use tonic::transport::Channel;
pub struct AuthClient {
client: AuthServiceClient<Channel>,
}
impl AuthClient {
pub async fn connect(endpoint: &str) -> Result<Self, Box<dyn std::error::Error>> {
let client = AuthServiceClient::connect(endpoint.to_string()).await?;
Ok(AuthClient { client })
}
pub async fn login(&mut self, username: &str, password: &str) -> Result<LoginResponse, Box<dyn std::error::Error + Send + Sync>> {
let request = LoginRequest {
username: username.to_string(),
password: password.to_string(),
};
let response = self.client.login(request).await?;
Ok(response.into_inner())
}
}

View File

@@ -0,0 +1,31 @@
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::{Semaphore, Mutex};
const MAX_PACKET_SIZE: usize = 1024;
pub struct BufferPool {
buffers: Mutex<VecDeque<Vec<u8>>>,
sem: Semaphore,
}
impl BufferPool {
pub fn new(pool_size: usize) -> Arc<Self> {
Arc::new(Self {
buffers: Mutex::new((0..pool_size).map(|_| vec![0u8; MAX_PACKET_SIZE]).collect()),
sem: Semaphore::new(pool_size),
})
}
pub async fn acquire(&self) -> Option<Vec<u8>> {
let _ = self.sem.acquire().await.ok()?;
let mut buffers = self.buffers.lock().await;
buffers.pop_front()
}
pub async fn release(&self, buffer: Vec<u8>) {
let mut buffers = self.buffers.lock().await;
buffers.push_back(buffer);
self.sem.add_permits(1);
}
}

View File

@@ -0,0 +1,72 @@
use crate::enums::{EquippedPosition, BulletType, RidingItem};
pub(crate) const MIN_SELL_TYPE: u32 = 1;
pub(crate) const MAX_SELL_TYPE: u32 = 11;
pub(crate) const MAX_STAT: u32 = 300;
pub(crate) const MAX_STACK: u32 = 999;
pub(crate) const MAX_UNION_COUNT: u32 = 10;
pub(crate) const MAX_BUFF_STATUS: u32 = 40;
pub(crate) const MAX_SKILL_COUNT: u32 = 120;
pub(crate) const MAX_HOTBAR_ITEMS: u32 = 32;
pub(crate) const MAX_DAMAGE: u32 = 99_999_999;
pub(crate) const WALK_SPEED: u32 = 200;
pub(crate) const BASE_MOVE_SPEED: u32 = 425;
pub(crate) const DAMAGE_ACTION_IMMEDIATE: u32 = 0x02;
pub(crate) const DAMAGE_ACTION_HIT: u32 = 0x04;
pub(crate) const DAMAGE_ACTION_CRITICAL: u32 = 0x08;
pub(crate) const DAMAGE_ACTION_DEAD: u32 = 0x10;
pub(crate) const MAX_CONDITIONS_EPISODE: u32 = 5;
pub(crate) const MAX_CONDITIONS_JOB: u32 = 3;
pub(crate) const MAX_CONDITIONS_PLANET: u32 = 7;
pub(crate) const MAX_CONDITIONS_UNION: u32 = 10;
pub(crate) const MAX_QUESTS: u32 = 10;
pub(crate) const MAX_SWITCHES: u32 = 16;
pub(crate) const MAX_QUEST_SWITCHES: u32 = 32;
pub(crate) const MAX_QUEST_VARS: u32 = 10;
pub(crate) const MAX_QUEST_ITEMS: u32 = 6;
pub(crate) const TAB_SIZE: u8 = 30;
pub(crate) const DROP_RANGE: f32 = 50.0;
pub(crate) const MAX_VISIBLE_ITEMS: u32 = 8;
pub(crate) const MAX_INVENTORY: u32 = 120;
// Assuming BulletType, RidingItem, and EquippedPosition enums are already defined as shown previously
pub(crate) const MAX_ITEMS: u32 = MAX_INVENTORY
+ BulletType::MaxBulletTypes as u32
+ RidingItem::MaxRidingItems as u32
+ EquippedPosition::MaxEquipItems as u32;
pub(crate) const FIRST_BULLET_SLOT: u32 = MAX_INVENTORY + EquippedPosition::MaxEquipItems as u32;
pub(crate) const MAX_STATUS_EFFECTS: u32 = 40;
pub(crate) const MAX_WISHLIST: u32 = 30;
// Classes
pub(crate) const CLASS_VISITOR: u32 = 0;
pub(crate) const CLASS_SOLDIER_111: u32 = 111;
pub(crate) const CLASS_SOLDIER_121: u32 = 121;
pub(crate) const CLASS_SOLDIER_122: u32 = 122;
pub(crate) const CLASS_SOLDIER_131: u32 = 131;
pub(crate) const CLASS_SOLDIER_132: u32 = 132;
pub(crate) const CLASS_MAGICIAN_211: u32 = 211;
pub(crate) const CLASS_MAGICIAN_221: u32 = 221;
pub(crate) const CLASS_MAGICIAN_222: u32 = 222;
pub(crate) const CLASS_MAGICIAN_231: u32 = 231;
pub(crate) const CLASS_MAGICIAN_232: u32 = 232;
pub(crate) const CLASS_MIXER_311: u32 = 311;
pub(crate) const CLASS_MIXER_321: u32 = 321;
pub(crate) const CLASS_MIXER_322: u32 = 322;
pub(crate) const CLASS_MIXER_331: u32 = 331;
pub(crate) const CLASS_MIXER_332: u32 = 332;
pub(crate) const CLASS_MERCHANT_411: u32 = 411;
pub(crate) const CLASS_MERCHANT_421: u32 = 421;
pub(crate) const CLASS_MERCHANT_422: u32 = 422;
pub(crate) const CLASS_MERCHANT_431: u32 = 431;
pub(crate) const CLASS_MERCHANT_432: u32 = 432;

114
packet-service/src/enums.rs Normal file
View File

@@ -0,0 +1,114 @@
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum BulletType {
Arrow = 0,
Bullet = 1,
Throw = 2,
MaxBulletTypes,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RidingItem {
Body = 0,
Engine = 1,
Legs,
Option, // weapon or back seat
Arms,
MaxRidingItems,
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ItemSubType {
Rring = 171,
Nnecklace,
Earring,
OneHSword = 211,
OneHBlunt,
TwoHSword = 221,
Spear = 222,
TwoHAxe = 223,
Bow = 231,
Gun,
Launcher,
Staff = 241,
Wand,
Katar = 251,
DualWield,
Xbow = 271,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum EquippedPosition {
Goggles = 1,
Helmet = 2,
Armor,
Backpack,
Gauntlet,
Boots,
WeaponR,
WeaponL,
Necklace,
Ring,
Earing,
MaxEquipItems,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum MoveMode {
Walk = 0,
Run = 1,
Drive = 2,
RideOn = 4,
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Command {
Stop = 0,
Move = 1,
Attack = 2,
Die = 3,
Pickup = 4,
Skill2Self = 6,
Skill2Obj = 7,
Skill2Pos = 8,
Runaway = 0x8009,
Sit = 10,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ItemType {
None = 0,
ItemGoggles = 1,
ItemHelmet = 2,
ItemArmor = 3,
ItemGauntlet = 4,
ItemBoots = 5,
ItemBackpack = 6,
ItemRing = 7,
ItemWeaponR = 8,
ItemWeaponL = 9,
ItemConsumable = 10,
ItemEtcGem = 11,
ItemEtc = 12,
ItemEtc2 = 13,
ItemRiding = 14,
Zuly = 0x1F,
}
mod party_req {
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Request {
Make = 0,
Join = 1,
Left,
ChangeOwner,
Kick = 0x81,
}
}

View File

@@ -0,0 +1,90 @@
use std::collections::HashMap;
use tonic::{Code, Status};
use std::error::Error;
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::Mutex;
use tracing::{debug, error, info, warn};
use crate::auth_client::AuthClient;
use crate::packet::{send_packet, Packet, PacketPayload};
use crate::packet_type::PacketType;
use crate::packets::cli_accept_req::CliAcceptReq;
use crate::packets::cli_join_server_req::CliJoinServerReq;
use crate::packets::cli_login_req::CliLoginReq;
use crate::packets::{srv_accept_reply, srv_login_reply};
use crate::packets::srv_accept_reply::SrvAcceptReply;
use crate::packets::srv_login_reply::SrvLoginReply;
pub(crate) async fn handle_accept_req(stream: &mut TcpStream, packet: Packet) -> Result<(), Box<dyn Error + Send + Sync>> {
let data = CliAcceptReq::decode(packet.payload.as_slice());
debug!("{:?}", data);
// We need to do reply to this packet
let data = SrvAcceptReply { result: srv_accept_reply::Result::Accepted, rand_value: 0 };
let response_packet = Packet::new(PacketType::PakssAcceptReply, &data)?;
debug!("{:?}", response_packet);
send_packet(stream, &response_packet).await?;
Ok(())
}
pub(crate) async fn handle_join_server_req(stream: &mut TcpStream, packet: Packet) -> Result<(), Box<dyn Error + Send + Sync>> {
let data = CliJoinServerReq::decode(packet.payload.as_slice());
debug!("{:?}", data);
Ok(())
}
pub(crate) async fn handle_login_req(stream: &mut TcpStream, packet: Packet, auth_client: Arc<Mutex<AuthClient>>) -> Result<(), Box<dyn Error + Send + Sync>> {
debug!("decoding packet payload of size {}", packet.payload.as_slice().len());
let data = CliLoginReq::decode(packet.payload.as_slice())?;
debug!("{:?}", data);
let mut auth_client = auth_client.lock().await;
match auth_client.login(&data.username.0, &data.password.password).await {
Ok(response) => {
debug!("successfully logged in");
let data = SrvLoginReply { result: srv_login_reply::Result::Ok, right: 0, type_: 0, servers_info: Vec::new() };
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
send_packet(stream, &response_packet).await?;
}
Err(status) => {
if let Some(tonic_status) = status.downcast_ref::<Status>() {
match tonic_status.code() {
Code::Unauthenticated => {
info!("Login failed: Invalid credentials");
let data = SrvLoginReply { result: srv_login_reply::Result::UnknownAccount, right: 0, type_: 0, servers_info: Vec::new() };
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
send_packet(stream, &response_packet).await?;
}
Code::Unavailable => {
warn!("Login failed: Service is unavailable");
let data = SrvLoginReply { result: srv_login_reply::Result::Failed, right: 0, type_: 0, servers_info: Vec::new() };
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
send_packet(stream, &response_packet).await?;
}
_ => {
error!("Unexpected error: {}", tonic_status.message());
let data = SrvLoginReply { result: srv_login_reply::Result::Failed, right: 0, type_: 0, servers_info: Vec::new() };
let response_packet = Packet::new(PacketType::PaklcLoginReply, &data)?;
send_packet(stream, &response_packet).await?;
}
}
}
}
}
Ok(())
}
pub(crate) async fn handle_server_select_req(stream: &mut TcpStream, packet: Packet) -> Result<(), Box<dyn Error + Send + Sync>> {
let data = CliJoinServerReq::decode(packet.payload.as_slice());
debug!("{:?}", data);
Ok(())
}
pub(crate) async fn handle_channel_list_req(stream: &mut TcpStream, packet: Packet) -> Result<(), Box<dyn Error + Send + Sync>> {
let data = CliJoinServerReq::decode(packet.payload.as_slice());
debug!("{:?}", data);
Ok(())
}

View File

@@ -0,0 +1,2 @@
pub mod auth;
pub mod null_string;

View File

@@ -0,0 +1,43 @@
use bincode::{Decode, Encode, de::Decoder, enc::Encoder, error::{DecodeError, EncodeError}};
use std::str;
use bincode::de::read::Reader;
use bincode::enc::write::Writer;
#[derive(Debug)]
pub struct NullTerminatedString(pub String);
impl NullTerminatedString {
pub fn new(string: &str) -> Self {
NullTerminatedString(string.into())
}
}
impl Encode for NullTerminatedString {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
let bytes = self.0.as_bytes();
encoder.writer().write(bytes)?; // Write the string bytes
encoder.writer().write(&[0])?; // Add the null terminator
Ok(())
}
}
impl Decode for NullTerminatedString {
fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
let mut buffer = Vec::new();
let mut byte = [0u8; 1];
// Read until the null terminator
while decoder.reader().read(&mut byte).is_ok() {
if byte[0] == 0 {
break; // Null terminator found
}
buffer.push(byte[0]);
}
let string = str::from_utf8(&buffer)
.map_err(|e| DecodeError::OtherString(format!("Invalid UTF-8: {}", e)))?
.to_string();
Ok(NullTerminatedString(string))
}
}

View File

@@ -1,13 +1,67 @@
use std::env;
use std::error::Error;
use std::net::ToSocketAddrs;
use std::str::FromStr;
use std::sync::Arc;
use dotenv::dotenv;
use tokio::net::TcpListener;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt};
use tracing::{info, error};
use tokio::{select, signal};
use tokio::sync::{Mutex, Semaphore};
use tracing::{info, error, debug, warn};
use tracing::Level;
use utils::consul_registration;
use utils::service_discovery::get_service_address;
use warp::Filter;
use crate::auth_client::AuthClient;
use crate::bufferpool::BufferPool;
use crate::metrics::{ACTIVE_CONNECTIONS, PACKETS_RECEIVED};
use crate::packet::Packet;
mod packet_type;
mod packet;
mod router;
mod packets;
mod enums;
mod dataconsts;
mod types;
mod handlers;
mod bufferpool;
mod metrics;
mod auth_client;
pub mod auth {
tonic::include_proto!("auth"); // Path matches the package name in auth.proto
}
const BUFFER_POOL_SIZE: usize = 1000;
const MAX_CONCURRENT_CONNECTIONS: usize = 100;
async fn handle_connection(stream: &mut TcpStream, pool: Arc<BufferPool>, auth_client: Arc<Mutex<AuthClient>>) -> Result<(), Box<dyn Error + Send + Sync>> {
ACTIVE_CONNECTIONS.inc();
while let Some(mut buffer) = pool.acquire().await {
// Read data into the buffer
let n = stream.read(&mut buffer).await?;
if n == 0 {
break; // Connection closed
}
PACKETS_RECEIVED.inc();
// Process the packet
match Packet::from_raw(&buffer[..n]) {
Ok(packet) => {
debug!("Parsed Packet: {:?}", packet);
// Handle the parsed packet (route it, process it, etc.)
router::route_packet(stream, packet, auth_client.clone()).await?;
}
Err(e) => warn!("Failed to parse packet: {}", e),
}
pool.release(buffer).await;
}
ACTIVE_CONNECTIONS.dec();
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -16,25 +70,73 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.with_max_level(Level::from_str(&env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string())).unwrap_or_else(|_| Level::INFO))
.init();
let listener = TcpListener::bind("127.0.0.1:4000").await?;
info!("Packet service listening on 127.0.0.1:4000");
// Set the gRPC server address
let addr = env::var("PACKET_SERVICE_ADDR").unwrap_or_else(|_| "127.0.0.1".to_string());
let port = env::var("PACKET_SERVICE_PORT").unwrap_or_else(|_| "4000".to_string());
let health_port = env::var("HEALTH_CHECK_PORT").unwrap_or_else(|_| "8082".to_string());
let consul_url = env::var("CONSUL_URL").unwrap_or_else(|_| "http://127.0.0.1:8500".to_string());
let service_name = env::var("SERVICE_NAME").unwrap_or_else(|_| "packet-service".to_string());
let service_address = addr.as_str();
let service_port = port.clone();
let health_check_url = format!("http://{}:{}/health", service_address, health_port);
let health_check_endpoint_addr = format!("{}:{}", service_address, health_port);
let auth_address = get_service_address(&consul_url, "auth-service").await?;
// Register service with Consul
consul_registration::register_service(
&consul_url,
service_name.as_str(),
service_address,
service_port.parse().unwrap_or(50052),
&health_check_url,
)
.await?;
// Start health-check endpoint
let health_route = warp::path!("health")
.map(|| warp::reply::with_status("OK", warp::http::StatusCode::OK));
tokio::spawn(warp::serve(health_route).run(health_check_endpoint_addr.to_socket_addrs()?.next().unwrap()));
let auth_url = format!("http://{}:{}", auth_address.Address, auth_address.Port);
let auth_client = Arc::new(Mutex::new(AuthClient::connect(&auth_url).await?));
let full_addr = format!("{}:{}", &addr, port);
// let address = full_addr.parse().expect("Invalid address");
loop {
let (mut socket, addr) = listener.accept().await?;
info!("New connection from {}", addr);
tokio::spawn(async move {
let mut buffer = vec![0u8; 1024];
let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_CONNECTIONS));
let listener = TcpListener::bind(full_addr.clone()).await.unwrap();
let buffer_pool = BufferPool::new(BUFFER_POOL_SIZE);
match socket.read(&mut buffer).await {
Ok(n) if n > 0 => {
if let Err(e) = router::route_packet(&buffer[..n]).await {
error!("Failed to route packet: {}", e);
}
}
Ok(_) => info!("Connection closed by {}", addr),
Err(e) => error!("Failed to read from socket: {}", e),
info!("Packet service listening on {}", full_addr);
loop {
let (mut socket, addr) = listener.accept().await.unwrap();
let auth_client = auth_client.clone();
info!("New connection from {}", addr);
let pool = buffer_pool.clone();
let permit = semaphore.clone().acquire_owned().await.unwrap();
// Spawn a new task for each connection
tokio::spawn(async move {
let _permit = permit;
if let Err(e) = handle_connection(&mut socket, pool, auth_client).await {
error!("Error handling connection: {}", e);
}
});
}
});
select! {
_ = signal::ctrl_c() => {},
}
consul_registration::deregister_service(&consul_url, service_name.as_str()).await.expect("");
info!("service {} deregistered", service_name);
Ok(())
}

View File

@@ -0,0 +1,22 @@
use prometheus::{Encoder, TextEncoder, Counter, Gauge, Histogram, register_counter, register_gauge, register_histogram};
use lazy_static::lazy_static;
lazy_static! {
// Counter to track the number of packets received
pub static ref PACKETS_RECEIVED: Counter = register_counter!(
"packets_received_total",
"Total number of packets received"
).unwrap();
// Gauge to track the number of active connections
pub static ref ACTIVE_CONNECTIONS: Gauge = register_gauge!(
"active_connections",
"Number of currently active connections"
).unwrap();
// Histogram to track packet processing latency
pub static ref PACKET_PROCESSING_TIME: Histogram = register_histogram!(
"packet_processing_time_seconds",
"Histogram of packet processing times"
).unwrap();
}

View File

@@ -1,31 +1,103 @@
use serde::{Serialize, Deserialize};
use bincode::{Encode, Decode};
use std::error::Error;
use tracing::debug;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use tracing::{debug, error};
use crate::packet_type::PacketType;
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug)]
pub struct Packet {
pub packet_size: u16,
pub packet_type: u16,
pub packet_type: PacketType,
pub packet_crc: u16,
pub payload: Vec<u8>,
}
pub trait PacketPayload: Encode + Decode {
fn encode(&self) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
let config = bincode::config::standard()
.with_fixed_int_encoding();
Ok(bincode::encode_to_vec(self, config)?)
}
fn decode(data: &[u8]) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>
where
Self: Sized,
{
let config = bincode::config::standard()
.with_fixed_int_encoding();
Ok(bincode::decode_from_slice(data, config)?.0)
}
}
impl Packet {
pub fn parse(data: &[u8]) -> Result<Self, Box<dyn Error>> {
pub fn new<T: PacketPayload>(packet_type: PacketType, payload: &T) -> Result<Self, Box<dyn Error + Send + Sync>> {
let encoded_payload = <T as PacketPayload>::encode(payload)?;
let packet_size = (6 + encoded_payload.len()) as u16;
// let packet_crc = crc::crc16::checksum_x25(&encoded_payload);
let packet_crc = 0;
Ok(Self {
packet_size,
packet_type,
packet_crc,
payload: encoded_payload,
})
}
pub fn from_raw(data: &[u8]) -> Result<Self, Box<dyn Error + Send + Sync>> {
if data.len() < 6 {
return Err("Invalid packet: Too short".into());
}
let packet_size = u16::from_be_bytes(data[0..2].try_into()?);
let packet_type = u16::from_be_bytes(data[2..4].try_into()?);
let packet_crc = u16::from_be_bytes(data[4..6].try_into()?);
// Extract packet fields
let packet_size = u16::from_le_bytes(data[0..2].try_into()?);
let raw_packet_type = u16::from_le_bytes(data[2..4].try_into()?);
let packet_crc = u16::from_le_bytes(data[4..6].try_into()?);
// Validate the size
if packet_size as usize != data.len() {
error!("Invalid packet: Size mismatch, expected: {}, actual: {}", packet_size, data.len());
return Err("Invalid packet: Size mismatch".into());
}
debug!("size: {:#X}, raw_type: {:#X}, crc: {:#X}", packet_size, raw_packet_type, packet_crc);
// Convert raw packet type into `PacketType` enum
let packet_type = PacketType::try_from(raw_packet_type)?;
// Extract the payload
let payload = data[6..].to_vec();
debug!("Packet Size: {}", packet_size);
debug!("Packet Type: {}", packet_type);
debug!("Packet CRC: {}", packet_crc);
debug!("Payload: {:?}", String::from_utf8(payload.clone())?);
Ok(Self {
packet_size,
packet_type,
packet_crc,
payload,
})
}
Ok(Packet { packet_size, packet_type, packet_crc, payload })
/// Serialize the `Packet` back to raw bytes
pub fn to_raw(&self) -> Vec<u8> {
let mut raw = Vec::with_capacity(self.packet_size as usize);
// Serialize fields
raw.extend(&self.packet_size.to_le_bytes());
raw.extend(&(self.packet_type as u16).to_le_bytes());
raw.extend(&self.packet_crc.to_le_bytes());
raw.extend(&self.payload);
raw
}
pub fn parse<T: PacketPayload>(&self) -> Result<T, Box<dyn Error + Send + Sync>> {
<T as PacketPayload>::decode(&self.payload)
}
}
pub async fn send_packet(stream: &mut TcpStream, packet: &Packet) -> Result<(), std::io::Error> {
let data = packet.to_raw();
debug!("Sending data: {:?}", data);
stream.write_all(&data).await?;
Ok(())
}

View File

@@ -0,0 +1,455 @@
use std::convert::TryFrom;
use thiserror::Error;
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PacketType {
PakcsAlive = 0x0500,
PakssError = 0x0501,
PakssAnnounceText = 0x0502,
PakswAnnounceChat = 0x0503,
PakcsAcceptReq = 0x0504,
PakcsChannelListReq = 0x0505,
PaklcChannelListReply = 0x0506,
PakcsLogoutReq = 0x0507,
PakwcLogoutReply = 0x0508,
PakcsLoginReq = 0x0509,
PaklcLoginReply = 0x050A,
PakgcLoginReply = 0x050B,
PakcsSrvSelectReq = 0x050C,
PaklcSrvSelectReply = 0x050D,
PakcsJoinServerReq = 0x050E,
PakscJoinServerReply = 0x050F,
PakwcGmCommand = 0x0510,
PakwcGlobalVars = 0x0511,
PakwcGlobalFlags = 0x0512,
PakccSwitchServer = 0x0513,
PakcsCharListReq = 0x0514,
PakccCharListReply = 0x0515,
PakcsCreateCharReq = 0x0516,
PakccCreateCharReply = 0x0517,
PakcsDeleteCharReq = 0x0518,
PakccDeleteCharReply = 0x0519,
PakcsSelectCharReq = 0x051A,
PakwcSelectCharReply = 0x051B,
PakwcInventoryData = 0x051C,
PakwcSetMoneyAndItem = 0x051D,
PakwcSetItem = 0x051E,
PakwcServerData = 0x051F,
PakwcQuestData = 0x0520,
PakcsChangeCharReq = 0x0521,
PakccChanCharReply = 0x0522,
PakwcSetMoney = 0x0523,
PakwcQuestRewardMoney = 0x0524,
PakwcQuestRewardItem = 0x0525,
PakwcQuestRewardAddValue = 0x0526,
PakwcQuestRewardSetValue = 0x0527,
PakcsCancelLogout = 0x0528,
PakwcQuestUpdate = 0x0529,
PakwcWishList = 0x052A,
PakcsQuestDataReq = 0x052B,
PakwcQuestDataReply = 0x052C,
PakwcNpcEvent = 0x052D,
PakwcGmCommandCode = 0x052E,
PakcsChangeMapReq = 0x052F,
PakwcChangeMapReply = 0x0530,
PakwcInitData = 0x0531,
PakcsReviveReq = 0x0532,
PakwcReviveReply = 0x0533,
PakcsReviveSetPos = 0x0534,
PakcsSetServerVarReq = 0x0535,
PakwcSetServerVarReply = 0x0536,
PakcsCharInfoReq = 0x0537,
PakwcCharInfoReply = 0x0538,
PakcsSetWeightReq = 0x0539,
PakwcSetWeight = 0x053A,
PakwcSetPosition = 0x053B,
PakcsStopMoving = 0x053C,
PakwcStopMoving = 0x053D,
PakwcUpdateNpc = 0x053E,
PakcsSummonCmd = 0x053F,
PakwcSummonCmd = 0x0540,
PakcsSetAnimation = 0x0541,
PacwcSetAnimation = 0x0542,
PakcsToggleMove = 0x0543,
PakwcToggleMove = 0x0544,
PakcsNormalChat = 0x0545,
PakwcNormalChat = 0x0546,
PakcsWhisperChat = 0x0547,
PakwcWhisperChat = 0x0548,
PakcsShoutChat = 0x0549,
PakwcShoutChat = 0x054A,
PakcsPartyChat = 0x054B,
PakwcPartyChat = 0x054C,
PakcsClanChat = 0x054D,
PakwcClanChat = 0x054E,
PakcsAlliedChat = 0x054F,
PakwcAlliedChat = 0x0550,
PakcsAlliedShoutChat = 0x0551,
PakwcAlliedShoutChat = 0x0552,
PakwcEventStatus = 0x0553,
PakwcNpcChar = 0x0554,
PakwcMobChar = 0x0555,
PakwcPlayerChar = 0x0556,
PakwcRemoveObject = 0x0557,
PakcsSetPosition = 0x0558,
PakcsStop = 0x0559,
PakwcStop = 0x055A,
PakwcMove = 0x055B,
PakcsAttack = 0x055C,
PakwcAttack = 0x055D,
PakcsDamage = 0x055E,
PakwcDamage = 0x055F,
PakcsMouseCmd = 0x0560,
PakwcMouseCmd = 0x0561,
PakwcSetExp = 0x0562,
PakwcLevelup = 0x0563,
PakcsHpReq = 0x0564,
PakwcHpReply = 0x0565,
PakwcSetHpAndMp = 0x0566,
PakcsStoreTradeReq = 0x0567,
PakwcStoreTradeReply = 0x0568,
PakcsUseItem = 0x0569,
PakwcUseItem = 0x056A,
PakcsDropItem = 0x056B,
PakcsEquipItem = 0x056C,
PakwcEquipItem = 0x056D,
PakwcDropItem = 0x056E,
PakcsPickupItemReq = 0x056F,
PakwcPickupItemReply = 0x0570,
PakcsTeleportReq = 0x0571,
PakwcTeleportReply = 0x0572,
PakcsStatAddReq = 0x0573,
PakwcStatAddReply = 0x0574,
PakcsHotbarSetIconReq = 0x0575,
PakwcHotbarSetIconReply = 0x0576,
PakcsEquipProjectile = 0x0577,
PakwcEquipProjectile = 0x0578,
PakwcChangeSkin = 0x0579,
PakcsBankListReq = 0x057A,
PakwcBankListReply = 0x057B,
PakcsBankMoveItem = 0x057C,
PakwcBankMoveItem = 0x057D,
PakcsCraftReq = 0x057E,
PakwcCraftReply = 0x057F,
PakwcSkillLearn = 0x0580,
PakcsSkillLevelReq = 0x0581,
PakwcSkillLevelReply = 0x0582,
PakcsSkillCastSelf = 0x0583,
PakwcSkillCastSelf = 0x0584,
PakcsSkillCastTarget = 0x0585,
PakwcSkillCastTarget = 0x0586,
PakcsSkillCastPosition = 0x0587,
PakwcSkillCastPosition = 0x0588,
PakwcSkillEffect = 0x0589,
PakwcSkillDamage = 0x058A,
PakwcClearStatus = 0x058B,
PakwcSpeedChanged = 0x058C,
PakwcSkillFinish = 0x058D,
PakcsAppraisalReq = 0x058E,
PakwcAppraisalReply = 0x058F,
PakwcSkillStart = 0x0590,
PakcsCraftEnhanceReq = 0x0591,
PakwcCraftEnhanceReply = 0x0592,
PakwcSkillCancel = 0x0593,
PakcsWishlistAdd = 0x0594,
PakcsTrade = 0x0595,
PakwcTrade = 0x0596,
PakcsTradeItem = 0x0597,
PakwcTradeItem = 0x0598,
PakcsShopOpen = 0x0599,
PakwcShopOpen = 0x059A,
PakcsShopClose = 0x059B,
PakwcShopClose = 0x059C,
PakcsShopListReq = 0x059D,
PakwcShopListReply = 0x059E,
PakcsShopBuyReq = 0x059F,
PakcsShopSellReq = 0x05A0,
PakcsShopBuysellReply = 0x05A1,
PakcsEquipItemRide = 0x05A2,
PakwcEquipItemRide = 0x05A3,
PakcsRepairUseItem = 0x05A4,
PakcsRepairNpc = 0x05A5,
PakwcSetItemLife = 0x05A6,
PakcsPartyReq = 0x05A7,
PakwcPartyReq = 0x05A8,
PakcsPartyReply = 0x05A9,
PakwcPartyReply = 0x05AA,
PakwcPartyMember = 0x05AB,
PakwcPartyItem = 0x05AC,
PakwcPartyLevelexp = 0x05AD,
PakwcPartyMemberUpdate = 0x05AE,
PakwcEventAdd = 0x05AF,
PakcsPartyRule = 0x05B0,
PakwcPartyRule = 0x05B1,
PakcsCraftStatus = 0x05B2,
PakwcCraftStatus = 0x05B3,
PakcsBankMoveMoney = 0x05B4,
PakwcBankMoveMoney = 0x05B5,
PakwcNpcShow = 0x05B6,
PakwcFairy = 0x05B7,
PakcsRideRequest = 0x05B8,
PakwcRideRequest = 0x05B9,
PakwcBillingMessage = 0x05BA,
PakwcBillingMessageExt = 0x05BB,
PakcsClanCommand = 0x05BC,
PakccClanCommand = 0x05BD,
PakcsMessenger = 0x05BE,
PakccMessenger = 0x05BF,
PakcsMessengerChat = 0x05C0,
PakccMessengerChat = 0x05C1,
PakcsChatroom = 0x05C2,
PakccChatroom = 0x05C3,
PakcsChatroomMessage = 0x05C4,
PakccChatroomMessage = 0x05C5,
PakcsMemo = 0x05C6,
PakccMemo = 0x05C7,
PakcsClanIconSet = 0x05C8,
PakcsClanIconReq = 0x05C9,
PakccClanIconReply = 0x05CA,
PakcsClanIconTimestamp = 0x05CB,
PakccClanIconTimestamp = 0x05CC,
PakwcRideStateChange = 0x05CD,
PawkcCharStateChange = 0x05CE,
PakcsScreenShotTimeReq = 0x05CF,
PakscScreenShotTimeReply = 0x05D0,
PakwcUpdateName = 0x05D1,
PakssAcceptReply = 0x05D2,
Epacketmax = 0x05D3,
Stress = 0x6F6D,
}
#[derive(Debug, Error)]
pub enum PacketError {
#[error("Unknown packet type: {0}")]
UnknownPacket(u16),
}
impl TryFrom<u16> for PacketType {
type Error = PacketError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0500 => Ok(PacketType::PakcsAlive),
0x0501 => Ok(PacketType::PakssError),
0x0502 => Ok(PacketType::PakssAnnounceText),
0x0503 => Ok(PacketType::PakswAnnounceChat),
0x0504 => Ok(PacketType::PakcsAcceptReq),
0x0505 => Ok(PacketType::PakcsChannelListReq),
0x0506 => Ok(PacketType::PaklcChannelListReply),
0x0507 => Ok(PacketType::PakcsLogoutReq),
0x0508 => Ok(PacketType::PakwcLogoutReply),
0x0509 => Ok(PacketType::PakcsLoginReq),
0x050A => Ok(PacketType::PaklcLoginReply),
0x050B => Ok(PacketType::PakgcLoginReply),
0x050C => Ok(PacketType::PakcsSrvSelectReq),
0x050D => Ok(PacketType::PaklcSrvSelectReply),
0x050E => Ok(PacketType::PakcsJoinServerReq),
0x050F => Ok(PacketType::PakscJoinServerReply),
0x0510 => Ok(PacketType::PakwcGmCommand),
0x0511 => Ok(PacketType::PakwcGlobalVars),
0x0512 => Ok(PacketType::PakwcGlobalFlags),
0x0513 => Ok(PacketType::PakccSwitchServer),
0x0514 => Ok(PacketType::PakcsCharListReq),
0x0515 => Ok(PacketType::PakccCharListReply),
0x0516 => Ok(PacketType::PakcsCreateCharReq),
0x0517 => Ok(PacketType::PakccCreateCharReply),
0x0518 => Ok(PacketType::PakcsDeleteCharReq),
0x0519 => Ok(PacketType::PakccDeleteCharReply),
0x051A => Ok(PacketType::PakcsSelectCharReq),
0x051B => Ok(PacketType::PakwcSelectCharReply),
0x051C => Ok(PacketType::PakwcInventoryData),
0x051D => Ok(PacketType::PakwcSetMoneyAndItem),
0x051E => Ok(PacketType::PakwcSetItem),
0x051F => Ok(PacketType::PakwcServerData),
0x0520 => Ok(PacketType::PakwcQuestData),
0x0521 => Ok(PacketType::PakcsChangeCharReq),
0x0522 => Ok(PacketType::PakccChanCharReply),
0x0523 => Ok(PacketType::PakwcSetMoney),
0x0524 => Ok(PacketType::PakwcQuestRewardMoney),
0x0525 => Ok(PacketType::PakwcQuestRewardItem),
0x0526 => Ok(PacketType::PakwcQuestRewardAddValue),
0x0527 => Ok(PacketType::PakwcQuestRewardSetValue),
0x0528 => Ok(PacketType::PakcsCancelLogout),
0x0529 => Ok(PacketType::PakwcQuestUpdate),
0x052A => Ok(PacketType::PakwcWishList),
0x052B => Ok(PacketType::PakcsQuestDataReq),
0x052C => Ok(PacketType::PakwcQuestDataReply),
0x052D => Ok(PacketType::PakwcNpcEvent),
0x052E => Ok(PacketType::PakwcGmCommandCode),
0x052F => Ok(PacketType::PakcsChangeMapReq),
0x0530 => Ok(PacketType::PakwcChangeMapReply),
0x0531 => Ok(PacketType::PakwcInitData),
0x0532 => Ok(PacketType::PakcsReviveReq),
0x0533 => Ok(PacketType::PakwcReviveReply),
0x0534 => Ok(PacketType::PakcsReviveSetPos),
0x0535 => Ok(PacketType::PakcsSetServerVarReq),
0x0536 => Ok(PacketType::PakwcSetServerVarReply),
0x0537 => Ok(PacketType::PakcsCharInfoReq),
0x0538 => Ok(PacketType::PakwcCharInfoReply),
0x0539 => Ok(PacketType::PakcsSetWeightReq),
0x053A => Ok(PacketType::PakwcSetWeight),
0x053B => Ok(PacketType::PakwcSetPosition),
0x053C => Ok(PacketType::PakcsStopMoving),
0x053D => Ok(PacketType::PakwcStopMoving),
0x053E => Ok(PacketType::PakwcUpdateNpc),
0x053F => Ok(PacketType::PakcsSummonCmd),
0x0540 => Ok(PacketType::PakwcSummonCmd),
0x0541 => Ok(PacketType::PakcsSetAnimation),
0x0542 => Ok(PacketType::PacwcSetAnimation),
0x0543 => Ok(PacketType::PakcsToggleMove),
0x0544 => Ok(PacketType::PakwcToggleMove),
0x0545 => Ok(PacketType::PakcsNormalChat),
0x0546 => Ok(PacketType::PakwcNormalChat),
0x0547 => Ok(PacketType::PakcsWhisperChat),
0x0548 => Ok(PacketType::PakwcWhisperChat),
0x0549 => Ok(PacketType::PakcsShoutChat),
0x054A => Ok(PacketType::PakwcShoutChat),
0x054B => Ok(PacketType::PakcsPartyChat),
0x054C => Ok(PacketType::PakwcPartyChat),
0x054D => Ok(PacketType::PakcsClanChat),
0x054E => Ok(PacketType::PakwcClanChat),
0x054F => Ok(PacketType::PakcsAlliedChat),
0x0550 => Ok(PacketType::PakwcAlliedChat),
0x0551 => Ok(PacketType::PakcsAlliedShoutChat),
0x0552 => Ok(PacketType::PakwcAlliedShoutChat),
0x0553 => Ok(PacketType::PakwcEventStatus),
0x0554 => Ok(PacketType::PakwcNpcChar),
0x0555 => Ok(PacketType::PakwcMobChar),
0x0556 => Ok(PacketType::PakwcPlayerChar),
0x0557 => Ok(PacketType::PakwcRemoveObject),
0x0558 => Ok(PacketType::PakcsSetPosition),
0x0559 => Ok(PacketType::PakcsStop),
0x055A => Ok(PacketType::PakwcStop),
0x055B => Ok(PacketType::PakwcMove),
0x055C => Ok(PacketType::PakcsAttack),
0x055D => Ok(PacketType::PakwcAttack),
0x055E => Ok(PacketType::PakcsDamage),
0x055F => Ok(PacketType::PakwcDamage),
0x0560 => Ok(PacketType::PakcsMouseCmd),
0x0561 => Ok(PacketType::PakwcMouseCmd),
0x0562 => Ok(PacketType::PakwcSetExp),
0x0563 => Ok(PacketType::PakwcLevelup),
0x0564 => Ok(PacketType::PakcsHpReq),
0x0565 => Ok(PacketType::PakwcHpReply),
0x0566 => Ok(PacketType::PakwcSetHpAndMp),
0x0567 => Ok(PacketType::PakcsStoreTradeReq),
0x0568 => Ok(PacketType::PakwcStoreTradeReply),
0x0569 => Ok(PacketType::PakcsUseItem),
0x056A => Ok(PacketType::PakwcUseItem),
0x056B => Ok(PacketType::PakcsDropItem),
0x056C => Ok(PacketType::PakcsEquipItem),
0x056D => Ok(PacketType::PakwcEquipItem),
0x056E => Ok(PacketType::PakwcDropItem),
0x056F => Ok(PacketType::PakcsPickupItemReq),
0x0570 => Ok(PacketType::PakwcPickupItemReply),
0x0571 => Ok(PacketType::PakcsTeleportReq),
0x0572 => Ok(PacketType::PakwcTeleportReply),
0x0573 => Ok(PacketType::PakcsStatAddReq),
0x0574 => Ok(PacketType::PakwcStatAddReply),
0x0575 => Ok(PacketType::PakcsHotbarSetIconReq),
0x0576 => Ok(PacketType::PakwcHotbarSetIconReply),
0x0577 => Ok(PacketType::PakcsEquipProjectile),
0x0578 => Ok(PacketType::PakwcEquipProjectile),
0x0579 => Ok(PacketType::PakwcChangeSkin),
0x057A => Ok(PacketType::PakcsBankListReq),
0x057B => Ok(PacketType::PakwcBankListReply),
0x057C => Ok(PacketType::PakcsBankMoveItem),
0x057D => Ok(PacketType::PakwcBankMoveItem),
0x057E => Ok(PacketType::PakcsCraftReq),
0x057F => Ok(PacketType::PakwcCraftReply),
0x0580 => Ok(PacketType::PakwcSkillLearn),
0x0581 => Ok(PacketType::PakcsSkillLevelReq),
0x0582 => Ok(PacketType::PakwcSkillLevelReply),
0x0583 => Ok(PacketType::PakcsSkillCastSelf),
0x0584 => Ok(PacketType::PakwcSkillCastSelf),
0x0585 => Ok(PacketType::PakcsSkillCastTarget),
0x0586 => Ok(PacketType::PakwcSkillCastTarget),
0x0587 => Ok(PacketType::PakcsSkillCastPosition),
0x0588 => Ok(PacketType::PakwcSkillCastPosition),
0x0589 => Ok(PacketType::PakwcSkillEffect),
0x058A => Ok(PacketType::PakwcSkillDamage),
0x058B => Ok(PacketType::PakwcClearStatus),
0x058C => Ok(PacketType::PakwcSpeedChanged),
0x058D => Ok(PacketType::PakwcSkillFinish),
0x058E => Ok(PacketType::PakcsAppraisalReq),
0x058F => Ok(PacketType::PakwcAppraisalReply),
0x0590 => Ok(PacketType::PakwcSkillStart),
0x0591 => Ok(PacketType::PakcsCraftEnhanceReq),
0x0592 => Ok(PacketType::PakwcCraftEnhanceReply),
0x0593 => Ok(PacketType::PakwcSkillCancel),
0x0594 => Ok(PacketType::PakcsWishlistAdd),
0x0595 => Ok(PacketType::PakcsTrade),
0x0596 => Ok(PacketType::PakwcTrade),
0x0597 => Ok(PacketType::PakcsTradeItem),
0x0598 => Ok(PacketType::PakwcTradeItem),
0x0599 => Ok(PacketType::PakcsShopOpen),
0x059A => Ok(PacketType::PakwcShopOpen),
0x059B => Ok(PacketType::PakcsShopClose),
0x059C => Ok(PacketType::PakwcShopClose),
0x059D => Ok(PacketType::PakcsShopListReq),
0x059E => Ok(PacketType::PakwcShopListReply),
0x059F => Ok(PacketType::PakcsShopBuyReq),
0x05A0 => Ok(PacketType::PakcsShopSellReq),
0x05A1 => Ok(PacketType::PakcsShopBuysellReply),
0x05A2 => Ok(PacketType::PakcsEquipItemRide),
0x05A3 => Ok(PacketType::PakwcEquipItemRide),
0x05A4 => Ok(PacketType::PakcsRepairUseItem),
0x05A5 => Ok(PacketType::PakcsRepairNpc),
0x05A6 => Ok(PacketType::PakwcSetItemLife),
0x05A7 => Ok(PacketType::PakcsPartyReq),
0x05A8 => Ok(PacketType::PakwcPartyReq),
0x05A9 => Ok(PacketType::PakcsPartyReply),
0x05AA => Ok(PacketType::PakwcPartyReply),
0x05AB => Ok(PacketType::PakwcPartyMember),
0x05AC => Ok(PacketType::PakwcPartyItem),
0x05AD => Ok(PacketType::PakwcPartyLevelexp),
0x05AE => Ok(PacketType::PakwcPartyMemberUpdate),
0x05AF => Ok(PacketType::PakwcEventAdd),
0x05B0 => Ok(PacketType::PakcsPartyRule),
0x05B1 => Ok(PacketType::PakwcPartyRule),
0x05B2 => Ok(PacketType::PakcsCraftStatus),
0x05B3 => Ok(PacketType::PakwcCraftStatus),
0x05B4 => Ok(PacketType::PakcsBankMoveMoney),
0x05B5 => Ok(PacketType::PakwcBankMoveMoney),
0x05B6 => Ok(PacketType::PakwcNpcShow),
0x05B7 => Ok(PacketType::PakwcFairy),
0x05B8 => Ok(PacketType::PakcsRideRequest),
0x05B9 => Ok(PacketType::PakwcRideRequest),
0x05BA => Ok(PacketType::PakwcBillingMessage),
0x05BB => Ok(PacketType::PakwcBillingMessageExt),
0x05BC => Ok(PacketType::PakcsClanCommand),
0x05BD => Ok(PacketType::PakccClanCommand),
0x05BE => Ok(PacketType::PakcsMessenger),
0x05BF => Ok(PacketType::PakccMessenger),
0x05C0 => Ok(PacketType::PakcsMessengerChat),
0x05C1 => Ok(PacketType::PakccMessengerChat),
0x05C2 => Ok(PacketType::PakcsChatroom),
0x05C3 => Ok(PacketType::PakccChatroom),
0x05C4 => Ok(PacketType::PakcsChatroomMessage),
0x05C5 => Ok(PacketType::PakccChatroomMessage),
0x05C6 => Ok(PacketType::PakcsMemo),
0x05C7 => Ok(PacketType::PakccMemo),
0x05C8 => Ok(PacketType::PakcsClanIconSet),
0x05C9 => Ok(PacketType::PakcsClanIconReq),
0x05CA => Ok(PacketType::PakccClanIconReply),
0x05CB => Ok(PacketType::PakcsClanIconTimestamp),
0x05CC => Ok(PacketType::PakccClanIconTimestamp),
0x05CD => Ok(PacketType::PakwcRideStateChange),
0x05CE => Ok(PacketType::PawkcCharStateChange),
0x05CF => Ok(PacketType::PakcsScreenShotTimeReq),
0x05D0 => Ok(PacketType::PakscScreenShotTimeReply),
0x05D1 => Ok(PacketType::PakwcUpdateName),
0x05D2 => Ok(PacketType::PakssAcceptReply),
0x05D3 => Ok(PacketType::Epacketmax),
0x6F6D => Ok(PacketType::Stress),
_ => Err(PacketError::UnknownPacket(value)),
}
}
}
impl From<PacketType> for u16 {
fn from(packet_type: PacketType) -> Self {
packet_type as u16
}
}

View File

@@ -1,13 +1,29 @@
use crate::packet::Packet;
// use crate::handlers::{chat, movement};
use crate::packet::{Packet};
use crate::handlers::{auth};
use crate::packet_type::PacketType;
use std::error::Error;
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::Mutex;
use tracing::{debug, warn};
use crate::auth_client::AuthClient;
pub async fn route_packet(data: &[u8]) -> Result<(), Box<dyn Error>> {
let packet = Packet::parse(data)?;
pub async fn route_packet(stream: &mut TcpStream, packet: Packet, auth_client: Arc<Mutex<AuthClient>>) -> Result<(), Box<dyn Error + Send + Sync>> {
debug!("Routing packet: {:?}", packet);
match packet.packet_type {
PacketType::PakcsAlive => Ok(()),
PacketType::PakcsAcceptReq => auth::handle_accept_req(stream, packet).await,
PacketType::PakcsJoinServerReq => auth::handle_join_server_req(stream, packet).await,
// Login Stuff
PacketType::PakcsLoginReq => auth::handle_login_req(stream, packet, auth_client).await,
PacketType::PakcsSrvSelectReq => auth::handle_server_select_req(stream, packet).await,
PacketType::PakcsChannelListReq => auth::handle_channel_list_req(stream, packet).await,
// 1 => chat::handle_chat(packet).await?,
// 2 => movement::handle_movement(packet).await?,
_ => Err("Unknown packet type".into()),
_ => {
warn!("Unhandled packet type: {:?}", packet.packet_type);
Ok(())
},
}
}

View File

@@ -0,0 +1,38 @@
use std::time::{Duration};
use bincode::{Encode, Decode};
// `HotbarItem` structure converted to Rust.
#[derive(Clone, Copy)]
struct HotbarItem {
data: HotbarItemData,
}
// Definition for `data` union in `HotbarItem`.
#[derive(Clone, Copy)]
union HotbarItemData {
item: u16,
components: HotbarItemComponents,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
#[repr(C)]
struct HotbarItemComponents {
type_: u8,
slot_id: u16,
}
// `Skill` structure converted to Rust.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
struct Skill {
id: u16,
level: u8,
}
// `StatusEffect` structure converted to Rust.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
struct StatusEffect {
expired: Duration,
value: u16,
unknown: u16,
dt: Duration,
}

9
utils/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "utils"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12.9", features = ["json"] }
tracing = "0.1"

2
utils/src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod consul_registration;
pub mod service_discovery;

View File

@@ -20,16 +20,16 @@ struct Weights {
#[derive(Deserialize)]
pub struct Service {
pub(crate) ID: String,
pub(crate) Service: String,
pub(crate) Tags: Vec<String>,
pub(crate) Port: u16,
pub(crate) Address: String,
pub(crate) TaggedAddresses: TaggedAddresses,
pub(crate) Weights: Weights,
pub(crate) EnableTagOverride: bool,
pub(crate) ContentHash: String,
pub(crate) Datacenter: String,
pub ID: String,
pub Service: String,
pub Tags: Vec<String>,
pub Port: u16,
pub Address: String,
pub TaggedAddresses: TaggedAddresses,
pub Weights: Weights,
pub EnableTagOverride: bool,
pub ContentHash: String,
pub Datacenter: String,
}
pub async fn get_service_address(consul_url: &str, service_name: &str) -> Result<(Service), Box<dyn std::error::Error>> {