feat: Implement database schema and CRUD operations for turtle homes, configurations, world blocks, paths, tasks, and mining areas
This commit is contained in:
287
server/database.js
Normal file
287
server/database.js
Normal file
@@ -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 };
|
||||
Reference in New Issue
Block a user