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 cors from 'cors';
|
||||
import { createServer } from 'http';
|
||||
import * as db from './database.js';
|
||||
|
||||
const app = express();
|
||||
const PORT = 3001;
|
||||
@@ -10,6 +11,16 @@ const WS_PORT = 3002;
|
||||
app.use(cors());
|
||||
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
|
||||
const webClients = new Set();
|
||||
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 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)
|
||||
const TURTLE_TIMEOUT = 30000;
|
||||
|
||||
@@ -62,6 +89,9 @@ function storeBlock(x, y, z, blockData, turtleID) {
|
||||
discoveredBy: turtleID,
|
||||
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
|
||||
@@ -250,13 +280,16 @@ app.post('/api/turtle/:id/home', (req, res) => {
|
||||
const turtleID = parseInt(req.params.id);
|
||||
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' });
|
||||
}
|
||||
|
||||
turtleHomes.set(turtleID, position);
|
||||
console.log(`📍 Set home for turtle ${turtleID}:`, position);
|
||||
|
||||
// Persist to database
|
||||
db.saveTurtleHome(turtleID, position);
|
||||
|
||||
// Update turtle data
|
||||
if (turtleData.has(turtleID)) {
|
||||
const turtle = turtleData.get(turtleID);
|
||||
@@ -350,6 +383,192 @@ setInterval(() => {
|
||||
}
|
||||
}, 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, () => {
|
||||
console.log(`✅ Server ready!`);
|
||||
console.log(`\nConfigured turtles to send updates to:`);
|
||||
|
||||
Reference in New Issue
Block a user