-- nanomachine_server.lua
local component = require("component")
local event = require("event")
local serialization = require("serialization")
local filesystem = require("filesystem")
-- Config
local PORT = 2000
local UPDATE_INTERVAL = 1
local SAVE_FILE = "/home/nanomachine_data.cfg"
-- Components
local modem = component.modem
local nano = component.nanomachines
-- State Management
local state = {
power = 0,
maxPower = 0,
health = 0,
maxHealth = 20,
hunger = 0,
activeInputs = 0,
totalInputs = 0,
activeEffects = 0,
currentProfile = nil,
autoProtect = false,
healthThreshold = 10,
hungerThreshold = 6,
discoveryActive = false,
discoveryProgress = 0,
discoveryTested = 0,
discoveryFound = 0,
lastUpdate = 0
}
-- Data Storage
local profiles = {}
local inputStates = {}
local discoveredEffects = {}
local discoveryQueue = {}
local discoveryIndex = 1
-- Initialize
local function initialize()
print("=== NANOMACHINE SERVER INITIALIZING ===")
print("")
modem.open(PORT)
print("✓ Modem listening on port " .. PORT)
print("✓ Server Address: " .. modem.address)
print("")
-- Check for nanomachines
if not nano then
print("⚠ WARNING: No nanomachines component found!")
print("Make sure nanomachines are installed and connected.")
return false
end
print("✓ Nanomachines detected")
-- Get total inputs
state.totalInputs = nano.getInputCount()
print("✓ Total inputs: " .. state.totalInputs)
-- Initialize input states
for i = 1, state.totalInputs do
inputStates[i] = {
id = i,
active = false,
lastToggled = 0
}
end
-- Load saved data
loadData()
print("")
print("=== SERVER READY ===")
print("")
return true
end
-- Save/Load Functions
local function saveData()
local data = {
profiles = profiles,
discoveredEffects = discoveredEffects,
autoProtect = state.autoProtect,
healthThreshold = state.healthThreshold,
hungerThreshold = state.hungerThreshold
}
local file = io.open(SAVE_FILE, "w")
if file then
file:write(serialization.serialize(data))
file:close()
print("[SAVE] Data saved successfully")
return true
else
print("[ERROR] Failed to save data")
return false
end
end
local function loadData()
if not filesystem.exists(SAVE_FILE) then
print("[LOAD] No saved data found, starting fresh")
-- Create default profiles
profiles = {
{
name = "Combat",
description = "Speed + Strength + Resistance",
inputs = {}
},
{
name = "Mining",
description = "Haste + Night Vision",
inputs = {}
},
{
name = "Safe",
description = "Regeneration only",
inputs = {}
}
}
return
end
local file = io.open(SAVE_FILE, "r")
if file then
local content = file:read("*a")
file:close()
local success, data = pcall(serialization.unserialize, content)
if success and data then
profiles = data.profiles or {}
discoveredEffects = data.discoveredEffects or {}
state.autoProtect = data.autoProtect or false
state.healthThreshold = data.healthThreshold or 10
state.hungerThreshold = data.hungerThreshold or 6
print("[LOAD] Data loaded successfully")
print(" Profiles: " .. #profiles)
print(" Discovered Effects: " .. #discoveredEffects)
else
print("[ERROR] Failed to parse saved data")
end
end
end
-- Nanomachine Control Functions
local function updateNanoStatus()
if not nano then return end
-- Get power
state.power = nano.getPowerState()
state.maxPower = nano.getMaxPower()
-- Count active inputs
state.activeInputs = 0
for i = 1, state.totalInputs do
if nano.getInput(i) then
inputStates[i].active = true
state.activeInputs = state.activeInputs + 1
else
inputStates[i].active = false
end
end
-- Get active effects
local effects = nano.getActiveEffects()
state.activeEffects = effects and #effects or 0
state.lastUpdate = os.time()
end
local function setInput(inputId, active)
if not nano or inputId < 1 or inputId > state.totalInputs then
return false
end
nano.setInput(inputId, active)
inputStates[inputId].active = active
inputStates[inputId].lastToggled = os.time()
print("[INPUT] Input " .. inputId .. " set to " .. (active and "ON" or "OFF"))
return true
end
local function toggleInput(inputId)
if not nano or inputId < 1 or inputId > state.totalInputs then
return false
end
local currentState = nano.getInput(inputId)
return setInput(inputId, not currentState)
end
local function disableAllInputs()
print("[ACTION] Disabling all inputs...")
for i = 1, state.totalInputs do
setInput(i, false)
end
state.currentProfile = nil
print("[ACTION] All inputs disabled")
end
-- Profile Management
local function activateProfile(profileName)
print("[PROFILE] Activating profile: " .. profileName)
-- Find profile
local profile = nil
for _, p in ipairs(profiles) do
if p.name == profileName then
profile = p
break
end
end
if not profile then
print("[ERROR] Profile not found: " .. profileName)
return false
end
-- Disable all first
disableAllInputs()
-- Activate profile inputs
if profile.inputs and #profile.inputs > 0 then
for _, inputId in ipairs(profile.inputs) do
setInput(inputId, true)
end
end
state.currentProfile = profileName
print("[PROFILE] Profile activated: " .. profileName)
return true
end
local function createProfile(profileName)
print("[PROFILE] Creating profile: " .. profileName)
-- Get current active inputs
local activeInputs = {}
for i = 1, state.totalInputs do
if inputStates[i].active then
table.insert(activeInputs, i)
end
end
-- Create profile
local profile = {
name = profileName,
description = "Custom profile",
inputs = activeInputs,
created = os.time()
}
table.insert(profiles, profile)
saveData()
print("[PROFILE] Profile created with " .. #activeInputs .. " inputs")
return true
end
local function deleteProfile(profileName)
for i, profile in ipairs(profiles) do
if profile.name == profileName then
table.remove(profiles, i)
saveData()
print("[PROFILE] Deleted profile: " .. profileName)
return true
end
end
print("[ERROR] Profile not found: " .. profileName)
return false
end
-- Auto-Protection System
local function checkAutoProtection()
if not state.autoProtect or not nano then
return
end
-- Check health (would need player component or other method to get real health)
-- This is a placeholder - you'd need to implement actual health checking
-- For now, we'll use a simple low power protection
if state.power < (state.maxPower * 0.2) then
print("[AUTO-PROTECT] Low power detected, activating emergency profile")
-- Could activate a specific safe profile here
end
end
-- Discovery System
local function initializeDiscovery()
print("[DISCOVERY] Initializing discovery system...")
discoveryQueue = {}
-- Generate all possible single input combinations
for i = 1, state.totalInputs do
table.insert(discoveryQueue, {i})
end
-- Generate all possible double input combinations
for i = 1, state.totalInputs do
for j = i + 1, state.totalInputs do
table.insert(discoveryQueue, {i, j})
end
end
-- Could add triple combinations but that's a lot
-- For 32 inputs: 32 + 496 + 4960 = 5488 combinations
print("[DISCOVERY] Queue size: " .. #discoveryQueue .. " combinations")
discoveryIndex = 1
state.discoveryTested = 0
state.discoveryFound = 0
end
local function runDiscoveryStep()
if not state.discoveryActive or discoveryIndex > #discoveryQueue then
state.discoveryActive = false
print("[DISCOVERY] Discovery complete!")
saveData()
return
end
local combination = discoveryQueue[discoveryIndex]
-- Disable all inputs
for i = 1, state.totalInputs do
setInput(i, false)
end
-- Wait a bit for effects to clear
os.sleep(0.5)
-- Enable combination
for _, inputId in ipairs(combination) do
setInput(inputId, true)
end
-- Wait for effects to activate
os.sleep(1)
-- Check for effects
local effects = nano.getActiveEffects()
if effects and #effects > 0 then
local effectData = {
combination = combination,
effects = effects,
discovered = os.time()
}
table.insert(discoveredEffects, effectData)
state.discoveryFound = state.discoveryFound + 1
print("[DISCOVERY] Found effects: " .. table.concat(effects, ", "))
print(" Inputs: " .. table.concat(combination, ", "))
end
state.discoveryTested = state.discoveryTested + 1
state.discoveryProgress = math.floor((discoveryIndex / #discoveryQueue) * 100)
discoveryIndex = discoveryIndex + 1
end
-- Message Handler
local function handleMessage(sender, port, distance, command, data)
print("[REQUEST] " .. command .. " from " .. sender:sub(1, 8))
if command == "GET_STATUS" then
local serialized = serialization.serialize(state)
modem.send(sender, PORT, "GET_STATUS_RESPONSE", serialized)
elseif command == "GET_PROFILES" then
local serialized = serialization.serialize(profiles)
modem.send(sender, PORT, "GET_PROFILES_RESPONSE", serialized)
elseif command == "GET_INPUTS" then
local serialized = serialization.serialize(inputStates)
modem.send(sender, PORT, "GET_INPUTS_RESPONSE", serialized)
elseif command == "ACTIVATE_PROFILE" then
activateProfile(data)
modem.send(sender, PORT, "ACK")
elseif command == "CREATE_PROFILE" then
createProfile(data)
modem.send(sender, PORT, "ACK")
elseif command == "DELETE_PROFILE" then
deleteProfile(data)
modem.send(sender, PORT, "ACK")
elseif command == "TOGGLE_INPUT" then
toggleInput(data)
modem.send(sender, PORT, "ACK")
elseif command == "DISABLE_ALL" then
disableAllInputs()
modem.send(sender, PORT, "ACK")
elseif command == "EMERGENCY_STOP" then
disableAllInputs()
state.autoProtect = false
state.discoveryActive = false
print("[EMERGENCY] Emergency stop activated!")
modem.send(sender, PORT, "ACK")
elseif command == "TOGGLE_AUTO" then
state.autoProtect = not state.autoProtect
saveData()
print("[AUTO-PROTECT] " .. (state.autoProtect and "ENABLED" or "DISABLED"))
modem.send(sender, PORT, "ACK")
elseif command == "SET_HEALTH_THRESHOLD" then
state.healthThreshold = data
saveData()
print("[SETTING] Health threshold set to " .. data)
modem.send(sender, PORT, "ACK")
elseif command == "SET_HUNGER_THRESHOLD" then
state.hungerThreshold = data
saveData()
print("[SETTING] Hunger threshold set to " .. data)
modem.send(sender, PORT, "ACK")
elseif command == "START_DISCOVERY" then
initializeDiscovery()
state.discoveryActive = true
print("[DISCOVERY] Discovery started")
modem.send(sender, PORT, "ACK")
elseif command == "PAUSE_DISCOVERY" then
state.discoveryActive = false
print("[DISCOVERY] Discovery paused")
modem.send(sender, PORT, "ACK")
elseif command == "RESET_DISCOVERY" then
initializeDiscovery()
state.discoveryActive = true
print("[DISCOVERY] Discovery reset and restarted")
modem.send(sender, PORT, "ACK")
elseif command == "PING" then
modem.send(sender, PORT, "PONG")
else
modem.send(sender, PORT, "ERROR")
end
end
-- Status Display
local function displayStatus()
print("╔═══════════════════════════════════════════════╗")
print("║ NANOMACHINE SERVER STATUS ║")
print("╚═══════════════════════════════════════════════╝")
print("")
print("Server: " .. modem.address:sub(1, 8) .. "...")
print("Port: " .. PORT)
print("")
print("Power: " .. state.power .. "/" .. state.maxPower)
print("Active Inputs: " .. state.activeInputs .. "/" .. state.totalInputs)
print("Active Effects: " .. state.activeEffects)
print("")
print("Current Profile: " .. (state.currentProfile or "None"))
print("Auto-Protect: " .. (state.autoProtect and "ENABLED" or "DISABLED"))
print("")
if state.discoveryActive then
print("Discovery: ACTIVE")
print(" Progress: " .. state.discoveryProgress .. "%")
print(" Tested: " .. state.discoveryTested)
print(" Found: " .. state.discoveryFound .. " effects")
else
print("Discovery: IDLE")
print(" Total Discovered: " .. #discoveredEffects .. " effects")
end
print("")
print("Profiles: " .. #profiles)
print("")
print("Press Ctrl+C to stop server")
print("───────────────────────────────────────────────")
end
-- Main Loop
local function main()
if not initialize() then
print("Failed to initialize server")
return
end
local lastStatusDisplay = 0
local lastDataUpdate = 0
local lastAutoCheck = 0
local lastDiscoveryStep = 0
while true do
local currentTime = os.time()
-- Update nanomachine status
if currentTime - lastDataUpdate >= UPDATE_INTERVAL then
updateNanoStatus()
lastDataUpdate = currentTime
end
-- Check auto-protection
if currentTime - lastAutoCheck >= 2 then
checkAutoProtection()
lastAutoCheck = currentTime
end
-- Run discovery step
if state.discoveryActive and currentTime - lastDiscoveryStep >= 2 then
runDiscoveryStep()
lastDiscoveryStep = currentTime
end
-- Display status
if currentTime - lastStatusDisplay >= 5 then
displayStatus()
lastStatusDisplay = currentTime
end
-- Handle messages
local _, _, sender, port, distance, command, data = event.pull(0.1, "modem_message")
if command then
handleMessage(sender, port, distance, command, data)
end
end
end
-- Cleanup
local function cleanup()
print("")
print("=== SERVER SHUTTING DOWN ===")
-- Save data
saveData()
-- Close modem
modem.close(PORT)
print("✓ Server stopped")
end
-- Error handling
local success, err = xpcall(main, debug.traceback)
if not success then
print("")
print("ERROR: " .. tostring(err))
end
cleanup()