summaryrefslogtreecommitdiffstats
path: root/src/Protocol
diff options
context:
space:
mode:
Diffstat (limited to 'src/Protocol')
-rw-r--r--src/Protocol/Authenticator.cpp138
-rw-r--r--src/Protocol/Authenticator.h23
-rw-r--r--src/Protocol/CMakeLists.txt14
-rw-r--r--src/Protocol/ChunkDataSerializer.cpp79
-rw-r--r--src/Protocol/ChunkDataSerializer.h6
-rw-r--r--src/Protocol/MojangAPI.cpp911
-rw-r--r--src/Protocol/MojangAPI.h226
-rw-r--r--src/Protocol/Protocol.h246
-rw-r--r--src/Protocol/Protocol125.cpp2128
-rw-r--r--src/Protocol/Protocol125.h181
-rw-r--r--src/Protocol/Protocol132.cpp873
-rw-r--r--src/Protocol/Protocol132.h115
-rw-r--r--src/Protocol/Protocol14x.cpp265
-rw-r--r--src/Protocol/Protocol14x.h63
-rw-r--r--src/Protocol/Protocol15x.cpp209
-rw-r--r--src/Protocol/Protocol15x.h42
-rw-r--r--src/Protocol/Protocol16x.cpp312
-rw-r--r--src/Protocol/Protocol16x.h78
-rw-r--r--src/Protocol/Protocol17x.cpp602
-rw-r--r--src/Protocol/Protocol17x.h169
-rw-r--r--src/Protocol/Protocol18x.cpp3333
-rw-r--r--src/Protocol/Protocol18x.h338
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp318
-rw-r--r--src/Protocol/ProtocolRecognizer.h179
24 files changed, 5592 insertions, 5256 deletions
diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp
index 2a7cbc7bc..984000795 100644
--- a/src/Protocol/Authenticator.cpp
+++ b/src/Protocol/Authenticator.cpp
@@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Authenticator.h"
+#include "MojangAPI.h"
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
@@ -18,67 +19,6 @@
#define DEFAULT_AUTH_SERVER "sessionserver.mojang.com"
#define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%"
-/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert:
-Downloaded from http://certs.starfieldtech.com/repository/ */
-static const AString StarfieldCACert()
-{
- return AString(
- // G2 cert
- "-----BEGIN CERTIFICATE-----\n"
- "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n"
- "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n"
- "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n"
- "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n"
- "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n"
- "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n"
- "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n"
- "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
- "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n"
- "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n"
- "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n"
- "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n"
- "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n"
- "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n"
- "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n"
- "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n"
- "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n"
- "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n"
- "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n"
- "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n"
- "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n"
- "-----END CERTIFICATE-----\n\n"
- // Original (G1) cert:
- "-----BEGIN CERTIFICATE-----\n"
- "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n"
- "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n"
- "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n"
- "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n"
- "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n"
- "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n"
- "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n"
- "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n"
- "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n"
- "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n"
- "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n"
- "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n"
- "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n"
- "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n"
- "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n"
- "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n"
- "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n"
- "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n"
- "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n"
- "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n"
- "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n"
- "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n"
- "-----END CERTIFICATE-----\n"
- );
-}
-
-
-
-
-
cAuthenticator::cAuthenticator(void) :
super("cAuthenticator"),
m_Server(DEFAULT_AUTH_SERVER),
@@ -102,8 +42,8 @@ cAuthenticator::~cAuthenticator()
void cAuthenticator::ReadINI(cIniFile & IniFile)
{
- m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER);
- m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
+ m_Server = IniFile.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER);
+ m_Address = IniFile.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
}
@@ -193,62 +133,6 @@ void cAuthenticator::Execute(void)
-bool cAuthenticator::SecureGetFromAddress(const AString & a_CACerts, const AString & a_ExpectedPeerName, const AString & a_Data, AString & a_Response)
-{
- // Connect the socket:
- cBlockingSslClientSocket Socket;
- Socket.SetTrustedRootCertsFromString(a_CACerts, a_ExpectedPeerName);
- if (!Socket.Connect(a_ExpectedPeerName, 443))
- {
- LOGWARNING("cAuthenticator: Can't connect to %s: %s", a_ExpectedPeerName.c_str(), Socket.GetLastErrorText().c_str());
- return false;
- }
-
- if (!Socket.Send(a_Data.c_str(), a_Data.size()))
- {
- LOGWARNING("cAuthenticator: Writing SSL data failed: %s", Socket.GetLastErrorText().c_str());
- return false;
- }
-
- // Read the HTTP response:
- int ret;
- unsigned char buf[1024];
-
- for (;;)
- {
- ret = Socket.Receive(buf, sizeof(buf));
-
- if ((ret == POLARSSL_ERR_NET_WANT_READ) || (ret == POLARSSL_ERR_NET_WANT_WRITE))
- {
- // This value should never be returned, it is handled internally by cBlockingSslClientSocket
- LOGWARNING("cAuthenticator: SSL reading failed internally");
- return false;
- }
- if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY)
- {
- break;
- }
- if (ret < 0)
- {
- LOGWARNING("cAuthenticator: SSL reading failed: -0x%x", -ret);
- return false;
- }
- if (ret == 0)
- {
- break;
- }
-
- a_Response.append((const char *)buf, (size_t)ret);
- }
-
- Socket.Disconnect();
- return true;
-}
-
-
-
-
-
bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties)
{
LOGD("Trying to authenticate user %s", a_UserName.c_str());
@@ -266,7 +150,7 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S
Request += "\r\n";
AString Response;
- if (!SecureGetFromAddress(StarfieldCACert(), m_Server, Request, Response))
+ if (!cMojangAPI::SecureRequest(m_Server, Request, Response))
{
return false;
}
@@ -304,17 +188,11 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S
return false;
}
a_UserName = root.get("name", "Unknown").asString();
- a_UUID = root.get("id", "").asString();
+ a_UUID = cMojangAPI::MakeUUIDShort(root.get("id", "").asString());
a_Properties = root["properties"];
-
- // If the UUID doesn't contain the hashes, insert them at the proper places:
- if (a_UUID.size() == 32)
- {
- a_UUID.insert(8, "-");
- a_UUID.insert(13, "-");
- a_UUID.insert(18, "-");
- a_UUID.insert(23, "-");
- }
+
+ // Store the player's profile in the MojangAPI caches:
+ cRoot::Get()->GetMojangAPI().AddPlayerProfile(a_UserName, a_UUID, a_Properties);
return true;
}
diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h
index 244d94c0b..853eff535 100644
--- a/src/Protocol/Authenticator.h
+++ b/src/Protocol/Authenticator.h
@@ -11,8 +11,6 @@
#pragma once
-#ifndef CAUTHENTICATOR_H_INCLUDED
-#define CAUTHENTICATOR_H_INCLUDED
#include "../OSSupport/IsThread.h"
@@ -52,7 +50,7 @@ public:
/** Stops the authenticator thread. The thread may be started and stopped repeatedly */
void Stop(void);
-
+
private:
class cUser
@@ -76,29 +74,26 @@ private:
cUserList m_Queue;
cEvent m_QueueNonempty;
+ /** The server that is to be contacted for auth / UUID conversions */
AString m_Server;
+
+ /** The URL to use for auth, without server part.
+ %USERNAME% will be replaced with actual user name.
+ %SERVERID% will be replaced with server's ID.
+ For example "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%". */
AString m_Address;
+
AString m_PropertiesAddress;
bool m_ShouldAuthenticate;
/** cIsThread override: */
virtual void Execute(void) override;
- /** Connects to a hostname using SSL, sends given data, and sets the response, returning whether all was successful or not */
- bool SecureGetFromAddress(const AString & a_CACerts, const AString & a_ExpectedPeerName, const AString & a_Request, AString & a_Response);
-
/** Returns true if the user authenticated okay, false on error
- Sets the username, UUID, and properties (i.e. skin) fields
- */
+ Returns the case-corrected username, UUID, and properties (eg. skin). */
bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties);
};
-
-#endif // CAUTHENTICATOR_H_INCLUDED
-
-
-
-
diff --git a/src/Protocol/CMakeLists.txt b/src/Protocol/CMakeLists.txt
index ae447ce54..7c97e67bc 100644
--- a/src/Protocol/CMakeLists.txt
+++ b/src/Protocol/CMakeLists.txt
@@ -7,24 +7,18 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
SET (SRCS
Authenticator.cpp
ChunkDataSerializer.cpp
- Protocol125.cpp
- Protocol132.cpp
- Protocol14x.cpp
- Protocol15x.cpp
- Protocol16x.cpp
+ MojangAPI.cpp
Protocol17x.cpp
+ Protocol18x.cpp
ProtocolRecognizer.cpp)
SET (HDRS
Authenticator.h
ChunkDataSerializer.h
+ MojangAPI.h
Protocol.h
- Protocol125.h
- Protocol132.h
- Protocol14x.h
- Protocol15x.h
- Protocol16x.h
Protocol17x.h
+ Protocol18x.h
ProtocolRecognizer.h)
if(NOT MSVC)
diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp
index ebe61631b..5d080656d 100644
--- a/src/Protocol/ChunkDataSerializer.cpp
+++ b/src/Protocol/ChunkDataSerializer.cpp
@@ -8,6 +8,8 @@
#include "Globals.h"
#include "ChunkDataSerializer.h"
#include "zlib/zlib.h"
+#include "ByteBuffer.h"
+#include "Protocol18x.h"
@@ -30,7 +32,7 @@ cChunkDataSerializer::cChunkDataSerializer(
-const AString & cChunkDataSerializer::Serialize(int a_Version)
+const AString & cChunkDataSerializer::Serialize(int a_Version, int a_ChunkX, int a_ChunkZ)
{
Serializations::const_iterator itr = m_Serializations.find(a_Version);
if (itr != m_Serializations.end())
@@ -43,6 +45,7 @@ const AString & cChunkDataSerializer::Serialize(int a_Version)
{
case RELEASE_1_2_5: Serialize29(data); break;
case RELEASE_1_3_2: Serialize39(data); break;
+ case RELEASE_1_8_0: Serialize47(data, a_ChunkX, a_ChunkZ); break;
// TODO: Other protocol versions may serialize the data differently; implement here
default:
@@ -52,7 +55,10 @@ const AString & cChunkDataSerializer::Serialize(int a_Version)
break;
}
}
- m_Serializations[a_Version] = data;
+ if (!data.empty())
+ {
+ m_Serializations[a_Version] = data;
+ }
return m_Serializations[a_Version];
}
@@ -174,3 +180,72 @@ void cChunkDataSerializer::Serialize39(AString & a_Data)
+
+void cChunkDataSerializer::Serialize47(AString & a_Data, int a_ChunkX, int a_ChunkZ)
+{
+ // This function returns the fully compressed packet (including packet size), not the raw packet!
+
+ // Create the packet:
+ cByteBuffer Packet(512 KiB);
+ Packet.WriteVarInt(0x21); // Packet id (Chunk Data packet)
+ Packet.WriteBEInt(a_ChunkX);
+ Packet.WriteBEInt(a_ChunkZ);
+ Packet.WriteBool(true); // "Ground-up continuous", or rather, "biome data present" flag
+ Packet.WriteBEUShort(0xffff); // We're aways sending the full chunk with no additional data, so the bitmap is 0xffff
+
+ // Write the chunk size:
+ const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width;
+ UInt32 ChunkSize = (
+ (cChunkDef::NumBlocks * 2) + // Block meta + type
+ sizeof(m_BlockLight) + // Block light
+ sizeof(m_BlockSkyLight) + // Block sky light
+ BiomeDataSize // Biome data
+ );
+ Packet.WriteVarInt(ChunkSize);
+
+ // Write the block types to the packet:
+ for (size_t Index = 0; Index < cChunkDef::NumBlocks; Index++)
+ {
+ BLOCKTYPE BlockType = m_BlockTypes[Index] & 0xFF;
+ NIBBLETYPE BlockMeta = m_BlockMetas[Index / 2] >> ((Index & 1) * 4) & 0x0f;
+ Packet.WriteByte((unsigned char)(BlockType << 4) | BlockMeta);
+ Packet.WriteByte((unsigned char)(BlockType >> 4));
+ }
+
+ // Write the rest:
+ Packet.WriteBuf(m_BlockLight, sizeof(m_BlockLight));
+ Packet.WriteBuf(m_BlockSkyLight, sizeof(m_BlockSkyLight));
+ Packet.WriteBuf(m_BiomeData, BiomeDataSize);
+
+ AString PacketData;
+ Packet.ReadAll(PacketData);
+ Packet.CommitRead();
+
+ cByteBuffer Buffer(20);
+ if (PacketData.size() >= 256)
+ {
+ if (!cProtocol180::CompressPacket(PacketData, a_Data))
+ {
+ ASSERT(!"Packet compression failed.");
+ a_Data.clear();
+ return;
+ }
+ }
+ else
+ {
+ AString PostData;
+ Buffer.WriteVarInt((UInt32)Packet.GetUsedSpace() + 1);
+ Buffer.WriteVarInt(0);
+ Buffer.ReadAll(PostData);
+ Buffer.CommitRead();
+
+ a_Data.clear();
+ a_Data.reserve(PostData.size() + PacketData.size());
+ a_Data.append(PostData.data(), PostData.size());
+ a_Data.append(PacketData.data(), PacketData.size());
+ }
+}
+
+
+
+
diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h
index a42856356..a082ef3d8 100644
--- a/src/Protocol/ChunkDataSerializer.h
+++ b/src/Protocol/ChunkDataSerializer.h
@@ -23,13 +23,15 @@ protected:
Serializations m_Serializations;
void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5
- void Serialize39(AString & a_Data); // Release 1.3.1 and 1.3.2
+ void Serialize39(AString & a_Data); // Release 1.3.1 to 1.7.10
+ void Serialize47(AString & a_Data, int a_ChunkX, int a_ChunkZ); // Release 1.8
public:
enum
{
RELEASE_1_2_5 = 29,
RELEASE_1_3_2 = 39,
+ RELEASE_1_8_0 = 47,
} ;
cChunkDataSerializer(
@@ -40,7 +42,7 @@ public:
const unsigned char * a_BiomeData
);
- const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[]
+ const AString & Serialize(int a_Version, int a_ChunkX, int a_ChunkZ); // Returns one of the internal m_Serializations[]
} ;
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
new file mode 100644
index 000000000..0a6716e6f
--- /dev/null
+++ b/src/Protocol/MojangAPI.cpp
@@ -0,0 +1,911 @@
+
+// MojangAPI.cpp
+
+// Implements the cMojangAPI class representing the various API points provided by Mojang's webservices, and a cache for their results
+
+#include "Globals.h"
+#include "MojangAPI.h"
+#include "SQLiteCpp/Database.h"
+#include "SQLiteCpp/Statement.h"
+#include "inifile/iniFile.h"
+#include "json/json.h"
+#include "PolarSSL++/BlockingSslClientSocket.h"
+#include "../RankManager.h"
+#include "../OSSupport/IsThread.h"
+#include "../Root.h"
+
+
+
+
+
+/** The maximum age for items to be kept in the cache. Any item older than this will be removed. */
+const Int64 MAX_AGE = 7 * 24 * 60 * 60; // 7 days ago
+
+/** The maximum number of names to send in a single query */
+const int MAX_PER_QUERY = 100;
+
+
+
+
+
+#define DEFAULT_NAME_TO_UUID_SERVER "api.mojang.com"
+#define DEFAULT_NAME_TO_UUID_ADDRESS "/profiles/minecraft"
+#define DEFAULT_UUID_TO_PROFILE_SERVER "sessionserver.mojang.com"
+#define DEFAULT_UUID_TO_PROFILE_ADDRESS "/session/minecraft/profile/%UUID%?unsigned=false"
+
+
+
+
+
+
+/** This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert:
+Downloaded from http://certs.starfieldtech.com/repository/ */
+static const AString & StarfieldCACert(void)
+{
+ static const AString Cert(
+ // G2 cert
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n"
+ "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n"
+ "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n"
+ "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n"
+ "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n"
+ "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n"
+ "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n"
+ "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n"
+ "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n"
+ "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n"
+ "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n"
+ "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n"
+ "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n"
+ "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n"
+ "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n"
+ "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n"
+ "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n"
+ "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n"
+ "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n"
+ "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n"
+ "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n"
+ "-----END CERTIFICATE-----\n\n"
+ // Original (G1) cert:
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n"
+ "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n"
+ "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n"
+ "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n"
+ "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n"
+ "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n"
+ "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n"
+ "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n"
+ "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n"
+ "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n"
+ "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n"
+ "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n"
+ "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n"
+ "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n"
+ "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n"
+ "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n"
+ "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n"
+ "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n"
+ "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n"
+ "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n"
+ "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n"
+ "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n"
+ "-----END CERTIFICATE-----\n"
+ );
+
+ return Cert;
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cMojangAPI::sProfile:
+
+cMojangAPI::sProfile::sProfile(
+ const AString & a_PlayerName,
+ const AString & a_UUID,
+ const Json::Value & a_Properties,
+ Int64 a_DateTime
+) :
+ m_PlayerName(a_PlayerName),
+ m_UUID(a_UUID),
+ m_Textures(),
+ m_TexturesSignature(),
+ m_DateTime(a_DateTime)
+{
+ /*
+ Example a_Profile contents:
+ "properties":
+ [
+ {
+ "name": "textures",
+ "value": "eyJ0aW1lc3RhbXAiOjE0MDcwNzAzMjEyNzEsInByb2ZpbGVJZCI6ImIxY2FmMjQyMDJhODQxYTc4MDU1YTA3OWM0NjBlZWU3IiwicHJvZmlsZU5hbWUiOiJ4b2Z0IiwiaXNQdWJsaWMiOnRydWUsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iNzc5YmFiZjVhNTg3Zjk0OGFkNjc0N2VhOTEyNzU0MjliNjg4Mjk1YWUzYzA3YmQwZTJmNWJmNGQwNTIifX19",
+ "signature": "XCty+jGEF39hEPrPhYNnCX087kPaoCjYruzYI/DS4nkL5hbjnkSM5Rh15hnUyv/FHhC8OF5rif3D1tQjtMI19KSVaXoUFXpbJM8/+PB8GDgEbX8Fc3u9nYkzOcM/xfxdYsFAdFhLQMkvase/BZLSuPhdy9DdI+TCrO7xuSTZfYmmwVuWo3w5gCY+mSIAnqltnOzaOOTcly75xvO0WYpVk7nJdnR2tvSi0wfrQPDrIg/uzhX7p0SnDqijmBU4QaNez/TNKiFxy69dAzt0RSotlQzqkDbyVKhhv9a4eY8h3pXi4UMftKEj4FAKczxLImkukJXuOn5NN15/Q+le0rJVBC60/xjKIVzltEsMN6qjWD0lQjey7WEL+4pGhCVuWY5KzuZjFvgqszuJTFz7lo+bcHiceldJtea8/fa02eTRObZvdLxbWC9ZfFY0IhpOVKfcLdno/ddDMNMQMi5kMrJ8MZZ/PcW1w5n7MMGWPGCla1kOaC55AL0QYSMGRVEZqgU9wXI5M7sHGZKGM4mWxkbEJYBkpI/p3GyxWgV6v33ZWlsz65TqlNrR1gCLaoFCm7Sif8NqPBZUAONHYon0roXhin/DyEanS93WV6i6FC1Wisscjq2AcvnOlgTo/5nN/1QsMbjNumuMGo37sqjRqlXoPb8zEUbAhhztYuJjEfQ2Rd8="
+ }
+ ]
+ */
+
+ // Parse the Textures and TexturesSignature from the Profile:
+ if (!a_Properties.isArray())
+ {
+ // Properties is not a valid array, bail out
+ return;
+ }
+ Json::UInt Size = a_Properties.size();
+ for (Json::UInt i = 0; i < Size; i++)
+ {
+ const Json::Value & Prop = a_Properties[i];
+ AString PropName = Prop.get("name", "").asString();
+ if (PropName != "textures")
+ {
+ continue;
+ }
+ m_Textures = Prop.get("value", "").asString();
+ m_TexturesSignature = Prop.get("signature", "").asString();
+ break;
+ } // for i - Properties[]
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cMojangAPI::cUpdateThread:
+
+class cMojangAPI::cUpdateThread :
+ public cIsThread
+{
+ typedef cIsThread super;
+public:
+ cUpdateThread() :
+ super("cMojangAPI::cUpdateThread")
+ {
+ }
+
+ ~cUpdateThread()
+ {
+ m_evtNotify.Set();
+ Stop();
+ }
+
+protected:
+ cEvent m_evtNotify;
+
+ virtual void Execute(void) override
+ {
+ do
+ {
+ cRoot::Get()->GetMojangAPI().Update();
+ } while (!m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes
+ }
+} ;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cMojangAPI:
+
+cMojangAPI::cMojangAPI(void) :
+ m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
+ m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
+ m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
+ m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
+ m_RankMgr(NULL),
+ m_UpdateThread(new cUpdateThread())
+{
+}
+
+
+
+
+
+cMojangAPI::~cMojangAPI()
+{
+ SaveCachesToDisk();
+}
+
+
+
+
+
+void cMojangAPI::Start(cIniFile & a_SettingsIni)
+{
+ m_NameToUUIDServer = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER);
+ m_NameToUUIDAddress = a_SettingsIni.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS);
+ m_UUIDToProfileServer = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER);
+ m_UUIDToProfileAddress = a_SettingsIni.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS);
+ LoadCachesFromDisk();
+ m_UpdateThread->Start();
+}
+
+
+
+
+
+AString cMojangAPI::GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached)
+{
+ // Convert the playername to lowercase:
+ AString lcPlayerName = StrToLower(a_PlayerName);
+
+ // Request the cache to query the name if not yet cached:
+ if (!a_UseOnlyCached)
+ {
+ AStringVector PlayerNames;
+ PlayerNames.push_back(lcPlayerName);
+ CacheNamesToUUIDs(PlayerNames);
+ }
+
+ // Retrieve from cache:
+ cCSLock Lock(m_CSNameToUUID);
+ cProfileMap::const_iterator itr = m_NameToUUID.find(lcPlayerName);
+ if (itr == m_NameToUUID.end())
+ {
+ // No UUID found
+ return "";
+ }
+ return itr->second.m_UUID;
+}
+
+
+
+
+
+AString cMojangAPI::GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached)
+{
+ // Normalize the UUID to lowercase short format that is used as the map key:
+ AString UUID = MakeUUIDShort(a_UUID);
+
+ // Retrieve from caches:
+ {
+ cCSLock Lock(m_CSUUIDToProfile);
+ cProfileMap::const_iterator itr = m_UUIDToProfile.find(UUID);
+ if (itr != m_UUIDToProfile.end())
+ {
+ return itr->second.m_PlayerName;
+ }
+ }
+ {
+ cCSLock Lock(m_CSUUIDToName);
+ cProfileMap::const_iterator itr = m_UUIDToName.find(UUID);
+ if (itr != m_UUIDToName.end())
+ {
+ return itr->second.m_PlayerName;
+ }
+ }
+
+ // Name not yet cached, request cache and retry:
+ if (!a_UseOnlyCached)
+ {
+ CacheUUIDToProfile(UUID);
+ return GetPlayerNameFromUUID(a_UUID, true);
+ }
+
+ // No value found, none queried. Return an error:
+ return "";
+}
+
+
+
+
+
+AStringVector cMojangAPI::GetUUIDsFromPlayerNames(const AStringVector & a_PlayerNames, bool a_UseOnlyCached)
+{
+ // Convert all playernames to lowercase:
+ AStringVector PlayerNames;
+ for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr)
+ {
+ PlayerNames.push_back(StrToLower(*itr));
+ } // for itr - a_PlayerNames[]
+
+ // Request the cache to populate any names not yet contained:
+ if (!a_UseOnlyCached)
+ {
+ CacheNamesToUUIDs(PlayerNames);
+ }
+
+ // Retrieve from cache:
+ size_t idx = 0;
+ AStringVector res;
+ res.resize(PlayerNames.size());
+ cCSLock Lock(m_CSNameToUUID);
+ for (AStringVector::const_iterator itr = PlayerNames.begin(), end = PlayerNames.end(); itr != end; ++itr, ++idx)
+ {
+ cProfileMap::const_iterator itrN = m_NameToUUID.find(*itr);
+ if (itrN != m_NameToUUID.end())
+ {
+ res[idx] = itrN->second.m_UUID;
+ }
+ } // for itr - PlayerNames[]
+ return res;
+}
+
+
+
+
+
+void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID)
+{
+ AString UUID = MakeUUIDShort(a_UUID);
+ Int64 Now = time(NULL);
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now);
+ }
+ {
+ cCSLock Lock(m_CSUUIDToName);
+ m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
+ }
+ NotifyNameUUID(a_PlayerName, a_UUID);
+}
+
+
+
+
+
+void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties)
+{
+ AString UUID = MakeUUIDShort(a_UUID);
+ Int64 Now = time(NULL);
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ m_NameToUUID[StrToLower(a_PlayerName)] = sProfile(a_PlayerName, UUID, "", "", Now);
+ }
+ {
+ cCSLock Lock(m_CSUUIDToName);
+ m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
+ }
+ {
+ cCSLock Lock(m_CSUUIDToProfile);
+ m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now);
+ }
+ NotifyNameUUID(a_PlayerName, a_UUID);
+}
+
+
+
+
+
+bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response)
+{
+ // Connect the socket:
+ cBlockingSslClientSocket Socket;
+ Socket.SetTrustedRootCertsFromString(StarfieldCACert(), a_ServerName);
+ if (!Socket.Connect(a_ServerName, 443))
+ {
+ LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str());
+ return false;
+ }
+
+ if (!Socket.Send(a_Request.c_str(), a_Request.size()))
+ {
+ LOGWARNING("%s: Writing SSL data failed: %s", __FUNCTION__, Socket.GetLastErrorText().c_str());
+ return false;
+ }
+
+ // Read the HTTP response:
+ int ret;
+ unsigned char buf[1024];
+
+ for (;;)
+ {
+ ret = Socket.Receive(buf, sizeof(buf));
+
+ if ((ret == POLARSSL_ERR_NET_WANT_READ) || (ret == POLARSSL_ERR_NET_WANT_WRITE))
+ {
+ // This value should never be returned, it is handled internally by cBlockingSslClientSocket
+ LOGWARNING("%s: SSL reading failed internally", __FUNCTION__);
+ return false;
+ }
+ if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY)
+ {
+ break;
+ }
+ if (ret < 0)
+ {
+ LOGWARNING("%s: SSL reading failed: -0x%x", __FUNCTION__, -ret);
+ return false;
+ }
+ if (ret == 0)
+ {
+ break;
+ }
+
+ a_Response.append((const char *)buf, (size_t)ret);
+ }
+
+ Socket.Disconnect();
+ return true;
+}
+
+
+
+
+
+AString cMojangAPI::MakeUUIDShort(const AString & a_UUID)
+{
+ // Note: we only check the string's length, not the actual content
+ switch (a_UUID.size())
+ {
+ case 32:
+ {
+ // Already is a short UUID, only lowercase
+ return StrToLower(a_UUID);
+ }
+
+ case 36:
+ {
+ // Remove the dashes from the string by appending together the parts between them:
+ AString res;
+ res.reserve(32);
+ res.append(a_UUID, 0, 8);
+ res.append(a_UUID, 9, 4);
+ res.append(a_UUID, 14, 4);
+ res.append(a_UUID, 19, 4);
+ res.append(a_UUID, 24, 12);
+ return StrToLower(res);
+ }
+ }
+ LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str());
+ return "";
+}
+
+
+
+
+
+AString cMojangAPI::MakeUUIDDashed(const AString & a_UUID)
+{
+ // Note: we only check the string's length, not the actual content
+ switch (a_UUID.size())
+ {
+ case 36:
+ {
+ // Already is a dashed UUID, only lowercase
+ return StrToLower(a_UUID);
+ }
+
+ case 32:
+ {
+ // Insert dashes at the proper positions:
+ AString res;
+ res.reserve(36);
+ res.append(a_UUID, 0, 8);
+ res.push_back('-');
+ res.append(a_UUID, 8, 4);
+ res.push_back('-');
+ res.append(a_UUID, 12, 4);
+ res.push_back('-');
+ res.append(a_UUID, 16, 4);
+ res.push_back('-');
+ res.append(a_UUID, 20, 12);
+ return StrToLower(res);
+ }
+ }
+ LOGWARNING("%s: Not an UUID: \"%s\".", __FUNCTION__, a_UUID.c_str());
+ return "";
+}
+
+
+
+
+
+void cMojangAPI::LoadCachesFromDisk(void)
+{
+ try
+ {
+ // Open up the SQLite DB:
+ SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+ db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)");
+ db.exec("CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)");
+
+ // Retrieve all entries:
+ {
+ SQLite::Statement stmt(db, "SELECT PlayerName, UUID, DateTime FROM PlayerNameToUUID");
+ while (stmt.executeStep())
+ {
+ AString PlayerName = stmt.getColumn(0);
+ AString UUID = stmt.getColumn(1);
+ Int64 DateTime = stmt.getColumn(2);
+ UUID = MakeUUIDShort(UUID);
+ m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, UUID, "", "", DateTime);
+ m_UUIDToName[UUID] = sProfile(PlayerName, UUID, "", "", DateTime);
+ }
+ }
+ {
+ SQLite::Statement stmt(db, "SELECT PlayerName, UUID, Textures, TexturesSignature, DateTime FROM UUIDToProfile");
+ while (stmt.executeStep())
+ {
+ AString PlayerName = stmt.getColumn(0);
+ AString UUID = stmt.getColumn(1);
+ AString Textures = stmt.getColumn(2);
+ AString TexturesSignature = stmt.getColumn(2);
+ Int64 DateTime = stmt.getColumn(4);
+ UUID = MakeUUIDShort(UUID);
+ m_UUIDToProfile[UUID] = sProfile(PlayerName, UUID, Textures, TexturesSignature, DateTime);
+ }
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGINFO("Loading MojangAPI cache failed: %s", ex.what());
+ }
+}
+
+
+
+
+
+void cMojangAPI::SaveCachesToDisk(void)
+{
+ try
+ {
+ // Open up the SQLite DB:
+ SQLite::Database db("MojangAPI.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+ db.exec("CREATE TABLE IF NOT EXISTS PlayerNameToUUID (PlayerName, UUID, DateTime)");
+ db.exec("CREATE TABLE IF NOT EXISTS UUIDToProfile (UUID, PlayerName, Textures, TexturesSignature, DateTime)");
+
+ // Remove all entries:
+ db.exec("DELETE FROM PlayerNameToUUID");
+ db.exec("DELETE FROM UUIDToProfile");
+
+ // Save all cache entries - m_PlayerNameToUUID:
+ Int64 LimitDateTime = time(NULL) - MAX_AGE;
+ {
+ SQLite::Statement stmt(db, "INSERT INTO PlayerNameToUUID(PlayerName, UUID, DateTime) VALUES (?, ?, ?)");
+ cCSLock Lock(m_CSNameToUUID);
+ for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr)
+ {
+ if (itr->second.m_DateTime < LimitDateTime)
+ {
+ // This item is too old, do not save
+ continue;
+ }
+ stmt.bind(1, itr->second.m_PlayerName);
+ stmt.bind(2, itr->second.m_UUID);
+ stmt.bind(3, itr->second.m_DateTime);
+ stmt.exec();
+ stmt.reset();
+ }
+ }
+
+ // Save all cache entries - m_UUIDToProfile:
+ {
+ SQLite::Statement stmt(db, "INSERT INTO UUIDToProfile(UUID, PlayerName, Textures, TexturesSignature, DateTime) VALUES (?, ?, ?, ?, ?)");
+ cCSLock Lock(m_CSUUIDToProfile);
+ for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr)
+ {
+ if (itr->second.m_DateTime < LimitDateTime)
+ {
+ // This item is too old, do not save
+ continue;
+ }
+ stmt.bind(1, itr->second.m_UUID);
+ stmt.bind(2, itr->second.m_PlayerName);
+ stmt.bind(3, itr->second.m_Textures);
+ stmt.bind(4, itr->second.m_TexturesSignature);
+ stmt.bind(5, itr->second.m_DateTime);
+ stmt.exec();
+ stmt.reset();
+ }
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGINFO("Saving MojangAPI cache failed: %s", ex.what());
+ }
+}
+
+
+
+
+
+void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames)
+{
+ // Create a list of names to query, by removing those that are already cached:
+ AStringVector NamesToQuery;
+ NamesToQuery.reserve(a_PlayerNames.size());
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr)
+ {
+ if (m_NameToUUID.find(*itr) == m_NameToUUID.end())
+ {
+ NamesToQuery.push_back(*itr);
+ }
+ } // for itr - a_PlayerNames[]
+ } // Lock(m_CSNameToUUID)
+
+ QueryNamesToUUIDs(NamesToQuery);
+}
+
+
+
+
+
+void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery)
+{
+ while (!a_NamesToQuery.empty())
+ {
+ // Create the request body - a JSON containing up to MAX_PER_QUERY playernames:
+ Json::Value root;
+ int Count = 0;
+ AStringVector::iterator itr = a_NamesToQuery.begin(), end = a_NamesToQuery.end();
+ for (; (itr != end) && (Count < MAX_PER_QUERY); ++itr, ++Count)
+ {
+ Json::Value req(*itr);
+ root.append(req);
+ } // for itr - a_PlayerNames[]
+ a_NamesToQuery.erase(a_NamesToQuery.begin(), itr);
+ Json::FastWriter Writer;
+ AString RequestBody = Writer.write(root);
+
+ // Create the HTTP request:
+ AString Request;
+ Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding
+ Request += "Host: " + m_NameToUUIDServer + "\r\n";
+ Request += "User-Agent: MCServer\r\n";
+ Request += "Connection: close\r\n";
+ Request += "Content-Type: application/json\r\n";
+ Request += Printf("Content-Length: %u\r\n", (unsigned)RequestBody.length());
+ Request += "\r\n";
+ Request += RequestBody;
+
+ // Get the response from the server:
+ AString Response;
+ if (!SecureRequest(m_NameToUUIDServer, Request, Response))
+ {
+ continue;
+ }
+
+ // Check the HTTP status line:
+ const AString Prefix("HTTP/1.1 200 OK");
+ AString HexDump;
+ if (Response.compare(0, Prefix.size(), Prefix))
+ {
+ LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__);
+ LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ continue;
+ }
+
+ // Erase the HTTP headers from the response:
+ size_t idxHeadersEnd = Response.find("\r\n\r\n");
+ if (idxHeadersEnd == AString::npos)
+ {
+ LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__);
+ LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ continue;
+ }
+ Response.erase(0, idxHeadersEnd + 4);
+
+ // Parse the returned string into Json:
+ Json::Reader reader;
+ if (!reader.parse(Response, root, false) || !root.isArray())
+ {
+ LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__);
+ LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ continue;
+ }
+
+ // Store the returned results into cache:
+ Json::Value::UInt JsonCount = root.size();
+ Int64 Now = time(NULL);
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
+ {
+ Json::Value & Val = root[idx];
+ AString JsonName = Val.get("name", "").asString();
+ AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString());
+ if (JsonUUID.empty())
+ {
+ continue;
+ }
+ m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now);
+ NotifyNameUUID(JsonName, JsonUUID);
+ } // for idx - root[]
+ } // cCSLock (m_CSNameToUUID)
+
+ // Also cache the UUIDToName:
+ {
+ cCSLock Lock(m_CSUUIDToName);
+ for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
+ {
+ Json::Value & Val = root[idx];
+ AString JsonName = Val.get("name", "").asString();
+ AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString());
+ if (JsonUUID.empty())
+ {
+ continue;
+ }
+ m_UUIDToName[JsonUUID] = sProfile(JsonName, JsonUUID, "", "", Now);
+ } // for idx - root[]
+ }
+ } // while (!NamesToQuery.empty())
+}
+
+
+
+
+
+void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID)
+{
+ ASSERT(a_UUID.size() == 32);
+
+ // Check if already present:
+ {
+ cCSLock Lock(m_CSUUIDToProfile);
+ if (m_UUIDToProfile.find(a_UUID) != m_UUIDToProfile.end())
+ {
+ return;
+ }
+ }
+
+ QueryUUIDToProfile(a_UUID);
+}
+
+
+
+
+
+void cMojangAPI::QueryUUIDToProfile(const AString & a_UUID)
+{
+ // Create the request address:
+ AString Address = m_UUIDToProfileAddress;
+ ReplaceString(Address, "%UUID%", a_UUID);
+
+ // Create the HTTP request:
+ AString Request;
+ Request += "GET " + Address + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding
+ Request += "Host: " + m_UUIDToProfileServer + "\r\n";
+ Request += "User-Agent: MCServer\r\n";
+ Request += "Connection: close\r\n";
+ Request += "Content-Length: 0\r\n";
+ Request += "\r\n";
+
+ // Get the response from the server:
+ AString Response;
+ if (!SecureRequest(m_UUIDToProfileServer, Request, Response))
+ {
+ return;
+ }
+
+ // Check the HTTP status line:
+ const AString Prefix("HTTP/1.1 200 OK");
+ AString HexDump;
+ if (Response.compare(0, Prefix.size(), Prefix))
+ {
+ LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__);
+ LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ return;
+ }
+
+ // Erase the HTTP headers from the response:
+ size_t idxHeadersEnd = Response.find("\r\n\r\n");
+ if (idxHeadersEnd == AString::npos)
+ {
+ LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__);
+ LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ return;
+ }
+ Response.erase(0, idxHeadersEnd + 4);
+
+ // Parse the returned string into Json:
+ Json::Reader reader;
+ Json::Value root;
+ if (!reader.parse(Response, root, false) || !root.isObject())
+ {
+ LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__);
+ LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str());
+ return;
+ }
+
+ /* Example response:
+ {
+ "id": "b1caf24202a841a78055a079c460eee7",
+ "name": "xoft",
+ "properties":
+ [
+ {
+ "name": "textures",
+ "value": "eyJ0aW1lc3RhbXAiOjE0MDcwNzAzMjEyNzEsInByb2ZpbGVJZCI6ImIxY2FmMjQyMDJhODQxYTc4MDU1YTA3OWM0NjBlZWU3IiwicHJvZmlsZU5hbWUiOiJ4b2Z0IiwiaXNQdWJsaWMiOnRydWUsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iNzc5YmFiZjVhNTg3Zjk0OGFkNjc0N2VhOTEyNzU0MjliNjg4Mjk1YWUzYzA3YmQwZTJmNWJmNGQwNTIifX19",
+ "signature": "XCty+jGEF39hEPrPhYNnCX087kPaoCjYruzYI/DS4nkL5hbjnkSM5Rh15hnUyv/FHhC8OF5rif3D1tQjtMI19KSVaXoUFXpbJM8/+PB8GDgEbX8Fc3u9nYkzOcM/xfxdYsFAdFhLQMkvase/BZLSuPhdy9DdI+TCrO7xuSTZfYmmwVuWo3w5gCY+mSIAnqltnOzaOOTcly75xvO0WYpVk7nJdnR2tvSi0wfrQPDrIg/uzhX7p0SnDqijmBU4QaNez/TNKiFxy69dAzt0RSotlQzqkDbyVKhhv9a4eY8h3pXi4UMftKEj4FAKczxLImkukJXuOn5NN15/Q+le0rJVBC60/xjKIVzltEsMN6qjWD0lQjey7WEL+4pGhCVuWY5KzuZjFvgqszuJTFz7lo+bcHiceldJtea8/fa02eTRObZvdLxbWC9ZfFY0IhpOVKfcLdno/ddDMNMQMi5kMrJ8MZZ/PcW1w5n7MMGWPGCla1kOaC55AL0QYSMGRVEZqgU9wXI5M7sHGZKGM4mWxkbEJYBkpI/p3GyxWgV6v33ZWlsz65TqlNrR1gCLaoFCm7Sif8NqPBZUAONHYon0roXhin/DyEanS93WV6i6FC1Wisscjq2AcvnOlgTo/5nN/1QsMbjNumuMGo37sqjRqlXoPb8zEUbAhhztYuJjEfQ2Rd8="
+ }
+ ]
+ }
+ */
+
+ // Store the returned result into caches:
+ AString PlayerName = root.get("name", "").asString();
+ if (PlayerName.empty())
+ {
+ // No valid playername, bail out
+ return;
+ }
+ Json::Value Properties = root.get("properties", "");
+ Int64 Now = time(NULL);
+ {
+ cCSLock Lock(m_CSUUIDToProfile);
+ m_UUIDToProfile[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now);
+ }
+ {
+ cCSLock Lock(m_CSUUIDToName);
+ m_UUIDToName[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now);
+ }
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now);
+ }
+ NotifyNameUUID(PlayerName, a_UUID);
+}
+
+
+
+
+
+void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
+{
+ // Notify the rank manager:
+ cCSLock Lock(m_CSRankMgr);
+ if (m_RankMgr != NULL)
+ {
+ m_RankMgr->NotifyNameUUID(a_PlayerName, a_UUID);
+ }
+}
+
+
+
+
+
+void cMojangAPI::Update(void)
+{
+ Int64 LimitDateTime = time(NULL) - MAX_AGE;
+
+ // Re-query all playernames that are stale:
+ AStringVector PlayerNames;
+ {
+ cCSLock Lock(m_CSNameToUUID);
+ for (cProfileMap::const_iterator itr = m_NameToUUID.begin(), end = m_NameToUUID.end(); itr != end; ++itr)
+ {
+ if (itr->second.m_DateTime < LimitDateTime)
+ {
+ PlayerNames.push_back(itr->first);
+ }
+ } // for itr - m_NameToUUID[]
+ }
+ if (!PlayerNames.empty())
+ {
+ LOG("cMojangAPI: Updating name-to-uuid cache for %u names", (unsigned)PlayerNames.size());
+ QueryNamesToUUIDs(PlayerNames);
+ }
+
+ // Re-query all profiles that are stale:
+ AStringVector ProfileUUIDs;
+ {
+ cCSLock Lock(m_CSUUIDToProfile);
+ for (cProfileMap::const_iterator itr = m_UUIDToProfile.begin(), end = m_UUIDToProfile.end(); itr != end; ++itr)
+ {
+ if (itr->second.m_DateTime < LimitDateTime)
+ {
+ ProfileUUIDs.push_back(itr->first);
+ }
+ } // for itr - m_UUIDToProfile[]
+ }
+ if (!ProfileUUIDs.empty())
+ {
+ LOG("cMojangAPI: Updating uuid-to-profile cache for %u uuids", (unsigned)ProfileUUIDs.size());
+ for (AStringVector::const_iterator itr = ProfileUUIDs.begin(), end = ProfileUUIDs.end(); itr != end; ++itr)
+ {
+ QueryUUIDToProfile(*itr);
+ }
+ }
+}
+
+
+
+
diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h
new file mode 100644
index 000000000..fa4c16e4e
--- /dev/null
+++ b/src/Protocol/MojangAPI.h
@@ -0,0 +1,226 @@
+
+// MojangAPI.h
+
+// Declares the cMojangAPI class representing the various API points provided by Mojang's webservices, and a cache for their results
+
+
+
+
+
+#pragma once
+
+#include <time.h>
+
+
+
+
+
+// fwd: ../RankManager.h"
+class cRankManager;
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+class cMojangAPI
+{
+public:
+ // tolua_end
+
+ cMojangAPI(void);
+ ~cMojangAPI();
+
+ /** Initializes the API; reads the settings from the specified ini file.
+ Loads cached results from disk. */
+ void Start(cIniFile & a_SettingsIni);
+
+ /** Connects to the specified server using SSL, sends the given request and receives the response.
+ Checks Mojang certificates using the hard-coded Starfield root CA certificate.
+ Returns true if all was successful, false on failure. */
+ static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response);
+
+ /** Normalizes the given UUID to its short form (32 bytes, no dashes, lowercase).
+ Logs a warning and returns empty string if not a UUID.
+ Note: only checks the string's length, not the actual content. */
+ static AString MakeUUIDShort(const AString & a_UUID);
+
+ /** Normalizes the given UUID to its dashed form (36 bytes, 4 dashes, lowercase).
+ Logs a warning and returns empty string if not a UUID.
+ Note: only checks the string's length, not the actual content. */
+ static AString MakeUUIDDashed(const AString & a_UUID);
+
+ /** Converts a player name into a UUID.
+ The UUID will be empty on error.
+ If a_UseOnlyCached is true, the function only consults the cached values.
+ If a_UseOnlyCached is false and the name is not found in the cache, it is looked up online, which is a blocking
+ operation, do not use this in world-tick thread!
+ If you have multiple names to resolve, use the GetUUIDsFromPlayerNames() function, it uses a single request for multiple names. */
+ AString GetUUIDFromPlayerName(const AString & a_PlayerName, bool a_UseOnlyCached = false);
+
+ /** Converts a UUID into a playername.
+ The returned playername will be empty on error.
+ Both short and dashed UUID formats are accepted.
+ Uses both m_UUIDToName and m_UUIDToProfile to search for the value. Uses m_UUIDToProfile for cache.
+ If a_UseOnlyCached is true, the function only consults the cached values.
+ If a_UseOnlyCached is false and the name is not found in the cache, it is looked up online, which is a blocking
+ operation, do not use this in world-tick thread! */
+ AString GetPlayerNameFromUUID(const AString & a_UUID, bool a_UseOnlyCached = false);
+
+ /** Converts the player names into UUIDs.
+ a_PlayerName[idx] will be converted to UUID and returned as idx-th value
+ The UUID will be empty on error.
+ If a_UseOnlyCached is true, only the cached values are returned.
+ If a_UseOnlyCached is false, the names not found in the cache are looked up online, which is a blocking
+ operation, do not use this in world-tick thread! */
+ AStringVector GetUUIDsFromPlayerNames(const AStringVector & a_PlayerName, bool a_UseOnlyCached = false);
+
+ /** Called by the Authenticator to add a PlayerName -> UUID mapping that it has received from
+ authenticating a user. This adds the cache item and "refreshes" it if existing, adjusting its datetime
+ stamp to now. */
+ void AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const AString & a_UUID);
+
+ /** Called by the Authenticator to add a profile that it has received from authenticating a user. Adds
+ the profile to the respective mapping caches and updtes their datetime stamp to now. */
+ void AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties);
+
+ /** Sets the m_RankMgr that is used for name-uuid notifications. Accepts NULL to remove the binding. */
+ void SetRankManager(cRankManager * a_RankManager) { m_RankMgr = a_RankManager; }
+
+protected:
+ /** The thread that periodically checks for stale data and re-queries it from the server. */
+ class cUpdateThread;
+
+
+ /** Holds data for a single player profile. */
+ struct sProfile
+ {
+ AString m_PlayerName; // Case-correct playername
+ AString m_UUID; // Short lowercased UUID
+ AString m_Textures; // The Textures field of the profile properties
+ AString m_TexturesSignature; // The signature of the Textures field of the profile properties
+ Int64 m_DateTime; // UNIXtime of the profile lookup
+
+ /** Default constructor for the container's sake. */
+ sProfile(void) :
+ m_PlayerName(),
+ m_UUID(),
+ m_Textures(),
+ m_TexturesSignature(),
+ m_DateTime(time(NULL))
+ {
+ }
+
+ /** Constructor for the storage creation. */
+ sProfile(
+ const AString & a_PlayerName,
+ const AString & a_UUID,
+ const AString & a_Textures,
+ const AString & a_TexturesSignature,
+ Int64 a_DateTime
+ ) :
+ m_PlayerName(a_PlayerName),
+ m_UUID(a_UUID),
+ m_Textures(a_Textures),
+ m_TexturesSignature(a_TexturesSignature),
+ m_DateTime(a_DateTime)
+ {
+ }
+
+ /** Constructor that parses the values from the Json profile. */
+ sProfile(
+ const AString & a_PlayerName,
+ const AString & a_UUID,
+ const Json::Value & a_Properties,
+ Int64 a_DateTime
+ );
+ };
+ typedef std::map<AString, sProfile> cProfileMap;
+
+
+ /** The server to connect to when converting player names to UUIDs. For example "api.mojang.com". */
+ AString m_NameToUUIDServer;
+
+ /** The URL to use for converting player names to UUIDs, without server part.
+ For example "/profiles/page/1". */
+ AString m_NameToUUIDAddress;
+
+ /** The server to connect to when converting UUID to profile. For example "sessionserver.mojang.com". */
+ AString m_UUIDToProfileServer;
+
+ /** The URL to use for converting UUID to profile, without the server part.
+ Will replace %UUID% with the actual UUID. For example "session/minecraft/profile/%UUID%?unsigned=false". */
+ AString m_UUIDToProfileAddress;
+
+ /** Cache for the Name-to-UUID lookups. The map key is lowercased PlayerName. Protected by m_CSNameToUUID. */
+ cProfileMap m_NameToUUID;
+
+ /** Protects m_NameToUUID against simultaneous multi-threaded access. */
+ cCriticalSection m_CSNameToUUID;
+
+ /** Cache for the Name-to-UUID lookups. The map key is lowercased short UUID. Protected by m_CSUUIDToName. */
+ cProfileMap m_UUIDToName;
+
+ /** Protects m_UUIDToName against simultaneous multi-threaded access. */
+ cCriticalSection m_CSUUIDToName;
+
+ /** Cache for the UUID-to-profile lookups. The map key is lowercased short UUID.
+ Protected by m_CSUUIDToProfile. */
+ cProfileMap m_UUIDToProfile;
+
+ /** Protects m_UUIDToProfile against simultaneous multi-threaded access. */
+ cCriticalSection m_CSUUIDToProfile;
+
+ /** The rank manager that is notified of the name-uuid pairings. May be NULL. Protected by m_CSRankMgr. */
+ cRankManager * m_RankMgr;
+
+ /** Protects m_RankMgr agains simultaneous multi-threaded access. */
+ cCriticalSection m_CSRankMgr;
+
+ /** The thread that periodically updates the stale data in the DB from the Mojang servers. */
+ SharedPtr<cUpdateThread> m_UpdateThread;
+
+
+ /** Loads the caches from a disk storage. */
+ void LoadCachesFromDisk(void);
+
+ /** Saves the caches to a disk storage. */
+ void SaveCachesToDisk(void);
+
+ /** Makes sure all specified names are in the m_PlayerNameToUUID cache. Downloads any missing ones from Mojang API servers.
+ Names that are not valid are not added into the cache.
+ ASSUMEs that a_PlayerNames contains lowercased player names. */
+ void CacheNamesToUUIDs(const AStringVector & a_PlayerNames);
+
+ /** Queries all the specified names and stores them into the m_PlayerNameToUUID cache.
+ Names that are not valid are not added into the cache.
+ ASSUMEs that a_PlayerNames contans lowercased player names.
+ For performance reasons takes a non-const reference and modifies the list given to it, until empty. */
+ void QueryNamesToUUIDs(AStringVector & a_PlayerNames);
+
+ /** Makes sure the specified UUID is in the m_UUIDToProfile cache. If missing, downloads it from Mojang API servers.
+ UUIDs that are not valid will not be added into the cache.
+ ASSUMEs that a_UUID is a lowercased short UUID. */
+ void CacheUUIDToProfile(const AString & a_UUID);
+
+ /** Queries the specified UUID's profile and stores it in the m_UUIDToProfile cache. If already present, updates the cache entry.
+ UUIDs that are not valid will not be added into the cache.
+ ASSUMEs that a_UUID is a lowercased short UUID. */
+ void QueryUUIDToProfile(const AString & a_UUID);
+
+ /** Called for each name-uuid pairing that is discovered.
+ If assigned, notifies the m_RankManager of the event. */
+ void NotifyNameUUID(const AString & a_PlayerName, const AString & a_PlayerUUID);
+
+ /** Updates the stale values in the DB from the Mojang servers. Called from the cUpdateThread, blocks on the HTTPS API calls. */
+ void Update(void);
+} ; // tolua_export
+
+
+
+
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index d110f2af9..7225f663d 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -50,82 +50,87 @@ public:
m_Client(a_Client)
{
}
+
virtual ~cProtocol() {}
/// Called when client sends some data
virtual void DataReceived(const char * a_Data, size_t a_Size) = 0;
// Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
- virtual void SendChat (const AString & a_Message) = 0;
- virtual void SendChat (const cCompositeChat & a_Message) = 0;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
- virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) = 0;
- virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
- virtual void SendDisconnect (const AString & a_Reason) = 0;
- virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
- virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) = 0;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
- virtual void SendEntityLook (const cEntity & a_Entity) = 0;
- virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
- virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
- virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
- virtual void SendGameMode (eGameMode a_GameMode) = 0;
- virtual void SendHealth (void) = 0;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
- virtual void SendKeepAlive (int a_PingID) = 0;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
- virtual void SendLoginSuccess (void) = 0;
- virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0;
- virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0;
- virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0;
- virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
- virtual void SendPlayerAbilities (void) = 0;
- virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0;
- virtual void SendParticleEffect (const AString & a_SoundName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) = 0;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) = 0;
- virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
- virtual void SendPlayerMoveLook (void) = 0;
- virtual void SendPlayerPosition (void) = 0;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
- virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
- virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
- virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0;
- virtual void SendExperience (void) = 0;
- virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
- virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
- virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0;
- virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0;
- virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) = 0;
- virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
- virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
- virtual void SendStatistics (const cStatManager & a_Manager) = 0;
- virtual void SendTabCompletionResults(const AStringVector & a_Results) = 0;
- virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
- virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0;
- virtual 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) = 0;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
- virtual void SendWeather (eWeather a_Weather) = 0;
- virtual void SendWholeInventory (const cWindow & a_Window) = 0;
- virtual void SendWindowClose (const cWindow & a_Window) = 0;
- virtual void SendWindowOpen (const cWindow & a_Window) = 0;
- virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) = 0;
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) = 0;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) = 0;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
+ virtual void SendChat (const AString & a_Message) = 0;
+ virtual void SendChat (const cCompositeChat & a_Message) = 0;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
+ virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) = 0;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
+ virtual void SendDisconnect (const AString & a_Reason) = 0;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) = 0;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityLook (const cEntity & a_Entity) = 0;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) = 0;
+ virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) = 0;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
+ virtual void SendGameMode (eGameMode a_GameMode) = 0;
+ virtual void SendHealth (void) = 0;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
+ virtual void SendKeepAlive (int a_PingID) = 0;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendLoginSuccess (void) = 0;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) = 0;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) = 0;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0;
+ virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
+ virtual void SendPlayerAbilities (void) = 0;
+ virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0;
+ virtual void SendParticleEffect (const AString & a_SoundName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) = 0;
+ virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) = 0;
+ virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) = 0;
+ virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) = 0;
+ virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) = 0;
+ virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) = 0;
+ virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+)
+ virtual void SendPlayerMoveLook (void) = 0;
+ virtual void SendPlayerPosition (void) = 0;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
+ virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0;
+ virtual void SendExperience (void) = 0;
+ virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0;
+ virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) = 0;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
+ virtual void SendSpawnMob (const cMonster & a_Mob) = 0;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) = 0;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) = 0;
+ virtual void SendStatistics (const cStatManager & a_Manager) = 0;
+ virtual void SendTabCompletionResults (const AStringVector & a_Results) = 0;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) = 0;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) = 0;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0;
+ virtual 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) = 0;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
+ virtual void SendWeather (eWeather a_Weather) = 0;
+ virtual void SendWholeInventory (const cWindow & a_Window) = 0;
+ virtual void SendWindowClose (const cWindow & a_Window) = 0;
+ virtual void SendWindowOpen (const cWindow & a_Window) = 0;
+ virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) = 0;
/// Returns the ServerID used for authentication through session.minecraft.net
virtual AString GetAuthServerID(void) = 0;
@@ -133,108 +138,9 @@ public:
protected:
cClientHandle * m_Client;
cCriticalSection m_CSPacket; // Each SendXYZ() function must acquire this CS in order to send the whole packet at once
-
+
/// A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it
virtual void SendData(const char * a_Data, size_t a_Size) = 0;
-
- /// Called after writing each packet, enables descendants to flush their buffers
- virtual void Flush(void) {}
-
- // Helpers for writing partial packet data, write using SendData()
- void WriteByte(Byte a_Value)
- {
- SendData((const char *)&a_Value, 1);
- }
-
- void WriteChar(char a_Value)
- {
- SendData(&a_Value, 1);
- }
-
- void WriteShort(short a_Value)
- {
- u_short Value = htons((u_short)a_Value);
- SendData((const char *)&Value, 2);
- }
-
- /*
- void WriteShort(unsigned short a_Value)
- {
- a_Value = htons(a_Value);
- SendData((const char *)&a_Value, 2);
- }
- */
-
- void WriteInt(int a_Value)
- {
- u_long Value = htonl((u_long)a_Value);
- SendData((const char *)&Value, 4);
- }
-
- void WriteUInt(unsigned int a_Value)
- {
- a_Value = htonl(a_Value);
- SendData((const char *)&a_Value, 4);
- }
-
- void WriteInt64 (Int64 a_Value)
- {
- UInt64 Value = HostToNetwork8(&a_Value);
- SendData((const char *)&Value, 8);
- }
-
- void WriteFloat (float a_Value)
- {
- UInt32 val = HostToNetwork4(&a_Value);
- SendData((const char *)&val, 4);
- }
-
- void WriteDouble(double a_Value)
- {
- UInt64 val = HostToNetwork8(&a_Value);
- SendData((const char *)&val, 8);
- }
-
- void WriteString(const AString & a_Value)
- {
- AString UTF16;
- UTF8ToRawBEUTF16(a_Value.c_str(), a_Value.length(), UTF16);
- WriteShort((short)(UTF16.size() / 2));
- SendData(UTF16.data(), UTF16.size());
- }
-
- void WriteBool(bool a_Value)
- {
- WriteByte(a_Value ? 1 : 0);
- }
-
- void WriteVectorI(const Vector3i & a_Vector)
- {
- WriteInt(a_Vector.x);
- WriteInt(a_Vector.y);
- WriteInt(a_Vector.z);
- }
-
- void WriteVarInt(UInt32 a_Value)
- {
- // A 32-bit integer can be encoded by at most 5 bytes:
- unsigned char b[5];
- size_t idx = 0;
- do
- {
- b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
- a_Value = a_Value >> 7;
- idx++;
- } while (a_Value > 0);
-
- SendData((const char *)b, idx);
- }
-
- void WriteVarUTF8String(const AString & a_String)
- {
- WriteVarInt((UInt32)a_String.size());
- SendData(a_String.data(), a_String.size());
- }
} ;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
deleted file mode 100644
index 538d31642..000000000
--- a/src/Protocol/Protocol125.cpp
+++ /dev/null
@@ -1,2128 +0,0 @@
-
-// Protocol125.cpp
-
-// Implements the cProtocol125 class representing the release 1.2.5 protocol (#29)
-/*
-Documentation:
- - protocol: http://wiki.vg/wiki/index.php?title=Protocol&oldid=2513
- - session handling: http://wiki.vg/wiki/index.php?title=Session&oldid=2262
- - slot format: http://wiki.vg/wiki/index.php?title=Slot_Data&oldid=2152
-*/
-
-#include "Globals.h"
-
-#include "Protocol125.h"
-
-#include "../ClientHandle.h"
-#include "../World.h"
-#include "ChunkDataSerializer.h"
-#include "../Entities/Entity.h"
-#include "../Entities/ExpOrb.h"
-#include "../Mobs/Monster.h"
-#include "../Entities/Pickup.h"
-#include "../Entities/Player.h"
-#include "../ChatColor.h"
-#include "../UI/Window.h"
-#include "../Root.h"
-#include "../Server.h"
-
-#include "../Entities/ArrowEntity.h"
-#include "../Entities/Minecart.h"
-#include "../Entities/FallingBlock.h"
-
-#include "../Mobs/IncludeAllMonsters.h"
-
-#include "../CompositeChat.h"
-
-
-
-
-
-enum
-{
- PACKET_KEEP_ALIVE = 0x00,
- PACKET_LOGIN = 0x01,
- PACKET_HANDSHAKE = 0x02,
- PACKET_CHAT = 0x03,
- PACKET_UPDATE_TIME = 0x04,
- PACKET_ENTITY_EQUIPMENT = 0x05,
- PACKET_USE_ENTITY = 0x07,
- PACKET_UPDATE_HEALTH = 0x08,
- PACKET_RESPAWN = 0x09,
- PACKET_PLAYER_ON_GROUND = 0x0a,
- PACKET_PLAYER_POS = 0x0b,
- PACKET_PLAYER_LOOK = 0x0c,
- PACKET_PLAYER_MOVE_LOOK = 0x0d,
- PACKET_BLOCK_DIG = 0x0e,
- PACKET_BLOCK_PLACE = 0x0f,
- PACKET_SLOT_SELECTED = 0x10,
- PACKET_USE_BED = 0x11,
- PACKET_ANIMATION = 0x12,
- PACKET_PACKET_ENTITY_ACTION = 0x13,
- PACKET_PLAYER_SPAWN = 0x14,
- PACKET_PICKUP_SPAWN = 0x15,
- PACKET_COLLECT_PICKUP = 0x16,
- PACKET_SPAWN_OBJECT = 0x17,
- PACKET_SPAWN_MOB = 0x18,
- PACKET_ENTITY_VELOCITY = 0x1c,
- PACKET_DESTROY_ENTITY = 0x1d,
- PACKET_ENTITY = 0x1e,
- PACKET_ENT_REL_MOVE = 0x1f,
- PACKET_ENT_LOOK = 0x20,
- PACKET_ENT_REL_MOVE_LOOK = 0x21,
- PACKET_ENT_TELEPORT = 0x22,
- PACKET_ENT_HEAD_LOOK = 0x23,
- PACKET_ENT_STATUS = 0x26,
- PACKET_ATTACH_ENTITY = 0x27,
- PACKET_METADATA = 0x28,
- PACKET_ENTITY_EFFECT = 0x29,
- PACKET_SPAWN_EXPERIENCE_ORB = 0x1A,
- PACKET_REMOVE_ENTITY_EFFECT = 0x2a,
- PACKET_EXPERIENCE = 0x2b,
- PACKET_PRE_CHUNK = 0x32,
- PACKET_MAP_CHUNK = 0x33,
- PACKET_MULTI_BLOCK = 0x34,
- PACKET_BLOCK_CHANGE = 0x35,
- PACKET_BLOCK_ACTION = 0x36,
- PACKET_EXPLOSION = 0x3C,
- PACKET_SOUND_EFFECT = 0x3e,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
- PACKET_CHANGE_GAME_STATE = 0x46,
- PACKET_THUNDERBOLT = 0x47,
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_WINDOW_CLOSE = 0x65,
- PACKET_WINDOW_CLICK = 0x66,
- PACKET_INVENTORY_SLOT = 0x67,
- PACKET_INVENTORY_WHOLE = 0x68,
- PACKET_WINDOW_PROPERTY = 0x69,
- PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
- PACKET_ENCHANT_ITEM = 0x6C,
- PACKET_UPDATE_SIGN = 0x82,
- PACKET_ITEM_DATA = 0x83,
- PACKET_INCREMENT_STATISTIC = 0xC8,
- PACKET_PLAYER_LIST_ITEM = 0xC9,
- PACKET_PLAYER_ABILITIES = 0xca,
- PACKET_PLUGIN_MESSAGE = 0xfa,
- PACKET_PING = 0xfe,
- PACKET_DISCONNECT = 0xff
-} ;
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-typedef unsigned char Byte;
-
-
-
-
-
-cProtocol125::cProtocol125(cClientHandle * a_Client) :
- super(a_Client),
- m_ReceivedData(32 KiB),
- m_LastSentDimension(dimNotSet)
-{
-}
-
-
-
-
-
-void cProtocol125::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ATTACH_ENTITY);
- WriteInt(a_Entity.GetUniqueID());
- WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
-{
- UNUSED(a_BlockType);
-
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_ACTION);
- WriteInt (a_BlockX);
- WriteShort((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteChar (a_Byte1);
- WriteChar (a_Byte2);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
-{
- // Not supported in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_BLOCK_CHANGE);
- WriteInt (a_BlockX);
- WriteByte((unsigned char)a_BlockY);
- WriteInt (a_BlockZ);
- WriteByte(a_BlockType);
- WriteByte(a_BlockMeta);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
-{
- cCSLock Lock(m_CSPacket);
- if (a_Changes.size() == 1)
- {
- // Special packet for single-block changes
- const sSetBlock & blk = a_Changes.front();
- SendBlockChange(a_ChunkX * cChunkDef::Width + blk.x, blk.y, a_ChunkZ * cChunkDef::Width + blk.z, blk.BlockType, blk.BlockMeta);
- return;
- }
-
- WriteByte (PACKET_MULTI_BLOCK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- WriteShort((short)a_Changes.size());
- WriteUInt ((UInt32)(4 * a_Changes.size()));
- for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
- {
- UInt32 Coords = ((UInt32)itr->y) | ((UInt32)(itr->z << 8)) | ((UInt32)(itr->x << 12));
- UInt32 Blocks = ((UInt32)itr->BlockMeta) | ((UInt32)(itr->BlockType << 4));
- WriteUInt(Coords << 16 | Blocks);
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendChat(const AString & a_Message)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_CHAT);
- WriteString(a_Message);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendChat(const cCompositeChat & a_Message)
-{
- // This version doesn't support composite messages, just extract each part's text and use it:
-
- // Send the message:
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_CHAT);
- WriteString(a_Message.ExtractText());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
-{
- cCSLock Lock(m_CSPacket);
-
- // Send the pre-chunk:
- SendPreChunk(a_ChunkX, a_ChunkZ, true);
-
- // Send the chunk data:
- AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5);
- WriteByte(PACKET_MAP_CHUNK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- SendData(Serialized.data(), Serialized.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COLLECT_PICKUP);
- WriteInt (a_Entity.GetUniqueID());
- WriteInt (a_Player.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendDestroyEntity(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_DESTROY_ENTITY);
- WriteInt (a_Entity.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendDisconnect(const AString & a_Reason)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte ((unsigned char)PACKET_DISCONNECT);
- WriteString(a_Reason);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- // This protocol version doesn't support this packet, sign editor is invoked by the client automatically
- UNUSED(a_BlockX);
- UNUSED(a_BlockY);
- UNUSED(a_BlockZ);
-}
-
-
-
-
-
-void cProtocol125::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENTITY_EFFECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte ((Byte)a_EffectID);
- WriteByte ((Byte)a_Amplifier);
- WriteShort(a_Duration);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENTITY_EQUIPMENT);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort(a_SlotNum);
- WriteShort(a_Item.m_ItemType);
- WriteShort(a_Item.m_ItemDamage);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityHeadLook(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_HEAD_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar((char)((a_Entity.GetHeadYaw() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityLook(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar((char)((a_Entity.GetYaw() / 360.f) * 256));
- WriteChar((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityMetadata(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_METADATA);
- WriteInt (a_Entity.GetUniqueID());
-
- WriteCommonMetadata(a_Entity);
- if (a_Entity.IsMob())
- {
- WriteMobMetadata(((const cMonster &)a_Entity));
- }
- else
- {
- WriteEntityMetadata(a_Entity);
- }
- WriteByte(0x7f);
-
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityProperties(const cEntity & a_Entity)
-{
- // Not supported in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_REL_MOVE);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_RelX);
- WriteChar(a_RelY);
- WriteChar(a_RelZ);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_REL_MOVE_LOOK);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_RelX);
- WriteChar(a_RelY);
- WriteChar(a_RelZ);
- WriteChar((char)((a_Entity.GetYaw() / 360.f) * 256));
- WriteChar((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityStatus(const cEntity & a_Entity, char a_Status)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENT_STATUS);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_Status);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityVelocity(const cEntity & a_Entity)
-{
- ASSERT(a_Entity.GetUniqueID() != m_Client->GetPlayer()->GetUniqueID()); // Must not send for self
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ENTITY_VELOCITY);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort((short) (a_Entity.GetSpeedX() * 400)); // 400 = 8000 / 20
- WriteShort((short) (a_Entity.GetSpeedY() * 400));
- WriteShort((short) (a_Entity.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_EXPLOSION);
- WriteDouble (a_BlockX);
- WriteDouble (a_BlockY);
- WriteDouble (a_BlockZ);
- WriteFloat (a_Radius);
- WriteInt ((Int32)a_BlocksAffected.size());
- int BlockX = (int)a_BlockX;
- int BlockY = (int)a_BlockY;
- int BlockZ = (int)a_BlockZ;
- for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(); itr != a_BlocksAffected.end(); ++itr)
- {
- WriteByte((Byte)(itr->x - BlockX));
- WriteByte((Byte)(itr->y - BlockY));
- WriteByte((Byte)(itr->z - BlockZ));
- }
- WriteFloat((float)a_PlayerMotion.x);
- WriteFloat((float)a_PlayerMotion.y);
- WriteFloat((float)a_PlayerMotion.z);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendGameMode(eGameMode a_GameMode)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(3);
- WriteChar((char)a_GameMode);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendHandshake(const AString & a_ConnectionHash)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_HANDSHAKE);
- WriteString(a_ConnectionHash);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendHealth(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_HEALTH);
- cPlayer * Player = m_Client->GetPlayer();
- WriteShort((short)Player->GetHealth());
- WriteShort((short)Player->GetFoodLevel());
- WriteFloat((float)Player->GetFoodSaturationLevel());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_INVENTORY_SLOT);
- WriteChar (a_WindowID);
- WriteShort(a_SlotNum);
- WriteItem (a_Item);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendKeepAlive(int a_PingID)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_KEEP_ALIVE);
- WriteInt (a_PingID);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
-{
- UNUSED(a_World);
- cCSLock Lock(m_CSPacket);
-
- WriteByte (PACKET_LOGIN);
- WriteInt (a_Player.GetUniqueID()); // EntityID of the player
- WriteString(""); // Username, not used
- WriteString("default"); // Level type
- WriteInt ((int)a_Player.GetGameMode());
- WriteInt ((int)(a_World.GetDimension()));
- WriteByte (2); // TODO: Difficulty
- WriteByte (0); // Unused
- WriteByte (60); // Client list width or something
- Flush();
- m_LastSentDimension = a_World.GetDimension();
-}
-
-
-
-
-
-void cProtocol125::SendLoginSuccess(void)
-{
- // Not supported in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
-{
- cCSLock Lock(m_CSPacket);
-
- WriteByte (PACKET_ITEM_DATA);
- WriteShort(E_ITEM_MAP);
- WriteShort((short)a_ID);
- WriteShort((short)(3 + a_Length));
-
- WriteByte(0);
- WriteChar((char)a_X);
- WriteChar((char)a_Y);
-
- SendData((const char *)a_Colors, a_Length);
-
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
-{
- cCSLock Lock(m_CSPacket);
-
- WriteByte (PACKET_ITEM_DATA);
- WriteShort(E_ITEM_MAP);
- WriteShort((short)a_ID);
- WriteShort((short)(1 + (3 * a_Decorators.size())));
-
- WriteByte(1);
-
- for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
- {
- WriteByte((Byte)(it->GetType() << 4) | (it->GetRot() & 0xf));
- WriteByte((Byte)it->GetPixelX());
- WriteByte((Byte)it->GetPixelZ());
- }
-
- Flush();
-}
-
-
-
-
-
-
-void cProtocol125::SendMapInfo(int a_ID, unsigned int a_Scale)
-{
- // This protocol doesn't support such message
- UNUSED(a_ID);
- UNUSED(a_Scale);
-}
-
-
-
-
-
-void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PICKUP_SPAWN);
- WriteInt (a_Pickup.GetUniqueID());
- const cItem & Item = a_Pickup.GetItem();
- WriteShort (Item.m_ItemType);
- WriteChar (Item.m_ItemCount);
- WriteShort (Item.m_ItemDamage);
- WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
- WriteByte ((char)(a_Pickup.GetSpeedX() * 8));
- WriteByte ((char)(a_Pickup.GetSpeedY() * 8));
- WriteByte ((char)(a_Pickup.GetSpeedZ() * 8));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ANIMATION);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_Animation);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
-{
- // Not supported by this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendPaintingSpawn(const cPainting & a_Painting)
-{
- // Not implemented in this protocol version
- UNUSED(a_Painting);
-}
-
-
-
-
-
-void cProtocol125::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
-{
- cCSLock Lock(m_CSPacket);
- AString PlayerName(a_Player.GetColor());
- PlayerName.append(a_Player.GetName());
- if (PlayerName.length() > 14)
- {
- PlayerName.erase(14);
- }
- PlayerName += cChatColor::White;
-
- WriteByte ((unsigned char)PACKET_PLAYER_LIST_ITEM);
- WriteString(PlayerName);
- WriteBool (a_IsOnline);
- WriteShort (a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerMaxSpeed(void)
-{
- // Not supported by this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendPlayerMoveLook(void)
-{
- cCSLock Lock(m_CSPacket);
-
- /*
- LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d",
- m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
- );
- */
-
- WriteByte(PACKET_PLAYER_MOVE_LOOK);
- cPlayer * Player = m_Client->GetPlayer();
- WriteDouble(Player->GetPosX());
- WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
- WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
- WriteDouble(Player->GetPosZ());
- WriteFloat ((float)(Player->GetYaw()));
- WriteFloat ((float)(Player->GetPitch()));
- WriteBool (Player->IsOnGround());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPlayerPosition(void)
-{
- cCSLock Lock(m_CSPacket);
- LOGD("Ignore send PlayerPos"); // PlayerPos is a C->S packet only now
-}
-
-
-
-
-
-void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
-{
- const cItem & HeldItem = a_Player.GetEquippedItem();
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PLAYER_SPAWN);
- WriteInt (a_Player.GetUniqueID());
- WriteString(a_Player.GetName());
- WriteInt ((int)(a_Player.GetPosX() * 32));
- WriteInt ((int)(a_Player.GetPosY() * 32));
- WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteChar ((char)((a_Player.GetYaw() / 360.f) * 256));
- WriteChar ((char)((a_Player.GetPitch() / 360.f) * 256));
- WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_PLUGIN_MESSAGE);
- WriteString(a_Channel);
- WriteShort((short)a_Message.size());
- SendData(a_Message.data(), a_Message.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_REMOVE_ENTITY_EFFECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar((char)a_EffectID);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
-{
- cCSLock Lock(m_CSPacket);
- if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
- {
- // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
- return;
- }
- cPlayer * Player = m_Client->GetPlayer();
- WriteByte (PACKET_RESPAWN);
- WriteInt ((int)(a_Dimension));
- WriteByte (2); // TODO: Difficulty; 2 = Normal
- WriteChar ((char)Player->GetGameMode());
- WriteShort (256); // Current world height
- WriteString("default");
- Flush();
- m_LastSentDimension = a_Dimension;
-}
-
-
-
-
-
-void cProtocol125::SendExperience(void)
-{
- cCSLock Lock(m_CSPacket);
- cPlayer * Player = m_Client->GetPlayer();
- WriteByte (PACKET_EXPERIENCE);
- WriteFloat (Player->GetXpPercentage());
- WriteShort (Player->GetXpLevel());
- WriteShort (Player->GetCurrentXp());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendExperienceOrb(const cExpOrb & a_ExpOrb)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_EXPERIENCE_ORB);
- WriteInt(a_ExpOrb.GetUniqueID());
- WriteInt((int) a_ExpOrb.GetPosX());
- WriteInt((int) a_ExpOrb.GetPosY());
- WriteInt((int) a_ExpOrb.GetPosZ());
- WriteShort((short)a_ExpOrb.GetReward());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
-{
- // This protocol version doesn't support such message
- UNUSED(a_Name);
- UNUSED(a_DisplayName);
- UNUSED(a_Mode);
-}
-
-
-
-
-
-void cProtocol125::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
-{
- // Not needed in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- // Not implemented in this protocol version
-}
-
-
-
-
-
-void cProtocol125::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
-{
- // This protocol version implements falling blocks using the spawn object / vehicle packet:
- SendSpawnObject(a_FallingBlock, 70, a_FallingBlock.GetBlockType(), 0, 0);
-}
-
-
-
-
-
-void cProtocol125::SendSpawnMob(const cMonster & a_Mob)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_MOB);
- WriteInt (a_Mob.GetUniqueID());
- WriteByte ((Byte)a_Mob.GetMobType());
- WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
- WriteByte (0);
- WriteByte (0);
- WriteByte (0);
-
- WriteCommonMetadata(a_Mob);
- WriteMobMetadata(a_Mob);
- WriteByte(0x7f);
-
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
-{
- UNUSED(a_Yaw);
- UNUSED(a_Pitch);
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_ObjectType);
- WriteInt ((int)(a_Entity.GetPosX() * 32));
- WriteInt ((int)(a_Entity.GetPosY() * 32));
- WriteInt ((int)(a_Entity.GetPosZ() * 32));
- WriteByte(a_Pitch);
- WriteByte(a_Yaw);
- WriteInt (a_ObjectData);
- if (a_ObjectData != 0)
- {
- WriteShort((short)(a_Entity.GetSpeedX() * 400));
- WriteShort((short)(a_Entity.GetSpeedY() * 400));
- WriteShort((short)(a_Entity.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_OBJECT);
- WriteInt (a_Vehicle.GetUniqueID());
- WriteChar (a_VehicleType);
- WriteInt ((int)(a_Vehicle.GetPosX() * 32));
- WriteInt ((int)(a_Vehicle.GetPosY() * 32));
- WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
- WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256));
- WriteInt (a_VehicleSubType);
- if (a_VehicleSubType != 0)
- {
- WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendStatistics(const cStatManager & a_Manager)
-{
- /* NOTE:
- Versions prior to minecraft 1.7 use an incremental statistic sync
- method. The current setup does not allow us to implement that, because
- of performance considerations.
- */
-#if 0
- for (unsigned int i = 0; i < (unsigned int)statCount; ++i)
- {
- StatValue Value = m_Manager->GetValue((eStatistic) i);
-
- unsigned int StatID = cStatInfo::GetID((eStatistic) i);
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_INCREMENT_STATISTIC);
- WriteInt(StatID);
- WriteByte(Value); /* Can overflow! */
- Flush();
- }
-#endif
-}
-
-
-
-
-
-void cProtocol125::SendTabCompletionResults(const AStringVector & a_Results)
-{
- // This protocol version doesn't support tab completion
- UNUSED(a_Results);
-}
-
-
-
-
-
-void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENT_TELEPORT);
- WriteInt (a_Entity.GetUniqueID());
- WriteInt ((int)(floor(a_Entity.GetPosX() * 32)));
- WriteInt ((int)(floor(a_Entity.GetPosY() * 32)));
- WriteInt ((int)(floor(a_Entity.GetPosZ() * 32)));
- WriteChar ((char)((a_Entity.GetYaw() / 360.f) * 256));
- WriteChar ((char)((a_Entity.GetPitch() / 360.f) * 256));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_THUNDERBOLT);
- WriteInt (0x7fffffff); // Entity ID of the thunderbolt; we use a constant one
- WriteBool(true); // Unknown bool
- WriteInt (a_BlockX * 32);
- WriteInt (a_BlockY * 32);
- WriteInt (a_BlockZ * 32);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_TIME);
- // Use a_WorldAge for daycount, and a_TimeOfDay for the proper time of day:
- WriteInt64((24000 * (a_WorldAge / 24000)) + (a_TimeOfDay % 24000));
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- cCSLock Lock(m_CSPacket);
- SendPreChunk(a_ChunkX, a_ChunkZ, false);
-}
-
-
-
-
-
-void cProtocol125::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
-)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte ((unsigned char)PACKET_UPDATE_SIGN);
- WriteInt (a_BlockX);
- WriteShort ((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteString(a_Line1);
- WriteString(a_Line2);
- WriteString(a_Line3);
- WriteString(a_Line4);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_USE_BED);
- WriteInt (a_Entity.GetUniqueID());
- WriteByte(0); // Unknown byte only 0 has been observed
- WriteInt (a_BlockX);
- WriteByte((Byte)a_BlockY);
- WriteInt (a_BlockZ);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWeather(eWeather a_Weather)
-{
- cCSLock Lock(m_CSPacket);
- switch (a_Weather)
- {
- case eWeather_Sunny:
- {
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(2); // Stop rain
- WriteByte(0); // Unused
- Flush();
- break;
- }
-
- case eWeather_Rain:
- case eWeather_ThunderStorm:
- {
- WriteByte(PACKET_CHANGE_GAME_STATE);
- WriteByte(1); // Begin rain
- WriteByte(0); // Unused
- Flush();
- break;
- }
- }
-}
-
-
-
-
-
-void cProtocol125::SendWholeInventory(const cWindow & a_Window)
-{
- cCSLock Lock(m_CSPacket);
- cItems Slots;
- a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
- SendWindowSlots(a_Window.GetWindowID(), (int)Slots.size(), &(Slots[0]));
-}
-
-
-
-
-
-void cProtocol125::SendWindowClose(const cWindow & a_Window)
-{
- if (a_Window.GetWindowType() == cWindow::wtInventory)
- {
- // Do not send inventory-window-close
- return;
- }
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_WINDOW_CLOSE);
- WriteChar(a_Window.GetWindowID());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWindowOpen(const cWindow & a_Window)
-{
- if (a_Window.GetWindowType() < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteChar (a_Window.GetWindowID());
- WriteByte ((Byte)a_Window.GetWindowType());
- WriteString(a_Window.GetWindowTitle());
- WriteByte ((Byte)a_Window.GetNumNonInventorySlots());
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_PROPERTY);
- WriteChar (a_Window.GetWindowID());
- WriteShort(a_Property);
- WriteShort(a_Value);
- Flush();
-}
-
-
-
-
-
-AString cProtocol125::GetAuthServerID(void)
-{
- // http://wiki.vg/wiki/index.php?title=Session&oldid=2262
- // The server generates a random hash and that is used for all clients, unmodified
- return cRoot::Get()->GetServer()->GetServerID();
-}
-
-
-
-
-
-void cProtocol125::SendData(const char * a_Data, size_t a_Size)
-{
- m_Client->SendData(a_Data, a_Size);
-}
-
-
-
-
-
-void cProtocol125::DataReceived(const char * a_Data, size_t a_Size)
-{
- if (!m_ReceivedData.Write(a_Data, a_Size))
- {
- // Too much data in the incoming queue, report to caller:
- m_Client->PacketBufferFull();
- return;
- }
-
- // Parse and handle all complete packets in m_ReceivedData:
- while (m_ReceivedData.CanReadBytes(1))
- {
- unsigned char PacketType;
- m_ReceivedData.ReadByte(PacketType);
- switch (ParsePacket(PacketType))
- {
- case PARSE_UNKNOWN:
- {
- // An unknown packet has been received, notify the client and abort:
- m_Client->PacketUnknown(PacketType);
- return;
- }
- case PARSE_ERROR:
- {
- // An error occurred while parsing a known packet, notify the client and abort:
- m_Client->PacketError(PacketType);
- return;
- }
- case PARSE_INCOMPLETE:
- {
- // Incomplete packet, bail out and process with the next batch of data
- m_ReceivedData.ResetRead();
- return;
- }
- default:
- {
- // Packet successfully parsed, commit the read data and try again one more packet
- m_ReceivedData.CommitRead();
- break;
- }
- }
- }
-}
-
-
-
-
-
-int cProtocol125::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- default: return PARSE_UNKNOWN;
- case PACKET_ANIMATION: return ParseArmAnim();
- case PACKET_BLOCK_DIG: return ParseBlockDig();
- case PACKET_BLOCK_PLACE: return ParseBlockPlace();
- case PACKET_CHAT: return ParseChat();
- case PACKET_CREATIVE_INVENTORY_ACTION: return ParseCreativeInventoryAction();
- case PACKET_DISCONNECT: return ParseDisconnect();
- case PACKET_HANDSHAKE: return ParseHandshake();
- case PACKET_KEEP_ALIVE: return ParseKeepAlive();
- case PACKET_LOGIN: return ParseLogin();
- case PACKET_PACKET_ENTITY_ACTION: return ParseEntityAction();
- case PACKET_PING: return ParsePing();
- case PACKET_PLAYER_ABILITIES: return ParsePlayerAbilities();
- case PACKET_PLAYER_LOOK: return ParsePlayerLook();
- case PACKET_PLAYER_MOVE_LOOK: return ParsePlayerMoveLook();
- case PACKET_PLAYER_ON_GROUND: return ParsePlayerOnGround();
- case PACKET_PLAYER_POS: return ParsePlayerPosition();
- case PACKET_PLUGIN_MESSAGE: return ParsePluginMessage();
- case PACKET_RESPAWN: return ParseRespawn();
- case PACKET_SLOT_SELECTED: return ParseSlotSelected();
- case PACKET_UPDATE_SIGN: return ParseUpdateSign();
- case PACKET_USE_ENTITY: return ParseUseEntity();
- case PACKET_ENCHANT_ITEM: return ParseEnchantItem();
- case PACKET_WINDOW_CLICK: return ParseWindowClick();
- case PACKET_WINDOW_CLOSE: return ParseWindowClose();
- }
-}
-
-
-
-
-
-int cProtocol125::ParseArmAnim(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, Animation);
- m_Client->HandleAnimation(Animation);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseBlockDig(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, Status);
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
- m_Client->HandleLeftClick(PosX, PosY, PosZ, static_cast<eBlockFace>(BlockFace), Status);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseBlockPlace(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
-
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // 1.2.5 didn't have any cursor position, so use 8, 8, 8, so that halfslabs and stairs work correctly and the special value is recognizable.
- m_Client->HandleRightClick(PosX, PosY, PosZ, static_cast<eBlockFace>(BlockFace), 8, 8, 8, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseChat(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Message);
- m_Client->HandleChat(Message);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseCreativeInventoryAction(void)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
- m_Client->HandleCreativeInventory(SlotNum, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseDisconnect(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Reason);
- m_Client->HandleDisconnect(Reason);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseEntityAction(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, ActionID);
-
- switch (ActionID)
- {
- case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch
- case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch
- case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed
- case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting
- case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseHandshake(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
-
- AStringVector UserData = StringSplit(Username, ";"); // "FakeTruth;localhost:25565"
- if (UserData.empty())
- {
- m_Client->Kick("Did not receive username");
- return PARSE_OK;
- }
- m_Username = UserData[0];
-
- LOGD("HANDSHAKE %s", Username.c_str());
-
- if (!m_Client->HandleHandshake( m_Username))
- {
- return PARSE_OK; // Player is not allowed into the server
- }
-
- SendHandshake(cRoot::Get()->GetServer()->GetServerID());
- LOGD("User \"%s\" was sent a handshake response", m_Username.c_str());
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseKeepAlive(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, KeepAliveID);
- m_Client->HandleKeepAlive(KeepAliveID);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseLogin(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, ProtocolVersion);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
- HANDLE_PACKET_READ(ReadBEInt, int, ServerMode);
- HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
- HANDLE_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_PACKET_READ(ReadByte, Byte, WorldHeight);
- HANDLE_PACKET_READ(ReadByte, Byte, MaxPlayers);
-
- if (ProtocolVersion < 29)
- {
- m_Client->Kick("Your client is outdated!");
- return PARSE_OK;
- }
- else if (ProtocolVersion > 29)
- {
- m_Client->Kick("Your client version is higher than the server!");
- return PARSE_OK;
- }
-
- if (m_Username.compare(Username) != 0)
- {
- LOGWARNING("Login Username (\"%s\") does not match Handshake username (\"%s\") for client @ \"%s\", kicking",
- Username.c_str(),
- m_Username.c_str(),
- m_Client->GetIPString().c_str()
- );
- m_Client->Kick("Hacked client"); // Don't tell them why we don't want them
- return PARSE_OK;
- }
-
- m_Client->HandleLogin(ProtocolVersion, Username);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePing(void)
-{
- // Packet has no more data
- m_Client->HandlePing();
- return PARSE_OK;
-}
-
-
-
-
-
-
-int cProtocol125::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, Invulnerable);
- HANDLE_PACKET_READ(ReadBool, bool, IsFlying);
- HANDLE_PACKET_READ(ReadBool, bool, CanFly);
- HANDLE_PACKET_READ(ReadBool, bool, InstaMine);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerLook(void)
-{
- HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
- HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- m_Client->HandlePlayerLook(Rotation, Pitch, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerMoveLook(void)
-{
- HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
- HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
- HANDLE_PACKET_READ(ReadBEFloat, float, Rotation);
- HANDLE_PACKET_READ(ReadBEFloat, float, Pitch);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- // LOGD("Recv PML: {%0.2f, %0.2f, %0.2f}, Stance %0.2f, Gnd: %d", PosX, PosY, PosZ, Stance, IsOnGround ? 1 : 0);
- m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, Stance, Rotation, Pitch, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerOnGround(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- // TODO: m_Client->HandleFlying(IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePlayerPosition(void)
-{
- HANDLE_PACKET_READ(ReadBEDouble, double, PosX);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosY);
- HANDLE_PACKET_READ(ReadBEDouble, double, Stance);
- HANDLE_PACKET_READ(ReadBEDouble, double, PosZ);
- HANDLE_PACKET_READ(ReadBool, bool, IsOnGround);
- m_Client->HandlePlayerPos(PosX, PosY, PosZ, Stance, IsOnGround);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParsePluginMessage(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ChannelName);
- HANDLE_PACKET_READ(ReadBEShort, short, Length);
- AString Data;
- if (!m_ReceivedData.ReadString(Data, (size_t)Length))
- {
- m_ReceivedData.CheckValid();
- return PARSE_INCOMPLETE;
- }
- m_ReceivedData.CheckValid();
-
- // TODO: Process the data
- LOGD("Received %d bytes of plugin data on channel \"%s\".", Length, ChannelName.c_str());
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseRespawn(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, Dimension);
- HANDLE_PACKET_READ(ReadChar, char, Difficulty);
- HANDLE_PACKET_READ(ReadChar, char, CreativeMode);
- HANDLE_PACKET_READ(ReadBEShort, short, WorldHeight);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, LevelType);
- m_Client->HandleRespawn();
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseSlotSelected(void)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- m_Client->HandleSlotSelected(SlotNum);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseUpdateSign(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, BlockX);
- HANDLE_PACKET_READ(ReadBEShort, short, BlockY);
- HANDLE_PACKET_READ(ReadBEInt, int, BlockZ);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line1);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line2);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line3);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Line4);
- m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Line1, Line2, Line3, Line4);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseUseEntity(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, SourceEntityID);
- HANDLE_PACKET_READ(ReadBEInt, int, TargetEntityID);
- HANDLE_PACKET_READ(ReadBool, bool, IsLeftClick);
- m_Client->HandleUseEntity(TargetEntityID, IsLeftClick);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseEnchantItem(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, WindowID);
- HANDLE_PACKET_READ(ReadByte, Byte, Enchantment);
-
- m_Client->HandleEnchantItem(WindowID, Enchantment);
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseWindowClick(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- HANDLE_PACKET_READ(ReadBool, bool, IsRightClick);
- HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
- HANDLE_PACKET_READ(ReadBool, bool, IsShiftPressed);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // Convert IsShiftPressed, IsRightClick, SlotNum and HeldItem into eClickAction used in the newer protocols:
- eClickAction Action;
- if (IsRightClick)
- {
- if (IsShiftPressed)
- {
- Action = caShiftRightClick;
- }
- else
- {
- if (SlotNum == -999)
- {
- Action = (HeldItem.IsEmpty()) ? caRightClickOutsideHoldNothing : caRightClickOutside;
- }
- else
- {
- Action = caRightClick;
- }
- }
- }
- else
- {
- // IsLeftClick
- if (IsShiftPressed)
- {
- Action = caShiftLeftClick;
- }
- else
- {
- if (SlotNum == -999)
- {
- Action = (HeldItem.IsEmpty()) ? caLeftClickOutsideHoldNothing : caRightClickOutside;
- }
- else
- {
- Action = caLeftClick;
- }
- }
- }
- m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol125::ParseWindowClose(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- m_Client->HandleWindowClose(WindowID);
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol125::SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad)
-{
- WriteByte(PACKET_PRE_CHUNK);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- WriteBool(a_ShouldLoad);
- Flush();
-}
-
-
-
-
-
-void cProtocol125::SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items)
-{
- WriteByte (PACKET_INVENTORY_WHOLE);
- WriteChar (a_WindowID);
- WriteShort((short)a_NumItems);
-
- for (int j = 0; j < a_NumItems; j++)
- {
- WriteItem(a_Items[j]);
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol125::WriteItem(const cItem & a_Item)
-{
- short ItemType = a_Item.m_ItemType;
- ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
- if (ItemType <= 0)
- {
- // Fix, to make sure no invalid values are sent.
- ItemType = -1;
- }
-
- WriteShort(ItemType);
- if (a_Item.IsEmpty())
- {
- return;
- }
-
- WriteChar (a_Item.m_ItemCount);
- WriteShort(a_Item.m_ItemDamage);
-
- if (cItem::IsEnchantable(a_Item.m_ItemType))
- {
- // TODO: Implement enchantments
- WriteShort(-1);
- }
-}
-
-
-
-
-
-int cProtocol125::ParseItem(cItem & a_Item)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
-
- if (ItemType <= -1)
- {
- a_Item.Empty();
- return PARSE_OK;
- }
- a_Item.m_ItemType = ItemType;
-
- HANDLE_PACKET_READ(ReadChar, char, ItemCount);
- HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
- a_Item.m_ItemCount = ItemCount;
- a_Item.m_ItemDamage = ItemDamage;
- if (ItemCount <= 0)
- {
- a_Item.Empty();
- }
-
- if (!cItem::IsEnchantable(ItemType))
- {
- return PARSE_OK;
- }
-
- HANDLE_PACKET_READ(ReadBEShort, short, EnchantNumBytes);
-
- if (EnchantNumBytes <= 0)
- {
- return PARSE_OK;
- }
-
- // TODO: Enchantment not implemented yet!
- if (!m_ReceivedData.SkipRead((size_t)EnchantNumBytes))
- {
- return PARSE_INCOMPLETE;
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol125::WriteCommonMetadata(const cEntity & a_Entity)
-{
- Byte CommonMetadata = 0;
-
- if (a_Entity.IsOnFire())
- {
- CommonMetadata |= 0x1;
- }
- if (a_Entity.IsCrouched())
- {
- CommonMetadata |= 0x2;
- }
- if (a_Entity.IsRiding())
- {
- CommonMetadata |= 0x4;
- }
- if (a_Entity.IsSprinting())
- {
- CommonMetadata |= 0x8;
- }
- if (a_Entity.IsRclking())
- {
- CommonMetadata |= 0x10;
- }
- if (a_Entity.IsInvisible())
- {
- CommonMetadata |= 0x20;
- }
-
- WriteByte(0x0);
- WriteByte(CommonMetadata);
-}
-
-
-
-
-
-void cProtocol125::WriteEntityMetadata(const cEntity & a_Entity)
-{
- if (a_Entity.IsMinecart())
- {
- WriteByte(0x51);
- // No idea how Mojang makes their carts shakey shakey, so here is a complicated one-liner expression that does something similar
- WriteInt( (((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4);
- WriteByte(0x52);
- WriteInt(1); // Shaking direction, doesn't seem to affect anything
- WriteByte(0x73);
- WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
-
- if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
- {
- WriteByte(0x10);
- WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0); // Fueled?
- }
- }
- else if ((a_Entity.IsProjectile() && ((cProjectileEntity &)a_Entity).GetProjectileKind() == cProjectileEntity::pkArrow))
- {
- WriteByte(0x10);
- WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0); // Critical hitting arrow?
- }
-}
-
-
-
-
-
-void cProtocol125::WriteMobMetadata(const cMonster & a_Mob)
-{
- switch (a_Mob.GetMobType())
- {
- case cMonster::mtCreeper:
- {
- WriteByte(0x10);
- WriteChar(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); // Blowing up?
- WriteByte(0x11);
- WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); // Lightning-charged?
- break;
- }
- case cMonster::mtBat:
- {
- WriteByte(0x10);
- WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0); // Upside down?
- break;
- }
- case cMonster::mtPig:
- {
- WriteByte(0x10);
- WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0); // Saddled?
- break;
- }
- case cMonster::mtVillager:
- {
- WriteByte(0x50);
- WriteInt(((const cVillager &)a_Mob).GetVilType()); // What sort of TESTIFICATE?
- break;
- }
- case cMonster::mtZombie:
- {
- WriteByte(0xC);
- WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0); // Baby zombie?
- WriteByte(0xD);
- WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0); // Converted zombie?
- WriteByte(0xE);
- WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0); // Converted-but-converting-back zombllager?
- break;
- }
- case cMonster::mtGhast:
- {
- WriteByte(0x10);
- WriteByte(((const cGhast &)a_Mob).IsCharging()); // About to spit a flameball?
- break;
- }
- case cMonster::mtWolf:
- {
- Byte WolfStatus = 0;
- if (((const cWolf &)a_Mob).IsSitting())
- {
- WolfStatus |= 0x1;
- }
- if (((const cWolf &)a_Mob).IsAngry())
- {
- WolfStatus |= 0x2;
- }
- if (((const cWolf &)a_Mob).IsTame())
- {
- WolfStatus |= 0x4;
- }
- WriteByte(0x10);
- WriteByte(WolfStatus);
-
- WriteByte(0x72);
- WriteFloat((float)(a_Mob.GetHealth())); // Tail health-o-meter (only shown when tamed, by the way)
- WriteByte(0x13);
- WriteByte(((const cWolf &)a_Mob).IsBegging() ? 1 : 0); // Ultra cute mode?
- break;
- }
- case cMonster::mtSheep:
- {
- // [1](1111)
- // [] = Is sheared? () = Color, from 0 to 15
-
- WriteByte(0x10);
- Byte SheepMetadata = 0;
- SheepMetadata = (Byte)((const cSheep &)a_Mob).GetFurColor();
-
- if (((const cSheep &)a_Mob).IsSheared())
- {
- SheepMetadata |= 0x16;
- }
- WriteByte(SheepMetadata);
- break;
- }
- case cMonster::mtEnderman:
- {
- WriteByte(0x10);
- WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock())); // Block that he stole from your house
- WriteByte(0x11);
- WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta())); // Meta of block that he stole from your house
- WriteByte(0x12);
- WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0); // Screaming at your face?
- break;
- }
- case cMonster::mtSkeleton:
- {
- WriteByte(0xD);
- WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0); // It's a skeleton, but it's not
- break;
- }
- case cMonster::mtWitch:
- {
- WriteByte(0x15);
- WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything
- break;
- }
- case cMonster::mtWither:
- {
- WriteByte(0x54); // Int at index 20
- WriteInt((Int32)((const cWither &)a_Mob).GetWitherInvulnerableTicks());
- WriteByte(0x66); // Float at index 6
- WriteFloat((float)(a_Mob.GetHealth()));
- break;
- }
- case cMonster::mtSlime:
- case cMonster::mtMagmaCube:
- {
- WriteByte(0x10);
- if (a_Mob.GetMobType() == cMonster::mtSlime)
- {
- WriteByte((Byte)((const cSlime &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
- }
- else
- {
- WriteByte((Byte)((const cMagmaCube &)a_Mob).GetSize()); // Size of slime - HEWGE, meh, cute BABBY SLIME
- }
- break;
- }
- case cMonster::mtHorse:
- {
- int Flags = 0;
- if (((const cHorse &)a_Mob).IsTame())
- {
- Flags |= 0x2;
- }
- if (((const cHorse &)a_Mob).IsSaddled())
- {
- Flags |= 0x4;
- }
- if (((const cHorse &)a_Mob).IsChested())
- {
- Flags |= 0x8;
- }
- if (((const cHorse &)a_Mob).IsBaby())
- {
- Flags |= 0x10; // IsBred flag, according to wiki.vg - don't think it does anything in multiplayer
- }
- if (((const cHorse &)a_Mob).IsEating())
- {
- Flags |= 0x20;
- }
- if (((const cHorse &)a_Mob).IsRearing())
- {
- Flags |= 0x40;
- }
- if (((const cHorse &)a_Mob).IsMthOpen())
- {
- Flags |= 0x80;
- }
- WriteByte(0x50);
- WriteInt(Flags);
-
- WriteByte(0x13);
- WriteByte((Byte)((const cHorse &)a_Mob).GetHorseType()); // Type of horse (donkey, chestnut, etc.)
-
- WriteByte(0x54);
- int Appearance = 0;
- Appearance = ((const cHorse &)a_Mob).GetHorseColor(); // Mask FF
- Appearance |= ((const cHorse &)a_Mob).GetHorseStyle() * 256; // Mask FF00, so multiply by 256
- WriteInt(Appearance);
-
- WriteByte(0x56);
- WriteInt(((const cHorse &)a_Mob).GetHorseArmour()); // Horshey armour
- break;
- }
- default:
- {
- break;
- }
- }
-}
-
-
-
-
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
deleted file mode 100644
index 18efeb079..000000000
--- a/src/Protocol/Protocol125.h
+++ /dev/null
@@ -1,181 +0,0 @@
-
-// Protocol125.h
-
-// Interfaces to the cProtocol125 class representing the release 1.2.5 protocol (#29)
-
-
-
-
-
-#pragma once
-
-#include "Protocol.h"
-#include "../ByteBuffer.h"
-#include "../Entities/Painting.h"
-
-
-
-
-
-class cProtocol125 :
- public cProtocol
-{
- typedef cProtocol super;
-public:
- cProtocol125(cClientHandle * a_Client);
-
- /// Called when client sends some data:
- virtual void DataReceived(const char * a_Data, size_t a_Size) override;
-
- /// Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChat (const cCompositeChat & a_Message) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendDisconnect (const AString & a_Reason) override;
- virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
- virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
- virtual void SendEntityLook (const cEntity & a_Entity) override;
- virtual void SendEntityMetadata (const cEntity & a_Entity) override;
- virtual void SendEntityProperties (const cEntity & a_Entity) override;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
- virtual void SendEntityVelocity (const cEntity & a_Entity) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendKeepAlive (int a_PingID) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendLoginSuccess (void) override;
- virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
- virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
- virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
- virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
- virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message
- virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
- virtual void SendPlayerMaxSpeed (void) override;
- virtual void SendPlayerMoveLook (void) override;
- virtual void SendPlayerPosition (void) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
- virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
- virtual void SendExperience (void) override;
- virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
- virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
- virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message
- virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message
- virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
- virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
- virtual void SendStatistics (const cStatManager & a_Manager) override;
- virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
- virtual void SendTeleportEntity (const cEntity & a_Entity) override;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override {}
- virtual 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) override;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
- virtual void SendWindowOpen (const cWindow & a_Window) override;
- virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
-
- virtual AString GetAuthServerID(void) override;
-
-protected:
- /// Results of packet-parsing:
- enum
- {
- PARSE_OK = 1,
- PARSE_ERROR = -1,
- PARSE_UNKNOWN = -2,
- PARSE_INCOMPLETE = -3,
- } ;
-
- cByteBuffer m_ReceivedData; ///< Buffer for the received data
-
- AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
-
- /** The dimension that was last sent to a player in a Respawn or Login packet.
- Used to avoid Respawning into the same dimension, which confuses the client. */
- eDimension m_LastSentDimension;
-
- virtual void SendData(const char * a_Data, size_t a_Size) override;
-
- /// Sends the Handshake packet
- void SendHandshake(const AString & a_ConnectionHash);
-
- /// Parse the packet of the specified type from m_ReceivedData (switch into ParseXYZ())
- virtual int ParsePacket(unsigned char a_PacketType);
-
- // Specific packet parsers:
- virtual int ParseArmAnim (void);
- virtual int ParseBlockDig (void);
- virtual int ParseBlockPlace (void);
- virtual int ParseChat (void);
- virtual int ParseCreativeInventoryAction(void);
- virtual int ParseDisconnect (void);
- virtual int ParseEntityAction (void);
- virtual int ParseHandshake (void);
- virtual int ParseKeepAlive (void);
- virtual int ParseLogin (void);
- virtual int ParsePing (void);
- virtual int ParsePlayerAbilities (void);
- virtual int ParsePlayerLook (void);
- virtual int ParsePlayerMoveLook (void);
- virtual int ParsePlayerOnGround (void);
- virtual int ParsePlayerPosition (void);
- virtual int ParsePluginMessage (void);
- virtual int ParseRespawn (void);
- virtual int ParseSlotSelected (void);
- virtual int ParseUpdateSign (void);
- virtual int ParseUseEntity (void);
- virtual int ParseEnchantItem (void);
- virtual int ParseWindowClick (void);
- virtual int ParseWindowClose (void);
-
- // Utility functions:
- /// Writes a "pre-chunk" packet
- void SendPreChunk(int a_ChunkX, int a_ChunkZ, bool a_ShouldLoad);
-
- /// Writes a "set window items" packet with the specified params
- void SendWindowSlots(char a_WindowID, int a_NumItems, const cItem * a_Items);
-
- /// Writes one item, "slot" as the protocol wiki calls it
- virtual void WriteItem(const cItem & a_Item);
-
- /// Parses one item, "slot" as the protocol wiki calls it, from m_ReceivedData; returns the usual ParsePacket() codes
- virtual int ParseItem(cItem & a_Item);
-
- /// Writes the COMMON entity metadata
- void WriteCommonMetadata(const cEntity & a_Entity);
-
- /// Writes normal entity metadata
- void WriteEntityMetadata(const cEntity & a_Entity);
-
- /// Writes mobile entity metadata
- void WriteMobMetadata(const cMonster & a_Mob);
-} ;
-
-
-
-
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
deleted file mode 100644
index 5c58ab0ba..000000000
--- a/src/Protocol/Protocol132.cpp
+++ /dev/null
@@ -1,873 +0,0 @@
-
-// Protocol132.cpp
-
-// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
-
-#include "Globals.h"
-#include "ChunkDataSerializer.h"
-#include "Protocol132.h"
-#include "../Root.h"
-#include "../Server.h"
-#include "../World.h"
-#include "../ClientHandle.h"
-#include "../Item.h"
-#include "../Entities/Player.h"
-#include "../Mobs/Monster.h"
-#include "../UI/Window.h"
-#include "../Entities/Pickup.h"
-#include "../WorldStorage/FastNBT.h"
-#include "../WorldStorage/EnchantmentSerializer.h"
-#include "../StringCompression.h"
-#include "PolarSSL++/Sha1Checksum.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
-
-
-
-
-
-enum
-{
- PACKET_KEEP_ALIVE = 0x00,
- PACKET_LOGIN = 0x01,
- PACKET_ENTITY_EQUIPMENT = 0x05,
- PACKET_COMPASS = 0x06,
- PACKET_PLAYER_SPAWN = 0x14,
- PACKET_COLLECT_PICKUP = 0x16,
- PACKET_SPAWN_MOB = 0x18,
- PACKET_DESTROY_ENTITIES = 0x1d,
- PACKET_CHUNK_DATA = 0x33,
- PACKET_BLOCK_CHANGE = 0x35,
- PACKET_BLOCK_ACTION = 0x36,
- PACKET_BLOCK_BREAK_ANIM = 0x37,
- PACKET_SOUND_EFFECT = 0x3e,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
- PACKET_TAB_COMPLETION = 0xcb,
- PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
- PACKET_CLIENT_STATUSES = 0xcd,
- PACKET_ENCRYPTION_KEY_RESP = 0xfc,
-} ;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol132:
-
-cProtocol132::cProtocol132(cClientHandle * a_Client) :
- super(a_Client),
- m_IsEncrypted(false)
-{
-}
-
-
-
-
-
-cProtocol132::~cProtocol132()
-{
- if (!m_DataToSend.empty())
- {
- LOGD("There are " SIZE_T_FMT " unsent bytes while deleting cProtocol132", m_DataToSend.size());
- }
-}
-
-
-
-
-
-void cProtocol132::DataReceived(const char * a_Data, size_t a_Size)
-{
- if (m_IsEncrypted)
- {
- Byte Decrypted[512];
- while (a_Size > 0)
- {
- size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
- m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes);
- super::DataReceived((const char *)Decrypted, NumBytes);
- a_Size -= NumBytes;
- a_Data += NumBytes;
- }
- }
- else
- {
- super::DataReceived(a_Data, a_Size);
- }
-}
-
-
-
-
-
-void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_ACTION);
- WriteInt (a_BlockX);
- WriteShort((short)a_BlockY);
- WriteInt (a_BlockZ);
- WriteChar (a_Byte1);
- WriteChar (a_Byte2);
- WriteShort(a_BlockType);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_BREAK_ANIM);
- WriteInt (a_entityID);
- WriteInt (a_BlockX);
- WriteInt (a_BlockY);
- WriteInt (a_BlockZ);
- WriteChar (stage);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_BLOCK_CHANGE);
- WriteInt (a_BlockX);
- WriteByte ((unsigned char)a_BlockY);
- WriteInt (a_BlockZ);
- WriteShort(a_BlockType);
- WriteByte (a_BlockMeta);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
-{
- cCSLock Lock(m_CSPacket);
-
- // Pre-chunk not used in 1.3.2. Finally.
-
- // Send the chunk data:
- AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
- WriteByte(PACKET_CHUNK_DATA);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- SendData(Serialized.data(), Serialized.size());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COLLECT_PICKUP);
- WriteInt (a_Entity.GetUniqueID());
- WriteInt (a_Player.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
-{
- if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
- {
- // Do not send "destroy self" to the client, the client would crash (FS #254)
- return;
- }
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_DESTROY_ENTITIES);
- WriteByte(1); // entity count
- WriteInt (a_Entity.GetUniqueID());
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_ENTITY_EQUIPMENT);
- WriteInt (a_Entity.GetUniqueID());
- WriteShort(a_SlotNum);
- WriteItem (a_Item);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_LOGIN);
- WriteInt (a_Player.GetUniqueID()); // EntityID of the player
- WriteString("default"); // Level type
- WriteByte ((Byte)a_Player.GetGameMode());
- WriteByte ((Byte)(a_World.GetDimension()));
- WriteByte (2); // TODO: Difficulty
- WriteByte (0); // Unused, used to be world height
- WriteByte (8); // Client list width or something
- Flush();
- m_LastSentDimension = a_World.GetDimension();
- SendCompass(a_World);
-}
-
-
-
-
-
-void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
-{
- const cItem & HeldItem = a_Player.GetEquippedItem();
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PLAYER_SPAWN);
- WriteInt (a_Player.GetUniqueID());
- WriteString(a_Player.GetName());
- WriteInt ((int)(a_Player.GetPosX() * 32));
- WriteInt ((int)(a_Player.GetPosY() * 32));
- WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteChar ((char)((a_Player.GetYaw() / 360.f) * 256));
- WriteChar ((char)((a_Player.GetPitch() / 360.f) * 256));
- WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
- // Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
- WriteByte (0); // Index 0, byte (flags)
- WriteByte (0); // Flags, empty
- WriteByte (0x7f); // End of metadata
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SOUND_EFFECT);
- WriteString (a_SoundName);
- WriteInt ((int)(a_X * 8.0));
- WriteInt ((int)(a_Y * 8.0));
- WriteInt ((int)(a_Z * 8.0));
- WriteFloat (a_Volume);
- WriteChar ((char)(a_Pitch * 63.0f));
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
- WriteInt (a_EffectID);
- WriteInt (a_SrcX);
- WriteByte((Byte)a_SrcY);
- WriteInt (a_SrcZ);
- WriteInt (a_Data);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_MOB);
- WriteInt (a_Mob.GetUniqueID());
- WriteByte ((Byte)a_Mob.GetMobType());
- WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
- WriteByte ((Byte)((a_Mob.GetYaw() / 360.f) * 256));
- WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
- WriteShort ((short)(a_Mob.GetSpeedX() * 400));
- WriteShort ((short)(a_Mob.GetSpeedY() * 400));
- WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
-
- WriteCommonMetadata(a_Mob);
- WriteMobMetadata(a_Mob);
- WriteByte(0x7f);
-
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results)
-{
- if (a_Results.empty())
- {
- // No results to send
- return;
- }
-
- AString Serialized(a_Results[0]);
- for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr)
- {
- Serialized.push_back(0);
- Serialized.append(*itr);
- } // for itr - a_Results[]
-
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_TAB_COMPLETION);
- WriteString(Serialized);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- // Unloading the chunk is done by sending a "map chunk" packet
- // with IncludeInitialize set to true and primary bitmap set to 0:
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_CHUNK_DATA);
- WriteInt (a_ChunkX);
- WriteInt (a_ChunkZ);
- WriteBool(true); // IncludeInitialize
- WriteShort(0); // Primary bitmap
- WriteShort(0); // Add bitmap
- WriteInt(0);
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendWholeInventory(const cWindow & a_Window)
-{
- // 1.3.2 requires player inventory slots to be sent as SetSlot packets,
- // otherwise it sometimes fails to update the window
-
- // Send the entire window:
- super::SendWholeInventory(a_Window);
-
- // Send the player inventory and hotbar:
- cPlayer * Player = m_Client->GetPlayer();
- const cInventory & Inventory = Player->GetInventory();
- int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
- char WindowID = a_Window.GetWindowID();
- for (short i = 0; i < cInventory::invInventoryCount; i++)
- {
- SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
- } // for i - Inventory[]
- BaseOffset += cInventory::invInventoryCount;
- for (short i = 0; i < cInventory::invHotbarCount; i++)
- {
- SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
- } // for i - Hotbar[]
-
- // Send even the item being dragged:
- SendInventorySlot(-1, -1, Player->GetDraggingItem());
-}
-
-
-
-
-
-AString cProtocol132::GetAuthServerID(void)
-{
- // http://wiki.vg/wiki/index.php?title=Session&oldid=2615
- // Server uses SHA1 to mix ServerID, Client secret and server public key together
- // The mixing is done in StartEncryption, the result is in m_AuthServerID
-
- return m_AuthServerID;
-}
-
-
-
-
-
-int cProtocol132::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
- case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
- case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
- case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
- case PACKET_TAB_COMPLETION: return ParseTabCompletion();
- }
-}
-
-
-
-
-
-int cProtocol132::ParseBlockPlace(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, PosX);
- HANDLE_PACKET_READ(ReadByte, Byte, PosY);
- HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
- HANDLE_PACKET_READ(ReadChar, char, BlockFace);
-
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- HANDLE_PACKET_READ(ReadChar, char, CursorX);
- HANDLE_PACKET_READ(ReadChar, char, CursorY);
- HANDLE_PACKET_READ(ReadChar, char, CursorZ);
-
- m_Client->HandleRightClick(PosX, PosY, PosZ, static_cast<eBlockFace>(BlockFace), CursorX, CursorY, CursorZ, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseHandshake(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
- HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
- m_Username = Username;
-
- if (!m_Client->HandleHandshake( m_Username))
- {
- return PARSE_OK; // Player is not allowed into the server
- }
-
- // Send a 0xfd Encryption Key Request http://wiki.vg/Protocol#0xFD
- SendEncryptionKeyRequest();
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseClientStatuses(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, Status);
- if ((Status & 1) == 0)
- {
- m_Client->HandleLogin(39, m_Username);
- }
- else
- {
- m_Client->HandleRespawn();
- }
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseEncryptionKeyResponse(void)
-{
- // Read the encryption key:
- HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
- if (EncKeyLength > MAX_ENC_LEN)
- {
- LOGD("Too long encryption key");
- m_Client->Kick("Hacked client");
- return PARSE_OK;
- }
- AString EncKey;
- if (!m_ReceivedData.ReadString(EncKey, (size_t)EncKeyLength))
- {
- return PARSE_INCOMPLETE;
- }
-
- // Read the encryption nonce:
- HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
- AString EncNonce;
- if (!m_ReceivedData.ReadString(EncNonce, (size_t)EncNonceLength))
- {
- return PARSE_INCOMPLETE;
- }
- if (EncNonceLength > MAX_ENC_LEN)
- {
- LOGD("Too long encryption nonce");
- m_Client->Kick("Hacked client");
- return PARSE_OK;
- }
-
- HandleEncryptionKeyResponse(EncKey, EncNonce);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseLocaleViewDistance(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
- HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
- HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
- HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
- m_Client->SetLocale(Locale);
- // TODO: m_Client->HandleViewDistance(ViewDistance);
- // TODO: m_Client->HandleChatFlags(ChatFlags);
- // Ignoring client difficulty
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseLogin(void)
-{
- // Login packet not used in 1.3.2
- return PARSE_ERROR;
-}
-
-
-
-
-
-int cProtocol132::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadBool, bool, Flags);
- HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
- HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol132::ParseTabCompletion(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text);
- m_Client->HandleTabCompletion(Text);
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol132::SendData(const char * a_Data, size_t a_Size)
-{
- m_DataToSend.append(a_Data, a_Size);
-}
-
-
-
-
-
-void cProtocol132::Flush(void)
-{
- ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
-
- if (m_DataToSend.empty())
- {
- LOGD("Flushing empty");
- return;
- }
- const char * Data = m_DataToSend.data();
- size_t Size = m_DataToSend.size();
- if (m_IsEncrypted)
- {
- Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
- while (Size > 0)
- {
- size_t NumBytes = (Size > sizeof(Encrypted)) ? sizeof(Encrypted) : Size;
- m_Encryptor.ProcessData(Encrypted, (Byte *)Data, NumBytes);
- super::SendData((const char *)Encrypted, NumBytes);
- Size -= NumBytes;
- Data += NumBytes;
- }
- }
- else
- {
- super::SendData(Data, Size);
- }
- m_DataToSend.clear();
-}
-
-
-
-
-
-void cProtocol132::WriteItem(const cItem & a_Item)
-{
- short ItemType = a_Item.m_ItemType;
- ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
- if (ItemType <= 0)
- {
- // Fix, to make sure no invalid values are sent.
- ItemType = -1;
- }
-
- if (a_Item.IsEmpty())
- {
- WriteShort(-1);
- return;
- }
-
- WriteShort(ItemType);
- WriteChar (a_Item.m_ItemCount);
- WriteShort(a_Item.m_ItemDamage);
-
- if (a_Item.m_Enchantments.IsEmpty())
- {
- WriteShort(-1);
- return;
- }
-
- // Send the enchantments:
- cFastNBTWriter Writer;
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName);
- Writer.Finish();
- AString Compressed;
- CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
- WriteShort((short)Compressed.size());
- SendData(Compressed.data(), Compressed.size());
-}
-
-
-
-
-
-int cProtocol132::ParseItem(cItem & a_Item)
-{
- HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
-
- if (ItemType <= -1)
- {
- a_Item.Empty();
- return PARSE_OK;
- }
- a_Item.m_ItemType = ItemType;
-
- HANDLE_PACKET_READ(ReadChar, char, ItemCount);
- HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
- a_Item.m_ItemCount = ItemCount;
- a_Item.m_ItemDamage = ItemDamage;
- if (ItemCount <= 0)
- {
- a_Item.Empty();
- }
-
- HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
- if (MetadataLength <= 0)
- {
- return PARSE_OK;
- }
-
- // Read the metadata
- AString Metadata;
- Metadata.resize((size_t)MetadataLength);
- if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), (size_t)MetadataLength))
- {
- return PARSE_INCOMPLETE;
- }
-
- return ParseItemMetadata(a_Item, Metadata);
-}
-
-
-
-
-
-int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
-{
- // Uncompress the GZIPped data:
- AString Uncompressed;
- if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
- {
- AString HexDump;
- CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
- LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
- return PARSE_ERROR;
- }
-
- // Parse into NBT:
- cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
- if (!NBT.IsValid())
- {
- AString HexDump;
- CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
- LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
- return PARSE_ERROR;
- }
-
- // Load enchantments from the NBT:
- for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
- {
- if (
- (NBT.GetType(tag) == TAG_List) &&
- (
- (NBT.GetName(tag) == "ench") ||
- (NBT.GetName(tag) == "StoredEnchantments")
- )
- )
- {
- EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
- }
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol132::SendCompass(const cWorld & a_World)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_COMPASS);
- WriteInt((int)(a_World.GetSpawnX()));
- WriteInt((int)(a_World.GetSpawnY()));
- WriteInt((int)(a_World.GetSpawnZ()));
- Flush();
-}
-
-
-
-
-
-void cProtocol132::SendEncryptionKeyRequest(void)
-{
- cCSLock Lock(m_CSPacket);
- cServer * Server = cRoot::Get()->GetServer();
- WriteByte(0xfd);
- WriteString(Server->GetServerID());
- const AString & PublicKeyDER = Server->GetPublicKeyDER();
- WriteShort((short)(PublicKeyDER.size()));
- SendData(PublicKeyDER.data(), PublicKeyDER.size());
- WriteShort(4);
- WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
- Flush();
-}
-
-
-
-
-
-void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
-{
- // Decrypt EncNonce using privkey
- cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
-
- Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
- int res = rsaDecryptor.Decrypt((const Byte *)a_EncNonce.data(), a_EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
- if (res != 4)
- {
- LOGD("Bad nonce length");
- m_Client->Kick("Hacked client");
- return;
- }
- if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
- {
- LOGD("Bad nonce value");
- m_Client->Kick("Hacked client");
- return;
- }
-
- // Decrypt the symmetric encryption key using privkey:
- Byte DecryptedKey[MAX_ENC_LEN];
- res = rsaDecryptor.Decrypt((const Byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
- if (res != 16)
- {
- LOGD("Bad key length");
- m_Client->Kick("Hacked client");
- return;
- }
-
- {
- // Send encryption key response:
- cCSLock Lock(m_CSPacket);
- WriteByte(0xfc);
- WriteShort(0);
- WriteShort(0);
- Flush();
- }
-
- #ifdef _DEBUG
- AString DecryptedKeyHex;
- CreateHexDump(DecryptedKeyHex, DecryptedKey, res, 16);
- LOGD("Received encryption key, %d bytes:\n%s", res, DecryptedKeyHex.c_str());
- #endif
-
- StartEncryption(DecryptedKey);
- return;
-}
-
-
-
-
-
-void cProtocol132::StartEncryption(const Byte * a_Key)
-{
- m_Encryptor.Init(a_Key, a_Key);
- m_Decryptor.Init(a_Key, a_Key);
- m_IsEncrypted = true;
-
- // Prepare the m_AuthServerID:
- cSha1Checksum Checksum;
- cServer * Server = cRoot::Get()->GetServer();
- AString ServerID = Server->GetServerID();
- Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
- Checksum.Update(a_Key, 16);
- Checksum.Update((const Byte *)Server->GetPublicKeyDER().data(), Server->GetPublicKeyDER().size());
- Byte Digest[20];
- Checksum.Finalize(Digest);
- cSha1Checksum::DigestToJava(Digest, m_AuthServerID);
-}
-
-
-
-
diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h
deleted file mode 100644
index 1124a7253..000000000
--- a/src/Protocol/Protocol132.h
+++ /dev/null
@@ -1,115 +0,0 @@
-
-// Protocol132.h
-
-// Interfaces to the cProtocol132 class representing the release 1.3.2 protocol (#39)
-
-
-
-
-
-#pragma once
-
-#include "Protocol125.h"
-
-#ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable:4127)
- #pragma warning(disable:4189)
- #pragma warning(disable:4231)
- #pragma warning(disable:4244)
- #pragma warning(disable:4702)
-#endif
-
-#ifdef _MSC_VER
- #pragma warning(pop)
-#endif
-
-#include "PolarSSL++/AesCfb128Decryptor.h"
-#include "PolarSSL++/AesCfb128Encryptor.h"
-
-
-
-
-
-class cProtocol132 :
- public cProtocol125
-{
- typedef cProtocol125 super;
-public:
-
- cProtocol132(cClientHandle * a_Client);
- virtual ~cProtocol132();
-
- /// Called when client sends some data:
- virtual void DataReceived(const char * a_Data, size_t a_Size) override;
-
- // Sending commands (alphabetically sorted):
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
- virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
-
- virtual AString GetAuthServerID(void) override;
-
- /// Handling of the additional packets:
- virtual int ParsePacket(unsigned char a_PacketType) override;
-
- // Modified packets:
- virtual int ParseBlockPlace (void) override;
- virtual int ParseHandshake (void) override;
- virtual int ParseLogin (void) override;
- virtual int ParsePlayerAbilities(void) override;
-
- // New packets:
- virtual int ParseClientStatuses (void);
- virtual int ParseEncryptionKeyResponse(void);
- virtual int ParseLocaleViewDistance (void);
- virtual int ParseTabCompletion (void);
-
-protected:
- bool m_IsEncrypted;
-
- cAesCfb128Decryptor m_Decryptor;
- cAesCfb128Encryptor m_Encryptor;
-
- AString m_DataToSend;
-
- /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
- AString m_AuthServerID;
-
- virtual void SendData(const char * a_Data, size_t a_Size) override;
-
- // DEBUG:
- virtual void Flush(void) override;
-
- // Items in slots are sent differently
- virtual void WriteItem(const cItem & a_Item) override;
- virtual int ParseItem(cItem & a_Item) override;
-
- /// Parses the metadata that may come with the item.
- int ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
-
- virtual void SendCompass(const cWorld & a_World);
- virtual void SendEncryptionKeyRequest(void);
-
- /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
- void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
-
- /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
- void StartEncryption(const Byte * a_Key);
-} ;
-
-
-
-
diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp
deleted file mode 100644
index 8b177ea48..000000000
--- a/src/Protocol/Protocol14x.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-
-// Protocol14x.cpp
-
-/*
-Implements the 1.4.x protocol classes representing these protocols:
-- cProtocol142:
- - release 1.4.2 protocol (#47)
- - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
- - release 1.4.5 protocol (same as 1.4.4)
-- cProtocol146:
- - release 1.4.6 protocol (#51)
-*/
-
-#include "Globals.h"
-#include "Protocol14x.h"
-#include "../Root.h"
-#include "../Server.h"
-#include "../ClientHandle.h"
-#include "../Item.h"
-#include "ChunkDataSerializer.h"
-#include "../Entities/Player.h"
-#include "../Mobs/Monster.h"
-#include "../UI/Window.h"
-#include "../Entities/Pickup.h"
-#include "../Entities/FallingBlock.h"
-
-#ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable:4127)
- #pragma warning(disable:4244)
- #pragma warning(disable:4231)
- #pragma warning(disable:4189)
- #pragma warning(disable:4702)
-#endif
-
-#ifdef _MSC_VER
- #pragma warning(pop)
-#endif
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_UPDATE_TIME = 0x04,
- PACKET_PICKUP_SPAWN = 0x15,
- PACKET_SPAWN_OBJECT = 0x17,
- PACKET_ENTITY_METADATA = 0x28,
- PACKET_SOUND_PARTICLE_EFFECT = 0x3d
-} ;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol142:
-
-cProtocol142::cProtocol142(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-int cProtocol142::ParseLocaleViewDistance(void)
-{
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
- HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
- HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
- HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
- HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
- m_Client->SetLocale(Locale);
- // TODO: m_Client->HandleViewDistance(ViewDistance);
- // TODO: m_Client->HandleChatFlags(ChatFlags);
- // Ignoring client difficulty
- return PARSE_OK;
-}
-
-
-
-
-
-void cProtocol142::SendPickupSpawn(const cPickup & a_Pickup)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_PICKUP_SPAWN);
- WriteInt (a_Pickup.GetUniqueID());
- WriteItem (a_Pickup.GetItem());
- WriteVectorI((Vector3i)(a_Pickup.GetPosition() * 32));
- WriteChar((char)(a_Pickup.GetSpeedX() * 8));
- WriteChar((char)(a_Pickup.GetSpeedY() * 8));
- WriteChar((char)(a_Pickup.GetSpeedZ() * 8));
- Flush();
-}
-
-
-
-
-
-void cProtocol142::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
- WriteInt (a_EffectID);
- WriteInt (a_SrcX);
- WriteByte((Byte)a_SrcY);
- WriteInt (a_SrcZ);
- WriteInt (a_Data);
- WriteBool(0);
- Flush();
-}
-
-
-
-
-
-void cProtocol142::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_TIME);
- WriteInt64(a_WorldAge);
- WriteInt64(a_TimeOfDay);
- Flush();
-}
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol146:
-
-cProtocol146::cProtocol146(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol146::SendPickupSpawn(const cPickup & a_Pickup)
-{
- ASSERT(!a_Pickup.GetItem().IsEmpty());
-
- cCSLock Lock(m_CSPacket);
-
- // Send a SPAWN_OBJECT packet for the base entity:
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Pickup.GetUniqueID());
- WriteByte(0x02);
- WriteInt ((int)(a_Pickup.GetPosX() * 32));
- WriteInt ((int)(a_Pickup.GetPosY() * 32));
- WriteInt ((int)(a_Pickup.GetPosZ() * 32));
- WriteInt (1);
- WriteShort((short)(a_Pickup.GetSpeedX() * 32));
- WriteShort((short)(a_Pickup.GetSpeedY() * 32));
- WriteShort((short)(a_Pickup.GetSpeedZ() * 32));
- WriteByte(0);
- WriteByte(0);
-
- // Send a ENTITY_METADATA packet with the slot info:
- WriteByte(PACKET_ENTITY_METADATA);
- WriteInt(a_Pickup.GetUniqueID());
- WriteByte(0xaa); // a slot value at index 10
- WriteItem(a_Pickup.GetItem());
- WriteByte(0x7f); // End of metadata
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
-{
- // Send a spawn object / vehicle packet
- cCSLock Lock(m_CSPacket);
-
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_FallingBlock.GetUniqueID());
- WriteByte(70);
- WriteInt ((int)(a_FallingBlock.GetPosX() * 32));
- WriteInt ((int)(a_FallingBlock.GetPosY() * 32));
- WriteInt ((int)(a_FallingBlock.GetPosZ() * 32));
- WriteByte (0); // Pitch
- WriteByte (0); // Yaw
- WriteInt (a_FallingBlock.GetBlockType()); // data indicator = blocktype
- WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
- WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
- WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SPAWN_OBJECT);
- WriteInt (a_Entity.GetUniqueID());
- WriteChar(a_ObjectType);
- WriteInt ((int)(a_Entity.GetPosX() * 32));
- WriteInt ((int)(a_Entity.GetPosY() * 32));
- WriteInt ((int)(a_Entity.GetPosZ() * 32));
- WriteByte(a_Pitch);
- WriteByte(a_Yaw);
- WriteInt (a_ObjectData);
- if (a_ObjectData != 0)
- {
- WriteShort((short)(a_Entity.GetSpeedX() * 400));
- WriteShort((short)(a_Entity.GetSpeedY() * 400));
- WriteShort((short)(a_Entity.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
-void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_SPAWN_OBJECT);
- WriteInt (a_Vehicle.GetUniqueID());
- WriteChar (a_VehicleType);
- WriteInt ((int)(a_Vehicle.GetPosX() * 32));
- WriteInt ((int)(a_Vehicle.GetPosY() * 32));
- WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
- WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256));
- WriteInt (a_VehicleSubType);
- if (a_VehicleSubType != 0)
- {
- WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
- WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
- }
- Flush();
-}
-
-
-
-
-
diff --git a/src/Protocol/Protocol14x.h b/src/Protocol/Protocol14x.h
deleted file mode 100644
index ca497bbc1..000000000
--- a/src/Protocol/Protocol14x.h
+++ /dev/null
@@ -1,63 +0,0 @@
-
-// Protocol14x.h
-
-/*
-Interfaces to the 1.4.x protocol classes representing these protocols:
-- cProtocol142:
- - release 1.4.2 protocol (#47)
- - release 1.4.4 protocol (#49) - the same protocol class is used, because the only difference is in a packet that MCServer doesn't implement yet (ITEM_DATA)
- - release 1.4.5 protocol (same as 1.4.4)
-- cProtocol146:
- - release 1.4.6 protocol (#51)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol132.h"
-
-
-
-
-
-class cProtocol142 :
- public cProtocol132
-{
- typedef cProtocol132 super;
-
-public:
- cProtocol142(cClientHandle * a_Client);
-
- // Sending commands (alphabetically sorted):
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
-
- // Specific packet parsers:
- virtual int ParseLocaleViewDistance(void) override;
-} ;
-
-
-
-
-
-class cProtocol146 :
- public cProtocol142
-{
- typedef cProtocol142 super;
-
-public:
- cProtocol146(cClientHandle * a_Client);
-
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
-} ;
-
-
-
-
diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp
deleted file mode 100644
index 2b1f01b08..000000000
--- a/src/Protocol/Protocol15x.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-
-// Protocol15x.cpp
-
-/*
-Implements the 1.5.x protocol classes:
- - cProtocol150
- - release 1.5 protocol (#60)
- - release 1.5.2 protocol (#61, no relevant changes found)
-*/
-
-#include "Globals.h"
-#include "Protocol15x.h"
-#include "../ClientHandle.h"
-#include "../Item.h"
-#include "../UI/Window.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_PARTICLE_EFFECT = 0x3F,
- PACKET_SCOREBOARD_OBJECTIVE = 0xCE,
- PACKET_SCORE_UPDATE = 0xCF,
- PACKET_DISPLAY_OBJECTIVE = 0xD0
-} ;
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol150:
-
-cProtocol150::cProtocol150(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol150::SendWindowOpen(const cWindow & a_Window)
-{
- if (a_Window.GetWindowType() < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteByte (a_Window.GetWindowID());
- WriteByte (a_Window.GetWindowType());
- WriteString(a_Window.GetWindowTitle());
- WriteByte (a_Window.GetNumNonInventorySlots());
- WriteByte (1); // Use title
- Flush();
-}
-
-
-
-
-
-void cProtocol150::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_PARTICLE_EFFECT);
- WriteString(a_ParticleName);
- WriteFloat(a_SrcX);
- WriteFloat(a_SrcY);
- WriteFloat(a_SrcZ);
- WriteFloat(a_OffsetX);
- WriteFloat(a_OffsetY);
- WriteFloat(a_OffsetZ);
- WriteFloat(a_ParticleData);
- WriteInt(a_ParticleAmmount);
- Flush();
-}
-
-
-
-
-
-void cProtocol150::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SCOREBOARD_OBJECTIVE);
- WriteString(a_Name);
- WriteString(a_DisplayName);
- WriteByte(a_Mode);
- Flush();
-}
-
-
-
-
-
-void cProtocol150::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_SCORE_UPDATE);
- WriteString(a_Player);
- WriteByte(a_Mode);
-
- if (a_Mode != 1)
- {
- WriteString(a_Objective);
- WriteInt((int) a_Score);
- }
-
- Flush();
-}
-
-
-
-
-
-void cProtocol150::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_DISPLAY_OBJECTIVE);
- WriteByte((int) a_Display);
- WriteString(a_Objective);
- Flush();
-}
-
-
-
-
-
-int cProtocol150::ParseWindowClick(void)
-{
- HANDLE_PACKET_READ(ReadChar, char, WindowID);
- HANDLE_PACKET_READ(ReadBEShort, short, SlotNum);
- HANDLE_PACKET_READ(ReadByte, Byte, Button);
- HANDLE_PACKET_READ(ReadBEShort, short, TransactionID);
- HANDLE_PACKET_READ(ReadByte, Byte, Mode);
- cItem HeldItem;
- int res = ParseItem(HeldItem);
- if (res < 0)
- {
- return res;
- }
-
- // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
- eClickAction Action = caUnknown;
- switch ((Mode << 8) | Button)
- {
- case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
- case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
- case 0x0100: Action = caShiftLeftClick; break;
- case 0x0101: Action = caShiftRightClick; break;
- case 0x0200: Action = caNumber1; break;
- case 0x0201: Action = caNumber2; break;
- case 0x0202: Action = caNumber3; break;
- case 0x0203: Action = caNumber4; break;
- case 0x0204: Action = caNumber5; break;
- case 0x0205: Action = caNumber6; break;
- case 0x0206: Action = caNumber7; break;
- case 0x0207: Action = caNumber8; break;
- case 0x0208: Action = caNumber9; break;
- case 0x0300: Action = caMiddleClick; break;
- case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
- case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
- case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
- case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
- case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
- case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
- case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
- case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
- case 0x0600: Action = caDblClick; break;
- }
-
- if (Action == caUnknown)
- {
- LOGWARNING("Received an unknown click action combination: Mode = %d, Button = %d, Slot = %d, HeldItem = %s. Ignoring packet.",
- Mode, Button, SlotNum, ItemToFullString(HeldItem).c_str()
- );
- ASSERT(!"Unknown click action");
- return PARSE_OK;
- }
-
- m_Client->HandleWindowClick(WindowID, SlotNum, Action, HeldItem);
- return PARSE_OK;
-}
-
-
-
-
-
diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h
deleted file mode 100644
index 0d171a67c..000000000
--- a/src/Protocol/Protocol15x.h
+++ /dev/null
@@ -1,42 +0,0 @@
-
-// Protocol15x.h
-
-/*
-Declares the 1.5.x protocol classes:
- - cProtocol150
- - release 1.5 and 1.5.1 protocol (#60)
- - release 1.5.2 protocol (#61; no relevant changes found)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol14x.h"
-
-
-
-
-
-class cProtocol150 :
- public cProtocol146
-{
- typedef cProtocol146 super;
-
-public:
- cProtocol150(cClientHandle * a_Client);
-
- virtual void SendWindowOpen (const cWindow & a_Window) override;
- virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
- virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
- virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
- virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
-
- virtual int ParseWindowClick(void);
-} ;
-
-
-
-
diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp
deleted file mode 100644
index 0d354a030..000000000
--- a/src/Protocol/Protocol16x.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-
-// Protocol16x.cpp
-
-/*
-Implements the 1.6.x protocol classes:
- - cProtocol161
- - release 1.6.1 protocol (#73)
- - cProtocol162
- - release 1.6.2 protocol (#74)
- - release 1.6.3 protocol (#77) - no relevant changes
- - release 1.6.4 protocol (#78) - no relevant changes
-(others may be added later in the future for the 1.6 release series)
-*/
-
-#include "Globals.h"
-#include "Protocol16x.h"
-#include "../ClientHandle.h"
-#include "../Entities/Entity.h"
-#include "../Entities/Player.h"
-#include "../UI/Window.h"
-#include "../CompositeChat.h"
-
-
-
-
-
-#define HANDLE_PACKET_READ(Proc, Type, Var) \
- Type Var; \
- { \
- if (!m_ReceivedData.Proc(Var)) \
- { \
- m_ReceivedData.CheckValid(); \
- return PARSE_INCOMPLETE; \
- } \
- m_ReceivedData.CheckValid(); \
- }
-
-
-
-
-
-enum
-{
- PACKET_CHAT = 0x03,
- PACKET_UPDATE_HEALTH = 0x08,
- PACKET_STEER_VEHICLE = 0x1b,
- PACKET_ATTACH_ENTITY = 0x27,
- PACKET_ENTITY_PROPERTIES = 0x2c,
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_TILE_EDITOR_OPEN = 0x85,
- PACKET_PLAYER_ABILITIES = 0xca,
-} ;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol161:
-
-cProtocol161::cProtocol161(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol161::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_ATTACH_ENTITY);
- WriteInt(a_Entity.GetUniqueID());
- WriteInt((a_Vehicle == NULL) ? -1 : a_Vehicle->GetUniqueID());
- WriteBool(false); // TODO: "Should use leash?" -> no
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendChat(const AString & a_Message)
-{
- super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
-}
-
-
-
-
-
-void cProtocol161::SendChat(const cCompositeChat & a_Message)
-{
- // This protocol version doesn't support composite messages to the full
- // Just extract each part's text and use it:
-
- super::SendChat(Printf("{\"text\":\"%s\"}", EscapeString(a_Message.ExtractText()).c_str()));
-}
-
-
-
-
-
-void cProtocol161::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte(PACKET_TILE_EDITOR_OPEN);
- WriteByte(0);
- WriteInt(a_BlockX);
- WriteInt(a_BlockY);
- WriteInt(a_BlockZ);
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendGameMode(eGameMode a_GameMode)
-{
- super::SendGameMode(a_GameMode);
- SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cProtocol161::SendHealth(void)
-{
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_UPDATE_HEALTH);
- cPlayer * Player = m_Client->GetPlayer();
- WriteFloat((float)Player->GetHealth());
- WriteShort((short)Player->GetFoodLevel());
- WriteFloat((float)Player->GetFoodSaturationLevel());
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendPlayerMaxSpeed(void)
-{
- cCSLock Lock(m_CSPacket);
- cPlayer * Player = m_Client->GetPlayer();
- WriteByte(PACKET_ENTITY_PROPERTIES);
- WriteInt(Player->GetUniqueID());
- WriteInt(1);
- WriteString("generic.movementSpeed");
- WriteDouble(0.1 * Player->GetMaxSpeed());
- Flush();
-}
-
-
-
-
-
-void cProtocol161::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
-{
- // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
- super::SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
- SendPlayerMaxSpeed();
-}
-
-
-
-
-
-void cProtocol161::SendWindowOpen(const cWindow & a_Window)
-{
- if (a_Window.GetWindowType() < 0)
- {
- // Do not send for inventory windows
- return;
- }
- cCSLock Lock(m_CSPacket);
- WriteByte (PACKET_WINDOW_OPEN);
- WriteChar (a_Window.GetWindowID());
- WriteByte ((Byte)a_Window.GetWindowType());
- WriteString(a_Window.GetWindowTitle());
- WriteByte ((Byte)a_Window.GetNumNonInventorySlots());
- WriteByte (1); // Use title
- if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
- {
- WriteInt(0); // TODO: The animal's EntityID
- }
- Flush();
-}
-
-
-
-
-
-int cProtocol161::ParseEntityAction(void)
-{
- HANDLE_PACKET_READ(ReadBEInt, int, EntityID);
- HANDLE_PACKET_READ(ReadChar, char, ActionID);
- HANDLE_PACKET_READ(ReadBEInt, int, UnknownHorseVal);
-
- switch (ActionID)
- {
- case 1: m_Client->HandleEntityCrouch(EntityID, true); break; // Crouch
- case 2: m_Client->HandleEntityCrouch(EntityID, false); break; // Uncrouch
- case 3: m_Client->HandleEntityLeaveBed(EntityID); break; // Leave Bed
- case 4: m_Client->HandleEntitySprinting(EntityID, true); break; // Start sprinting
- case 5: m_Client->HandleEntitySprinting(EntityID, false); break; // Stop sprinting
- }
-
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParseLogin(void)
-{
- // The login packet is sent by Forge clients only
- // Only parse the packet, do no extra processing
- // Note that the types and the names have been only guessed and are not verified at all!
- HANDLE_PACKET_READ(ReadBEInt, int, Int1);
- HANDLE_PACKET_READ(ReadBEUTF16String16, AString, String1);
- HANDLE_PACKET_READ(ReadChar, char, Char1);
- HANDLE_PACKET_READ(ReadChar, char, Char2);
- HANDLE_PACKET_READ(ReadChar, char, Char3);
- HANDLE_PACKET_READ(ReadByte, Byte, Byte1);
- HANDLE_PACKET_READ(ReadByte, Byte, Byte2);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParsePlayerAbilities(void)
-{
- HANDLE_PACKET_READ(ReadByte, Byte, Flags);
- HANDLE_PACKET_READ(ReadBEFloat, float, FlyingSpeed);
- HANDLE_PACKET_READ(ReadBEFloat, float, WalkingSpeed);
- // TODO: m_Client->HandlePlayerAbilities(...);
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParseSteerVehicle(void)
-{
- HANDLE_PACKET_READ(ReadBEFloat, float, Sideways);
- HANDLE_PACKET_READ(ReadBEFloat, float, Forward);
- HANDLE_PACKET_READ(ReadBool, bool, Jump);
- HANDLE_PACKET_READ(ReadBool, bool, Unmount);
- if (Unmount)
- {
- m_Client->HandleUnmount();
- }
- else
- {
- m_Client->HandleSteerVehicle(Forward, Sideways);
- }
- return PARSE_OK;
-}
-
-
-
-
-
-int cProtocol161::ParsePacket(unsigned char a_PacketType)
-{
- switch (a_PacketType)
- {
- case PACKET_STEER_VEHICLE: return ParseSteerVehicle();
- default: return super::ParsePacket(a_PacketType);
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cProtocol162:
-
-cProtocol162::cProtocol162(cClientHandle * a_Client) :
- super(a_Client)
-{
-}
-
-
-
-
-
-void cProtocol162::SendPlayerMaxSpeed(void)
-{
- cCSLock Lock(m_CSPacket);
- cPlayer * Player = m_Client->GetPlayer();
- WriteByte(PACKET_ENTITY_PROPERTIES);
- WriteInt(Player->GetUniqueID());
- WriteInt(1);
- WriteString("generic.movementSpeed");
- WriteDouble(0.1 * Player->GetMaxSpeed());
- WriteShort(0);
- Flush();
-}
-
-
-
-
diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h
deleted file mode 100644
index add761d1e..000000000
--- a/src/Protocol/Protocol16x.h
+++ /dev/null
@@ -1,78 +0,0 @@
-
-// Protocol16x.h
-
-/*
-Declares the 1.6.x protocol classes:
- - cProtocol161
- - release 1.6.1 protocol (#73)
- - cProtocol162
- - release 1.6.2 protocol (#74)
- - release 1.6.3 protocol (#77) - no relevant changes
- - release 1.6.4 protocol (#78) - no relevant changes
-(others may be added later in the future for the 1.6 release series)
-*/
-
-
-
-
-
-#pragma once
-
-#include "Protocol15x.h"
-
-
-
-
-
-class cProtocol161 :
- public cProtocol150
-{
- typedef cProtocol150 super;
-
-public:
- cProtocol161(cClientHandle * a_Client);
-
-protected:
-
- // cProtocol150 overrides:
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChat (const cCompositeChat & a_Message) override;
- virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendPlayerMaxSpeed(void) override;
- virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
- virtual void SendWindowOpen (const cWindow & a_Window) override;
-
- virtual int ParseEntityAction (void) override;
- virtual int ParseLogin (void) override;
- virtual int ParsePlayerAbilities(void) override;
-
- // New packets:
- virtual int ParseSteerVehicle(void);
-
- // Enable new packets' handling
- virtual int ParsePacket(unsigned char a_PacketType) override;
-} ;
-
-
-
-
-
-class cProtocol162 :
- public cProtocol161
-{
- typedef cProtocol161 super;
-
-public:
- cProtocol162(cClientHandle * a_Client);
-
-protected:
- // cProtocol161 overrides:
- virtual void SendPlayerMaxSpeed(void) override;
-} ;
-
-
-
-
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 45d39e0e9..a7abd240f 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -1,10 +1,12 @@
+
// Protocol17x.cpp
/*
Implements the 1.7.x protocol classes:
- cProtocol172
- release 1.7.2 protocol (#4)
-(others may be added later in the future for the 1.7 release series)
+ - cProtocol176
+ - release 1.7.6 protocol (#5)
*/
#include "Globals.h"
@@ -37,9 +39,11 @@ Implements the 1.7.x protocol classes:
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
+#include "../BlockEntities/BeaconEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
+#include "Bindings/PluginManager.h"
@@ -47,7 +51,10 @@ Implements the 1.7.x protocol classes:
#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
Type Var; \
- ByteBuf.Proc(Var);
+ if (!ByteBuf.Proc(Var))\
+ {\
+ return;\
+ }
@@ -95,6 +102,19 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
m_IsEncrypted(false),
m_LastSentDimension(dimNotSet)
{
+ // BungeeCord handling:
+ // If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field:
+ // hostname\00ip-address\00uuid\00profile-properties-as-json
+ AStringVector Params;
+ if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord() && SplitZeroTerminatedStrings(a_ServerAddress, Params) && (Params.size() == 4))
+ {
+ LOGD("Player at %s connected via BungeeCord", Params[1].c_str());
+ m_ServerAddress = Params[0];
+ m_Client->SetIPString(Params[1]);
+ m_Client->SetUUID(cMojangAPI::MakeUUIDShort(Params[2]));
+ m_Client->SetProperties(Params[3]);
+ }
+
// Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut)
{
@@ -232,101 +252,13 @@ void cProtocol172::SendChat(const AString & a_Message)
void cProtocol172::SendChat(const cCompositeChat & a_Message)
{
ASSERT(m_State == 3); // In game mode?
-
- // Compose the complete Json string to send:
- Json::Value msg;
+
cWorld * World = m_Client->GetPlayer()->GetWorld();
- msg["text"] = cClientHandle::FormatMessageType((World == NULL) ? false : World->ShouldUseChatPrefixes(), a_Message.GetMessageType(), a_Message.GetAdditionalMessageTypeData()); // The client crashes without this field being present
- const cCompositeChat::cParts & Parts = a_Message.GetParts();
- for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr)
- {
- Json::Value Part;
- switch ((*itr)->m_PartType)
- {
- case cCompositeChat::ptText:
- {
- Part["text"] = (*itr)->m_Text;
- AddChatPartStyle(Part, (*itr)->m_Style);
- break;
- }
-
- case cCompositeChat::ptClientTranslated:
- {
- const cCompositeChat::cClientTranslatedPart & p = (const cCompositeChat::cClientTranslatedPart &)**itr;
- Part["translate"] = p.m_Text;
- Json::Value With;
- for (AStringVector::const_iterator itrW = p.m_Parameters.begin(), endW = p.m_Parameters.end(); itrW != endW; ++itr)
- {
- With.append(*itrW);
- }
- if (!p.m_Parameters.empty())
- {
- Part["with"] = With;
- }
- AddChatPartStyle(Part, p.m_Style);
- break;
- }
-
- case cCompositeChat::ptUrl:
- {
- const cCompositeChat::cUrlPart & p = (const cCompositeChat::cUrlPart &)**itr;
- Part["text"] = p.m_Text;
- Json::Value Url;
- Url["action"] = "open_url";
- Url["value"] = p.m_Url;
- Part["clickEvent"] = Url;
- AddChatPartStyle(Part, p.m_Style);
- break;
- }
-
- case cCompositeChat::ptSuggestCommand:
- case cCompositeChat::ptRunCommand:
- {
- const cCompositeChat::cCommandPart & p = (const cCompositeChat::cCommandPart &)**itr;
- Part["text"] = p.m_Text;
- Json::Value Cmd;
- Cmd["action"] = (p.m_PartType == cCompositeChat::ptRunCommand) ? "run_command" : "suggest_command";
- Cmd["value"] = p.m_Command;
- Part["clickEvent"] = Cmd;
- AddChatPartStyle(Part, p.m_Style);
- break;
- }
+ bool ShouldUseChatPrefixes = (World == NULL) ? false : World->ShouldUseChatPrefixes();
- case cCompositeChat::ptShowAchievement:
- {
- const cCompositeChat::cShowAchievementPart & p = (const cCompositeChat::cShowAchievementPart &)**itr;
- Part["translate"] = "chat.type.achievement";
-
- Json::Value Ach;
- Ach["action"] = "show_achievement";
- Ach["value"] = p.m_Text;
-
- Json::Value AchColourAndName;
- AchColourAndName["color"] = "green";
- AchColourAndName["translate"] = p.m_Text;
- AchColourAndName["hoverEvent"] = Ach;
-
- Json::Value Extra;
- Extra.append(AchColourAndName);
-
- Json::Value Name;
- Name["text"] = p.m_PlayerName;
-
- Json::Value With;
- With.append(Name);
- With.append(Extra);
-
- Part["with"] = With;
- AddChatPartStyle(Part, p.m_Style);
- break;
- }
- }
- msg["extra"].append(Part);
- } // for itr - Parts[]
-
// Send the message to the client:
cPacketizer Pkt(*this, 0x02);
- Pkt.WriteString(msg.toStyledString());
+ Pkt.WriteString(a_Message.CreateJsonString(ShouldUseChatPrefixes));
}
@@ -339,7 +271,7 @@ void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
// This contains the flags and bitmasks, too
- const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
+ const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2, a_ChunkX, a_ChunkZ);
cPacketizer Pkt(*this, 0x21); // Chunk Data packet
Pkt.WriteInt(a_ChunkX);
@@ -680,7 +612,7 @@ void cProtocol172::SendLoginSuccess(void)
{
cPacketizer Pkt(*this, 0x02); // Login success packet
- Pkt.WriteString(m_Client->GetUUID());
+ Pkt.WriteString(cMojangAPI::MakeUUIDDashed(m_Client->GetUUID()));
Pkt.WriteString(m_Client->GetUsername());
}
@@ -708,7 +640,7 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
-void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale)
{
ASSERT(m_State == 3); // In game mode?
@@ -730,7 +662,7 @@ void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo
-void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale)
{
ASSERT(m_State == 3); // In game mode?
@@ -768,7 +700,6 @@ void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale)
-
void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
{
ASSERT(m_State == 3); // In game mode?
@@ -819,7 +750,7 @@ void cProtocol172::SendPlayerAbilities(void)
}
Pkt.WriteByte(Flags);
Pkt.WriteFloat((float)(0.05 * Player->GetFlyingMaxSpeed()));
- Pkt.WriteFloat((float)(0.1 * Player->GetMaxSpeed()));
+ Pkt.WriteFloat((float)(0.1 * Player->GetNormalMaxSpeed()));
}
@@ -839,7 +770,7 @@ void cProtocol172::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio
-void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
+void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
{
ASSERT(m_State == 3); // In game mode?
@@ -852,21 +783,66 @@ void cProtocol172::SendParticleEffect(const AString & a_ParticleName, float a_Sr
Pkt.WriteFloat(a_OffsetY);
Pkt.WriteFloat(a_OffsetZ);
Pkt.WriteFloat(a_ParticleData);
- Pkt.WriteInt(a_ParticleAmmount);
+ Pkt.WriteInt(a_ParticleAmount);
}
-void cProtocol172::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+void cProtocol172::SendPlayerListAddPlayer(const cPlayer & a_Player)
{
ASSERT(m_State == 3); // In game mode?
-
+
cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
- Pkt.WriteString(a_Player.GetName());
- Pkt.WriteBool(a_IsOnline);
- Pkt.WriteShort(a_IsOnline ? a_Player.GetClientHandle()->GetPing() : 0);
+ Pkt.WriteString(a_Player.GetPlayerListName());
+ Pkt.WriteBool(true);
+ Pkt.WriteShort(a_Player.GetClientHandle()->GetPing());
+}
+
+
+
+
+
+void cProtocol172::SendPlayerListRemovePlayer(const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38);
+ Pkt.WriteString(a_Player.GetPlayerListName());
+ Pkt.WriteBool(false);
+ Pkt.WriteShort(0);
+}
+
+
+
+
+
+void cProtocol172::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
+{
+ // Not implemented in this protocol version
+ UNUSED(a_Player);
+}
+
+
+
+
+
+void cProtocol172::SendPlayerListUpdatePing(const cPlayer & a_Player)
+{
+ // It is a simple add player packet in this protocol.
+ SendPlayerListAddPlayer(a_Player);
+}
+
+
+
+
+
+void cProtocol172::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
+{
+ // Not implemented in this protocol version
+ UNUSED(a_Player);
+ UNUSED(a_CustomName);
}
@@ -940,9 +916,16 @@ void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player)
// Called to spawn another player for the client
cPacketizer Pkt(*this, 0x0c); // Spawn Player packet
- Pkt.WriteVarInt(a_Player.GetUniqueID());
- Pkt.WriteString(a_Player.GetClientHandle()->GetUUID());
- Pkt.WriteString(a_Player.GetName());
+ Pkt.WriteVarInt((UInt32) a_Player.GetUniqueID());
+ Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID()));
+ if (a_Player.HasCustomName())
+ {
+ Pkt.WriteString(a_Player.GetCustomName());
+ }
+ else
+ {
+ Pkt.WriteString(a_Player.GetName());
+ }
Pkt.WriteFPInt(a_Player.GetPosX());
Pkt.WriteFPInt(a_Player.GetPosY());
Pkt.WriteFPInt(a_Player.GetPosZ());
@@ -1028,9 +1011,9 @@ void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb)
cPacketizer Pkt(*this, 0x11);
Pkt.WriteVarInt(a_ExpOrb.GetUniqueID());
- Pkt.WriteInt((int) a_ExpOrb.GetPosX());
- Pkt.WriteInt((int) a_ExpOrb.GetPosY());
- Pkt.WriteInt((int) a_ExpOrb.GetPosZ());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosX());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosY());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosZ());
Pkt.WriteShort(a_ExpOrb.GetReward());
}
@@ -1285,9 +1268,14 @@ void cProtocol172::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
-void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+void cProtocol172::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
{
ASSERT(m_State == 3); // In game mode?
+ if (!a_DoDaylightCycle)
+ {
+ // When writing a "-" before the number the client ignores it but it will stop the client-side time expiration.
+ a_TimeOfDay = std::min(-a_TimeOfDay, -1LL);
+ }
cPacketizer Pkt(*this, 0x03);
Pkt.WriteInt64(a_WorldAge);
@@ -1328,6 +1316,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
{
case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
+ case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
@@ -1344,6 +1333,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
void cProtocol172::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)
{
ASSERT(m_State == 3); // In game mode?
+ ASSERT((a_BlockY >= 0) && (a_BlockY < cChunkDef::Height));
cPacketizer Pkt(*this, 0x33);
Pkt.WriteInt(a_BlockX);
@@ -1489,14 +1479,14 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
);
m_CommLogFile.Flush();
}
-
+
if (!m_ReceivedData.Write(a_Data, a_Size))
{
// Too much data in the incoming queue, report to caller:
m_Client->PacketBufferFull();
return;
}
-
+
// Handle all complete packets:
for (;;)
{
@@ -1516,10 +1506,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
cByteBuffer bb(PacketLen + 1);
VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen));
m_ReceivedData.CommitRead();
-
- // Write one NUL extra, so that we can detect over-reads
- bb.Write("\0", 1);
-
+
UInt32 PacketType;
if (!bb.ReadVarInt(PacketType))
{
@@ -1527,6 +1514,9 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
break;
}
+ // Write one NUL extra, so that we can detect over-reads
+ bb.Write("\0", 1);
+
// Log the packet info into the comm log file:
if (g_ShouldLogCommIn)
{
@@ -1534,7 +1524,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
bb.ReadAll(PacketData);
bb.ResetRead();
bb.ReadVarInt(PacketType);
- ASSERT(PacketData.size() > 0);
+ ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read
PacketData.resize(PacketData.size() - 1);
AString PacketDataHex;
CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16);
@@ -1542,7 +1532,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str()
);
}
-
+
if (!HandlePacket(bb, PacketType))
{
// Unknown packet, already been reported, but without the length. Log the length here:
@@ -1567,7 +1557,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
return;
}
-
+
if (bb.GetReadableSpace() != 1)
{
// Read more or less than packet length, report as error
@@ -1588,7 +1578,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size)
m_Client->PacketError(PacketType);
}
} // for (ever)
-
+
// Log any leftover bytes into the logfile:
if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0))
{
@@ -1698,9 +1688,8 @@ bool cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
void cProtocol172::HandlePacketStatusPing(cByteBuffer & a_ByteBuffer)
{
- Int64 Timestamp;
- a_ByteBuffer.ReadBEInt64(Timestamp);
-
+ HANDLE_READ(a_ByteBuffer, ReadBEInt64, Int64, Timestamp);
+
cPacketizer Pkt(*this, 0x01); // Ping packet
Pkt.WriteInt64(Timestamp);
}
@@ -1711,21 +1700,41 @@ void cProtocol172::HandlePacketStatusPing(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
{
- // Send the response:
- AString Response = "{\"version\":{\"name\":\"1.7.2\", \"protocol\":4}, \"players\":{";
cServer * Server = cRoot::Get()->GetServer();
- AppendPrintf(Response, "\"max\":%u, \"online\":%u, \"sample\":[]},",
- Server->GetMaxPlayers(),
- Server->GetNumPlayers()
- );
- AppendPrintf(Response, "\"description\":{\"text\":\"%s\"},",
- Server->GetDescription().c_str()
- );
- AppendPrintf(Response, "\"favicon\": \"data:image/png;base64,%s\"",
- Server->GetFaviconData().c_str()
- );
- Response.append("}");
-
+ AString ServerDescription = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
+
+ // Version:
+ Json::Value Version;
+ Version["name"] = "1.7.2";
+ Version["protocol"] = 4;
+
+ // Players:
+ Json::Value Players;
+ Players["online"] = NumPlayers;
+ Players["max"] = MaxPlayers;
+ // TODO: Add "sample"
+
+ // Description:
+ Json::Value Description;
+ Description["text"] = ServerDescription.c_str();
+
+ // Create the response:
+ Json::Value ResponseValue;
+ ResponseValue["version"] = Version;
+ ResponseValue["players"] = Players;
+ ResponseValue["description"] = Description;
+ if (!Favicon.empty())
+ {
+ ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
+ }
+
+ Json::StyledWriter Writer;
+ AString Response = Writer.write(ResponseValue);
+
cPacketizer Pkt(*this, 0x00); // Response packet
Pkt.WriteString(Response);
}
@@ -1794,7 +1803,11 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe
void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
{
AString Username;
- a_ByteBuffer.ReadVarUTF8String(Username);
+ if (!a_ByteBuffer.ReadVarUTF8String(Username))
+ {
+ m_Client->Kick("Bad username");
+ return;
+ }
if (!m_Client->HandleHandshake(Username))
{
@@ -1888,7 +1901,7 @@ void cProtocol172::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape);
m_Client->SetLocale(Locale);
- // TODO: handle in m_Client
+ // TODO: Do anything with the other values.
}
@@ -2051,8 +2064,27 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
{
HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
HANDLE_READ(a_ByteBuffer, ReadBEShort, short, Length);
+ if (Length + 1 != (int)a_ByteBuffer.GetReadableSpace())
+ {
+ LOGD("Invalid plugin message packet, payload length doesn't match packet length (exp %d, got %d)",
+ (int)a_ByteBuffer.GetReadableSpace() - 1, Length
+ );
+ return;
+ }
+
+ // If the plugin channel is recognized vanilla, handle it directly:
+ if (Channel.substr(0, 3) == "MC|")
+ {
+ HandleVanillaPluginMessage(a_ByteBuffer, Channel, Length);
+ return;
+ }
+
+ // Read the plugin message and relay to clienthandle:
AString Data;
- a_ByteBuffer.ReadString(Data, Length);
+ if (!a_ByteBuffer.ReadString(Data, Length))
+ {
+ return;
+ }
m_Client->HandlePluginMessage(Channel, Data);
}
@@ -2201,14 +2233,76 @@ void cProtocol172::HandlePacketWindowClose(cByteBuffer & a_ByteBuffer)
-void cProtocol172::WritePacket(cByteBuffer & a_Packet)
+void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel, short a_PayloadLength)
{
- cCSLock Lock(m_CSPacket);
- AString Pkt;
- a_Packet.ReadAll(Pkt);
- WriteVarInt((UInt32)Pkt.size());
- SendData(Pkt.data(), Pkt.size());
- Flush();
+ if (a_Channel == "MC|AdvCdm")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode);
+ switch (Mode)
+ {
+ case 0x00:
+ {
+ // Block-based commandblock update:
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockY);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ);
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Command);
+ m_Client->HandleCommandBlockBlockChange(BlockX, BlockY, BlockZ, Command);
+ break;
+ }
+
+ // TODO: Entity-based commandblock update
+
+ default:
+ {
+ m_Client->SendChat(Printf("Failure setting command block command; unhandled mode %d", Mode), mtFailure);
+ LOG("Unhandled MC|AdvCdm packet mode.");
+ return;
+ }
+ } // switch (Mode)
+ return;
+ }
+ else if (a_Channel == "MC|Brand")
+ {
+ // Read the client's brand:
+ AString Brand;
+ if (a_ByteBuffer.ReadString(Brand, a_PayloadLength))
+ {
+ m_Client->SetClientBrand(Brand);
+ }
+
+ // Send back our brand:
+ SendPluginMessage("MC|Brand", "MCServer");
+ return;
+ }
+ else if (a_Channel == "MC|Beacon")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect1);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect2);
+ m_Client->HandleBeaconSelection(Effect1, Effect2);
+ return;
+ }
+ else if (a_Channel == "MC|ItemName")
+ {
+ AString ItemName;
+ if (a_ByteBuffer.ReadString(ItemName, a_PayloadLength))
+ {
+ m_Client->HandleAnvilItemName(ItemName);
+ }
+ return;
+ }
+ else if (a_Channel == "MC|TrSel")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, SlotNum);
+ m_Client->HandleNPCTrade(SlotNum);
+ return;
+ }
+ LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
+
+ // Read the payload and send it through to the clienthandle:
+ AString Message;
+ VERIFY(a_ByteBuffer.ReadString(Message, a_PayloadLength));
+ m_Client->HandlePluginMessage(a_Channel, Message);
}
@@ -2239,7 +2333,6 @@ void cProtocol172::SendData(const char * a_Data, size_t a_Size)
-
bool cProtocol172::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item)
{
HANDLE_PACKET_READ(a_ByteBuffer, ReadBEShort, short, ItemType);
@@ -2384,85 +2477,6 @@ void cProtocol172::StartEncryption(const Byte * a_Key)
-void cProtocol172::AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle)
-{
- size_t len = a_PartStyle.length();
- for (size_t i = 0; i < len; i++)
- {
- switch (a_PartStyle[i])
- {
- case 'b':
- {
- // bold
- a_Value["bold"] = Json::Value(true);
- break;
- }
-
- case 'i':
- {
- // italic
- a_Value["italic"] = Json::Value(true);
- break;
- }
-
- case 'u':
- {
- // Underlined
- a_Value["underlined"] = Json::Value(true);
- break;
- }
-
- case 's':
- {
- // strikethrough
- a_Value["strikethrough"] = Json::Value(true);
- break;
- }
-
- case 'o':
- {
- // obfuscated
- a_Value["obfuscated"] = Json::Value(true);
- break;
- }
-
- case '@':
- {
- // Color, specified by the next char:
- i++;
- if (i >= len)
- {
- // String too short, didn't contain a color
- break;
- }
- switch (a_PartStyle[i])
- {
- case '0': a_Value["color"] = Json::Value("black"); break;
- case '1': a_Value["color"] = Json::Value("dark_blue"); break;
- case '2': a_Value["color"] = Json::Value("dark_green"); break;
- case '3': a_Value["color"] = Json::Value("dark_aqua"); break;
- case '4': a_Value["color"] = Json::Value("dark_red"); break;
- case '5': a_Value["color"] = Json::Value("dark_purple"); break;
- case '6': a_Value["color"] = Json::Value("gold"); break;
- case '7': a_Value["color"] = Json::Value("gray"); break;
- case '8': a_Value["color"] = Json::Value("dark_gray"); break;
- case '9': a_Value["color"] = Json::Value("blue"); break;
- case 'a': a_Value["color"] = Json::Value("green"); break;
- case 'b': a_Value["color"] = Json::Value("aqua"); break;
- case 'c': a_Value["color"] = Json::Value("red"); break;
- case 'd': a_Value["color"] = Json::Value("light_purple"); break;
- case 'e': a_Value["color"] = Json::Value("yellow"); break;
- case 'f': a_Value["color"] = Json::Value("white"); break;
- } // switch (color)
- } // case '@'
- } // switch (Style[i])
- } // for i - a_PartStyle[]
-}
-
-
-
-
-
////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
@@ -2472,6 +2486,7 @@ cProtocol172::cPacketizer::~cPacketizer()
// Send the packet length
UInt32 PacketLen = (UInt32)m_Out.GetUsedSpace();
+
m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen);
m_Protocol.m_OutPacketLenBuffer.ReadAll(DataToSend);
m_Protocol.SendData(DataToSend.data(), DataToSend.size());
@@ -2566,6 +2581,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, Writer, (ENUM_ITEM_ID)a_Item.m_ItemType);
}
Writer.Finish();
+
AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
WriteShort((short)Compressed.size());
@@ -2575,12 +2591,26 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
+
void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEntity)
{
cFastNBTWriter Writer;
switch (a_BlockEntity.GetBlockType())
{
+ case E_BLOCK_BEACON:
+ {
+ cBeaconEntity & BeaconEntity = (cBeaconEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", BeaconEntity.GetPosX());
+ Writer.AddInt("y", BeaconEntity.GetPosY());
+ Writer.AddInt("z", BeaconEntity.GetPosZ());
+ Writer.AddInt("Primary", BeaconEntity.GetPrimaryEffect());
+ Writer.AddInt("Secondary", BeaconEntity.GetSecondaryEffect());
+ Writer.AddInt("Levels", BeaconEntity.GetBeaconLevel());
+ Writer.AddString("id", "Beacon"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+ break;
+ }
case E_BLOCK_COMMAND_BLOCK:
{
cCommandBlockEntity & CommandBlockEntity = (cCommandBlockEntity &)a_BlockEntity;
@@ -2773,7 +2803,7 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
WriteByte(0xA2);
WriteItem(Frame.GetItem());
WriteByte(0x3);
- WriteByte(Frame.GetRotation());
+ WriteByte(Frame.GetRotation() / 2);
break;
}
default: break;
@@ -2788,7 +2818,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
{
switch (a_Mob.GetMobType())
{
- case cMonster::mtCreeper:
+ case mtCreeper:
{
WriteByte(0x10);
WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1);
@@ -2797,28 +2827,28 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtBat:
+ case mtBat:
{
WriteByte(0x10);
WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0);
break;
}
- case cMonster::mtPig:
+ case mtPig:
{
WriteByte(0x10);
WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0);
break;
}
- case cMonster::mtVillager:
+ case mtVillager:
{
WriteByte(0x50);
WriteInt(((const cVillager &)a_Mob).GetVilType());
break;
}
- case cMonster::mtZombie:
+ case mtZombie:
{
WriteByte(0x0c);
WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0);
@@ -2829,14 +2859,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtGhast:
+ case mtGhast:
{
WriteByte(0x10);
WriteByte(((const cGhast &)a_Mob).IsCharging());
break;
}
- case cMonster::mtWolf:
+ case mtWolf:
{
const cWolf & Wolf = (const cWolf &)a_Mob;
Byte WolfStatus = 0;
@@ -2864,7 +2894,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtSheep:
+ case mtSheep:
{
WriteByte(0x10);
Byte SheepMetadata = 0;
@@ -2877,7 +2907,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtEnderman:
+ case mtEnderman:
{
WriteByte(0x10);
WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock()));
@@ -2888,21 +2918,21 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtSkeleton:
+ case mtSkeleton:
{
WriteByte(0x0d);
WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0);
break;
}
- case cMonster::mtWitch:
+ case mtWitch:
{
WriteByte(0x15);
WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0);
break;
}
- case cMonster::mtWither:
+ case mtWither:
{
WriteByte(0x54); // Int at index 20
WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks());
@@ -2911,21 +2941,21 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
- case cMonster::mtSlime:
+ case mtSlime:
{
WriteByte(0x10);
WriteByte(((const cSlime &)a_Mob).GetSize());
break;
}
- case cMonster::mtMagmaCube:
+ case mtMagmaCube:
{
WriteByte(0x10);
WriteByte(((const cMagmaCube &)a_Mob).GetSize());
break;
}
- case cMonster::mtHorse:
+ case mtHorse:
{
const cHorse & Horse = (const cHorse &)a_Mob;
int Flags = 0;
@@ -2971,6 +3001,15 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
break;
}
} // switch (a_Mob.GetType())
+
+ // Custom name:
+ if (a_Mob.HasCustomName())
+ {
+ WriteByte(0x8a);
+ WriteString(a_Mob.GetCustomName());
+ WriteByte(0x0b);
+ WriteByte(a_Mob.IsCustomNameAlwaysVisible() ? 1 : 0);
+ }
}
@@ -3014,18 +3053,24 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player)
// Called to spawn another player for the client
cPacketizer Pkt(*this, 0x0c); // Spawn Player packet
Pkt.WriteVarInt(a_Player.GetUniqueID());
- Pkt.WriteString(a_Player.GetClientHandle()->GetUUID());
- Pkt.WriteString(a_Player.GetName());
+ Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID()));
+ if (a_Player.HasCustomName())
+ {
+ Pkt.WriteString(a_Player.GetCustomName());
+ }
+ else
+ {
+ Pkt.WriteString(a_Player.GetName());
+ }
- const Json::Value & Properties = m_Client->GetProperties();
- const Json::Value::const_iterator End = Properties.end();
+ const Json::Value & Properties = a_Player.GetClientHandle()->GetProperties();
Pkt.WriteVarInt(Properties.size());
- for (Json::Value::iterator itr = Properties.begin(); itr != End; ++itr)
+ for (Json::Value::iterator itr = Properties.begin(), end = Properties.end(); itr != end; ++itr)
{
- Pkt.WriteString(((Json::Value)*itr).get("name", "").toStyledString());
- Pkt.WriteString(((Json::Value)*itr).get("value", "").toStyledString());
- Pkt.WriteString(((Json::Value)*itr).get("signature", "").toStyledString());
+ Pkt.WriteString(((Json::Value)*itr).get("name", "").asString());
+ Pkt.WriteString(((Json::Value)*itr).get("value", "").asString());
+ Pkt.WriteString(((Json::Value)*itr).get("signature", "").asString());
}
Pkt.WriteFPInt(a_Player.GetPosX());
@@ -3046,20 +3091,41 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player)
void cProtocol176::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
{
- // Send the response:
- AString Response = "{\"version\": {\"name\": \"1.7.6\", \"protocol\":5}, \"players\": {";
- AppendPrintf(Response, "\"max\": %u, \"online\": %u, \"sample\": []},",
- cRoot::Get()->GetServer()->GetMaxPlayers(),
- cRoot::Get()->GetServer()->GetNumPlayers()
- );
- AppendPrintf(Response, "\"description\": {\"text\": \"%s\"},",
- cRoot::Get()->GetServer()->GetDescription().c_str()
- );
- AppendPrintf(Response, "\"favicon\": \"data:image/png;base64,%s\"",
- cRoot::Get()->GetServer()->GetFaviconData().c_str()
- );
- Response.append("}");
-
+ cServer * Server = cRoot::Get()->GetServer();
+ AString Motd = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, Motd, NumPlayers, MaxPlayers, Favicon);
+
+ // Version:
+ Json::Value Version;
+ Version["name"] = "1.7.6";
+ Version["protocol"] = 5;
+
+ // Players:
+ Json::Value Players;
+ Players["online"] = NumPlayers;
+ Players["max"] = MaxPlayers;
+ // TODO: Add "sample"
+
+ // Description:
+ Json::Value Description;
+ Description["text"] = Motd.c_str();
+
+ // Create the response:
+ Json::Value ResponseValue;
+ ResponseValue["version"] = Version;
+ ResponseValue["players"] = Players;
+ ResponseValue["description"] = Description;
+ if (!Favicon.empty())
+ {
+ ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
+ }
+
+ Json::StyledWriter Writer;
+ AString Response = Writer.write(ResponseValue);
+
cPacketizer Pkt(*this, 0x00); // Response packet
Pkt.WriteString(Response);
}
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 9c9f563e0..7709df59d 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -5,7 +5,8 @@
Declares the 1.7.x protocol classes:
- cProtocol172
- release 1.7.2 protocol (#4)
-(others may be added later in the future for the 1.7 release series)
+ - cProtocol176
+ - release 1.7.6 protocol (#5)
*/
@@ -60,76 +61,80 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/** Sending stuff to clients (alphabetically sorted): */
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChat (const cCompositeChat & a_Message) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendDisconnect (const AString & a_Reason) override;
- virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
- virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
- virtual void SendEntityLook (const cEntity & a_Entity) override;
- virtual void SendEntityMetadata (const cEntity & a_Entity) override;
- virtual void SendEntityProperties (const cEntity & a_Entity) override;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
- virtual void SendEntityVelocity (const cEntity & a_Entity) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendKeepAlive (int a_PingID) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendLoginSuccess (void) override;
- virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
- virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
- virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
- virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendPlayerAbilities (void) override;
- virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
- virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
- virtual void SendPlayerMaxSpeed (void) override;
- virtual void SendPlayerMoveLook (void) override;
- virtual void SendPlayerPosition (void) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
- virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
- virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
- virtual void SendExperience (void) override;
- virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
- virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
- virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
- virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
- virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
- virtual void SendStatistics (const cStatManager & a_Manager) override;
- virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
- virtual void SendTeleportEntity (const cEntity & a_Entity) override;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
- virtual 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) override;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
- virtual void SendWindowOpen (const cWindow & a_Window) override;
- virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
+ virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExperience (void) override;
+ virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendLoginSuccess (void) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
+ virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override;
+ virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
+ virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
+ virtual void SendTabCompletionResults (const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
+ virtual 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) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
@@ -251,19 +256,19 @@ protected:
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, size_t a_Size);
-
+
/** Reads and handles the packet. The packet length and type have already been read.
Returns true if the packet was understood, false if it was an unknown packet
*/
bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType);
-
+
// Packet handlers while in the Status state (m_State == 1):
- void HandlePacketStatusPing (cByteBuffer & a_ByteBuffer);
+ void HandlePacketStatusPing(cByteBuffer & a_ByteBuffer);
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer);
-
+
// Packet handlers while in the Login state (m_State == 2):
void HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer);
- void HandlePacketLoginStart (cByteBuffer & a_ByteBuffer);
+ void HandlePacketLoginStart(cByteBuffer & a_ByteBuffer);
// Packet handlers while in the Game state (m_State == 3):
void HandlePacketAnimation (cByteBuffer & a_ByteBuffer);
@@ -290,25 +295,23 @@ protected:
void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer);
void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer);
+ /** Parses Vanilla plugin messages into specific ClientHandle calls.
+ The message payload is still in the bytebuffer, to be read by this function. */
+ void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel, short a_PayloadLength);
- /** Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. */
- void WritePacket(cByteBuffer & a_Packet);
-
/** Sends the data to the client, encrypting them if needed. */
virtual void SendData(const char * a_Data, size_t a_Size) override;
void SendCompass(const cWorld & a_World);
/** Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data */
- bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item);
+ virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item);
/** Parses item metadata as read by ReadItem(), into the item enchantments. */
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
void StartEncryption(const Byte * a_Key);
-
- /** Adds the chat part's style (represented by the part's stylestring) into the Json object. */
- void AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle);
+
} ;
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
new file mode 100644
index 000000000..acdb48cf7
--- /dev/null
+++ b/src/Protocol/Protocol18x.cpp
@@ -0,0 +1,3333 @@
+
+// Protocol18x.cpp
+
+/*
+Implements the 1.8.x protocol classes:
+ - cProtocol180
+ - release 1.8.0 protocol (#47)
+(others may be added later in the future for the 1.8 release series)
+*/
+
+#include "Globals.h"
+#include "json/json.h"
+#include "Protocol18x.h"
+#include "ChunkDataSerializer.h"
+#include "PolarSSL++/Sha1Checksum.h"
+
+#include "../ClientHandle.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../World.h"
+#include "../StringCompression.h"
+#include "../CompositeChat.h"
+#include "../Statistics.h"
+
+#include "../WorldStorage/FastNBT.h"
+#include "../WorldStorage/EnchantmentSerializer.h"
+
+#include "../Entities/ExpOrb.h"
+#include "../Entities/Minecart.h"
+#include "../Entities/FallingBlock.h"
+#include "../Entities/Painting.h"
+#include "../Entities/Pickup.h"
+#include "../Entities/Player.h"
+#include "../Entities/ItemFrame.h"
+#include "../Entities/ArrowEntity.h"
+#include "../Entities/FireworkEntity.h"
+
+#include "../Mobs/IncludeAllMonsters.h"
+#include "../UI/Window.h"
+
+#include "../BlockEntities/BeaconEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
+#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/FlowerPotEntity.h"
+#include "Bindings/PluginManager.h"
+
+
+
+
+
+#define HANDLE_READ(ByteBuf, Proc, Type, Var) \
+ Type Var; \
+ if (!ByteBuf.Proc(Var))\
+ {\
+ return;\
+ }
+
+
+
+
+
+#define HANDLE_PACKET_READ(ByteBuf, Proc, Type, Var) \
+ Type Var; \
+ { \
+ if (!ByteBuf.Proc(Var)) \
+ { \
+ ByteBuf.CheckValid(); \
+ return false; \
+ } \
+ ByteBuf.CheckValid(); \
+ }
+
+
+
+
+
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+const uLongf MAX_COMPRESSED_PACKET_LEN = 200 KiB; // Maximum size of compressed packets.
+
+
+
+
+
+// fwd: main.cpp:
+extern bool g_ShouldLogCommIn, g_ShouldLogCommOut;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cProtocol180:
+
+cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
+ super(a_Client),
+ m_ServerAddress(a_ServerAddress),
+ m_ServerPort(a_ServerPort),
+ m_State(a_State),
+ m_ReceivedData(32 KiB),
+ m_OutPacketBuffer(64 KiB),
+ m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt
+ m_IsEncrypted(false),
+ m_LastSentDimension(dimNotSet)
+{
+ // Create the comm log file, if so requested:
+ if (g_ShouldLogCommIn || g_ShouldLogCommOut)
+ {
+ static int sCounter = 0;
+ cFile::CreateFolder("CommLogs");
+ AString FileName = Printf("CommLogs/%x_%d__%s.log", (unsigned)time(NULL), sCounter++, a_Client->GetIPString().c_str());
+ m_CommLogFile.Open(FileName, cFile::fmWrite);
+ }
+}
+
+
+
+
+
+void cProtocol180::DataReceived(const char * a_Data, size_t a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ Byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes);
+ AddReceivedData((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ AddReceivedData(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocol180::SendAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteInt((a_Vehicle != NULL) ? a_Vehicle->GetUniqueID() : 0);
+ Pkt.WriteBool(false);
+}
+
+
+
+
+
+void cProtocol180::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x24); // Block Action packet
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+ Pkt.WriteByte(a_Byte1);
+ Pkt.WriteByte(a_Byte2);
+ Pkt.WriteVarInt(a_BlockType);
+}
+
+
+
+
+
+void cProtocol180::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x25); // Block Break Animation packet
+ Pkt.WriteVarInt(a_EntityID);
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+ Pkt.WriteChar(a_Stage);
+}
+
+
+
+
+
+void cProtocol180::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x23); // Block Change packet
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+ Pkt.WriteVarInt(((UInt32)a_BlockType << 4) | ((UInt32)a_BlockMeta & 15));
+}
+
+
+
+
+
+void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x22); // Multi Block Change packet
+ Pkt.WriteInt(a_ChunkX);
+ Pkt.WriteInt(a_ChunkZ);
+ Pkt.WriteVarInt((UInt32)a_Changes.size());
+ for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
+ {
+ short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12));
+ Pkt.WriteShort(Coords);
+ Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF));
+ } // for itr - a_Changes[]
+}
+
+
+
+
+
+void cProtocol180::SendChat(const AString & a_Message)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x02); // Chat Message packet
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Message).c_str()));
+ Pkt.WriteChar(0);
+}
+
+
+
+
+
+void cProtocol180::SendChat(const cCompositeChat & a_Message)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cWorld * World = m_Client->GetPlayer()->GetWorld();
+ bool ShouldUseChatPrefixes = (World == NULL) ? false : World->ShouldUseChatPrefixes();
+
+ // Send the message to the client:
+ cPacketizer Pkt(*this, 0x02);
+ Pkt.WriteString(a_Message.CreateJsonString(ShouldUseChatPrefixes));
+ Pkt.WriteChar(0);
+}
+
+
+
+
+
+void cProtocol180::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ // Serialize first, before creating the Packetizer (the packetizer locks a CS)
+ // This contains the flags and bitmasks, too
+ const AString & ChunkData = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_8_0, a_ChunkX, a_ChunkZ);
+
+ cCSLock Lock(m_CSPacket);
+ SendData(ChunkData.data(), ChunkData.size());
+}
+
+
+
+
+
+void cProtocol180::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x0d); // Collect Item packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteVarInt(a_Player.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol180::SendDestroyEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x13); // Destroy Entities packet
+ Pkt.WriteVarInt(1);
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+}
+
+
+
+
+
+void cProtocol180::SendDisconnect(const AString & a_Reason)
+{
+ switch (m_State)
+ {
+ case 2:
+ {
+ // During login:
+ cPacketizer Pkt(*this, 0);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+ break;
+ }
+ case 3:
+ {
+ // In-game:
+ cPacketizer Pkt(*this, 0x40);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol180::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x36); // Sign Editor Open packet
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocol180::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1D); // Entity Effect packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_EffectID);
+ Pkt.WriteByte(a_Amplifier);
+ Pkt.WriteVarInt((UInt32)a_Duration);
+ Pkt.WriteBool(false); // Hide particles
+}
+
+
+
+
+
+void cProtocol180::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x04); // Entity Equipment packet
+ Pkt.WriteVarInt((UInt32)a_Entity.GetUniqueID());
+ Pkt.WriteShort(a_SlotNum);
+ Pkt.WriteItem(a_Item);
+}
+
+
+
+
+
+void cProtocol180::SendEntityHeadLook(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x19); // Entity Head Look packet
+ Pkt.WriteVarInt((UInt32)a_Entity.GetUniqueID());
+ Pkt.WriteByteAngle(a_Entity.GetHeadYaw());
+}
+
+
+
+
+
+void cProtocol180::SendEntityLook(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x16); // Entity Look packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+ Pkt.WriteBool(true); // TODO: IsOnGround() on entities
+}
+
+
+
+
+
+void cProtocol180::SendEntityMetadata(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteEntityMetadata(a_Entity);
+ Pkt.WriteByte(0x7f); // The termination byte
+}
+
+
+
+
+
+void cProtocol180::SendEntityProperties(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x20); // Entity Properties packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteEntityProperties(a_Entity);
+}
+
+
+
+
+
+void cProtocol180::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_RelX);
+ Pkt.WriteByte(a_RelY);
+ Pkt.WriteByte(a_RelZ);
+ Pkt.WriteBool(true); // TODO: IsOnGround() on entities
+}
+
+
+
+
+
+void cProtocol180::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_RelX);
+ Pkt.WriteByte(a_RelY);
+ Pkt.WriteByte(a_RelZ);
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+ Pkt.WriteBool(true); // TODO: IsOnGround() on entities
+}
+
+
+
+
+
+void cProtocol180::SendEntityStatus(const cEntity & a_Entity, char a_Status)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1a); // Entity Status packet
+ Pkt.WriteInt(a_Entity.GetUniqueID());
+ Pkt.WriteChar(a_Status);
+}
+
+
+
+
+
+void cProtocol180::SendEntityVelocity(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x12); // Entity Velocity packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ // 400 = 8000 / 20 ... Conversion from our speed in m/s to 8000 m/tick
+ Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+}
+
+
+
+
+
+void cProtocol180::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x27); // Explosion packet
+ Pkt.WriteFloat((float)a_BlockX);
+ Pkt.WriteFloat((float)a_BlockY);
+ Pkt.WriteFloat((float)a_BlockZ);
+ Pkt.WriteFloat((float)a_Radius);
+ Pkt.WriteInt((int)a_BlocksAffected.size());
+ for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr)
+ {
+ Pkt.WriteChar((char)itr->x);
+ Pkt.WriteChar((char)itr->y);
+ Pkt.WriteChar((char)itr->z);
+ } // for itr - a_BlockAffected[]
+ Pkt.WriteFloat((float)a_PlayerMotion.x);
+ Pkt.WriteFloat((float)a_PlayerMotion.y);
+ Pkt.WriteFloat((float)a_PlayerMotion.z);
+}
+
+
+
+
+
+void cProtocol180::SendGameMode(eGameMode a_GameMode)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x2b); // Change Game State packet
+ Pkt.WriteByte(3); // Reason: Change game mode
+ Pkt.WriteFloat((float)a_GameMode);
+}
+
+
+
+
+
+void cProtocol180::SendHealth(void)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x06); // Update Health packet
+ cPlayer * Player = m_Client->GetPlayer();
+ Pkt.WriteFloat((float)Player->GetHealth());
+ Pkt.WriteVarInt((UInt32)Player->GetFoodLevel());
+ Pkt.WriteFloat((float)Player->GetFoodSaturationLevel());
+}
+
+
+
+
+
+void cProtocol180::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x2f); // Set Slot packet
+ Pkt.WriteChar(a_WindowID);
+ Pkt.WriteShort(a_SlotNum);
+ Pkt.WriteItem(a_Item);
+}
+
+
+
+
+
+void cProtocol180::SendKeepAlive(int a_PingID)
+{
+ // Drop the packet if the protocol is not in the Game state yet (caused a client crash):
+ if (m_State != 3)
+ {
+ LOGWARNING("Trying to send a KeepAlive packet to a player who's not yet fully logged in (%d). The protocol class prevented the packet.", m_State);
+ return;
+ }
+
+ cPacketizer Pkt(*this, 0x00); // Keep Alive packet
+ Pkt.WriteVarInt(a_PingID);
+}
+
+
+
+
+
+void cProtocol180::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+{
+ // Send the Join Game packet:
+ {
+ cServer * Server = cRoot::Get()->GetServer();
+ cPacketizer Pkt(*this, 0x01); // Join Game packet
+ Pkt.WriteInt(a_Player.GetUniqueID());
+ Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
+ Pkt.WriteChar((char)a_World.GetDimension());
+ Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
+ Pkt.WriteByte(Server->GetMaxPlayers());
+ Pkt.WriteString("default"); // Level type - wtf?
+ Pkt.WriteBool(false); // Reduced Debug Info - wtf?
+ }
+ m_LastSentDimension = a_World.GetDimension();
+
+ // Send the spawn position:
+ {
+ cPacketizer Pkt(*this, 0x05); // Spawn Position packet
+ Pkt.WritePosition((int)a_World.GetSpawnX(), (int)a_World.GetSpawnY(), (int)a_World.GetSpawnZ());
+ }
+
+ // Send the server difficulty:
+ {
+ cPacketizer Pkt(*this, 0x41);
+ Pkt.WriteChar(1);
+ }
+
+ // Send player abilities:
+ SendPlayerAbilities();
+}
+
+
+
+
+void cProtocol180::SendLoginSuccess(void)
+{
+ ASSERT(m_State == 2); // State: login?
+
+ // Enable compression:
+ {
+ cPacketizer Pkt(*this, 0x03); // Set compression packet
+ Pkt.WriteVarInt(256);
+ }
+
+ m_State = 3; // State = Game
+
+ {
+ cPacketizer Pkt(*this, 0x02); // Login success packet
+ Pkt.WriteString(cMojangAPI::MakeUUIDDashed(m_Client->GetUUID()));
+ Pkt.WriteString(m_Client->GetUsername());
+ }
+}
+
+
+
+
+
+void cProtocol180::SendPaintingSpawn(const cPainting & a_Painting)
+{
+ ASSERT(m_State == 3); // In game mode?
+ double PosX = a_Painting.GetPosX();
+ double PosY = a_Painting.GetPosY();
+ double PosZ = a_Painting.GetPosZ();
+
+ switch (a_Painting.GetDirection())
+ {
+ case 0: PosZ += 1; break;
+ case 1: PosX -= 1; break;
+ case 2: PosZ -= 1; break;
+ case 3: PosX += 1; break;
+ }
+
+ cPacketizer Pkt(*this, 0x10); // Spawn Painting packet
+ Pkt.WriteVarInt(a_Painting.GetUniqueID());
+ Pkt.WriteString(a_Painting.GetName().c_str());
+ Pkt.WritePosition((int)PosX, (int)PosY, (int)PosZ);
+ Pkt.WriteChar(a_Painting.GetDirection());
+}
+
+
+
+
+
+void cProtocol180::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteByte(m_Scale);
+
+ Pkt.WriteVarInt(0);
+ Pkt.WriteByte(1);
+ Pkt.WriteByte(a_Length);
+ Pkt.WriteByte(a_X);
+ Pkt.WriteByte(a_Y);
+
+ Pkt.WriteVarInt(a_Length);
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ Pkt.WriteByte(a_Colors[i]);
+ }
+}
+
+
+
+
+
+void cProtocol180::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteByte(m_Scale);
+ Pkt.WriteVarInt(a_Decorators.size());
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ Pkt.WriteByte(it->GetPixelX());
+ Pkt.WriteByte(it->GetPixelZ());
+ }
+
+ Pkt.WriteByte(0);
+}
+
+
+
+
+
+void cProtocol180::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ UNUSED(a_ID);
+ UNUSED(a_Scale);
+
+ // This packet was removed in 1.8
+}
+
+
+
+
+
+void cProtocol180::SendPickupSpawn(const cPickup & a_Pickup)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ {
+ cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
+ Pkt.WriteVarInt(a_Pickup.GetUniqueID());
+ Pkt.WriteByte(2); // Type = Pickup
+ Pkt.WriteFPInt(a_Pickup.GetPosX());
+ Pkt.WriteFPInt(a_Pickup.GetPosY());
+ Pkt.WriteFPInt(a_Pickup.GetPosZ());
+ Pkt.WriteByteAngle(a_Pickup.GetYaw());
+ Pkt.WriteByteAngle(a_Pickup.GetPitch());
+ Pkt.WriteInt(0); // No object data
+ }
+
+ {
+ cPacketizer Pkt(*this, 0x1c); // Entity Metadata packet
+ Pkt.WriteVarInt(a_Pickup.GetUniqueID());
+ Pkt.WriteByte((0x05 << 5) | 10); // Slot type + index 10
+ Pkt.WriteItem(a_Pickup.GetItem());
+ Pkt.WriteByte(0x7f); // End of metadata
+ }
+}
+
+
+
+
+
+void cProtocol180::SendPlayerAbilities(void)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x39); // Player Abilities packet
+ Byte Flags = 0;
+ cPlayer * Player = m_Client->GetPlayer();
+ if (Player->IsGameModeCreative())
+ {
+ Flags |= 0x01;
+ Flags |= 0x08; // Godmode, used for creative
+ }
+ if (Player->IsFlying())
+ {
+ Flags |= 0x02;
+ }
+ if (Player->CanFly())
+ {
+ Flags |= 0x04;
+ }
+ Pkt.WriteByte(Flags);
+ Pkt.WriteFloat((float)(0.05 * Player->GetFlyingMaxSpeed()));
+ Pkt.WriteFloat((float)(0.1 * Player->GetNormalMaxSpeed()));
+}
+
+
+
+
+
+void cProtocol180::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x0b); // Animation packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteChar(a_Animation);
+}
+
+
+
+
+
+void cProtocol180::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
+{
+ ASSERT(m_State == 3); // In game mode?
+ int ParticleID = GetParticleID(a_ParticleName);
+
+ cPacketizer Pkt(*this, 0x2A);
+ Pkt.WriteInt(ParticleID);
+ Pkt.WriteBool(false);
+ Pkt.WriteFloat(a_SrcX);
+ Pkt.WriteFloat(a_SrcY);
+ Pkt.WriteFloat(a_SrcZ);
+ Pkt.WriteFloat(a_OffsetX);
+ Pkt.WriteFloat(a_OffsetY);
+ Pkt.WriteFloat(a_OffsetZ);
+ Pkt.WriteFloat(a_ParticleData);
+ Pkt.WriteInt(a_ParticleAmount);
+}
+
+
+
+
+
+void cProtocol180::SendPlayerListAddPlayer(const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteVarInt(0);
+ Pkt.WriteVarInt(1);
+ Pkt.WriteUUID(a_Player.GetUUID());
+ Pkt.WriteString(a_Player.GetPlayerListName());
+
+ const Json::Value & Properties = a_Player.GetClientHandle()->GetProperties();
+ Pkt.WriteVarInt(Properties.size());
+ for (Json::Value::iterator itr = Properties.begin(), end = Properties.end(); itr != end; ++itr)
+ {
+ Pkt.WriteString(((Json::Value)*itr).get("name", "").asString());
+ Pkt.WriteString(((Json::Value)*itr).get("value", "").asString());
+ AString Signature = ((Json::Value)*itr).get("signature", "").asString();
+ if (Signature.empty())
+ {
+ Pkt.WriteBool(false);
+ }
+ else
+ {
+ Pkt.WriteBool(true);
+ Pkt.WriteString(Signature);
+ }
+ }
+
+ Pkt.WriteVarInt((UInt32)a_Player.GetGameMode());
+ Pkt.WriteVarInt((UInt32)a_Player.GetClientHandle()->GetPing());
+ Pkt.WriteBool(false);
+}
+
+
+
+
+
+void cProtocol180::SendPlayerListRemovePlayer(const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteVarInt(4);
+ Pkt.WriteVarInt(1);
+ Pkt.WriteUUID(a_Player.GetUUID());
+}
+
+
+
+
+
+void cProtocol180::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteVarInt(1);
+ Pkt.WriteVarInt(1);
+ Pkt.WriteUUID(a_Player.GetUUID());
+ Pkt.WriteVarInt((UInt32)a_Player.GetGameMode());
+}
+
+
+
+
+
+void cProtocol180::SendPlayerListUpdatePing(const cPlayer & a_Player)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteVarInt(2);
+ Pkt.WriteVarInt(1);
+ Pkt.WriteUUID(a_Player.GetUUID());
+ Pkt.WriteVarInt((UInt32)a_Player.GetClientHandle()->GetPing());
+}
+
+
+
+
+
+void cProtocol180::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x38); // Playerlist Item packet
+ Pkt.WriteVarInt(3);
+ Pkt.WriteVarInt(1);
+ Pkt.WriteUUID(a_Player.GetUUID());
+
+ if (a_CustomName.empty())
+ {
+ Pkt.WriteBool(false);
+ }
+ else
+ {
+ Pkt.WriteBool(true);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_CustomName.c_str()));
+ }
+}
+
+
+
+
+
+void cProtocol180::SendPlayerMaxSpeed(void)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x20); // Entity Properties
+ cPlayer * Player = m_Client->GetPlayer();
+ Pkt.WriteVarInt(Player->GetUniqueID());
+ Pkt.WriteInt(1); // Count
+ Pkt.WriteString("generic.movementSpeed");
+ // The default game speed is 0.1, multiply that value by the relative speed:
+ Pkt.WriteDouble(0.1 * Player->GetNormalMaxSpeed());
+ if (Player->IsSprinting())
+ {
+ Pkt.WriteVarInt(1); // Modifier count
+ Pkt.WriteInt64(0x662a6b8dda3e4c1c);
+ Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier
+ Pkt.WriteDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed());
+ Pkt.WriteByte(2);
+ }
+ else
+ {
+ Pkt.WriteVarInt(0); // Modifier count
+ }
+}
+
+
+
+
+
+void cProtocol180::SendPlayerMoveLook(void)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x08); // Player Position And Look packet
+ cPlayer * Player = m_Client->GetPlayer();
+ Pkt.WriteDouble(Player->GetPosX());
+
+ // The "+ 0.001" is there because otherwise the player falls through the block they were standing on.
+ Pkt.WriteDouble(Player->GetStance() + 0.001);
+
+ Pkt.WriteDouble(Player->GetPosZ());
+ Pkt.WriteFloat((float)Player->GetYaw());
+ Pkt.WriteFloat((float)Player->GetPitch());
+ Pkt.WriteByte(0);
+}
+
+
+
+
+
+void cProtocol180::SendPlayerPosition(void)
+{
+ // There is no dedicated packet for this, send the whole thing:
+ SendPlayerMoveLook();
+}
+
+
+
+
+
+void cProtocol180::SendPlayerSpawn(const cPlayer & a_Player)
+{
+ // Called to spawn another player for the client
+ cPacketizer Pkt(*this, 0x0c); // Spawn Player packet
+ Pkt.WriteVarInt(a_Player.GetUniqueID());
+ Pkt.WriteUUID(cMojangAPI::MakeUUIDShort(a_Player.GetUUID()));
+ Pkt.WriteFPInt(a_Player.GetPosX());
+ Pkt.WriteFPInt(a_Player.GetPosY());
+ Pkt.WriteFPInt(a_Player.GetPosZ());
+ Pkt.WriteByteAngle(a_Player.GetYaw());
+ Pkt.WriteByteAngle(a_Player.GetPitch());
+ short ItemType = a_Player.GetEquippedItem().IsEmpty() ? 0 : a_Player.GetEquippedItem().m_ItemType;
+ Pkt.WriteShort(ItemType);
+ Pkt.WriteByte((3 << 5) | 6); // Metadata: float + index 6
+ Pkt.WriteFloat((float)a_Player.GetHealth());
+ Pkt.WriteByte((4 << 5 | (2 & 0x1F)) & 0xFF);
+ Pkt.WriteString(a_Player.GetName());
+ Pkt.WriteByte(0x7f); // Metadata: end
+}
+
+
+
+
+
+void cProtocol180::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x3f);
+ Pkt.WriteString(a_Channel);
+ Pkt.WriteBuf(a_Message.data(), a_Message.size());
+}
+
+
+
+
+
+void cProtocol180::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1e);
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_EffectID);
+}
+
+
+
+
+
+void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
+{
+ if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
+ {
+ // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
+ return;
+ }
+
+ cPacketizer Pkt(*this, 0x07); // Respawn packet
+ cPlayer * Player = m_Client->GetPlayer();
+ Pkt.WriteInt((int)a_Dimension);
+ Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
+ Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
+ Pkt.WriteString("default");
+ m_LastSentDimension = a_Dimension;
+}
+
+
+
+
+
+void cProtocol180::SendExperience(void)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x1f); // Experience Packet
+ cPlayer * Player = m_Client->GetPlayer();
+ Pkt.WriteFloat(Player->GetXpPercentage());
+ Pkt.WriteVarInt((UInt32)Player->GetXpLevel());
+ Pkt.WriteVarInt((UInt32)Player->GetCurrentXp());
+}
+
+
+
+
+
+void cProtocol180::SendExperienceOrb(const cExpOrb & a_ExpOrb)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x11);
+ Pkt.WriteVarInt(a_ExpOrb.GetUniqueID());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosX());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosY());
+ Pkt.WriteFPInt(a_ExpOrb.GetPosZ());
+ Pkt.WriteShort(a_ExpOrb.GetReward());
+}
+
+
+
+
+
+void cProtocol180::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x3b);
+ Pkt.WriteString(a_Name);
+ Pkt.WriteByte(a_Mode);
+ if ((a_Mode == 0) || (a_Mode == 2))
+ {
+ Pkt.WriteString(a_DisplayName);
+ Pkt.WriteString("integer");
+ }
+}
+
+
+
+
+
+void cProtocol180::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x3c);
+ Pkt.WriteString(a_Player);
+ Pkt.WriteByte(a_Mode);
+ Pkt.WriteString(a_Objective);
+
+ if (a_Mode != 1)
+ {
+ Pkt.WriteVarInt((UInt32) a_Score);
+ }
+}
+
+
+
+
+
+void cProtocol180::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x3d);
+ Pkt.WriteByte((int) a_Display);
+ Pkt.WriteString(a_Objective);
+}
+
+
+
+
+
+void cProtocol180::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x29); // Sound Effect packet
+ Pkt.WriteString(a_SoundName);
+ Pkt.WriteInt((int)(a_X * 8.0));
+ Pkt.WriteInt((int)(a_Y * 8.0));
+ Pkt.WriteInt((int)(a_Z * 8.0));
+ Pkt.WriteFloat(a_Volume);
+ Pkt.WriteByte((Byte)(a_Pitch * 63));
+}
+
+
+
+
+
+void cProtocol180::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x28); // Effect packet
+ Pkt.WriteInt(a_EffectID);
+ Pkt.WritePosition(a_SrcX, a_SrcY, a_SrcZ);
+ Pkt.WriteInt(a_Data);
+ Pkt.WriteBool(false);
+}
+
+
+
+
+
+void cProtocol180::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x0e); // Spawn Object packet
+ Pkt.WriteVarInt(a_FallingBlock.GetUniqueID());
+ Pkt.WriteByte(70); // Falling block
+ Pkt.WriteFPInt(a_FallingBlock.GetPosX());
+ Pkt.WriteFPInt(a_FallingBlock.GetPosY());
+ Pkt.WriteFPInt(a_FallingBlock.GetPosZ());
+ Pkt.WriteByteAngle(a_FallingBlock.GetYaw());
+ Pkt.WriteByteAngle(a_FallingBlock.GetPitch());
+ Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 12));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400));
+}
+
+
+
+
+
+void cProtocol180::SendSpawnMob(const cMonster & a_Mob)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet
+ Pkt.WriteVarInt(a_Mob.GetUniqueID());
+ Pkt.WriteByte((Byte)a_Mob.GetMobType());
+ Pkt.WriteFPInt(a_Mob.GetPosX());
+ Pkt.WriteFPInt(a_Mob.GetPosY());
+ Pkt.WriteFPInt(a_Mob.GetPosZ());
+ Pkt.WriteByteAngle(a_Mob.GetPitch());
+ Pkt.WriteByteAngle(a_Mob.GetHeadYaw());
+ Pkt.WriteByteAngle(a_Mob.GetYaw());
+ Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400));
+ Pkt.WriteEntityMetadata(a_Mob);
+ Pkt.WriteByte(0x7f); // Metadata terminator
+}
+
+
+
+
+
+void cProtocol180::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
+{
+ ASSERT(m_State == 3); // In game mode?
+ double PosX = a_Entity.GetPosX();
+ double PosZ = a_Entity.GetPosZ();
+ double Yaw = a_Entity.GetYaw();
+ if (a_ObjectType == 71)
+ {
+ FixItemFramePositions(a_ObjectData, PosX, PosZ, Yaw);
+ }
+
+ cPacketizer Pkt(*this, 0xe); // Spawn Object packet
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteByte(a_ObjectType);
+ Pkt.WriteFPInt(PosX);
+ Pkt.WriteFPInt(a_Entity.GetPosY());
+ Pkt.WriteFPInt(PosZ);
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+ Pkt.WriteByteAngle(Yaw);
+ Pkt.WriteInt(a_ObjectData);
+ if (a_ObjectData != 0)
+ {
+ Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400));
+ }
+}
+
+
+
+
+
+void cProtocol180::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0xe); // Spawn Object packet
+ Pkt.WriteVarInt(a_Vehicle.GetUniqueID());
+ Pkt.WriteByte(a_VehicleType);
+ Pkt.WriteFPInt(a_Vehicle.GetPosX());
+ Pkt.WriteFPInt(a_Vehicle.GetPosY());
+ Pkt.WriteFPInt(a_Vehicle.GetPosZ());
+ Pkt.WriteByteAngle(a_Vehicle.GetPitch());
+ Pkt.WriteByteAngle(a_Vehicle.GetYaw());
+ Pkt.WriteInt(a_VehicleSubType);
+ if (a_VehicleSubType != 0)
+ {
+ Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400));
+ Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400));
+ Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400));
+ }
+}
+
+
+
+
+
+void cProtocol180::SendStatistics(const cStatManager & a_Manager)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x37);
+ Pkt.WriteVarInt(statCount); // TODO 2014-05-11 xdot: Optimization: Send "dirty" statistics only
+
+ for (size_t i = 0; i < (size_t)statCount; ++i)
+ {
+ StatValue Value = a_Manager.GetValue((eStatistic) i);
+ const AString & StatName = cStatInfo::GetName((eStatistic) i);
+
+ Pkt.WriteString(StatName);
+ Pkt.WriteVarInt(Value);
+ }
+}
+
+
+
+
+
+void cProtocol180::SendTabCompletionResults(const AStringVector & a_Results)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet
+ Pkt.WriteVarInt((int)a_Results.size());
+
+ for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr)
+ {
+ Pkt.WriteString(*itr);
+ }
+}
+
+
+
+
+
+void cProtocol180::SendTeleportEntity(const cEntity & a_Entity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x18);
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WriteFPInt(a_Entity.GetPosX());
+ Pkt.WriteFPInt(a_Entity.GetPosY());
+ Pkt.WriteFPInt(a_Entity.GetPosZ());
+ Pkt.WriteByteAngle(a_Entity.GetYaw());
+ Pkt.WriteByteAngle(a_Entity.GetPitch());
+ Pkt.WriteBool(true); // TODO: IsOnGrond() on entities
+}
+
+
+
+
+
+void cProtocol180::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x2c); // Spawn Global Entity packet
+ Pkt.WriteVarInt(0); // EntityID = 0, always
+ Pkt.WriteByte(1); // Type = Thunderbolt
+ Pkt.WriteFPInt(a_BlockX);
+ Pkt.WriteFPInt(a_BlockY);
+ Pkt.WriteFPInt(a_BlockZ);
+}
+
+
+
+
+
+void cProtocol180::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
+{
+ ASSERT(m_State == 3); // In game mode?
+ if (!a_DoDaylightCycle)
+ {
+ // When writing a "-" before the number the client ignores it but it will stop the client-side time expiration.
+ a_TimeOfDay = std::min(-a_TimeOfDay, -1LL);
+ }
+
+ cPacketizer Pkt(*this, 0x03);
+ Pkt.WriteInt64(a_WorldAge);
+ Pkt.WriteInt64(a_TimeOfDay);
+}
+
+
+
+
+
+void cProtocol180::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x21); // Chunk Data packet
+ Pkt.WriteInt(a_ChunkX);
+ Pkt.WriteInt(a_ChunkZ);
+ Pkt.WriteBool(true);
+ Pkt.WriteShort(0); // Primary bitmap
+ Pkt.WriteVarInt(0); // Data size
+}
+
+
+
+
+void cProtocol180::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x35); // Update tile entity packet
+ Pkt.WritePosition(a_BlockEntity.GetPosX(), a_BlockEntity.GetPosY(), a_BlockEntity.GetPosZ());
+
+ Byte Action = 0;
+ switch (a_BlockEntity.GetBlockType())
+ {
+ case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
+ case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
+ case E_BLOCK_BEACON: Action = 3; break; // Update beacon entity
+ case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity
+ case E_BLOCK_FLOWER_POT: Action = 5; break; // Update flower pot
+ default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
+ }
+ Pkt.WriteByte(Action);
+
+ Pkt.WriteBlockEntity(a_BlockEntity);
+}
+
+
+
+
+
+void cProtocol180::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)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x33);
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+
+ Json::StyledWriter JsonWriter;
+ AString Lines[] = { a_Line1, a_Line2, a_Line3, a_Line4 };
+ for (size_t i = 0; i < ARRAYCOUNT(Lines); i++)
+ {
+ Json::Value RootValue;
+ RootValue["text"] = Lines[i];
+ Pkt.WriteString(JsonWriter.write(RootValue).c_str());
+ }
+}
+
+
+
+
+
+void cProtocol180::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x0a);
+ Pkt.WriteVarInt(a_Entity.GetUniqueID());
+ Pkt.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+}
+
+
+
+
+
+void cProtocol180::SendWeather(eWeather a_Weather)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ {
+ cPacketizer Pkt(*this, 0x2b); // Change Game State packet
+ Pkt.WriteByte((a_Weather == wSunny) ? 1 : 2); // End rain / begin rain
+ Pkt.WriteFloat(0); // Unused for weather
+ }
+
+ // TODO: Fade effect, somehow
+}
+
+
+
+
+
+void cProtocol180::SendWholeInventory(const cWindow & a_Window)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x30); // Window Items packet
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteShort(a_Window.GetNumSlots());
+ cItems Slots;
+ a_Window.GetSlots(*(m_Client->GetPlayer()), Slots);
+ for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr)
+ {
+ Pkt.WriteItem(*itr);
+ } // for itr - Slots[]
+}
+
+
+
+
+
+void cProtocol180::SendWindowClose(const cWindow & a_Window)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x2e);
+ Pkt.WriteChar(a_Window.GetWindowID());
+}
+
+
+
+
+
+void cProtocol180::SendWindowOpen(const cWindow & a_Window)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ if (a_Window.GetWindowType() < 0)
+ {
+ // Do not send this packet for player inventory windows
+ return;
+ }
+
+ cPacketizer Pkt(*this, 0x2d);
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteString(a_Window.GetWindowTypeName());
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", a_Window.GetWindowTitle().c_str()));
+
+ switch (a_Window.GetWindowType())
+ {
+ case cWindow::wtWorkbench:
+ case cWindow::wtEnchantment:
+ case cWindow::wtAnvil:
+ {
+ Pkt.WriteChar(0);
+ break;
+ }
+ default:
+ {
+ Pkt.WriteChar(a_Window.GetNumNonInventorySlots());
+ break;
+ }
+ }
+
+ if (a_Window.GetWindowType() == cWindow::wtAnimalChest)
+ {
+ Pkt.WriteInt(0); // TODO: The animal's EntityID
+ }
+}
+
+
+
+
+
+void cProtocol180::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value)
+{
+ ASSERT(m_State == 3); // In game mode?
+
+ cPacketizer Pkt(*this, 0x31); // Window Property packet
+ Pkt.WriteChar(a_Window.GetWindowID());
+ Pkt.WriteShort(a_Property);
+ Pkt.WriteShort(a_Value);
+}
+
+
+
+
+
+bool cProtocol180::CompressPacket(const AString & a_Packet, AString & a_CompressedData)
+{
+ // Compress the data:
+ char CompressedData[MAX_COMPRESSED_PACKET_LEN];
+
+ uLongf CompressedSize = compressBound(a_Packet.size());
+ if (CompressedSize >= MAX_COMPRESSED_PACKET_LEN)
+ {
+ ASSERT(!"Too high packet size.");
+ return false;
+ }
+
+ int Status = compress2((Bytef*)CompressedData, &CompressedSize, (const Bytef*)a_Packet.data(), a_Packet.size(), Z_DEFAULT_COMPRESSION);
+ if (Status != Z_OK)
+ {
+ return false;
+ }
+
+ AString LengthData;
+ cByteBuffer Buffer(20);
+ Buffer.WriteVarInt((UInt32)a_Packet.size());
+ Buffer.ReadAll(LengthData);
+ Buffer.CommitRead();
+
+ Buffer.WriteVarInt(CompressedSize + LengthData.size());
+ Buffer.WriteVarInt(a_Packet.size());
+ Buffer.ReadAll(LengthData);
+ Buffer.CommitRead();
+
+ a_CompressedData.clear();
+ a_CompressedData.reserve(LengthData.size() + CompressedSize);
+ a_CompressedData.append(LengthData.data(), LengthData.size());
+ a_CompressedData.append(CompressedData, CompressedSize);
+ return true;
+}
+
+
+
+
+
+int cProtocol180::GetParticleID(const AString & a_ParticleName)
+{
+ static bool IsInitialized = false;
+ static std::map<AString, int> ParticleMap;
+ if (!IsInitialized)
+ {
+ // Initialize the ParticleMap:
+ ParticleMap["explode"] = 0;
+ ParticleMap["largeexplode"] = 1;
+ ParticleMap["hugeexplosion"] = 2;
+ ParticleMap["fireworksspark"] = 3;
+ ParticleMap["bubble"] = 4;
+ ParticleMap["splash"] = 5;
+ ParticleMap["wake"] = 6;
+ ParticleMap["suspended"] = 7;
+ ParticleMap["depthsuspend"] = 8;
+ ParticleMap["crit"] = 9;
+ ParticleMap["magiccrit"] = 10;
+ ParticleMap["smoke"] = 11;
+ ParticleMap["largesmoke"] = 12;
+ ParticleMap["spell"] = 13;
+ ParticleMap["instantspell"] = 14;
+ ParticleMap["mobspell"] = 15;
+ ParticleMap["mobspellambient"] = 16;
+ ParticleMap["witchmagic"] = 17;
+ ParticleMap["dripwater"] = 18;
+ ParticleMap["driplava"] = 19;
+ ParticleMap["angryvillager"] = 20;
+ ParticleMap["happyVillager"] = 21;
+ ParticleMap["townaura"] = 22;
+ ParticleMap["note"] = 23;
+ ParticleMap["portal"] = 24;
+ ParticleMap["enchantmenttable"] = 25;
+ ParticleMap["flame"] = 26;
+ ParticleMap["lava"] = 27;
+ ParticleMap["footstep"] = 28;
+ ParticleMap["cloud"] = 29;
+ ParticleMap["reddust"] = 30;
+ ParticleMap["snowballpoof"] = 31;
+ ParticleMap["snowshovel"] = 32;
+ ParticleMap["slime"] = 33;
+ ParticleMap["heart"] = 34;
+ ParticleMap["barrier"] = 35;
+ ParticleMap["iconcrack"] = 36;
+ ParticleMap["blockcrack"] = 37;
+ ParticleMap["blockdust"] = 38;
+ ParticleMap["droplet"] = 39;
+ ParticleMap["take"] = 40;
+ ParticleMap["mobappearance"] = 41;
+ }
+
+ AString ParticleName = StrToLower(a_ParticleName);
+ if (ParticleMap.find(ParticleName) == ParticleMap.end())
+ {
+ LOGWARNING("Unknown particle: %s", a_ParticleName.c_str());
+ ASSERT(!"Unknown particle");
+ return 0;
+ }
+
+ return ParticleMap[ParticleName];
+}
+
+
+
+
+
+void cProtocol180::FixItemFramePositions(int a_ObjectData, double & a_PosX, double & a_PosZ, double & a_Yaw)
+{
+ switch (a_ObjectData)
+ {
+ case 0:
+ {
+ a_PosZ += 1;
+ a_Yaw = 0;
+ break;
+ }
+ case 1:
+ {
+ a_PosX -= 1;
+ a_Yaw = 90;
+ break;
+ }
+ case 2:
+ {
+ a_PosZ -= 1;
+ a_Yaw = 180;
+ break;
+ }
+ case 3:
+ {
+ a_PosX += 1;
+ a_Yaw = 270;
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size)
+{
+ // Write the incoming data into the comm log file:
+ if (g_ShouldLogCommIn)
+ {
+ if (m_ReceivedData.GetReadableSpace() > 0)
+ {
+ AString AllData;
+ size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadAll(AllData);
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("Incoming data, " SIZE_T_FMT " (0x" SIZE_T_FMT_HEX ") unparsed bytes already present in buffer:\n%s\n",
+ AllData.size(), AllData.size(), Hex.c_str()
+ );
+ }
+ AString Hex;
+ CreateHexDump(Hex, a_Data, a_Size, 16);
+ m_CommLogFile.Printf("Incoming data: %d (0x%x) bytes: \n%s\n",
+ (unsigned)a_Size, (unsigned)a_Size, Hex.c_str()
+ );
+ m_CommLogFile.Flush();
+ }
+
+ if (!m_ReceivedData.Write(a_Data, a_Size))
+ {
+ // Too much data in the incoming queue, report to caller:
+ m_Client->PacketBufferFull();
+ return;
+ }
+
+ // Handle all complete packets:
+ for (;;)
+ {
+ UInt32 PacketLen;
+ if (!m_ReceivedData.ReadVarInt(PacketLen))
+ {
+ // Not enough data
+ m_ReceivedData.ResetRead();
+ break;
+ }
+ if (!m_ReceivedData.CanReadBytes(PacketLen))
+ {
+ // The full packet hasn't been received yet
+ m_ReceivedData.ResetRead();
+ break;
+ }
+
+ // Check packet for compression:
+ UInt32 CompressedSize = 0;
+ AString UncompressedData;
+ if (m_State == 3)
+ {
+ UInt32 NumBytesRead = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadVarInt(CompressedSize);
+ if (CompressedSize > PacketLen)
+ {
+ m_Client->Kick("Bad compression");
+ return;
+ }
+ if (CompressedSize > 0)
+ {
+ // Decompress the data:
+ AString CompressedData;
+ m_ReceivedData.ReadString(CompressedData, CompressedSize);
+ InflateString(CompressedData.data(), CompressedSize, UncompressedData);
+ PacketLen = UncompressedData.size();
+ }
+ else
+ {
+ NumBytesRead -= m_ReceivedData.GetReadableSpace(); // How many bytes has the CompressedSize taken up?
+ ASSERT(PacketLen > NumBytesRead);
+ PacketLen -= NumBytesRead;
+ }
+ }
+
+ // Move the packet payload to a separate cByteBuffer, bb:
+ cByteBuffer bb(PacketLen + 1);
+ if (CompressedSize == 0)
+ {
+ // No compression was used, move directly
+ VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen));
+ }
+ else
+ {
+ // Compression was used, move the uncompressed data:
+ VERIFY(bb.Write(UncompressedData.data(), UncompressedData.size()));
+ }
+ m_ReceivedData.CommitRead();
+
+ UInt32 PacketType;
+ if (!bb.ReadVarInt(PacketType))
+ {
+ // Not enough data
+ break;
+ }
+
+ // Write one NUL extra, so that we can detect over-reads
+ bb.Write("\0", 1);
+
+ // Log the packet info into the comm log file:
+ if (g_ShouldLogCommIn)
+ {
+ AString PacketData;
+ bb.ReadAll(PacketData);
+ bb.ResetRead();
+ bb.ReadVarInt(PacketType);
+ ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read
+ PacketData.resize(PacketData.size() - 1);
+ AString PacketDataHex;
+ CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16);
+ m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n",
+ PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str()
+ );
+ }
+
+ if (!HandlePacket(bb, PacketType))
+ {
+ // Unknown packet, already been reported, but without the length. Log the length here:
+ LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen);
+
+ #ifdef _DEBUG
+ // Dump the packet contents into the log:
+ bb.ResetRead();
+ AString Packet;
+ bb.ReadAll(Packet);
+ Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection
+ AString Out;
+ CreateHexDump(Out, Packet.data(), (int)Packet.size(), 24);
+ LOGD("Packet contents:\n%s", Out.c_str());
+ #endif // _DEBUG
+
+ // Put a message in the comm log:
+ if (g_ShouldLogCommIn)
+ {
+ m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n");
+ }
+
+ return;
+ }
+
+ // The packet should have 1 byte left in the buffer - the NUL we had added
+ if (bb.GetReadableSpace() != 1)
+ {
+ // Read more or less than packet length, report as error
+ LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read " SIZE_T_FMT " bytes, packet contained %u bytes",
+ PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen
+ );
+
+ // Put a message in the comm log:
+ if (g_ShouldLogCommIn)
+ {
+ m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got " SIZE_T_FMT " left) ^^^^^^\n\n\n",
+ 1, bb.GetReadableSpace()
+ );
+ m_CommLogFile.Flush();
+ }
+
+ ASSERT(!"Read wrong number of bytes!");
+ m_Client->PacketError(PacketType);
+ }
+ } // for (ever)
+
+ // Log any leftover bytes into the logfile:
+ if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0))
+ {
+ AString AllData;
+ size_t OldReadableSpace = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadAll(AllData);
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("There are " SIZE_T_FMT " (0x" SIZE_T_FMT_HEX ") bytes of non-parse-able data left in the buffer:\n%s",
+ m_ReceivedData.GetReadableSpace(), m_ReceivedData.GetReadableSpace(), Hex.c_str()
+ );
+ m_CommLogFile.Flush();
+ }
+}
+
+
+
+
+bool cProtocol180::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
+{
+ switch (m_State)
+ {
+ case 1:
+ {
+ // Status
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true;
+ case 0x01: HandlePacketStatusPing (a_ByteBuffer); return true;
+ }
+ break;
+ }
+
+ case 2:
+ {
+ // Login
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketLoginStart (a_ByteBuffer); return true;
+ case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true;
+ }
+ break;
+ }
+
+ case 3:
+ {
+ // Game
+ switch (a_PacketType)
+ {
+ case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return true;
+ case 0x01: HandlePacketChatMessage (a_ByteBuffer); return true;
+ case 0x02: HandlePacketUseEntity (a_ByteBuffer); return true;
+ case 0x03: HandlePacketPlayer (a_ByteBuffer); return true;
+ case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return true;
+ case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return true;
+ case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return true;
+ case 0x07: HandlePacketBlockDig (a_ByteBuffer); return true;
+ case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return true;
+ case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return true;
+ case 0x0a: HandlePacketAnimation (a_ByteBuffer); return true;
+ case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return true;
+ case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return true;
+ case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return true;
+ case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return true;
+ case 0x0f: // Confirm transaction - not used in MCS
+ case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
+ case 0x11: HandlePacketEnchantItem (a_ByteBuffer); return true;
+ case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return true;
+ case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return true;
+ case 0x14: HandlePacketTabComplete (a_ByteBuffer); return true;
+ case 0x15: HandlePacketClientSettings (a_ByteBuffer); return true;
+ case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true;
+ case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true;
+ }
+ break;
+ }
+ default:
+ {
+ // Received a packet in an unknown state, report:
+ LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State);
+
+ // Cannot kick the client - we don't know this state and thus the packet number for the kick packet
+
+ // Switch to a state when all further packets are silently ignored:
+ m_State = 255;
+ return false;
+ }
+ case 255:
+ {
+ // This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
+ // Do not output anything (the caller will do that for us), just return failure
+ return false;
+ }
+ } // switch (m_State)
+
+ // Unknown packet type, report to the ClientHandle:
+ m_Client->PacketUnknown(a_PacketType);
+ return false;
+}
+
+
+
+
+
+void cProtocol180::HandlePacketStatusPing(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEInt64, Int64, Timestamp);
+
+ cPacketizer Pkt(*this, 0x01); // Ping packet
+ Pkt.WriteInt64(Timestamp);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
+{
+ cServer * Server = cRoot::Get()->GetServer();
+ AString ServerDescription = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
+
+ // Version:
+ Json::Value Version;
+ Version["name"] = "1.8";
+ Version["protocol"] = 47;
+
+ // Players:
+ Json::Value Players;
+ Players["online"] = NumPlayers;
+ Players["max"] = MaxPlayers;
+ // TODO: Add "sample"
+
+ // Description:
+ Json::Value Description;
+ Description["text"] = ServerDescription.c_str();
+
+ // Create the response:
+ Json::Value ResponseValue;
+ ResponseValue["version"] = Version;
+ ResponseValue["players"] = Players;
+ ResponseValue["description"] = Description;
+ if (!Favicon.empty())
+ {
+ ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
+ }
+
+ Json::StyledWriter Writer;
+ AString Response = Writer.write(ResponseValue);
+
+ cPacketizer Pkt(*this, 0x00); // Response packet
+ Pkt.WriteString(Response);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer)
+{
+ UInt32 EncKeyLength, EncNonceLength;
+ if (!a_ByteBuffer.ReadVarInt(EncKeyLength))
+ {
+ return;
+ }
+ AString EncKey;
+ if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
+ {
+ return;
+ }
+ if (!a_ByteBuffer.ReadVarInt(EncNonceLength))
+ {
+ return;
+ }
+ AString EncNonce;
+ if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
+ {
+ return;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt EncNonce using privkey
+ cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+ Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
+ int res = rsaDecryptor.Decrypt((const Byte *)EncNonce.data(), EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
+ if (res != 4)
+ {
+ LOGD("Bad nonce length: got %d, exp %d", res, 4);
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ Byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt((const Byte *)EncKey.data(), EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
+ if (res != 16)
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ StartEncryption(DecryptedKey);
+ m_Client->HandleLogin(4, m_Client->GetUsername());
+}
+
+
+
+
+
+void cProtocol180::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
+{
+ AString Username;
+ if (!a_ByteBuffer.ReadVarUTF8String(Username))
+ {
+ m_Client->Kick("Bad username");
+ return;
+ }
+
+ if (!m_Client->HandleHandshake(Username))
+ {
+ // The client is not welcome here, they have been sent a Kick packet already
+ return;
+ }
+
+ cServer * Server = cRoot::Get()->GetServer();
+ // If auth is required, then send the encryption request:
+ if (Server->ShouldAuthenticate())
+ {
+ cPacketizer Pkt(*this, 0x01);
+ Pkt.WriteString(Server->GetServerID());
+ const AString & PubKeyDer = Server->GetPublicKeyDER();
+ Pkt.WriteVarInt((short)PubKeyDer.size());
+ Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size());
+ Pkt.WriteVarInt(4);
+ Pkt.WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ m_Client->SetUsername(Username);
+ return;
+ }
+
+ m_Client->HandleLogin(4, Username);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketAnimation(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, EntityID);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Animation);
+ m_Client->HandleAnimation(Animation);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketBlockDig(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Status);
+
+ int BlockX, BlockY, BlockZ;
+ if (!a_ByteBuffer.ReadPosition(BlockX, BlockY, BlockZ))
+ {
+ return;
+ }
+
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Face);
+ m_Client->HandleLeftClick(BlockX, BlockY, BlockZ, static_cast<eBlockFace>(Face), Status);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketBlockPlace(cByteBuffer & a_ByteBuffer)
+{
+ int BlockX, BlockY, BlockZ;
+ if (!a_ByteBuffer.ReadPosition(BlockX, BlockY, BlockZ))
+ {
+ return;
+ }
+
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Face);
+ if (Face == 255)
+ {
+ Face = 0;
+ }
+
+ cItem Item;
+ ReadItem(a_ByteBuffer, Item, 3);
+
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, CursorX);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, CursorY);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, CursorZ);
+ m_Client->HandleRightClick(BlockX, BlockY, BlockZ, static_cast<eBlockFace>(Face), CursorX, CursorY, CursorZ, m_Client->GetPlayer()->GetEquippedItem());
+}
+
+
+
+
+
+void cProtocol180::HandlePacketChatMessage(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Message);
+ m_Client->HandleChat(Message);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Locale);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ViewDistance);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ChatFlags);
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, ChatColors);
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, SkinFlags);
+
+ m_Client->SetLocale(Locale);
+ // TODO: Handle other values
+}
+
+
+
+
+
+void cProtocol180::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, ActionID);
+ switch (ActionID)
+ {
+ case 0:
+ {
+ // Respawn
+ m_Client->HandleRespawn();
+ break;
+ }
+ case 1:
+ {
+ // Request stats
+ const cStatManager & Manager = m_Client->GetPlayer()->GetStatManager();
+ SendStatistics(Manager);
+
+ break;
+ }
+ case 2:
+ {
+ // Open Inventory achievement
+ m_Client->GetPlayer()->AwardAchievement(achOpenInv);
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol180::HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEShort, short, SlotNum);
+ cItem Item;
+ if (!ReadItem(a_ByteBuffer, Item))
+ {
+ return;
+ }
+ m_Client->HandleCreativeInventory(SlotNum, Item);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketEntityAction(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, PlayerID);
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, Action);
+ HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, JumpBoost);
+
+ switch (Action)
+ {
+ case 0: m_Client->HandleEntityCrouch(PlayerID, true); break; // Crouch
+ case 1: m_Client->HandleEntityCrouch(PlayerID, false); break; // Uncrouch
+ case 2: m_Client->HandleEntityLeaveBed(PlayerID); break; // Leave Bed
+ case 3: m_Client->HandleEntitySprinting(PlayerID, true); break; // Start sprinting
+ case 4: m_Client->HandleEntitySprinting(PlayerID, false); break; // Stop sprinting
+ }
+}
+
+
+
+
+
+void cProtocol180::HandlePacketKeepAlive(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, KeepAliveID);
+ m_Client->HandleKeepAlive((int)KeepAliveID);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPlayer(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
+ // TODO: m_Client->HandlePlayerOnGround(IsOnGround);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPlayerAbilities(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Flags);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, FlyingSpeed);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, WalkingSpeed);
+
+ bool IsFlying = false, CanFly = false;
+ if ((Flags & 2) != 0)
+ {
+ IsFlying = true;
+ }
+ if ((Flags & 4) != 0)
+ {
+ CanFly = true;
+ }
+
+ m_Client->HandlePlayerAbilities(CanFly, IsFlying, FlyingSpeed, WalkingSpeed);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPlayerLook(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Yaw);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Pitch);
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerLook(Yaw, Pitch, IsOnGround);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPlayerPos(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosX);
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY);
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ);
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerPos(PosX, PosY, PosZ, PosY + 1.62, IsOnGround);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPlayerPosLook(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosX);
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosY);
+ HANDLE_READ(a_ByteBuffer, ReadBEDouble, double, PosZ);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Yaw);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Pitch);
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, IsOnGround);
+ m_Client->HandlePlayerMoveLook(PosX, PosY, PosZ, PosY + 1.62, Yaw, Pitch, IsOnGround);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Channel);
+
+ // If the plugin channel is recognized vanilla, handle it directly:
+ if (Channel.substr(0, 3) == "MC|")
+ {
+ HandleVanillaPluginMessage(a_ByteBuffer, Channel);
+ return;
+ }
+
+ // Read the plugin message and relay to clienthandle:
+ AString Data;
+ VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds
+ m_Client->HandlePluginMessage(Channel, Data);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEShort, short, SlotNum);
+ m_Client->HandleSlotSelected(SlotNum);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Forward);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, Flags);
+
+ if ((Flags & 0x2) != 0)
+ {
+ m_Client->HandleUnmount();
+ }
+ else if ((Flags & 0x1) != 0)
+ {
+ m_Client->HandleSteerVehicle(Forward, Sideways);
+ }
+}
+
+
+
+
+
+void cProtocol180::HandlePacketTabComplete(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Text);
+ HANDLE_READ(a_ByteBuffer, ReadBool, bool, HasPosition);
+
+ if (HasPosition)
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt64, Int64, Position);
+ }
+
+ m_Client->HandleTabCompletion(Text);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketUpdateSign(cByteBuffer & a_ByteBuffer)
+{
+ int BlockX, BlockY, BlockZ;
+ if (!a_ByteBuffer.ReadPosition(BlockX, BlockY, BlockZ))
+ {
+ return;
+ }
+
+ AString Lines[4];
+ for (int i = 0; i < 4; i++)
+ {
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Line);
+ Lines[i] = Line.substr(1, Line.length() - 2); // Remove ""
+ }
+
+ m_Client->HandleUpdateSign(BlockX, BlockY, BlockZ, Lines[0], Lines[1], Lines[2], Lines[3]);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketUseEntity(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, EntityID);
+ HANDLE_READ(a_ByteBuffer, ReadVarInt, UInt32, Type);
+
+ switch (Type)
+ {
+ case 0:
+ {
+ m_Client->HandleUseEntity((int)EntityID, false);
+ break;
+ }
+ case 1:
+ {
+ m_Client->HandleUseEntity((int)EntityID, true);
+ break;
+ }
+ case 2:
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetX);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetY);
+ HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, TargetZ);
+
+ // TODO: Do anything
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled use entity type!");
+ return;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol180::HandlePacketEnchantItem(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, WindowID);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Enchantment);
+
+ m_Client->HandleEnchantItem(WindowID, Enchantment);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketWindowClick(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, WindowID);
+ HANDLE_READ(a_ByteBuffer, ReadBEShort, short, SlotNum);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Button);
+ HANDLE_READ(a_ByteBuffer, ReadBEShort, short, TransactionID);
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode);
+ cItem Item;
+ ReadItem(a_ByteBuffer, Item);
+
+ // Convert Button, Mode, SlotNum and HeldItem into eClickAction:
+ eClickAction Action;
+ switch ((Mode << 8) | Button)
+ {
+ case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
+ case 0x0001: Action = (SlotNum != -999) ? caRightClick : caRightClickOutside; break;
+ case 0x0100: Action = caShiftLeftClick; break;
+ case 0x0101: Action = caShiftRightClick; break;
+ case 0x0200: Action = caNumber1; break;
+ case 0x0201: Action = caNumber2; break;
+ case 0x0202: Action = caNumber3; break;
+ case 0x0203: Action = caNumber4; break;
+ case 0x0204: Action = caNumber5; break;
+ case 0x0205: Action = caNumber6; break;
+ case 0x0206: Action = caNumber7; break;
+ case 0x0207: Action = caNumber8; break;
+ case 0x0208: Action = caNumber9; break;
+ case 0x0300: Action = caMiddleClick; break;
+ case 0x0400: Action = (SlotNum == -999) ? caLeftClickOutsideHoldNothing : caDropKey; break;
+ case 0x0401: Action = (SlotNum == -999) ? caRightClickOutsideHoldNothing : caCtrlDropKey; break;
+ case 0x0500: Action = (SlotNum == -999) ? caLeftPaintBegin : caUnknown; break;
+ case 0x0501: Action = (SlotNum != -999) ? caLeftPaintProgress : caUnknown; break;
+ case 0x0502: Action = (SlotNum == -999) ? caLeftPaintEnd : caUnknown; break;
+ case 0x0504: Action = (SlotNum == -999) ? caRightPaintBegin : caUnknown; break;
+ case 0x0505: Action = (SlotNum != -999) ? caRightPaintProgress : caUnknown; break;
+ case 0x0506: Action = (SlotNum == -999) ? caRightPaintEnd : caUnknown; break;
+ case 0x0600: Action = caDblClick; break;
+ default:
+ {
+ LOGWARNING("Unhandled window click mode / button combination: %d (0x%x)", (Mode << 8) | Button, (Mode << 8) | Button);
+ Action = caUnknown;
+ break;
+ }
+ }
+
+ m_Client->HandleWindowClick(WindowID, SlotNum, Action, Item);
+}
+
+
+
+
+
+void cProtocol180::HandlePacketWindowClose(cByteBuffer & a_ByteBuffer)
+{
+ HANDLE_READ(a_ByteBuffer, ReadChar, char, WindowID);
+ m_Client->HandleWindowClose(WindowID);
+}
+
+
+
+
+
+void cProtocol180::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel)
+{
+ if (a_Channel == "MC|AdvCdm")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode)
+ switch (Mode)
+ {
+ case 0x00:
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockY);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ);
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Command);
+ m_Client->HandleCommandBlockBlockChange(BlockX, BlockY, BlockZ, Command);
+ break;
+ }
+
+ default:
+ {
+ m_Client->SendChat(Printf("Failure setting command block command; unhandled mode %d", Mode), mtFailure);
+ LOG("Unhandled MC|AdvCdm packet mode.");
+ return;
+ }
+ } // switch (Mode)
+ return;
+ }
+ else if (a_Channel == "MC|Brand")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand);
+ m_Client->SetClientBrand(Brand);
+ // Send back our brand, including the length:
+ SendPluginMessage("MC|Brand", "\x08MCServer");
+ return;
+ }
+ else if (a_Channel == "MC|Beacon")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect1);
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, Effect2);
+ m_Client->HandleBeaconSelection(Effect1, Effect2);
+ return;
+ }
+ else if (a_Channel == "MC|ItemName")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, ItemName);
+ m_Client->HandleAnvilItemName(ItemName);
+ return;
+ }
+ else if (a_Channel == "MC|TrSel")
+ {
+ HANDLE_READ(a_ByteBuffer, ReadBEInt, int, SlotNum);
+ m_Client->HandleNPCTrade(SlotNum);
+ return;
+ }
+ LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str());
+
+ // Read the payload and send it through to the clienthandle:
+ AString Message;
+ VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1));
+ m_Client->HandlePluginMessage(a_Channel, Message);
+}
+
+
+
+
+
+void cProtocol180::SendData(const char * a_Data, size_t a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ size_t NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes);
+ m_Client->SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ m_Client->SendData(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+bool cProtocol180::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes)
+{
+ HANDLE_PACKET_READ(a_ByteBuffer, ReadBEShort, short, ItemType);
+ if (ItemType == -1)
+ {
+ // The item is empty, no more data follows
+ a_Item.Empty();
+ return true;
+ }
+ a_Item.m_ItemType = ItemType;
+
+ HANDLE_PACKET_READ(a_ByteBuffer, ReadChar, char, ItemCount);
+ HANDLE_PACKET_READ(a_ByteBuffer, ReadBEShort, short, ItemDamage);
+ a_Item.m_ItemCount = ItemCount;
+ a_Item.m_ItemDamage = ItemDamage;
+ if (ItemCount <= 0)
+ {
+ a_Item.Empty();
+ }
+
+ AString Metadata;
+ if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0))
+ {
+ // No metadata
+ return true;
+ }
+
+ ParseItemMetadata(a_Item, Metadata);
+ return true;
+}
+
+
+
+
+
+void cProtocol180::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+{
+ // Parse into NBT:
+ cParsedNBT NBT(a_Metadata.data(), a_Metadata.size());
+ if (!NBT.IsValid())
+ {
+ AString HexDump;
+ CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
+ LOGWARNING("Cannot parse NBT item metadata: (" SIZE_T_FMT " bytes)\n%s", a_Metadata.size(), HexDump.c_str());
+ return;
+ }
+
+ // Load enchantments and custom display names from the NBT data:
+ for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
+ {
+ AString TagName = NBT.GetName(tag);
+ switch (NBT.GetType(tag))
+ {
+ case TAG_List:
+ {
+ if ((TagName == "ench") || (TagName == "StoredEnchantments")) // Enchantments tags
+ {
+ EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
+ }
+ break;
+ }
+ case TAG_Compound:
+ {
+ if (TagName == "display") // Custom name and lore tag
+ {
+ for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag))
+ {
+ if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag
+ {
+ a_Item.m_CustomName = NBT.GetString(displaytag);
+ }
+ else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag
+ {
+ AString Lore;
+
+ for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
+ {
+ AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;)
+ }
+
+ a_Item.m_Lore = Lore;
+ }
+ }
+ }
+ else if ((TagName == "Fireworks") || (TagName == "Explosion"))
+ {
+ cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, NBT, tag, (ENUM_ITEM_ID)a_Item.m_ItemType);
+ }
+ break;
+ }
+ case TAG_Int:
+ {
+ if (TagName == "RepairCost")
+ {
+ a_Item.m_RepairCost = NBT.GetInt(tag);
+ }
+ }
+ default: LOGD("Unimplemented NBT data when parsing!"); break;
+ }
+ }
+}
+
+
+
+
+
+void cProtocol180::StartEncryption(const Byte * a_Key)
+{
+ m_Encryptor.Init(a_Key, a_Key);
+ m_Decryptor.Init(a_Key, a_Key);
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ cSha1Checksum Checksum;
+ cServer * Server = cRoot::Get()->GetServer();
+ const AString & ServerID = Server->GetServerID();
+ Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const Byte *)Server->GetPublicKeyDER().data(), Server->GetPublicKeyDER().size());
+ Byte Digest[20];
+ Checksum.Finalize(Digest);
+ cSha1Checksum::DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cProtocol180::cPacketizer:
+
+cProtocol180::cPacketizer::~cPacketizer()
+{
+ UInt32 PacketLen = (UInt32)m_Out.GetUsedSpace();
+ AString PacketData, CompressedPacket;
+ m_Out.ReadAll(PacketData);
+ m_Out.CommitRead();
+
+ if ((m_Protocol.m_State == 3) && (PacketLen >= 256))
+ {
+ if (!cProtocol180::CompressPacket(PacketData, CompressedPacket))
+ {
+ return;
+ }
+ }
+ else if (m_Protocol.m_State == 3)
+ {
+ m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen + 1);
+ m_Protocol.m_OutPacketLenBuffer.WriteVarInt(0);
+
+ AString LengthData;
+ m_Protocol.m_OutPacketLenBuffer.ReadAll(LengthData);
+ m_Protocol.SendData(LengthData.data(), LengthData.size());
+ }
+ else
+ {
+ m_Protocol.m_OutPacketLenBuffer.WriteVarInt(PacketLen);
+
+ AString LengthData;
+ m_Protocol.m_OutPacketLenBuffer.ReadAll(LengthData);
+ m_Protocol.SendData(LengthData.data(), LengthData.size());
+ }
+
+ if (CompressedPacket.empty())
+ {
+ m_Protocol.m_OutPacketLenBuffer.CommitRead();
+ m_Protocol.SendData(PacketData.data(), PacketData.size());
+ }
+ else
+ {
+ m_Protocol.SendData(CompressedPacket.data(), CompressedPacket.size());
+ }
+
+ // Log the comm into logfile:
+ if (g_ShouldLogCommOut)
+ {
+ AString Hex;
+ ASSERT(PacketData.size() > 0);
+ CreateHexDump(Hex, PacketData.data() + 1, PacketData.size() - 1, 16);
+ m_Protocol.m_CommLogFile.Printf("Outgoing packet: type %d (0x%x), length %u (0x%x), state %d. Payload:\n%s\n",
+ PacketData[0], PacketData[0], PacketLen, PacketLen, m_Protocol.m_State, Hex.c_str()
+ );
+ }
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteUUID(const AString & a_UUID)
+{
+ if (a_UUID.length() != 32)
+ {
+ LOGWARNING("Attempt to send a bad uuid (length isn't 32): %s", a_UUID.c_str());
+ ASSERT(!"Wrong uuid length!");
+ return;
+ }
+ AString UUID_1 = a_UUID.substr(0, 16);
+ AString UUID_2 = a_UUID.substr(16);
+
+ Int64 Value_1, Value_2;
+ sscanf(UUID_1.c_str(), "%llx", &Value_1);
+ sscanf(UUID_2.c_str(), "%llx", &Value_2);
+
+ WriteInt64(Value_1);
+ WriteInt64(Value_2);
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteItem(const cItem & a_Item)
+{
+ short ItemType = a_Item.m_ItemType;
+ ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
+ if (ItemType <= 0)
+ {
+ // Fix, to make sure no invalid values are sent.
+ ItemType = -1;
+ }
+
+ if (a_Item.IsEmpty())
+ {
+ WriteShort(-1);
+ return;
+ }
+
+ WriteShort(ItemType);
+ WriteByte (a_Item.m_ItemCount);
+ WriteShort(a_Item.m_ItemDamage);
+
+ if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR))
+ {
+ WriteChar(0);
+ return;
+ }
+
+ // Send the enchantments and custom names:
+ cFastNBTWriter Writer;
+ if (a_Item.m_RepairCost != 0)
+ {
+ Writer.AddInt("RepairCost", a_Item.m_RepairCost);
+ }
+ if (!a_Item.m_Enchantments.IsEmpty())
+ {
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName);
+ }
+ if (!a_Item.IsBothNameAndLoreEmpty())
+ {
+ Writer.BeginCompound("display");
+ if (!a_Item.IsCustomNameEmpty())
+ {
+ Writer.AddString("Name", a_Item.m_CustomName.c_str());
+ }
+ if (!a_Item.IsLoreEmpty())
+ {
+ Writer.BeginList("Lore", TAG_String);
+
+ AStringVector Decls = StringSplit(a_Item.m_Lore, "`");
+ for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr)
+ {
+ if (itr->empty())
+ {
+ // The decl is empty (two `s), ignore
+ continue;
+ }
+ Writer.AddString("", itr->c_str());
+ }
+
+ Writer.EndList();
+ }
+ Writer.EndCompound();
+ }
+ if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
+ {
+ cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, Writer, (ENUM_ITEM_ID)a_Item.m_ItemType);
+ }
+ Writer.Finish();
+
+ AString Result = Writer.GetResult();
+ if (Result.size() == 0)
+ {
+ WriteChar(0);
+ return;
+ }
+ WriteBuf(Result.data(), Result.size());
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEntity)
+{
+ cFastNBTWriter Writer;
+
+ switch (a_BlockEntity.GetBlockType())
+ {
+ case E_BLOCK_BEACON:
+ {
+ cBeaconEntity & BeaconEntity = (cBeaconEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", BeaconEntity.GetPosX());
+ Writer.AddInt("y", BeaconEntity.GetPosY());
+ Writer.AddInt("z", BeaconEntity.GetPosZ());
+ Writer.AddInt("Primary", BeaconEntity.GetPrimaryEffect());
+ Writer.AddInt("Secondary", BeaconEntity.GetSecondaryEffect());
+ Writer.AddInt("Levels", BeaconEntity.GetBeaconLevel());
+ Writer.AddString("id", "Beacon"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+ break;
+ }
+ case E_BLOCK_COMMAND_BLOCK:
+ {
+ cCommandBlockEntity & CommandBlockEntity = (cCommandBlockEntity &)a_BlockEntity;
+
+ Writer.AddByte("TrackOutput", 1); // Neither I nor the MC wiki has any idea about this
+ Writer.AddInt("SuccessCount", CommandBlockEntity.GetResult());
+ Writer.AddInt("x", CommandBlockEntity.GetPosX());
+ Writer.AddInt("y", CommandBlockEntity.GetPosY());
+ Writer.AddInt("z", CommandBlockEntity.GetPosZ());
+ Writer.AddString("Command", CommandBlockEntity.GetCommand().c_str());
+ // You can set custom names for windows in Vanilla
+ // For a command block, this would be the 'name' prepended to anything it outputs into global chat
+ // MCS doesn't have this, so just leave it @ '@'. (geddit?)
+ Writer.AddString("CustomName", "@");
+ Writer.AddString("id", "Control"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+
+ if (!CommandBlockEntity.GetLastOutput().empty())
+ {
+ AString Output;
+ Printf(Output, "{\"text\":\"%s\"}", CommandBlockEntity.GetLastOutput().c_str());
+
+ Writer.AddString("LastOutput", Output.c_str());
+ }
+ break;
+ }
+ case E_BLOCK_HEAD:
+ {
+ cMobHeadEntity & MobHeadEntity = (cMobHeadEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", MobHeadEntity.GetPosX());
+ Writer.AddInt("y", MobHeadEntity.GetPosY());
+ Writer.AddInt("z", MobHeadEntity.GetPosZ());
+ Writer.AddByte("SkullType", MobHeadEntity.GetType() & 0xFF);
+ Writer.AddByte("Rot", MobHeadEntity.GetRotation() & 0xFF);
+ Writer.AddString("ExtraType", MobHeadEntity.GetOwner().c_str());
+ Writer.AddString("id", "Skull"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+ break;
+ }
+ case E_BLOCK_FLOWER_POT:
+ {
+ cFlowerPotEntity & FlowerPotEntity = (cFlowerPotEntity &)a_BlockEntity;
+
+ Writer.AddInt("x", FlowerPotEntity.GetPosX());
+ Writer.AddInt("y", FlowerPotEntity.GetPosY());
+ Writer.AddInt("z", FlowerPotEntity.GetPosZ());
+ Writer.AddInt("Item", (Int32) FlowerPotEntity.GetItem().m_ItemType);
+ Writer.AddInt("Data", (Int32) FlowerPotEntity.GetItem().m_ItemDamage);
+ Writer.AddString("id", "FlowerPot"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+ break;
+ }
+ default: break;
+ }
+
+ Writer.Finish();
+ WriteBuf(Writer.GetResult().data(), Writer.GetResult().size());
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteByteAngle(double a_Angle)
+{
+ WriteByte((char)(255 * a_Angle / 360));
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteFPInt(double a_Value)
+{
+ int Value = (int)(a_Value * 32);
+ WriteInt(Value);
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
+{
+ // Common metadata:
+ Byte Flags = 0;
+ if (a_Entity.IsOnFire())
+ {
+ Flags |= 0x01;
+ }
+ if (a_Entity.IsCrouched())
+ {
+ Flags |= 0x02;
+ }
+ if (a_Entity.IsSprinting())
+ {
+ Flags |= 0x08;
+ }
+ if (a_Entity.IsRclking())
+ {
+ Flags |= 0x10;
+ }
+ if (a_Entity.IsInvisible())
+ {
+ Flags |= 0x20;
+ }
+ WriteByte(0); // Byte(0) + index 0
+ WriteByte(Flags);
+
+ switch (a_Entity.GetEntityType())
+ {
+ case cEntity::etPlayer: break; // TODO?
+ case cEntity::etPickup:
+ {
+ WriteByte((5 << 5) | 10); // Slot(5) + index 10
+ WriteItem(((const cPickup &)a_Entity).GetItem());
+ break;
+ }
+ case cEntity::etMinecart:
+ {
+ WriteByte(0x51);
+
+ // The following expression makes Minecarts shake more with less health or higher damage taken
+ // It gets half the maximum health, and takes it away from the current health minus the half health:
+ /*
+ Health: 5 | 3 - (5 - 3) = 1 (shake power)
+ Health: 3 | 3 - (3 - 3) = 3
+ Health: 1 | 3 - (1 - 3) = 5
+ */
+ WriteInt((((a_Entity.GetMaxHealth() / 2) - (a_Entity.GetHealth() - (a_Entity.GetMaxHealth() / 2))) * ((const cMinecart &)a_Entity).LastDamage()) * 4);
+ WriteByte(0x52);
+ WriteInt(1); // Shaking direction, doesn't seem to affect anything
+ WriteByte(0x73);
+ WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
+
+ if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpNone)
+ {
+ cRideableMinecart & RideableMinecart = ((cRideableMinecart &)a_Entity);
+ const cItem & MinecartContent = RideableMinecart.GetContent();
+ if (!MinecartContent.IsEmpty())
+ {
+ WriteByte(0x54);
+ int Content = MinecartContent.m_ItemType;
+ Content |= MinecartContent.m_ItemDamage << 8;
+ WriteInt(Content);
+ WriteByte(0x55);
+ WriteInt(RideableMinecart.GetBlockHeight());
+ WriteByte(0x56);
+ WriteByte(1);
+ }
+ }
+ else if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
+ {
+ WriteByte(0x10);
+ WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0);
+ }
+ break;
+ }
+ case cEntity::etProjectile:
+ {
+ cProjectileEntity & Projectile = (cProjectileEntity &)a_Entity;
+ switch (Projectile.GetProjectileKind())
+ {
+ case cProjectileEntity::pkArrow:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cArrowEntity &)a_Entity).IsCritical() ? 1 : 0);
+ break;
+ }
+ case cProjectileEntity::pkFirework:
+ {
+ WriteByte(0xA8);
+ WriteItem(((const cFireworkEntity &)a_Entity).GetItem());
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ case cEntity::etMonster:
+ {
+ WriteMobMetadata((const cMonster &)a_Entity);
+ break;
+ }
+ case cEntity::etItemFrame:
+ {
+ cItemFrame & Frame = (cItemFrame &)a_Entity;
+ WriteByte(0xA8);
+ WriteItem(Frame.GetItem());
+ WriteByte(0x09);
+ WriteByte(Frame.GetRotation());
+ break;
+ }
+ default: break;
+ }
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteMobMetadata(const cMonster & a_Mob)
+{
+ switch (a_Mob.GetMobType())
+ {
+ case mtCreeper:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1);
+ WriteByte(0x11);
+ WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0);
+ break;
+ }
+
+ case mtBat:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cBat &)a_Mob).IsHanging() ? 1 : 0);
+ break;
+ }
+
+ case mtPig:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cPig &)a_Mob).IsSaddled() ? 1 : 0);
+ break;
+ }
+
+ case mtVillager:
+ {
+ WriteByte(0x50);
+ WriteInt(((const cVillager &)a_Mob).GetVilType());
+ break;
+ }
+
+ case mtZombie:
+ {
+ WriteByte(0x0c);
+ WriteByte(((const cZombie &)a_Mob).IsBaby() ? 1 : 0);
+ WriteByte(0x0d);
+ WriteByte(((const cZombie &)a_Mob).IsVillagerZombie() ? 1 : 0);
+ WriteByte(0x0e);
+ WriteByte(((const cZombie &)a_Mob).IsConverting() ? 1 : 0);
+ break;
+ }
+
+ case mtGhast:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cGhast &)a_Mob).IsCharging());
+ break;
+ }
+
+ case mtWolf:
+ {
+ const cWolf & Wolf = (const cWolf &)a_Mob;
+ Byte WolfStatus = 0;
+ if (Wolf.IsSitting())
+ {
+ WolfStatus |= 0x1;
+ }
+ if (Wolf.IsAngry())
+ {
+ WolfStatus |= 0x2;
+ }
+ if (Wolf.IsTame())
+ {
+ WolfStatus |= 0x4;
+ }
+ WriteByte(0x10);
+ WriteByte(WolfStatus);
+
+ WriteByte(0x72);
+ WriteFloat((float)(a_Mob.GetHealth()));
+ WriteByte(0x13);
+ WriteByte(Wolf.IsBegging() ? 1 : 0);
+ WriteByte(0x14);
+ WriteByte(Wolf.GetCollarColor());
+ break;
+ }
+
+ case mtSheep:
+ {
+ WriteByte(0x10);
+ Byte SheepMetadata = 0;
+ SheepMetadata = ((const cSheep &)a_Mob).GetFurColor();
+ if (((const cSheep &)a_Mob).IsSheared())
+ {
+ SheepMetadata |= 0x10;
+ }
+ WriteByte(SheepMetadata);
+ break;
+ }
+
+ case mtEnderman:
+ {
+ WriteByte(0x30);
+ WriteShort((Byte)(((const cEnderman &)a_Mob).GetCarriedBlock()));
+ WriteByte(0x11);
+ WriteByte((Byte)(((const cEnderman &)a_Mob).GetCarriedMeta()));
+ WriteByte(0x12);
+ WriteByte(((const cEnderman &)a_Mob).IsScreaming() ? 1 : 0);
+ break;
+ }
+
+ case mtSkeleton:
+ {
+ WriteByte(0x0d);
+ WriteByte(((const cSkeleton &)a_Mob).IsWither() ? 1 : 0);
+ break;
+ }
+
+ case mtWitch:
+ {
+ WriteByte(0x15);
+ WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0);
+ break;
+ }
+
+ case mtWither:
+ {
+ WriteByte(0x54); // Int at index 20
+ WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks());
+ WriteByte(0x66); // Float at index 6
+ WriteFloat((float)(a_Mob.GetHealth()));
+ break;
+ }
+
+ case mtSlime:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cSlime &)a_Mob).GetSize());
+ break;
+ }
+
+ case mtMagmaCube:
+ {
+ WriteByte(0x10);
+ WriteByte(((const cMagmaCube &)a_Mob).GetSize());
+ break;
+ }
+
+ case mtHorse:
+ {
+ const cHorse & Horse = (const cHorse &)a_Mob;
+ int Flags = 0;
+ if (Horse.IsTame())
+ {
+ Flags |= 0x02;
+ }
+ if (Horse.IsSaddled())
+ {
+ Flags |= 0x04;
+ }
+ if (Horse.IsChested())
+ {
+ Flags |= 0x08;
+ }
+ if (Horse.IsBaby())
+ {
+ Flags |= 0x10;
+ }
+ if (Horse.IsEating())
+ {
+ Flags |= 0x20;
+ }
+ if (Horse.IsRearing())
+ {
+ Flags |= 0x40;
+ }
+ if (Horse.IsMthOpen())
+ {
+ Flags |= 0x80;
+ }
+ WriteByte(0x50); // Int at index 16
+ WriteInt(Flags);
+ WriteByte(0x13); // Byte at index 19
+ WriteByte(Horse.GetHorseType());
+ WriteByte(0x54); // Int at index 20
+ int Appearance = 0;
+ Appearance = Horse.GetHorseColor();
+ Appearance |= Horse.GetHorseStyle() << 8;
+ WriteInt(Appearance);
+ WriteByte(0x56); // Int at index 22
+ WriteInt(Horse.GetHorseArmour());
+ break;
+ }
+ } // switch (a_Mob.GetType())
+}
+
+
+
+
+
+void cProtocol180::cPacketizer::WriteEntityProperties(const cEntity & a_Entity)
+{
+ if (!a_Entity.IsMob())
+ {
+ // No properties for anything else than mobs
+ WriteInt(0);
+ return;
+ }
+
+ // const cMonster & Mob = (const cMonster &)a_Entity;
+
+ // TODO: Send properties and modifiers based on the mob type
+
+ WriteInt(0); // NumProperties
+}
+
+
+
diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h
new file mode 100644
index 000000000..8c0b77a21
--- /dev/null
+++ b/src/Protocol/Protocol18x.h
@@ -0,0 +1,338 @@
+
+// Protocol18x.h
+
+/*
+Declares the 1.8.x protocol classes:
+ - cProtocol180
+ - release 1.8.0 protocol (#47)
+(others may be added later in the future for the 1.8 release series)
+*/
+
+
+
+
+
+#pragma once
+
+#include "Protocol.h"
+#include "../ByteBuffer.h"
+
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4702)
+#endif
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
+#include "PolarSSL++/AesCfb128Decryptor.h"
+#include "PolarSSL++/AesCfb128Encryptor.h"
+
+
+
+
+
+// fwd:
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+class cProtocol180 :
+ public cProtocol
+{
+ typedef cProtocol super;
+
+public:
+
+ cProtocol180(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
+
+ /** Called when client sends some data: */
+ virtual void DataReceived(const char * a_Data, size_t a_Size) override;
+
+ /** Sending stuff to clients (alphabetically sorted): */
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendLoginSuccess (void) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
+ virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override;
+ virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
+ virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
+ virtual void SendExperience (void) override;
+ virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
+ virtual void SendTabCompletionResults (const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
+ virtual 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) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
+
+ virtual AString GetAuthServerID(void) override { return m_AuthServerID; }
+
+ /** Compress the packet. a_Packet must be without packet length.
+ a_Compressed will be set to the compressed packet includes packet length and data length.
+ If compression fails, the function returns false. */
+ static bool CompressPacket(const AString & a_Packet, AString & a_Compressed);
+
+ /** The 1.8 protocol use a particle id instead of a string. This function converts the name to the id. If the name is incorrect, it returns 0. */
+ static int GetParticleID(const AString & a_ParticleName);
+
+ /** Minecraft 1.8 use other locations to spawn the item frame. This function converts the 1.7 positions to 1.8 positions. */
+ static void FixItemFramePositions(int a_ObjectData, double & a_PosX, double & a_PosZ, double & a_Yaw);
+
+protected:
+
+ /** Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed */
+ class cPacketizer
+ {
+ public:
+ cPacketizer(cProtocol180 & a_Protocol, UInt32 a_PacketType) :
+ m_Protocol(a_Protocol),
+ m_Out(a_Protocol.m_OutPacketBuffer),
+ m_Lock(a_Protocol.m_CSPacket)
+ {
+ m_Out.WriteVarInt(a_PacketType);
+ }
+
+ ~cPacketizer();
+
+ void WriteBool(bool a_Value)
+ {
+ m_Out.WriteBool(a_Value);
+ }
+
+ void WriteByte(Byte a_Value)
+ {
+ m_Out.WriteByte(a_Value);
+ }
+
+ void WriteChar(char a_Value)
+ {
+ m_Out.WriteChar(a_Value);
+ }
+
+ void WriteShort(short a_Value)
+ {
+ m_Out.WriteBEShort(a_Value);
+ }
+
+ void WriteInt(int a_Value)
+ {
+ m_Out.WriteBEInt(a_Value);
+ }
+
+ void WriteInt64(Int64 a_Value)
+ {
+ m_Out.WriteBEInt64(a_Value);
+ }
+
+ void WriteFloat(float a_Value)
+ {
+ m_Out.WriteBEFloat(a_Value);
+ }
+
+ void WriteDouble(double a_Value)
+ {
+ m_Out.WriteBEDouble(a_Value);
+ }
+
+ void WriteVarInt(UInt32 a_Value)
+ {
+ m_Out.WriteVarInt(a_Value);
+ }
+
+ void WriteString(const AString & a_Value)
+ {
+ m_Out.WriteVarUTF8String(a_Value);
+ }
+
+ void WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ)
+ {
+ m_Out.WritePosition(a_BlockX, a_BlockY, a_BlockZ);
+ }
+
+ void WriteUUID(const AString & a_UUID);
+
+ void WriteBuf(const char * a_Data, size_t a_Size)
+ {
+ m_Out.Write(a_Data, a_Size);
+ }
+
+ void WriteItem(const cItem & a_Item);
+ void WriteByteAngle(double a_Angle); // Writes the specified angle using a single byte
+ void WriteFPInt(double a_Value); // Writes the double value as a 27:5 fixed-point integer
+ void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f
+ void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob
+ void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field
+ void WriteBlockEntity(const cBlockEntity & a_BlockEntity);
+
+ protected:
+ cProtocol180 & m_Protocol;
+ cByteBuffer & m_Out;
+ cCSLock m_Lock;
+ } ;
+
+ AString m_ServerAddress;
+
+ UInt16 m_ServerPort;
+
+ AString m_AuthServerID;
+
+ /** State of the protocol. 1 = status, 2 = login, 3 = game */
+ UInt32 m_State;
+
+ /** Buffer for the received data */
+ cByteBuffer m_ReceivedData;
+
+ /** Buffer for composing the outgoing packets, through cPacketizer */
+ cByteBuffer m_OutPacketBuffer;
+
+ /** Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) */
+ cByteBuffer m_OutPacketLenBuffer;
+
+ bool m_IsEncrypted;
+
+ cAesCfb128Decryptor m_Decryptor;
+ cAesCfb128Encryptor m_Encryptor;
+
+ /** The logfile where the comm is logged, when g_ShouldLogComm is true */
+ cFile m_CommLogFile;
+
+ /** The dimension that was last sent to a player in a Respawn or Login packet.
+ Used to avoid Respawning into the same dimension, which confuses the client. */
+ eDimension m_LastSentDimension;
+
+
+ /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
+ void AddReceivedData(const char * a_Data, size_t a_Size);
+
+ /** Reads and handles the packet. The packet length and type have already been read.
+ Returns true if the packet was understood, false if it was an unknown packet
+ */
+ bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType);
+
+ // Packet handlers while in the Status state (m_State == 1):
+ void HandlePacketStatusPing(cByteBuffer & a_ByteBuffer);
+ void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer);
+
+ // Packet handlers while in the Login state (m_State == 2):
+ void HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer);
+ void HandlePacketLoginStart(cByteBuffer & a_ByteBuffer);
+
+ // Packet handlers while in the Game state (m_State == 3):
+ void HandlePacketAnimation (cByteBuffer & a_ByteBuffer);
+ void HandlePacketBlockDig (cByteBuffer & a_ByteBuffer);
+ void HandlePacketBlockPlace (cByteBuffer & a_ByteBuffer);
+ void HandlePacketChatMessage (cByteBuffer & a_ByteBuffer);
+ void HandlePacketClientSettings (cByteBuffer & a_ByteBuffer);
+ void HandlePacketClientStatus (cByteBuffer & a_ByteBuffer);
+ void HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffer);
+ void HandlePacketEntityAction (cByteBuffer & a_ByteBuffer);
+ void HandlePacketKeepAlive (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPlayer (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPlayerAbilities (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPlayerLook (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPlayerPos (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPlayerPosLook (cByteBuffer & a_ByteBuffer);
+ void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
+ void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
+ void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
+ void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
+ void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);
+ void HandlePacketUseEntity (cByteBuffer & a_ByteBuffer);
+ void HandlePacketEnchantItem (cByteBuffer & a_ByteBuffer);
+ void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer);
+ void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer);
+
+ /** Parses Vanilla plugin messages into specific ClientHandle calls.
+ The message payload is still in the bytebuffer, the handler reads it specifically for each handled channel */
+ void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel);
+
+
+ /** Sends the data to the client, encrypting them if needed. */
+ virtual void SendData(const char * a_Data, size_t a_Size) override;
+
+ void SendCompass(const cWorld & a_World);
+
+ /** Reads an item out of the received data, sets a_Item to the values read.
+ Returns false if not enough received data.
+ a_KeepRemainingBytes tells the function to keep that many bytes at the end of the buffer. */
+ virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes = 0);
+
+ /** Parses item metadata as read by ReadItem(), into the item enchantments. */
+ void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ void StartEncryption(const Byte * a_Key);
+
+} ;
+
+
+
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index a7fb7bcc2..15bcd03b1 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -7,17 +7,14 @@
#include "Globals.h"
#include "ProtocolRecognizer.h"
-#include "Protocol125.h"
-#include "Protocol132.h"
-#include "Protocol14x.h"
-#include "Protocol15x.h"
-#include "Protocol16x.h"
#include "Protocol17x.h"
+#include "Protocol18x.h"
#include "../ClientHandle.h"
#include "../Root.h"
#include "../Server.h"
#include "../World.h"
#include "../ChatColor.h"
+#include "Bindings/PluginManager.h"
@@ -26,7 +23,7 @@
cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) :
super(a_Client),
m_Protocol(NULL),
- m_Buffer(512)
+ m_Buffer(8192) // We need a larger buffer to support BungeeCord - it sends one huge packet at the start
{
}
@@ -48,19 +45,9 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
{
switch (a_ProtocolVersion)
{
- case PROTO_VERSION_1_2_5: return "1.2.5";
- case PROTO_VERSION_1_3_2: return "1.3.2";
- case PROTO_VERSION_1_4_2: return "1.4.2";
- case PROTO_VERSION_1_4_4: return "1.4.4";
- case PROTO_VERSION_1_4_6: return "1.4.6";
- case PROTO_VERSION_1_5_0: return "1.5";
- case PROTO_VERSION_1_5_2: return "1.5.2";
- case PROTO_VERSION_1_6_1: return "1.6.1";
- case PROTO_VERSION_1_6_2: return "1.6.2";
- case PROTO_VERSION_1_6_3: return "1.6.3";
- case PROTO_VERSION_1_6_4: return "1.6.4";
case PROTO_VERSION_1_7_2: return "1.7.2";
case PROTO_VERSION_1_7_6: return "1.7.6";
+ case PROTO_VERSION_1_8_0: return "1.8";
}
ASSERT(!"Unknown protocol version");
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
@@ -210,8 +197,13 @@ void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
else
{
// This is used when the client sends a server-ping, respond with the default packet:
- WriteByte (0xff); // PACKET_DISCONNECT
- WriteString(a_Reason);
+ static const int Packet = 0xff; // PACKET_DISCONNECT
+ SendData((const char *)&Packet, 1); // WriteByte()
+
+ AString UTF16 = UTF8ToRawBEUTF16(a_Reason.c_str(), a_Reason.length());
+ static const u_short Size = htons((u_short)(UTF16.size() / 2));
+ SendData((const char *)&Size, 2); // WriteShort()
+ SendData(UTF16.data(), UTF16.size()); // WriteString()
}
}
@@ -408,20 +400,20 @@ void cProtocolRecognizer::SendLoginSuccess(void)
-void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length, m_Scale);
}
-void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators, m_Scale);
}
@@ -438,10 +430,10 @@ void cProtocolRecognizer::SendMapInfo(int a_ID, unsigned int a_Scale)
-void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
+void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount);
+ m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount);
}
@@ -486,10 +478,50 @@ void cProtocolRecognizer::SendEntityAnimation(const cEntity & a_Entity, char a_A
-void cProtocolRecognizer::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline)
+void cProtocolRecognizer::SendPlayerListAddPlayer(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListAddPlayer(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListRemovePlayer(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListRemovePlayer(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListUpdateGameMode(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListUpdatePing(const cPlayer & a_Player)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPlayerListUpdatePing(a_Player);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendPlayerListItem(a_Player, a_IsOnline);
+ m_Protocol->SendPlayerListUpdateDisplayName(a_Player, a_CustomName);
}
@@ -716,10 +748,10 @@ void cProtocolRecognizer::SendThunderbolt(int a_BlockX, int a_BlockY, int a_Bloc
-void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay)
+void cProtocolRecognizer::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay);
+ m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle);
}
@@ -830,51 +862,8 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
{
// NOTE: If a new protocol is added or an old one is removed, adjust MCS_CLIENT_VERSIONS and
// MCS_PROTOCOL_VERSIONS macros in the header file, as well as PROTO_VERSION_LATEST macro
-
- // The first packet should be a Handshake, 0x02:
- unsigned char PacketType;
- if (!m_Buffer.ReadByte(PacketType))
- {
- return false;
- }
- switch (PacketType)
- {
- case 0x02: return TryRecognizeLengthlessProtocol(); // Handshake, continue recognizing
- case 0xfe:
- {
- // This may be either a packet length or the length-less Ping packet
- Byte NextByte;
- if (!m_Buffer.ReadByte(NextByte))
- {
- // Not enough data for either protocol
- // This could actually happen with the 1.2 / 1.3 client, but their support is fading out anyway
- return false;
- }
- if (NextByte != 0x01)
- {
- // This is definitely NOT a length-less Ping packet, handle as lengthed protocol:
- break;
- }
- if (!m_Buffer.ReadByte(NextByte))
- {
- // There is no more data. Although this *could* mean TCP fragmentation, it is highly unlikely
- // and rather this is a 1.4 client sending a regular Ping packet (without the following Plugin message)
- SendLengthlessServerPing();
- return false;
- }
- if (NextByte == 0xfa)
- {
- // Definitely a length-less Ping followed by a Plugin message
- SendLengthlessServerPing();
- return false;
- }
- // Definitely a lengthed Initial handshake, handle below:
- break;
- }
- } // switch (PacketType)
- // This must be a lengthed protocol, try if it has the entire initial handshake packet:
- m_Buffer.ResetRead();
+ // Lengthed protocol, try if it has the entire initial handshake packet:
UInt32 PacketLen;
UInt32 ReadSoFar = (UInt32)m_Buffer.GetReadableSpace();
if (!m_Buffer.ReadVarInt(PacketLen))
@@ -895,61 +884,6 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
-bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
-{
- // The comm started with 0x02, which is a Handshake packet in the length-less protocol family
- // 1.3.2 starts with 0x02 0x39 <name-length-short>
- // 1.2.5 starts with 0x02 <name-length-short> and name is expected to less than 0x3900 long :)
- char ch;
- if (!m_Buffer.ReadChar(ch))
- {
- return false;
- }
- switch (ch)
- {
- case PROTO_VERSION_1_3_2:
- {
- m_Protocol = new cProtocol132(m_Client);
- return true;
- }
- case PROTO_VERSION_1_4_2:
- case PROTO_VERSION_1_4_4:
- {
- m_Protocol = new cProtocol142(m_Client);
- return true;
- }
- case PROTO_VERSION_1_4_6:
- {
- m_Protocol = new cProtocol146(m_Client);
- return true;
- }
- case PROTO_VERSION_1_5_0:
- case PROTO_VERSION_1_5_2:
- {
- m_Protocol = new cProtocol150(m_Client);
- return true;
- }
- case PROTO_VERSION_1_6_1:
- {
- m_Protocol = new cProtocol161(m_Client);
- return true;
- }
- case PROTO_VERSION_1_6_2:
- case PROTO_VERSION_1_6_3:
- case PROTO_VERSION_1_6_4:
- {
- m_Protocol = new cProtocol162(m_Client);
- return true;
- }
- }
- m_Protocol = new cProtocol125(m_Client);
- return true;
-}
-
-
-
-
-
bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining)
{
UInt32 PacketType;
@@ -971,6 +905,7 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
{
return false;
}
+ m_Client->SetProtocolVersion(ProtocolVersion);
switch (ProtocolVersion)
{
case PROTO_VERSION_1_7_2:
@@ -978,9 +913,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
@@ -990,13 +934,43 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
}
+ case PROTO_VERSION_1_8_0:
+ {
+ AString ServerAddress;
+ short ServerPort;
+ UInt32 NextState;
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
+ m_Buffer.CommitRead();
+ m_Protocol = new cProtocol180(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
+ return true;
+ }
}
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u)",
m_Client->GetIPString().c_str(), ProtocolVersion
@@ -1008,79 +982,3 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
-
-void cProtocolRecognizer::SendLengthlessServerPing(void)
-{
- AString Reply;
- cServer * Server = cRoot::Get()->GetServer();
- switch (cRoot::Get()->GetPrimaryServerVersion())
- {
- case PROTO_VERSION_1_2_5:
- case PROTO_VERSION_1_3_2:
- {
- // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
- Printf(Reply, "%s%s%i%s%i",
- Server->GetDescription().c_str(),
- cChatColor::Delimiter,
- Server->GetNumPlayers(),
- cChatColor::Delimiter,
- Server->GetMaxPlayers()
- );
- break;
- }
-
- case PROTO_VERSION_1_4_2:
- case PROTO_VERSION_1_4_4:
- case PROTO_VERSION_1_4_6:
- case PROTO_VERSION_1_5_0:
- case PROTO_VERSION_1_5_2:
- case PROTO_VERSION_1_6_1:
- case PROTO_VERSION_1_6_2:
- case PROTO_VERSION_1_6_3:
- case PROTO_VERSION_1_6_4:
- {
- // The server list ping now has 1 more byte of "magic". Mojang just loves to complicate stuff.
- // http://wiki.vg/wiki/index.php?title=Protocol&oldid=3101#Server_List_Ping_.280xFE.29
- // _X 2012_10_31: I know that this needn't eat the byte, since it still may be in transit.
- // Who cares? We're disconnecting anyway.
- m_Buffer.ResetRead();
- if (m_Buffer.CanReadBytes(2))
- {
- Byte val;
- m_Buffer.ReadByte(val); // Packet type - Serverlist ping
- m_Buffer.ReadByte(val); // 0x01 magic value
- ASSERT(val == 0x01);
- }
-
- // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
- AString NumPlayers;
- Printf(NumPlayers, "%d", Server->GetNumPlayers());
- AString MaxPlayers;
- Printf(MaxPlayers, "%d", Server->GetMaxPlayers());
-
- AString ProtocolVersionNum;
- Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
- AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
-
- // Cannot use Printf() because of in-string NUL bytes.
- Reply = cChatColor::Delimiter;
- Reply.append("1");
- Reply.push_back(0);
- Reply.append(ProtocolVersionNum);
- Reply.push_back(0);
- Reply.append(ProtocolVersionTxt);
- Reply.push_back(0);
- Reply.append(Server->GetDescription());
- Reply.push_back(0);
- Reply.append(NumPlayers);
- Reply.push_back(0);
- Reply.append(MaxPlayers);
- break;
- }
- } // switch (m_PrimaryServerVersion)
- m_Client->Kick(Reply);
-}
-
-
-
-
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 65829ef73..b42cfdec2 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -18,8 +18,8 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.7.9"
-#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4, 5"
+#define MCS_CLIENT_VERSIONS "1.7.x, 1.8"
+#define MCS_PROTOCOL_VERSIONS "4, 5, 47"
@@ -33,24 +33,9 @@ class cProtocolRecognizer :
public:
enum
{
- PROTO_VERSION_1_2_5 = 29,
- PROTO_VERSION_1_3_2 = 39,
- PROTO_VERSION_1_4_2 = 47,
- PROTO_VERSION_1_4_4 = 49,
- PROTO_VERSION_1_4_6 = 51,
- PROTO_VERSION_1_5_0 = 60,
- PROTO_VERSION_1_5_2 = 61,
- PROTO_VERSION_1_6_1 = 73,
- PROTO_VERSION_1_6_2 = 74,
- PROTO_VERSION_1_6_3 = 77,
- PROTO_VERSION_1_6_4 = 78,
-
- PROTO_VERSION_NEXT,
- PROTO_VERSION_LATEST = PROTO_VERSION_NEXT - 1, ///< Automatically assigned to the last protocol version, this serves as the default for PrimaryServerVersion
-
- // These will be kept "under" the next / latest, because the next and latest are only needed for previous protocols
PROTO_VERSION_1_7_2 = 4,
PROTO_VERSION_1_7_6 = 5,
+ PROTO_VERSION_1_8_0 = 47,
} ;
cProtocolRecognizer(cClientHandle * a_Client);
@@ -63,76 +48,80 @@ public:
virtual void DataReceived(const char * a_Data, size_t a_Size) override;
/// Sending stuff to clients (alphabetically sorted):
- virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
- virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
- virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
- virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
- virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
- virtual void SendChat (const AString & a_Message) override;
- virtual void SendChat (const cCompositeChat & a_Message) override;
- virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
- virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
- virtual void SendDestroyEntity (const cEntity & a_Entity) override;
- virtual void SendDisconnect (const AString & a_Reason) override;
- virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
- virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
- virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
- virtual void SendEntityLook (const cEntity & a_Entity) override;
- virtual void SendEntityMetadata (const cEntity & a_Entity) override;
- virtual void SendEntityProperties (const cEntity & a_Entity) override;
- virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
- virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
- virtual void SendEntityVelocity (const cEntity & a_Entity) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
- virtual void SendGameMode (eGameMode a_GameMode) override;
- virtual void SendHealth (void) override;
- virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
- virtual void SendKeepAlive (int a_PingID) override;
- virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
- virtual void SendLoginSuccess (void) override;
- virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
- virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
- virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
- virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
- virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
- virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
- virtual void SendPlayerAbilities (void) override;
- virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
- virtual void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline) override;
- virtual void SendPlayerMaxSpeed (void) override;
- virtual void SendPlayerMoveLook (void) override;
- virtual void SendPlayerPosition (void) override;
- virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
- virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
- virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
- virtual void SendExperience (void) override;
- virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
- virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
- virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
- virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
- virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
- virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
- virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
- virtual void SendSpawnMob (const cMonster & a_Mob) override;
- virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
- virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
- virtual void SendStatistics (const cStatManager & a_Manager) override;
- virtual void SendTabCompletionResults(const AStringVector & a_Results) override;
- virtual void SendTeleportEntity (const cEntity & a_Entity) override;
- virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
- virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
- virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
- virtual 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) override;
- virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
- virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
- virtual void SendWindowOpen (const cWindow & a_Window) override;
- virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
+ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
+ virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
+ virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
+ virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
+ virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
+ virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
+ virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override;
+ virtual void SendDestroyEntity (const cEntity & a_Entity) override;
+ virtual void SendDisconnect (const AString & a_Reason) override;
+ virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+)
+ virtual void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) override;
+ virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendEntityHeadLook (const cEntity & a_Entity) override;
+ virtual void SendEntityLook (const cEntity & a_Entity) override;
+ virtual void SendEntityMetadata (const cEntity & a_Entity) override;
+ virtual void SendEntityProperties (const cEntity & a_Entity) override;
+ virtual void SendEntityRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) override;
+ virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) override;
+ virtual void SendEntityVelocity (const cEntity & a_Entity) override;
+ virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendGameMode (eGameMode a_GameMode) override;
+ virtual void SendHealth (void) override;
+ virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
+ virtual void SendKeepAlive (int a_PingID) override;
+ virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendLoginSuccess (void) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
+ virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount) override;
+ virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
+ virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
+ virtual void SendPlayerAbilities (void) override;
+ virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
+ virtual void SendPlayerListAddPlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) override;
+ virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override;
+ virtual void SendPlayerMaxSpeed (void) override;
+ virtual void SendPlayerMoveLook (void) override;
+ virtual void SendPlayerPosition (void) override;
+ virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
+ virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
+ virtual void SendExperience (void) override;
+ virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
+ virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
+ virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
+ virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
+ virtual void SendSpawnMob (const cMonster & a_Mob) override;
+ virtual void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch) override;
+ virtual void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) override;
+ virtual void SendStatistics (const cStatManager & a_Manager) override;
+ virtual void SendTabCompletionResults (const AStringVector & a_Results) override;
+ virtual void SendTeleportEntity (const cEntity & a_Entity) override;
+ virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
+ virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
+ virtual 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) override;
+ virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
+ virtual void SendWeather (eWeather a_Weather) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWindowOpen (const cWindow & a_Window) override;
+ virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override;
virtual AString GetAuthServerID(void) override;
@@ -145,23 +134,11 @@ protected:
/// Tries to recognize protocol based on m_Buffer contents; returns true if recognized
bool TryRecognizeProtocol(void);
- /** Tries to recognize a protocol in the length-less family, based on m_Buffer; returns true if recognized.
- Handles protocols before release 1.7, that didn't include packet lengths, and started with a 0x02 handshake packet
- Note that length-less server ping is handled directly in TryRecognizeProtocol(), this function is called only
- when the 0x02 Handshake packet has been received
- */
- bool TryRecognizeLengthlessProtocol(void);
-
- /** Tries to recognize a protocol in the leghted family (1.7+), based on m_Buffer; returns true if recognized.
+ /** Tries to recognize a protocol in the lengthed family (1.7+), based on m_Buffer; returns true if recognized.
The packet length and type have already been read, type is 0
The number of bytes remaining in the packet is passed as a_PacketLengthRemaining
**/
bool TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRemaining);
-
- /** Called when the recognizer gets a length-less protocol's server ping packet
- Responds with server stats and destroys the client.
- */
- void SendLengthlessServerPing(void);
} ;