Первый фикс

Пачки некоторых позиций увеличены
This commit is contained in:
2024-03-01 20:53:32 +03:00
commit 7c9c708c92
23653 changed files with 767936 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
--- For working with mod configurations.
-- @module Misc.Config
-- @usage require('__stdlib__/stdlib/config/config')
---
-- @tfield function new
-- @tfield function get
-- @tfield function set
-- @tfield function delete
-- @tfield function is_set
-- @table Config
local M = {
__class = 'Config',
__index = require('__stdlib__/stdlib/core')
}
setmetatable(M, M)
local table = require('__stdlib__/stdlib/utils/table')
local string = require('__stdlib__/stdlib/utils/string')
-----------------------------------------------------------------------
--Setup repeated code for use in sub functions here
-----------------------------------------------------------------------
local reservedCharacters = [[`~!@#$%^&*+=|;:/\\\'",?()[]{}<>]]
local testReservedCharacters = function(path)
local reserved = reservedCharacters
for c in reserved:gmatch('.') do
if path:find(c, 1, true) then
return c
end
end
return nil
end
--- Creates a new Config object to ease the management of a config table.
-- @tparam table config_table the config table to manage
-- @treturn Config the Config object to manage the config table
--
-- @usage --[Use a global table for config that persists across game save/loads]
-- CONFIG = Config.new(global.testtable)
--
-- @usage --[You can also create a temporary scratch pad config]
-- CONFIG = Config.new({}) -- Temporary scratch pad
--
-- @usage --[Setting data in Config]
-- CONFIG = Config.new(global.testtable)
-- CONFIG.set("your.path.here", "myvalue")
--
-- @usage --[Getting data out of Config]
-- CONFIG = Config.new(global.testtable)
-- my_data = CONFIG.get("your.path.here")
--
-- @usage --[Getting data out of Config with a default to use if path is not found in Config]
-- CONFIG = Config.new(global.testtable)
-- my_data = CONFIG.get("your.path.here", "Your Default here")
--
-- @usage --[Deleting a path from Config]
-- CONFIG = Config.new(global.testtable)
-- CONFIG.delete("your.path.here")
--
-- @usage --[Checking if a path exists in Config]
-- CONFIG = Config.new(global.testtable)
-- CONFIG.is_set("your.path.here")
function M.new(config_table)
if not config_table then
error('config_table is a required parameter.', 2)
elseif type(config_table) ~= 'table' then
error('config_table must be a table. Was given [' .. type(config_table) .. ']', 2)
elseif type(config_table.get) == 'function' then
error("Config can't manage another Config object", 2)
end
-----------------------------------------------------------------------
--Setup the Config object
-----------------------------------------------------------------------
local Config = {}
--- Get a stored config value.
-- @tparam string path the variable to retrieve
-- @tparam[opt] Mixed default value to be used if path is nil
-- @treturn Mixed value at path or nil if not found and no default given
function Config.get(path, default)
if type(path) ~= 'string' or path == '' then
error('path is invalid', 2)
end
local config = config_table
local c = testReservedCharacters(path)
if c ~= nil then
error("path '" .. path .. "' contains the reserved character '" .. c .. "'", 2)
end
local pathParts = string.split(path, '.')
local part = config
local value = nil
for key = 1, #pathParts, 1 do
local partKey = pathParts[key]
if (type(part) ~= 'table') then
value = nil
break
end
value = part[partKey]
part = part[partKey]
end
if (type(value) == 'table') then
--Force break references.
return table.deepcopy(value)
elseif (value ~= nil) then
return value
else
return default
end
end
--- Set a stored config value.
-- @tparam string path the config path to set
-- @tparam ?|nil|Mixed data the value to set the path to. If *nil*, it behaves identical to @{delete|Config.delete()}
-- @treturn uint 0 on failure or the number of affected paths on success
function Config.set(path, data)
if type(path) ~= 'string' or path == '' then
error('path is invalid', 2)
end
local config = config_table
local c = testReservedCharacters(path)
if c ~= nil then
error("path contains the reserved character '" .. c .. "'", 2)
end
local pathParts = string.split(path, '.')
local part = config
for key = 1, #pathParts - 1, 1 do
local partKey = pathParts[key]
if (type(part[partKey]) ~= 'table') then
part[partKey] = {}
end
part = part[partKey]
end
part[pathParts[#pathParts]] = data
return 1
end
--- Delete a stored config value.
-- @tparam string path the config path to delete
-- @treturn uint 0 on failure or the number of affected paths on success
function Config.delete(path)
if type(path) ~= 'string' or string == '' then
error('path is invalid', 2)
end
local config = config_table
local c = testReservedCharacters(path)
if c ~= nil then
error("path contains the reserved character '" .. c .. "'", 2)
end
local pathParts = string.split(path, '.')
local part = config
for key = 1, #pathParts - 1, 1 do
local partKey = pathParts[key]
if (type(part[partKey]) ~= 'table') then
return 0
end
part = part[partKey]
end
if part[pathParts[#pathParts]] == nil then
return 0
else
part[pathParts[#pathParts]] = nil
return 1
end
end
--- Test the existence of a stored config value.
-- @tparam string path the config path to test
-- @treturn boolean true if the value exists, false otherwise
function Config.is_set(path)
if type(path) ~= 'string' or path == '' then
error('path is invalid', 2)
end
return Config.get(path) ~= nil
end
return Config
end
return M

View File

@@ -0,0 +1,153 @@
--- For logging debug information to files.
-- @module Misc.Logger
-- @usage
-- local Logger = require('__stdlib__/stdlib/misc/logger')
-- -- or to create a new logger directly:
-- local Log = require('__stdlib__/stdlib/misc/logger').new()
-- -- log files are saved to script-output/modname/log.log by default
local Logger = {
__class = 'Logger',
_loggers = {},
__call = function(self, ...)
return self.get(...)
end,
__index = require('__stdlib__/stdlib/core')
}
setmetatable(Logger, Logger)
local table = require('__stdlib__/stdlib/utils/table')
-- Set on the individual log object, either logs a message or writes immediatly if nil.
local _Log_mt = {
__call = function(self, msg)
if msg then
return self, self.log(msg)
else
return self, self.write()
end
end
}
local format = string.format
--- Get a saved log or create a new one if there is no saved log.
function Logger.get(...)
local log_name = (...) or 'log'
return Logger._loggers[log_name] or Logger.new(...)
end
--- Creates a new logger object.
-- In debug mode, the logger writes to file immediately, otherwise the logger buffers the lines.
-- <p>The logger flushes the logged messages every 60 seconds since the last message.
-- <p>A table of @{options} may be specified when creating a logger.
-- @usage
--Log = Logger.new()
--Log("this msg will be logged in /script-output/YourModName/log.log!")
-- -- Immediately Write everything buffered in the log file
-- Log()
--
-- @usage
--Log = Logger.new('test', true)
--Log("this msg will be logged and written immediately in /script-output/YourModName/test.log!")
--
-- @usage
--Log = Logger.new('cool_mod_name', 'test', true, { file_extension = data })
--Log("this msg will be logged and written immediately in /script-output/YourModName/test.data!")
--
-- @tparam[opt='log'] string log_name the name of the logger
-- @tparam[opt=false] boolean debug_mode toggles the debug state of logger
-- @tparam[opt={...}] options options a table with optional arguments
-- @return (<span class="types">@{Logger}</span>) the logger instance
function Logger.new(log_name, debug_mode, options)
local mod_name = script and script.mod_name or 'Data'
log_name = log_name or 'log'
Logger._loggers[log_name] = nil
options = options or {}
local Log = {
__class = 'Log',
mod_name = mod_name,
log_name = log_name,
debug_mode = debug_mode,
buffer = {},
last_written = 0,
ever_written = false,
}
---
-- Used in the @{new} function for logging game ticks, specifying logfile extension, or forcing the logs to append to the end of the logfile.
-- @tfield[opt=false] boolean log_ticks whether to include the game tick timestamp in the logs
-- @tfield[opt="log"] string file_extension a string that overrides the default logfile extension
-- @tfield[opt=false] boolean force_append if true, every new message appends to the current logfile instead of creating a new one
-- @table Log.options
Log.options = {
log_ticks = options.log_ticks or false,
file_extension = options.file_extension or 'log',
force_append = options.force_append or false
}
Log.file_name = mod_name .. '/' .. log_name .. (log_name:find('%.') and '' or '.' .. Log.options.file_extension)
Log.ever_written = Log.options.force_append
--- Logs a message.
-- @tparam string|table msg the message to log. @{table}s will be dumped using [serpent](https://github.com/pkulchenko/serpent)
-- which is included in the official Factorio Lualib
-- @return (<span class="types">@{Logger}</span>) the logger instance
-- @see https://forums.factorio.com/viewtopic.php?f=25&t=23844 Debugging utilities built in to Factorio
function Log.log(msg)
if type(msg) ~= 'string' then
msg = serpent.block(msg, { comment = false, nocode = true, sparse = true })
end
if _G.game then
local tick = game.tick
local floor = math.floor
local time_s = floor(tick / 60)
local time_minutes = floor(time_s / 60)
local time_hours = floor(time_minutes / 60)
if Log.options.log_ticks then
table.insert(Log.buffer, format('%02d:%02d:%02d.%02d: %s\n', time_hours, time_minutes % 60, time_s % 60, tick - time_s * 60, msg))
else
table.insert(Log.buffer, format('%02d:%02d:%02d: %s\n', time_hours, time_minutes % 60, time_s % 60, msg))
end
-- write the log every minute
if (Log.debug_mode or (tick - Log.last_written) > 3600) then
return Log.write()
end
else
if _G.script then --buffer when a save is loaded but _G.game isn't available
if Log.options.log_ticks then
table.insert(Log.buffer, format('00:00:00:00: %s\n', msg))
else
table.insert(Log.buffer, format('00:00:00: %s\n', msg))
end
else --log in data stage
log(format('%s/%s: %s', Log.mod_name, Log.log_name, msg))
end
end
return Log
end
--- Writes out all buffered messages immediately.
-- @return (<span class="types">@{Logger}</span>) the logger instance
function Log.write()
if _G.game and table.size(Log.buffer) > 0 then
Log.last_written = game.tick
game.write_file(Log.file_name, table.concat(Log.buffer), Log.ever_written)
Log.buffer = {}
Log.ever_written = true
end
return Log
end
setmetatable(Log, _Log_mt)
Logger._loggers[log_name] = Log
return Log
end
return Logger

View File

@@ -0,0 +1,41 @@
--- Migration helper functions
-- @module Misc.Migrate
local Migrate = {
__class = 'Migrate',
__index = require('__stdlib__/stdlib/core')
}
setmetatable(Migrate, Migrate)
local Is = require('__stdlib__/stdlib/utils/is')
--- Migrate a dictionary of recipe -> tech names
-- @tparam dictionary dictionary
function Migrate.Recipes(dictionary)
Is.Assert.Table(dictionary, 'dictionary of recipes->technology not found')
for _, force in pairs(game.forces) do
for recipe, tech in pairs(dictionary) do
if force.technologies[tech] and force.technologies[tech].researched then
if force.recipes[recipe] then
force.recipes[recipe].enabled = true
end
end
end
end
end
function Migrate.all_recipes()
for _, force in pairs(game.forces) do
for _, tech in pairs(force.technologies) do
if tech.researched then
for _, unlock in pairs(tech.effects or {}) do
if unlock.type == 'unlock-recipe' then
force.recipes[unlock.recipe].enabled = true
end
end
end
end
end
end
return Migrate

View File

@@ -0,0 +1,362 @@
--- A double queue.
-- Taken from ***Programming in Lua*** [Queues and Double Queues](http://www.lua.org/pil/11.4.html)
-- and modified to not allow nil values, and returns nil if @{pop_first} or @{pop_last} is used when the queue is empty.
-- @module Misc.Queue
-- @usage local Queue = require('__stdlib__/stdlib/misc/queue')
-- local q = Queue() -- create a new empty queue
-- q('my value') -- push a value onto the queue
-- q() -- pop the last value off the queue
-- game.print(#q) -- print the number of items in the queue
local Queue = {
__class = 'Queue',
__index = require('__stdlib__/stdlib/core')
}
setmetatable(Queue, Queue)
local table = require('__stdlib__/stdlib/utils/table')
local t_size = table_size
local Inspect = require('__stdlib__/stdlib/vendor/inspect')
local meta = {}
function Queue.__call(_, ...)
local queue = { first = 1, last = 0, objects = {} }
setmetatable(queue, meta)
for _, push in pairs { ... } do
queue(push)
end
return queue
end
--- Constructs a new Queue object.
-- @param ... mixed, values to push into the queue
-- @treturn @{Queue} a new queue
function Queue.new(...)
return Queue.__call(nil, ...)
end
--- Load global.queue or queues during on_load, as metatables are not persisted.
-- <p>This is only needed if you are using the queue as an object and storing it in global.
-- @tparam table queue (<span class="types">@{Queue}</span>,...)
-- @usage global.myqueue1 = Queue.new()
-- script.on_load(function() Queue.load(global.myqueue))
function Queue.load(queue)
if type(queue) == 'table' and queue.first then
return setmetatable(queue, meta)
end
end
--- Push a new element to the front of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam Mixed value the element to push
function Queue.push_first(queue, ...)
for _, value in pairs { ... } do
queue.first = queue.first - 1
queue.objects[queue.first] = value
end
return queue
end
--- Push a new element to the back of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam Mixed ... the element(s) to push
function Queue.push_last(queue, ...)
for _, value in pairs { ... } do
queue.last = queue.last + 1
queue.objects[queue.last] = value
end
return queue
end
--- Shortcut for @{Queue.push_last}
-- @function Queue.push
Queue.push = Queue.push_last
--- Push a new element to a specific location of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam number index the index to push to.
-- @tparam Mixed value the element to push.
function Queue.push_at(queue, index, value)
if index < queue.first then
return Queue.push_first(queue, value)
elseif index > queue.last then
return Queue.push_last(queue, value)
else
table.insert(queue.objects, index, value)
queue.last = queue.last + 1
end
return queue
end
function Queue.wrapper(self, func_name, ...)
if Queue[func_name] then
Queue[func_name](self, ...)
end
return self
end
--- Retrieve the element at the front of the queue and remove it from the queue.
-- @tparam Queue queue the queue to retrieve the element from
-- @treturn Mixed value the element at the front of the queue
function Queue.pop_first(queue)
if Queue.is_empty(queue) then
return nil
end
local first = queue.first
local value = queue.objects[first]
queue.objects[first] = nil -- to allow garbage collection
queue.first = first + 1
return value
end
--- Shortcut for @{Queue.pop_first}
-- @function Queue.pop
Queue.pop = Queue.pop_first
local function remove(queue, index)
local ret = queue.objects[index]
if ret ~= nil then
for i = index + 1, queue.last do
queue.objects[i - 1] = queue.objects[i]
end
queue.objects[queue.last] = nil
queue.last = queue.last - 1
end
return ret
end
--- Pop an element at a specific location of the queue.
-- @tparam Queue queue the queue to push an element to
-- @tparam number index the index to push to.
-- @treturn Mixed value the popped element.
function Queue.pop_at(queue, index)
return remove(queue, index)
end
--- Peek at an element in the queue without disturbing the queue.
-- @tparam Queue queue the queue to peek at
-- @tparam number index the index in the queue to peek at
-- @treturn Mixed the value of the peeked element
function Queue.peek_at(queue, index)
return queue.objects[index]
end
--- Return the element at the front of the queue and remove it from the queue.
-- @tparam Queue queue the queue to retrieve the element from
-- @treturn Mixed the element at the front of the queue
function Queue.peek_first(queue)
return queue.objects[queue.first]
end
--- Shortcut for @{Queue.peek_first}
-- @function Queue.peek
Queue.peek = Queue.peek_first
--- Retrieve the element at the back of the queue and remove it from the queue.
-- @tparam Queue queue the queue to retrieve the element from
-- @treturn Mixed the element at the back of the queue
function Queue.pop_last(queue)
if queue.is_empty(queue) then
return nil
end
local last = queue.last
local value = queue.objects[last]
queue.objects[last] = nil -- to allow garbage collection
queue.last = last - 1
return value
end
--- Return the element at the back of the queue.
-- @tparam Queue queue the queue to retrieve the element from
-- @treturn Mixed the element at the back of the queue
function Queue.peek_last(queue)
return queue.objects[queue.last]
end
--- Returns the popped value and pushes back into the queue.
-- @tparam Queue queue the queue
-- @return Mixed the value that was popped.
function Queue.pop_and_push(queue)
local ret = queue.pop(queue)
queue.push(queue, ret)
return ret
end
--- Returns the queue after popping the last element and pushing it to the top.
-- @tparam Queue queue the queue
-- @treturn @{Queue} the queue
function Queue.cycle(queue)
return queue.push(queue, queue.pop(queue))
end
--- Gets the first index which matches the stored data. does not compare inside tables.
function Queue.find(queue, find)
for i, v in pairs(queue) do
if v == find then
return i
end
end
end
local function _sort_func(a, b)
local lhs = type(a) == 'table' and '{' or tostring(a)
local rhs = type(b) == 'table' and '{' or tostring(b)
return lhs < rhs
end
--- sort and reorder the queue
function Queue.sort(queue, func)
local sorted = {}
for _, v in pairs(queue) do
if v ~= nil then
sorted[#sorted + 1] = v
end
end
table.sort(sorted, func or _sort_func)
queue.objects = sorted
queue.first, queue.last = 1, #queue.objects
return queue
end
--- Returns true if the given queue is empty.
-- @tparam Queue queue the queue to check
-- @treturn boolean true if empty, false otherwise
function Queue.is_empty(queue)
return queue.first > queue.last
end
--- Returns the number of items in the queue.
-- @tparam Queue queue the queue to check
-- @treturn number the number of items in the queue
function Queue.size(queue)
return t_size(queue.objects)
end
--- Shortcut for @{Queue.size}
-- @function Queue.count
Queue.count = Queue.size
--- Return the next element in the queue
-- @tparam Queue queue the queue to check
-- @tparam number|nil index if nil return the first value, else return the next index value
-- @tparam boolean pop pop the value off the queue
-- @treturn number|nil the index
-- @treturn Mixed|nil the value at queue index
function Queue.next(queue, index, pop)
index = not index and queue.first or index + (pop and 0 or 1)
for i = index, queue.last do
local v = queue.objects[i]
if v ~= nil then
return i, pop and Queue.pop_at(queue, i) or v
end
end
return nil, nil
end
--- Return the previous element in the queue
-- @tparam Queue queue the queue to check
-- @tparam number|nil index if nil return the last value, else return the previous index value
-- @tparam boolean pop pop the value off the queue
-- @treturn number|nil the index
-- @treturn Mixed|nil the value at queue index
function Queue.rnext(queue, index, pop)
-- next returns index of next or nil and data,
index = not index and queue.last or (index < queue.first and queue.first or index) - 1
for i = index, queue.first, -1 do
local v = queue.objects[i]
if v ~= nil then
return i, pop and Queue.pop_at(queue, i) or v
end
end
return nil, nil
end
local function next_pop(queue, index)
return Queue.next(queue, index, true)
end
local function rnext_pop(queue, index)
return Queue.rnext(queue, index, true)
end
--- Iterate the queue forward
function Queue.pairs(queue, pop)
return pop and next_pop or Queue.next, queue, nil
end
--- Iterate the queue backwards
function Queue.rpairs(queue, pop)
return pop and rnext_pop or Queue.rnext, queue, nil
end
do
meta.__class = 'queue'
meta.__len = Queue.size
meta.__unm = Queue.pop
meta.__parent = Queue
meta.__debugline = [[<Queue>{[}first={first},last={last}{]}]]
-- Allows queue[3] to return the item at queue.objects[3]
meta.__index = function(self, k)
if type(k) == 'number' then
return self:peek_at(k)
else
local v = rawget(self, k)
if v == nil then
return Queue[k]
end
return v
end
end
meta.__newindex = function(self, k, v)
if type(k) == 'number' then
if v ~= nil then
self:push_at(k, v)
else
error('Attempt to modify Queue structure')
end
else
rawset(self, k, v)
end
end
-- Allows queue() to pop_first and queue(data) to push_last
meta.__call = function(self, ...)
if ... then
return self:push(...)
else
return self:pop()
end
end
meta.__tostring = function(self)
return Inspect({ first = self.first, last = self.last, objects = self.objects }, { arraykeys = true })
end
meta.__add = function(queue1, queue2)
local new = Queue.new()
local lhs = getmetatable(queue1) == meta and true
local rhs = getmetatable(queue2) == meta and true
if lhs then
for _, v in pairs(queue1.objects) do
new:push(v)
end
else
new:push(queue1)
end
if rhs then
for _, v in pairs(queue2.objects) do
new:push(v)
end
else
new:push(queue2)
end
return new
end
end
return Queue