(tools, client, server) feat: Remove gRPC support, add TCP back and reorganized project

This commit is contained in:
2025-08-30 17:07:03 +08:00
parent 8fd5e24865
commit 362aa799b9
28 changed files with 378 additions and 490 deletions

89
Server/Cargo.lock generated
View File

@ -710,8 +710,6 @@ dependencies = [
"prettyplease",
"prost",
"prost-types",
"pulldown-cmark",
"pulldown-cmark-to-cmark",
"regex",
"syn",
"tempfile",
@ -739,26 +737,6 @@ dependencies = [
"prost",
]
[[package]]
name = "pulldown-cmark"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
dependencies = [
"bitflags",
"memchr",
"unicase",
]
[[package]]
name = "pulldown-cmark-to-cmark"
version = "21.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75"
dependencies = [
"pulldown-cmark",
]
[[package]]
name = "quote"
version = "1.0.40"
@ -871,11 +849,9 @@ dependencies = [
"colored",
"log",
"prost",
"prost-build",
"tokio",
"tonic",
"tonic-prost",
"tonic-prost-build",
"tonic-web",
]
[[package]]
@ -1029,63 +1005,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "tonic-build"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d"
dependencies = [
"prettyplease",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tonic-prost"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d"
dependencies = [
"bytes",
"prost",
"tonic",
]
[[package]]
name = "tonic-prost-build"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1"
dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"prost-types",
"quote",
"syn",
"tempfile",
"tonic-build",
]
[[package]]
name = "tonic-web"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd70b30990a5e47d404c5b2223c9cc194603ab400d2ee4248099533181e7b747"
dependencies = [
"base64",
"bytes",
"http",
"http-body",
"pin-project",
"tokio-stream",
"tonic",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
@ -1154,12 +1073,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
version = "1.0.18"

View File

@ -12,8 +12,6 @@ log = "0.4"
prost = "0.14"
tokio = { version = "1", features = ["full"] }
tonic = "0.14"
tonic-prost = "0.14"
tonic-web = "0.14"
[build-dependencies]
tonic-prost-build = "0.14"
prost-build = "0.14"

View File

@ -5,6 +5,11 @@ fn main() -> io::Result<()> {
unsafe {
env::set_var("PROTOC", "../Tools/ProtoBuf/bin/protoc.exe");
}
tonic_prost_build::compile_protos("../Tools/ProtoBuf/proto/message.proto")?;
prost_build::compile_protos(
&["../Tools/ProtoBuf/proto/message.proto"],
&["../Tools/ProtoBuf/proto"],
)?;
Ok(())
}

View File

@ -1,6 +1,9 @@
use std::io::Write;
pub(crate) fn run() {
use crate::servers::tcp_server::TCP_SERVER;
use crate::servers::udp_server::UDP_SERVER;
pub(crate) async fn run() {
let stdin = std::io::stdin();
loop {
@ -14,7 +17,12 @@ pub(crate) fn run() {
.expect("Failed to read from standard input!");
match input.trim() {
"exit" => break,
"exit" => {
TCP_SERVER.lock().await.stop().await;
UDP_SERVER.lock().await.stop().await;
break;
}
_ => println!("Usage: <command>"),
}
}

View File

@ -1,28 +0,0 @@
use std::net::SocketAddr;
use tokio::task;
use tonic::transport::Server;
use tonic_web::GrpcWebLayer;
use crate::protocol::game_service_server::GameServiceServer;
use crate::services::game_service::GameServiceImpl;
pub(crate) struct GrpcServer;
impl GrpcServer {
pub(crate) async fn init() {
let addr = SocketAddr::new([127, 0, 0, 1].into(), 12345);
let game_service = GameServiceServer::new(GameServiceImpl);
task::spawn(async move {
Server::builder()
.accept_http1(true)
.layer(GrpcWebLayer::new())
.add_service(game_service)
.serve(addr)
.await
.unwrap_or_else(|e| log::error!("Failed to build server: {e}"));
});
}
}

View File

@ -1,13 +1,12 @@
mod command_helper;
mod grpc_server;
mod protocol;
mod server_logger;
mod servers;
mod services;
mod udp_server;
use grpc_server::GrpcServer;
use server_logger::ServerLogger;
use udp_server::UdpServer;
use servers::tcp_server::TCP_SERVER;
use servers::udp_server::UDP_SERVER;
#[tokio::main]
async fn main() {
@ -15,10 +14,10 @@ async fn main() {
log::info!("Starting server...");
GrpcServer::init().await;
UdpServer::init();
TCP_SERVER.lock().await.start().await;
UDP_SERVER.lock().await.start().await;
log::info!("Server successfully started!");
command_helper::run();
command_helper::run().await;
}

4
Server/src/servers.rs Normal file
View File

@ -0,0 +1,4 @@
pub(crate) mod tcp_server;
pub(crate) mod udp_server;
const SERVER_ADDR: &str = "127.0.0.1:12345";

View File

@ -0,0 +1,120 @@
use std::collections::HashMap;
use std::io;
use std::net::SocketAddr;
use std::sync::LazyLock;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::{Mutex, mpsc};
use tokio::task::JoinHandle;
use super::SERVER_ADDR;
pub(crate) static TCP_SERVER: LazyLock<Mutex<TcpServer>> =
LazyLock::new(|| Mutex::new(TcpServer::new()));
pub(crate) struct TcpServer {
is_running: bool,
clients: HashMap<SocketAddr, JoinHandle<()>>,
shutdown_tx: Option<mpsc::Sender<()>>,
}
impl TcpServer {
fn new() -> Self {
Self {
is_running: false,
clients: HashMap::new(),
shutdown_tx: None,
}
}
pub(crate) async fn start(&mut self) {
if self.is_running {
log::warn!("TCP server is already running");
return;
}
match TcpListener::bind(SERVER_ADDR).await {
Ok(listener) => {
let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
self.is_running = true;
self.shutdown_tx = Some(shutdown_tx);
tokio::spawn(async move {
Self::listen_to_clients(listener, shutdown_rx).await;
});
log::info!("TCP Server started on {}", SERVER_ADDR);
}
Err(e) => log::error!("Failed to bind to address: {e}"),
}
}
pub(crate) async fn stop(&mut self) {
if !self.is_running {
return;
}
self.is_running = false;
if let Some(shutdown_tx) = self.shutdown_tx.take() {
_ = shutdown_tx.send(()).await;
}
for (addr, connection) in self.clients.drain() {
log::info!("Closing connection to {}", addr);
connection.abort();
}
}
async fn listen_to_clients(listener: TcpListener, mut shutdown_rx: mpsc::Receiver<()>) {
loop {
tokio::select! {
result = listener.accept() => {
match result {
Ok((socket, addr)) => {
log::info!("New client connected: {addr}");
let task_handle = tokio::spawn(async move {
if let Err(e) = Self::handle_client(socket, addr).await {
log::error!("Client {addr} error: {e}");
}
log::info!("Client {addr} disconnected");
});
let mut server = TCP_SERVER.lock().await;
server.clients.insert(addr, task_handle);
}
Err(e) => log::error!("Couldn't get client: {e}"),
}
}
_ = shutdown_rx.recv() => {
log::info!("TCP Server shutting down");
break;
}
}
}
}
async fn handle_client(mut socket: TcpStream, addr: SocketAddr) -> io::Result<()> {
let mut buffer = [0; 1024];
loop {
let len = socket.read(&mut buffer).await?;
if len == 0 {
break;
}
log::debug!("Received {} bytes from {}", len, addr);
// TODO: Deserialize data
socket.write_all(&buffer[..len]).await?;
}
let mut server = TCP_SERVER.lock().await;
server.clients.remove(&addr);
Ok(())
}
}

View File

@ -0,0 +1,68 @@
use std::io;
use std::sync::LazyLock;
use tokio::net::UdpSocket;
use tokio::sync::Mutex;
use super::SERVER_ADDR;
pub(crate) static UDP_SERVER: LazyLock<Mutex<UdpServer>> =
LazyLock::new(|| Mutex::new(UdpServer::new()));
pub(crate) struct UdpServer {
is_running: bool,
}
impl UdpServer {
fn new() -> Self {
Self { is_running: false }
}
pub(crate) async fn start(&mut self) {
if self.is_running {
log::warn!("UDP server is already running");
return;
}
match UdpSocket::bind(SERVER_ADDR).await {
Ok(socket) => {
self.is_running = true;
tokio::spawn(async move {
Self::handle_client(&socket)
.await
.unwrap_or_else(|e| log::error!("Failed to process data: {e}"))
});
log::info!("UDP Server started on {}", SERVER_ADDR);
}
Err(e) => log::error!("Failed to bind to address: {e}"),
}
}
pub(crate) async fn stop(&mut self) {
if !self.is_running {
return;
}
self.is_running = false;
}
async fn handle_client(socket: &UdpSocket) -> io::Result<()> {
loop {
let mut buffer = [0; 1024];
let (len, addr) = socket.recv_from(&mut buffer).await?;
if len == 0 {
break;
}
log::info!("Received message from client {addr}");
// TODO: Deserialize data
let buffer = &buffer[..len];
socket.send_to(buffer, addr).await?;
}
Ok(())
}
}

View File

@ -1,33 +1 @@
use tonic::{Request, Response, Status};
use crate::protocol::game_service_server::GameService;
use crate::protocol::{LoginRequest, LoginResponse, RequestResult, SignupRequest, SignupResponse};
pub(crate) struct GameServiceImpl;
#[tonic::async_trait]
impl GameService for GameServiceImpl {
async fn login(
&self,
request: Request<LoginRequest>,
) -> Result<Response<LoginResponse>, Status> {
log::info!("User {} logged in!", request.get_ref().username);
Ok(Response::new(LoginResponse {
result: RequestResult::Success.into(),
message: "".into(),
}))
}
async fn signup(
&self,
request: Request<SignupRequest>,
) -> Result<Response<SignupResponse>, Status> {
log::info!("User {} signed up!", request.get_ref().username);
Ok(Response::new(SignupResponse {
result: RequestResult::Success.into(),
message: "".into(),
}))
}
}

View File

@ -1,27 +0,0 @@
use std::net::UdpSocket;
use tokio::task;
pub(crate) struct UdpServer;
impl UdpServer {
pub(crate) fn init() {
match UdpSocket::bind("127.0.0.1:12345") {
Ok(socket) => {
task::spawn(async move {
loop {
let mut buf = [0; 1500];
let (amt, src) = socket.recv_from(&mut buf).unwrap();
log::info!("Received message from client {src}");
// TODO: Process received data in an independent method.
let buf = &buf[..amt];
socket.send_to(buf, src).unwrap();
}
});
}
Err(e) => log::error!("Failed to bind to address: {e}"),
}
}
}