Add support for per-player FOV overrides and multipliers

This commit is contained in:
Anand S 2018-07-15 05:56:30 +05:30 committed by sfan5
parent 5c9983400f
commit 47da640d77
13 changed files with 168 additions and 62 deletions

@ -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

@ -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);

@ -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);

@ -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");
}

@ -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

@ -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;

@ -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,
/*

@ -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
};

@ -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<HudElement *> hud;
private:

@ -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<f32>(luaL_checknumber(L, 2)), readParam<bool>(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),

@ -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);

@ -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)
{

@ -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<double> m_uptime;
/*
Client interface
*/
Client interface
*/
ClientInterface m_clients;
/*