diff --git a/cardGen.js b/cardGen.js index 9adaf11..80ff642 100644 --- a/cardGen.js +++ b/cardGen.js @@ -4,22 +4,12 @@ const database = require('./database'); const util = require('util') -// 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? +// Get the decks requested function getDecks(deckIds = false){ // Await promise, once it's done get the data, if errors send err const dPromise = new Promise((resolve, reject) => { - database.dbGetDecks().then(data => { + database.dbGetDecks(deckIds).then(data => { let decks = []; @@ -48,10 +38,10 @@ function getDecks(deckIds = false){ return dPromise; } //getDecks(); -function getDeckList(){ +function getDeckList(deckIds = false){ const dPromise = new Promise((resolve, reject) => { - database.dbGetDeckList().then(data => { + database.dbGetDeckList(deckIds).then(data => { let deckList = []; @@ -70,11 +60,10 @@ function getDeckList(){ }); return dPromise; } -//getDeckList(); -function getCards(){ +function getCards(cardIds = false){ const dPromise = new Promise((resolve, reject) => { - database.dbGetCards().then(data => { + database.dbGetCards(cardIds).then(data => { let cards = []; @@ -97,10 +86,9 @@ function getCards(){ }); return dPromise; } -//getCards(); -function getCardClasses(){ +function getCardClasses(cardIds = false){ const dPromise = new Promise((resolve, reject) => { - database.dbGetCardClasses().then(data => { + database.dbGetCardClasses(cardIds).then(data => { let cardClasses = []; @@ -118,10 +106,9 @@ function getCardClasses(){ }); return dPromise; } -//getCardClasses(); -function getCardColourRequirement(){ +function getCardColourRequirement(cardIds = false){ const dPromise = new Promise((resolve, reject) => { - database.dbGetCardColourRequirement().then(data => { + database.dbGetCardColourRequirement(cardIds).then(data => { let colourRequirements = []; @@ -141,9 +128,9 @@ function getCardColourRequirement(){ }); return dPromise; } -function getCardManaColour(){ +function getCardManaColour(cardIds = false){ const cPromise = new Promise((resolve, reject) => { - database.dbGetCardManaColour().then(data => { + database.dbGetCardManaColour(cardIds).then(data => { let manaColours = []; @@ -274,25 +261,9 @@ function getCardPassive(){ return cPromise; } -//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 -// TODO: When this is functionally working, need to split up into other func/modules -// such as: playerMod, cardMod, deckMod, miscMod ? function requestDeck(itemData = null){ return new Promise((resolve, reject) => { (async () => { @@ -300,53 +271,73 @@ function requestDeck(itemData = null){ // 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 + + // Build array of decks to get + let deckIds = []; + for(let i = 0; i < itemData.players.length; i++){ + let deckStuff = itemData.players[i][1].deck; + deckIds.push([deckStuff.playerId, deckStuff.deckId]); + // So should be array of [playerId, deckId] which is primary key in DB + } + + // Get said decks, and their deckLists const [decks, deckList] = await Promise.all([ - getDecks(), - getDeckList() + getDecks(deckIds), + getDeckList(deckIds) ]); - //console.log(decks); - //console.log(deckList); + /* + console.log('--- decks ---'); + console.log(decks); + console.log('= deckLists =') + console.log(deckList); + console.log('------'); + */ + // 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, cardManaColours, cardPassives] = await Promise.all([ - getCards(), - getCardClasses(), - getCardColourRequirement(), - getCardManaColour(), - getCardPassive(), + getCards(deckCardIds), + getCardClasses(deckCardIds), + getCardColourRequirement(deckCardIds), + getCardManaColour(deckCardIds), + getCardPassive(deckCardIds), ]); - // ^^^^ 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 + // Return all effect data from DB + const [effects] = + await Promise.all([ + database.dbGetEffect(deckCardIds), // Get effects + ]); - // 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); + // Loop the effects for their effectIds to then get the steps/triggers from DB + let effectIds = []; + await effects.forEach((effect) => { + effectIds.push(effect.effectId); + }); - // Return all effect data from DB - const [effects, effectSteps, effectTriggers] = + // Then pass the effectIds to get their steps/triggers + const [effectSteps, effectTriggers] = await Promise.all([ - database.dbGetEffect(), - database.dbGetEffectStep(), - database.dbGetEffectTrigger(), + database.dbGetEffectStep(effectIds), + database.dbGetEffectTrigger(effectIds), ]); + /* + console.log('--- Effects ---'); + console.log(effects); + */ + // Build Effects const [cardEffects] = await Promise.all([ @@ -460,6 +451,8 @@ function requestDeck(itemData = null){ // For each new card, loop to the cardCount (how many cards in deck) // and add to the deck + // TODO: Associate player to deck differently (both could be using same deck + // via some kind of 'try a deck' or 'use friends deck' option) 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 diff --git a/components.js b/components.js new file mode 100644 index 0000000..c1617b7 --- /dev/null +++ b/components.js @@ -0,0 +1,99 @@ +// A seperate list for components, so they're easy to recall + +// Done as object, so it can be added to different rooms with components = NEW component? +const component = { + + // Entity Stuff + //item : [], + //itemCount : 0, + + roomId : null, + turn : 0, + playerTurn : 0, + + // Card Stuff + cardData : {}, + cardFace : {}, + cardStatus : { + tapped : {}, + attacking : {}, + inspected : {}, + }, + cardAttack : {}, + cardColours : {}, // Replace with colour + colour : { + white : {}, // entityId, amountOfColour + blue : {}, + red : {}, + }, + cardManaColour : {}, + cardEffect : {}, // TODO: Split this into effect, trigger, step, targets. + cardCost : {}, + cardSprite : {}, // id, position in spritesheet [0,4] e.g. + //cardPlayer = {}, + + // Deck Stuff? + deckIn : {}, + deckData : {}, + + // UI (so users know what's been targetted) + selected : {}, + selectable : {}, + + // Effect (break it up?) + effect : {}, + effectTrigger : {}, + // etc, etc. + + // Board Elements + // loop component.shield for shield items + boardElement : {}, + // Replace with following + realDeck : {}, + inDeck : {}, + hand : {}, + board : {}, + shield : {}, + mana : {}, + grave : {}, + void : {}, + + // + listPosition : {}, + // position (clientside) + // size (clientside) + + // Passives + // component.passive.flight ? + passive : { + flight : {}, + reach : {}, + taunt : {}, + }, + + type : { + unit : {}, + spell : {}, + token : {}, + }, + + classes : { + orc : {}, + human : {}, + spirit : {}, + }, + + +}; + +// For front-end +// position, size + +// These should be used as such (Not 100% as yet) +// For onBoard()... for player()... for passive.flight()... +// Check the board, items belonging to playerX, for flight passive? + +module.exports = { + component +} + diff --git a/database.js b/database.js index 8f89c93..ece68f0 100644 --- a/database.js +++ b/database.js @@ -17,7 +17,7 @@ function disconnect(){ } // My DB stuffs -function dbGetDecks(){ +function dbGetDecks(deckIds = false){ const dPromise = new Promise((resolve, reject) => { let cards = []; let sql = `SELECT @@ -25,8 +25,16 @@ function dbGetDecks(){ ,playerId ,deckName FROM deck - LIMIT 10 - `; // TODO: Remove limit when happy/this accepts params + `; + + // TODO: Jank, need to unjank it + if(deckIds){ + for(let i = 0; i < deckIds.length; i++){ + if(i == 0){ sql += ' WHERE '; } + else{ sql += ' OR '; } + sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')'; + } + } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -35,7 +43,7 @@ function dbGetDecks(){ }); return dPromise; } -function dbGetDeckList(){ +function dbGetDeckList(deckIds = false){ const dPromise = new Promise((resolve, reject) => { let cards = []; let sql = `SELECT @@ -46,6 +54,15 @@ function dbGetDeckList(){ FROM deck_cards `; + // TODO: Jank, need to unjank it + if(deckIds){ + for(let i = 0; i < deckIds.length; i++){ + if(i == 0){ sql += ' WHERE '; } + else{ sql += ' OR '; } + sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')'; + } + } + con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } resolve(result); @@ -53,7 +70,7 @@ function dbGetDeckList(){ }); return dPromise; } -function dbGetCards(){ +function dbGetCards(cardIds = false){ // Start with basic stuff in card table const dPromise = new Promise((resolve, reject) => { let sql = `SELECT @@ -65,6 +82,7 @@ function dbGetCards(){ ,cardRarity FROM card `; + if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -73,7 +91,7 @@ function dbGetCards(){ }); return dPromise; } -function dbGetCardClasses(){ +function dbGetCardClasses(cardIds = false){ // Get the classes assoc. on each card const dPromise = new Promise((resolve, reject) => { let sql = `SELECT @@ -81,6 +99,7 @@ function dbGetCardClasses(){ ,classId FROM card_class `; + if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -89,7 +108,7 @@ function dbGetCardClasses(){ }); return dPromise; } -function dbGetCardColourRequirement(){ +function dbGetCardColourRequirement(cardIds = false){ // Get the classes assoc. on each card const dPromise = new Promise((resolve, reject) => { let sql = `SELECT @@ -98,6 +117,7 @@ function dbGetCardColourRequirement(){ ,cost FROM card_colour_requirement `; + if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -107,7 +127,7 @@ function dbGetCardColourRequirement(){ return dPromise; } -function dbGetCardManaColour(){ +function dbGetCardManaColour(cardIds = false){ // Get the classes assoc. on each card const cPromise = new Promise((resolve, reject) => { let sql = `SELECT @@ -115,6 +135,7 @@ function dbGetCardManaColour(){ ,colourId FROM card_mana_colour `; + if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -125,7 +146,7 @@ function dbGetCardManaColour(){ } // Effect stuff -function dbGetEffect(){ +function dbGetEffect(cardIds = false){ const ePromise = new Promise((resolve, reject) => { let sql = `SELECT cardId @@ -136,6 +157,7 @@ function dbGetEffect(){ INNER JOIN effect ON effect.id = card_effect.effectId `; + if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -144,7 +166,7 @@ function dbGetEffect(){ }); return ePromise; } -function dbGetEffectStep(){ +function dbGetEffectStep(effectIds = false){ const ePromise = new Promise((resolve, reject) => { let sql = `SELECT effectId, @@ -163,6 +185,7 @@ function dbGetEffectStep(){ effect_step_target ON effect_step_target.effectStep = effect_step.id `; + if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -172,7 +195,7 @@ function dbGetEffectStep(){ return ePromise; } // Effect Trigger stuff -function dbGetEffectTrigger(){ +function dbGetEffectTrigger(effectIds = false){ const ePromise = new Promise((resolve, reject) => { let sql = `SELECT effect_trigger.id AS triggerId, @@ -196,6 +219,7 @@ function dbGetEffectTrigger(){ effect_trigger_target ON effect_trigger_target.effectTriggerId = effect_trigger.triggerTypeId `; + if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } @@ -206,13 +230,14 @@ function dbGetEffectTrigger(){ } // Passive stuff -function dbGetPassive(){ +function dbGetPassive(cardIds = false){ const pPromise = new Promise((resolve, reject) => { let sql = `SELECT cardId ,passiveId FROM card_passive `; + if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; } con.query(sql, function (err, result, fields) { if (err) { throw err; reject(new Error(err)); } diff --git a/gameMod.js b/gameMod.js new file mode 100644 index 0000000..6d94736 --- /dev/null +++ b/gameMod.js @@ -0,0 +1,25 @@ +// For anything related to the actual game itself (kinda) +// this will be split into different bits, but should be what manages a rooms +// game states, and alladat +// Basically here to prevent circular dependencies (where I can) + + +// PlayerId is using array 0,1,2 for now, not the actual id +// actual Id would be better, but the player should be passed correctly +// from the client. They can edit data, but server-side validation SHOULD prevent +// in the future +function passTurn(roomId, playerId){ + + // Check playerId (not correct atm) before doing the stuff, to verify the user + // IS the user, and in the room + + // Test alert so that different data/emits can be sent per-player + global.socketAlert(roomData[roomId].playerData[0].socketId, 'Pass', 'alert'); + +} + + +module.exports = { + passTurn +}; + diff --git a/public/board.js b/public/board.js index cd51410..5286186 100644 --- a/public/board.js +++ b/public/board.js @@ -42,6 +42,7 @@ let cardColours = {}; let cardManaColour = {}; let cardEffect = {}; let inEvent = null; +let roomId = null; // To disable drawing each time something changes let drawEachEvent = true; // For disabling draw each time and only occuring where I want to test @@ -1298,6 +1299,7 @@ function loadBoard(data) { cardColours = data.cardColours; cardManaColour = data.cardManaColour; cardEffect = data.cardEffect; + roomId = data.roomId; // Passives flight = data.flight; diff --git a/public/index.html b/public/index.html index 83a167a..c90e939 100644 --- a/public/index.html +++ b/public/index.html @@ -17,6 +17,10 @@ +
+ +

+
diff --git a/public/main.js b/public/main.js index dbb0606..7ebcf74 100644 --- a/public/main.js +++ b/public/main.js @@ -182,3 +182,19 @@ socket.on('responseStartGame', function (data) { loadBoard(data.message); }); +// ALERTS +socket.on('alert', function (data) { + console.log('<< alert'); + alert(data.message); +}); + +// GAME SERVER STUFF +// PASS TURN +function passTurn(){ + // TODO: Use itemData.player/itemData.room or whatnot here + console.log(roomId); + let playerId = prompt('PlayerId 0/1', 0); // TODO: very temp, will be playerId/arrayId + console.log('+ passTurn'); + socket.emit('passTurn', roomId, playerId); +} + diff --git a/roomMod.js b/roomMod.js index 8223345..be7b19f 100644 --- a/roomMod.js +++ b/roomMod.js @@ -1,41 +1,34 @@ // Build a room, fill will players, etc. const cardGen = require('./cardGen'); - +const components = require('./components'); // Room should, setPlayers, add them to correct team (TODO), build their decks, and first shuffle function startItemCount(){ + let item = []; let itemCount = 0; returns = {'item': item, 'itemCount': itemCount}; return(returns); } -function setPlayers(playerCount = 2, itemData){ - // Add new item attribute for 'players' - // TODO: Maybe check if exists, and add to in that case (for replacing people?) - // Doubt that would ever make it into the game, but could still be worth + +function setPlayers(playerData, itemData){ + itemData.player = {}; //let player = {}; // Player item belongs to itemData.players = []; // List of the players (an associated playerItem?) - //itemData. - - // Can be done with just referring to itemData[x], but less readable - // Will likely redefine vars each new function. For now will keep this as-is let playerNo = 0 + itemData['itemCount']; // Start loop from current itemCount - playerCount = playerCount + itemData['itemCount']; // End at playerCount diff from itemCount + playerCount = playerData.length + itemData['itemCount']; // End at playerCount diff from itemCount + let i = 0; for(playerNo; playerNo < playerCount; playerNo++){ - // REMOVED PLAYERS AS ITEM, BECAUSE THEY WERE BORKING - // TODO: Add back at some point, or don't bother - //itemData['item'].push(playerNo); - //itemData['player'][itemData['itemCount']] = playerNo; // The player belongs to itself - //itemData['itemCount']++; - itemData['players'].push(playerNo); // Add player no to array so can be looped + + itemData['players'].push([playerNo, playerData[i]]); // Add player no to array so can be looped + i++; + } - // Return related item and item attributes - //returns = {'item': item, 'itemCount': itemCount, 'player': player}; + return itemData; - //return([item, itemCount, player]); } // For future, when 2v2s, and 5v1 Raids, etc. @@ -43,26 +36,72 @@ function setTeams(){ } -function roomGeneration(playerCount, teamCount = null, playerTeams = null){ +function roomGeneration(roomId){ return new Promise((resolve, reject) => { (async () => { + + // Player's sockets + console.log('--- Room for generation ---'); + console.log(io.sockets.adapter.rooms.get(roomId)); + let itemData = startItemCount(); - // Create 2 players for the room - itemData = setPlayers(2, itemData); + // Player data with Sockets + let playerData = []; + let playerOrder = {}; // Need a better name + let i = 1; + let clients = global.io.sockets.adapter.rooms.get(roomId); + for (const clientId of clients) { + const clientSocket = global.io.sockets.sockets.get(clientId); - // TODO: Get their selected decks (will need to pass somewhere) + // Which order the player is? Just using the array + playerOrder[clientSocket.playerId] = playerData.length; + + playerData.push({ + 'playerDataId': playerData.length + ,'playerId': clientSocket.playerId + ,'deck':{'playerId':i,'deckId':1} + ,'socketId': clientSocket.id // TODO: ONLY FOR SERVERSIDE!!! + }); + + i++; + + } + + + // Add players for the room (seperate to playerData, currently what's used) + // ?? + itemData = setPlayers(playerData, itemData); + + // TODO: Get their selected decks + + // Add all the empty components to the room itemData + itemData.component = components.component; // Generate the decks, and card within the deck cardLists [itemData] = await Promise.all([ cardGen.requestDeck(itemData) ]); - //console.log('deckData'); - //console.log(deckData); + // Some room stuff, likely change this + itemData.roomId = roomId; + itemData.turn = 0; // The turn count of the match + itemData.playersTurn = 0; // This means it's playerData[0] turn + + // Room has just been created, so add the itemData and playerData to the room + // on server-side so it's easily accessible + roomData[roomId].itemData = itemData; + roomData[roomId].playerData = playerData; + roomData[roomId].playerOrder = playerOrder; + + // Return the all the itemData to the client(s) + // TODO: This will need to give different data for each, or at least + // to differ the boardside return resolve(itemData); })() }); } +// TODO: disconnect, reconnect, resume + module.exports = { startItemCount ,setPlayers diff --git a/rooms.js b/rooms.js new file mode 100644 index 0000000..7f3bfa5 --- /dev/null +++ b/rooms.js @@ -0,0 +1,229 @@ +const roomMod = require('./roomMod'); + +// Variables for server overall +let numRooms = 0; +const maxRooms = 3; +const maxPlayersPerRoom = 2; +const maxSpectatorsPerRoom = 0; + +function requestRooms(socket, filter){ + console.log('+ requestRooms recieved'); + console.log('- filter: '+filter); + + let response = getRooms(filter, dump = true); + global.io.to(socket.id).emit('returnRooms', response); + + console.log(''); +} + +function getRooms(filter = 'all', dump = false){ + console.log('+ getRooms'); + let response = { + random: 'randomStuff', + roomData: global.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); + global.io.to(socket.id).emit('returnCreateRoom', response); + + if(response.success){ + let response = getRooms(filter = 'all', dump = true); + global.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['playerIds'] = {}; + + global.roomData[roomId] = room; + numRooms = numRooms + 1; + + response = { + success: true, + message: 'Room Created: '+roomName, + }; + } + + if(dump){ + console.log(response); + console.log(''); + } + return response; +} + +function requestJoinRoom(socket, playerName, roomId){ + + console.log('+ requestJoinRoom recieved'); + + let room = global.roomData[roomId]; + + // https://stackoverflow.com/a/18096649 + socket.playerId = playerName; + + if(room === undefined){ + console.log('>> Room does not exist'); + return 'error'; + } + + let roomName = 'Room_' + roomId; + let people = room['people']; + + if(isUserInRoom(playerName, roomId)){ + console.log('>> Already in room'); + return 'already in room'; + } + + if (people < maxPlayersPerRoom) { + + // Update people in room count + people = room['people'] += 1; + + // Add playerId to room (playerName for now while Ids don't exist TODO) + room['playerIds'][playerName] = playerName; + + // https://socket.io/docs/v4/rooms/ + // https://stackoverflow.com/a/25028953 + socket.join(roomId); + + console.log('>> User ' + playerName + + ' connected on ' + roomName + ' (' + (people) + '/' + maxPlayersPerRoom + ')'); + + // Joined room (emit to the player that just joined) + global.io.to(socket.id).emit('responseRoom', response); + + if (people >= maxPlayersPerRoom) { + console.log('- starting game'); + // startGame for room + startGame(roomId); + } + + } + +} + +// Will need to be different to playerName in future (in case dupes) +// would use playerId TODO +function isUserInRoom(playerName, roomId){ + if(playerName in global.roomData[roomId]['playerIds']){ + return true; + } + return false; +} + +async function startGame(roomId){ + + console.log('>> Room: ' + roomId + ': Requesting game...'); + let people = global.roomData[roomId].players; + /* + try { + //people = io.sockets.adapter.rooms.get(roomId).size; + } catch (e) { + console.log('>> Room: ' + roomId + ': No people here...'); + return; + } + */ + + // For now, if there's 2 people only. Will need changing for + // 3v1, 5v1, 2v2, etc... + + let response = {success: false, message: 'Failed requestStartGame() server.js'}; + if(people < maxPlayersPerRoom){ + console.log('Too few people'); + } + + console.log('>> Room: ' + roomId + ': Starting'); + + + // https://stackoverflow.com/a/25028953 + //console.log(util.inspect(io.sockets.adapter.rooms.get(roomId), true, 4, true)) + let clients = global.io.sockets.adapter.rooms.get(roomId); + + // This should return the deck data, etc. for each client + // ideally only returning the items that the user can/should + // see i.e. shouldn't give them the inDeck card list just a counter + // shouldn't have opponent card data/their hand shouldn't be flipped + + // Not sure how to catch errors for these await alls + // TODO: Look into error handling for await alls + const [itemData] = + await Promise.all([ + roomMod.roomGeneration(roomId), + ]); + + + // data is the 'itemData' not all the roomData + response.success = true; + response.message = itemData; + + // Each player then gets sent the roomGeneration stuff + for (const clientId of clients) { + + const clientSocket = global.io.sockets.sockets.get(clientId); + console.log('>> responseStartGame: '+clientSocket.playerId); + + // TODO: TESTING STUFF, REMOVE WHEN SORTED + let message = 'You are player: '+roomData[roomId].playerOrder[clientSocket.playerId]; + global.socketAlert(clientSocket.id, message, 'alert'); + + // Emit the itemData to client socket (TODO: only emit what each player should see/recieve) + global.io.to(clientSocket.id).emit('responseStartGame', response); + + } + + +} + +// TODO: Need a 'leave room'/disconnect + +module.exports = { + requestRooms + ,requestJoinRoom + ,requestCreateRoom +}; diff --git a/server.js b/server.js index c29979d..255de91 100644 --- a/server.js +++ b/server.js @@ -1,13 +1,21 @@ - const express = require('express'); + const database = require('./database'); -const cardGen = require('./cardGen'); -const roomMod = require('./roomMod'); +const rooms = require('./rooms'); +const gameMod = require('./gameMod'); + const app = express(); const http = require('http').Server(app); const port = process.env.PORT || 3000; - const io = require('socket.io')(http); +global.io = io; + +// To log the player sockets, so they can be easily referred to +// maybe jank, but can't see alternative currently +global.playerSocket = {}; +global.roomData = {}; // Made global for now, as to not replicate. Maybe sub-optimal? + + // 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)) @@ -21,309 +29,38 @@ 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); -} - function onConnection(socket){ console.log('+ User connected'); console.log(''); + // Rooms (joining, creating, etc) socket.on('requestRooms', function(filter) { - requestRooms(socket, filter); + rooms.requestRooms(socket, filter); }); socket.on('requestJoinRoom', function(playerName, roomId) { - requestJoinRoom(socket, playerName, roomId); + rooms.requestJoinRoom(socket, playerName, roomId); }); socket.on('requestCreateRoom', function(playerName) { - requestCreateRoom(socket, playerName); + rooms.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['playerIds'] = {}; - - //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? -// TODO: Need a 'leave room'/disconnect -function requestJoinRoom(socket, playerName, roomId){ - - console.log('+ requestJoinRoom recieved'); - - let room = roomData[roomId]; - - // Add socket for playerName so that players in room get - // responses, etc. from the room (something like that) - - // Add player to socket object, so it can be referred to - // as each socket is individual to a player, but rooms need to - // emit to multiple players. These .player can be used in loops - - // https://socket.io/docs/v4/rooms/ - // https://stackoverflow.com/a/18096649 - socket.playerId = playerName; - - if(room === undefined){ - io.to(socket.id).emit('responseRoom', 'error'); - console.log('>> Room does not exist'); - } - - let roomName = 'Room_' + roomId; - let people = room['people']; - // people = io.sockets.adapter.rooms[roomId].length; // This gets the sockets in the room (i.e. the players) - - //console.log(util.inspect(io.sockets.adapter.rooms, true, 4, true)) - //console.log('- people(socket: '+io.sockets.adapter.rooms[player]+')'); - //console.log('- maxPlayersPerRoom: '+maxPlayersPerRoom); - - if(isUserInRoom(playerName, roomId)){ - console.log('Already in room'); - return false; - } - - if (people < maxPlayersPerRoom) { - - // Update people in room count - people = room['people'] += 1; - - // Add playerId to room (playerName for now while Ids don't exist TODO) - room['playerIds'][playerName] = playerName; - - // https://socket.io/docs/v4/rooms/ - // https://stackoverflow.com/a/25028953 - socket.join(roomId); - - // https://stackoverflow.com/a/25028953 - //console.log(util.inspect(io.sockets.adapter.rooms.get(roomId), true, 4, true)) - /* - let clients = io.sockets.adapter.rooms.get(roomId); - let numClients = clients ? clients.size : 0; - for (const clientId of clients) { - //this is the socket of each client in the room. - const clientSocket = io.sockets.sockets.get(clientId); - console.log(clientSocket.playerId); // The playerId set beggining of func. - } - */ - - console.log('>> User ' + playerName + - ' connected on ' + roomName + ' (' + (people) + '/' + maxPlayersPerRoom + ')'); - - // Joined room (emit to the player that just joined) - io.to(socket.id).emit('responseJoinRoom', roomName); - - if (people >= maxPlayersPerRoom) { - console.log('- starting game'); - // startGame for room - startGame(roomId); - } - - } - -} - -// Will need to be different to playerName in future (in case dupes) -// would use playerId TODO -function isUserInRoom(playerName, roomId){ - if(playerName in roomData[roomId]['playerIds']){ - return true; - } - return false; -} - -function startGame(roomId){ - - console.log('>> Room: ' + roomId + ': Requesting game...'); - let people = roomData[roomId].players; - /* - try { - //people = io.sockets.adapter.rooms.get(roomId).size; - } catch (e) { - console.log('>> Room: ' + roomId + ': No people here...'); - return; - } - */ - - // For now, if there's 2 people only. Will need changing for - // 3v1, 5v1, 2v2, etc... - - let response = {success: false, message: 'Failed requestStartGame() server.js'}; - if(people < maxPlayersPerRoom){ - console.log('Too few people'); - } - - console.log('>> Room: ' + roomId + ': Starting'); - - - // https://stackoverflow.com/a/25028953 - //console.log(util.inspect(io.sockets.adapter.rooms.get(roomId), true, 4, true)) - let clients = io.sockets.adapter.rooms.get(roomId); - - /* - for (const clientId of clients) { - //this is the socket of each client in the room. - const clientSocket = io.sockets.sockets.get(clientId); - console.log(clientSocket.playerId); // The playerId set in requestJoin - - // Just so I know how to access stuff in future. - console.log(roomData[roomId].playerIds[clientSocket.playerId] + 'is in the game'); - } - */ - - // This should return the deck data, etc. for each client - // ideally only returning the items that the user can/should - // see i.e. shouldn't give them the inDeck card list just a counter - // shouldn't have opponent card data/their hand shouldn't be flipped - roomMod.roomGeneration().then(data => { - response.success = true; - response.message = data; - // Each player then gets the roomGeneration stuff - for (const clientId of clients) { - const clientSocket = io.sockets.sockets.get(clientId); - console.log('>> responseStartGame: '+clientSocket.playerId); - // Emit to client socket - io.to(clientSocket.id).emit('responseStartGame', response); - } - - }) - .catch(err => { - response.message = err; - // Each player then gets the error message - for (const clientId of clients) { - const clientSocket = io.sockets.sockets.get(clientId); - // Emit to client socket - io.to(clientSocket.id).emit('responseStartGame', err); - } + // Game (actual things relating to the game) + // The socket should only be in one game, so socket.on should + // do this, but passing room/player anyways as it's how I've written some + // roomData bits. TODO: Look if I can do this better... + socket.on('passTurn', function(roomId, playerId) { + gameMod.passTurn(roomId, playerId); }); } -// Then do functions like this? -function shuffleDeck(roomId, playerId){ - +// Globals for easier clientside alerts/logs, etc. +global.socketAlert = function(socket, message, type = 'alert'){ + global.io.to(socket).emit( + type, {'message': message} + ); }