You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
608 lines
17 KiB
JavaScript
608 lines
17 KiB
JavaScript
|
|
const express = require('express');
|
|
const database = require('./database');
|
|
const app = express();
|
|
const http = require('http').Server(app);
|
|
const port = process.env.PORT || 3000;
|
|
|
|
const io = require('socket.io')(http);
|
|
// util is what nodejs uses for console.log, but has a depth of 2 set
|
|
// so console.logs show [Array]/[Object] instead of useful info.
|
|
// This can be overridden console.log(util.inspect(LOGDATA, true, 4, true))
|
|
// 4 being new depth, true (last one) is to show colours
|
|
const util = require('util')
|
|
|
|
app.use(express.static(__dirname + '/public'));
|
|
|
|
http.listen(port, () => console.log('listening on port ' + port));
|
|
database.connect();
|
|
|
|
io.on('connection', onConnection);
|
|
|
|
// Variables
|
|
let numRooms = 0;
|
|
let numRoomsToPreGen = 1;
|
|
const maxRooms = 3;
|
|
const maxPlayersPerRoom = 2;
|
|
const maxSpectatorsPerRoom = 0;
|
|
|
|
// All the room
|
|
//let data = []; // Normal array
|
|
let data = {}; // Object array (this one for returning to player, and JSON stringify while keeping named ids)
|
|
let roomData = {};
|
|
for (let roomId = 1; roomId <= numRoomsToPreGen; roomId++) {
|
|
// Never have more rooms than max rooms!!!
|
|
if(numRooms > maxRooms){
|
|
break;
|
|
}
|
|
|
|
createRoom(roomId);
|
|
}
|
|
|
|
|
|
// For testing DB/makeshift ORM/building my objects
|
|
// Maybe if this works, put it into a deck and card module
|
|
// Doing similar to I have to other stuff, that will need to be
|
|
// migrated serverside anyways.
|
|
|
|
/*
|
|
let item = [];
|
|
let deckItem = {};
|
|
let deckList = {};
|
|
let cardItem = {};
|
|
*/
|
|
|
|
// cardClass, cardColourRequirement
|
|
// may want to be seperate too (as well as in cardItem), so that
|
|
// during match they can be altered by effects while keeping the OG card
|
|
// for inspecting (and compare against new stats,reqs,etc.)
|
|
// same with attack, cost, etc. things that will can be visually shown as
|
|
// changed in game
|
|
|
|
// Just grabbing everything from DB for now, as it's quite small at current
|
|
// then will rejig when needed.
|
|
// Should all cards, effects, classes etc. be loaded in on server start
|
|
// then just load decks and decklists when needed?
|
|
function getDecks(deckId, playerId, item = [], deckItem = {}){
|
|
// Await promise, once it's done get the data, if errors send err
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
|
|
database.dbGetDecks().then(data => {
|
|
|
|
let decks = [];
|
|
|
|
data.forEach((deck) => {
|
|
//let itemId = item.length;
|
|
//item.push(itemId); // Add the next available item
|
|
// Add the deck info to deckItem
|
|
// deckItem[itemId] = {};
|
|
decks.push({
|
|
'deckId': deck.deckId,
|
|
'playerId': deck.playerId,
|
|
'deckName': deck.deckName,
|
|
});
|
|
});
|
|
|
|
// Resolve the decks pulled
|
|
resolve(decks);
|
|
|
|
//console.log(item);
|
|
//console.log(deckItem);
|
|
//console.log(decks);
|
|
})
|
|
.catch(err => { throw err; })
|
|
|
|
});
|
|
return dPromise;
|
|
}
|
|
//getDecks();
|
|
function getDeckList(){
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
|
|
database.dbGetDeckList().then(data => {
|
|
|
|
let deckList = [];
|
|
|
|
data.forEach((listItem) => {
|
|
// Add the deck info to deckItem
|
|
deckList.push({
|
|
'deckId': listItem.deckId,
|
|
'playerId': listItem.playerId,
|
|
'cardId': listItem.cardId,
|
|
'cardCount': listItem.cardCount,
|
|
});
|
|
resolve(deckList);
|
|
});
|
|
})
|
|
.catch(err => { throw err; })
|
|
});
|
|
return dPromise;
|
|
}
|
|
//getDeckList();
|
|
function getCards(){
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
|
|
database.dbGetCards().then(data => {
|
|
|
|
let cards = [];
|
|
|
|
data.forEach((card) => {
|
|
cards.push({
|
|
'id': card.id,
|
|
'cardName': card.cardName,
|
|
'cardCost': card.cardCost,
|
|
'cardType': card.cardType,
|
|
'cardAttack': card.cardAttack,
|
|
'cardRarity': card.cardRarity,
|
|
'cardClass': [],
|
|
'cardColourRequirement': [],
|
|
});
|
|
});
|
|
resolve(cards);
|
|
})
|
|
.catch(err => { throw err; })
|
|
|
|
});
|
|
return dPromise;
|
|
}
|
|
//getCards();
|
|
function getCardClasses(){
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
database.dbGetCardClasses().then(data => {
|
|
|
|
let cardClasses = [];
|
|
|
|
data.forEach((cardClass) => {
|
|
|
|
cardClasses.push({
|
|
'cardId': cardClass.cardId,
|
|
'classId': cardClass.classId,
|
|
});
|
|
|
|
});
|
|
resolve(cardClasses);
|
|
})
|
|
.catch(err => { throw err; })
|
|
});
|
|
return dPromise;
|
|
}
|
|
//getCardClasses();
|
|
function getCardColourRequirement(){
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
database.dbGetCardColourRequirement().then(data => {
|
|
|
|
let colourRequirements = [];
|
|
|
|
data.forEach((cardColourReq) => {
|
|
|
|
colourRequirements.push({
|
|
'cardId': cardColourReq.cardId,
|
|
'colourId': cardColourReq.colourId,
|
|
'cost': cardColourReq.cost,
|
|
});
|
|
|
|
});
|
|
resolve(colourRequirements);
|
|
|
|
})
|
|
.catch(err => { throw err; })
|
|
});
|
|
return dPromise;
|
|
}
|
|
//getCardColourRequirement();
|
|
// Then effects which will have effects with parent triggers, and unit type checks
|
|
// colour checks, all sorts. So will be more difficult. Basic (flight, etc)
|
|
// shouldn't be too bad
|
|
// something like effect_basic, card_effect, effect_trigger, effect_requirement, effect_option
|
|
// effect_stage, effect_advanced, with advanced being an id with x triggers, triggers have
|
|
// x req, advanced as with x options, x stages to be able to fine-tune fancy stuff
|
|
// combining all other effects, units, cards, colours, etc. Will be a lot of though,
|
|
// but better than hard coding anything more than basic effects and effect check logic
|
|
// TODO: effect (as above)
|
|
|
|
// request a deck in the format CURRENTLY used in the game
|
|
// decks will likely be changed around
|
|
|
|
// https://www.geeksforgeeks.org/how-to-wait-for-multiple-promises-in-javascript/
|
|
// https://medium.com/@nikolozz/using-socket-io-with-async-await-13fa8c2dc9d9
|
|
// using last example
|
|
function requestDeck(socket, playerId, deckId){
|
|
return new Promise((resolve, reject) => {
|
|
(async () => {
|
|
|
|
// Get the deck(s) requested.
|
|
// Not 100% on how to do two differening atm
|
|
// Besides all of playerId, and all of deckId. But 1,2/2,3 for instance
|
|
const [decks, deckList] =
|
|
await Promise.all([
|
|
getDecks(),
|
|
getDeckList()
|
|
// Don't need to do this for each when awaiting all
|
|
// Instead it'll wait until each is done, set the data to the const
|
|
// Then do the stuff after the variable's are set!!
|
|
/*
|
|
getDecks().then(data => {
|
|
//console.log(data);
|
|
}).catch(err => { throw err; }),
|
|
*/
|
|
|
|
]);
|
|
|
|
//console.log(decks);
|
|
//console.log(deckList);
|
|
// Now loop the deckList for the cardIds
|
|
let deckCardIds = [];
|
|
deckList.forEach((deckItem) => {
|
|
deckCardIds.push(deckItem.cardId);
|
|
|
|
});
|
|
//console.log(deckCardIds);
|
|
|
|
// Next, get the cards in the deck by their ID
|
|
// TODO: https://stackoverflow.com/a/65510676
|
|
// Change SQL to accept for just the cards passed
|
|
|
|
// Get each cards data, colourReqs, and classes
|
|
const [cards, cardClasses, cardColourRequirements] =
|
|
await Promise.all([
|
|
getCards(),
|
|
getCardClasses(),
|
|
getCardColourRequirement(),
|
|
]);
|
|
// ^^^^ Classes async? Can pass the cardsIds, then loop classes, if the class cardId
|
|
// matches the cardId in cards[] then push the class to cards[x].classes
|
|
|
|
// TODO: Build the card so far async for it's done alongside getting effects?
|
|
// Then when both done add the effects to the existing cardObjects? Yep good idea me
|
|
//console.log(cards);
|
|
//console.info(cardClasses);
|
|
//console.log(cardColourRequirements);
|
|
|
|
const [builtCards] =
|
|
await Promise.all([
|
|
buildCards(cards, cardClasses, cardColourRequirements),
|
|
// TODO: builtEffects
|
|
]);
|
|
|
|
//console.log(builtCards);
|
|
// TODO: addEffectsToCards
|
|
// TODO: Finally do the effects for each card. This will be the complicater
|
|
// since it's not 100% thought out yet... (
|
|
|
|
// Add the cards (x of how many cardObjects with cardId are in count in decklist)
|
|
// to a deck array to pass through. Or decks array with deckId specified?
|
|
console.log(deckList);
|
|
// These are the four basic fellas for this
|
|
// from public/board.js where current game testing/logic is applied
|
|
// So creating as a test for a 'room' to see if it's simple enough to
|
|
// send and play with
|
|
// BUILD THE DATA TO SEND TO CLIENTS! (Kinda)
|
|
let item = [];
|
|
let itemCount = 0;
|
|
let deckData = {}; // New but may be useful
|
|
let cardData = {};
|
|
let boardElement = {};
|
|
// TODO: Set the player. For now will do this in front-end as testing currently
|
|
|
|
// Loop each item in the deckList
|
|
// Loop inside it X times where X is cardCount
|
|
// Add the builtCard with same cardId as deckList item X times
|
|
deckList.forEach((deckListItem) => {
|
|
// Check if it's a new deck (not sure how)
|
|
if(false){ // TODO: Logic check
|
|
item.push(itemCount); // Add new item to add stuff for
|
|
deckData[itemCount] = [deckListItem.deckId, deckListItem.playerId];
|
|
boardElement[itemCount] = 'realDeck';
|
|
itemCount++;
|
|
}
|
|
|
|
// For each new card, loop to the cardCount (how many cards in deck)
|
|
// and add to the deck
|
|
for(let i = 0; i < deckListItem.cardCount; i++){
|
|
item.push(itemCount); // Add new item to add stuff for
|
|
// ^ not using item.length incase anything in future gets deleted
|
|
// from item for instance
|
|
cardData[itemCount] = builtCards[deckListItem.cardId]; // builtCards id set to cardId from DB, so adding the builtCard object is based on the deckList's cardId (from DB)
|
|
boardElement[itemCount] = 'deck'; // Add all cards to deck at match start
|
|
itemCount++; // Increment item to not overwrite
|
|
}
|
|
|
|
});
|
|
|
|
//console.log(cardData);
|
|
return resolve(cardData);
|
|
|
|
// Promise stuff testing
|
|
})()
|
|
});
|
|
|
|
// TODO: In future the item inc. all the decks, cards, locations, and other attributes
|
|
// will come from here
|
|
|
|
// TODO: unrelated, but thought. If lots of players on, generating cards each time
|
|
// will be a big hit, as well as all the DB hits, so may need to load all cardObjects
|
|
// into memory/array when server starts. This then prevents any DB changes to alter
|
|
// things, but outside of testing that won't occur(?), may need to test this at some
|
|
// point to see. For now DB, and generating is ok, as still working on it
|
|
}
|
|
|
|
function requestDeckStart(socket){
|
|
// For future:
|
|
// Don't try to use promises between server/client
|
|
// It's painful and doesn't appear to work. Instead, let client have a loading
|
|
// screen, or wait for a while before doing something that requires server data
|
|
// like loading the decks... Then have the socket.on clientside trigger
|
|
// whatever functions, and stuff needs to happen
|
|
// Wasted so much time trying to do async server-client stuff. Don't bother
|
|
response = {success: false, message: 'Failed requestDeckStart() server.js'};
|
|
requestDeck().then(data => {
|
|
response.success = true;
|
|
response.message = data;
|
|
io.to(socket.id).emit('responseGetDeck', response);
|
|
|
|
})
|
|
.catch(err => {
|
|
response.message = err;
|
|
io.to(socket.id).emit('responseGetDeck', err);
|
|
});
|
|
}
|
|
|
|
function buildCards(cards, cardClasses, cardColourRequirements){
|
|
|
|
const dPromise = new Promise((resolve, reject) => {
|
|
|
|
builtCards = {};
|
|
|
|
//console.log(cards);
|
|
// Loop the cards and build the base
|
|
cards.forEach((card) => {
|
|
|
|
let builtCard = {
|
|
id: card.id,
|
|
name: card.cardName,
|
|
colour: [],
|
|
cost: card.cardCost,
|
|
costReq: [],
|
|
type: card.cardType,
|
|
atk: card.cardAttack,
|
|
rarity: card.cardRarity,
|
|
effect: [],
|
|
cardClass: [],
|
|
};
|
|
// Give the card an easily accessible Id for compares
|
|
// and to add to the cardItem being built
|
|
builtCards[card.id] = builtCard;
|
|
|
|
});
|
|
//console.log(builtCards);
|
|
|
|
// Next loop each class, and add to the new builtCard (if cardIds match)
|
|
cardClasses.forEach((cardClass) => {
|
|
|
|
// Check the card exists (it should always, but don't want jank)
|
|
if(cardClass.cardId in builtCards){
|
|
// It's the Id for now
|
|
// TODO: Send the text too (from join) but don't use for compares
|
|
//console.log(cardClass.cardId);
|
|
//console.log(cardClass.classId);
|
|
//console.log('---');
|
|
//console.log(builtCards[cardClass.cardId]);
|
|
|
|
// Add the class to the class array (cards can have multiple)
|
|
builtCards[cardClass.cardId].cardClass.push(cardClass.classId);
|
|
|
|
// TODO: As an array [classId, className] then
|
|
// card.classes[x][0] can be compared as numeric in code
|
|
}
|
|
|
|
});
|
|
|
|
resolve(builtCards);
|
|
|
|
});
|
|
return dPromise;
|
|
}
|
|
// TODO: Add the effects onto the cards passed through
|
|
// Takes cards array (of obj), and array of effects (from DB or built, probabaly pre-built?)
|
|
function addCardsEffects(){}
|
|
|
|
// End For testing
|
|
|
|
function onConnection(socket){
|
|
|
|
console.log('+ User connected');
|
|
console.log('');
|
|
|
|
socket.on('requestGetCards', function(deckId, playerId) {
|
|
requestGetCards(socket, deckId, playerId);
|
|
});
|
|
// New testing fella (working afaik)
|
|
// TODO: request specific deckId/playerId (and multiples, i.e. get 6 decks at same
|
|
// time, based on deckId/playerId combo. Maybe pass as array [deckId, playerId],[deck
|
|
socket.on('requestDeck', function() {
|
|
requestDeckStart(socket);
|
|
});
|
|
socket.on("exampleEvent", (data) => {
|
|
io.emit("exampleEvent", "hello from server");
|
|
});
|
|
|
|
socket.on('requestRooms', function(filter) {
|
|
requestRooms(socket, filter);
|
|
});
|
|
|
|
socket.on('requestJoinRoom', function(playerName, roomId) {
|
|
requestJoinRoom(socket, playerName, roomId);
|
|
});
|
|
|
|
socket.on('requestCreateRoom', function(playerName) {
|
|
requestCreateRoom(socket, playerName);
|
|
});
|
|
|
|
}
|
|
|
|
function requestRooms(socket, filter){
|
|
console.log('+ requestRooms recieved');
|
|
console.log('- filter: '+filter);
|
|
|
|
let response = getRooms(filter, dump = true);
|
|
io.to(socket.id).emit('returnRooms', response);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
function getRooms(filter = 'all', dump = false){
|
|
console.log('+ getRooms');
|
|
let response = {
|
|
random: 'randomStuff',
|
|
roomData: roomData,
|
|
};
|
|
|
|
if(dump){
|
|
console.log(response);
|
|
console.log('');
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
function requestCreateRoom(socket, playerName){
|
|
console.log('+ createRoom recieved');
|
|
console.log('- requested by: '+playerName);
|
|
|
|
response = createRoom(roomId = false, dump = true);
|
|
io.to(socket.id).emit('returnCreateRoom', response);
|
|
|
|
if(response.success){
|
|
let response = getRooms(filter = 'all', dump = true);
|
|
io.to(socket.id).emit('returnRooms', response);
|
|
}
|
|
|
|
console.log('');
|
|
}
|
|
|
|
function createRoom(roomId = false, dump = true){
|
|
let roomName = false;
|
|
if(roomId == false){
|
|
roomId = numRooms + 1;
|
|
}
|
|
console.log(roomId);
|
|
|
|
let response = {
|
|
success: false,
|
|
message: 'No idea bossman'
|
|
};
|
|
|
|
// Max room limit reached
|
|
console.log(numRooms);
|
|
console.log(maxRooms);
|
|
if(numRooms >= maxRooms){
|
|
console.log('- Room limit reached');
|
|
|
|
response = {
|
|
success: false,
|
|
message: 'No space '+numRooms+' out of '+maxRooms+' created.'
|
|
};
|
|
|
|
// Create room
|
|
}else{
|
|
console.log('- Creating room')
|
|
let room = {};
|
|
room['id'] = roomId;
|
|
room['name'] = 'Room:'+room['id'];
|
|
roomName = room['name'];
|
|
room['password'] = '';
|
|
room['timeout'] = {};
|
|
room['timeout']['s'] = 10;
|
|
room['people'] = 0;
|
|
|
|
//room['deck'] = [];
|
|
//room['turn'] = 0;
|
|
|
|
// Removed players for now, players may be seperate
|
|
// and back-end only with an assoc. to current room
|
|
|
|
//let players = {};
|
|
//for (let j = 0; j < maxPlayersPerRoom; j++) {
|
|
//let p = {};
|
|
//p['id'] = 0;
|
|
//p['name'] = "";
|
|
//p['hand'] = {};
|
|
//players[j] = p;
|
|
//}
|
|
|
|
//room['players'] = players;
|
|
roomData[roomId] = room;
|
|
numRooms = numRooms + 1;
|
|
|
|
response = {
|
|
success: true,
|
|
message: 'Room Created: '+roomName,
|
|
};
|
|
}
|
|
|
|
if(dump){
|
|
console.log(response);
|
|
console.log('');
|
|
}
|
|
return response;
|
|
}
|
|
|
|
// TODO: break into requestJoinRoom, and JoinRoom?
|
|
// Maybe not needed here? As won't be done via backend?
|
|
function requestJoinRoom(socket, playerName, roomId){
|
|
|
|
console.log('+ requestJoinRoom recieved');
|
|
socket.playerName = playerName;
|
|
|
|
for (let i = 1; i <= numRooms; i++) {
|
|
let name = 'Room_' + i;
|
|
let people = roomData[i]['people'];
|
|
|
|
console.log('- people: '+people);
|
|
console.log('- maxPlayersPerRoom: '+maxPlayersPerRoom);
|
|
|
|
if (true || people < maxPlayersPerRoom) {
|
|
|
|
people = roomData[i]['people'] += 1;
|
|
socket.join(name);
|
|
console.log('>> User ' + socket.playerName +
|
|
' connected on ' + name + ' (' + (people) + '/' + maxPlayersPerRoom + ')');
|
|
|
|
io.to(socket.id).emit('responseJoinRoom', name);
|
|
|
|
if (people >= maxPlayersPerRoom) {
|
|
console.log('- starting game');
|
|
//startGame(name);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
io.to(socket.id).emit('responseRoom', 'error');
|
|
console.log('>> Rooms exceeded');
|
|
|
|
}
|
|
|
|
// Change to Decks, or 'getPlayerDecks' for the game
|
|
// Could also be a specific deck if player clicks into one? Although maybe when entering
|
|
// decks they get all their decks (maybe just id actually) then load from there?
|
|
function requestGetCards(socket, deckId, playerId){
|
|
console.log(deckId);
|
|
console.log(playerId);
|
|
let response = {'success':false, 'message':'Nothing happened'};
|
|
response.success = true;
|
|
|
|
// Await promise, once it's done get the data, if errors send err
|
|
database.getCardsFromDeck(deckId, playerId).then(data => {
|
|
console.log(data);
|
|
response.message = data;
|
|
io.to(socket.id).emit('responseGetCards', response);
|
|
})
|
|
.catch(err => {
|
|
response.message = err;
|
|
io.to(socket.id).emit('responseGetCards', err);
|
|
})
|
|
}
|