1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2020 Minetest core developers & community
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #pragma once
22 
23 #include "constants.h"
24 #include "network/networkprotocol.h"
25 #include "unit_sao.h"
26 #include "util/numeric.h"
27 
28 /*
29 	PlayerSAO needs some internals exposed.
30 */
31 
32 class LagPool
33 {
34 	float m_pool = 15.0f;
35 	float m_max = 15.0f;
36 
37 public:
38 	LagPool() = default;
39 
setMax(float new_max)40 	void setMax(float new_max)
41 	{
42 		m_max = new_max;
43 		if (m_pool > new_max)
44 			m_pool = new_max;
45 	}
46 
add(float dtime)47 	void add(float dtime)
48 	{
49 		m_pool -= dtime;
50 		if (m_pool < 0)
51 			m_pool = 0;
52 	}
53 
empty()54 	void empty() { m_pool = m_max; }
55 
grab(float dtime)56 	bool grab(float dtime)
57 	{
58 		if (dtime <= 0)
59 			return true;
60 		if (m_pool + dtime > m_max)
61 			return false;
62 		m_pool += dtime;
63 		return true;
64 	}
65 };
66 
67 class RemotePlayer;
68 
69 class PlayerSAO : public UnitSAO
70 {
71 public:
72 	PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t peer_id_,
73 			bool is_singleplayer);
74 
getType()75 	ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_PLAYER; }
getSendType()76 	ActiveObjectType getSendType() const { return ACTIVEOBJECT_TYPE_GENERIC; }
77 	std::string getDescription();
78 
79 	/*
80 		Active object <-> environment interface
81 	*/
82 
83 	void addedToEnvironment(u32 dtime_s);
84 	void removingFromEnvironment();
isStaticAllowed()85 	bool isStaticAllowed() const { return false; }
shouldUnload()86 	bool shouldUnload() const { return false; }
87 	std::string getClientInitializationData(u16 protocol_version);
88 	void getStaticData(std::string *result) const;
89 	void step(float dtime, bool send_recommended);
90 	void setBasePosition(const v3f &position);
91 	void setPos(const v3f &pos);
92 	void moveTo(v3f pos, bool continuous);
93 	void setPlayerYaw(const float yaw);
94 	// Data should not be sent at player initialization
95 	void setPlayerYawAndSend(const float yaw);
96 	void setLookPitch(const float pitch);
97 	// Data should not be sent at player initialization
98 	void setLookPitchAndSend(const float pitch);
getLookPitch()99 	f32 getLookPitch() const { return m_pitch; }
getRadLookPitch()100 	f32 getRadLookPitch() const { return m_pitch * core::DEGTORAD; }
101 	// Deprecated
getRadLookPitchDep()102 	f32 getRadLookPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; }
103 	void setFov(const float pitch);
getFov()104 	f32 getFov() const { return m_fov; }
105 	void setWantedRange(const s16 range);
getWantedRange()106 	s16 getWantedRange() const { return m_wanted_range; }
107 
108 	/*
109 		Interaction interface
110 	*/
111 
112 	u16 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher,
113 			float time_from_last_punch);
114 	void rightClick(ServerActiveObject *clicker);
115 	void setHP(s32 hp, const PlayerHPChangeReason &reason);
setHPRaw(u16 hp)116 	void setHPRaw(u16 hp) { m_hp = hp; }
getBreath()117 	u16 getBreath() const { return m_breath; }
118 	void setBreath(const u16 breath, bool send = true);
119 
120 	/*
121 		Inventory interface
122 	*/
123 	Inventory *getInventory() const;
124 	InventoryLocation getInventoryLocation() const;
setInventoryModified()125 	void setInventoryModified() {}
getWieldList()126 	std::string getWieldList() const { return "main"; }
127 	u16 getWieldIndex() const;
128 	ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const;
129 	bool setWieldedItem(const ItemStack &item);
130 
131 	/*
132 		PlayerSAO-specific
133 	*/
134 
135 	void disconnected();
136 
getPlayer()137 	RemotePlayer *getPlayer() { return m_player; }
getPeerID()138 	session_t getPeerID() const { return m_peer_id; }
139 
140 	// Cheat prevention
141 
getLastGoodPosition()142 	v3f getLastGoodPosition() const { return m_last_good_position; }
resetTimeFromLastPunch()143 	float resetTimeFromLastPunch()
144 	{
145 		float r = m_time_from_last_punch;
146 		m_time_from_last_punch = 0.0;
147 		return r;
148 	}
noCheatDigStart(const v3s16 & p)149 	void noCheatDigStart(const v3s16 &p)
150 	{
151 		m_nocheat_dig_pos = p;
152 		m_nocheat_dig_time = 0;
153 	}
getNoCheatDigPos()154 	v3s16 getNoCheatDigPos() { return m_nocheat_dig_pos; }
getNoCheatDigTime()155 	float getNoCheatDigTime() { return m_nocheat_dig_time; }
noCheatDigEnd()156 	void noCheatDigEnd() { m_nocheat_dig_pos = v3s16(32767, 32767, 32767); }
getDigPool()157 	LagPool &getDigPool() { return m_dig_pool; }
158 	void setMaxSpeedOverride(const v3f &vel);
159 	// Returns true if cheated
160 	bool checkMovementCheat();
161 
162 	// Other
163 
updatePrivileges(const std::set<std::string> & privs,bool is_singleplayer)164 	void updatePrivileges(const std::set<std::string> &privs, bool is_singleplayer)
165 	{
166 		m_privs = privs;
167 		m_is_singleplayer = is_singleplayer;
168 	}
169 
170 	bool getCollisionBox(aabb3f *toset) const;
171 	bool getSelectionBox(aabb3f *toset) const;
collideWithObjects()172 	bool collideWithObjects() const { return true; }
173 
174 	void finalize(RemotePlayer *player, const std::set<std::string> &privs);
175 
getEyePosition()176 	v3f getEyePosition() const { return m_base_position + getEyeOffset(); }
177 	v3f getEyeOffset() const;
178 	float getZoomFOV() const;
179 
getMeta()180 	inline Metadata &getMeta() { return m_meta; }
181 
182 private:
183 	std::string getPropertyPacket();
184 	void unlinkPlayerSessionAndSave();
185 	std::string generateUpdatePhysicsOverrideCommand() const;
186 
187 	RemotePlayer *m_player = nullptr;
188 	session_t m_peer_id = 0;
189 
190 	// Cheat prevention
191 	LagPool m_dig_pool;
192 	LagPool m_move_pool;
193 	v3f m_last_good_position;
194 	float m_time_from_last_teleport = 0.0f;
195 	float m_time_from_last_punch = 0.0f;
196 	v3s16 m_nocheat_dig_pos = v3s16(32767, 32767, 32767);
197 	float m_nocheat_dig_time = 0.0f;
198 	float m_max_speed_override_time = 0.0f;
199 	v3f m_max_speed_override = v3f(0.0f, 0.0f, 0.0f);
200 
201 	// Timers
202 	IntervalLimiter m_breathing_interval;
203 	IntervalLimiter m_drowning_interval;
204 	IntervalLimiter m_node_hurt_interval;
205 
206 	bool m_position_not_sent = false;
207 
208 	// Cached privileges for enforcement
209 	std::set<std::string> m_privs;
210 	bool m_is_singleplayer;
211 
212 	u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
213 	f32 m_pitch = 0.0f;
214 	f32 m_fov = 0.0f;
215 	s16 m_wanted_range = 0.0f;
216 
217 	Metadata m_meta;
218 
219 public:
220 	float m_physics_override_speed = 1.0f;
221 	float m_physics_override_jump = 1.0f;
222 	float m_physics_override_gravity = 1.0f;
223 	bool m_physics_override_sneak = true;
224 	bool m_physics_override_sneak_glitch = false;
225 	bool m_physics_override_new_move = true;
226 	bool m_physics_override_sent = false;
227 };
228 
229 struct PlayerHPChangeReason
230 {
231 	enum Type : u8
232 	{
233 		SET_HP,
234 		PLAYER_PUNCH,
235 		FALL,
236 		NODE_DAMAGE,
237 		DROWNING,
238 		RESPAWN
239 	};
240 
241 	Type type = SET_HP;
242 	bool from_mod = false;
243 	int lua_reference = -1;
244 
245 	// For PLAYER_PUNCH
246 	ServerActiveObject *object = nullptr;
247 	// For NODE_DAMAGE
248 	std::string node;
249 
hasLuaReferencePlayerHPChangeReason250 	inline bool hasLuaReference() const { return lua_reference >= 0; }
251 
setTypeFromStringPlayerHPChangeReason252 	bool setTypeFromString(const std::string &typestr)
253 	{
254 		if (typestr == "set_hp")
255 			type = SET_HP;
256 		else if (typestr == "punch")
257 			type = PLAYER_PUNCH;
258 		else if (typestr == "fall")
259 			type = FALL;
260 		else if (typestr == "node_damage")
261 			type = NODE_DAMAGE;
262 		else if (typestr == "drown")
263 			type = DROWNING;
264 		else if (typestr == "respawn")
265 			type = RESPAWN;
266 		else
267 			return false;
268 
269 		return true;
270 	}
271 
getTypeAsStringPlayerHPChangeReason272 	std::string getTypeAsString() const
273 	{
274 		switch (type) {
275 		case PlayerHPChangeReason::SET_HP:
276 			return "set_hp";
277 		case PlayerHPChangeReason::PLAYER_PUNCH:
278 			return "punch";
279 		case PlayerHPChangeReason::FALL:
280 			return "fall";
281 		case PlayerHPChangeReason::NODE_DAMAGE:
282 			return "node_damage";
283 		case PlayerHPChangeReason::DROWNING:
284 			return "drown";
285 		case PlayerHPChangeReason::RESPAWN:
286 			return "respawn";
287 		default:
288 			return "?";
289 		}
290 	}
291 
PlayerHPChangeReasonPlayerHPChangeReason292 	PlayerHPChangeReason(Type type) : type(type) {}
293 
PlayerHPChangeReasonPlayerHPChangeReason294 	PlayerHPChangeReason(Type type, ServerActiveObject *object) :
295 			type(type), object(object)
296 	{
297 	}
298 
PlayerHPChangeReasonPlayerHPChangeReason299 	PlayerHPChangeReason(Type type, std::string node) : type(type), node(node) {}
300 };
301