import { Sockpuppet } from "puppet/client"; import { acceptEULA, checkEULA } from "../util/EULA.ts"; import { Loader } from "../types/mcgrizzconf.ts"; import { updateConfFile } from "../util/confFile.ts"; import { IS_BROWSER } from "$fresh/runtime.ts"; type MCServerEvent = 'message'; type MCServerEventCallback = (msg: string) => void; class ServerState { private _status: "running" | "stopped" = "stopped"; public get status() { return this._status; } private command!: Deno.Command; private process!: Deno.ChildProcess; private _eulaAccepted: boolean; private sockpuppet!: Sockpuppet; private _channelId = "blanaba"; public get channelId() { return this._channelId; } public get eulaAccepted() { return this._eulaAccepted; } private stdin!: WritableStreamDefaultWriter; private _serverType: Loader = 'unset'; public get serverType(): Loader { return this.serverType; } public set serverType(loader: Loader) { updateConfFile({loader}); this._serverType = loader; } constructor() { this._eulaAccepted = checkEULA(); this.sockpuppet = new Sockpuppet( "ws://sockpuppet.cyborggrizzly.com", () => { this.sockpuppet.joinChannel(this.channelId, (msg) => { this.sendStdIn(msg); }); }, ); } public get channel() { return this.sockpuppet.getChannel(this.channelId); } public async sendStdIn(message: string) { if (IS_BROWSER || !this.stdin) return; const msg = new TextEncoder().encode(message + "\n"); await this.stdin.write(msg); } // "instance" should be moved to a private member once multi-instance support is implemented public startMCServer(instance = "server") { this.command = new Deno.Command("java", { args: [ "-Xmx2G", "-jar", "./server.jar", "nogui", ], cwd: "./" + instance, stdin: "piped", stdout: "piped", }); this.process = this.command.spawn(); const {readable, writable} = new TransformStream(); readable.pipeTo(this.process.stdin); this.stdin = writable.getWriter(); this._status = "running"; this.startStream(); } private async startStream() { const stream = this.process.stdout.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await stream.read(); if (value) { const line = decoder.decode(value); this.channel?.send(line); const stdoutMsg = new CustomEvent('stdoutmsg', { detail: line }) globalThis.dispatchEvent(stdoutMsg); } if (done) break; } } public gracefullyStopMCServer() { this._status = "stopped"; this.sendStdIn("stop"); } public forceStopMCServer() { this._status = "stopped"; this.process.kill(); } public async restartMCServer() { if (this.status === "running") { await this.sendStdIn("stop"); // while (true) { await this.process.status; // } } this.startMCServer(); } public acceptEULA() { this._eulaAccepted = true; acceptEULA(); } } export const SERVER_STATE = new ServerState();