From 581273dc46d7ba139792c256193a8ac57f361cfa Mon Sep 17 00:00:00 2001 From: Gerkiz Date: Sat, 26 Oct 2019 13:10:56 +0200 Subject: [PATCH] Minor changes Task is no longer global global.lua now returns token. --- utils/global.lua | 8 +++ utils/priority_queue.lua | 91 ++++++++++++++++++++++----------- utils/queue.lua | 61 +++++++++++++++++++++-- utils/server.lua | 2 +- utils/task.lua | 105 +++++++++++++++++++++++++++------------ 5 files changed, 200 insertions(+), 67 deletions(-) diff --git a/utils/global.lua b/utils/global.lua index 4d23bd43..ef0537cc 100644 --- a/utils/global.lua +++ b/utils/global.lua @@ -14,6 +14,8 @@ function Global.register(tbl, callback) callback(Token.get_global(token)) end ) + + return token end function Global.register_init(tbl, init_handler, callback) @@ -34,6 +36,8 @@ function Global.register_init(tbl, init_handler, callback) callback(Token.get_global(token)) end ) + + return token end if _DEBUG then @@ -53,6 +57,8 @@ if _DEBUG then callback(Token.get_global(token)) end ) + + return token end function Global.register_init(tbl, init_handler, callback) @@ -73,6 +79,8 @@ if _DEBUG then callback(Token.get_global(token)) end ) + + return token end end diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua index bccd2358..18ab9e8b 100644 --- a/utils/priority_queue.lua +++ b/utils/priority_queue.lua @@ -1,20 +1,46 @@ +local Debug = require 'utils.debug' +local is_closure = Debug.is_closure +local floor = math.floor + local PriorityQueue = {} -function PriorityQueue.new() - return {} -end - -local function default_comp(a, b) +local function default_comparator(a, b) return a < b end -local function HeapifyFromEndToStart(queue, comp) - comp = comp or default_comp - local pos = #queue +--- Min heap implementation of a priority queue. Smaller elements, as determined by the comparator, +-- have a higher priority. +-- @param comparator the comparator function used to compare elements, if nil the +-- deafult comparator is used. +-- @usage +-- local PriorityQueue = require 'utils.priority_queue' +-- +-- local queue = PriorityQueue.new() +-- PriorityQueue.push(queue, 4) +-- PriorityQueue.push(queue, 7) +-- PriorityQueue.push(queue, 2) +-- +-- game.print(PriorityQueue.pop(queue)) -- 2 +-- game.print(PriorityQueue.pop(queue)) -- 4 +-- game.print(PriorityQueue.pop(queue)) -- 7 +function PriorityQueue.new(comparator) + if comparator == nil then + comparator = default_comparator + elseif is_closure(comparator) then + error('comparator cannot be a closure.', 2) + end + + return {_comparator = comparator} +end + +local function heapify_from_end_to_start(self) + local comparator = self._comparator + local pos = #self while pos > 1 do - local parent = bit32.rshift(pos, 1) -- integer division by 2 - if comp(queue[pos], queue[parent]) then - queue[pos], queue[parent] = queue[parent], queue[pos] + local parent = floor(pos * 0.5) + local a, b = self[pos], self[parent] + if comparator(a, b) then + self[pos], self[parent] = b, a pos = parent else break @@ -22,25 +48,26 @@ local function HeapifyFromEndToStart(queue, comp) end end -local function HeapifyFromStartToEnd(queue, comp) - comp = comp or default_comp +local function heapify_from_start_to_end(self) + local comparator = self._comparator local parent = 1 local smallest = 1 + local count = #self while true do local child = parent * 2 - if child > #queue then + if child > count then break end - if comp(queue[child], queue[parent]) then + if comparator(self[child], self[parent]) then smallest = child end child = child + 1 - if child <= #queue and comp(queue[child], queue[smallest]) then + if child <= count and comparator(self[child], self[smallest]) then smallest = child end if parent ~= smallest then - queue[parent], queue[smallest] = queue[smallest], queue[parent] + self[parent], self[smallest] = self[smallest], self[parent] parent = smallest else break @@ -48,27 +75,33 @@ local function HeapifyFromStartToEnd(queue, comp) end end -function PriorityQueue.size(queue) - return #queue +--- Returns the number of the number of elements in the priority queue. +function PriorityQueue.size(self) + return #self end -function PriorityQueue.push(queue, element, comp) - table.insert(queue, element) - HeapifyFromEndToStart(queue, comp) +-- Inserts an element into the priority queue. +function PriorityQueue.push(self, element) + self[#self + 1] = element + heapify_from_end_to_start(self) end -function PriorityQueue.pop(queue, comp) - local element = queue[1] +-- Removes and returns the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. +function PriorityQueue.pop(self) + local element = self[1] - queue[1] = queue[#queue] - queue[#queue] = nil - HeapifyFromStartToEnd(queue, comp) + self[1] = self[#self] + self[#self] = nil + heapify_from_start_to_end(self) return element end -function PriorityQueue.peek(queue) - return queue[1] +-- Returns, without removing, the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. +function PriorityQueue.peek(self) + return self[1] end return PriorityQueue diff --git a/utils/queue.lua b/utils/queue.lua index 1907c30b..f50915d1 100644 --- a/utils/queue.lua +++ b/utils/queue.lua @@ -1,24 +1,49 @@ local Queue = {} function Queue.new() - local queue = {_head = 0, _tail = 0} + local queue = { + _head = 1, + _tail = 0} return queue end function Queue.size(queue) - return queue._tail - queue._head + return queue._head - queue._tail end function Queue.push(queue, element) local index = queue._head queue[index] = element - queue._head = index - 1 + queue._head = index + 1 +end + +--- Pushes the element such that it would be the next element pop'ed. +function Queue.push_to_end(queue, element) + local index = queue._tail - 1 + queue[index] = element + queue._tail = index +end + +function Queue.empty (queue) + return queue._head > queue._tail end function Queue.peek(queue) return queue[queue._tail] end +function Queue.peek_first(queue) + return queue[queue._head] +end + +function Queue.peek_start(queue) + return queue[queue._head - 1] +end + +function Queue.peek_index(queue, index) + return queue[queue._tail + index - 1] +end + function Queue.pop(queue) local index = queue._tail @@ -26,9 +51,37 @@ function Queue.pop(queue) queue[index] = nil if element then - queue._tail = index - 1 + queue._tail = index + 1 end + return element end +function Queue.to_array(queue) + local n = 1 + local res = {} + + for i = queue._tail, queue._head - 1 do + res[n] = queue[i] + n = n + 1 + end + + return res +end + +function Queue.pairs(queue) + local index = queue._tail + return function() + local element = queue[index] + + if element then + local old = index + index = index + 1 + return old, element + else + return nil + end + end +end + return Queue diff --git a/utils/server.lua b/utils/server.lua index 934a84b9..5c3aed08 100644 --- a/utils/server.lua +++ b/utils/server.lua @@ -3,7 +3,6 @@ local Task = require 'utils.task' local Global = require 'utils.global' local Event = require 'utils.event' local Game = require 'utils.game' -local Timestamp = require 'utils.timestamp' local Print = require('utils.print_override') local serialize = serpent.serialize @@ -17,6 +16,7 @@ local serialize_options = {sparse = true, compact = true} local Public = {} local server_time = {secs = nil, tick = 0} +local requests = {} Global.register( { diff --git a/utils/task.lua b/utils/task.lua index a43c3174..a2143491 100644 --- a/utils/task.lua +++ b/utils/task.lua @@ -8,57 +8,83 @@ local Queue = require 'utils.queue' local PriorityQueue = require 'utils.priority_queue' local Event = require 'utils.event' local Token = require 'utils.token' +local Global = require 'utils.global' + +local floor = math.floor +local log10 = math.log10 +local Token_get = Token.get +local pcall = pcall +local Queue_peek = Queue.peek +local Queue_pop = Queue.pop +local Queue_push = Queue.push +local PriorityQueue_peek = PriorityQueue.peek +local PriorityQueue_pop = PriorityQueue.pop +local PriorityQueue_push = PriorityQueue.push local Task = {} -global.callbacks = global.callbacks or PriorityQueue.new() -global.next_async_callback_time = -1 -global.task_queue = global.task_queue or Queue.new() -global.total_task_weight = 0 -global.task_queue_speed = 1 - -local function comp(a, b) +local function comparator(a, b) return a.time < b.time end -global.tpt = global.task_queue_speed -local function get_task_per_tick() - if game.tick % 300 == 0 then - local size = global.total_task_weight - global.tpt = math.floor(math.log10(size + 1)) * global.task_queue_speed - if global.tpt < 1 then - global.tpt = 1 - end +local callbacks = PriorityQueue.new(comparator) +local task_queue = Queue.new() +local primitives = { + next_async_callback_time = -1, + total_task_weight = 0, + task_queue_speed = 1, + task_per_tick = 1 +} + +Global.register( + {callbacks = callbacks, task_queue = task_queue, primitives = primitives}, + function(tbl) + callbacks = tbl.callbacks + task_queue = tbl.task_queue + primitives = tbl.primitives end - return global.tpt +) + +local function get_task_per_tick(tick) + if tick % 300 == 0 then + local size = primitives.total_task_weight + local task_per_tick = floor(log10(size + 1)) * primitives.task_queue_speed + if task_per_tick < 1 then + task_per_tick = 1 + end + + primitives.task_per_tick = task_per_tick + return task_per_tick + end + return primitives.task_per_tick end local function on_tick() - local queue = global.task_queue - for i = 1, get_task_per_tick() do - local task = Queue.peek(queue) + local tick = game.tick + + for i = 1, get_task_per_tick(tick) do + local task = Queue_peek(task_queue) if task ~= nil then -- result is error if not success else result is a boolean for if the task should stay in the queue. - local success, result = pcall(Token.get(task.func_token), task.params) + local success, result = pcall(Token_get(task.func_token), task.params) if not success then if _DEBUG then error(result) else log(result) end - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight elseif not result then - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight end end end - local callbacks = global.callbacks - local callback = PriorityQueue.peek(callbacks) - while callback ~= nil and game.tick >= callback.time do - local success, result = pcall(Token.get(callback.func_token), callback.params) + local callback = PriorityQueue_peek(callbacks) + while callback ~= nil and tick >= callback.time do + local success, result = pcall(Token_get(callback.func_token), callback.params) if not success then if _DEBUG then error(result) @@ -66,8 +92,8 @@ local function on_tick() log(result) end end - PriorityQueue.pop(callbacks, comp) - callback = PriorityQueue.peek(callbacks) + PriorityQueue_pop(callbacks) + callback = PriorityQueue_peek(callbacks) end end @@ -82,7 +108,7 @@ function Task.set_timeout_in_ticks(ticks, func_token, params) end local time = game.tick + ticks local callback = {time = time, func_token = func_token, params = params} - PriorityQueue.push(global.callbacks, callback, comp) + PriorityQueue_push(callbacks, callback) end --- Allows you to set a timer (in seconds) after which the tokened function will be run with params given as an argument @@ -106,8 +132,21 @@ end -- Ex. if the task is expected to repeat multiple times (ie. the function returns true and loops several ticks) function Task.queue_task(func_token, params, weight) weight = weight or 1 - global.total_task_weight = global.total_task_weight + weight - Queue.push(global.task_queue, {func_token = func_token, params = params, weight = weight}) + primitives.total_task_weight = primitives.total_task_weight + weight + Queue_push(task_queue, {func_token = func_token, params = params, weight = weight}) +end + +function Task.get_queue_speed() + return primitives.task_queue_speed +end + +function Task.set_queue_speed(value) + value = value or 1 + if value < 0 then + value = 0 + end + + primitives.task_queue_speed = value end Event.add(defines.events.on_tick, on_tick)