Merge branch 'feature/database' into develop

develop
Nathan Steel 1 year ago
commit 57b0194f9b

@ -13,6 +13,8 @@
`git pull <this-repo>`
`npm install express --save`
socketio, mysql
## Run
`node server.js`
@ -21,3 +23,17 @@ Access through
`http://localhost:3000`
### Development
BACKEND/SERVER DEVELOPMENT ONLY
To prevent the exhausting task of restarting the server on change
install and run with nodemon, it'll handle server restart each time
a js file is saved (this does also restart for front-end)
`npm install -g nodemon`
Run with
`nodemon --inspect server.js`

@ -0,0 +1,277 @@
const mysql = require('mysql');
const con = mysql.createConnection({
host: "localhost",
user: "realmsdivided",
password: "realmsdivided",
database: "realms_divided"
});
function connect(){
con.connect(function(err) {
if (err) { console.log(err); }
else { console.log('DB Connected'); }
});
}
function disconnect(){
con.end();
}
// Is calling a promise in a promise the best way to do this?
// Keeping because this is how I've figured to call other funcs from database
function getCardById(cardId){
const dPromise = new Promise((resolve, reject) => {
getCards(' card.id = '+cardId).then(cards => {
resolve(cards);
})
.catch(err => {
throw err; reject(new Error(err));
});
});
return dPromise;
}
function getCards(whereClause){
const cPromise = new Promise((resolve, reject) => {
// Get main card info
let cards = [];
let sql = `SELECT
card.id AS cardId -- TEMP UNTIL UID
,cardName
,cardCost
,typeName
,cardAttack
,rarityName
FROM card
LEFT JOIN type ON type.id = card.id
LEFT JOIN rarity ON rarity.id = card.cardRarity
`;
// TODO: Rewrite this so it's not so insecure!!!!
if(whereClause){ sql = sql + " WHERE "+whereClause; }
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
cards = dbResultToCards(result);
//console.log(cards);
resolve(cards);
});
// Now get the
// class
// colour requirements
// card effects
});
return cPromise;
}
// May want a FromDecks to get the SQL done in one blast
// then filter the results according?
function getCardsFromDeck(playerId, deckId){
const cPromise = new Promise((resolve, reject) => {
if(playerId == null || deckId == null){ reject(new Error('Player/Deck Id not provided')); }
let cards = [];
// TODO: Change rarity, and maybe cardName to be the id (prolly card name for fancy effects)
// Then have a SQL loop at start of game that adds [name, id], [rariry, id] etc.
let sql = `SELECT
cardName
,cardCost
,typeName
,cardAttack
,rarityName
,cardCount
FROM deck
INNER JOIN deck_cards ON deck_cards.deckId = deck.deckId AND deck_cards.playerId = deck.playerId
INNER JOIN card ON card.id = deck_cards.cardId
LEFT JOIN type ON type.id = card.id
LEFT JOIN rarity ON rarity.id = card.cardRarity`;
// TODO: Make more secure!!
sql += ' WHERE deck.deckId ='+deckId+' AND deck.playerId = '+playerId;
sql += ';';
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
cards = dbResultToCards(result);
resolve(cards);
});
// Card list has been built
// Resolve/return the data of the cards Array/deck
});
return cPromise;
}
// This may be more jank than I'd like. Was going procedure route, or JOINs
// but thinking of future effects that could have 4-5 effects, that check 2-3 colours
// and X amounts of cards/classes, to finally trigger something, those feel like they
// wouldn't work. So this is it for now, and when I think of something better I'll rewrite
function dbGetCardClasses(cardId){
// Maybe this isn't too bad as async?
// But I imagine for lets say 100 1v1s 200 loops of 35cards each, eekers
const cPromise = new Promise((resolve, reject) => {
if(cardId == null){ reject(new Error('cardId not provided')); }
let classes = [];
// Just getting the class IDs, as intend to load all the class data [id, name] into
// an array or file on game load. This way just the ID is needed, and no text compares
let sql = `SELECT classId
FROM card_class
WHERE cardId = `+cardId
; // TODO: As all these SQL, need to make more secure
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
result.forEach((card) => {
// Add the classId to array to be used in card buildery doodad
classes.push(result[0]);
});
});
// Card list has been built
// Resolve/return the data of the cards Array/deck
resolve(classes);
});
return cPromise;
}
// Don't really want this in DB layer, but it wouldn't play
// in the main server file, so it's here until I figure out why
function dbResultToCards(result){
let cards = [];
result.forEach((card) => {
let tempCard = {};
tempCard.id = card.cardId;
tempCard.name = card.cardName;
tempCard.colour = null;
tempCard.cost = card.cardCost;
tempCard.type = card.typeName;
tempCard.atk = card.cardAttack;
tempCard.rarity = card.rarityName;
tempCard.effect = null;
// TODO: Will need more SQL statements, or some function/procedure
// class
// colour requirements
// card effects
// Add the 'completed' cards into the deck
if(card.cardCount){
// Add as many cards into the deck as is in cardCount
for(let i = 0; i < card.cardCount; i++){
cards.push(tempCard);
}
}else{
// Or just one
cards.push(tempCard);
}
});
return cards;
}
// Testing, and trialing rewrites
function dbGetDecks(){
const dPromise = new Promise((resolve, reject) => {
let cards = [];
let sql = `SELECT
deckId
,playerId
,deckName
FROM deck
LIMIT 10
`; // TODO: Remove limit when happy/this accepts params
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return dPromise;
}
function dbGetDeckList(){
const dPromise = new Promise((resolve, reject) => {
let cards = [];
let sql = `SELECT
deckId
,playerId
,cardId
,cardCount
FROM deck_cards
`;
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return dPromise;
}
function dbGetCards(){
// Start with basic stuff in card table
const dPromise = new Promise((resolve, reject) => {
let sql = `SELECT
id
,cardName
,cardCost
,cardType
,cardAttack
,cardRarity
FROM card
`;
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return dPromise;
}
function dbGetCardClasses(){
// Get the classes assoc. on each card
const dPromise = new Promise((resolve, reject) => {
let sql = `SELECT
cardId
,classId
FROM card_class
`;
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return dPromise;
}
function dbGetCardColourRequirement(){
// Get the classes assoc. on each card
const dPromise = new Promise((resolve, reject) => {
let sql = `SELECT
cardId
,colourId
,cost
FROM card_colour_requirement
`;
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return dPromise;
}
module.exports = {
connect, disconnect
, getCards
, getCardById
, getCardsFromDeck
// Testing, and trailing
, dbGetDecks
, dbGetDeckList
, dbGetCards
, dbGetCardClasses
, dbGetCardColourRequirement
};

@ -0,0 +1,206 @@
-- --------------------------------------------------------
-- Host: 127.0.0.1
-- Server version: 10.7.8-MariaDB-1:10.7.8+maria~ubu2004 - mariadb.org binary distribution
-- Server OS: debian-linux-gnu
-- HeidiSQL Version: 12.6.0.6765
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Dumping database structure for realms_divided
CREATE DATABASE IF NOT EXISTS `realms_divided` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
USE `realms_divided`;
-- Dumping structure for table realms_divided.card
CREATE TABLE IF NOT EXISTS `card` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cardName` varchar(255) NOT NULL,
`cardCost` tinyint(4) NOT NULL DEFAULT 2,
`cardType` tinyint(4) DEFAULT NULL,
`cardAttack` int(11) DEFAULT NULL,
`cardRarity` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.card: ~5 rows (approximately)
INSERT INTO `card` (`id`, `cardName`, `cardCost`, `cardType`, `cardAttack`, `cardRarity`) VALUES
(1, 'Red1', 1, 1, 500, 1),
(2, 'White1', 1, 1, 1000, 1),
(3, 'Blue1', 2, 1, 1000, 1),
(4, 'White 8', 3, 2, NULL, 2),
(5, 'Red7', 2, 2, NULL, 2);
-- Dumping structure for table realms_divided.card_class
CREATE TABLE IF NOT EXISTS `card_class` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cardId` int(11) DEFAULT NULL,
`classId` smallint(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.card_class: ~4 rows (approximately)
INSERT INTO `card_class` (`id`, `cardId`, `classId`) VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1),
(4, 1, 2);
-- Dumping structure for table realms_divided.card_colour_requirement
CREATE TABLE IF NOT EXISTS `card_colour_requirement` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`cardId` int(11) DEFAULT NULL,
`colourId` tinyint(4) DEFAULT NULL,
`cost` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.card_colour_requirement: ~5 rows (approximately)
INSERT INTO `card_colour_requirement` (`id`, `cardId`, `colourId`, `cost`) VALUES
(1, 1, 3, 1),
(2, 2, 1, 1),
(3, 3, 2, 1),
(4, 4, 1, 2),
(5, 5, 3, 2);
-- Dumping structure for table realms_divided.card_effect
CREATE TABLE IF NOT EXISTS `card_effect` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cardId` int(11) DEFAULT NULL,
`effectId` smallint(6) DEFAULT NULL,
`value` smallint(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.card_effect: ~5 rows (approximately)
INSERT INTO `card_effect` (`id`, `cardId`, `effectId`, `value`) VALUES
(1, 1, 2, NULL),
(2, 3, 1, NULL),
(3, 2, 3, NULL),
(4, 4, 4, 1000),
(5, 5, 5, 3000);
-- Dumping structure for table realms_divided.class
CREATE TABLE IF NOT EXISTS `class` (
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`parentId` tinyint(4) DEFAULT NULL,
`className` tinytext NOT NULL,
`primaryColour` smallint(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.class: ~10 rows (approximately)
INSERT INTO `class` (`id`, `parentId`, `className`, `primaryColour`) VALUES
(1, NULL, 'Goblin', 3),
(2, NULL, 'Human', 1),
(3, NULL, 'Spirit', 2),
(4, NULL, 'Warrior', 1),
(5, NULL, 'Orc', 3),
(6, NULL, 'Beast', 5),
(7, NULL, 'Plant', 5),
(8, NULL, 'Undead', 4),
(9, NULL, 'Mechanical', 6),
(10, NULL, 'Void', 7);
-- Dumping structure for table realms_divided.colour
CREATE TABLE IF NOT EXISTS `colour` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`colourName` tinytext DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.colour: ~7 rows (approximately)
INSERT INTO `colour` (`id`, `colourName`) VALUES
(1, 'White'),
(2, 'Blue'),
(3, 'Red'),
(4, 'Black'),
(5, 'Green'),
(6, 'Brown'),
(7, 'Void');
-- Dumping structure for table realms_divided.deck
CREATE TABLE IF NOT EXISTS `deck` (
`deckId` int(11) NOT NULL,
`playerId` int(11) NOT NULL,
`deckName` tinytext NOT NULL,
PRIMARY KEY (`deckId`,`playerId`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.deck: ~2 rows (approximately)
INSERT INTO `deck` (`deckId`, `playerId`, `deckName`) VALUES
(1, 1, 'Deck 1 P1'),
(1, 2, 'Deck 1 P2');
-- Dumping structure for table realms_divided.deck_cards
CREATE TABLE IF NOT EXISTS `deck_cards` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deckId` int(11) DEFAULT NULL,
`playerId` int(11) DEFAULT NULL,
`cardId` int(11) DEFAULT NULL,
`cardCount` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.deck_cards: ~5 rows (approximately)
INSERT INTO `deck_cards` (`id`, `deckId`, `playerId`, `cardId`, `cardCount`) VALUES
(1, 1, 1, 1, 25),
(2, 1, 1, 5, 10),
(3, 1, 2, 2, 15),
(4, 1, 2, 3, 14),
(5, 1, 2, 4, 6);
-- Dumping structure for table realms_divided.effect
CREATE TABLE IF NOT EXISTS `effect` (
`id` smallint(6) NOT NULL AUTO_INCREMENT,
`effectName` tinytext DEFAULT NULL,
`effectDescription` varchar(250) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.effect: ~5 rows (approximately)
INSERT INTO `effect` (`id`, `effectName`, `effectDescription`) VALUES
(1, 'Flight', 'Can ignore Taunt, cannot be tagetted by attack'),
(2, 'Reach', 'Can attack Flight units'),
(3, 'Equip', 'Add this cards attack, and effect(s) to another'),
(4, 'Heal', 'Untap X shield(s)'),
(5, 'Hurt', 'Deal X damage to target unit, this combat');
-- Dumping structure for table realms_divided.rarity
CREATE TABLE IF NOT EXISTS `rarity` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`rarityName` tinytext DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.rarity: ~2 rows (approximately)
INSERT INTO `rarity` (`id`, `rarityName`) VALUES
(1, 'Common'),
(2, 'Uncommon'),
(3, 'Rare');
-- Dumping structure for table realms_divided.type
CREATE TABLE IF NOT EXISTS `type` (
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
`typeName` tinytext DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Dumping data for table realms_divided.type: ~2 rows (approximately)
INSERT INTO `type` (`id`, `typeName`) VALUES
(1, 'Unit'),
(2, 'Spell'),
(3, 'Token');
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

@ -15,6 +15,7 @@
"homepage": "anetwork.uk",
"dependencies": {
"express": "^4.21.0",
"mysql": "^2.18.1",
"socket.io": "^4.8.0"
}
}

@ -802,21 +802,127 @@ class Board{
// Run board commands here for testing
let board = new Board;
// Fill each players deck with their cards
createDecks();
// Board is now async (initialisation)
// Everything beyond initialisation shouldn't be required so no need to wait
// At least I think, for now. May need more stuff in future
// Request the deck(s) from server/db
requestDeck();
// This will trigger an emit to and on from the server will trigger loadBoard();
// public/main.js
// socket.on('responseGetDeck', function (data) {
function loadBoard(data) {
console.log('DATA');
console.log(data);
// Built the original boardstate using the data passed from server
// This is only for the first time recieve (TODO: ALL) game data at match start
// Then ideally only what's needed to be passed to/from client/server sent after
item = data.item;
itemCount = data.itemCount;
boardElement = data.boardElement;
cardData = data.cardData;
// position; // Not to do, as calculated per client (and will be per screen-size)
// size; // Like position, calculated per client.
//cardStatus; // TODO: ? Statuses on load?, TODO: OR if player DCs and needs to reload!
player = {}; // TODO: Set each item to correct player
listPosition = {}; // TODO:
cardFace = {}; // TODO: Like status, for DCs and reloads
cardSprite = {}; // TODO: ? Maybe, or this could be done clientside based
// ^ on card id?
cardFace = {}; // TODO: For DB/Reconnect more-so
let deckIn = data.deckIn; // NEW, may be used, for now player substitute
let deckData = data.deckData;
// Stuff not currently in the getDecks TODO: To be stripped down and
// rewritten/made into generateItems or something, for each match
// 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
let player0DeckCount = 0;
let player1DeckCount = 0;
for(let i = 0; i < itemCount; i++){
// Set player for decks (to just be their array index for now) TODO: Properly
if(boardElement[i] == 'realDeck'){
player[i] = i; // Jank...
//player[i] = deckData[i];
//console.log(deckData[i]);
}else{
// Everything else missing in a jank fashion
player[i] = deckIn[i]; // 1-1 here, but likely not in future
}
cardStatus[i] = null; // Just building on first load, as deck gen
// substite at the mo, so all cards are free of status
cardFace[i] = 0; // As above, deck gen sub. Everything starts facedown
// Set position for cards in each deck...
// TODO: JANK, tidy, remove, make good!!
// using the new player set just above, which in itself is jank
if(player[i] == 0 && boardElement[i] != 'realDeck'){
listPosition[i] = player0DeckCount+1;
player0DeckCount++;
}
// Just an else, as in DB/for this test only 2 decks
// TODO: MAKE BETTER!
else{
listPosition[i] = player0DeckCount+1;
player0DeckCount++;
}
// Play shield from top of each players deck to the maximum allowed (4 typically)
for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){
// TODO: JANK IN, CHANGE CODE TO USE NEW ARRAY!!
// Temp jank, set colour to first colour req.
if(cardData[i] !== undefined){ // i may not have carddata, as realDeck
cardData[i].colour = 0;//cardData[itemCount].colour[0];
// Set the artwork (this would be done front-end I think)
cardSprite[i] = [0,0];
// Temp sprite set based on colour TODO: Change to set correct sprite from DB
switch (cardData[i].colour){
case 0: // White
cardSprite[i] = [0,0];
break;
case 1: // Blue
cardSprite[i] = [0,1];
break;
case 2: // Red
cardSprite[i] = [1,0];
break;
case 3: // Green
cardSprite[i] = [1,1];
break;
default:
break;
}
}
}
shuffleDeck(0); // Shuffle player 0 deck
shuffleDeck(1); // Shuffle player 1 deck
//return false;
// Fill each players deck with their cards
//createDecks();
// Play shield from top of each players deck to the maximum allowed (4 typically)
for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){
board.playShield(1, 'deck', currentPlayer, maxShield);
}
}
// Draw the graphics of the board/game
// Wait for the cardArt to load on first load
// Otherwise it'll be boxes, and art will flash in on first click event triggered
cardArt.onload = function(){
// Draw the graphics of the board/game
// Wait for the cardArt to load on first load
// Otherwise it'll be boxes, and art will flash in on first click event triggered
cardArt.onload = function(){
board.drawBoard(true);
};
};
}
// Right Click, Rightclick, rightclick, right click
canvas.addEventListener('contextmenu', function(event) {
@ -1171,6 +1277,7 @@ function calculateItemSizePosition(itemKey){
positionY = 60;
width = cardWidth*1.5;
height = cardHeight*1.5;
console.log('HELLO 1');
}
if(itemPlayer == 0 && itemElement == 'realDeck'){
@ -1178,6 +1285,7 @@ function calculateItemSizePosition(itemKey){
positionY = canvas.height-cardHeight*1.5-60;
width = cardWidth*1.5;
height = cardHeight*1.5;
console.log('HELLO 0');
}
if(itemPlayer == 1 && itemElement == 'board'){

@ -56,11 +56,16 @@
</select>
</div>
<hr>
<div>
<button onclick="requestGetCards()">Get Cards (DB)</button>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="/cards.js"></script><!-- Temp until DB -->
<script src="/shapes.js"></script>
<script src="/board.js"></script>
<script src="/main.js"></script>
<script src="/board.js"></script>
</body>
</html>

@ -148,3 +148,43 @@ socket.on('responseJoinRoom', function (name) {
}
});
// getCards (TODO: remove if test below is a-okay)
function requestGetCards(deckId, playerId){
console.log('+ requestGetCards');
socket.emit('requestGetCards', deckId, playerId);
}
function responseGetCards(data){
console.log(data);
if(!data.success){
alert(data.message);
}
}
socket.on('responseGetCards', function (data) {
console.log('<< responseGetCards');
responseGetCards(data);
});
// Testing getting cards from server/DB
function requestDeck(){
console.log('+ requestDeck');
socket.emit('requestDeck');
}
function responseGetDeck(data){
console.log(data);
if(!data.success){
alert(data.message);
return false;
}
return data;
}
socket.on('responseGetDeck', function (data) {
console.log('<< responseGetDeck');
responseGetDeck(data);
console.log('Load board?');
if(data.success !== true){
alert('Err with responseGetDeck. '+data.message);
}
// Pass only the data to loadBoard
loadBoard(data.message);
});

@ -1,13 +1,21 @@
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);
@ -31,11 +39,427 @@ 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();
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);
});
@ -195,3 +619,23 @@ function requestJoinRoom(socket, playerName, roomId){
}
// 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);
})
}

Loading…
Cancel
Save