forked from MrGeorgen/2smart2wait
-added multidimension support for chunk caching
-discord Bot sends a message when the queue is about to finish
This commit is contained in:
177
src/main.js
177
src/main.js
@ -11,15 +11,17 @@ const {DateTime} = require("luxon");
|
||||
const https = require("https");
|
||||
const prompt = require("prompt");
|
||||
const ping = require('minecraft-server-util');
|
||||
const cycle = require("cycle")
|
||||
const sleep = require("sleep-ms")
|
||||
const save = "../saveid"
|
||||
var timedStart;
|
||||
var lastQueuePlace;
|
||||
var queueData;
|
||||
var secRun;
|
||||
var chunkData = [];
|
||||
var c = 0;
|
||||
var notisend = false;
|
||||
var loginpacket;
|
||||
var id;
|
||||
webserver.restartQueue = config.reconnect.notConnectedQueueEnd;
|
||||
prioQueueLength();
|
||||
if (config.webserver) {
|
||||
webserver.createServer(config.ports.web); // create the webserver
|
||||
webserver.password = config.password
|
||||
@ -41,11 +43,12 @@ let server; // the minecraft server to pass packets
|
||||
|
||||
//comand prompt
|
||||
prompt.start();
|
||||
cmdInput();
|
||||
|
||||
function cmdInput() {
|
||||
prompt.get("cmd", function (err, result) {
|
||||
userInput(result.cmd, false);
|
||||
cmdInput()
|
||||
cmdInput();
|
||||
});
|
||||
}
|
||||
|
||||
@ -59,61 +62,80 @@ function stop() {
|
||||
proxyClient.end("Stopped the proxy."); // boot the player from the server
|
||||
}
|
||||
server.close(); // close the server
|
||||
dc.user.setActivity("Queue is stopped.");
|
||||
activity("Queue is stopped.");
|
||||
}
|
||||
|
||||
// function to start the whole thing
|
||||
function startQueuing() {
|
||||
var playerid;
|
||||
let startTime = DateTime.local();
|
||||
webserver.isInQueue = true;
|
||||
dc.user.setActivity("Starting the queue...");
|
||||
client = mc.createClient({ // connect to 2b2t
|
||||
activity("Starting the queue...");
|
||||
let options = {
|
||||
host: config.minecraftserver.hostname,
|
||||
port: config.minecraftserver.port,
|
||||
username: secrets.username,
|
||||
password: secrets.password,
|
||||
version: config.minecraftserver.version
|
||||
});
|
||||
}
|
||||
if (config.minecraftserver.onlinemode) {
|
||||
options.password = secrets.password;
|
||||
options.username = secrets.username;
|
||||
} else options.username = config.minecraftserver.username;
|
||||
client = mc.createClient(options);// connect to 2b2t
|
||||
let finishedQueue = false;
|
||||
client.on("packet", (data, meta) => { // each time 2b2t sends a packet
|
||||
if (meta.name === "map_chunk") {
|
||||
chunkData.push(data);
|
||||
}
|
||||
if (!finishedQueue && meta.name === "playerlist_header" && config.minecraftserver.hostname === "2b2t.org") { // if the packet contains the player list, we can use it to see our place in the queue
|
||||
let headermessage = JSON.parse(data.header);
|
||||
let positioninqueue = headermessage.text.split("\n")[5].substring(25);
|
||||
let ETA = headermessage.text.split("\n")[6].substring(27);
|
||||
webserver.queuePlace = positioninqueue; // update info on the web page
|
||||
webserver.ETA = ETA;
|
||||
server.motd = `Place in queue: ${positioninqueue}`; // set the MOTD because why not
|
||||
if (webserver.queuePlace != "None") dc.user.setActivity("Pos: " + webserver.queuePlace + " ETA: " + webserver.ETA); //set the Discord Activity
|
||||
if (lastQueuePlace != webserver.queuePlace) {
|
||||
log("Position in Queue: " + webserver.queuePlace)
|
||||
}
|
||||
lastQueuePlace = webserver.queuePlace;
|
||||
}
|
||||
if (meta.name === "login") {
|
||||
playerid = data.entityId;
|
||||
}
|
||||
if (finishedQueue === false && meta.name === "chat") { // we can know if we're about to finish the queue by reading the chat message
|
||||
// we need to know if we finished the queue otherwise we crash when we're done, because the queue info is no longer in packets the server sends us.
|
||||
let chatMessage = JSON.parse(data.message);
|
||||
if (chatMessage.text && chatMessage.text === "Connecting to the server...") {
|
||||
if (webserver.restartQueue && proxyClient == null) { //if we have no client connected and we should restart
|
||||
stop();
|
||||
setTimeout(reconnect, 4000);
|
||||
} else {
|
||||
finishedQueue = true;
|
||||
webserver.queuePlace = "FINISHED";
|
||||
webserver.ETA = "NOW";
|
||||
switch (meta.name) {
|
||||
case "map_chunk":
|
||||
chunkData.push(data);
|
||||
break;
|
||||
case "playerlist_header":
|
||||
if (!finishedQueue && config.minecraftserver.hostname === "2b2t.org") { // if the packet contains the player list, we can use it to see our place in the queue
|
||||
let headermessage = JSON.parse(data.header);
|
||||
let positioninqueue = headermessage.text.split("\n")[5].substring(25);
|
||||
let ETA = headermessage.text.split("\n")[6].substring(27);
|
||||
webserver.queuePlace = positioninqueue; // update info on the web page
|
||||
webserver.ETA = ETA;
|
||||
server.motd = `Place in queue: ${positioninqueue}`; // set the MOTD because why not
|
||||
if (webserver.queuePlace !== "None" && lastQueuePlace !== webserver.queuePlace) {
|
||||
activity("Pos: " + webserver.queuePlace + " ETA: " + webserver.ETA); //set the Discord Activity
|
||||
log("Position in Queue: " + webserver.queuePlace)
|
||||
if (config.notification.enabled && webserver.queuePlace <= config.notification.queuePlace && !notisend && config.discordBot && id != null) {
|
||||
dc.fetchUser(id, false).then(user => {
|
||||
user.send("The queue is almost finished. You are in Position: " + webserver.queuePlace);
|
||||
})
|
||||
notisend = true;
|
||||
}
|
||||
}
|
||||
lastQueuePlace = webserver.queuePlace;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "chat":
|
||||
if (finishedQueue === false) { // we can know if we're about to finish the queue by reading the chat message
|
||||
// we need to know if we finished the queue otherwise we crash when we're done, because the queue info is no longer in packets the server sends us.
|
||||
let chatMessage = JSON.parse(data.message);
|
||||
if (chatMessage.text && chatMessage.text === "Connecting to the server...") {
|
||||
if (webserver.restartQueue && proxyClient == null) { //if we have no client connected and we should restart
|
||||
stop();
|
||||
setTimeout(reconnect, 4000);
|
||||
} else {
|
||||
finishedQueue = true;
|
||||
webserver.queuePlace = "FINISHED";
|
||||
webserver.ETA = "NOW";
|
||||
activity("Queue is finished")
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "respawn":
|
||||
Object.assign(loginpacket, data);
|
||||
chunkData = [];
|
||||
break;
|
||||
case "login":
|
||||
loginpacket = data;
|
||||
break;
|
||||
case "game_state_change":
|
||||
loginpacket.gameMode = data.gameMode;
|
||||
break;
|
||||
}
|
||||
/*if (c<50) {
|
||||
console.log("meta: " + JSON.stringify(meta) + "\ndata: " + JSON.stringify(data));
|
||||
c++;
|
||||
}*/
|
||||
//if (meta.name !== "map_chunk" && !meta.name.includes("entity")) console.log("meta: " + JSON.stringify(meta) + "\ndata: " + JSON.stringify(data));
|
||||
|
||||
if (proxyClient) { // if we are connected to the proxy, forward the packet we recieved to our game.
|
||||
filterPacketAndSend(data, meta, proxyClient);
|
||||
}
|
||||
@ -137,7 +159,10 @@ function startQueuing() {
|
||||
}
|
||||
stop();
|
||||
log(`Connection error by 2b2t server. Error message: ${err} Reconnecting...`);
|
||||
if (config.reconnect.onError) setTimeout(reconnect, 4000);
|
||||
if (config.reconnect.onError) {
|
||||
if (err == "Error: Invalid credentials. Invalid username or password.") setTimeout(reconnect, 60000);
|
||||
else setTimeout(reconnect, 4000);
|
||||
}
|
||||
});
|
||||
|
||||
server = mc.createServer({ // create a server for us to connect to
|
||||
@ -151,15 +176,7 @@ function startQueuing() {
|
||||
|
||||
server.on('login', (newProxyClient) => { // handle login
|
||||
setTimeout(send, 50)
|
||||
newProxyClient.write('login', {
|
||||
entityId: playerid,
|
||||
levelType: 'default',
|
||||
gameMode: 0,
|
||||
dimension: 0,
|
||||
difficulty: 2,
|
||||
maxPlayers: server.maxPlayers,
|
||||
reducedDebugInfo: false
|
||||
});
|
||||
newProxyClient.write('login', loginpacket);
|
||||
newProxyClient.write('position', {
|
||||
x: 0,
|
||||
y: 1.62,
|
||||
@ -172,12 +189,12 @@ function startQueuing() {
|
||||
newProxyClient.on('packet', (data, meta) => { // redirect everything we do to 2b2t
|
||||
let chunkPos = {};
|
||||
if (meta.name === "position") {
|
||||
chunkPos.x = roundToZero(data.x / 16);
|
||||
chunkPos.z = roundToZero(data.z / 16);
|
||||
chunkPos.x = round(data.x / 16);
|
||||
chunkPos.z = round(data.z / 16);
|
||||
if (chunkPos.z !== chunkPos.lx || chunkPos.x !== chunkPos.lx) {
|
||||
|
||||
for (let i = 0; i < chunkData.length; i++) {
|
||||
if (chunkData[i].x < chunkPos.x - 10 || chunkData[i].x > chunkPos + 10 || chunkData[i].z < chunkPos.z - 10 || chunkData[i] > chunkPos.z + 10) { //if a cached chunk is outside of the render distance
|
||||
if (chunkData[i].x < chunkPos.x - config.minecraftserver.renderDistance || chunkData[i].x > chunkPos + config.minecraftserver.renderDistance || chunkData[i].z < chunkPos.z - config.minecraftserver.renderDistance || chunkData[i] > chunkPos.z + config.minecraftserver.renderDistance) { //if a cached chunk is outside of the render distance
|
||||
chunkData.splice(i, 1); // we delete it.
|
||||
}
|
||||
}
|
||||
@ -219,22 +236,6 @@ function reconnect() {
|
||||
});
|
||||
}
|
||||
|
||||
function prioQueueLength() {
|
||||
https.get("https://api.2b2t.dev/prioq", (resp) => {
|
||||
let qdata = '';
|
||||
resp.on('data', (chunk) => {
|
||||
qdata += chunk;
|
||||
});
|
||||
resp.on("end", () => {
|
||||
queueData = JSON.parse(qdata);
|
||||
secRun = true;
|
||||
})
|
||||
}).on("error", (err) => {
|
||||
log("prioQueue api error: " + err)
|
||||
});
|
||||
if (secRun) return queueData[1];
|
||||
}
|
||||
|
||||
//function to filter out some packets that would make us disconnect otherwise.
|
||||
//this is where you could filter out packets with sign data to prevent chunk bans.
|
||||
function filterPacketAndSend(data, meta, dest) {
|
||||
@ -243,24 +244,42 @@ function filterPacketAndSend(data, meta, dest) {
|
||||
}
|
||||
}
|
||||
|
||||
function roundToZero(number) {
|
||||
if (number < 0) return Math.ceil(number);
|
||||
function round(number) {
|
||||
if (number > 0) return Math.ceil(number);
|
||||
else return Math.floor(number);
|
||||
}
|
||||
|
||||
function activity(string) {
|
||||
if (config.discordBot) dc.user.setActivity(string);
|
||||
}
|
||||
|
||||
//the discordBot part starts here.
|
||||
if (config.discordBot) {
|
||||
fs.access(save, error => {
|
||||
fs.readFile(save, (err, data) => {
|
||||
if (err) log(err)
|
||||
id = data;
|
||||
});
|
||||
});
|
||||
var dc = new discord.Client()
|
||||
dc.on('ready', () => {
|
||||
dc.user.setActivity("Queue is stopped.");
|
||||
cmdInput();
|
||||
});
|
||||
|
||||
dc.on('message', msg => {
|
||||
if (msg.author.username !== dc.user.username) {
|
||||
userInput(msg.content, true, msg);
|
||||
if (msg.author.id !== id) {
|
||||
fs.writeFile(save, msg.author.id, function (err) {
|
||||
if (err) {
|
||||
log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
id = msg.author.id
|
||||
}
|
||||
});
|
||||
|
||||
dc.login(secrets.BotToken);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user