diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 840841e6b..9fd2ba3f5 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5465,6 +5465,14 @@ This is basically a reference to a C++ `ServerActiveObject` * `get_player_name()`: returns `""` if is not a player * `get_player_velocity()`: returns `nil` if is not a player, otherwise a table {x, y, z} representing the player's instantaneous velocity in nodes/s +* `add_player_velocity(vel)` + * Adds to player velocity, this happens client-side and only once. + * Does not apply during free_move. + * Note that since the player speed is normalized at each move step, + increasing e.g. Y velocity beyond what would usually be achieved + (see: physics overrides) will cause existing X/Z velocity to be reduced. + * Example: `add_player_velocity({x=0, y=6.5, z=0})` is equivalent to + pressing the jump key (assuming default settings) * `get_look_dir()`: get camera direction as a unit vector * `get_look_vertical()`: pitch in radians * Angle ranges between -pi/2 and pi/2, which are straight up and down diff --git a/src/client/client.h b/src/client/client.h index 15a4689c1..3bfb1631e 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -227,6 +227,7 @@ public: void handleCommand_SrpBytesSandB(NetworkPacket *pkt); void handleCommand_FormspecPrepend(NetworkPacket *pkt); void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); + void handleCommand_PlayerSpeed(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 0e273a16a..c356f7c24 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -188,6 +188,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, // Copy parent position if local player is attached if (isAttached) { setPosition(overridePosition); + added_velocity = v3f(); // ignored return; } @@ -201,9 +202,13 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if (noclip && free_move) { position += m_speed * dtime; setPosition(position); + added_velocity = v3f(); // ignored return; } + m_speed += added_velocity; + added_velocity = v3f(); + /* Collision detection */ @@ -782,6 +787,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, if (isAttached) { setPosition(overridePosition); m_sneak_node_exists = false; + added_velocity = v3f(); return; } @@ -795,9 +801,13 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, position += m_speed * dtime; setPosition(position); m_sneak_node_exists = false; + added_velocity = v3f(); return; } + m_speed += added_velocity; + added_velocity = v3f(); + /* Collision detection */ diff --git a/src/client/localplayer.h b/src/client/localplayer.h index 84cfa583a..16e7996ae 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -149,6 +149,11 @@ public: bool getAutojump() const { return m_autojump; } + inline void addVelocity(const v3f &vel) + { + added_velocity += vel; + } + private: void accelerate(const v3f &target_speed, const f32 max_increase_H, const f32 max_increase_V, const bool use_pitch); @@ -194,6 +199,7 @@ private: float m_zoom_fov = 0.0f; bool m_autojump = false; float m_autojump_time = 0.0f; + v3f added_velocity = v3f(0.0f, 0.0f, 0.0f); // cleared on each move() GenericCAO *m_cao = nullptr; Client *m_client; diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 0f1e2f435..de300481f 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1091,6 +1091,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) 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. @@ -1412,6 +1413,19 @@ std::string PlayerSAO::getPropertyPacket() return gob_cmd_set_properties(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 || @@ -1431,6 +1445,14 @@ bool PlayerSAO::checkMovementCheat() 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 @@ -1439,10 +1461,13 @@ bool PlayerSAO::checkMovementCheat() 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) diff --git a/src/content_sao.h b/src/content_sao.h index beaf697e6..2dd839e11 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -322,6 +322,7 @@ public: { return m_dig_pool; } + void setMaxSpeedOverride(const v3f &vel); // Returns true if cheated bool checkMovementCheat(); @@ -361,6 +362,8 @@ private: 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; diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 7f3ab50ed..8641cadec 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -67,7 +67,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CSMRestrictionFlags }, // 0x2A - null_command_handler, + { "TOCLIENT_PLAYER_SPEED", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerSpeed }, // 0x2B null_command_handler, null_command_handler, null_command_handler, diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 1ae47d190..520fcfa81 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1383,6 +1383,17 @@ void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt) loadMods(); } +void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt) +{ + v3f added_vel; + + *pkt >> added_vel; + + LocalPlayer *player = m_env.getLocalPlayer(); + assert(player != NULL); + player->addVelocity(added_vel); +} + /* * Mod channels */ diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 0ab11ee7e..451518bbf 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -194,6 +194,7 @@ with this program; if not, write to the Free Software Foundation, Inc., New network float format ContentFeatures version 13 Add full Euler rotations instead of just yaw + Add TOCLIENT_PLAYER_SPEED */ #define LATEST_PROTOCOL_VERSION 37 @@ -295,6 +296,11 @@ enum ToClientCommand u32 CSMRestrictionFlags byteflag */ + TOCLIENT_PLAYER_SPEED = 0x2B, + /* + v3f added_vel + */ + // (oops, there is some gap here) TOCLIENT_CHAT_MESSAGE = 0x2F, diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 013b549c6..8c5579a36 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -156,7 +156,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = null_command_factory, { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A - null_command_factory, + { "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B null_command_factory, null_command_factory, null_command_factory, diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 5dba63159..be2172f1b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1092,6 +1092,27 @@ int ObjectRef::l_get_player_velocity(lua_State *L) return 1; } +// add_player_velocity(self, {x=num, y=num, z=num}) +int ObjectRef::l_add_player_velocity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + v3f vel = checkFloatPos(L, 2); + + RemotePlayer *player = getplayer(ref); + PlayerSAO *co = getplayersao(ref); + if (!player || !co) + return 0; + + session_t peer_id = player->getPeerId(); + if (peer_id == PEER_ID_INEXISTENT) + return 0; + // Do it + co->setMaxSpeedOverride(vel); + getServer(L)->SendPlayerSpeed(peer_id, vel); + return 0; +} + // get_look_dir(self) int ObjectRef::l_get_look_dir(lua_State *L) { @@ -1931,6 +1952,7 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, is_player_connected), luamethod(ObjectRef, get_player_name), luamethod(ObjectRef, get_player_velocity), + luamethod(ObjectRef, add_player_velocity), luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 653d833f6..2390c5195 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -212,6 +212,9 @@ private: // get_player_velocity(self) static int l_get_player_velocity(lua_State *L); + // add_player_velocity(self, {x=num, y=num, z=num}) + static int l_add_player_velocity(lua_State *L); + // get_look_dir(self) static int l_get_look_dir(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 27388e666..5dd3d0061 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1958,6 +1958,13 @@ void Server::SendCSMRestrictionFlags(session_t peer_id) Send(&pkt); } +void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel) +{ + NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id); + pkt << added_vel; + Send(&pkt); +} + s32 Server::playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms) { diff --git a/src/server.h b/src/server.h index 4232653eb..06adbad68 100644 --- a/src/server.h +++ b/src/server.h @@ -335,6 +335,7 @@ public: void SendPlayerBreath(PlayerSAO *sao); void SendInventory(PlayerSAO* playerSAO); void SendMovePlayer(session_t peer_id); + void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); virtual bool registerModStorage(ModMetadata *storage); virtual void unregisterModStorage(const std::string &name);