q13x-eaglerproxy/server/plugins/EagProxyAAS/index.js
2024-09-04 12:02:00 +00:00

212 lines
11 KiB
JavaScript

import { config } from "./config.js";
import { createServer } from "minecraft-protocol";
import { ConnectionState } from "./types.js";
import { handleConnect, hushConsole, sendChatComponent, setSG } from "./utils.js";
import path from "path";
import { readFileSync } from "fs";
import { handleCommand } from "./commands.js";
import { registerEndpoints } from "./service/endpoints.js";
const PluginManager = PLUGIN_MANAGER;
const metadata = JSON.parse(readFileSync(process.platform == "win32" ? path.join(path.dirname(new URL(import.meta.url).pathname), "metadata.json").slice(1) : path.join(path.dirname(new URL(import.meta.url).pathname), "metadata.json")).toString());
const Logger = PluginManager.Logger;
const Enums = PluginManager.Enums;
const Chat = PluginManager.Chat;
const Constants = PluginManager.Constants;
const Motd = PluginManager.Motd;
const Player = PluginManager.Player;
const MineProtocol = PluginManager.MineProtocol;
const EaglerSkins = PluginManager.EaglerSkins;
const Util = PluginManager.Util;
hushConsole();
const logger = new Logger("EaglerProxyAAS");
logger.info(`Starting ${metadata.name} v${metadata.version}...`);
logger.info(`(internal server port: ${config.bindInternalServerPort}, internal server IP: ${config.bindInternalServerIp})`);
logger.info("Starting internal server...");
let server = createServer({
host: config.bindInternalServerIp,
port: config.bindInternalServerPort,
motdMsg: `${Enums.ChatColor.GOLD}EaglerProxy as a Service`,
"online-mode": false,
version: "1.8.9",
}), sGlobals = {
server: server,
players: new Map(),
};
setSG(sGlobals);
server.on("login", (client) => {
const proxyPlayer = PluginManager.proxy.players.get(client.username);
if (proxyPlayer != null) {
const url = new URL(proxyPlayer.ws.httpRequest.url, `http${PluginManager.proxy.config.tls?.enabled ? "s" : ""}://${proxyPlayer.ws.httpRequest.headers.host}`);
if (url.pathname == "/connect-vanilla") {
const host = url.searchParams.get("ip"), port = url.searchParams.get("port"), type = url.searchParams.get("authType");
if (isNaN(Number(port)))
return proxyPlayer.disconnect(Enums.ChatColor.RED + "Bad port number");
if (!/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/.test(host)) {
return proxyPlayer.disconnect(Enums.ChatColor.RED + "Bad host provided");
}
if (type == "ONLINE") {
const _profile = proxyPlayer.ws.httpRequest.headers["Minecraft-Profile"];
if (!_profile)
proxyPlayer.disconnect(Enums.ChatColor.RED + "Missing Minecraft-Profile header");
let profile;
try {
profile = JSON.parse(_profile);
}
catch (err) {
proxyPlayer.disconnect(Enums.ChatColor.RED + "Could not read Minecraft-Profile header");
}
logger.info(`Direct OFFLINE proxy forward connection from Eaglercraft player (${client.username}) received.`);
proxyPlayer.on("vanillaPacket", (packet, origin) => {
if (origin == "CLIENT" && packet.name == "chat" && packet.params.message.toLowerCase().startsWith("/eag-") && !packet.cancel) {
packet.cancel = true;
handleCommand(proxyPlayer, packet.params.message);
}
});
sendChatComponent(client, {
text: `Joining server under ${profile.selectedProfile.name}/your Minecraft account's username! Run `,
color: "aqua",
extra: [
{
text: "/eag-help",
color: "gold",
hoverEvent: {
action: "show_text",
value: Enums.ChatColor.GOLD + "Click me to run this command!",
},
clickEvent: {
action: "run_command",
value: "/eag-help",
},
},
{
text: " for a list of proxy commands.",
color: "aqua",
},
],
});
proxyPlayer._onlineSession = {
auth: "mojang",
username: profile.selectedProfile.name,
session: {
accessToken: profile.accessToken,
clientToken: profile.selectedProfile.id,
selectedProfile: {
id: profile.selectedProfile.id,
name: profile.selectedProfile.name,
},
},
};
proxyPlayer
.switchServers({
host: host,
port: Number(port),
version: "1.8.8",
username: profile.selectedProfile.name,
auth: "mojang",
keepAlive: false,
session: {
accessToken: profile.accessToken,
clientToken: profile.selectedProfile.id,
selectedProfile: {
id: profile.selectedProfile.id,
name: profile.selectedProfile.name,
},
},
skipValidation: true,
hideErrors: true,
})
.catch((err) => {
if (!client.ended) {
proxyPlayer.disconnect(Enums.ChatColor.RED +
`Something went wrong whilst switching servers: ${err.message}${err.code == "ENOTFOUND" ? (host.includes(":") ? `\n${Enums.ChatColor.GRAY}Suggestion: Replace the : in your IP with a space.` : "\nIs that IP valid?") : ""}`);
}
});
}
else if (type == "OFFLINE") {
logger.info(`Direct ONLINE proxy forward connection from Eaglercraft player (${client.username}) received.`);
logger.info(`Player ${client.username} is attempting to connect to ${host}:${port} under their Eaglercraft username (${client.username}) using offline mode!`);
proxyPlayer.on("vanillaPacket", (packet, origin) => {
if (origin == "CLIENT" && packet.name == "chat" && packet.params.message.toLowerCase().startsWith("/eag-") && !packet.cancel) {
packet.cancel = true;
handleCommand(proxyPlayer, packet.params.message);
}
});
sendChatComponent(client, {
text: `Joining server under ${client.username}/your Eaglercraft account's username! Run `,
color: "aqua",
extra: [
{
text: "/eag-help",
color: "gold",
hoverEvent: {
action: "show_text",
value: Enums.ChatColor.GOLD + "Click me to run this command!",
},
clickEvent: {
action: "run_command",
value: "/eag-help",
},
},
{
text: " for a list of proxy commands.",
color: "aqua",
},
],
});
proxyPlayer
.switchServers({
host: host,
port: Number(port),
auth: "offline",
username: client.username,
version: "1.8.8",
keepAlive: false,
skipValidation: true,
hideErrors: true,
})
.catch((err) => {
if (!client.ended) {
proxyPlayer.disconnect(Enums.ChatColor.RED +
`Something went wrong whilst switching servers: ${err.message}${err.code == "ENOTFOUND" ? (host.includes(":") ? `\n${Enums.ChatColor.GRAY}Suggestion: Replace the : in your IP with a space.` : "\nIs that IP valid?") : ""}`);
}
});
}
else {
proxyPlayer.disconnect(Enums.ChatColor.RED + "Missing authentication type");
}
}
else {
logger.info(`Client ${client.username} has connected to the authentication server.`);
client.on("end", () => {
sGlobals.players.delete(client.username);
logger.info(`Client ${client.username} has disconnected from the authentication server.`);
});
const cs = {
state: ConnectionState.AUTH,
gameClient: client,
token: null,
lastStatusUpdate: null,
};
sGlobals.players.set(client.username, cs);
handleConnect(cs);
}
}
else {
logger.warn(`Proxy player object is null for ${client.username}?!`);
client.end("Indirect connection to internal authentication server detected!");
}
});
logger.info("Redirecting backend server IP... (this is required for the plugin to function)");
CONFIG.adapter.server = {
host: config.bindInternalServerIp,
port: config.bindInternalServerPort,
};
CONFIG.adapter.motd = {
l1: Enums.ChatColor.GOLD + "EaglerProxy as a Service",
};
if (config.allowDirectConnectEndpoints) {
PLUGIN_MANAGER.addListener("proxyFinishLoading", () => {
registerEndpoints();
});
}