Last active
July 20, 2024 02:09
-
-
Save kawashirov/da4bb7eb4b7ce7560447e22f8cd79998 to your computer and use it in GitHub Desktop.
CC AE2 Autocraft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local pretty = require "cc.pretty" | |
local expect = require "cc.expect" | |
local LogManager = require "LogManager" | |
local basalt = require("basalt") | |
local log = LogManager.new("autocraft", 1024 * 1024, 5, false) -- 1MB, 5 файлов истории | |
log:print("Log open!") | |
local SIMPLIFY = { | |
["minecraft"] = "MC", | |
["modern_industrialization"] = "MI", | |
["techreborn"] = "TR", | |
} | |
-- -- -- "статические" функции | |
local function simplify_ns(s) | |
-- modern_industrialization:very_fucking_long_name -> MI:very_fucking_long_name | |
expect.expect(1, s, "string") | |
local simplified = s | |
for simplify_key, simplify_val in pairs(SIMPLIFY) do | |
simplified = string.gsub(simplified, simplify_key, simplify_val) | |
end | |
return simplified | |
end | |
function simplify_num(number) | |
if not number then | |
return tostring(number) | |
elseif number >= 1.5e8 then | |
return string.format("%.1e", number) | |
elseif number >= 1e6 then | |
return string.format("%.3gM", number / 1e6) | |
elseif number >= 1e3 then | |
return string.format("%.3gK", number / 1e3) | |
else | |
return tostring(number) | |
end | |
end | |
local function iv2kv(raw_t, key_k, value_k) | |
-- index-value table -> key-value table | |
expect.expect(1, raw_t, "table") | |
expect.expect(2, key_k, "string") | |
expect.expect(3, value_k, "string") | |
local new_t = {} | |
for _, item in ipairs(raw_t) do | |
key = item[key_k] | |
assert(type(key) ~= "nil") | |
value = item[value_k] | |
new_t[key] = value | |
end | |
return new_t | |
end | |
local function kv2iv(raw_t) | |
-- index-value table -> key-value table | |
expect.expect(1, raw_t, "table") | |
local new_t = {} | |
for _, item in pairs(raw_t) do | |
table.insert(new_t, item) | |
end | |
return new_t | |
end | |
local function pop(t) | |
expect.expect(1, t, "table") | |
for k, v in pairs(t) do | |
t[k] = nil | |
return k, v | |
end | |
end | |
local function count_pairs(t) | |
expect.expect(1, t, "table") | |
-- count pairs in key-table | |
local count = 0 | |
for key, value in pairs(t) do | |
count = count + 1 | |
end | |
return count | |
end | |
-- -- -- рабочие контекстно-зависимые функции и переменные | |
local config = nil | |
local rules = {} -- правила как ключ-значение | |
local sorted_rules = {} -- правила как индекс-значение, отсортированные | |
local rules_count = 0 | |
local rules_satisfied = 0 | |
local monitor = nil | |
local monitor_frame = nil | |
local monitor_list = nil | |
local monitor_status = nil | |
local ae2upw = nil -- переферал AE2 Unlimited Peripheral Works | |
local ae2cc = nil -- переферал AE2 CC Bridge | |
local all_objects = {} -- все предметы в AE2 как ключ-значение | |
local all_objects_c = 0 | |
local all_cpus = {} -- все профессоры в AE2 | |
local busy_player_cpus_c = 0 | |
local busy_cpus_c = 0 | |
local free_cpus_c = 0 | |
local all_cpus_c = 0 | |
local usage_cpus = 0 | |
local can_craft = false | |
local active_craftings = {} | |
local function update_objects() | |
all_objects = {} | |
all_objects_c = 0 | |
for _, item in pairs(ae2upw.items()) do | |
local name_id = item.technicalName | |
all_objects[name_id] = { | |
type = "item", name = name_id, amount = item.count | |
} | |
all_objects_c = all_objects_c + 1 | |
end | |
for _, fluid in pairs(ae2upw.tanks()) do | |
local name_id = fluid.name | |
all_objects[name_id] = { | |
type = "fluid", name = name_id, amount = fluid.amount | |
} | |
all_objects_c = all_objects_c + 1 | |
end | |
end | |
local function update_cpus() | |
all_cpus = ae2upw.getCraftingCPUs() | |
busy_player_cpus_c, busy_cpus_c, free_cpus_c = 0, 0, 0 | |
for _, cpu in pairs(all_cpus) do | |
if cpu.storage > 65536 then | |
-- player only 256K cpu | |
if cpu.isBusy then | |
busy_player_cpus_c = busy_player_cpus_c + 1 | |
end | |
else | |
-- auto carfting 64K cpu | |
if cpu.isBusy then | |
busy_cpus_c = busy_cpus_c + 1 | |
else | |
free_cpus_c = free_cpus_c + 1 | |
end | |
end | |
end | |
all_cpus_c = busy_cpus_c + free_cpus_c | |
usage_cpus = busy_cpus_c / all_cpus_c | |
local allowed_usage = busy_player_cpus_c > 0 and 4/10 or 9/10 | |
can_craft = usage_cpus < allowed_usage and free_cpus_c > 1 | |
end | |
local function get_or_alloc_rule(name, rule_type) | |
expect.expect(1, name, "string") | |
expect.expect(2, rule_type, "string") | |
local rule = rules[name] | |
if not rule then | |
rule = { | |
name = name, | |
type = rule_type, | |
parents = { }, | |
parents_n = 0, | |
scale = config.default_scale, | |
priority = 0 | |
} | |
rules[name] = rule | |
end | |
return rule | |
end | |
local function get_or_alloc_rule_from_upw_raw_table(upw_raw_table) | |
expect.expect(1, upw_raw_table, "table") | |
if upw_raw_table.type == "item" then | |
return get_or_alloc_rule(upw_raw_table.technicalName, "item") | |
elseif upw_raw_table.type == "fluid" then | |
return get_or_alloc_rule(upw_raw_table.name, "item") | |
else | |
error("Unknown type: " .. tostring(upw_raw_table.type)) | |
end | |
end | |
local function normalize_fluid_amount(amount) | |
expect.expect(1, amount, "number") | |
-- нормализация mB к слиткам. | |
-- 1000mB = 1B = = 1 блок = 9 слитков | |
return amount * (9 / 1000) | |
end | |
local function denormalize_fluid_amount(amount) | |
expect.expect(1, amount, "number") | |
-- 1000mB = 1B = = 1 блок = 9 слитков | |
return amount * (1000 / 9) | |
end | |
local function max_of_variants(parent_variants) | |
expect.expect(1, parent_variants, "table", "nil") | |
if not parent_variants then return 0 end | |
local max = -math.huge | |
for _, parent_variant in pairs(parent_variants) do | |
local parent_rule = rules[parent_variant] | |
local object = all_objects[parent_rule.name] | |
local amount = (object and object.amount) or 0 | |
if parent_rule.type == "fluid" then | |
amount = normalize_fluid_amount(amount) | |
end | |
if amount > max then | |
max = amount | |
end | |
end | |
return max | |
end | |
local function min_of_parents(parents) | |
expect.expect(1, parents, "table", "nil") | |
if not parents then return 0 end | |
local min = math.huge | |
for _, parent in pairs(parents) do | |
local amount = 0 | |
if type(parent) == "string" then -- это ссылка на правило | |
local parent_rule = rules[parent] | |
local object = all_objects[parent_rule.name] | |
amount = (object and object.amount or 0) or 0 | |
if parent_rule.type == "fluid" then | |
amount = normalize_fluid_amount(amount) | |
end | |
elseif type(parent) == "table" then -- это варианты | |
amount = max_of_variants(parent) | |
else | |
error("Unknown parent type: " .. type(parent)) | |
end | |
if amount < min then | |
min = amount | |
end | |
end | |
return min | |
end | |
local function build_rules_phase1_init(queue) | |
expect.expect(1, queue, "table") | |
for name, base_rule in pairs(config.rules) do | |
local rule_type = base_rule.type or "item" | |
local rule = get_or_alloc_rule(name, rule_type) | |
for opt_key, opt_val in pairs(base_rule) do | |
rule[opt_key] = opt_val | |
end | |
rule.explicit_scale = base_rule.scale | |
rule.explicit_weight = base_rule.weight | |
table.insert(queue, rule) | |
end | |
end | |
local function build_rules_phase1_entry(queue, rule) | |
expect.expect(1, queue, "table") | |
expect.expect(2, rule, "table") | |
rule.parents = {} | |
rule.parents_n = 0 | |
-- log:print(pretty.pretty(rule)) | |
-- local datas_raw = ae2upw.getPatternsFor(rule.type, rule.name) | |
local status, result = pcall(function () return ae2upw.getPatternsFor(rule.type, rule.name) end) | |
if not status then | |
log:only(("getPatternsFor failed: %s %s"):format(rule.name, result)) | |
-- bug workaround | |
local dummy = "kawashirov:dummy" | |
get_or_alloc_rule(dummy, "item") | |
rule.parents = { [1] = dummy } | |
rule.parents_n = 1 | |
rule.exact = rule.type == "item" and 32 or 1 | |
return | |
end | |
datas_raw = result | |
if #datas_raw > 1 then | |
log:error(("Two ways found for: %s"):format(rule.name)) | |
return | |
elseif #datas_raw < 1 then | |
-- Материал не крафтится, это ОК | |
return | |
end | |
local data_raw = datas_raw[1] | |
local parents = {} | |
for _, data_raw_in in pairs(data_raw.inputs) do | |
if data_raw_in.variants then | |
-- log:only((" Inputs variants: %s <- %s %s %s"):format( | |
-- rule.name, data_raw_in.name, data_raw_in.type, pretty.pretty(data_raw_in) | |
-- )) | |
local variants = {} | |
for _, data_raw_in_var in pairs(data_raw_in.variants) do | |
local parent_rule = get_or_alloc_rule_from_upw_raw_table(data_raw_in_var) | |
table.insert(queue, parent_rule) | |
table.insert(variants, name) | |
end | |
table.insert(parents, variants) | |
else | |
local parent_rule = get_or_alloc_rule_from_upw_raw_table(data_raw_in) | |
table.insert(queue, parent_rule) | |
parents[parent_rule.name] = parent_rule.name | |
end | |
end | |
parents = kv2iv(parents) | |
rule.parents = parents | |
rule.parents_n = #parents | |
-- log:only("Inputs for ", rule.name) | |
-- log:only(" ", pretty.render(pretty.pretty(parents))) | |
end | |
local function build_rules_phase2_parent(queue, parent, try_scale, try_priority) | |
expect.expect(1, queue, "table") | |
expect.expect(2, parent, "table", "string", "nil") | |
expect.expect(3, try_scale, "number") | |
expect.expect(4, try_priority, "number") | |
local parent_type = type(parent) | |
if parent_type == "string" then | |
local rule = rules[parent] | |
local old_scale = rule.scale | |
local new_scale = rule.explicit_scale or math.max(old_scale or 0, try_scale) | |
if old_scale ~= new_scale then | |
rule.scale = new_scale | |
queue[rule.name] = true | |
end | |
local old_priority = rule.priority | |
local new_priority = rule.explicit_priority or math.max(old_priority or 0, try_priority) | |
if old_priority ~= new_priority then | |
rule.priority = new_priority | |
queue[rule.name] = true | |
end | |
elseif parent_type == "table" then | |
for _, sub_parent in pairs(parent) do | |
build_rules_phase2_parent(queue, sub_parent, try_scale, try_priority * 1.05) | |
end | |
else | |
error(("Invalid parent on phase 3: %s, %s"):format(parent_type, parent)) | |
end | |
return changed | |
end | |
local function build_rules() | |
-- посроение дерева рецептов | |
local start_time = os.clock() | |
rules = {} -- сброс | |
log:print(("Rebuilding rules, phase 1... (Base rules: %d)"):format(count_pairs(config.rules))) | |
-- Фаза 1: создание записей | |
local queue1 = {} | |
build_rules_phase1_init(queue1) | |
local visited = {} | |
local i1, t1 = 0, os.clock() | |
while #queue1 > 0 do | |
i1 = i1 + 1 | |
if i1 % 100 == 0 then sleep(0.001) end -- yield | |
local clock = os.clock() | |
if t1 + 3 < clock then | |
t1 = clock | |
log:print(("Phase 1... %d loops, %d rules"):format(i1, count_pairs(rules))) | |
end | |
local queue_entry = table.remove(queue1) | |
if not visited[queue_entry.name] then | |
build_rules_phase1_entry(queue1, queue_entry) | |
visited[queue_entry.name] = true | |
end | |
end | |
log:print(("Phase 1 done in %d loops. Phase 2..."):format(i1)) | |
-- Фаза 2: обновление динамик параметров scale и priority | |
local queue2 = {} | |
rules_c = 0 | |
for _, rule in pairs(rules) do | |
rules_c = rules_c + 1 | |
queue2[rule.name] = true | |
end | |
local i2 = 0 | |
while count_pairs(queue2) > 0 do | |
i2 = i2 + 1 | |
if i2 % 100 == 0 then sleep(0.001) end -- yield | |
local queue_entry, _ = pop(queue2) | |
local rule = rules[queue_entry] | |
queue2[queue_entry] = nil | |
build_rules_phase2_parent(queue2, rule.parents, rule.scale, rule.priority or 0) | |
end | |
local work_time = os.clock() - start_time | |
log:print(("Phase 2 done in %d loops. Rebuilt %d rules, took %fs"):format(i2, rules_c, work_time)) | |
end | |
local function select_rule_single(rule) | |
rule.need_amount = 0 | |
rule.dyn_weight = -math.huge | |
rule.dyn_priority = -math.huge | |
rule.goal_amount = 0 | |
rule.current_amount = 0 | |
rule.parent_amount = 0 | |
local has_job = active_craftings[rule.name] | |
assert(rule.parents_n) | |
assert(rule.scale) | |
if has_job or rule.parents_n < 1 or rule.scale < 0 then | |
-- Задача уже запущена, оно не крафтится или отключено | |
-- log:only(("Selector %s: job=%s, parents=%d, scale=%d, priority=%.1f"):format( | |
-- rule.name, has_job, rule.parents_n, rule.scale, rule.priority | |
-- )) | |
return false | |
end | |
rule.object = all_objects[rule.name] | |
rule.current_amount = (rule.object and rule.object.amount) or 0 | |
rule.parent_amount = min_of_parents(rule.parents) | |
if rule.type == "fluid" then | |
-- parent_amount всегда в формате вещей, | |
-- для корректного расчета необходимого mB приводим 9 вещей в 1B | |
rule.parent_amount = denormalize_fluid_amount(rule.parent_amount) | |
end | |
if rule.exact then | |
rule.goal_amount = rule.exact | |
rule.min = rule.exact | |
rule.max = rule.exact | |
else | |
rule.goal_amount = math.max(math.floor(rule.parent_amount * rule.scale), 1) | |
if rule.min then rule.goal_amount = math.max(rule.goal_amount, rule.min) end | |
if rule.max then rule.goal_amount = math.min(rule.goal_amount, rule.max) end | |
end | |
assert(rule.current_amount) | |
assert(rule.goal_amount) | |
if rule.goal_amount <= 0 or rule.current_amount >= rule.goal_amount then | |
-- У нас уже есть нужное кол-во | |
-- log:only(("Selector %s: current=%d > goal=%d, scale=%.1f, priority=%.1f"):format( | |
-- rule.name, rule.current_amount, rule.goal_amount, rule.scale, rule.priority | |
-- )) | |
return false | |
end | |
rule.need_amount = math.max(math.floor(rule.goal_amount - rule.current_amount), 1) | |
local emptyness = rule.need_amount / rule.goal_amount | |
emptyness = math.max(0, math.min(1, emptyness)) | |
rule.dyn_weight = emptyness * rule.need_amount * (rule.explicit_weight or 1) | |
rule.dyn_priority = rule.priority | |
assert(rule.dyn_weight) | |
assert(rule.dyn_priority) | |
-- log:only(("Selector %s: current=%d + need=%d = goal=%d, scale=%.1f, dyn_weight=%.1f, dyn_priority=%.1f"):format( | |
-- rule.name, rule.current_amount, rule.need_amount, rule.goal_amount, rule.scale, rule.dyn_weight, rule.dyn_priority | |
-- )) | |
return true | |
end | |
local function select_rule() | |
-- выбрать самое неудовлетворенное правило | |
sorted_rules = {} | |
for _, rule in pairs(rules) do | |
select_rule_single(rule) | |
if select_rule_single(rule) then | |
table.insert(sorted_rules, rule) | |
end | |
end | |
local function compare(a, b) | |
if a.dyn_priority ~= b.dyn_priority then | |
return a.dyn_priority > b.dyn_priority | |
else | |
return a.dyn_weight > b.dyn_weight | |
end | |
end | |
table.sort(sorted_rules, compare) | |
return sorted_rules[1] | |
end | |
local function monitor_positions(width, height) | |
monitor_status:setPosition(1, 1):setSize(width, 1):show() | |
monitor_header:setPosition(1, 2):setSize(width, 1):show() | |
monitor_list:setPosition(1, 3):setSize(width, height-2):show() | |
end | |
local function monitor_init() | |
monitor = peripheral.find("monitor") | |
monitor_frame = basalt.addMonitor() | |
monitor_frame:setMonitor(monitor) | |
monitor_status = monitor_frame:addLabel():setText("Status") | |
monitor_header = monitor_frame:addLabel():setText("Header") | |
monitor_list = monitor_frame:addList() | |
local width, height = monitor.getSize() | |
monitor_positions(width, height) | |
basalt.update("amogus") | |
end | |
local function monitor_update() | |
monitor_frame:setMonitor(monitor) | |
local width, height = monitor.getSize() | |
local sr = ("Rules: %d base, %d total"):format(count_pairs(config.rules), rules_c) | |
local sc = ("CPUs: %d / %d"):format(busy_cpus_c, all_cpus_c) | |
local sj = ("Jobs: %d"):format(count_pairs(active_craftings)) | |
monitor_status:setText(("%s; %s; %s"):format(sr, sc, sj)) | |
monitor_header:setText(" # Prio Scale Curr Target Need Name") | |
monitor_list:clear() | |
for i, rule in ipairs(sorted_rules) do | |
if i >= height then break end | |
-- local priority = ("%.3f"):format(rule.priority):sub(3) | |
-- local scale = ("%.2f"):format(rule.scale):sub(3) | |
local priority = ("%4.1f"):format(rule.priority) | |
local scale = ("%.2f"):format(rule.scale):sub(3) | |
local item = ("%2d %5s %3s %5s %5s %5s %s"):format( | |
i, priority, scale, | |
simplify_num(rule.current_amount), simplify_num(rule.goal_amount), simplify_num(rule.need_amount), | |
simplify_ns(rule.name) | |
) | |
monitor_list:addItem(item) | |
end | |
monitor_positions(width, height) | |
basalt.update("amogus") | |
end | |
local function sync_jobs() | |
log:print("Syncing jobs...") | |
active_craftings = {} | |
for _, ac in pairs(ae2upw.getActiveCraftings()) do | |
if ac and ac.target then | |
if ac.target.type == "fluid" then | |
active_craftings[ac.target.name] = true | |
elseif ac.target.type == "item" then | |
active_craftings[ac.target.technicalName] = true | |
end | |
end | |
end | |
log:print(("Synced %d jobs!"):format(count_pairs(active_craftings))) | |
end | |
local function main_loop_inner() | |
-- term.clear() | |
-- term.setCursorPos(1, 1) | |
-- log:print(" ") | |
update_cpus() | |
update_objects() | |
sync_jobs() | |
local rule = select_rule() | |
monitor_update() | |
if not (rule and can_craft) then | |
log:print(("All %d rules satisfied or crafting!"):format(count_pairs(rules))) | |
-- Если правилла удовлетворены 10 раз подряд, то ливаем из цикла и перестраиваем правила. | |
-- Иначе считаем и ждем 5сек | |
if rules_satisfied > 10 then | |
return nil | |
else | |
rules_satisfied = rules_satisfied + 1 | |
sync_jobs() | |
return 1 | |
end | |
end | |
-- Но если какое-то правило не было удовлетварено, то сбрасываем счетчик. | |
rules_satisfied = 0 | |
assert(rule.current_amount) | |
assert(rule.goal_amount) | |
assert(rule.need_amount) | |
assert(rule.scale) | |
assert(rule.dyn_weight) | |
assert(rule.dyn_priority) | |
local name_simple = simplify_ns(rule.name) | |
log:print(" ") | |
log:print(("Have %d, goal %d, need %d of %s (s=%.1f, w=%.1f p=%.1f). "):format( | |
rule.current_amount, rule.goal_amount, rule.need_amount, name_simple, rule.scale, rule.dyn_weight, rule.dyn_priority | |
)) | |
local request_amount = rule.need_amount | |
request_amount = math.max(math.floor(request_amount * config.request_scale), 1) | |
-- Заказы не более request_limit | |
local request_limit = math.min(config.request_limit, (rule.request_limit or math.huge)) | |
request_amount = math.min(request_amount, request_limit) | |
-- workaround т.к. почему-то запланированный крафт появляется с пингом. | |
-- sleep(0.001) -- yield прежде чем таки запланируем задачу | |
sync_jobs() | |
if active_craftings[rule.name] then | |
log:error(("Already crafting %s!"):format(name_simple)) | |
return 0 --.001 -- избегаем возможных дубликатов работы | |
end | |
log:print(("Scheduling %d of %s..."):format(request_amount, name_simple)) | |
-- ae2upw.getEnergy() -- syncronize call to main thread? | |
local result, reason = ae2cc.scheduleCrafting(rule.type, rule.name, request_amount) | |
active_craftings[rule.name] = true | |
if not result then | |
log:print(("Result: %s %s"):format(result, reason)) | |
end | |
return 0 | |
end | |
local function main_loop_outer() | |
log:print(" ") | |
config = dofile("config.lua") | |
log:print("Reconnecting AE2...") | |
ae2upw = peripheral.find("ae2:energy_cell") | |
ae2cc = peripheral.find("ae2cc_adapter") | |
monitor = peripheral.find("monitor") | |
sleep(0.5) | |
update_cpus() | |
log:print(("CPUs: %d busy and %d free of %d, %d player's"):format( | |
busy_cpus_c, free_cpus_c, all_cpus_c, busy_player_cpus_c | |
)) | |
sync_jobs() | |
if not can_craft then | |
log:print("Can not craft!") | |
return math.max(10, busy_player_cpus_c * 10) | |
end | |
build_rules() | |
local loops = 0 | |
while true do | |
loops = loops + 1 | |
result = main_loop_inner() | |
if not result then | |
local s = 10 + math.max(0, 20 - loops) | |
return s | |
elseif result > 0 then | |
log:print("Sleep inner: ", result) | |
sleep(result) | |
end | |
end | |
end | |
local function main_loop() | |
monitor_init() | |
while true do | |
-- local status, result = pcall(main_loop) | |
local status, result = true, main_loop_outer() | |
if status then | |
log:print("Sleep outer: ", result) | |
sleep(result) | |
else | |
log:error("Failure:", result) | |
log:print("Sleep outer: ", 10) | |
sleep(10) | |
end | |
end | |
end | |
log:print("Starting loops...") | |
main_loop() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
return { | |
default_scale = 0.25, | |
request_scale = 0.1, | |
request_limit = 64 * 8, | |
rules = { | |
["minecraft:paper"] = { priority = 50 }, | |
["minecraft:magma_cream"] = { }, | |
["minecraft:blaze_powder"] = { }, | |
["minecraft:slime_ball"] = { priority = 50 }, | |
["minecraft:birch_planks"] = { priority = 50 }, | |
["minecraft:blue_ice"] = { }, | |
-- -- -- | |
["modern_industrialization:coal_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:diamond_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:emerald_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:lapis_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:redstone_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:quartz_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:ligite_coal_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:bauxite_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:salt_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:chromium_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:manganese_crushed_dust"] = { exact = 0 }, | |
["modern_industrialization:monazite_crushed_dust"] = { exact = 0 }, | |
-- -- -- | |
["minecraft:white_dye"] = { exact = 64 }, | |
["minecraft:light_gray_dye"] = { exact = 64 }, | |
["minecraft:gray_dye"] = { exact = 64 }, | |
["minecraft:black_dye"] = { exact = 64 }, | |
["minecraft:brown_dye"] = { exact = 64 }, | |
["minecraft:red_dye"] = { exact = 64 }, | |
["minecraft:orange_dye"] = { exact = 64 }, | |
["minecraft:yellow_dye"] = { exact = 64 }, | |
["minecraft:lime_dye"] = { exact = 64 }, | |
["minecraft:green_dye"] = { exact = 64 }, | |
["minecraft:cyan_dye"] = { exact = 64 }, | |
["minecraft:light_blue_dye"] = { exact = 64 }, | |
["minecraft:blue_dye"] = { exact = 64 }, | |
["minecraft:purple_dye"] = { exact = 64 }, | |
["minecraft:magenta_dye"] = { exact = 64 }, | |
["minecraft:pink_dye"] = { exact = 64 }, | |
-- -- -- | |
["modern_industrialization:lignite_coal_block"] = { priority = 70 }, | |
["minecraft:coal_block"] = { priority = 40 }, | |
["minecraft:iron_ingot"] = { }, | |
["minecraft:gold_ingot"] = { }, | |
["minecraft:copper_ingot"] = { }, | |
["minecraft:redstone"] = { }, | |
["minecraft:lapis_lazuli"] = { }, | |
["minecraft:emerald"] = { }, | |
["minecraft:diamond"] = { }, | |
["minecraft:gravel"] = { }, | |
["minecraft:sand"] = { priority = 50 }, | |
["minecraft:glass"] = { }, | |
["minecraft:tinted_glass"] = { }, | |
-- -- -- | |
["techreborn:chrome_dust"] = { scale = 50 }, | |
["techreborn:titanium_dust"] = { scale = 50 }, | |
["techreborn:platinum_dust"] = { scale = 50 }, | |
-- -- -- | |
["ae2:calculation_processor"] = { min = 64 }, | |
["ae2:logic_processor"] = { min = 32 }, | |
["ae2:engineering_processor"] = { min = 16 }, | |
["ae2:quartz_fiber"] = { min = 64, priority = 20 }, | |
["ae2:fluix_glass_cable"] = { min = 64, priority = 20 }, | |
["ae2:me_chest"] = { min = 1 }, | |
["ae2:cell_component_64k"] = { min = 1 }, | |
["ae2:basic_card"] = { min = 32, priority = 97 }, | |
["ae2:advanced_card"] = { min = 32, priority = 90 }, | |
["megacells:accumulation_processor"] = { min = 8 }, | |
-- -- -- | |
["techreborn:steel_ingot"] = { }, | |
["modern_industrialization:stainless_steel_ingot"] = { }, | |
["modern_industrialization:turbo_machine_hull"] = { min = 8 }, | |
["modern_industrialization:basic_upgrade"] = { min = 64 }, | |
["modern_industrialization:advanced_upgrade"] = { min = 32 }, | |
["modern_industrialization:turbo_upgrade"] = { min = 16 }, | |
["modern_industrialization:highly_advanced_upgrade"] = { min = 8 }, | |
["modern_industrialization:boosted_diesel"] = { type = "fluid", priority = 80, exact = 60 * 1000, scale = 0.01 }, | |
["modern_industrialization:bronze_drill"] = { priority = 100, exact = 16 }, | |
["modern_industrialization:steel_drill"] = { priority = 99, exact = 16 }, | |
["modern_industrialization:gold_drill"] = { priority = 98, exact = 16 }, | |
["modern_industrialization:aluminum_drill"] = { priority = 97, exact = 16 }, | |
["modern_industrialization:stainless_steel_drill"] = { priority = 96, exact = 16 }, | |
["modern_industrialization:titanium_drill"] = { priority = 95, exact = 16 }, | |
["modern_industrialization:uranium_fuel_rod"] = { priority = 110, exact = 64 }, | |
-- workarounds | |
["modern_industrialization:chlorine"] = { type = "fluid", exact = 128 * 1000 }, | |
["modern_industrialization:chloroform"] = { type = "fluid", exact = 64 * 1000 }, | |
["modern_industrialization:tetrafluoroethylene"] = { type = "fluid", exact = 32 * 1000 }, | |
["modern_industrialization:polytetrafluoroethylene"] = { type = "fluid", exact = 16 * 1000 }, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local expect = require "cc.expect" | |
local LogManager = {} | |
LogManager.__index = LogManager | |
function LogManager.new(baseName, maxFileSize, maxFiles) | |
local self = setmetatable({}, LogManager) | |
self.baseName = baseName | |
self.maxFileSize = maxFileSize | |
self.maxFiles = maxFiles | |
self.currentFile = 1 | |
self:rotateFiles() | |
return self | |
end | |
function LogManager:rotateFiles() | |
if self.currentFileHandle then | |
self.currentFileHandle:close() | |
self.currentFileHandle = nil | |
end | |
for i = self.maxFiles, 1, -1 do | |
local oldName = self.baseName .. "." .. i .. ".log" | |
if i == self.maxFiles then | |
if fs.exists(oldName) then | |
fs.delete(oldName) | |
end | |
else | |
if fs.exists(oldName) then | |
local newName = self.baseName .. "." .. (i + 1) .. ".log" | |
fs.move(oldName, newName) | |
end | |
end | |
end | |
self.currentFileName = self.baseName .. ".1" .. ".log" | |
self.currentFileHandle = io.open(self.currentFileName, "w") | |
end | |
function LogManager:checkFileSize() | |
expect.field(self, "currentFileHandle", "table") | |
local currentSize = self.currentFileHandle:seek("end") or 0 | |
assert(type(currentSize) == "number", type(currentSize) .. " " .. tostring(currentSize)) | |
expect.field(self, "maxFileSize", "number") | |
if currentSize >= self.maxFileSize then | |
self:rotateFiles() | |
else | |
self.currentFileHandle:seek("set") | |
end | |
end | |
function LogManager:manualRotate() | |
self:rotateFiles() | |
end | |
function LogManager:log(message) | |
self:checkFileSize() | |
self.currentFileHandle:write("[" .. os.date("%y-%m-%d %H:%M:%S") .. "] " .. message .. "\n") | |
self.currentFileHandle:flush() | |
end | |
function LogManager:print(...) | |
local args = {...} | |
for i, v in ipairs(args) do | |
args[i] = tostring(v) | |
end | |
local message = table.concat(args, " ") | |
print(message) | |
self:log(message) | |
end | |
function LogManager:error(...) | |
local args = {...} | |
for i, v in ipairs(args) do | |
args[i] = tostring(v) | |
end | |
local message = table.concat(args, " ") | |
printError(message) | |
self:log(message) | |
end | |
function LogManager:only(...) | |
local args = {...} | |
for i, v in ipairs(args) do | |
args[i] = tostring(v) | |
end | |
local message = table.concat(args, " ") | |
self:log(message) | |
end | |
return LogManager |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment