-added multidimension support for chunk caching

-discord Bot sends a message when the queue is about to finish
This commit is contained in:
MrGeorgen
2020-04-14 18:31:31 +02:00
parent 1f2ce221e7
commit ada57813e5
3 changed files with 109 additions and 84 deletions

View File

@ -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);
}