Last active
June 29, 2024 05:20
-
-
Save kawashirov/da4bb7eb4b7ce7560447e22f8cd79998 to your computer and use it in GitHub Desktop.
CC AE2 Autocraft
This file contains 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 log = LogManager.new("autocraft", 1024 * 1024, 5) -- 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 | |
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 rules_count = 0 | |
local rules_satisfied = 0 | |
local ae2cc = nil -- переферал AE2CC | |
local ae2upw = nil -- переферал AE2 Unlimited Peripheral Works | |
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 jobs_by_uuid = {} | |
local jobs_by_object = {} | |
local function update_objects(raw_t) | |
-- index-value table -> key-value table | |
all_objects = {} | |
all_objects_c = 0 | |
raw_objects = ae2cc.getAvailableObjects() | |
for _, item in pairs(raw_objects) do | |
all_objects[item.id] = item | |
all_objects_c = all_objects_c + 1 | |
end | |
end | |
local function update_cpus() | |
all_cpus = ae2cc.getCraftingCPUs() | |
busy_player_cpus_c, busy_cpus_c, free_cpus_c = 0, 0, 0 | |
for _, cpu in pairs(all_cpus) do | |
if cpu.selectionMode == "PLAYER_ONLY" then | |
if cpu.jobStatus then | |
busy_player_cpus_c = busy_player_cpus_c + 1 | |
end | |
else | |
if cpu.jobStatus 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 | |
if busy == 0 then | |
-- Если все процессоры не загружены, значит мы точно знаем, | |
-- что все задачи выполнены и можно сбросить таймеры | |
jobs_by_uuid = {} | |
jobs_by_object = {} | |
end | |
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 = {} } | |
rules[name] = rule | |
rule.scale = config.default_scale | |
rule.priority = 0 | |
end | |
return rule | |
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") | |
local datas = ae2upw.getPatternsFor(rule.type, rule.name) | |
if #datas > 1 then | |
log:error(("Two ways found for: %s"):format(rule.name)) | |
return | |
elseif #datas < 1 then | |
-- Материал не крафтится, это ОК | |
rule.parents = {} | |
rule.parents_n = 0 | |
return | |
end | |
local data = datas[1] | |
local parents = {} | |
for _, data_in in pairs(data.inputs) do | |
if data_in.variants then | |
-- log:only((" Inputs variants: %s <- %s %s %s"):format( | |
-- rule.name, data_in.name, data_in.type, pretty.pretty(data_in) | |
-- )) | |
local variants = {} | |
for _, data_in_var in pairs(data_in.variants) do | |
local parent_rule = get_or_alloc_rule(data_in_var.name, data_in_var.type) | |
table.insert(queue, parent_rule) | |
table.insert(variants, data_in_var.name) | |
end | |
table.insert(parents, variants) | |
else | |
local parent_rule = get_or_alloc_rule(data_in.name, data_in.type) | |
table.insert(queue, parent_rule) | |
parents[data_in.name] = data_in.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 * 0.95) | |
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) | |
local has_job = jobs_by_object[rule.name] and true or false | |
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 -math.huge, -math.huge | |
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 = rule.parent_amount * rule.scale | |
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 | |
if 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 -math.huge, -math.huge | |
end | |
rule.need_amount = rule.goal_amount - rule.current_amount | |
rule.need_amount = math.max(math.floor(rule.need_amount), 1) | |
local weight = rule.need_amount * (rule.explicit_weight or 1) | |
rule.weight = weight | |
assert(rule.priority) | |
log:only(("Selector %s: current=%d + need=%d = goal=%d, scale=%.1f, priority=%.1f"):format( | |
rule.name, rule.current_amount, rule.need_amount, rule.goal_amount, rule.scale, rule.priority | |
)) | |
return weight, rule.priority | |
end | |
local function select_rule() | |
-- выбрать самое неудовлетворенное правило | |
local sel_rule, sel_weight, sel_priority = nil, -math.huge, -math.huge | |
for _, rule in pairs(rules) do | |
local weight, priority = select_rule_single(rule) | |
if priority > sel_priority or (priority == sel_priority and weight > sel_weight) then | |
log:only(("Pick new rule: %s, w=%.1f, p=%.1f"):format(rule.name, weight, priority)) | |
sel_rule, sel_weight, sel_priority = rule, weight, priority | |
end | |
end | |
return sel_rule, sel_weight, sel_priority | |
end | |
local function count_jobs() | |
return count_pairs(jobs_by_uuid), count_pairs(jobs_by_object) | |
end | |
local function add_job(object_name, job_uuid) | |
expect.expect(1, object_name, "string") | |
expect.expect(2, job_uuid, "string") | |
local c_b_uuid, c_b_object = count_jobs() | |
if object_name and job_uuid then | |
jobs_by_uuid[job_uuid] = object_name | |
jobs_by_object[object_name] = job_uuid | |
end | |
local c_a_uuid, c_a_object = count_jobs() | |
local message = ("Added job %s %s, %d %d -> %d %d"):format(job_uuid, object_name, c_b_uuid, c_b_object, c_a_uuid, c_a_object) | |
if c_a_uuid == c_a_object and c_b_uuid + 1 == c_a_uuid and c_b_object + 1 == c_a_object then | |
log:only(message) | |
else | |
log:error(message) | |
end | |
end | |
local function remove_job_uuid(job_uuid) | |
expect.expect(1, job_uuid, "string") | |
local c_b_uuid, c_b_object = count_jobs() | |
local object_name = jobs_by_uuid[job_uuid] | |
jobs_by_uuid[job_uuid] = nil | |
if object_name then | |
jobs_by_object[object_name] = nil | |
end | |
local c_a_uuid, c_a_object = count_jobs() | |
local message = ("Removed job %s %s, %d %d -> %d %d"):format(job_uuid, object_name, c_b_uuid, c_b_object, c_a_uuid, c_a_object) | |
if c_a_uuid == c_a_object and c_b_uuid == c_a_uuid + 1 and c_b_object == c_a_object + 1 then | |
log:only(message) | |
else | |
log:error(message) | |
end | |
end | |
local function sync_jobs() | |
sleep(0.001) | |
local c_b_uuid, c_b_object = count_jobs() | |
local issued_list = ae2cc.getIssuedCraftingJobs() | |
if #issued_list == 0 then | |
jobs_by_uuid = {} | |
jobs_by_object = {} | |
else | |
local issued_set = {} | |
for _, job in ipairs(issued_list) do | |
issued_set[job.jobID] = true | |
end | |
for job_uuid, object_name in pairs(jobs_by_uuid) do | |
if not issued_set[job_uuid] then | |
jobs_by_uuid[job_uuid] = nil | |
jobs_by_object[object_name] = nil | |
end | |
end | |
for object_name, job_uuid in pairs(jobs_by_object) do | |
if not issued_set[job_uuid] then | |
jobs_by_uuid[job_uuid] = nil | |
jobs_by_object[object_name] = nil | |
end | |
end | |
end | |
local c_a_uuid, c_a_object = count_jobs() | |
local message = ("Synced %d jobs: %d %d -> %d %d"):format(#issued_list, c_b_uuid, c_b_object, c_a_uuid, c_a_object) | |
if c_a_uuid == c_a_object and c_b_uuid >= c_a_uuid and c_b_object >= c_a_object then | |
log:only(message) | |
else | |
log:error(message) | |
end | |
return new_t | |
end | |
local function main_loop_inner() | |
-- term.clear() | |
-- term.setCursorPos(1, 1) | |
-- log:print(" ") | |
update_cpus() | |
if not can_craft then return nil end | |
update_objects() | |
local rule, rule_w, rule_p = select_rule() | |
if not rule 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 | |
return 5 | |
end | |
end | |
-- Но если какое-то правило не было удовлетварено, то сбрасываем счетчик. | |
rules_satisfied = 0 | |
assert(rule.current_amount) | |
assert(rule.goal_amount) | |
assert(rule.need_amount) | |
assert(rule.scale) | |
assert(rule_w) | |
assert(rule_p) | |
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_w, rule_p | |
)) | |
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) | |
sleep(0.001) -- yield прежде чем таки запланируем задачу | |
if not jobs_by_object[rule.name] then | |
-- проверяем, вдруг ивенты нам что-то обновили по работам, | |
-- избегаем возмодных дубликатов работы | |
log:print(("Scheduling %d of %s..."):format(request_amount, name_simple)) | |
local job_id = ae2cc.scheduleCrafting(rule.type, rule.name, request_amount) | |
add_job(rule.name, job_id) | |
log:print(("Result: %s"):format(job_id)) | |
if request_amount == 1 then | |
return 0.001 -- без задержек, когда быстрые рецепты | |
else | |
return 1 | |
end | |
else | |
return 5 | |
end | |
end | |
local function main_loop_outer() | |
log:print(" ") | |
config = dofile("config.lua") | |
log:print("Reconnecting AE2...") | |
ae2cc = peripheral.find("ae2cc_adapter") | |
ae2upw = peripheral.find("ae2:energy_cell") | |
sleep(1) | |
update_cpus() | |
sync_jobs() | |
local c_b_uuid, c_b_object = count_jobs() | |
log:print(("CPUs: %d busy and %d free of %d, %d player's; Jobs: %d ~ %d"):format( | |
busy_cpus_c, free_cpus_c, all_cpus_c, busy_player_cpus_c, c_b_uuid, c_b_object | |
)) | |
if not can_craft then | |
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 result then | |
sleep(result) | |
else | |
return 10 + math.max(0, 20 - loops) | |
end | |
end | |
end | |
local function main_loop() | |
while true do | |
-- local status, result = pcall(main_loop) | |
local status, result = true, main_loop_outer() | |
if status then | |
sleep(result) | |
else | |
log:error("Failure:", result) | |
sleep(10) | |
end | |
end | |
end | |
local function event_loop() | |
while true do | |
sleep(0.001) | |
local eventData = {os.pullEvent()} | |
local event = eventData[1] | |
if event == "ae2cc:crafting_started" then | |
local uuid = eventData[2] | |
local object_name = jobs_by_uuid[uuid] | |
log:print(("Job %s %s started!"):format(uuid, object_name)) | |
elseif event == "ae2cc:crafting_cancelled" then | |
local uuid, reason = eventData[2], eventData[3] | |
local object_name = jobs_by_uuid[uuid] | |
log:error(("Job %s %s cancelled: %s"):format(uuid, object_name, reason)) | |
os.sleep(1) | |
remove_job_uuid(uuid) | |
elseif event == "ae2cc:crafting_done" then | |
local uuid = eventData[2] | |
local object_name = jobs_by_uuid[uuid] | |
log:print(("Job %s %s done!"):format(uuid, object_name)) | |
os.sleep(1) | |
remove_job_uuid(uuid) | |
end | |
end | |
end | |
log:print("Starting loops...") | |
parallel.waitForAll(event_loop, main_loop) |
This file contains 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.5, | |
request_scale = 0.1, | |
request_limit = 64 * 8, | |
rules = { | |
["minecraft:paper"] = { priority = 500 }, | |
["minecraft:magma_cream"] = { }, | |
["minecraft:blaze_powder"] = { }, | |
["minecraft:slime_ball"] = { priority = 500 }, | |
["minecraft:birch_planks"] = { priority = 500 }, | |
["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 = 1024, priority = 500 }, | |
["minecraft:light_gray_dye"] = { exact = 1024, priority = 500 }, | |
["minecraft:gray_dye"] = { exact = 1024, priority = 500 }, | |
["minecraft:black_dye"] = { exact = 1024, priority = 500 }, | |
["minecraft:brown_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:red_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:orange_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:yellow_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:lime_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:green_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:cyan_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:light_blue_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:blue_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:purple_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:magenta_dye"] = { exact = 1024, priority = 200 }, | |
["minecraft:pink_dye"] = { exact = 1024, priority = 200 }, | |
-- -- -- | |
["modern_industrialization:lignite_coal_block"] = { priority = 2000 }, | |
["minecraft:coal_block"] = { priority = 2000 }, | |
["minecraft:iron_ingot"] = { }, | |
["minecraft:gold_ingot"] = { }, | |
["minecraft:copper_ingot"] = { }, | |
["minecraft:redstone"] = { }, | |
["minecraft:lapis_lazuli"] = { }, | |
["minecraft:emerald"] = { }, | |
["minecraft:diamond"] = { }, | |
["minecraft:gravel"] = { }, | |
["minecraft:sand"] = { priority = 500 }, | |
["minecraft:glass"] = { }, | |
["minecraft:tinted_glass"] = { }, | |
-- -- -- | |
["techreborn:chrome_dust"] = { scale = 0.05 }, | |
["techreborn:titanium_dust"] = { scale = 0.05 }, | |
["techreborn:platinum_dust"] = { scale = 0.05 }, | |
-- -- -- | |
["ae2:calculation_processor"] = { min = 64 }, | |
["ae2:logic_processor"] = { min = 32 }, | |
["ae2:engineering_processor"] = { min = 16 }, | |
["ae2:quartz_fiber"] = { min = 64, priority = 200 }, | |
["ae2:fluix_glass_cable"] = { min = 64, priority = 200 }, | |
["ae2:me_chest"] = { min = 1 }, | |
["ae2:cell_component_64k"] = { min = 1 }, | |
["ae2:basic_card"] = { min = 32 }, | |
["ae2:advanced_card"] = { min = 32 }, | |
["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 = 900, exact = 60 * 1000 }, | |
["modern_industrialization:bronze_drill"] = { priority = 1000, exact = 8 }, | |
["modern_industrialization:steel_drill"] = { priority = 990, exact = 8 }, | |
["modern_industrialization:gold_drill"] = { priority = 980, exact = 8 }, | |
["modern_industrialization:aluminum_drill"] = { priority = 970, exact = 8 }, | |
["modern_industrialization:stainless_steel_drill"] = { priority = 960, exact = 8 }, | |
["modern_industrialization:titanium_drill"] = { priority = 950, exact = 1 }, | |
} | |
} |
This file contains 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