minetest_modding_book/_en/quality/common_mistakes.md

168 lines
4.8 KiB
Markdown
Raw Normal View History

2018-04-03 15:11:27 +03:00
---
title: Common Mistakes
layout: default
2018-07-15 21:36:35 +03:00
root: ../..
2019-08-14 16:45:42 +03:00
idx: 8.1
2018-07-15 21:13:16 +03:00
redirect_from: /en/chapters/common_mistakes.html
2018-04-03 15:11:27 +03:00
---
## Introduction <!-- omit in toc -->
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
This chapter details common mistakes, and how to avoid them.
2018-04-03 15:11:27 +03:00
- [Never Store ObjectRefs (ie: players or entities)](#never-store-objectrefs-ie-players-or-entities)
- [Don't Trust Formspec Submissions](#dont-trust-formspec-submissions)
- [Set ItemStacks After Changing Them](#set-itemstacks-after-changing-them)
2018-04-03 15:11:27 +03:00
## Never Store ObjectRefs (ie: players or entities)
2018-10-03 20:58:35 +03:00
If the object an ObjectRef represents is deleted - for example, if the player goes
offline or the entity is unloaded - then calling methods on that object
will result in a crash.
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
For example, don't do this:
2018-04-03 15:11:27 +03:00
```lua
2018-04-03 15:11:27 +03:00
minetest.register_on_joinplayer(function(player)
local function func()
local pos = player:get_pos() -- BAD!
2018-10-03 20:58:35 +03:00
-- `player` is stored then accessed later.
2018-04-03 15:11:27 +03:00
-- If the player leaves in that second, the server *will* crash.
end
minetest.after(1, func)
foobar[player:get_player_name()] = player
-- RISKY
2018-10-03 20:58:35 +03:00
-- It's not recommended to do this.
-- Use minetest.get_connected_players() and
-- minetest.get_player_by_name() instead.
2018-04-03 15:11:27 +03:00
end)
```
2018-04-03 15:11:27 +03:00
2018-10-27 05:10:37 +03:00
Do this instead:
2018-04-03 15:11:27 +03:00
```lua
2018-04-03 15:11:27 +03:00
minetest.register_on_joinplayer(function(player)
local function func(name)
-- Attempt to get the ref again
local player = minetest.get_player_by_name(name)
-- Check that the player is still online
if player then
-- Yay! This is fine
local pos = player:get_pos()
end
end
-- Pass the name into the function
minetest.after(1, func, player:get_player_name())
end)
```
2018-04-03 15:11:27 +03:00
## Don't Trust Formspec Submissions
2018-10-27 05:10:37 +03:00
Malicious clients can submit formspecs whenever they like, with
whatever content they like.
2018-04-03 15:11:27 +03:00
2018-10-27 05:10:37 +03:00
For example, the following code has a vulnerability which allows players to
2018-10-03 20:58:35 +03:00
give themselves moderator privileges:
2018-04-03 15:11:27 +03:00
```lua
2018-04-03 15:11:27 +03:00
local function show_formspec(name)
if not minetest.check_player_privs(name, { privs = true }) then
return false
end
minetest.show_formspec(name, "modman:modman", [[
size[3,2]
field[0,0;3,1;target;Name;]
button_exit[0,1;3,1;sub;Promote]
]])
return true
})
2018-09-24 19:16:00 +03:00
minetest.register_on_player_receive_fields(function(player,
formname, fields)
2018-04-03 15:11:27 +03:00
-- BAD! Missing privilege check here!
local privs = minetest.get_player_privs(fields.target)
privs.kick = true
privs.ban = true
minetest.set_player_privs(fields.target, privs)
return true
end)
```
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
Add a privilege check to solve this:
2018-04-03 15:11:27 +03:00
```lua
2018-09-24 19:16:00 +03:00
minetest.register_on_player_receive_fields(function(player,
formname, fields)
2018-04-03 15:11:27 +03:00
if not minetest.check_player_privs(name, { privs = true }) then
return false
end
-- code
end)
```
2018-04-03 15:11:27 +03:00
## Set ItemStacks After Changing Them
2018-10-03 20:58:35 +03:00
Have you noticed that it's simply called an `ItemStack` in the API, not an `ItemStackRef`,
similar to `InvRef`? This is because an `ItemStack` isn't a reference - it's a
2018-04-12 03:06:07 +03:00
copy. Stacks work on a copy of the data rather than the stack in the inventory.
This means that modifying a stack won't actually modify that stack in the inventory.
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
For example, don't do this:
2018-04-03 15:11:27 +03:00
```lua
2018-04-03 15:11:27 +03:00
local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Partially eaten")
-- BAD! Modification will be lost
```
2018-04-03 15:11:27 +03:00
2018-10-27 05:10:37 +03:00
Do this instead:
2018-04-03 15:11:27 +03:00
```lua
2018-04-03 15:11:27 +03:00
local inv = player:get_inventory()
local stack = inv:get_stack("main", 1)
stack:get_meta():set_string("description", "Partially eaten")
inv:set_stack("main", 1, stack)
-- Correct! Item stack is set
```
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
The behaviour of callbacks is slightly more complicated. Modifying an `ItemStack` you
are given will change it for the caller too, and any subsequent callbacks. However,
2018-04-03 15:11:27 +03:00
it will only be saved in the engine if the callback caller sets it.
```lua
2018-09-24 19:16:00 +03:00
minetest.register_on_item_eat(function(hp_change, replace_with_item,
itemstack, user, pointed_thing)
2018-04-03 15:11:27 +03:00
itemstack:get_meta():set_string("description", "Partially eaten")
2018-09-24 19:16:00 +03:00
-- Almost correct! Data will be lost if another
-- callback cancels the behaviour
2018-04-03 15:11:27 +03:00
end)
```
2018-04-03 15:11:27 +03:00
2018-10-03 20:58:35 +03:00
If no callbacks cancel this, the stack will be set and the description will be updated,
2018-10-27 05:10:37 +03:00
but if a callback does cancel this, then the update may be lost.
2018-10-03 20:58:35 +03:00
It's better to do this instead:
2018-04-03 15:11:27 +03:00
```lua
2018-09-24 19:16:00 +03:00
minetest.register_on_item_eat(function(hp_change, replace_with_item,
itemstack, user, pointed_thing)
2018-04-03 15:11:27 +03:00
itemstack:get_meta():set_string("description", "Partially eaten")
2018-09-24 19:16:00 +03:00
user:get_inventory():set_stack("main", user:get_wield_index(),
itemstack)
2018-04-03 15:11:27 +03:00
-- Correct, description will always be set!
end)
```
2018-04-03 15:11:27 +03:00
If the callbacks cancel or the callback runner doesn't set the stack,
2018-10-03 20:58:35 +03:00
then the update will still be set.
If the callbacks or the callback runner set the stack, then the use of
2018-05-01 02:00:27 +03:00
set_stack doesn't matter.