summaryrefslogtreecommitdiffstats
path: root/source/cClientHandle.h
blob: 3ea94fa98743f19570fd161a31707bd7e76dd876 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

// cClientHandle.h

// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet





#pragma once
#ifndef CCLIENTHANDLE_H_INCLUDED
#define CCLIENTHANDLE_H_INCLUDED

#include "Defines.h"
#include "Vector3d.h"
#include "cSocketThreads.h"
#include "ChunkDef.h"
#include "ByteBuffer.h"





class cChunkDataSerializer;
class cInventory;
class cMonster;
class cPawn;
class cPickup;
class cPlayer;
class cProtocol;
class cRedstone;
class cWindow;




class cClientHandle :  // tolua_export
	public cSocketThreads::cCallback
{											// tolua_export
public:
	enum ENUM_PRIORITY
	{
		E_PRIORITY_LOW,
		E_PRIORITY_NORMAL
	};

	static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick
	
	static const int DEFAULT_VIEW_DISTANCE = 9;  // The default ViewDistance (used when no value is set in Settings.ini)
	static const int MAX_VIEW_DISTANCE = 10;
	static const int MIN_VIEW_DISTANCE = 4;

	cClientHandle(const cSocket & a_Socket, int a_ViewDistance);
	~cClientHandle();

	cSocket &       GetSocket  (void)       { return m_Socket; }
	const AString & GetIPString(void) const { return m_Socket.GetIPString(); }
	
	cPlayer* GetPlayer() { return m_Player; }	// tolua_export

	void Kick(const AString & a_Reason);		//tolua_export
	void Authenticate(void);  // Called by cAuthenticator when the user passes authentication

	void StreamChunks(void);
	
	// Removes the client from all chunks. Used when switching worlds or destroying the player
	void RemoveFromAllChunks(void);
	
	inline bool IsLoggedIn(void) const { return m_State >= csAuthenticating; }

	void Tick(float a_Dt);

	bool IsDestroyed() { return m_bDestroyed; }
	void Destroy();
	
	bool IsPlaying(void) const {return (m_State == csPlaying); }

	void SendDisconnect(const AString & a_Reason);
	void SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item);
	void SendChat(const AString & a_Message);
	void SendPlayerAnimation(const cPlayer & a_Player, char a_Animation);
	void SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item);
	void SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots);
	void SendWindowClose(char a_WindowID);
	void SendWholeInventory(const cInventory & a_Inventory);
	void SendWholeInventory(const cWindow    & a_Window);
	void SendTeleportEntity(const cEntity & a_Entity);
	void SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline);
	void SendPlayerPosition(void);
	void SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
	void SendEntRelMove    (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
	void SendEntLook       (const cEntity & a_Entity);
	void SendEntHeadLook   (const cEntity & a_Entity);
	void SendBlockAction   (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType);
	void SendHealth (void);
	void SendRespawn(void);
	void SendGameMode(eGameMode a_GameMode);
	void SendDestroyEntity(const cEntity & a_Entity);
	void SendPlayerMoveLook(void);
	void SendEntityStatus(const cEntity & a_Entity, char a_Status);
	void SendMetadata(const cPawn & a_Entity);
	void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value);
	void SendPlayerSpawn(const cPlayer & a_Player);
	void SendPickupSpawn(const cPickup & a_Pickup);
	void SendSpawnMob   (const cMonster & a_Mob);
	void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
	void SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player);
	void SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
	void SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
	void SendUnloadChunk(int a_ChunkX, int a_ChunkZ);
	void SendWeather(eWeather a_Weather);
	void SendTimeUpdate(Int64 a_WorldTime);
	void SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ);
	void SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);

	const AString & GetUsername(void) const;		//tolua_export
	
	inline short GetPing() const { return m_Ping; }	//tolua_export
	
	void SetViewDistance(int a_ViewDistance);		//tolua_export
	int GetViewDistance() { return m_ViewDistance; }//tolua_export

	int GetUniqueID() const { return m_UniqueID; }	//tolua_export
	
	/// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend)
	bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
	
	/// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend)
	void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
	
	// Calls that cProtocol descendants use to report state:
	void PacketBufferFull(void);
	void PacketUnknown(unsigned char a_PacketType);
	void PacketError(unsigned char a_PacketType);

	// Calls that cProtocol descendants use for handling packets:
	void HandlePing             (void);
	void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
	void HandlePlayerPos        (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround);
	void HandleBlockDig         (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
	void HandleBlockPlace       (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem);
	void HandleChat             (const AString & a_Message);
	void HandlePlayerLook       (float a_Rotation, float a_Pitch, bool a_IsOnGround);
	void HandlePlayerMoveLook   (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround);  // While m_bPositionConfirmed (normal gameplay)
	void HandleAnimation        (char a_Animation);
	void HandleSlotSelected     (short a_SlotNum);
	void HandleWindowClose      (char a_WindowID);
	void HandleWindowClick      (char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem);
	void HandleUpdateSign       (
		int a_BlockX, int a_BlockY, int a_BlockZ, 
		const AString & a_Line1, const AString & a_Line2, 
		const AString & a_Line3, const AString & a_Line4
	);
	void HandleUseEntity        (int a_TargetEntityID, bool a_IsLeftClick);
	void HandleRespawn          (void);
	void HandleDisconnect       (const AString & a_Reason);
	void HandleKeepAlive        (int a_KeepAliveID);
	bool HandleHandshake        (const AString & a_Username);
	
	/** Called when the protocol has finished logging the user in.
	Return true to allow the user in; false to kick them.
	*/
	bool HandleLogin(int a_ProtocolVersion, const AString & a_Username);
	
	void SendData(const char * a_Data, int a_Size);
private:

	int m_ViewDistance;  // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 )
	
	static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation

	int     m_ProtocolVersion;
	AString m_Username;
	AString m_Password;

	cCriticalSection m_CSChunkLists;
	cChunkCoordsList m_LoadedChunks;  // Chunks that the player belongs to
	cChunkCoordsList m_ChunksToSend;  // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them)

	cSocket     m_Socket;
	cProtocol * m_Protocol;
	
	cCriticalSection m_CSOutgoingData;
	cByteBuffer      m_OutgoingData;
	AString          m_OutgoingDataOverflow;  //< For data that didn't fit into the m_OutgoingData ringbuffer temporarily

	cCriticalSection m_CriticalSection;

	Vector3d m_ConfirmPosition;

	bool      m_bDestroyed;
	cPlayer * m_Player;
	bool      m_bKicking;
	
	// Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk
	int m_LastStreamedChunkX;
	int m_LastStreamedChunkZ;

	float m_TimeLastPacket;
	
	short m_Ping;
	int   m_PingID;
	long long m_PingStartTime;
	long long m_LastPingTime;
	static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms)

	enum eState
	{
		csConnected,         // The client has just connected, waiting for their handshake / login
		csAuthenticating,    // The client has logged in, waiting for external authentication
		csDownloadingWorld,  // The client is waiting for chunks, we're waiting for the loader to provide and send them
 		csConfirmingPos,     // The client has been sent the position packet, waiting for them to repeat the position back
		csPlaying,           // Normal gameplay
		
		// TODO: Add Kicking and Destroyed here as well
	} ;
	
	eState m_State;

	bool m_bKeepThreadGoing;
	
	/// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded()
	bool m_ShouldCheckDownloaded;

	/// Returns true if the rate block interactions is within a reasonable limit (bot protection)
	bool CheckBlockInteractionsRate(void);
	
	/// Checks whether all loaded chunks have been sent to the client; if so, sends the position to confirm
	void CheckIfWorldDownloaded(void);
	
	/// Sends the PlayerMoveLook packet that the client needs to reply to for the game to start
	void SendConfirmPosition(void);
	
	/// Adds a single chunk to be streamed to the client; used by StreamChunks()
	void StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
	
	// cSocketThreads::cCallback overrides:
	virtual void DataReceived   (const char * a_Data, int a_Size) override;  // Data is received from the client
	virtual void GetOutgoingData(AString & a_Data) override;  // Data can be sent to client
	virtual void SocketClosed   (void) override;  // The socket has been closed for any reason

	static int s_ClientCount;
	int m_UniqueID;
};										// tolua_export




#endif  // CCLIENTHANDLE_H_INCLUDED