Scrawl/main.js

408 lines
8.2 KiB
JavaScript

/*
* Copyright (C) 2021 Thomas Van Acker
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*
* This project is hosted on https://git.bitscuit.be/bitscuit/Scrawl
*
*/
// Imports
const http = require("http");
const url = require("url");
const fs = require("fs");
const util = require("util");
// Vars
const hostname = "localhost"; // Enter your local IP address here
const port = 5000; // Enter a port number here
const STATE_LOBBY = 0;
const STATE_MANUAL = 10;
const STATE_WRITE = 20;
const STATE_PICK = 30;
const STATE_RESULT = 40;
const STATE_END = 50;
var state = STATE_LOBBY;
var userSockets = [];
var userNames = [];
var userOK = [];
var lines = [];
var lineNr = 0; // Current line
var linesTotal = 10; // Total nr of lines before game ends
var submissions = [];
var userWrite = [];
var userPick = [];
var userPoints = [];
// Create HTTP server
const server = http.createServer((req, res) => {
// Parse url
const requrl = url.parse(req.url);
var reqpath = requrl.pathname;
if(reqpath == "/") reqpath = "/index.html";
// Respond to requests
try{
// Return requested page
res.statusCode = 200;
res.write(fs.readFileSync("html"+reqpath));
res.end();
}catch(error){
// 404 if page not found
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain");
res.end("404 - Page Not Found");
}
});
// Create socket.io server
const io = require("socket.io")(server); // Can only be done when server is created
io.on("connection", (socket) => {
console.log("New user connected!");
// Init socket listeners
// Disconnect
socket.on("disconnect", () => {
console.log("User disconnected!");
// Remove from user lists
if(userSockets.indexOf(socket) != -1){
// When in users list
name = userNames[userSockets.indexOf(socket)];
userNames.splice(userSockets.indexOf(socket), 1);
userSockets.splice(userSockets.indexOf(socket), 1);
if(state == STATE_LOBBY){
// Send new lobby
userSockets.forEach(function(s){s.emit("LOBBY", userNames);});
}else{
// Send disconnect message
userSockets.forEach(function(s){s.emit("DISCONNECT", name);});
}
}
});
// JOIN
socket.on("JOIN", (username) => {
console.log("JOIN request for "+username);
// Check if can enter in lobby
if(state == STATE_LOBBY){
// Add user to lists
userSockets.push(socket);
userNames.push(username);
console.log(userNames);
// Send lobby data
userSockets.forEach(function(s){s.emit("LOBBY", userNames);});
}else{
// When can't enter
socket.emit("LOBBY_CLOSED");
}
});
// START
socket.on("START", () => {
console.log("START request");
// Send START to all users
userSockets.forEach(function(s){s.emit("START");});
// Set state
state = STATE_MANUAL;
// Reset vars
lines = [];
lineNr = 0;
userPoints = [];
for(i=0; i<userSockets.length; i++){
userPoints.push(0);
}
// Send game data
var dataUser = {usernames:userNames, linesTotal:linesTotal};
for(i=0; i<userSockets.length; i++){
userSockets[i].emit("START", dataUser);
}
console.log("Game data sent!");
// Reset OK
userOK = [];
for(i=0; i<userSockets.length; i++){
userOK[i] = false;
}
});
// OK
socket.on("OK", () => {
ok(socket);
});
// WRITE
socket.on("WRITE", (data) => {
// When received write from player
// Check if empty
if(data.write == ""){
socket.emit("WRITE_REPLY", {error:"Please enter some text!"});
return;
}
// Add to list
userWrite[userSockets.indexOf(socket)] = data.write;
// Perform ok
ok(socket);
});
// PICK
socket.on("PICK", (id) => {
// When received pick form player
// Check if not my entry
if(userWrite[userSockets.indexOf(socket)] == submissions[id]){
socket.emit("PICK_REPLY", {error:"You can't vote for your own submission!"});
return;
}
// Add to list
userPick[userSockets.indexOf(socket)] = id;
// Perform ok
ok(socket);
});
});
// Start HTTP server
server.listen(port, hostname, () => {
console.log("Server running at http://"+hostname+":"+port);
});
// Game methods
function next(){
// Go to next step of game
// Send LOADING
userSockets.forEach(function(s){s.emit("LOADING");});
if(state == STATE_MANUAL || state == STATE_RESULT){
// Write
state = STATE_WRITE;
// Update vars
lineNr++;
userWrite = [];
for(i=0; i<userSockets.length; i++){
userWrite.push("");
}
userPick = [];
for(i=0; i<userSockets.length; i++){
userPick.push("");
}
// Stop if got all questions
if(lineNr > linesTotal){
// End
state = STATE_END;
// Gather data
var sorted = [];
for(i=0; i<userNames.length; i++){
sorted.push({name:userNames[i], score:userPoints[i]});
}
sorted.sort((a,b) => b.score-a.score);
console.log(sorted);
var ranks = [];
var r = 1;
var ls = sorted[0].score;
for(i=0; i<sorted.length; i++){
if(sorted[i].score < ls){
r = i+1;
ls = sorted[i].score;
}
ranks.push({name:sorted[i].name, score:sorted[i].score, rank:r});
}
console.log(ranks);
// Send data
var data = {ranks:ranks, lineNr:lineNr, lines:lines};
userSockets.forEach(function(s){s.emit("STOP", data);});
// Reset vars
state = STATE_LOBBY;
userSockets = [];
userNames = [];
}else{
// Load data for new line
userSockets.forEach(function(s){s.emit("WRITE", {lineNr:lineNr, linesTotal:linesTotal, lines:lines});});
}
}else if(state == STATE_WRITE){
// Pick
state = STATE_PICK;
console.log("Preparing PICK");
// Send story and all writes to players
submissions = [];
for(i=0; i<userWrite.length; i++){
submissions.splice(rand(0, submissions.length+1), 0, userWrite[i]);
}
console.log(submissions);
// Send
var data = {lineNr:lineNr, linesTotal:linesTotal, lines:lines, submissions:submissions};
userSockets.forEach(function(s){s.emit("PICK", data);});
console.log(data);
}else if(state == STATE_PICK){
// Answer
state = STATE_RESULT;
console.log("Preparing ANSWER");
// Update points
var pointsDiff = [];
for(i=0; i<userSockets.length; i++){
pointsDiff.push(0);
}
for(i=0; i<userSockets.length; i++){
// Check writes
for(j=0; j<userWrite.length; j++){
if(submissions[userPick[i]].toUpperCase() == userWrite[j].toUpperCase() && i != j){
userPoints[j] += 1;
pointsDiff[j] += 1;
}
}
}
// Get line with most votes
var bestLine = 0;
var lineVotes = [];
for(i=0; i<userSockets.length; i++){
lineVotes.push(0);
}
for(i=0; i<userSockets.length; i++){
lineVotes[userPick[i]] += 1;
}
for(i=0; i<userSockets.length; i++){
if(lineVotes[i] > lineVotes[bestLine]){
bestLine = i;
}
}
// Add bestLine to lines
lines.push(submissions[bestLine]);
// Send data to players
var data = {lineNr:lineNr, linesTotal:linesTotal, lines:lines, submissions:submissions, bestLine:bestLine, userPick:userPick, userWrite:userWrite, userPoints:userPoints, pointsDiff:pointsDiff, usernames:userNames};
userSockets.forEach(function(s){s.emit("ANSWER", data);});
console.log(data);
}
}
function ok(socket){
// Set OK for this player
userOK[userSockets.indexOf(socket)] = true;
// Check if everybody OK
var OK = true;
for(i=0; i<userOK.length; i++){
OK = OK && userOK[i];
}
// Send OK data
var waitingFor = [];
for(i=0; i<userOK.length; i++){
if(!userOK[i]) waitingFor.push(userNames[i]);
}
var dataOK = {users:waitingFor};
userSockets.forEach(function(s){s.emit("OK_REPLY", dataOK)});
// Perform next step if OK
if(OK){
// Reset OK
userOK = [];
for(i=0; i<userSockets.length; i++){
userOK[i] = false;
}
// Next step
next();
}
}
// Helper methods
function rand(min, max){
return Math.floor(Math.random()*(max-min) + min);
}