Skip to content

Commit

Permalink
Implement events and command modules
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettjoecox committed Sep 6, 2021
1 parent f6f1beb commit 7299ae6
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 11 deletions.
92 changes: 92 additions & 0 deletions command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Config } from "./config.ts";
import { JavaServer, JavaServerEvents } from "./java_server.ts";
import get from "https://deno.land/x/[email protected]/src/object/get.ts";
import { useEvents } from "./events.ts";

interface CommandEvent {
player: string;
command: string;
args: string[];
timestamp: number;
}

type CommandCallback = (event: CommandEvent) => void;

export interface CommandConfig {
initialized: boolean;
prefix: string;
commands: {
[cmd: string]: CommandCallback[];
};
commandFunc?: (cmd: string, callback: CommandCallback) => void;
}

declare module "./config.ts" {
export interface Config {
command: CommandConfig;
}
}

declare module "./java_server.ts" {
export interface JavaServerEvents {
command: CommandCallback;
}
}

const DEFAULT_CONFIG: CommandConfig = {
initialized: false,
prefix: "~",
commands: {},
};

export function useCommand(javaServer: JavaServer) {
if (javaServer.config?.command?.initialized) {
return javaServer.config.command.commandFunc!;
}

useEvents(javaServer);

// @ts-ignore
javaServer.config = { command: DEFAULT_CONFIG, ...javaServer.config };
javaServer.config.command.initialized = true;

const regex = new RegExp(
`^${javaServer.config.command.prefix}([\\w]+)\\s?(.*)`,
"i"
);

javaServer.on("chat", (event) => {
const stripped = event.message.match(regex);
if (stripped) {
const command = stripped[1].toLowerCase();
javaServer.emit("command", {
player: event.player,
command,
args: stripped[2].split(" "),
timestamp: Date.now(),
});
}
});

javaServer.on("command", (event) => {
if (javaServer.config.command.commands.hasOwnProperty(event.command)) {
javaServer.config.command.commands[event.command].forEach((callback) => {
Promise.resolve()
.then(() => callback(event))
.catch((e) => console.error(e));
});
}
});

javaServer.config.command.commandFunc = (
cmd: string,
callback: CommandCallback
) => {
cmd = cmd.toLowerCase();
javaServer.config.command.commands[cmd] =
javaServer.config.command.commands[cmd] || [];
javaServer.config.command.commands[cmd].push(callback);
};

return javaServer.config.command.commandFunc;
}
169 changes: 169 additions & 0 deletions events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { Config } from "./config.ts";
import { JavaServer, JavaServerEvents } from "./java_server.ts";
import get from "https://deno.land/x/[email protected]/src/object/get.ts";

export interface EventsConfig {
initialized: boolean;
flavorSpecific: {
default: {
parseChatEvent: (consoleOutput: string) => {
player: string;
message: string;
} | void;
parseLoginEvent: (consoleOutput: string) => {
player: string;
ip: string;
} | void;
parseLogoutEvent: (consoleOutput: string) => {
player: string;
reason: string;
} | void;
parseAchievementEvent: (consoleOutput: string) => {
player: string;
achievement: string;
} | void;
parseStartEvent: (consoleOutput: string) => {} | void;
parseStopEvent: (consoleOutput: string) => {} | void;
};
};
}

declare module "./config.ts" {
export interface Config {
events: EventsConfig;
}
}

declare module "./java_server.ts" {
export interface JavaServerEvents {
chat: (event: {
player: string;
message: string;
timestamp: number;
}) => void;
login: (event: { player: string; ip: string; timestamp: number }) => void;
logout: (event: {
player: string;
reason: string;
timestamp: number;
}) => void;
achievement: (event: {
player: string;
achievement: string;
timestamp: number;
}) => void;
start: (event: { timestamp: number }) => void;
stop: (event: { timestamp: number }) => void;
}
}

const DEFAULT_CONFIG: EventsConfig = {
initialized: false,
flavorSpecific: {
default: {
parseChatEvent(consoleOutput) {
const parsed = consoleOutput.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: <(\w+)> (.*)/i
);
if (parsed) {
return {
player: parsed[1] as string,
message: parsed[2] as string,
};
}
},
parseLoginEvent(string) {
const parsed = string.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: (\w+)\[\/([\d.:]+)\] logged in/
);
if (parsed) {
return {
player: parsed[1],
ip: parsed[2],
};
}
},
parseLogoutEvent(string) {
const parsed = string.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: (\w+) lost connection: (.+)/
);
if (parsed) {
return {
player: parsed[1],
reason: parsed[2],
};
}
},
parseAchievementEvent(string) {
const parsed = string.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: (\w+) has made the advancement \[([\w\s]+)\]/
);
if (parsed) {
return {
player: parsed[1],
achievement: parsed[2],
};
}
},
parseStartEvent(string) {
const parsed = string.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: Done/
);
if (parsed) {
return {};
}
},
parseStopEvent(string) {
const parsed = string.match(
/^\[[\d:]{8}\] \[Server thread\/INFO\]: Stopping server/
);
if (parsed) {
return {};
}
},
},
},
};

const EVENTS_MAP = [
["chat", "parseChatEvent"],
["login", "parseLoginEvent"],
["logout", "parseLogoutEvent"],
["achievement", "parseAchievementEvent"],
["start", "parseStartEvent"],
["stop", "parseStopEvent"],
] as const;

export function useEvents(javaServer: JavaServer) {
if (javaServer.config?.events?.initialized) {
return;
}

// @ts-ignore
javaServer.config = { events: DEFAULT_CONFIG, ...javaServer.config };
javaServer.config.events.initialized = true;

javaServer.on("console", (consoleLine) => {
const result = EVENTS_MAP.reduce<null | {
event: keyof JavaServerEvents;
payload: any;
}>((acc, event) => {
if (acc) return acc;

const parseEvent = get(
javaServer.config.events,
`flavorSpecific.${javaServer.config.flavor}.${event[1]}`,
javaServer.config.events.flavorSpecific.default[event[1]]
);
const matches = parseEvent(consoleLine);
if (matches) return { event: event[0], payload: matches };

return null;
}, null);

if (result) {
result.payload.timestamp = Date.now();
javaServer.emit(result.event, result.payload);
}
});
}
19 changes: 17 additions & 2 deletions java_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ declare module "./config.ts" {
}
}

const DEFAULT_CONFIG = {
export interface JavaServerEvents {
console: (consoleLine: string) => void;
}

const DEFAULT_CONFIG: JavaServerConfig = {
jar: "server.jar",
args: ["-Xmx1024M", "-Xms1024M"],
path: "./server",
Expand All @@ -27,7 +31,18 @@ const DEFAULT_CONFIG = {
const textDecoder = new TextDecoder();

export class JavaServer extends EventEmitter {
private config: Config;
// @ts-expect-error
on<U extends keyof JavaServerEvents>(
event: U,
listener: JavaServerEvents[U]
): this;

// @ts-expect-error
emit<U extends keyof JavaServerEvents>(
event: U,
...args: Parameters<JavaServerEvents[U]>
): boolean;
public config: Config;
private process?: Deno.Process;

constructor(config: Partial<Config> = {}) {
Expand Down
25 changes: 20 additions & 5 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { ScriptServer } from "./script_server.ts";
import { useEvents } from "./events.ts";
import { useCommand } from "./command.ts";

const scriptServer = new ScriptServer();
(() => {
try {
const scriptServer = new ScriptServer();

scriptServer.start();
useEvents(scriptServer.javaServer);
const command = useCommand(scriptServer.javaServer);

scriptServer.on("start", async () => {
console.log(await scriptServer.send("help"));
});
scriptServer.javaServer.on("command", (event) => {
console.log("command", event);
});

command("head", (event) => {
console.log("head", event);
});

scriptServer.start();
} catch (error) {
console.error(error);
}
})();
4 changes: 2 additions & 2 deletions rcon_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ declare module "./config.ts" {
}
}

const DEFAULT_CONFIG = {
const DEFAULT_CONFIG: RconConnectionConfig = {
host: "localhost",
rconPort: 25575,
rconPassword: "0000",
rconBuffer: 100,
};

export class RconConnection extends EventEmitter {
private config: Config;
public config: Config;

private authenticated = false;
private queue: [string, (value: string) => void][] = [];
Expand Down
4 changes: 2 additions & 2 deletions script_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ declare module "./config.ts" {
}
}

const DEFAULT_CONFIG = {
const DEFAULT_CONFIG: ScriptServerConfig = {
flavorSpecific: {
default: {
rconRunningRegExp: /^\[[\d:]{8}\] \[Server thread\/INFO\]: RCON running/i,
Expand All @@ -29,7 +29,7 @@ const DEFAULT_CONFIG = {
export class ScriptServer extends EventEmitter {
public javaServer: JavaServer;
public rconConnection: RconConnection;
private config: Config;
public config: Config;

constructor(config: Partial<Config> = {}) {
super();
Expand Down

0 comments on commit 7299ae6

Please sign in to comment.