185 lines
4.6 KiB
TypeScript
185 lines
4.6 KiB
TypeScript
import { Sockpuppet } from "puppet/client";
|
|
import { acceptEULA, checkEULA } from "../util/EULA.ts";
|
|
import { Loader } from "../types/mcgrizzconf.ts";
|
|
import { getConfFile, updateConfFile } from "../util/confFile.ts";
|
|
import { IS_BROWSER } from "$fresh/runtime.ts";
|
|
import { getServerStartCommands } from "../serverConfigs/start.ts";
|
|
|
|
type MCServerEvent = "message";
|
|
type MCServerEventCallback = (msg: string) => void;
|
|
|
|
type status = "running" | "stopped";
|
|
|
|
class ServerState {
|
|
private _status: status = "stopped";
|
|
public get status() {
|
|
return this._status;
|
|
}
|
|
private set status(s: status) {
|
|
this._status = s;
|
|
|
|
const statusEvent = new CustomEvent("serverstatuschange", {
|
|
detail: this._status,
|
|
});
|
|
globalThis.dispatchEvent(statusEvent);
|
|
}
|
|
|
|
private command!: Deno.Command;
|
|
private process!: Deno.ChildProcess;
|
|
|
|
private _eulaAccepted = false;
|
|
|
|
private sockpuppet!: Sockpuppet;
|
|
private _channelId;
|
|
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;
|
|
}
|
|
|
|
private _serverVersion: string;
|
|
public get serverVersion(): string {
|
|
return this._serverVersion;
|
|
}
|
|
public set serverVersion(version: string) {
|
|
updateConfFile({ version });
|
|
this._serverVersion = version;
|
|
}
|
|
|
|
constructor() {
|
|
this._channelId = crypto.randomUUID();
|
|
|
|
const conf = getConfFile();
|
|
this._serverType = conf.loader;
|
|
this._serverVersion = conf.version;
|
|
|
|
this._eulaAccepted = checkEULA();
|
|
|
|
this.sockpuppet = new Sockpuppet(
|
|
"ws://sockpuppet.cyborggrizzly.com",
|
|
() => {
|
|
this.sockpuppet.createChannel(this._channelId);
|
|
this.sockpuppet.joinChannel(this.channelId, async (msg) => {
|
|
if (msg === "log" && !IS_BROWSER) {
|
|
try {
|
|
const log = await Deno.readTextFile("./server/logs/latest.log");
|
|
this.channel?.send(log);
|
|
} catch {
|
|
1;
|
|
}
|
|
} else {
|
|
console.log(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 async startMCServer(instance = "server") {
|
|
this.command = getServerStartCommands(this._serverType, instance);
|
|
|
|
this.channel?.send("clear");
|
|
|
|
this.process = this.command.spawn();
|
|
|
|
const { readable, writable } = new TransformStream();
|
|
readable.pipeTo(this.process.stdin);
|
|
this.stdin = writable.getWriter();
|
|
|
|
this.status = "running";
|
|
|
|
this.startStream();
|
|
await this.process.status;
|
|
this.status = 'stopped';
|
|
}
|
|
|
|
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;
|
|
// }
|
|
|
|
await this.process.stdout.pipeThrough(new TextDecoderStream()).pipeTo(
|
|
new WritableStream({
|
|
write: (chunk) => {
|
|
this.channel?.send(chunk);
|
|
const stdoutMsg = new CustomEvent("stdoutmsg", {
|
|
detail: chunk,
|
|
});
|
|
globalThis.dispatchEvent(stdoutMsg);
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
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");
|
|
const statusEvent = new CustomEvent("serverstatuschange", {
|
|
detail: "restarting",
|
|
});
|
|
|
|
globalThis.dispatchEvent(statusEvent);
|
|
|
|
// while (true) {
|
|
await this.process.status;
|
|
// }
|
|
}
|
|
|
|
this.startMCServer();
|
|
}
|
|
|
|
public acceptEULA() {
|
|
this._eulaAccepted = true;
|
|
acceptEULA();
|
|
}
|
|
}
|
|
|
|
export const SERVER_STATE = new ServerState();
|