From de73f989eb1397b1103236031fd91309b294583c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 Apr 2020 20:13:23 +0200 Subject: Overall improvements to log messages (#9598) Hide some unnecessarily verbose ones behind --trace or disable them entirely. Remove duplicate ones. Improve their contents in some places. --- src/script/lua_api/l_item.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 0c174feca..ff77cba32 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -522,7 +522,6 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) lua_getfield(L, table, "name"); if(lua_isstring(L, -1)){ name = readParam(L, -1); - verbosestream<<"register_item_raw: "< Date: Wed, 8 Apr 2020 20:14:08 +0200 Subject: Work around LuaJIT issues on aarch64 (#9614) - Move the text segment below the 47-bit limit, needed for script_exception_wrapper which must be lightuserdata - Replace CUSTOM_RIDX_SCRIPTAPI with full userdata --- src/CMakeLists.txt | 10 ++++++++-- src/script/common/c_internal.h | 9 +++++++++ src/script/cpp_api/s_base.cpp | 4 ++++ src/script/cpp_api/s_security.cpp | 7 ++++++- src/script/lua_api/l_base.cpp | 7 ++++++- 5 files changed, 33 insertions(+), 4 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6afa5b8fe..d579bb965 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -661,7 +661,7 @@ endif() # Set some optimizations and tweaks -include(CheckCXXCompilerFlag) +include(CheckCSourceCompiles) if(MSVC) # Visual Studio @@ -695,13 +695,19 @@ else() else() set(RELEASE_WARNING_FLAGS "") endif() - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(WARNING_FLAGS "${WARNING_FLAGS} -Wsign-compare") endif() + if(APPLE AND USE_LUAJIT) # required per http://luajit.org/install.html SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pagezero_size 10000 -image_base 100000000") + elseif(UNIX AND USE_LUAJIT) + check_c_source_compiles("#ifndef __aarch64__\n#error\n#endif\nint main(){}" IS_AARCH64) + if(IS_AARCH64) + # Move text segment below LuaJIT's 47-bit limit (see issue #9367) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Ttext-segment=0x200000000") + endif() endif() if(MINGW) diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index d8cf3fe76..747400769 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -54,6 +54,15 @@ extern "C" { #define CUSTOM_RIDX_CURRENT_MOD_NAME (CUSTOM_RIDX_BASE + 2) #define CUSTOM_RIDX_BACKTRACE (CUSTOM_RIDX_BASE + 3) +// Determine if CUSTOM_RIDX_SCRIPTAPI will hold a light or full userdata +#if defined(__aarch64__) && USE_LUAJIT +/* LuaJIT has a 47-bit limit for lightuserdata on this platform and we cannot + * assume that the ScriptApi class was allocated at a fitting address. */ +#define INDIRECT_SCRIPTAPI_RIDX 1 +#else +#define INDIRECT_SCRIPTAPI_RIDX 0 +#endif + // Pushes the error handler onto the stack and returns its index #define PUSH_ERROR_HANDLER(L) \ (lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE), lua_gettop((L))) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index ecb1ba39b..f234a15d4 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -90,7 +90,11 @@ ScriptApiBase::ScriptApiBase(ScriptingType type): luaL_openlibs(m_luastack); // Make the ScriptApiBase* accessible to ModApiBase +#if INDIRECT_SCRIPTAPI_RIDX + *(void **)(lua_newuserdata(m_luastack, sizeof(void *))) = this; +#else lua_pushlightuserdata(m_luastack, this); +#endif lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); // Add and save an error handler diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index b5abcfb5d..2afa3a191 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -499,7 +499,12 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, // Get server from registry lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); - ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); + ScriptApiBase *script; +#if INDIRECT_SCRIPTAPI_RIDX + script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1)); +#else + script = (ScriptApiBase *) lua_touserdata(L, -1); +#endif lua_pop(L, 1); const IGameDef *gamedef = script->getGameDef(); if (!gamedef) diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index 8486fc7bc..c980bba39 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -30,7 +30,12 @@ ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L) { // Get server from registry lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); - ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1); + ScriptApiBase *sapi_ptr; +#if INDIRECT_SCRIPTAPI_RIDX + sapi_ptr = (ScriptApiBase*) *(void**)(lua_touserdata(L, -1)); +#else + sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1); +#endif lua_pop(L, 1); return sapi_ptr; } -- cgit v1.2.3 From 054c5dfaa35dd79560a465ccc0ef214cb5f34c88 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 22:06:24 +0200 Subject: scriptapi: Sort out ServerEnvironment / Environment distinction properly The API implementation is shared between CSM and SSM. Functions should retrieve a plain env when they do not need any server-specific functions. --- src/environment.cpp | 18 +++++++++++++++ src/environment.h | 10 +++++++++ src/script/lua_api/l_env.cpp | 50 +++++++++++++---------------------------- src/script/lua_api/l_internal.h | 29 ++++++++++++++++++++++-- src/script/lua_api/l_object.cpp | 2 +- src/serverenvironment.cpp | 18 --------------- src/serverenvironment.h | 10 --------- 7 files changed, 72 insertions(+), 65 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/environment.cpp b/src/environment.cpp index 906f35219..c997be3ff 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -83,6 +83,24 @@ float Environment::getTimeOfDayF() return m_time_of_day_f; } +bool Environment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p) +{ + // Iterate trough nodes on the line + voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS); + do { + MapNode n = getMap().getNode(iterator.m_current_node_pos); + + // Return non-air + if (n.param0 != CONTENT_AIR) { + if (p) + *p = iterator.m_current_node_pos; + return false; + } + iterator.next(); + } while (iterator.m_current_index <= iterator.m_last_index); + return true; +} + /* Check if a node is pointable */ diff --git a/src/environment.h b/src/environment.h index f568ba228..91c33ba15 100644 --- a/src/environment.h +++ b/src/environment.h @@ -76,6 +76,16 @@ public: u32 getDayCount(); + /*! + * Returns false if the given line intersects with a + * non-air node, true otherwise. + * \param pos1 start of the line + * \param pos2 end of the line + * \param p output, position of the first non-air node + * the line intersects + */ + bool line_of_sight(v3f pos1, v3f pos2, v3s16 *p = nullptr); + /*! * Gets the objects pointed by the shootline as * pointed things. diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 3169fa4cf..31e582d3d 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -139,8 +139,7 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) int LuaRaycast::l_next(lua_State *L) { - MAP_LOCK_REQUIRED; - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; bool csm = false; #ifndef SERVER @@ -384,7 +383,7 @@ int ModApiEnvMod::l_get_node_or_nil(lua_State *L) // timeofday: nil = current time, 0 = night, 0.5 = day int ModApiEnvMod::l_get_node_light(lua_State *L) { - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; // Do it v3s16 pos = read_v3s16(L, 1); @@ -488,10 +487,7 @@ int ModApiEnvMod::l_punch_node(lua_State *L) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_get_node_max_level(lua_State *L) { - Environment *env = getEnv(L); - if (!env) { - return 0; - } + GET_PLAIN_ENV_PTR; v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNode(pos); @@ -503,10 +499,7 @@ int ModApiEnvMod::l_get_node_max_level(lua_State *L) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_get_node_level(lua_State *L) { - Environment *env = getEnv(L); - if (!env) { - return 0; - } + GET_PLAIN_ENV_PTR; v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNode(pos); @@ -551,7 +544,7 @@ int ModApiEnvMod::l_add_node_level(lua_State *L) // find_nodes_with_meta(pos1, pos2) int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L) { - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; std::vector positions = env->getMap().findNodesWithMetadata( check_v3s16(L, 1), check_v3s16(L, 2)); @@ -728,10 +721,7 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L) // get_timeofday() -> 0...1 int ModApiEnvMod::l_get_timeofday(lua_State *L) { - Environment *env = getEnv(L); - if (!env) { - return 0; - } + GET_PLAIN_ENV_PTR; // Do it int timeofday_mh = env->getTimeOfDay(); @@ -743,10 +733,7 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L) // get_day_count() -> int int ModApiEnvMod::l_get_day_count(lua_State *L) { - Environment *env = getEnv(L); - if (!env) { - return 0; - } + GET_PLAIN_ENV_PTR; lua_pushnumber(L, env->getDayCount()); return 1; @@ -767,12 +754,9 @@ int ModApiEnvMod::l_get_gametime(lua_State *L) // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" int ModApiEnvMod::l_find_node_near(lua_State *L) { - Environment *env = getEnv(L); - if (!env) { - return 0; - } + GET_PLAIN_ENV_PTR; - const NodeDefManager *ndef = getGameDef(L)->ndef(); + const NodeDefManager *ndef = env->getGameDef()->ndef(); v3s16 pos = read_v3s16(L, 1); int radius = luaL_checkinteger(L, 2); std::vector filter; @@ -815,20 +799,19 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) { - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); sortBoxVerticies(minp, maxp); + const NodeDefManager *ndef = env->getGameDef()->ndef(); + #ifndef SERVER - const NodeDefManager *ndef = getClient(L) ? getClient(L)->ndef() : getServer(L)->ndef(); if (getClient(L)) { minp = getClient(L)->CSMClampPos(minp); maxp = getClient(L)->CSMClampPos(maxp); } -#else - const NodeDefManager *ndef = getServer(L)->ndef(); #endif v3s16 cube = maxp - minp + 1; @@ -892,20 +875,19 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) * TODO */ - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); sortBoxVerticies(minp, maxp); + const NodeDefManager *ndef = env->getGameDef()->ndef(); + #ifndef SERVER - const NodeDefManager *ndef = getClient(L) ? getClient(L)->ndef() : getServer(L)->ndef(); if (getClient(L)) { minp = getClient(L)->CSMClampPos(minp); maxp = getClient(L)->CSMClampPos(maxp); } -#else - const NodeDefManager *ndef = getServer(L)->ndef(); #endif v3s16 cube = maxp - minp + 1; @@ -1034,7 +1016,7 @@ int ModApiEnvMod::l_clear_objects(lua_State *L) // line_of_sight(pos1, pos2) -> true/false, pos int ModApiEnvMod::l_line_of_sight(lua_State *L) { - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; // read position 1 from lua v3f pos1 = checkFloatPos(L, 1); diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index bbedfe46e..a86eeaf79 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -32,14 +32,39 @@ with this program; if not, write to the Free Software Foundation, Inc., #define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name} #define API_FCT(name) registerFunction(L, #name, l_##name, top) -#define MAP_LOCK_REQUIRED -#define NO_MAP_LOCK_REQUIRED +// For future use +#define MAP_LOCK_REQUIRED ((void)0) +#define NO_MAP_LOCK_REQUIRED ((void)0) +/* In debug mode ensure no code tries to retrieve the server env when it isn't + * actually available (in CSM) */ +#if !defined(SERVER) && !defined(NDEBUG) +#define DEBUG_ASSERT_NO_CLIENTAPI \ + FATAL_ERROR_IF(getClient(L) != nullptr, "Tried " \ + "to retrieve ServerEnvironment on client") +#else +#define DEBUG_ASSERT_NO_CLIENTAPI ((void)0) +#endif + +// Retrieve ServerEnvironment pointer as `env` (no map lock) #define GET_ENV_PTR_NO_MAP_LOCK \ + DEBUG_ASSERT_NO_CLIENTAPI; \ ServerEnvironment *env = (ServerEnvironment *)getEnv(L); \ if (env == NULL) \ return 0 +// Retrieve ServerEnvironment pointer as `env` #define GET_ENV_PTR \ MAP_LOCK_REQUIRED; \ GET_ENV_PTR_NO_MAP_LOCK + +// Retrieve Environment pointer as `env` (no map lock) +#define GET_PLAIN_ENV_PTR_NO_MAP_LOCK \ + Environment *env = (Environment *)getEnv(L); \ + if (env == NULL) \ + return 0 + +// Retrieve Environment pointer as `env` +#define GET_PLAIN_ENV_PTR \ + MAP_LOCK_REQUIRED; \ + GET_PLAIN_ENV_PTR_NO_MAP_LOCK diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 23ed1ffe0..5cd978d73 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -533,7 +533,7 @@ int ObjectRef::l_set_local_animation(lua_State *L) // get_local_animation(self) int ObjectRef::l_get_local_animation(lua_State *L) { - NO_MAP_LOCK_REQUIRED + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); if (player == NULL) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 2d3ee078e..0ccbd772b 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -545,24 +545,6 @@ bool ServerEnvironment::removePlayerFromDatabase(const std::string &name) return m_player_database->removePlayer(name); } -bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p) -{ - // Iterate trough nodes on the line - voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS); - do { - MapNode n = getMap().getNode(iterator.m_current_node_pos); - - // Return non-air - if (n.param0 != CONTENT_AIR) { - if (p) - *p = iterator.m_current_node_pos; - return false; - } - iterator.next(); - } while (iterator.m_current_index <= iterator.m_last_index); - return true; -} - void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, const std::string &str_reason, bool reconnect) { diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 3c7b7d059..bf10e3ca8 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -334,16 +334,6 @@ public: // This makes stuff happen void step(f32 dtime); - /*! - * Returns false if the given line intersects with a - * non-air node, true otherwise. - * \param pos1 start of the line - * \param pos2 end of the line - * \param p output, position of the first non-air node - * the line intersects - */ - bool line_of_sight(v3f pos1, v3f pos2, v3s16 *p = NULL); - u32 getGameTime() const { return m_game_time; } void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } -- cgit v1.2.3 From 5f3a17eb65c1ad9259f03bde346a8d69fe0c1069 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 22:53:08 +0200 Subject: Implement minetest.sound_fade() --- doc/client_lua_api.txt | 7 +++++++ src/script/lua_api/l_client.cpp | 22 +++++++++++++++++++--- src/script/lua_api/l_client.h | 6 ++++++ src/script/lua_api/l_nodemeta.cpp | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 3612f851b..2c8ffd4d4 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -734,6 +734,13 @@ Call these functions only at load time! * `spec` is a `SimpleSoundSpec` * `parameters` is a sound parameter table * `minetest.sound_stop(handle)` + * `handle` is a handle returned by `minetest.sound_play` +* `minetest.sound_fade(handle, step, gain)` + * `handle` is a handle returned by `minetest.sound_play` + * `step` determines how fast a sound will fade. + Negative step will lower the sound volume, positive step will increase + the sound volume. + * `gain` the target gain for the fade. ### Timing * `minetest.after(time, func, ...)` diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index fba182492..e30c05260 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -209,7 +209,7 @@ int ModApiClient::l_gettext(lua_State *L) return 1; } -// get_node(pos) +// get_node_or_nil(pos) // pos = {x=num, y=num, z=num} int ModApiClient::l_get_node_or_nil(lua_State *L) { @@ -228,6 +228,7 @@ int ModApiClient::l_get_node_or_nil(lua_State *L) return 1; } +// get_langauge() int ModApiClient::l_get_language(lua_State *L) { #ifdef _WIN32 @@ -244,6 +245,7 @@ int ModApiClient::l_get_language(lua_State *L) return 2; } +// get_wielded_item() int ModApiClient::l_get_wielded_item(lua_State *L) { Client *client = getClient(L); @@ -266,12 +268,14 @@ int ModApiClient::l_get_meta(lua_State *L) return 1; } +// sound_play(spec, parameters) int ModApiClient::l_sound_play(lua_State *L) { ISoundManager *sound = getClient(L)->getSoundManager(); SimpleSoundSpec spec; read_soundspec(L, 1, spec); + float gain = 1.0f; float pitch = 1.0f; bool looped = false; @@ -293,21 +297,32 @@ int ModApiClient::l_sound_play(lua_State *L) } } - handle = sound->playSound(spec.name, looped, gain * spec.gain, 0.0f, pitch); + handle = sound->playSound(spec.name, looped, gain * spec.gain, spec.fade, pitch); lua_pushinteger(L, handle); return 1; } +// sound_stop(handle) int ModApiClient::l_sound_stop(lua_State *L) { - u32 handle = luaL_checkinteger(L, 1); + s32 handle = luaL_checkinteger(L, 1); getClient(L)->getSoundManager()->stopSound(handle); return 0; } +// sound_fade(handle, step, gain) +int ModApiClient::l_sound_fade(lua_State *L) +{ + s32 handle = luaL_checkinteger(L, 1); + float step = readParam(L, 2); + float gain = readParam(L, 3); + getClient(L)->getSoundManager()->fadeSound(handle, step, gain); + return 0; +} + // get_server_info() int ModApiClient::l_get_server_info(lua_State *L) { @@ -426,6 +441,7 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(get_meta); API_FCT(sound_play); API_FCT(sound_stop); + API_FCT(sound_fade); API_FCT(get_server_info); API_FCT(get_item_def); API_FCT(get_node_def); diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 6d1f70b1d..5dc3efdad 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -69,6 +69,7 @@ private: // get_node(pos) static int l_get_node_or_nil(lua_State *L); + // get_language() static int l_get_language(lua_State *L); // get_wielded_item() @@ -77,10 +78,15 @@ private: // get_meta(pos) static int l_get_meta(lua_State *L); + // sound_play(spec, parameters) static int l_sound_play(lua_State *L); + // sound_stop(handle) static int l_sound_stop(lua_State *L); + // sound_fade(handle, step, gain) + static int l_sound_fade(lua_State *L); + // get_server_info() static int l_get_server_info(lua_State *L); diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 229ce73db..57052cb42 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -55,11 +55,13 @@ Metadata* NodeMetaRef::getmeta(bool auto_create) void NodeMetaRef::clearMeta() { + SANITY_CHECK(!m_is_local); m_env->getMap().removeNodeMetadata(m_p); } void NodeMetaRef::reportMetadataChange(const std::string *name) { + SANITY_CHECK(!m_is_local); // NOTE: This same code is in rollback_interface.cpp // Inform other things that the metadata has changed NodeMetadata *meta = dynamic_cast(m_meta); -- cgit v1.2.3 From 1292bdbbcec45613c95aff9f2ea88aa49af25011 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 11 Apr 2020 00:09:11 +0200 Subject: Various features and fixes --- clientmods/preview/init.lua | 40 +++---------------- doc/client_lua_api.txt | 31 ++++++++++++--- src/script/lua_api/l_client.cpp | 26 +++++-------- src/script/lua_api/l_localplayer.cpp | 74 ++++++++++++++++++++++++++++++------ src/script/lua_api/l_localplayer.h | 21 +++++++++- 5 files changed, 123 insertions(+), 69 deletions(-) (limited to 'src/script/lua_api') diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index 5777adcaf..d2440369a 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -31,6 +31,7 @@ core.after(4, function() end) core.after(1, function() + print("armor: " .. dump(core.localplayer:get_armor_groups())) id = core.localplayer:hud_add({ hud_elem_type = "text", name = "example", @@ -125,19 +126,6 @@ core.register_chatcommand("dump", { end, }) -core.register_chatcommand("colorize_test", { - func = function(param) - return true, core.colorize("red", param) - end, -}) - -core.register_chatcommand("test_node", { - func = function(param) - core.display_chat_message(dump(core.get_node({x=0, y=0, z=0}))) - core.display_chat_message(dump(core.get_node_or_nil({x=0, y=0, z=0}))) - end, -}) - local function preview_minimap() local minimap = core.ui.minimap if not minimap then @@ -157,7 +145,7 @@ end core.after(2, function() print("[PREVIEW] loaded " .. modname .. " mod") modstorage:set_string("current_mod", modname) - print(modstorage:get_string("current_mod")) + assert(modstorage:get_string("current_mod") == modname) preview_minimap() end) @@ -184,30 +172,12 @@ end) core.register_on_punchnode(function(pos, node) print("The local player punched a node!") - local itemstack = core.get_wielded_item() - --[[ - -- getters - print(dump(itemstack:is_empty())) - print(dump(itemstack:get_name())) - print(dump(itemstack:get_count())) - print(dump(itemstack:get_wear())) - print(dump(itemstack:get_meta())) - print(dump(itemstack:get_metadata() - print(dump(itemstack:is_known())) - --print(dump(itemstack:get_definition())) - print(dump(itemstack:get_tool_capabilities())) - print(dump(itemstack:to_string())) - print(dump(itemstack:to_table())) - -- setters - print(dump(itemstack:set_name("default:dirt"))) - print(dump(itemstack:set_count("95"))) - print(dump(itemstack:set_wear(934))) - print(dump(itemstack:get_meta())) - print(dump(itemstack:get_metadata())) - --]] + local itemstack = core.localplayer:get_wielded_item() print(dump(itemstack:to_table())) print("pos:" .. dump(pos)) print("node:" .. dump(node)) + local meta = core.get_meta(pos) + print("punched meta: " .. (meta and dump(meta:to_table()) or "(missing)")) return false end) diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 2c8ffd4d4..71df91c68 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -804,8 +804,6 @@ Call these functions only at load time! * get max available level for leveled node ### Player -* `minetest.get_wielded_item()` - * Returns the itemstack the local player is holding * `minetest.send_chat_message(message)` * Act as if `message` was typed by the player into the terminal. * `minetest.run_server_chatcommand(cmd, param)` @@ -1006,6 +1004,10 @@ Methods: * returns player HP * `get_name()` * returns player name +* `get_wield_index()` + * returns the index of the wielded item +* `get_wielded_item()` + * returns the itemstack the player is holding * `is_attached()` * returns true if player is attached * `is_touching_ground()` @@ -1029,7 +1031,8 @@ Methods: jump = float, gravity = float, sneak = boolean, - sneak_glitch = boolean + sneak_glitch = boolean, + new_move = boolean, } ``` @@ -1081,8 +1084,26 @@ Methods: * returns last look horizontal angle * `get_last_look_vertical()`: * returns last look vertical angle -* `get_key_pressed()`: - * returns last key typed by the player +* `get_control()`: + * returns pressed player controls + +```lua + { + up = boolean, + down = boolean, + left = boolean, + right = boolean, + jump = boolean, + aux1 = boolean, + sneak = boolean, + zoom = boolean, + LMB = boolean, + RMB = boolean, + } +``` + +* `get_armor_groups()` + * returns a table with the armor group ratings * `hud_add(definition)` * add a HUD element described by HUD def, returns ID number on success and `nil` on failure. * See [`HUD definition`](#hud-definition-hud_add-hud_get) diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index e30c05260..aaced7cd0 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_base.h" #include "gettext.h" #include "l_internal.h" -#include "lua_api/l_item.h" #include "lua_api/l_nodemeta.h" #include "gui/mainmenumanager.h" #include "map.h" @@ -245,25 +244,18 @@ int ModApiClient::l_get_language(lua_State *L) return 2; } -// get_wielded_item() -int ModApiClient::l_get_wielded_item(lua_State *L) -{ - Client *client = getClient(L); - LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (!player) - return 0; - - ItemStack selected_item; - player->getWieldedItem(&selected_item, nullptr); - LuaItemStack::create(L, selected_item); - return 1; -} - // get_meta(pos) int ModApiClient::l_get_meta(lua_State *L) { v3s16 p = read_v3s16(L, 1); - NodeMetadata *meta = getClient(L)->getEnv().getMap().getNodeMetadata(p); + + // check restrictions first + bool pos_ok; + getClient(L)->CSMGetNode(p, &pos_ok); + if (!pos_ok) + return 0; + + NodeMetadata *meta = getEnv(L)->getMap().getNodeMetadata(p); NodeMetaRef::createClient(L, meta); return 1; } @@ -390,6 +382,7 @@ int ModApiClient::l_get_node_def(lua_State *L) return 1; } +// get_privilege_list() int ModApiClient::l_get_privilege_list(lua_State *L) { const Client *client = getClient(L); @@ -436,7 +429,6 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(send_respawn); API_FCT(gettext); API_FCT(get_node_or_nil); - API_FCT(get_wielded_item); API_FCT(disconnect); API_FCT(get_meta); API_FCT(sound_play); diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 821b1cb66..851ede535 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -19,10 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "l_localplayer.h" #include "l_internal.h" +#include "lua_api/l_item.h" #include "script/common/c_converter.h" #include "client/localplayer.h" #include "hud.h" #include "common/c_content.h" +#include "client/content_cao.h" LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m) : m_localplayer(m) { @@ -74,6 +76,26 @@ int LuaLocalPlayer::l_get_name(lua_State *L) return 1; } +// get_wield_index(self) +int LuaLocalPlayer::l_get_wield_index(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + lua_pushinteger(L, player->getWieldIndex()); + return 1; +} + +// get_wielded_item(self) +int LuaLocalPlayer::l_get_wielded_item(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + + ItemStack selected_item; + player->getWieldedItem(&selected_item, nullptr); + LuaItemStack::create(L, selected_item); + return 1; +} + int LuaLocalPlayer::l_is_attached(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -130,6 +152,7 @@ int LuaLocalPlayer::l_swimming_vertical(lua_State *L) return 1; } +// get_physics_override(self) int LuaLocalPlayer::l_get_physics_override(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -150,14 +173,9 @@ int LuaLocalPlayer::l_get_physics_override(lua_State *L) 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); + lua_pushboolean(L, player->physics_override_new_move); + lua_setfield(L, -2, "new_move"); - push_v3f(L, player->getPosition()); return 1; } @@ -193,14 +211,33 @@ int LuaLocalPlayer::l_get_last_look_horizontal(lua_State *L) return 1; } -int LuaLocalPlayer::l_get_key_pressed(lua_State *L) +// get_control(self) +int LuaLocalPlayer::l_get_control(lua_State *L) { LocalPlayer *player = getobject(L, 1); + const PlayerControl &c = player->getPlayerControl(); + + auto set = [L] (const char *name, bool value) { + lua_pushboolean(L, value); + lua_setfield(L, -2, name); + }; + + lua_createtable(L, 0, 12); + set("up", c.up); + set("down", c.down); + set("left", c.left); + set("right", c.right); + set("jump", c.jump); + set("aux1", c.aux1); + set("sneak", c.sneak); + set("zoom", c.zoom); + set("LMB", c.LMB); + set("RMB", c.RMB); - lua_pushinteger(L, player->last_keyPressed); return 1; } +// get_breath(self) int LuaLocalPlayer::l_get_breath(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -209,6 +246,7 @@ int LuaLocalPlayer::l_get_breath(lua_State *L) return 1; } +// get_pos(self) int LuaLocalPlayer::l_get_pos(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -217,6 +255,7 @@ int LuaLocalPlayer::l_get_pos(lua_State *L) return 1; } +// get_movement_acceleration(self) int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -234,6 +273,7 @@ int LuaLocalPlayer::l_get_movement_acceleration(lua_State *L) return 1; } +// get_movement_speed(self) int LuaLocalPlayer::l_get_movement_speed(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -257,6 +297,7 @@ int LuaLocalPlayer::l_get_movement_speed(lua_State *L) return 1; } +// get_movement(self) int LuaLocalPlayer::l_get_movement(lua_State *L) { LocalPlayer *player = getobject(L, 1); @@ -278,6 +319,13 @@ int LuaLocalPlayer::l_get_movement(lua_State *L) return 1; } +// get_armor_groups(self) +int LuaLocalPlayer::l_get_armor_groups(lua_State *L) +{ + LocalPlayer *player = getobject(L, 1); + push_groups(L, player->getCAO()->getGroups()); + return 1; +} // hud_add(self, form) int LuaLocalPlayer::l_hud_add(lua_State *L) @@ -407,6 +455,8 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, get_velocity), luamethod(LuaLocalPlayer, get_hp), luamethod(LuaLocalPlayer, get_name), + luamethod(LuaLocalPlayer, get_wield_index), + luamethod(LuaLocalPlayer, get_wielded_item), luamethod(LuaLocalPlayer, is_attached), luamethod(LuaLocalPlayer, is_touching_ground), luamethod(LuaLocalPlayer, is_in_liquid), @@ -415,17 +465,19 @@ const luaL_Reg LuaLocalPlayer::methods[] = { luamethod(LuaLocalPlayer, is_climbing), luamethod(LuaLocalPlayer, swimming_vertical), luamethod(LuaLocalPlayer, get_physics_override), - luamethod(LuaLocalPlayer, get_override_pos), + // TODO: figure our if these are useful in any way 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_control), luamethod(LuaLocalPlayer, get_breath), luamethod(LuaLocalPlayer, get_pos), luamethod(LuaLocalPlayer, get_movement_acceleration), luamethod(LuaLocalPlayer, get_movement_speed), luamethod(LuaLocalPlayer, get_movement), + luamethod(LuaLocalPlayer, get_armor_groups), luamethod(LuaLocalPlayer, hud_add), luamethod(LuaLocalPlayer, hud_remove), luamethod(LuaLocalPlayer, hud_change), diff --git a/src/script/lua_api/l_localplayer.h b/src/script/lua_api/l_localplayer.h index 01de2ed4e..4413f2bdb 100644 --- a/src/script/lua_api/l_localplayer.h +++ b/src/script/lua_api/l_localplayer.h @@ -32,12 +32,21 @@ private: // garbage collector static int gc_object(lua_State *L); + // get_velocity(self) static int l_get_velocity(lua_State *L); + // get_hp(self) static int l_get_hp(lua_State *L); + // get_name(self) static int l_get_name(lua_State *L); + // get_wield_index(self) + static int l_get_wield_index(lua_State *L); + + // get_wielded_item(self) + static int l_get_wielded_item(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); @@ -54,18 +63,28 @@ private: 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); + // get_control(self) + static int l_get_control(lua_State *L); + + // get_breath(self) static int l_get_breath(lua_State *L); + // get_pos(self) static int l_get_pos(lua_State *L); + // get_movement_acceleration(self) static int l_get_movement_acceleration(lua_State *L); + // get_movement_speed(self) static int l_get_movement_speed(lua_State *L); + // get_movement(self) static int l_get_movement(lua_State *L); + // get_armor_groups(self) + static int l_get_armor_groups(lua_State *L); + // hud_add(self, id, form) static int l_hud_add(lua_State *L); -- cgit v1.2.3 From 6d43736172f8459cb70219186ae003c56c389f2a Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Fri, 10 Apr 2020 21:25:42 +0200 Subject: Move serveractiveobject & unitsao Move serverobject.{cpp,h} to server/serveractiveobject.{cpp,h} Move UnitSAO class to dedicated files --- build/android/jni/Android.mk | 2 +- src/CMakeLists.txt | 1 - src/collision.cpp | 2 +- src/content_sao.cpp | 296 ------------------------------ src/content_sao.h | 92 +--------- src/emerge.cpp | 1 - src/environment.cpp | 1 - src/inventorymanager.cpp | 2 +- src/mapgen/mapgen_v6.cpp | 1 - src/script/common/c_content.cpp | 2 +- src/script/cpp_api/s_base.cpp | 2 +- src/script/lua_api/l_object.cpp | 2 +- src/server.cpp | 2 +- src/server/CMakeLists.txt | 2 + src/server/activeobjectmgr.h | 2 +- src/server/serveractiveobject.cpp | 114 ++++++++++++ src/server/serveractiveobject.h | 265 +++++++++++++++++++++++++++ src/server/unit_sao.cpp | 318 +++++++++++++++++++++++++++++++++ src/server/unit_sao.h | 113 ++++++++++++ src/serverobject.cpp | 114 ------------ src/serverobject.h | 265 --------------------------- util/travis/clang-format-whitelist.txt | 6 +- 22 files changed, 823 insertions(+), 782 deletions(-) create mode 100644 src/server/serveractiveobject.cpp create mode 100644 src/server/serveractiveobject.h create mode 100644 src/server/unit_sao.cpp create mode 100644 src/server/unit_sao.h delete mode 100644 src/serverobject.cpp delete mode 100644 src/serverobject.h (limited to 'src/script/lua_api') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index a2f32440a..32a16c174 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -259,7 +259,7 @@ LOCAL_SRC_FILES := \ jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/server/mods.cpp \ - jni/src/serverobject.cpp \ + jni/src/server/serveractiveobject.cpp \ jni/src/settings.cpp \ jni/src/staticobject.cpp \ jni/src/tileanimation.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b1d6d647..faa117d41 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -421,7 +421,6 @@ set(common_SRCS server.cpp serverenvironment.cpp serverlist.cpp - serverobject.cpp settings.cpp staticobject.cpp terminal_chat_console.cpp diff --git a/src/collision.cpp b/src/collision.cpp index 0d37ea436..d9fbd3202 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/localplayer.h" #endif #include "serverenvironment.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "util/timetaker.h" #include "profiler.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index be7674f52..0d387b53a 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -106,302 +106,6 @@ private: // Prototype (registers item for deserialization) TestSAO proto_TestSAO(NULL, v3f(0,0,0)); -/* - UnitSAO - */ - -UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos) -{ - // Initialize something to armor groups - m_armor_groups["fleshy"] = 100; -} - -ServerActiveObject *UnitSAO::getParent() const -{ - if (!m_attachment_parent_id) - return nullptr; - // Check if the parent still exists - ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); - - return obj; -} - -void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups) -{ - m_armor_groups = armor_groups; - m_armor_groups_sent = false; -} - -const ItemGroupList &UnitSAO::getArmorGroups() const -{ - return m_armor_groups; -} - -void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) -{ - // store these so they can be updated to clients - m_animation_range = frame_range; - m_animation_speed = frame_speed; - m_animation_blend = frame_blend; - m_animation_loop = frame_loop; - m_animation_sent = false; -} - -void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) -{ - *frame_range = m_animation_range; - *frame_speed = m_animation_speed; - *frame_blend = m_animation_blend; - *frame_loop = m_animation_loop; -} - -void UnitSAO::setAnimationSpeed(float frame_speed) -{ - m_animation_speed = frame_speed; - m_animation_speed_sent = false; -} - -void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) -{ - // store these so they can be updated to clients - m_bone_position[bone] = core::vector2d(position, rotation); - m_bone_position_sent = false; -} - -void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) -{ - *position = m_bone_position[bone].X; - *rotation = m_bone_position[bone].Y; -} - -void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) -{ - // Attachments need to be handled on both the server and client. - // If we just attach on the server, we can only copy the position of the parent. Attachments - // are still sent to clients at an interval so players might see them lagging, plus we can't - // read and attach to skeletal bones. - // If we just attach on the client, the server still sees the child at its original location. - // This breaks some things so we also give the server the most accurate representation - // even if players only see the client changes. - - int old_parent = m_attachment_parent_id; - m_attachment_parent_id = parent_id; - m_attachment_bone = bone; - m_attachment_position = position; - m_attachment_rotation = rotation; - m_attachment_sent = false; - - if (parent_id != old_parent) { - onDetach(old_parent); - onAttach(parent_id); - } -} - -void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const -{ - *parent_id = m_attachment_parent_id; - *bone = m_attachment_bone; - *position = m_attachment_position; - *rotation = m_attachment_rotation; -} - -void UnitSAO::clearChildAttachments() -{ - for (int child_id : m_attachment_child_ids) { - // Child can be NULL if it was deleted earlier - if (ServerActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } - m_attachment_child_ids.clear(); -} - -void UnitSAO::clearParentAttachment() -{ - ServerActiveObject *parent = nullptr; - if (m_attachment_parent_id) { - parent = m_env->getActiveObject(m_attachment_parent_id); - setAttachment(0, "", m_attachment_position, m_attachment_rotation); - } else { - setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); - } - // Do it - if (parent) - parent->removeAttachmentChild(m_id); -} - -void UnitSAO::addAttachmentChild(int child_id) -{ - m_attachment_child_ids.insert(child_id); -} - -void UnitSAO::removeAttachmentChild(int child_id) -{ - m_attachment_child_ids.erase(child_id); -} - -const std::unordered_set &UnitSAO::getAttachmentChildIds() const -{ - return m_attachment_child_ids; -} - -void UnitSAO::onAttach(int parent_id) -{ - if (!parent_id) - return; - - ServerActiveObject *parent = m_env->getActiveObject(parent_id); - - if (!parent || parent->isGone()) - return; // Do not try to notify soon gone parent - - if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { - // Call parent's on_attach field - m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this); - } -} - -void UnitSAO::onDetach(int parent_id) -{ - if (!parent_id) - return; - - ServerActiveObject *parent = m_env->getActiveObject(parent_id); - if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY) - m_env->getScriptIface()->luaentity_on_detach(m_id, parent); - - if (!parent || parent->isGone()) - return; // Do not try to notify soon gone parent - - if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) - m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); -} - -ObjectProperties* UnitSAO::accessObjectProperties() -{ - return &m_prop; -} - -void UnitSAO::notifyObjectPropertiesModified() -{ - m_properties_sent = false; -} - -std::string UnitSAO::generateUpdateAttachmentCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_ATTACH_TO); - // parameters - writeS16(os, m_attachment_parent_id); - os << serializeString(m_attachment_bone); - writeV3F32(os, m_attachment_position); - writeV3F32(os, m_attachment_rotation); - return os.str(); -} - -std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_BONE_POSITION); - // parameters - os << serializeString(bone); - writeV3F32(os, position); - writeV3F32(os, rotation); - return os.str(); -} - - -std::string UnitSAO::generateUpdateAnimationSpeedCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_ANIMATION_SPEED); - // parameters - writeF32(os, m_animation_speed); - return os.str(); -} - -std::string UnitSAO::generateUpdateAnimationCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_ANIMATION); - // parameters - writeV2F32(os, m_animation_range); - writeF32(os, m_animation_speed); - writeF32(os, m_animation_blend); - // these are sent inverted so we get true when the server sends nothing - writeU8(os, !m_animation_loop); - return os.str(); -} - - -std::string UnitSAO::generateUpdateArmorGroupsCommand() const -{ - std::ostringstream os(std::ios::binary); - writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); - writeU16(os, m_armor_groups.size()); - for (const auto &armor_group : m_armor_groups) { - os< &getAttachmentChildIds() const; - ServerActiveObject *getParent() const; - ObjectProperties* accessObjectProperties(); - void notifyObjectPropertiesModified(); - - std::string generateUpdateAttachmentCommand() const; - std::string generateUpdateAnimationSpeedCommand() const; - std::string generateUpdateAnimationCommand() const; - std::string generateUpdateArmorGroupsCommand() const; - static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, - const v3f &acceleration, const v3f &rotation, bool do_interpolate, - bool is_movement_end, f32 update_interval); - std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; - void sendPunchCommand(); - static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); - -protected: - u16 m_hp = 1; - - v3f m_rotation; - - bool m_properties_sent = true; - ObjectProperties m_prop; - - ItemGroupList m_armor_groups; - bool m_armor_groups_sent = false; - - v2f m_animation_range; - float m_animation_speed = 0.0f; - float m_animation_blend = 0.0f; - bool m_animation_loop = true; - bool m_animation_sent = false; - bool m_animation_speed_sent = false; - - // Stores position and rotation for each bone name - std::unordered_map> m_bone_position; - bool m_bone_position_sent = false; - - int m_attachment_parent_id = 0; - std::unordered_set m_attachment_child_ids; - std::string m_attachment_bone = ""; - v3f m_attachment_position; - v3f m_attachment_rotation; - bool m_attachment_sent = false; -private: - void onAttach(int parent_id); - void onDetach(int parent_id); - - std::string generatePunchCommand(u16 result_hp) const; -}; - /* LuaEntitySAO needs some internals exposed. */ diff --git a/src/emerge.cpp b/src/emerge.cpp index 4835c3fad..fe885447c 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -42,7 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "scripting_server.h" #include "server.h" -#include "serverobject.h" #include "settings.h" #include "voxel.h" diff --git a/src/environment.cpp b/src/environment.cpp index c997be3ff..6751f39e4 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "collision.h" #include "raycast.h" -#include "serverobject.h" #include "scripting_server.h" #include "server.h" #include "daynightratio.h" diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 5a24f95a4..b6f464901 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "serverenvironment.h" #include "scripting_server.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "settings.h" #include "craftdef.h" #include "rollback_interface.h" diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index 8a863c044..f473f725d 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -//#include "serverobject.h" #include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index accbb1a87..60f12052f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "log.h" #include "tool.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "porting.h" #include "mapgen/mg_schematic.h" #include "noise.h" diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index f234a15d4..16c20eeae 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" #include "lua_api/l_object.h" #include "common/c_converter.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "filesys.h" #include "content/mods.h" #include "porting.h" diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 5cd978d73..1ea144a1c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "content_sao.h" #include "remoteplayer.h" #include "server.h" diff --git a/src/server.cpp b/src/server.cpp index 062fe0798..529466f6b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "filesys.h" #include "mapblock.h" -#include "serverobject.h" +#include "server/serveractiveobject.h" #include "settings.h" #include "profiler.h" #include "log.h" diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index e964c69ff..9fa5ed9fa 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,4 +1,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/activeobjectmgr.h b/src/server/activeobjectmgr.h index a502ac6ed..5fea1bea6 100644 --- a/src/server/activeobjectmgr.h +++ b/src/server/activeobjectmgr.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "../activeobjectmgr.h" -#include "serverobject.h" +#include "serveractiveobject.h" namespace server { diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp new file mode 100644 index 000000000..3aa78c7d5 --- /dev/null +++ b/src/server/serveractiveobject.cpp @@ -0,0 +1,114 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +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 "serveractiveobject.h" +#include +#include "inventory.h" +#include "constants.h" // BS +#include "log.h" + +ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): + ActiveObject(0), + m_env(env), + m_base_position(pos) +{ +} + +ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, + ServerEnvironment *env, u16 id, v3f pos, + const std::string &data) +{ + // Find factory function + std::map::iterator n; + n = m_types.find(type); + if(n == m_types.end()) { + // These are 0.3 entity types, return without error. + if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { + return NULL; + } + + // If factory is not found, just return. + warningstream<<"ServerActiveObject: No factory for type=" + <second; + ServerActiveObject *object = (*f)(env, pos, data); + return object; +} + +void ServerActiveObject::registerType(u16 type, Factory f) +{ + std::map::iterator n; + n = m_types.find(type); + if(n != m_types.end()) + return; + m_types[type] = f; +} + +float ServerActiveObject::getMinimumSavedMovement() +{ + return 2.0*BS; +} + +ItemStack ServerActiveObject::getWieldedItem(ItemStack *selected, ItemStack *hand) const +{ + *selected = ItemStack(); + if (hand) + *hand = ItemStack(); + + return ItemStack(); +} + +bool ServerActiveObject::setWieldedItem(const ItemStack &item) +{ + return false; +} + +std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SPAWN_INFANT); + // parameters + writeU16(os, infant_id); + writeU8(os, getSendType()); + os << serializeLongString(getClientInitializationData(protocol_version)); + return os.str(); +} + +std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); + // parameters + writeU8(os, 1); // version for forward compatibility + writeARGB8(os, color); + return os.str(); +} + +void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) +{ + while (!m_messages_out.empty()) { + queue.push(m_messages_out.front()); + m_messages_out.pop(); + } +} \ No newline at end of file diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h new file mode 100644 index 000000000..2e013a6b6 --- /dev/null +++ b/src/server/serveractiveobject.h @@ -0,0 +1,265 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +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. +*/ + +#pragma once + +#include +#include "irrlichttypes_bloated.h" +#include "activeobject.h" +#include "inventorymanager.h" +#include "itemgroup.h" +#include "util/container.h" + +/* + +Some planning +------------- + +* Server environment adds an active object, which gets the id 1 +* The active object list is scanned for each client once in a while, + and it finds out what objects have been added that are not known + by the client yet. This scan is initiated by the Server class and + the result ends up directly to the server. +* A network packet is created with the info and sent to the client. +* Environment converts objects to static data and static data to + objects, based on how close players are to them. + +*/ + +class ServerEnvironment; +struct ItemStack; +struct ToolCapabilities; +struct ObjectProperties; +struct PlayerHPChangeReason; + +class ServerActiveObject : public ActiveObject +{ +public: + /* + NOTE: m_env can be NULL, but step() isn't called if it is. + Prototypes are used that way. + */ + ServerActiveObject(ServerEnvironment *env, v3f pos); + virtual ~ServerActiveObject() = default; + + virtual ActiveObjectType getSendType() const + { return getType(); } + + // Called after id has been set and has been inserted in environment + virtual void addedToEnvironment(u32 dtime_s){}; + // Called before removing from environment + virtual void removingFromEnvironment(){}; + // Returns true if object's deletion is the job of the + // environment + virtual bool environmentDeletes() const + { return true; } + + // Create a certain type of ServerActiveObject + static ServerActiveObject* create(ActiveObjectType type, + ServerEnvironment *env, u16 id, v3f pos, + const std::string &data); + + /* + Some simple getters/setters + */ + v3f getBasePosition() const { return m_base_position; } + void setBasePosition(v3f pos){ m_base_position = pos; } + ServerEnvironment* getEnv(){ return m_env; } + + /* + Some more dynamic interface + */ + + virtual void setPos(const v3f &pos) + { setBasePosition(pos); } + // continuous: if true, object does not stop immediately at pos + virtual void moveTo(v3f pos, bool continuous) + { setBasePosition(pos); } + // If object has moved less than this and data has not changed, + // saving to disk may be omitted + virtual float getMinimumSavedMovement(); + + virtual std::string getDescription(){return "SAO";} + + /* + Step object in time. + Messages added to messages are sent to client over network. + + send_recommended: + True at around 5-10 times a second, same for all objects. + This is used to let objects send most of the data at the + same time so that the data can be combined in a single + packet. + */ + virtual void step(float dtime, bool send_recommended){} + + /* + The return value of this is passed to the client-side object + when it is created + */ + virtual std::string getClientInitializationData(u16 protocol_version) {return "";} + + /* + The return value of this is passed to the server-side object + when it is created (converted from static to active - actually + the data is the static form) + */ + virtual void getStaticData(std::string *result) const + { + assert(isStaticAllowed()); + *result = ""; + } + /* + Return false in here to never save and instead remove object + on unload. getStaticData() will not be called in that case. + */ + virtual bool isStaticAllowed() const + {return true;} + + // Returns tool wear + virtual u16 punch(v3f dir, + const ToolCapabilities *toolcap = nullptr, + ServerActiveObject *puncher = nullptr, + float time_from_last_punch = 1000000.0f) + { return 0; } + virtual void rightClick(ServerActiveObject *clicker) + {} + virtual void setHP(s32 hp, const PlayerHPChangeReason &reason) + {} + virtual u16 getHP() const + { return 0; } + + virtual void setArmorGroups(const ItemGroupList &armor_groups) + {} + virtual const ItemGroupList &getArmorGroups() const + { static ItemGroupList rv; return rv; } + virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) + {} + virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop) + {} + virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop) + {} + virtual void setAnimationSpeed(float frame_speed) + {} + virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) + {} + virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) + {} + virtual const std::unordered_set &getAttachmentChildIds() const + { static std::unordered_set rv; return rv; } + virtual ServerActiveObject *getParent() const { return nullptr; } + virtual ObjectProperties* accessObjectProperties() + { return NULL; } + virtual void notifyObjectPropertiesModified() + {} + + // Inventory and wielded item + virtual Inventory *getInventory() const + { return NULL; } + virtual InventoryLocation getInventoryLocation() const + { return InventoryLocation(); } + virtual void setInventoryModified() + {} + virtual std::string getWieldList() const + { return ""; } + virtual u16 getWieldIndex() const + { return 0; } + virtual ItemStack getWieldedItem(ItemStack *selected, + ItemStack *hand = nullptr) const; + virtual bool setWieldedItem(const ItemStack &item); + inline void attachParticleSpawner(u32 id) + { + m_attached_particle_spawners.insert(id); + } + inline void detachParticleSpawner(u32 id) + { + m_attached_particle_spawners.erase(id); + } + + std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); + std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; + + void dumpAOMessagesToQueue(std::queue &queue); + + /* + Number of players which know about this object. Object won't be + deleted until this is 0 to keep the id preserved for the right + object. + */ + u16 m_known_by_count = 0; + + /* + - Whether this object is to be removed when nobody knows about + it anymore. + - Removal is delayed to preserve the id for the time during which + it could be confused to some other object by some client. + - This is usually set to true by the step() method when the object wants + to be deleted but can be set by anything else too. + */ + bool m_pending_removal = false; + + /* + Same purpose as m_pending_removal but for deactivation. + deactvation = save static data in block, remove active object + + If this is set alongside with m_pending_removal, removal takes + priority. + */ + bool m_pending_deactivation = false; + + /* + A getter that unifies the above to answer the question: + "Can the environment still interact with this object?" + */ + inline bool isGone() const + { return m_pending_removal || m_pending_deactivation; } + + /* + Whether the object's static data has been stored to a block + */ + bool m_static_exists = false; + /* + The block from which the object was loaded from, and in which + a copy of the static data resides. + */ + v3s16 m_static_block = v3s16(1337,1337,1337); + +protected: + virtual void onAttach(int parent_id) {} + virtual void onDetach(int parent_id) {} + + // Used for creating objects based on type + typedef ServerActiveObject* (*Factory) + (ServerEnvironment *env, v3f pos, + const std::string &data); + static void registerType(u16 type, Factory f); + + ServerEnvironment *m_env; + v3f m_base_position; + std::unordered_set m_attached_particle_spawners; + + /* + Queue of messages to be sent to the client + */ + std::queue m_messages_out; + +private: + // Used for creating objects based on type + static std::map m_types; +}; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp new file mode 100644 index 000000000..66be67522 --- /dev/null +++ b/src/server/unit_sao.cpp @@ -0,0 +1,318 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +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 "unit_sao.h" +#include "scripting_server.h" +#include "serverenvironment.h" + +/* + UnitSAO + */ + +UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): + ServerActiveObject(env, pos) +{ + // Initialize something to armor groups + m_armor_groups["fleshy"] = 100; +} + +ServerActiveObject *UnitSAO::getParent() const +{ + if (!m_attachment_parent_id) + return nullptr; + // Check if the parent still exists + ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); + + return obj; +} + +void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} + +const ItemGroupList &UnitSAO::getArmorGroups() const +{ + return m_armor_groups; +} + +void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) +{ + // store these so they can be updated to clients + m_animation_range = frame_range; + m_animation_speed = frame_speed; + m_animation_blend = frame_blend; + m_animation_loop = frame_loop; + m_animation_sent = false; +} + +void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) +{ + *frame_range = m_animation_range; + *frame_speed = m_animation_speed; + *frame_blend = m_animation_blend; + *frame_loop = m_animation_loop; +} + +void UnitSAO::setAnimationSpeed(float frame_speed) +{ + m_animation_speed = frame_speed; + m_animation_speed_sent = false; +} + +void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) +{ + // store these so they can be updated to clients + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} + +void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) +{ + *position = m_bone_position[bone].X; + *rotation = m_bone_position[bone].Y; +} + +void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + int old_parent = m_attachment_parent_id; + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; + + if (parent_id != old_parent) { + onDetach(old_parent); + onAttach(parent_id); + } +} + +void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const +{ + *parent_id = m_attachment_parent_id; + *bone = m_attachment_bone; + *position = m_attachment_position; + *rotation = m_attachment_rotation; +} + +void UnitSAO::clearChildAttachments() +{ + for (int child_id : m_attachment_child_ids) { + // Child can be NULL if it was deleted earlier + if (ServerActiveObject *child = m_env->getActiveObject(child_id)) + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + m_attachment_child_ids.clear(); +} + +void UnitSAO::clearParentAttachment() +{ + ServerActiveObject *parent = nullptr; + if (m_attachment_parent_id) { + parent = m_env->getActiveObject(m_attachment_parent_id); + setAttachment(0, "", m_attachment_position, m_attachment_rotation); + } else { + setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + // Do it + if (parent) + parent->removeAttachmentChild(m_id); +} + +void UnitSAO::addAttachmentChild(int child_id) +{ + m_attachment_child_ids.insert(child_id); +} + +void UnitSAO::removeAttachmentChild(int child_id) +{ + m_attachment_child_ids.erase(child_id); +} + +const std::unordered_set &UnitSAO::getAttachmentChildIds() const +{ + return m_attachment_child_ids; +} + +void UnitSAO::onAttach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + // Call parent's on_attach field + m_env->getScriptIface()->luaentity_on_attach_child(parent_id, this); + } +} + +void UnitSAO::onDetach(int parent_id) +{ + if (!parent_id) + return; + + ServerActiveObject *parent = m_env->getActiveObject(parent_id); + if (getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach(m_id, parent); + + if (!parent || parent->isGone()) + return; // Do not try to notify soon gone parent + + if (parent->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) + m_env->getScriptIface()->luaentity_on_detach_child(parent_id, this); +} + +ObjectProperties* UnitSAO::accessObjectProperties() +{ + return &m_prop; +} + +void UnitSAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; +} + +std::string UnitSAO::generateUpdateAttachmentCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_ATTACH_TO); + // parameters + writeS16(os, m_attachment_parent_id); + os << serializeString(m_attachment_bone); + writeV3F32(os, m_attachment_position); + writeV3F32(os, m_attachment_rotation); + return os.str(); +} + +std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_BONE_POSITION); + // parameters + os << serializeString(bone); + writeV3F32(os, position); + writeV3F32(os, rotation); + return os.str(); +} + + +std::string UnitSAO::generateUpdateAnimationSpeedCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION_SPEED); + // parameters + writeF32(os, m_animation_speed); + return os.str(); +} + +std::string UnitSAO::generateUpdateAnimationCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_ANIMATION); + // parameters + writeV2F32(os, m_animation_range); + writeF32(os, m_animation_speed); + writeF32(os, m_animation_blend); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_animation_loop); + return os.str(); +} + + +std::string UnitSAO::generateUpdateArmorGroupsCommand() const +{ + std::ostringstream os(std::ios::binary); + writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); + writeU16(os, m_armor_groups.size()); + for (const auto &armor_group : m_armor_groups) { + os< +Copyright (C) 2013-2020 Minetest core developers & community + +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. +*/ + +#pragma once + +#include "object_properties.h" +#include "serveractiveobject.h" + +class UnitSAO: public ServerActiveObject +{ +public: + UnitSAO(ServerEnvironment *env, v3f pos); + virtual ~UnitSAO() = default; + + void setRotation(v3f rotation) { m_rotation = rotation; } + const v3f &getRotation() const { return m_rotation; } + v3f getRadRotation() { return m_rotation * core::DEGTORAD; } + + // Deprecated + f32 getRadYawDep() const { return (m_rotation.Y + 90.) * core::DEGTORAD; } + + u16 getHP() const { return m_hp; } + // Use a function, if isDead can be defined by other conditions + bool isDead() const { return m_hp == 0; } + + inline bool isAttached() const + { return getParent(); } + + inline bool isImmortal() const + { return itemgroup_get(getArmorGroups(), "immortal"); } + + void setArmorGroups(const ItemGroupList &armor_groups); + const ItemGroupList &getArmorGroups() const; + void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); + void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); + void setAnimationSpeed(float frame_speed); + void setBonePosition(const std::string &bone, v3f position, v3f rotation); + void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) const; + void clearChildAttachments(); + void clearParentAttachment(); + void addAttachmentChild(int child_id); + void removeAttachmentChild(int child_id); + const std::unordered_set &getAttachmentChildIds() const; + ServerActiveObject *getParent() const; + ObjectProperties* accessObjectProperties(); + void notifyObjectPropertiesModified(); + + std::string generateUpdateAttachmentCommand() const; + std::string generateUpdateAnimationSpeedCommand() const; + std::string generateUpdateAnimationCommand() const; + std::string generateUpdateArmorGroupsCommand() const; + static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, + const v3f &acceleration, const v3f &rotation, bool do_interpolate, + bool is_movement_end, f32 update_interval); + std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; + void sendPunchCommand(); + static std::string generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation); + +protected: + u16 m_hp = 1; + + v3f m_rotation; + + bool m_properties_sent = true; + ObjectProperties m_prop; + + ItemGroupList m_armor_groups; + bool m_armor_groups_sent = false; + + v2f m_animation_range; + float m_animation_speed = 0.0f; + float m_animation_blend = 0.0f; + bool m_animation_loop = true; + bool m_animation_sent = false; + bool m_animation_speed_sent = false; + + // Stores position and rotation for each bone name + std::unordered_map> m_bone_position; + bool m_bone_position_sent = false; + + int m_attachment_parent_id = 0; + std::unordered_set m_attachment_child_ids; + std::string m_attachment_bone = ""; + v3f m_attachment_position; + v3f m_attachment_rotation; + bool m_attachment_sent = false; +private: + void onAttach(int parent_id); + void onDetach(int parent_id); + + std::string generatePunchCommand(u16 result_hp) const; +}; diff --git a/src/serverobject.cpp b/src/serverobject.cpp deleted file mode 100644 index 119a41b7b..000000000 --- a/src/serverobject.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -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 "serverobject.h" -#include -#include "inventory.h" -#include "constants.h" // BS -#include "log.h" - -ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): - ActiveObject(0), - m_env(env), - m_base_position(pos) -{ -} - -ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data) -{ - // Find factory function - std::map::iterator n; - n = m_types.find(type); - if(n == m_types.end()) { - // These are 0.3 entity types, return without error. - if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { - return NULL; - } - - // If factory is not found, just return. - warningstream<<"ServerActiveObject: No factory for type=" - <second; - ServerActiveObject *object = (*f)(env, pos, data); - return object; -} - -void ServerActiveObject::registerType(u16 type, Factory f) -{ - std::map::iterator n; - n = m_types.find(type); - if(n != m_types.end()) - return; - m_types[type] = f; -} - -float ServerActiveObject::getMinimumSavedMovement() -{ - return 2.0*BS; -} - -ItemStack ServerActiveObject::getWieldedItem(ItemStack *selected, ItemStack *hand) const -{ - *selected = ItemStack(); - if (hand) - *hand = ItemStack(); - - return ItemStack(); -} - -bool ServerActiveObject::setWieldedItem(const ItemStack &item) -{ - return false; -} - -std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SPAWN_INFANT); - // parameters - writeU16(os, infant_id); - writeU8(os, getSendType()); - os << serializeLongString(getClientInitializationData(protocol_version)); - return os.str(); -} - -std::string ServerActiveObject::generateUpdateNametagAttributesCommand(const video::SColor &color) const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_UPDATE_NAMETAG_ATTRIBUTES); - // parameters - writeU8(os, 1); // version for forward compatibility - writeARGB8(os, color); - return os.str(); -} - -void ServerActiveObject::dumpAOMessagesToQueue(std::queue &queue) -{ - while (!m_messages_out.empty()) { - queue.push(m_messages_out.front()); - m_messages_out.pop(); - } -} \ No newline at end of file diff --git a/src/serverobject.h b/src/serverobject.h deleted file mode 100644 index 2e013a6b6..000000000 --- a/src/serverobject.h +++ /dev/null @@ -1,265 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -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. -*/ - -#pragma once - -#include -#include "irrlichttypes_bloated.h" -#include "activeobject.h" -#include "inventorymanager.h" -#include "itemgroup.h" -#include "util/container.h" - -/* - -Some planning -------------- - -* Server environment adds an active object, which gets the id 1 -* The active object list is scanned for each client once in a while, - and it finds out what objects have been added that are not known - by the client yet. This scan is initiated by the Server class and - the result ends up directly to the server. -* A network packet is created with the info and sent to the client. -* Environment converts objects to static data and static data to - objects, based on how close players are to them. - -*/ - -class ServerEnvironment; -struct ItemStack; -struct ToolCapabilities; -struct ObjectProperties; -struct PlayerHPChangeReason; - -class ServerActiveObject : public ActiveObject -{ -public: - /* - NOTE: m_env can be NULL, but step() isn't called if it is. - Prototypes are used that way. - */ - ServerActiveObject(ServerEnvironment *env, v3f pos); - virtual ~ServerActiveObject() = default; - - virtual ActiveObjectType getSendType() const - { return getType(); } - - // Called after id has been set and has been inserted in environment - virtual void addedToEnvironment(u32 dtime_s){}; - // Called before removing from environment - virtual void removingFromEnvironment(){}; - // Returns true if object's deletion is the job of the - // environment - virtual bool environmentDeletes() const - { return true; } - - // Create a certain type of ServerActiveObject - static ServerActiveObject* create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - - /* - Some simple getters/setters - */ - v3f getBasePosition() const { return m_base_position; } - void setBasePosition(v3f pos){ m_base_position = pos; } - ServerEnvironment* getEnv(){ return m_env; } - - /* - Some more dynamic interface - */ - - virtual void setPos(const v3f &pos) - { setBasePosition(pos); } - // continuous: if true, object does not stop immediately at pos - virtual void moveTo(v3f pos, bool continuous) - { setBasePosition(pos); } - // If object has moved less than this and data has not changed, - // saving to disk may be omitted - virtual float getMinimumSavedMovement(); - - virtual std::string getDescription(){return "SAO";} - - /* - Step object in time. - Messages added to messages are sent to client over network. - - send_recommended: - True at around 5-10 times a second, same for all objects. - This is used to let objects send most of the data at the - same time so that the data can be combined in a single - packet. - */ - virtual void step(float dtime, bool send_recommended){} - - /* - The return value of this is passed to the client-side object - when it is created - */ - virtual std::string getClientInitializationData(u16 protocol_version) {return "";} - - /* - The return value of this is passed to the server-side object - when it is created (converted from static to active - actually - the data is the static form) - */ - virtual void getStaticData(std::string *result) const - { - assert(isStaticAllowed()); - *result = ""; - } - /* - Return false in here to never save and instead remove object - on unload. getStaticData() will not be called in that case. - */ - virtual bool isStaticAllowed() const - {return true;} - - // Returns tool wear - virtual u16 punch(v3f dir, - const ToolCapabilities *toolcap = nullptr, - ServerActiveObject *puncher = nullptr, - float time_from_last_punch = 1000000.0f) - { return 0; } - virtual void rightClick(ServerActiveObject *clicker) - {} - virtual void setHP(s32 hp, const PlayerHPChangeReason &reason) - {} - virtual u16 getHP() const - { return 0; } - - virtual void setArmorGroups(const ItemGroupList &armor_groups) - {} - virtual const ItemGroupList &getArmorGroups() const - { static ItemGroupList rv; return rv; } - virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) - {} - virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop) - {} - virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop) - {} - virtual void setAnimationSpeed(float frame_speed) - {} - virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation) - {} - virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation) - {} - virtual const std::unordered_set &getAttachmentChildIds() const - { static std::unordered_set rv; return rv; } - virtual ServerActiveObject *getParent() const { return nullptr; } - virtual ObjectProperties* accessObjectProperties() - { return NULL; } - virtual void notifyObjectPropertiesModified() - {} - - // Inventory and wielded item - virtual Inventory *getInventory() const - { return NULL; } - virtual InventoryLocation getInventoryLocation() const - { return InventoryLocation(); } - virtual void setInventoryModified() - {} - virtual std::string getWieldList() const - { return ""; } - virtual u16 getWieldIndex() const - { return 0; } - virtual ItemStack getWieldedItem(ItemStack *selected, - ItemStack *hand = nullptr) const; - virtual bool setWieldedItem(const ItemStack &item); - inline void attachParticleSpawner(u32 id) - { - m_attached_particle_spawners.insert(id); - } - inline void detachParticleSpawner(u32 id) - { - m_attached_particle_spawners.erase(id); - } - - std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version); - std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const; - - void dumpAOMessagesToQueue(std::queue &queue); - - /* - Number of players which know about this object. Object won't be - deleted until this is 0 to keep the id preserved for the right - object. - */ - u16 m_known_by_count = 0; - - /* - - Whether this object is to be removed when nobody knows about - it anymore. - - Removal is delayed to preserve the id for the time during which - it could be confused to some other object by some client. - - This is usually set to true by the step() method when the object wants - to be deleted but can be set by anything else too. - */ - bool m_pending_removal = false; - - /* - Same purpose as m_pending_removal but for deactivation. - deactvation = save static data in block, remove active object - - If this is set alongside with m_pending_removal, removal takes - priority. - */ - bool m_pending_deactivation = false; - - /* - A getter that unifies the above to answer the question: - "Can the environment still interact with this object?" - */ - inline bool isGone() const - { return m_pending_removal || m_pending_deactivation; } - - /* - Whether the object's static data has been stored to a block - */ - bool m_static_exists = false; - /* - The block from which the object was loaded from, and in which - a copy of the static data resides. - */ - v3s16 m_static_block = v3s16(1337,1337,1337); - -protected: - virtual void onAttach(int parent_id) {} - virtual void onDetach(int parent_id) {} - - // Used for creating objects based on type - typedef ServerActiveObject* (*Factory) - (ServerEnvironment *env, v3f pos, - const std::string &data); - static void registerType(u16 type, Factory f); - - ServerEnvironment *m_env; - v3f m_base_position; - std::unordered_set m_attached_particle_spawners; - - /* - Queue of messages to be sent to the client - */ - std::queue m_messages_out; - -private: - // Used for creating objects based on type - static std::map m_types; -}; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 7b2fd8236..816ec2c59 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -402,16 +402,14 @@ src/script/scripting_server.cpp src/script/scripting_server.h src/serialization.cpp src/serialization.h -src/serveractiveobjectmap.cpp -src/serveractiveobjectmap.h src/server.cpp src/serverenvironment.cpp src/serverenvironment.h src/server.h src/serverlist.cpp src/serverlist.h -src/serverobject.cpp -src/serverobject.h +src/server/serveractiveobject.cpp +src/server/serveractiveobject.h src/settings.cpp src/settings.h src/settings_translation_file.cpp -- cgit v1.2.3 From 894a34aef48024a752a1ef151d046955d83858d0 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 09:59:09 +0200 Subject: Move PlayerSAO to dedicated files --- src/clientiface.cpp | 3 +- src/content_sao.cpp | 676 +--------------------------------- src/content_sao.h | 299 --------------- src/database/database-files.cpp | 2 +- src/database/database-postgresql.cpp | 2 +- src/database/database-sqlite3.cpp | 2 +- src/network/serverpackethandler.cpp | 2 +- src/remoteplayer.cpp | 2 +- src/script/common/c_content.cpp | 3 +- src/script/cpp_api/s_base.cpp | 2 +- src/script/cpp_api/s_player.h | 1 + src/script/lua_api/l_env.cpp | 3 +- src/script/lua_api/l_object.cpp | 2 +- src/server.cpp | 2 +- src/server/CMakeLists.txt | 1 + src/server/player_sao.cpp | 695 +++++++++++++++++++++++++++++++++++ src/server/player_sao.h | 325 ++++++++++++++++ src/server/unit_sao.cpp | 4 - src/serverenvironment.cpp | 4 +- 19 files changed, 1038 insertions(+), 992 deletions(-) create mode 100644 src/server/player_sao.cpp create mode 100644 src/server/player_sao.h (limited to 'src/script/lua_api') diff --git a/src/clientiface.cpp b/src/clientiface.cpp index dceaa64f2..17237f73e 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "clientiface.h" +#include "content_sao.h" #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" @@ -27,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "map.h" #include "emerge.h" -#include "content_sao.h" // TODO this is used for cleanup of only +#include "server/player_sao.h" #include "log.h" #include "util/srp.h" #include "face_position_cache.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 0d387b53a..7ec17aa82 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server.h" #include "scripting_server.h" +#include "server/player_sao.h" #include "settings.h" #include #include @@ -678,678 +679,3 @@ bool LuaEntitySAO::collideWithObjects() const { return m_prop.collideWithObjects; } - -/* - PlayerSAO -*/ - -// No prototype, PlayerSAO does not need to be deserialized - -PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer): - UnitSAO(env_, v3f(0,0,0)), - m_player(player_), - m_peer_id(peer_id_), - m_is_singleplayer(is_singleplayer) -{ - SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); - - m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; - m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; - m_prop.physical = false; - m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); - m_prop.pointable = true; - // Start of default appearance, this should be overwritten by Lua - m_prop.visual = "upright_sprite"; - m_prop.visual_size = v3f(1, 2, 1); - m_prop.textures.clear(); - m_prop.textures.emplace_back("player.png"); - m_prop.textures.emplace_back("player_back.png"); - m_prop.colors.clear(); - m_prop.colors.emplace_back(255, 255, 255, 255); - m_prop.spritediv = v2s16(1,1); - m_prop.eye_height = 1.625f; - // End of default appearance - m_prop.is_visible = true; - m_prop.backface_culling = false; - m_prop.makes_footstep_sound = true; - m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; - m_hp = m_prop.hp_max; - m_breath = m_prop.breath_max; - // Disable zoom in survival mode using a value of 0 - m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; - - if (!g_settings->getBool("enable_damage")) - m_armor_groups["immortal"] = 1; -} - -void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) -{ - assert(player); - m_player = player; - m_privs = privs; -} - -v3f PlayerSAO::getEyeOffset() const -{ - return v3f(0, BS * m_prop.eye_height, 0); -} - -std::string PlayerSAO::getDescription() -{ - return std::string("player ") + m_player->getName(); -} - -// Called after id has been set and has been inserted in environment -void PlayerSAO::addedToEnvironment(u32 dtime_s) -{ - ServerActiveObject::addedToEnvironment(dtime_s); - ServerActiveObject::setBasePosition(m_base_position); - m_player->setPlayerSAO(this); - m_player->setPeerId(m_peer_id); - m_last_good_position = m_base_position; -} - -// Called before removing from environment -void PlayerSAO::removingFromEnvironment() -{ - ServerActiveObject::removingFromEnvironment(); - if (m_player->getPlayerSAO() == this) { - unlinkPlayerSessionAndSave(); - for (u32 attached_particle_spawner : m_attached_particle_spawners) { - m_env->deleteParticleSpawner(attached_particle_spawner, false); - } - } -} - -std::string PlayerSAO::getClientInitializationData(u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - - // Protocol >= 15 - writeU8(os, 1); // version - os << serializeString(m_player->getName()); // name - writeU8(os, 1); // is_player - writeS16(os, getId()); // id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, getHP()); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 - // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); - } - } - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); - - // return result - return os.str(); -} - -void PlayerSAO::getStaticData(std::string * result) const -{ - FATAL_ERROR("Obsolete function"); -} - -void PlayerSAO::step(float dtime, bool send_recommended) -{ - if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If node generates drown - if (c.drowning > 0 && m_hp > 0) { - if (m_breath > 0) - setBreath(m_breath - 1); - - // No more breath, damage player - if (m_breath == 0) { - PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); - setHP(m_hp - c.drowning, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - } - - if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { - // Get nose/mouth position, approximate with eye position - v3s16 p = floatToInt(getEyePosition(), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - // If player is alive & not drowning & not in ignore & not immortal, breathe - if (m_breath < m_prop.breath_max && c.drowning == 0 && - n.getContent() != CONTENT_IGNORE && m_hp > 0) - setBreath(m_breath + 1); - } - - if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { - u32 damage_per_second = 0; - std::string nodename; - // Lowest and highest damage points are 0.1 within collisionbox - float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; - - // Sequence of damage points, starting 0.1 above feet and progressing - // upwards in 1 node intervals, stopping below top damage point. - for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { - v3s16 p = floatToInt(m_base_position + - v3f(0.0f, dam_height * BS, 0.0f), BS); - MapNode n = m_env->getMap().getNode(p); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - } - - // Top damage point - v3s16 ptop = floatToInt(m_base_position + - v3f(0.0f, dam_top * BS, 0.0f), BS); - MapNode ntop = m_env->getMap().getNode(ptop); - const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); - if (c.damage_per_second > damage_per_second) { - damage_per_second = c.damage_per_second; - nodename = c.name; - } - - if (damage_per_second != 0 && m_hp > 0) { - s32 newhp = (s32)m_hp - (s32)damage_per_second; - PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); - setHP(newhp, reason); - m_env->getGameDef()->SendPlayerHPOrDie(this, reason); - } - } - - if (!m_properties_sent) { - m_properties_sent = true; - std::string str = getPropertyPacket(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - m_env->getScriptIface()->player_event(this, "properties_changed"); - } - - // If attached, check that our parent is still there. If it isn't, detach. - if (m_attachment_parent_id && !isAttached()) { - m_attachment_parent_id = 0; - m_attachment_bone = ""; - m_attachment_position = v3f(0.0f, 0.0f, 0.0f); - m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); - setBasePosition(m_last_good_position); - m_env->getGameDef()->SendMovePlayer(m_peer_id); - } - - //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; - if(lag_pool_max < LAG_POOL_MIN) - lag_pool_max = LAG_POOL_MIN; - m_dig_pool.setMax(lag_pool_max); - m_move_pool.setMax(lag_pool_max); - - // Increment cheat prevention timers - m_dig_pool.add(dtime); - m_move_pool.add(dtime); - m_time_from_last_teleport += dtime; - m_time_from_last_punch += dtime; - m_nocheat_dig_time += dtime; - m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); - - // Each frame, parent position is copied if the object is attached, - // otherwise it's calculated normally. - // If the object gets detached this comes into effect automatically from - // the last known origin. - if (isAttached()) { - v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); - m_last_good_position = pos; - setBasePosition(pos); - } - - if (!send_recommended) - return; - - if (m_position_not_sent) { - m_position_not_sent = false; - float update_interval = m_env->getSendRecommendedInterval(); - v3f pos; - // When attached, the position is only sent to clients where the - // parent isn't known - if (isAttached()) - pos = m_last_good_position; - else - pos = m_base_position; - - std::string str = generateUpdatePositionCommand( - pos, - v3f(0.0f, 0.0f, 0.0f), - v3f(0.0f, 0.0f, 0.0f), - m_rotation, - true, - false, - update_interval - ); - // create message and add to list - m_messages_out.emplace(getId(), false, str); - } - - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_physics_override_sent) { - m_physics_override_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - m_messages_out.emplace(getId(), true, str); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } -} - -std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); - // parameters - writeF32(os, m_physics_override_speed); - writeF32(os, m_physics_override_jump); - writeF32(os, m_physics_override_gravity); - // these are sent inverted so we get true when the server sends nothing - writeU8(os, !m_physics_override_sneak); - writeU8(os, !m_physics_override_sneak_glitch); - writeU8(os, !m_physics_override_new_move); - return os.str(); -} - -void PlayerSAO::setBasePosition(const v3f &position) -{ - if (m_player && position != m_base_position) - m_player->setDirty(true); - - // This needs to be ran for attachments too - ServerActiveObject::setBasePosition(position); - - // Updating is not wanted/required for player migration - if (m_env) { - m_position_not_sent = true; - } -} - -void PlayerSAO::setPos(const v3f &pos) -{ - if(isAttached()) - return; - - // Send mapblock of target location - v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); - m_env->getGameDef()->SendBlock(m_peer_id, blockpos); - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::moveTo(v3f pos, bool continuous) -{ - if(isAttached()) - return; - - setBasePosition(pos); - // Movement caused by this command is always valid - m_last_good_position = pos; - m_move_pool.empty(); - m_time_from_last_teleport = 0.0; - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setPlayerYaw(const float yaw) -{ - v3f rotation(0, yaw, 0); - if (m_player && yaw != m_rotation.Y) - m_player->setDirty(true); - - // Set player model yaw, not look view - UnitSAO::setRotation(rotation); -} - -void PlayerSAO::setFov(const float fov) -{ - if (m_player && fov != m_fov) - m_player->setDirty(true); - - m_fov = fov; -} - -void PlayerSAO::setWantedRange(const s16 range) -{ - if (m_player && range != m_wanted_range) - m_player->setDirty(true); - - m_wanted_range = range; -} - -void PlayerSAO::setPlayerYawAndSend(const float yaw) -{ - setPlayerYaw(yaw); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -void PlayerSAO::setLookPitch(const float pitch) -{ - if (m_player && pitch != m_pitch) - m_player->setDirty(true); - - m_pitch = pitch; -} - -void PlayerSAO::setLookPitchAndSend(const float pitch) -{ - setLookPitch(pitch); - m_env->getGameDef()->SendMovePlayer(m_peer_id); -} - -u16 PlayerSAO::punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch) -{ - if (!toolcap) - return 0; - - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - - // No effect if PvP disabled or if immortal - if (isImmortal() || !g_settings->getBool("enable_pvp")) { - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - return 0; - } - } - - s32 old_hp = getHP(); - HitParams hitparams = getHitParams(m_armor_groups, toolcap, - time_from_last_punch); - - PlayerSAO *playersao = m_player->getPlayerSAO(); - - bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, - puncher, time_from_last_punch, toolcap, dir, - hitparams.hp); - - if (!damage_handled) { - setHP((s32)getHP() - (s32)hitparams.hp, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - } else { // override client prediction - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // create message and add to list - sendPunchCommand(); - } - } - - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - - return hitparams.wear; -} - -void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) -{ - s32 oldhp = m_hp; - - hp = rangelim(hp, 0, m_prop.hp_max); - - if (oldhp != hp) { - s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); - if (hp_change == 0) - return; - - hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); - } - - if (hp < oldhp && isImmortal()) - return; - - m_hp = hp; - - // Update properties on death - if ((hp == 0) != (oldhp == 0)) - m_properties_sent = false; -} - -void PlayerSAO::setBreath(const u16 breath, bool send) -{ - if (m_player && breath != m_breath) - m_player->setDirty(true); - - m_breath = rangelim(breath, 0, m_prop.breath_max); - - if (send) - m_env->getGameDef()->SendPlayerBreath(this); -} - -Inventory *PlayerSAO::getInventory() const -{ - return m_player ? &m_player->inventory : nullptr; -} - -InventoryLocation PlayerSAO::getInventoryLocation() const -{ - InventoryLocation loc; - loc.setPlayer(m_player->getName()); - return loc; -} - -u16 PlayerSAO::getWieldIndex() const -{ - return m_player->getWieldIndex(); -} - -ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const -{ - return m_player->getWieldedItem(selected, hand); -} - -bool PlayerSAO::setWieldedItem(const ItemStack &item) -{ - InventoryList *mlist = m_player->inventory.getList(getWieldList()); - if (mlist) { - mlist->changeItem(m_player->getWieldIndex(), item); - return true; - } - return false; -} - -void PlayerSAO::disconnected() -{ - m_peer_id = PEER_ID_INEXISTENT; - m_pending_removal = true; -} - -void PlayerSAO::unlinkPlayerSessionAndSave() -{ - assert(m_player->getPlayerSAO() == this); - m_player->setPeerId(PEER_ID_INEXISTENT); - m_env->savePlayer(m_player); - m_player->setPlayerSAO(NULL); - m_env->removePlayer(m_player); -} - -std::string PlayerSAO::getPropertyPacket() -{ - m_prop.is_visible = (true); - return generateSetPropertiesCommand(m_prop); -} - -void PlayerSAO::setMaxSpeedOverride(const v3f &vel) -{ - if (m_max_speed_override_time == 0.0f) - m_max_speed_override = vel; - else - m_max_speed_override += vel; - if (m_player) { - float accel = MYMIN(m_player->movement_acceleration_default, - m_player->movement_acceleration_air); - m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; - } -} - -bool PlayerSAO::checkMovementCheat() -{ - if (isAttached() || m_is_singleplayer || - g_settings->getBool("disable_anticheat")) { - m_last_good_position = m_base_position; - return false; - } - - bool cheated = false; - /* - Check player movements - - NOTE: Actually the server should handle player physics like the - client does and compare player's position to what is calculated - on our side. This is required when eg. players fly due to an - explosion. Altough a node-based alternative might be possible - too, and much more lightweight. - */ - - float override_max_H, override_max_V; - if (m_max_speed_override_time > 0.0f) { - override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); - override_max_V = fabs(m_max_speed_override.Y); - } else { - override_max_H = override_max_V = 0.0f; - } - - float player_max_walk = 0; // horizontal movement - float player_max_jump = 0; // vertical upwards movement - - if (m_privs.count("fast") != 0) - player_max_walk = m_player->movement_speed_fast; // Fast speed - else - player_max_walk = m_player->movement_speed_walk; // Normal speed - player_max_walk *= m_physics_override_speed; - player_max_walk = MYMAX(player_max_walk, override_max_H); - - player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; - // FIXME: Bouncy nodes cause practically unbound increase in Y speed, - // until this can be verified correctly, tolerate higher jumping speeds - player_max_jump *= 2.0; - player_max_jump = MYMAX(player_max_jump, override_max_V); - - // Don't divide by zero! - if (player_max_walk < 0.0001f) - player_max_walk = 0.0001f; - if (player_max_jump < 0.0001f) - player_max_jump = 0.0001f; - - v3f diff = (m_base_position - m_last_good_position); - float d_vert = diff.Y; - diff.Y = 0; - float d_horiz = diff.getLength(); - float required_time = d_horiz / player_max_walk; - - // FIXME: Checking downwards movement is not easily possible currently, - // the server could calculate speed differences to examine the gravity - if (d_vert > 0) { - // In certain cases (water, ladders) walking speed is applied vertically - float s = MYMAX(player_max_jump, player_max_walk); - required_time = MYMAX(required_time, d_vert / s); - } - - if (m_move_pool.grab(required_time)) { - m_last_good_position = m_base_position; - } else { - const float LAG_POOL_MIN = 5.0; - float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; - lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); - if (m_time_from_last_teleport > lag_pool_max) { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; - cheated = true; - } - setBasePosition(m_last_good_position); - } - return cheated; -} - -bool PlayerSAO::getCollisionBox(aabb3f *toset) const -{ - //update collision box - toset->MinEdge = m_prop.collisionbox.MinEdge * BS; - toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; - - toset->MinEdge += m_base_position; - toset->MaxEdge += m_base_position; - return true; -} - -bool PlayerSAO::getSelectionBox(aabb3f *toset) const -{ - if (!m_prop.is_visible || !m_prop.pointable) { - return false; - } - - toset->MinEdge = m_prop.selectionbox.MinEdge * BS; - toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; - - return true; -} - -float PlayerSAO::getZoomFOV() const -{ - return m_prop.zoom_fov; -} diff --git a/src/content_sao.h b/src/content_sao.h index 32ab922d6..5387fd108 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -99,302 +99,3 @@ private: std::string m_current_texture_modifier = ""; }; -/* - PlayerSAO needs some internals exposed. -*/ - -class LagPool -{ - float m_pool = 15.0f; - float m_max = 15.0f; -public: - LagPool() = default; - - void setMax(float new_max) - { - m_max = new_max; - if(m_pool > new_max) - m_pool = new_max; - } - - void add(float dtime) - { - m_pool -= dtime; - if(m_pool < 0) - m_pool = 0; - } - - void empty() - { - m_pool = m_max; - } - - bool grab(float dtime) - { - if(dtime <= 0) - return true; - if(m_pool + dtime > m_max) - return false; - m_pool += dtime; - return true; - } -}; - -class RemotePlayer; - -class PlayerSAO : public UnitSAO -{ -public: - PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, - bool is_singleplayer); - - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_PLAYER; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } - std::string getDescription(); - - /* - Active object <-> environment interface - */ - - void addedToEnvironment(u32 dtime_s); - void removingFromEnvironment(); - bool isStaticAllowed() const { return false; } - std::string getClientInitializationData(u16 protocol_version); - void getStaticData(std::string *result) const; - void step(float dtime, bool send_recommended); - void setBasePosition(const v3f &position); - void setPos(const v3f &pos); - void moveTo(v3f pos, bool continuous); - void setPlayerYaw(const float yaw); - // Data should not be sent at player initialization - void setPlayerYawAndSend(const float yaw); - void setLookPitch(const float pitch); - // Data should not be sent at player initialization - void setLookPitchAndSend(const float pitch); - f32 getLookPitch() const { return m_pitch; } - f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } - // Deprecated - f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } - void setFov(const float pitch); - f32 getFov() const { return m_fov; } - void setWantedRange(const s16 range); - s16 getWantedRange() const { return m_wanted_range; } - - /* - Interaction interface - */ - - u16 punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch); - void rightClick(ServerActiveObject *clicker) {} - void setHP(s32 hp, const PlayerHPChangeReason &reason); - void setHPRaw(u16 hp) { m_hp = hp; } - s16 readDamage(); - u16 getBreath() const { return m_breath; } - void setBreath(const u16 breath, bool send = true); - - /* - Inventory interface - */ - Inventory *getInventory() const; - InventoryLocation getInventoryLocation() const; - void setInventoryModified() {} - std::string getWieldList() const { return "main"; } - u16 getWieldIndex() const; - ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; - bool setWieldedItem(const ItemStack &item); - - /* - PlayerSAO-specific - */ - - void disconnected(); - - RemotePlayer *getPlayer() { return m_player; } - session_t getPeerID() const { return m_peer_id; } - - // Cheat prevention - - v3f getLastGoodPosition() const - { - return m_last_good_position; - } - float resetTimeFromLastPunch() - { - float r = m_time_from_last_punch; - m_time_from_last_punch = 0.0; - return r; - } - void noCheatDigStart(const v3s16 &p) - { - m_nocheat_dig_pos = p; - m_nocheat_dig_time = 0; - } - v3s16 getNoCheatDigPos() - { - return m_nocheat_dig_pos; - } - float getNoCheatDigTime() - { - return m_nocheat_dig_time; - } - void noCheatDigEnd() - { - m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - } - LagPool& getDigPool() - { - return m_dig_pool; - } - void setMaxSpeedOverride(const v3f &vel); - // Returns true if cheated - bool checkMovementCheat(); - - // Other - - void updatePrivileges(const std::set &privs, - bool is_singleplayer) - { - m_privs = privs; - m_is_singleplayer = is_singleplayer; - } - - bool getCollisionBox(aabb3f *toset) const; - bool getSelectionBox(aabb3f *toset) const; - bool collideWithObjects() const { return true; } - - void finalize(RemotePlayer *player, const std::set &privs); - - v3f getEyePosition() const { return m_base_position + getEyeOffset(); } - v3f getEyeOffset() const; - float getZoomFOV() const; - - inline Metadata &getMeta() { return m_meta; } - -private: - std::string getPropertyPacket(); - void unlinkPlayerSessionAndSave(); - std::string generateUpdatePhysicsOverrideCommand() const; - - RemotePlayer *m_player = nullptr; - session_t m_peer_id = 0; - - // Cheat prevention - LagPool m_dig_pool; - LagPool m_move_pool; - v3f m_last_good_position; - float m_time_from_last_teleport = 0.0f; - float m_time_from_last_punch = 0.0f; - v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - float m_nocheat_dig_time = 0.0f; - float m_max_speed_override_time = 0.0f; - v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); - - // Timers - IntervalLimiter m_breathing_interval; - IntervalLimiter m_drowning_interval; - IntervalLimiter m_node_hurt_interval; - - bool m_position_not_sent = false; - - // Cached privileges for enforcement - std::set m_privs; - bool m_is_singleplayer; - - u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; - f32 m_pitch = 0.0f; - f32 m_fov = 0.0f; - s16 m_wanted_range = 0.0f; - - Metadata m_meta; -public: - float m_physics_override_speed = 1.0f; - float m_physics_override_jump = 1.0f; - float m_physics_override_gravity = 1.0f; - bool m_physics_override_sneak = true; - bool m_physics_override_sneak_glitch = false; - bool m_physics_override_new_move = true; - bool m_physics_override_sent = false; -}; - - -struct PlayerHPChangeReason { - enum Type : u8 { - SET_HP, - PLAYER_PUNCH, - FALL, - NODE_DAMAGE, - DROWNING, - RESPAWN - }; - - Type type = SET_HP; - bool from_mod = false; - int lua_reference = -1; - - // For PLAYER_PUNCH - ServerActiveObject *object = nullptr; - // For NODE_DAMAGE - std::string node; - - inline bool hasLuaReference() const - { - return lua_reference >= 0; - } - - bool setTypeFromString(const std::string &typestr) - { - if (typestr == "set_hp") - type = SET_HP; - else if (typestr == "punch") - type = PLAYER_PUNCH; - else if (typestr == "fall") - type = FALL; - else if (typestr == "node_damage") - type = NODE_DAMAGE; - else if (typestr == "drown") - type = DROWNING; - else if (typestr == "respawn") - type = RESPAWN; - else - return false; - - return true; - } - - std::string getTypeAsString() const - { - switch (type) { - case PlayerHPChangeReason::SET_HP: - return "set_hp"; - case PlayerHPChangeReason::PLAYER_PUNCH: - return "punch"; - case PlayerHPChangeReason::FALL: - return "fall"; - case PlayerHPChangeReason::NODE_DAMAGE: - return "node_damage"; - case PlayerHPChangeReason::DROWNING: - return "drown"; - case PlayerHPChangeReason::RESPAWN: - return "respawn"; - default: - return "?"; - } - } - - PlayerHPChangeReason(Type type): - type(type) - {} - - PlayerHPChangeReason(Type type, ServerActiveObject *object): - type(type), object(object) - {} - - PlayerHPChangeReason(Type type, std::string node): - type(type), node(node) - {} -}; diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index d09f1c074..d2b0b1543 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "database-files.h" -#include "content_sao.h" #include "remoteplayer.h" #include "settings.h" #include "porting.h" #include "filesys.h" +#include "server/player_sao.h" #include "util/string.h" // !!! WARNING !!! diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index d7c94ff15..77385e240 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "exceptions.h" #include "settings.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : m_connect_string(connect_string) diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 1bacdfe6c..4560743b9 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -33,8 +33,8 @@ SQLite format specification: #include "settings.h" #include "porting.h" #include "util/string.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" #include diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 23bcc867f..b2fdb2a22 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "server.h" #include "log.h" -#include "content_sao.h" #include "emerge.h" #include "mapblock.h" #include "modchannels.h" @@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/connection.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" +#include "server/player_sao.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 1a8fec68c..7a603d53e 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -20,13 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include -#include "content_sao.h" #include "filesys.h" #include "gamedef.h" #include "porting.h" // strlcpy #include "server.h" #include "settings.h" #include "convert_json.h" +#include "server/player_sao.h" /* RemotePlayer diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 60f12052f..c8cd7539f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_types.h" #include "nodedef.h" #include "object_properties.h" -#include "content_sao.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" @@ -29,10 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "porting.h" #include "mapgen/mg_schematic.h" #include "noise.h" +#include "server/player_sao.h" #include "util/pointedthing.h" #include "debug.h" // For FATAL_ERROR #include diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 16c20eeae..150baf77e 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_security.h" #include "lua_api/l_object.h" #include "common/c_converter.h" -#include "server/serveractiveobject.h" +#include "server/player_sao.h" #include "filesys.h" #include "content/mods.h" #include "porting.h" diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index cf24ddc73..7ca3d8f30 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -28,6 +28,7 @@ struct InventoryLocation; struct ItemStack; struct ToolCapabilities; struct PlayerHPChangeReason; +class ServerActiveObject; class ScriptApiPlayer : virtual public ScriptApiBase { diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 31e582d3d..438669feb 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "lua_api/l_env.h" #include "lua_api/l_internal.h" #include "lua_api/l_nodemeta.h" @@ -25,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include #include "scripting_server.h" #include "environment.h" #include "mapblock.h" @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "face_position_cache.h" #include "remoteplayer.h" +#include "server/player_sao.h" #ifndef SERVER #include "client/client.h" #endif diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 1ea144a1c..95c96235e 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,12 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "server/serveractiveobject.h" #include "content_sao.h" #include "remoteplayer.h" #include "server.h" #include "hud.h" #include "scripting_server.h" +#include "server/player_sao.h" /* ObjectRef diff --git a/src/server.cpp b/src/server.cpp index 529466f6b..85d07fbc4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen/mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" -#include "content_sao.h" #include "content/mods.h" #include "modchannels.h" #include "serverlist.h" @@ -64,6 +63,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chatmessage.h" #include "chat_interface.h" #include "remoteplayer.h" +#include "server/player_sao.h" class ClientNotFoundException : public BaseException { diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 9fa5ed9fa..26eaed5ac 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,6 +1,7 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp new file mode 100644 index 000000000..58fcea5fe --- /dev/null +++ b/src/server/player_sao.cpp @@ -0,0 +1,695 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +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 "player_sao.h" +#include "nodedef.h" +#include "remoteplayer.h" +#include "scripting_server.h" +#include "server.h" +#include "serverenvironment.h" + +PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer): + UnitSAO(env_, v3f(0,0,0)), + m_player(player_), + m_peer_id(peer_id_), + m_is_singleplayer(is_singleplayer) +{ + SANITY_CHECK(m_peer_id != PEER_ID_INEXISTENT); + + m_prop.hp_max = PLAYER_MAX_HP_DEFAULT; + m_prop.breath_max = PLAYER_MAX_BREATH_DEFAULT; + m_prop.physical = false; + m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f); + m_prop.pointable = true; + // Start of default appearance, this should be overwritten by Lua + m_prop.visual = "upright_sprite"; + m_prop.visual_size = v3f(1, 2, 1); + m_prop.textures.clear(); + m_prop.textures.emplace_back("player.png"); + m_prop.textures.emplace_back("player_back.png"); + m_prop.colors.clear(); + m_prop.colors.emplace_back(255, 255, 255, 255); + m_prop.spritediv = v2s16(1,1); + m_prop.eye_height = 1.625f; + // End of default appearance + m_prop.is_visible = true; + m_prop.backface_culling = false; + m_prop.makes_footstep_sound = true; + m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; + m_hp = m_prop.hp_max; + m_breath = m_prop.breath_max; + // Disable zoom in survival mode using a value of 0 + m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f; + + if (!g_settings->getBool("enable_damage")) + m_armor_groups["immortal"] = 1; +} + +void PlayerSAO::finalize(RemotePlayer *player, const std::set &privs) +{ + assert(player); + m_player = player; + m_privs = privs; +} + +v3f PlayerSAO::getEyeOffset() const +{ + return v3f(0, BS * m_prop.eye_height, 0); +} + +std::string PlayerSAO::getDescription() +{ + return std::string("player ") + m_player->getName(); +} + +// Called after id has been set and has been inserted in environment +void PlayerSAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + ServerActiveObject::setBasePosition(m_base_position); + m_player->setPlayerSAO(this); + m_player->setPeerId(m_peer_id); + m_last_good_position = m_base_position; +} + +// Called before removing from environment +void PlayerSAO::removingFromEnvironment() +{ + ServerActiveObject::removingFromEnvironment(); + if (m_player->getPlayerSAO() == this) { + unlinkPlayerSessionAndSave(); + for (u32 attached_particle_spawner : m_attached_particle_spawners) { + m_env->deleteParticleSpawner(attached_particle_spawner, false); + } + } +} + +std::string PlayerSAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + // Protocol >= 15 + writeU8(os, 1); // version + os << serializeString(m_player->getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); // id + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, getHP()); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + } + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + // (AO_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. + msg_os << serializeLongString(generateUpdateNametagAttributesCommand(m_prop.nametag_color)); // 6 + int message_count = 6 + m_bone_position.size(); + for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); + ii != m_attachment_child_ids.end(); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + } + } + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + + // return result + return os.str(); +} + +void PlayerSAO::getStaticData(std::string * result) const +{ + FATAL_ERROR("Obsolete function"); +} + +void PlayerSAO::step(float dtime, bool send_recommended) +{ + if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If node generates drown + if (c.drowning > 0 && m_hp > 0) { + if (m_breath > 0) + setBreath(m_breath - 1); + + // No more breath, damage player + if (m_breath == 0) { + PlayerHPChangeReason reason(PlayerHPChangeReason::DROWNING); + setHP(m_hp - c.drowning, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + } + + if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { + // Get nose/mouth position, approximate with eye position + v3s16 p = floatToInt(getEyePosition(), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + // If player is alive & not drowning & not in ignore & not immortal, breathe + if (m_breath < m_prop.breath_max && c.drowning == 0 && + n.getContent() != CONTENT_IGNORE && m_hp > 0) + setBreath(m_breath + 1); + } + + if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { + u32 damage_per_second = 0; + std::string nodename; + // Lowest and highest damage points are 0.1 within collisionbox + float dam_top = m_prop.collisionbox.MaxEdge.Y - 0.1f; + + // Sequence of damage points, starting 0.1 above feet and progressing + // upwards in 1 node intervals, stopping below top damage point. + for (float dam_height = 0.1f; dam_height < dam_top; dam_height++) { + v3s16 p = floatToInt(m_base_position + + v3f(0.0f, dam_height * BS, 0.0f), BS); + MapNode n = m_env->getMap().getNode(p); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + } + + // Top damage point + v3s16 ptop = floatToInt(m_base_position + + v3f(0.0f, dam_top * BS, 0.0f), BS); + MapNode ntop = m_env->getMap().getNode(ptop); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(ntop); + if (c.damage_per_second > damage_per_second) { + damage_per_second = c.damage_per_second; + nodename = c.name; + } + + if (damage_per_second != 0 && m_hp > 0) { + s32 newhp = (s32)m_hp - (s32)damage_per_second; + PlayerHPChangeReason reason(PlayerHPChangeReason::NODE_DAMAGE, nodename); + setHP(newhp, reason); + m_env->getGameDef()->SendPlayerHPOrDie(this, reason); + } + } + + if (!m_properties_sent) { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + m_env->getScriptIface()->player_event(this, "properties_changed"); + } + + // If attached, check that our parent is still there. If it isn't, detach. + if (m_attachment_parent_id && !isAttached()) { + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0.0f, 0.0f, 0.0f); + m_attachment_rotation = v3f(0.0f, 0.0f, 0.0f); + setBasePosition(m_last_good_position); + m_env->getGameDef()->SendMovePlayer(m_peer_id); + } + + //dstream<<"PlayerSAO::step: dtime: "<getMaxLagEstimate() * 2.0f; + if(lag_pool_max < LAG_POOL_MIN) + lag_pool_max = LAG_POOL_MIN; + m_dig_pool.setMax(lag_pool_max); + m_move_pool.setMax(lag_pool_max); + + // Increment cheat prevention timers + m_dig_pool.add(dtime); + m_move_pool.add(dtime); + m_time_from_last_teleport += dtime; + m_time_from_last_punch += dtime; + m_nocheat_dig_time += dtime; + m_max_speed_override_time = MYMAX(m_max_speed_override_time - dtime, 0.0f); + + // Each frame, parent position is copied if the object is attached, + // otherwise it's calculated normally. + // If the object gets detached this comes into effect automatically from + // the last known origin. + if (isAttached()) { + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_last_good_position = pos; + setBasePosition(pos); + } + + if (!send_recommended) + return; + + if (m_position_not_sent) { + m_position_not_sent = false; + float update_interval = m_env->getSendRecommendedInterval(); + v3f pos; + // When attached, the position is only sent to clients where the + // parent isn't known + if (isAttached()) + pos = m_last_good_position; + else + pos = m_base_position; + + std::string str = generateUpdatePositionCommand( + pos, + v3f(0.0f, 0.0f, 0.0f), + v3f(0.0f, 0.0f, 0.0f), + m_rotation, + true, + false, + update_interval + ); + // create message and add to list + m_messages_out.emplace(getId(), false, str); + } + + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_physics_override_sent) { + m_physics_override_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + std::string str = generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y); + // create message and add to list + m_messages_out.emplace(getId(), true, str); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + std::string str = generateUpdateAttachmentCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } +} + +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); + // parameters + writeF32(os, m_physics_override_speed); + writeF32(os, m_physics_override_jump); + writeF32(os, m_physics_override_gravity); + // these are sent inverted so we get true when the server sends nothing + writeU8(os, !m_physics_override_sneak); + writeU8(os, !m_physics_override_sneak_glitch); + writeU8(os, !m_physics_override_new_move); + return os.str(); +} + +void PlayerSAO::setBasePosition(const v3f &position) +{ + if (m_player && position != m_base_position) + m_player->setDirty(true); + + // This needs to be ran for attachments too + ServerActiveObject::setBasePosition(position); + + // Updating is not wanted/required for player migration + if (m_env) { + m_position_not_sent = true; + } +} + +void PlayerSAO::setPos(const v3f &pos) +{ + if(isAttached()) + return; + + // Send mapblock of target location + v3s16 blockpos = v3s16(pos.X / MAP_BLOCKSIZE, pos.Y / MAP_BLOCKSIZE, pos.Z / MAP_BLOCKSIZE); + m_env->getGameDef()->SendBlock(m_peer_id, blockpos); + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::moveTo(v3f pos, bool continuous) +{ + if(isAttached()) + return; + + setBasePosition(pos); + // Movement caused by this command is always valid + m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setPlayerYaw(const float yaw) +{ + v3f rotation(0, yaw, 0); + if (m_player && yaw != m_rotation.Y) + m_player->setDirty(true); + + // Set player model yaw, not look view + UnitSAO::setRotation(rotation); +} + +void PlayerSAO::setFov(const float fov) +{ + if (m_player && fov != m_fov) + m_player->setDirty(true); + + m_fov = fov; +} + +void PlayerSAO::setWantedRange(const s16 range) +{ + if (m_player && range != m_wanted_range) + m_player->setDirty(true); + + m_wanted_range = range; +} + +void PlayerSAO::setPlayerYawAndSend(const float yaw) +{ + setPlayerYaw(yaw); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +void PlayerSAO::setLookPitch(const float pitch) +{ + if (m_player && pitch != m_pitch) + m_player->setDirty(true); + + m_pitch = pitch; +} + +void PlayerSAO::setLookPitchAndSend(const float pitch) +{ + setLookPitch(pitch); + m_env->getGameDef()->SendMovePlayer(m_peer_id); +} + +u16 PlayerSAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + if (!toolcap) + return 0; + + FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); + + // No effect if PvP disabled or if immortal + if (isImmortal() || !g_settings->getBool("enable_pvp")) { + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + return 0; + } + } + + s32 old_hp = getHP(); + HitParams hitparams = getHitParams(m_armor_groups, toolcap, + time_from_last_punch); + + PlayerSAO *playersao = m_player->getPlayerSAO(); + + bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao, + puncher, time_from_last_punch, toolcap, dir, + hitparams.hp); + + if (!damage_handled) { + setHP((s32)getHP() - (s32)hitparams.hp, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + } else { // override client prediction + if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // create message and add to list + sendPunchCommand(); + } + } + + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ") punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; + + return hitparams.wear; +} + +void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) +{ + s32 oldhp = m_hp; + + hp = rangelim(hp, 0, m_prop.hp_max); + + if (oldhp != hp) { + s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); + if (hp_change == 0) + return; + + hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); + } + + if (hp < oldhp && isImmortal()) + return; + + m_hp = hp; + + // Update properties on death + if ((hp == 0) != (oldhp == 0)) + m_properties_sent = false; +} + +void PlayerSAO::setBreath(const u16 breath, bool send) +{ + if (m_player && breath != m_breath) + m_player->setDirty(true); + + m_breath = rangelim(breath, 0, m_prop.breath_max); + + if (send) + m_env->getGameDef()->SendPlayerBreath(this); +} + +Inventory *PlayerSAO::getInventory() const +{ + return m_player ? &m_player->inventory : nullptr; +} + +InventoryLocation PlayerSAO::getInventoryLocation() const +{ + InventoryLocation loc; + loc.setPlayer(m_player->getName()); + return loc; +} + +u16 PlayerSAO::getWieldIndex() const +{ + return m_player->getWieldIndex(); +} + +ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const +{ + return m_player->getWieldedItem(selected, hand); +} + +bool PlayerSAO::setWieldedItem(const ItemStack &item) +{ + InventoryList *mlist = m_player->inventory.getList(getWieldList()); + if (mlist) { + mlist->changeItem(m_player->getWieldIndex(), item); + return true; + } + return false; +} + +void PlayerSAO::disconnected() +{ + m_peer_id = PEER_ID_INEXISTENT; + m_pending_removal = true; +} + +void PlayerSAO::unlinkPlayerSessionAndSave() +{ + assert(m_player->getPlayerSAO() == this); + m_player->setPeerId(PEER_ID_INEXISTENT); + m_env->savePlayer(m_player); + m_player->setPlayerSAO(NULL); + m_env->removePlayer(m_player); +} + +std::string PlayerSAO::getPropertyPacket() +{ + m_prop.is_visible = (true); + return generateSetPropertiesCommand(m_prop); +} + +void PlayerSAO::setMaxSpeedOverride(const v3f &vel) +{ + if (m_max_speed_override_time == 0.0f) + m_max_speed_override = vel; + else + m_max_speed_override += vel; + if (m_player) { + float accel = MYMIN(m_player->movement_acceleration_default, + m_player->movement_acceleration_air); + m_max_speed_override_time = m_max_speed_override.getLength() / accel / BS; + } +} + +bool PlayerSAO::checkMovementCheat() +{ + if (isAttached() || m_is_singleplayer || + g_settings->getBool("disable_anticheat")) { + m_last_good_position = m_base_position; + return false; + } + + bool cheated = false; + /* + Check player movements + + NOTE: Actually the server should handle player physics like the + client does and compare player's position to what is calculated + on our side. This is required when eg. players fly due to an + explosion. Altough a node-based alternative might be possible + too, and much more lightweight. + */ + + float override_max_H, override_max_V; + if (m_max_speed_override_time > 0.0f) { + override_max_H = MYMAX(fabs(m_max_speed_override.X), fabs(m_max_speed_override.Z)); + override_max_V = fabs(m_max_speed_override.Y); + } else { + override_max_H = override_max_V = 0.0f; + } + + float player_max_walk = 0; // horizontal movement + float player_max_jump = 0; // vertical upwards movement + + if (m_privs.count("fast") != 0) + player_max_walk = m_player->movement_speed_fast; // Fast speed + else + player_max_walk = m_player->movement_speed_walk; // Normal speed + player_max_walk *= m_physics_override_speed; + player_max_walk = MYMAX(player_max_walk, override_max_H); + + player_max_jump = m_player->movement_speed_jump * m_physics_override_jump; + // FIXME: Bouncy nodes cause practically unbound increase in Y speed, + // until this can be verified correctly, tolerate higher jumping speeds + player_max_jump *= 2.0; + player_max_jump = MYMAX(player_max_jump, override_max_V); + + // Don't divide by zero! + if (player_max_walk < 0.0001f) + player_max_walk = 0.0001f; + if (player_max_jump < 0.0001f) + player_max_jump = 0.0001f; + + v3f diff = (m_base_position - m_last_good_position); + float d_vert = diff.Y; + diff.Y = 0; + float d_horiz = diff.getLength(); + float required_time = d_horiz / player_max_walk; + + // FIXME: Checking downwards movement is not easily possible currently, + // the server could calculate speed differences to examine the gravity + if (d_vert > 0) { + // In certain cases (water, ladders) walking speed is applied vertically + float s = MYMAX(player_max_jump, player_max_walk); + required_time = MYMAX(required_time, d_vert / s); + } + + if (m_move_pool.grab(required_time)) { + m_last_good_position = m_base_position; + } else { + const float LAG_POOL_MIN = 5.0; + float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; + lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); + if (m_time_from_last_teleport > lag_pool_max) { + actionstream << "Player " << m_player->getName() + << " moved too fast; resetting position" + << std::endl; + cheated = true; + } + setBasePosition(m_last_good_position); + } + return cheated; +} + +bool PlayerSAO::getCollisionBox(aabb3f *toset) const +{ + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + return true; +} + +bool PlayerSAO::getSelectionBox(aabb3f *toset) const +{ + if (!m_prop.is_visible || !m_prop.pointable) { + return false; + } + + toset->MinEdge = m_prop.selectionbox.MinEdge * BS; + toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; + + return true; +} + +float PlayerSAO::getZoomFOV() const +{ + return m_prop.zoom_fov; +} diff --git a/src/server/player_sao.h b/src/server/player_sao.h new file mode 100644 index 000000000..ce1cb1677 --- /dev/null +++ b/src/server/player_sao.h @@ -0,0 +1,325 @@ + +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +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 "constants.h" +#include "network/networkprotocol.h" +#include "unit_sao.h" +#include "util/numeric.h" + +/* + PlayerSAO needs some internals exposed. +*/ + +class LagPool +{ + float m_pool = 15.0f; + float m_max = 15.0f; +public: + LagPool() = default; + + void setMax(float new_max) + { + m_max = new_max; + if(m_pool > new_max) + m_pool = new_max; + } + + void add(float dtime) + { + m_pool -= dtime; + if(m_pool < 0) + m_pool = 0; + } + + void empty() + { + m_pool = m_max; + } + + bool grab(float dtime) + { + if(dtime <= 0) + return true; + if(m_pool + dtime > m_max) + return false; + m_pool += dtime; + return true; + } +}; + +class RemotePlayer; + +class PlayerSAO : public UnitSAO +{ +public: + PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, + bool is_singleplayer); + + ActiveObjectType getType() const + { return ACTIVEOBJECT_TYPE_PLAYER; } + ActiveObjectType getSendType() const + { return ACTIVEOBJECT_TYPE_GENERIC; } + std::string getDescription(); + + /* + Active object <-> environment interface + */ + + void addedToEnvironment(u32 dtime_s); + void removingFromEnvironment(); + bool isStaticAllowed() const { return false; } + std::string getClientInitializationData(u16 protocol_version); + void getStaticData(std::string *result) const; + void step(float dtime, bool send_recommended); + void setBasePosition(const v3f &position); + void setPos(const v3f &pos); + void moveTo(v3f pos, bool continuous); + void setPlayerYaw(const float yaw); + // Data should not be sent at player initialization + void setPlayerYawAndSend(const float yaw); + void setLookPitch(const float pitch); + // Data should not be sent at player initialization + void setLookPitchAndSend(const float pitch); + f32 getLookPitch() const { return m_pitch; } + f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; } + // Deprecated + f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; } + void setFov(const float pitch); + f32 getFov() const { return m_fov; } + void setWantedRange(const s16 range); + s16 getWantedRange() const { return m_wanted_range; } + + /* + Interaction interface + */ + + u16 punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch); + void rightClick(ServerActiveObject *clicker) {} + void setHP(s32 hp, const PlayerHPChangeReason &reason); + void setHPRaw(u16 hp) { m_hp = hp; } + s16 readDamage(); + u16 getBreath() const { return m_breath; } + void setBreath(const u16 breath, bool send = true); + + /* + Inventory interface + */ + Inventory *getInventory() const; + InventoryLocation getInventoryLocation() const; + void setInventoryModified() {} + std::string getWieldList() const { return "main"; } + u16 getWieldIndex() const; + ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const; + bool setWieldedItem(const ItemStack &item); + + /* + PlayerSAO-specific + */ + + void disconnected(); + + RemotePlayer *getPlayer() { return m_player; } + session_t getPeerID() const { return m_peer_id; } + + // Cheat prevention + + v3f getLastGoodPosition() const + { + return m_last_good_position; + } + float resetTimeFromLastPunch() + { + float r = m_time_from_last_punch; + m_time_from_last_punch = 0.0; + return r; + } + void noCheatDigStart(const v3s16 &p) + { + m_nocheat_dig_pos = p; + m_nocheat_dig_time = 0; + } + v3s16 getNoCheatDigPos() + { + return m_nocheat_dig_pos; + } + float getNoCheatDigTime() + { + return m_nocheat_dig_time; + } + void noCheatDigEnd() + { + m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + } + LagPool& getDigPool() + { + return m_dig_pool; + } + void setMaxSpeedOverride(const v3f &vel); + // Returns true if cheated + bool checkMovementCheat(); + + // Other + + void updatePrivileges(const std::set &privs, + bool is_singleplayer) + { + m_privs = privs; + m_is_singleplayer = is_singleplayer; + } + + bool getCollisionBox(aabb3f *toset) const; + bool getSelectionBox(aabb3f *toset) const; + bool collideWithObjects() const { return true; } + + void finalize(RemotePlayer *player, const std::set &privs); + + v3f getEyePosition() const { return m_base_position + getEyeOffset(); } + v3f getEyeOffset() const; + float getZoomFOV() const; + + inline Metadata &getMeta() { return m_meta; } + +private: + std::string getPropertyPacket(); + void unlinkPlayerSessionAndSave(); + std::string generateUpdatePhysicsOverrideCommand() const; + + RemotePlayer *m_player = nullptr; + session_t m_peer_id = 0; + + // Cheat prevention + LagPool m_dig_pool; + LagPool m_move_pool; + v3f m_last_good_position; + float m_time_from_last_teleport = 0.0f; + float m_time_from_last_punch = 0.0f; + v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767); + float m_nocheat_dig_time = 0.0f; + float m_max_speed_override_time = 0.0f; + v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f); + + // Timers + IntervalLimiter m_breathing_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_node_hurt_interval; + + bool m_position_not_sent = false; + + // Cached privileges for enforcement + std::set m_privs; + bool m_is_singleplayer; + + u16 m_breath = PLAYER_MAX_BREATH_DEFAULT; + f32 m_pitch = 0.0f; + f32 m_fov = 0.0f; + s16 m_wanted_range = 0.0f; + + Metadata m_meta; +public: + float m_physics_override_speed = 1.0f; + float m_physics_override_jump = 1.0f; + float m_physics_override_gravity = 1.0f; + bool m_physics_override_sneak = true; + bool m_physics_override_sneak_glitch = false; + bool m_physics_override_new_move = true; + bool m_physics_override_sent = false; +}; + + +struct PlayerHPChangeReason { + enum Type : u8 { + SET_HP, + PLAYER_PUNCH, + FALL, + NODE_DAMAGE, + DROWNING, + RESPAWN + }; + + Type type = SET_HP; + bool from_mod = false; + int lua_reference = -1; + + // For PLAYER_PUNCH + ServerActiveObject *object = nullptr; + // For NODE_DAMAGE + std::string node; + + inline bool hasLuaReference() const + { + return lua_reference >= 0; + } + + bool setTypeFromString(const std::string &typestr) + { + if (typestr == "set_hp") + type = SET_HP; + else if (typestr == "punch") + type = PLAYER_PUNCH; + else if (typestr == "fall") + type = FALL; + else if (typestr == "node_damage") + type = NODE_DAMAGE; + else if (typestr == "drown") + type = DROWNING; + else if (typestr == "respawn") + type = RESPAWN; + else + return false; + + return true; + } + + std::string getTypeAsString() const + { + switch (type) { + case PlayerHPChangeReason::SET_HP: + return "set_hp"; + case PlayerHPChangeReason::PLAYER_PUNCH: + return "punch"; + case PlayerHPChangeReason::FALL: + return "fall"; + case PlayerHPChangeReason::NODE_DAMAGE: + return "node_damage"; + case PlayerHPChangeReason::DROWNING: + return "drown"; + case PlayerHPChangeReason::RESPAWN: + return "respawn"; + default: + return "?"; + } + } + + PlayerHPChangeReason(Type type): + type(type) + {} + + PlayerHPChangeReason(Type type, ServerActiveObject *object): + type(type), object(object) + {} + + PlayerHPChangeReason(Type type, std::string node): + type(type), node(node) + {} +}; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index b30b7a76b..74b0508b8 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -22,10 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "serverenvironment.h" -/* - UnitSAO - */ - UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos) : ServerActiveObject(env, pos) { // Initialize something to armor groups diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 0ccbd772b..c2ab5c07d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "serverenvironment.h" -#include "content_sao.h" #include "settings.h" #include "log.h" #include "mapblock.h" @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif -#include +#include "server/player_sao.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" -- cgit v1.2.3 From 5146086a64d5eeb480948d612a008a2ec81455d4 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 11 Apr 2020 11:22:15 +0200 Subject: Drop content_sao.{cpp,h} Move LuaEntitySAO to a new dedicated file Drop TestSAO (useless object) Drop the old static startup initialized SAO factory, which was pretty useless. This factory was using a std::map for 2 elements, now just use a simple condition owned by ServerEnvironment, which will be lightweight, that will also drop a one time useful test on each LuaEntitySAO creation. This should reduce server load on massive SAO creation --- build/android/jni/Android.mk | 4 +- src/CMakeLists.txt | 1 - src/clientiface.cpp | 2 +- src/content_sao.cpp | 681 --------------------------------- src/content_sao.h | 101 ----- src/mapgen/mapgen.cpp | 1 - src/mapgen/mapgen_carpathian.cpp | 1 - src/mapgen/mapgen_flat.cpp | 1 - src/mapgen/mapgen_fractal.cpp | 1 - src/mapgen/mapgen_v5.cpp | 1 - src/mapgen/mapgen_v6.cpp | 1 - src/mapgen/mapgen_v7.cpp | 1 - src/script/cpp_api/s_base.cpp | 1 - src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 1 - src/script/lua_api/l_object.cpp | 2 +- src/server/CMakeLists.txt | 1 + src/server/luaentity_sao.cpp | 581 ++++++++++++++++++++++++++++ src/server/luaentity_sao.h | 93 +++++ src/server/player_sao.h | 83 ++-- src/server/serveractiveobject.cpp | 33 -- src/server/serveractiveobject.h | 10 - src/serverenvironment.cpp | 19 +- src/serverenvironment.h | 2 + src/staticobject.cpp | 2 +- src/unittest/test_player.cpp | 1 - util/travis/clang-format-whitelist.txt | 4 +- 27 files changed, 731 insertions(+), 900 deletions(-) delete mode 100644 src/content_sao.cpp delete mode 100644 src/content_sao.h create mode 100644 src/server/luaentity_sao.cpp create mode 100644 src/server/luaentity_sao.h (limited to 'src/script/lua_api') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 32a16c174..fdbfba9bc 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -161,7 +161,6 @@ LOCAL_SRC_FILES := \ jni/src/content/mods.cpp \ jni/src/content_nodemeta.cpp \ jni/src/content/packages.cpp \ - jni/src/content_sao.cpp \ jni/src/content/subgames.cpp \ jni/src/convert_json.cpp \ jni/src/craftdef.cpp \ @@ -258,8 +257,11 @@ LOCAL_SRC_FILES := \ jni/src/server.cpp \ jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ + jni/src/server/luaentity_sao.cpp \ jni/src/server/mods.cpp \ + jni/src/server/player_sao.cpp \ jni/src/server/serveractiveobject.cpp \ + jni/src/server/unit_sao.cpp \ jni/src/settings.cpp \ jni/src/staticobject.cpp \ jni/src/tileanimation.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index faa117d41..0ebbd628c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -375,7 +375,6 @@ set(common_SRCS collision.cpp content_mapnode.cpp content_nodemeta.cpp - content_sao.cpp convert_json.cpp craftdef.cpp debug.cpp diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 17237f73e..4f954342a 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "clientiface.h" -#include "content_sao.h" #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" @@ -28,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "map.h" #include "emerge.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" #include "log.h" #include "util/srp.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp deleted file mode 100644 index 7ec17aa82..000000000 --- a/src/content_sao.cpp +++ /dev/null @@ -1,681 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -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 "content_sao.h" -#include "util/serialize.h" -#include "collision.h" -#include "environment.h" -#include "tool.h" // For ToolCapabilities -#include "gamedef.h" -#include "nodedef.h" -#include "remoteplayer.h" -#include "server.h" -#include "scripting_server.h" -#include "server/player_sao.h" -#include "settings.h" -#include -#include - -std::map ServerActiveObject::m_types; - -/* - TestSAO -*/ - -class TestSAO : public ServerActiveObject -{ -public: - TestSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_timer1(0), - m_age(0) - { - ServerActiveObject::registerType(getType(), create); - } - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_TEST; } - - static ServerActiveObject* create(ServerEnvironment *env, v3f pos, - const std::string &data) - { - return new TestSAO(env, pos); - } - - void step(float dtime, bool send_recommended) - { - m_age += dtime; - if(m_age > 10) - { - m_pending_removal = true; - return; - } - - m_base_position.Y += dtime * BS * 2; - if(m_base_position.Y > 8*BS) - m_base_position.Y = 2*BS; - - if (!send_recommended) - return; - - m_timer1 -= dtime; - if(m_timer1 < 0.0) - { - m_timer1 += 0.125; - - std::string data; - - data += itos(0); // 0 = position - data += " "; - data += itos(m_base_position.X); - data += " "; - data += itos(m_base_position.Y); - data += " "; - data += itos(m_base_position.Z); - - ActiveObjectMessage aom(getId(), false, data); - m_messages_out.push(aom); - } - } - - bool getCollisionBox(aabb3f *toset) const { return false; } - - virtual bool getSelectionBox(aabb3f *toset) const { return false; } - - bool collideWithObjects() const { return false; } - -private: - float m_timer1; - float m_age; -}; - -// Prototype (registers item for deserialization) -TestSAO proto_TestSAO(NULL, v3f(0,0,0)); - -/* - LuaEntitySAO -*/ - -// Prototype (registers item for deserialization) -LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", ""); - -LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, - const std::string &name, const std::string &state): - UnitSAO(env, pos), - m_init_name(name), - m_init_state(state) -{ - // Only register type if no environment supplied - if(env == NULL){ - ServerActiveObject::registerType(getType(), create); - return; - } -} - -LuaEntitySAO::~LuaEntitySAO() -{ - if(m_registered){ - m_env->getScriptIface()->luaentity_Remove(m_id); - } - - for (u32 attached_particle_spawner : m_attached_particle_spawners) { - m_env->deleteParticleSpawner(attached_particle_spawner, false); - } -} - -void LuaEntitySAO::addedToEnvironment(u32 dtime_s) -{ - ServerActiveObject::addedToEnvironment(dtime_s); - - // Create entity from name - m_registered = m_env->getScriptIface()-> - luaentity_Add(m_id, m_init_name.c_str()); - - if(m_registered){ - // Get properties - m_env->getScriptIface()-> - luaentity_GetProperties(m_id, this, &m_prop); - // Initialize HP from properties - m_hp = m_prop.hp_max; - // Activate entity, supplying serialized state - m_env->getScriptIface()-> - luaentity_Activate(m_id, m_init_state, dtime_s); - } else { - m_prop.infotext = m_init_name; - } -} - -ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, - const std::string &data) -{ - std::string name; - std::string state; - u16 hp = 1; - v3f velocity; - v3f rotation; - - while (!data.empty()) { // breakable, run for one iteration - std::istringstream is(data, std::ios::binary); - // 'version' does not allow to incrementally extend the parameter list thus - // we need another variable to build on top of 'version=1'. Ugly hack but works™ - u8 version2 = 0; - u8 version = readU8(is); - - name = deSerializeString(is); - state = deSerializeLongString(is); - - if (version < 1) - break; - - hp = readU16(is); - velocity = readV3F1000(is); - // yaw must be yaw to be backwards-compatible - rotation.Y = readF1000(is); - - if (is.good()) // EOF for old formats - version2 = readU8(is); - - if (version2 < 1) // PROTOCOL_VERSION < 37 - break; - - // version2 >= 1 - rotation.X = readF1000(is); - rotation.Z = readF1000(is); - - // if (version2 < 2) - // break; - // - break; - } - // create object - infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\"" - << state << "\")" << std::endl; - LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state); - sao->m_hp = hp; - sao->m_velocity = velocity; - sao->m_rotation = rotation; - return sao; -} - -void LuaEntitySAO::step(float dtime, bool send_recommended) -{ - if(!m_properties_sent) - { - m_properties_sent = true; - std::string str = getPropertyPacket(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - - // If attached, check that our parent is still there. If it isn't, detach. - if(m_attachment_parent_id && !isAttached()) - { - m_attachment_parent_id = 0; - m_attachment_bone = ""; - m_attachment_position = v3f(0,0,0); - m_attachment_rotation = v3f(0,0,0); - sendPosition(false, true); - } - - m_last_sent_position_timer += dtime; - - // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally - // If the object gets detached this comes into effect automatically from the last known origin - if(isAttached()) - { - v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); - m_base_position = pos; - m_velocity = v3f(0,0,0); - m_acceleration = v3f(0,0,0); - } - else - { - if(m_prop.physical){ - aabb3f box = m_prop.collisionbox; - box.MinEdge *= BS; - box.MaxEdge *= BS; - collisionMoveResult moveresult; - f32 pos_max_d = BS*0.25; // Distance per iteration - v3f p_pos = m_base_position; - v3f p_velocity = m_velocity; - v3f p_acceleration = m_acceleration; - moveresult = collisionMoveSimple(m_env, m_env->getGameDef(), - pos_max_d, box, m_prop.stepheight, dtime, - &p_pos, &p_velocity, p_acceleration, - this, m_prop.collideWithObjects); - - // Apply results - m_base_position = p_pos; - m_velocity = p_velocity; - m_acceleration = p_acceleration; - } else { - m_base_position += dtime * m_velocity + 0.5 * dtime - * dtime * m_acceleration; - m_velocity += dtime * m_acceleration; - } - - if (m_prop.automatic_face_movement_dir && - (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) { - float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI - + m_prop.automatic_face_movement_dir_offset; - float max_rotation_per_sec = - m_prop.automatic_face_movement_max_rotation_per_sec; - - if (max_rotation_per_sec > 0) { - m_rotation.Y = wrapDegrees_0_360(m_rotation.Y); - wrappedApproachShortest(m_rotation.Y, target_yaw, - dtime * max_rotation_per_sec, 360.f); - } else { - // Negative values of max_rotation_per_sec mean disabled. - m_rotation.Y = target_yaw; - } - } - } - - if(m_registered){ - m_env->getScriptIface()->luaentity_Step(m_id, dtime); - } - - if (!send_recommended) - return; - - if(!isAttached()) - { - // TODO: force send when acceleration changes enough? - float minchange = 0.2*BS; - if(m_last_sent_position_timer > 1.0){ - minchange = 0.01*BS; - } else if(m_last_sent_position_timer > 0.2){ - minchange = 0.05*BS; - } - float move_d = m_base_position.getDistanceFrom(m_last_sent_position); - move_d += m_last_sent_move_precision; - float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); - if (move_d > minchange || vel_d > minchange || - std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f || - std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f || - std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) { - - sendPosition(true, false); - } - } - - if (!m_armor_groups_sent) { - m_armor_groups_sent = true; - // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); - } - - if (!m_animation_sent) { - m_animation_sent = true; - std::string str = generateUpdateAnimationCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - - if (!m_animation_speed_sent) { - m_animation_speed_sent = true; - std::string str = generateUpdateAnimationSpeedCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - - if (!m_bone_position_sent) { - m_bone_position_sent = true; - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ - std::string str = generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } - } - - if (!m_attachment_sent) { - m_attachment_sent = true; - std::string str = generateUpdateAttachmentCommand(); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); - } -} - -std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) -{ - std::ostringstream os(std::ios::binary); - - // PROTOCOL_VERSION >= 37 - writeU8(os, 1); // version - os << serializeString(""); // name - writeU8(os, 0); // is_player - writeU16(os, getId()); //id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, m_hp); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 - for (std::unordered_map>::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - int message_count = 4 + m_bone_position.size(); - for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); - (ii != m_attachment_child_ids.end()); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - // TODO after a protocol bump: only send the object initialization data - // to older clients (superfluous since this message exists) - msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); - } - } - - msg_os << serializeLongString(generateSetTextureModCommand()); - message_count++; - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); - - // return result - return os.str(); -} - -void LuaEntitySAO::getStaticData(std::string *result) const -{ - verbosestream<getScriptIface()-> - luaentity_GetStaticdata(m_id); - os<= 37 - - writeF1000(os, m_rotation.X); - writeF1000(os, m_rotation.Z); - - // - - *result = os.str(); -} - -u16 LuaEntitySAO::punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch) -{ - if (!m_registered) { - // Delete unknown LuaEntities when punched - m_pending_removal = true; - return 0; - } - - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - - s32 old_hp = getHP(); - ItemStack selected_item, hand_item; - ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); - - PunchDamageResult result = getPunchDamage( - m_armor_groups, - toolcap, - &tool_item, - time_from_last_punch); - - bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher, - time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0); - - if (!damage_handled) { - if (result.did_punch) { - setHP((s32)getHP() - result.damage, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); - - // create message and add to list - sendPunchCommand(); - } - } - - if (getHP() == 0 && !isGone()) { - clearParentAttachment(); - clearChildAttachments(); - m_env->getScriptIface()->luaentity_on_death(m_id, puncher); - m_pending_removal = true; - } - - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - - // TODO: give Lua control over wear - return result.wear; -} - -void LuaEntitySAO::rightClick(ServerActiveObject *clicker) -{ - if (!m_registered) - return; - - m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); -} - -void LuaEntitySAO::setPos(const v3f &pos) -{ - if(isAttached()) - return; - m_base_position = pos; - sendPosition(false, true); -} - -void LuaEntitySAO::moveTo(v3f pos, bool continuous) -{ - if(isAttached()) - return; - m_base_position = pos; - if(!continuous) - sendPosition(true, true); -} - -float LuaEntitySAO::getMinimumSavedMovement() -{ - return 0.1 * BS; -} - -std::string LuaEntitySAO::getDescription() -{ - std::ostringstream oss; - oss << "LuaEntitySAO \"" << m_init_name << "\" "; - auto pos = floatToInt(m_base_position, BS); - oss << "at " << PP(pos); - return oss.str(); -} - -void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) -{ - m_hp = rangelim(hp, 0, U16_MAX); -} - -u16 LuaEntitySAO::getHP() const -{ - return m_hp; -} - -void LuaEntitySAO::setVelocity(v3f velocity) -{ - m_velocity = velocity; -} - -v3f LuaEntitySAO::getVelocity() -{ - return m_velocity; -} - -void LuaEntitySAO::setAcceleration(v3f acceleration) -{ - m_acceleration = acceleration; -} - -v3f LuaEntitySAO::getAcceleration() -{ - return m_acceleration; -} - -void LuaEntitySAO::setTextureMod(const std::string &mod) -{ - m_current_texture_modifier = mod; - // create message and add to list - m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); -} - -std::string LuaEntitySAO::getTextureMod() const -{ - return m_current_texture_modifier; -} - - -std::string LuaEntitySAO::generateSetTextureModCommand() const -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_TEXTURE_MOD); - // parameters - os << serializeString(m_current_texture_modifier); - return os.str(); -} - -std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, - f32 framelength, bool select_horiz_by_yawpitch) -{ - std::ostringstream os(std::ios::binary); - // command - writeU8(os, AO_CMD_SET_SPRITE); - // parameters - writeV2S16(os, p); - writeU16(os, num_frames); - writeF32(os, framelength); - writeU8(os, select_horiz_by_yawpitch); - return os.str(); -} - -void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, - bool select_horiz_by_yawpitch) -{ - std::string str = generateSetSpriteCommand( - p, - num_frames, - framelength, - select_horiz_by_yawpitch - ); - // create message and add to list - m_messages_out.emplace(getId(), true, str); -} - -std::string LuaEntitySAO::getName() -{ - return m_init_name; -} - -std::string LuaEntitySAO::getPropertyPacket() -{ - return generateSetPropertiesCommand(m_prop); -} - -void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) -{ - // If the object is attached client-side, don't waste bandwidth sending its position to clients - if(isAttached()) - return; - - m_last_sent_move_precision = m_base_position.getDistanceFrom( - m_last_sent_position); - m_last_sent_position_timer = 0; - m_last_sent_position = m_base_position; - m_last_sent_velocity = m_velocity; - //m_last_sent_acceleration = m_acceleration; - m_last_sent_rotation = m_rotation; - - float update_interval = m_env->getSendRecommendedInterval(); - - std::string str = generateUpdatePositionCommand( - m_base_position, - m_velocity, - m_acceleration, - m_rotation, - do_interpolate, - is_movement_end, - update_interval - ); - // create message and add to list - m_messages_out.emplace(getId(), false, str); -} - -bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const -{ - if (m_prop.physical) - { - //update collision box - toset->MinEdge = m_prop.collisionbox.MinEdge * BS; - toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; - - toset->MinEdge += m_base_position; - toset->MaxEdge += m_base_position; - - return true; - } - - return false; -} - -bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const -{ - if (!m_prop.is_visible || !m_prop.pointable) { - return false; - } - - toset->MinEdge = m_prop.selectionbox.MinEdge * BS; - toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; - - return true; -} - -bool LuaEntitySAO::collideWithObjects() const -{ - return m_prop.collideWithObjects; -} diff --git a/src/content_sao.h b/src/content_sao.h deleted file mode 100644 index 5387fd108..000000000 --- a/src/content_sao.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola - -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. -*/ - -#pragma once - -#include "network/networkprotocol.h" -#include "util/numeric.h" -#include "server/unit_sao.h" -#include "itemgroup.h" -#include "constants.h" - -/* - LuaEntitySAO needs some internals exposed. -*/ - -class LuaEntitySAO : public UnitSAO -{ -public: - LuaEntitySAO(ServerEnvironment *env, v3f pos, - const std::string &name, const std::string &state); - ~LuaEntitySAO(); - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_LUAENTITY; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } - virtual void addedToEnvironment(u32 dtime_s); - static ServerActiveObject* create(ServerEnvironment *env, v3f pos, - const std::string &data); - void step(float dtime, bool send_recommended); - std::string getClientInitializationData(u16 protocol_version); - bool isStaticAllowed() const - { return m_prop.static_save; } - void getStaticData(std::string *result) const; - u16 punch(v3f dir, - const ToolCapabilities *toolcap = nullptr, - ServerActiveObject *puncher = nullptr, - float time_from_last_punch = 1000000.0f); - void rightClick(ServerActiveObject *clicker); - void setPos(const v3f &pos); - void moveTo(v3f pos, bool continuous); - float getMinimumSavedMovement(); - std::string getDescription(); - void setHP(s32 hp, const PlayerHPChangeReason &reason); - u16 getHP() const; - - /* LuaEntitySAO-specific */ - void setVelocity(v3f velocity); - void addVelocity(v3f velocity) - { - m_velocity += velocity; - } - v3f getVelocity(); - void setAcceleration(v3f acceleration); - v3f getAcceleration(); - - void setTextureMod(const std::string &mod); - std::string getTextureMod() const; - void setSprite(v2s16 p, int num_frames, float framelength, - bool select_horiz_by_yawpitch); - std::string getName(); - bool getCollisionBox(aabb3f *toset) const; - bool getSelectionBox(aabb3f *toset) const; - bool collideWithObjects() const; -private: - std::string getPropertyPacket(); - void sendPosition(bool do_interpolate, bool is_movement_end); - std::string generateSetTextureModCommand() const; - static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, f32 framelength, - bool select_horiz_by_yawpitch); - - std::string m_init_name; - std::string m_init_state; - bool m_registered = false; - - v3f m_velocity; - v3f m_acceleration; - - v3f m_last_sent_position; - v3f m_last_sent_velocity; - v3f m_last_sent_rotation; - float m_last_sent_position_timer = 0.0f; - float m_last_sent_move_precision = 0.0f; - std::string m_current_texture_modifier = ""; -}; - diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index 6c426ba41..0094608ec 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "emerge.h" #include "voxelalgorithms.h" diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp index 0dc1d33be..bd7ae5e7c 100644 --- a/src/mapgen/mapgen_carpathian.cpp +++ b/src/mapgen/mapgen_carpathian.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp index 879435948..272964b51 100644 --- a/src/mapgen/mapgen_flat.cpp +++ b/src/mapgen/mapgen_flat.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp index 96febb4f4..97f77d947 100644 --- a/src/mapgen/mapgen_fractal.cpp +++ b/src/mapgen/mapgen_fractal.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp index 447fe8c50..3bd7615c4 100644 --- a/src/mapgen/mapgen_v5.cpp +++ b/src/mapgen/mapgen_v5.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index f473f725d..da9ae1428 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 325c4957a..82556cc4f 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "mapnode.h" #include "map.h" -#include "content_sao.h" #include "nodedef.h" #include "voxelalgorithms.h" //#include "profiler.h" // For TimeTaker diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 150baf77e..df07206d7 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -43,7 +43,6 @@ extern "C" { #include #include #include "script/common/c_content.h" -#include "content_sao.h" #include diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 438669feb..40e50e64a 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -33,12 +33,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "daynightratio.h" #include "util/pointedthing.h" -#include "content_sao.h" #include "mapgen/treegen.h" #include "emerge.h" #include "pathfinder.h" #include "face_position_cache.h" #include "remoteplayer.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" #ifndef SERVER #include "client/client.h" diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index ff77cba32..9f12d3ac7 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "nodedef.h" #include "server.h" -#include "content_sao.h" #include "inventory.h" #include "log.h" diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 95c96235e..fa34260bf 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -27,11 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" #include "tool.h" -#include "content_sao.h" #include "remoteplayer.h" #include "server.h" #include "hud.h" #include "scripting_server.h" +#include "server/luaentity_sao.h" #include "server/player_sao.h" /* diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 26eaed5ac..4d94504f6 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,5 +1,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/luaentity_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp new file mode 100644 index 000000000..125939241 --- /dev/null +++ b/src/server/luaentity_sao.cpp @@ -0,0 +1,581 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +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 "luaentity_sao.h" +#include "collision.h" +#include "constants.h" +#include "player_sao.h" +#include "scripting_server.h" +#include "server.h" +#include "serverenvironment.h" + +LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &data) + : UnitSAO(env, pos) +{ + std::string name; + std::string state; + u16 hp = 1; + v3f velocity; + v3f rotation; + + while (!data.empty()) { // breakable, run for one iteration + std::istringstream is(data, std::ios::binary); + // 'version' does not allow to incrementally extend the parameter list thus + // we need another variable to build on top of 'version=1'. Ugly hack but works™ + u8 version2 = 0; + u8 version = readU8(is); + + name = deSerializeString(is); + state = deSerializeLongString(is); + + if (version < 1) + break; + + hp = readU16(is); + velocity = readV3F1000(is); + // yaw must be yaw to be backwards-compatible + rotation.Y = readF1000(is); + + if (is.good()) // EOF for old formats + version2 = readU8(is); + + if (version2 < 1) // PROTOCOL_VERSION < 37 + break; + + // version2 >= 1 + rotation.X = readF1000(is); + rotation.Z = readF1000(is); + + // if (version2 < 2) + // break; + // + break; + } + // create object + infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\"" + << state << "\")" << std::endl; + + m_init_name = name; + m_init_state = state; + m_hp = hp; + m_velocity = velocity; + m_rotation = rotation; +} + +LuaEntitySAO::~LuaEntitySAO() +{ + if(m_registered){ + m_env->getScriptIface()->luaentity_Remove(m_id); + } + + for (u32 attached_particle_spawner : m_attached_particle_spawners) { + m_env->deleteParticleSpawner(attached_particle_spawner, false); + } +} + +void LuaEntitySAO::addedToEnvironment(u32 dtime_s) +{ + ServerActiveObject::addedToEnvironment(dtime_s); + + // Create entity from name + m_registered = m_env->getScriptIface()-> + luaentity_Add(m_id, m_init_name.c_str()); + + if(m_registered){ + // Get properties + m_env->getScriptIface()-> + luaentity_GetProperties(m_id, this, &m_prop); + // Initialize HP from properties + m_hp = m_prop.hp_max; + // Activate entity, supplying serialized state + m_env->getScriptIface()-> + luaentity_Activate(m_id, m_init_state, dtime_s); + } else { + m_prop.infotext = m_init_name; + } +} + +void LuaEntitySAO::step(float dtime, bool send_recommended) +{ + if(!m_properties_sent) + { + m_properties_sent = true; + std::string str = getPropertyPacket(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + + // If attached, check that our parent is still there. If it isn't, detach. + if(m_attachment_parent_id && !isAttached()) + { + m_attachment_parent_id = 0; + m_attachment_bone = ""; + m_attachment_position = v3f(0,0,0); + m_attachment_rotation = v3f(0,0,0); + sendPosition(false, true); + } + + m_last_sent_position_timer += dtime; + + // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally + // If the object gets detached this comes into effect automatically from the last known origin + if(isAttached()) + { + v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + m_base_position = pos; + m_velocity = v3f(0,0,0); + m_acceleration = v3f(0,0,0); + } + else + { + if(m_prop.physical){ + aabb3f box = m_prop.collisionbox; + box.MinEdge *= BS; + box.MaxEdge *= BS; + collisionMoveResult moveresult; + f32 pos_max_d = BS*0.25; // Distance per iteration + v3f p_pos = m_base_position; + v3f p_velocity = m_velocity; + v3f p_acceleration = m_acceleration; + moveresult = collisionMoveSimple(m_env, m_env->getGameDef(), + pos_max_d, box, m_prop.stepheight, dtime, + &p_pos, &p_velocity, p_acceleration, + this, m_prop.collideWithObjects); + + // Apply results + m_base_position = p_pos; + m_velocity = p_velocity; + m_acceleration = p_acceleration; + } else { + m_base_position += dtime * m_velocity + 0.5 * dtime + * dtime * m_acceleration; + m_velocity += dtime * m_acceleration; + } + + if (m_prop.automatic_face_movement_dir && + (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) { + float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI + + m_prop.automatic_face_movement_dir_offset; + float max_rotation_per_sec = + m_prop.automatic_face_movement_max_rotation_per_sec; + + if (max_rotation_per_sec > 0) { + m_rotation.Y = wrapDegrees_0_360(m_rotation.Y); + wrappedApproachShortest(m_rotation.Y, target_yaw, + dtime * max_rotation_per_sec, 360.f); + } else { + // Negative values of max_rotation_per_sec mean disabled. + m_rotation.Y = target_yaw; + } + } + } + + if(m_registered){ + m_env->getScriptIface()->luaentity_Step(m_id, dtime); + } + + if (!send_recommended) + return; + + if(!isAttached()) + { + // TODO: force send when acceleration changes enough? + float minchange = 0.2*BS; + if(m_last_sent_position_timer > 1.0){ + minchange = 0.01*BS; + } else if(m_last_sent_position_timer > 0.2){ + minchange = 0.05*BS; + } + float move_d = m_base_position.getDistanceFrom(m_last_sent_position); + move_d += m_last_sent_move_precision; + float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity); + if (move_d > minchange || vel_d > minchange || + std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f || + std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f || + std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) { + + sendPosition(true, false); + } + } + + if (!m_armor_groups_sent) { + m_armor_groups_sent = true; + // create message and add to list + m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand()); + } + + if (!m_animation_sent) { + m_animation_sent = true; + std::string str = generateUpdateAnimationCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + + if (!m_animation_speed_sent) { + m_animation_speed_sent = true; + std::string str = generateUpdateAnimationSpeedCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + + if (!m_bone_position_sent) { + m_bone_position_sent = true; + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){ + std::string str = generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } + } + + if (!m_attachment_sent) { + m_attachment_sent = true; + std::string str = generateUpdateAttachmentCommand(); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } +} + +std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) +{ + std::ostringstream os(std::ios::binary); + + // PROTOCOL_VERSION >= 37 + writeU8(os, 1); // version + os << serializeString(""); // name + writeU8(os, 0); // is_player + writeU16(os, getId()); //id + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, m_hp); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + for (std::unordered_map>::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size + } + msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + int message_count = 4 + m_bone_position.size(); + for (std::unordered_set::const_iterator ii = m_attachment_child_ids.begin(); + (ii != m_attachment_child_ids.end()); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + // TODO after a protocol bump: only send the object initialization data + // to older clients (superfluous since this message exists) + msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version)); + } + } + + msg_os << serializeLongString(generateSetTextureModCommand()); + message_count++; + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + + // return result + return os.str(); +} + +void LuaEntitySAO::getStaticData(std::string *result) const +{ + verbosestream<getScriptIface()-> + luaentity_GetStaticdata(m_id); + os<= 37 + + writeF1000(os, m_rotation.X); + writeF1000(os, m_rotation.Z); + + // + + *result = os.str(); +} + +u16 LuaEntitySAO::punch(v3f dir, + const ToolCapabilities *toolcap, + ServerActiveObject *puncher, + float time_from_last_punch) +{ + if (!m_registered) { + // Delete unknown LuaEntities when punched + m_pending_removal = true; + return 0; + } + + FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); + + s32 old_hp = getHP(); + ItemStack selected_item, hand_item; + ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); + + PunchDamageResult result = getPunchDamage( + m_armor_groups, + toolcap, + &tool_item, + time_from_last_punch); + + bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher, + time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0); + + if (!damage_handled) { + if (result.did_punch) { + setHP((s32)getHP() - result.damage, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); + + // create message and add to list + sendPunchCommand(); + } + } + + if (getHP() == 0 && !isGone()) { + clearParentAttachment(); + clearChildAttachments(); + m_env->getScriptIface()->luaentity_on_death(m_id, puncher); + m_pending_removal = true; + } + + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ") punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; + + // TODO: give Lua control over wear + return result.wear; +} + +void LuaEntitySAO::rightClick(ServerActiveObject *clicker) +{ + if (!m_registered) + return; + + m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker); +} + +void LuaEntitySAO::setPos(const v3f &pos) +{ + if(isAttached()) + return; + m_base_position = pos; + sendPosition(false, true); +} + +void LuaEntitySAO::moveTo(v3f pos, bool continuous) +{ + if(isAttached()) + return; + m_base_position = pos; + if(!continuous) + sendPosition(true, true); +} + +float LuaEntitySAO::getMinimumSavedMovement() +{ + return 0.1 * BS; +} + +std::string LuaEntitySAO::getDescription() +{ + std::ostringstream oss; + oss << "LuaEntitySAO \"" << m_init_name << "\" "; + auto pos = floatToInt(m_base_position, BS); + oss << "at " << PP(pos); + return oss.str(); +} + +void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason) +{ + m_hp = rangelim(hp, 0, U16_MAX); +} + +u16 LuaEntitySAO::getHP() const +{ + return m_hp; +} + +void LuaEntitySAO::setVelocity(v3f velocity) +{ + m_velocity = velocity; +} + +v3f LuaEntitySAO::getVelocity() +{ + return m_velocity; +} + +void LuaEntitySAO::setAcceleration(v3f acceleration) +{ + m_acceleration = acceleration; +} + +v3f LuaEntitySAO::getAcceleration() +{ + return m_acceleration; +} + +void LuaEntitySAO::setTextureMod(const std::string &mod) +{ + m_current_texture_modifier = mod; + // create message and add to list + m_messages_out.emplace(getId(), true, generateSetTextureModCommand()); +} + +std::string LuaEntitySAO::getTextureMod() const +{ + return m_current_texture_modifier; +} + + +std::string LuaEntitySAO::generateSetTextureModCommand() const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_TEXTURE_MOD); + // parameters + os << serializeString(m_current_texture_modifier); + return os.str(); +} + +std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch) +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_SET_SPRITE); + // parameters + writeV2S16(os, p); + writeU16(os, num_frames); + writeF32(os, framelength); + writeU8(os, select_horiz_by_yawpitch); + return os.str(); +} + +void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, + bool select_horiz_by_yawpitch) +{ + std::string str = generateSetSpriteCommand( + p, + num_frames, + framelength, + select_horiz_by_yawpitch + ); + // create message and add to list + m_messages_out.emplace(getId(), true, str); +} + +std::string LuaEntitySAO::getName() +{ + return m_init_name; +} + +std::string LuaEntitySAO::getPropertyPacket() +{ + return generateSetPropertiesCommand(m_prop); +} + +void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) +{ + // If the object is attached client-side, don't waste bandwidth sending its position to clients + if(isAttached()) + return; + + m_last_sent_move_precision = m_base_position.getDistanceFrom( + m_last_sent_position); + m_last_sent_position_timer = 0; + m_last_sent_position = m_base_position; + m_last_sent_velocity = m_velocity; + //m_last_sent_acceleration = m_acceleration; + m_last_sent_rotation = m_rotation; + + float update_interval = m_env->getSendRecommendedInterval(); + + std::string str = generateUpdatePositionCommand( + m_base_position, + m_velocity, + m_acceleration, + m_rotation, + do_interpolate, + is_movement_end, + update_interval + ); + // create message and add to list + m_messages_out.emplace(getId(), false, str); +} + +bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const +{ + if (m_prop.physical) + { + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + + return true; + } + + return false; +} + +bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const +{ + if (!m_prop.is_visible || !m_prop.pointable) { + return false; + } + + toset->MinEdge = m_prop.selectionbox.MinEdge * BS; + toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS; + + return true; +} + +bool LuaEntitySAO::collideWithObjects() const +{ + return m_prop.collideWithObjects; +} diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h new file mode 100644 index 000000000..2520c8f5d --- /dev/null +++ b/src/server/luaentity_sao.h @@ -0,0 +1,93 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2013-2020 Minetest core developers & community + +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. +*/ + +#pragma once + +#include "unit_sao.h" + +class LuaEntitySAO : public UnitSAO +{ +public: + LuaEntitySAO() = delete; + // Used by the environment to load SAO + LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &data); + // Used by the Lua API + LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &name, + const std::string &state) : + UnitSAO(env, pos), + m_init_name(name), m_init_state(state) + { + } + ~LuaEntitySAO(); + ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_LUAENTITY; } + ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; } + virtual void addedToEnvironment(u32 dtime_s); + void step(float dtime, bool send_recommended); + std::string getClientInitializationData(u16 protocol_version); + bool isStaticAllowed() const { return m_prop.static_save; } + void getStaticData(std::string *result) const; + u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr, + ServerActiveObject *puncher = nullptr, + float time_from_last_punch = 1000000.0f); + void rightClick(ServerActiveObject *clicker); + void setPos(const v3f &pos); + void moveTo(v3f pos, bool continuous); + float getMinimumSavedMovement(); + std::string getDescription(); + void setHP(s32 hp, const PlayerHPChangeReason &reason); + u16 getHP() const; + + /* LuaEntitySAO-specific */ + void setVelocity(v3f velocity); + void addVelocity(v3f velocity) { m_velocity += velocity; } + v3f getVelocity(); + void setAcceleration(v3f acceleration); + v3f getAcceleration(); + + void setTextureMod(const std::string &mod); + std::string getTextureMod() const; + void setSprite(v2s16 p, int num_frames, float framelength, + bool select_horiz_by_yawpitch); + std::string getName(); + bool getCollisionBox(aabb3f *toset) const; + bool getSelectionBox(aabb3f *toset) const; + bool collideWithObjects() const; + +private: + std::string getPropertyPacket(); + void sendPosition(bool do_interpolate, bool is_movement_end); + std::string generateSetTextureModCommand() const; + static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, + f32 framelength, bool select_horiz_by_yawpitch); + + std::string m_init_name; + std::string m_init_state; + bool m_registered = false; + + v3f m_velocity; + v3f m_acceleration; + + v3f m_last_sent_position; + v3f m_last_sent_velocity; + v3f m_last_sent_rotation; + float m_last_sent_position_timer = 0.0f; + float m_last_sent_move_precision = 0.0f; + std::string m_current_texture_modifier = ""; +}; diff --git a/src/server/player_sao.h b/src/server/player_sao.h index ce1cb1677..8571bd4f9 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -1,4 +1,3 @@ - /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola @@ -19,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "constants.h" #include "network/networkprotocol.h" #include "unit_sao.h" @@ -32,33 +33,31 @@ class LagPool { float m_pool = 15.0f; float m_max = 15.0f; + public: LagPool() = default; void setMax(float new_max) { m_max = new_max; - if(m_pool > new_max) + if (m_pool > new_max) m_pool = new_max; } void add(float dtime) { m_pool -= dtime; - if(m_pool < 0) + if (m_pool < 0) m_pool = 0; } - void empty() - { - m_pool = m_max; - } + void empty() { m_pool = m_max; } bool grab(float dtime) { - if(dtime <= 0) + if (dtime <= 0) return true; - if(m_pool + dtime > m_max) + if (m_pool + dtime > m_max) return false; m_pool += dtime; return true; @@ -73,10 +72,8 @@ public: PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_, bool is_singleplayer); - ActiveObjectType getType() const - { return ACTIVEOBJECT_TYPE_PLAYER; } - ActiveObjectType getSendType() const - { return ACTIVEOBJECT_TYPE_GENERIC; } + ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_PLAYER; } + ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; } std::string getDescription(); /* @@ -111,10 +108,8 @@ public: Interaction interface */ - u16 punch(v3f dir, - const ToolCapabilities *toolcap, - ServerActiveObject *puncher, - float time_from_last_punch); + u16 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher, + float time_from_last_punch); void rightClick(ServerActiveObject *clicker) {} void setHP(s32 hp, const PlayerHPChangeReason &reason); void setHPRaw(u16 hp) { m_hp = hp; } @@ -144,10 +139,7 @@ public: // Cheat prevention - v3f getLastGoodPosition() const - { - return m_last_good_position; - } + v3f getLastGoodPosition() const { return m_last_good_position; } float resetTimeFromLastPunch() { float r = m_time_from_last_punch; @@ -159,30 +151,17 @@ public: m_nocheat_dig_pos = p; m_nocheat_dig_time = 0; } - v3s16 getNoCheatDigPos() - { - return m_nocheat_dig_pos; - } - float getNoCheatDigTime() - { - return m_nocheat_dig_time; - } - void noCheatDigEnd() - { - m_nocheat_dig_pos = v3s16(32767, 32767, 32767); - } - LagPool& getDigPool() - { - return m_dig_pool; - } + v3s16 getNoCheatDigPos() { return m_nocheat_dig_pos; } + float getNoCheatDigTime() { return m_nocheat_dig_time; } + void noCheatDigEnd() { m_nocheat_dig_pos = v3s16(32767, 32767, 32767); } + LagPool &getDigPool() { return m_dig_pool; } void setMaxSpeedOverride(const v3f &vel); // Returns true if cheated bool checkMovementCheat(); // Other - void updatePrivileges(const std::set &privs, - bool is_singleplayer) + void updatePrivileges(const std::set &privs, bool is_singleplayer) { m_privs = privs; m_is_singleplayer = is_singleplayer; @@ -236,6 +215,7 @@ private: s16 m_wanted_range = 0.0f; Metadata m_meta; + public: float m_physics_override_speed = 1.0f; float m_physics_override_jump = 1.0f; @@ -246,9 +226,10 @@ public: bool m_physics_override_sent = false; }; - -struct PlayerHPChangeReason { - enum Type : u8 { +struct PlayerHPChangeReason +{ + enum Type : u8 + { SET_HP, PLAYER_PUNCH, FALL, @@ -266,10 +247,7 @@ struct PlayerHPChangeReason { // For NODE_DAMAGE std::string node; - inline bool hasLuaReference() const - { - return lua_reference >= 0; - } + inline bool hasLuaReference() const { return lua_reference >= 0; } bool setTypeFromString(const std::string &typestr) { @@ -311,15 +289,12 @@ struct PlayerHPChangeReason { } } - PlayerHPChangeReason(Type type): - type(type) - {} + PlayerHPChangeReason(Type type) : type(type) {} - PlayerHPChangeReason(Type type, ServerActiveObject *object): + PlayerHPChangeReason(Type type, ServerActiveObject *object) : type(type), object(object) - {} + { + } - PlayerHPChangeReason(Type type, std::string node): - type(type), node(node) - {} + PlayerHPChangeReason(Type type, std::string node) : type(type), node(node) {} }; diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index 3aa78c7d5..8345ebd47 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -30,39 +30,6 @@ ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): { } -ServerActiveObject* ServerActiveObject::create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data) -{ - // Find factory function - std::map::iterator n; - n = m_types.find(type); - if(n == m_types.end()) { - // These are 0.3 entity types, return without error. - if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) { - return NULL; - } - - // If factory is not found, just return. - warningstream<<"ServerActiveObject: No factory for type=" - <second; - ServerActiveObject *object = (*f)(env, pos, data); - return object; -} - -void ServerActiveObject::registerType(u16 type, Factory f) -{ - std::map::iterator n; - n = m_types.find(type); - if(n != m_types.end()) - return; - m_types[type] = f; -} - float ServerActiveObject::getMinimumSavedMovement() { return 2.0*BS; diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index 2e013a6b6..927009aef 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -244,12 +244,6 @@ protected: virtual void onAttach(int parent_id) {} virtual void onDetach(int parent_id) {} - // Used for creating objects based on type - typedef ServerActiveObject* (*Factory) - (ServerEnvironment *env, v3f pos, - const std::string &data); - static void registerType(u16 type, Factory f); - ServerEnvironment *m_env; v3f m_base_position; std::unordered_set m_attached_particle_spawners; @@ -258,8 +252,4 @@ protected: Queue of messages to be sent to the client */ std::queue m_messages_out; - -private: - // Used for creating objects based on type - static std::map m_types; }; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c2ab5c07d..32d10f8c0 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif +#include "server/luaentity_sao.h" #include "server/player_sao.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" @@ -1778,6 +1779,18 @@ static void print_hexdump(std::ostream &o, const std::string &data) } } +ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos, + const std::string &data) +{ + switch (type) { + case ACTIVEOBJECT_TYPE_LUAENTITY: + return new LuaEntitySAO(this, pos, data); + default: + warningstream << "ServerActiveObject: No factory for type=" << type << std::endl; + } + return nullptr; +} + /* Convert stored objects from blocks near the players to active. */ @@ -1811,10 +1824,10 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) std::vector new_stored; for (const StaticObject &s_obj : block->m_static_objects.m_stored) { // Create an active object from the data - ServerActiveObject *obj = ServerActiveObject::create - ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); + ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos, + s_obj.data); // If couldn't create object, store static data back. - if(obj == NULL) { + if (!obj) { errorstream<<"ServerEnvironment::activateObjects(): " <<"failed to create active object from static object " <<"in block "< m_particle_spawners; std::unordered_map m_particle_spawner_attachments; + + ServerActiveObject* createSAO(ActiveObjectType type, v3f pos, const std::string &data); }; diff --git a/src/staticobject.cpp b/src/staticobject.cpp index bebca12ec..5ccb7baf5 100644 --- a/src/staticobject.cpp +++ b/src/staticobject.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "staticobject.h" #include "util/serialize.h" -#include "content_sao.h" +#include "server/serveractiveobject.h" StaticObject::StaticObject(const ServerActiveObject *s_obj, const v3f &pos_): type(s_obj->getType()), diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp index e2b1cd855..6990b4016 100644 --- a/src/unittest/test_player.cpp +++ b/src/unittest/test_player.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "remoteplayer.h" -#include "content_sao.h" #include "server.h" class TestPlayer : public TestBase diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 816ec2c59..bb97da7b5 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -118,8 +118,6 @@ src/content_mapblock.h src/content_mapnode.cpp src/content_nodemeta.cpp src/content_nodemeta.h -src/content_sao.cpp -src/content_sao.h src/convert_json.cpp src/convert_json.h src/craftdef.cpp @@ -408,6 +406,8 @@ src/serverenvironment.h src/server.h src/serverlist.cpp src/serverlist.h +src/server/luaentity_sao.cpp +src/server/player_sao.cpp src/server/serveractiveobject.cpp src/server/serveractiveobject.h src/settings.cpp -- cgit v1.2.3 From 5cc06e4748a82acb36310fee89e72f30b2b35a36 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sat, 11 Apr 2020 19:59:43 +0200 Subject: Reduce ServerEnvironment propagation (#9642) ServerEnvironment is a huge class with many accessors. In various places it's not needed Remove it to reduce the ServerEnvironment view. Idea here is to reduce size of some of our objects to transport lightweight managers and permit easier testing Pathfinder is now tied to a generic map, not a ServerMap, it can be ported to client --- src/mapgen/treegen.cpp | 4 +- src/mapgen/treegen.h | 5 +- src/pathfinder.cpp | 102 ++++++++++++++++--------------------- src/pathfinder.h | 17 ++++--- src/script/lua_api/l_env.cpp | 7 +-- src/script/lua_api/l_nodetimer.cpp | 44 +++------------- src/script/lua_api/l_nodetimer.h | 10 ++-- 7 files changed, 71 insertions(+), 118 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index 89bdef73b..d538e15b4 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "map.h" #include "mapblock.h" -#include "serverenvironment.h" #include "nodedef.h" #include "treegen.h" #include "voxelalgorithms.h" @@ -120,10 +119,9 @@ void make_tree(MMVManip &vmanip, v3s16 p0, bool is_apple_tree, // L-System tree LUA spawner -treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, +treegen::error spawn_ltree(ServerMap *map, v3s16 p0, const NodeDefManager *ndef, const TreeDef &tree_definition) { - ServerMap *map = &env->getServerMap(); std::map modified_blocks; MMVManip vmanip(map); v3s16 tree_blockp = getNodeBlockPos(p0); diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h index 5ab79f428..447baabb3 100644 --- a/src/mapgen/treegen.h +++ b/src/mapgen/treegen.h @@ -26,8 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class MMVManip; class NodeDefManager; -class ServerEnvironment; - +class ServerMap; namespace treegen { @@ -73,7 +72,7 @@ namespace treegen { treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef, TreeDef tree_definition); // Spawn L-systems tree from LUA - treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, + treegen::error spawn_ltree (ServerMap *map, v3s16 p0, const NodeDefManager *ndef, const TreeDef &tree_definition); // L-System tree gen helper functions diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 8195bd643..3f0b98c10 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -23,8 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., /******************************************************************************/ #include "pathfinder.h" -#include "serverenvironment.h" -#include "server.h" +#include "map.h" #include "nodedef.h" //#define PATHFINDER_DEBUG @@ -180,10 +179,8 @@ private: class Pathfinder { public: - /** - * default constructor - */ - Pathfinder() = default; + Pathfinder() = delete; + Pathfinder(Map *map, const NodeDefManager *ndef) : m_map(map), m_ndef(ndef) {} ~Pathfinder(); @@ -197,8 +194,7 @@ public: * @param max_drop maximum number of blocks a path may drop * @param algo Algorithm to use for finding a path */ - std::vector getPath(ServerEnvironment *env, - v3s16 source, + std::vector getPath(v3s16 source, v3s16 destination, unsigned int searchdistance, unsigned int max_jump, @@ -328,7 +324,9 @@ private: friend class GridNodeContainer; GridNodeContainer *m_nodes_container = nullptr; - ServerEnvironment *m_env = 0; /**< minetest environment pointer */ + Map *m_map = nullptr; + + const NodeDefManager *m_ndef = nullptr; friend class PathfinderCompareHeuristic; @@ -410,18 +408,15 @@ class PathfinderCompareHeuristic /* implementation */ /******************************************************************************/ -std::vector get_path(ServerEnvironment* env, - v3s16 source, - v3s16 destination, - unsigned int searchdistance, - unsigned int max_jump, - unsigned int max_drop, - PathAlgorithm algo) +std::vector get_path(Map* map, const NodeDefManager *ndef, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + PathAlgorithm algo) { - Pathfinder searchclass; - - return searchclass.getPath(env, - source, destination, + return Pathfinder(map, ndef).getPath(source, destination, searchdistance, max_jump, max_drop, algo); } @@ -521,13 +516,13 @@ void PathGridnode::setCost(v3s16 dir, const PathCost &cost) void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node) { - const NodeDefManager *ndef = m_pathf->m_env->getGameDef()->ndef(); + const NodeDefManager *ndef = m_pathf->m_ndef; PathGridnode &elem = *p_node; v3s16 realpos = m_pathf->getRealPos(ipos); - MapNode current = m_pathf->m_env->getMap().getNode(realpos); - MapNode below = m_pathf->m_env->getMap().getNode(realpos + v3s16(0, -1, 0)); + MapNode current = m_pathf->m_map->getNode(realpos); + MapNode below = m_pathf->m_map->getNode(realpos + v3s16(0, -1, 0)); if ((current.param0 == CONTENT_IGNORE) || @@ -610,8 +605,7 @@ PathGridnode &MapGridNodeContainer::access(v3s16 p) /******************************************************************************/ -std::vector Pathfinder::getPath(ServerEnvironment *env, - v3s16 source, +std::vector Pathfinder::getPath(v3s16 source, v3s16 destination, unsigned int searchdistance, unsigned int max_jump, @@ -624,15 +618,8 @@ std::vector Pathfinder::getPath(ServerEnvironment *env, #endif std::vector retval; - //check parameters - if (env == 0) { - ERROR_TARGET << "Missing environment pointer" << std::endl; - return retval; - } - //initialization m_searchdistance = searchdistance; - m_env = env; m_maxjump = max_jump; m_maxdrop = max_drop; m_start = source; @@ -681,15 +668,14 @@ std::vector Pathfinder::getPath(ServerEnvironment *env, #endif //fail if source or destination is walkable - const NodeDefManager *ndef = m_env->getGameDef()->ndef(); - MapNode node_at_pos = m_env->getMap().getNode(destination); - if (ndef->get(node_at_pos).walkable) { + MapNode node_at_pos = m_map->getNode(destination); + if (m_ndef->get(node_at_pos).walkable) { VERBOSE_TARGET << "Destination is walkable. " << "Pos: " << PP(destination) << std::endl; return retval; } - node_at_pos = m_env->getMap().getNode(source); - if (ndef->get(node_at_pos).walkable) { + node_at_pos = m_map->getNode(source); + if (m_ndef->get(node_at_pos).walkable) { VERBOSE_TARGET << "Source is walkable. " << "Pos: " << PP(source) << std::endl; return retval; @@ -843,7 +829,6 @@ v3s16 Pathfinder::getRealPos(v3s16 ipos) /******************************************************************************/ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) { - const NodeDefManager *ndef = m_env->getGameDef()->ndef(); PathCost retval; retval.updated = true; @@ -857,7 +842,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) return retval; } - MapNode node_at_pos2 = m_env->getMap().getNode(pos2); + MapNode node_at_pos2 = m_map->getNode(pos2); //did we get information about node? if (node_at_pos2.param0 == CONTENT_IGNORE ) { @@ -866,9 +851,9 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) return retval; } - if (!ndef->get(node_at_pos2).walkable) { + if (!m_ndef->get(node_at_pos2).walkable) { MapNode node_below_pos2 = - m_env->getMap().getNode(pos2 + v3s16(0, -1, 0)); + m_map->getNode(pos2 + v3s16(0, -1, 0)); //did we get information about node? if (node_below_pos2.param0 == CONTENT_IGNORE ) { @@ -878,7 +863,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) } //test if the same-height neighbor is suitable - if (ndef->get(node_below_pos2).walkable) { + if (m_ndef->get(node_below_pos2).walkable) { //SUCCESS! retval.valid = true; retval.value = 1; @@ -889,19 +874,19 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) else { //test if we can fall a couple of nodes (m_maxdrop) v3s16 testpos = pos2 + v3s16(0, -1, 0); - MapNode node_at_pos = m_env->getMap().getNode(testpos); + MapNode node_at_pos = m_map->getNode(testpos); while ((node_at_pos.param0 != CONTENT_IGNORE) && - (!ndef->get(node_at_pos).walkable) && + (!m_ndef->get(node_at_pos).walkable) && (testpos.Y > m_limits.MinEdge.Y)) { testpos += v3s16(0, -1, 0); - node_at_pos = m_env->getMap().getNode(testpos); + node_at_pos = m_map->getNode(testpos); } //did we find surface? if ((testpos.Y >= m_limits.MinEdge.Y) && (node_at_pos.param0 != CONTENT_IGNORE) && - (ndef->get(node_at_pos).walkable)) { + (m_ndef->get(node_at_pos).walkable)) { if ((pos2.Y - testpos.Y - 1) <= m_maxdrop) { //SUCCESS! retval.valid = true; @@ -927,34 +912,34 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) v3s16 targetpos = pos2; // position for jump target v3s16 jumppos = pos; // position for checking if jumping space is free - MapNode node_target = m_env->getMap().getNode(targetpos); - MapNode node_jump = m_env->getMap().getNode(jumppos); + MapNode node_target = m_map->getNode(targetpos); + MapNode node_jump = m_map->getNode(jumppos); bool headbanger = false; // true if anything blocks jumppath while ((node_target.param0 != CONTENT_IGNORE) && - (ndef->get(node_target).walkable) && + (m_ndef->get(node_target).walkable) && (targetpos.Y < m_limits.MaxEdge.Y)) { //if the jump would hit any solid node, discard if ((node_jump.param0 == CONTENT_IGNORE) || - (ndef->get(node_jump).walkable)) { + (m_ndef->get(node_jump).walkable)) { headbanger = true; break; } targetpos += v3s16(0, 1, 0); jumppos += v3s16(0, 1, 0); - node_target = m_env->getMap().getNode(targetpos); - node_jump = m_env->getMap().getNode(jumppos); + node_target = m_map->getNode(targetpos); + node_jump = m_map->getNode(jumppos); } //check headbanger one last time if ((node_jump.param0 == CONTENT_IGNORE) || - (ndef->get(node_jump).walkable)) { + (m_ndef->get(node_jump).walkable)) { headbanger = true; } //did we find surface without banging our head? if ((!headbanger) && (targetpos.Y <= m_limits.MaxEdge.Y) && - (!ndef->get(node_target).walkable)) { + (!m_ndef->get(node_target).walkable)) { if (targetpos.Y - pos2.Y <= m_maxjump) { //SUCCESS! @@ -1254,21 +1239,20 @@ v3s16 Pathfinder::walkDownwards(v3s16 pos, unsigned int max_down) { if (max_down == 0) return pos; v3s16 testpos = v3s16(pos); - MapNode node_at_pos = m_env->getMap().getNode(testpos); - const NodeDefManager *ndef = m_env->getGameDef()->ndef(); + MapNode node_at_pos = m_map->getNode(testpos); unsigned int down = 0; while ((node_at_pos.param0 != CONTENT_IGNORE) && - (!ndef->get(node_at_pos).walkable) && + (!m_ndef->get(node_at_pos).walkable) && (testpos.Y > m_limits.MinEdge.Y) && (down <= max_down)) { testpos += v3s16(0, -1, 0); down++; - node_at_pos = m_env->getMap().getNode(testpos); + node_at_pos = m_map->getNode(testpos); } //did we find surface? if ((testpos.Y >= m_limits.MinEdge.Y) && (node_at_pos.param0 != CONTENT_IGNORE) && - (ndef->get(node_at_pos).walkable)) { + (m_ndef->get(node_at_pos).walkable)) { if (down == 0) { pos = testpos; } else if ((down - 1) <= max_down) { diff --git a/src/pathfinder.h b/src/pathfinder.h index 70f3d6bbc..526aa0ee8 100644 --- a/src/pathfinder.h +++ b/src/pathfinder.h @@ -29,7 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Forward declarations */ /******************************************************************************/ -class ServerEnvironment; +class NodeDefManager; +class Map; /******************************************************************************/ /* Typedefs and macros */ @@ -54,10 +55,10 @@ typedef enum { /******************************************************************************/ /** c wrapper function to use from scriptapi */ -std::vector get_path(ServerEnvironment *env, - v3s16 source, - v3s16 destination, - unsigned int searchdistance, - unsigned int max_jump, - unsigned int max_drop, - PathAlgorithm algo); +std::vector get_path(Map *map, const NodeDefManager *ndef, + v3s16 source, + v3s16 destination, + unsigned int searchdistance, + unsigned int max_jump, + unsigned int max_drop, + PathAlgorithm algo); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 40e50e64a..e3afe1862 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -577,7 +577,7 @@ int ModApiEnvMod::l_get_node_timer(lua_State *L) // Do it v3s16 p = read_v3s16(L, 1); - NodeTimerRef::create(L, p, env); + NodeTimerRef::create(L, p, &env->getServerMap()); return 1; } @@ -1193,7 +1193,7 @@ int ModApiEnvMod::l_find_path(lua_State *L) algo = PA_DIJKSTRA; } - std::vector path = get_path(env, pos1, pos2, + std::vector path = get_path(&env->getServerMap(), env->getGameDef()->ndef(), pos1, pos2, searchdistance, max_jump, max_drop, algo); if (!path.empty()) { @@ -1257,8 +1257,9 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L) else return 0; + ServerMap *map = &env->getServerMap(); treegen::error e; - if ((e = treegen::spawn_ltree (env, p0, ndef, tree_def)) != treegen::SUCCESS) { + if ((e = treegen::spawn_ltree (map, p0, ndef, tree_def)) != treegen::SUCCESS) { if (e == treegen::UNBALANCED_BRACKETS) { luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket"); } else { diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index 15a59744f..c2df52c05 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -41,11 +41,9 @@ int NodeTimerRef::l_set(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; f32 t = readParam(L,2); f32 e = readParam(L,3); - env->getMap().setNodeTimer(NodeTimer(t, e, o->m_p)); + o->m_map->setNodeTimer(NodeTimer(t, e, o->m_p)); return 0; } @@ -53,10 +51,8 @@ int NodeTimerRef::l_start(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; f32 t = readParam(L,2); - env->getMap().setNodeTimer(NodeTimer(t, 0, o->m_p)); + o->m_map->setNodeTimer(NodeTimer(t, 0, o->m_p)); return 0; } @@ -64,9 +60,7 @@ int NodeTimerRef::l_stop(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - env->getMap().removeNodeTimer(o->m_p); + o->m_map->removeNodeTimer(o->m_p); return 0; } @@ -74,10 +68,7 @@ int NodeTimerRef::l_is_started(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); lua_pushboolean(L,(t.timeout != 0)); return 1; } @@ -86,10 +77,7 @@ int NodeTimerRef::l_get_timeout(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); lua_pushnumber(L,t.timeout); return 1; } @@ -98,37 +86,21 @@ int NodeTimerRef::l_get_elapsed(lua_State *L) { MAP_LOCK_REQUIRED; NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); + NodeTimer t = o->m_map->getNodeTimer(o->m_p); lua_pushnumber(L,t.elapsed); return 1; } - -NodeTimerRef::NodeTimerRef(v3s16 p, ServerEnvironment *env): - m_p(p), - m_env(env) -{ -} - // Creates an NodeTimerRef and leaves it on top of stack // Not callable from Lua; all references are created on the C side. -void NodeTimerRef::create(lua_State *L, v3s16 p, ServerEnvironment *env) +void NodeTimerRef::create(lua_State *L, v3s16 p, ServerMap *map) { - NodeTimerRef *o = new NodeTimerRef(p, env); + NodeTimerRef *o = new NodeTimerRef(p, map); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); } -void NodeTimerRef::set_null(lua_State *L) -{ - NodeTimerRef *o = checkobject(L, -1); - o->m_env = NULL; -} - void NodeTimerRef::Register(lua_State *L) { lua_newtable(L); diff --git a/src/script/lua_api/l_nodetimer.h b/src/script/lua_api/l_nodetimer.h index b894c5c8c..bbc975fd2 100644 --- a/src/script/lua_api/l_nodetimer.h +++ b/src/script/lua_api/l_nodetimer.h @@ -22,13 +22,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "lua_api/l_base.h" -class ServerEnvironment; +class ServerMap; class NodeTimerRef : public ModApiBase { private: v3s16 m_p; - ServerEnvironment *m_env = nullptr; + ServerMap *m_map; static const char className[]; static const luaL_Reg methods[]; @@ -50,14 +50,12 @@ private: static int l_get_elapsed(lua_State *L); public: - NodeTimerRef(v3s16 p, ServerEnvironment *env); + NodeTimerRef(v3s16 p, ServerMap *map) : m_p(p), m_map(map) {} ~NodeTimerRef() = default; // Creates an NodeTimerRef and leaves it on top of stack // Not callable from Lua; all references are created on the C side. - static void create(lua_State *L, v3s16 p, ServerEnvironment *env); - - static void set_null(lua_State *L); + static void create(lua_State *L, v3s16 p, ServerMap *map); static void Register(lua_State *L); }; -- cgit v1.2.3 From f780bae05cc2fdd23a6d7326c770783da8d94ea3 Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Sat, 11 Apr 2020 16:39:30 -0400 Subject: Formspecs: Add state-selection to style elements (#9378) --- doc/lua_api.txt | 61 +++++++++--- games/minimal/mods/test/formspec.lua | 5 +- src/client/game.cpp | 9 +- src/gui/StyleSpec.h | 65 ++++++++++-- src/gui/guiButton.cpp | 163 +++++++++++++++--------------- src/gui/guiButton.h | 34 ++++--- src/gui/guiButtonImage.cpp | 102 ++++--------------- src/gui/guiButtonImage.h | 26 ++--- src/gui/guiButtonItemImage.cpp | 15 +-- src/gui/guiButtonItemImage.h | 9 +- src/gui/guiConfirmRegistration.cpp | 8 +- src/gui/guiConfirmRegistration.h | 4 +- src/gui/guiFormSpecMenu.cpp | 187 ++++++++++++++++++++++++----------- src/gui/guiFormSpecMenu.h | 8 +- src/gui/guiKeyChangeMenu.cpp | 12 ++- src/gui/guiKeyChangeMenu.h | 5 +- src/gui/guiPasswordChange.cpp | 10 +- src/gui/guiPasswordChange.h | 5 +- src/gui/guiVolumeChange.cpp | 7 +- src/gui/guiVolumeChange.h | 7 +- src/script/lua_api/l_mainmenu.cpp | 19 ++-- src/util/numeric.h | 10 ++ 22 files changed, 454 insertions(+), 317 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6ba87d619..b083b2907 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2188,12 +2188,12 @@ Elements * 9-sliced background. See https://en.wikipedia.org/wiki/9-slice_scaling * Middle is a rect which defines the middle of the 9-slice. - * `x` - The middle will be x pixels from all sides. - * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. - * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values - will be added to the width and height of the texture, allowing it to be used as the - distance from the far end. - * All numbers in middle are integers. + * `x` - The middle will be x pixels from all sides. + * `x,y` - The middle will be x pixels from the horizontal and y from the vertical. + * `x,y,x2,y2` - The middle will start at x,y, and end at x2, y2. Negative x2 and y2 values + will be added to the width and height of the texture, allowing it to be used as the + distance from the far end. + * All numbers in middle are integers. * Example for formspec 8x4 in 16x resolution: image shall be sized 8 times 16px times 4 times 16px * If `auto_clip` is `true`, the background is clipped to the formspec size @@ -2508,16 +2508,28 @@ Elements * `span=`: number of following columns to affect (default: infinite). -### `style[,,...;;;...]` +### `style[,;;;...]` -* Set the style for the named element(s) `name`. +* Set the style for the element(s) matching `selector` by name. +* `selector` can be one of: + * `` - An element name. + * `:` - An element name, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * Note: this **must** be before the element is defined. * See [Styling Formspecs]. -### `style_type[,,...;;;...]` +### `style_type[,;;;...]` -* Sets the style for all elements of type(s) `type` which appear after this element. +* Set the style for the element(s) matching `selector` by type. +* `selector` can be one of: + * `` - An element type. + * `:` - An element type, a colon, and one or more states. +* `state` is a list of states separated by the `+` character. + * If a state is provided, the style will only take effect when the element is in that state. + * All provided states must be active for the style to apply. * See [Styling Formspecs]. Migrating to Real Coordinates @@ -2560,23 +2572,32 @@ Styling Formspecs Formspec elements can be themed using the style elements: - style[,,...;;;...] - style_type[,,...;;;...] + style[,;;;...] + style[:,:;;;...] + style_type[,;;;...] + style_type[:,:;;;...] Where a prop is: property_name=property_value +For example: + + style_type[button;bgcolor=#006699] + style[world_delete;bgcolor=red;textcolor=yellow] + button[4,3.95;2.6,1;world_delete;Delete] + A name/type can optionally be a comma separated list of names/types, like so: world_delete,world_create,world_configure button,image_button -For example: +Any name/type in the list can also be accompanied by a `+`-separated list of states, like so: - style_type[button;bgcolor=#006699] - style[world_delete;bgcolor=red;textcolor=yellow] - button[4,3.95;2.6,1;world_delete;Delete] + world_delete:hovered+pressed + button:pressed + +States allow you to apply styles in response to changes in the element, instead of applying at all times. Setting a property to nothing will reset it to the default value. For example: @@ -2654,6 +2675,14 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +### Valid States + +* *all elements* + * default - Equivalent to providing no states +* button, button_exit, image_button, item_image_button + * hovered - Active when the mouse is hovering over the element + * pressed - Active when the button is pressed + Markup Language --------------- diff --git a/games/minimal/mods/test/formspec.lua b/games/minimal/mods/test/formspec.lua index d2123b4af..14886aad2 100644 --- a/games/minimal/mods/test/formspec.lua +++ b/games/minimal/mods/test/formspec.lua @@ -65,7 +65,10 @@ local style_fs = [[ style[one_btn13;border=false] item_image_button[1.25,8.35;1,1;default:sword_steel;one_btn13;NoBor] - style[one_btn14;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png;fgimg=bubble.png;fgimg_hovered=default_apple.png;fgimg_pressed=heart.png] + style[one_btn14;border=false;bgimg=test_bg.png;fgimg=bubble.png] + style[one_btn14:hovered;bgimg=test_bg_hovered.png;fgimg=default_apple.png;textcolor=red] + style[one_btn14:pressed;bgimg=test_bg_pressed.png;fgimg=heart.png;textcolor=green] + style[one_btn14:hovered+pressed;textcolor=blue] image_button[0,9.6;1,1;bubble.png;one_btn14;Bg] style[one_btn15;border=false;bgimg=test_bg.png;bgimg_hovered=test_bg_hovered.png;bgimg_pressed=test_bg_pressed.png] diff --git a/src/client/game.cpp b/src/client/game.cpp index 505108caf..06e76d170 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1556,7 +1556,8 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, connection_aborted))->drop(); + &g_menumgr, client, playername, password, + connection_aborted, texture_src))->drop(); } } else { wait_time += dtime; @@ -1711,19 +1712,19 @@ inline bool Game::handleCallbacks() if (g_gamecallback->changepassword_requested) { (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client))->drop(); + &g_menumgr, client, texture_src))->drop(); g_gamecallback->changepassword_requested = false; } if (g_gamecallback->changevolume_requested) { (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->changevolume_requested = false; } if (g_gamecallback->keyconfig_requested) { (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr))->drop(); + &g_menumgr, texture_src))->drop(); g_gamecallback->keyconfig_requested = false; } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 999c1d237..799fbf46d 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "client/tile.h" // ITextureSource +#include "debug.h" #include "irrlichttypes_extrabloated.h" #include "util/string.h" #include @@ -31,25 +32,34 @@ public: { TEXTCOLOR, BGCOLOR, - BGCOLOR_HOVERED, - BGCOLOR_PRESSED, + BGCOLOR_HOVERED, // Note: Deprecated property + BGCOLOR_PRESSED, // Note: Deprecated property NOCLIP, BORDER, BGIMG, - BGIMG_HOVERED, + BGIMG_HOVERED, // Note: Deprecated property BGIMG_MIDDLE, - BGIMG_PRESSED, + BGIMG_PRESSED, // Note: Deprecated property FGIMG, - FGIMG_HOVERED, - FGIMG_PRESSED, + FGIMG_HOVERED, // Note: Deprecated property + FGIMG_PRESSED, // Note: Deprecated property ALPHA, NUM_PROPERTIES, NONE }; + enum State + { + STATE_DEFAULT = 0, + STATE_HOVERED = 1 << 0, + STATE_PRESSED = 1 << 1, + NUM_STATES = 1 << 2, + STATE_INVALID = 1 << 3, + }; private: std::array property_set{}; std::array properties; + State state_map = STATE_DEFAULT; public: static Property GetPropertyByName(const std::string &name) @@ -99,6 +109,49 @@ public: property_set[prop] = true; } + //! Parses a name and returns the corresponding state enum + static State getStateByName(const std::string &name) + { + if (name == "default") { + return STATE_DEFAULT; + } else if (name == "hovered") { + return STATE_HOVERED; + } else if (name == "pressed") { + return STATE_PRESSED; + } else { + return STATE_INVALID; + } + } + + //! Gets the state that this style is intended for + State getState() const + { + return state_map; + } + + //! Set the given state on this style + void addState(State state) + { + FATAL_ERROR_IF(state >= NUM_STATES, "Out-of-bounds state received"); + + state_map = static_cast(state_map | state); + } + + //! Using a list of styles mapped to state values, calculate the final + // combined style for a state by propagating values in its component states + static StyleSpec getStyleFromStatePropagation(const std::array &styles, State state) + { + StyleSpec temp = styles[StyleSpec::STATE_DEFAULT]; + temp.state_map = state; + for (int i = StyleSpec::STATE_DEFAULT + 1; i <= state; i++) { + if ((state & i) != 0) { + temp = temp | styles[i]; + } + } + + return temp; + } + video::SColor getColor(Property prop, video::SColor def) const { const auto &val = properties[prop]; diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 4c16ee237..9dfe36bc4 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -14,6 +14,7 @@ #include "irrlicht_changes/static_text.h" #include "porting.h" #include "StyleSpec.h" +#include "util/numeric.h" using namespace irr; using namespace gui; @@ -26,14 +27,15 @@ using namespace gui; //! constructor GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, - s32 id, core::rect rectangle, bool noclip) + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip) : IGUIButton(environment, parent, id, rectangle), SpriteBank(0), OverrideFont(0), OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)), ClickTime(0), HoverTime(0), FocusTime(0), ClickShiftState(false), ClickControlState(false), IsPushButton(false), Pressed(false), - UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) + UseAlphaChannel(false), DrawBorder(true), ScaleImage(false), TSrc(tsrc) { setNotClipped(noclip); @@ -44,14 +46,6 @@ GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, // PATCH for (size_t i = 0; i < 4; i++) { Colors[i] = Environment->getSkin()->getColor((EGUI_DEFAULT_COLOR)i); - HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id); StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); @@ -262,6 +256,13 @@ void GUIButton::draw() return; // PATCH + // Track hovered state, if it has changed then we need to update the style. + bool hovered = isHovered(); + if (hovered != WasHovered) { + WasHovered = hovered; + setFromState(); + } + GUISkin* skin = dynamic_cast(Environment->getSkin()); video::IVideoDriver* driver = Environment->getVideoDriver(); // END PATCH @@ -271,21 +272,24 @@ void GUIButton::draw() if (!Pressed) { // PATCH - skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect, - isHovered() ? HoveredColors : Colors); + skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, + &AbsoluteClippingRect, Colors); // END PATCH } else { // PATCH - skin->drawColored3DButtonPanePressed(this, - AbsoluteRect, &AbsoluteClippingRect, PressedColors); + skin->drawColored3DButtonPanePressed(this, AbsoluteRect, + &AbsoluteClippingRect, Colors); // END PATCH } } const core::position2di buttonCenter(AbsoluteRect.getCenter()); - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed); + // PATCH + // The image changes based on the state, so we use the default every time. + EGUI_BUTTON_IMAGE_STATE imageState = EGBIS_IMAGE_UP; + // END PATCH if ( ButtonImages[(u32)imageState].Texture ) { core::position2d pos(buttonCenter); @@ -548,18 +552,6 @@ void GUIButton::setPressedImage(video::ITexture* image, const core::rect& p setImage(gui::EGBIS_IMAGE_DOWN, image, pos); } -void GUIButton::setHoveredImage(video::ITexture* image) -{ - setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image); - setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image); -} - -void GUIButton::setHoveredImage(video::ITexture* image, const core::rect& pos) -{ - setImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos); - setImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos); -} - //! Sets the text displayed by the button void GUIButton::setText(const wchar_t* text) { @@ -618,6 +610,8 @@ void GUIButton::setPressed(bool pressed) skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y))); } } + + setFromState(); } } @@ -729,10 +723,12 @@ void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWri } // PATCH -GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext) +GUIButton* GUIButton::addButton(IGUIEnvironment *environment, + const core::rect& rectangle, ISimpleTextureSource *tsrc, + IGUIElement* parent, s32 id, const wchar_t* text, + const wchar_t *tooltiptext) { - GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle); + GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc); if (text) button->setText(text); @@ -749,76 +745,87 @@ void GUIButton::setColor(video::SColor color) for (size_t i = 0; i < 4; i++) { video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); Colors[i] = base.getInterpolated(color, d); - HoveredColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_HOVERED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_HOVERED_MOD, 0, 255)); - PressedColors[i] = irr::video::SColor(Colors[i].getAlpha(), - core::clamp(Colors[i].getRed() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getGreen() * COLOR_PRESSED_MOD, 0, 255), - core::clamp(Colors[i].getBlue() * COLOR_PRESSED_MOD, 0, 255)); } } -void GUIButton::setHoveredColor(video::SColor color) -{ - float d = 0.65f; - for (size_t i = 0; i < 4; i++) { - video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); - HoveredColors[i] = base.getInterpolated(color, d); - } -} -void GUIButton::setPressedColor(video::SColor color) + +//! Set element properties from a StyleSpec corresponding to the button state +void GUIButton::setFromState() { - float d = 0.65f; - for (size_t i = 0; i < 4; i++) { - video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); - PressedColors[i] = base.getInterpolated(color, d); - } + StyleSpec::State state = StyleSpec::STATE_DEFAULT; + + if (isPressed()) + state = static_cast(state | StyleSpec::STATE_PRESSED); + + if (isHovered()) + state = static_cast(state | StyleSpec::STATE_HOVERED); + + setFromStyle(StyleSpec::getStyleFromStatePropagation(Styles, state)); } //! Set element properties from a StyleSpec -void GUIButton::setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc) +void GUIButton::setFromStyle(const StyleSpec& style) { + bool hovered = (style.getState() & StyleSpec::STATE_HOVERED) != 0; + bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0; + if (style.isNotDefault(StyleSpec::BGCOLOR)) { + setColor(style.getColor(StyleSpec::BGCOLOR)); - } - if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) { - setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED)); - } - if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) { - setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED)); + + // If we have a propagated hover/press color, we need to automatically + // lighten/darken it + if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) { + for (size_t i = 0; i < 4; i++) { + if (pressed) { + Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD); + } else if (hovered) { + Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD); + } + } + } + + } else { + for (size_t i = 0; i < 4; i++) { + video::SColor base = + Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); + if (pressed) { + Colors[i] = multiplyColorValue(base, COLOR_PRESSED_MOD); + } else if (hovered) { + Colors[i] = multiplyColorValue(base, COLOR_HOVERED_MOD); + } else { + Colors[i] = base; + } + } } if (style.isNotDefault(StyleSpec::TEXTCOLOR)) { setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR)); + } else { + setOverrideColor(video::SColor(255,255,255,255)); + OverrideColorEnabled = false; } - setNotClipped(style.getBool(StyleSpec::NOCLIP, isNotClipped())); - setDrawBorder(style.getBool(StyleSpec::BORDER, DrawBorder)); + setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + setDrawBorder(style.getBool(StyleSpec::BORDER, true)); setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); const core::position2di buttonCenter(AbsoluteRect.getCenter()); core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::BGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, tsrc); - + video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, + getTextureSource()); setImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::BGIMG_HOVERED)) { - video::ITexture *hovered_texture = style.getTexture(StyleSpec::BGIMG_HOVERED, tsrc); - - setHoveredImage(guiScalingImageButton( - Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::BGIMG_PRESSED)) { - video::ITexture *pressed_texture = style.getTexture(StyleSpec::BGIMG_PRESSED, tsrc); - - setPressedImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); + Environment->getVideoDriver(), texture, geom.X, geom.Y)); setScaleImage(true); + } else { + setImage(nullptr); } BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); } + +//! Set the styles used for each state +void GUIButton::setStyles(const std::array& styles) +{ + Styles = styles; + setFromState(); +} // END PATCH diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 3d1f98c32..ef10f926e 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -13,6 +13,7 @@ #include "ITexture.h" #include "SColor.h" #include "guiSkin.h" +#include "StyleSpec.h" using namespace irr; @@ -67,7 +68,6 @@ using namespace irr; #endif class ISimpleTextureSource; -class StyleSpec; class GUIButton : public gui::IGUIButton { @@ -75,7 +75,8 @@ public: //! constructor GUIButton(gui::IGUIEnvironment* environment, gui::IGUIElement* parent, - s32 id, core::rect rectangle, bool noclip=false); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip=false); //! destructor virtual ~GUIButton(); @@ -125,16 +126,10 @@ public: //! Sets an image which should be displayed on the button when it is in pressed state. virtual void setPressedImage(video::ITexture* image, const core::rect& pos) override; - //! Sets an image which should be displayed on the button when it is in hovered state. - virtual void setHoveredImage(video::ITexture* image=nullptr); - //! Sets the text displayed by the button virtual void setText(const wchar_t* text) override; // END PATCH - //! Sets an image which should be displayed on the button when it is in hovered state. - virtual void setHoveredImage(video::ITexture* image, const core::rect& pos); - //! Sets the sprite bank used by the button virtual void setSpriteBank(gui::IGUISpriteBank* bank=0) override; @@ -225,22 +220,29 @@ public: void setColor(video::SColor color); // PATCH - void setHoveredColor(video::SColor color); - void setPressedColor(video::SColor color); + //! Set element properties from a StyleSpec corresponding to the button state + void setFromState(); //! Set element properties from a StyleSpec - virtual void setFromStyle(const StyleSpec& style, ISimpleTextureSource *tsrc); + virtual void setFromStyle(const StyleSpec& style); + + //! Set the styles used for each state + void setStyles(const std::array& styles); // END PATCH //! Do not drop returned handle - static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect& rectangle, - IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext=L""); + static GUIButton* addButton(gui::IGUIEnvironment *environment, + const core::rect& rectangle, ISimpleTextureSource *tsrc, + IGUIElement* parent, s32 id, const wchar_t* text, + const wchar_t *tooltiptext=L""); protected: void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center); gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const; + ISimpleTextureSource *getTextureSource() { return TSrc; } + struct ButtonImage { ButtonImage() : Texture(0), SourceRect(core::rect(0,0,0,0)) @@ -308,6 +310,8 @@ private: ButtonImage ButtonImages[gui::EGBIS_COUNT]; + std::array Styles; + gui::IGUIFont* OverrideFont; bool OverrideColorEnabled; @@ -326,8 +330,8 @@ private: video::SColor Colors[4]; // PATCH - video::SColor HoveredColors[4]; - video::SColor PressedColors[4]; + bool WasHovered = false; + ISimpleTextureSource *TSrc; gui::IGUIStaticText *StaticText; diff --git a/src/gui/guiButtonImage.cpp b/src/gui/guiButtonImage.cpp index 02d920277..2658ad967 100644 --- a/src/gui/guiButtonImage.cpp +++ b/src/gui/guiButtonImage.cpp @@ -30,8 +30,9 @@ using namespace irr; using namespace gui; GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, - gui::IGUIElement *parent, s32 id, core::rect rectangle, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) + gui::IGUIElement *parent, s32 id, core::rect rectangle, + ISimpleTextureSource *tsrc, bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = Environment->addImage( core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), this); @@ -39,100 +40,38 @@ GUIButtonImage::GUIButtonImage(gui::IGUIEnvironment *environment, sendToBack(m_image); } -bool GUIButtonImage::OnEvent(const SEvent& event) -{ - bool result = GUIButton::OnEvent(event); - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - video::ITexture *texture = m_foreground_images[(u32)imageState].Texture; - if (texture != nullptr) - { - m_image->setImage(texture); - } - - m_image->setVisible(texture != nullptr); - - return result; -} - -void GUIButtonImage::setForegroundImage(EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image, const core::rect &sourceRect) +void GUIButtonImage::setForegroundImage(video::ITexture *image) { - if (state >= EGBIS_COUNT) + if (image == m_foreground_image) return; - if (image) + if (image != nullptr) image->grab(); - u32 stateIdx = (u32)state; - if (m_foreground_images[stateIdx].Texture) - m_foreground_images[stateIdx].Texture->drop(); - - m_foreground_images[stateIdx].Texture = image; - m_foreground_images[stateIdx].SourceRect = sourceRect; - - EGUI_BUTTON_IMAGE_STATE imageState = getImageState(isPressed(), m_foreground_images); - if (imageState == stateIdx) - m_image->setImage(image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image); -} - -void GUIButtonImage::setForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP, image, pos); -} - -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image); -} + if (m_foreground_image != nullptr) + m_foreground_image->drop(); -void GUIButtonImage::setPressedForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_DOWN, image, pos); + m_foreground_image = image; + m_image->setImage(image); } -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image) +//! Set element properties from a StyleSpec +void GUIButtonImage::setFromStyle(const StyleSpec& style) { - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image); -} - -void GUIButtonImage::setHoveredForegroundImage(video::ITexture *image, const core::rect &pos) -{ - setForegroundImage(gui::EGBIS_IMAGE_UP_MOUSEOVER, image, pos); - setForegroundImage(gui::EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, image, pos); -} - -void GUIButtonImage::setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) -{ - GUIButton::setFromStyle(style, tsrc); + GUIButton::setFromStyle(style); video::IVideoDriver *driver = Environment->getVideoDriver(); const core::position2di buttonCenter(AbsoluteRect.getCenter()); core::position2d geom(buttonCenter); if (style.isNotDefault(StyleSpec::FGIMG)) { - video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, tsrc); + video::ITexture *texture = style.getTexture(StyleSpec::FGIMG, + getTextureSource()); setForegroundImage(guiScalingImageButton(driver, texture, geom.X, geom.Y)); setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_HOVERED)) { - video::ITexture *hovered_texture = style.getTexture(StyleSpec::FGIMG_HOVERED, tsrc); - - setHoveredForegroundImage(guiScalingImageButton(driver, hovered_texture, geom.X, geom.Y)); - setScaleImage(true); - } - if (style.isNotDefault(StyleSpec::FGIMG_PRESSED)) { - video::ITexture *pressed_texture = style.getTexture(StyleSpec::FGIMG_PRESSED, tsrc); - - setPressedForegroundImage(guiScalingImageButton(driver, pressed_texture, geom.X, geom.Y)); - setScaleImage(true); + } else { + setForegroundImage(nullptr); } } @@ -143,11 +82,12 @@ void GUIButtonImage::setScaleImage(bool scaleImage) } GUIButtonImage *GUIButtonImage::addButton(IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext) + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext) { GUIButtonImage *button = new GUIButtonImage(environment, - parent ? parent : environment->getRootGUIElement(), id, rectangle); + parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc); if (text) button->setText(text); diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index 15901ee5d..a948d772b 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -27,33 +27,23 @@ class GUIButtonImage : public GUIButton public: //! constructor GUIButtonImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, bool noclip = false); - - virtual bool OnEvent(const SEvent& event) override; - - void setForegroundImage(gui::EGUI_BUTTON_IMAGE_STATE state, - video::ITexture *image = nullptr, - const core::rect &sourceRect = core::rect(0, 0, 0, 0)); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + bool noclip = false); void setForegroundImage(video::ITexture *image = nullptr); - void setForegroundImage(video::ITexture *image, const core::rect &pos); - - void setPressedForegroundImage(video::ITexture *image = nullptr); - void setPressedForegroundImage(video::ITexture *image, const core::rect &pos); - - void setHoveredForegroundImage(video::ITexture *image = nullptr); - void setHoveredForegroundImage(video::ITexture *image, const core::rect &pos); - virtual void setFromStyle(const StyleSpec &style, ISimpleTextureSource *tsrc) override; + //! Set element properties from a StyleSpec + virtual void setFromStyle(const StyleSpec& style) override; virtual void setScaleImage(bool scaleImage=true) override; //! Do not drop returned handle static GUIButtonImage *addButton(gui::IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, const wchar_t *tooltiptext = L""); + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, + const wchar_t *tooltiptext = L""); private: - ButtonImage m_foreground_images[gui::EGBIS_COUNT]; + video::ITexture *m_foreground_image = nullptr; gui::IGUIImage *m_image; }; diff --git a/src/gui/guiButtonItemImage.cpp b/src/gui/guiButtonItemImage.cpp index 5c48b2acd..d8b9042ac 100644 --- a/src/gui/guiButtonItemImage.cpp +++ b/src/gui/guiButtonItemImage.cpp @@ -28,9 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc., using namespace irr; using namespace gui; -GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, std::string item, Client *client, bool noclip) - : GUIButton (environment, parent, id, rectangle, noclip) +GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, + gui::IGUIElement *parent, s32 id, core::rect rectangle, + ISimpleTextureSource *tsrc, std::string item, Client *client, + bool noclip) + : GUIButton (environment, parent, id, rectangle, tsrc, noclip) { m_image = new GUIItemImage(environment, this, id, core::rect(0,0,rectangle.getWidth(),rectangle.getHeight()), @@ -42,12 +44,13 @@ GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::I } GUIButtonItemImage *GUIButtonItemImage::addButton(IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client) + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client) { GUIButtonItemImage *button = new GUIButtonItemImage(environment, parent ? parent : environment->getRootGUIElement(), - id, rectangle, item, client); + id, rectangle, tsrc, item, client); if (text) button->setText(text); diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 0a61874da..9cd0f6188 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -30,13 +30,14 @@ class GUIButtonItemImage : public GUIButton public: //! constructor GUIButtonItemImage(gui::IGUIEnvironment *environment, gui::IGUIElement *parent, - s32 id, core::rect rectangle, std::string item, - Client *client, bool noclip = false); + s32 id, core::rect rectangle, ISimpleTextureSource *tsrc, + std::string item, Client *client, bool noclip = false); //! Do not drop returned handle static GUIButtonItemImage *addButton(gui::IGUIEnvironment *environment, - const core::rect &rectangle, IGUIElement *parent, s32 id, - const wchar_t *text, std::string item, Client *client); + const core::rect &rectangle, ISimpleTextureSource *tsrc, + IGUIElement *parent, s32 id, const wchar_t *text, std::string item, + Client *client); private: std::string m_item_name; diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 0d8bdf54e..58ac42740 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -40,10 +40,10 @@ const int ID_message = 266; GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted) : + bool *aborted, ISimpleTextureSource *tsrc) : GUIModalMenu(env, parent, id, menumgr), m_client(client), m_playername(playername), m_password(password), - m_aborted(aborted) + m_aborted(aborted), m_tsrc(tsrc) { #ifdef __ANDROID__ m_touchscreen_visible = false; @@ -130,14 +130,14 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) core::rect rect2(0, 0, 230 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 - 220 * s, ypos); text = wgettext("Register and Join"); - GUIButton::addButton(Environment, rect2, this, ID_confirm, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_confirm, text); delete[] text; } { core::rect rect2(0, 0, 120 * s, 35 * s); rect2 = rect2 + v2s32(size.X / 2 + 70 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect2, this, ID_cancel, text); + GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_cancel, text); delete[] text; } { diff --git a/src/gui/guiConfirmRegistration.h b/src/gui/guiConfirmRegistration.h index 42c07e4ed..d8387201d 100644 --- a/src/gui/guiConfirmRegistration.h +++ b/src/gui/guiConfirmRegistration.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class Client; +class ISimpleTextureSource; class GUIConfirmRegistration : public GUIModalMenu { @@ -32,7 +33,7 @@ public: GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, const std::string &playername, const std::string &password, - bool *aborted); + bool *aborted, ISimpleTextureSource *tsrc); ~GUIConfirmRegistration(); void removeChildren(); @@ -63,4 +64,5 @@ private: const std::string &m_password; bool *m_aborted = nullptr; std::wstring m_pass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index acb153569..6a383a791 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -553,7 +553,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this, spec.fid, spec.flabel.c_str()); - auto style = getStyleForElement("checkbox", name); + auto style = getDefaultStyleForElement("checkbox", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == data->focused_fieldname) { @@ -613,7 +613,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect, is_horizontal, true); - auto style = getStyleForElement("scrollbar", name); + auto style = getDefaultStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); @@ -740,7 +740,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true); e->setImage(texture); e->setScaleImage(true); - auto style = getStyleForElement("image", spec.fname); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -776,7 +776,7 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) ); gui::IGUIImage *e = Environment->addImage(texture, pos, true, this, spec.fid, 0); - auto style = getStyleForElement("image", spec.fname); + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); m_fields.push_back(spec); @@ -841,7 +841,7 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el if (parts.size() >= 7) e->setFrameIndex(stoi(parts[6]) - 1); - auto style = getStyleForElement("animated_image", spec.fname, "image"); + auto style = getDefaultStyleForElement("animated_image", spec.fname, "image"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->drop(); @@ -888,7 +888,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid, core::rect(pos, pos + geom), name, m_font, m_client); - auto style = getStyleForElement("item_image", spec.fname); + auto style = getDefaultStyleForElement("item_image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); // item images should let events through @@ -949,10 +949,11 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, if(type == "button_exit") spec.is_exit = true; - GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, this, + spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); - e->setFromStyle(style, m_tsrc); + e->setStyles(style); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1155,7 +1156,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("table", name); + auto style = getDefaultStyleForElement("table", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1231,7 +1232,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getStyleForElement("textlist", name); + auto style = getDefaultStyleForElement("textlist", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_tables.emplace_back(spec, e); @@ -1306,7 +1307,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element if (!str_initial_selection.empty()) e->setSelected(stoi(str_initial_selection)-1); - auto style = getStyleForElement("dropdown", name); + auto style = getDefaultStyleForElement("dropdown", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1394,7 +1395,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element e->setPasswordBox(true,L'*'); - auto style = getStyleForElement("pwdfield", name, "field"); + auto style = getDefaultStyleForElement("pwdfield", name, "field"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1454,7 +1455,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } } - auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname); + auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname); if (e) { if (is_editable && spec.fname == data->focused_fieldname) @@ -1752,7 +1753,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); - auto style = getStyleForElement("label", spec.fname); + auto style = getDefaultStyleForElement("label", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1832,7 +1833,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - auto style = getStyleForElement("vertlabel", spec.fname, "label"); + auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label"); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); @@ -1863,17 +1864,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); - bool noclip = false; - bool drawborder = true; std::string pressed_image_name; - if (parts.size() >= 7) { - if (parts[5] == "true") - noclip = true; - if (parts[6] == "false") - drawborder = false; - } - if (parts.size() >= 8) { pressed_image_name = parts[7]; } @@ -1911,35 +1903,30 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem if (type == "image_button_exit") spec.is_exit = true; - GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc, + this, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } auto style = getStyleForElement("image_button", spec.fname); - e->setFromStyle(style, m_tsrc); - // We explicitly handle these arguments *after* the style properties in - // order to override them if they are provided + // Override style properties with values specified directly in the element if (!image_name.empty()) - { - video::ITexture *texture = m_tsrc->getTexture(image_name); - e->setForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, geom.X, geom.Y)); - } - if (!pressed_image_name.empty()) { - video::ITexture *pressed_texture = m_tsrc->getTexture(pressed_image_name); - e->setPressedForegroundImage(guiScalingImageButton( - Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); - } - e->setScaleImage(true); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); + + if (!pressed_image_name.empty()) + style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, pressed_image_name); if (parts.size() >= 7) { - e->setNotClipped(noclip); - e->setDrawBorder(drawborder); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, parts[5]); + style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, parts[6]); } + e->setStyles(style); + e->setScaleImage(true); + m_fields.push_back(spec); return; } @@ -2033,7 +2020,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen Environment->setFocus(e); } - auto style = getStyleForElement("tabheader", name); + auto style = getDefaultStyleForElement("tabheader", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2118,10 +2105,12 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & 2 ); - GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, rect, this, spec_btn.fid, spec_btn.flabel.c_str(), item_name, m_client); + GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment, + rect, m_tsrc, this, spec_btn.fid, spec_btn.flabel.c_str(), + item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); - e_btn->setFromStyle(style, m_tsrc); + e_btn->setStyles(style); if (spec_btn.fname == data->focused_fieldname) { Environment->setFocus(e_btn); @@ -2177,7 +2166,7 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color); - auto style = getStyleForElement("box", spec.fname); + auto style = getDefaultStyleForElement("box", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); e->drop(); @@ -2469,6 +2458,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b StyleSpec spec; + // Parse properties for (size_t i = 1; i < parts.size(); i++) { size_t equal_pos = parts[i].find('='); if (equal_pos == std::string::npos) { @@ -2500,16 +2490,92 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b for (size_t sel = 0; sel < selectors.size(); sel++) { std::string selector = trim(selectors[sel]); - if (selector.empty()) { - errorstream << "Invalid style element (Empty selector): '" << element - << "'" << std::endl; + // Copy the style properties to a new StyleSpec + // This allows a separate state mask per-selector + StyleSpec selector_spec = spec; + + // Parse state information, if it exists + bool state_valid = true; + size_t state_pos = selector.find(':'); + if (state_pos != std::string::npos) { + std::string state_str = selector.substr(state_pos + 1); + selector = selector.substr(0, state_pos); + + if (state_str.empty()) { + errorstream << "Invalid style element (Invalid state): '" << element + << "'" << std::endl; + state_valid = false; + } else { + std::vector states = split(state_str, '+'); + for (std::string &state : states) { + StyleSpec::State converted = StyleSpec::getStateByName(state); + if (converted == StyleSpec::STATE_INVALID) { + infostream << "Unknown style state " << state << + " in element '" << element << "'" << std::endl; + state_valid = false; + break; + } + + selector_spec.addState(converted); + } + } + } + + if(!state_valid) { + // Skip this selector continue; } if (style_type) { - theme_by_type[selector] |= spec; + theme_by_type[selector].push_back(selector_spec); } else { - theme_by_name[selector] |= spec; + theme_by_name[selector].push_back(selector_spec); + } + + // Backwards-compatibility for existing _hovered/_pressed properties + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED) + || selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED) + || selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + StyleSpec hover_spec; + hover_spec.addState(StyleSpec::STATE_HOVERED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_HOVERED)) { + hover_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_HOVERED)) { + hover_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_HOVERED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_HOVERED)) { + hover_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_HOVERED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(hover_spec); + } else { + theme_by_name[selector].push_back(hover_spec); + } + } + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED) + || selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED) + || selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + StyleSpec press_spec; + press_spec.addState(StyleSpec::STATE_PRESSED); + + if (selector_spec.hasProperty(StyleSpec::BGCOLOR_PRESSED)) { + press_spec.set(StyleSpec::BGCOLOR, selector_spec.get(StyleSpec::BGCOLOR_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::BGIMG_PRESSED)) { + press_spec.set(StyleSpec::BGIMG, selector_spec.get(StyleSpec::BGIMG_PRESSED, "")); + } + if (selector_spec.hasProperty(StyleSpec::FGIMG_PRESSED)) { + press_spec.set(StyleSpec::FGIMG, selector_spec.get(StyleSpec::FGIMG_PRESSED, "")); + } + + if (style_type) { + theme_by_type[selector].push_back(press_spec); + } else { + theme_by_name[selector].push_back(press_spec); + } } } @@ -3080,7 +3146,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2 ); const wchar_t *text = wgettext("Proceed"); - GUIButton::addButton(Environment, mydata.rect, this, 257, text); + GUIButton::addButton(Environment, mydata.rect, m_tsrc, this, 257, text); delete[] text; } } @@ -4432,25 +4498,34 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) return L""; } -StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type, +StyleSpec GUIFormSpecMenu::getDefaultStyleForElement(const std::string &type, const std::string &name, const std::string &parent_type) { - StyleSpec ret; + return getStyleForElement(type, name, parent_type)[StyleSpec::STATE_DEFAULT]; +} + +std::array GUIFormSpecMenu::getStyleForElement(const std::string &type, + const std::string &name, const std::string &parent_type) +{ + std::array ret; if (!parent_type.empty()) { auto it = theme_by_type.find(parent_type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } } auto it = theme_by_type.find(type); if (it != theme_by_type.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } it = theme_by_name.find(name); if (it != theme_by_name.end()) { - ret |= it->second; + for (const StyleSpec &spec : it->second) + ret[(u32)spec.getState()] |= spec; } return ret; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 17bfef205..b3bf67110 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -274,11 +274,13 @@ protected: v2s32 getRealCoordinateBasePos(const std::vector &v_pos); v2s32 getRealCoordinateGeometry(const std::vector &v_geom); - std::unordered_map theme_by_type; - std::unordered_map theme_by_name; + std::unordered_map> theme_by_type; + std::unordered_map> theme_by_name; std::unordered_set property_warned; - StyleSpec getStyleForElement(const std::string &type, + StyleSpec getDefaultStyleForElement(const std::string &type, + const std::string &name="", const std::string &parent_type=""); + std::array getStyleForElement(const std::string &type, const std::string &name="", const std::string &parent_type=""); v2s32 padding; diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 3f270fc7a..da0e25c23 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -82,8 +82,10 @@ enum }; GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) : -GUIModalMenu(env, parent, id, menumgr) + gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, + ISimpleTextureSource *tsrc) : + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { init_keys(); } @@ -157,7 +159,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s); const wchar_t *text = wgettext(k->key.name()); - k->button = GUIButton::addButton(Environment, rect, this, k->id, text); + k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id, text); delete[] text; } if ((i + 1) % KMaxButtonPerColumns == 0) { @@ -217,14 +219,14 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 - 105 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Save"); - GUIButton::addButton(Environment, rect, this, GUI_ID_BACK_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_BACK_BUTTON, text); delete[] text; } { core::rect rect(0, 0, 100 * s, 30 * s); rect += topleft + v2s32(size.X / 2 + 5 * s, size.Y - 40 * s); const wchar_t *text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, GUI_ID_ABORT_BUTTON, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, GUI_ID_ABORT_BUTTON, text); delete[] text; } } diff --git a/src/gui/guiKeyChangeMenu.h b/src/gui/guiKeyChangeMenu.h index 528827fd9..1c0f40247 100644 --- a/src/gui/guiKeyChangeMenu.h +++ b/src/gui/guiKeyChangeMenu.h @@ -28,6 +28,8 @@ #include #include +class ISimpleTextureSource; + struct key_setting { int id; @@ -41,7 +43,7 @@ class GUIKeyChangeMenu : public GUIModalMenu { public: GUIKeyChangeMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIKeyChangeMenu(); void removeChildren(); @@ -74,4 +76,5 @@ private: key_setting *active_key = nullptr; gui::IGUIStaticText *key_used_text = nullptr; std::vector key_settings; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index af91ce84c..965a2d6f7 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -38,10 +38,12 @@ const int ID_cancel = 261; GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - Client* client + Client* client, + ISimpleTextureSource *tsrc ): GUIModalMenu(env, parent, id, menumgr), - m_client(client) + m_client(client), + m_tsrc(tsrc) { } @@ -146,14 +148,14 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 56 * s, ypos); text = wgettext("Change"); - GUIButton::addButton(Environment, rect, this, ID_change, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_change, text); delete[] text; } { core::rect rect(0, 0, 100 * s, 30 * s); rect = rect + v2s32(size.X / 4 + 185 * s, ypos); text = wgettext("Cancel"); - GUIButton::addButton(Environment, rect, this, ID_cancel, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_cancel, text); delete[] text; } diff --git a/src/gui/guiPasswordChange.h b/src/gui/guiPasswordChange.h index 58541a399..7141100c0 100644 --- a/src/gui/guiPasswordChange.h +++ b/src/gui/guiPasswordChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include class Client; +class ISimpleTextureSource; class GUIPasswordChange : public GUIModalMenu { public: GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - IMenuManager *menumgr, Client *client); + IMenuManager *menumgr, Client *client, + ISimpleTextureSource *tsrc); ~GUIPasswordChange(); void removeChildren(); @@ -57,4 +59,5 @@ private: std::wstring m_oldpass = L""; std::wstring m_newpass = L""; std::wstring m_newpass_confirm = L""; + ISimpleTextureSource *m_tsrc; }; diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp index 9428cde83..07b11248c 100644 --- a/src/gui/guiVolumeChange.cpp +++ b/src/gui/guiVolumeChange.cpp @@ -38,9 +38,10 @@ const int ID_soundMuteButton = 266; GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr + IMenuManager *menumgr, ISimpleTextureSource *tsrc ): - GUIModalMenu(env, parent, id, menumgr) + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) { } @@ -104,7 +105,7 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize) core::rect rect(0, 0, 80 * s, 30 * s); rect = rect + v2s32(size.X / 2 - 80 * s / 2, size.Y / 2 + 55 * s); const wchar_t *text = wgettext("Exit"); - GUIButton::addButton(Environment, rect, this, ID_soundExitButton, text); + GUIButton::addButton(Environment, rect, m_tsrc, this, ID_soundExitButton, text); delete[] text; } { diff --git a/src/gui/guiVolumeChange.h b/src/gui/guiVolumeChange.h index f4f4e9eea..466e17f9d 100644 --- a/src/gui/guiVolumeChange.h +++ b/src/gui/guiVolumeChange.h @@ -23,12 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "modalMenu.h" #include +class ISimpleTextureSource; + class GUIVolumeChange : public GUIModalMenu { public: GUIVolumeChange(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr); + IMenuManager *menumgr, ISimpleTextureSource *tsrc); ~GUIVolumeChange(); void removeChildren(); @@ -46,4 +48,7 @@ public: protected: std::wstring getLabelByID(s32 id) { return L""; } std::string getNameByID(s32 id) { return ""; } + +private: + ISimpleTextureSource *m_tsrc; }; diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 76db7ed13..867e84e13 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -571,9 +571,10 @@ int ModApiMainMenu::l_show_keys_menu(lua_State *L) sanity_check(engine != NULL); GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager); + engine->m_parent, + -1, + engine->m_menumanager, + engine->m_texture_source); kmenu->drop(); return 0; } @@ -904,12 +905,12 @@ int ModApiMainMenu::l_show_path_select_dialog(lua_State *L) GUIFileSelectMenu* fileOpenMenu = new GUIFileSelectMenu(RenderingEngine::get_gui_env(), - engine->m_parent, - -1, - engine->m_menumanager, - title, - formname, - is_file_select); + engine->m_parent, + -1, + engine->m_menumanager, + title, + formname, + is_file_select); fileOpenMenu->setTextDest(engine->m_buttonhandler); fileOpenMenu->drop(); return 0; diff --git a/src/util/numeric.h b/src/util/numeric.h index 6f82a18c1..864ab7543 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v2d.h" #include "irr_v3d.h" #include "irr_aabb3d.h" +#include "SColor.h" #include #define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d))) @@ -432,3 +433,12 @@ inline v3f getPitchYawRoll(const core::matrix4 &m) { return getPitchYawRollRad(m) * core::RADTODEG64; } + +// Muliply the RGB value of a color linearly, and clamp to black/white +inline irr::video::SColor multiplyColorValue(const irr::video::SColor &color, float mod) +{ + return irr::video::SColor(color.getAlpha(), + core::clamp(color.getRed() * mod, 0, 255), + core::clamp(color.getGreen() * mod, 0, 255), + core::clamp(color.getBlue() * mod, 0, 255)); +} -- cgit v1.2.3 From 2d5bd3bf794672285cfc796edebab2f672f2cab0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Apr 2020 20:44:18 +0200 Subject: scriptapi: Some small optimizations to value pushing (#9669) --- src/script/common/c_content.cpp | 34 ++++++++++++++++----------------- src/script/common/c_converter.cpp | 40 +++++++++++++++++++-------------------- src/script/lua_api/l_env.cpp | 8 ++++---- src/script/lua_api/l_mapgen.cpp | 15 +++++++-------- src/script/lua_api/l_noise.cpp | 14 +++++++------- src/script/lua_api/l_object.cpp | 9 +-------- src/script/lua_api/l_vmanip.cpp | 6 +++--- 7 files changed, 59 insertions(+), 67 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ff9ceec6d..d73ecedab 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -350,7 +350,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) push_v3f(L, prop->visual_size); lua_setfield(L, -2, "visual_size"); - lua_newtable(L); + lua_createtable(L, prop->textures.size(), 0); u16 i = 1; for (const std::string &texture : prop->textures) { lua_pushlstring(L, texture.c_str(), texture.size()); @@ -358,7 +358,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) } lua_setfield(L, -2, "textures"); - lua_newtable(L); + lua_createtable(L, prop->colors.size(), 0); i = 1; for (const video::SColor &color : prop->colors) { push_ARGB8(L, color); @@ -840,7 +840,7 @@ void push_content_features(lua_State *L, const ContentFeatures &c) lua_pushnumber(L, c.connect_sides); lua_setfield(L, -2, "connect_sides"); - lua_newtable(L); + lua_createtable(L, c.connects_to.size(), 0); u16 i = 1; for (const std::string &it : c.connects_to) { lua_pushlstring(L, it.c_str(), it.size()); @@ -963,7 +963,7 @@ void push_nodebox(lua_State *L, const NodeBox &box) void push_box(lua_State *L, const std::vector &box) { - lua_newtable(L); + lua_createtable(L, box.size(), 0); u8 i = 1; for (const aabb3f &it : box) { push_aabb3f(L, it); @@ -1040,7 +1040,7 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) void push_soundspec(lua_State *L, const SimpleSoundSpec &spec) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushstring(L, spec.name.c_str()); lua_setfield(L, -2, "name"); lua_pushnumber(L, spec.gain); @@ -1125,12 +1125,12 @@ MapNode readnode(lua_State *L, int index, const NodeDefManager *ndef) /******************************************************************************/ void pushnode(lua_State *L, const MapNode &n, const NodeDefManager *ndef) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushstring(L, ndef->get(n).name.c_str()); lua_setfield(L, -2, "name"); - lua_pushnumber(L, n.getParam1()); + lua_pushinteger(L, n.getParam1()); lua_setfield(L, -2, "param1"); - lua_pushnumber(L, n.getParam2()); + lua_pushinteger(L, n.getParam2()); lua_setfield(L, -2, "param2"); } @@ -1163,7 +1163,7 @@ bool string_to_enum(const EnumString *spec, int &result, { const EnumString *esp = spec; while(esp->str){ - if(str == std::string(esp->str)){ + if (!strcmp(str.c_str(), esp->str)) { result = esp->num; return true; } @@ -1438,7 +1438,7 @@ ToolCapabilities read_tool_capabilities( /******************************************************************************/ void push_dig_params(lua_State *L,const DigParams ¶ms) { - lua_newtable(L); + lua_createtable(L, 0, 3); setboolfield(L, -1, "diggable", params.diggable); setfloatfield(L, -1, "time", params.time); setintfield(L, -1, "wear", params.wear); @@ -1447,7 +1447,7 @@ void push_dig_params(lua_State *L,const DigParams ¶ms) /******************************************************************************/ void push_hit_params(lua_State *L,const HitParams ¶ms) { - lua_newtable(L); + lua_createtable(L, 0, 3); setintfield(L, -1, "hp", params.hp); setintfield(L, -1, "wear", params.wear); } @@ -1540,9 +1540,9 @@ void read_groups(lua_State *L, int index, ItemGroupList &result) /******************************************************************************/ void push_groups(lua_State *L, const ItemGroupList &groups) { - lua_newtable(L); + lua_createtable(L, 0, groups.size()); for (const auto &group : groups) { - lua_pushnumber(L, group.second); + lua_pushinteger(L, group.second); lua_setfield(L, -2, group.first.c_str()); } } @@ -1587,7 +1587,7 @@ void luaentity_get(lua_State *L, u16 id) lua_getglobal(L, "core"); lua_getfield(L, -1, "luaentities"); luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); + lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // Remove luaentities lua_remove(L, -2); // Remove core @@ -1689,7 +1689,7 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, lua_pushboolean(L, value.asInt()); break; case Json::arrayValue: - lua_newtable(L); + lua_createtable(L, value.size(), 0); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { push_json_value_helper(L, *it, nullindex); @@ -1697,7 +1697,7 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, } break; case Json::objectValue: - lua_newtable(L); + lua_createtable(L, 0, value.size()); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { #ifndef JSONCPP_STRING @@ -1824,7 +1824,7 @@ void push_objectRef(lua_State *L, const u16 id) lua_getglobal(L, "core"); lua_getfield(L, -1, "object_refs"); luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); + lua_pushinteger(L, id); lua_gettable(L, -2); lua_remove(L, -2); // object_refs lua_remove(L, -2); // core diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 3c2f75641..334af61c3 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -62,7 +62,7 @@ void push_float_string(lua_State *L, float value) void push_v3f(lua_State *L, v3f p) { - lua_newtable(L); + lua_createtable(L, 0, 3); lua_pushnumber(L, p.X); lua_setfield(L, -2, "x"); lua_pushnumber(L, p.Y); @@ -73,7 +73,7 @@ void push_v3f(lua_State *L, v3f p) void push_v2f(lua_State *L, v2f p) { - lua_newtable(L); + lua_createtable(L, 0, 2); lua_pushnumber(L, p.X); lua_setfield(L, -2, "x"); lua_pushnumber(L, p.Y); @@ -82,7 +82,7 @@ void push_v2f(lua_State *L, v2f p) void push_v3_float_string(lua_State *L, v3f p) { - lua_newtable(L); + lua_createtable(L, 0, 3); push_float_string(L, p.X); lua_setfield(L, -2, "x"); push_float_string(L, p.Y); @@ -93,7 +93,7 @@ void push_v3_float_string(lua_State *L, v3f p) void push_v2_float_string(lua_State *L, v2f p) { - lua_newtable(L); + lua_createtable(L, 0, 2); push_float_string(L, p.X); lua_setfield(L, -2, "x"); push_float_string(L, p.Y); @@ -115,19 +115,19 @@ v2s16 read_v2s16(lua_State *L, int index) void push_v2s16(lua_State *L, v2s16 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 2); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); } void push_v2s32(lua_State *L, v2s32 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 2); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); } @@ -250,14 +250,14 @@ v3d check_v3d(lua_State *L, int index) void push_ARGB8(lua_State *L, video::SColor color) { - lua_newtable(L); - lua_pushnumber(L, color.getAlpha()); + lua_createtable(L, 0, 4); + lua_pushinteger(L, color.getAlpha()); lua_setfield(L, -2, "a"); - lua_pushnumber(L, color.getRed()); + lua_pushinteger(L, color.getRed()); lua_setfield(L, -2, "r"); - lua_pushnumber(L, color.getGreen()); + lua_pushinteger(L, color.getGreen()); lua_setfield(L, -2, "g"); - lua_pushnumber(L, color.getBlue()); + lua_pushinteger(L, color.getBlue()); lua_setfield(L, -2, "b"); } @@ -274,12 +274,12 @@ v3f checkFloatPos(lua_State *L, int index) void push_v3s16(lua_State *L, v3s16 p) { - lua_newtable(L); - lua_pushnumber(L, p.X); + lua_createtable(L, 0, 3); + lua_pushinteger(L, p.X); lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); + lua_pushinteger(L, p.Y); lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); + lua_pushinteger(L, p.Z); lua_setfield(L, -2, "z"); } @@ -386,7 +386,7 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale) void push_aabb3f(lua_State *L, aabb3f box) { - lua_newtable(L); + lua_createtable(L, 6, 0); lua_pushnumber(L, box.MinEdge.X); lua_rawseti(L, -2, 1); lua_pushnumber(L, box.MinEdge.Y); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index e3afe1862..8c45a1510 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -73,7 +73,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, lua_remove(L, -2); // Remove core // Get registered_abms[m_id] - lua_pushnumber(L, m_id); + lua_pushinteger(L, m_id); lua_gettable(L, -2); if(lua_isnil(L, -1)) FATAL_ERROR(""); @@ -116,7 +116,7 @@ void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) lua_remove(L, -2); // Remove core // Get registered_lbms[m_id] - lua_pushnumber(L, m_id); + lua_pushinteger(L, m_id); lua_gettable(L, -2); FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); lua_remove(L, -2); // Remove registered_lbms @@ -550,7 +550,7 @@ int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L) std::vector positions = env->getMap().findNodesWithMetadata( check_v3s16(L, 1), check_v3s16(L, 2)); - lua_newtable(L); + lua_createtable(L, positions.size(), 0); for (size_t i = 0; i != positions.size(); i++) { push_v3s16(L, positions[i]); lua_rawseti(L, -2, i + 1); @@ -1197,7 +1197,7 @@ int ModApiEnvMod::l_find_path(lua_State *L) searchdistance, max_jump, max_drop, algo); if (!path.empty()) { - lua_newtable(L); + lua_createtable(L, path.size(), 0); int top = lua_gettop(L); unsigned int index = 1; for (const v3s16 &i : path) { diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index cb0d6ac95..afe77826a 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -710,7 +710,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) if (!mg->heightmap) return 0; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushinteger(L, mg->heightmap[i]); lua_rawseti(L, -2, i + 1); @@ -722,7 +722,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) if (!mg->biomegen) return 0; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushinteger(L, mg->biomegen->biomemap[i]); lua_rawseti(L, -2, i + 1); @@ -736,7 +736,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, bg->heatmap[i]); lua_rawseti(L, -2, i + 1); @@ -751,7 +751,7 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, bg->humidmap[i]); lua_rawseti(L, -2, i + 1); @@ -761,13 +761,12 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) } case MGOBJ_GENNOTIFY: { std::map >event_map; - std::map >::iterator it; mg->gennotify.getEvents(event_map); - lua_newtable(L); - for (it = event_map.begin(); it != event_map.end(); ++it) { - lua_newtable(L); + lua_createtable(L, 0, event_map.size()); + for (auto it = event_map.begin(); it != event_map.end(); ++it) { + lua_createtable(L, it->second.size(), 0); for (size_t j = 0; j != it->second.size(); j++) { push_v3s16(L, it->second[j]); diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index e38d319f4..9aeb15709 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -171,9 +171,9 @@ int LuaPerlinNoiseMap::l_get_2d_map(lua_State *L) Noise *n = o->noise; n->perlinMap2D(p.X, p.Y); - lua_newtable(L); + lua_createtable(L, n->sy, 0); for (u32 y = 0; y != n->sy; y++) { - lua_newtable(L); + lua_createtable(L, n->sx, 0); for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); @@ -200,7 +200,7 @@ int LuaPerlinNoiseMap::l_get_2d_map_flat(lua_State *L) if (use_buffer) lua_pushvalue(L, 3); else - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); @@ -224,11 +224,11 @@ int LuaPerlinNoiseMap::l_get_3d_map(lua_State *L) Noise *n = o->noise; n->perlinMap3D(p.X, p.Y, p.Z); - lua_newtable(L); + lua_createtable(L, n->sz, 0); for (u32 z = 0; z != n->sz; z++) { - lua_newtable(L); + lua_createtable(L, n->sy, 0); for (u32 y = 0; y != n->sy; y++) { - lua_newtable(L); + lua_createtable(L, n->sx, 0); for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); @@ -260,7 +260,7 @@ int LuaPerlinNoiseMap::l_get_3d_map_flat(lua_State *L) if (use_buffer) lua_pushvalue(L, 3); else - lua_newtable(L); + lua_createtable(L, maplen, 0); for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index fa34260bf..bb1456ac9 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -123,14 +123,7 @@ int ObjectRef::l_get_pos(lua_State *L) ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; - v3f pos = co->getBasePosition() / BS; - lua_newtable(L); - lua_pushnumber(L, pos.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, pos.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, pos.Z); - lua_setfield(L, -2, "z"); + push_v3f(L, co->getBasePosition() / BS); return 1; } diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index fd73d21d1..b99b1d98c 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -72,7 +72,7 @@ int LuaVoxelManip::l_get_data(lua_State *L) if (use_buffer) lua_pushvalue(L, 2); else - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer cid = vm->m_data[i].getContent(); @@ -261,7 +261,7 @@ int LuaVoxelManip::l_get_light_data(lua_State *L) u32 volume = vm->m_area.getVolume(); - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer light = vm->m_data[i].param1; lua_pushinteger(L, light); @@ -309,7 +309,7 @@ int LuaVoxelManip::l_get_param2_data(lua_State *L) if (use_buffer) lua_pushvalue(L, 2); else - lua_newtable(L); + lua_createtable(L, volume, 0); for (u32 i = 0; i != volume; i++) { lua_Integer param2 = vm->m_data[i].param2; -- cgit v1.2.3 From e8ac5a31cf12afcfddf8e3ed31e8038930edb06f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 16 Apr 2020 08:25:48 +0200 Subject: Optimize get_objects_inside_radius calls (#9671) * Optimize getObjectsInsideRadius calls our previous implementation calls the ActiveObjectMgr to return ids and then lookup those ids in the same map and test each object Instead now we call the global map to return the pointers directly and we ask filtering when building the list using lamba. This drop double looping over ranges of active objects (and then filtered one) and drop x lookups on the map regarding the first call results --- src/collision.cpp | 20 +++++++++++--------- src/script/lua_api/l_env.cpp | 24 ++++++++++++------------ src/server/activeobjectmgr.cpp | 10 ++++++---- src/server/activeobjectmgr.h | 5 +++-- src/serverenvironment.cpp | 12 +++++------- src/serverenvironment.h | 5 +++-- src/unittest/test_serveractiveobjectmgr.cpp | 18 +++++++++++++++--- 7 files changed, 55 insertions(+), 39 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/collision.cpp b/src/collision.cpp index d9fbd3202..6d24bc699 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -360,17 +360,19 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Calculate distance by speed, add own extent and 1.5m of tolerance f32 distance = speed_f->getLength() * dtime + box_0.getExtent().getLength() + 1.5f * BS; - std::vector s_objects; - s_env->getObjectsInsideRadius(s_objects, *pos_f, distance); - for (u16 obj_id : s_objects) { - ServerActiveObject *current = s_env->getActiveObject(obj_id); - - if (!self || (self != current && - self != current->getParent())) { - objects.push_back((ActiveObject*)current); + // search for objects which are not us, or we are not its parent + // we directly use the callback to populate the result to prevent + // a useless result loop here + auto include_obj_cb = [self, &objects] (ServerActiveObject *obj) { + if (!self || (self != obj && self != obj->getParent())) { + objects.push_back((ActiveObject *)obj); } - } + return false; + }; + + std::vector s_objects; + s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb); } } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 8c45a1510..831464d3b 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -681,22 +681,22 @@ int ModApiEnvMod::l_get_player_by_name(lua_State *L) int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) { GET_ENV_PTR; + ScriptApiBase *script = getScriptApiBase(L); // Do it v3f pos = checkFloatPos(L, 1); float radius = readParam(L, 2) * BS; - std::vector ids; - env->getObjectsInsideRadius(ids, pos, radius); - ScriptApiBase *script = getScriptApiBase(L); - lua_createtable(L, ids.size(), 0); - std::vector::const_iterator iter = ids.begin(); - for(u32 i = 0; iter != ids.end(); ++iter) { - ServerActiveObject *obj = env->getActiveObject(*iter); - if (!obj->isGone()) { - // Insert object reference into table - script->objectrefGetOrCreate(L, obj); - lua_rawseti(L, -2, ++i); - } + std::vector objs; + + auto include_obj_cb = [](ServerActiveObject *obj){ return !obj->isGone(); }; + env->getObjectsInsideRadius(objs, pos, radius, include_obj_cb); + + int i = 0; + lua_createtable(L, objs.size(), 0); + for (const auto obj : objs) { + // Insert object reference into table + script->objectrefGetOrCreate(L, obj); + lua_rawseti(L, -2, ++i); } return 1; } diff --git a/src/server/activeobjectmgr.cpp b/src/server/activeobjectmgr.cpp index 984ae7794..1b8e31409 100644 --- a/src/server/activeobjectmgr.cpp +++ b/src/server/activeobjectmgr.cpp @@ -111,17 +111,19 @@ void ActiveObjectMgr::removeObject(u16 id) } // clang-format on -void ActiveObjectMgr::getObjectsInsideRadius( - const v3f &pos, float radius, std::vector &result) +void ActiveObjectMgr::getObjectsInsideRadius(const v3f &pos, float radius, + std::vector &result, + std::function include_obj_cb) { float r2 = radius * radius; for (auto &activeObject : m_active_objects) { ServerActiveObject *obj = activeObject.second; - u16 id = activeObject.first; const v3f &objectpos = obj->getBasePosition(); if (objectpos.getDistanceFromSQ(pos) > r2) continue; - result.push_back(id); + + if (!include_obj_cb || include_obj_cb(obj)) + result.push_back(obj); } } diff --git a/src/server/activeobjectmgr.h b/src/server/activeobjectmgr.h index 5fea1bea6..bc2085499 100644 --- a/src/server/activeobjectmgr.h +++ b/src/server/activeobjectmgr.h @@ -35,8 +35,9 @@ public: bool registerObject(ServerActiveObject *obj) override; void removeObject(u16 id) override; - void getObjectsInsideRadius( - const v3f &pos, float radius, std::vector &result); + void getObjectsInsideRadius(const v3f &pos, float radius, + std::vector &result, + std::function include_obj_cb); void getAddedActiveObjectsAroundPos(const v3f &player_pos, f32 radius, f32 player_radius, std::set ¤t_objects, diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 739384673..27f0c1e3d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1608,14 +1608,12 @@ void ServerEnvironment::getSelectedActiveObjects( const core::line3d &shootline_on_map, std::vector &objects) { - std::vector objectIds; - getObjectsInsideRadius(objectIds, shootline_on_map.start, - shootline_on_map.getLength() + 10.0f); + std::vector objs; + getObjectsInsideRadius(objs, shootline_on_map.start, + shootline_on_map.getLength() + 10.0f, nullptr); const v3f line_vector = shootline_on_map.getVector(); - for (u16 objectId : objectIds) { - ServerActiveObject* obj = getActiveObject(objectId); - + for (auto obj : objs) { aabb3f selection_box; if (!obj->getSelectionBox(&selection_box)) continue; @@ -1630,7 +1628,7 @@ void ServerEnvironment::getSelectedActiveObjects( if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, ¤t_intersection, ¤t_normal)) { objects.emplace_back( - (s16) objectId, current_intersection, current_normal, + (s16) obj->getId(), current_intersection, current_normal, (current_intersection - shootline_on_map.start).getLengthSQ()); } } diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 55ecbd05f..f814b95c0 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -323,9 +323,10 @@ public: bool swapNode(v3s16 p, const MapNode &n); // Find all active objects inside a radius around a point - void getObjectsInsideRadius(std::vector &objects, const v3f &pos, float radius) + void getObjectsInsideRadius(std::vector &objects, const v3f &pos, float radius, + std::function include_obj_cb) { - return m_ao_manager.getObjectsInsideRadius(pos, radius, objects); + return m_ao_manager.getObjectsInsideRadius(pos, radius, objects, include_obj_cb); } // Clear objects, loading and going through every MapBlock diff --git a/src/unittest/test_serveractiveobjectmgr.cpp b/src/unittest/test_serveractiveobjectmgr.cpp index 0806972ab..aa0047400 100644 --- a/src/unittest/test_serveractiveobjectmgr.cpp +++ b/src/unittest/test_serveractiveobjectmgr.cpp @@ -148,14 +148,26 @@ void TestServerActiveObjectMgr::testGetObjectsInsideRadius() saomgr.registerObject(new TestServerActiveObject(p)); } - std::vector result; - saomgr.getObjectsInsideRadius(v3f(), 50, result); + std::vector result; + saomgr.getObjectsInsideRadius(v3f(), 50, result, nullptr); UASSERTCMP(int, ==, result.size(), 1); result.clear(); - saomgr.getObjectsInsideRadius(v3f(), 750, result); + saomgr.getObjectsInsideRadius(v3f(), 750, result, nullptr); UASSERTCMP(int, ==, result.size(), 2); + result.clear(); + saomgr.getObjectsInsideRadius(v3f(), 750000, result, nullptr); + UASSERTCMP(int, ==, result.size(), 5); + + result.clear(); + auto include_obj_cb = [](ServerActiveObject *obj) { + return (obj->getBasePosition().X != 10); + }; + + saomgr.getObjectsInsideRadius(v3f(), 750000, result, include_obj_cb); + UASSERTCMP(int, ==, result.size(), 4); + clearSAOMgr(&saomgr); } -- cgit v1.2.3 From 87829cd7446dd13d1dfd27d96e0b4aeb2f234e33 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 18 Apr 2020 17:21:58 +0200 Subject: script: Move SAO usability check so that it covers all functions (#9698) see also 91eef646a59575bd9ae792e257bb6ad12fafc0b1 --- src/script/lua_api/l_object.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index bb1456ac9..d7afb84da 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -50,6 +50,8 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) { ServerActiveObject *co = ref->m_object; + if (co && co->isGone()) + return NULL; return co; } @@ -60,8 +62,6 @@ LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) return NULL; if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) return NULL; - if (obj->isGone()) - return NULL; return (LuaEntitySAO*)obj; } @@ -72,8 +72,6 @@ PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) return NULL; if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) return NULL; - if (obj->isGone()) - return NULL; return (PlayerSAO*)obj; } @@ -132,7 +130,6 @@ int ObjectRef::l_set_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; // pos @@ -147,7 +144,6 @@ int ObjectRef::l_move_to(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; // pos @@ -1102,17 +1098,13 @@ int ObjectRef::l_add_player_velocity(lua_State *L) ObjectRef *ref = checkobject(L, 1); v3f vel = checkFloatPos(L, 2); - RemotePlayer *player = getplayer(ref); PlayerSAO *co = getplayersao(ref); - if (!player || !co) + if (!co) return 0; - session_t peer_id = player->getPeerId(); - if (peer_id == PEER_ID_INEXISTENT) - return 0; // Do it co->setMaxSpeedOverride(vel); - getServer(L)->SendPlayerSpeed(peer_id, vel); + getServer(L)->SendPlayerSpeed(co->getPeerID(), vel); return 0; } -- cgit v1.2.3 From 338195ff250bd7552ef8167348de2eb05e421c29 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 19 Apr 2020 19:07:54 +0200 Subject: Fix alias handling of get_content_id (#9712) fixes #9632 --- src/itemdef.cpp | 34 +++++++++++++--------------------- src/script/lua_api/l_item.cpp | 13 ++++++++++++- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/itemdef.cpp b/src/itemdef.cpp index a13b3f7d4..8e0492827 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -270,17 +270,16 @@ public: // Convert name according to possible alias std::string name = getAlias(name_); // Get the definition - std::map::const_iterator i; - i = m_item_definitions.find(name); - if(i == m_item_definitions.end()) + auto i = m_item_definitions.find(name); + if (i == m_item_definitions.cend()) i = m_item_definitions.find("unknown"); - assert(i != m_item_definitions.end()); + assert(i != m_item_definitions.cend()); return *(i->second); } virtual const std::string &getAlias(const std::string &name) const { - StringMap::const_iterator it = m_aliases.find(name); - if (it != m_aliases.end()) + auto it = m_aliases.find(name); + if (it != m_aliases.cend()) return it->second; return name; } @@ -300,8 +299,7 @@ public: // Convert name according to possible alias std::string name = getAlias(name_); // Get the definition - std::map::const_iterator i; - return m_item_definitions.find(name) != m_item_definitions.end(); + return m_item_definitions.find(name) != m_item_definitions.cend(); } #ifndef SERVER public: @@ -443,11 +441,9 @@ public: } void clear() { - for(std::map::const_iterator - i = m_item_definitions.begin(); - i != m_item_definitions.end(); ++i) + for (auto &i : m_item_definitions) { - delete i->second; + delete i.second; } m_item_definitions.clear(); m_aliases.clear(); @@ -520,10 +516,8 @@ public: u16 count = m_item_definitions.size(); writeU16(os, count); - for (std::map::const_iterator - it = m_item_definitions.begin(); - it != m_item_definitions.end(); ++it) { - ItemDefinition *def = it->second; + for (const auto &it : m_item_definitions) { + ItemDefinition *def = it.second; // Serialize ItemDefinition and write wrapped in a string std::ostringstream tmp_os(std::ios::binary); def->serialize(tmp_os, protocol_version); @@ -532,11 +526,9 @@ public: writeU16(os, m_aliases.size()); - for (StringMap::const_iterator - it = m_aliases.begin(); - it != m_aliases.end(); ++it) { - os << serializeString(it->first); - os << serializeString(it->second); + for (const auto &it : m_aliases) { + os << serializeString(it.first); + os << serializeString(it.second); } } void deSerialize(std::istream &is) diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 9f12d3ac7..0a403acbd 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -609,10 +609,21 @@ int ModApiItemMod::l_get_content_id(lua_State *L) NO_MAP_LOCK_REQUIRED; std::string name = luaL_checkstring(L, 1); + const IItemDefManager *idef = getGameDef(L)->getItemDefManager(); const NodeDefManager *ndef = getGameDef(L)->getNodeDefManager(); + + // If this is called at mod load time, NodeDefManager isn't aware of + // aliases yet, so we need to handle them manually + std::string alias_name = idef->getAlias(name); + content_t content_id; - if (!ndef->getId(name, content_id)) + if (alias_name != name) { + if (!ndef->getId(alias_name, content_id)) + throw LuaError("Unknown node: " + alias_name + + " (from alias " + name + ")"); + } else if (!ndef->getId(name, content_id)) { throw LuaError("Unknown node: " + name); + } lua_pushinteger(L, content_id); return 1; /* number of results */ -- cgit v1.2.3 From 4361bfcb4da0b6f9de74c7de9f2d08084877713e Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Wed, 22 Apr 2020 00:07:12 +0200 Subject: Fix configuration caching in log_deprecated (#9697) * Fix configuration caching in log_deprecated The configured variable was never set to true. I've set the variables to thread_local because the configuration should be reloaded after reentering the world from mainmenu. --- src/script/common/c_internal.cpp | 9 +++++---- src/script/lua_api/l_util.cpp | 2 +- src/script/lua_api/l_util.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index b19af9f82..6df1f8b7b 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -157,9 +157,9 @@ static void script_log(lua_State *L, const std::string &message, void log_deprecated(lua_State *L, const std::string &message, int stack_depth) { - static bool configured = false; - static bool do_log = false; - static bool do_error = false; + static thread_local bool configured = false; + static thread_local bool do_log = false; + static thread_local bool do_error = false; // Only read settings on first call if (!configured) { @@ -167,9 +167,10 @@ void log_deprecated(lua_State *L, const std::string &message, int stack_depth) if (value == "log") { do_log = true; } else if (value == "error") { - do_log = true; + do_log = true; do_error = true; } + configured = true; } if (do_log) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index ae3e5df3d..28ee39fc8 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -44,7 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // log([level,] text) // Writes a line to the logger. -// The one-argument version logs to infostream. +// The one-argument version logs to LL_NONE. // The two-argument version accepts a log level. // Either the special case "deprecated" for deprecation notices, or any specified in // Logger::stringToLevel(name). diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 5697aab15..9ff91bb53 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -37,7 +37,7 @@ private: // log([level,] text) // Writes a line to the logger. - // The one-argument version logs to infostream. + // The one-argument version logs to LL_NONE. // The two-argument version accepts a log level. static int l_log(lua_State *L); -- cgit v1.2.3 From cee3c5e73d7af2a876aa76275234ee76e7cb1bbc Mon Sep 17 00:00:00 2001 From: EvidenceB Kidscode <49488517+EvidenceBKidscode@users.noreply.github.com> Date: Sat, 25 Apr 2020 07:20:00 +0200 Subject: Add server side translations capability (#9733) * Add server side translations capability --- doc/lua_api.txt | 15 ++++++++++++++ src/client/client.cpp | 2 +- src/client/game.cpp | 2 +- src/clientiface.h | 6 ++++++ src/network/serverpackethandler.cpp | 3 +++ src/script/lua_api/l_env.cpp | 16 +++++++++++++++ src/script/lua_api/l_env.h | 3 +++ src/script/lua_api/l_server.cpp | 7 ++++++- src/server.cpp | 22 +++++++++++++++++++- src/server.h | 5 ++++- src/translation.cpp | 13 ++++++++++-- src/translation.h | 5 ++++- src/util/string.cpp | 41 ++++++++++++++++++++++++++++--------- src/util/string.h | 4 ++++ 14 files changed, 126 insertions(+), 18 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 77f06682f..3ca32649a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3176,8 +3176,22 @@ Strings that need to be translated can contain several escapes, preceded by `@`. `minetest.translate`, but is in translation files. * `@n` acts as a literal newline as well. +Server side translations +------------------------ + +On some specific cases, server translation could be useful. For example, filter +a list on labels and send results to client. A method is supplied to achieve +that: + +`minetest.get_translated_string(lang_code, string)`: Translates `string` using +translations for `lang_code` language. It gives the same result as if the string +was translated by the client. +The `lang_code` to use for a given player can be retrieved from +the table returned by `minetest.get_player_information(name)`. +IMPORTANT: This functionality should only be used for sorting, filtering or similar purposes. +You do not need to use this to get translated strings to show up on the client. Perlin noise ============ @@ -4153,6 +4167,7 @@ Utilities connection_uptime = 200, -- seconds since client connected protocol_version = 32, -- protocol version used by client formspec_version = 2, -- supported formspec version + lang_code = "fr" -- Language code used for translation -- following information is available on debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client diff --git a/src/client/client.cpp b/src/client/client.cpp index 8ee0869cd..941fc203d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -736,7 +736,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) if (!name.empty()) { TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); - g_translations->loadTranslation(data); + g_client_translations->loadTranslation(data); return true; } diff --git a/src/client/game.cpp b/src/client/game.cpp index 3429cc57b..610522dc2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1055,7 +1055,7 @@ bool Game::startup(bool *kill, m_invert_mouse = g_settings->getBool("invert_mouse"); m_first_loop_after_window_activation = true; - g_translations->clear(); + g_client_translations->clear(); if (!init(map_dir, address, port, gamespec)) return false; diff --git a/src/clientiface.h b/src/clientiface.h index bf95df4a8..83fa6fe99 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -339,12 +339,18 @@ public: u8 getMinor() const { return m_version_minor; } u8 getPatch() const { return m_version_patch; } const std::string &getFull() const { return m_full_version; } + + void setLangCode(const std::string &code) { m_lang_code = code; } + const std::string &getLangCode() const { return m_lang_code; } private: // Version is stored in here after INIT before INIT2 u8 m_pending_serialization_version = SER_FMT_VER_INVALID; /* current state of client */ ClientState m_state = CS_Created; + + // Client sent language code + std::string m_lang_code; /* Blocks that have been sent to client. diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index c685500ce..5136eb0ec 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -311,6 +311,9 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) RemoteClient *client = getClient(peer_id, CS_InitDone); + // Keep client language for server translations + client->setLangCode(lang); + // Send active objects { PlayerSAO *sao = getPlayerSAO(peer_id); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 831464d3b..3fb58b8c8 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -40,6 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "remoteplayer.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "util/string.h" +#include "translation.h" #ifndef SERVER #include "client/client.h" #endif @@ -1302,6 +1304,19 @@ int ModApiEnvMod::l_forceload_free_block(lua_State *L) return 0; } +// get_translated_string(lang_code, string) +int ModApiEnvMod::l_get_translated_string(lua_State * L) +{ + GET_ENV_PTR; + std::string lang_code = luaL_checkstring(L, 1); + std::string string = luaL_checkstring(L, 2); + getServer(L)->loadTranslationLanguage(lang_code); + string = wide_to_utf8(translate_string(utf8_to_wide(string), + &(*g_server_translations)[lang_code])); + lua_pushstring(L, string.c_str()); + return 1; +} + void ModApiEnvMod::Initialize(lua_State *L, int top) { API_FCT(set_node); @@ -1349,6 +1364,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(transforming_liquid_add); API_FCT(forceload_block); API_FCT(forceload_free_block); + API_FCT(get_translated_string); } void ModApiEnvMod::InitializeClient(lua_State *L, int top) diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index ac2f8b588..9050b4306 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -187,6 +187,9 @@ private: // stops forceloading a position static int l_forceload_free_block(lua_State *L); + // Get a string translated server side + static int l_get_translated_string(lua_State * L); + public: static void Initialize(lua_State *L, int top); static void InitializeClient(lua_State *L, int top); diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 00e849cdf..7137484e8 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -163,6 +163,7 @@ int ModApiServer::l_get_player_information(lua_State *L) u16 prot_vers; u8 ser_vers,major,minor,patch; std::string vers_string; + std::string lang_code; #define ERET(code) \ if (!(code)) { \ @@ -182,7 +183,7 @@ int ModApiServer::l_get_player_information(lua_State *L) &avg_jitter)) ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers, - &prot_vers, &major, &minor, &patch, &vers_string)) + &prot_vers, &major, &minor, &patch, &vers_string, &lang_code)) lua_newtable(L); int table = lua_gettop(L); @@ -237,6 +238,10 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_pushnumber(L, player->formspec_version); lua_settable(L, table); + lua_pushstring(L, "lang_code"); + lua_pushstring(L, lang_code.c_str()); + lua_settable(L, table); + #ifndef NDEBUG lua_pushstring(L,"serialization_version"); lua_pushnumber(L, ser_vers); diff --git a/src/server.cpp b/src/server.cpp index c32aa5306..af6d3e40d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "translation.h" class ClientNotFoundException : public BaseException { @@ -1266,7 +1267,8 @@ bool Server::getClientInfo( u8* major, u8* minor, u8* patch, - std::string* vers_string + std::string* vers_string, + std::string* lang_code ) { *state = m_clients.getClientState(peer_id); @@ -1286,6 +1288,7 @@ bool Server::getClientInfo( *minor = client->getMinor(); *patch = client->getPatch(); *vers_string = client->getFull(); + *lang_code = client->getLangCode(); m_clients.unlock(); @@ -3937,3 +3940,20 @@ void Server::broadcastModChannelMessage(const std::string &channel, m_script->on_modchannel_message(channel, sender, message); } } + +void Server::loadTranslationLanguage(const std::string &lang_code) +{ + if (g_server_translations->count(lang_code)) + return; // Already loaded + + std::string suffix = "." + lang_code + ".tr"; + for (const auto &i : m_media) { + if (str_ends_with(i.first, suffix)) { + std::ifstream t(i.second.path); + std::string data((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + (*g_server_translations)[lang_code].loadTranslation(data); + } + } +} diff --git a/src/server.h b/src/server.h index eecc2c0f0..b995aba28 100644 --- a/src/server.h +++ b/src/server.h @@ -334,7 +334,7 @@ public: bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval); bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime, u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch, - std::string* vers_string); + std::string* vers_string, std::string* lang_code); void printToConsoleOnly(const std::string &text); @@ -358,6 +358,9 @@ public: // Send block to specific player only bool SendBlock(session_t peer_id, const v3s16 &blockpos); + // Load translations for a language + void loadTranslationLanguage(const std::string &lang_code); + // Bind address Address m_bind_addr; diff --git a/src/translation.cpp b/src/translation.cpp index d17467ce7..8bbaee0a3 100644 --- a/src/translation.cpp +++ b/src/translation.cpp @@ -20,9 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "translation.h" #include "log.h" #include "util/string.h" +#include -static Translations main_translations; -Translations *g_translations = &main_translations; + +#ifndef SERVER +// Client translations +Translations client_translations; +Translations *g_client_translations = &client_translations; +#endif + +// Per language server translations +std::unordered_map server_translations; +std::unordered_map *g_server_translations = &server_translations; Translations::~Translations() { diff --git a/src/translation.h b/src/translation.h index 18fc6c38f..71423b15e 100644 --- a/src/translation.h +++ b/src/translation.h @@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class Translations; -extern Translations *g_translations; +extern std::unordered_map *g_server_translations; +#ifndef SERVER +extern Translations *g_client_translations; +#endif class Translations { diff --git a/src/util/string.cpp b/src/util/string.cpp index 2ee3ec735..6e1db798c 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -693,10 +693,12 @@ void str_replace(std::string &str, char from, char to) * before filling it again. */ -void translate_all(const std::wstring &s, size_t &i, std::wstring &res); +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res); -void translate_string(const std::wstring &s, const std::wstring &textdomain, - size_t &i, std::wstring &res) { +void translate_string(const std::wstring &s, Translations *translations, + const std::wstring &textdomain, size_t &i, std::wstring &res) +{ std::wostringstream output; std::vector args; int arg_number = 1; @@ -750,7 +752,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, if (arg_number >= 10) { errorstream << "Ignoring too many arguments to translation" << std::endl; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); continue; } @@ -758,7 +760,7 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, output << arg_number; ++arg_number; std::wstring arg; - translate_all(s, i, arg); + translate_all(s, i, translations, arg); args.push_back(arg); } else { // This is an escape sequence *inside* the template string to translate itself. @@ -767,8 +769,13 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, } } + std::wstring toutput; // Translate the template. - std::wstring toutput = g_translations->getTranslation(textdomain, output.str()); + if (translations != nullptr) + toutput = translations->getTranslation( + textdomain, output.str()); + else + toutput = output.str(); // Put back the arguments in the translated template. std::wostringstream result; @@ -802,7 +809,9 @@ void translate_string(const std::wstring &s, const std::wstring &textdomain, res = result.str(); } -void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { +void translate_all(const std::wstring &s, size_t &i, + Translations *translations, std::wstring &res) +{ std::wostringstream output; while (i < s.length()) { // Not an escape sequence: just add the character. @@ -851,7 +860,7 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { if (parts.size() > 1) textdomain = parts[1]; std::wstring translated; - translate_string(s, textdomain, i, translated); + translate_string(s, translations, textdomain, i, translated); output << translated; } else { // Another escape sequence, such as colors. Preserve it. @@ -862,9 +871,21 @@ void translate_all(const std::wstring &s, size_t &i, std::wstring &res) { res = output.str(); } -std::wstring translate_string(const std::wstring &s) { +// Translate string server side +std::wstring translate_string(const std::wstring &s, Translations *translations) +{ size_t i = 0; std::wstring res; - translate_all(s, i, res); + translate_all(s, i, translations, res); return res; } + +// Translate string client side +std::wstring translate_string(const std::wstring &s) +{ +#ifdef SERVER + return translate_string(s, nullptr); +#else + return translate_string(s, g_client_translations); +#endif +} diff --git a/src/util/string.h b/src/util/string.h index 0d2a6bdb2..185fb55e2 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +class Translations; + #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -650,6 +652,8 @@ std::vector > split(const std::basic_string &s, T delim) return tokens; } +std::wstring translate_string(const std::wstring &s, Translations *translations); + std::wstring translate_string(const std::wstring &s); inline std::wstring unescape_translate(const std::wstring &s) { -- cgit v1.2.3 From f6f6dd140f14a40a43a63a1ba3eccc66fd56d514 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 26 Apr 2020 20:29:16 +0200 Subject: script: Fix add_entity returning unusable ref if object deleted in on_activate --- src/script/lua_api/l_env.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 3fb58b8c8..cabca124d 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -589,19 +589,19 @@ int ModApiEnvMod::l_add_entity(lua_State *L) { GET_ENV_PTR; - // pos v3f pos = checkFloatPos(L, 1); - // content const char *name = luaL_checkstring(L, 2); - // staticdata const char *staticdata = luaL_optstring(L, 3, ""); - // Do it + ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata); int objectid = env->addActiveObject(obj); // If failed to add, return nothing (reads as nil) if(objectid == 0) return 0; - // Return ObjectRef + + // If already deleted (can happen in on_activate), return nil + if (obj->isGone()) + return 0; getScriptApiBase(L)->objectrefGetOrCreate(L, obj); return 1; } -- cgit v1.2.3 From 8a03097450441cc7589507509bf755082a027cc7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 26 Apr 2020 20:57:27 +0200 Subject: script: Put getGuiEngine() inside a client-only #ifdef --- src/script/cpp_api/s_base.h | 4 ++++ src/script/lua_api/l_base.cpp | 2 ++ src/script/lua_api/l_base.h | 5 +++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 697e5f556..86f7f7bac 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -136,8 +136,10 @@ protected: Environment* getEnv() { return m_environment; } void setEnv(Environment* env) { m_environment = env; } +#ifndef SERVER GUIEngine* getGuiEngine() { return m_guiengine; } void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; } +#endif void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj); @@ -158,6 +160,8 @@ private: IGameDef *m_gamedef = nullptr; Environment *m_environment = nullptr; +#ifndef SERVER GUIEngine *m_guiengine = nullptr; +#endif ScriptingType m_type; }; diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index c980bba39..b8658f62b 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -62,10 +62,12 @@ Environment *ModApiBase::getEnv(lua_State *L) return getScriptApiBase(L)->getEnv(); } +#ifndef SERVER GUIEngine *ModApiBase::getGuiEngine(lua_State *L) { return getScriptApiBase(L)->getGuiEngine(); } +#endif std::string ModApiBase::getCurrentModPath(lua_State *L) { diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index b46b5b567..e32647628 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -32,12 +32,12 @@ extern "C" { #ifndef SERVER class Client; +class GUIEngine; #endif class ScriptApiBase; class Server; class Environment; -class GUIEngine; class ModApiBase : protected LuaHelper { @@ -46,12 +46,13 @@ public: static Server* getServer(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); + static GUIEngine* getGuiEngine(lua_State *L); #endif // !SERVER static IGameDef* getGameDef(lua_State *L); static Environment* getEnv(lua_State *L); - static GUIEngine* getGuiEngine(lua_State *L); + // When we are not loading the mod, this function returns "." static std::string getCurrentModPath(lua_State *L); -- cgit v1.2.3 From be71e70a91a3c857652a8b037dac7adf9d0fcdd1 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 27 Apr 2020 07:02:39 +0200 Subject: Script: Enforce type checks if not nil (#9748) * Script: Enforce type checks if not nil --- src/script/common/c_content.cpp | 76 ++++++++++++++++++++++---------------- src/script/common/c_converter.cpp | 32 ++++++++-------- src/script/common/c_converter.h | 6 +-- src/script/lua_api/l_inventory.cpp | 14 ++++--- 4 files changed, 72 insertions(+), 56 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 25dada757..8335fccb5 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -102,7 +102,8 @@ void read_item_definition(lua_State* L, int index, lua_pop(L, 1); lua_getfield(L, index, "sounds"); - if(lua_istable(L, -1)){ + if (!lua_isnil(L, -1)) { + luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "place"); read_soundspec(L, -1, def.sound_place); lua_pop(L, 1); @@ -182,9 +183,11 @@ void read_object_properties(lua_State *L, int index, { if(index < 0) index = lua_gettop(L) + 1 + index; - if(!lua_istable(L, index)) + if (lua_isnil(L, index)) return; + luaL_checktype(L, -1, LUA_TTABLE); + int hp_max = 0; if (getintfield(L, -1, "hp_max", hp_max)) { prop->hp_max = (u16)rangelim(hp_max, 0, U16_MAX); @@ -1027,13 +1030,15 @@ void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) { if(index < 0) index = lua_gettop(L) + 1 + index; - if(lua_isnil(L, index)){ - } else if(lua_istable(L, index)){ + if (lua_isnil(L, index)) + return; + + if (lua_istable(L, index)) { getstringfield(L, index, "name", spec.name); getfloatfield(L, index, "gain", spec.gain); getfloatfield(L, index, "fade", spec.fade); getfloatfield(L, index, "pitch", spec.pitch); - } else if(lua_isstring(L, index)){ + } else if (lua_isstring(L, index)) { spec.name = lua_tostring(L, index); } } @@ -1055,9 +1060,13 @@ void push_soundspec(lua_State *L, const SimpleSoundSpec &spec) NodeBox read_nodebox(lua_State *L, int index) { NodeBox nodebox; - if(lua_istable(L, -1)){ - nodebox.type = (NodeBoxType)getenumfield(L, index, "type", - ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); + if (lua_isnil(L, -1)) + return nodebox; + + luaL_checktype(L, -1, LUA_TTABLE); + + nodebox.type = (NodeBoxType)getenumfield(L, index, "type", + ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); #define NODEBOXREAD(n, s){ \ lua_getfield(L, index, (s)); \ @@ -1067,30 +1076,30 @@ NodeBox read_nodebox(lua_State *L, int index) } #define NODEBOXREADVEC(n, s) \ - lua_getfield(L, index, (s)); \ - if (lua_istable(L, -1)) \ - (n) = read_aabb3f_vector(L, -1, BS); \ - lua_pop(L, 1); + lua_getfield(L, index, (s)); \ + if (lua_istable(L, -1)) \ + (n) = read_aabb3f_vector(L, -1, BS); \ + lua_pop(L, 1); + + NODEBOXREADVEC(nodebox.fixed, "fixed"); + NODEBOXREAD(nodebox.wall_top, "wall_top"); + NODEBOXREAD(nodebox.wall_bottom, "wall_bottom"); + NODEBOXREAD(nodebox.wall_side, "wall_side"); + NODEBOXREADVEC(nodebox.connect_top, "connect_top"); + NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom"); + NODEBOXREADVEC(nodebox.connect_front, "connect_front"); + NODEBOXREADVEC(nodebox.connect_left, "connect_left"); + NODEBOXREADVEC(nodebox.connect_back, "connect_back"); + NODEBOXREADVEC(nodebox.connect_right, "connect_right"); + NODEBOXREADVEC(nodebox.disconnected_top, "disconnected_top"); + NODEBOXREADVEC(nodebox.disconnected_bottom, "disconnected_bottom"); + NODEBOXREADVEC(nodebox.disconnected_front, "disconnected_front"); + NODEBOXREADVEC(nodebox.disconnected_left, "disconnected_left"); + NODEBOXREADVEC(nodebox.disconnected_back, "disconnected_back"); + NODEBOXREADVEC(nodebox.disconnected_right, "disconnected_right"); + NODEBOXREADVEC(nodebox.disconnected, "disconnected"); + NODEBOXREADVEC(nodebox.disconnected_sides, "disconnected_sides"); - NODEBOXREADVEC(nodebox.fixed, "fixed"); - NODEBOXREAD(nodebox.wall_top, "wall_top"); - NODEBOXREAD(nodebox.wall_bottom, "wall_bottom"); - NODEBOXREAD(nodebox.wall_side, "wall_side"); - NODEBOXREADVEC(nodebox.connect_top, "connect_top"); - NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom"); - NODEBOXREADVEC(nodebox.connect_front, "connect_front"); - NODEBOXREADVEC(nodebox.connect_left, "connect_left"); - NODEBOXREADVEC(nodebox.connect_back, "connect_back"); - NODEBOXREADVEC(nodebox.connect_right, "connect_right"); - NODEBOXREADVEC(nodebox.disconnected_top, "disconnected_top"); - NODEBOXREADVEC(nodebox.disconnected_bottom, "disconnected_bottom"); - NODEBOXREADVEC(nodebox.disconnected_front, "disconnected_front"); - NODEBOXREADVEC(nodebox.disconnected_left, "disconnected_left"); - NODEBOXREADVEC(nodebox.disconnected_back, "disconnected_back"); - NODEBOXREADVEC(nodebox.disconnected_right, "disconnected_right"); - NODEBOXREADVEC(nodebox.disconnected, "disconnected"); - NODEBOXREADVEC(nodebox.disconnected_sides, "disconnected_sides"); - } return nodebox; } @@ -1519,8 +1528,11 @@ void push_flags_string(lua_State *L, FlagDesc *flagdesc, u32 flags, u32 flagmask /******************************************************************************/ void read_groups(lua_State *L, int index, ItemGroupList &result) { - if (!lua_istable(L,index)) + if (lua_isnil(L, index)) return; + + luaL_checktype(L, index, LUA_TTABLE); + result.clear(); lua_pushnil(L); if (index < 0) diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 334af61c3..279d8b1a5 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -33,10 +33,9 @@ extern "C" { #define CHECK_TYPE(index, name, type) { \ int t = lua_type(L, (index)); \ if (t != (type)) { \ - std::string traceback = script_get_backtrace(L); \ throw LuaError(std::string("Invalid ") + (name) + \ " (expected " + lua_typename(L, (type)) + \ - " got " + lua_typename(L, t) + ").\n" + traceback); \ + " got " + lua_typename(L, t) + ")."); \ } \ } #define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER) @@ -457,12 +456,22 @@ size_t read_stringlist(lua_State *L, int index, std::vector *result Table field getters */ +bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname) +{ + if (lua_isnil(L, index)) + return false; + + CHECK_TYPE(index, std::string("field \"") + fieldname + '"', type); + return true; +} + bool getstringfield(lua_State *L, int table, const char *fieldname, std::string &result) { lua_getfield(L, table, fieldname); bool got = false; - if(lua_isstring(L, -1)){ + + if (check_field_or_nil(L, -1, LUA_TSTRING, fieldname)) { size_t len = 0; const char *ptr = lua_tolstring(L, -1, &len); if (ptr) { @@ -479,7 +488,8 @@ bool getfloatfield(lua_State *L, int table, { lua_getfield(L, table, fieldname); bool got = false; - if(lua_isnumber(L, -1)){ + + if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) { result = lua_tonumber(L, -1); got = true; } @@ -492,7 +502,8 @@ bool getboolfield(lua_State *L, int table, { lua_getfield(L, table, fieldname); bool got = false; - if(lua_isboolean(L, -1)){ + + if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){ result = lua_toboolean(L, -1); got = true; } @@ -511,17 +522,6 @@ size_t getstringlistfield(lua_State *L, int table, const char *fieldname, return num_strings_read; } -std::string checkstringfield(lua_State *L, int table, - const char *fieldname) -{ - lua_getfield(L, table, fieldname); - CHECK_TYPE(-1, std::string("field \"") + fieldname + '"', LUA_TSTRING); - size_t len; - const char *s = lua_tolstring(L, -1, &len); - lua_pop(L, 1); - return std::string(s, len); -} - std::string getstringfield_default(lua_State *L, int table, const char *fieldname, const std::string &default_) { diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index 9620bf75a..a4a7079fd 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -45,13 +45,15 @@ float getfloatfield_default(lua_State *L, int table, int getintfield_default(lua_State *L, int table, const char *fieldname, int default_); +bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname); + template bool getintfield(lua_State *L, int table, const char *fieldname, T &result) { lua_getfield(L, table, fieldname); bool got = false; - if (lua_isnumber(L, -1)){ + if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)){ result = lua_tointeger(L, -1); got = true; } @@ -87,8 +89,6 @@ bool getboolfield(lua_State *L, int table, const char *fieldname, bool &result); bool getfloatfield(lua_State *L, int table, const char *fieldname, float &result); -std::string checkstringfield(lua_State *L, int table, - const char *fieldname); void setstringfield(lua_State *L, int table, const char *fieldname, const std::string &value); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 6e7afa4a4..4c8977898 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -487,7 +487,9 @@ int ModApiInventory::l_get_inventory(lua_State *L) { InventoryLocation loc; - std::string type = checkstringfield(L, 1, "type"); + lua_getfield(L, 1, "type"); + std::string type = luaL_checkstring(L, -1); + lua_pop(L, 1); if(type == "node"){ MAP_LOCK_REQUIRED; @@ -504,11 +506,13 @@ int ModApiInventory::l_get_inventory(lua_State *L) NO_MAP_LOCK_REQUIRED; if (type == "player") { - std::string name = checkstringfield(L, 1, "name"); - loc.setPlayer(name); + lua_getfield(L, 1, "name"); + loc.setPlayer(luaL_checkstring(L, -1)); + lua_pop(L, 1); } else if (type == "detached") { - std::string name = checkstringfield(L, 1, "name"); - loc.setDetached(name); + lua_getfield(L, 1, "name"); + loc.setDetached(luaL_checkstring(L, -1)); + lua_pop(L, 1); } if (getServer(L)->getInventory(loc) != NULL) -- cgit v1.2.3 From 91c4f7f0ea0b653d15bfabc05f5474f8fa1a8806 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Mon, 27 Apr 2020 11:27:27 +0200 Subject: Forbid object:attach(obj, ...) (#9762) Fixes #9761 --- src/script/lua_api/l_object.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index d7afb84da..77e1e7dc2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -674,8 +674,13 @@ int ObjectRef::l_set_attach(lua_State *L) ServerActiveObject *parent = getobject(parent_ref); if (co == NULL) return 0; + if (parent == NULL) return 0; + + if (co == parent) + throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed."); + // Do it int parent_id = 0; std::string bone; -- cgit v1.2.3 From 74d9b6010fd42573d20cdbe78f7aaa07badfbf1b Mon Sep 17 00:00:00 2001 From: Lejo Date: Fri, 1 May 2020 16:47:17 +0200 Subject: Give the online lua mainmenu also the client_list and mods (#8691) --- doc/menu_lua_api.txt | 25 ++++++------ src/script/lua_api/l_mainmenu.cpp | 81 +++++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 36 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index df6424ad7..8f5460acb 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -156,18 +156,21 @@ core.get_favorites(location) -> list of favorites (possible in async calls) ^ location: "local" or "online" ^ returns { [1] = { - clients = , - clients_max = , - version = , - password = , - creative = , - damage = , - pvp = , - description = , - name = , - address =
, - port = + clients = , + clients_max = , + version = , + password = , + creative = , + damage = , + pvp = , + description = , + name = , + address =
, + port = + clients_list = + mods = }, + ... } core.delete_favorite(id, location) -> success diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 867e84e13..a76e9f079 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -280,8 +280,8 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) { std::string listtype = "local"; - if (!lua_isnone(L,1)) { - listtype = luaL_checkstring(L,1); + if (!lua_isnone(L, 1)) { + listtype = luaL_checkstring(L, 1); } std::vector servers; @@ -298,7 +298,7 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) for (const Json::Value &server : servers) { - lua_pushnumber(L,index); + lua_pushnumber(L, index); lua_newtable(L); int top_lvl2 = lua_gettop(L); @@ -306,11 +306,11 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) if (!server["clients"].asString().empty()) { std::string clients_raw = server["clients"].asString(); char* endptr = 0; - int numbervalue = strtol(clients_raw.c_str(),&endptr,10); + int numbervalue = strtol(clients_raw.c_str(), &endptr,10); if ((!clients_raw.empty()) && (*endptr == 0)) { - lua_pushstring(L,"clients"); - lua_pushnumber(L,numbervalue); + lua_pushstring(L, "clients"); + lua_pushnumber(L, numbervalue); lua_settable(L, top_lvl2); } } @@ -319,83 +319,83 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) std::string clients_max_raw = server["clients_max"].asString(); char* endptr = 0; - int numbervalue = strtol(clients_max_raw.c_str(),&endptr,10); + int numbervalue = strtol(clients_max_raw.c_str(), &endptr,10); if ((!clients_max_raw.empty()) && (*endptr == 0)) { - lua_pushstring(L,"clients_max"); - lua_pushnumber(L,numbervalue); + lua_pushstring(L, "clients_max"); + lua_pushnumber(L, numbervalue); lua_settable(L, top_lvl2); } } if (!server["version"].asString().empty()) { - lua_pushstring(L,"version"); + lua_pushstring(L, "version"); std::string topush = server["version"].asString(); - lua_pushstring(L,topush.c_str()); + lua_pushstring(L, topush.c_str()); lua_settable(L, top_lvl2); } if (!server["proto_min"].asString().empty()) { - lua_pushstring(L,"proto_min"); + lua_pushstring(L, "proto_min"); lua_pushinteger(L, server["proto_min"].asInt()); lua_settable(L, top_lvl2); } if (!server["proto_max"].asString().empty()) { - lua_pushstring(L,"proto_max"); + lua_pushstring(L, "proto_max"); lua_pushinteger(L, server["proto_max"].asInt()); lua_settable(L, top_lvl2); } if (!server["password"].asString().empty()) { - lua_pushstring(L,"password"); + lua_pushstring(L, "password"); lua_pushboolean(L, server["password"].asBool()); lua_settable(L, top_lvl2); } if (!server["creative"].asString().empty()) { - lua_pushstring(L,"creative"); + lua_pushstring(L, "creative"); lua_pushboolean(L, server["creative"].asBool()); lua_settable(L, top_lvl2); } if (!server["damage"].asString().empty()) { - lua_pushstring(L,"damage"); + lua_pushstring(L, "damage"); lua_pushboolean(L, server["damage"].asBool()); lua_settable(L, top_lvl2); } if (!server["pvp"].asString().empty()) { - lua_pushstring(L,"pvp"); + lua_pushstring(L, "pvp"); lua_pushboolean(L, server["pvp"].asBool()); lua_settable(L, top_lvl2); } if (!server["description"].asString().empty()) { - lua_pushstring(L,"description"); + lua_pushstring(L, "description"); std::string topush = server["description"].asString(); - lua_pushstring(L,topush.c_str()); + lua_pushstring(L, topush.c_str()); lua_settable(L, top_lvl2); } if (!server["name"].asString().empty()) { - lua_pushstring(L,"name"); + lua_pushstring(L, "name"); std::string topush = server["name"].asString(); - lua_pushstring(L,topush.c_str()); + lua_pushstring(L, topush.c_str()); lua_settable(L, top_lvl2); } if (!server["address"].asString().empty()) { - lua_pushstring(L,"address"); + lua_pushstring(L, "address"); std::string topush = server["address"].asString(); - lua_pushstring(L,topush.c_str()); + lua_pushstring(L, topush.c_str()); lua_settable(L, top_lvl2); } if (!server["port"].asString().empty()) { - lua_pushstring(L,"port"); + lua_pushstring(L, "port"); std::string topush = server["port"].asString(); - lua_pushstring(L,topush.c_str()); + lua_pushstring(L, topush.c_str()); lua_settable(L, top_lvl2); } @@ -406,6 +406,37 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) lua_settable(L, top_lvl2); } + if (server["clients_list"].isArray()) { + unsigned int index_lvl2 = 1; + lua_pushstring(L, "clients_list"); + lua_newtable(L); + int top_lvl3 = lua_gettop(L); + for (const Json::Value &client : server["clients_list"]) { + lua_pushnumber(L, index_lvl2); + std::string topush = client.asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl3); + index_lvl2++; + } + lua_settable(L, top_lvl2); + } + + if (server["mods"].isArray()) { + unsigned int index_lvl2 = 1; + lua_pushstring(L, "mods"); + lua_newtable(L); + int top_lvl3 = lua_gettop(L); + for (const Json::Value &mod : server["mods"]) { + + lua_pushnumber(L, index_lvl2); + std::string topush = mod.asString(); + lua_pushstring(L, topush.c_str()); + lua_settable(L, top_lvl3); + index_lvl2++; + } + lua_settable(L, top_lvl2); + } + lua_settable(L, top); index++; } -- cgit v1.2.3 From ac368af4fe27f61f5a4209cdfe90956ff745993c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 1 May 2020 21:44:28 +0200 Subject: Allow connection info to be missing from minetest.get_player_information() (#9739) fixes #9352 This reverts commit 23c907befea02005e2c0c87fca0131b60aace18a. --- doc/lua_api.txt | 11 +++-- src/network/connection.h | 6 +-- src/script/lua_api/l_server.cpp | 102 ++++++++++++++++++++-------------------- 3 files changed, 61 insertions(+), 58 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5e4e18b62..f9107b623 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4160,17 +4160,18 @@ Utilities { address = "127.0.0.1", -- IP address of client ip_version = 4, -- IPv4 / IPv6 + connection_uptime = 200, -- seconds since client connected + protocol_version = 32, -- protocol version used by client + formspec_version = 2, -- supported formspec version + lang_code = "fr" -- Language code used for translation + -- the following keys can be missing if no stats have been collected yet min_rtt = 0.01, -- minimum round trip time max_rtt = 0.2, -- maximum round trip time avg_rtt = 0.02, -- average round trip time min_jitter = 0.01, -- minimum packet time jitter max_jitter = 0.5, -- maximum packet time jitter avg_jitter = 0.03, -- average packet time jitter - connection_uptime = 200, -- seconds since client connected - protocol_version = 32, -- protocol version used by client - formspec_version = 2, -- supported formspec version - lang_code = "fr" -- Language code used for translation - -- following information is available on debug build only!!! + -- the following information is available in a debug build only!!! -- DO NOT USE IN MODS --ser_vers = 26, -- serialization version used by client --major = 0, -- major version number diff --git a/src/network/connection.h b/src/network/connection.h index 85f021c4c..47b0805ce 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -612,16 +612,16 @@ class Peer { struct rttstats { float jitter_min = FLT_MAX; float jitter_max = 0.0f; - float jitter_avg = -2.0f; + float jitter_avg = -1.0f; float min_rtt = FLT_MAX; float max_rtt = 0.0f; - float avg_rtt = -2.0f; + float avg_rtt = -1.0f; rttstats() = default; }; rttstats m_rtt; - float m_last_rtt = -2.0f; + float m_last_rtt = -1.0f; // current usage count unsigned int m_usage = 0; diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 7137484e8..b6754938e 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -138,53 +138,54 @@ int ModApiServer::l_get_player_ip(lua_State *L) // get_player_information(name) int ModApiServer::l_get_player_information(lua_State *L) { - NO_MAP_LOCK_REQUIRED; - const char * name = luaL_checkstring(L, 1); - RemotePlayer *player = dynamic_cast(getEnv(L))->getPlayer(name); - if (player == NULL) { + + Server *server = getServer(L); + + const char *name = luaL_checkstring(L, 1); + RemotePlayer *player = server->getEnv().getPlayer(name); + if (!player) { lua_pushnil(L); // no such player return 1; } Address addr; - try - { - addr = getServer(L)->getPeerAddress(player->getPeerId()); - } catch(const con::PeerNotFoundException &) { + try { + addr = server->getPeerAddress(player->getPeerId()); + } catch (const con::PeerNotFoundException &) { dstream << FUNCTION_NAME << ": peer was not found" << std::endl; lua_pushnil(L); // error return 1; } - float min_rtt,max_rtt,avg_rtt,min_jitter,max_jitter,avg_jitter; + float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter; ClientState state; u32 uptime; u16 prot_vers; - u8 ser_vers,major,minor,patch; - std::string vers_string; - std::string lang_code; - -#define ERET(code) \ - if (!(code)) { \ - dstream << FUNCTION_NAME << ": peer was not found" << std::endl; \ - lua_pushnil(L); /* error */ \ - return 1; \ + u8 ser_vers, major, minor, patch; + std::string vers_string, lang_code; + + auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool { + return server->getClientConInfo(player->getPeerId(), type, value); + }; + + bool have_con_info = + getConInfo(con::MIN_RTT, &min_rtt) && + getConInfo(con::MAX_RTT, &max_rtt) && + getConInfo(con::AVG_RTT, &avg_rtt) && + getConInfo(con::MIN_JITTER, &min_jitter) && + getConInfo(con::MAX_JITTER, &max_jitter) && + getConInfo(con::AVG_JITTER, &avg_jitter); + + bool r = server->getClientInfo(player->getPeerId(), &state, &uptime, + &ser_vers, &prot_vers, &major, &minor, &patch, &vers_string, + &lang_code); + if (!r) { + dstream << FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; } - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MIN_RTT, &min_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MAX_RTT, &max_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::AVG_RTT, &avg_rtt)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MIN_JITTER, - &min_jitter)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::MAX_JITTER, - &max_jitter)) - ERET(getServer(L)->getClientConInfo(player->getPeerId(), con::AVG_JITTER, - &avg_jitter)) - - ERET(getServer(L)->getClientInfo(player->getPeerId(), &state, &uptime, &ser_vers, - &prot_vers, &major, &minor, &patch, &vers_string, &lang_code)) - lua_newtable(L); int table = lua_gettop(L); @@ -202,29 +203,31 @@ int ModApiServer::l_get_player_information(lua_State *L) } lua_settable(L, table); - lua_pushstring(L,"min_rtt"); - lua_pushnumber(L, min_rtt); - lua_settable(L, table); + if (have_con_info) { // may be missing + lua_pushstring(L, "min_rtt"); + lua_pushnumber(L, min_rtt); + lua_settable(L, table); - lua_pushstring(L,"max_rtt"); - lua_pushnumber(L, max_rtt); - lua_settable(L, table); + lua_pushstring(L, "max_rtt"); + lua_pushnumber(L, max_rtt); + lua_settable(L, table); - lua_pushstring(L,"avg_rtt"); - lua_pushnumber(L, avg_rtt); - lua_settable(L, table); + lua_pushstring(L, "avg_rtt"); + lua_pushnumber(L, avg_rtt); + lua_settable(L, table); - lua_pushstring(L,"min_jitter"); - lua_pushnumber(L, min_jitter); - lua_settable(L, table); + lua_pushstring(L, "min_jitter"); + lua_pushnumber(L, min_jitter); + lua_settable(L, table); - lua_pushstring(L,"max_jitter"); - lua_pushnumber(L, max_jitter); - lua_settable(L, table); + lua_pushstring(L, "max_jitter"); + lua_pushnumber(L, max_jitter); + lua_settable(L, table); - lua_pushstring(L,"avg_jitter"); - lua_pushnumber(L, avg_jitter); - lua_settable(L, table); + lua_pushstring(L, "avg_jitter"); + lua_pushnumber(L, avg_jitter); + lua_settable(L, table); + } lua_pushstring(L,"connection_uptime"); lua_pushnumber(L, uptime); @@ -268,7 +271,6 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_settable(L, table); #endif -#undef ERET return 1; } -- cgit v1.2.3 From e0ea87f1f32273dba2eb5421c2a8c890479ba078 Mon Sep 17 00:00:00 2001 From: ANAND Date: Sat, 2 May 2020 16:22:11 +0530 Subject: set_fov: Add support for time-based transitions (#9705) --- doc/lua_api.txt | 15 ++++--- src/client/camera.cpp | 84 +++++++++++++++++++++++++++++-------- src/client/camera.h | 15 ++++++- src/network/clientpackethandler.cpp | 15 ++++++- src/network/networkprotocol.h | 3 +- src/player.h | 12 ++++-- src/script/lua_api/l_object.cpp | 11 +++-- src/server.cpp | 4 +- 8 files changed, 124 insertions(+), 35 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f9107b623..44f62f7a7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5998,15 +5998,18 @@ object you are working with still exists. * max: bubbles bar is not shown * See [Object properties] for more information * Is limited to range 0 ... 65535 (2^16 - 1) -* `set_fov(fov, is_multiplier)`: Sets player's FOV +* `set_fov(fov, is_multiplier, transition_time)`: Sets player's FOV * `fov`: FOV value. * `is_multiplier`: Set to `true` if the FOV value is a multiplier. Defaults to `false`. - * Set to 0 to clear FOV override. -* `get_fov()`: - * Returns player's FOV override in degrees, and a boolean depending on whether - the value is a multiplier. - * Returns 0 as first value if player's FOV hasn't been overridden. + * `transition_time`: If defined, enables smooth FOV transition. + Interpreted as the time (in seconds) to reach target FOV. + If set to 0, FOV change is instantaneous. Defaults to 0. + * Set `fov` to 0 to clear FOV override. +* `get_fov()`: Returns the following: + * Server-sent FOV value. Returns 0 if an FOV override doesn't exist. + * Boolean indicating whether the FOV value is a multiplier. + * Time (in seconds) taken for the FOV transition. Set by `set_fov`. * `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 69bd82a47..1a5253db4 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -86,6 +86,51 @@ Camera::~Camera() m_wieldmgr->drop(); } +void Camera::notifyFovChange() +{ + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + assert(player); + + PlayerFovSpec spec = player->getFov(); + + /* + * Update m_old_fov_degrees first - it serves as the starting point of the + * upcoming transition. + * + * If an FOV transition is already active, mark current FOV as the start of + * the new transition. If not, set it to the previous transition's target FOV. + */ + if (m_fov_transition_active) + m_old_fov_degrees = m_curr_fov_degrees; + else + m_old_fov_degrees = m_server_sent_fov ? m_target_fov_degrees : m_cache_fov; + + /* + * Update m_server_sent_fov next - it corresponds to the target FOV of the + * upcoming transition. + * + * Set it to m_cache_fov, if server-sent FOV is 0. Otherwise check if + * server-sent FOV is a multiplier, and multiply it with m_cache_fov instead + * of overriding. + */ + if (spec.fov == 0.0f) { + m_server_sent_fov = false; + m_target_fov_degrees = m_cache_fov; + } else { + m_server_sent_fov = true; + m_target_fov_degrees = spec.is_multiplier ? m_cache_fov * spec.fov : spec.fov; + } + + if (spec.transition_time > 0.0f) + m_fov_transition_active = true; + + // If FOV smooth transition is active, initialize required variables + if (m_fov_transition_active) { + m_transition_time = spec.transition_time; + m_fov_diff = m_target_fov_degrees - m_old_fov_degrees; + } +} + bool Camera::successfullyCreated(std::string &error_message) { if (!m_playernode) { @@ -462,33 +507,38 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_camera_position = my_cp; /* - * Apply server-sent FOV. If server doesn't enforce FOV, - * check for zoom and set to zoom FOV. - * Otherwise, default to m_cache_fov + * Apply server-sent FOV, instantaneous or smooth transition. + * If not, check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov. */ - - f32 fov_degrees; - PlayerFovSpec fov_spec = player->getFov(); - if (fov_spec.fov > 0.0f) { - // If server-sent FOV is a multiplier, multiply - // it with m_cache_fov instead of overriding - if (fov_spec.is_multiplier) - fov_degrees = m_cache_fov * fov_spec.fov; - else - fov_degrees = fov_spec.fov; + if (m_fov_transition_active) { + // Smooth FOV transition + // Dynamically calculate FOV delta based on frametimes + f32 delta = (frametime / m_transition_time) * m_fov_diff; + m_curr_fov_degrees += delta; + + // Mark transition as complete if target FOV has been reached + if ((m_fov_diff > 0.0f && m_curr_fov_degrees >= m_target_fov_degrees) || + (m_fov_diff < 0.0f && m_curr_fov_degrees <= m_target_fov_degrees)) { + m_fov_transition_active = false; + m_curr_fov_degrees = m_target_fov_degrees; + } + } else if (m_server_sent_fov) { + // Instantaneous FOV change + m_curr_fov_degrees = m_target_fov_degrees; } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { // Player requests zoom, apply zoom FOV - fov_degrees = player->getZoomFOV(); + m_curr_fov_degrees = player->getZoomFOV(); } else { // Set to client's selected FOV - fov_degrees = m_cache_fov; + m_curr_fov_degrees = m_cache_fov; } - fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); + m_curr_fov_degrees = rangelim(m_curr_fov_degrees, 1.0f, 160.0f); // FOV and aspect ratio const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize(); m_aspect = (f32) window_size.X / (f32) window_size.Y; - m_fov_y = fov_degrees * M_PI / 180.0; + m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); diff --git a/src/client/camera.h b/src/client/camera.h index 6ec37fe10..3a59637bc 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -112,6 +112,9 @@ public: return MYMAX(m_fov_x, m_fov_y); } + // Notify about new server-sent FOV and initialize smooth FOV transition + void notifyFovChange(); + // Checks if the constructor was able to create the scene nodes bool successfullyCreated(std::string &error_message); @@ -186,6 +189,9 @@ private: Client *m_client; + // Default Client FOV (as defined by the "fov" setting) + f32 m_cache_fov; + // Absolute camera position v3f m_camera_position; // Absolute camera direction @@ -193,6 +199,14 @@ private: // Camera offset v3s16 m_camera_offset; + // Server-sent FOV variables + bool m_server_sent_fov = false; + f32 m_curr_fov_degrees, m_old_fov_degrees, m_target_fov_degrees; + + // FOV transition variables + bool m_fov_transition_active = false; + f32 m_fov_diff, m_transition_time; + v2f m_wieldmesh_offset = v2f(55.0f, -35.0f); v2f m_arm_dir; v2f m_cam_vel; @@ -230,7 +244,6 @@ private: f32 m_cache_fall_bobbing_amount; f32 m_cache_view_bobbing_amount; - f32 m_cache_fov; bool m_arm_inertia; std::list m_nametags; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d19dc3818..6428ed752 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #include "util/base64.h" +#include "client/camera.h" #include "chatmessage.h" #include "client/clientmedia.h" #include "log.h" @@ -530,11 +531,21 @@ void Client::handleCommand_Movement(NetworkPacket* pkt) void Client::handleCommand_Fov(NetworkPacket *pkt) { f32 fov; - bool is_multiplier; + bool is_multiplier = false; + f32 transition_time = 0.0f; + *pkt >> fov >> is_multiplier; + // Wrap transition_time extraction within a + // try-catch to preserve backwards compat + try { + *pkt >> transition_time; + } catch (PacketError &e) {}; + LocalPlayer *player = m_env.getLocalPlayer(); - player->setFov({ fov, is_multiplier }); + assert(player); + player->setFov({ fov, is_multiplier, transition_time }); + m_camera->notifyFovChange(); } void Client::handleCommand_HP(NetworkPacket *pkt) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 4b7345b15..73523ea42 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -384,8 +384,9 @@ enum ToClientCommand /* Sends an FOV override/multiplier to client. - float fov + f32 fov bool is_multiplier + f32 transition_time */ TOCLIENT_DEATHSCREEN = 0x37, diff --git a/src/player.h b/src/player.h index de7f427e9..3bc7762fa 100644 --- a/src/player.h +++ b/src/player.h @@ -35,7 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc., struct PlayerFovSpec { f32 fov; + + // Whether to multiply the client's FOV or to override it bool is_multiplier; + + // The time to be take to trasition to the new FOV value. + // Transition is instantaneous if omitted. Omitted by default. + f32 transition_time; }; struct PlayerControl @@ -186,12 +192,12 @@ public: void setFov(const PlayerFovSpec &spec) { - m_fov_spec = spec; + m_fov_override_spec = spec; } const PlayerFovSpec &getFov() const { - return m_fov_spec; + return m_fov_override_spec; } u32 keyPressed = 0; @@ -208,7 +214,7 @@ protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; u16 m_wield_index = 0; - PlayerFovSpec m_fov_spec = { 0.0f, false }; + PlayerFovSpec m_fov_override_spec = { 0.0f, false, 0.0f }; std::vector hud; private: diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 77e1e7dc2..dcaee10b2 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1249,7 +1249,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } -// set_fov(self, degrees[, is_multiplier]) +// set_fov(self, degrees[, is_multiplier, transition_time]) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -1258,7 +1258,11 @@ int ObjectRef::l_set_fov(lua_State *L) if (!player) return 0; - player->setFov({ static_cast(luaL_checknumber(L, 2)), readParam(L, 3) }); + player->setFov({ + static_cast(luaL_checknumber(L, 2)), + readParam(L, 3, false), + lua_isnumber(L, 4) ? static_cast(luaL_checknumber(L, 4)) : 0.0f + }); getServer(L)->SendPlayerFov(player->getPeerId()); return 0; @@ -1276,8 +1280,9 @@ int ObjectRef::l_get_fov(lua_State *L) PlayerFovSpec fov_spec = player->getFov(); lua_pushnumber(L, fov_spec.fov); lua_pushboolean(L, fov_spec.is_multiplier); + lua_pushnumber(L, fov_spec.transition_time); - return 2; + return 3; } // set_breath(self, breath) diff --git a/src/server.cpp b/src/server.cpp index a7eb52837..0346d197d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1892,10 +1892,10 @@ void Server::SendMovePlayer(session_t peer_id) void Server::SendPlayerFov(session_t peer_id) { - NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id); PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); - pkt << fov_spec.fov << fov_spec.is_multiplier; + pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time; Send(&pkt); } -- cgit v1.2.3 From 808eb4c5714da5ac36f4a70653d6b3805060828c Mon Sep 17 00:00:00 2001 From: Lejo Date: Sat, 2 May 2020 12:57:04 +0200 Subject: Auto delete MetaData when = 0 (#8770) * Auto delete MetaData when = 0 --- src/script/lua_api/l_metadata.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index 21002e6a7..61a25a761 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -153,7 +153,9 @@ int MetaDataRef::l_set_int(lua_State *L) MetaDataRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); int a = luaL_checkint(L, 3); - std::string str = itos(a); + std::string str; + if (a != 0) + str = itos(a); Metadata *meta = ref->getmeta(true); if (meta == NULL || str == meta->getString(name)) @@ -191,7 +193,9 @@ int MetaDataRef::l_set_float(lua_State *L) MetaDataRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); float a = readParam(L, 3); - std::string str = ftos(a); + std::string str; + if (a != 0) + str = ftos(a); Metadata *meta = ref->getmeta(true); if (meta == NULL || str == meta->getString(name)) -- cgit v1.2.3 From cad5b987ad4720bd6a49d3604be9e81ea348f799 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 4 May 2020 20:19:12 +0200 Subject: Sky API: Rename *_tint to fog_*_tint for consistency --- src/client/game.cpp | 12 ++++++------ src/client/sky.cpp | 12 ++++++------ src/network/clientpackethandler.cpp | 8 ++++---- src/network/networkprotocol.h | 6 +++--- src/remoteplayer.cpp | 6 +++--- src/script/lua_api/l_object.cpp | 22 +++++++++++----------- src/server.cpp | 4 ++-- src/skyparams.h | 6 +++--- 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/client/game.cpp b/src/client/game.cpp index 3bdac786c..d1eb3bba2 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2799,9 +2799,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) // Update mesh based skybox colours if applicable. sky->setSkyColors(*event->set_sky); sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); } else if (event->set_sky->type == "skybox" && event->set_sky->textures.size() == 6) { @@ -2811,9 +2811,9 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) sky->setFallbackBgColor(event->set_sky->bgcolor); // Set sunrise and sunset fog tinting: sky->setHorizonTint( - event->set_sky->sun_tint, - event->set_sky->moon_tint, - event->set_sky->tint_type + event->set_sky->fog_sun_tint, + event->set_sky->fog_moon_tint, + event->set_sky->fog_tint_type ); // Add textures to skybox. for (int i = 0; i < 6; i++) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 7a7b188ce..ce33b96ae 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -529,7 +529,7 @@ void Sky::update(float time_of_day, float time_brightness, pointcolor_sun_f.g = pointcolor_light * (float)m_materials[3].EmissiveColor.getGreen() / 255; } else if (!m_default_tint) { - pointcolor_sun_f = m_sky_params.sun_tint; + pointcolor_sun_f = m_sky_params.fog_sun_tint; } else { pointcolor_sun_f.r = pointcolor_light * 1; pointcolor_sun_f.b = pointcolor_light * @@ -548,9 +548,9 @@ void Sky::update(float time_of_day, float time_brightness, ); } else { pointcolor_moon_f = video::SColorf( - (m_sky_params.moon_tint.getRed() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getGreen() / 255) * pointcolor_light, - (m_sky_params.moon_tint.getBlue() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light, 1 ); } @@ -941,8 +941,8 @@ void Sky::setHorizonTint(video::SColor sun_tint, video::SColor moon_tint, std::string use_sun_tint) { // Change sun and moon tinting: - m_sky_params.sun_tint = sun_tint; - m_sky_params.moon_tint = moon_tint; + m_sky_params.fog_sun_tint = sun_tint; + m_sky_params.fog_moon_tint = moon_tint; // Faster than comparing strings every rendering frame if (use_sun_tint == "default") m_default_tint = true; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 6428ed752..8d0225a3d 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1276,9 +1276,9 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) // Fix for "regular" skies, as color isn't kept: if (skybox.type == "regular") { skybox.sky_color = sky_defaults.getSkyColorDefaults(); - skybox.tint_type = "default"; - skybox.moon_tint = video::SColor(255, 255, 255, 255); - skybox.sun_tint = video::SColor(255, 255, 255, 255); + skybox.fog_tint_type = "default"; + skybox.fog_moon_tint = video::SColor(255, 255, 255, 255); + skybox.fog_sun_tint = video::SColor(255, 255, 255, 255); } else { sun.visible = false; @@ -1313,7 +1313,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) std::string texture; *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >> - skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type; + skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type; if (skybox.type == "skybox") { *pkt >> texture_count; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 73523ea42..527ebba7c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -634,9 +634,9 @@ enum ToClientCommand u8[4] night_sky (ARGB) u8[4] night_horizon (ARGB) u8[4] indoors (ARGB) - u8[4] sun_tint (ARGB) - u8[4] moon_tint (ARGB) - std::string tint_type + u8[4] fog_sun_tint (ARGB) + u8[4] fog_moon_tint (ARGB) + std::string fog_tint_type */ TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO = 0x50, diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 7a603d53e..bef60c792 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -74,9 +74,9 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef): m_skybox_params.sky_color = sky_defaults.getSkyColorDefaults(); m_skybox_params.type = "regular"; m_skybox_params.clouds = true; - m_skybox_params.sun_tint = video::SColor(255, 244, 125, 29); - m_skybox_params.moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); - m_skybox_params.tint_type = "default"; + m_skybox_params.fog_sun_tint = video::SColor(255, 244, 125, 29); + m_skybox_params.fog_moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor(); + m_skybox_params.fog_tint_type = "default"; m_sun_params = sky_defaults.getSunDefaults(); m_moon_params = sky_defaults.getMoonDefaults(); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index dcaee10b2..f71130378 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1782,19 +1782,19 @@ int ObjectRef::l_set_sky(lua_State *L) lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.sun_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.sun_tint); + read_color(L, -1, &skybox_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.moon_tint = video::SColor(255, 255, 255, 255); + skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.moon_tint); + read_color(L, -1, &skybox_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.tint_type = luaL_checkstring(L, -1); + skybox_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1912,12 +1912,12 @@ int ObjectRef::l_get_sky_color(lua_State *L) push_ARGB8(L, skybox_params.sky_color.indoors); lua_setfield(L, -2, "indoors"); } - push_ARGB8(L, skybox_params.sun_tint); - lua_setfield(L, -2, "sun_tint"); - push_ARGB8(L, skybox_params.moon_tint); - lua_setfield(L, -2, "moon_tint"); - lua_pushstring(L, skybox_params.tint_type.c_str()); - lua_setfield(L, -2, "tint_type"); + push_ARGB8(L, skybox_params.fog_sun_tint); + lua_setfield(L, -2, "fog_sun_tint"); + push_ARGB8(L, skybox_params.fog_moon_tint); + lua_setfield(L, -2, "fog_moon_tint"); + lua_pushstring(L, skybox_params.fog_tint_type.c_str()); + lua_setfield(L, -2, "fog_tint_type"); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 0346d197d..05584be2d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1770,8 +1770,8 @@ void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms) pkt << params.clouds; } else { // Handle current clients and future clients pkt << params.bgcolor << params.type - << params.clouds << params.sun_tint - << params.moon_tint << params.tint_type; + << params.clouds << params.fog_sun_tint + << params.fog_moon_tint << params.fog_tint_type; if (params.type == "skybox") { pkt << (u16) params.textures.size(); diff --git a/src/skyparams.h b/src/skyparams.h index 9fdfd89da..c362ef8f3 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -37,9 +37,9 @@ struct SkyboxParams std::vector textures; bool clouds; SkyColor sky_color; - video::SColor sun_tint; - video::SColor moon_tint; - std::string tint_type; + video::SColor fog_sun_tint; + video::SColor fog_moon_tint; + std::string fog_tint_type; }; struct SunParams -- cgit v1.2.3 From 3c65d1acec27366d88fc3686d3f820175673e203 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 02:05:20 +0200 Subject: Give the Mapgen on each EmergeThread its own Biome/Ore/Deco/SchemManager copy --- src/emerge.cpp | 60 ++++++++++++++++++++++++++++++++++++-- src/emerge.h | 48 ++++++++++++++++++++++++++---- src/mapgen/mapgen.cpp | 17 ++++++----- src/mapgen/mapgen.h | 15 +++++----- src/mapgen/mapgen_carpathian.cpp | 2 +- src/mapgen/mapgen_carpathian.h | 2 +- src/mapgen/mapgen_flat.cpp | 2 +- src/mapgen/mapgen_flat.h | 2 +- src/mapgen/mapgen_fractal.cpp | 2 +- src/mapgen/mapgen_fractal.h | 2 +- src/mapgen/mapgen_singlenode.cpp | 2 +- src/mapgen/mapgen_singlenode.h | 2 +- src/mapgen/mapgen_v5.cpp | 2 +- src/mapgen/mapgen_v5.h | 2 +- src/mapgen/mapgen_v6.cpp | 4 ++- src/mapgen/mapgen_v6.h | 4 +-- src/mapgen/mapgen_v7.cpp | 2 +- src/mapgen/mapgen_v7.h | 2 +- src/mapgen/mapgen_valleys.cpp | 2 +- src/mapgen/mapgen_valleys.h | 2 +- src/mapgen/mg_biome.cpp | 8 ++--- src/mapgen/mg_biome.h | 6 ++-- src/mapgen/mg_decoration.cpp | 16 ++++++++-- src/mapgen/mg_decoration.h | 2 ++ src/mapgen/mg_schematic.cpp | 27 +++++++++++++++-- src/mapgen/mg_schematic.h | 4 ++- src/script/lua_api/l_mapgen.cpp | 63 ++++++++++++++++++++++++---------------- 27 files changed, 225 insertions(+), 77 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/emerge.cpp b/src/emerge.cpp index fe885447c..6e05a45c9 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -109,6 +109,28 @@ private: VoxelArea *m_ignorevariable; }; +EmergeParams::~EmergeParams() +{ + infostream << "EmergeParams: destroying " << this << std::endl; + // Delete everything that was cloned on creation of EmergeParams + delete biomemgr; + delete oremgr; + delete decomgr; + delete schemmgr; +} + +EmergeParams::EmergeParams(EmergeManager *parent, const BiomeManager *biomemgr, + const OreManager *oremgr, const DecorationManager *decomgr, + const SchematicManager *schemmgr) : + ndef(parent->ndef), + enable_mapgen_debug_info(parent->enable_mapgen_debug_info), + gen_notify_on(parent->gen_notify_on), + gen_notify_on_deco_ids(&parent->gen_notify_on_deco_ids), + biomemgr(biomemgr->clone()), oremgr(oremgr->clone()), + decomgr(decomgr->clone()), schemmgr(schemmgr->clone()) +{ +} + //// //// EmergeManager //// @@ -182,14 +204,48 @@ EmergeManager::~EmergeManager() } +BiomeManager *EmergeManager::getWritableBiomeManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return biomemgr; +} + +OreManager *EmergeManager::getWritableOreManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return oremgr; +} + +DecorationManager *EmergeManager::getWritableDecorationManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return decomgr; +} + +SchematicManager *EmergeManager::getWritableSchematicManager() +{ + FATAL_ERROR_IF(!m_mapgens.empty(), + "Writable managers can only be returned before mapgen init"); + return schemmgr; +} + + void EmergeManager::initMapgens(MapgenParams *params) { FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialised."); mgparams = params; - for (u32 i = 0; i != m_threads.size(); i++) - m_mapgens.push_back(Mapgen::createMapgen(params->mgtype, params, this)); + for (u32 i = 0; i != m_threads.size(); i++) { + EmergeParams *p = new EmergeParams( + this, biomemgr, oremgr, decomgr, schemmgr); + infostream << "EmergeManager: Created params " << p + << " for thread " << i << std::endl; + m_mapgens.push_back(Mapgen::createMapgen(params->mgtype, params, p)); + } } diff --git a/src/emerge.h b/src/emerge.h index df849e542..ab9fca2ba 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -86,6 +86,30 @@ struct BlockEmergeData { EmergeCallbackList callbacks; }; +class EmergeParams { + friend class EmergeManager; +public: + EmergeParams() = delete; + ~EmergeParams(); + DISABLE_CLASS_COPY(EmergeParams); + + const NodeDefManager *ndef; // shared + bool enable_mapgen_debug_info; + + u32 gen_notify_on; + const std::set *gen_notify_on_deco_ids; // shared + + BiomeManager *biomemgr; + OreManager *oremgr; + DecorationManager *decomgr; + SchematicManager *schemmgr; + +private: + EmergeParams(EmergeManager *parent, const BiomeManager *biomemgr, + const OreManager *oremgr, const DecorationManager *decomgr, + const SchematicManager *schemmgr); +}; + class EmergeManager { public: const NodeDefManager *ndef; @@ -106,17 +130,22 @@ public: // Environment is not created until after script initialization. MapSettingsManager *map_settings_mgr; - // Managers of various map generation-related components - BiomeManager *biomemgr; - OreManager *oremgr; - DecorationManager *decomgr; - SchematicManager *schemmgr; - // Methods EmergeManager(Server *server); ~EmergeManager(); DISABLE_CLASS_COPY(EmergeManager); + // no usage restrictions + const BiomeManager *getBiomeManager() const { return biomemgr; } + const OreManager *getOreManager() const { return oremgr; } + const DecorationManager *getDecorationManager() const { return decomgr; } + const SchematicManager *getSchematicManager() const { return schemmgr; } + // only usable before mapgen init + BiomeManager *getWritableBiomeManager(); + OreManager *getWritableOreManager(); + DecorationManager *getWritableDecorationManager(); + SchematicManager *getWritableSchematicManager(); + void initMapgens(MapgenParams *mgparams); void startThreads(); @@ -160,6 +189,13 @@ private: u16 m_qlimit_diskonly; u16 m_qlimit_generate; + // Managers of various map generation-related components + // Note that each Mapgen gets a copy(!) of these to work with + BiomeManager *biomemgr; + OreManager *oremgr; + DecorationManager *decomgr; + SchematicManager *schemmgr; + // Requires m_queue_mutex held EmergeThread *getOptimalThread(); diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index 0094608ec..f57529082 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -106,8 +106,8 @@ STATIC_ASSERT( //// Mapgen //// -Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) : - gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids) +Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeParams *emerge) : + gennotify(emerge->gen_notify_on, emerge->gen_notify_on_deco_ids) { id = mapgenid; water_level = params->water_level; @@ -156,7 +156,7 @@ const char *Mapgen::getMapgenName(MapgenType mgtype) Mapgen *Mapgen::createMapgen(MapgenType mgtype, MapgenParams *params, - EmergeManager *emerge) + EmergeParams *emerge) { switch (mgtype) { case MAPGEN_CARPATHIAN: @@ -585,7 +585,7 @@ void Mapgen::spreadLight(const v3s16 &nmin, const v3s16 &nmax) //// MapgenBasic //// -MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeParams *emerge) : Mapgen(mapgenid, params, emerge) { this->m_emerge = emerge; @@ -642,6 +642,8 @@ MapgenBasic::~MapgenBasic() { delete biomegen; delete []heightmap; + + delete m_emerge; // destroying EmergeParams is our responsibility } @@ -974,7 +976,7 @@ void MapgenBasic::generateDungeons(s16 max_stone_y) //// GenerateNotifier::GenerateNotifier(u32 notify_on, - std::set *notify_on_deco_ids) + const std::set *notify_on_deco_ids) { m_notify_on = notify_on; m_notify_on_deco_ids = notify_on_deco_ids; @@ -987,7 +989,8 @@ void GenerateNotifier::setNotifyOn(u32 notify_on) } -void GenerateNotifier::setNotifyOnDecoIds(std::set *notify_on_deco_ids) +void GenerateNotifier::setNotifyOnDecoIds( + const std::set *notify_on_deco_ids) { m_notify_on_deco_ids = notify_on_deco_ids; } @@ -999,7 +1002,7 @@ bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id) return false; if (type == GENNOTIFY_DECORATION && - m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end()) + m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->cend()) return false; GenNotifyEvent gne; diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h index dc325c791..7845c5349 100644 --- a/src/mapgen/mapgen.h +++ b/src/mapgen/mapgen.h @@ -51,6 +51,7 @@ class Biome; class BiomeGen; struct BiomeParams; class BiomeManager; +class EmergeParams; class EmergeManager; class MapBlock; class VoxelManipulator; @@ -87,10 +88,10 @@ struct GenNotifyEvent { class GenerateNotifier { public: GenerateNotifier() = default; - GenerateNotifier(u32 notify_on, std::set *notify_on_deco_ids); + GenerateNotifier(u32 notify_on, const std::set *notify_on_deco_ids); void setNotifyOn(u32 notify_on); - void setNotifyOnDecoIds(std::set *notify_on_deco_ids); + void setNotifyOnDecoIds(const std::set *notify_on_deco_ids); bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0); void getEvents(std::map > &event_map); @@ -98,7 +99,7 @@ public: private: u32 m_notify_on = 0; - std::set *m_notify_on_deco_ids; + const std::set *m_notify_on_deco_ids; std::list m_notify_events; }; @@ -176,7 +177,7 @@ public: GenerateNotifier gennotify; Mapgen() = default; - Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge); + Mapgen(int mapgenid, MapgenParams *params, EmergeParams *emerge); virtual ~Mapgen() = default; DISABLE_CLASS_COPY(Mapgen); @@ -215,7 +216,7 @@ public: static MapgenType getMapgenType(const std::string &mgname); static const char *getMapgenName(MapgenType mgtype); static Mapgen *createMapgen(MapgenType mgtype, MapgenParams *params, - EmergeManager *emerge); + EmergeParams *emerge); static MapgenParams *createMapgenParams(MapgenType mgtype); static void getMapgenNames(std::vector *mgnames, bool include_hidden); static void setDefaultSettings(Settings *settings); @@ -243,7 +244,7 @@ private: */ class MapgenBasic : public Mapgen { public: - MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenBasic(int mapgenid, MapgenParams *params, EmergeParams *emerge); virtual ~MapgenBasic(); virtual void generateBiomes(); @@ -254,7 +255,7 @@ public: virtual void generateDungeons(s16 max_stone_y); protected: - EmergeManager *m_emerge; + EmergeParams *m_emerge; BiomeManager *m_bmgr; Noise *noise_filler_depth; diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp index bd7ae5e7c..feb9b428c 100644 --- a/src/mapgen/mapgen_carpathian.cpp +++ b/src/mapgen/mapgen_carpathian.cpp @@ -49,7 +49,7 @@ FlagDesc flagdesc_mapgen_carpathian[] = { /////////////////////////////////////////////////////////////////////////////// -MapgenCarpathian::MapgenCarpathian(MapgenCarpathianParams *params, EmergeManager *emerge) +MapgenCarpathian::MapgenCarpathian(MapgenCarpathianParams *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_CARPATHIAN, params, emerge) { base_level = params->base_level; diff --git a/src/mapgen/mapgen_carpathian.h b/src/mapgen/mapgen_carpathian.h index acd379958..31b2b91d8 100644 --- a/src/mapgen/mapgen_carpathian.h +++ b/src/mapgen/mapgen_carpathian.h @@ -79,7 +79,7 @@ struct MapgenCarpathianParams : public MapgenParams class MapgenCarpathian : public MapgenBasic { public: - MapgenCarpathian(MapgenCarpathianParams *params, EmergeManager *emerge); + MapgenCarpathian(MapgenCarpathianParams *params, EmergeParams *emerge); ~MapgenCarpathian(); virtual MapgenType getType() const { return MAPGEN_CARPATHIAN; } diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp index 272964b51..369777ad2 100644 --- a/src/mapgen/mapgen_flat.cpp +++ b/src/mapgen/mapgen_flat.cpp @@ -47,7 +47,7 @@ FlagDesc flagdesc_mapgen_flat[] = { /////////////////////////////////////////////////////////////////////////////////////// -MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeManager *emerge) +MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_FLAT, params, emerge) { spflags = params->spflags; diff --git a/src/mapgen/mapgen_flat.h b/src/mapgen/mapgen_flat.h index c314c7605..4902a802c 100644 --- a/src/mapgen/mapgen_flat.h +++ b/src/mapgen/mapgen_flat.h @@ -64,7 +64,7 @@ struct MapgenFlatParams : public MapgenParams class MapgenFlat : public MapgenBasic { public: - MapgenFlat(MapgenFlatParams *params, EmergeManager *emerge); + MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge); ~MapgenFlat(); virtual MapgenType getType() const { return MAPGEN_FLAT; } diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp index 97f77d947..cb55bc288 100644 --- a/src/mapgen/mapgen_fractal.cpp +++ b/src/mapgen/mapgen_fractal.cpp @@ -47,7 +47,7 @@ FlagDesc flagdesc_mapgen_fractal[] = { /////////////////////////////////////////////////////////////////////////////////////// -MapgenFractal::MapgenFractal(MapgenFractalParams *params, EmergeManager *emerge) +MapgenFractal::MapgenFractal(MapgenFractalParams *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_FRACTAL, params, emerge) { spflags = params->spflags; diff --git a/src/mapgen/mapgen_fractal.h b/src/mapgen/mapgen_fractal.h index 971dfd822..23af925bc 100644 --- a/src/mapgen/mapgen_fractal.h +++ b/src/mapgen/mapgen_fractal.h @@ -72,7 +72,7 @@ struct MapgenFractalParams : public MapgenParams class MapgenFractal : public MapgenBasic { public: - MapgenFractal(MapgenFractalParams *params, EmergeManager *emerge); + MapgenFractal(MapgenFractalParams *params, EmergeParams *emerge); ~MapgenFractal(); virtual MapgenType getType() const { return MAPGEN_FRACTAL; } diff --git a/src/mapgen/mapgen_singlenode.cpp b/src/mapgen/mapgen_singlenode.cpp index b64524e1c..cade9e7a8 100644 --- a/src/mapgen/mapgen_singlenode.cpp +++ b/src/mapgen/mapgen_singlenode.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "emerge.h" -MapgenSinglenode::MapgenSinglenode(MapgenParams *params, EmergeManager *emerge) +MapgenSinglenode::MapgenSinglenode(MapgenParams *params, EmergeParams *emerge) : Mapgen(MAPGEN_SINGLENODE, params, emerge) { const NodeDefManager *ndef = emerge->ndef; diff --git a/src/mapgen/mapgen_singlenode.h b/src/mapgen/mapgen_singlenode.h index c21089eda..e056d9ab1 100644 --- a/src/mapgen/mapgen_singlenode.h +++ b/src/mapgen/mapgen_singlenode.h @@ -38,7 +38,7 @@ public: content_t c_node; u8 set_light; - MapgenSinglenode(MapgenParams *params, EmergeManager *emerge); + MapgenSinglenode(MapgenParams *params, EmergeParams *emerge); ~MapgenSinglenode() = default; virtual MapgenType getType() const { return MAPGEN_SINGLENODE; } diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp index 3bd7615c4..124667e5d 100644 --- a/src/mapgen/mapgen_v5.cpp +++ b/src/mapgen/mapgen_v5.cpp @@ -44,7 +44,7 @@ FlagDesc flagdesc_mapgen_v5[] = { }; -MapgenV5::MapgenV5(MapgenV5Params *params, EmergeManager *emerge) +MapgenV5::MapgenV5(MapgenV5Params *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_V5, params, emerge) { spflags = params->spflags; diff --git a/src/mapgen/mapgen_v5.h b/src/mapgen/mapgen_v5.h index 17bc466f0..cf4ee4899 100644 --- a/src/mapgen/mapgen_v5.h +++ b/src/mapgen/mapgen_v5.h @@ -64,7 +64,7 @@ struct MapgenV5Params : public MapgenParams class MapgenV5 : public MapgenBasic { public: - MapgenV5(MapgenV5Params *params, EmergeManager *emerge); + MapgenV5(MapgenV5Params *params, EmergeParams *emerge); ~MapgenV5(); virtual MapgenType getType() const { return MAPGEN_V5; } diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index da9ae1428..e9692246c 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -54,7 +54,7 @@ FlagDesc flagdesc_mapgen_v6[] = { ///////////////////////////////////////////////////////////////////////////// -MapgenV6::MapgenV6(MapgenV6Params *params, EmergeManager *emerge) +MapgenV6::MapgenV6(MapgenV6Params *params, EmergeParams *emerge) : Mapgen(MAPGEN_V6, params, emerge) { m_emerge = emerge; @@ -160,6 +160,8 @@ MapgenV6::~MapgenV6() delete noise_humidity; delete[] heightmap; + + delete m_emerge; // our responsibility } diff --git a/src/mapgen/mapgen_v6.h b/src/mapgen/mapgen_v6.h index d8cdcb26f..ff565edec 100644 --- a/src/mapgen/mapgen_v6.h +++ b/src/mapgen/mapgen_v6.h @@ -83,7 +83,7 @@ struct MapgenV6Params : public MapgenParams { class MapgenV6 : public Mapgen { public: - EmergeManager *m_emerge; + EmergeParams *m_emerge; int ystride; u32 spflags; @@ -133,7 +133,7 @@ public: content_t c_stair_cobble; content_t c_stair_desert_stone; - MapgenV6(MapgenV6Params *params, EmergeManager *emerge); + MapgenV6(MapgenV6Params *params, EmergeParams *emerge); ~MapgenV6(); virtual MapgenType getType() const { return MAPGEN_V6; } diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 82556cc4f..43d5d822f 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -51,7 +51,7 @@ FlagDesc flagdesc_mapgen_v7[] = { //////////////////////////////////////////////////////////////////////////////// -MapgenV7::MapgenV7(MapgenV7Params *params, EmergeManager *emerge) +MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_V7, params, emerge) { spflags = params->spflags; diff --git a/src/mapgen/mapgen_v7.h b/src/mapgen/mapgen_v7.h index 0605c5c97..eeae3a956 100644 --- a/src/mapgen/mapgen_v7.h +++ b/src/mapgen/mapgen_v7.h @@ -75,7 +75,7 @@ struct MapgenV7Params : public MapgenParams { class MapgenV7 : public MapgenBasic { public: - MapgenV7(MapgenV7Params *params, EmergeManager *emerge); + MapgenV7(MapgenV7Params *params, EmergeParams *emerge); ~MapgenV7(); virtual MapgenType getType() const { return MAPGEN_V7; } diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp index ff908b7bb..efcc8ee85 100644 --- a/src/mapgen/mapgen_valleys.cpp +++ b/src/mapgen/mapgen_valleys.cpp @@ -54,7 +54,7 @@ FlagDesc flagdesc_mapgen_valleys[] = { }; -MapgenValleys::MapgenValleys(MapgenValleysParams *params, EmergeManager *emerge) +MapgenValleys::MapgenValleys(MapgenValleysParams *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_VALLEYS, params, emerge) { // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal diff --git a/src/mapgen/mapgen_valleys.h b/src/mapgen/mapgen_valleys.h index 1aec68842..34a923dfa 100644 --- a/src/mapgen/mapgen_valleys.h +++ b/src/mapgen/mapgen_valleys.h @@ -84,7 +84,7 @@ class MapgenValleys : public MapgenBasic { public: MapgenValleys(MapgenValleysParams *params, - EmergeManager *emerge); + EmergeParams *emerge); ~MapgenValleys(); virtual MapgenType getType() const { return MAPGEN_VALLEYS; } diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index 3a72c43bf..f3bc4e829 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -78,7 +78,7 @@ void BiomeManager::clear() EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations - DecorationManager *decomgr = emerge->decomgr; + DecorationManager *decomgr = emerge->getWritableDecorationManager(); for (size_t i = 0; i != decomgr->getNumObjects(); i++) { Decoration *deco = (Decoration *)decomgr->getRaw(i); deco->biomes.clear(); @@ -104,7 +104,7 @@ BiomeManager *BiomeManager::clone() const // For BiomeGen type 'BiomeGenOriginal' float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat, - NoiseParams &np_heat_blend, u64 seed) + NoiseParams &np_heat_blend, u64 seed) const { return NoisePerlin2D(&np_heat, pos.X, pos.Z, seed) + @@ -114,7 +114,7 @@ float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat, // For BiomeGen type 'BiomeGenOriginal' float BiomeManager::getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity, - NoiseParams &np_humidity_blend, u64 seed) + NoiseParams &np_humidity_blend, u64 seed) const { return NoisePerlin2D(&np_humidity, pos.X, pos.Z, seed) + @@ -123,7 +123,7 @@ float BiomeManager::getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity // For BiomeGen type 'BiomeGenOriginal' -Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos) +Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos) const { Biome *biome_closest = nullptr; Biome *biome_closest_blend = nullptr; diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 6aadc32fa..0a4471cc3 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -230,10 +230,10 @@ public: // For BiomeGen type 'BiomeGenOriginal' float getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat, - NoiseParams &np_heat_blend, u64 seed); + NoiseParams &np_heat_blend, u64 seed) const; float getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity, - NoiseParams &np_humidity_blend, u64 seed); - Biome *getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos); + NoiseParams &np_humidity_blend, u64 seed) const; + Biome *getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos) const; private: BiomeManager() {}; diff --git a/src/mapgen/mg_decoration.cpp b/src/mapgen/mg_decoration.cpp index db9c696ed..a9b67d239 100644 --- a/src/mapgen/mg_decoration.cpp +++ b/src/mapgen/mg_decoration.cpp @@ -391,6 +391,13 @@ size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) /////////////////////////////////////////////////////////////////////////////// +DecoSchematic::~DecoSchematic() +{ + if (was_cloned) + delete schematic; +} + + ObjDef *DecoSchematic::clone() const { auto def = new DecoSchematic(); @@ -398,9 +405,12 @@ ObjDef *DecoSchematic::clone() const NodeResolver::cloneTo(def); def->rotation = rotation; - /* FIXME: This is not ideal, we only have a pointer to the schematic despite - * not owning it. Optimally this would be a handle. */ - def->schematic = schematic; // not cloned + /* FIXME: We do not own this schematic, yet we only have a pointer to it + * and not a handle. We are left with no option but to clone it ourselves. + * This is a waste of memory and should be replaced with an alternative + * approach sometime. */ + def->schematic = dynamic_cast(schematic->clone()); + def->was_cloned = true; return def; } diff --git a/src/mapgen/mg_decoration.h b/src/mapgen/mg_decoration.h index af8fcd7bf..1f9eb4510 100644 --- a/src/mapgen/mg_decoration.h +++ b/src/mapgen/mg_decoration.h @@ -99,11 +99,13 @@ public: ObjDef *clone() const; DecoSchematic() = default; + virtual ~DecoSchematic(); virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling); Rotation rotation; Schematic *schematic = nullptr; + bool was_cloned = false; // see FIXME inside DecoSchemtic::clone() }; diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index 2e04d30e6..c3bd89f3a 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -43,12 +43,21 @@ SchematicManager::SchematicManager(Server *server) : } +SchematicManager *SchematicManager::clone() const +{ + auto mgr = new SchematicManager(); + assert(mgr); + ObjDefManager::cloneTo(mgr); + return mgr; +} + + void SchematicManager::clear() { EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations - DecorationManager *decomgr = emerge->decomgr; + DecorationManager *decomgr = emerge->getWritableDecorationManager(); for (size_t i = 0; i != decomgr->getNumObjects(); i++) { Decoration *deco = (Decoration *)decomgr->getRaw(i); @@ -79,7 +88,21 @@ Schematic::~Schematic() ObjDef *Schematic::clone() const { - FATAL_ERROR("not cloneable"); + auto def = new Schematic(); + ObjDef::cloneTo(def); + NodeResolver::cloneTo(def); + + def->c_nodes = c_nodes; + def->flags = flags; + def->size = size; + FATAL_ERROR_IF(!schemdata, "Schematic can only be cloned after loading"); + u32 nodecount = size.X * size.Y * size.Z; + def->schemdata = new MapNode[nodecount]; + memcpy(def->schemdata, schemdata, sizeof(MapNode) * nodecount); + def->slice_probs = new u8[size.Y]; + memcpy(def->slice_probs, slice_probs, sizeof(u8) * size.Y); + + return def; } diff --git a/src/mapgen/mg_schematic.h b/src/mapgen/mg_schematic.h index 770f74053..3222085e6 100644 --- a/src/mapgen/mg_schematic.h +++ b/src/mapgen/mg_schematic.h @@ -130,7 +130,7 @@ public: SchematicManager(Server *server); virtual ~SchematicManager() = default; - // not cloneable + SchematicManager *clone() const; virtual void clear(); @@ -145,6 +145,8 @@ public: } private: + SchematicManager() {}; + Server *m_server; }; diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index afe77826a..ba0304be3 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -91,7 +91,7 @@ struct EnumString ModApiMapgen::es_SchematicFormatType[] = {0, NULL}, }; -ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr); +ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr); Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr); @@ -114,7 +114,7 @@ bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic /////////////////////////////////////////////////////////////////////////////// -ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr) +ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr) { if (index < 0) index = lua_gettop(L) + 1 + index; @@ -486,7 +486,7 @@ int ModApiMapgen::l_get_biome_id(lua_State *L) if (!biome_str) return 0; - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) return 0; @@ -508,7 +508,7 @@ int ModApiMapgen::l_get_biome_name(lua_State *L) int biome_id = luaL_checkinteger(L, 1); - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) return 0; @@ -546,7 +546,7 @@ int ModApiMapgen::l_get_heat(lua_State *L) u64 seed; ss >> seed; - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) return 0; @@ -587,7 +587,7 @@ int ModApiMapgen::l_get_humidity(lua_State *L) u64 seed; ss >> seed; - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) return 0; @@ -635,7 +635,7 @@ int ModApiMapgen::l_get_biome_data(lua_State *L) u64 seed; ss >> seed; - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) return 0; @@ -1066,7 +1066,8 @@ int ModApiMapgen::l_get_decoration_id(lua_State *L) if (!deco_str) return 0; - DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; + const DecorationManager *dmgr = + getServer(L)->getEmergeManager()->getDecorationManager(); if (!dmgr) return 0; @@ -1091,7 +1092,7 @@ int ModApiMapgen::l_register_biome(lua_State *L) luaL_checktype(L, index, LUA_TTABLE); const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + BiomeManager *bmgr = getServer(L)->getEmergeManager()->getWritableBiomeManager(); Biome *biome = read_biome_def(L, index, ndef); if (!biome) @@ -1117,9 +1118,10 @@ int ModApiMapgen::l_register_decoration(lua_State *L) luaL_checktype(L, index, LUA_TTABLE); const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); - DecorationManager *decomgr = getServer(L)->getEmergeManager()->decomgr; - BiomeManager *biomemgr = getServer(L)->getEmergeManager()->biomemgr; - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + EmergeManager *emerge = getServer(L)->getEmergeManager(); + DecorationManager *decomgr = emerge->getWritableDecorationManager(); + BiomeManager *biomemgr = emerge->getWritableBiomeManager(); + SchematicManager *schemmgr = emerge->getWritableSchematicManager(); enum DecorationType decotype = (DecorationType)getenumfield(L, index, "deco_type", es_DecorationType, -1); @@ -1274,8 +1276,9 @@ int ModApiMapgen::l_register_ore(lua_State *L) luaL_checktype(L, index, LUA_TTABLE); const NodeDefManager *ndef = getServer(L)->getNodeDefManager(); - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; - OreManager *oremgr = getServer(L)->getEmergeManager()->oremgr; + EmergeManager *emerge = getServer(L)->getEmergeManager(); + BiomeManager *bmgr = emerge->getWritableBiomeManager(); + OreManager *oremgr = emerge->getWritableOreManager(); enum OreType oretype = (OreType)getenumfield(L, index, "ore_type", es_OreType, ORE_SCATTER); @@ -1422,7 +1425,8 @@ int ModApiMapgen::l_register_schematic(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + SchematicManager *schemmgr = + getServer(L)->getEmergeManager()->getWritableSchematicManager(); StringMap replace_names; if (lua_istable(L, 2)) @@ -1449,7 +1453,8 @@ int ModApiMapgen::l_clear_registered_biomes(lua_State *L) { NO_MAP_LOCK_REQUIRED; - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + BiomeManager *bmgr = + getServer(L)->getEmergeManager()->getWritableBiomeManager(); bmgr->clear(); return 0; } @@ -1460,7 +1465,8 @@ int ModApiMapgen::l_clear_registered_decorations(lua_State *L) { NO_MAP_LOCK_REQUIRED; - DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; + DecorationManager *dmgr = + getServer(L)->getEmergeManager()->getWritableDecorationManager(); dmgr->clear(); return 0; } @@ -1471,7 +1477,8 @@ int ModApiMapgen::l_clear_registered_ores(lua_State *L) { NO_MAP_LOCK_REQUIRED; - OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; + OreManager *omgr = + getServer(L)->getEmergeManager()->getWritableOreManager(); omgr->clear(); return 0; } @@ -1482,7 +1489,8 @@ int ModApiMapgen::l_clear_registered_schematics(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr; + SchematicManager *smgr = + getServer(L)->getEmergeManager()->getWritableSchematicManager(); smgr->clear(); return 0; } @@ -1508,7 +1516,8 @@ int ModApiMapgen::l_generate_ores(lua_State *L) u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); - emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); + OreManager *oremgr = (OreManager*) emerge->getOreManager(); // FIXME FIXME + oremgr->placeAllOres(&mg, blockseed, pmin, pmax); return 0; } @@ -1534,7 +1543,8 @@ int ModApiMapgen::l_generate_decorations(lua_State *L) u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); - emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); + DecorationManager *decomgr = (DecorationManager*) emerge->getDecorationManager(); // FIXME FIXME + decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); return 0; } @@ -1614,7 +1624,8 @@ int ModApiMapgen::l_place_schematic(lua_State *L) GET_ENV_PTR; ServerMap *map = &(env->getServerMap()); - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + SchematicManager *schemmgr = (SchematicManager*) + getServer(L)->getEmergeManager()->getSchematicManager(); // FIXME FIXME //// Read position v3s16 p = check_v3s16(L, 1); @@ -1659,7 +1670,8 @@ int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + SchematicManager *schemmgr = (SchematicManager*) + getServer(L)->getEmergeManager()->getSchematicManager(); // FIXME FIXME //// Read VoxelManip object MMVManip *vm = LuaVoxelManip::checkobject(L, 1)->vm; @@ -1707,7 +1719,7 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + const SchematicManager *schemmgr = getServer(L)->getEmergeManager()->getSchematicManager(); //// Read options bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false); @@ -1758,7 +1770,8 @@ int ModApiMapgen::l_read_schematic(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + const SchematicManager *schemmgr = + getServer(L)->getEmergeManager()->getSchematicManager(); //// Read options std::string write_yslice = getstringfield_default(L, 2, "write_yslice_prob", "all"); -- cgit v1.2.3 From c28fbd06a8bfafc9691a92c90f0cf10ec94cd314 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 10 Apr 2020 02:43:49 +0200 Subject: Fix remaining issues with mapgen scriptapi --- src/emerge.h | 6 ++++++ src/mapgen/mg_biome.cpp | 3 ++- src/mapgen/mg_biome.h | 4 +++- src/mapgen/mg_schematic.cpp | 5 +++-- src/mapgen/mg_schematic.h | 5 +++-- src/objdef.h | 1 + src/script/lua_api/l_mapgen.cpp | 24 ++++++++---------------- 7 files changed, 26 insertions(+), 22 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/emerge.h b/src/emerge.h index ab9fca2ba..6f204666d 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -44,6 +44,7 @@ class OreManager; class DecorationManager; class SchematicManager; class Server; +class ModApiMapgen; // Structure containing inputs/outputs for chunk generation struct BlockMakeData { @@ -111,6 +112,11 @@ private: }; class EmergeManager { + /* The mod API needs unchecked access to allow: + * - using decomgr or oremgr to place decos/ores + * - using schemmgr to load and place schematics + */ + friend class ModApiMapgen; public: const NodeDefManager *ndef; bool enable_mapgen_debug_info; diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index f3bc4e829..8c59ac9e6 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -123,7 +123,8 @@ float BiomeManager::getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity // For BiomeGen type 'BiomeGenOriginal' -Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos) const +const Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, + float humidity, v3s16 pos) const { Biome *biome_closest = nullptr; Biome *biome_closest_blend = nullptr; diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 0a4471cc3..57f4aa20d 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -90,6 +90,7 @@ struct BiomeParams { s32 seed; }; +// WARNING: this class is not thread-safe class BiomeGen { public: virtual ~BiomeGen() = default; @@ -233,7 +234,8 @@ public: NoiseParams &np_heat_blend, u64 seed) const; float getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity, NoiseParams &np_humidity_blend, u64 seed) const; - Biome *getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos) const; + const Biome *getBiomeFromNoiseOriginal(float heat, float humidity, + v3s16 pos) const; private: BiomeManager() {}; diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index c3bd89f3a..ba102d997 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -359,7 +359,7 @@ bool Schematic::deserializeFromMts(std::istream *is, bool Schematic::serializeToMts(std::ostream *os, - const std::vector &names) + const std::vector &names) const { std::ostream &ss = *os; @@ -383,7 +383,8 @@ bool Schematic::serializeToMts(std::ostream *os, bool Schematic::serializeToLua(std::ostream *os, - const std::vector &names, bool use_comments, u32 indent_spaces) + const std::vector &names, bool use_comments, + u32 indent_spaces) const { std::ostream &ss = *os; diff --git a/src/mapgen/mg_schematic.h b/src/mapgen/mg_schematic.h index 3222085e6..6b31251b6 100644 --- a/src/mapgen/mg_schematic.h +++ b/src/mapgen/mg_schematic.h @@ -106,9 +106,10 @@ public: bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2); bool deserializeFromMts(std::istream *is, std::vector *names); - bool serializeToMts(std::ostream *os, const std::vector &names); + bool serializeToMts(std::ostream *os, + const std::vector &names) const; bool serializeToLua(std::ostream *os, const std::vector &names, - bool use_comments, u32 indent_spaces); + bool use_comments, u32 indent_spaces) const; void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place); bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place); diff --git a/src/objdef.h b/src/objdef.h index 20565029c..e40324a88 100644 --- a/src/objdef.h +++ b/src/objdef.h @@ -66,6 +66,7 @@ protected: // WARNING: Ownership of ObjDefs is transferred to the ObjDefManager it is // added/set to. Note that ObjDefs managed by ObjDefManager are NOT refcounted, // so the same ObjDef instance must not be referenced multiple +// TODO: const correctness for getter methods class ObjDefManager { public: ObjDefManager(IGameDef *gamedef, ObjDefType type); diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index ba0304be3..584085428 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -490,7 +490,7 @@ int ModApiMapgen::l_get_biome_id(lua_State *L) if (!bmgr) return 0; - Biome *biome = (Biome *)bmgr->getByName(biome_str); + const Biome *biome = (Biome *)bmgr->getByName(biome_str); if (!biome || biome->index == OBJDEF_INVALID_INDEX) return 0; @@ -512,7 +512,7 @@ int ModApiMapgen::l_get_biome_name(lua_State *L) if (!bmgr) return 0; - Biome *b = (Biome *)bmgr->getRaw(biome_id); + const Biome *b = (Biome *)bmgr->getRaw(biome_id); lua_pushstring(L, b->name.c_str()); return 1; @@ -551,8 +551,6 @@ int ModApiMapgen::l_get_heat(lua_State *L) return 0; float heat = bmgr->getHeatAtPosOriginal(pos, np_heat, np_heat_blend, seed); - if (!heat) - return 0; lua_pushnumber(L, heat); @@ -593,8 +591,6 @@ int ModApiMapgen::l_get_humidity(lua_State *L) float humidity = bmgr->getHumidityAtPosOriginal(pos, np_humidity, np_humidity_blend, seed); - if (!humidity) - return 0; lua_pushnumber(L, humidity); @@ -648,7 +644,7 @@ int ModApiMapgen::l_get_biome_data(lua_State *L) if (!humidity) return 0; - Biome *biome = (Biome *)bmgr->getBiomeFromNoiseOriginal(heat, humidity, pos); + const Biome *biome = bmgr->getBiomeFromNoiseOriginal(heat, humidity, pos); if (!biome || biome->index == OBJDEF_INVALID_INDEX) return 0; @@ -1516,8 +1512,7 @@ int ModApiMapgen::l_generate_ores(lua_State *L) u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); - OreManager *oremgr = (OreManager*) emerge->getOreManager(); // FIXME FIXME - oremgr->placeAllOres(&mg, blockseed, pmin, pmax); + emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); return 0; } @@ -1543,8 +1538,7 @@ int ModApiMapgen::l_generate_decorations(lua_State *L) u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); - DecorationManager *decomgr = (DecorationManager*) emerge->getDecorationManager(); // FIXME FIXME - decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); + emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); return 0; } @@ -1624,8 +1618,7 @@ int ModApiMapgen::l_place_schematic(lua_State *L) GET_ENV_PTR; ServerMap *map = &(env->getServerMap()); - SchematicManager *schemmgr = (SchematicManager*) - getServer(L)->getEmergeManager()->getSchematicManager(); // FIXME FIXME + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read position v3s16 p = check_v3s16(L, 1); @@ -1670,8 +1663,7 @@ int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L) { NO_MAP_LOCK_REQUIRED; - SchematicManager *schemmgr = (SchematicManager*) - getServer(L)->getEmergeManager()->getSchematicManager(); // FIXME FIXME + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read VoxelManip object MMVManip *vm = LuaVoxelManip::checkobject(L, 1)->vm; @@ -1727,7 +1719,7 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L) //// Get schematic bool was_loaded = false; - Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr); + const Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr); if (!schem) { schem = load_schematic(L, 1, NULL, NULL); was_loaded = true; -- cgit v1.2.3 From 454dbf83a9bf292910c1495a2aa49fd8b960c28f Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Thu, 7 May 2020 22:38:41 +0200 Subject: Server class code cleanups (#9769) * Server::overrideDayNightRatio doesn't require to return bool There is no sense to sending null player, the caller should send a valid object * Server::init: make private & cleanup This function is always called before start() and loads some variables which can be loaded in constructor directly. Make it private and call it directly in start * Split Server inventory responsibility to a dedicated object This splits permit to found various historical issues: * duplicate lookups on player connection * sending inventory to non related player when a player connects * non friendly lookups on detached inventories ownership This reduce the detached inventory complexity and also increased the lookup performance in a quite interesting way for servers with thousands of inventories. --- src/client/game.cpp | 1 - src/main.cpp | 2 - src/network/serverpackethandler.cpp | 11 ++- src/script/lua_api/l_base.cpp | 5 + src/script/lua_api/l_base.h | 2 + src/script/lua_api/l_inventory.cpp | 13 +-- src/script/lua_api/l_object.cpp | 7 +- src/server.cpp | 192 +++++------------------------------- src/server.h | 33 ++----- src/server/CMakeLists.txt | 1 + src/server/serverinventorymgr.cpp | 192 ++++++++++++++++++++++++++++++++++++ src/server/serverinventorymgr.h | 60 +++++++++++ 12 files changed, 312 insertions(+), 207 deletions(-) create mode 100644 src/server/serverinventorymgr.cpp create mode 100644 src/server/serverinventorymgr.h (limited to 'src/script/lua_api') diff --git a/src/client/game.cpp b/src/client/game.cpp index 1577a37db..4d7a85526 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1302,7 +1302,6 @@ bool Game::createSingleplayerServer(const std::string &map_dir, } server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); - server->init(); server->start(); return true; diff --git a/src/main.cpp b/src/main.cpp index 147f686ed..b3b17c2d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -887,7 +887,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true, &iface); - server.init(); g_term_console.setup(&iface, &kill, admin_nick); @@ -922,7 +921,6 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & // Create server Server server(game_params.world_path, game_params.game_spec, false, bind_addr, true); - server.init(); server.start(); // Run server diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index adaa9a965..39a912827 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" @@ -620,9 +621,9 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ma->from_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ma->from_inv); + m_inventory_mgr->setInventoryModified(ma->from_inv); if (ma->from_inv != ma->to_inv) - setInventoryModified(ma->to_inv); + m_inventory_mgr->setInventoryModified(ma->to_inv); bool from_inv_is_current_player = (ma->from_inv.type == InventoryLocation::PLAYER) && @@ -687,7 +688,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) da->from_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(da->from_inv); + m_inventory_mgr->setInventoryModified(da->from_inv); /* Disable dropping items out of craftpreview @@ -723,7 +724,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) ca->craft_inv.applyCurrentPlayer(player->getName()); - setInventoryModified(ca->craft_inv); + m_inventory_mgr->setInventoryModified(ca->craft_inv); //bool craft_inv_is_current_player = // (ca->craft_inv.type == InventoryLocation::PLAYER) && @@ -739,7 +740,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) } // Do the action - a->apply(this, playersao, this); + a->apply(m_inventory_mgr.get(), playersao, this); // Eat the action delete a; } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index b8658f62b..2bee09436 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -45,6 +45,11 @@ Server *ModApiBase::getServer(lua_State *L) return getScriptApiBase(L)->getServer(); } +ServerInventoryManager *ModApiBase::getServerInventoryMgr(lua_State *L) +{ + return getScriptApiBase(L)->getServer()->getInventoryMgr(); +} + #ifndef SERVER Client *ModApiBase::getClient(lua_State *L) { diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index e32647628..65fce8481 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -38,12 +38,14 @@ class GUIEngine; class ScriptApiBase; class Server; class Environment; +class ServerInventoryManager; class ModApiBase : protected LuaHelper { public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); + static ServerInventoryManager *getServerInventoryMgr(lua_State *L); #ifndef SERVER static Client* getClient(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 4c8977898..e41b5cb41 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" +#include "server/serverinventorymgr.h" #include "remoteplayer.h" /* @@ -38,7 +39,7 @@ InvRef* InvRef::checkobject(lua_State *L, int narg) Inventory* InvRef::getinv(lua_State *L, InvRef *ref) { - return getServer(L)->getInventory(ref->m_loc); + return getServerInventoryMgr(L)->getInventory(ref->m_loc); } InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, @@ -54,7 +55,7 @@ InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed - getServer(L)->setInventoryModified(ref->m_loc); + getServerInventoryMgr(L)->setInventoryModified(ref->m_loc); } // Exported functions @@ -497,7 +498,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -515,7 +516,7 @@ int ModApiInventory::l_get_inventory(lua_State *L) lua_pop(L, 1); } - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); @@ -530,7 +531,7 @@ int ModApiInventory::l_create_detached_inventory_raw(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *name = luaL_checkstring(L, 1); std::string player = readParam(L, 2, ""); - if (getServer(L)->createDetachedInventory(name, player) != NULL) { + if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) { InventoryLocation loc; loc.setDetached(name); InvRef::create(L, loc); @@ -545,7 +546,7 @@ int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L) { NO_MAP_LOCK_REQUIRED; const std::string &name = luaL_checkstring(L, 1); - lua_pushboolean(L, getServer(L)->removeDetachedInventory(name)); + lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name)); return 1; } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f71130378..0a9f3117b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_server.h" #include "server/luaentity_sao.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" /* ObjectRef @@ -289,7 +290,7 @@ int ObjectRef::l_get_inventory(lua_State *L) if (co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if (getServer(L)->getInventory(loc) != NULL) + if (getServerInventoryMgr(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -2172,9 +2173,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) ratio = readParam(L, 2); } - if (!getServer(L)->overrideDayNightRatio(player, do_override, ratio)) - return 0; - + getServer(L)->overrideDayNightRatio(player, do_override, ratio); lua_pushboolean(L, true); return 1; } diff --git a/src/server.cpp b/src/server.cpp index 05584be2d..16e026ce2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat_interface.h" #include "remoteplayer.h" #include "server/player_sao.h" +#include "server/serverinventorymgr.h" #include "translation.h" class ClientNotFoundException : public BaseException @@ -338,11 +339,6 @@ Server::~Server() infostream << "Server: Deinitializing scripting" << std::endl; delete m_script; - // Delete detached inventories - for (auto &detached_inventory : m_detached_inventories) { - delete detached_inventory.second; - } - while (!m_unsent_map_edit_queue.empty()) { delete m_unsent_map_edit_queue.front(); m_unsent_map_edit_queue.pop(); @@ -388,6 +384,9 @@ void Server::init() m_script = new ServerScripting(this); + // Must be created before mod loading because we have some inventory creation + m_inventory_mgr = std::unique_ptr(new ServerInventoryManager()); + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); m_modmgr->loadMods(m_script); @@ -422,6 +421,7 @@ void Server::init() // Initialize Environment m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + m_inventory_mgr->setEnv(m_env); m_clients.setEnv(m_env); if (!servermap->settings_mgr.makeMapgenParams()) @@ -443,6 +443,8 @@ void Server::init() m_env->loadMeta(); + // Those settings can be overwritten in world.mt, they are + // intended to be cached after environment loading. m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags"); @@ -451,6 +453,8 @@ void Server::init() void Server::start() { + init(); + infostream << "Starting server on " << m_bind_addr.serializeString() << "..." << std::endl; @@ -1180,82 +1184,6 @@ void Server::onMapEditEvent(const MapEditEvent &event) m_unsent_map_edit_queue.push(new MapEditEvent(event)); } -Inventory* Server::getInventory(const InventoryLocation &loc) -{ - switch (loc.type) { - case InventoryLocation::UNDEFINED: - case InventoryLocation::CURRENT_PLAYER: - break; - case InventoryLocation::PLAYER: - { - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - if(!player) - return NULL; - PlayerSAO *playersao = player->getPlayerSAO(); - if(!playersao) - return NULL; - return playersao->getInventory(); - } - break; - case InventoryLocation::NODEMETA: - { - NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); - if(!meta) - return NULL; - return meta->getInventory(); - } - break; - case InventoryLocation::DETACHED: - { - if(m_detached_inventories.count(loc.name) == 0) - return NULL; - return m_detached_inventories[loc.name]; - } - break; - default: - sanity_check(false); // abort - break; - } - return NULL; -} - -void Server::setInventoryModified(const InventoryLocation &loc) -{ - switch(loc.type){ - case InventoryLocation::UNDEFINED: - break; - case InventoryLocation::PLAYER: - { - - RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); - - if (!player) - return; - - player->setModified(true); - player->inventory.setModified(true); - // Updates are sent in ServerEnvironment::step() - } - break; - case InventoryLocation::NODEMETA: - { - MapEditEvent event; - event.type = MEET_BLOCK_NODE_METADATA_CHANGED; - event.p = loc.p; - m_env->getMap().dispatchEvent(event); - } - break; - case InventoryLocation::DETACHED: - { - // Updates are sent in ServerEnvironment::step() - } - break; - default: - sanity_check(false); // abort - break; - } -} - void Server::SetBlocksNotSent(std::map& block) { std::vector clients = m_clients.getClientIDs(); @@ -2712,40 +2640,20 @@ void Server::sendRequestedMedia(session_t peer_id, } } -void Server::sendDetachedInventory(const std::string &name, session_t peer_id) +void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { - const auto &inv_it = m_detached_inventories.find(name); - const auto &player_it = m_detached_inventories_player.find(name); - - if (player_it == m_detached_inventories_player.end() || - player_it->second.empty()) { - // OK. Send to everyone - } else { - if (!m_env) - return; // Mods are not done loading - - RemotePlayer *p = m_env->getPlayer(player_it->second.c_str()); - if (!p) - return; // Player is offline - - if (peer_id != PEER_ID_INEXISTENT && peer_id != p->getPeerId()) - return; // Caller requested send to a different player, so don't send. - - peer_id = p->getPeerId(); - } - NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); pkt << name; - if (inv_it == m_detached_inventories.end()) { + if (!inventory) { pkt << false; // Remove inventory } else { pkt << true; // Update inventory // Serialization & NetworkPacket isn't a love story std::ostringstream os(std::ios_base::binary); - inv_it->second->serialize(os); - inv_it->second->setModified(false); + inventory->serialize(os); + inventory->setModified(false); const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients @@ -2760,16 +2668,17 @@ void Server::sendDetachedInventory(const std::string &name, session_t peer_id) void Server::sendDetachedInventories(session_t peer_id, bool incremental) { - for (const auto &detached_inventory : m_detached_inventories) { - const std::string &name = detached_inventory.first; - if (incremental) { - Inventory *inv = detached_inventory.second; - if (!inv || !inv->checkModified()) - continue; - } - - sendDetachedInventory(name, peer_id); + // Lookup player name, to filter detached inventories just after + std::string peer_name; + if (peer_id != PEER_ID_INEXISTENT) { + peer_name = getClient(peer_id, CS_Created)->getName(); } + + auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) { + sendDetachedInventory(inv, name, peer_id); + }; + + m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb); } /* @@ -3442,15 +3351,12 @@ void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms) SendCloudParams(player->getPeerId(), params); } -bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, +void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, float ratio) { - if (!player) - return false; - + sanity_check(player); player->overrideDayNightRatio(do_override, ratio); SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio); - return true; } void Server::notifyPlayers(const std::wstring &msg) @@ -3541,52 +3447,6 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player) -{ - if(m_detached_inventories.count(name) > 0){ - infostream<<"Server clearing detached inventory \""<second; - m_detached_inventories.erase(inv_it); - - if (!m_env) // Mods are not done loading - return true; - - const auto &player_it = m_detached_inventories_player.find(name); - if (player_it != m_detached_inventories_player.end()) { - RemotePlayer *player = m_env->getPlayer(player_it->second.c_str()); - - if (player && player->getPeerId() != PEER_ID_INEXISTENT) - sendDetachedInventory(name, player->getPeerId()); - - m_detached_inventories_player.erase(player_it); - } else { - // Notify all players about the change - sendDetachedInventory(name, PEER_ID_INEXISTENT); - } - return true; -} - // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, @@ -3607,7 +3467,7 @@ bool Server::rollbackRevertActions(const std::list &actions, for (const RollbackAction &action : actions) { num_tried++; - bool success = action.applyRevert(map, this, this); + bool success = action.applyRevert(map, m_inventory_mgr.get(), this); if(!success){ num_failed++; std::ostringstream os; diff --git a/src/server.h b/src/server.h index 71059dd30..7a1de9370 100644 --- a/src/server.h +++ b/src/server.h @@ -68,6 +68,7 @@ struct MoonParams; struct StarParams; class ServerThread; class ServerModManager; +class ServerInventoryManager; enum ClientDeletionReason { CDR_LEAVE, @@ -116,7 +117,7 @@ struct ServerPlayingSound }; class Server : public con::PeerHandler, public MapEventReceiver, - public InventoryManager, public IGameDef + public IGameDef { public: /* @@ -134,7 +135,6 @@ public: ~Server(); DISABLE_CLASS_COPY(Server); - void init(); void start(); void stop(); // This is mainly a way to pass the time to the server. @@ -196,12 +196,6 @@ public: */ void onMapEditEvent(const MapEditEvent &event); - /* - Shall be called with the environment and the connection locked. - */ - Inventory* getInventory(const InventoryLocation &loc); - void setInventoryModified(const InventoryLocation &loc); - // Connection must be locked when called std::wstring getStatusString(); inline double getUptime() const { return m_uptime_counter->get(); } @@ -253,10 +247,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); - // Creates or resets inventory - Inventory *createDetachedInventory(const std::string &name, - const std::string &player = ""); - bool removeDetachedInventory(const std::string &name); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } + void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); // Envlock and conlock should be locked when using scriptapi ServerScripting *getScriptIface(){ return m_script; } @@ -318,7 +310,7 @@ public: void setClouds(RemotePlayer *player, const CloudParams ¶ms); - bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); + void overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness); /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); @@ -389,6 +381,8 @@ private: float m_timer = 0.0f; }; + void init(); + void SendMovement(session_t peer_id); void SendHP(session_t peer_id, u16 hp); void SendBreath(session_t peer_id, u16 breath); @@ -457,8 +451,6 @@ private: void sendRequestedMedia(session_t peer_id, const std::vector &tosend); - void sendDetachedInventory(const std::string &name, session_t peer_id); - // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, u16 amount, float spawntime, @@ -656,14 +648,6 @@ private: s32 m_next_sound_id = 0; // positive values only s32 nextSoundId(); - /* - Detached inventories (behind m_env_mutex) - */ - // key = name - std::map m_detached_inventories; - // value = "" (visible to all players) or player name - std::map m_detached_inventories_player; - std::unordered_map m_mod_storages; float m_mod_storage_save_timer = 10.0f; @@ -674,6 +658,9 @@ private: // ModChannel manager std::unique_ptr m_modchannel_mgr; + // Inventory manager + std::unique_ptr m_inventory_mgr; + // Global server metrics backend std::unique_ptr m_metrics_backend; diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 4d94504f6..0a5a8f3a7 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -4,5 +4,6 @@ set(server_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/player_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveractiveobject.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serverinventorymgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit_sao.cpp PARENT_SCOPE) diff --git a/src/server/serverinventorymgr.cpp b/src/server/serverinventorymgr.cpp new file mode 100644 index 000000000..555e01ec6 --- /dev/null +++ b/src/server/serverinventorymgr.cpp @@ -0,0 +1,192 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +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 "serverinventorymgr.h" +#include "map.h" +#include "nodemetadata.h" +#include "player_sao.h" +#include "remoteplayer.h" +#include "server.h" +#include "serverenvironment.h" + +ServerInventoryManager::ServerInventoryManager() : InventoryManager() +{ +} + +ServerInventoryManager::~ServerInventoryManager() +{ + // Delete detached inventories + for (auto &detached_inventory : m_detached_inventories) { + delete detached_inventory.second.inventory; + } +} + +Inventory *ServerInventoryManager::getInventory(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + case InventoryLocation::PLAYER: { + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + if (!player) + return NULL; + PlayerSAO *playersao = player->getPlayerSAO(); + if (!playersao) + return NULL; + return playersao->getInventory(); + } break; + case InventoryLocation::NODEMETA: { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if (!meta) + return NULL; + return meta->getInventory(); + } break; + case InventoryLocation::DETACHED: { + auto it = m_detached_inventories.find(loc.name); + if (it == m_detached_inventories.end()) + return nullptr; + return it->second.inventory; + } break; + default: + sanity_check(false); // abort + break; + } + return NULL; +} + +void ServerInventoryManager::setInventoryModified(const InventoryLocation &loc) +{ + switch (loc.type) { + case InventoryLocation::UNDEFINED: + break; + case InventoryLocation::PLAYER: { + + RemotePlayer *player = m_env->getPlayer(loc.name.c_str()); + + if (!player) + return; + + player->setModified(true); + player->inventory.setModified(true); + // Updates are sent in ServerEnvironment::step() + } break; + case InventoryLocation::NODEMETA: { + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = loc.p; + m_env->getMap().dispatchEvent(event); + } break; + case InventoryLocation::DETACHED: { + // Updates are sent in ServerEnvironment::step() + } break; + default: + sanity_check(false); // abort + break; + } +} + +Inventory *ServerInventoryManager::createDetachedInventory( + const std::string &name, IItemDefManager *idef, const std::string &player) +{ + if (m_detached_inventories.count(name) > 0) { + infostream << "Server clearing detached inventory \"" << name << "\"" + << std::endl; + delete m_detached_inventories[name].inventory; + } else { + infostream << "Server creating detached inventory \"" << name << "\"" + << std::endl; + } + + Inventory *inv = new Inventory(idef); + sanity_check(inv); + m_detached_inventories[name].inventory = inv; + if (!player.empty()) { + m_detached_inventories[name].owner = player; + + if (!m_env) + return inv; // Mods are not loaded yet, ignore + + RemotePlayer *p = m_env->getPlayer(name.c_str()); + + // if player is connected, send him the inventory + if (p && p->getPeerId() != PEER_ID_INEXISTENT) { + m_env->getGameDef()->sendDetachedInventory( + inv, name, p->getPeerId()); + } + } else { + if (!m_env) + return inv; // Mods are not loaded yet, don't send + + // Inventory is for everybody, broadcast + m_env->getGameDef()->sendDetachedInventory(inv, name, PEER_ID_INEXISTENT); + } + + return inv; +} + +bool ServerInventoryManager::removeDetachedInventory(const std::string &name) +{ + const auto &inv_it = m_detached_inventories.find(name); + if (inv_it == m_detached_inventories.end()) + return false; + + delete inv_it->second.inventory; + const std::string &owner = inv_it->second.owner; + + if (!owner.empty()) { + RemotePlayer *player = m_env->getPlayer(owner.c_str()); + + if (player && player->getPeerId() != PEER_ID_INEXISTENT) + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, player->getPeerId()); + + } else { + // Notify all players about the change + m_env->getGameDef()->sendDetachedInventory( + nullptr, name, PEER_ID_INEXISTENT); + } + + m_detached_inventories.erase(inv_it); + + return true; +} + +void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name, + bool incremental, + std::function apply_cb) +{ + for (const auto &detached_inventory : m_detached_inventories) { + const DetachedInventory &dinv = detached_inventory.second; + if (incremental) { + if (!dinv.inventory || !dinv.inventory->checkModified()) + continue; + } + + // if we are pushing inventories to a specific player + // we should filter to send only the right inventories + if (!peer_name.empty()) { + const std::string &attached_player = dinv.owner; + if (!attached_player.empty() && peer_name != attached_player) + continue; + } + + apply_cb(detached_inventory.first, detached_inventory.second.inventory); + } +} diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h new file mode 100644 index 000000000..d0aac4dae --- /dev/null +++ b/src/server/serverinventorymgr.h @@ -0,0 +1,60 @@ +/* +Minetest +Copyright (C) 2010-2020 Minetest core development team + +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. +*/ + +#pragma once + +#include "inventorymanager.h" +#include + +class ServerEnvironment; + +class ServerInventoryManager : public InventoryManager +{ +public: + ServerInventoryManager(); + virtual ~ServerInventoryManager(); + + void setEnv(ServerEnvironment *env) + { + assert(!m_env); + m_env = env; + } + + Inventory *getInventory(const InventoryLocation &loc); + void setInventoryModified(const InventoryLocation &loc); + + // Creates or resets inventory + Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, + const std::string &player = ""); + bool removeDetachedInventory(const std::string &name); + + void sendDetachedInventories(const std::string &peer_name, bool incremental, + std::function apply_cb); + +private: + struct DetachedInventory + { + Inventory *inventory; + std::string owner; + }; + + ServerEnvironment *m_env = nullptr; + + std::unordered_map m_detached_inventories; +}; \ No newline at end of file -- cgit v1.2.3 From 36d35f2fe31a429c1510df680801940472416d45 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 14 May 2020 21:16:45 +0200 Subject: CSM: Bugfixes to camera:get_pos() and camera:get_fov() closes #9857 --- clientmods/preview/init.lua | 2 +- doc/client_lua_api.txt | 2 +- src/client/camera.cpp | 2 +- src/script/lua_api/l_camera.cpp | 59 +++++++++++++++++++++++++---------------- src/script/lua_api/l_env.cpp | 16 +++++------ 5 files changed, 47 insertions(+), 34 deletions(-) (limited to 'src/script/lua_api') diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua index d2440369a..089955d2f 100644 --- a/clientmods/preview/init.lua +++ b/clientmods/preview/init.lua @@ -79,7 +79,7 @@ core.register_on_item_use(function(itemstack, pointed_thing) return false end - local pos = vector.add(core.localplayer:get_pos(), core.camera:get_offset()) + local pos = core.camera:get_pos() local pos2 = vector.add(pos, vector.multiply(core.camera:get_look_dir(), 100)) local rc = core.raycast(pos, pos2) diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 53442d308..c9cd8ac93 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -967,7 +967,7 @@ Please do not try to access the reference until the camera is initialized, other * `get_camera_mode()` * Returns 0, 1, or 2 as described above * `get_fov()` - * Returns: + * Returns a table with X, Y, maximum and actual FOV in degrees: ```lua { diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 1a5253db4..9b311171a 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -540,7 +540,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r m_aspect = (f32) window_size.X / (f32) window_size.Y; m_fov_y = m_curr_fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) - m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); + m_fov_y *= core::clamp(sqrt(16./10. / m_aspect), 1.0, 1.4); m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index 9c1470284..bfa60be67 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -51,6 +51,7 @@ void LuaCamera::create(lua_State *L, Camera *m) lua_setfield(L, objectstable, "camera"); } +// set_camera_mode(self, mode) int LuaCamera::l_set_camera_mode(lua_State *L) { Camera *camera = getobject(L, 1); @@ -67,17 +68,19 @@ int LuaCamera::l_set_camera_mode(lua_State *L) return 0; } +// get_camera_mode(self) int LuaCamera::l_get_camera_mode(lua_State *L) { Camera *camera = getobject(L, 1); if (!camera) return 0; - lua_pushnumber(L, (int)camera->getCameraMode()); + lua_pushinteger(L, (int)camera->getCameraMode()); return 1; } +// get_fov(self) int LuaCamera::l_get_fov(lua_State *L) { Camera *camera = getobject(L, 1); @@ -85,9 +88,9 @@ int LuaCamera::l_get_fov(lua_State *L) return 0; lua_newtable(L); - lua_pushnumber(L, camera->getFovX() * core::DEGTORAD); + lua_pushnumber(L, camera->getFovX() * core::RADTODEG); lua_setfield(L, -2, "x"); - lua_pushnumber(L, camera->getFovY() * core::DEGTORAD); + lua_pushnumber(L, camera->getFovY() * core::RADTODEG); lua_setfield(L, -2, "y"); lua_pushnumber(L, camera->getCameraNode()->getFOV() * core::RADTODEG); lua_setfield(L, -2, "actual"); @@ -96,16 +99,18 @@ int LuaCamera::l_get_fov(lua_State *L) return 1; } +// get_pos(self) int LuaCamera::l_get_pos(lua_State *L) { Camera *camera = getobject(L, 1); if (!camera) return 0; - push_v3f(L, camera->getPosition()); + push_v3f(L, camera->getPosition() / BS); return 1; } +// get_offset(self) int LuaCamera::l_get_offset(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); @@ -115,38 +120,40 @@ int LuaCamera::l_get_offset(lua_State *L) return 1; } +// get_look_dir(self) int LuaCamera::l_get_look_dir(lua_State *L) { - LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); - sanity_check(player); - - float pitch = -1.0 * player->getPitch() * core::DEGTORAD; - float yaw = (player->getYaw() + 90.) * core::DEGTORAD; - v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), - std::cos(pitch) * std::sin(yaw)); + Camera *camera = getobject(L, 1); + if (!camera) + return 0; - push_v3f(L, v); + push_v3f(L, camera->getDirection()); return 1; } +// get_look_horizontal(self) +// FIXME: wouldn't localplayer be a better place for this? int LuaCamera::l_get_look_horizontal(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); sanity_check(player); - lua_pushnumber(L, (player->getYaw() + 90.) * core::DEGTORAD); + lua_pushnumber(L, (player->getYaw() + 90.f) * core::DEGTORAD); return 1; } +// get_look_vertical(self) +// FIXME: wouldn't localplayer be a better place for this? int LuaCamera::l_get_look_vertical(lua_State *L) { LocalPlayer *player = getClient(L)->getEnv().getLocalPlayer(); sanity_check(player); - lua_pushnumber(L, -1.0 * player->getPitch() * core::DEGTORAD); + lua_pushnumber(L, -1.0f * player->getPitch() * core::DEGTORAD); return 1; } +// get_aspect_ratio(self) int LuaCamera::l_get_aspect_ratio(lua_State *L) { Camera *camera = getobject(L, 1); @@ -215,13 +222,19 @@ void LuaCamera::Register(lua_State *L) lua_pop(L, 1); } +// clang-format off const char LuaCamera::className[] = "Camera"; -const luaL_Reg LuaCamera::methods[] = {luamethod(LuaCamera, set_camera_mode), - luamethod(LuaCamera, get_camera_mode), luamethod(LuaCamera, get_fov), - luamethod(LuaCamera, get_pos), luamethod(LuaCamera, get_offset), - luamethod(LuaCamera, get_look_dir), - luamethod(LuaCamera, get_look_vertical), - luamethod(LuaCamera, get_look_horizontal), - luamethod(LuaCamera, get_aspect_ratio), - - {0, 0}}; +const luaL_Reg LuaCamera::methods[] = { + luamethod(LuaCamera, set_camera_mode), + luamethod(LuaCamera, get_camera_mode), + luamethod(LuaCamera, get_fov), + luamethod(LuaCamera, get_pos), + luamethod(LuaCamera, get_offset), + luamethod(LuaCamera, get_look_dir), + luamethod(LuaCamera, get_look_vertical), + luamethod(LuaCamera, get_look_horizontal), + luamethod(LuaCamera, get_aspect_ratio), + + {0, 0} +}; +// clang-format on diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index cabca124d..b8a8a5ce1 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -780,8 +780,8 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) #ifndef SERVER // Client API limitations - if (getClient(L)) - radius = getClient(L)->CSMClampRadius(pos, radius); + if (Client *client = getClient(L)) + radius = client->CSMClampRadius(pos, radius); #endif for (int d = start_radius; d <= radius; d++) { @@ -811,9 +811,9 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) const NodeDefManager *ndef = env->getGameDef()->ndef(); #ifndef SERVER - if (getClient(L)) { - minp = getClient(L)->CSMClampPos(minp); - maxp = getClient(L)->CSMClampPos(maxp); + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); } #endif @@ -887,9 +887,9 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) const NodeDefManager *ndef = env->getGameDef()->ndef(); #ifndef SERVER - if (getClient(L)) { - minp = getClient(L)->CSMClampPos(minp); - maxp = getClient(L)->CSMClampPos(maxp); + if (Client *client = getClient(L)) { + minp = client->CSMClampPos(minp); + maxp = client->CSMClampPos(maxp); } #endif -- cgit v1.2.3 From a9c3a423231e26ea3edee51d5f0bf949ca8e529b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 17 May 2020 19:09:10 +0100 Subject: Add core.open_url() to main menu API (#8592) --- .../java/net/minetest/minetest/GameActivity.java | 6 ++ builtin/mainmenu/dlg_contentstore.lua | 113 ++++----------------- builtin/mainmenu/tab_credits.lua | 11 +- doc/menu_lua_api.txt | 5 + src/porting.cpp | 30 +++++- src/porting.h | 3 + src/porting_android.cpp | 12 +++ src/porting_android.h | 2 + src/script/lua_api/l_mainmenu.cpp | 9 ++ src/script/lua_api/l_mainmenu.h | 3 + 10 files changed, 98 insertions(+), 96 deletions(-) (limited to 'src/script/lua_api') diff --git a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java index 02b61b598..635512569 100644 --- a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -22,6 +22,7 @@ package net.minetest.minetest; import android.app.NativeActivity; import android.content.Intent; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.View; @@ -117,4 +118,9 @@ public class GameActivity extends NativeActivity { public int getDisplayWidth() { return getResources().getDisplayMetrics().widthPixels; } + + public void openURL(String url) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(browserIntent); + } } diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 3bc5f60bb..ce5c061c6 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -16,7 +16,6 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. local store = { packages = {}, packages_full = {} } -local package_dialog = {} -- Screenshot local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" @@ -44,8 +43,6 @@ local filter_types_type = { } - - local function download_package(param) if core.download_file(param.package.url, param.filename) then return { @@ -195,74 +192,6 @@ local function get_screenshot(package) return defaulttexturedir .. "loading_screenshot.png" end - - -function package_dialog.get_formspec() - local package = package_dialog.package - - store.update_paths() - - local formspec = { - "size[9,4;true]", - "image[0,1;4.5,3;", core.formspec_escape(get_screenshot(package)), ']', - "label[3.8,1;", - minetest.colorize(mt_color_green, core.formspec_escape(package.title)), "\n", - minetest.colorize('#BFBFBF', "by " .. core.formspec_escape(package.author)), "]", - "textarea[4,2;5.3,2;;;", core.formspec_escape(package.short_description), "]", - "button[0,0;2,1;back;", fgettext("Back"), "]", - } - - if not package.path then - formspec[#formspec + 1] = "button[7,0;2,1;install;" - formspec[#formspec + 1] = fgettext("Install") - formspec[#formspec + 1] = "]" - elseif package.installed_release < package.release then - -- The install_ action also handles updating - formspec[#formspec + 1] = "button[7,0;2,1;install;" - formspec[#formspec + 1] = fgettext("Update") - formspec[#formspec + 1] = "]" - formspec[#formspec + 1] = "button[5,0;2,1;uninstall;" - formspec[#formspec + 1] = fgettext("Uninstall") - formspec[#formspec + 1] = "]" - else - formspec[#formspec + 1] = "button[7,0;2,1;uninstall;" - formspec[#formspec + 1] = fgettext("Uninstall") - formspec[#formspec + 1] = "]" - end - - return table.concat(formspec, "") -end - -function package_dialog.handle_submit(this, fields) - if fields.back then - this:delete() - return true - end - - if fields.install then - start_install(this, package_dialog.package) - return true - end - - if fields.uninstall then - local dlg_delmod = create_delete_content_dlg(package_dialog.package) - dlg_delmod:set_parent(this) - this:hide() - dlg_delmod:show() - return true - end - - return false -end - -function package_dialog.create(package) - package_dialog.package = package - return dialog_create("package_view", - package_dialog.get_formspec, - package_dialog.handle_submit, - nil) -end - function store.load() local tmpdir = os.tempfolder() local target = tmpdir .. DIR_DELIM .. "packages.json" @@ -462,44 +391,45 @@ function store.get_formspec(dlgdata) minetest.colorize("#BFBFBF", " by " .. package.author)) formspec[#formspec + 1] = "]" - -- description - if package.path and package.installed_release < package.release then - formspec[#formspec + 1] = "textarea[1.25,0.3;7.5,1;;;" - else - formspec[#formspec + 1] = "textarea[1.25,0.3;9,1;;;" - end - formspec[#formspec + 1] = core.formspec_escape(package.short_description) - formspec[#formspec + 1] = "]" - -- buttons + local description_width = 7.5 if not package.path then - formspec[#formspec + 1] = "button[9.9,0;1.5,1;install_" + formspec[#formspec + 1] = "button[8.4,0;1.5,1;install_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Install") formspec[#formspec + 1] = "]" else if package.installed_release < package.release then + description_width = 6 + -- The install_ action also handles updating - formspec[#formspec + 1] = "button[8.4,0;1.5,1;install_" + formspec[#formspec + 1] = "button[6.9,0;1.5,1;install_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Update") formspec[#formspec + 1] = "]" end - formspec[#formspec + 1] = "button[9.9,0;1.5,1;uninstall_" + formspec[#formspec + 1] = "button[8.4,0;1.5,1;uninstall_" formspec[#formspec + 1] = tostring(i) formspec[#formspec + 1] = ";" formspec[#formspec + 1] = fgettext("Uninstall") formspec[#formspec + 1] = "]" end - --formspec[#formspec + 1] = "button[9.9,0;1.5,1;view_" - --formspec[#formspec + 1] = tostring(i) - --formspec[#formspec + 1] = ";" - --formspec[#formspec + 1] = fgettext("View") - --formspec[#formspec + 1] = "]" + formspec[#formspec + 1] = "button[9.9,0;1.5,1;view_" + formspec[#formspec + 1] = tostring(i) + formspec[#formspec + 1] = ";" + formspec[#formspec + 1] = fgettext("View") + formspec[#formspec + 1] = "]" + + -- description + formspec[#formspec + 1] = "textarea[1.25,0.3;" + formspec[#formspec + 1] = tostring(description_width) + formspec[#formspec + 1] = ",1;;;" + formspec[#formspec + 1] = core.formspec_escape(package.short_description) + formspec[#formspec + 1] = "]" formspec[#formspec + 1] = "container_end[]" end @@ -576,10 +506,9 @@ function store.handle_submit(this, fields) end if fields["view_" .. i] then - local dlg = package_dialog.create(package) - dlg:set_parent(this) - this:hide() - dlg:show() + local url = ("%s/packages/%s?protocol_version=%d"):format( + core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto()) + core.open_url(url) return true end end diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index 962d2a3b4..c2b7e503a 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -101,8 +101,8 @@ return { local logofile = defaulttexturedir .. "logo.png" local version = core.get_version() return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" .. - "label[0.5,3.2;" .. version.project .. " " .. version.string .. "]" .. - "label[0.5,3.5;http://minetest.net]" .. + "label[0.5,2.8;" .. version.project .. " " .. version.string .. "]" .. + "button[0.5,3;2,2;homepage;minetest.net]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. "table[3.5,-0.25;8.5,6.05;list_credits;" .. @@ -115,5 +115,10 @@ return { "#FFFF00," .. fgettext("Previous Contributors") .. ",," .. buildCreditList(previous_contributors) .. "," .. ";1]" - end + end, + cbf_button_handler = function(this, fields, name, tabdata) + if fields.homepage then + core.open_url("https://www.minetest.net") + end + end, } diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 8f5460acb..485c50110 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -234,6 +234,11 @@ core.get_min_supp_proto() core.get_max_supp_proto() ^ returns the maximum supported network protocol version +Other: +core.open_url(url) +^ opens the URL in a web browser, returns false on failure. +^ Must begin with http:// or https:// + Async: core.handle_async(async_job,parameters,finished) ^ execute a function asynchronously diff --git a/src/porting.cpp b/src/porting.cpp index c0381ad06..ef1640467 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -33,22 +33,28 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include + #include #endif #if !defined(_WIN32) #include #include + #if !defined(__ANDROID__) + #include + #endif #endif #if defined(__hpux) #define _PSTAT64 #include #endif +#if defined(__ANDROID__) + #include "porting_android.h" +#endif #include "config.h" #include "debug.h" #include "filesys.h" #include "log.h" #include "util/string.h" -#include "settings.h" #include #include #include @@ -697,6 +703,28 @@ int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...) return c; } +bool openURL(const std::string &url) +{ + if ((url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://") || + url.find_first_of("\r\n") != std::string::npos) { + errorstream << "Invalid url: " << url << std::endl; + return false; + } + +#if defined(_WIN32) + return (intptr_t)ShellExecuteA(NULL, NULL, url.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32; +#elif defined(__ANDROID__) + openURLAndroid(url); + return true; +#elif defined(__APPLE__) + const char *argv[] = {"open", url.c_str(), NULL}; + return posix_spawnp(NULL, "open", NULL, NULL, (char**)argv, environ) == 0; +#else + const char *argv[] = {"xdg-open", url.c_str(), NULL}; + return posix_spawnp(NULL, "xdg-open", NULL, NULL, (char**)argv, environ) == 0; +#endif +} + // Load performance counter frequency only once at startup #ifdef _WIN32 diff --git a/src/porting.h b/src/porting.h index 4d30a5970..f50f0a950 100644 --- a/src/porting.h +++ b/src/porting.h @@ -329,6 +329,9 @@ bool secure_rand_fill_buf(void *buf, size_t len); void attachOrCreateConsole(); int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...); + +bool openURL(const std::string &url); + } // namespace porting #ifdef __ANDROID__ diff --git a/src/porting_android.cpp b/src/porting_android.cpp index 2c91df235..41b521ec2 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -213,6 +213,18 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint, jacceptButton, jhint, jcurrent, jeditType); } +void openURLAndroid(const std::string &url) +{ + jmethodID url_open = jnienv->GetMethodID(nativeActivity, "openURL", + "(Ljava/lang/String;)V"); + + FATAL_ERROR_IF(url_open == nullptr, + "porting::openURLAndroid unable to find java openURL method"); + + jstring jurl = jnienv->NewStringUTF(url.c_str()); + jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); +} + int getInputDialogState() { jmethodID dialogstate = jnienv->GetMethodID(nativeActivity, diff --git a/src/porting_android.h b/src/porting_android.h index 42f90b60b..6eb054041 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -58,6 +58,8 @@ void initializePathsAndroid(); void showInputDialog(const std::string &acceptButton, const std::string &hint, const std::string ¤t, int editType); +void openURLAndroid(const std::string &url); + /** * WORKAROUND for not working callbacks from java -> c++ * get current state of input dialog diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index a76e9f079..f32c477c2 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -1063,6 +1063,14 @@ int ModApiMainMenu::l_get_max_supp_proto(lua_State *L) return 1; } +/******************************************************************************/ +int ModApiMainMenu::l_open_url(lua_State *L) +{ + std::string url = luaL_checkstring(L, 1); + lua_pushboolean(L, porting::openURL(url)); + return 1; +} + /******************************************************************************/ int ModApiMainMenu::l_do_async_callback(lua_State *L) { @@ -1125,6 +1133,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(get_screen_info); API_FCT(get_min_supp_proto); API_FCT(get_max_supp_proto); + API_FCT(open_url); API_FCT(do_async_callback); } diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index b2ca49320..5a16b3bfe 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -145,6 +145,9 @@ private: static int l_get_max_supp_proto(lua_State *L); + // other + static int l_open_url(lua_State *L); + // async static int l_do_async_callback(lua_State *L); -- cgit v1.2.3 From c94d37827dd3a8be9dcc59bb693032ba7ea07922 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Tue, 19 May 2020 21:08:37 +0200 Subject: Rework functionality of leveled nodes (#9852) Co-authored-by: sfan5 Co-authored-by: SmallJoker --- builtin/game/falling.lua | 58 ++++++++++++++++++++++++++++++----------- doc/lua_api.txt | 10 ++++--- src/mapnode.cpp | 21 ++++++++------- src/mapnode.h | 6 ++--- src/nodedef.cpp | 6 +++++ src/nodedef.h | 4 ++- src/script/common/c_content.cpp | 4 +++ src/script/lua_api/l_env.cpp | 4 +-- 8 files changed, 79 insertions(+), 34 deletions(-) (limited to 'src/script/lua_api') diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 7037ae885..cdbb13acc 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -109,6 +109,7 @@ core.register_entity(":__builtin:falling_node", { if core.is_colored_paramtype(def.paramtype2) then itemstring = core.itemstring_with_palette(itemstring, node.param2) end + -- FIXME: solution needed for paramtype2 == "leveled" local vsize if def.visual_scale then local s = def.visual_scale * SCALE @@ -122,6 +123,24 @@ core.register_entity(":__builtin:falling_node", { }) end + -- Set collision box (certain nodeboxes only for now) + local nb_types = {fixed=true, leveled=true, connected=true} + if def.drawtype == "nodebox" and def.node_box and + nb_types[def.node_box.type] then + local box = table.copy(def.node_box.fixed) + if type(box[1]) == "table" then + box = #box == 1 and box[1] or nil -- We can only use a single box + end + if box then + if def.paramtype2 == "leveled" and (self.node.level or 0) > 0 then + box[5] = -0.5 + self.node.level / 64 + end + self.object:set_properties({ + collisionbox = box + }) + end + end + -- Rotate entity if def.drawtype == "torchlike" then self.object:set_yaw(math.pi*0.25) @@ -196,13 +215,16 @@ core.register_entity(":__builtin:falling_node", { try_place = function(self, bcp, bcn) local bcd = core.registered_nodes[bcn.name] -- Add levels if dropped on same leveled node - if bcd and bcd.leveled and + if bcd and bcd.paramtype2 == "leveled" and bcn.name == self.node.name then local addlevel = self.node.level - if not addlevel or addlevel <= 0 then + if (addlevel or 0) <= 0 then addlevel = bcd.leveled end - if core.add_node_level(bcp, addlevel) == 0 then + if core.add_node_level(bcp, addlevel) < addlevel then + return true + elseif bcd.buildable_to then + -- Node level has already reached max, don't place anything return true end end @@ -351,6 +373,7 @@ local function convert_to_falling_node(pos, node) if not obj then return false end + -- remember node level, the entities' set_node() uses this node.level = core.get_node_level(pos) local meta = core.get_meta(pos) local metatable = meta and meta:to_table() or {} @@ -436,18 +459,23 @@ function core.check_single_for_falling(p) -- Only spawn falling node if node below is loaded local n_bottom = core.get_node_or_nil(p_bottom) local d_bottom = n_bottom and core.registered_nodes[n_bottom.name] - if d_bottom and - - (core.get_item_group(n.name, "float") == 0 or - d_bottom.liquidtype == "none") and - - (n.name ~= n_bottom.name or (d_bottom.leveled and - core.get_node_level(p_bottom) < - core.get_node_max_level(p_bottom))) and - - (not d_bottom.walkable or d_bottom.buildable_to) then - convert_to_falling_node(p, n) - return true + if d_bottom then + local same = n.name == n_bottom.name + -- Let leveled nodes fall if it can merge with the bottom node + if same and d_bottom.paramtype2 == "leveled" and + core.get_node_level(p_bottom) < + core.get_node_max_level(p_bottom) then + convert_to_falling_node(p, n) + return true + end + -- Otherwise only if the bottom node is considered "fall through" + if not same and + (not d_bottom.walkable or d_bottom.buildable_to) and + (core.get_item_group(n.name, "float") == 0 or + d_bottom.liquidtype == "none") then + convert_to_falling_node(p, n) + return true + end end end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9c7c42436..8b7c412ab 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4882,7 +4882,7 @@ Environment access * `minetest.add_node_level(pos, level)` * increase level of leveled node by level, default `level` equals `1` * if `totallevel > maxlevel`, returns rest (`total-max`) - * can be negative for decreasing + * `level` must be between -127 and 127 * `minetest.fix_light(pos1, pos2)`: returns `true`/`false` * resets the light in a cuboid-shaped part of the map and removes lighting bugs. @@ -7012,11 +7012,15 @@ Used by `minetest.register_node`. -- If true, a new liquid source can be created by placing two or more -- sources nearby - leveled = 16, + leveled = 0, -- Only valid for "nodebox" drawtype with 'type = "leveled"'. -- Allows defining the nodebox height without using param2. -- The nodebox height is 'leveled' / 64 nodes. - -- The maximum value of 'leveled' is 127. + -- The maximum value of 'leveled' is `leveled_max`. + + leveled_max = 127, + -- Maximum value for `leveled` (0-127), enforced in + -- `minetest.set_node_level` and `minetest.add_node_level`. liquid_range = 8, -- Number of flowing nodes around source (max. 8) diff --git a/src/mapnode.cpp b/src/mapnode.cpp index bf7e79a71..24d62b504 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -584,7 +584,7 @@ u8 MapNode::getMaxLevel(const NodeDefManager *nodemgr) const if( f.liquid_type == LIQUID_FLOWING || f.param_type_2 == CPT2_FLOWINGLIQUID) return LIQUID_LEVEL_MAX; if(f.leveled || f.param_type_2 == CPT2_LEVELED) - return LEVELED_MAX; + return f.leveled_max; return 0; } @@ -603,14 +603,15 @@ u8 MapNode::getLevel(const NodeDefManager *nodemgr) const if (level) return level; } - if (f.leveled > LEVELED_MAX) - return LEVELED_MAX; + // Return static value from nodedef if param2 isn't used for level + if (f.leveled > f.leveled_max) + return f.leveled_max; return f.leveled; } -u8 MapNode::setLevel(const NodeDefManager *nodemgr, s8 level) +s8 MapNode::setLevel(const NodeDefManager *nodemgr, s16 level) { - u8 rest = 0; + s8 rest = 0; const ContentFeatures &f = nodemgr->get(*this); if (f.param_type_2 == CPT2_FLOWINGLIQUID || f.liquid_type == LIQUID_FLOWING @@ -631,18 +632,18 @@ u8 MapNode::setLevel(const NodeDefManager *nodemgr, s8 level) if (level < 0) { // zero means default for a leveled nodebox rest = level; level = 0; - } else if (level > LEVELED_MAX) { - rest = level - LEVELED_MAX; - level = LEVELED_MAX; + } else if (level > f.leveled_max) { + rest = level - f.leveled_max; + level = f.leveled_max; } setParam2((level & LEVELED_MASK) | (getParam2() & ~LEVELED_MASK)); } return rest; } -u8 MapNode::addLevel(const NodeDefManager *nodemgr, s8 add) +s8 MapNode::addLevel(const NodeDefManager *nodemgr, s16 add) { - s8 level = getLevel(nodemgr); + s16 level = getLevel(nodemgr); level += add; return setLevel(nodemgr, level); } diff --git a/src/mapnode.h b/src/mapnode.h index 7a3d30ddc..32ac1b4f6 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -268,12 +268,12 @@ struct MapNode std::vector *boxes, u8 neighbors = 0) const; /* - Liquid helpers + Liquid/leveled helpers */ u8 getMaxLevel(const NodeDefManager *nodemgr) const; u8 getLevel(const NodeDefManager *nodemgr) const; - u8 setLevel(const NodeDefManager *nodemgr, s8 level = 1); - u8 addLevel(const NodeDefManager *nodemgr, s8 add = 1); + s8 setLevel(const NodeDefManager *nodemgr, s16 level = 1); + s8 addLevel(const NodeDefManager *nodemgr, s16 add = 1); /* Serialization functions diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 65199830f..b8211fceb 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -368,6 +368,7 @@ void ContentFeatures::reset() floodable = false; rightclickable = true; leveled = 0; + leveled_max = LEVELED_MAX; liquid_type = LIQUID_NONE; liquid_alternative_flowing = ""; liquid_alternative_source = ""; @@ -478,6 +479,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, legacy_wallmounted); os << serializeString(node_dig_prediction); + writeU8(os, leveled_max); } void ContentFeatures::correctAlpha(TileDef *tiles, int length) @@ -586,6 +588,10 @@ void ContentFeatures::deSerialize(std::istream &is) try { node_dig_prediction = deSerializeString(is); + u8 tmp_leveled_max = readU8(is); + if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */ + throw SerializationError(""); + leveled_max = tmp_leveled_max; } catch(SerializationError &e) {}; } diff --git a/src/nodedef.h b/src/nodedef.h index 0fce6eab1..497e7ee0e 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -326,8 +326,10 @@ struct ContentFeatures std::vector connects_to_ids; // Post effect color, drawn when the camera is inside the node. video::SColor post_effect_color; - // Flowing liquid or snow, value = default level + // Flowing liquid or leveled nodebox, value = default level u8 leveled; + // Maximum value for leveled nodes + u8 leveled_max; // --- LIGHTING-RELATED --- diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index de9634c42..116a59c09 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -694,6 +694,8 @@ ContentFeatures read_content_features(lua_State *L, int index) f.liquid_range = getintfield_default(L, index, "liquid_range", f.liquid_range); f.leveled = getintfield_default(L, index, "leveled", f.leveled); + f.leveled_max = getintfield_default(L, index, + "leveled_max", f.leveled_max); getboolfield(L, index, "liquid_renewable", f.liquid_renewable); f.drowning = getintfield_default(L, index, @@ -860,6 +862,8 @@ void push_content_features(lua_State *L, const ContentFeatures &c) lua_setfield(L, -2, "post_effect_color"); lua_pushnumber(L, c.leveled); lua_setfield(L, -2, "leveled"); + lua_pushnumber(L, c.leveled_max); + lua_setfield(L, -2, "leveled_max"); lua_pushboolean(L, c.sunlight_propagates); lua_setfield(L, -2, "sunlight_propagates"); lua_pushnumber(L, c.light_source); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index b8a8a5ce1..89ec9dc7e 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -529,13 +529,13 @@ int ModApiEnvMod::l_set_node_level(lua_State *L) // add_node_level(pos, level) // pos = {x=num, y=num, z=num} -// level: 0..63 +// level: -127..127 int ModApiEnvMod::l_add_node_level(lua_State *L) { GET_ENV_PTR; v3s16 pos = read_v3s16(L, 1); - u8 level = 1; + s16 level = 1; if(lua_isnumber(L, 2)) level = lua_tonumber(L, 2); MapNode n = env->getMap().getNode(pos); -- cgit v1.2.3 From 42fcfb75e85523a2fa5d99a453c2fabc2c04c0f6 Mon Sep 17 00:00:00 2001 From: Paramat Date: Wed, 20 May 2020 22:16:14 +0100 Subject: Allow more than 255 biomes, document new maximum (#9855) Change biomemap data type from u8 to u16. New technical (not practical) maximum is 65535 biomes. --- doc/lua_api.txt | 4 ++++ src/mapgen/cavegen.cpp | 8 ++++---- src/mapgen/cavegen.h | 8 +++++--- src/mapgen/mapgen.h | 8 ++++---- src/mapgen/mg_biome.h | 6 +++--- src/mapgen/mg_decoration.cpp | 6 ++---- src/mapgen/mg_decoration.h | 4 +++- src/mapgen/mg_ore.cpp | 28 ++++++++++++++-------------- src/mapgen/mg_ore.h | 22 ++++++++++++---------- src/script/lua_api/l_mapgen.cpp | 4 ++-- src/script/lua_api/l_mapgen.h | 2 ++ 11 files changed, 55 insertions(+), 45 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 8b7c412ab..0101bd4cf 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7443,6 +7443,10 @@ Biome definition Used by `minetest.register_biome`. +The maximum number of biomes that can be used is 65535. However, using an +excessive number of biomes will slow down map generation. Depending on desired +performance and computing power the practical limit is much lower. + { name = "tundra", diff --git a/src/mapgen/cavegen.cpp b/src/mapgen/cavegen.cpp index a9df4506f..340079821 100644 --- a/src/mapgen/cavegen.cpp +++ b/src/mapgen/cavegen.cpp @@ -1,8 +1,8 @@ /* Minetest -Copyright (C) 2010-2018 celeron55, Perttu Ahola -Copyright (C) 2010-2018 kwolekr, Ryan Kwolek -Copyright (C) 2015-2018 paramat +Copyright (C) 2010-2020 celeron55, Perttu Ahola +Copyright (C) 2015-2020 paramat +Copyright (C) 2010-2016 kwolekr, Ryan Kwolek 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 @@ -69,7 +69,7 @@ CavesNoiseIntersection::~CavesNoiseIntersection() void CavesNoiseIntersection::generateCaves(MMVManip *vm, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { assert(vm); assert(biomemap); diff --git a/src/mapgen/cavegen.h b/src/mapgen/cavegen.h index ff09f9423..d678d365b 100644 --- a/src/mapgen/cavegen.h +++ b/src/mapgen/cavegen.h @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2010-2018 kwolekr, Ryan Kwolek -Copyright (C) 2015-2018 paramat +Copyright (C) 2015-2020 paramat +Copyright (C) 2010-2016 kwolekr, Ryan Kwolek 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 @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include + class GenerateNotifier; /* @@ -44,7 +46,7 @@ public: NoiseParams *np_cave2, s32 seed, float cave_width); ~CavesNoiseIntersection(); - void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap); + void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, biome_t *biomemap); private: const NodeDefManager *m_ndef; diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h index 7845c5349..a92b3b0d0 100644 --- a/src/mapgen/mapgen.h +++ b/src/mapgen/mapgen.h @@ -1,8 +1,8 @@ /* Minetest -Copyright (C) 2010-2018 celeron55, Perttu Ahola -Copyright (C) 2013-2018 kwolekr, Ryan Kwolek -Copyright (C) 2015-2018 paramat +Copyright (C) 2010-2020 celeron55, Perttu Ahola +Copyright (C) 2015-2020 paramat +Copyright (C) 2013-2016 kwolekr, Ryan Kwolek 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 @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MG_DECORATIONS 0x20 #define MG_BIOMES 0x40 -typedef u8 biome_t; // copy from mg_biome.h to avoid an unnecessary include +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include class Settings; class MMVManip; diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 57f4aa20d..be4cfea4d 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2014-2018 kwolekr, Ryan Kwolek -Copyright (C) 2014-2018 paramat +Copyright (C) 2014-2020 paramat +Copyright (C) 2014-2016 kwolekr, Ryan Kwolek 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 @@ -32,7 +32,7 @@ class BiomeManager; //// Biome //// -typedef u8 biome_t; +typedef u16 biome_t; #define BIOME_NONE ((biome_t)0) diff --git a/src/mapgen/mg_decoration.cpp b/src/mapgen/mg_decoration.cpp index a9b67d239..a4cada396 100644 --- a/src/mapgen/mg_decoration.cpp +++ b/src/mapgen/mg_decoration.cpp @@ -206,8 +206,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) // All-surfaces decorations // Check biome of column if (mg->biomemap && !biomes.empty()) { - std::unordered_set::const_iterator iter = - biomes.find(mg->biomemap[mapindex]); + auto iter = biomes.find(mg->biomemap[mapindex]); if (iter == biomes.end()) continue; } @@ -259,8 +258,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) continue; if (mg->biomemap && !biomes.empty()) { - std::unordered_set::const_iterator iter = - biomes.find(mg->biomemap[mapindex]); + auto iter = biomes.find(mg->biomemap[mapindex]); if (iter == biomes.end()) continue; } diff --git a/src/mapgen/mg_decoration.h b/src/mapgen/mg_decoration.h index 1f9eb4510..1ea02a527 100644 --- a/src/mapgen/mg_decoration.h +++ b/src/mapgen/mg_decoration.h @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "nodedef.h" +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include + class Mapgen; class MMVManip; class PcgRandom; @@ -72,7 +74,7 @@ public: s16 nspawnby; s16 place_offset_y = 0; - std::unordered_set biomes; + std::unordered_set biomes; protected: void cloneTo(Decoration *def) const; diff --git a/src/mapgen/mg_ore.cpp b/src/mapgen/mg_ore.cpp index db647f82b..b50ed6a32 100644 --- a/src/mapgen/mg_ore.cpp +++ b/src/mapgen/mg_ore.cpp @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2014-2018 kwolekr, Ryan Kwolek -Copyright (C) 2015-2018 paramat +Copyright (C) 2015-2020 paramat +Copyright (C) 2014-2016 kwolekr, Ryan Kwolek 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 @@ -146,7 +146,7 @@ ObjDef *OreScatter::clone() const void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed); MapNode n_ore(c_ore, 0, ore_param2); @@ -170,7 +170,7 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X); - std::unordered_set::const_iterator it = biomes.find(biomemap[index]); + auto it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -208,7 +208,7 @@ ObjDef *OreSheet::clone() const void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed + 4234); MapNode n_ore(c_ore, 0, ore_param2); @@ -237,7 +237,7 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed, continue; if (biomemap && !biomes.empty()) { - std::unordered_set::const_iterator it = biomes.find(biomemap[index]); + auto it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -285,7 +285,7 @@ ObjDef *OrePuff::clone() const void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed + 4234); MapNode n_ore(c_ore, 0, ore_param2); @@ -312,7 +312,7 @@ void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed, continue; if (biomemap && !biomes.empty()) { - std::unordered_set::const_iterator it = biomes.find(biomemap[index]); + auto it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -366,7 +366,7 @@ ObjDef *OreBlob::clone() const void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed + 2404); MapNode n_ore(c_ore, 0, ore_param2); @@ -388,7 +388,7 @@ void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X); - std::unordered_set::const_iterator it = biomes.find(biomemap[bmapidx]); + auto it = biomes.find(biomemap[bmapidx]); if (it == biomes.end()) continue; } @@ -451,7 +451,7 @@ ObjDef *OreVein::clone() const void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed + 520); MapNode n_ore(c_ore, 0, ore_param2); @@ -485,7 +485,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X); - std::unordered_set::const_iterator it = biomes.find(biomemap[bmapidx]); + auto it = biomes.find(biomemap[bmapidx]); if (it == biomes.end()) continue; } @@ -532,7 +532,7 @@ ObjDef *OreStratum::clone() const void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) + v3s16 nmin, v3s16 nmax, biome_t *biomemap) { PcgRandom pr(blockseed + 4234); MapNode n_ore(c_ore, 0, ore_param2); @@ -560,7 +560,7 @@ void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed, for (int z = nmin.Z; z <= nmax.Z; z++) for (int x = nmin.X; x <= nmax.X; x++, index++) { if (biomemap && !biomes.empty()) { - std::unordered_set::const_iterator it = biomes.find(biomemap[index]); + auto it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } diff --git a/src/mapgen/mg_ore.h b/src/mapgen/mg_ore.h index 213bdc964..76420fab4 100644 --- a/src/mapgen/mg_ore.h +++ b/src/mapgen/mg_ore.h @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2014-2018 kwolekr, Ryan Kwolek -Copyright (C) 2015-2018 paramat +Copyright (C) 2015-2020 paramat +Copyright (C) 2014-2016 kwolekr, Ryan Kwolek 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 @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "nodedef.h" +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include + class Noise; class Mapgen; class MMVManip; @@ -64,7 +66,7 @@ public: float nthresh; // threshold for noise at which an ore is placed NoiseParams np; // noise for distribution of clusters (NULL for uniform scattering) Noise *noise = nullptr; - std::unordered_set biomes; + std::unordered_set biomes; Ore() = default;; virtual ~Ore(); @@ -73,7 +75,7 @@ public: size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0; + v3s16 nmin, v3s16 nmax, biome_t *biomemap) = 0; protected: void cloneTo(Ore *def) const; @@ -86,7 +88,7 @@ public: ObjDef *clone() const; virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OreSheet : public Ore { @@ -100,7 +102,7 @@ public: float column_midpoint_factor; virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OrePuff : public Ore { @@ -118,7 +120,7 @@ public: virtual ~OrePuff(); virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OreBlob : public Ore { @@ -128,7 +130,7 @@ public: ObjDef *clone() const; virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OreVein : public Ore { @@ -145,7 +147,7 @@ public: virtual ~OreVein(); virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OreStratum : public Ore { @@ -162,7 +164,7 @@ public: virtual ~OreStratum(); virtual void generate(MMVManip *vm, int mapseed, u32 blockseed, - v3s16 nmin, v3s16 nmax, u8 *biomemap); + v3s16 nmin, v3s16 nmax, biome_t *biomemap); }; class OreManager : public ObjDefManager { diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 584085428..834938e56 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -97,7 +97,7 @@ Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr); Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef); size_t get_biome_list(lua_State *L, int index, - BiomeManager *biomemgr, std::unordered_set *biome_id_list); + BiomeManager *biomemgr, std::unordered_set *biome_id_list); Schematic *get_or_load_schematic(lua_State *L, int index, SchematicManager *schemmgr, StringMap *replace_names); @@ -425,7 +425,7 @@ Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef) size_t get_biome_list(lua_State *L, int index, - BiomeManager *biomemgr, std::unordered_set *biome_id_list) + BiomeManager *biomemgr, std::unordered_set *biome_id_list) { if (index < 0) index = lua_gettop(L) + 1 + index; diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index 4a6a9ccf4..0bdc56fc5 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_base.h" +typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include + class ModApiMapgen : public ModApiBase { private: -- cgit v1.2.3 From 1357ea1da25bf01acaf95d5f5419d4f83a84ed61 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 13:23:25 +0200 Subject: Cleanup of particle & particlespawner structures and code (#9893) --- src/CMakeLists.txt | 1 + src/client/clientevent.h | 45 +---- src/client/particles.cpp | 333 ++++++++++++------------------- src/client/particles.h | 58 ++---- src/network/clientpackethandler.cpp | 136 ++++--------- src/particles.cpp | 53 +++++ src/particles.h | 73 +++++++ src/script/lua_api/l_particles.cpp | 186 ++++++++--------- src/script/lua_api/l_particles_local.cpp | 149 ++++++-------- src/server.cpp | 106 ++++------ src/server.h | 42 +--- 11 files changed, 502 insertions(+), 680 deletions(-) create mode 100644 src/particles.cpp create mode 100644 src/particles.h (limited to 'src/script/lua_api') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd68ceec9..3d6d1b0ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -427,6 +427,7 @@ set(common_SRCS noise.cpp objdef.cpp object_properties.cpp + particles.cpp pathfinder.cpp player.cpp porting.cpp diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 7f3984b03..9bd31efce 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -21,8 +21,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" -#include "hud.h" -#include "skyparams.h" + +struct ParticleParameters; +struct ParticleSpawnerParameters; +struct SkyboxParams; +struct SunParams; +struct MoonParams; +struct StarParams; enum ClientEventType : u8 { @@ -77,44 +82,12 @@ struct ClientEvent } show_formspec; // struct{ //} textures_updated; + ParticleParameters *spawn_particle; struct { - v3f *pos; - v3f *vel; - v3f *acc; - f32 expirationtime; - f32 size; - bool collisiondetection; - bool collision_removal; - bool object_collision; - bool vertical; - std::string *texture; - struct TileAnimationParams animation; - u8 glow; - } spawn_particle; - struct - { - u16 amount; - f32 spawntime; - v3f *minpos; - v3f *maxpos; - v3f *minvel; - v3f *maxvel; - v3f *minacc; - v3f *maxacc; - f32 minexptime; - f32 maxexptime; - f32 minsize; - f32 maxsize; - bool collisiondetection; - bool collision_removal; - bool object_collision; + ParticleSpawnerParameters *p; u16 attached_id; - bool vertical; - std::string *texture; u64 id; - struct TileAnimationParams animation; - u8 glow; } add_particlespawner; struct { diff --git a/src/client/particles.cpp b/src/client/particles.cpp index a0e4e54eb..c2e751b4f 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -37,32 +37,31 @@ with this program; if not, write to the Free Software Foundation, Inc., Utility */ -v3f random_v3f(v3f min, v3f max) +static f32 random_f32(f32 min, f32 max) +{ + return rand() / (float)RAND_MAX * (max - min) + min; +} + +static v3f random_v3f(v3f min, v3f max) { return v3f( - rand() / (float)RAND_MAX * (max.X - min.X) + min.X, - rand() / (float)RAND_MAX * (max.Y - min.Y) + min.Y, - rand() / (float)RAND_MAX * (max.Z - min.Z) + min.Z); + random_f32(min.X, max.X), + random_f32(min.Y, max.Y), + random_f32(min.Z, max.Z)); } +/* + Particle +*/ + Particle::Particle( IGameDef *gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color ): scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), @@ -81,33 +80,28 @@ Particle::Particle( m_material.setTexture(0, texture); m_texpos = texpos; m_texsize = texsize; - m_animation = anim; + m_animation = p.animation; // Color m_base_color = color; m_color = color; // Particle related - m_pos = pos; - m_velocity = velocity; - m_acceleration = acceleration; - m_expiration = expirationtime; + m_pos = p.pos; + m_velocity = p.vel; + m_acceleration = p.acc; + m_expiration = p.expirationtime; m_player = player; - m_size = size; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; - m_vertical = vertical; - m_glow = glow; + m_size = p.size; + m_collisiondetection = p.collisiondetection; + m_collision_removal = p.collision_removal; + m_object_collision = p.object_collision; + m_vertical = p.vertical; + m_glow = p.glow; // Irrlicht stuff - m_collisionbox = aabb3f( - -size / 2, - -size / 2, - -size / 2, - size / 2, - size / 2, - size / 2); + const float c = p.size / 2; + m_collisionbox = aabb3f(-c, -c, -c, c, c, c); this->setAutomaticCulling(scene::EAC_OFF); // Init lighting @@ -255,52 +249,22 @@ void Particle::updateVertices() ParticleSpawner::ParticleSpawner( IGameDef *gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, - u8 glow, ParticleManager *p_manager ): - m_particlemanager(p_manager) + m_particlemanager(p_manager), p(p) { m_gamedef = gamedef; m_player = player; - m_amount = amount; - m_spawntime = time; - m_minpos = minpos; - m_maxpos = maxpos; - m_minvel = minvel; - m_maxvel = maxvel; - m_minacc = minacc; - m_maxacc = maxacc; - m_minexptime = minexptime; - m_maxexptime = maxexptime; - m_minsize = minsize; - m_maxsize = maxsize; - m_collisiondetection = collisiondetection; - m_collision_removal = collision_removal; - m_object_collision = object_collision; m_attached_id = attached_id; - m_vertical = vertical; m_texture = texture; m_time = 0; - m_animation = anim; - m_glow = glow; - for (u16 i = 0; i <= m_amount; i++) - { - float spawntime = (float)rand() / (float)RAND_MAX * m_spawntime; + m_spawntimes.reserve(p.amount + 1); + for (u16 i = 0; i <= p.amount; i++) { + float spawntime = rand() / (float)RAND_MAX * p.time; m_spawntimes.push_back(spawntime); } } @@ -309,7 +273,7 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, const core::matrix4 *attached_absolute_pos_rot_matrix) { v3f ppos = m_player->getPosition() / BS; - v3f pos = random_v3f(m_minpos, m_maxpos); + v3f pos = random_v3f(p.minpos, p.maxpos); // Need to apply this first or the following check // will be wrong for attached spawners @@ -326,41 +290,32 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, if (pos.getDistanceFrom(ppos) > radius) return; - v3f vel = random_v3f(m_minvel, m_maxvel); - v3f acc = random_v3f(m_minacc, m_maxacc); + // Parameters for the single particle we're about to spawn + ParticleParameters pp; + pp.pos = pos; + + pp.vel = random_v3f(p.minvel, p.maxvel); + pp.acc = random_v3f(p.minacc, p.maxacc); if (attached_absolute_pos_rot_matrix) { // Apply attachment rotation - attached_absolute_pos_rot_matrix->rotateVect(vel); - attached_absolute_pos_rot_matrix->rotateVect(acc); + attached_absolute_pos_rot_matrix->rotateVect(pp.vel); + attached_absolute_pos_rot_matrix->rotateVect(pp.acc); } - float exptime = rand() / (float)RAND_MAX - * (m_maxexptime - m_minexptime) - + m_minexptime; + pp.expirationtime = random_f32(p.minexptime, p.maxexptime); + pp.size = random_f32(p.minsize, p.maxsize); - float size = rand() / (float)RAND_MAX - * (m_maxsize - m_minsize) - + m_minsize; + p.copyCommon(pp); m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, - pos, - vel, - acc, - exptime, - size, - m_collisiondetection, - m_collision_removal, - m_object_collision, - m_vertical, + pp, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - m_animation, - m_glow + v2f(1.0, 1.0) )); } @@ -381,12 +336,11 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) } } - if (m_spawntime != 0) { + if (p.time != 0) { // Spawner exists for a predefined timespan - for (std::vector::iterator i = m_spawntimes.begin(); - i != m_spawntimes.end();) { - if ((*i) <= m_time && m_amount > 0) { - --m_amount; + for (auto i = m_spawntimes.begin(); i != m_spawntimes.end(); ) { + if ((*i) <= m_time && p.amount > 0) { + --p.amount; // Pretend to, but don't actually spawn a particle if it is // attached to an unloaded object or distant from player. @@ -405,13 +359,16 @@ void ParticleSpawner::step(float dtime, ClientEnvironment *env) if (unloaded) return; - for (int i = 0; i <= m_amount; i++) { + for (int i = 0; i <= p.amount; i++) { if (rand() / (float)RAND_MAX < dtime) spawnParticle(env, radius, attached_absolute_pos_rot_matrix); } } } +/* + ParticleManager +*/ ParticleManager::ParticleManager(ClientEnvironment *env) : m_env(env) @@ -479,99 +436,84 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, { switch (event->type) { case CE_DELETE_PARTICLESPAWNER: { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->delete_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->delete_particlespawner.id)->second; - m_particle_spawners.erase(event->delete_particlespawner.id); - } + deleteParticleSpawner(event->delete_particlespawner.id); // no allocated memory in delete event break; } case CE_ADD_PARTICLESPAWNER: { - { - MutexAutoLock lock(m_spawner_list_lock); - if (m_particle_spawners.find(event->add_particlespawner.id) != - m_particle_spawners.end()) { - delete m_particle_spawners.find(event->add_particlespawner.id)->second; - m_particle_spawners.erase(event->add_particlespawner.id); - } - } + deleteParticleSpawner(event->add_particlespawner.id); + + const ParticleSpawnerParameters &p = *event->add_particlespawner.p; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(p.texture); auto toadd = new ParticleSpawner(client, player, - event->add_particlespawner.amount, - event->add_particlespawner.spawntime, - *event->add_particlespawner.minpos, - *event->add_particlespawner.maxpos, - *event->add_particlespawner.minvel, - *event->add_particlespawner.maxvel, - *event->add_particlespawner.minacc, - *event->add_particlespawner.maxacc, - event->add_particlespawner.minexptime, - event->add_particlespawner.maxexptime, - event->add_particlespawner.minsize, - event->add_particlespawner.maxsize, - event->add_particlespawner.collisiondetection, - event->add_particlespawner.collision_removal, - event->add_particlespawner.object_collision, + p, event->add_particlespawner.attached_id, - event->add_particlespawner.vertical, texture, - event->add_particlespawner.animation, - event->add_particlespawner.glow, this); - /* delete allocated content of event */ - delete event->add_particlespawner.minpos; - delete event->add_particlespawner.maxpos; - delete event->add_particlespawner.minvel; - delete event->add_particlespawner.maxvel; - delete event->add_particlespawner.minacc; - delete event->add_particlespawner.texture; - delete event->add_particlespawner.maxacc; - - { - MutexAutoLock lock(m_spawner_list_lock); - m_particle_spawners[event->add_particlespawner.id] = toadd; - } + addParticleSpawner(event->add_particlespawner.id, toadd); + + delete event->add_particlespawner.p; break; } case CE_SPAWN_PARTICLE: { + const ParticleParameters &p = *event->spawn_particle; video::ITexture *texture = - client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(p.texture); Particle *toadd = new Particle(client, player, m_env, - *event->spawn_particle.pos, - *event->spawn_particle.vel, - *event->spawn_particle.acc, - event->spawn_particle.expirationtime, - event->spawn_particle.size, - event->spawn_particle.collisiondetection, - event->spawn_particle.collision_removal, - event->spawn_particle.object_collision, - event->spawn_particle.vertical, + p, texture, v2f(0.0, 0.0), - v2f(1.0, 1.0), - event->spawn_particle.animation, - event->spawn_particle.glow); + v2f(1.0, 1.0)); addParticle(toadd); - delete event->spawn_particle.pos; - delete event->spawn_particle.vel; - delete event->spawn_particle.acc; - delete event->spawn_particle.texture; - + delete event->spawn_particle; break; } default: break; } } +bool ParticleManager::getNodeParticleParams(const MapNode &n, + const ContentFeatures &f, ParticleParameters &p, + video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) +{ + // No particles for "airlike" nodes + if (f.drawtype == NDT_AIRLIKE) + return false; + + // Texture + u8 texid = rand() % 6; + const TileLayer &tile = f.tiles[texid].layers[0]; + p.animation.type = TAT_NONE; + + // Only use first frame of animated texture + if (tile.material_flags & MATERIAL_FLAG_ANIMATION) + *texture = (*tile.frames)[0].texture; + else + *texture = tile.texture; + + float size = (rand() % 8) / 64.0f; + p.size = BS * size; + if (tile.scale) + size /= tile.scale; + texsize = v2f(size * 2.0f, size * 2.0f); + texpos.X = (rand() % 64) / 64.0f - texsize.X; + texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + + if (tile.has_color) + *color = tile.color; + else + n.getColor(f, color); + + return true; +} + // The final burst of particles when a node is finally dug, *not* particles // spawned during the digging of a node. @@ -593,73 +535,41 @@ void ParticleManager::addDiggingParticles(IGameDef *gamedef, void ParticleManager::addNodeParticle(IGameDef *gamedef, LocalPlayer *player, v3s16 pos, const MapNode &n, const ContentFeatures &f) { - // No particles for "airlike" nodes - if (f.drawtype == NDT_AIRLIKE) - return; - - // Texture - u8 texid = myrand_range(0, 5); - const TileLayer &tile = f.tiles[texid].layers[0]; + ParticleParameters p; video::ITexture *texture; - struct TileAnimationParams anim; - anim.type = TAT_NONE; + v2f texpos, texsize; + video::SColor color; - // Only use first frame of animated texture - if (tile.material_flags & MATERIAL_FLAG_ANIMATION) - texture = (*tile.frames)[0].texture; - else - texture = tile.texture; + if (!getNodeParticleParams(n, f, p, &texture, texpos, texsize, &color)) + return; - float size = (rand() % 8) / 64.0f; - float visual_size = BS * size; - if (tile.scale) - size /= tile.scale; - v2f texsize(size * 2.0f, size * 2.0f); - v2f texpos; - texpos.X = (rand() % 64) / 64.0f - texsize.X; - texpos.Y = (rand() % 64) / 64.0f - texsize.Y; + p.expirationtime = (rand() % 100) / 100.0f; // Physics - v3f velocity( + p.vel = v3f( (rand() % 150) / 50.0f - 1.5f, (rand() % 150) / 50.0f, (rand() % 150) / 50.0f - 1.5f ); - v3f acceleration( + p.acc = v3f( 0.0f, -player->movement_gravity * player->physics_override_gravity / BS, 0.0f ); - v3f particlepos = v3f( + p.pos = v3f( (f32)pos.X + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Y + (rand() % 100) / 200.0f - 0.25f, (f32)pos.Z + (rand() % 100) / 200.0f - 0.25f ); - video::SColor color; - if (tile.has_color) - color = tile.color; - else - n.getColor(f, &color); - Particle *toadd = new Particle( gamedef, player, m_env, - particlepos, - velocity, - acceleration, - (rand() % 100) / 100.0f, // expiration time - visual_size, - true, - false, - false, - false, + p, texture, texpos, texsize, - anim, - 0, color); addParticle(toadd); @@ -670,3 +580,20 @@ void ParticleManager::addParticle(Particle *toadd) MutexAutoLock lock(m_particle_list_lock); m_particles.push_back(toadd); } + + +void ParticleManager::addParticleSpawner(u64 id, ParticleSpawner *toadd) +{ + MutexAutoLock lock(m_spawner_list_lock); + m_particle_spawners[id] = toadd; +} + +void ParticleManager::deleteParticleSpawner(u64 id) +{ + MutexAutoLock lock(m_spawner_list_lock); + auto it = m_particle_spawners.find(id); + if (it != m_particle_spawners.end()) { + delete it->second; + m_particle_spawners.erase(it); + } +} diff --git a/src/client/particles.h b/src/client/particles.h index e7b8cbe24..7dda0e1b1 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "client/tile.h" #include "localplayer.h" -#include "tileanimation.h" +#include "../particles.h" struct ClientEvent; class ParticleManager; @@ -38,20 +38,10 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, LocalPlayer *player, ClientEnvironment *env, - v3f pos, - v3f velocity, - v3f acceleration, - float expirationtime, - float size, - bool collisiondetection, - bool collision_removal, - bool object_collision, - bool vertical, + const ParticleParameters &p, video::ITexture *texture, v2f texpos, v2f texsize, - const struct TileAnimationParams &anim, - u8 glow, video::SColor color = video::SColor(0xFFFFFFFF) ); ~Particle() = default; @@ -119,20 +109,9 @@ class ParticleSpawner public: ParticleSpawner(IGameDef* gamedef, LocalPlayer *player, - u16 amount, - float time, - v3f minp, v3f maxp, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, - bool collision_removal, - bool object_collision, + const ParticleSpawnerParameters &p, u16 attached_id, - bool vertical, video::ITexture *texture, - const struct TileAnimationParams &anim, u8 glow, ParticleManager* p_manager); ~ParticleSpawner() = default; @@ -140,7 +119,7 @@ public: void step(float dtime, ClientEnvironment *env); bool get_expired () - { return (m_amount <= 0) && m_spawntime != 0; } + { return p.amount <= 0 && p.time != 0; } private: void spawnParticle(ClientEnvironment *env, float radius, @@ -150,27 +129,10 @@ private: float m_time; IGameDef *m_gamedef; LocalPlayer *m_player; - u16 m_amount; - float m_spawntime; - v3f m_minpos; - v3f m_maxpos; - v3f m_minvel; - v3f m_maxvel; - v3f m_minacc; - v3f m_maxacc; - float m_minexptime; - float m_maxexptime; - float m_minsize; - float m_maxsize; + ParticleSpawnerParameters p; video::ITexture *m_texture; std::vector m_spawntimes; - bool m_collisiondetection; - bool m_collision_removal; - bool m_object_collision; - bool m_vertical; u16 m_attached_id; - struct TileAnimationParams m_animation; - u8 m_glow; }; /** @@ -197,8 +159,8 @@ public: /** * This function is only used by client particle spawners * - * We don't need to check the particle spawner list because client ID will n - * ever overlap (u64) + * We don't need to check the particle spawner list because client ID will + * never overlap (u64) * @return new id */ u64 generateSpawnerId() @@ -207,9 +169,15 @@ public: } protected: + static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, + ParticleParameters &p, video::ITexture **texture, v2f &texpos, + v2f &texsize, video::SColor *color); + void addParticle(Particle* toadd); private: + void addParticleSpawner(u64 id, ParticleSpawner *toadd); + void deleteParticleSpawner(u64 id); void stepParticles(float dtime); void stepSpawners(float dtime); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 7b1b1368c..054e60c3c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -958,114 +958,56 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) std::string datastring(pkt->getString(0), pkt->getSize()); std::istringstream is(datastring, std::ios_base::binary); - v3f pos = readV3F32(is); - v3f vel = readV3F32(is); - v3f acc = readV3F32(is); - float expirationtime = readF32(is); - float size = readF32(is); - bool collisiondetection = readU8(is); - std::string texture = deSerializeLongString(is); - - bool vertical = false; - bool collision_removal = false; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - vertical = readU8(is); - collision_removal = readU8(is); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleParameters p; + p.deSerialize(is, m_proto_ver); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.object_collision = object_collision; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); m_client_event_queue.push(event); } void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) { - u16 amount; - float spawntime; - v3f minpos; - v3f maxpos; - v3f minvel; - v3f maxvel; - v3f minacc; - v3f maxacc; - float minexptime; - float maxexptime; - float minsize; - float maxsize; - bool collisiondetection; - u32 server_id; - - *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel - >> minacc >> maxacc >> minexptime >> maxexptime >> minsize - >> maxsize >> collisiondetection; - - std::string texture = pkt->readLongString(); - - *pkt >> server_id; - - bool vertical = false; - bool collision_removal = false; - u16 attached_id = 0; - TileAnimationParams animation; - animation.type = TAT_NONE; - u8 glow = 0; - bool object_collision = false; - try { - *pkt >> vertical; - *pkt >> collision_removal; - *pkt >> attached_id; + std::string datastring(pkt->getString(0), pkt->getSize()); + std::istringstream is(datastring, std::ios_base::binary); - // This is horrible but required (why are there two ways to deserialize pkts?) - std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes()); - std::istringstream is(datastring, std::ios_base::binary); - animation.deSerialize(is, m_proto_ver); - glow = readU8(is); - object_collision = readU8(is); - } catch (...) {} + ParticleSpawnerParameters p; + u32 server_id; + u16 attached_id = 0; + + p.amount = readU16(is); + p.time = readF32(is); + p.minpos = readV3F32(is); + p.maxpos = readV3F32(is); + p.minvel = readV3F32(is); + p.maxvel = readV3F32(is); + p.minacc = readV3F32(is); + p.maxacc = readV3F32(is); + p.minexptime = readF32(is); + p.maxexptime = readF32(is); + p.minsize = readF32(is); + p.maxsize = readF32(is); + p.collisiondetection = readU8(is); + p.texture = deSerializeLongString(is); + + server_id = readU32(is); + + p.vertical = readU8(is); + p.collision_removal = readU8(is); + + attached_id = readU16(is); + + p.animation.deSerialize(is, m_proto_ver); + p.glow = readU8(is); + p.object_collision = readU8(is); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = spawntime; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.object_collision = object_collision; - event->add_particlespawner.attached_id = attached_id; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = server_id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = attached_id; + event->add_particlespawner.id = server_id; m_client_event_queue.push(event); } diff --git a/src/particles.cpp b/src/particles.cpp new file mode 100644 index 000000000..711d189f6 --- /dev/null +++ b/src/particles.cpp @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2020 sfan5 + +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 "particles.h" +#include "util/serialize.h" + +void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const +{ + writeV3F32(os, pos); + writeV3F32(os, vel); + writeV3F32(os, acc); + writeF32(os, expirationtime); + writeF32(os, size); + writeU8(os, collisiondetection); + os << serializeLongString(texture); + writeU8(os, vertical); + writeU8(os, collision_removal); + animation.serialize(os, 6); /* NOT the protocol ver */ + writeU8(os, glow); + writeU8(os, object_collision); +} + +void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) +{ + pos = readV3F32(is); + vel = readV3F32(is); + acc = readV3F32(is); + expirationtime = readF32(is); + size = readF32(is); + collisiondetection = readU8(is); + texture = deSerializeLongString(is); + vertical = readU8(is); + collision_removal = readU8(is); + animation.deSerialize(is, 6); /* NOT the protocol ver */ + glow = readU8(is); + object_collision = readU8(is); +} diff --git a/src/particles.h b/src/particles.h new file mode 100644 index 000000000..659c1249f --- /dev/null +++ b/src/particles.h @@ -0,0 +1,73 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +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. +*/ + +#pragma once + +#include +#include "irrlichttypes_bloated.h" +#include "tileanimation.h" + +// This file defines the particle-related structures that both the server and +// client need. The ParticleManager and rendering is in client/particles.h + +struct CommonParticleParams { + bool collisiondetection = false; + bool collision_removal = false; + bool object_collision = false; + bool vertical = false; + std::string texture; + struct TileAnimationParams animation; + u8 glow = 0; + + CommonParticleParams() { + animation.type = TAT_NONE; + } + + /* This helper is useful for copying params from + * ParticleSpawnerParameters to ParticleParameters */ + inline void copyCommon(CommonParticleParams &to) const { + to.collisiondetection = collisiondetection; + to.collision_removal = collision_removal; + to.object_collision = object_collision; + to.vertical = vertical; + to.texture = texture; + to.animation = animation; + to.glow = glow; + } +}; + +struct ParticleParameters : CommonParticleParams { + v3f pos; + v3f vel; + v3f acc; + f32 expirationtime = 1; + f32 size = 1; + + void serialize(std::ostream &os, u16 protocol_ver) const; + void deSerialize(std::istream &is, u16 protocol_ver); +}; + +struct ParticleSpawnerParameters : CommonParticleParams { + u16 amount = 1; + v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; + f32 time = 1; + f32 minexptime = 1, maxexptime = 1, minsize = 1, maxsize = 1; + + // For historical reasons no (de-)serialization methods here +}; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 340903ebf..7680aa17b 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "server.h" -#include "client/particles.h" +#include "particles.h" // add_particle({pos=, velocity=, acceleration=, expirationtime=, // size=, collisiondetection=, collision_removal=, object_collision=, @@ -40,85 +40,81 @@ with this program; if not, write to the Free Software Foundation, Inc., // glow = num int ModApiParticles::l_add_particle(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - expirationtime = size = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - std::string texture; + struct ParticleParameters p; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) // deprecated { - log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); - pos = check_v3f(L, 1); - vel = check_v3f(L, 2); - acc = check_v3f(L, 3); - expirationtime = luaL_checknumber(L, 4); - size = luaL_checknumber(L, 5); - collisiondetection = readParam(L, 6); - texture = luaL_checkstring(L, 7); + log_deprecated(L, "Deprecated add_particle call with " + "individual parameters instead of definition"); + p.pos = check_v3f(L, 1); + p.vel = check_v3f(L, 2); + p.acc = check_v3f(L, 3); + p.expirationtime = luaL_checknumber(L, 4); + p.size = luaL_checknumber(L, 5); + p.collisiondetection = readParam(L, 6); + p.texture = luaL_checkstring(L, 7); if (lua_gettop(L) == 8) // only spawn for a single player playername = luaL_checkstring(L, 8); } else if (lua_istable(L, 1)) { lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "vel"); if (lua_istable(L, -1)) { - vel = check_v3f(L, -1); + p.vel = check_v3f(L, -1); log_deprecated(L, "The use of vel is deprecated. " "Use velocity instead"); } lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel; + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acc"); if (lua_istable(L, -1)) { - acc = check_v3f(L, -1); + p.acc = check_v3f(L, -1); log_deprecated(L, "The use of acc is deprecated. " "Use acceleration instead"); } lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc; + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); - vertical = getboolfield_default(L, 1, "vertical", vertical); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - playername = getstringfield_default(L, 1, "playername", ""); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); - glow = getintfield_default(L, 1, "glow", 0); + playername = getstringfield_default(L, 1, "playername", ""); } - getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, - collisiondetection, collision_removal, object_collision, vertical, - texture, animation, glow); + + getServer(L)->spawnParticle(playername, p); return 1; } @@ -146,84 +142,82 @@ int ModApiParticles::l_add_particle(lua_State *L) // glow = num int ModApiParticles::l_add_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters - u16 amount = 1; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - time = minexptime = maxexptime = minsize = maxsize = 1; - bool collisiondetection, vertical, collision_removal, object_collision; - collisiondetection = vertical = collision_removal = object_collision = false; - struct TileAnimationParams animation; - animation.type = TAT_NONE; + ParticleSpawnerParameters p; ServerActiveObject *attached = NULL; - std::string texture; std::string playername; - u8 glow = 0; if (lua_gettop(L) > 1) //deprecated { - log_deprecated(L,"Deprecated add_particlespawner call with individual parameters instead of definition"); - amount = luaL_checknumber(L, 1); - time = luaL_checknumber(L, 2); - minpos = check_v3f(L, 3); - maxpos = check_v3f(L, 4); - minvel = check_v3f(L, 5); - maxvel = check_v3f(L, 6); - minacc = check_v3f(L, 7); - maxacc = check_v3f(L, 8); - minexptime = luaL_checknumber(L, 9); - maxexptime = luaL_checknumber(L, 10); - minsize = luaL_checknumber(L, 11); - maxsize = luaL_checknumber(L, 12); - collisiondetection = readParam(L, 13); - texture = luaL_checkstring(L, 14); + log_deprecated(L, "Deprecated add_particlespawner call with " + "individual parameters instead of definition"); + p.amount = luaL_checknumber(L, 1); + p.time = luaL_checknumber(L, 2); + p.minpos = check_v3f(L, 3); + p.maxpos = check_v3f(L, 4); + p.minvel = check_v3f(L, 5); + p.maxvel = check_v3f(L, 6); + p.minacc = check_v3f(L, 7); + p.maxacc = check_v3f(L, 8); + p.minexptime = luaL_checknumber(L, 9); + p.maxexptime = luaL_checknumber(L, 10); + p.minsize = luaL_checknumber(L, 11); + p.maxsize = luaL_checknumber(L, 12); + p.collisiondetection = readParam(L, 13); + p.texture = luaL_checkstring(L, 14); if (lua_gettop(L) == 15) // only spawn for a single player playername = luaL_checkstring(L, 15); } else if (lua_istable(L, 1)) { - amount = getintfield_default(L, 1, "amount", amount); - time = getfloatfield_default(L, 1, "time", time); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos; + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos; + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel; + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel; + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc; + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc; + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", minexptime); - maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime); - minsize = getfloatfield_default(L, 1, "minsize", minsize); - maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); - collisiondetection = getboolfield_default(L, 1, - "collisiondetection", collisiondetection); - collision_removal = getboolfield_default(L, 1, - "collision_removal", collision_removal); - object_collision = getboolfield_default(L, 1, - "object_collision", object_collision); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "attached"); @@ -233,25 +227,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) attached = ObjectRef::getobject(ref); } - vertical = getboolfield_default(L, 1, "vertical", vertical); - texture = getstringfield_default(L, 1, "texture", ""); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.glow = getintfield_default(L, 1, "glow", p.glow); } - u32 id = getServer(L)->addParticleSpawner(amount, time, - minpos, maxpos, - minvel, maxvel, - minacc, maxacc, - minexptime, maxexptime, - minsize, maxsize, - collisiondetection, - collision_removal, - object_collision, - attached, - vertical, - texture, playername, - animation, glow); + u32 id = getServer(L)->addParticleSpawner(p, attached, playername); lua_pushnumber(L, id); return 1; @@ -261,7 +243,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) // player (string) is optional int ModApiParticles::l_delete_particlespawner(lua_State *L) { - MAP_LOCK_REQUIRED; + NO_MAP_LOCK_REQUIRED; // Get parameters u32 id = luaL_checknumber(L, 1); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index a9bf55665..9595b2fab 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -32,56 +32,44 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); // Get parameters - v3f pos, vel, acc; - float expirationtime, size; - bool collisiondetection, vertical, collision_removal; - - struct TileAnimationParams animation; - animation.type = TAT_NONE; - - std::string texture; - - u8 glow; + ParticleParameters p; lua_getfield(L, 1, "pos"); - pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.pos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "velocity"); - vel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.vel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "acceleration"); - acc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.acc = check_v3f(L, -1); lua_pop(L, 1); - expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); - size = getfloatfield_default(L, 1, "size", 1); - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.expirationtime = getfloatfield_default(L, 1, "expirationtime", + p.expirationtime); + p.size = getfloatfield_default(L, 1, "size", p.size); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - texture = getstringfield_default(L, 1, "texture", ""); - - glow = getintfield_default(L, 1, "glow", 0); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); ClientEvent *event = new ClientEvent(); - event->type = CE_SPAWN_PARTICLE; - event->spawn_particle.pos = new v3f (pos); - event->spawn_particle.vel = new v3f (vel); - event->spawn_particle.acc = new v3f (acc); - event->spawn_particle.expirationtime = expirationtime; - event->spawn_particle.size = size; - event->spawn_particle.collisiondetection = collisiondetection; - event->spawn_particle.collision_removal = collision_removal; - event->spawn_particle.vertical = vertical; - event->spawn_particle.texture = new std::string(texture); - event->spawn_particle.animation = animation; - event->spawn_particle.glow = glow; + event->type = CE_SPAWN_PARTICLE; + event->spawn_particle = new ParticleParameters(p); getClient(L)->pushToEventQueue(event); return 0; @@ -90,94 +78,69 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); - // Get parameters - u16 amount; - v3f minpos, maxpos, minvel, maxvel, minacc, maxacc; - float time, minexptime, maxexptime, minsize, maxsize; - bool collisiondetection, vertical, collision_removal; - struct TileAnimationParams animation; - animation.type = TAT_NONE; - // TODO: Implement this when there is a way to get an objectref. - // ServerActiveObject *attached = NULL; - std::string texture; - u8 glow; + // Get parameters + ParticleSpawnerParameters p; - amount = getintfield_default(L, 1, "amount", 1); - time = getfloatfield_default(L, 1, "time", 1); + p.amount = getintfield_default(L, 1, "amount", p.amount); + p.time = getfloatfield_default(L, 1, "time", p.time); lua_getfield(L, 1, "minpos"); - minpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxpos"); - maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxpos = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minvel"); - minvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxvel"); - maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxvel = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "minacc"); - minacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.minacc = check_v3f(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "maxacc"); - maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(0, 0, 0); + if (lua_istable(L, -1)) + p.maxacc = check_v3f(L, -1); lua_pop(L, 1); - minexptime = getfloatfield_default(L, 1, "minexptime", 1); - maxexptime = getfloatfield_default(L, 1, "maxexptime", 1); - minsize = getfloatfield_default(L, 1, "minsize", 1); - maxsize = getfloatfield_default(L, 1, "maxsize", 1); - - collisiondetection = getboolfield_default(L, 1, "collisiondetection", false); - collision_removal = getboolfield_default(L, 1, "collision_removal", false); - vertical = getboolfield_default(L, 1, "vertical", false); + p.minexptime = getfloatfield_default(L, 1, "minexptime", p.minexptime); + p.maxexptime = getfloatfield_default(L, 1, "maxexptime", p.maxexptime); + p.minsize = getfloatfield_default(L, 1, "minsize", p.minsize); + p.maxsize = getfloatfield_default(L, 1, "maxsize", p.maxsize); + p.collisiondetection = getboolfield_default(L, 1, + "collisiondetection", p.collisiondetection); + p.collision_removal = getboolfield_default(L, 1, + "collision_removal", p.collision_removal); + p.object_collision = getboolfield_default(L, 1, + "object_collision", p.object_collision); lua_getfield(L, 1, "animation"); - animation = read_animation_definition(L, -1); + p.animation = read_animation_definition(L, -1); lua_pop(L, 1); - // TODO: Implement this when a way to get an objectref on the client is added -// lua_getfield(L, 1, "attached"); -// if (!lua_isnil(L, -1)) { -// ObjectRef *ref = ObjectRef::checkobject(L, -1); -// lua_pop(L, 1); -// attached = ObjectRef::getobject(ref); -// } - - texture = getstringfield_default(L, 1, "texture", ""); - glow = getintfield_default(L, 1, "glow", 0); + p.vertical = getboolfield_default(L, 1, "vertical", p.vertical); + p.texture = getstringfield_default(L, 1, "texture", p.texture); + p.glow = getintfield_default(L, 1, "glow", p.glow); u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); - event->type = CE_ADD_PARTICLESPAWNER; - event->add_particlespawner.amount = amount; - event->add_particlespawner.spawntime = time; - event->add_particlespawner.minpos = new v3f (minpos); - event->add_particlespawner.maxpos = new v3f (maxpos); - event->add_particlespawner.minvel = new v3f (minvel); - event->add_particlespawner.maxvel = new v3f (maxvel); - event->add_particlespawner.minacc = new v3f (minacc); - event->add_particlespawner.maxacc = new v3f (maxacc); - event->add_particlespawner.minexptime = minexptime; - event->add_particlespawner.maxexptime = maxexptime; - event->add_particlespawner.minsize = minsize; - event->add_particlespawner.maxsize = maxsize; - event->add_particlespawner.collisiondetection = collisiondetection; - event->add_particlespawner.collision_removal = collision_removal; - event->add_particlespawner.attached_id = 0; - event->add_particlespawner.vertical = vertical; - event->add_particlespawner.texture = new std::string(texture); - event->add_particlespawner.id = id; - event->add_particlespawner.animation = animation; - event->add_particlespawner.glow = glow; + event->type = CE_ADD_PARTICLESPAWNER; + event->add_particlespawner.p = new ParticleSpawnerParameters(p); + event->add_particlespawner.attached_id = 0; + event->add_particlespawner.id = id; getClient(L)->pushToEventQueue(event); lua_pushnumber(L, id); diff --git a/src/server.cpp b/src/server.cpp index 92870f972..68b0131d4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1504,17 +1504,15 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms // Spawns a particle on peer with peer_id void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, bool collisiondetection, - bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) + const ParticleParameters &p) { static thread_local const float radius = g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; if (peer_id == PEER_ID_INEXISTENT) { std::vector clients = m_clients.getClientIDs(); + const v3f pos = p.pos * BS; + const float radius_sq = radius * radius; for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); @@ -1526,76 +1524,59 @@ void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, continue; // Do not send to distant clients - if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq) continue; - SendSpawnParticle(client_id, player->protocol_version, - pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(client_id, player->protocol_version, p); } return; } + assert(protocol_version != 0); NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); - pkt << pos << velocity << acceleration << expirationtime - << size << collisiondetection; - pkt.putLongString(texture); - pkt << vertical; - pkt << collision_removal; - // This is horrible but required (why are there two ways to serialize pkts?) - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + { + // NetworkPacket and iostreams are incompatible... + std::ostringstream oss(std::ios_base::binary); + p.serialize(oss, protocol_version); + pkt.putRawString(oss.str()); + } Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, bool collision_removal, - bool object_collision, u16 attached_id, bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow) + const ParticleSpawnerParameters &p, u16 attached_id, u32 id) { if (peer_id == PEER_ID_INEXISTENT) { - // This sucks and should be replaced: std::vector clients = m_clients.getClientIDs(); for (const session_t client_id : clients) { RemotePlayer *player = m_env->getPlayer(client_id); if (!player) continue; SendAddParticleSpawner(client_id, player->protocol_version, - amount, spawntime, minpos, maxpos, - minvel, maxvel, minacc, maxacc, minexptime, maxexptime, - minsize, maxsize, collisiondetection, collision_removal, - object_collision, attached_id, vertical, texture, id, - animation, glow); + p, attached_id, id); } return; } + assert(protocol_version != 0); - NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); + NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); - pkt << amount << spawntime << minpos << maxpos << minvel << maxvel - << minacc << maxacc << minexptime << maxexptime << minsize - << maxsize << collisiondetection; + pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel + << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime + << p.minsize << p.maxsize << p.collisiondetection; - pkt.putLongString(texture); + pkt.putLongString(p.texture); - pkt << id << vertical; - pkt << collision_removal; - pkt << attached_id; - // This is horrible but required - std::ostringstream os(std::ios_base::binary); - animation.serialize(os, protocol_version); - pkt.putRawString(os.str()); - pkt << glow; - pkt << object_collision; + pkt << id << p.vertical << p.collision_removal << attached_id; + { + std::ostringstream os(std::ios_base::binary); + p.animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + } + pkt << p.glow << p.object_collision; Send(&pkt); } @@ -1604,7 +1585,6 @@ void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id) { NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id); - // Ugly error in this packet pkt << id; if (peer_id != PEER_ID_INEXISTENT) @@ -3365,12 +3345,8 @@ void Server::notifyPlayers(const std::wstring &msg) SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg)); } -void Server::spawnParticle(const std::string &playername, v3f pos, - v3f velocity, v3f acceleration, - float expirationtime, float size, bool - collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow) +void Server::spawnParticle(const std::string &playername, + const ParticleParameters &p) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3386,18 +3362,11 @@ void Server::spawnParticle(const std::string &playername, v3f pos, proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, - expirationtime, size, collisiondetection, collision_removal, - object_collision, vertical, texture, animation, glow); + SendSpawnParticle(peer_id, proto_ver, p); } -u32 Server::addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, - float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow) +u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3417,16 +3386,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, u32 id; if (attached_id == 0) - id = m_env->addParticleSpawner(spawntime); + id = m_env->addParticleSpawner(p.time); else - id = m_env->addParticleSpawner(spawntime, attached_id); - - SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, - minpos, maxpos, minvel, maxvel, minacc, maxacc, - minexptime, maxexptime, minsize, maxsize, collisiondetection, - collision_removal, object_collision, attached_id, vertical, - texture, id, animation, glow); + id = m_env->addParticleSpawner(p.time, attached_id); + SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id); return id; } diff --git a/src/server.h b/src/server.h index 7a1de9370..27943cc29 100644 --- a/src/server.h +++ b/src/server.h @@ -27,7 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/mods.h" #include "inventorymanager.h" #include "content/subgames.h" -#include "tileanimation.h" // struct TileAnimationParams +#include "tileanimation.h" // TileAnimationParams +#include "particles.h" // ParticleParams #include "network/peerhandler.h" #include "network/address.h" #include "util/numeric.h" @@ -226,24 +227,12 @@ public: void notifyPlayer(const char *name, const std::wstring &msg); void notifyPlayers(const std::wstring &msg); + void spawnParticle(const std::string &playername, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); - - u32 addParticleSpawner(u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - ServerActiveObject *attached, - bool vertical, const std::string &texture, - const std::string &playername, const struct TileAnimationParams &animation, - u8 glow); + const ParticleParameters &p); + + u32 addParticleSpawner(const ParticleSpawnerParameters &p, + ServerActiveObject *attached, const std::string &playername); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -453,26 +442,13 @@ private: // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(session_t peer_id, u16 protocol_version, - u16 amount, float spawntime, - v3f minpos, v3f maxpos, - v3f minvel, v3f maxvel, - v3f minacc, v3f maxacc, - float minexptime, float maxexptime, - float minsize, float maxsize, - bool collisiondetection, bool collision_removal, bool object_collision, - u16 attached_id, - bool vertical, const std::string &texture, u32 id, - const struct TileAnimationParams &animation, u8 glow); + const ParticleSpawnerParameters &p, u16 attached_id, u32 id); void SendDeleteParticleSpawner(session_t peer_id, u32 id); // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) void SendSpawnParticle(session_t peer_id, u16 protocol_version, - v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool collision_removal, bool object_collision, - bool vertical, const std::string &texture, - const struct TileAnimationParams &animation, u8 glow); + const ParticleParameters &p); void SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao); void SendActiveObjectMessages(session_t peer_id, const std::string &datas, -- cgit v1.2.3 From e79bc40c0a5312baf4e8c3e33048d50b41b4a2ff Mon Sep 17 00:00:00 2001 From: Lejo1 Date: Wed, 20 May 2020 21:54:52 +0200 Subject: Check for valid base64 before decoding (#9904) --- doc/lua_api.txt | 2 +- src/script/lua_api/l_util.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 0101bd4cf..bd0cb8acb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5449,7 +5449,7 @@ Misc. * Example: `minetest.rgba(10, 20, 30, 40)`, returns `"#0A141E28"` * `minetest.encode_base64(string)`: returns string encoded in base64 * Encodes a string in base64. -* `minetest.decode_base64(string)`: returns string +* `minetest.decode_base64(string)`: returns string or nil for invalid base64 * Decodes a string encoded in base64. * `minetest.is_protected(pos, name)`: returns boolean * Returning `true` restricts the player `name` from modifying (i.e. digging, diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 28ee39fc8..cd63e20c2 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -318,9 +318,13 @@ int ModApiUtil::l_decode_base64(lua_State *L) NO_MAP_LOCK_REQUIRED; size_t size; - const char *data = luaL_checklstring(L, 1, &size); + const char *d = luaL_checklstring(L, 1, &size); + const std::string data = std::string(d, size); + + if (!base64_is_valid(data)) + return 0; - std::string out = base64_decode(std::string(data, size)); + std::string out = base64_decode(data); lua_pushlstring(L, out.data(), out.size()); return 1; -- cgit v1.2.3 From 037422fdba9a47bd538480988fbf8aad67d66c85 Mon Sep 17 00:00:00 2001 From: Awkor Date: Sat, 23 May 2020 13:23:05 +0200 Subject: Modernize include guards and add missing ones (#9898) --- src/client/hud.h | 4 +--- src/client/render/factory.h | 2 ++ src/gui/guiButtonImage.h | 2 ++ src/gui/guiButtonItemImage.h | 2 ++ src/hud.h | 4 +--- src/script/lua_api/l_particles_local.h | 2 ++ src/util/md32_common.h | 2 ++ 7 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/client/hud.h b/src/client/hud.h index 6274b1a83..e2abf326c 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef CLIENT_HUD_HEADER -#define CLIENT_HUD_HEADER +#pragma once #include #include @@ -147,4 +146,3 @@ void drawItemStack( const v3s16 &angle, const v3s16 &rotation_speed); -#endif diff --git a/src/client/render/factory.h b/src/client/render/factory.h index 22738404a..e3339a836 100644 --- a/src/client/render/factory.h +++ b/src/client/render/factory.h @@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include #include "core.h" diff --git a/src/gui/guiButtonImage.h b/src/gui/guiButtonImage.h index a948d772b..59a25b4f0 100644 --- a/src/gui/guiButtonImage.h +++ b/src/gui/guiButtonImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h index 9cd0f6188..aad923bda 100644 --- a/src/gui/guiButtonItemImage.h +++ b/src/gui/guiButtonItemImage.h @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "guiButton.h" #include "IGUIButton.h" diff --git a/src/hud.h b/src/hud.h index bab420ed2..e015baec1 100644 --- a/src/hud.h +++ b/src/hud.h @@ -18,8 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef HUD_HEADER -#define HUD_HEADER +#pragma once #include "irrlichttypes_extrabloated.h" #include @@ -101,4 +100,3 @@ extern const EnumString es_HudElementType[]; extern const EnumString es_HudElementStat[]; extern const EnumString es_HudBuiltinElement[]; -#endif diff --git a/src/script/lua_api/l_particles_local.h b/src/script/lua_api/l_particles_local.h index 5dff153b3..d8bb2b1c6 100644 --- a/src/script/lua_api/l_particles_local.h +++ b/src/script/lua_api/l_particles_local.h @@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "lua_api/l_base.h" class ModApiParticlesLocal : public ModApiBase diff --git a/src/util/md32_common.h b/src/util/md32_common.h index 085d1d7a5..a4c2099c9 100644 --- a/src/util/md32_common.h +++ b/src/util/md32_common.h @@ -109,6 +109,8 @@ * */ +#pragma once + #if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) # error "DATA_ORDER must be defined!" #endif -- cgit v1.2.3 From 9d6e7e48d6fb1daff8fedcb2f111164bef61f1e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 22 May 2020 14:17:03 +0200 Subject: Implement spawning particles with node texture appearance --- build/android/native/jni/Android.mk | 1 + doc/lua_api.txt | 30 ++++++++++++- src/client/particles.cpp | 75 ++++++++++++++++++++++++-------- src/client/particles.h | 4 +- src/network/clientpackethandler.cpp | 10 +++++ src/particles.cpp | 10 +++++ src/particles.h | 6 +++ src/script/lua_api/l_particles.cpp | 14 ++++++ src/script/lua_api/l_particles_local.cpp | 14 ++++++ src/server.cpp | 1 + 10 files changed, 145 insertions(+), 20 deletions(-) (limited to 'src/script/lua_api') diff --git a/build/android/native/jni/Android.mk b/build/android/native/jni/Android.mk index a5cb099e6..140947e6a 100644 --- a/build/android/native/jni/Android.mk +++ b/build/android/native/jni/Android.mk @@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \ ../../../src/noise.cpp \ ../../../src/objdef.cpp \ ../../../src/object_properties.cpp \ + ../../../src/particles.cpp \ ../../../src/pathfinder.cpp \ ../../../src/player.cpp \ ../../../src/porting.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 26061eccb..5b3f61c99 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -7835,6 +7835,8 @@ Used by `minetest.add_particle`. size = 1, -- Scales the visual size of the particle texture. + -- If `node` is set, size can be set to 0 to spawn a randomly-sized + -- particle (just like actual node dig particles). collisiondetection = false, -- If true collides with `walkable` nodes and, depending on the @@ -7853,6 +7855,7 @@ Used by `minetest.add_particle`. -- If true faces player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particle only on the player's client @@ -7863,6 +7866,17 @@ Used by `minetest.add_particle`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particle will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } @@ -7892,7 +7906,9 @@ Used by `minetest.add_particlespawner`. maxsize = 1, -- The particles' properties are random values between the min and max -- values. - -- pos, velocity, acceleration, expirationtime, size + -- applies to: pos, velocity, acceleration, expirationtime, size + -- If `node` is set, min and maxsize can be set to 0 to spawn + -- randomly-sized particles (just like actual node dig particles). collisiondetection = false, -- If true collide with `walkable` nodes and, depending on the @@ -7915,6 +7931,7 @@ Used by `minetest.add_particlespawner`. -- If true face player using y axis only texture = "image.png", + -- The texture of the particle playername = "singleplayer", -- Optional, if specified spawns particles only on the player's client @@ -7925,6 +7942,17 @@ Used by `minetest.add_particlespawner`. glow = 0 -- Optional, specify particle self-luminescence in darkness. -- Values 0-14. + + node = {name = "ignore", param2 = 0}, + -- Optional, if specified the particles will have the same appearance as + -- node dig particles for the given node. + -- `texture` and `animation` will be ignored if this is set. + + node_tile = 0, + -- Optional, only valid in combination with `node` + -- If set to a valid number 1-6, specifies the tile from which the + -- particle texture is picked. + -- Otherwise, the default behavior is used. (currently: any random tile) } `HTTPRequest` definition diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c2e751b4f..c78a3e71a 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -304,18 +304,37 @@ void ParticleSpawner::spawnParticle(ClientEnvironment *env, float radius, } pp.expirationtime = random_f32(p.minexptime, p.maxexptime); - pp.size = random_f32(p.minsize, p.maxsize); - p.copyCommon(pp); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = + m_particlemanager->m_env->getGameDef()->ndef()->get(p.node); + if (!ParticleManager::getNodeParticleParams(p.node, f, pp, &texture, + texpos, texsize, &color, p.node_tile)) + return; + } else { + texture = m_texture; + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (p.maxsize > 0.0f) + pp.size = random_f32(p.minsize, p.maxsize); + m_particlemanager->addParticle(new Particle( m_gamedef, m_player, env, pp, - m_texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0) + texture, + texpos, + texsize, + color )); } @@ -460,17 +479,35 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, break; } case CE_SPAWN_PARTICLE: { - const ParticleParameters &p = *event->spawn_particle; - video::ITexture *texture = - client->tsrc()->getTextureForMesh(p.texture); + ParticleParameters &p = *event->spawn_particle; - Particle *toadd = new Particle(client, player, m_env, - p, - texture, - v2f(0.0, 0.0), - v2f(1.0, 1.0)); + video::ITexture *texture; + v2f texpos, texsize; + video::SColor color(0xFFFFFFFF); + + f32 oldsize = p.size; + + if (p.node.getContent() != CONTENT_IGNORE) { + const ContentFeatures &f = m_env->getGameDef()->ndef()->get(p.node); + if (!getNodeParticleParams(p.node, f, p, &texture, texpos, + texsize, &color, p.node_tile)) + texture = nullptr; + } else { + texture = client->tsrc()->getTextureForMesh(p.texture); + texpos = v2f(0.0f, 0.0f); + texsize = v2f(1.0f, 1.0f); + } + + // Allow keeping default random size + if (oldsize > 0.0f) + p.size = oldsize; - addParticle(toadd); + if (texture) { + Particle *toadd = new Particle(client, player, m_env, + p, texture, texpos, texsize, color); + + addParticle(toadd); + } delete event->spawn_particle; break; @@ -480,15 +517,19 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, } bool ParticleManager::getNodeParticleParams(const MapNode &n, - const ContentFeatures &f, ParticleParameters &p, - video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color) + const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, + v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum) { // No particles for "airlike" nodes if (f.drawtype == NDT_AIRLIKE) return false; // Texture - u8 texid = rand() % 6; + u8 texid; + if (tilenum > 0 && tilenum <= 6) + texid = tilenum - 1; + else + texid = rand() % 6; const TileLayer &tile = f.tiles[texid].layers[0]; p.animation.type = TAT_NONE; diff --git a/src/client/particles.h b/src/client/particles.h index 7dda0e1b1..2011f0262 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -42,7 +42,7 @@ class Particle : public scene::ISceneNode video::ITexture *texture, v2f texpos, v2f texsize, - video::SColor color = video::SColor(0xFFFFFFFF) + video::SColor color ); ~Particle() = default; @@ -171,7 +171,7 @@ public: protected: static bool getNodeParticleParams(const MapNode &n, const ContentFeatures &f, ParticleParameters &p, video::ITexture **texture, v2f &texpos, - v2f &texsize, video::SColor *color); + v2f &texsize, video::SColor *color, u8 tilenum = 0); void addParticle(Particle* toadd); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 054e60c3c..e000acc92 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1003,6 +1003,16 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) p.glow = readU8(is); p.object_collision = readU8(is); + // This is kinda awful + do { + u16 tmp_param0 = readU16(is); + if (is.eof()) + break; + p.node.param0 = tmp_param0; + p.node.param2 = readU8(is); + p.node_tile = readU8(is); + } while (0); + auto event = new ClientEvent(); event->type = CE_ADD_PARTICLESPAWNER; event->add_particlespawner.p = new ParticleSpawnerParameters(p); diff --git a/src/particles.cpp b/src/particles.cpp index 711d189f6..fd81238dc 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -34,6 +34,9 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const animation.serialize(os, 6); /* NOT the protocol ver */ writeU8(os, glow); writeU8(os, object_collision); + writeU16(os, node.param0); + writeU8(os, node.param2); + writeU8(os, node_tile); } void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) @@ -50,4 +53,11 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) animation.deSerialize(is, 6); /* NOT the protocol ver */ glow = readU8(is); object_collision = readU8(is); + // This is kinda awful + u16 tmp_param0 = readU16(is); + if (is.eof()) + return; + node.param0 = tmp_param0; + node.param2 = readU8(is); + node_tile = readU8(is); } diff --git a/src/particles.h b/src/particles.h index 659c1249f..6f518b771 100644 --- a/src/particles.h +++ b/src/particles.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_bloated.h" #include "tileanimation.h" +#include "mapnode.h" // This file defines the particle-related structures that both the server and // client need. The ParticleManager and rendering is in client/particles.h @@ -34,9 +35,12 @@ struct CommonParticleParams { std::string texture; struct TileAnimationParams animation; u8 glow = 0; + MapNode node; + u8 node_tile = 0; CommonParticleParams() { animation.type = TAT_NONE; + node.setContent(CONTENT_IGNORE); } /* This helper is useful for copying params from @@ -49,6 +53,8 @@ struct CommonParticleParams { to.texture = texture; to.animation = animation; to.glow = glow; + to.node = node; + to.node_tile = node_tile; } }; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 7680aa17b..a51c4fe20 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -111,6 +111,13 @@ int ModApiParticles::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + playername = getstringfield_default(L, 1, "playername", ""); } @@ -231,6 +238,13 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); playername = getstringfield_default(L, 1, "playername", ""); p.glow = getintfield_default(L, 1, "glow", p.glow); + + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); } u32 id = getServer(L)->addParticleSpawner(p, attached, playername); diff --git a/src/script/lua_api/l_particles_local.cpp b/src/script/lua_api/l_particles_local.cpp index 9595b2fab..cc68b13a5 100644 --- a/src/script/lua_api/l_particles_local.cpp +++ b/src/script/lua_api/l_particles_local.cpp @@ -67,6 +67,13 @@ int ModApiParticlesLocal::l_add_particle(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + ClientEvent *event = new ClientEvent(); event->type = CE_SPAWN_PARTICLE; event->spawn_particle = new ParticleParameters(p); @@ -134,6 +141,13 @@ int ModApiParticlesLocal::l_add_particlespawner(lua_State *L) p.texture = getstringfield_default(L, 1, "texture", p.texture); p.glow = getintfield_default(L, 1, "glow", p.glow); + lua_getfield(L, 1, "node"); + if (lua_istable(L, -1)) + p.node = readnode(L, -1, getGameDef(L)->ndef()); + lua_pop(L, 1); + + p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); + u64 id = getClient(L)->getParticleManager()->generateSpawnerId(); auto event = new ClientEvent(); diff --git a/src/server.cpp b/src/server.cpp index 68b0131d4..d6e545498 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1577,6 +1577,7 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, pkt.putRawString(os.str()); } pkt << p.glow << p.object_collision; + pkt << p.node.param0 << p.node.param2 << p.node_tile; Send(&pkt); } -- cgit v1.2.3 From 60bab8b2d7b61383188c10f5d931dc7b5522d042 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 6 Jun 2020 17:17:08 +0100 Subject: Add HTTP API to main menu (#9998) --- builtin/fstk/dialog.lua | 19 +++ builtin/fstk/ui.lua | 2 +- builtin/init.lua | 1 + builtin/mainmenu/dlg_contentstore.lua | 49 +++--- doc/fst_api.txt | 7 +- doc/menu_lua_api.txt | 273 +++++++++++++++++++++++----------- src/script/lua_api/l_http.cpp | 44 +++++- src/script/lua_api/l_http.h | 6 + src/script/scripting_mainmenu.cpp | 2 + 9 files changed, 290 insertions(+), 113 deletions(-) (limited to 'src/script/lua_api') diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua index df887f413..ea57df1d2 100644 --- a/builtin/fstk/dialog.lua +++ b/builtin/fstk/dialog.lua @@ -67,3 +67,22 @@ function dialog_create(name,get_formspec,buttonhandler,eventhandler) ui.add(self) return self end + +function messagebox(name, message) + return dialog_create(name, + function() + return ([[ + formspec_version[3] + size[8,3] + textarea[0.375,0.375;7.25,1.2;;;%s] + button[3,1.825;2,0.8;ok;%s] + ]]):format(message, fgettext("OK")) + end, + function(this, fields) + if fields.ok then + this:delete() + return true + end + end, + nil) +end diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index 884100543..6d26aabf0 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -85,7 +85,7 @@ function ui.update() "box[0.5,1.2;13,5;#000]", ("textarea[0.5,1.2;13,5;;%s;%s]"):format( error_title, error_message), - "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("Ok") .. "]" + "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]" } else local active_toplevel_ui_elements = 0 diff --git a/builtin/init.lua b/builtin/init.lua index f76174be7..75bb3db85 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -36,6 +36,7 @@ dofile(commonpath .. "misc_helpers.lua") if INIT == "game" then dofile(gamepath .. "init.lua") + assert(not core.get_http_api) elseif INIT == "mainmenu" then local mm_script = core.settings:get("main_menu_script") if mm_script and mm_script ~= "" then diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua index 8ac4dba68..ae119f48f 100644 --- a/builtin/mainmenu/dlg_contentstore.lua +++ b/builtin/mainmenu/dlg_contentstore.lua @@ -1,5 +1,5 @@ --Minetest ---Copyright (C) 2018 rubenwardy +--Copyright (C) 2018-20 rubenwardy -- --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 @@ -15,8 +15,18 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +if not minetest.get_http_api then + function create_store_dlg() + return messagebox("store", + fgettext("ContentDB is not available when Minetest was compiled without cURL")) + end + return +end + local store = { packages = {}, packages_full = {} } +local http = minetest.get_http_api() + -- Screenshot local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb" assert(core.create_dir(screenshot_dir)) @@ -171,11 +181,6 @@ local function get_screenshot(package) end function store.load() - local tmpdir = os.tempfolder() - local target = tmpdir .. DIR_DELIM .. "packages.json" - - assert(core.create_dir(tmpdir)) - local version = core.get_version() local base_url = core.settings:get("contentdb_url") local url = base_url .. @@ -189,31 +194,29 @@ function store.load() end end - core.download_file(url, target) + local timeout = tonumber(minetest.settings:get("curl_file_download_timeout")) + local response = http.fetch_sync({ url = url, timeout = timeout }) + if not response.succeeded then + return + end - local file = io.open(target, "r") - if file then - store.packages_full = core.parse_json(file:read("*all")) or {} - file:close() + store.packages_full = core.parse_json(response.data) or {} - for _, package in pairs(store.packages_full) do - package.url = base_url .. "/packages/" .. + for _, package in pairs(store.packages_full) do + package.url = base_url .. "/packages/" .. package.author .. "/" .. package.name .. "/releases/" .. package.release .. "/download/" - local name_len = #package.name - if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then - package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5) - else - package.id = package.author:lower() .. "/" .. package.name - end + local name_len = #package.name + if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then + package.id = package.author:lower() .. "/" .. package.name:sub(1, name_len - 5) + else + package.id = package.author:lower() .. "/" .. package.name end - - store.packages = store.packages_full - store.loaded = true end - core.delete_dir(tmpdir) + store.packages = store.packages_full + store.loaded = true end function store.update_paths() diff --git a/doc/fst_api.txt b/doc/fst_api.txt index c8bcb1e91..6f9aa14b3 100644 --- a/doc/fst_api.txt +++ b/doc/fst_api.txt @@ -101,6 +101,9 @@ dialog_create(name, cbf_formspec, cbf_button_handler, cbf_events) ^ cbf_events: function to handle events function(dialog, event) +messagebox(name, message) +^ creates a message dialog + Class reference dialog: methods: @@ -113,13 +116,13 @@ methods: ^ hide dialog - delete() ^ delete dialog from ui - + members: - data ^ variable data attached to this dialog - parent ^ parent component to return to on exit - + File: fst/buttonbar.lua ----------------------- diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 485c50110..4eccf5d10 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -3,34 +3,53 @@ Minetest Lua Mainmenu API Reference 5.3.0 Introduction ------------- + The main menu is defined as a formspec by Lua in builtin/mainmenu/ Description of formspec language to show your menu is in lua_api.txt + Callbacks --------- + core.buttonhandler(fields): called when a button is pressed. ^ fields = {name1 = value1, name2 = value2, ...} core.event_handler(event) ^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter" + Gamedata -------- + The "gamedata" table is read when calling core.start(). It should contain: { - playername = , - password = , - address = , - port = , - selected_world = , -- 0 for client mode - singleplayer = , + playername = , + password = , + address = , + port = , + selected_world = , -- 0 for client mode + singleplayer = , } + Functions --------- + core.start() core.close() +core.get_min_supp_proto() +^ returns the minimum supported network protocol version +core.get_max_supp_proto() +^ returns the maximum supported network protocol version +core.open_url(url) +^ opens the URL in a web browser, returns false on failure. +^ Must begin with http:// or https:// +core.get_version() (possible in async calls) +^ returns current core version + + +Filesystem +---------- -Filesystem: core.get_builtin_path() ^ returns path to builtin root core.create_dir(absolute_path) (possible in async calls) @@ -48,12 +67,6 @@ core.extract_zip(zipfile,destination) [unzip within path required] ^ zipfile to extract ^ destination folder to extract to ^ returns true/false -core.download_file(url,target) (possible in async calls) -^ url to download -^ target to store to -^ returns true/false -core.get_version() (possible in async calls) -^ returns current core version core.sound_play(spec, looped) -> handle ^ spec = SimpleSoundSpec (see lua-api.txt) ^ looped = bool @@ -67,7 +80,82 @@ core.get_mapgen_names([include_hidden=false]) -> table of map generator algorith registered in the core (possible in async calls) core.get_cache_path() -> path of cache -Formspec: + +HTTP Requests +------------- + +* core.download_file(url, target) (possible in async calls) + * url to download, and target to store to + * returns true/false +* `minetest.get_http_api()` (possible in async calls) + * returns `HTTPApiTable` containing http functions. + * The returned table contains the functions `fetch_sync`, `fetch_async` and + `fetch_async_get` described below. + * Function only exists if minetest server was built with cURL support. +* `HTTPApiTable.fetch_sync(HTTPRequest req)`: returns HTTPRequestResult + * Performs given request synchronously +* `HTTPApiTable.fetch_async(HTTPRequest req)`: returns handle + * Performs given request asynchronously and returns handle for + `HTTPApiTable.fetch_async_get` +* `HTTPApiTable.fetch_async_get(handle)`: returns HTTPRequestResult + * Return response data for given asynchronous HTTP request + +### `HTTPRequest` definition + +Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`. + + { + url = "http://example.org", + + timeout = 10, + -- Timeout for connection in seconds. Default is 3 seconds. + + post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"}, + -- Optional, if specified a POST request with post_data is performed. + -- Accepts both a string and a table. If a table is specified, encodes + -- table as x-www-form-urlencoded key-value pairs. + -- If post_data is not specified, a GET request is performed instead. + + user_agent = "ExampleUserAgent", + -- Optional, if specified replaces the default minetest user agent with + -- given string + + extra_headers = { "Accept-Language: en-us", "Accept-Charset: utf-8" }, + -- Optional, if specified adds additional headers to the HTTP request. + -- You must make sure that the header strings follow HTTP specification + -- ("Key: Value"). + + multipart = boolean + -- Optional, if true performs a multipart HTTP request. + -- Default is false. + } + +### `HTTPRequestResult` definition + +Passed to `HTTPApiTable.fetch` callback. Returned by +`HTTPApiTable.fetch_async_get`. + + { + completed = true, + -- If true, the request has finished (either succeeded, failed or timed + -- out) + + succeeded = true, + -- If true, the request was successful + + timeout = false, + -- If true, the request timed out + + code = 200, + -- HTTP status code + + data = "response" + } + + +Formspec +-------- + core.update_formspec(formspec) core.get_table_index(tablename) -> index ^ can also handle textlists @@ -82,7 +170,10 @@ core.explode_textlist_event(string) -> table core.set_formspec_prepend(formspec) ^ string to be added to every mainmenu formspec, to be used for theming. -GUI: + +GUI +--- + core.set_background(type, texturepath,[tile],[minsize]) ^ type: "background", "overlay", "header" or "footer" ^ tile: tile the image instead of scaling (background only) @@ -102,86 +193,96 @@ core.show_path_select_dialog(formname, caption, is_file_select) ^ returns nil or selected file/folder core.get_screen_info() ^ returns { - density = , - display_width = , - display_height = , - window_width = , - window_height = - } + density = , + display_width = , + display_height = , + window_width = , + window_height = + } -### Content and Packages + +Content and Packages +-------------------- Content - an installed mod, modpack, game, or texture pack (txt) Package - content which is downloadable from the content db, may or may not be installed. * core.get_modpath() (possible in async calls) - * returns path to global modpath + * returns path to global modpath * core.get_clientmodpath() (possible in async calls) - * returns path to global client-side modpath + * returns path to global client-side modpath * core.get_gamepath() (possible in async calls) - * returns path to global gamepath + * returns path to global gamepath * core.get_texturepath() (possible in async calls) - * returns path to default textures + * returns path to default textures * core.get_game(index) - * returns: - - { - id = , - path = , - gamemods_path = , - name = , - menuicon_path = , - author = "author", - DEPRECATED: - addon_mods_paths = {[1] = ,}, - } + * returns: + + { + id = , + path = , + gamemods_path = , + name = , + menuicon_path = , + author = "author", + DEPRECATED: + addon_mods_paths = {[1] = ,}, + } * core.get_games() -> table of all games in upper format (possible in async calls) * core.get_content_info(path) - * returns + * returns - { - name = "name of content", - type = "mod" or "modpack" or "game" or "txp", - description = "description", - author = "author", - path = "path/to/content", - depends = {"mod", "names"}, -- mods only - optional_depends = {"mod", "names"}, -- mods only - } + { + name = "name of content", + type = "mod" or "modpack" or "game" or "txp", + description = "description", + author = "author", + path = "path/to/content", + depends = {"mod", "names"}, -- mods only + optional_depends = {"mod", "names"}, -- mods only + } -Favorites: +Favorites +--------- + core.get_favorites(location) -> list of favorites (possible in async calls) ^ location: "local" or "online" ^ returns { - [1] = { - clients = , - clients_max = , - version = , - password = , - creative = , - damage = , - pvp = , - description = , - name = , - address =
, - port = - clients_list = - mods = - }, - ... + [1] = { + clients = , + clients_max = , + version = , + password = , + creative = , + damage = , + pvp = , + description = , + name = , + address =
, + port = + clients_list = + mods = + }, + ... } core.delete_favorite(id, location) -> success -Logging: + +Logging +------- + core.debug(line) (possible in async calls) ^ Always printed to stderr and logfile (print() is redirected here) core.log(line) (possible in async calls) core.log(loglevel, line) (possible in async calls) ^ loglevel one of "error", "action", "info", "verbose" -Settings: + +Settings +-------- + core.settings:set(name, value) core.settings:get(name) -> string or nil (possible in async calls) core.settings:set_bool(name, value) @@ -191,19 +292,25 @@ core.settings:save() -> nil, save all settings to config file For a complete list of methods of the Settings object see [lua_api.txt](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt) -Worlds: + +Worlds +------ + core.get_worlds() -> list of worlds (possible in async calls) ^ returns { - [1] = { - path = , - name = , - gameid = , - }, + [1] = { + path = , + name = , + gameid = , + }, } core.create_world(worldname, gameid) core.delete_world(index) -Helpers: + +Helpers +------- + core.get_us_time() ^ returns time with microsecond precision core.gettext(string) -> string @@ -228,18 +335,10 @@ minetest.encode_base64(string) (possible in async calls) minetest.decode_base64(string) (possible in async calls) ^ Decodes a string encoded in base64. -Version compat: -core.get_min_supp_proto() -^ returns the minimum supported network protocol version -core.get_max_supp_proto() -^ returns the maximum supported network protocol version -Other: -core.open_url(url) -^ opens the URL in a web browser, returns false on failure. -^ Must begin with http:// or https:// +Async +----- -Async: core.handle_async(async_job,parameters,finished) ^ execute a function asynchronously ^ async_job is a function receiving one parameter and returning one parameter @@ -250,11 +349,13 @@ core.handle_async(async_job,parameters,finished) Limitations of Async operations -No access to global lua variables, don't even try -Limited set of available functions - e.g. No access to functions modifying menu like core.start,core.close, - core.show_path_select_dialog + e.g. No access to functions modifying menu like core.start,core.close, + core.show_path_select_dialog + Background music ---------------- + The main menu supports background music. It looks for a `main_menu` sound in `$USER_PATH/sounds`. The same naming conventions as for normal sounds apply. diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp index 2ff651cb5..73b4586e0 100644 --- a/src/script/lua_api/l_http.cpp +++ b/src/script/lua_api/l_http.cpp @@ -83,6 +83,24 @@ void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool setstringfield(L, -1, "data", res.data); } +// http_api.fetch_sync(HTTPRequest definition) +int ModApiHttp::l_http_fetch_sync(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + HTTPFetchRequest req; + read_http_fetch_request(L, req); + + infostream << "Mod performs HTTP request with URL " << req.url << std::endl; + + HTTPFetchResult res; + httpfetch_sync(req, res); + + push_http_fetch_result(L, res, true); + + return 1; +} + // http_api.fetch_async(HTTPRequest definition) int ModApiHttp::l_http_fetch_async(lua_State *L) { @@ -180,11 +198,35 @@ int ModApiHttp::l_request_http_api(lua_State *L) return 1; } + +int ModApiHttp::l_get_http_api(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + lua_newtable(L); + HTTP_API(fetch_async); + HTTP_API(fetch_async_get); + HTTP_API(fetch_sync); + + return 1; +} + #endif void ModApiHttp::Initialize(lua_State *L, int top) { #if USE_CURL - API_FCT(request_http_api); + + bool isMainmenu = false; +#ifndef SERVER + isMainmenu = ModApiBase::getGuiEngine(L) != nullptr; +#endif + + if (isMainmenu) { + API_FCT(get_http_api); + } else { + API_FCT(request_http_api); + } + #endif } diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h index 3d9cdad29..c665235a9 100644 --- a/src/script/lua_api/l_http.h +++ b/src/script/lua_api/l_http.h @@ -32,6 +32,9 @@ private: static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req); static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true); + // http_fetch_sync({url=, timeout=, post_data=}) + static int l_http_fetch_sync(lua_State *L); + // http_fetch_async({url=, timeout=, post_data=}) static int l_http_fetch_async(lua_State *L); @@ -40,6 +43,9 @@ private: // request_http_api() static int l_request_http_api(lua_State *L); + + // get_http_api() + static int l_get_http_api(lua_State *L); #endif public: diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index b6068439a..08858b1a5 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/mods.h" #include "cpp_api/s_internal.h" #include "lua_api/l_base.h" +#include "lua_api/l_http.h" #include "lua_api/l_mainmenu.h" #include "lua_api/l_sound.h" #include "lua_api/l_util.h" @@ -67,6 +68,7 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top) ModApiMainMenu::Initialize(L, top); ModApiUtil::Initialize(L, top); ModApiSound::Initialize(L, top); + ModApiHttp::Initialize(L, top); asyncEngine.registerStateInitializer(registerLuaClasses); asyncEngine.registerStateInitializer(ModApiMainMenu::InitializeAsync); -- cgit v1.2.3 From b16f841756ef86e83710ad2fddf2cd5bafdf4bcc Mon Sep 17 00:00:00 2001 From: Paul Ouellette Date: Tue, 9 Jun 2020 13:37:25 -0400 Subject: LuaItemStack: Add __tostring metamethod (#8785) * LuaItemStack: Add __tostring metamethod * Clean up LuaItemStack::checkobject --- builtin/common/misc_helpers.lua | 2 ++ src/inventory.cpp | 29 ++++++++++++++++------------- src/inventory.h | 4 ++-- src/script/lua_api/l_item.cpp | 25 ++++++++++++++++++------- src/script/lua_api/l_item.h | 3 +++ 5 files changed, 41 insertions(+), 22 deletions(-) (limited to 'src/script/lua_api') diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index a88adf96d..e29a9f422 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -20,6 +20,8 @@ local function basic_dump(o) -- dump's output is intended for humans. --elseif tp == "function" then -- return string.format("loadstring(%q)", string.dump(o)) + elseif tp == "userdata" then + return tostring(o) else return string.format("<%s>", tp) end diff --git a/src/inventory.cpp b/src/inventory.cpp index 77ecf5876..349ee503d 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -56,28 +56,31 @@ ItemStack::ItemStack(const std::string &name_, u16 count_, count = 1; } -void ItemStack::serialize(std::ostream &os) const +void ItemStack::serialize(std::ostream &os, bool serialize_meta) const { if (empty()) return; // Check how many parts of the itemstring are needed int parts = 1; - if(count != 1) - parts = 2; - if(wear != 0) - parts = 3; if (!metadata.empty()) parts = 4; + else if (wear != 0) + parts = 3; + else if (count != 1) + parts = 2; - os<= 2) - os<<" "<= 3) - os<<" "<= 2) + os << " " << count; + if (parts >= 3) + os << " " << wear; if (parts >= 4) { os << " "; - metadata.serialize(os); + if (serialize_meta) + metadata.serialize(os); + else + os << ""; } } @@ -240,10 +243,10 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef) deSerialize(is, itemdef); } -std::string ItemStack::getItemString() const +std::string ItemStack::getItemString(bool include_meta) const { std::ostringstream os(std::ios::binary); - serialize(os); + serialize(os, include_meta); return os.str(); } diff --git a/src/inventory.h b/src/inventory.h index 2828d3e5a..67a7859ed 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -40,13 +40,13 @@ struct ItemStack ~ItemStack() = default; // Serialization - void serialize(std::ostream &os) const; + void serialize(std::ostream &os, bool serialize_meta = true) const; // Deserialization. Pass itemdef unless you don't want aliases resolved. void deSerialize(std::istream &is, IItemDefManager *itemdef = NULL); void deSerialize(const std::string &s, IItemDefManager *itemdef = NULL); // Returns the string used for inventory - std::string getItemString() const; + std::string getItemString(bool include_meta = true) const; // Returns the tooltip std::string getDescription(IItemDefManager *itemdef) const; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 0a403acbd..d67cab76f 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -37,6 +37,15 @@ int LuaItemStack::gc_object(lua_State *L) return 0; } +// __tostring metamethod +int LuaItemStack::mt_tostring(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + std::string itemstring = o->m_stack.getItemString(false); + lua_pushfstring(L, "ItemStack(\"%s\")", itemstring.c_str()); + return 1; +} + // is_empty(self) -> true/false int LuaItemStack::l_is_empty(lua_State *L) { @@ -433,12 +442,9 @@ int LuaItemStack::create(lua_State *L, const ItemStack &item) return 1; } -LuaItemStack* LuaItemStack::checkobject(lua_State *L, int narg) +LuaItemStack *LuaItemStack::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 *(LuaItemStack**)ud; // unbox pointer + return *(LuaItemStack **)luaL_checkudata(L, narg, className); } void LuaItemStack::Register(lua_State *L) @@ -448,9 +454,10 @@ void LuaItemStack::Register(lua_State *L) luaL_newmetatable(L, className); int metatable = lua_gettop(L); + // hide metatable from Lua getmetatable() lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -460,12 +467,16 @@ void LuaItemStack::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__tostring"); + lua_pushcfunction(L, mt_tostring); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable luaL_openlib(L, 0, methods, 0); // fill methodtable lua_pop(L, 1); // drop methodtable - // Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil)) + // Can be created from Lua (ItemStack(itemstack or itemstring or table or nil)) lua_register(L, className, create_object); } diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index 6fab58045..98744c071 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -34,6 +34,9 @@ private: // garbage collector static int gc_object(lua_State *L); + // __tostring metamethod + static int mt_tostring(lua_State *L); + // is_empty(self) -> true/false static int l_is_empty(lua_State *L); -- cgit v1.2.3 From 2424dfe007e451bb02f87884c2b272cf307d6e7c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 13 Jun 2020 19:03:26 +0200 Subject: Server pushing media at runtime (#9961) --- doc/lua_api.txt | 14 +++ src/client/client.cpp | 12 ++- src/client/client.h | 9 +- src/client/clientmedia.cpp | 10 +- src/client/clientmedia.h | 5 + src/client/filecache.cpp | 8 ++ src/client/filecache.h | 1 + src/filesys.cpp | 6 ++ src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 46 +++++++++ src/network/networkprotocol.h | 9 ++ src/network/serveropcodes.cpp | 2 +- src/script/lua_api/l_server.cpp | 22 +++- src/script/lua_api/l_server.h | 3 + src/server.cpp | 195 +++++++++++++++++++++++------------- src/server.h | 4 + 16 files changed, 263 insertions(+), 85 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ed060c4ad..cb968958f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5217,6 +5217,20 @@ Server * Returns a code (0: successful, 1: no such player, 2: player is connected) * `minetest.remove_player_auth(name)`: remove player authentication data * Returns boolean indicating success (false if player nonexistant) +* `minetest.dynamic_add_media(filepath)` + * Adds the file at the given path to the media sent to clients by the server + on startup and also pushes this file to already connected clients. + The file must be a supported image, sound or model format. It must not be + modified, deleted, moved or renamed after calling this function. + The list of dynamically added media is not persisted. + * Returns boolean indicating success (duplicate files count as error) + * The media will be ready to use (in e.g. entity textures, sound_play) + immediately after calling this function. + Old clients that lack support for this feature will not see the media + unless they reconnect to the server. + * Since media transferred this way does not use client caching or HTTP + transfers, dynamic media should not be used with big files or performance + will suffer. Bans ---- diff --git a/src/client/client.cpp b/src/client/client.cpp index c03c062c6..34f97a9de 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -670,11 +670,9 @@ void Client::step(float dtime) } } -bool Client::loadMedia(const std::string &data, const std::string &filename) +bool Client::loadMedia(const std::string &data, const std::string &filename, + bool from_media_push) { - // Silly irrlicht's const-incorrectness - Buffer data_rw(data.c_str(), data.size()); - std::string name; const char *image_ext[] = { @@ -690,6 +688,9 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) io::IFileSystem *irrfs = RenderingEngine::get_filesystem(); video::IVideoDriver *vdrv = RenderingEngine::get_video_driver(); + // Silly irrlicht's const-incorrectness + Buffer data_rw(data.c_str(), data.size()); + // Create an irrlicht memory file io::IReadFile *rfile = irrfs->createMemoryReadFile( *data_rw, data_rw.getSize(), "_tempreadfile"); @@ -727,7 +728,6 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) ".x", ".b3d", ".md2", ".obj", NULL }; - name = removeStringEnd(filename, model_ext); if (!name.empty()) { verbosestream<<"Client: Storing model into memory: " @@ -744,6 +744,8 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) }; name = removeStringEnd(filename, translate_ext); if (!name.empty()) { + if (from_media_push) + return false; TRACESTREAM(<< "Client: Loading translation: " << "\"" << filename << "\"" << std::endl); g_client_translations->loadTranslation(data); diff --git a/src/client/client.h b/src/client/client.h index 3b1095ac2..733634db1 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -222,6 +222,7 @@ public: void handleCommand_FormspecPrepend(NetworkPacket *pkt); void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); + void handleCommand_MediaPush(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -376,7 +377,8 @@ public: // The following set of functions is used by ClientMediaDownloader // Insert a media file appropriately into the appropriate manager - bool loadMedia(const std::string &data, const std::string &filename); + bool loadMedia(const std::string &data, const std::string &filename, + bool from_media_push = false); // Send a request for conventional media transfer void request_media(const std::vector &file_requests); @@ -488,6 +490,7 @@ private: Camera *m_camera = nullptr; Minimap *m_minimap = nullptr; bool m_minimap_disabled_by_server = false; + // Server serialization version u8 m_server_ser_ver; @@ -529,7 +532,6 @@ private: AuthMechanism m_chosen_auth_mech; void *m_auth_data = nullptr; - bool m_access_denied = false; bool m_access_denied_reconnect = false; std::string m_access_denied_reason = ""; @@ -538,7 +540,10 @@ private: bool m_nodedef_received = false; bool m_activeobjects_received = false; bool m_mods_loaded = false; + ClientMediaDownloader *m_media_downloader; + // Set of media filenames pushed by server at runtime + std::unordered_set m_media_pushed_files; // time_of_day speed approximation for old protocol bool m_time_of_day_set = false; diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 6da99bbbf..8cd3b6bcc 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -35,6 +35,15 @@ static std::string getMediaCacheDir() return porting::path_cache + DIR_DELIM + "media"; } +bool clientMediaUpdateCache(const std::string &raw_hash, const std::string &filedata) +{ + FileCache media_cache(getMediaCacheDir()); + std::string sha1_hex = hex_encode(raw_hash); + if (!media_cache.exists(sha1_hex)) + return media_cache.update(sha1_hex, filedata); + return true; +} + /* ClientMediaDownloader */ @@ -559,7 +568,6 @@ bool ClientMediaDownloader::checkAndLoad( return true; } - /* Minetest Hashset File Format diff --git a/src/client/clientmedia.h b/src/client/clientmedia.h index 92831082c..5a918535b 100644 --- a/src/client/clientmedia.h +++ b/src/client/clientmedia.h @@ -33,6 +33,11 @@ struct HTTPFetchResult; #define MTHASHSET_FILE_SIGNATURE 0x4d544853 // 'MTHS' #define MTHASHSET_FILE_NAME "index.mth" +// Store file into media cache (unless it exists already) +// Validating the hash is responsibility of the caller +bool clientMediaUpdateCache(const std::string &raw_hash, + const std::string &filedata); + class ClientMediaDownloader { public: diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index 3d1b302a8..46bbe4059 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -82,8 +82,16 @@ bool FileCache::update(const std::string &name, const std::string &data) std::string path = m_dir + DIR_DELIM + name; return updateByPath(path, data); } + bool FileCache::load(const std::string &name, std::ostream &os) { std::string path = m_dir + DIR_DELIM + name; return loadByPath(path, os); } + +bool FileCache::exists(const std::string &name) +{ + std::string path = m_dir + DIR_DELIM + name; + std::ifstream fis(path.c_str(), std::ios_base::binary); + return fis.good(); +} diff --git a/src/client/filecache.h b/src/client/filecache.h index 96e4c8ba1..ea6afc4b2 100644 --- a/src/client/filecache.h +++ b/src/client/filecache.h @@ -33,6 +33,7 @@ public: bool update(const std::string &name, const std::string &data); bool load(const std::string &name, std::ostream &os); + bool exists(const std::string &name); private: std::string m_dir; diff --git a/src/filesys.cpp b/src/filesys.cpp index f61b39b94..0bc351669 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -691,6 +691,12 @@ std::string AbsolutePath(const std::string &path) const char *GetFilenameFromPath(const char *path) { const char *filename = strrchr(path, DIR_DELIM_CHAR); + // Consistent with IsDirDelimiter this function handles '/' too + if (DIR_DELIM_CHAR != '/') { + const char *tmp = strrchr(path, '/'); + if (tmp && tmp > filename) + filename = tmp; + } return filename ? filename + 1 : path; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 0f20047c0..f812a08a1 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -68,7 +68,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A { "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B - null_command_handler, + { "TOCLIENT_MEDIA_PUSH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MediaPush }, // 0x2C null_command_handler, null_command_handler, { "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index e000acc92..5934eaf8c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" +#include "util/sha1.h" #include "tileanimation.h" #include "gettext.h" #include "skyparams.h" @@ -1471,6 +1472,51 @@ void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt) player->addVelocity(added_vel); } +void Client::handleCommand_MediaPush(NetworkPacket *pkt) +{ + std::string raw_hash, filename, filedata; + bool cached; + + *pkt >> raw_hash >> filename >> cached; + filedata = pkt->readLongString(); + + if (raw_hash.size() != 20 || filedata.empty() || filename.empty() || + !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + throw PacketError("Illegal filename, data or hash"); + } + + verbosestream << "Server pushes media file \"" << filename << "\" with " + << filedata.size() << " bytes of data (cached=" << cached + << ")" << std::endl; + + if (m_media_pushed_files.count(filename) != 0) { + // Silently ignore for synchronization purposes + return; + } + + // Compute and check checksum of data + std::string computed_hash; + { + SHA1 ctx; + ctx.addBytes(filedata.c_str(), filedata.size()); + unsigned char *buf = ctx.getDigest(); + computed_hash.assign((char*) buf, 20); + free(buf); + } + if (raw_hash != computed_hash) { + verbosestream << "Hash of file data mismatches, ignoring." << std::endl; + return; + } + + // Actually load media + loadMedia(filedata, filename, true); + m_media_pushed_files.insert(filename); + + // Cache file for the next time when this client joins the same server + if (cached) + clientMediaUpdateCache(raw_hash, filedata); +} + /* * Mod channels */ diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ab924f1db..fd683eac9 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -323,6 +323,15 @@ enum ToClientCommand v3f added_vel */ + TOCLIENT_MEDIA_PUSH = 0x2C, + /* + std::string raw_hash + std::string filename + bool should_be_cached + u32 len + char filedata[len] + */ + // (oops, there is some gap here) TOCLIENT_CHAT_MESSAGE = 0x2F, diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 6ee4ff256..2fc3197c2 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -167,7 +167,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A { "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B - null_command_factory, // 0x2C + { "TOCLIENT_MEDIA_PUSH", 0, true }, // 0x2C (sent over channel 1 too) null_command_factory, // 0x2D null_command_factory, // 0x2E { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index b6754938e..6f934bb9d 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_converter.h" #include "common/c_content.h" #include "cpp_api/s_base.h" +#include "cpp_api/s_security.h" #include "server.h" #include "environment.h" #include "remoteplayer.h" @@ -412,9 +413,6 @@ int ModApiServer::l_get_modnames(lua_State *L) std::vector modlist; getServer(L)->getModNames(modlist); - // Take unsorted items from mods_unsorted and sort them into - // mods_sorted; not great performance but the number of mods on a - // server will likely be small. std::sort(modlist.begin(), modlist.end()); // Package them up for Lua @@ -474,6 +472,23 @@ int ModApiServer::l_sound_fade(lua_State *L) return 0; } +// dynamic_add_media(filepath) +int ModApiServer::l_dynamic_add_media(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + // Reject adding media before the server has started up + if (!getEnv(L)) + throw LuaError("Dynamic media cannot be added before server has started up"); + + std::string filepath = readParam(L, 1); + CHECK_SECURE_PATH(L, filepath.c_str(), false); + + bool ok = getServer(L)->dynamicAddMedia(filepath); + lua_pushboolean(L, ok); + return 1; +} + // is_singleplayer() int ModApiServer::l_is_singleplayer(lua_State *L) { @@ -538,6 +553,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); API_FCT(sound_fade); + API_FCT(dynamic_add_media); API_FCT(get_player_information); API_FCT(get_player_privs); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 3aa1785a2..938bfa8ef 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -70,6 +70,9 @@ private: // sound_fade(handle, step, gain) static int l_sound_fade(lua_State *L); + // dynamic_add_media(filepath) + static int l_dynamic_add_media(lua_State *L); + // get_player_privs(name, text) static int l_get_player_privs(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 6ecbd7097..fe2bb3840 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2405,9 +2405,87 @@ bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos) return true; } +bool Server::addMediaFile(const std::string &filename, + const std::string &filepath, std::string *filedata_to, + std::string *digest_to) +{ + // If name contains illegal characters, ignore the file + if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { + infostream << "Server: ignoring illegal file name: \"" + << filename << "\"" << std::endl; + return false; + } + // If name is not in a supported format, ignore it + const char *supported_ext[] = { + ".png", ".jpg", ".bmp", ".tga", + ".pcx", ".ppm", ".psd", ".wal", ".rgb", + ".ogg", + ".x", ".b3d", ".md2", ".obj", + // Custom translation file format + ".tr", + NULL + }; + if (removeStringEnd(filename, supported_ext).empty()) { + infostream << "Server: ignoring unsupported file extension: \"" + << filename << "\"" << std::endl; + return false; + } + // Ok, attempt to load the file and add to cache + + // Read data + std::ifstream fis(filepath.c_str(), std::ios_base::binary); + if (!fis.good()) { + errorstream << "Server::addMediaFile(): Could not open \"" + << filename << "\" for reading" << std::endl; + return false; + } + std::string filedata; + bool bad = false; + for (;;) { + char buf[1024]; + fis.read(buf, sizeof(buf)); + std::streamsize len = fis.gcount(); + filedata.append(buf, len); + if (fis.eof()) + break; + if (!fis.good()) { + bad = true; + break; + } + } + if (bad) { + errorstream << "Server::addMediaFile(): Failed to read \"" + << filename << "\"" << std::endl; + return false; + } else if (filedata.empty()) { + errorstream << "Server::addMediaFile(): Empty file \"" + << filepath << "\"" << std::endl; + return false; + } + + SHA1 sha1; + sha1.addBytes(filedata.c_str(), filedata.length()); + + unsigned char *digest = sha1.getDigest(); + std::string sha1_base64 = base64_encode(digest, 20); + std::string sha1_hex = hex_encode((char*) digest, 20); + if (digest_to) + *digest_to = std::string((char*) digest, 20); + free(digest); + + // Put in list + m_media[filename] = MediaInfo(filepath, sha1_base64); + verbosestream << "Server: " << sha1_hex << " is " << filename + << std::endl; + + if (filedata_to) + *filedata_to = std::move(filedata); + return true; +} + void Server::fillMediaCache() { - infostream<<"Server: Calculating media file checksums"< paths; @@ -2419,80 +2497,15 @@ void Server::fillMediaCache() for (const std::string &mediapath : paths) { std::vector dirlist = fs::GetDirListing(mediapath); for (const fs::DirListNode &dln : dirlist) { - if (dln.dir) // Ignode dirs - continue; - std::string filename = dln.name; - // If name contains illegal characters, ignore the file - if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) { - infostream<<"Server: ignoring illegal file name: \"" - << filename << "\"" << std::endl; - continue; - } - // If name is not in a supported format, ignore it - const char *supported_ext[] = { - ".png", ".jpg", ".bmp", ".tga", - ".pcx", ".ppm", ".psd", ".wal", ".rgb", - ".ogg", - ".x", ".b3d", ".md2", ".obj", - // Custom translation file format - ".tr", - NULL - }; - if (removeStringEnd(filename, supported_ext).empty()){ - infostream << "Server: ignoring unsupported file extension: \"" - << filename << "\"" << std::endl; + if (dln.dir) // Ignore dirs continue; - } - // Ok, attempt to load the file and add to cache - std::string filepath; - filepath.append(mediapath).append(DIR_DELIM).append(filename); - - // Read data - std::ifstream fis(filepath.c_str(), std::ios_base::binary); - if (!fis.good()) { - errorstream << "Server::fillMediaCache(): Could not open \"" - << filename << "\" for reading" << std::endl; - continue; - } - std::ostringstream tmp_os(std::ios_base::binary); - bool bad = false; - for(;;) { - char buf[1024]; - fis.read(buf, 1024); - std::streamsize len = fis.gcount(); - tmp_os.write(buf, len); - if (fis.eof()) - break; - if (!fis.good()) { - bad = true; - break; - } - } - if(bad) { - errorstream<<"Server::fillMediaCache(): Failed to read \"" - << filename << "\"" << std::endl; - continue; - } - if(tmp_os.str().length() == 0) { - errorstream << "Server::fillMediaCache(): Empty file \"" - << filepath << "\"" << std::endl; - continue; - } - - SHA1 sha1; - sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length()); - - unsigned char *digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest, 20); - std::string sha1_hex = hex_encode((char*)digest, 20); - free(digest); - - // Put in list - m_media[filename] = MediaInfo(filepath, sha1_base64); - verbosestream << "Server: " << sha1_hex << " is " << filename - << std::endl; + std::string filepath = mediapath; + filepath.append(DIR_DELIM).append(dln.name); + addMediaFile(dln.name, filepath); } } + + infostream << "Server: " << m_media.size() << " media files collected" << std::endl; } void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code) @@ -3428,6 +3441,44 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } +bool Server::dynamicAddMedia(const std::string &filepath) +{ + std::string filename = fs::GetFilenameFromPath(filepath.c_str()); + if (m_media.find(filename) != m_media.end()) { + errorstream << "Server::dynamicAddMedia(): file \"" << filename + << "\" already exists in media cache" << std::endl; + return false; + } + + // Load the file and add it to our media cache + std::string filedata, raw_hash; + bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash); + if (!ok) + return false; + + // Push file to existing clients + NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0); + pkt << raw_hash << filename << (bool) true; + pkt.putLongString(filedata); + + auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent); + for (session_t client_id : client_ids) { + /* + The network layer only guarantees ordered delivery inside a channel. + Since the very next packet could be one that uses the media, we have + to push the media over ALL channels to ensure it is processed before + it is used. + In practice this means we have to send it twice: + - channel 1 (HUD) + - channel 0 (everything else: e.g. play_sound, object messages) + */ + m_clients.send(client_id, 1, &pkt, true); + m_clients.send(client_id, 0, &pkt, true); + } + + return true; +} + // actions: time-reversed list // Return value: success/failure bool Server::rollbackRevertActions(const std::list &actions, diff --git a/src/server.h b/src/server.h index 27943cc29..f44716531 100644 --- a/src/server.h +++ b/src/server.h @@ -236,6 +236,8 @@ public: void deleteParticleSpawner(const std::string &playername, u32 id); + bool dynamicAddMedia(const std::string &filepath); + ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); } void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id); @@ -435,6 +437,8 @@ private: // Sends blocks to clients (locks env and con on its own) void SendBlocks(float dtime); + bool addMediaFile(const std::string &filename, const std::string &filepath, + std::string *filedata = nullptr, std::string *digest = nullptr); void fillMediaCache(); void sendMediaAnnouncement(session_t peer_id, const std::string &lang_code); void sendRequestedMedia(session_t peer_id, -- cgit v1.2.3 From e7e065f553b430173e9112ad55c7046cfc02f2c5 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 13 Jun 2020 22:46:20 +0200 Subject: Exposing the zoom key to Lua API (#9903) Co-authored-by: Raul Ferriz --- doc/lua_api.txt | 7 ++++--- src/client/game.cpp | 5 +++-- src/network/serverpackethandler.cpp | 1 + src/script/lua_api/l_object.cpp | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cb968958f..07e9698e8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6106,13 +6106,14 @@ object you are working with still exists. * `get_formspec_prepend(formspec)`: returns a formspec string. * `get_player_control()`: returns table with player pressed keys * The table consists of fields with boolean value representing the pressed - keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up. + keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom. * example: `{jump=false, right=true, left=false, LMB=false, RMB=false, - sneak=true, aux1=false, down=false, up=false}` + sneak=true, aux1=false, down=false, up=false, zoom=false}` + * The `zoom` field is available since 5.3 * `get_player_control_bits()`: returns integer with bit packed player pressed keys. * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak, - 7/LMB, 8/RMB + 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3) * `set_physics_override(override_table)` * `override_table` is a table with the following fields: * `speed`: multiplier to default walking speed value (default: `1`) diff --git a/src/client/game.cpp b/src/client/game.cpp index 139742cec..069c482ca 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2490,7 +2490,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); - u32 keypress_bits = + u32 keypress_bits = ( ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | @@ -2499,7 +2499,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); #ifdef ANDROID diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index fed3b6f85..b3008bb50 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -501,6 +501,7 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, player->control.sneak = (keyPressed & 64); player->control.LMB = (keyPressed & 128); player->control.RMB = (keyPressed & 256); + player->control.zoom = (keyPressed & 512); if (playersao->checkMovementCheat()) { // Call callbacks diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 0a9f3117b..e7394133a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1459,6 +1459,8 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "LMB"); lua_pushboolean(L, control.RMB); lua_setfield(L, -2, "RMB"); + lua_pushboolean(L, control.zoom); + lua_setfield(L, -2, "zoom"); return 1; } -- cgit v1.2.3 From 4cb18d4874126b384ad5faa1b10a208c8fdce4db Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 22 Jun 2020 14:40:04 +0100 Subject: Fix HTTP API not being available in async mainmenu (#10086) --- src/script/lua_api/l_http.cpp | 7 +++++++ src/script/lua_api/l_http.h | 1 + src/script/scripting_mainmenu.cpp | 1 + 3 files changed, 9 insertions(+) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp index 73b4586e0..ec43bf174 100644 --- a/src/script/lua_api/l_http.cpp +++ b/src/script/lua_api/l_http.cpp @@ -230,3 +230,10 @@ void ModApiHttp::Initialize(lua_State *L, int top) #endif } + +void ModApiHttp::InitializeAsync(lua_State *L, int top) +{ +#if USE_CURL + API_FCT(get_http_api); +#endif +} diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h index c665235a9..de6e51b37 100644 --- a/src/script/lua_api/l_http.h +++ b/src/script/lua_api/l_http.h @@ -50,4 +50,5 @@ private: public: static void Initialize(lua_State *L, int top); + static void InitializeAsync(lua_State *L, int top); }; diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index 08858b1a5..0f672f917 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -73,6 +73,7 @@ void MainMenuScripting::initializeModApi(lua_State *L, int top) asyncEngine.registerStateInitializer(registerLuaClasses); asyncEngine.registerStateInitializer(ModApiMainMenu::InitializeAsync); asyncEngine.registerStateInitializer(ModApiUtil::InitializeAsync); + asyncEngine.registerStateInitializer(ModApiHttp::InitializeAsync); // Initialize async environment //TODO possibly make number of async threads configurable -- cgit v1.2.3 From 4b4513a67d9fc426d0f33798d0810a3a0594baaf Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Jul 2020 22:36:53 +0200 Subject: Implement grouped mode for find_nodes_in_area (#9888) plus general improvements to find_node_* functions --- doc/client_lua_api.txt | 13 ++-- doc/lua_api.txt | 13 ++-- src/script/lua_api/l_env.cpp | 176 +++++++++++++++++++++++++------------------ src/script/lua_api/l_env.h | 5 ++ 4 files changed, 124 insertions(+), 83 deletions(-) (limited to 'src/script/lua_api') diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 04815aaa4..3b0046b4f 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -768,12 +768,15 @@ Call these functions only at load time! * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `search_center` is an optional boolean (default: `false`) If true `pos` is also checked for the nodes -* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of - positions. +* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])` + * `pos1` and `pos2` are the min and max positions of the area to search. * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` - * First return value: Table with all node positions - * Second return value: Table with the count of each node with the node name - as index. + * If `grouped` is true the return value is a table indexed by node name + which contains lists of positions. + * If `grouped` is false or absent the return values are as follows: + first value: Table with all node positions + second value: Table with the count of each node with the node name + as index * Area volume is limited to 4,096,000 nodes * `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions. diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d3a367b27..e0c895c97 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4762,12 +4762,15 @@ Environment access * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` * `search_center` is an optional boolean (default: `false`) If true `pos` is also checked for the nodes -* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of - positions. +* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])` + * `pos1` and `pos2` are the min and max positions of the area to search. * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"` - * First return value: Table with all node positions - * Second return value: Table with the count of each node with the node name - as index. + * If `grouped` is true the return value is a table indexed by node name + which contains lists of positions. + * If `grouped` is false or absent the return values are as follows: + first value: Table with all node positions + second value: Table with the count of each node with the node name + as index * Area volume is limited to 4,096,000 nodes * `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a list of positions. diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 89ec9dc7e..b2bc8c5f6 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -752,29 +752,36 @@ int ModApiEnvMod::l_get_gametime(lua_State *L) return 1; } - -// find_node_near(pos, radius, nodenames, search_center) -> pos or nil -// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" -int ModApiEnvMod::l_find_node_near(lua_State *L) +void ModApiEnvMod::collectNodeIds(lua_State *L, int idx, const NodeDefManager *ndef, + std::vector &filter) { - GET_PLAIN_ENV_PTR; - - const NodeDefManager *ndef = env->getGameDef()->ndef(); - v3s16 pos = read_v3s16(L, 1); - int radius = luaL_checkinteger(L, 2); - std::vector filter; - if (lua_istable(L, 3)) { + if (lua_istable(L, idx)) { lua_pushnil(L); - while (lua_next(L, 3) != 0) { + while (lua_next(L, idx) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(readParam(L, -1), filter); // removes value, keeps key for next iteration lua_pop(L, 1); } - } else if (lua_isstring(L, 3)) { + } else if (lua_isstring(L, idx)) { ndef->getIds(readParam(L, 3), filter); } +} + +// find_node_near(pos, radius, nodenames, [search_center]) -> pos or nil +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_node_near(lua_State *L) +{ + GET_PLAIN_ENV_PTR; + + const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); + + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::vector filter; + collectNodeIds(L, 3, ndef, filter); int start_radius = (lua_isboolean(L, 4) && readParam(L, 4)) ? 0 : 1; @@ -785,10 +792,10 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) #endif for (int d = start_radius; d <= radius; d++) { - std::vector list = FacePositionCache::getFacePositions(d); + const std::vector &list = FacePositionCache::getFacePositions(d); for (const v3s16 &i : list) { v3s16 p = pos + i; - content_t c = env->getMap().getNode(p).getContent(); + content_t c = map.getNode(p).getContent(); if (CONTAINS(filter, c)) { push_v3s16(L, p); return 1; @@ -798,8 +805,7 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) return 0; } -// find_nodes_in_area(minp, maxp, nodenames) -> list of positions -// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +// find_nodes_in_area(minp, maxp, nodenames, [grouped]) int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) { GET_PLAIN_ENV_PTR; @@ -809,6 +815,7 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) sortBoxVerticies(minp, maxp); const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); #ifndef SERVER if (Client *client = getClient(L)) { @@ -826,45 +833,79 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) } std::vector filter; - if (lua_istable(L, 3)) { - lua_pushnil(L); - while (lua_next(L, 3) != 0) { - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - ndef->getIds(readParam(L, -1), filter); - // removes value, keeps key for next iteration - lua_pop(L, 1); + collectNodeIds(L, 3, ndef, filter); + + bool grouped = lua_isboolean(L, 4) && readParam(L, 4); + + if (grouped) { + // create the table we will be returning + lua_createtable(L, 0, filter.size()); + int base = lua_gettop(L); + + // create one table for each filter + std::vector idx; + idx.resize(filter.size()); + for (u32 i = 0; i < filter.size(); i++) + lua_newtable(L); + + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Y = minp.Y; p.Y <= maxp.Y; p.Y++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + content_t c = map.getNode(p).getContent(); + + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + // Calculate index of the table and append the position + u32 filt_index = it - filter.begin(); + push_v3s16(L, p); + lua_rawseti(L, base + 1 + filt_index, ++idx[filt_index]); + } } - } else if (lua_isstring(L, 3)) { - ndef->getIds(readParam(L, 3), filter); - } - std::vector individual_count; - individual_count.resize(filter.size()); + // last filter table is at top of stack + u32 i = filter.size() - 1; + do { + if (idx[i] == 0) { + // No such node found -> drop the empty table + lua_pop(L, 1); + } else { + // This node was found -> put table into the return table + lua_setfield(L, base, ndef->get(filter[i]).name.c_str()); + } + } while (i-- != 0); - lua_newtable(L); - u64 i = 0; - for (s16 x = minp.X; x <= maxp.X; x++) - for (s16 y = minp.Y; y <= maxp.Y; y++) - for (s16 z = minp.Z; z <= maxp.Z; z++) { - v3s16 p(x, y, z); - content_t c = env->getMap().getNode(p).getContent(); - - std::vector::iterator it = std::find(filter.begin(), filter.end(), c); - if (it != filter.end()) { - push_v3s16(L, p); - lua_rawseti(L, -2, ++i); + assert(lua_gettop(L) == base); + return 1; + } else { + std::vector individual_count; + individual_count.resize(filter.size()); + + lua_newtable(L); + u32 i = 0; + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Y = minp.Y; p.Y <= maxp.Y; p.Y++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + content_t c = env->getMap().getNode(p).getContent(); - u32 filt_index = it - filter.begin(); - individual_count[filt_index]++; + auto it = std::find(filter.begin(), filter.end(), c); + if (it != filter.end()) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + + u32 filt_index = it - filter.begin(); + individual_count[filt_index]++; + } } + + lua_createtable(L, 0, filter.size()); + for (u32 i = 0; i < filter.size(); i++) { + lua_pushinteger(L, individual_count[i]); + lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); + } + return 2; } - lua_newtable(L); - for (u32 i = 0; i < filter.size(); i++) { - lua_pushnumber(L, individual_count[i]); - lua_setfield(L, -2, ndef->get(filter[i]).name.c_str()); - } - return 2; } // find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions @@ -885,6 +926,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) sortBoxVerticies(minp, maxp); const NodeDefManager *ndef = env->getGameDef()->ndef(); + Map &map = env->getMap(); #ifndef SERVER if (Client *client = getClient(L)) { @@ -902,33 +944,21 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) } std::vector filter; - - if (lua_istable(L, 3)) { - lua_pushnil(L); - while (lua_next(L, 3) != 0) { - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - ndef->getIds(readParam(L, -1), filter); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if (lua_isstring(L, 3)) { - ndef->getIds(readParam(L, 3), filter); - } + collectNodeIds(L, 3, ndef, filter); lua_newtable(L); - u64 i = 0; - for (s16 x = minp.X; x <= maxp.X; x++) - for (s16 z = minp.Z; z <= maxp.Z; z++) { - s16 y = minp.Y; - v3s16 p(x, y, z); - content_t c = env->getMap().getNode(p).getContent(); - for (; y <= maxp.Y; y++) { - v3s16 psurf(x, y + 1, z); - content_t csurf = env->getMap().getNode(psurf).getContent(); + u32 i = 0; + v3s16 p; + for (p.X = minp.X; p.X <= maxp.X; p.X++) + for (p.Z = minp.Z; p.Z <= maxp.Z; p.Z++) { + p.Y = minp.Y; + content_t c = map.getNode(p).getContent(); + for (; p.Y <= maxp.Y; p.Y++) { + v3s16 psurf(p.X, p.Y + 1, p.Z); + content_t csurf = map.getNode(psurf).getContent(); if (c != CONTENT_AIR && csurf == CONTENT_AIR && CONTAINS(filter, c)) { - push_v3s16(L, v3s16(x, y, z)); + push_v3s16(L, p); lua_rawseti(L, -2, ++i); } c = csurf; diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 9050b4306..07d4d2438 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -190,6 +190,11 @@ private: // Get a string translated server side static int l_get_translated_string(lua_State * L); + /* Helpers */ + + static void collectNodeIds(lua_State *L, int idx, + const NodeDefManager *ndef, std::vector &filter); + public: static void Initialize(lua_State *L, int top); static void InitializeClient(lua_State *L, int top); -- cgit v1.2.3 From 82731d0d3d8bfe9e56f89466991f13c037f3a61e Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Thu, 16 Jul 2020 19:11:24 +0200 Subject: Revert "Auto delete MetaData when = 0 (#8770)" (#10183) This reverts commit 808eb4c5714da5ac36f4a70653d6b3805060828c. --- src/script/lua_api/l_metadata.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/script/lua_api') diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index 61a25a761..21002e6a7 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -153,9 +153,7 @@ int MetaDataRef::l_set_int(lua_State *L) MetaDataRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); int a = luaL_checkint(L, 3); - std::string str; - if (a != 0) - str = itos(a); + std::string str = itos(a); Metadata *meta = ref->getmeta(true); if (meta == NULL || str == meta->getString(name)) @@ -193,9 +191,7 @@ int MetaDataRef::l_set_float(lua_State *L) MetaDataRef *ref = checkobject(L, 1); std::string name = luaL_checkstring(L, 2); float a = readParam(L, 3); - std::string str; - if (a != 0) - str = ftos(a); + std::string str = ftos(a); Metadata *meta = ref->getmeta(true); if (meta == NULL || str == meta->getString(name)) -- cgit v1.2.3