Skip to content

Commit

Permalink
fix: limit game count
Browse files Browse the repository at this point in the history
  • Loading branch information
Romira915 committed Apr 5, 2023
1 parent abf86f0 commit 3aa6e37
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 36 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions homeserver_receive_process/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ version = "0.1.0"
actix-files = "0.6.0"
actix-web = "4.1.0"

anyhow = "1.0.70"
derive_builder = "0.10.2"
duct = "0.13.5"
getset = "0.1.1"
Expand Down
1 change: 1 addition & 0 deletions homeserver_receive_process/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod home_server_config;
pub mod models;

use derive_builder::Builder;
use getset::Getters;
Expand Down
230 changes: 194 additions & 36 deletions homeserver_receive_process/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,95 +1,250 @@
use std::{env, fs::File, io::Read, time::Duration};
use std::{
env,
fs::File,
io::{self, Read},
process::Output,
sync::{Arc, Mutex},
time::Duration,
};

use actix_files::Files;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use anyhow::{bail, Context, Result};
use duct::cmd;
use homeserver_receive_process::{home_server_config::Config, init_logger, Command};
use homeserver_receive_process::{
home_server_config::Config,
init_logger,
models::{Game, GameServerExecutingState},
Command,
};

const CONFIG_PATH: &str = ".config/home_server_config.toml";

async fn exec_systemctl(command: web::Json<Command>, service_name: &str) -> impl Responder {
async fn exec_systemctl(command: &web::Json<Command>, service: Game) -> Result<Output> {
#[allow(clippy::if_same_then_else)]
if let "start" | "status" = command.request().as_str() {
// start and status
} else if let ("stop" | "restart", true) = (command.request().as_str(), command.administrator())
{
// stop and restart with admin
} else {
return HttpResponse::MethodNotAllowed().body("Not Allowed command");
bail!("Not Allowed command");
}

let response = match cmd!("systemctl", &command.request(), service_name)
let request = if command.request() == "status" {
"is-active"
} else {
command.request()
};

let result = cmd!("systemctl", request, service.to_service_name())
.stdout_capture()
.stderr_capture()
.run()
{
.context("Failed to systemctl");

result
}

fn into_response_by_cmd_output(output: &Result<Output>) -> HttpResponse {
match output {
Ok(output) => {
let exit_code = output.status;
log::info!("exit code {}", exit_code);

if exit_code.success() {
let content = if command.request() == "status" {
let output = String::from_utf8(output.stdout).unwrap();
let mut split = output.split_whitespace();
split.position(|p| p == "Active:");

format!("{} {}", split.next().unwrap(), split.next().unwrap())
} else {
"Success".to_string()
};

HttpResponse::Ok().body(content)
HttpResponse::Ok().body("Success".to_string())
} else {
HttpResponse::ExpectationFailed()
.body(format!("Failed to systemctl\n{}", exit_code))
}
}
Err(e) => {
if command.request() == "status" {
HttpResponse::ExpectationFailed().body("inactive (dead)")
Err(e) => HttpResponse::ExpectationFailed().body(format!("Failed to cmd! macro\n{}", e)),
}
}

fn into_response_by_cmd_output_with_status(output: &Result<Output>) -> HttpResponse {
match output {
Ok(output) => {
let exit_code = output.status;
log::info!("exit code {}", exit_code);

if exit_code.success() {
HttpResponse::Ok().body("active".to_string())
} else {
HttpResponse::ExpectationFailed().body(format!("Failed to cmd! macro\n{}", e))
HttpResponse::ExpectationFailed().body("inactive".to_string())
}
}
};

response
Err(e) => HttpResponse::ExpectationFailed().body(format!("Failed to cmd! macro\n{}", e)),
}
}

#[post("/minecraft")]
async fn post_minecraft(command: web::Json<Command>) -> impl Responder {
async fn post_minecraft(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post minecraft");
exec_systemctl(command, "minecraft-server-mgpf.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::MinecraftServerMgpf).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().minecraft_server_mgpf = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().minecraft_server_mgpf = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[post("/sdtd")]
async fn post_sdtd(command: web::Json<Command>) -> impl Responder {
async fn post_sdtd(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post sdtd");
exec_systemctl(command, "sdtd-server.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::SdtdServer).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().sdtd_server = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().sdtd_server = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[post("/terraria")]
async fn post_terraria(command: web::Json<Command>) -> impl Responder {
async fn post_terraria(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post terraria");
exec_systemctl(command, "terraria-server.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::TerrariaServer).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().terraria_server = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().terraria_server = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[post("/ark-first")]
async fn post_ark(command: web::Json<Command>) -> impl Responder {
async fn post_ark(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post ark");
exec_systemctl(command, "ark-server.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::ArkServer).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().ark_server = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().ark_server = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[post("/ark-second")]
async fn post_ark_second(command: web::Json<Command>) -> impl Responder {
async fn post_ark_second(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post ark-second");
exec_systemctl(command, "ark-server-second.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::ArkServerSecond).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().ark_server_second = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().ark_server_second = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[post("/ark-third")]
async fn post_ark_third(command: web::Json<Command>) -> impl Responder {
async fn post_ark_third(
command: web::Json<Command>,
state: web::Data<Arc<Mutex<GameServerExecutingState>>>,
) -> impl Responder {
log::info!("post ark-third");
exec_systemctl(command, "ark-server-third.service").await

if state.lock().unwrap().current_executing_count() >= 2 {
return HttpResponse::ExpectationFailed().body("Two games have already been activated.");
}

let result = exec_systemctl(&command, Game::ArkServerThird).await;

if let Ok(_) = &result {
if let "start" | "restart" = command.request().as_str() {
state.lock().unwrap().ark_server_third = true;
} else if let "stop" = command.request().as_str() {
state.lock().unwrap().ark_server_third = false;
}
}

if command.request() == "status" {
into_response_by_cmd_output_with_status(&result)
} else {
into_response_by_cmd_output(&result)
}
}

#[get("/test")]
Expand Down Expand Up @@ -117,6 +272,9 @@ async fn main() -> std::io::Result<()> {
let mut well_known_path = exe_dir.clone();
well_known_path.push(".well-known");
App::new()
.data_factory(|| async {
Ok::<_, ()>(Arc::new(Mutex::new(GameServerExecutingState::default())))
})
.service(index)
.service(post_minecraft)
.service(post_sdtd)
Expand Down
43 changes: 43 additions & 0 deletions homeserver_receive_process/src/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#[derive(Debug, Default)]

pub struct GameServerExecutingState {
pub minecraft_server_mgpf: bool,
pub sdtd_server: bool,
pub terraria_server: bool,
pub ark_server: bool,
pub ark_server_second: bool,
pub ark_server_third: bool,
}

impl GameServerExecutingState {
pub fn current_executing_count(&self) -> usize {
self.minecraft_server_mgpf as usize
+ self.sdtd_server as usize
+ self.terraria_server as usize
+ self.ark_server as usize
+ self.ark_server_second as usize
+ self.ark_server_third as usize
}
}

pub enum Game {
MinecraftServerMgpf,
SdtdServer,
TerrariaServer,
ArkServer,
ArkServerSecond,
ArkServerThird,
}

impl Game {
pub fn to_service_name(&self) -> String {
match self {
Game::MinecraftServerMgpf => "minecraft-server-mgpf.service".to_string(),
Game::SdtdServer => "sdtd-server.service".to_string(),
Game::TerrariaServer => "terraria-server.service".to_string(),
Game::ArkServer => "ark-server.service".to_string(),
Game::ArkServerSecond => "ark-server-second.service".to_string(),
Game::ArkServerThird => "ark-server-third.service".to_string(),
}
}
}

0 comments on commit 3aa6e37

Please sign in to comment.