-added multidimension support for chunk caching
-discord Bot sends a message when the queue is about to finish
This commit is contained in:
@ -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)
|
||||||
|
|
||||||
|
|||||||
12
config.json
12
config.json
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
177
src/main.js
177
src/main.js
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user