From fb53056e8596658ae3dab964d57086910d74c788 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Thu, 19 Feb 2026 22:39:53 -0500 Subject: [PATCH] feat: Implement database schema and CRUD operations for turtle homes, configurations, world blocks, paths, tasks, and mining areas --- server/database.js | 287 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 server/database.js diff --git a/server/database.js b/server/database.js new file mode 100644 index 0000000..f2ea8ab --- /dev/null +++ b/server/database.js @@ -0,0 +1,287 @@ +import Database from 'better-sqlite3'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const db = new Database(path.join(__dirname, 'turtle_control.db')); + +// Initialize database schema +export function initializeDatabase() { + // Turtle homes table + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_homes ( + turtle_id INTEGER PRIMARY KEY, + x INTEGER NOT NULL, + y INTEGER NOT NULL, + z INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + + // Turtle configuration table + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_config ( + turtle_id INTEGER PRIMARY KEY, + max_distance INTEGER DEFAULT 200, + facing INTEGER DEFAULT 0, + config_json TEXT, + updated_at INTEGER NOT NULL + ) + `); + + // World blocks table + db.exec(` + CREATE TABLE IF NOT EXISTS world_blocks ( + x INTEGER NOT NULL, + y INTEGER NOT NULL, + z INTEGER NOT NULL, + block_name TEXT NOT NULL, + metadata INTEGER DEFAULT 0, + discovered_by INTEGER NOT NULL, + discovered_at INTEGER NOT NULL, + PRIMARY KEY (x, y, z) + ) + `); + + // Turtle paths table (for path recording) + db.exec(` + CREATE TABLE IF NOT EXISTS turtle_paths ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + turtle_id INTEGER NOT NULL, + path_name TEXT NOT NULL, + path_data TEXT NOT NULL, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + + // Task queue table (for multi-turtle coordination) + db.exec(` + CREATE TABLE IF NOT EXISTS task_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_type TEXT NOT NULL, + task_data TEXT NOT NULL, + assigned_turtle_id INTEGER, + priority INTEGER DEFAULT 0, + status TEXT DEFAULT 'pending', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + + // Mining areas table (for area visualization) + db.exec(` + CREATE TABLE IF NOT EXISTS mining_areas ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + turtle_id INTEGER NOT NULL, + min_x INTEGER NOT NULL, + min_y INTEGER NOT NULL, + min_z INTEGER NOT NULL, + max_x INTEGER NOT NULL, + max_y INTEGER NOT NULL, + max_z INTEGER NOT NULL, + status TEXT DEFAULT 'active', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + + // Create indexes for better performance + db.exec(` + CREATE INDEX IF NOT EXISTS idx_world_blocks_discovered + ON world_blocks(discovered_by); + `); + + db.exec(` + CREATE INDEX IF NOT EXISTS idx_task_queue_status + ON task_queue(status, priority DESC); + `); + + console.log('✅ Database initialized'); +} + +// Turtle Homes +export function saveTurtleHome(turtleId, position) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO turtle_homes (turtle_id, x, y, z, updated_at) + VALUES (?, ?, ?, ?, ?) + `); + stmt.run(turtleId, position.x, position.y, position.z, Date.now()); +} + +export function getTurtleHome(turtleId) { + const stmt = db.prepare('SELECT x, y, z FROM turtle_homes WHERE turtle_id = ?'); + return stmt.get(turtleId); +} + +export function getAllTurtleHomes() { + const stmt = db.prepare('SELECT turtle_id, x, y, z FROM turtle_homes'); + return stmt.all(); +} + +// Turtle Configuration +export function saveTurtleConfig(turtleId, config) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO turtle_config (turtle_id, max_distance, facing, config_json, updated_at) + VALUES (?, ?, ?, ?, ?) + `); + stmt.run( + turtleId, + config.maxDistance || 200, + config.facing || 0, + JSON.stringify(config), + Date.now() + ); +} + +export function getTurtleConfig(turtleId) { + const stmt = db.prepare('SELECT * FROM turtle_config WHERE turtle_id = ?'); + const row = stmt.get(turtleId); + if (row && row.config_json) { + return JSON.parse(row.config_json); + } + return null; +} + +// World Blocks +export function saveWorldBlock(x, y, z, blockName, metadata, discoveredBy) { + const stmt = db.prepare(` + INSERT OR REPLACE INTO world_blocks (x, y, z, block_name, metadata, discovered_by, discovered_at) + VALUES (?, ?, ?, ?, ?, ?, ?) + `); + stmt.run(x, y, z, blockName, metadata || 0, discoveredBy, Date.now()); +} + +export function getWorldBlocks(limit = 10000) { + const stmt = db.prepare('SELECT * FROM world_blocks LIMIT ?'); + return stmt.all(limit); +} + +export function getWorldBlocksInArea(minX, minY, minZ, maxX, maxY, maxZ) { + const stmt = db.prepare(` + SELECT * FROM world_blocks + WHERE x BETWEEN ? AND ? + AND y BETWEEN ? AND ? + AND z BETWEEN ? AND ? + `); + return stmt.all(minX, maxX, minY, maxY, minZ, maxZ); +} + +export function clearOldBlocks(daysOld = 7) { + const cutoffTime = Date.now() - (daysOld * 24 * 60 * 60 * 1000); + const stmt = db.prepare('DELETE FROM world_blocks WHERE discovered_at < ?'); + const result = stmt.run(cutoffTime); + return result.changes; +} + +// Turtle Paths +export function savePath(turtleId, pathName, pathData) { + const stmt = db.prepare(` + INSERT INTO turtle_paths (turtle_id, path_name, path_data, created_at, updated_at) + VALUES (?, ?, ?, ?, ?) + `); + const now = Date.now(); + stmt.run(turtleId, pathName, JSON.stringify(pathData), now, now); +} + +export function getPaths(turtleId) { + const stmt = db.prepare('SELECT * FROM turtle_paths WHERE turtle_id = ? ORDER BY created_at DESC'); + return stmt.all(turtleId).map(row => ({ + ...row, + path_data: JSON.parse(row.path_data) + })); +} + +export function deletePath(pathId) { + const stmt = db.prepare('DELETE FROM turtle_paths WHERE id = ?'); + return stmt.run(pathId); +} + +// Task Queue +export function createTask(taskType, taskData, priority = 0) { + const stmt = db.prepare(` + INSERT INTO task_queue (task_type, task_data, priority, status, created_at, updated_at) + VALUES (?, ?, ?, 'pending', ?, ?) + `); + const now = Date.now(); + const result = stmt.run(taskType, JSON.stringify(taskData), priority, now, now); + return result.lastInsertRowid; +} + +export function getNextTask() { + const stmt = db.prepare(` + SELECT * FROM task_queue + WHERE status = 'pending' + ORDER BY priority DESC, created_at ASC + LIMIT 1 + `); + const row = stmt.get(); + if (row) { + return { + ...row, + task_data: JSON.parse(row.task_data) + }; + } + return null; +} + +export function assignTask(taskId, turtleId) { + const stmt = db.prepare(` + UPDATE task_queue + SET assigned_turtle_id = ?, status = 'assigned', updated_at = ? + WHERE id = ? + `); + stmt.run(turtleId, Date.now(), taskId); +} + +export function completeTask(taskId) { + const stmt = db.prepare(` + UPDATE task_queue + SET status = 'completed', updated_at = ? + WHERE id = ? + `); + stmt.run(Date.now(), taskId); +} + +export function getAllTasks() { + const stmt = db.prepare('SELECT * FROM task_queue ORDER BY priority DESC, created_at DESC'); + return stmt.all().map(row => ({ + ...row, + task_data: JSON.parse(row.task_data) + })); +} + +// Mining Areas +export function saveMiningArea(turtleId, bounds) { + const stmt = db.prepare(` + INSERT INTO mining_areas (turtle_id, min_x, min_y, min_z, max_x, max_y, max_z, status, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, 'active', ?, ?) + `); + const now = Date.now(); + stmt.run( + turtleId, + bounds.minX, bounds.minY, bounds.minZ, + bounds.maxX, bounds.maxY, bounds.maxZ, + now, now + ); +} + +export function getMiningAreas() { + const stmt = db.prepare('SELECT * FROM mining_areas WHERE status = \'active\''); + return stmt.all(); +} + +export function closeMiningArea(areaId) { + const stmt = db.prepare('UPDATE mining_areas SET status = \'closed\', updated_at = ? WHERE id = ?'); + stmt.run(Date.now(), areaId); +} + +// Cleanup function +export function closeDatabase() { + db.close(); +} + +// Export database instance for custom queries if needed +export { db };