Compare commits
7 commits
f3908219cd
...
2d15914d1a
Author | SHA1 | Date | |
---|---|---|---|
2d15914d1a | |||
82aae16766 | |||
80c884f489 | |||
4358ec6483 | |||
6bae7ef997 | |||
fa1d23cb3e | |||
77ad597963 |
19 changed files with 265 additions and 21 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
/target
|
||||
.env
|
||||
|
||||
.env
|
||||
config.toml
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -408,11 +408,13 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"diesel",
|
||||
"dotenvy",
|
||||
"maplit",
|
||||
"password-auth",
|
||||
"regex",
|
||||
"rocket",
|
||||
"serde",
|
||||
"sha",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -654,6 +656,12 @@ dependencies = [
|
|||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -6,8 +6,10 @@ edition = "2021"
|
|||
[dependencies]
|
||||
diesel = { version = "=2.1.0", features = ["postgres"] }
|
||||
dotenvy = "0.15.7"
|
||||
maplit = "1.0.2"
|
||||
password-auth = "0.3.0"
|
||||
regex = "1.9.4"
|
||||
rocket = "=0.5.0-rc.3"
|
||||
serde = { version = "1.0.188", features = ["derive"] }
|
||||
sha = "1.0.3"
|
||||
toml = "0.7.6"
|
||||
|
|
19
config.example.toml
Normal file
19
config.example.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[general]
|
||||
# if this path is encountered during path traversal,
|
||||
# it will be removed. this is useful for instances
|
||||
# where your absolute domain path is not long enough
|
||||
# to replace boomlings.com, because you can then point
|
||||
# it at a different, longer path to fill the gap
|
||||
#
|
||||
# example:
|
||||
# boomlings.com/database/
|
||||
# example.com/aaaaaaaaaa/
|
||||
# ^^^^^^^^^^^
|
||||
# leaving as "/" will disable this
|
||||
append_path = "/"
|
||||
# where can your server be accessible?
|
||||
port = 8000
|
||||
|
||||
[accounts]
|
||||
# allow new accounts to be created
|
||||
allow_registration = true
|
|
@ -28,7 +28,6 @@ CREATE TABLE users (
|
|||
wave INTEGER NOT NULL DEFAULT 0,
|
||||
robot INTEGER NOT NULL DEFAULT 0,
|
||||
spider INTEGER NOT NULL DEFAULT 0,
|
||||
swing_copter INTEGER NOT NULL DEFAULT 0,
|
||||
explosion INTEGER NOT NULL DEFAULT 0,
|
||||
special INTEGER NOT NULL DEFAULT 0,
|
||||
glow INTEGER NOT NULL DEFAULT 0,
|
||||
|
|
13
readme.md
13
readme.md
|
@ -1,10 +1,8 @@
|
|||
# gdps-server
|
||||
|
||||
a [Geometry Dash](https://store.steampowered.com/app/322170/Geometry_Dash/) server reimplementation in [Rust](https://rust-lang.org), focusing on 1:1 recreations of vanilla GD features
|
||||
a [Geometry Dash](https://store.steampowered.com/app/322170/Geometry_Dash/) server reimplementation in [Rust](https://rust-lang.org)
|
||||
|
||||
_this project is in early stages. it is NOT production ready._
|
||||
|
||||
_ONLY 2.2 is supported._
|
||||
this project is based off of (stolen from) the [crystal-gauntlet](https://git.oat.zone/oat/crystal-gauntlet) server
|
||||
|
||||
## why?
|
||||
|
||||
|
@ -28,7 +26,7 @@ _these features are implemented_
|
|||
|
||||
### testing
|
||||
|
||||
- run `cargo run run`
|
||||
- run `cargo run`
|
||||
|
||||
### building
|
||||
|
||||
|
@ -36,5 +34,6 @@ _these features are implemented_
|
|||
|
||||
## todo
|
||||
|
||||
- add login endpoint....... NOW!
|
||||
- our passwords are a little insecure (`argon2(sha1(password + "mI29fmAnxgTs"))`) and there isnt anything we can do about this because gpj2 is forced like that!! thanks robtop!! (try and find a fix anyway lul)
|
||||
- ___fix the fucking get users for usernames!!!!___
|
||||
- move authorization logic to (./src/helpers/accounts.rs)[./src/helpers/accounts.rs]
|
||||
- make gjp2 authentication faster (bcrypt?)
|
34
src/config.rs
Normal file
34
src/config.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Config {
|
||||
pub general: ConfigGeneral,
|
||||
pub accounts: ConfigAccounts
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigGeneral {
|
||||
pub append_path: String,
|
||||
pub port: u16
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigAccounts {
|
||||
pub allow_registration: bool
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load_from_file(file_path: &str) -> Self {
|
||||
let toml_str = fs::read_to_string(file_path).expect("Error finding toml config:");
|
||||
let config: Config = toml::from_str(toml_str.as_str()).expect("Error parsing toml config:");
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
pub static CONFIG: LazyLock<Config> = LazyLock::new(|| {
|
||||
let config = Config::load_from_file("config.toml");
|
||||
config
|
||||
});
|
|
@ -62,7 +62,6 @@ pub struct User {
|
|||
pub wave: i32,
|
||||
pub robot: i32,
|
||||
pub spider: i32,
|
||||
pub swing_copter: i32,
|
||||
pub explosion: i32,
|
||||
pub special: i32,
|
||||
pub glow: i32,
|
||||
|
|
|
@ -47,7 +47,6 @@ diesel::table! {
|
|||
wave -> Int4,
|
||||
robot -> Int4,
|
||||
spider -> Int4,
|
||||
swing_copter -> Int4,
|
||||
explosion -> Int4,
|
||||
special -> Int4,
|
||||
glow -> Int4,
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod accounts;
|
||||
pub mod accounts;
|
||||
pub mod users;
|
|
@ -1 +1,2 @@
|
|||
pub mod login_account;
|
||||
pub mod register_account;
|
55
src/endpoints/accounts/login_account.rs
Normal file
55
src/endpoints/accounts/login_account.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use password_auth::verify_password;
|
||||
use rocket::form::Form;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::status;
|
||||
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::helpers;
|
||||
use crate::db;
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FromLoginAccount {
|
||||
userName: String,
|
||||
password: String
|
||||
}
|
||||
|
||||
#[post("/accounts/loginGJAccount.php", data = "<input>")]
|
||||
pub fn login_account(input: Form<FromLoginAccount>) -> status::Custom<&'static str> {
|
||||
let connection = &mut db::establish_connection_pg();
|
||||
|
||||
if input.userName != helpers::clean::clean(input.userName.as_ref()) {
|
||||
return status::Custom(Status::Ok, "-4")
|
||||
}
|
||||
|
||||
if input.password.len() < 6 {
|
||||
return status::Custom(Status::Ok, "-8")
|
||||
}
|
||||
|
||||
if input.userName.len() < 3 {
|
||||
return status::Custom(Status::Ok, "-9")
|
||||
}
|
||||
|
||||
{
|
||||
use crate::schema::accounts::dsl::*;
|
||||
|
||||
let account_id_gjp2_result = accounts
|
||||
.select((id, gjp2))
|
||||
.filter(username.eq(input.userName.clone()))
|
||||
.get_result::<(i32, String)>(connection);
|
||||
|
||||
match account_id_gjp2_result {
|
||||
Ok(account_id_gjp2) => {
|
||||
let user_id = helpers::accounts::get_user_id_from_account_id(account_id_gjp2.0);
|
||||
|
||||
match verify_password(helpers::gjp2::get_gjp2(input.password.clone()).as_bytes(), account_id_gjp2.1.as_str()) {
|
||||
Ok(_) => return status::Custom(Status::Ok,
|
||||
Box::leak(format!("{},{}", account_id_gjp2.0, user_id).into_boxed_str())
|
||||
),
|
||||
Err(_) => return status::Custom(Status::Ok, "-11")
|
||||
};
|
||||
},
|
||||
Err(_) => return status::Custom(Status::Ok, "-1")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ use rocket::response::status;
|
|||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
|
||||
use crate::CONFIG;
|
||||
use crate::helpers;
|
||||
use crate::db;
|
||||
|
||||
|
@ -15,9 +16,13 @@ pub struct FormRegisterAccount {
|
|||
email: String
|
||||
}
|
||||
|
||||
#[post("/memaddrefix/accounts/registerGJAccount.php", data = "<input>")]
|
||||
#[post("/accounts/registerGJAccount.php", data = "<input>")]
|
||||
pub fn register_account(input: Form<FormRegisterAccount>) -> status::Custom<&'static str> {
|
||||
let connection = &mut db::establish_connection_pg();
|
||||
|
||||
if CONFIG.accounts.allow_registration == false {
|
||||
return status::Custom(Status::Ok, "-1")
|
||||
}
|
||||
|
||||
if input.userName != helpers::clean::clean(input.userName.as_ref()) {
|
||||
return status::Custom(Status::Ok, "-4")
|
||||
|
|
1
src/endpoints/users.rs
Normal file
1
src/endpoints/users.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod get_users;
|
74
src/endpoints/users/get_users.rs
Normal file
74
src/endpoints/users/get_users.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use rocket::form::Form;
|
||||
use rocket::http::Status;
|
||||
use rocket::response::status;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
|
||||
use crate::helpers;
|
||||
use crate::db;
|
||||
|
||||
#[derive(FromForm)]
|
||||
pub struct FormGetUsers {
|
||||
page: i64,
|
||||
str: String
|
||||
}
|
||||
|
||||
#[post("/accounts/getGJUsers20.php", data = "<input>")]
|
||||
pub fn get_users(input: Form<FormGetUsers>) -> status::Custom<&'static str> {
|
||||
let connection = &mut db::establish_connection_pg();
|
||||
|
||||
// query users
|
||||
use crate::schema::users::dsl::*;
|
||||
use crate::models::User;
|
||||
|
||||
let mut query = users.into_boxed();
|
||||
|
||||
match input.str.parse::<i32>() {
|
||||
Ok(id_value) => query = query.filter(id.eq(id_value)),
|
||||
Err(_) => query = query.filter(username.like(input.str.to_owned() + "%"))
|
||||
};
|
||||
|
||||
let results = query
|
||||
.order(stars.desc())
|
||||
.limit(10)
|
||||
.offset(input.page * 10)
|
||||
.get_result::<User, >(connection)
|
||||
.expect("Fatal error loading users");
|
||||
|
||||
let response = helpers::format::format(hashmap! {
|
||||
1 => results.username,
|
||||
2 => results.id.to_string(),
|
||||
3 => results.stars.to_string(),
|
||||
4 => results.demons.to_string(),
|
||||
8 => results.creator_points.to_string(),
|
||||
9 => {
|
||||
vec![
|
||||
results.cube,
|
||||
results.ship,
|
||||
results.ball,
|
||||
results.ufo,
|
||||
results.wave,
|
||||
results.robot
|
||||
][results.icon_type as usize].to_string()
|
||||
},
|
||||
10 => results.color1.to_string(),
|
||||
11 => results.color2.to_string(),
|
||||
13 => results.coins.to_string(),
|
||||
14 => results.icon_type.to_string(),
|
||||
15 => results.special.to_string(),
|
||||
16 => {
|
||||
match results.account_id {
|
||||
Some(account_id_value) => account_id_value.to_string(),
|
||||
None => match results.udid {
|
||||
Some(udid_value) => udid_value.to_string(),
|
||||
None => panic!("user has no account_id or udid?!?!?")
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
println!("{}", response);
|
||||
|
||||
return status::Custom(Status::Ok, "1")
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
pub mod gjp2;
|
||||
pub mod clean;
|
||||
pub mod accounts;
|
||||
pub mod clean;
|
||||
pub mod format;
|
||||
pub mod gjp2;
|
17
src/helpers/accounts.rs
Normal file
17
src/helpers/accounts.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use diesel::prelude::*;
|
||||
|
||||
use crate::db;
|
||||
|
||||
pub fn get_user_id_from_account_id(ext_id: i32) -> i32 {
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
let connection = &mut db::establish_connection_pg();
|
||||
|
||||
let user_id = users
|
||||
.filter(udid.eq(ext_id.to_string()).or(account_id.eq(ext_id)))
|
||||
.select(id)
|
||||
.get_result::<i32>(connection)
|
||||
.expect("No user associated with account?!?!?");
|
||||
|
||||
return user_id
|
||||
}
|
16
src/helpers/format.rs
Normal file
16
src/helpers/format.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
pub fn format(map: HashMap<i32, impl ToString>) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
for (k, v) in map {
|
||||
result.push_str(&format!("{}:{}", k, v.to_string()));
|
||||
result.push(':');
|
||||
}
|
||||
|
||||
if !result.is_empty() {
|
||||
result.pop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
23
src/main.rs
23
src/main.rs
|
@ -1,5 +1,7 @@
|
|||
#![feature(decl_macro)]
|
||||
#![feature(lazy_cell)]
|
||||
|
||||
#[macro_use] extern crate maplit;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
mod db;
|
||||
|
@ -11,16 +13,25 @@ use helpers::*;
|
|||
mod endpoints;
|
||||
use endpoints::*;
|
||||
|
||||
mod config;
|
||||
use config::*;
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> String {
|
||||
return String::from("index | coming soon to a localhost:8000 near u");
|
||||
return String::from("gdps-server | https://git.reidlab.online/reidlab/gdps-server");
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
rocket::build().mount("/", routes![
|
||||
index,
|
||||
|
||||
endpoints::accounts::register_account::register_account
|
||||
])
|
||||
rocket::build()
|
||||
.configure(rocket::Config::figment().merge(("port", CONFIG.general.port)))
|
||||
.mount("/", routes![
|
||||
index,
|
||||
])
|
||||
.mount(CONFIG.general.append_path.as_str(), routes![
|
||||
endpoints::accounts::login_account::login_account,
|
||||
endpoints::accounts::register_account::register_account,
|
||||
|
||||
endpoints::users::get_users::get_users
|
||||
])
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue