const express = require('express'); const sqlite3 = require('sqlite3').verbose(); const bcrypt = require('bcrypt'); const cors = require('cors'); const bodyParser = require('body-parser'); const crypto = require('crypto'); const multer = require('multer'); // Add this to your dependencies const storage = multer.memoryStorage(); const upload = multer({ storage: storage, limits: { fileSize: 50 * 1024 * 1024 // 50MB limit } }); const app = express(); const port = 5000; // Middleware app.use(cors()); app.use(bodyParser.json({limit: '50mb'})); // Increase JSON payload limit app.use(bodyParser.urlencoded({limit: '50mb', extended: true})); // Increase URL-encoded payload limit // SQLite database setup const db = new sqlite3.Database('./database.db', (err) => { if (err) { console.error(err.message); } else { console.log('Connected to SQLite database.'); } }); // Create users table if it doesn't exist db.run(`CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, email TEXT UNIQUE, password TEXT )`); // Create games table if it doesn't exist db.run(`CREATE TABLE IF NOT EXISTS games ( game_id TEXT PRIMARY KEY, name TEXT, description TEXT, game_master_id INTEGER, participants TEXT )`); //User Part // Registration route app.post('/register', async (req, res) => { const { username, email, password } = req.body; console.log("Trying to Register: ", username); try { const hashedPassword = await bcrypt.hash(password, 10); const stmt = db.prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)'); stmt.run([username, email, hashedPassword], function (err) { if (err) { return res.status(400).json({ error: 'User already exists or invalid data.' }); } res.status(201).json({ message: 'User registered successfully!', userId: this.lastID }); }); stmt.finalize(); } catch (error) { res.status(500).json({ error: 'Internal server error' }); } }); // Login route app.post('/login', (req, res) => { const { username, password } = req.body; db.get('SELECT * FROM users WHERE username = ?', [username], async (err, row) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } if (!row || !(await bcrypt.compare(password, row.password))) { return res.status(400).json({ error: 'Invalid username or password.' }); } res.json({ success: true, message: 'Login successful!', userId: row.id }); }); }); // Game Part // Fetch games for a specific user app.get('/games/:userId', (req, res) => { const userId = req.params.userId; db.all( `SELECT * FROM games WHERE game_master_id = ? OR participants LIKE ?`, [userId, `%${userId}%`], (err, rows) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } res.json(rows); } ); }); // Fetch game details including game master app.get('/games/:gameId/master', (req, res) => { const gameId = req.params.gameId; db.get('SELECT * FROM games WHERE game_id = ?', [gameId], (err, row) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } if (!row) { return res.status(404).json({ error: 'Game not found' }); } // Convert game_master_id to number for consistent comparison res.json(row); }); }); // Join Game app.post('/joinGame/:gameId', (req, res) => { const gameId = req.params.gameId; const userId = req.body.userId; db.get('SELECT * FROM games WHERE game_id = ?', [gameId], (err, game) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } if (!game) { return res.status(404).json({ error: 'Game not found' }); } const participants = JSON.parse(game.participants); const gameMasterId = game.game_master_id; // Check if the user is already a participant or the game master if (gameMasterId === userId || participants.includes(userId)) { return res.status(400).json({ error: 'User is already a participant or the game master.' }); } participants.push(userId); const stmt = db.prepare('UPDATE games SET participants = ? WHERE game_id = ?'); stmt.run([JSON.stringify(participants), gameId], function (err) { if (err) { return res.status(400).json({ error: 'Failed to join game.' }); } res.status(201).json({ message: 'User joined game successfully!', gameId: gameId }); }); stmt.finalize(); }); }); // Create a new game app.post('/games', (req, res) => { const { name, description, gameMasterId, participants } = req.body; const stmt = db.prepare('INSERT INTO games (name, description, game_master_id, participants) VALUES (?, ?, ?, ?)'); stmt.run([name, description, gameMasterId, JSON.stringify(participants)], function (err) { if (err) { return res.status(400).json({ error: 'Failed to create game.' }); } res.status(201).json({ message: 'Game created successfully!', gameId: this.lastID }); }); stmt.finalize(); }); app.post('/createGame', (req, res) => { const { name, description, gameMasterId, participants } = req.body; const gameId = crypto.randomBytes(4).toString('hex'); // Convert gameMasterId to integer const parsedGameMasterId = parseInt(gameMasterId); const stmt = db.prepare('INSERT INTO games (game_id, name, description, game_master_id, participants) VALUES (?, ?, ?, ?, ?)'); stmt.run([gameId, name, description, parsedGameMasterId, JSON.stringify(participants)], function (err) { if (err) { console.error('Database error:', err); return res.status(400).json({ error: 'Failed to create game.' }); } res.status(201).json({ message: 'Game created successfully!', gameId: gameId }); }); stmt.finalize(); }); // Master Part // Fetch game Participant Characters app.get('/games/:gameId/playerchars', (req, res) => { const gameId = req.params.gameId; db.all('SELECT * FROM PlayerCharacter WHERE GameID = ?', [gameId], (err, rows) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } // Process each row and convert BLOB to base64 if image exists const processedRows = rows.map(row => { if (row.Img) { row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`; } return row; }); res.json(processedRows); }); }); // Fetch game NPCs app.get('/games/:gameId/npcs', (req, res) => { const gameId = req.params.gameId; db.all('SELECT * FROM NPC WHERE GameID = ?', [gameId], (err, rows) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } // Process each row and convert BLOB to base64 if image exists const processedRows = rows.map(row => { if (row.Img) { row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`; } return row; }); res.json(processedRows); }); }); // Fetch game Items app.get('/games/:gameId/items', (req, res) => { const gameId = req.params.gameId; db.all(` SELECT i.*, COALESCE(pc.CharName, n.Name) as OwnerName FROM Item i LEFT JOIN PlayerCharacter pc ON i.OwnerID = pc.CharID LEFT JOIN NPC n ON i.OwnerID = n.NPCID WHERE i.GameID = ?`, [gameId], (err, rows) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } // Convert image data to base64 and store as Img property const processedRows = rows.map(row => { if (row.img) { // Database column is lowercase row.Img = `data:image/jpeg;base64,${row.img.toString('base64')}`; // Store as uppercase Img } return row; }); res.json(processedRows); } ); }); // Player Part // Create Player Character app.post('/games/character/create', upload.single('image'), (req, res) => { const { charName, race, sex, age, job, description, maxHealth, maxMana, strength, dexterity, agility, endurance, gameId, playerId } = req.body; const imageBuffer = req.file ? req.file.buffer : null; const stmt = db.prepare(` INSERT INTO PlayerCharacter ( GameID, PlayerID, CharName, Race, Sex, Age, Job, description, maxHealth, currentHealth, maxMana, currentMana, Strength, Dexterity, Agility, Endurance, Level, Gold, Img ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?) `); stmt.run([ gameId, playerId, charName, race, sex, age, job, description, maxHealth, maxHealth, maxMana, maxMana, strength, dexterity, agility, endurance, imageBuffer ], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } res.status(201).json({ message: 'Character created successfully!', characterId: this.lastID }); }); stmt.finalize(); }); // Fetch all Player Characters of Player app.get('/games/:userId/characters', (req, res) => { const userId = req.params.userId; db.all('SELECT * FROM PlayerCharacter WHERE PlayerId = ?', [userId], (err, rows) => { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } // Process each row and convert BLOB to base64 if image exists const processedRows = rows.map(row => { if (row.Img) { row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`; } return row; }); res.json(processedRows); }); }); // Fetch Player Character app.get('/games/:gameId/:userId/character', (req, res) => { const gameId = req.params.gameId; const userId = req.params.userId; console.log(`Fetching character for gameId: ${gameId}, userId: ${userId}`); // Debug output db.get('SELECT * FROM PlayerCharacter WHERE GameID = ? AND PlayerID = ?', [gameId, userId], (err, row) => { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } if (!row) { return res.status(404).json({ error: 'No character found' }); } // Convert BLOB to base64 string if image exists if (row.Img) { row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`; } res.json(row); }); }); // Update character image app.put('/games/:gameId/:userId/character/image', upload.single('image'), (req, res) => { const gameId = req.params.gameId; const userId = req.params.userId; const imageBuffer = req.file.buffer; const stmt = db.prepare('UPDATE PlayerCharacter SET Img = ? WHERE GameID = ? AND PlayerID = ?'); stmt.run([imageBuffer, gameId, userId], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } res.json({ message: 'Image updated successfully!' }); }); stmt.finalize(); }); // Update Player Character Description app.put('/games/:gameId/:userId/character', (req, res) => { const gameId = req.params.gameId; const userId = req.params.userId; const { description } = req.body; const stmt = db.prepare('UPDATE PlayerCharacter SET description = ? WHERE GameID = ? AND PlayerID = ?'); stmt.run([description, gameId, userId], function (err) { if (err) { console.error('Database error:', err); // Debug output return res.status(500).json({ error: 'Internal server error' }); } res.json({ message: 'Description updated successfully!' }); }); stmt.finalize(); }); // Update Player Character app.put('/games/character/:charId', async (req, res) => { const charId = req.params.charId; const { charName, race, sex, age, job, description, maxHealth, currentHealth, maxMana, currentMana, strength, dexterity, agility, endurance, level, gold } = req.body; const stmt = db.prepare(` UPDATE PlayerCharacter SET CharName = ?, Race = ?, Sex = ?, Age = ?, Job = ?, Description = ?, MaxHealth = ?, CurrentHealth = ?, MaxMana = ?, CurrentMana = ?, Strength = ?, Dexterity = ?, Agility = ?, Endurance = ?, Level = ?, Gold = ? WHERE CharID = ? `); stmt.run([ charName, race, sex, age, job, description, maxHealth, currentHealth, maxMana, currentMana, strength, dexterity, agility, endurance, level, gold, charId ], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Character not found' }); } res.json({ message: 'Character updated successfully!' }); }); stmt.finalize(); }); // Fetch Player Items app.get('/games/:gameId/:charId/items', (req, res) => { const gameId = req.params.gameId; const charId = req.params.charId; console.log(`Fetching Items for GameID: ${gameId}, CharID: ${charId}`); db.all('SELECT * FROM Item WHERE GameID = ? AND OwnerID = ?', [gameId, charId], (err, rows) => { if (err) { console.error('Database error:', err); // Debug output return res.status(500).json({ error: 'Internal server error' }); } console.log(rows); // Debug output res.json(rows); }); }); // Item Part // Create Item app.post('/games/item/create', upload.single('image'), (req, res) => { const { ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability, GoldValue, Abilities, GameID, AP } = req.body; const imageBuffer = req.file ? req.file.buffer : null; const stmt = db.prepare(` INSERT INTO Item ( ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability, GoldValue, Abilities, GameID, OwnerID, img, AP ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?) `); stmt.run([ ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability, GoldValue, Abilities, GameID, imageBuffer, AP ], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } res.status(201).json({ message: 'Item created successfully!', itemId: this.lastID }); }); stmt.finalize(); }); // Set Item Owner app.post('/games/:charId/:itemId/owner', (req, res) => { const gameId = req.params.gameId; const itemId = req.params.itemId; const ownerId = req.body.ownerId; db.run('UPDATE Item SET OwnerID = ? WHERE ItemID = ?', [ownerId, gameId, itemId], function (err) { if (err) { return res.status(400).json({ error: 'Failed to update item owner.' }); } res.json({ message: 'Item owner updated successfully!' }); }); }) // Update Item details including owner app.put('/games/item/:itemId', (req, res) => { const itemId = req.params.itemId; const { ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability, GoldValue, Abilities, OwnerID, AP } = req.body; // Validate required fields if (!ItemName) { return res.status(400).json({ error: 'Item name is required' }); } const stmt = db.prepare(` UPDATE Item SET ItemName = ?, Type = ?, Art = ?, Rarity = ?, MaxDurability = ?, CurrentDurability = ?, GoldValue = ?, Abilities = ?, OwnerID = ?, AP = ? WHERE ItemID = ? `); stmt.run([ ItemName, Type, Art, Rarity, MaxDurability, CurrentDurability, GoldValue, Abilities, OwnerID, AP, itemId ], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Item not found' }); } res.json({ message: 'Item updated successfully!' }); }); stmt.finalize(); }); //NPC part // Create NPC app.post('/games/npc/create', upload.single('image'), (req, res) => { const { Name, Race, Sex, Age, Job, Description, MaxHealth, MaxMana, Strength, Dexterity, Agility, Endurance, Allied, Level, GameID } = req.body; const imageBuffer = req.file ? req.file.buffer : null; const stmt = db.prepare(` INSERT INTO NPC ( GameID, Name, Race, Sex, Age, Job, Description, MaxHealth, CurrentHealth, MaxMana, CurrentMana, Strength, Dexterity, Agility, Endurance, Level, Allied, Img ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `); stmt.run([ GameID, Name, Race, Sex, Age, Job, Description, MaxHealth, MaxHealth, MaxMana, MaxMana, Strength, Dexterity, Agility, Endurance, Level, Allied, imageBuffer ], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } res.status(201).json({ message: 'NPC created successfully!', npcId: this.lastID }); }); stmt.finalize(); }); // Update NPC details app.put('/games/npc/:npcId', upload.single('image'), (req, res) => { const npcId = req.params.npcId; const { Name, Race, Sex, Age, Job, Description, MaxHealth, CurrentHealth, MaxMana, CurrentMana, Strength, Dexterity, Agility, Endurance, Level, Allied } = req.body; let updateQuery = ` UPDATE NPC SET Name = ?, Race = ?, Sex = ?, Age = ?, Job = ?, Description = ?, MaxHealth = ?, CurrentHealth = ?, MaxMana = ?, CurrentMana = ?, Strength = ?, Dexterity = ?, Agility = ?, Endurance = ?, Level = ?, Allied = ? `; let params = [ Name, Race, Sex, Age, Job, Description, MaxHealth, CurrentHealth, MaxMana, CurrentMana, Strength, Dexterity, Agility, Endurance, Level, Allied ]; // If new image is uploaded, add it to the update if (req.file) { updateQuery += `, Img = ?`; params.push(req.file.buffer); } updateQuery += ` WHERE NPCID = ?`; params.push(npcId); const stmt = db.prepare(updateQuery); stmt.run(params, function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'NPC not found' }); } res.json({ message: 'NPC updated successfully!' }); }); stmt.finalize(); }); // Get NPC details app.get('/games/npc/:npcId', (req, res) => { const npcId = req.params.npcId; db.get('SELECT * FROM NPC WHERE NPCID = ?', [npcId], (err, row) => { if (err) { return res.status(500).json({ error: 'Internal server error' }); } if (!row) { return res.status(404).json({ error: 'NPC not found' }); } // Convert BLOB to base64 string if image exists if (row.Img) { row.Img = `data:image/jpeg;base64,${row.Img.toString('base64')}`; } res.json(row); }); }); // Update NPC image app.put('/games/npc/:npcId/image', upload.single('image'), (req, res) => { const npcId = req.params.npcId; const imageBuffer = req.file.buffer; const stmt = db.prepare('UPDATE NPC SET Img = ? WHERE NPCID = ?'); stmt.run([imageBuffer, npcId], function(err) { if (err) { console.error('Database error:', err); return res.status(500).json({ error: 'Internal server error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'NPC not found' }); } res.json({ message: 'NPC image updated successfully!' }); }); stmt.finalize(); }); // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); });