// .env Variables require("dotenv").config({path: "../.env"}); //socket.io setup let server = require("http").createServer(); let io = require("socket.io")(server); let ioAuth = require("socketio-auth"); ioAuth(io, { authenticate: function(socket, data, callback) { return callback(null, data.SERVER_PASS_KEY === process.env.SERVER_PASS_KEY); }, postAuthenticate: function(socket, data) { console.log("Authenticated: " + data.username); socket.client.username = data.username; } }); //mysql let mysql = require("mysql"); let dbConnection = mysql.createConnection({ host: process.env.DATABASE_HOST, user: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD }); dbConnection.connect((err) => { if (err) throw err; console.log("Connected to the database"); dbConnection.query("USE sanctum;"); }); //shared code let calcRandom = require('../Shared/calc_random'); //TODO: isolate these responses to specific bots io.on("connection", async (socket) => { console.log("made socket connection"); socket.on("disconnect", async () => { console.log(socket.client.username + " disconnected"); }); socket.on("updateStamina", handleUpdateStamina); socket.on("conversion", handleConversion); socket.on("checkin", handleCheckin); socket.on("wallet", handleWallet); //TODO: server ping from ADAM socket.on("transfer", handleTransfer); socket.on("userStats", handleUserStats); socket.on("addXP", handleAddXP); socket.on("levelUp", handleLevelUp); }); //listen server.listen(process.env.SERVER_PORT); console.log("listening on port " + process.env.SERVER_PORT); //update the playerbase's stamina on command async function handleUpdateStamina({ userID, data }) { let query = "UPDATE users SET stamina = stamina + 1 WHERE stamina < maxStamina;"; dbConnection.query(query, (err, result) => { if (err) throw err; console.log("updated stamina for all users"); }); }; //handle initial faction join and faction conversions async function handleConversion({ data }, fn) { //data[0] = user ID //data[1] = factionRole //possible arguments to fn: ["joined", "alreadyJoined", "conversionLocked", "newUser"] //find the last time this user converted let query = `SELECT faction FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //check if this is a new user if (result.length === 0) { let query = `INSERT INTO users (userID, faction, factionChanged) VALUES (${data[0]}, ${data[1]}, NOW());`; return dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(data[0], "new user", `joined faction ${data[1]}`); return fn("newUser"); }); } //check if already joined this faction if (result[0].faction == data[1]) { //faction == factionRole return fn("alreadyJoined"); } //check if enough time has passed to join a new faction let query = `SELECT TIME_TO_SEC(TIMEDIFF(NOW(), factionChanged)) FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; if(result[0]['TIME_TO_SEC(TIMEDIFF(NOW(), factionChanged))'] < 60 * 60 * 24 * 7) { //7 days return fn("conversionLocked"); //too soon } else { //update the database with the join query = `UPDATE users SET faction = ${data[1]}, factionChanged = NOW() WHERE userID='${data[0]}';`; return dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(data[0], "joined", `joined faction ${data[1]}`); return fn("joined"); }); } }) }); } //handle checkin, and add 1 XP async function handleCheckin({ data }, fn) { //handle checkins (grant crystal bonus) //arguments to fn: ["available", time since last checkin], randomAmount let randomAmount = calcRandom.Random(4, 9); let query = `SELECT TIME_TO_SEC(TIMEDIFF(NOW(), lastCheckin)) FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; if (result[0]['TIME_TO_SEC(TIMEDIFF(NOW(), lastCheckin))'] == null || result[0]['TIME_TO_SEC(TIMEDIFF(NOW(), lastCheckin))'] > 60 * 60 * 22) { //22 hours let query = `UPDATE users SET lastCheckin = NOW(), wallet = wallet + ${randomAmount} WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(data[0], "checkin", `gained ${randomAmount} to wallet`); addExperience(data[0], 1); //Add 1 XP on every checkin return fn("available", randomAmount); }); } else { return fn(calculateTimeAgo(result[0]['TIME_TO_SEC(TIMEDIFF(NOW(), lastCheckin))'])); } }); } //handle account requests async function handleWallet({ data }, fn) { //data[0] = ID of the person to check let query = `SELECT wallet FROM users WHERE userID='${data[0]}' LIMIT 1;`; dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(data[0], "wallet query", `result: ${result[0].wallet}`); fn(result[0].wallet); }); } //handle transfering wallet balance between accounts async function handleTransfer({ data }, fn) { console.log("received a transfer request..."); //data[0] = ID of the source account //data[1] = ID of the destination account //data[2] = amount to send //parameters to fn: ["success", "failure"] //check there's enough in the sender's wallet let query = `SELECT wallet - ${data[2]} FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //too little in there if (result[0][`wallet - ${data[2]}`] < 0) { return fn("failure"); } //check the recipient is real let query = `SELECT * FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; if (result.length == 0) { return fn("failure"); } //subtract from the sender let query = `UPDATE users SET wallet = wallet - ${data[2]} WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //add to the recipient let query = `UPDATE users SET wallet = wallet + ${data[2]} WHERE userID='${data[1]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //finally dbLog(data[0], "wallet transfer", `${data[2]} to ${data[1]}`); return fn("success"); }); }); }); }); } //handle the user stats async function handleUserStats({ data }, fn) { console.log("received a userStats request..."); //data[0] = user ID //parameters to fn: stat structure let query = `SELECT level, experience, maxHealth, health, maxStamina, stamina, strength, speed, upgradePoints, wallet FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; let stats = { level: result[0].level, experience: result[0].experience, levelProgress: calculateLevelProgress(result[0].experience), maxHealth: result[0].maxHealth, health: result[0].health, maxStamina: result[0].maxStamina, stamina: result[0].stamina, strength: result[0].strength, speed: result[0].speed, upgradePoints: result[0].upgradePoints, wallet: result[0].wallet }; return fn(stats); }); } //DEBUGGING? async function handleAddXP({ userID, data }) { console.log("received an addXP request..."); //data[0] = amount addExperience(userID, data[0]); } //handle levelling up async function handleLevelUp({ data }, fn) { //NOTE: levelling up is handled manually because of reasons console.log("received a levelUp request..."); //data[0] = user ID //parameters to fn: ["none", "levelUp"], level, upgradePoints //get the current level and total amount of experience let query = `SELECT level, experience, upgradePoints FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //calculate the correct level, and compare it with the result let newLevel = Math.floor(calculateLevel(result[0].experience)); //if no levelling, return if (newLevel == result[0].level) { return fn("none", result[0].level, result[0].upgradePoints); } //update the level and the upgrade points let query = `UPDATE users SET level = ${newLevel}, upgradePoints = upgradePoints + ${newLevel - result[0].level} WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; //finally, pass the level and upgrade points to the client let query = `SELECT level, upgradePoints FROM users WHERE userID='${data[0]}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(data[0], "level up", `level: ${result[0]}, upgrade points: ${result[0].upgradePoints}`); return fn("levelUp", result[0].level, result[0].upgradePoints); }); }); }); } //utility functions function calculateLevel(experience) { const levelBase = 1.2; return levelBase * Math.sqrt(experience); } function calculateLevelProgress(experience) { let level = calculateLevel(experience); let base = Math.floor(level); let decimal = level - base; return Math.floor(decimal * 100); //percentage } function addExperience(userID, amount) { //Add an amount of XP to a user account let query = `UPDATE users SET experience = experience + ${amount} WHERE userID='${userID}' LIMIT 1;`; return dbConnection.query(query, (err, result) => { if (err) throw err; dbLog(userID, "xp up", `amount added: ${amount}`); }); } function dbLog(id, type, data) { let query = `INSERT INTO log (discordID, type, data) VALUES ('${id}', '${type}', '${data}')`; return dbConnection.query(query, (err, result) => { if (err) throw err; }); } function calculateTimeAgo(seconds) { if (seconds < 60) { return "just now"; } if (seconds < 60 * 60) { return "just this hour"; } if (seconds < 60 * 60 * 24) { return "just today"; } return "just recently"; }