From 0ae35b81328690e429df5336bde6bb05ce23b866 Mon Sep 17 00:00:00 2001 From: slayernominee Date: Tue, 9 Jan 2024 15:13:06 +0100 Subject: [PATCH] listing player for dashboard, player quick actions (zap, ban, kick) --- README.md | 2 +- app/dashboard/page.tsx | 161 +++++++++++++++++++++++++---------------- dash/src/main.rs | 74 ++++++++++++++++++- 3 files changed, 172 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 02399ea..1437e81 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A free & opensource Minecraft Dashboard to control your server - [ ] Backend Modifiable Cors - [ ] File Editor - [ ] Explorer Feature: Duplicate, Download, Move -- [ ] Dashboard: Kick, Ban, Get World, Get Gamemode, Change Gamemode, List Players +- [ ] Dashboard: Get World, Get Gamemode # Setup diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 061b0ca..362002c 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -75,10 +75,24 @@ async function check_if_setup() { return data } +async function get_players() { + const data = await fetch(process.env.NEXT_PUBLIC_API_URL + '/api/list_players', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + localStorage.getItem('token'), + } + }).then((res) => res.json()) + return data +} + export default function Home() { const [is_running, set_is_running] = useState(false) const [fetched_running, set_fetched_running] = useState(false) + + const [players, setPlayers] = useState([]) + + const [max_count, set_max_count] = useState(0) useEffect(() => { @@ -92,6 +106,24 @@ export default function Home() { set_is_running(is_running) set_fetched_running(true) }) + + get_players().then((player_data) => { + setPlayers(player_data.players) + set_max_count(player_data.max) + }) + + setInterval(() => { + get_is_running().then((is_running) => { + set_is_running(is_running) + set_fetched_running(true) + }) + + get_players().then((player_data) => { + setPlayers(player_data.players) + set_max_count(player_data.max) + }) + }, 5000) + }, []) const switch_running_handler = () => { @@ -115,6 +147,70 @@ export default function Home() { } + const playerList = players.map((player_name, idx) => + + { player_name } + + + + World + + + + + + + + +

Hit the player with a lightning bolt

+
+
+
+ + + + + + + + +

Kick Player

+
+
+
+ + + + + + + +

Ban Player

+
+
+
+ + +
+
+); + return (

General

@@ -141,8 +237,7 @@ export default function Home() {

Players

- {1} / {20} Players are online - + {players.length} / {max_count} Players are online @@ -155,67 +250,7 @@ export default function Home() { - - MR12345 - - - - Nether - - - - - - - - -

Hit the player with a lightning bolt

-
-
-
- - - - - - - - -

Kick Player

-
-
-
- - - - - - - -

Ban Player

-
-
-
- - -
-
+ { playerList }
diff --git a/dash/src/main.rs b/dash/src/main.rs index d4c1f1d..6258be5 100644 --- a/dash/src/main.rs +++ b/dash/src/main.rs @@ -1,4 +1,5 @@ use actix_web::web::scope; +use serde::Serialize; use tokio::process::Command; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}; use actix::{ActorContext, AsyncContext, Message, Handler, Addr, StreamHandler, Actor}; @@ -21,6 +22,7 @@ static mut INP: Option>>> = None static mut AUTHENTICATED_SOCKETS: Option>>>> = None; static mut IS_RUNNING: bool = false; +static mut LAST_PLAYER_LIST_OUTPUT: Option = None; pub struct BroadcastMessage { pub data: String, @@ -43,7 +45,17 @@ async fn server_process() -> Result<(), Box> { let mut lines = stdout.lines(); while let Some(line) = lines.next_line().await.unwrap() { println!("{}", line); - + + if line.contains("players online") { + unsafe { + if let Some(last_output) = &mut LAST_PLAYER_LIST_OUTPUT { + *last_output = line.clone(); + } else { + LAST_PLAYER_LIST_OUTPUT = Some(line.clone()); + } + } + } + let data = line.to_string(); // Replace with your actual data unsafe { if let Some(authenticated_sockets) = &AUTHENTICATED_SOCKETS { @@ -213,6 +225,63 @@ async fn is_running() -> HttpResponse { } +#[derive(Debug, Serialize)] +struct PlayerListing { + count: u32, + max: u32, + players: Vec, +} + +async fn get_players() -> HttpResponse { + let is_running = unsafe { IS_RUNNING }; + if !is_running { + let players = PlayerListing { + count: 0, + max: 0, + players: vec![], + }; + return HttpResponse::Ok().json(players); + } + + cmd_handler("list".to_string()); + + let players: String; + + tokio::time::sleep(Duration::from_millis(100)).await; + + unsafe { + if let Some(last_output) = &LAST_PLAYER_LIST_OUTPUT { + players = last_output.clone().split("INFO]: There are ").collect::>()[1].to_string(); + } else { + let players = PlayerListing { + count: 0, + max: 0, + players: vec![], + }; + return HttpResponse::Ok().json(players); + } + } + + // "0 of a max of 20 players online: " + let count = players.split(" of a max of ").collect::>()[0].parse::().unwrap(); + let max = players.split(" of a max of ").collect::>()[1].split(" players online:").collect::>()[0].parse::().unwrap(); + let mut players = players.split(" players online: ").collect::>()[1].split(", ").map(|s| s.to_string()).collect::>(); + + if players.len() == 1 && players[0] == "" { + players = vec![]; + } + + let players = PlayerListing { + count, + max, + players, + }; + + println!("{:?}", players); + + HttpResponse::Ok().json(players) +} + async fn switch_running() -> HttpResponse { let is_running = unsafe { IS_RUNNING }; if is_running { @@ -267,9 +336,12 @@ async fn main() -> std::io::Result<()> { // api routes .service(scope("/api") .wrap(tokencheck::TokenCheck) + .route("/is_running", web::post().to(is_running)) .route("/switch_running", web::post().to(switch_running)) .route("/execute_command", web::post().to(execute_command)) + .route("/list_players", web::post().to(get_players)) + .service(files::get_files) .service(files::upload) .service(files::rename)