Add player iteration methods to builtin, optimization of many mods

master
stujones11 2019-06-30 21:42:14 +01:00 committed by MoNTE48
parent 37ab30c2cc
commit b757cdb6e2
15 changed files with 247 additions and 190 deletions

View File

@ -556,6 +556,99 @@ core.registered_on_protection_violation, core.register_on_protection_violation =
core.registered_on_item_eats, core.register_on_item_eat = make_registration()
core.registered_on_punchplayers, core.register_on_punchplayer = make_registration()
-- Player step iterration
players_per_step = core.settings:get("players_per_globalstep")
players_per_step = players_per_step and tonumber(players_per_step) or 20
local player_iter = nil
local player_iter_forced = nil
local playerstep_iter = nil
local playerstep_funcs = {}
local playerstep_funcs_forced = {}
local playernames = {}
local playernames_forced = {}
core.register_playerstep = function(func, force)
local funcs = force and playerstep_funcs_forced or playerstep_funcs
funcs[#funcs + 1] = func
end
local function table_iter(t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then
return t[i]
end
end
end
function core.get_player_iter()
local names = {}
for player in table_iter(core.get_connected_players()) do
local name = player:get_player_name()
if name then
names[#names + 1] = name
end
end
return table_iter(names)
end
local function get_playerstep_func()
if playerstep_iter == nil then
playerstep_iter = table_iter(playerstep_funcs)
playernames = {}
for _ = 1, players_per_step do
if player_iter == nil then
player_iter = core.get_player_iter()
end
local name = player_iter()
if not name then
player_iter = nil
break
end
playernames[#playernames + 1] = name
end
end
local func = playerstep_iter()
playerstep_iter = func and playerstep_iter
return func or get_playerstep_func()
end
-- Run playerstep callbacks
core.register_globalstep(function(dtime)
-- Run forced callbacks
if #playerstep_funcs_forced ~= 0 then
playernames_forced = {}
for _ = 1, players_per_step do
if player_iter_forced == nil then
player_iter_forced = core.get_player_iter()
end
local name = player_iter_forced()
if not name then
player_iter_forced = nil
break
end
playernames_forced[#playernames_forced + 1] = name
end
for func in table_iter(playerstep_funcs_forced) do
if type(func) == "function" then
func(dtime, playernames_forced)
end
end
end
if #playerstep_funcs ~= 0 then
-- Run single step callbacks
local func = get_playerstep_func()
if type(func) == "function" then
func(dtime, playernames)
end
end
end)
--
-- Compatibility for on_mapgen_init()
--

View File

@ -185,16 +185,9 @@ core.register_on_joinplayer(function(player)
initialize_builtin_statbars(player)
end)
local time = 0
local update_time = 1
core.register_globalstep(function(dtime)
time = time + dtime
if time > update_time then
local players = core.get_connected_players()
for i = 1, #players do
local player = players[i]
minetest.register_playerstep(function(dtime, playernames)
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
local player_name = player:get_player_name()
local wielded_item = player:get_wielded_item()
@ -224,6 +217,4 @@ core.register_globalstep(function(dtime)
hud.change_item(player, "itemname", {text = description})
end
end
time = 0
end
end)
end, minetest.is_singleplayer()) -- Force step in singlplayer mode only

View File

@ -2363,6 +2363,17 @@ Call these functions only at load time!
* `minetest.register_globalstep(func(dtime))`
* Called every server step, usually interval of 0.1s
* `minetest.register_playerstep(function(dtime, playernames), force)`
* Called one registration per step, see `force`
* `dtime` : Delta time since last callback, see `dedicated_server_step` setting
* `playernames` : Table of player names, see `players_per_globalstep` setting
* `force` : Boolean value (optional)
* Force this callback to run on every server step, default: `false`
* Note: This callback should not be rate limited or players may be missed!
* See the [fire] mod for an example of how this may be done reliably
* `minetest.get_player_iter()`
* Returns a player name iterrator, as used by `minetest.register_playerstep`
* See `builtin/game/register.lua` for example usage
* `minetest.register_on_shutdown(func())`
* Called before server shutdown
* **Warning**: If the server terminates abnormally (i.e. crashes), the registered

View File

@ -56,33 +56,42 @@ local get_highest_priority = function (actions)
return highesti
end
local m_time = 0
local m_time, timer = 0, 0
local resumetime = mesecon.setting("resumetime", 4)
local delaytime = mesecon.setting("delaytime", core.settings:get("dedicated_server_step") * 3)
if not minetest.is_singleplayer() then
delaytime = delaytime * 3
end
minetest.register_globalstep(function (dtime)
m_time = m_time + dtime
-- don't even try if server has not been running for XY seconds; resumetime = time to wait
-- after starting the server before processing the ActionQueue, don't set this too low
if (m_time < resumetime) then return end
local actions = mesecon.tablecopy(mesecon.queue.actions)
local actions_now={}
timer = timer + dtime
if timer > delaytime then
local actions = mesecon.tablecopy(mesecon.queue.actions)
local actions_now={}
mesecon.queue.actions = {}
mesecon.queue.actions = {}
-- sort actions into two categories:
-- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions)
for i, ac in ipairs(actions) do
if ac.time > 0 then
ac.time = ac.time - dtime -- executed later
table.insert(mesecon.queue.actions, ac)
else
table.insert(actions_now, ac)
-- sort actions into two categories:
-- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions)
for i, ac in ipairs(actions) do
if ac.time > 0 then
ac.time = ac.time - dtime -- executed later
table.insert(mesecon.queue.actions, ac)
else
table.insert(actions_now, ac)
end
end
end
while(#actions_now > 0) do -- execute highest priorities first, until all are executed
local hp = get_highest_priority(actions_now)
mesecon.queue:execute(actions_now[hp])
table.remove(actions_now, hp)
while(#actions_now > 0) do -- execute highest priorities first, until all are executed
local hp = get_highest_priority(actions_now)
mesecon.queue:execute(actions_now[hp])
table.remove(actions_now, hp)
end
timer = 0
end
end)

View File

@ -4,9 +4,9 @@ local freq = 2
local kpos = {}
if PLATFORM == "Android" or PLATFORM == "iOS" then
local radius = 4
local height = 2
local freq = 3
radius = 4
height = 2
freq = 3
end
minetest.register_on_joinplayer(function(player)

View File

@ -1,7 +1,6 @@
local age = 0.5 -- How old an item has to be before collecting
local radius_magnet = 2 -- Radius of item magnet
local player_collect_height = 1.3 -- Added to their pos y value
local players_per_step = 20 -- How many players to process in one server step
local function collect_items(player)
local pos = player:get_pos()
@ -40,48 +39,13 @@ local function collect_items(player)
end
end
local function table_iter(t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then
return t[i]
end
end
end
local player_iter = nil
local function get_next_player()
if player_iter == nil then
local names = {}
for player in table_iter(minetest.get_connected_players()) do
local name = player:get_player_name()
if name then
table.insert(names, name)
end
end
player_iter = table_iter(names)
if players_per_step > #names then
players_per_step = #names + 1
end
return
end
local name = player_iter()
player_iter = name and player_iter
return name or get_next_player()
end
-- Item collection
minetest.register_globalstep(function()
-- only deal with * player count on each server step
for i = 1, players_per_step do
local name = get_next_player()
if name then
local player = minetest.get_player_by_name(name)
if player and player:is_player() and player:get_hp() > 0 then
collect_items(player)
end
minetest.register_playerstep(function(dtime, playernames)
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() and player:get_hp() > 0 then
collect_items(player)
end
end
end)
end, minetest.is_singleplayer()) -- Force step in singlplayer mode only

View File

@ -107,14 +107,13 @@ minetest.register_on_dieplayer(function(player)
init_data(player, true)
end)
minetest.register_globalstep(function(dtime)
local players = get_players()
for i = 1, #players do
local player = players[i]
local name = player:get_player_name()
minetest.register_playerstep(function(dtime, playernames)
for i = 1, #playernames do
local name = playernames[i]
local player = minetest.get_player_by_name(name)
local pos = player:get_pos()
pos.y = pos.y + 0.5
pos.y = pos.y + 0.5
xp[name].timer = (xp[name].timer or 0) + dtime
for _, obj in ipairs(get_objs_rad(pos, ORB_COLLECT_RADIUS)) do
@ -162,7 +161,7 @@ minetest.register_globalstep(function(dtime)
-- player:hud_change(_hud[name].lvl, "offset",
-- {x = (xp[name].level >= 10 and 13 or 6), y = -202})
end
end)
end, minetest.is_singleplayer()) -- Force step in singlplayer mode only
minetest.register_entity("experience:orb", {
timer = 0,

View File

@ -274,18 +274,20 @@ if flame_sound then
-- Cycle for updating players sounds
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < cycle then
return
local cycles = {}
minetest.register_playerstep(function(dtime, playernames)
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() then
cycles[name] = cycles[name] or 0
cycles[name] = cycles[name] + dtime
if cycles[name] >= cycle then
fire.update_player_sound(player)
cycles[name] = 0
end
end
end
timer = 0
local players = minetest.get_connected_players()
for n = 1, #players do
fire.update_player_sound(players[n])
end
end)
end, true) -- We can force this since it is already rate-limited
-- Stop sound and clear handle on player leave
@ -294,6 +296,7 @@ if flame_sound then
if handles[player_name] then
minetest.sound_stop(handles[player_name])
handles[player_name] = nil
cycles[player_name] = nil
end
end)
end

View File

@ -102,13 +102,31 @@ local hunger_timer = 0
local health_timer = 0
local action_timer = 0
local function hunger_globaltimer(dtime)
local function get_player_refs(playernames)
local refs = {}
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() then
refs[#refs + 1] = player
end
end
end
local function hunger_globaltimer(dtime, playernames)
hunger_timer = hunger_timer + dtime
health_timer = health_timer + dtime
action_timer = action_timer + dtime
local players_refs = {}
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() then
players_refs[#players_refs + 1] = player
end
end
if action_timer > HUNGER_MOVE_TICK then
for _,player in ipairs(minetest.get_connected_players()) do
for _, player in ipairs(players_refs) do
local controls = player:get_player_control()
-- Determine if the player is walking
if controls.up or controls.down or controls.left or controls.right then
@ -120,7 +138,7 @@ local function hunger_globaltimer(dtime)
-- lower saturation by 1 point after <HUNGER_TICK> second(s)
if hunger_timer > HUNGER_TICK then
for _,player in ipairs(minetest.get_connected_players()) do
for _, player in ipairs(players_refs) do
local name = player:get_player_name()
local tab = hunger.players[name]
if tab then
@ -135,7 +153,7 @@ local function hunger_globaltimer(dtime)
-- heal or damage player, depending on saturation
if health_timer > HUNGER_HEALTH_TICK then
for _,player in ipairs(minetest.get_connected_players()) do
for _, player in ipairs(players_refs) do
local name = player:get_player_name()
local tab = hunger.players[name]
if tab then
@ -158,7 +176,7 @@ local function hunger_globaltimer(dtime)
end
end
minetest.register_globalstep(hunger_globaltimer)
minetest.register_playerstep(hunger_globaltimer, minetest.is_singleplayer())
-- food functions
local food = hunger.food

View File

@ -1833,24 +1833,25 @@ end
-- follow player if owner or holding item, if fish outta water then flop
function mob_class:follow_flop()
-- find player to follow
if (self.follow ~= ""
or self.order == "follow")
and not self.following
and self.state ~= "attack"
and self.state ~= "runaway" then
local s = self.object:get_pos()
local players = minetest.get_connected_players()
for n = 1, #players do
if get_distance(players[n]:get_pos(), s) < self.view_range
and not mobs.invis[ players[n]:get_player_name() ] then
self.following = players[n]
if (self.follow ~= "" or self.order == "follow") and
not self.following and
self.state ~= "attack" and
self.state ~= "runaway" then
if self.player_iter == nil then
self.player_iter = minetest.get_player_iter()
end
local pos = self.object:get_pos()
for _ = 1, players_per_step do
local name = self.player_iter()
if not name then
self.player_iter = nil
break
end
local player = minetest.get_player_by_name(name)
if player and not mobs.invis[name] and
get_distance(player:get_pos(), pos) < self.view_range then
self.following = player
break
end
end

View File

@ -415,20 +415,10 @@ minetest.register_on_joinplayer(function(player)
end
end)
playereffects.globalstep_timer = 0
playereffects.autosave_timer = 0
minetest.register_globalstep(function(dtime)
playereffects.globalstep_timer = playereffects.globalstep_timer + dtime
playereffects.autosave_timer = playereffects.autosave_timer + dtime
-- Update HUDs of all players
if(playereffects.globalstep_timer >= 1) then
playereffects.globalstep_timer = 0
local players = minetest.get_connected_players()
for p=1,#players do
playereffects.hud_update(players[p])
end
end
-- Autosave into file
if(playereffects.use_autosave == true and playereffects.autosave_timer >= playereffects.autosave_time) then
playereffects.autosave_timer = 0
@ -437,6 +427,13 @@ minetest.register_globalstep(function(dtime)
end
end)
minetest.register_playerstep(function(dtime, playernames)
-- Update HUDs of all players
for _, name in pairs(playernames) do
playereffects.hud_update(minetest.get_player_by_name(name))
end
end, minetest.is_singleplayer()) -- Force step in singlplayer mode only
--[=[ HUD ]=]
function playereffects.hud_update(player)
if(playereffects.use_hud == true) then

View File

@ -1,8 +1,5 @@
player_api = {}
-- How many players to process in one server step
local players_per_step = 20
-- Player animation blending
-- Note: This is currently broken due to a bug in Irrlicht, leave at 0
local animation_blend = 0
@ -101,42 +98,18 @@ local function table_iter(t)
end
end
local player_iter = nil
local function get_next_player()
if player_iter == nil then
local names = {}
for player in table_iter(minetest.get_connected_players()) do
local name = player:get_player_name()
if name then
table.insert(names, name)
end
end
player_iter = table_iter(names)
if players_per_step > #names then
players_per_step = #names + 1
end
return
end
local name = player_iter()
player_iter = name and player_iter
return name or get_next_player()
end
-- Localize for better performance.
local player_set_animation = player_api.set_animation
local player_attached = player_api.player_attached
-- Check each player and apply animations
minetest.register_globalstep(function(dtime)
-- only deal with * player count on each server step
for i = 1, players_per_step do
local name = get_next_player()
if name then
local player = minetest.get_player_by_name(name)
minetest.register_playerstep(function(dtime, playernames)
for _, name in pairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() then
local model_name = player_model[name]
local model = model_name and models[model_name]
if model and not player_attached[name] and player and player:is_player() then
if model and not player_attached[name] then
local controls = player:get_player_control()
local walking = false
local animation_speed_mod = model.animation_speed or 30
@ -176,4 +149,4 @@ minetest.register_globalstep(function(dtime)
end
end
end
end)
end, true) -- Force this callback to run every step for smoother animations

View File

@ -2,16 +2,14 @@ function round(num)
return math.floor(num + 0.5)
end
local lasttime = "default"
minetest.register_globalstep(function(dtime)
minetest.register_playerstep(function(dtime, playernames)
local now = round((minetest.get_timeofday() * 24) % 12)
if now ~= lasttime then
lasttime = now
if now == 12 then now = 0 end
local players = minetest.get_connected_players()
for i,player in ipairs(players) do
if string.sub(player:get_wielded_item():get_name(), 0, 6) == "watch:" then
if now == 12 then now = 0 end
for i, name in ipairs(playernames) do
local player = minetest.get_player_by_name(name)
if player and player:is_player() then
local item = player:get_wielded_item()
if item and string.sub(item:get_name(), 0, 6) == "watch:" then
player:set_wielded_item("watch:"..now)
end
for i,stack in ipairs(player:get_inventory():get_list("main")) do
@ -22,7 +20,7 @@ minetest.register_globalstep(function(dtime)
end
end
end
end)
end, minetest.is_singleplayer()) -- Force step in singlplayer mode only
local images = {
"watch_0.png",

View File

@ -57,11 +57,11 @@ wieldview.get_item_texture = function(self, item)
return texture
end
wieldview.update_wielded_item = function(self, player)
if not player or minetest.is_singleplayer() then
wieldview.update_wielded_item = function(self, name)
local player = minetest.get_player_by_name(name)
if not player or not player:is_player() then
return
end
local name = player:get_player_name()
local stack = player:get_wielded_item()
local item = stack:get_name()
if not item then
@ -75,21 +75,17 @@ wieldview.update_wielded_item = function(self, player)
end
self.wielded_item[name] = item
end
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
wieldview.wielded_item[name] = ""
minetest.after(0, function(player)
wieldview:update_wielded_item(player)
end, player)
end)
minetest.register_globalstep(function(dtime)
time = time + dtime
if time > update_time then
for _,player in ipairs(minetest.get_connected_players()) do
wieldview:update_wielded_item(player)
if not minetest.is_singleplayer() then
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
wieldview.wielded_item[name] = ""
minetest.after(0, function(player)
wieldview:update_wielded_item(name)
end, player)
end)
minetest.register_playerstep(function(dtime, playernames)
for _, name in pairs(playernames) do
wieldview:update_wielded_item(name)
end
time = 0
end
end)
end)
end

View File

@ -1115,6 +1115,10 @@
# type: float
# dedicated_server_step = 0.1
# Maxumim number of players to process per step, see `minetest.register_playerstep`
# type: int
# players_per_globalstep = 20
# Time in between active block management cycles
# type: float
# active_block_mgmt_interval = 2.0