From 47da640d7763ee1e00badb7476ac5afc4f864367 Mon Sep 17 00:00:00 2001 From: Anand S Date: Sun, 15 Jul 2018 05:56:30 +0530 Subject: [PATCH] Add support for per-player FOV overrides and multipliers --- doc/lua_api.txt | 13 ++++- src/client/camera.cpp | 20 ++++++- src/client/client.h | 1 + src/client/game.cpp | 2 +- src/network/clientopcodes.cpp | 8 +-- src/network/clientpackethandler.cpp | 13 ++++- src/network/networkprotocol.h | 9 ++- src/network/serveropcodes.cpp | 88 ++++++++++++++--------------- src/player.h | 18 ++++++ src/script/lua_api/l_object.cpp | 33 +++++++++++ src/script/lua_api/l_object.h | 6 ++ src/server.cpp | 10 ++++ src/server.h | 9 ++- 13 files changed, 168 insertions(+), 62 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 5a991c278..4eb9c797c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -5580,12 +5580,21 @@ This is basically a reference to a C++ `ServerActiveObject` `set_look_vertical`. * `set_look_yaw(radians)`: sets look yaw - Deprecated. Use `set_look_horizontal`. -* `get_breath()`: returns players breath -* `set_breath(value)`: sets players breath +* `get_breath()`: returns player's breath +* `set_breath(value)`: sets player's breath * values: * `0`: player is drowning * max: bubbles bar is not shown * See [Object properties] for more information +* `set_fov(fov, is_multiplier)`: Sets player's FOV + * `fov`: FOV value. + * `is_multiplier`: Set to `true` if the FOV value is a multiplier. + Defaults to `false`. + * Set to 0 to clear FOV override. +* `get_fov()`: + * Returns player's FOV override in degrees, and a boolean depending on whether + the value is a multiplier. + * Returns 0 as first value if player's FOV hasn't been overridden. * `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 025bd081d..d1e76026d 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -448,12 +448,26 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r if (m_camera_mode != CAMERA_MODE_FIRST) m_camera_position = my_cp; - // Get FOV + /* + * Apply server-sent FOV. If server doesn't enforce FOV, + * check for zoom and set to zoom FOV. + * Otherwise, default to m_cache_fov + */ + f32 fov_degrees; - // Disable zoom with zoom FOV = 0 - if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { + PlayerFovSpec fov_spec = player->getFov(); + if (fov_spec.fov > 0.0f) { + // If server-sent FOV is a multiplier, multiply + // it with m_cache_fov instead of overriding + if (fov_spec.is_multiplier) + fov_degrees = m_cache_fov * fov_spec.fov; + else + fov_degrees = fov_spec.fov; + } else if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) { + // Player requests zoom, apply zoom FOV fov_degrees = player->getZoomFOV(); } else { + // Set to client's selected FOV fov_degrees = m_cache_fov; } fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f); diff --git a/src/client/client.h b/src/client/client.h index dee60b6d5..e3c931837 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -193,6 +193,7 @@ public: void handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt); void handleCommand_ActiveObjectMessages(NetworkPacket* pkt); void handleCommand_Movement(NetworkPacket* pkt); + void handleCommand_Fov(NetworkPacket *pkt); void handleCommand_HP(NetworkPacket* pkt); void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_MovePlayer(NetworkPacket* pkt); diff --git a/src/client/game.cpp b/src/client/game.cpp index ef846fe18..042b61a78 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2332,7 +2332,7 @@ void Game::toggleFullViewRange() void Game::checkZoomEnabled() { LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (player->getZoomFOV() < 0.001f) + if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f) m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 8641cadec..498583df9 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -78,7 +78,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33 { "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34 { "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35 - null_command_handler, + { "TOCLIENT_FOV", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Fov }, // 0x36 { "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37 { "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38 null_command_handler, @@ -151,9 +151,9 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x14 null_command_factory, // 0x15 null_command_factory, // 0x16 - { "TOSERVER_MODCHANNEL_JOIN", 0, true }, // 0x17 - { "TOSERVER_MODCHANNEL_LEAVE", 0, true }, // 0x18 - { "TOSERVER_MODCHANNEL_MSG", 0, true }, // 0x19 + { "TOSERVER_MODCHANNEL_JOIN", 0, true }, // 0x17 + { "TOSERVER_MODCHANNEL_LEAVE", 0, true }, // 0x18 + { "TOSERVER_MODCHANNEL_MSG", 0, true }, // 0x19 null_command_factory, // 0x1a null_command_factory, // 0x1b null_command_factory, // 0x1c diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d47571d14..b6e9defb0 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -523,13 +523,22 @@ void Client::handleCommand_Movement(NetworkPacket* pkt) player->movement_gravity = g * BS; } -void Client::handleCommand_HP(NetworkPacket* pkt) +void Client::handleCommand_Fov(NetworkPacket *pkt) { + f32 fov; + bool is_multiplier; + *pkt >> fov >> is_multiplier; + LocalPlayer *player = m_env.getLocalPlayer(); + player->setFov({ fov, is_multiplier }); +} + +void Client::handleCommand_HP(NetworkPacket *pkt) +{ LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); - u16 oldhp = player->hp; + u16 oldhp = player->hp; u16 hp; *pkt >> hp; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 52cdf489c..5a13c1353 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -199,6 +199,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Incremental inventory sending mode Unknown inventory serialization fields no longer throw an error Mod-specific formspec version + Player FOV override API */ #define LATEST_PROTOCOL_VERSION 38 @@ -370,7 +371,13 @@ enum ToClientCommand wstring reason */ - TOCLIENT_PLAYERITEM = 0x36, // Obsolete + TOCLIENT_FOV = 0x36, + /* + Sends an FOV override/multiplier to client. + + float fov + bool is_multiplier + */ TOCLIENT_DEATHSCREEN = 0x37, /* diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 8c5579a36..8c8d49955 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -104,9 +104,9 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = null_command_handler, // 0x4d null_command_handler, // 0x4e null_command_handler, // 0x4f - { "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50 - { "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51 - { "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52 + { "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50 + { "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51 + { "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52 }; const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false }; @@ -115,51 +115,51 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { null_command_factory, // 0x00 null_command_factory, // 0x01 - { "TOCLIENT_HELLO", 0, true }, // 0x02 - { "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03 - { "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04 - { "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05 + { "TOCLIENT_HELLO", 0, true }, // 0x02 + { "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03 + { "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04 + { "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05 null_command_factory, // 0x06 null_command_factory, // 0x07 null_command_factory, // 0x08 null_command_factory, // 0x09 - { "TOCLIENT_ACCESS_DENIED", 0, true }, // 0x0A + { "TOCLIENT_ACCESS_DENIED", 0, true }, // 0x0A null_command_factory, // 0x0B null_command_factory, // 0x0C null_command_factory, // 0x0D null_command_factory, // 0x0E null_command_factory, // 0x0F - { "TOCLIENT_INIT", 0, true }, // 0x10 - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, + { "TOCLIENT_INIT", 0, true }, // 0x10 + null_command_factory, // 0x11 + null_command_factory, // 0x12 + null_command_factory, // 0x13 + null_command_factory, // 0x14 + null_command_factory, // 0x15 + null_command_factory, // 0x16 + null_command_factory, // 0x17 + null_command_factory, // 0x18 + null_command_factory, // 0x19 + null_command_factory, // 0x1A + null_command_factory, // 0x1B + null_command_factory, // 0x1C + null_command_factory, // 0x1D + null_command_factory, // 0x1E + null_command_factory, // 0x1F { "TOCLIENT_BLOCKDATA", 2, true }, // 0x20 { "TOCLIENT_ADDNODE", 0, true }, // 0x21 { "TOCLIENT_REMOVENODE", 0, true }, // 0x22 - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, + null_command_factory, // 0x23 + null_command_factory, // 0x24 + null_command_factory, // 0x25 + null_command_factory, // 0x26 { "TOCLIENT_INVENTORY", 0, true }, // 0x27 - null_command_factory, + null_command_factory, // 0x28 { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29 { "TOCLIENT_CSM_RESTRICTION_FLAGS", 0, true }, // 0x2A { "TOCLIENT_PLAYER_SPEED", 0, true }, // 0x2B - null_command_factory, - null_command_factory, - null_command_factory, + null_command_factory, // 0x2C + null_command_factory, // 0x2D + null_command_factory, // 0x2E { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x2F null_command_factory, // 0x30 { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31 @@ -167,15 +167,15 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_HP", 0, true }, // 0x33 { "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34 { "TOCLIENT_ACCESS_DENIED_LEGACY", 0, true }, // 0x35 - null_command_factory, // 0x36 + { "TOCLIENT_FOV", 0, true }, // 0x36 { "TOCLIENT_DEATHSCREEN", 0, true }, // 0x37 { "TOCLIENT_MEDIA", 2, true }, // 0x38 null_command_factory, // 0x39 - { "TOCLIENT_NODEDEF", 0, true }, // 0x3a - null_command_factory, // 0x3b - { "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3c - { "TOCLIENT_ITEMDEF", 0, true }, // 0x3d - null_command_factory, + { "TOCLIENT_NODEDEF", 0, true }, // 0x3A + null_command_factory, // 0x3B + { "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3C + { "TOCLIENT_ITEMDEF", 0, true }, // 0x3D + null_command_factory, // 0x3E { "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f { "TOCLIENT_STOP_SOUND", 0, true }, // 0x40 { "TOCLIENT_PRIVILEGES", 0, true }, // 0x41 @@ -203,12 +203,12 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_MODCHANNEL_MSG", 0, true }, // 0x57 { "TOCLIENT_MODCHANNEL_SIGNAL", 0, true }, // 0x58 { "TOCLIENT_NODEMETA_CHANGED", 0, true }, // 0x59 - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, - null_command_factory, + null_command_factory, // 0x5A + null_command_factory, // 0x5B + null_command_factory, // 0x5C + null_command_factory, // 0x5D + null_command_factory, // 0x5E + null_command_factory, // 0x5F { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61 }; diff --git a/src/player.h b/src/player.h index b0c3b2574..de7f427e9 100644 --- a/src/player.h +++ b/src/player.h @@ -32,6 +32,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" #define PLAYERNAME_ALLOWED_CHARS_USER_EXPL "'a' to 'z', 'A' to 'Z', '0' to '9', '-', '_'" +struct PlayerFovSpec +{ + f32 fov; + bool is_multiplier; +}; + struct PlayerControl { PlayerControl() = default; @@ -178,6 +184,16 @@ public: void setWieldIndex(u16 index); u16 getWieldIndex() const { return m_wield_index; } + void setFov(const PlayerFovSpec &spec) + { + m_fov_spec = spec; + } + + const PlayerFovSpec &getFov() const + { + return m_fov_spec; + } + u32 keyPressed = 0; HudElement* getHud(u32 id); @@ -187,10 +203,12 @@ public: u32 hud_flags; s32 hud_hotbar_itemcount; + protected: char m_name[PLAYERNAME_SIZE]; v3f m_speed; u16 m_wield_index = 0; + PlayerFovSpec m_fov_spec = { 0.0f, false }; std::vector hud; private: diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 68cc1c0a9..1ad79024f 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1249,6 +1249,37 @@ int ObjectRef::l_set_look_yaw(lua_State *L) return 1; } +// set_fov(self, degrees[, is_multiplier]) +int ObjectRef::l_set_fov(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (!player) + return 0; + + player->setFov({ static_cast(luaL_checknumber(L, 2)), readParam(L, 3) }); + getServer(L)->SendPlayerFov(player->getPeerId()); + + return 0; +} + +// get_fov(self) +int ObjectRef::l_get_fov(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (!player) + return 0; + + PlayerFovSpec fov_spec = player->getFov(); + lua_pushnumber(L, fov_spec.fov); + lua_pushboolean(L, fov_spec.is_multiplier); + + return 2; +} + // set_breath(self, breath) int ObjectRef::l_set_breath(lua_State *L) { @@ -1962,6 +1993,8 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_look_vertical), luamethod(ObjectRef, set_look_yaw), luamethod(ObjectRef, set_look_pitch), + luamethod(ObjectRef, get_fov), + luamethod(ObjectRef, set_fov), luamethod(ObjectRef, get_breath), luamethod(ObjectRef, set_breath), luamethod(ObjectRef, get_attribute), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 2390c5195..e817e1d33 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -215,6 +215,9 @@ private: // add_player_velocity(self, {x=num, y=num, z=num}) static int l_add_player_velocity(lua_State *L); + // get_fov(self) + static int l_get_fov(lua_State *L); + // get_look_dir(self) static int l_get_look_dir(lua_State *L); @@ -232,6 +235,9 @@ private: // get_look_yaw2(self) static int l_get_look_horizontal(lua_State *L); + // set_fov(self, degrees, is_multiplier) + static int l_set_fov(lua_State *L); + // set_look_vertical(self, radians) static int l_set_look_vertical(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index a848c0ae9..c0ed02b9f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1765,6 +1765,16 @@ void Server::SendMovePlayer(session_t peer_id) Send(&pkt); } +void Server::SendPlayerFov(session_t peer_id) +{ + NetworkPacket pkt(TOCLIENT_FOV, 4 + 1, peer_id); + + PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov(); + pkt << fov_spec.fov << fov_spec.is_multiplier; + + Send(&pkt); +} + void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4], f32 animation_speed) { diff --git a/src/server.h b/src/server.h index aa7d6385a..f01733179 100644 --- a/src/server.h +++ b/src/server.h @@ -336,6 +336,7 @@ public: void SendInventory(PlayerSAO *playerSAO, bool incremental); void SendMovePlayer(session_t peer_id); void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); + void SendPlayerFov(session_t peer_id); void sendDetachedInventories(session_t peer_id, bool incremental); @@ -515,7 +516,6 @@ private: /* Variables */ - // World directory std::string m_path_world; // Subgame specification @@ -575,7 +575,6 @@ private: /* Threads */ - // A buffer for time steps // step() increments and AsyncRunStep() run by m_thread reads it. float m_step_dtime = 0.0f; @@ -590,14 +589,14 @@ private: /* Time related stuff */ - // Timer for sending time of day over network float m_time_of_day_send_timer = 0.0f; // Uptime of server in seconds MutexedVariable m_uptime; + /* - Client interface - */ + Client interface + */ ClientInterface m_clients; /*