socket server with rust

This commit is contained in:
2025-03-06 00:33:08 +01:00
parent 553db4c8a6
commit 6b406c24d3
16 changed files with 2332 additions and 1476 deletions
+1 -130
View File
@@ -1,130 +1 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
/target
Generated
+2157
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "auraclient-api"
version = "0.1.0"
edition = "2021"
[dependencies]
mongodb = { version = "3.2.1", features = ["sync"] }
serde = "1.0.218"
+21 -1
View File
@@ -1,2 +1,22 @@
# auraclient-api
# SaturnClient API
The api for AuraClient which is a Minecraft mod Client
# Protocol base
All whitespace is used
```
method
@key=value
```
For private requests (requires microsoft session tokens)
```
@SessionToken=<session-token>
```
# Example method
```
ping
```
- Response
```
pong
```
-1109
View File
File diff suppressed because it is too large Load Diff
-29
View File
@@ -1,29 +0,0 @@
{
"name": "auraclient-api",
"version": "1.0.0",
"description": "The api for AuraClient which is a Minecraft mod Client",
"main": "dist/index.js",
"scripts": {
"start": "tsc && node dist/index.js",
"test": "tsc && node test.mjs"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kleo-dev/auraclient-api.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/kleo-dev/auraclient-api/issues"
},
"homepage": "https://github.com/kleo-dev/auraclient-api#readme",
"dependencies": {
"@types/express": "^5.0.0",
"@types/mongodb": "^4.0.6",
"@types/node": "^22.13.5",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"mongodb": "^6.13.1"
}
}
+1
View File
@@ -0,0 +1 @@
pub const CLOAKS: &[&str] = &["glitch"];
-10
View File
@@ -1,10 +0,0 @@
import { MongoClient } from "mongodb";
import { Cloak, Player, mongoUrl } from "./utils";
export const validCloaks: Cloak[] = ["glitch"];
const mongoClient = new MongoClient(mongoUrl);
const db = mongoClient.db("saturnclient");
export const players = db.collection<Player>("players");
-165
View File
@@ -1,165 +0,0 @@
import express from "express";
import { players, validCloaks } from "./database";
import { Cloak } from "./utils";
async function getPlayerData(token: string) {
try {
const response = await fetch(
`https://api.minecraftservices.com/minecraft/profile`,
{
headers: {
Authorization: "Bearer " + token,
},
}
);
if (!response.ok) {
throw new Error("Invalid session");
}
const { id, name } = await response.json();
return { uuid: id, name };
} catch (error) {
throw new Error("Failed to validate session");
}
}
const port = 3000;
const app = express();
app.use(express.json());
app.get("/", (_, res) => {
res.send({
version: "0.0.1-alpha.0",
});
});
app.get("/player/:uuid", async (req, res) => {
const player = await players.findOne({
uuid: req.params.uuid.replaceAll("-", ""),
});
if (!player) {
res.send({
saturn: false,
});
return;
}
const response = await fetch(
`https://api.minecraftservices.com/minecraft/profile/lookup/${player.uuid}`
);
const { name } = await response.json();
res.send({
saturn: true,
cloak: player.cloak,
name,
});
});
app.get("/auth", async (req, res) => {
const { token } = req.query;
try {
const { uuid, name } = await getPlayerData(token as string);
const player = await players.findOne({ uuid });
if (!player) {
await players.insertOne({
uuid,
cloak: "",
cloaks: [],
});
res.send({
success: true,
uuid,
cloak: "",
cloaks: [],
});
return;
}
res.send({
success: true,
uuid,
name,
cloak: player.cloak,
cloaks: player.cloaks,
});
} catch (error) {
res.status(500).send({
success: false,
error,
});
}
});
app.post("/cloak/:cloak", async (req, res) => {
const { cloak } = req.params;
const { token } = req.query;
const { uuid } = await getPlayerData(token as string);
const player = await players.findOne({ uuid });
if (!player) {
res.status(400).send({
success: false,
error: "Player not found",
});
}
if (!validCloaks.includes(cloak as Cloak)) {
res.status(400).send({
success: false,
error: "Invalid cloak",
});
return;
}
if (!player.cloaks.includes(cloak as Cloak)) {
res.status(400).send({
success: false,
error: "You don't have this cloak",
});
return;
}
await players.updateOne({ uuid }, { $set: { cloak: cloak as Cloak } });
console.log("Cloak set to " + cloak + " for " + uuid);
res.send({ success: true });
});
app.post("/cloak/", async (req, res) => {
const { token } = req.query;
const { uuid } = await getPlayerData(token as string);
const player = await players.findOne({ uuid });
if (!player) {
res.status(400).send({
success: false,
error: "Player not found",
});
}
await players.updateOne({ uuid }, { $set: { cloak: "" } });
console.log("Cloak set to none for " + uuid);
res.send({ success: true });
});
app.listen(3000, () => {
console.log(`listening as http://localhost:${port}/`);
});
+33
View File
@@ -0,0 +1,33 @@
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"),
}
}
}
+68
View File
@@ -0,0 +1,68 @@
pub mod assets;
pub mod io;
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/";
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")?;
println!("Server listening on port 8080");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
println!("New connection: {}", stream.peer_addr().unwrap());
thread::spawn({
let database = Arc::clone(&database);
move || handle_client(stream, database)
});
}
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");
}
+14
View File
@@ -0,0 +1,14 @@
use std::{collections::HashMap, sync::Arc};
use crate::io::{Database, Response};
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()),
}
}
+29
View File
@@ -0,0 +1,29 @@
use std::{collections::HashMap, str::FromStr};
pub trait ParamMap {
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, String>;
}
impl ParamMap for HashMap<String, String> {
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, String> {
match self.get(param) {
Some(value) => T::from_str(value).map_err(|_| format!("Invalid parameter: {}", param)),
None => Err(format!("Parameter not found: {}", param)),
}
}
}
pub fn parse(s: &str) -> Result<(String, HashMap<String, String>), String> {
let mut parts = s.split("@");
let method = parts.next().unwrap().trim().to_lowercase();
let params = parts
.map(|p| {
let parts = p.split("=").collect::<Vec<&str>>();
(parts[0].trim().to_string(), parts[1].trim().to_string())
})
.collect::<HashMap<String, String>>();
Ok((method, params))
}
-16
View File
@@ -1,16 +0,0 @@
import { config } from "dotenv";
config();
function notFound(): string {
throw "'MONGO_URL' not found in .env";
}
export const mongoUrl = process.env.MONGO_URL || notFound();
export type Cloak = "glitch" | "forrest" | "";
export interface Player {
uuid: string;
cloak: Cloak;
cloaks: Cloak[];
}
-3
View File
@@ -1,3 +0,0 @@
await fetch("http://localhost:3000/player/d362a042-28bd-49e1-a807-ae74dbe8aba9", {
method: "GET",
});
-13
View File
@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "NodeNext",
"esModuleInterop": true,
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"outDir": "dist/"
},
"include": ["src/**/*", "test.ts"],
"exclude": ["**/*.spec.ts"]
}