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 --- src/script/common/c_content.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/common/c_content.cpp') 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" -- 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/common/c_content.cpp') 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 af2e6a6a10121cf971d4ce4c33d523b6dc037d31 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Sat, 11 Apr 2020 23:09:46 +0200 Subject: Improve waypoints and add image variant (#9480) --- doc/client_lua_api.txt | 25 ++++++++- doc/lua_api.txt | 20 ++++++++ src/client/hud.cpp | 111 ++++++++++++++++++++++++---------------- src/client/hud.h | 1 + src/hud.cpp | 1 + src/hud.h | 1 + src/script/common/c_content.cpp | 6 ++- 7 files changed, 118 insertions(+), 47 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 71df91c68..53ed680d0 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -1416,12 +1416,35 @@ Displays a horizontal bar made up of half-images. * `offset`: offset in pixels from position. ### `waypoint` + Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. -* `number:` An integer containing the RGB value of the color used to draw the text. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` +* `number:` An integer containing the RGB value of the color used to draw the + text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. + +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. + +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. ### Particle definition (`add_particle`) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index b083b2907..1a9aad344 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1364,10 +1364,30 @@ Displays distance to selected world position. * `name`: The name of the waypoint. * `text`: Distance suffix. Can be blank. +* `precision`: Waypoint precision, integer >= 0. Defaults to 10. + If set to 0, distance is not shown. Shown value is `floor(distance*precision)/precision`. + When the precision is an integer multiple of 10, there will be `log_10(precision)` digits after the decimal point. + `precision = 1000`, for example, will show 3 decimal places (eg: `0.999`). + `precision = 2` will show multiples of `0.5`; precision = 5 will show multiples of `0.2` and so on: + `precision = n` will show multiples of `1/n` * `number:` An integer containing the RGB value of the color used to draw the text. * `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the waypoint. + +### `image_waypoint` + +Same as `image`, but does not accept a `position`; the position is instead determined by `world_pos`, the world position of the waypoint. +* `scale`: The scale of the image, with 1 being the original texture size. + Only the X coordinate scale is used (positive values). + Negative values represent that percentage of the screen it + should take; e.g. `x=-100` means 100% (width). +* `text`: The name of the texture that is displayed. +* `alignment`: The alignment of the image. +* `world_pos`: World position of the waypoint. +* `offset`: offset in pixels from position. diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 37de6640b..56763e7e4 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -272,6 +272,25 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } } +// Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false. +bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos) +{ + v3f w_pos = e->world_pos * BS; + scene::ICameraSceneNode* camera = + RenderingEngine::get_scene_manager()->getActiveCamera(); + w_pos -= intToFloat(camera_offset, BS); + core::matrix4 trans = camera->getProjectionMatrix(); + trans *= camera->getViewMatrix(); + f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f }; + trans.multiplyWith1x4Matrix(transformed_pos); + if (transformed_pos[3] < 0) + return false; + f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : + core::reciprocal(transformed_pos[3]); + pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5); + pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5); + return true; +} void Hud::drawLuaElements(const v3s16 &camera_offset) { @@ -299,28 +318,6 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5), floor(e->pos.Y * (float) m_screensize.Y + 0.5)); switch (e->type) { - case HUD_ELEM_IMAGE: { - video::ITexture *texture = tsrc->getTexture(e->text); - if (!texture) - continue; - - const video::SColor color(255, 255, 255, 255); - const video::SColor colors[] = {color, color, color, color}; - core::dimension2di imgsize(texture->getOriginalSize()); - v2s32 dstsize(imgsize.Width * e->scale.X, - imgsize.Height * e->scale.Y); - if (e->scale.X < 0) - dstsize.X = m_screensize.X * (e->scale.X * -0.01); - if (e->scale.Y < 0) - dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01); - v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, - (e->align.Y - 1.0) * dstsize.Y / 2); - core::rect rect(0, 0, dstsize.X, dstsize.Y); - rect += pos + offset + v2s32(e->offset.X, e->offset.Y); - draw2DImageFilterScaled(driver, texture, rect, - core::rect(core::position2d(0,0), imgsize), - NULL, colors, true); - break; } case HUD_ELEM_TEXT: { video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, @@ -343,34 +340,58 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) inv, e->item, e->dir); break; } case HUD_ELEM_WAYPOINT: { - v3f p_pos = player->getPosition() / BS; - v3f w_pos = e->world_pos * BS; - float distance = std::floor(10 * p_pos.getDistanceFrom(e->world_pos)) / - 10.0f; - scene::ICameraSceneNode* camera = - RenderingEngine::get_scene_manager()->getActiveCamera(); - w_pos -= intToFloat(camera_offset, BS); - core::matrix4 trans = camera->getProjectionMatrix(); - trans *= camera->getViewMatrix(); - f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f }; - trans.multiplyWith1x4Matrix(transformed_pos); - if (transformed_pos[3] < 0) + if (!calculateScreenPos(camera_offset, e, &pos)) break; - f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : - core::reciprocal(transformed_pos[3]); - pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5); - pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5); + v3f p_pos = player->getPosition() / BS; + pos += v2s32(e->offset.X, e->offset.Y); video::SColor color(255, (e->number >> 16) & 0xFF, (e->number >> 8) & 0xFF, (e->number >> 0) & 0xFF); - core::rect size(0, 0, 200, 2 * text_height); std::wstring text = unescape_translate(utf8_to_wide(e->name)); - font->draw(text.c_str(), size + pos, color); - std::ostringstream os; - os << distance << e->text; - text = unescape_translate(utf8_to_wide(os.str())); - pos.Y += text_height; - font->draw(text.c_str(), size + pos, color); + const std::string &unit = e->text; + // waypoints reuse the item field to store precision, item = precision + 1 + u32 item = e->item; + float precision = (item == 0) ? 10.0f : (item - 1.f); + bool draw_precision = precision > 0; + + core::rect bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height); + pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2; + bounds += pos; + font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color); + if (draw_precision) { + std::ostringstream os; + float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision; + os << distance << unit; + text = unescape_translate(utf8_to_wide(os.str())); + bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width; + font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color); + } + break; } + case HUD_ELEM_IMAGE_WAYPOINT: { + if (!calculateScreenPos(camera_offset, e, &pos)) + break; + } + case HUD_ELEM_IMAGE: { + video::ITexture *texture = tsrc->getTexture(e->text); + if (!texture) + continue; + + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + core::dimension2di imgsize(texture->getOriginalSize()); + v2s32 dstsize(imgsize.Width * e->scale.X, + imgsize.Height * e->scale.Y); + if (e->scale.X < 0) + dstsize.X = m_screensize.X * (e->scale.X * -0.01); + if (e->scale.Y < 0) + dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01); + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2); + core::rect rect(0, 0, dstsize.X, dstsize.Y); + rect += pos + offset + v2s32(e->offset.X, e->offset.Y); + draw2DImageFilterScaled(driver, texture, rect, + core::rect(core::position2d(0,0), imgsize), + NULL, colors, true); break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << diff --git a/src/client/hud.h b/src/client/hud.h index d9b5e0686..cab115990 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -81,6 +81,7 @@ public: void drawLuaElements(const v3s16 &camera_offset); private: + bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, s32 count, v2s32 offset, v2s32 size = v2s32()); diff --git a/src/hud.cpp b/src/hud.cpp index 7711e3a4a..39625b5fd 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -27,6 +27,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_STATBAR, "statbar"}, {HUD_ELEM_INVENTORY, "inventory"}, {HUD_ELEM_WAYPOINT, "waypoint"}, + {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index 23f189dff..b0977c6a4 100644 --- a/src/hud.h +++ b/src/hud.h @@ -61,6 +61,7 @@ enum HudElementType { HUD_ELEM_STATBAR = 2, HUD_ELEM_INVENTORY = 3, HUD_ELEM_WAYPOINT = 4, + HUD_ELEM_IMAGE_WAYPOINT = 5 }; enum HudElementStat { diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index c8cd7539f..ff9ceec6d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1850,7 +1850,11 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->name = getstringfield_default(L, 2, "name", ""); elem->text = getstringfield_default(L, 2, "text", ""); elem->number = getintfield_default(L, 2, "number", 0); - elem->item = getintfield_default(L, 2, "item", 0); + if (elem->type == HUD_ELEM_WAYPOINT) + // waypoints reuse the item field to store precision, item = precision + 1 + elem->item = getintfield_default(L, 2, "precision", -1) + 1; + else + elem->item = getintfield_default(L, 2, "item", 0); elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); -- 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/common/c_content.cpp') 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 8ae8c1600a66c724565ce7a70e0e5f542f12e38e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 15 Apr 2020 08:01:28 +0200 Subject: Fix parsing JSON with large integers (#9674) --- src/script/common/c_content.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index d73ecedab..25dada757 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1671,10 +1671,10 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, lua_pushvalue(L, nullindex); break; case Json::intValue: - lua_pushinteger(L, value.asInt()); + lua_pushinteger(L, value.asLargestInt()); break; case Json::uintValue: - lua_pushinteger(L, value.asUInt()); + lua_pushinteger(L, value.asLargestUInt()); break; case Json::realValue: lua_pushnumber(L, value.asDouble()); @@ -1700,7 +1700,7 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, lua_createtable(L, 0, value.size()); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { -#ifndef JSONCPP_STRING +#if !defined(JSONCPP_STRING) && (JSONCPP_VERSION_MAJOR < 1 || JSONCPP_VERSION_MINOR < 9) const char *str = it.memberName(); lua_pushstring(L, str ? str : ""); #else -- 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/common/c_content.cpp') 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 3475759d1adbd4a64c6250fd87981f783e64f69c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 Apr 2020 14:11:33 +0200 Subject: Expose collision information to LuaEntity on_step --- builtin/game/features.lua | 1 + doc/lua_api.txt | 25 ++++++++++++++++++- src/script/common/c_content.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ src/script/common/c_content.h | 4 ++- src/script/cpp_api/s_entity.cpp | 12 ++++++--- src/script/cpp_api/s_entity.h | 4 ++- src/server/luaentity_sao.cpp | 8 +++--- 7 files changed, 98 insertions(+), 10 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 623f8183b..a15475333 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -16,6 +16,7 @@ core.features = { formspec_version_element = true, area_store_persistent_ids = true, pathfinder_works = true, + object_step_has_moveresult = true, } function core.has_feature(arg) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 3ca32649a..5e4e18b62 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4147,6 +4147,8 @@ Utilities area_store_persistent_ids = true, -- Whether minetest.find_path is functional (5.2.0) pathfinder_works = true, + -- Whether Collision info is available to an objects' on_step (5.3.0) + object_step_has_moveresult = true, } * `minetest.has_feature(arg)`: returns `boolean, missing_features` @@ -6579,7 +6581,10 @@ Used by `minetest.register_entity`. on_activate = function(self, staticdata, dtime_s), - on_step = function(self, dtime), + on_step = function(self, dtime, moveresult), + -- Called every server step + -- dtime: Elapsed time + -- moveresult: Table with collision info (only available if physical=true) on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir), @@ -6594,6 +6599,24 @@ Used by `minetest.register_entity`. -- for more info) by using a '_' prefix } +Collision info passed to `on_step`: + + { + touching_ground = boolean, + collides = boolean, + standing_on_object = boolean, + collisions = { + { + type = string, -- "node" or "object", + axis = string, -- "x", "y" or "z" + node_pos = vector, -- if type is "node" + old_speed = vector, + new_speed = vector, + }, + ... + } + } + ABM (ActiveBlockModifier) definition ------------------------------------ diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8335fccb5..6ff642738 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -21,6 +21,7 @@ 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 "collision.h" #include "cpp_api/s_node.h" #include "lua_api/l_object.h" #include "lua_api/l_item.h" @@ -2002,3 +2003,56 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) } return stat; } + +/******************************************************************************/ + +// Indices must match values in `enum CollisionType` exactly!! +static const char *collision_type_str[] = { + "node", + "object", +}; + +// Indices must match values in `enum CollisionAxis` exactly!! +static const char *collision_axis_str[] = { + "x", + "y", + "z", +}; + +void push_collision_move_result(lua_State *L, const collisionMoveResult &res) +{ + lua_createtable(L, 0, 4); + + setboolfield(L, -1, "touching_ground", res.touching_ground); + setboolfield(L, -1, "collides", res.collides); + setboolfield(L, -1, "standing_on_object", res.standing_on_object); + + /* collisions */ + lua_createtable(L, res.collisions.size(), 0); + int i = 1; + for (const auto &c : res.collisions) { + lua_createtable(L, 0, 5); + + lua_pushstring(L, collision_type_str[c.type]); + lua_setfield(L, -2, "type"); + + assert(c.axis != COLLISION_AXIS_NONE); + lua_pushstring(L, collision_axis_str[c.axis]); + lua_setfield(L, -2, "axis"); + + if (c.type == COLLISION_NODE) { + push_v3s16(L, c.node_p); + lua_setfield(L, -2, "node_pos"); + } + + push_v3f(L, c.old_speed / BS); + lua_setfield(L, -2, "old_speed"); + + push_v3f(L, c.new_speed / BS); + lua_setfield(L, -2, "new_speed"); + + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "collisions"); + /**/ +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 9e755682f..8f32e58eb 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -63,7 +63,9 @@ struct EnumString; struct NoiseParams; class Schematic; class ServerActiveObject; +struct collisionMoveResult; +extern struct EnumString es_TileAnimationType[]; ContentFeatures read_content_features (lua_State *L, int index); void push_content_features (lua_State *L, @@ -196,4 +198,4 @@ void push_hud_element (lua_State *L, HudElement *elem); HudElementStat read_hud_change (lua_State *L, HudElement *elem, void **value); -extern struct EnumString es_TileAnimationType[]; +void push_collision_move_result(lua_State *L, const collisionMoveResult &res); diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 26c7e8cd4..ea9320051 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -178,12 +178,11 @@ void ScriptApiEntity::luaentity_GetProperties(u16 id, lua_pop(L, 1); } -void ScriptApiEntity::luaentity_Step(u16 id, float dtime) +void ScriptApiEntity::luaentity_Step(u16 id, float dtime, + const collisionMoveResult *moveresult) { SCRIPTAPI_PRECHECKHEADER - //infostream<<"scriptapi_luaentity_step: id="<getScriptIface()->luaentity_Step(m_id, dtime); + if(m_registered) { + m_env->getScriptIface()->luaentity_Step(m_id, dtime, moveresult_p); } if (!send_recommended) -- cgit v1.2.3 From 1b6f40c35632ab5a2066822c8b5d4c09d469f49d Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 4 May 2020 20:02:33 +0200 Subject: Rename moveresult speed to velocity --- doc/lua_api.txt | 4 ++-- src/script/common/c_content.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 1ffb5c39b..b1099ec59 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6632,8 +6632,8 @@ Collision info passed to `on_step`: type = string, -- "node" or "object", axis = string, -- "x", "y" or "z" node_pos = vector, -- if type is "node" - old_speed = vector, - new_speed = vector, + old_velocity = vector, + new_velocity = vector, }, ... } diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6ff642738..95364000c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2046,10 +2046,10 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) } push_v3f(L, c.old_speed / BS); - lua_setfield(L, -2, "old_speed"); + lua_setfield(L, -2, "old_velocity"); push_v3f(L, c.new_speed / BS); - lua_setfield(L, -2, "new_speed"); + lua_setfield(L, -2, "new_velocity"); lua_rawseti(L, -2, i++); } -- cgit v1.2.3 From b6b80f55c8a2bf4eae440108b3274f2f921e3a94 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 26 Apr 2020 22:52:00 +0200 Subject: Expose collided objects in moveresult closes #9787 --- doc/lua_api.txt | 1 + src/collision.cpp | 40 ++++++++++++++++++++++++++-------------- src/collision.h | 1 + src/script/common/c_content.cpp | 3 +++ 4 files changed, 31 insertions(+), 14 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index b1099ec59..948e0f89e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6632,6 +6632,7 @@ Collision info passed to `on_step`: type = string, -- "node" or "object", axis = string, -- "x", "y" or "z" node_pos = vector, -- if type is "node" + object = ObjectRef, -- if type is "object" old_velocity = vector, new_velocity = vector, }, diff --git a/src/collision.cpp b/src/collision.cpp index a089f3377..3b5e79a66 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -37,18 +37,30 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif struct NearbyCollisionInfo { - NearbyCollisionInfo(bool is_ul, bool is_obj, int bouncy, - const v3s16 &pos, const aabb3f &box) : + // node + NearbyCollisionInfo(bool is_ul, int bouncy, const v3s16 &pos, + const aabb3f &box) : is_unloaded(is_ul), - is_object(is_obj), + obj(nullptr), bouncy(bouncy), position(pos), box(box) {} + // object + NearbyCollisionInfo(ActiveObject *obj, int bouncy, + const aabb3f &box) : + is_unloaded(false), + obj(obj), + bouncy(bouncy), + box(box) + {} + + inline bool isObject() const { return obj != nullptr; } + bool is_unloaded; bool is_step_up = false; - bool is_object; + ActiveObject *obj; int bouncy; v3s16 position; aabb3f box; @@ -312,13 +324,13 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, for (auto box : nodeboxes) { box.MinEdge += posf; box.MaxEdge += posf; - cinfo.emplace_back(false, false, n_bouncy_value, p, box); + cinfo.emplace_back(false, n_bouncy_value, p, box); } } else { // Collide with unloaded nodes (position invalid) and loaded // CONTENT_IGNORE nodes (position valid) aabb3f box = getNodeBox(p, BS); - cinfo.emplace_back(true, false, 0, p, box); + cinfo.emplace_back(true, 0, p, box); } } @@ -383,12 +395,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, iter != objects.end(); ++iter) { ActiveObject *object = *iter; - if (object) { + if (object && object->collideWithObjects()) { aabb3f object_collisionbox; - if (object->getCollisionBox(&object_collisionbox) && - object->collideWithObjects()) { - cinfo.emplace_back(false, true, 0, v3s16(), object_collisionbox); - } + if (object->getCollisionBox(&object_collisionbox)) + cinfo.emplace_back(object, 0, object_collisionbox); } } #ifndef SERVER @@ -399,7 +409,8 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, v3f lplayer_pos = lplayer->getPosition(); lplayer_collisionbox.MinEdge += lplayer_pos; lplayer_collisionbox.MaxEdge += lplayer_pos; - cinfo.emplace_back(false, true, 0, v3s16(), lplayer_collisionbox); + ActiveObject *obj = (ActiveObject*) lplayer->getCAO(); + cinfo.emplace_back(obj, 0, lplayer_collisionbox); } } #endif @@ -498,12 +509,13 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, is_collision = false; CollisionInfo info; - if (nearest_info.is_object) + if (nearest_info.isObject()) info.type = COLLISION_OBJECT; else info.type = COLLISION_NODE; info.node_p = nearest_info.position; + info.object = nearest_info.obj; info.old_speed = *speed_f; info.plane = nearest_collided; @@ -572,7 +584,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, if (std::fabs(cbox.MaxEdge.Y - box.MinEdge.Y) < 0.05f) { result.touching_ground = true; - if (box_info.is_object) + if (box_info.isObject()) result.standing_on_object = true; } } diff --git a/src/collision.h b/src/collision.h index fa47cccc1..87a502828 100644 --- a/src/collision.h +++ b/src/collision.h @@ -48,6 +48,7 @@ struct CollisionInfo CollisionType type = COLLISION_NODE; CollisionAxis axis = COLLISION_AXIS_NONE; v3s16 node_p = v3s16(-32768,-32768,-32768); // COLLISION_NODE + ActiveObject *object = nullptr; // COLLISION_OBJECT v3f old_speed; v3f new_speed; int plane = -1; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 95364000c..dac828316 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2043,6 +2043,9 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) if (c.type == COLLISION_NODE) { push_v3s16(L, c.node_p); lua_setfield(L, -2, "node_pos"); + } else if (c.type == COLLISION_OBJECT) { + push_objectRef(L, c.object->getId()); + lua_setfield(L, -2, "object"); } push_v3f(L, c.old_speed / BS); -- cgit v1.2.3 From 6e1372bd894d955300c40d69e5c882e9cc7d7523 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 11 May 2020 21:40:45 +0200 Subject: Add support for statbar “off state” icons (#9462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for optional “off state” icons for statbars. “off state icons” can be used to denote the lack of something, like missing hearts or bubbles. Add "off state" textures to the builtin statbars. Co-authored-by: SmallJoker --- builtin/game/statbars.lua | 4 ++ doc/lua_api.txt | 16 ++++-- doc/texture_packs.txt | 4 ++ src/client/clientevent.h | 1 + src/client/game.cpp | 7 +++ src/client/hud.cpp | 96 ++++++++++++++++++++++++++++++------ src/client/hud.h | 5 +- src/hud.cpp | 1 + src/hud.h | 2 + src/network/clientpackethandler.cpp | 15 ++---- src/network/networkprotocol.h | 6 ++- src/script/common/c_content.cpp | 8 +++ src/server.cpp | 3 +- textures/base/pack/bubble_gone.png | Bin 0 -> 68 bytes textures/base/pack/heart_gone.png | Bin 0 -> 68 bytes 15 files changed, 132 insertions(+), 36 deletions(-) create mode 100644 textures/base/pack/bubble_gone.png create mode 100644 textures/base/pack/heart_gone.png (limited to 'src/script/common/c_content.cpp') diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 6b5b54428..d192029c5 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -5,7 +5,9 @@ local health_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "heart.png", + text2 = "heart_gone.png", number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, direction = 0, size = {x = 24, y = 24}, offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, @@ -15,7 +17,9 @@ local breath_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "bubble.png", + text2 = "bubble_gone.png", number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, direction = 0, size = {x = 24, y = 24}, offset = {x = 25, y= -(48 + 24 + 16)}, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 961e1ff37..4078e21a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1289,9 +1289,9 @@ To account for differing resolutions, the position coordinates are the percentage of the screen, ranging in value from `0` to `1`. The name field is not yet used, but should contain a description of what the -HUD element represents. The direction field is the direction in which something -is drawn. +HUD element represents. +The `direction` field is the direction in which something is drawn. `0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom, and `3` draws from bottom to top. @@ -1355,12 +1355,16 @@ Displays text on the HUD. ### `statbar` -Displays a horizontal bar made up of half-images. +Displays a horizontal bar made up of half-images with an optional background. -* `text`: The name of the texture that is used. +* `text`: The name of the texture to use. +* `text2`: Optional texture name to enable a background / "off state" + texture (useful to visualize the maximal value). Both textures + must have the same size. * `number`: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. -* `direction` +* `item`: Same as `number` but for the "off state" texture +* `direction`: To which direction the images will extend to * `offset`: offset in pixels from position. * `size`: If used, will force full-image size to this value (override texture pack image size) @@ -7772,6 +7776,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. text = "", + text2 = "", + number = 2, item = 3, diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 4e7bc93c4..94151f1a4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -64,6 +64,8 @@ by texture packs. All existing fallback textures can be found in the directory * `bubble.png`: the bubble texture when the player is drowning (default size: 12×12) +* `bubble_gone.png`: like `bubble.png`, but denotes lack of breath + (transparent by default, same size as bubble.png) * `crack_anylength.png`: node overlay texture when digging @@ -76,6 +78,8 @@ by texture packs. All existing fallback textures can be found in the directory * `heart.png`: used to display the health points of the player (default size: 12×12) +* `heart_gone.png`: like `heart.png`, but denotes lack of health points + (transparent by default, same size as heart.png) * `minimap_mask_round.png`: round minimap mask, white gets replaced by the map * `minimap_mask_square.png`: mask used for the square minimap diff --git a/src/client/clientevent.h b/src/client/clientevent.h index f5689c25b..7f3984b03 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -136,6 +136,7 @@ struct ClientEvent v3f *world_pos; v2s32 *size; s16 z_index; + std::string *text2; } hudadd; struct { diff --git a/src/client/game.cpp b/src/client/game.cpp index 4d7a85526..422e17d4f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2672,6 +2672,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; return; } @@ -2689,6 +2690,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->world_pos = *event->hudadd.world_pos; e->size = *event->hudadd.size; e->z_index = event->hudadd.z_index; + e->text2 = *event->hudadd.text2; hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; @@ -2699,6 +2701,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; } void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) @@ -2771,6 +2774,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca case HUD_STAT_Z_INDEX: e->z_index = event->hudchange.data; break; + + case HUD_STAT_TEXT2: + e->text2 = *event->hudchange.sdata; + break; } delete event->hudchange.v3fdata; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 56763e7e4..f8f712762 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -332,7 +332,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); - drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size); + drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2, + e->number, e->item, offs, e->size); break; } case HUD_ELEM_INVENTORY: { InventoryList *inv = inventory->getList(e->text); @@ -401,8 +402,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } -void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size) +void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string &bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size) { const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; @@ -411,6 +413,11 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex if (!stat_texture) return; + video::ITexture *stat_texture_bg = nullptr; + if (!bgtexture.empty()) { + stat_texture_bg = tsrc->getTexture(bgtexture); + } + core::dimension2di srcd(stat_texture->getOriginalSize()); core::dimension2di dstd; if (size == v2s32()) { @@ -430,43 +437,100 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex p += offset; v2s32 steppos; - core::rect srchalfrect, dsthalfrect; switch (drawdir) { case HUD_DIR_RIGHT_LEFT: steppos = v2s32(-1, 0); - srchalfrect = core::rect(srcd.Width / 2, 0, srcd.Width, srcd.Height); - dsthalfrect = core::rect(dstd.Width / 2, 0, dstd.Width, dstd.Height); break; case HUD_DIR_TOP_BOTTOM: steppos = v2s32(0, 1); - srchalfrect = core::rect(0, 0, srcd.Width, srcd.Height / 2); - dsthalfrect = core::rect(0, 0, dstd.Width, dstd.Height / 2); break; case HUD_DIR_BOTTOM_TOP: steppos = v2s32(0, -1); - srchalfrect = core::rect(0, srcd.Height / 2, srcd.Width, srcd.Height); - dsthalfrect = core::rect(0, dstd.Height / 2, dstd.Width, dstd.Height); break; default: + // From left to right steppos = v2s32(1, 0); - srchalfrect = core::rect(0, 0, srcd.Width / 2, srcd.Height); - dsthalfrect = core::rect(0, 0, dstd.Width / 2, dstd.Height); + break; + } + + auto calculate_clipping_rect = [] (core::dimension2di src, + v2s32 steppos) -> core::rect { + + // Create basic rectangle + core::rect rect(0, 0, + src.Width - std::abs(steppos.X) * src.Width / 2, + src.Height - std::abs(steppos.Y) * src.Height / 2 + ); + // Move rectangle left or down + if (steppos.X == -1) + rect += v2s32(src.Width / 2, 0); + if (steppos.Y == -1) + rect += v2s32(0, src.Height / 2); + return rect; + }; + // Rectangles for 1/2 the actual value to display + core::rect srchalfrect, dsthalfrect; + // Rectangles for 1/2 the "off state" texture + core::rect srchalfrect2, dsthalfrect2; + + if (count % 2 == 1) { + // Need to draw halves: Calculate rectangles + srchalfrect = calculate_clipping_rect(srcd, steppos); + dsthalfrect = calculate_clipping_rect(dstd, steppos); + srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1); + dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1); } + steppos.X *= dstd.Width; steppos.Y *= dstd.Height; + // Draw full textures for (s32 i = 0; i < count / 2; i++) { core::rect srcrect(0, 0, srcd.Width, srcd.Height); - core::rect dstrect(0,0, dstd.Width, dstd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); dstrect += p; - draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true); + draw2DImageFilterScaled(driver, stat_texture, + dstrect, srcrect, NULL, colors, true); p += steppos; } if (count % 2 == 1) { - dsthalfrect += p; - draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true); + // Draw half a texture + draw2DImageFilterScaled(driver, stat_texture, + dsthalfrect + p, srchalfrect, NULL, colors, true); + + if (stat_texture_bg && maxcount > count) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect2 + p, srchalfrect2, + NULL, colors, true); + p += steppos; + } + } + + if (stat_texture_bg && maxcount > count / 2) { + // Draw "off state" textures + s32 start_offset; + if (count % 2 == 1) + start_offset = count / 2 + 1; + else + start_offset = count / 2; + for (s32 i = start_offset; i < maxcount / 2; i++) { + core::rect srcrect(0, 0, srcd.Width, srcd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); + + dstrect += p; + draw2DImageFilterScaled(driver, stat_texture_bg, + dstrect, srcrect, + NULL, colors, true); + p += steppos; + } + + if (maxcount % 2 == 1) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect + p, srchalfrect, + NULL, colors, true); + } } } diff --git a/src/client/hud.h b/src/client/hud.h index cab115990..6274b1a83 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -82,8 +82,9 @@ public: private: bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); - void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size = v2s32()); + void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string& bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, s32 inv_offset, InventoryList *mainlist, u16 selectitem, diff --git a/src/hud.cpp b/src/hud.cpp index 39625b5fd..3079b5cd8 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -46,6 +46,7 @@ const struct EnumString es_HudElementStat[] = {HUD_STAT_WORLD_POS, "world_pos"}, {HUD_STAT_SIZE, "size"}, {HUD_STAT_Z_INDEX, "z_index"}, + {HUD_STAT_TEXT2, "text2"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index b0977c6a4..bab420ed2 100644 --- a/src/hud.h +++ b/src/hud.h @@ -77,6 +77,7 @@ enum HudElementStat { HUD_STAT_WORLD_POS, HUD_STAT_SIZE, HUD_STAT_Z_INDEX, + HUD_STAT_TEXT2, }; struct HudElement { @@ -93,6 +94,7 @@ struct HudElement { v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; }; extern const EnumString es_HudElementType[]; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8d0225a3d..7b1b1368c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1102,22 +1102,16 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item >> dir >> align >> offset; try { *pkt >> world_pos; - } - catch(SerializationError &e) {}; - - try { *pkt >> size; - } catch(SerializationError &e) {}; - - try { *pkt >> z_index; - } - catch(PacketError &e) {} + *pkt >> text2; + } catch(PacketError &e) {}; ClientEvent *event = new ClientEvent(); event->type = CE_HUDADD; @@ -1135,6 +1129,7 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) event->hudadd.world_pos = new v3f(world_pos); event->hudadd.size = new v2s32(size); event->hudadd.z_index = z_index; + event->hudadd.text2 = new std::string(text2); m_client_event_queue.push(event); } @@ -1171,7 +1166,7 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE || stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET) *pkt >> v2fdata; - else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) + else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2) *pkt >> sdata; else if (stat == HUD_STAT_WORLD_POS) *pkt >> v3fdata; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 527ebba7c..ab924f1db 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -560,10 +560,10 @@ enum ToClientCommand u32 id u8 type v2f1000 pos - u32 len + u16 len u8[len] name v2f1000 scale - u32 len2 + u16 len2 u8[len2] text u32 number u32 item @@ -573,6 +573,8 @@ enum ToClientCommand v3f1000 world_pos v2s32 size s16 z_index + u16 len3 + u8[len3] text2 */ TOCLIENT_HUDRM = 0x4a, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index dac828316..540b7222f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1871,6 +1871,7 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); + elem->text2 = getstringfield_default(L, 2, "text2", ""); // Deprecated, only for compatibility's sake if (elem->dir == 0) @@ -1939,6 +1940,9 @@ void push_hud_element(lua_State *L, HudElement *elem) lua_pushnumber(L, elem->z_index); lua_setfield(L, -2, "z_index"); + + lua_pushstring(L, elem->text2.c_str()); + lua_setfield(L, -2, "text2"); } HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) @@ -2000,6 +2004,10 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, luaL_checknumber(L, 4))); *value = &elem->z_index; break; + case HUD_STAT_TEXT2: + elem->text2 = luaL_checkstring(L, 4); + *value = &elem->text2; + break; } return stat; } diff --git a/src/server.cpp b/src/server.cpp index 16e026ce2..b28c30e1e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1621,7 +1621,7 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir << form->align << form->offset << form->world_pos << form->size - << form->z_index; + << form->z_index << form->text2; Send(&pkt); } @@ -1647,6 +1647,7 @@ void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void break; case HUD_STAT_NAME: case HUD_STAT_TEXT: + case HUD_STAT_TEXT2: pkt << *(std::string *) value; break; case HUD_STAT_WORLD_POS: diff --git a/textures/base/pack/bubble_gone.png b/textures/base/pack/bubble_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/bubble_gone.png differ diff --git a/textures/base/pack/heart_gone.png b/textures/base/pack/heart_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/heart_gone.png differ -- cgit v1.2.3 From 9ba24f89f5846de6a8f0d7e01c89acfee1254704 Mon Sep 17 00:00:00 2001 From: Lars Müller <34514239+appgurueu@users.noreply.github.com> Date: Mon, 11 May 2020 21:41:36 +0200 Subject: Damage texture modifier (#9833) Adds a new object property "damage_texture_modifier" --- doc/lua_api.txt | 3 +++ src/client/content_cao.cpp | 14 +++++--------- src/object_properties.cpp | 5 +++++ src/object_properties.h | 1 + src/script/common/c_content.cpp | 4 ++++ 5 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4078e21a1..db13f4224 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6589,6 +6589,9 @@ Player properties need to be saved manually. -- deleted when the block gets unloaded. -- The get_staticdata() callback is never called then. -- Defaults to 'true'. + + damage_texture_modifier = "^[brighten", + -- Texture modifier to be applied for a short duration when object is hit } Entity definition diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index aadd33bb9..eb1dad22b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1532,7 +1532,7 @@ void GenericCAO::processMessage(const std::string &data) } else if (cmd == AO_CMD_SET_TEXTURE_MOD) { std::string mod = deSerializeString(is); - // immediatly reset a engine issued texture modifier if a mod sends a different one + // immediately reset a engine issued texture modifier if a mod sends a different one if (m_reset_textures_timer > 0) { m_reset_textures_timer = -1; updateTextures(m_previous_texture_modifier); @@ -1646,13 +1646,11 @@ void GenericCAO::processMessage(const std::string &data) m_smgr, m_env, m_position, v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); m_env->addSimpleObject(simple); - } else if (m_reset_textures_timer < 0) { - // TODO: Execute defined fast response - // Flashing shall suffice as there is no definition + } else if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) { m_reset_textures_timer = 0.05; if(damage >= 2) m_reset_textures_timer += 0.05 * damage; - updateTextures(m_current_texture_modifier + "^[brighten"); + updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier); } } @@ -1723,13 +1721,11 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, v2f(m_prop.visual_size.X, m_prop.visual_size.Y) * BS); m_env->addSimpleObject(simple); } - // TODO: Execute defined fast response - // Flashing shall suffice as there is no definition - if (m_reset_textures_timer < 0) { + if (m_reset_textures_timer < 0 && !m_prop.damage_texture_modifier.empty()) { m_reset_textures_timer = 0.05; if (result.damage >= 2) m_reset_textures_timer += 0.05 * result.damage; - updateTextures(m_current_texture_modifier + "^[brighten"); + updateTextures(m_current_texture_modifier + m_prop.damage_texture_modifier); } } diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 4cf180b18..6ff344dce 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -68,6 +68,7 @@ std::string ObjectProperties::dump() os << ", eye_height=" << eye_height; os << ", zoom_fov=" << zoom_fov; os << ", use_texture_alpha=" << use_texture_alpha; + os << ", damage_texture_modifier=" << damage_texture_modifier; return os.str(); } @@ -114,6 +115,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeF32(os, eye_height); writeF32(os, zoom_fov); writeU8(os, use_texture_alpha); + os << serializeString(damage_texture_modifier); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -166,4 +168,7 @@ void ObjectProperties::deSerialize(std::istream &is) eye_height = readF32(is); zoom_fov = readF32(is); use_texture_alpha = readU8(is); + try { + damage_texture_modifier = deSerializeString(is); + } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index 3895f3379..f7848f5a2 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -39,6 +39,7 @@ struct ObjectProperties std::string mesh = ""; v3f visual_size = v3f(1, 1, 1); std::vector textures; + std::string damage_texture_modifier = "^[brighten"; std::vector colors; v2s16 spritediv = v2s16(1, 1); v2s16 initial_sprite_basepos; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 540b7222f..de9634c42 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -327,6 +327,8 @@ void read_object_properties(lua_State *L, int index, getfloatfield(L, -1, "zoom_fov", prop->zoom_fov); getboolfield(L, -1, "use_texture_alpha", prop->use_texture_alpha); + + getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } /******************************************************************************/ @@ -409,6 +411,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "zoom_fov"); lua_pushboolean(L, prop->use_texture_alpha); lua_setfield(L, -2, "use_texture_alpha"); + lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size()); + lua_setfield(L, -2, "damage_texture_modifier"); } /******************************************************************************/ -- 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/common/c_content.cpp') 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 732c8008f495bc344957bd0ccbd4010adb939207 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 20 May 2020 20:13:16 +0200 Subject: CSM: Fix crashing minetest.get_item_def() fixes #9884 --- src/script/common/c_content.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script/common/c_content.cpp') diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 116a59c09..2157d087d 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -164,7 +164,7 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "usable"); lua_pushboolean(L, i.liquids_pointable); lua_setfield(L, -2, "liquids_pointable"); - if (i.type == ITEM_TOOL) { + if (i.tool_capabilities) { push_tool_capabilities(L, *i.tool_capabilities); lua_setfield(L, -2, "tool_capabilities"); } -- cgit v1.2.3 From 3a6dfda358112e2fb5d6f887a1a1e936eacddadd Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Tue, 16 Jun 2020 22:48:31 +0300 Subject: Make shading of CAOs optional (#10033) --- client/shaders/object_shader/opengl_vertex.glsl | 4 ++++ doc/lua_api.txt | 3 +++ games/devtest/mods/testentities/visuals.lua | 12 +++++++++++- src/client/content_cao.cpp | 15 +++++++++++---- src/client/mesh.cpp | 2 +- src/client/shader.cpp | 6 +++++- src/client/tile.h | 2 ++ src/object_properties.cpp | 6 ++++++ src/object_properties.h | 1 + src/script/common/c_content.cpp | 3 +++ 10 files changed, 47 insertions(+), 7 deletions(-) (limited to 'src/script/common/c_content.cpp') diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index 59171145f..968a07e22 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -39,11 +39,15 @@ void main(void) lightVec = sunPosition - worldPosition; eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz; +#if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA) + vIDiff = 1.0; +#else // This is intentional comparison with zero without any margin. // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector vIDiff = length(gl_Normal) == 0.0 ? 1.0 : directional_ambient(normalize(gl_Normal)); +#endif gl_FrontColor = gl_BackColor = gl_Color; } diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 07e9698e8..11c5cfb91 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6644,6 +6644,9 @@ Player properties need to be saved manually. damage_texture_modifier = "^[brighten", -- Texture modifier to be applied for a short duration when object is hit + + shaded = true, + -- Setting this to 'false' disables diffuse lighting of entity } Entity definition diff --git a/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index 314f51657..83f361f16 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -55,6 +55,17 @@ minetest.register_entity("testentities:mesh", { }, }) +minetest.register_entity("testentities:mesh_unshaded", { + initial_properties = { + visual = "mesh", + mesh = "testnodes_pyramid.obj", + textures = { + "testnodes_mesh_stripes2.png" + }, + shaded = false, + }, +}) + -- Advanced visual tests -- A test entity for testing animated and yaw-modulated sprites @@ -71,4 +82,3 @@ minetest.register_entity("testentities:yawsprite", { self.object:set_sprite({x=0, y=0}, 1, 0, true) end, }) - diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index eec4e3de0..5352c35f4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -577,10 +577,16 @@ void GenericCAO::addToScene(ITextureSource *tsrc) if (m_enable_shaders) { IShaderSource *shader_source = m_client->getShaderSource(); - u32 shader_id = shader_source->getShader( - "object_shader", - (m_prop.use_texture_alpha) ? TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC, - NDT_NORMAL); + MaterialType material_type; + + if (m_prop.shaded && m_prop.glow == 0) + material_type = (m_prop.use_texture_alpha) ? + TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC; + else + material_type = (m_prop.use_texture_alpha) ? + TILE_MATERIAL_PLAIN_ALPHA : TILE_MATERIAL_PLAIN; + + u32 shader_id = shader_source->getShader("object_shader", material_type, NDT_NORMAL); m_material_type = shader_source->getShaderInfo(shader_id).material; } else { m_material_type = (m_prop.use_texture_alpha) ? @@ -1504,6 +1510,7 @@ bool GenericCAO::visualExpiryRequired(const ObjectProperties &new_) const return old.backface_culling != new_.backface_culling || old.is_visible != new_.is_visible || old.mesh != new_.mesh || + old.shaded != new_.shaded || old.use_texture_alpha != new_.use_texture_alpha || old.visual != new_.visual || old.visual_size != new_.visual_size || diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 68832849e..e1ec22068 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -341,7 +341,7 @@ bool checkMeshNormals(scene::IMesh *mesh) // hurting the performance and covering only really weird broken models. f32 length = buffer->getNormal(0).getLength(); - if (!std::isfinite(length) || std::fabs(length) < 1e-10f) + if (!std::isfinite(length) || length < 1e-10f) return false; } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index eda415ce6..ee6079f7a 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -537,11 +537,13 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.base_material = video::EMT_SOLID; break; case TILE_MATERIAL_ALPHA: + case TILE_MATERIAL_PLAIN_ALPHA: case TILE_MATERIAL_LIQUID_TRANSPARENT: case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_BASIC: + case TILE_MATERIAL_PLAIN: case TILE_MATERIAL_WAVING_LEAVES: case TILE_MATERIAL_WAVING_PLANTS: case TILE_MATERIAL_WAVING_LIQUID_BASIC: @@ -644,9 +646,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp "TILE_MATERIAL_WAVING_LIQUID_BASIC", "TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT", "TILE_MATERIAL_WAVING_LIQUID_OPAQUE", + "TILE_MATERIAL_PLAIN", + "TILE_MATERIAL_PLAIN_ALPHA", }; - for (int i = 0; i < 10; i++){ + for (int i = 0; i < 12; i++){ shaders_header += "#define "; shaders_header += materialTypes[i]; shaders_header += " "; diff --git a/src/client/tile.h b/src/client/tile.h index 533df676e..52e0a2b2b 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -150,6 +150,8 @@ enum MaterialType{ TILE_MATERIAL_WAVING_LIQUID_BASIC, TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT, TILE_MATERIAL_WAVING_LIQUID_OPAQUE, + TILE_MATERIAL_PLAIN, + TILE_MATERIAL_PLAIN_ALPHA }; // Material flags diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 6ff344dce..8d51bcbfa 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -69,6 +69,7 @@ std::string ObjectProperties::dump() os << ", zoom_fov=" << zoom_fov; os << ", use_texture_alpha=" << use_texture_alpha; os << ", damage_texture_modifier=" << damage_texture_modifier; + os << ", shaded=" << shaded; return os.str(); } @@ -116,6 +117,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeF32(os, zoom_fov); writeU8(os, use_texture_alpha); os << serializeString(damage_texture_modifier); + writeU8(os, shaded); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -170,5 +172,9 @@ void ObjectProperties::deSerialize(std::istream &is) use_texture_alpha = readU8(is); try { damage_texture_modifier = deSerializeString(is); + u8 tmp = readU8(is); + if (is.eof()) + throw SerializationError(""); + shaded = tmp; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index f7848f5a2..f010c1daf 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -61,6 +61,7 @@ struct ObjectProperties float eye_height = 1.625f; float zoom_fov = 0.0f; bool use_texture_alpha = false; + bool shaded = true; ObjectProperties(); std::string dump(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 2157d087d..3dfd7ce61 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -327,6 +327,7 @@ void read_object_properties(lua_State *L, int index, getfloatfield(L, -1, "zoom_fov", prop->zoom_fov); getboolfield(L, -1, "use_texture_alpha", prop->use_texture_alpha); + getboolfield(L, -1, "shaded", prop->shaded); getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } @@ -411,6 +412,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "zoom_fov"); lua_pushboolean(L, prop->use_texture_alpha); lua_setfield(L, -2, "use_texture_alpha"); + lua_pushboolean(L, prop->shaded); + lua_setfield(L, -2, "shaded"); lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size()); lua_setfield(L, -2, "damage_texture_modifier"); } -- cgit v1.2.3