Support latest Eaglercraft and move to TheAltening

EasyMC shut down :(
This commit is contained in:
ayunami2000 2024-12-04 23:48:47 -05:00
parent 1687d30417
commit 6fba77dbfd
9 changed files with 510 additions and 489 deletions

844
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,15 @@
"@types/ws": "8.5.4",
"chalk": "5.2.0",
"dotenv": "16.0.3",
"minecraft-protocol": "^1.26.5",
"minecraft-protocol": "^1.51.0",
"node-fetch": "3.3.1",
"parse-domain": "7.0.1",
"prismarine-auth": "^2.4.1",
"prismarine-auth": "^2.5.0",
"prismarine-block": "1.16.3",
"prismarine-chunk": "1.33.0",
"prismarine-registry": "1.6.0",
"semver": "^7.6.0",
"sharp": "^0.33.2",
"semver": "^7.6.3",
"sharp": "^0.33.5",
"ws": "8.12.0"
},
"type": "module"

View File

@ -1,31 +0,0 @@
import mc from "minecraft-protocol";
import { Enums } from "../../proxy/Enums.js";
export async function getTokenProfileEasyMc(token: string): Promise<object> {
const fetchOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
token,
}),
};
const res = await fetch("https://api.easymc.io/v1/token/redeem", fetchOptions);
const resJson = await res.json();
if (resJson.error) throw new Error(Enums.ChatColor.RED + `${resJson.error}`);
if (!resJson) throw new Error(Enums.ChatColor.RED + "EasyMC replied with an empty response!?");
if (resJson.session?.length !== 43 || resJson.mcName?.length < 3 || resJson.uuid?.length !== 36) throw new Error(Enums.ChatColor.RED + "Invalid response from EasyMC received!");
return {
auth: "mojang",
sessionServer: "https://sessionserver.easymc.io",
username: resJson.mcName,
haveCredentials: true,
session: {
accessToken: resJson.session,
selectedProfile: {
name: resJson.mcName,
id: resJson.uuid,
},
},
};
}

View File

@ -0,0 +1,26 @@
import mc from "minecraft-protocol";
import { Enums } from "../../proxy/Enums.js";
export async function getTokenProfileTheAltening(token: string): Promise<object> {
const fetchOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: token,
password: "anything",
}),
};
const res = await fetch("http://authserver.thealtening.com/authenticate", fetchOptions);
const resJson = await res.json();
if (resJson.error) throw new Error(Enums.ChatColor.RED + `${resJson.error}`);
if (!resJson) throw new Error(Enums.ChatColor.RED + "TheAltening replied with an empty response!?");
if (resJson.selectedProfile?.name?.length < 3) throw new Error(Enums.ChatColor.RED + "Invalid response from TheAltening received!");
return {
auth: "mojang",
sessionServer: "http://sessionserver.thealtening.com",
username: resJson.selectedProfile.name,
haveCredentials: true,
session: resJson,
};
}

View File

@ -127,7 +127,7 @@ export function helpCommand(sender: Player) {
},
extra: [
{
text: " - Switch between servers on-the-fly. Switching to servers in online mode requires logging in via online mode or EasyMC!",
text: " - Switch between servers on-the-fly. Switching to servers in online mode requires logging in via online mode or TheAltening!",
color: "aqua",
},
],
@ -252,18 +252,18 @@ export async function switchServer(cmd: string, sender: Player) {
if (connectionType == ConnectType.ONLINE) {
if ((sender as any)._onlineSession == null) {
sendPluginChatMessage(sender, {
text: `You either connected to this proxy under offline mode, or your online/EasyMC session has timed out and has become invalid.`,
text: `You either connected to this proxy under offline mode, or your online/TheAltening session has timed out and has become invalid.`,
color: "red",
});
return sendPluginChatMessage(sender, {
text: `To switch to online servers, please reconnect and log-in through online/EasyMC mode.`,
text: `To switch to online servers, please reconnect and log-in through online/TheAltening mode.`,
color: "red",
});
} else {
const savedAuth = (sender as any)._onlineSession;
sendPluginChatMessage(sender, {
text: `(joining server under ${savedAuth.username}/your ${
savedAuth.isEasyMC ? "EasyMC" : "Minecraft"
savedAuth.isTheAltening ? "TheAltening" : "Minecraft"
} account's username)`,
color: "aqua",
});

View File

@ -47,5 +47,5 @@ export enum ChatColor {
export enum ConnectType {
ONLINE = "ONLINE",
OFFLINE = "OFFLINE",
EASYMC = "EASYMC",
THEALTENING = "THEALTENING",
}

View File

@ -8,7 +8,7 @@ import { ClientState, ConnectionState } from "./types.js";
import { auth, ServerDeviceCodeResponse } from "./auth.js";
import { config } from "./config.js";
import { handleCommand } from "./commands.js";
import { getTokenProfileEasyMc } from "./auth_easymc.js";
import { getTokenProfileTheAltening } from "./auth_thealtening.js";
const { Vec3 } = vec3 as any;
const Enums = PLUGIN_MANAGER.Enums;
@ -163,7 +163,7 @@ export function sendMessageLogin(client: Client, url: string, token: string) {
});
}
export function updateState(client: Client, newState: "CONNECTION_TYPE" | "AUTH_EASYMC" | "AUTH" | "SERVER", uri?: string, code?: string) {
export function updateState(client: Client, newState: "CONNECTION_TYPE" | "AUTH_THEALTENING" | "AUTH" | "SERVER", uri?: string, code?: string) {
switch (newState) {
case "CONNECTION_TYPE":
client.write("playerlist_header", {
@ -171,17 +171,17 @@ export function updateState(client: Client, newState: "CONNECTION_TYPE" | "AUTH_
text: ` ${Enums.ChatColor.GOLD}EaglerProxy Authentication Server `,
}),
footer: JSON.stringify({
text: `${Enums.ChatColor.RED}Choose the connection type: 1 = online, 2 = offline, 3 = EasyMC.`,
text: `${Enums.ChatColor.RED}Choose the connection type: 1 = online, 2 = offline, 3 = TheAltening.`,
}),
});
break;
case "AUTH_EASYMC":
case "AUTH_THEALTENING":
client.write("playerlist_header", {
header: JSON.stringify({
text: ` ${Enums.ChatColor.GOLD}EaglerProxy Authentication Server `,
}),
footer: JSON.stringify({
text: `${Enums.ChatColor.RED}easymc.io/get${Enums.ChatColor.GOLD} | ${Enums.ChatColor.RED}/login <alt_token>`,
text: `${Enums.ChatColor.RED}panel.thealtening.com/#generator${Enums.ChatColor.GOLD} | ${Enums.ChatColor.RED}/login <alt_token>`,
}),
});
break;
@ -301,7 +301,7 @@ export async function onConnect(client: ClientState) {
color: "gold",
extra: [
{
text: "Connect to an online server via EasyMC account pool (no Minecraft account needed)",
text: "Connect to an online server via TheAltening account pool (no Minecraft account needed)",
color: "white",
},
],
@ -314,7 +314,7 @@ export async function onConnect(client: ClientState) {
value: "$3",
},
});
sendCustomMessage(client.gameClient, "Select an option from the above (1 = online, 2 = offline, 3 = EasyMC), either by clicking or manually typing out the option's number on the list.", "green");
sendCustomMessage(client.gameClient, "Select an option from the above (1 = online, 2 = offline, 3 = TheAltening), either by clicking or manually typing out the option's number on the list.", "green");
updateState(client.gameClient, "CONNECTION_TYPE");
let chosenOption: ConnectType | null = null;
@ -331,7 +331,7 @@ export async function onConnect(client: ClientState) {
chosenOption = ConnectType.OFFLINE;
break;
case "3":
chosenOption = ConnectType.EASYMC;
chosenOption = ConnectType.THEALTENING;
break;
}
if (chosenOption != null) {
@ -479,19 +479,19 @@ export async function onConnect(client: ClientState) {
);
}
}
} else if (chosenOption == ConnectType.EASYMC) {
const EASYMC_GET_TOKEN_URL = "easymc.io/get";
} else if (chosenOption == ConnectType.THEALTENING) {
const THEALTENING_GET_TOKEN_URL = "panel.thealtening.com/#generator";
client.state = ConnectionState.AUTH;
client.lastStatusUpdate = Date.now();
updateState(client.gameClient, "AUTH_EASYMC");
updateState(client.gameClient, "AUTH_THEALTENING");
sendMessageWarning(client.gameClient, `WARNING: You've chosen to use an account from EasyMC's account pool. Please note that accounts and shared, and may be banned from whatever server you are attempting to join.`);
sendMessageWarning(client.gameClient, `WARNING: You've chosen to use an account from TheAltening's account pool. Please note that accounts and shared, and may be banned from whatever server you are attempting to join.`);
sendChatComponent(client.gameClient, {
text: "Please generate an alt token at ",
text: "Please log in and generate an alt token at ",
color: "white",
extra: [
{
text: EASYMC_GET_TOKEN_URL,
text: THEALTENING_GET_TOKEN_URL,
color: "gold",
hoverEvent: {
action: "show_text",
@ -499,7 +499,7 @@ export async function onConnect(client: ClientState) {
},
clickEvent: {
action: "open_url",
value: `https://${EASYMC_GET_TOKEN_URL}`,
value: `https://${THEALTENING_GET_TOKEN_URL}`,
},
},
{
@ -554,7 +554,7 @@ export async function onConnect(client: ClientState) {
});
} else {
const token = splitResponse[0];
if (token.length != 20) {
if (!token.endsWith("@alt.com")) {
sendChatComponent(client.gameClient, {
text: "Please provide a valid token (you can get one ",
color: "red",
@ -568,7 +568,7 @@ export async function onConnect(client: ClientState) {
},
clickEvent: {
action: "open_url",
value: `https://${EASYMC_GET_TOKEN_URL}`,
value: `https://${THEALTENING_GET_TOKEN_URL}`,
},
},
{
@ -596,12 +596,12 @@ export async function onConnect(client: ClientState) {
} else {
sendCustomMessage(client.gameClient, "Validating alt token...", "gray");
try {
appendOptions = await getTokenProfileEasyMc(token);
appendOptions = await getTokenProfileTheAltening(token);
sendCustomMessage(client.gameClient, `Successfully validated your alt token and retrieved your session profile! You'll be joining to your preferred server as ${appendOptions.username}.`, "green");
break;
} catch (err) {
sendChatComponent(client.gameClient, {
text: `EasyMC's servers replied with an error (${err.message}), please try again! `,
text: `TheAltening's servers replied with an error (${err.message}), please try again! `,
color: "red",
extra: [
{
@ -652,7 +652,7 @@ export async function onConnect(client: ClientState) {
}
try {
sendChatComponent(client.gameClient, {
text: `Joining server under ${appendOptions.username}/EasyMC account username! Run `,
text: `Joining server under ${appendOptions.username}/TheAltening account username! Run `,
color: "aqua",
extra: [
{
@ -673,7 +673,7 @@ export async function onConnect(client: ClientState) {
},
],
});
logger.info(`Player ${client.gameClient.username} is attempting to connect to ${host}:${port} under their EasyMC alt token's username (${appendOptions.username}) using EasyMC mode!`);
logger.info(`Player ${client.gameClient.username} is attempting to connect to ${host}:${port} under their TheAltening alt token's username (${appendOptions.username}) using TheAltening mode!`);
const player = PLUGIN_MANAGER.proxy.players.get(client.gameClient.username);
player.on("vanillaPacket", (packet, origin) => {
if (origin == "CLIENT" && packet.name == "chat" && (packet.params.message as string).toLowerCase().startsWith("/eag-") && !packet.cancel) {
@ -683,7 +683,7 @@ export async function onConnect(client: ClientState) {
});
(player as any)._onlineSession = {
...appendOptions,
isEasyMC: true,
isTheAltening: true,
};
await player.switchServers({

View File

@ -189,7 +189,7 @@ export class Proxy extends EventEmitter {
}
}, this.LOGIN_TIMEOUT);
try {
if (firstPacket.toString() === "Accept: MOTD") {
if (firstPacket.toString().toLowerCase() === "accept: motd") {
if (!this.ratelimit.motd.consume(req.socket.remoteAddress).success) {
return ws.close();
}

View File

@ -15,22 +15,15 @@ export default class CSLoginPacket implements Packet {
version: string;
username: string;
private _getMagicSeq(): Buffer {
return Buffer.concat(
[
[0x02, 0x00, 0x02, 0x00, 0x02, 0x00],
[this.networkVersion],
[0x00, 0x01, 0x00],
[this.gameVersion],
].map((arr) => Buffer.from(arr))
);
}
public serialize() {
return Buffer.concat(
[
[Enums.PacketId.CSLoginPacket],
this._getMagicSeq(),
[0x02],
MineProtocol.writeShort(0x01),
MineProtocol.writeShort(this.networkVersion),
MineProtocol.writeShort(0x01),
MineProtocol.writeShort(this.gameVersion),
MineProtocol.writeString(this.brand),
MineProtocol.writeString(this.version),
[0x00],
@ -41,8 +34,19 @@ export default class CSLoginPacket implements Packet {
public deserialize(packet: Buffer) {
if (packet[0] != this.packetId)
throw TypeError("Invalid packet ID detected!");
packet = packet.subarray(1 + this._getMagicSeq().length);
const brand = MineProtocol.readString(packet),
packet = packet.subarray(2);
let fard = MineProtocol.readShort(packet);
// Math.min used in feeble attempt at anti DoS
let fv = Math.min(8, fard.value);
for (let i = 0; i < fv; i++) {
fard = MineProtocol.readShort(fard.newBuffer);
}
fard = MineProtocol.readShort(fard.newBuffer);
fv = Math.min(8, fard.value);
for (let i = 0; i < fv; i++) {
fard = MineProtocol.readShort(fard.newBuffer);
}
const brand = MineProtocol.readString(fard.newBuffer),
version = MineProtocol.readString(brand.newBuffer),
username = MineProtocol.readString(version.newBuffer, 1);
this.brand = brand.value;