Continued work on everything.

Replace diesel with sqlx
Cleaned up config.rs
Slight changes to response handling (still needs a rewrite)
This commit is contained in:
Funtimes909
2025-02-18 17:39:32 +13:00
parent aa06a5ebee
commit 579dcc0d52
10 changed files with 1521 additions and 232 deletions
Generated
+1443 -80
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,5 +9,5 @@ serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.138"
toml = "0.8.19"
tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] }
diesel = { version = "2.2.7", features = ["postgres", "numeric"] }
bigdecimal = "0.4.7"
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio"] }
-9
View File
@@ -1,9 +0,0 @@
# For documentation on how to configure this file,
# see https://diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
[migrations_directory]
dir = "/home/funtimes909/Documents/Code/ServerSeekerV2-Rescanner/migrations"
+7 -7
View File
@@ -17,19 +17,19 @@ pub struct Database {
#[derive(Deserialize)]
pub struct Rescanner {
pub rescan_delay: u16,
pub speed: u16,
pub repeat: bool,
pub port_range: String
pub rescan_delay: u16,
pub port_range_start: u16,
pub port_range_end: u16,
}
impl Default for Rescanner {
fn default() -> Self {
Self {
rescan_delay: 60,
speed: 1000,
repeat: true,
port_range: "25560 - 25570".to_string()
rescan_delay: 60,
port_range_start: 25560,
port_range_end: 25570,
}
}
}
@@ -47,5 +47,5 @@ pub fn load_config(path: String) -> Config {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap_or_default();
toml::from_str(&contents).unwrap()
toml::from_str(&contents).expect("Failed to parse config!")
}
+46 -4
View File
@@ -1,7 +1,49 @@
use sqlx::{Connection, Error, PgConnection, Postgres, QueryBuilder, Row};
use sqlx::postgres::PgQueryResult;
use crate::response::Server;
use diesel::{Connection, PgConnection};
use diesel::connection::SimpleConnection;
pub fn connect(database_url: &str) -> PgConnection {
PgConnection::establish(&database_url).unwrap_or_else(|err| {panic!("Fatal Error! {}", err)})
pub async fn connect(database_url: &str) -> PgConnection {
PgConnection::connect(&database_url).await.unwrap()
}
// TODO! Return a stream of results instead of a Vec for performance
pub async fn fetch_servers(conn: &mut PgConnection) -> Vec<String> {
// Sort results by
sqlx::query("SELECT address FROM servers ORDER BY lastseen ASC")
.fetch_all(conn)
.await
.unwrap()
.iter()
.map(|row| {
row.try_get(0).unwrap()
})
.collect()
}
pub async fn update_server(server: &Server, conn: &mut PgConnection, address: &str) -> Result<PgQueryResult, Error> {
let mut query: QueryBuilder<Postgres> = QueryBuilder::new("UPDATE servers SET");
query.push(" version = ");
query.push_bind(&server.version);
query.push(", protocol = ");
query.push_bind(server.protocol);
query.push(", icon = ");
query.push_bind(&server.icon);
query.push(", motd = ");
query.push_bind(&server.motd);
query.push(", preventsreports = ");
query.push_bind(server.prevents_reports);
query.push(", enforcesecure = ");
query.push_bind(server.enforces_secure_chat);
query.push(" WHERE address = ");
query.push_bind(address);
let mut query = query.build();
query.execute(conn).await
}
+17 -11
View File
@@ -2,15 +2,14 @@ mod database;
mod config;
mod ping;
mod response;
mod models;
mod schema;
use std::io::Error;
use config::{load_config, Config};
use database::connect;
use diesel::prelude::*;
use std::io::{Read, Write};
use std::str::FromStr;
use crate::database::{connect, fetch_servers, update_server};
use crate::ping::ping_server;
use crate::response::Server;
#[tokio::main]
async fn main() {
@@ -24,13 +23,20 @@ async fn main() {
config.database.password,
config.database.url);
// let address: &str = "192.168.1.220";
let address: &str = "51.161.87.128";
// let address: &str = "127.0.0.1";
let port: u16 = 25565;
let server = ping_server(&address, &port).await;
let mut conn = connect(database_url.as_str()).await;
let servers = fetch_servers(&mut conn).await;
if let Ok(server) = server {
println!("Server {:?} responded! Version: {:?}, MOTD: {:?}, Chat Reports: {:?}", &address, &server.version, &server.motd, &server.prevents_reports);
for address in servers {
println!("Pinging: {}", &address);
let server_response: Result<Server, Error> = ping_server(&address, 25565u16).await;
if let Ok(server) = server_response {
if let Err(error) = update_server(&server, &mut conn, &*address).await {
println!("Failed to update server: {}", error);
} else {
println!("Server updated");
}
}
}
}
-30
View File
@@ -1,30 +0,0 @@
use diesel::prelude::*;
#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::servers)]
#[diesel(check_for_backend(diesel::pg::Pg))]
#[allow(dead_code)]
pub struct Server {
pub id: i32,
pub address: String,
pub port: bigdecimal::BigDecimal,
pub firstseen: i32,
pub lastseen: i32,
pub country: String,
pub asn: String,
pub reversedns: String,
pub organization: String,
pub version: String,
pub protocol: i32,
pub fmlnetworkversion: i32,
pub motd: String,
pub icon: String,
pub timesseen: i32,
pub preventsreports: bool,
pub enforcesecure: bool,
pub whitelist: bool,
pub cracked: bool,
pub maxplayers: i32,
pub onlineplayers: i32,
pub software: String,
}
+2 -3
View File
@@ -15,7 +15,7 @@ const REQUEST: [u8; 9] = [
0, // ID
];
pub async fn ping_server(address: &str, port: &u16) -> Result<Server, Error> {
pub async fn ping_server(address: &str, port: u16) -> Result<Server, Error> {
let address = format!("{}:{}", address, port);
let socket = SocketAddr::from_str(address.as_str()).unwrap();
@@ -45,8 +45,7 @@ pub async fn ping_server(address: &str, port: &u16) -> Result<Server, Error> {
let response: String = String::from_utf8_lossy(
&out_buff[
(buff_size.1 + 1 + json_bytes.1).into()
..
]
..]
).to_string();
Ok(parse_response(response.as_str())?)
+5 -60
View File
@@ -1,6 +1,5 @@
use std::io::{Error, ErrorKind};
use std::str::FromStr;
use diesel::row::NamedRow;
use serde_json::Value;
pub struct Server {
@@ -12,6 +11,8 @@ pub struct Server {
pub enforces_secure_chat: Option<bool>,
pub mods: Option<Vec<Mod>>,
pub players: Option<Vec<Player>>,
pub online_players: Option<i32>,
pub max_players: Option<i32>,
}
pub struct Player {
@@ -25,53 +26,6 @@ pub struct Mod {
pub mod_name: String,
}
// Lifetimes *should* solve this mess
// Recursive JSON array/object parser
fn parse_motd(motd: &Value, mut current_motd: String) -> Result<String, Error> {
let mut current_motd = current_motd.clone();
if let Some(object) = motd.as_object() {
if object.contains_key("color") {
&current_motd.push_str(&object["color"].to_string());
}
if object.contains_key("bold") {
&current_motd.push_str("§l");
}
if object.contains_key("underlined") {
&current_motd.push_str("§n");
}
if object.contains_key("text") {
if let Some(text) = object["text"].as_str() {
current_motd.push_str(text);
}
}
if object.contains_key("extra") {
match object.get("extra") {
Some(extra) => parse_motd(extra, current_motd.clone())?,
None => Err(Error::new(ErrorKind::InvalidData, "Extra array malformed"))?,
};
}
Ok(current_motd)
} else {
if let Some(array) = motd.as_array() {
for value in array {
if value.is_object() || value.is_array() {
parse_motd(value, current_motd.clone())?;
} else {
&current_motd.push_str(value.as_str().unwrap());
}
}
}
Ok(current_motd)
}
}
// I no longer care about trying to make the parsing code good,
// there is no proper standard for how servers should respond so this is what you get
pub fn parse_response(response: &str) -> Result<Server, Error> {
@@ -83,13 +37,9 @@ pub fn parse_response(response: &str) -> Result<Server, Error> {
let mut version: Option<String> = None;
let mut protocol: Option<i64> = None;
let mut icon: Option<String> = None;
let mut motd: Option<String> = None;
let mut prevents_reports: Option<bool> = None;
let mut enforces_secure_chat: Option<bool> = None;
let mut mods: Option<Vec<Mod>> = None;
let mut players: Option<Vec<Player>> = None;
// TODO! I think I can make this all better
if let Some(value) = json.get("version") {
if let Some(name) = value.get("name") {
version = name.as_str().map(|s| s.to_string());
@@ -104,13 +54,6 @@ pub fn parse_response(response: &str) -> Result<Server, Error> {
icon = value.as_str().map(|s| s.to_string())
}
if let Some(value) = json.get("description") {
motd = match parse_motd(value, "".to_string()) {
Ok(description) => Some(description),
Err(_) => None,
};
}
if let Some(value) = json.get("preventsChatReports") {
prevents_reports = value.as_bool();
}
@@ -123,10 +66,12 @@ pub fn parse_response(response: &str) -> Result<Server, Error> {
version,
protocol,
icon,
motd,
motd: None,
prevents_reports,
enforces_secure_chat,
mods: None,
players: None,
online_players: None,
max_players: None,
})
}
-27
View File
@@ -1,27 +0,0 @@
diesel::table! {
servers (id) {
id -> Integer,
address -> Text,
port -> Numeric,
firstseen -> Integer,
lastseen -> Integer,
country -> Text,
asn -> Text,
reversedns -> Text,
organization -> Text,
version -> Text,
protocol -> Integer,
fmlnetworkversion -> Integer,
motd -> Text,
icon -> Text,
timesseen -> Integer,
preventsreports -> Bool,
enforcesecure -> Bool,
whitelist -> Bool,
cracked -> Bool,
maxplayers -> Integer,
onlineplayers -> Integer,
#[sql_name = "type"]
software -> Text,
}
}