Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions Maintainer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,37 @@ local cfg = require("config")
local util = require("src.Utility")

local items = cfg.items
local fluids = cfg.fluids
local sleepInterval = cfg.sleep


if fluids and next(fluids) ~= nil and not ae2.hasFluidSupport() then
logInfo("WARNING: cfg.fluids is configured but the ME interface does not expose getFluidInNetwork (requires GTNH 2.9+). Fluid entries will be skipped.")
fluids = nil
end

while true do
local itemsCrafting = ae2.checkIfCrafting()

for item, config in pairs(items) do
if itemsCrafting[item] ~= true then
local success, answer = ae2.requestItem(item, config[1], config[2])
if itemsCrafting[item] == true then
logInfo(item .. " is already being crafted, skipping...")
else
local success, answer = ae2.requestItem(item, config[1], config[2], config[3])
logInfo(answer)
end


end

if fluids then
for fluid, config in pairs(fluids) do
if itemsCrafting[fluid] == true then
logInfo(fluid .. " is already being crafted, skipping...")
else
local success, answer = ae2.requestFluid(fluid, config[1], config[2], config[3])
logInfo(answer)
end
end
end

os.sleep(sleepInterval)
end
end
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,56 @@
# Infinite Maintainer

Lets you passive lines easily, without lag and randomness of AE2 maintainer.
Also supports having a threshold.

# Setup

- Full block ME interface connected to an adapter
- Crafting Monitors on your CPUs
- (Internet card)
- OC stuff to make a basic computer

# Installation

Download it

```bash
wget https://raw.githubusercontent.com/Niels1006/Infinite-Maintainer/master/installer.lua && installer
wget raw.githubusercontent.com/Echoloquate/Level-Maintainer/master/installer.lua && installer
```

Run it

```bash
Maintainer
```

# Config
You can change maintained items in `config.lua`. Pattern is as follows: `["item_name"] = {threshold, batch_size}` as well as the time inbetween craft checks.

**!! Keep in mind that threshold should only be added if necessary and preferrably not in mainnet, since it has a performance impact !!**
You can change maintained items in `config.lua`. There are two blocks: `cfg.items` for regular items (and the legacy `ae2fc:fluid_drop` workaround) and `cfg.fluids` for native fluid maintenance on GTNH 2.9+.

## Items

```lua
cfg["items"] = {
["Osmium Dust"] = {nil, 64}, -- no threshold
["drop of Molten SpaceTime"] = {1000000, 1, "spacetime"}, -- fluid drop with threshold + fluid name
}
```

Pattern: `["item_label"] = {threshold, batch_size, fluid_name?}`. The third value is only needed for `ae2fc:fluid_drop` items and is the fluid's registry name -- this path works on any GTNH version.

## Fluids (GTNH 2.9+)

GTNH 2.9 unified items and fluids in the OpenComputers AE2 integration, so fluid craftables can now be requested directly without going through `ae2fc:fluid_drop`. Threshold checks use real fluid amounts in mB.

```lua
cfg["fluids"] = {
["Molten SpaceTime"] = {1000000, 1000},
}
```

Pattern: `["fluid_label"] = {threshold_mb, batch_mb[, fluid_registry_name]}`. The label is the fluid's display name as shown in the AE crafting terminal. The fluid registry name is auto-detected from the craftable's stack -- pass it as a third value only as an override if auto-detection ever resolves to the wrong fluid. Omit the block entirely on pre-2.9 setups.

**!! Threshold has a performance impact -- only add it when necessary, and preferably not on mainnet !!**

Reboot after changing values.
23 changes: 17 additions & 6 deletions config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,26 @@ local cfg = {}

-- EXAMPLE --

-- [item_name] = {threshold, batch_size} -- keep in mind that no threshold has a better performance!
-- ["Osmium Dust"] = {nil, 64} -- without threshold, batch_size=64
-- ["drop of Molten SpaceTime"] = {1000000, 1} -- with threshold
-- [item_name] = {threshold, batch_size, fluid_name} -- fluid_name is REQUIRED for fluid drops
-- ["Osmium Dust"] = {nil, 64} -- regular item without threshold
-- ["drop of Molten SpaceTime"] = {1000000, 1, "spacetime"} -- fluid drop with threshold and fluid name

cfg["items"] = {
["drop of Molten SpaceTime"] = {nil, 1},
["drop of Molten White Dwarf Matter"] = {nil, 1}
["drop of Molten SpaceTime"] = {nil, 1, "spacetime"},
["drop of Molten White Dwarf Matter"] = {nil, 1, "white_dwarf_matter"}
}

-- Native fluid maintenance (GTNH 2.9+ only -- requires the StackApi-aware
-- ME interface). Safe to omit entirely on older versions.
--
-- [fluid_label] = {threshold_mb, batch_mb[, fluid_registry_name]}
-- The third value is an optional override -- the fluid registry name is
-- auto-detected from the craftable's stack, so usually you only need the first two.
-- Pass it explicitly only if auto-detection picks the wrong fluid.
cfg["fluids"] = {
-- ["Molten SpaceTime"] = {nil, 1000},
}

cfg["sleep"] = 10

return cfg
return cfg
2 changes: 1 addition & 1 deletion installer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local function exists(filename)
return filesystem.exists(shell.getWorkingDirectory() .. "/" .. filename)
end

local repo = "https://raw.githubusercontent.com/Niels1006/Infinite-Maintainer/";
local repo = "https://raw.githubusercontent.com/Echoloquate/Level-Maintainer/";
local branch = "master"

for i = 1, #paths do
Expand Down
127 changes: 114 additions & 13 deletions src/AE2.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,72 @@ local ME = component.me_interface

local AE2 = {}

function AE2.requestItem(name, threshold, count)
craftables = ME.getCraftables({
["label"] = name
})
-- Lightweight cache for specific items only.
-- Values: a craftable userdata (hit), or `false` (negative lookup).
local itemCache = {}
local fluidNameCache = {} -- name -> fluid registry name, or false if the craftable has no fluid stack
local cacheTimestamp = 0
local CACHE_DURATION = 600 -- 10 minutes in seconds

-- Function to get or cache a specific craftable item
local function getCraftableForItem(itemName)
local currentTime = os.time()

local cached = itemCache[itemName]
if cached ~= nil and currentTime - cacheTimestamp < CACHE_DURATION then
if cached == false then return nil end
return cached
end

-- If cache is too old, clear it completely to save memory
if currentTime - cacheTimestamp >= CACHE_DURATION then
itemCache = {}
fluidNameCache = {}
cacheTimestamp = currentTime
end

-- Look for this specific item in craftables
local craftables = ME.getCraftables({["label"] = itemName})
if #craftables >= 1 then
item = craftables[1].getItemStack()
itemCache[itemName] = craftables[1] -- Cache only this one item
return craftables[1]
end

itemCache[itemName] = false -- Cache the negative lookup so misspelled entries don't re-query every cycle
return nil
end

function AE2.requestItem(name, threshold, count, fluidName)
local craftable = getCraftableForItem(name)

if craftable then
local item = (craftable.getStack or craftable.getItemStack)(craftable)
if threshold ~= nil then
itemInSystem = ME.getItemsInNetwork({
["label"] = name
})
if (#itemInSystem > 0 and itemInSystem[1]["size"] > threshold) then
return table.unpack({false, "The amount of " .. itemInSystem[1]["label"] .. " exceeds threshold! Aborting request."})
local itemInSystem = nil

if fluidName then
local fluidTag = '{Fluid:' .. fluidName .. '}'
itemInSystem = ME.getItemInNetwork("ae2fc:fluid_drop", 0, fluidTag)
else
if item.name then
if item.tag then
itemInSystem = ME.getItemInNetwork(item.name, item.damage or 0, item.tag)
end

-- Fallback: try with just the internal name and damage
if itemInSystem == nil then
itemInSystem = ME.getItemInNetwork(item.name, item.damage or 0)
end
end
end

if itemInSystem ~= nil and itemInSystem.size >= threshold then
return table.unpack({false, "The amount of " .. (itemInSystem.label or name) .. " (" .. itemInSystem.size .. ") meets or exceeds threshold (" .. threshold .. ")! Aborting request."})
end
end

if item.label == name then
local craft = craftables[1].request(count)
local craft = craftable.request(count)

while craft.isComputing() == true do
os.sleep(1)
Expand All @@ -29,13 +78,53 @@ function AE2.requestItem(name, threshold, count)
else
return table.unpack({true, "Requested " .. name .. " x " .. count})
end
end
end
return table.unpack({false, name .. " is not craftable!"})
end

-- Native fluid maintenance via getFluidInNetwork (GTNH 2.9+).
-- `name` is the fluid craftable label; `fluidName` is the fluid registry name and is
-- auto-detected from the craftable's stack if omitted (pass it only as an override).
function AE2.requestFluid(name, threshold, count, fluidName)
local craftable = getCraftableForItem(name)

if craftable then
if threshold ~= nil then
if not fluidName then
local cached = fluidNameCache[name]
if cached == nil then
local stack = (craftable.getStack or craftable.getItemStack)(craftable)
cached = (stack and stack.name) or false
fluidNameCache[name] = cached
end
if cached then fluidName = cached end
end

if fluidName then
local fluidInSystem = ME.getFluidInNetwork(fluidName)
local amount = fluidInSystem and (fluidInSystem.size or fluidInSystem.amount)
if amount and amount >= threshold then
return table.unpack({false, "The amount of " .. (fluidInSystem.label or name) .. " (" .. amount .. " mB) meets or exceeds threshold (" .. threshold .. " mB)! Aborting request."})
end
end
end

local craft = craftable.request(count)

while craft.isComputing() == true do
os.sleep(1)
end
if craft.hasFailed() then
return table.unpack({false, "Failed to request " .. name .. " x " .. count .. " mB"})
else
return table.unpack({true, "Requested " .. name .. " x " .. count .. " mB"})
end
end
return table.unpack({false, name .. " is not craftable!"})
end

function AE2.checkIfCrafting()
function AE2.checkIfCrafting()
local cpus = ME.getCpus()
local items = {}
for k, v in pairs(cpus) do
Expand All @@ -48,4 +137,16 @@ function AE2.checkIfCrafting()
return items
end

return AE2
-- Returns true if the ME interface exposes the GTNH 2.9+ native fluid API.
function AE2.hasFluidSupport()
return ME.getFluidInNetwork ~= nil
end

-- Function to manually clear the cache if needed
function AE2.clearCache()
itemCache = {}
fluidNameCache = {}
cacheTimestamp = 0
end

return AE2
1 change: 0 additions & 1 deletion src/Utility.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ function dump(o, depth)
else
return tostring(o)
end

end

function parser(string)
Expand Down