This commit is contained in:
2025-03-06 22:38:53 +01:00
parent 6b406c24d3
commit f7fa5d5be4
8 changed files with 138 additions and 113 deletions
+17 -11
View File
@@ -1,22 +1,28 @@
# SaturnClient API
The api for AuraClient which is a Minecraft mod Client
# Handshake
In order to communicate with the server, you will have to make a handshake, here is how
```
<MOJANG-SESSION-TOKEN>
```
The session token is stored on your minecraft client when it communicates with Mojang servers, as you might understand, this token is a private token used for authentication with Mojang servers, Why do we need it? Because our server needs to understand that whoever logs in with SaturnClient is you and when you make changes in your client, the changes get synced with others, Example: Changing Cloak.
# Protocol base
All whitespace is used
Every request should be only one line
```
method
@key=value
```
For private requests (requires microsoft session tokens)
```
@SessionToken=<session-token>
method@key1=value1@key2=value2
```
# Example method
# Error handling
request
```
ping
invalid-method
```
- Response
response
```
pong
!Invalid method
```
On a response `!` means it's an error.
-1
View File
@@ -1 +0,0 @@
pub const CLOAKS: &[&str] = &["glitch"];
+18
View File
@@ -0,0 +1,18 @@
pub struct Player {
pub uuid: String,
pub cloaks: Vec<String>,
pub cloak: String,
}
pub struct Database {
pub players: mongodb::sync::Collection<Player>,
}
impl Database {
pub fn new(client: &mongodb::sync::Client) -> Self {
let db = client.database("saturn");
Self {
players: db.collection("players"),
}
}
}
-33
View File
@@ -1,33 +0,0 @@
use mongodb::sync::{Client, Collection};
use serde::{Deserialize, Serialize};
pub enum Response {
Pong,
}
impl Response {
pub fn to_string(&self) -> String {
match self {
Response::Pong => "pong".to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Player {
pub uuid: String,
pub cloaks: Vec<String>,
}
pub struct Database {
pub players: Collection<Player>,
}
impl Database {
pub fn new(client: &Client) -> Self {
let db = client.database("saturn");
Self {
players: db.collection("players"),
}
}
}
+31 -55
View File
@@ -1,68 +1,44 @@
pub mod assets;
pub mod io;
use std::{
io::{BufRead, BufReader, Write},
net::TcpListener,
sync::Arc,
};
pub mod database;
pub mod methods;
pub mod parser;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;
use std::thread;
use io::Database;
use methods::handle_request;
use mongodb::sync::Client;
const MONGO_URI: &str = "mongodb://admin:admin@localhost/";
pub mod response;
fn main() -> std::io::Result<()> {
let client = Client::with_uri_str(MONGO_URI).unwrap();
let database = Arc::new(Database::new(&client));
let listener = TcpListener::bind("127.0.0.1:8080")?;
let client = mongodb::sync::Client::with_uri_str("mongodb://admin:admin@localhost/").unwrap();
let database = Arc::new(database::Database::new(&client));
println!("Server listening on port 8080");
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
for stream in listener.incoming() {
match stream {
Ok(stream) => {
println!("New connection: {}", stream.peer_addr().unwrap());
thread::spawn({
println!("New connection");
let database = Arc::clone(&database);
move || handle_client(stream, database)
std::thread::spawn({
move || {
let mut stream = stream.unwrap();
let mut reader = BufReader::new(stream.try_clone().unwrap());
let session = methods::Session::new(&mut reader, database).unwrap();
loop {
let mut request_string = String::new();
match reader.read_line(&mut request_string).unwrap() {
0 => break,
_ => {
let (method, params) = parser::parse(&request_string).unwrap();
let response = session.handle_request(&method, &params);
stream.write_all(response.to_string().as_bytes()).unwrap();
}
};
}
}
});
}
Err(e) => eprintln!("Connection failed: {}", e),
}
}
Ok(())
}
pub fn handle_client(mut stream: TcpStream, database: Arc<Database>) {
let mut buffer = [0; 512];
loop {
match stream.read(&mut buffer) {
Ok(0) => break,
Ok(n) => {
let recv = String::from_utf8_lossy(&buffer[..n]).to_string();
match parser::parse(&recv) {
Ok((method, params)) => {
match handle_request(Arc::clone(&database), method, params) {
Ok(response) => {
stream.write_all(response.to_string().as_bytes()).unwrap()
}
Err(e) => eprintln!("Error handling request: {}", e),
}
}
Err(e) => eprintln!("Error parsing request: {}", e),
};
}
Err(e) => {
eprintln!("Error reading from client: {}", e);
break;
}
}
}
println!("Client disconnected");
}
+33 -9
View File
@@ -1,14 +1,38 @@
use std::{collections::HashMap, sync::Arc};
use std::{
collections::HashMap,
io::{self, BufRead, BufReader},
net::TcpStream,
sync::Arc,
};
use crate::io::{Database, Response};
pub struct Session {
pub session_id: String,
pub database: Arc<crate::database::Database>,
}
impl Session {
pub fn new(
reader: &mut BufReader<TcpStream>,
database: Arc<crate::database::Database>,
) -> io::Result<Self> {
let mut session_id = String::new();
reader.read_line(&mut session_id)?;
Ok(Self {
session_id,
database,
})
}
pub fn handle_request(
database: Arc<Database>,
method: String,
params: HashMap<String, String>,
) -> Result<Response, String> {
match method.as_str() {
"ping" => Ok(Response::Pong),
_ => Err("Invalid request: ".to_string()),
&self,
method: &str,
params: &HashMap<String, String>,
) -> crate::response::Response {
match method {
"ping" => crate::response::Response::Pong,
_ => crate::response::Response::Error(crate::response::Error::InvalidRequest),
}
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ impl ParamMap for HashMap<String, String> {
}
}
pub fn parse(s: &str) -> Result<(String, HashMap<String, String>), String> {
pub fn parse(s: &str) -> std::io::Result<(String, HashMap<String, String>)> {
let mut parts = s.split("@");
let method = parts.next().unwrap().trim().to_lowercase();
+35
View File
@@ -0,0 +1,35 @@
#[derive(Debug, Clone)]
pub enum Response {
Pong,
Error(Error),
}
impl std::fmt::Display for Response {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Response::Pong => write!(f, "Pong"),
Response::Error(error) => write!(f, "!{}", error),
}
}
}
#[derive(Debug, Clone)]
pub enum Error {
InvalidRequest,
InvalidMethod,
InvalidParameter,
ParameterNotFound,
InvalidSessionToken,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::InvalidRequest => write!(f, "Invalid request"),
Error::InvalidMethod => write!(f, "Invalid method"),
Error::InvalidParameter => write!(f, "Invalid parameter"),
Error::ParameterNotFound => write!(f, "Parameter not found"),
Error::InvalidSessionToken => write!(f, "Invalid session token"),
}
}
}