-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

@ -38,10 +38,10 @@ Do not repeatedly stop and start the queue, eventually you will not be able to l
- starting the queue too many times in a row can sometimes boot you out of your minecraft account (starting the queue or connecting in the minecraft client will tell you "wrong email or password"). to fix this, log in to your account at minecraft.net, then restart minecraft. both of these issues are limitations put in place by mojang to prevent account stealing, and are not fixable. - starting the queue too many times in a row can sometimes boot you out of your minecraft account (starting the queue or connecting in the minecraft client will tell you "wrong email or password"). to fix this, log in to your account at minecraft.net, then restart minecraft. both of these issues are limitations put in place by mojang to prevent account stealing, and are not fixable.
- Some people report not being able to ride animals using this proxy. - Some people report not being able to ride animals using this proxy.
- 2b2t sometimes bugs out and removes you from the queue without telling you. In this case, your queue position will no longer move. Reconnect to fix this. - 2b2t sometimes bugs out and removes you from the queue without telling you. In this case, your queue position will no longer move. Reconnect to fix this.
- If you connect after the the queue is finnished or reconnect the proxy will send cached chunk data. Otherwise you would fly in a emtpy world. Other data however like entities are not chached and will not displayed correctly. You can move out of render distance and come back to fix this issue. - If you connect after the the queue is finnished or reconnect the proxy will send cached chunk data. Otherwise you would fly in a emtpy world. Other data however like entities are not chached and will not displayed correctly. You can move out of render distance and come back to fix this issue. Sometimes the client renders a cached chunk with a blank texture.
# Todo # Todo
- [ ] Mention you once you get below 30 queue - [x] Mention you once you get below 30 queue
- [ ] Calculate the perfect time to join the queue based on queue size/eta. (E.g aim to join 2b2t between 8-9pm) - [ ] Calculate the perfect time to join the queue based on queue size/eta. (E.g aim to join 2b2t between 8-9pm)

View File

@ -14,9 +14,15 @@
"notConnectedQueueEnd": true // restart the queue if you are not connect at the end of it "notConnectedQueueEnd": true // restart the queue if you are not connect at the end of it
}, },
"minecraftserver": { // the server you want to connect. Make not much sense to change it, was just added for development purpose "minecraftserver": { // the server you want to connect. Make not much sense to change it, was just added for development purpose
"hostname": "localhost", "hostname": "2b2t.org",
"port": 25565, "port": 25565,
"renderDistance": 10, "renderDistance": 8,
"version": "1.12.2" "version": "1.12.2",
"onlinemode": true, // chunk caching does not work correctly if set to false
"username": "lol" // the username to use if onlinemode is false
},
"notification": { // sends a message via discord if the place in the queue reaches the specified number
"enabled": true, // you must send the bot a message once.
"queuePlace": 20
} }
} }

View File

@ -11,15 +11,17 @@ const {DateTime} = require("luxon");
const https = require("https"); const https = require("https");
const prompt = require("prompt"); const prompt = require("prompt");
const ping = require('minecraft-server-util'); const ping = require('minecraft-server-util');
const cycle = require("cycle")
const sleep = require("sleep-ms") const sleep = require("sleep-ms")
const save = "../saveid"
var timedStart; var timedStart;
var lastQueuePlace; var lastQueuePlace;
var queueData;
var secRun;
var chunkData = []; var chunkData = [];
var c = 0; var c = 0;
var notisend = false;
var loginpacket;
var id;
webserver.restartQueue = config.reconnect.notConnectedQueueEnd; webserver.restartQueue = config.reconnect.notConnectedQueueEnd;
prioQueueLength();
if (config.webserver) { if (config.webserver) {
webserver.createServer(config.ports.web); // create the webserver webserver.createServer(config.ports.web); // create the webserver
webserver.password = config.password webserver.password = config.password
@ -41,11 +43,12 @@ let server; // the minecraft server to pass packets
//comand prompt //comand prompt
prompt.start(); prompt.start();
cmdInput();
function cmdInput() { function cmdInput() {
prompt.get("cmd", function (err, result) { prompt.get("cmd", function (err, result) {
userInput(result.cmd, false); userInput(result.cmd, false);
cmdInput() cmdInput();
}); });
} }
@ -59,61 +62,80 @@ function stop() {
proxyClient.end("Stopped the proxy."); // boot the player from the server proxyClient.end("Stopped the proxy."); // boot the player from the server
} }
server.close(); // close the server server.close(); // close the server
dc.user.setActivity("Queue is stopped."); activity("Queue is stopped.");
} }
// function to start the whole thing // function to start the whole thing
function startQueuing() { function startQueuing() {
var playerid;
let startTime = DateTime.local();
webserver.isInQueue = true; webserver.isInQueue = true;
dc.user.setActivity("Starting the queue..."); activity("Starting the queue...");
client = mc.createClient({ // connect to 2b2t let options = {
host: config.minecraftserver.hostname, host: config.minecraftserver.hostname,
port: config.minecraftserver.port, port: config.minecraftserver.port,
username: secrets.username,
password: secrets.password,
version: config.minecraftserver.version 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; let finishedQueue = false;
client.on("packet", (data, meta) => { // each time 2b2t sends a packet client.on("packet", (data, meta) => { // each time 2b2t sends a packet
if (meta.name === "map_chunk") { switch (meta.name) {
chunkData.push(data); case "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 break;
let headermessage = JSON.parse(data.header); case "playerlist_header":
let positioninqueue = headermessage.text.split("\n")[5].substring(25); 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 ETA = headermessage.text.split("\n")[6].substring(27); let headermessage = JSON.parse(data.header);
webserver.queuePlace = positioninqueue; // update info on the web page let positioninqueue = headermessage.text.split("\n")[5].substring(25);
webserver.ETA = ETA; let ETA = headermessage.text.split("\n")[6].substring(27);
server.motd = `Place in queue: ${positioninqueue}`; // set the MOTD because why not webserver.queuePlace = positioninqueue; // update info on the web page
if (webserver.queuePlace != "None") dc.user.setActivity("Pos: " + webserver.queuePlace + " ETA: " + webserver.ETA); //set the Discord Activity webserver.ETA = ETA;
if (lastQueuePlace != webserver.queuePlace) { server.motd = `Place in queue: ${positioninqueue}`; // set the MOTD because why not
log("Position in Queue: " + webserver.queuePlace) if (webserver.queuePlace !== "None" && lastQueuePlace !== webserver.queuePlace) {
} activity("Pos: " + webserver.queuePlace + " ETA: " + webserver.ETA); //set the Discord Activity
lastQueuePlace = webserver.queuePlace; log("Position in Queue: " + webserver.queuePlace)
} if (config.notification.enabled && webserver.queuePlace <= config.notification.queuePlace && !notisend && config.discordBot && id != null) {
if (meta.name === "login") { dc.fetchUser(id, false).then(user => {
playerid = data.entityId; user.send("The queue is almost finished. You are in Position: " + webserver.queuePlace);
} })
if (finishedQueue === false && meta.name === "chat") { // we can know if we're about to finish the queue by reading the chat message notisend = true;
// 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...") { lastQueuePlace = webserver.queuePlace;
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";
} }
} 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) { //if (meta.name !== "map_chunk" && !meta.name.includes("entity")) console.log("meta: " + JSON.stringify(meta) + "\ndata: " + JSON.stringify(data));
console.log("meta: " + JSON.stringify(meta) + "\ndata: " + JSON.stringify(data));
c++;
}*/
if (proxyClient) { // if we are connected to the proxy, forward the packet we recieved to our game. if (proxyClient) { // if we are connected to the proxy, forward the packet we recieved to our game.
filterPacketAndSend(data, meta, proxyClient); filterPacketAndSend(data, meta, proxyClient);
} }
@ -137,7 +159,10 @@ function startQueuing() {
} }
stop(); stop();
log(`Connection error by 2b2t server. Error message: ${err} Reconnecting...`); 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 server = mc.createServer({ // create a server for us to connect to
@ -151,15 +176,7 @@ function startQueuing() {
server.on('login', (newProxyClient) => { // handle login server.on('login', (newProxyClient) => { // handle login
setTimeout(send, 50) setTimeout(send, 50)
newProxyClient.write('login', { newProxyClient.write('login', loginpacket);
entityId: playerid,
levelType: 'default',
gameMode: 0,
dimension: 0,
difficulty: 2,
maxPlayers: server.maxPlayers,
reducedDebugInfo: false
});
newProxyClient.write('position', { newProxyClient.write('position', {
x: 0, x: 0,
y: 1.62, y: 1.62,
@ -172,12 +189,12 @@ function startQueuing() {
newProxyClient.on('packet', (data, meta) => { // redirect everything we do to 2b2t newProxyClient.on('packet', (data, meta) => { // redirect everything we do to 2b2t
let chunkPos = {}; let chunkPos = {};
if (meta.name === "position") { if (meta.name === "position") {
chunkPos.x = roundToZero(data.x / 16); chunkPos.x = round(data.x / 16);
chunkPos.z = roundToZero(data.z / 16); chunkPos.z = round(data.z / 16);
if (chunkPos.z !== chunkPos.lx || chunkPos.x !== chunkPos.lx) { if (chunkPos.z !== chunkPos.lx || chunkPos.x !== chunkPos.lx) {
for (let i = 0; i < chunkData.length; i++) { 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. 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. //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. //this is where you could filter out packets with sign data to prevent chunk bans.
function filterPacketAndSend(data, meta, dest) { function filterPacketAndSend(data, meta, dest) {
@ -243,24 +244,42 @@ function filterPacketAndSend(data, meta, dest) {
} }
} }
function roundToZero(number) { function round(number) {
if (number < 0) return Math.ceil(number); if (number > 0) return Math.ceil(number);
else return Math.floor(number); else return Math.floor(number);
} }
function activity(string) {
if (config.discordBot) dc.user.setActivity(string);
}
//the discordBot part starts here. //the discordBot part starts here.
if (config.discordBot) { if (config.discordBot) {
fs.access(save, error => {
fs.readFile(save, (err, data) => {
if (err) log(err)
id = data;
});
});
var dc = new discord.Client() var dc = new discord.Client()
dc.on('ready', () => { dc.on('ready', () => {
dc.user.setActivity("Queue is stopped."); dc.user.setActivity("Queue is stopped.");
cmdInput();
}); });
dc.on('message', msg => { dc.on('message', msg => {
if (msg.author.username !== dc.user.username) { if (msg.author.username !== dc.user.username) {
userInput(msg.content, true, msg); 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); dc.login(secrets.BotToken);
} }