socket server with rust
This commit is contained in:
@@ -0,0 +1 @@
|
||||
pub const CLOAKS: &[&str] = &["glitch"];
|
||||
@@ -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
@@ -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}/`);
|
||||
});
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
Reference in New Issue
Block a user