feat: Implement database integration for turtle homes, world blocks, paths, tasks, and mining areas
This commit is contained in:
221
server/server.js
221
server/server.js
@@ -2,6 +2,7 @@ import express from 'express';
|
|||||||
import { WebSocketServer } from 'ws';
|
import { WebSocketServer } from 'ws';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import { createServer } from 'http';
|
import { createServer } from 'http';
|
||||||
|
import * as db from './database.js';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = 3001;
|
const PORT = 3001;
|
||||||
@@ -10,6 +11,16 @@ const WS_PORT = 3002;
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Initialize database
|
||||||
|
db.initializeDatabase();
|
||||||
|
|
||||||
|
// Load persisted data from database
|
||||||
|
console.log('📂 Loading persisted data from database...');
|
||||||
|
const savedHomes = db.getAllTurtleHomes();
|
||||||
|
const savedBlocks = db.getWorldBlocks();
|
||||||
|
console.log(` Loaded ${savedHomes.length} turtle homes`);
|
||||||
|
console.log(` Loaded ${savedBlocks.length} world blocks`);
|
||||||
|
|
||||||
// Store connected web clients and turtle data
|
// Store connected web clients and turtle data
|
||||||
const webClients = new Set();
|
const webClients = new Set();
|
||||||
const turtleData = new Map(); // turtleID -> turtle state
|
const turtleData = new Map(); // turtleID -> turtle state
|
||||||
@@ -17,6 +28,22 @@ const worldBlocks = new Map(); // "x,y,z" -> {name, metadata, discoveredBy, time
|
|||||||
const turtleHomes = new Map(); // turtleID -> {x, y, z} home position
|
const turtleHomes = new Map(); // turtleID -> {x, y, z} home position
|
||||||
const turtleConfig = new Map(); // turtleID -> {maxDistance, facing, etc}
|
const turtleConfig = new Map(); // turtleID -> {maxDistance, facing, etc}
|
||||||
|
|
||||||
|
// Load saved homes into memory
|
||||||
|
for (const home of savedHomes) {
|
||||||
|
turtleHomes.set(home.turtle_id, { x: home.x, y: home.y, z: home.z });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load saved blocks into memory
|
||||||
|
for (const block of savedBlocks) {
|
||||||
|
const key = `${block.x},${block.y},${block.z}`;
|
||||||
|
worldBlocks.set(key, {
|
||||||
|
name: block.block_name,
|
||||||
|
metadata: block.metadata,
|
||||||
|
discoveredBy: block.discovered_by,
|
||||||
|
timestamp: block.discovered_at
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Timeout for considering turtles offline (30 seconds)
|
// Timeout for considering turtles offline (30 seconds)
|
||||||
const TURTLE_TIMEOUT = 30000;
|
const TURTLE_TIMEOUT = 30000;
|
||||||
|
|
||||||
@@ -62,6 +89,9 @@ function storeBlock(x, y, z, blockData, turtleID) {
|
|||||||
discoveredBy: turtleID,
|
discoveredBy: turtleID,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Persist to database
|
||||||
|
db.saveWorldBlock(x, y, z, blockData.name, blockData.metadata, turtleID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to calculate block position based on turtle position and facing
|
// Helper to calculate block position based on turtle position and facing
|
||||||
@@ -250,13 +280,16 @@ app.post('/api/turtle/:id/home', (req, res) => {
|
|||||||
const turtleID = parseInt(req.params.id);
|
const turtleID = parseInt(req.params.id);
|
||||||
const { position } = req.body;
|
const { position } = req.body;
|
||||||
|
|
||||||
if (!position || !position.x || !position.y || !position.z) {
|
if (!position || position.x === undefined || position.y === undefined || position.z === undefined) {
|
||||||
return res.status(400).json({ error: 'Invalid position' });
|
return res.status(400).json({ error: 'Invalid position' });
|
||||||
}
|
}
|
||||||
|
|
||||||
turtleHomes.set(turtleID, position);
|
turtleHomes.set(turtleID, position);
|
||||||
console.log(`📍 Set home for turtle ${turtleID}:`, position);
|
console.log(`📍 Set home for turtle ${turtleID}:`, position);
|
||||||
|
|
||||||
|
// Persist to database
|
||||||
|
db.saveTurtleHome(turtleID, position);
|
||||||
|
|
||||||
// Update turtle data
|
// Update turtle data
|
||||||
if (turtleData.has(turtleID)) {
|
if (turtleData.has(turtleID)) {
|
||||||
const turtle = turtleData.get(turtleID);
|
const turtle = turtleData.get(turtleID);
|
||||||
@@ -350,6 +383,192 @@ setInterval(() => {
|
|||||||
}
|
}
|
||||||
}, 10000); // Check every 10 seconds
|
}, 10000); // Check every 10 seconds
|
||||||
|
|
||||||
|
// ========== PATH RECORDING ENDPOINTS ==========
|
||||||
|
|
||||||
|
// Save a recorded path
|
||||||
|
app.post('/api/paths', (req, res) => {
|
||||||
|
try {
|
||||||
|
const { turtleId, pathName, pathData } = req.body;
|
||||||
|
db.savePath(turtleId, pathName, pathData);
|
||||||
|
res.json({ success: true, message: 'Path saved' });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all paths for a turtle
|
||||||
|
app.get('/api/paths/:turtleId', (req, res) => {
|
||||||
|
try {
|
||||||
|
const turtleId = parseInt(req.params.turtleId);
|
||||||
|
const paths = db.getPaths(turtleId);
|
||||||
|
res.json({ paths });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete a path
|
||||||
|
app.delete('/api/paths/:pathId', (req, res) => {
|
||||||
|
try {
|
||||||
|
const pathId = parseInt(req.params.pathId);
|
||||||
|
db.deletePath(pathId);
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========== TASK QUEUE ENDPOINTS ==========
|
||||||
|
|
||||||
|
// Create a new task
|
||||||
|
app.post('/api/tasks', (req, res) => {
|
||||||
|
try {
|
||||||
|
const { taskType, taskData, priority } = req.body;
|
||||||
|
const taskId = db.createTask(taskType, taskData, priority || 0);
|
||||||
|
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'task_created',
|
||||||
|
taskId,
|
||||||
|
taskType,
|
||||||
|
priority
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true, taskId });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all tasks
|
||||||
|
app.get('/api/tasks', (req, res) => {
|
||||||
|
try {
|
||||||
|
const tasks = db.getAllTasks();
|
||||||
|
res.json({ tasks });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get next available task
|
||||||
|
app.get('/api/tasks/next', (req, res) => {
|
||||||
|
try {
|
||||||
|
const task = db.getNextTask();
|
||||||
|
res.json({ task });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assign task to turtle
|
||||||
|
app.post('/api/tasks/:taskId/assign', (req, res) => {
|
||||||
|
try {
|
||||||
|
const taskId = parseInt(req.params.taskId);
|
||||||
|
const { turtleId } = req.body;
|
||||||
|
db.assignTask(taskId, turtleId);
|
||||||
|
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'task_assigned',
|
||||||
|
taskId,
|
||||||
|
turtleId
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Complete a task
|
||||||
|
app.post('/api/tasks/:taskId/complete', (req, res) => {
|
||||||
|
try {
|
||||||
|
const taskId = parseInt(req.params.taskId);
|
||||||
|
db.completeTask(taskId);
|
||||||
|
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'task_completed',
|
||||||
|
taskId
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========== MINING AREA ENDPOINTS ==========
|
||||||
|
|
||||||
|
// Save a mining area claim
|
||||||
|
app.post('/api/mining-areas', (req, res) => {
|
||||||
|
try {
|
||||||
|
const { turtleId, bounds } = req.body;
|
||||||
|
db.saveMiningArea(turtleId, bounds);
|
||||||
|
|
||||||
|
const areas = db.getMiningAreas();
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'mining_areas_updated',
|
||||||
|
areas
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all active mining areas
|
||||||
|
app.get('/api/mining-areas', (req, res) => {
|
||||||
|
try {
|
||||||
|
const areas = db.getMiningAreas();
|
||||||
|
res.json({ areas });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close a mining area
|
||||||
|
app.post('/api/mining-areas/:areaId/close', (req, res) => {
|
||||||
|
try {
|
||||||
|
const areaId = parseInt(req.params.areaId);
|
||||||
|
db.closeMiningArea(areaId);
|
||||||
|
|
||||||
|
const areas = db.getMiningAreas();
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'mining_areas_updated',
|
||||||
|
areas
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========== STATISTICS ENDPOINTS ==========
|
||||||
|
|
||||||
|
// Get server statistics
|
||||||
|
app.get('/api/stats', (req, res) => {
|
||||||
|
try {
|
||||||
|
const stats = {
|
||||||
|
activeTurtles: turtleData.size,
|
||||||
|
totalBlocks: worldBlocks.size,
|
||||||
|
savedHomes: turtleHomes.size,
|
||||||
|
connectedClients: webClients.size,
|
||||||
|
tasks: db.getAllTasks().length,
|
||||||
|
miningAreas: db.getMiningAreas().length
|
||||||
|
};
|
||||||
|
res.json(stats);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Graceful shutdown
|
||||||
|
process.on('SIGINT', () => {
|
||||||
|
console.log('\n🛑 Shutting down server...');
|
||||||
|
db.closeDatabase();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(`✅ Server ready!`);
|
console.log(`✅ Server ready!`);
|
||||||
console.log(`\nConfigured turtles to send updates to:`);
|
console.log(`\nConfigured turtles to send updates to:`);
|
||||||
|
|||||||
Reference in New Issue
Block a user