diff --git a/README.md b/README.md index 52aa0d4..cf9bcfa 100644 --- a/README.md +++ b/README.md @@ -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. - 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. -- 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 -- [ ] 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) diff --git a/config.json b/config.json index d8d40dd..ed64042 100644 --- a/config.json +++ b/config.json @@ -14,9 +14,15 @@ "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 - "hostname": "localhost", + "hostname": "2b2t.org", "port": 25565, - "renderDistance": 10, - "version": "1.12.2" + "renderDistance": 8, + "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 } } diff --git a/src/main.js b/src/main.js index 80bf371..3c6e3f4 100644 --- a/src/main.js +++ b/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); }