Compare commits
6 Commits
668d4a3685
...
eff33dfe09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff33dfe09 | ||
|
|
f38a219ad0 | ||
|
|
0bf5590343 | ||
|
|
bf7db42384 | ||
|
|
8731da04f9 | ||
|
|
ff58778f3f |
@@ -20,6 +20,10 @@ import {
|
||||
RefuelingState,
|
||||
DumpInventoryState,
|
||||
FarmingState,
|
||||
ScanState,
|
||||
ExtractionState,
|
||||
BuildingState,
|
||||
AutocraftState,
|
||||
} from './states/index.js';
|
||||
|
||||
const STATE_MAP = {
|
||||
@@ -33,6 +37,14 @@ const STATE_MAP = {
|
||||
dumpInventory: DumpInventoryState,
|
||||
dumping: DumpInventoryState,
|
||||
farming: FarmingState,
|
||||
scanning: ScanState,
|
||||
scan: ScanState,
|
||||
extracting: ExtractionState,
|
||||
extraction: ExtractionState,
|
||||
building: BuildingState,
|
||||
build: BuildingState,
|
||||
autocrafting: AutocraftState,
|
||||
autocraft: AutocraftState,
|
||||
};
|
||||
|
||||
// Timeout for exec() commands (ms)
|
||||
@@ -486,6 +498,8 @@ export class Turtle extends EventEmitter {
|
||||
homePosition: this._homePosition,
|
||||
facing: this._facing,
|
||||
fuel: this._fuel,
|
||||
fuelLimit: this._fuelLimit,
|
||||
selectedSlot: this._selectedSlot,
|
||||
inventory: this._inventory,
|
||||
inventoryCount: Array.isArray(this._inventory)
|
||||
? this._inventory.length
|
||||
@@ -496,6 +510,9 @@ export class Turtle extends EventEmitter {
|
||||
connected: this.connected,
|
||||
lastUpdate: this.lastUpdate,
|
||||
label: this._label,
|
||||
peripherals: this._peripherals,
|
||||
error: this._error,
|
||||
warning: this._warning,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -384,6 +384,28 @@ app.post('/api/turtle/:id/commands/ack', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Receive real-time events from turtles (inventory, peripherals)
|
||||
app.post('/api/turtle/event', (req, res) => {
|
||||
try {
|
||||
const { turtleID, type, message } = req.body;
|
||||
|
||||
if (!turtleID || !type) {
|
||||
return res.status(400).json({ error: 'Missing turtleID or type' });
|
||||
}
|
||||
|
||||
const turtle = turtles.get(turtleID);
|
||||
if (turtle) {
|
||||
turtle.handleEvent(type, message);
|
||||
console.log(`📡 Event from T#${turtleID}: ${type}`);
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('❌ Error processing turtle event:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get all turtles
|
||||
app.get('/api/turtles', (req, res) => {
|
||||
res.json({
|
||||
@@ -970,6 +992,72 @@ app.post('/api/mining-areas/:areaId/close', (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ========== CHUNK ANALYSIS ENDPOINTS ==========
|
||||
|
||||
// Get all chunk analyses
|
||||
app.get('/api/chunks', (req, res) => {
|
||||
try {
|
||||
const chunks = db.getAllChunkAnalyses();
|
||||
res.json(chunks);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get specific chunk analysis
|
||||
app.get('/api/chunks/:x/:z', (req, res) => {
|
||||
try {
|
||||
const x = parseInt(req.params.x);
|
||||
const z = parseInt(req.params.z);
|
||||
const chunk = db.getChunkAnalysis(x, z);
|
||||
res.json(chunk || { x, z, analysis: {} });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Save chunk analysis
|
||||
app.post('/api/chunks', (req, res) => {
|
||||
try {
|
||||
const { x, z, analysis } = req.body;
|
||||
db.saveChunkAnalysis(x, z, analysis || {});
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Search blocks by name pattern in area
|
||||
app.get('/api/world/blocks/search', (req, res) => {
|
||||
try {
|
||||
const { fromX, fromY, fromZ, toX, toY, toZ, pattern } = req.query;
|
||||
if (!pattern) {
|
||||
return res.status(400).json({ error: 'Missing pattern parameter' });
|
||||
}
|
||||
const blocks = db.getBlocksWithNameLike(
|
||||
parseInt(fromX) || -1000, parseInt(fromY) || -64, parseInt(fromZ) || -1000,
|
||||
parseInt(toX) || 1000, parseInt(toY) || 320, parseInt(toZ) || 1000,
|
||||
pattern
|
||||
);
|
||||
res.json({ blocks });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get single block info
|
||||
app.get('/api/world/block/:x/:y/:z', (req, res) => {
|
||||
try {
|
||||
const x = parseInt(req.params.x);
|
||||
const y = parseInt(req.params.y);
|
||||
const z = parseInt(req.params.z);
|
||||
const block = db.getBlock(x, y, z);
|
||||
res.json(block || null);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ========== STATISTICS ENDPOINTS ==========
|
||||
|
||||
// Get server statistics
|
||||
|
||||
@@ -7,3 +7,7 @@ export { GoHomeState } from './GoHomeState.js';
|
||||
export { RefuelingState } from './RefuelingState.js';
|
||||
export { DumpInventoryState } from './DumpInventoryState.js';
|
||||
export { FarmingState } from './FarmingState.js';
|
||||
export { ScanState } from './ScanState.js';
|
||||
export { ExtractionState } from './ExtractionState.js';
|
||||
export { BuildingState } from './BuildingState.js';
|
||||
export { AutocraftState } from './AutocraftState.js';
|
||||
|
||||
60
turtle.lua
60
turtle.lua
@@ -639,6 +639,66 @@ parallel.waitForAny(
|
||||
end
|
||||
end,
|
||||
|
||||
function()
|
||||
-- Inventory change events (real-time)
|
||||
while true do
|
||||
os.pullEvent("turtle_inventory")
|
||||
local inventory = {}
|
||||
local fns = {}
|
||||
for i = 1, 16 do
|
||||
fns[i] = function()
|
||||
local item = turtle.getItemDetail(i, true)
|
||||
if item then
|
||||
inventory[tostring(i)] = item
|
||||
end
|
||||
end
|
||||
end
|
||||
parallel.waitForAll(table.unpack(fns))
|
||||
|
||||
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
||||
type = "inventory_update",
|
||||
turtleID = os.getComputerID(),
|
||||
inventory = inventory,
|
||||
timestamp = os.epoch("utc")
|
||||
})
|
||||
end
|
||||
end,
|
||||
|
||||
function()
|
||||
-- Peripheral attached events (real-time)
|
||||
while true do
|
||||
os.pullEvent("peripheral")
|
||||
sleep(0.1) -- Small delay to let CC register
|
||||
local peripherals = {}
|
||||
local names = peripheral.getNames()
|
||||
for _, name in ipairs(names) do
|
||||
peripherals[name] = {types = {peripheral.getType(name)}}
|
||||
end
|
||||
|
||||
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
||||
type = "peripheral_attached",
|
||||
turtleID = os.getComputerID(),
|
||||
peripherals = peripherals,
|
||||
timestamp = os.epoch("utc")
|
||||
})
|
||||
end
|
||||
end,
|
||||
|
||||
function()
|
||||
-- Peripheral detached events (real-time)
|
||||
while true do
|
||||
local _, side = os.pullEvent("peripheral_detach")
|
||||
if not peripheral.isPresent(side) then
|
||||
modem.transmit(CHANNEL_SEND, CHANNEL_RECEIVE, {
|
||||
type = "peripheral_detached",
|
||||
turtleID = os.getComputerID(),
|
||||
side = side,
|
||||
timestamp = os.epoch("utc")
|
||||
})
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
function()
|
||||
-- Local autonomous fallback
|
||||
while true do
|
||||
|
||||
@@ -573,6 +573,81 @@ while true do
|
||||
|
||||
addLog(" -> Sent " .. #blocks .. " blocks to server", colors.lime)
|
||||
end
|
||||
|
||||
elseif message.type == "inventory_update" then
|
||||
-- Turtle inventory changed in real-time
|
||||
local turtleID = message.turtleID
|
||||
addLog("Turtle #" .. turtleID .. " inventory update", colors.cyan)
|
||||
|
||||
local success = pcall(function()
|
||||
local response = http.post(
|
||||
SERVER_URL .. "/api/turtle/event",
|
||||
textutils.serializeJSON({
|
||||
turtleID = turtleID,
|
||||
type = "INVENTORY_UPDATE",
|
||||
message = message.inventory
|
||||
}),
|
||||
{["Content-Type"] = "application/json"}
|
||||
)
|
||||
if response then
|
||||
response.close()
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
stats.errors = stats.errors + 1
|
||||
addLog(" -> Failed to forward inventory update", colors.red)
|
||||
end
|
||||
|
||||
elseif message.type == "peripheral_attached" then
|
||||
-- Turtle peripheral attached in real-time
|
||||
local turtleID = message.turtleID
|
||||
addLog("Turtle #" .. turtleID .. " peripheral attached", colors.cyan)
|
||||
|
||||
local success = pcall(function()
|
||||
local response = http.post(
|
||||
SERVER_URL .. "/api/turtle/event",
|
||||
textutils.serializeJSON({
|
||||
turtleID = turtleID,
|
||||
type = "PERIPHERAL_ATTACHED",
|
||||
message = message.peripherals
|
||||
}),
|
||||
{["Content-Type"] = "application/json"}
|
||||
)
|
||||
if response then
|
||||
response.close()
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
stats.errors = stats.errors + 1
|
||||
addLog(" -> Failed to forward peripheral attach", colors.red)
|
||||
end
|
||||
|
||||
elseif message.type == "peripheral_detached" then
|
||||
-- Turtle peripheral detached in real-time
|
||||
local turtleID = message.turtleID
|
||||
addLog("Turtle #" .. turtleID .. " peripheral detached: " .. (message.side or "?"), colors.cyan)
|
||||
|
||||
local success = pcall(function()
|
||||
local response = http.post(
|
||||
SERVER_URL .. "/api/turtle/event",
|
||||
textutils.serializeJSON({
|
||||
turtleID = turtleID,
|
||||
type = "PERIPHERAL_DETACHED",
|
||||
message = message.side
|
||||
}),
|
||||
{["Content-Type"] = "application/json"}
|
||||
)
|
||||
if response then
|
||||
response.close()
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
stats.errors = stats.errors + 1
|
||||
addLog(" -> Failed to forward peripheral detach", colors.red)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif channel == POCKET_CHANNEL then
|
||||
|
||||
Reference in New Issue
Block a user