`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
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);
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);
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");
}
{ "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,
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
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;
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
wstring reason
*/
- TOCLIENT_PLAYERITEM = 0x36, // Obsolete
+ TOCLIENT_FOV = 0x36,
+ /*
+ Sends an FOV override/multiplier to client.
+
+ float fov
+ bool is_multiplier
+ */
TOCLIENT_DEATHSCREEN = 0x37,
/*
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 };
{
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
{ "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
{ "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
};
#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;
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);
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:
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)
{
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),
// 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);
// 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);
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)
{
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);
/*
Variables
*/
-
// World directory
std::string m_path_world;
// Subgame specification
/*
Threads
*/
-
// A buffer for time steps
// step() increments and AsyncRunStep() run by m_thread reads it.
float m_step_dtime = 0.0f;
/*
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;
/*