1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include "cpp_api/s_player.h"
21 #include "cpp_api/s_internal.h"
22 #include "common/c_converter.h"
23 #include "common/c_content.h"
24 #include "debug.h"
25 #include "inventorymanager.h"
26 #include "lua_api/l_inventory.h"
27 #include "lua_api/l_item.h"
28 #include "util/string.h"
29 
on_newplayer(ServerActiveObject * player)30 void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
31 {
32 	SCRIPTAPI_PRECHECKHEADER
33 
34 	// Get core.registered_on_newplayers
35 	lua_getglobal(L, "core");
36 	lua_getfield(L, -1, "registered_on_newplayers");
37 	// Call callbacks
38 	objectrefGetOrCreate(L, player);
39 	runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
40 }
41 
on_dieplayer(ServerActiveObject * player,const PlayerHPChangeReason & reason)42 void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player, const PlayerHPChangeReason &reason)
43 {
44 	SCRIPTAPI_PRECHECKHEADER
45 
46 	// Get callback table
47 	lua_getglobal(L, "core");
48 	lua_getfield(L, -1, "registered_on_dieplayers");
49 
50 	// Push arguments
51 	objectrefGetOrCreate(L, player);
52 	pushPlayerHPChangeReason(L, reason);
53 
54 	// Run callbacks
55 	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
56 }
57 
on_punchplayer(ServerActiveObject * player,ServerActiveObject * hitter,float time_from_last_punch,const ToolCapabilities * toolcap,v3f dir,s16 damage)58 bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
59 		ServerActiveObject *hitter,
60 		float time_from_last_punch,
61 		const ToolCapabilities *toolcap,
62 		v3f dir,
63 		s16 damage)
64 {
65 	SCRIPTAPI_PRECHECKHEADER
66 	// Get core.registered_on_punchplayers
67 	lua_getglobal(L, "core");
68 	lua_getfield(L, -1, "registered_on_punchplayers");
69 	// Call callbacks
70 	objectrefGetOrCreate(L, player);
71 	objectrefGetOrCreate(L, hitter);
72 	lua_pushnumber(L, time_from_last_punch);
73 	push_tool_capabilities(L, *toolcap);
74 	push_v3f(L, dir);
75 	lua_pushnumber(L, damage);
76 	runCallbacks(6, RUN_CALLBACKS_MODE_OR);
77 	return readParam<bool>(L, -1);
78 }
79 
on_rightclickplayer(ServerActiveObject * player,ServerActiveObject * clicker)80 void ScriptApiPlayer::on_rightclickplayer(ServerActiveObject *player,
81                 ServerActiveObject *clicker)
82 {
83 	SCRIPTAPI_PRECHECKHEADER
84 	// Get core.registered_on_rightclickplayers
85 	lua_getglobal(L, "core");
86 	lua_getfield(L, -1, "registered_on_rightclickplayers");
87 	// Call callbacks
88 	objectrefGetOrCreate(L, player);
89 	objectrefGetOrCreate(L, clicker);
90 	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
91 }
92 
on_player_hpchange(ServerActiveObject * player,s32 hp_change,const PlayerHPChangeReason & reason)93 s32 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
94 	s32 hp_change, const PlayerHPChangeReason &reason)
95 {
96 	SCRIPTAPI_PRECHECKHEADER
97 
98 	int error_handler = PUSH_ERROR_HANDLER(L);
99 
100 	// Get core.registered_on_player_hpchange
101 	lua_getglobal(L, "core");
102 	lua_getfield(L, -1, "registered_on_player_hpchange");
103 	lua_remove(L, -2);
104 
105 	// Push arguments
106 	objectrefGetOrCreate(L, player);
107 	lua_pushnumber(L, hp_change);
108 	pushPlayerHPChangeReason(L, reason);
109 
110 	// Call callbacks
111 	PCALL_RES(lua_pcall(L, 3, 1, error_handler));
112 	hp_change = lua_tointeger(L, -1);
113 	lua_pop(L, 2); // Pop result and error handler
114 	return hp_change;
115 }
116 
on_respawnplayer(ServerActiveObject * player)117 bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
118 {
119 	SCRIPTAPI_PRECHECKHEADER
120 
121 	// Get core.registered_on_respawnplayers
122 	lua_getglobal(L, "core");
123 	lua_getfield(L, -1, "registered_on_respawnplayers");
124 	// Call callbacks
125 	objectrefGetOrCreate(L, player);
126 	runCallbacks(1, RUN_CALLBACKS_MODE_OR);
127 	return readParam<bool>(L, -1);
128 }
129 
on_prejoinplayer(const std::string & name,const std::string & ip,std::string * reason)130 bool ScriptApiPlayer::on_prejoinplayer(
131 	const std::string &name,
132 	const std::string &ip,
133 	std::string *reason)
134 {
135 	SCRIPTAPI_PRECHECKHEADER
136 
137 	// Get core.registered_on_prejoinplayers
138 	lua_getglobal(L, "core");
139 	lua_getfield(L, -1, "registered_on_prejoinplayers");
140 	lua_pushstring(L, name.c_str());
141 	lua_pushstring(L, ip.c_str());
142 	runCallbacks(2, RUN_CALLBACKS_MODE_OR);
143 	if (lua_isstring(L, -1)) {
144 		reason->assign(readParam<std::string>(L, -1));
145 		return true;
146 	}
147 	return false;
148 }
149 
can_bypass_userlimit(const std::string & name,const std::string & ip)150 bool ScriptApiPlayer::can_bypass_userlimit(const std::string &name, const std::string &ip)
151 {
152 	SCRIPTAPI_PRECHECKHEADER
153 
154 	// Get core.registered_on_prejoinplayers
155 	lua_getglobal(L, "core");
156 	lua_getfield(L, -1, "registered_can_bypass_userlimit");
157 	lua_pushstring(L, name.c_str());
158 	lua_pushstring(L, ip.c_str());
159 	runCallbacks(2, RUN_CALLBACKS_MODE_OR);
160 	return lua_toboolean(L, -1);
161 }
162 
on_joinplayer(ServerActiveObject * player,s64 last_login)163 void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player, s64 last_login)
164 {
165 	SCRIPTAPI_PRECHECKHEADER
166 
167 	// Get core.registered_on_joinplayers
168 	lua_getglobal(L, "core");
169 	lua_getfield(L, -1, "registered_on_joinplayers");
170 	// Call callbacks
171 	objectrefGetOrCreate(L, player);
172 	if (last_login != -1)
173 		lua_pushinteger(L, last_login);
174 	else
175 		lua_pushnil(L);
176 	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
177 }
178 
on_leaveplayer(ServerActiveObject * player,bool timeout)179 void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player,
180 		bool timeout)
181 {
182 	SCRIPTAPI_PRECHECKHEADER
183 
184 	// Get core.registered_on_leaveplayers
185 	lua_getglobal(L, "core");
186 	lua_getfield(L, -1, "registered_on_leaveplayers");
187 	// Call callbacks
188 	objectrefGetOrCreate(L, player);
189 	lua_pushboolean(L, timeout);
190 	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
191 }
192 
on_cheat(ServerActiveObject * player,const std::string & cheat_type)193 void ScriptApiPlayer::on_cheat(ServerActiveObject *player,
194 		const std::string &cheat_type)
195 {
196 	SCRIPTAPI_PRECHECKHEADER
197 
198 	// Get core.registered_on_cheats
199 	lua_getglobal(L, "core");
200 	lua_getfield(L, -1, "registered_on_cheats");
201 	// Call callbacks
202 	objectrefGetOrCreate(L, player);
203 	lua_newtable(L);
204 	lua_pushlstring(L, cheat_type.c_str(), cheat_type.size());
205 	lua_setfield(L, -2, "type");
206 	runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
207 }
208 
on_playerReceiveFields(ServerActiveObject * player,const std::string & formname,const StringMap & fields)209 void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
210 		const std::string &formname,
211 		const StringMap &fields)
212 {
213 	SCRIPTAPI_PRECHECKHEADER
214 
215 	// Get core.registered_on_player_receive_fields
216 	lua_getglobal(L, "core");
217 	lua_getfield(L, -1, "registered_on_player_receive_fields");
218 	// Call callbacks
219 	// param 1
220 	objectrefGetOrCreate(L, player);
221 	// param 2
222 	lua_pushstring(L, formname.c_str());
223 	// param 3
224 	lua_newtable(L);
225 	StringMap::const_iterator it;
226 	for (it = fields.begin(); it != fields.end(); ++it) {
227 		const std::string &name = it->first;
228 		const std::string &value = it->second;
229 		lua_pushstring(L, name.c_str());
230 		lua_pushlstring(L, value.c_str(), value.size());
231 		lua_settable(L, -3);
232 	}
233 	runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC);
234 }
235 
on_authplayer(const std::string & name,const std::string & ip,bool is_success)236 void ScriptApiPlayer::on_authplayer(const std::string &name, const std::string &ip, bool is_success)
237 {
238 	SCRIPTAPI_PRECHECKHEADER
239 
240 	// Get core.registered_on_authplayers
241 	lua_getglobal(L, "core");
242 	lua_getfield(L, -1, "registered_on_authplayers");
243 
244 	// Call callbacks
245 	lua_pushstring(L, name.c_str());
246 	lua_pushstring(L, ip.c_str());
247 	lua_pushboolean(L, is_success);
248 	runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
249 }
250 
pushMoveArguments(const MoveAction & ma,int count,ServerActiveObject * player)251 void ScriptApiPlayer::pushMoveArguments(
252 		const MoveAction &ma, int count,
253 		ServerActiveObject *player)
254 {
255 	lua_State *L = getStack();
256 	objectrefGetOrCreate(L, player); // player
257 	lua_pushstring(L, "move");       // action
258 	InvRef::create(L, ma.from_inv);  // inventory
259 	lua_newtable(L);
260 	{
261 		// Table containing the action information
262 		lua_pushstring(L, ma.from_list.c_str());
263 		lua_setfield(L, -2, "from_list");
264 		lua_pushstring(L, ma.to_list.c_str());
265 		lua_setfield(L, -2, "to_list");
266 
267 		lua_pushinteger(L, ma.from_i + 1);
268 		lua_setfield(L, -2, "from_index");
269 		lua_pushinteger(L, ma.to_i + 1);
270 		lua_setfield(L, -2, "to_index");
271 
272 		lua_pushinteger(L, count);
273 		lua_setfield(L, -2, "count");
274 	}
275 }
276 
pushPutTakeArguments(const char * method,const InventoryLocation & loc,const std::string & listname,int index,const ItemStack & stack,ServerActiveObject * player)277 void ScriptApiPlayer::pushPutTakeArguments(
278 		const char *method, const InventoryLocation &loc,
279 		const std::string &listname, int index, const ItemStack &stack,
280 		ServerActiveObject *player)
281 {
282 	lua_State *L = getStack();
283 	objectrefGetOrCreate(L, player); // player
284 	lua_pushstring(L, method);       // action
285 	InvRef::create(L, loc);          // inventory
286 	lua_newtable(L);
287 	{
288 		// Table containing the action information
289 		lua_pushstring(L, listname.c_str());
290 		lua_setfield(L, -2, "listname");
291 
292 		lua_pushinteger(L, index + 1);
293 		lua_setfield(L, -2, "index");
294 
295 		LuaItemStack::create(L, stack);
296 		lua_setfield(L, -2, "stack");
297 	}
298 }
299 
300 // Return number of accepted items to be moved
player_inventory_AllowMove(const MoveAction & ma,int count,ServerActiveObject * player)301 int ScriptApiPlayer::player_inventory_AllowMove(
302 		const MoveAction &ma, int count,
303 		ServerActiveObject *player)
304 {
305 	SCRIPTAPI_PRECHECKHEADER
306 
307 	lua_getglobal(L, "core");
308 	lua_getfield(L, -1, "registered_allow_player_inventory_actions");
309 	pushMoveArguments(ma, count, player);
310 	runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
311 
312 	return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : count;
313 }
314 
315 // Return number of accepted items to be put
player_inventory_AllowPut(const MoveAction & ma,const ItemStack & stack,ServerActiveObject * player)316 int ScriptApiPlayer::player_inventory_AllowPut(
317 		const MoveAction &ma, const ItemStack &stack,
318 		ServerActiveObject *player)
319 {
320 	SCRIPTAPI_PRECHECKHEADER
321 
322 	lua_getglobal(L, "core");
323 	lua_getfield(L, -1, "registered_allow_player_inventory_actions");
324 	pushPutTakeArguments("put", ma.to_inv, ma.to_list, ma.to_i, stack, player);
325 	runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
326 
327 	return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count;
328 }
329 
330 // Return number of accepted items to be taken
player_inventory_AllowTake(const MoveAction & ma,const ItemStack & stack,ServerActiveObject * player)331 int ScriptApiPlayer::player_inventory_AllowTake(
332 		const MoveAction &ma, const ItemStack &stack,
333 		ServerActiveObject *player)
334 {
335 	SCRIPTAPI_PRECHECKHEADER
336 
337 	lua_getglobal(L, "core");
338 	lua_getfield(L, -1, "registered_allow_player_inventory_actions");
339 	pushPutTakeArguments("take", ma.from_inv, ma.from_list, ma.from_i, stack, player);
340 	runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
341 
342 	return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count;
343 }
344 
345 // Report moved items
player_inventory_OnMove(const MoveAction & ma,int count,ServerActiveObject * player)346 void ScriptApiPlayer::player_inventory_OnMove(
347 		const MoveAction &ma, int count,
348 		ServerActiveObject *player)
349 {
350 	SCRIPTAPI_PRECHECKHEADER
351 
352 	lua_getglobal(L, "core");
353 	lua_getfield(L, -1, "registered_on_player_inventory_actions");
354 	pushMoveArguments(ma, count, player);
355 	runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
356 }
357 
358 // Report put items
player_inventory_OnPut(const MoveAction & ma,const ItemStack & stack,ServerActiveObject * player)359 void ScriptApiPlayer::player_inventory_OnPut(
360 		const MoveAction &ma, const ItemStack &stack,
361 		ServerActiveObject *player)
362 {
363 	SCRIPTAPI_PRECHECKHEADER
364 
365 	lua_getglobal(L, "core");
366 	lua_getfield(L, -1, "registered_on_player_inventory_actions");
367 	pushPutTakeArguments("put", ma.to_inv, ma.to_list, ma.to_i, stack, player);
368 	runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
369 }
370 
371 // Report taken items
player_inventory_OnTake(const MoveAction & ma,const ItemStack & stack,ServerActiveObject * player)372 void ScriptApiPlayer::player_inventory_OnTake(
373 		const MoveAction &ma, const ItemStack &stack,
374 		ServerActiveObject *player)
375 {
376 	SCRIPTAPI_PRECHECKHEADER
377 
378 	lua_getglobal(L, "core");
379 	lua_getfield(L, -1, "registered_on_player_inventory_actions");
380 	pushPutTakeArguments("take", ma.from_inv, ma.from_list, ma.from_i, stack, player);
381 	runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
382 }
383