Added motd.rs
This commit is contained in:
parent
ec81b13385
commit
303aa30074
|
@ -10,6 +10,7 @@ mod client;
|
||||||
mod listener;
|
mod listener;
|
||||||
mod whitelist;
|
mod whitelist;
|
||||||
mod info_messages;
|
mod info_messages;
|
||||||
|
mod motd;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Yeahbut June 2024
|
||||||
|
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::Read;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
use rand::Rng;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
// Refresh every 60 minutes
|
||||||
|
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
||||||
|
|
||||||
|
struct CachedMotds {
|
||||||
|
motd_data: Value,
|
||||||
|
timestamp: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_motds() -> Value {
|
||||||
|
let file_path = "./motd.json";
|
||||||
|
|
||||||
|
let data = match fs::read_to_string(file_path) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(_) => return Value::Null,
|
||||||
|
};
|
||||||
|
|
||||||
|
let motd_data: Value = match serde_json::from_str(&data) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Value::Null,
|
||||||
|
};
|
||||||
|
|
||||||
|
motd_data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_motds() -> Value {
|
||||||
|
lazy_static! {
|
||||||
|
static ref MOTDS_CACHE: Arc<Mutex<Option<CachedMotds>>> =
|
||||||
|
Arc::new(Mutex::new(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cache = MOTDS_CACHE.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(cached_motds) = cache.as_ref() {
|
||||||
|
if cached_motds.timestamp.elapsed() >= EXPIRATION_DURATION {
|
||||||
|
println!("Refreshing MOTD cache");
|
||||||
|
*cache = Some(CachedMotds {
|
||||||
|
motd_data: load_motds(),
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*cache = Some(CachedMotds {
|
||||||
|
motd_data: load_motds(),
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let motds = cache.as_ref().unwrap().motd_data.clone();
|
||||||
|
|
||||||
|
std::mem::drop(cache);
|
||||||
|
|
||||||
|
motds
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn motd() -> String {
|
||||||
|
let default = "A Minecraft Server Proxy".to_string();
|
||||||
|
|
||||||
|
let motd_data = get_motds();
|
||||||
|
|
||||||
|
if motd_data == Value::Null {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
let length1 = motd_data["line1"].as_array().map_or(0, |v| v.len());
|
||||||
|
let length2 = motd_data["line2"].as_array().map_or(0, |v| v.len());
|
||||||
|
|
||||||
|
if length1 == 0 || length2 == 0 {
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let rand1 = rng.gen_range(0..length1) as usize;
|
||||||
|
let rand2 = rng.gen_range(0..length2) as usize;
|
||||||
|
|
||||||
|
let line1: &str = match motd_data["line1"][rand1].as_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return default,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Birthdays, Holidays, and Announcements
|
||||||
|
|
||||||
|
let line2: &str = match motd_data["line2"][rand2].as_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return default,
|
||||||
|
};
|
||||||
|
|
||||||
|
let line: String = format!("{}\n{}", line1, line2);
|
||||||
|
line
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn favicon() -> Option<String> {
|
||||||
|
let file_path = "./icon.png";
|
||||||
|
|
||||||
|
let mut file = match File::open(file_path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
if let Err(_) = file.read_to_end(&mut buffer) {
|
||||||
|
return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let base64_string = general_purpose::STANDARD_NO_PAD.encode(buffer);
|
||||||
|
let full_string: String =
|
||||||
|
format!("data:image/png;base64,{}", base64_string);
|
||||||
|
|
||||||
|
Some(full_string)
|
||||||
|
}
|
|
@ -1,15 +1,6 @@
|
||||||
// Yeahbut December 2023
|
// Yeahbut December 2023
|
||||||
|
|
||||||
use std::fs::{self, File};
|
|
||||||
use std::io::Read;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use serde_json::Value;
|
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
|
||||||
use rand::Rng;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use purple_cello_mc_protocol::{
|
use purple_cello_mc_protocol::{
|
||||||
mc_types::{self, Result, Packet, ProtocolConnection},
|
mc_types::{self, Result, Packet, ProtocolConnection},
|
||||||
|
@ -19,13 +10,7 @@ use purple_cello_mc_protocol::{
|
||||||
|
|
||||||
use crate::listener;
|
use crate::listener;
|
||||||
use crate::info_messages;
|
use crate::info_messages;
|
||||||
|
use crate::motd::{motd, favicon};
|
||||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(3600);
|
|
||||||
|
|
||||||
struct CachedMotds {
|
|
||||||
motd_data: Value,
|
|
||||||
timestamp: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn online_players(
|
async fn online_players(
|
||||||
proxy_info: listener::ProxyInfo,
|
proxy_info: listener::ProxyInfo,
|
||||||
|
@ -34,108 +19,6 @@ async fn online_players(
|
||||||
Ok(get_upstream_status(proxy_info, server_conn).await?.players)
|
Ok(get_upstream_status(proxy_info, server_conn).await?.players)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_motds() -> Value {
|
|
||||||
let file_path = "./motd.json";
|
|
||||||
|
|
||||||
let data = match fs::read_to_string(file_path) {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(_) => return Value::Null,
|
|
||||||
};
|
|
||||||
|
|
||||||
let motd_data: Value = match serde_json::from_str(&data) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(_) => return Value::Null,
|
|
||||||
};
|
|
||||||
|
|
||||||
motd_data
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_motds() -> Value {
|
|
||||||
lazy_static! {
|
|
||||||
static ref MOTDS_CACHE: Arc<Mutex<Option<CachedMotds>>> =
|
|
||||||
Arc::new(Mutex::new(None));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cache = MOTDS_CACHE.lock().unwrap();
|
|
||||||
|
|
||||||
if let Some(cached_motds) = cache.as_ref() {
|
|
||||||
if cached_motds.timestamp.elapsed() >= EXPIRATION_DURATION {
|
|
||||||
println!("Refreshing MOTD cache");
|
|
||||||
*cache = Some(CachedMotds {
|
|
||||||
motd_data: load_motds(),
|
|
||||||
timestamp: Instant::now(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*cache = Some(CachedMotds {
|
|
||||||
motd_data: load_motds(),
|
|
||||||
timestamp: Instant::now(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let motds = cache.as_ref().unwrap().motd_data.clone();
|
|
||||||
|
|
||||||
std::mem::drop(cache);
|
|
||||||
|
|
||||||
motds
|
|
||||||
}
|
|
||||||
|
|
||||||
fn motd() -> String {
|
|
||||||
let default = "A Minecraft Server Proxy".to_string();
|
|
||||||
|
|
||||||
let motd_data = get_motds();
|
|
||||||
|
|
||||||
if motd_data == Value::Null {
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
let length1 = motd_data["line1"].as_array().map_or(0, |v| v.len());
|
|
||||||
let length2 = motd_data["line2"].as_array().map_or(0, |v| v.len());
|
|
||||||
|
|
||||||
if length1 == 0 || length2 == 0 {
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let rand1 = rng.gen_range(0..length1) as usize;
|
|
||||||
let rand2 = rng.gen_range(0..length2) as usize;
|
|
||||||
|
|
||||||
let line1: &str = match motd_data["line1"][rand1].as_str() {
|
|
||||||
Some(s) => s,
|
|
||||||
None => return default,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Birthdays, Holidays, and Announcements
|
|
||||||
|
|
||||||
let line2: &str = match motd_data["line2"][rand2].as_str() {
|
|
||||||
Some(s) => s,
|
|
||||||
None => return default,
|
|
||||||
};
|
|
||||||
|
|
||||||
let line: String = format!("{}\n{}", line1, line2);
|
|
||||||
line
|
|
||||||
}
|
|
||||||
|
|
||||||
fn favicon() -> Option<String> {
|
|
||||||
let file_path = "./icon.png";
|
|
||||||
|
|
||||||
let mut file = match File::open(file_path) {
|
|
||||||
Ok(file) => file,
|
|
||||||
Err(_) => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
if let Err(_) = file.read_to_end(&mut buffer) {
|
|
||||||
return None
|
|
||||||
};
|
|
||||||
|
|
||||||
let base64_string = general_purpose::STANDARD_NO_PAD.encode(buffer);
|
|
||||||
let full_string: String =
|
|
||||||
format!("data:image/png;base64,{}", base64_string);
|
|
||||||
|
|
||||||
Some(full_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn respond_status(
|
pub async fn respond_status(
|
||||||
proxy_info: listener::ProxyInfo,
|
proxy_info: listener::ProxyInfo,
|
||||||
client_conn: &mut ProtocolConnection<'_>,
|
client_conn: &mut ProtocolConnection<'_>,
|
||||||
|
|
|
@ -6,7 +6,8 @@ use serde_json::Value;
|
||||||
|
|
||||||
use crate::info_messages;
|
use crate::info_messages;
|
||||||
|
|
||||||
const EXPIRATION_DURATION: Duration = Duration::from_secs(60);
|
// Refresh every 5 minutes
|
||||||
|
const EXPIRATION_DURATION: Duration = Duration::from_secs(300);
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
|
|
Loading…
Reference in New Issue