From 8ad3dad137df740ce63101bf224e977e66b9df2c Mon Sep 17 00:00:00 2001 From: Vincent Glize Date: Sat, 8 Apr 2017 12:26:45 +0200 Subject: [PATCH] LocalPlayer api lua --- doc/client_lua_api.md | 116 +++++++- src/client.cpp | 4 +- src/script/clientscripting.cpp | 8 + src/script/clientscripting.h | 2 + src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_localplayer.cpp | 416 +++++++++++++++++++++++++++ src/script/lua_api/l_localplayer.h | 95 ++++++ 7 files changed, 640 insertions(+), 2 deletions(-) create mode 100644 src/script/lua_api/l_localplayer.cpp create mode 100644 src/script/lua_api/l_localplayer.h diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index bee53636c..f762ae150 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -691,6 +691,8 @@ Call these functions only at load time! ### Player * `minetest.get_wielded_item()` * Returns the itemstack the local player is holding +* `minetest.localplayer` + * returns LocalPlayer handle. See LocalPlayer below. ### Client Environment * `minetest.get_player_names()` @@ -712,7 +714,7 @@ Call these functions only at load time! * `minetest.write_json(data[, styled])`: returns a string or `nil` and an error message * Convert a Lua table into a JSON string * styled: Outputs in a human-readable format if this is set, defaults to false - * Unserializable things like functions and userdata will cause an error. + * Unserializable things like functions and userdata are saved as null. * **Warning**: JSON is more strict than the Lua table format. 1. You can only use strings and positive integers of at least one as keys. 2. You can not mix string and integer keys. @@ -779,6 +781,118 @@ An interface to manipulate minimap on client UI * `get_mode()`: returns the current minimap mode * `toggle_shape()`: toggles minimap shape to round or square. +### LocalPlayer +An interface to retrieve information about the player. The player is +not accessible until the client is fully done loading and therefore +not at module init time. + +To get the localplayer handle correctly, use `on_connect()` as follows: + +```lua +local localplayer +minetest.register_on_connect(function() + localplayer = minetest.localplayer +end) +``` + +Methods: + +* `get_pos()` + * returns current player current position +* `get_velocity()` + * returns player speed vector +* `get_hp()` + * returns player HP +* `got_teleported()` + * returns true if player was teleported +* `is_attached()` + * returns true if player is attached +* `is_touching_ground()` + * returns true if player touching ground +* `is_in_liquid()` + * returns true if player is in a liquid (This oscillates so that the player jumps a bit above the surface) +* `is_in_liquid_stable()` + * returns true if player is in a stable liquid (This is more stable and defines the maximum speed of the player) +* `get_liquid_viscosity()` + * returns liquid viscosity (Gets the viscosity of liquid to calculate friction) +* `is_climbing()` + * returns true if player is climbing +* `swimming_vertical()` + * returns true if player is swimming in vertical +* `get_physics_override()` + * returns: + +```lua + { + speed = float, + jump = float, + gravity = float, + sneak = boolean, + sneak_glitch = boolean + } +``` + +* `get_override_pos()` + * returns override position +* `get_last_pos()` + * returns last player position before the current client step +* `get_last_velocity()` + * returns last player speed +* `get_breath()` + * returns the player's breath +* `get_look_dir()` + * returns look direction vector +* `get_look_horizontal()` + * returns look horizontal angle +* `get_look_vertical()` + * returns look vertical angle +* `get_eye_pos()` + * returns the player's eye position +* `get_eye_offset()` + * returns the player's eye shift vector +* `get_movement_acceleration()` + * returns acceleration of the player in different environments: + +```lua + { + fast = float, + air = float, + default = float, + } +``` + +* `get_movement_speed()` + * returns player's speed in different environments: + +```lua + { + walk = float, + jump = float, + crouch = float, + fast = float, + climb = float, + } +``` + +* `get_movement()` + * returns player's movement in different environments: + +```lua + { + liquid_fluidity = float, + liquid_sink = float, + liquid_fluidity_smooth = float, + gravity = float, + } +``` + +* `get_last_look_horizontal()`: + * returns last look horizontal angle +* `get_last_look_vertical()`: + * returns last look vertical angle +* `get_key_pressed()`: + * returns last key typed by the player + ### Settings An interface to read config files in the format of `minetest.conf`. diff --git a/src/client.cpp b/src/client.cpp index 3a3e33cfd..246525f62 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1865,8 +1865,10 @@ void Client::afterContentReceived(IrrlichtDevice *device) m_state = LC_Ready; sendReady(); - if (g_settings->getBool("enable_client_modding")) + if (g_settings->getBool("enable_client_modding")) { + m_script->on_client_ready(m_env.getLocalPlayer()); m_script->on_connect(); + } text = wgettext("Done!"); draw_load_screen(text, device, guienv, 0, 100); diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp index 6f91b82ec..ba3f910c0 100644 --- a/src/script/clientscripting.cpp +++ b/src/script/clientscripting.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_util.h" #include "lua_api/l_item.h" #include "lua_api/l_nodemeta.h" +#include "lua_api/l_localplayer.h" ClientScripting::ClientScripting(Client *client): ScriptApiBase() @@ -69,4 +70,11 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) StorageRef::Register(L); LuaMinimap::Register(L); NodeMetaRef::RegisterClient(L); + LuaLocalPlayer::Register(L); +} + +void ClientScripting::on_client_ready(LocalPlayer *localplayer) +{ + lua_State *L = getStack(); + LuaLocalPlayer::create(L, localplayer); } diff --git a/src/script/clientscripting.h b/src/script/clientscripting.h index e2a91f695..df94e8b71 100644 --- a/src/script/clientscripting.h +++ b/src/script/clientscripting.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" class Client; +class LocalPlayer; class ClientScripting: virtual public ScriptApiBase, public ScriptApiSecurity, @@ -33,6 +34,7 @@ class ClientScripting: { public: ClientScripting(Client *client); + void on_client_ready(LocalPlayer *localplayer); private: virtual void InitializeModApi(lua_State *L, int top); diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index f7f22870e..b03e94a09 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -28,4 +28,5 @@ set(client_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_minimap.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_sound.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_localplayer.cpp PARENT_SCOPE) diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp new file mode 100644 index 000000000..dc07d2445 --- /dev/null +++ b/src/script/lua_api/l_localplayer.cpp @@ -0,0 +1,416 @@ +/* +Minetest +Copyright (C) 2017 Dumbeldor, Vincent Glize + +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. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "script/common/c_converter.h" +#include "l_localplayer.h" +#include "l_internal.h" + +LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m) +{ + m_localplayer = m; +} + +void LuaLocalPlayer::create(lua_State *L, LocalPlayer *m) +{ + LuaLocalPlayer *o = new LuaLocalPlayer(m); + *(void **) (lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + + // Keep localplayer object stack id + int localplayer_object = lua_gettop(L); + + lua_getglobal(L, "core"); + luaL_checktype(L, -1, LUA_TTABLE); + int coretable = lua_gettop(L); + + lua_pushvalue(L, localplayer_object); + lua_setfield(L, coretable, "localplayer"); +} + +int LuaLocalPlayer::l_get_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getSpeed() / BS); + return 1; +} + +int LuaLocalPlayer::l_get_hp(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->hp); + return 1; +} + +int LuaLocalPlayer::l_get_name(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushstring(L, player->getName()); + return 1; +} + +int LuaLocalPlayer::l_is_teleported(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->got_teleported); + return 1; +} + +int LuaLocalPlayer::l_is_attached(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->isAttached); + return 1; +} + +int LuaLocalPlayer::l_is_touching_ground(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->touching_ground); + return 1; +} + +int LuaLocalPlayer::l_is_in_liquid(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->in_liquid); + return 1; +} + +int LuaLocalPlayer::l_is_in_liquid_stable(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->in_liquid_stable); + return 1; +} + +int LuaLocalPlayer::l_get_liquid_viscosity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->liquid_viscosity); + return 1; +} + +int LuaLocalPlayer::l_is_climbing(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->is_climbing); + return 1; +} + +int LuaLocalPlayer::l_swimming_vertical(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushboolean(L, player->swimming_vertical); + return 1; +} + +int LuaLocalPlayer::l_get_physics_override(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + lua_pushnumber(L, player->physics_override_speed); + lua_setfield(L, -2, "speed"); + + lua_pushnumber(L, player->physics_override_jump); + lua_setfield(L, -2, "jump"); + + lua_pushnumber(L, player->physics_override_gravity); + lua_setfield(L, -2, "gravity"); + + lua_pushboolean(L, player->physics_override_sneak); + lua_setfield(L, -2, "sneak"); + + lua_pushboolean(L, player->physics_override_sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + + return 1; +} + +int LuaLocalPlayer::l_get_override_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->overridePosition); + return 1; +} + +int LuaLocalPlayer::l_get_last_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->last_position / BS); + return 1; +} + +int LuaLocalPlayer::l_get_last_velocity(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->last_speed); + return 1; +} + +int LuaLocalPlayer::l_get_last_look_vertical(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, -1.0 * player->last_pitch * core::DEGTORAD); + return 1; +} + +int LuaLocalPlayer::l_get_last_look_horizontal(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, (player->last_yaw + 90.) * core::DEGTORAD); + return 1; +} + +int LuaLocalPlayer::l_get_key_pressed(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->last_keyPressed); + return 1; +} + +int LuaLocalPlayer::l_get_breath(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->getBreath()); + return 1; +} + +int LuaLocalPlayer::l_get_look_dir(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + float pitch = -1.0 * player->getPitch() * core::DEGTORAD; + float yaw = (player->getYaw() + 90.) * core::DEGTORAD; + v3f v(cos(pitch) * cos(yaw), sin(pitch), cos(pitch) * sin(yaw)); + + push_v3f(L, v); + return 1; +} + +int LuaLocalPlayer::l_get_look_horizontal(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD); + return 1; +} + +int LuaLocalPlayer::l_get_look_vertical(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD); + return 1; +} + +int LuaLocalPlayer::l_get_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getPosition() / BS); + return 1; +} + +int LuaLocalPlayer::l_get_eye_pos(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getEyePosition()); + return 1; +} + +int LuaLocalPlayer::l_get_eye_offset(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + push_v3f(L, player->getEyeOffset()); + return 1; +} + +int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + lua_pushnumber(L, player->movement_acceleration_default); + lua_setfield(L, -2, "default"); + + lua_pushnumber(L, player->movement_acceleration_air); + lua_setfield(L, -2, "air"); + + lua_pushnumber(L, player->movement_acceleration_fast); + lua_setfield(L, -2, "fast"); + + return 1; +} + +int LuaLocalPlayer::l_get_movement_speed(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + lua_pushnumber(L, player->movement_speed_walk); + lua_setfield(L, -2, "walk"); + + lua_pushnumber(L, player->movement_speed_crouch); + lua_setfield(L, -2, "crouch"); + + lua_pushnumber(L, player->movement_speed_fast); + lua_setfield(L, -2, "fast"); + + lua_pushnumber(L, player->movement_speed_climb); + lua_setfield(L, -2, "climb"); + + lua_pushnumber(L, player->movement_speed_jump); + lua_setfield(L, -2, "jump"); + + return 1; +} + +int LuaLocalPlayer::l_get_movement(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_newtable(L); + + lua_pushnumber(L, player->movement_liquid_fluidity); + lua_setfield(L, -2, "liquid_fluidity"); + + lua_pushnumber(L, player->movement_liquid_fluidity_smooth); + lua_setfield(L, -2, "liquid_fluidity_smooth"); + + lua_pushnumber(L, player->movement_liquid_sink); + lua_setfield(L, -2, "liquid_sink"); + + lua_pushnumber(L, player->movement_gravity); + lua_setfield(L, -2, "gravity"); + + return 1; +} + +LuaLocalPlayer *LuaLocalPlayer::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaLocalPlayer **) ud; +} + +LocalPlayer *LuaLocalPlayer::getobject(LuaLocalPlayer *ref) +{ + return ref->m_localplayer; +} + +LocalPlayer *LuaLocalPlayer::getobject(lua_State *L, int narg) +{ + LuaLocalPlayer *ref = checkobject(L, narg); + assert(ref); + LocalPlayer *player = getobject(ref); + assert(player); + return player; +} + +int LuaLocalPlayer::gc_object(lua_State *L) +{ + LuaLocalPlayer *o = *(LuaLocalPlayer **) (lua_touserdata(L, 1)); + delete o; + return 0; +} + +void LuaLocalPlayer::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // Drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // Drop methodtable +} + +const char LuaLocalPlayer::className[] = "LocalPlayer"; +const luaL_reg LuaLocalPlayer::methods[] = { + luamethod(LuaLocalPlayer, get_velocity), + luamethod(LuaLocalPlayer, get_hp), + luamethod(LuaLocalPlayer, get_name), + luamethod(LuaLocalPlayer, is_teleported), + luamethod(LuaLocalPlayer, is_attached), + luamethod(LuaLocalPlayer, is_touching_ground), + luamethod(LuaLocalPlayer, is_in_liquid), + luamethod(LuaLocalPlayer, is_in_liquid_stable), + luamethod(LuaLocalPlayer, get_liquid_viscosity), + luamethod(LuaLocalPlayer, is_climbing), + luamethod(LuaLocalPlayer, swimming_vertical), + luamethod(LuaLocalPlayer, get_physics_override), + luamethod(LuaLocalPlayer, get_override_pos), + luamethod(LuaLocalPlayer, get_last_pos), + luamethod(LuaLocalPlayer, get_last_velocity), + luamethod(LuaLocalPlayer, get_last_look_horizontal), + luamethod(LuaLocalPlayer, get_last_look_vertical), + luamethod(LuaLocalPlayer, get_key_pressed), + luamethod(LuaLocalPlayer, get_breath), + luamethod(LuaLocalPlayer, get_look_dir), + luamethod(LuaLocalPlayer, get_look_horizontal), + luamethod(LuaLocalPlayer, get_look_vertical), + luamethod(LuaLocalPlayer, get_pos), + luamethod(LuaLocalPlayer, get_eye_pos), + luamethod(LuaLocalPlayer, get_eye_offset), + luamethod(LuaLocalPlayer, get_movement_acceleration), + luamethod(LuaLocalPlayer, get_movement_speed), + luamethod(LuaLocalPlayer, get_movement), + + {0, 0} +}; diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h new file mode 100644 index 000000000..19012f01f --- /dev/null +++ b/src/script/lua_api/l_localplayer.h @@ -0,0 +1,95 @@ +/* +Minetest +Copyright (C) 2017 Dumbeldor, Vincent Glize + +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. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MINETEST_L_LOCALPLAYER_H +#define MINETEST_L_LOCALPLAYER_H + +#include "l_base.h" + + +class LocalPlayer; + +class LuaLocalPlayer : public ModApiBase { +private: + + static const char className[]; + static const luaL_Reg methods[]; + + // garbage collector + static int gc_object(lua_State *L); + + static int l_get_velocity(lua_State *L); + + static int l_get_hp(lua_State *L); + + static int l_get_name(lua_State *L); + + static int l_is_teleported(lua_State *L); + static int l_is_attached(lua_State *L); + static int l_is_touching_ground(lua_State *L); + static int l_is_in_liquid(lua_State *L); + static int l_is_in_liquid_stable(lua_State *L); + static int l_get_liquid_viscosity(lua_State *L); + static int l_is_climbing(lua_State *L); + static int l_swimming_vertical(lua_State *L); + + static int l_get_physics_override(lua_State *L); + + static int l_get_override_pos(lua_State *L); + + static int l_get_last_pos(lua_State *L); + static int l_get_last_velocity(lua_State *L); + static int l_get_last_look_vertical(lua_State *L); + static int l_get_last_look_horizontal(lua_State *L); + static int l_get_key_pressed(lua_State *L); + + static int l_get_breath(lua_State *L); + + static int l_get_look_dir(lua_State *L); + static int l_get_look_horizontal(lua_State *L); + static int l_get_look_vertical(lua_State *L); + + static int l_get_pos(lua_State *L); + + static int l_get_eye_pos(lua_State *L); + static int l_get_eye_offset(lua_State *L); + + static int l_get_movement_acceleration(lua_State *L); + + static int l_get_movement_speed(lua_State *L); + + static int l_get_movement(lua_State *L); + + LocalPlayer *m_localplayer; + +public: + LuaLocalPlayer(LocalPlayer *m); + ~LuaLocalPlayer() {} + + static void create(lua_State *L, LocalPlayer *m); + + static LuaLocalPlayer *checkobject(lua_State *L, int narg); + static LocalPlayer *getobject(LuaLocalPlayer *ref); + static LocalPlayer *getobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + + +#endif //MINETEST_L_LOCALPLAYER_H