Skip to content

Commit

Permalink
Automation
Browse files Browse the repository at this point in the history
- MongoDB tied to Grasscutter rather than game
- Grasscutter restarts when changing encryption if it is running
- AIO link updates without needing manual edits
  • Loading branch information
NotThorny committed Apr 3, 2023
1 parent 1607096 commit c56edd6
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src-tauri/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::process::Command;

#[cfg(windows)]
pub fn reopen_as_admin() {
use std::process::{exit, Command};

let install = std::env::current_exe().unwrap();

println!("Opening as admin: {}", install.to_str().unwrap());
Expand Down
105 changes: 104 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tauri::api::path::data_dir;
use tauri::async_runtime::block_on;

use std::thread;
use sysinfo::{System, SystemExt};
use sysinfo::{Pid, ProcessExt, System, SystemExt};

use crate::admin::reopen_as_admin;

Expand All @@ -28,6 +28,8 @@ mod unzip;
mod web;

static WATCH_GAME_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
static WATCH_GRASSCUTTER_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
static GC_PID: std::sync::Mutex<usize> = Mutex::new(696969);

fn try_flush() {
std::io::stdout().flush().unwrap_or(())
Expand Down Expand Up @@ -81,10 +83,13 @@ fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
enable_grasscutter_watcher,
connect,
disconnect,
req_get,
is_game_running,
is_grasscutter_running,
restart_grasscutter,
get_theme_list,
system_helpers::run_command,
system_helpers::run_program,
Expand Down Expand Up @@ -188,6 +193,104 @@ fn enable_process_watcher(window: tauri::Window, process: String) {
});
}

#[tauri::command]
fn is_grasscutter_running() -> bool {
// Grab the grasscutter process name
let proc = WATCH_GRASSCUTTER_PROCESS.lock().unwrap().to_string();

!proc.is_empty()
}

#[tauri::command]
fn restart_grasscutter(window: tauri::Window) -> bool {
let pid: usize = *GC_PID.lock().unwrap();
let system = System::new_all();
// Get the process
if let Some(process) = system.process(Pid::from(pid)) {
// Kill it
if process.kill() {
// Also kill the cmd it was open in
if let Some(parent) = system.process(Pid::from(process.parent().unwrap())) {
parent.kill();
}
for process_gc in system.processes_by_name("java") {
if process_gc.cmd().last().unwrap().contains(&"grasscutter") {
process_gc.kill();
}
}
window.emit("disable_grasscutter_watcher", &()).unwrap();
thread::sleep(std::time::Duration::from_secs(2));
// Start again
window.emit("start_grasscutter", &()).unwrap();
true
} else {
false
}
} else {
false
}
}

#[tauri::command]
fn enable_grasscutter_watcher(window: tauri::Window, process: String) {
let grasscutter_name = process.clone();
let mut gc_pid = Pid::from(696969);

*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = process;

window.listen("disable_grasscutter_watcher", |_e| {
*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = "".to_string();
});

println!("Starting grasscutter watcher...");

thread::spawn(move || {
// Initial sleep for 1 second while Grasscutter opens
std::thread::sleep(std::time::Duration::from_secs(3));

let mut system = System::new_all();

for process_gc in system.processes_by_name("java") {
if process_gc.cmd().last().unwrap().contains(&grasscutter_name) {
gc_pid = process_gc.pid();
*GC_PID.lock().unwrap() = gc_pid.into();
window
.emit("grasscutter_started", gc_pid.to_string())
.unwrap();
}
}

loop {
// Shorten loop timer to avoid user closing Cultivation before automatic stuff
thread::sleep(std::time::Duration::from_secs(2));

// Refresh system info
system.refresh_all();

// Grab the grasscutter process name
let proc = WATCH_GRASSCUTTER_PROCESS.lock().unwrap().to_string();

if !proc.is_empty() {
let mut exists = true;

if system.process(gc_pid).is_none() {
exists = false;
}

// If the grasscutter process closes.
if !exists {
println!("Grasscutter closed");

*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = "".to_string();

window.emit("grasscutter_closed", &()).unwrap();
break;
}
}
}
});
}

#[tauri::command]
async fn connect(port: u16, certificate_path: String) {
// Log message to console.
Expand Down
3 changes: 3 additions & 0 deletions src-tauri/src/system_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ pub fn wipe_registry(exec_name: String) {
}
}

#[cfg(windows)]
#[tauri::command]
pub fn service_status(service: String) -> bool {
let manager = match ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT) {
Expand All @@ -179,6 +180,7 @@ pub fn service_status(service: String) -> bool {
}
}

#[cfg(windows)]
#[tauri::command]
pub fn start_service(service: String) -> bool {
println!("Starting service: {}", service);
Expand All @@ -197,6 +199,7 @@ pub fn start_service(service: String) -> bool {
true
}

#[cfg(windows)]
#[tauri::command]
pub fn stop_service(service: String) -> bool {
println!("Stopping service: {}", service);
Expand Down
6 changes: 5 additions & 1 deletion src/ui/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export class Main extends React.Component<IProps, IState> {
// Emitted for rsa replacing-purposes
listen('game_closed', async () => {
const wasPatched = await getConfigOption('patch_rsa')
const autoService = await getConfigOption('auto_mongodb')

if (wasPatched) {
const unpatched = await unpatchGame()
Expand All @@ -77,6 +76,11 @@ export class Main extends React.Component<IProps, IState> {
alert(`Could not unpatch game! (Delete version.dll in your game folder)`)
}
}
})

// Emitted for automatic processes
listen('grasscutter_closed', async () => {
const autoService = await getConfigOption('auto_mongodb')

if (autoService) {
await invoke('stop_service', { service: 'MongoDB' })
Expand Down
22 changes: 20 additions & 2 deletions src/ui/components/ServerLaunchSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import Plus from '../../resources/icons/plus.svg'

import './ServerLaunchSection.css'
import { dataDir } from '@tauri-apps/api/path'
import { getGameExecutable, getGameVersion } from '../../utils/game'
import { getGameExecutable, getGameVersion, getGrasscutterJar } from '../../utils/game'
import { patchGame, unpatchGame } from '../../utils/rsa'
import { listen } from '@tauri-apps/api/event'

interface IProps {
openExtras: (playGame: () => void) => void
Expand All @@ -25,6 +26,7 @@ interface IState {
checkboxLabel: string
ip: string
port: string
launchServer: (proc_name?: string) => void

ipPlaceholder: string
portPlaceholder: string
Expand Down Expand Up @@ -54,6 +56,9 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
portHelpText: '',
httpsLabel: '',
httpsEnabled: false,
launchServer: () => {
alert('Error launching grasscutter')
},
swag: false,
akebiSet: false,
migotoSet: false,
Expand All @@ -64,6 +69,11 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
this.setIp = this.setIp.bind(this)
this.setPort = this.setPort.bind(this)
this.toggleHttps = this.toggleHttps.bind(this)
this.launchServer = this.launchServer.bind(this)

listen('start_grasscutter', async () => {
this.launchServer()
})
}

async componentDidMount() {
Expand Down Expand Up @@ -182,12 +192,20 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
else alert('Game not found! At: ' + (exe || config.game_install_path))
}

async launchServer() {
async launchServer(proc_name?: string) {
if (await invoke('is_grasscutter_running')) {
alert('Grasscutter already running!')
return
}
const config = await getConfig()

if (!config.grasscutter_path) return alert('Grasscutter not installed or set!')

if (config.auto_mongodb) {
const grasscutter_jar = await getGrasscutterJar()
await invoke('enable_grasscutter_watcher', {
process: proc_name || grasscutter_jar,
})
// Check if MongoDB is running and start it if not
await invoke('service_status', { service: 'MongoDB' })
}
Expand Down
3 changes: 1 addition & 2 deletions src/ui/components/menu/Downloads.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import { invoke } from '@tauri-apps/api'
import { listen } from '@tauri-apps/api/event'
import HelpButton from '../common/HelpButton'

const FULL_BUILD_DOWNLOAD =
'https://cdn.discordapp.com/attachments/615655311960965130/1091457240373919814/GrasscutterCulti3.5.zip'
const FULL_BUILD_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterCulti.zip' // Change to link that can be updated without modifying here
const STABLE_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/stable.zip'
const DEV_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/development.zip'
const STABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip'
Expand Down
6 changes: 5 additions & 1 deletion src/ui/components/menu/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,11 @@ export default class Options extends React.Component<IProps, IState> {
),
})

alert('Restart Grasscutter to apply encryption settings! If it is already closed, just start as normal.')
// Check if Grasscutter is running, and restart if so to apply changes
if (await invoke('is_grasscutter_running')) {
alert('Automatically restarting Grasscutter to apply encryption changes!')
await invoke('restart_grasscutter')
}
}

async removeRSA() {
Expand Down
11 changes: 11 additions & 0 deletions src/utils/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ export async function getGameExecutable() {
return pathArr[pathArr.length - 1]
}

export async function getGrasscutterJar() {
const config = await getConfig()

if (!config.grasscutter_path) {
return null
}

const pathArr = config.grasscutter_path.replace(/\\/g, '/').split('/')
return pathArr[pathArr.length - 1]
}

export async function getGameFolder() {
const config = await getConfig()

Expand Down

0 comments on commit c56edd6

Please sign in to comment.