From ceef95712e4def999a2ab7689e926c3db3714e2d Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 19 Oct 2024 13:37:24 +0100 Subject: [PATCH] Move card server logic test into cardGen.js module --- cardGen.js | 373 ++++++++++++++++++++++++++++++++++++++++++++++ public/board.js | 5 - server.js | 383 +----------------------------------------------- 3 files changed, 376 insertions(+), 385 deletions(-) create mode 100644 cardGen.js diff --git a/cardGen.js b/cardGen.js new file mode 100644 index 0000000..0b9b92d --- /dev/null +++ b/cardGen.js @@ -0,0 +1,373 @@ +// Need to require DB again. Apparently this is ok, as it'll +// still only have one require cross project (?) +const database = require('./database'); + + +// 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 deckIn = {}; // Which deck the item is in? Kinda the player/boardelement thing? + let cardData = {}; + let boardElement = {}; + // TODO: Set the player. For now will do this in front-end as testing currently + + // Loop and create the deck first + //console.log(decks); + decks.forEach((deck) => { + item.push(itemCount); // Add new item to add stuff for + deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName}; + boardElement[itemCount] = 'realDeck'; + itemCount++; + }) + console.log(deckData); + console.log(deckList); + + // 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) => { + + let deckItem = null; + // Loop each deck, if the deck/playerIds match, add association + for(key in deckData){ + //Object.keys(deckData).forEach(function(key) { // Less efficient than for + // Needs to check deck AND player id, as that's the primary key (together) + if(deckData[key].deckId == deckListItem.deckId && deckData[key].playerId == deckListItem.playerId){ + deckItem = key; // Key is the `item` key + } + + }; + // 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 + // Associate the card with the deck + // TODO: Change deckIn to something more sensical + deckIn[itemCount] = deckItem; + itemCount++; // Increment item to not overwrite + } + + }); + + // item, itemCount, deckData, cardData, boardElement + //console.log(cardData); + + // Returning everything to be looped in the front-end + // This won't be looped as it will at final, instead just for deck generation + // Returned as object over array, as easier to disect when gets to front-end + let dataReturn = { + item: item, + itemCount: itemCount, + deckData: deckData, + deckIn: deckIn, + cardData: cardData, + boardElement: boardElement, + }; + return resolve(dataReturn); + //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 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(){} + +module.exports = { + requestDeck +}; diff --git a/public/board.js b/public/board.js index 30dcc15..7749e29 100644 --- a/public/board.js +++ b/public/board.js @@ -842,8 +842,6 @@ function loadBoard(data) { // Decks can be as-is for getting deckLists for deckselection, but // TODO: for matches need something to generate and return every item, and attribute - console.log(cardData[0]); - // Temp solution // Loop all items, and set their related missing attributes // TODO: some more jank to get it 'playable' with DB entries @@ -1277,15 +1275,12 @@ function calculateItemSizePosition(itemKey){ positionY = 60; width = cardWidth*1.5; height = cardHeight*1.5; - console.log('HELLO 1'); - } if(itemPlayer == 0 && itemElement == 'realDeck'){ positionX = canvas.width-cardWidth*1.5-40; positionY = canvas.height-cardHeight*1.5-60; width = cardWidth*1.5; height = cardHeight*1.5; - console.log('HELLO 0'); } if(itemPlayer == 1 && itemElement == 'board'){ diff --git a/server.js b/server.js index cf3e629..1f8ff0a 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,7 @@ const express = require('express'); const database = require('./database'); +const cardGen = require('./cardGen'); const app = express(); const http = require('http').Server(app); const port = process.env.PORT || 3000; @@ -39,326 +40,8 @@ for (let roomId = 1; roomId <= numRoomsToPreGen; roomId++) { 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 deckIn = {}; // Which deck the item is in? Kinda the player/boardelement thing? - let cardData = {}; - let boardElement = {}; - // TODO: Set the player. For now will do this in front-end as testing currently - - // Loop and create the deck first - //console.log(decks); - decks.forEach((deck) => { - item.push(itemCount); // Add new item to add stuff for - deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName}; - boardElement[itemCount] = 'realDeck'; - itemCount++; - }) - console.log(deckData); - console.log(deckList); - - // 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) => { - - let deckItem = null; - // Loop each deck, if the deck/playerIds match, add association - for(key in deckData){ - //Object.keys(deckData).forEach(function(key) { // Less efficient than for - // Needs to check deck AND player id, as that's the primary key (together) - if(deckData[key].deckId == deckListItem.deckId && deckData[key].playerId == deckListItem.playerId){ - deckItem = key; // Key is the `item` key - } - - }; - // 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 - // Associate the card with the deck - // TODO: Change deckIn to something more sensical - deckIn[itemCount] = deckItem; - itemCount++; // Increment item to not overwrite - } - - }); - - // item, itemCount, deckData, cardData, boardElement - //console.log(cardData); - - // Returning everything to be looped in the front-end - // This won't be looped as it will at final, instead just for deck generation - // Returned as object over array, as easier to disect when gets to front-end - let dataReturn = { - item: item, - itemCount: itemCount, - deckData: deckData, - deckIn: deckIn, - cardData: cardData, - boardElement: boardElement, - }; - return resolve(dataReturn); - //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 -} // For testing -requestDeck(); +cardGen.requestDeck(); function requestDeckStart(socket){ // For future: @@ -369,7 +52,7 @@ function requestDeckStart(socket){ // 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 => { + cardGen.requestDeck().then(data => { response.success = true; response.message = data; io.to(socket.id).emit('responseGetDeck', response); @@ -380,66 +63,6 @@ function requestDeckStart(socket){ 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){