mirror of
https://files.creativekara.fr/git/poschangelib.git
synced 2024-12-12 08:53:22 +01:00
Version 0.1
This commit is contained in:
commit
39b6acf793
14
LICENSE
Normal file
14
LICENSE
Normal file
@ -0,0 +1,14 @@
|
||||
License of source code
|
||||
|
||||
GNU Lesser General Public License, version 2.1
|
||||
Copyright (C) 2017 Karamel <karamel@creativekara.fr>
|
||||
With knowledge from various Minetest developers, modders and documenters
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms
|
||||
of the GNU Lesser General Public License as published by the Free Software Foundation;
|
||||
either version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details:
|
||||
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
|
129
README.txt
Normal file
129
README.txt
Normal file
@ -0,0 +1,129 @@
|
||||
Minetest mod library: poschangelib
|
||||
==================================
|
||||
version 0.1
|
||||
|
||||
See LICENSE for license information
|
||||
|
||||
This lib adds utilities to watch player movements and trigger things when they are
|
||||
spotted moving.
|
||||
|
||||
It does nothing by itself but aim to ease event based upon players or item
|
||||
moving.
|
||||
|
||||
All positions are rounded to node position (integer coordinates).
|
||||
|
||||
Summary
|
||||
- General warning
|
||||
- Watch players' movements
|
||||
- Watch players walking on particular nodes
|
||||
- Add _on_walk on nodes
|
||||
- Configuration/Performances tweaking
|
||||
|
||||
|
||||
General warning
|
||||
---------------
|
||||
|
||||
This mod may be resources consuming. The mods relying upon this lib should use
|
||||
small functions not to decrease the server performances too much.
|
||||
|
||||
The more functions are provided, the more the server can lag (but probably a little
|
||||
less than running every of them without the lib).
|
||||
|
||||
|
||||
|
||||
Watch player's movements
|
||||
------------------------
|
||||
|
||||
Use poschangelib.add_player_pos_listener(name, my_callback)
|
||||
|
||||
Name is the identifier the listener, to use in remove_player_pos_listener. You should
|
||||
follow the naming convention like for node names. See http://dev.minetest.net/Intro
|
||||
|
||||
The my_callback is a function that takes 3 arguments: the player, last known position
|
||||
and new position.
|
||||
|
||||
On first call (once a player joins) the last known position will be nil. If your
|
||||
listener does something in that case, it will be called shortly after the player
|
||||
reconnects. It may so be triggered twice from the same position, before leaving and
|
||||
after joining.
|
||||
|
||||
Be aware that the new position may not always be a neigrbor of the old one.
|
||||
When on teleporting, programatic moves with setpos or moving fast it may be far away.
|
||||
|
||||
Quick code sample:
|
||||
|
||||
local function my_callback(player, old_pos, new_pos)
|
||||
if old_pos == nil then
|
||||
minetest.chat_send_player(player:get_player_name(), 'Welcome to the world!')
|
||||
else
|
||||
minetest.chat_send_player(player:get_player_name(),
|
||||
"You are now at x:" .. new_pos.x .. ", y:" .. new_pos.y ..
|
||||
"z:" .. new_pos.z)
|
||||
end
|
||||
end
|
||||
poschangelib.add_player_pos_listener("sample:pos_listener", my_callback)
|
||||
|
||||
|
||||
|
||||
Watch player walking on particular nodes
|
||||
----------------------------------------
|
||||
|
||||
Use poschangelib.add_player_walk_listener(name, my_callback, nodenames)
|
||||
|
||||
The name is used in the same way as for player position listeners. It aims at reducing
|
||||
the number of time the stepped node is fetched to share it accross all listeners.
|
||||
|
||||
The callback is a function that takes 4 arguments: the player, the position, the node
|
||||
stepped on and that node description.
|
||||
See http://dev.minetest.net/minetest.register_node for node description.
|
||||
|
||||
You can register the listener for a list of node name or groups, in the same way you
|
||||
do it to register an ABM. See http://dev.minetest.net/register_abm
|
||||
|
||||
For example:
|
||||
local function flop(player, pos, node, desc)
|
||||
minetest.chat_send_player(player:get_player_name(), 'Flop flop')
|
||||
end
|
||||
poschangelib.add_player_walk_listener('sample:flop', flop, {'default:dirt_with_grass'})
|
||||
|
||||
local function toptop(player, pos, node, desc)
|
||||
minetest.chat_send_player(player:get_player_name(), 'Top top top')
|
||||
end
|
||||
poschangelib.add_player_walk_listener('sample:top', toptop, {'group:choppy'})
|
||||
|
||||
|
||||
|
||||
Add _on_walk_over to nodes
|
||||
-------------------------
|
||||
|
||||
This behaviour is ported from the walkover mod only for compatibility.
|
||||
https://forum.minetest.net/viewtopic.php?f=9&t=15991
|
||||
|
||||
A new node property can be added in node definitions:
|
||||
_on_walk_over = <function>
|
||||
|
||||
This function takes the position, the node and the player as argument.
|
||||
|
||||
For compatibility with walkover, you can use on_walk_over (without the underscore
|
||||
prefix) but it is discouraged as stated in the forum post. This support may be dropped
|
||||
at any time when most mods have updated the name.
|
||||
|
||||
A performance tweak disable on walk checks if there is no player walk listener.
|
||||
To be sure the check is done if you are only using _on_walk_over, you may register a
|
||||
dummy listener on a unknown node name.
|
||||
|
||||
|
||||
|
||||
Configuration/Performances tweaking
|
||||
-----------------------------------
|
||||
|
||||
The lib checks for position at a given interval. Default is every 0.3 seconds.
|
||||
|
||||
This can be changed by setting poschangelib.check_interval in minetest.conf
|
||||
or in advanced settings.
|
||||
|
||||
Setting a lower value will make the lib more accurate but will be more demanding
|
||||
on resources (down to 0.05 which is a every server tick).
|
||||
|
||||
If the server is lagging, try increasing the interval. If the server can afford
|
||||
more precise checks you can decrease the value.
|
0
depends.txt
Normal file
0
depends.txt
Normal file
211
init.lua
Normal file
211
init.lua
Normal file
@ -0,0 +1,211 @@
|
||||
poschangelib = {}
|
||||
|
||||
--[[
|
||||
-- File table of contents
|
||||
-- 1. Settings and utilities
|
||||
-- 2. Player position listener functions
|
||||
-- 3. On walk listener functions
|
||||
-- 4. Tools for main loop
|
||||
-- 5. Main loop
|
||||
--]]
|
||||
|
||||
function poschangelib.setting_check_interval()
|
||||
return tonumber(minetest.setting_get('poschangelib.check_interval')) or 0.3
|
||||
end
|
||||
|
||||
--- Table of already called listeners in main loop to prevent triggering them
|
||||
-- more than once per loop (player) if they are registered for more than one event
|
||||
-- (for example triggered on walk on multiple groups)
|
||||
local triggered_listeners = {}
|
||||
|
||||
--- Internal utility to create an empty table on first registration.
|
||||
-- @param mothertable The main table that will hold other tables.
|
||||
-- @param item Key in the main table that should hold a table.
|
||||
-- @return The table in mothertable.item, created if nil.
|
||||
local function get_subtable_or_create(mothertable, item)
|
||||
if mothertable.item == nil then
|
||||
mothertable.item = {}
|
||||
end
|
||||
return mothertable.item
|
||||
end
|
||||
|
||||
--- Check if a listener can be triggered
|
||||
local function is_callable(name)
|
||||
-- Check if not aleady called
|
||||
if triggered_listeners.name then return false end
|
||||
-- Other checks will come here when required
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
-- Player position listeners
|
||||
--]]
|
||||
|
||||
--- Callback table filled with add_player_pos_listener.
|
||||
local player_listeners = {}
|
||||
|
||||
--- Register a callback that will be called everytime a player moves.
|
||||
-- @param name Unique name of the callback. Used to remove.
|
||||
-- @param callback Callback function. Take <player>, <old_pos>, <new_pos> arguments.
|
||||
-- The first call will have <old_pos> set to nil.
|
||||
function poschangelib.add_player_pos_listener(name, callback)
|
||||
if player_listeners[name] then
|
||||
minetest.log('error', 'Player pos listener ' .. name .. ' is already registered')
|
||||
return
|
||||
end
|
||||
player_listeners[name] = callback
|
||||
end
|
||||
|
||||
--- Remove a registered callback. It won't be called anymore.
|
||||
function poschangelib.remove_player_pos_listener(name)
|
||||
if player_listeners[name] then player_listeners[name] = nil end
|
||||
end
|
||||
|
||||
--- Trigger registered callbacks if not already triggered.
|
||||
-- Reset triggered_listeners to be able to recall the callback.
|
||||
local function trigger_player_position_listeners(player, old_pos, pos)
|
||||
for name, callback in pairs(player_listeners) do
|
||||
if is_callable(name) then
|
||||
callback(player, old_pos, pos)
|
||||
triggered_listeners[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
-- Walk listeners
|
||||
--]]
|
||||
|
||||
--- Callback tables filled with add_player_walk_listener.
|
||||
-- Indexed by node groups as "group:name" or node names.
|
||||
local walk_listeners = {}
|
||||
|
||||
--- Register a callback that will be called everytime a player moves on a block.
|
||||
-- @param callback Callback function. Takes <player>, <pos>, <node>,
|
||||
-- <node definition> as arguments.
|
||||
-- Node is the node below the player's position.
|
||||
-- @param nodenames List of node names or group (with 'group:X') to observe.
|
||||
-- The callback will be triggered only if the block has the same name or
|
||||
-- has one of these groups.
|
||||
function poschangelib.add_player_walk_listener(name, callback, nodenames)
|
||||
for _, nodename in ipairs(nodenames) do
|
||||
if not walk_listeners[nodename] then walk_listeners[nodename] = {} end
|
||||
if walk_listeners[nodename][name] then
|
||||
minetest.log('error', 'Walk listener ' .. name .. ' is already registered')
|
||||
end
|
||||
walk_listeners[nodename][name] = callback
|
||||
end
|
||||
end
|
||||
|
||||
function poschangelib.remove_player_walk_listener(name, nodenames)
|
||||
local counts = {}
|
||||
for _, nodename in ipairs(nodenames) do
|
||||
if not counts[nodename] then counts[nodename] = 0 end
|
||||
counts[nodename] = counts[nodename] + 1
|
||||
if walk_listeners[nodename] and walk_listeners[nodename][name] then
|
||||
walk_listeners[nodename][name] = nil
|
||||
counts[nodename] = counts[nodename] - 1
|
||||
end
|
||||
end
|
||||
-- If no listener left for the group, remove the group
|
||||
-- to be able to skip node check if there are none left
|
||||
for _, nodename in pairs(counts) do
|
||||
if counts[nodename] == 0 then
|
||||
walk_listeners[nodename] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function trigger_player_walk_listeners(player, pos, node, node_def, nodename)
|
||||
for name, callback in pairs(walk_listeners[nodename]) do
|
||||
if is_callable(name) then
|
||||
callback(player, pos, node, node_def)
|
||||
triggered_listeners[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
-- Tools for main loop
|
||||
--]]
|
||||
|
||||
--- Table of last rounded registered position of each players.
|
||||
local player_last_pos = {}
|
||||
local function remove_last_pos_on_leave(player)
|
||||
player_last_pos[player:get_player_name()] = nil
|
||||
end
|
||||
minetest.register_on_leaveplayer(remove_last_pos_on_leave)
|
||||
--- Check if position has changed for the player.
|
||||
-- @param player The player object.
|
||||
-- @returns {old_pos, new_pos} if the position has changed, nil otherwise
|
||||
local function get_updated_positions(player)
|
||||
local pos = vector.round(player:getpos())
|
||||
local old_pos = player_last_pos[player:get_player_name()]
|
||||
local ret = nil
|
||||
if old_pos == nil then
|
||||
-- Position of the player was set
|
||||
ret = {old = old_pos, new = pos}
|
||||
elseif pos then
|
||||
-- Check for position change
|
||||
if not vector.equals(old_pos, pos) then
|
||||
ret = {old = old_pos, new = pos}
|
||||
end
|
||||
end
|
||||
player_last_pos[player:get_player_name()] = pos
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Check and call on_walk triggers if required.
|
||||
local function check_on_walk_triggers(player, pos)
|
||||
local pos_below = vector.new(pos.x, pos.y - 1, pos.z)
|
||||
local node_below = minetest.get_node(pos_below)
|
||||
local node_def = minetest.registered_nodes[node_below.name]
|
||||
-- Trigger by node name
|
||||
if walk_listeners[node_below.name] then
|
||||
trigger_player_walk_listeners(player, pos_below, node_below, node_def, node_below.name)
|
||||
end
|
||||
-- Trigger by group
|
||||
local groups_below = node_def.groups
|
||||
if groups_below then
|
||||
for group, level in pairs(groups_below) do
|
||||
local group_name = 'group:' .. group
|
||||
if level > 0 and walk_listeners[group_name] then
|
||||
trigger_player_walk_listeners(player, pos_below, node_below, node_def, group_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Trigger _on_walk
|
||||
if node_def._on_walk then
|
||||
node_def._on_walk(pos_below, node_below, player)
|
||||
elseif node_below.on_walk then
|
||||
node_def.on_walk(pos_below, node_below, player)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
-- Main loop
|
||||
--]]
|
||||
|
||||
local timer = 0
|
||||
local function loop(dtime)
|
||||
timer = timer + dtime
|
||||
-- Wait loop
|
||||
if timer + dtime < poschangelib.setting_check_interval() then return end
|
||||
timer = 0
|
||||
-- Player checks
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local poss = get_updated_positions(player)
|
||||
if poss then
|
||||
trigger_player_position_listeners(player, poss.old, poss.new)
|
||||
if poss.old then -- Don't trigger on join
|
||||
check_on_walk_triggers(player, poss.new)
|
||||
end
|
||||
-- Reset the triggered listener to allow the next player to trigger them
|
||||
triggered_listeners = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.register_globalstep(loop)
|
3
settingtypes.txt
Normal file
3
settingtypes.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# Interval in seconds between position checking
|
||||
# The lesser it is, the more accurate it is but also the more resources demanding.
|
||||
poschangelib.check_interval (Check interval) float 0.3 0.05
|
Loading…
Reference in New Issue
Block a user