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