From e6786074d58d81a230a6d5d01a5d89fa2f5ab16e Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 00:27:42 +0200 Subject: [PATCH 01/41] Added cBufferedSslContext implementation. --- src/ByteBuffer.h | 40 +++++++++++++-------------- src/PolarSSL++/BufferedSslContext.cpp | 31 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 7656a5b13..c5ff05922 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -30,25 +30,25 @@ public: cByteBuffer(size_t a_BufferSize); ~cByteBuffer(); - /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not + /** Writes the bytes specified to the ringbuffer. Returns true if successful, false if not */ bool Write(const void * a_Bytes, size_t a_Count); - /// Returns the number of bytes that can be successfully written to the ringbuffer + /** Returns the number of bytes that can be successfully written to the ringbuffer */ size_t GetFreeSpace(void) const; - /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() + /** Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() */ size_t GetUsedSpace(void) const; - /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) + /** Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) */ size_t GetReadableSpace(void) const; - /// Returns the current data start index. For debugging purposes. + /** Returns the current data start index. For debugging purposes. */ size_t GetDataStart(void) const { return m_DataStart; } - /// Returns true if the specified amount of bytes are available for reading + /** Returns true if the specified amount of bytes are available for reading */ bool CanReadBytes(size_t a_Count) const; - /// Returns true if the specified amount of bytes are available for writing + /** Returns true if the specified amount of bytes are available for writing */ bool CanWriteBytes(size_t a_Count) const; // Read the specified datatype and advance the read pointer; return true if successfully read: @@ -65,7 +65,7 @@ public: bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 bool ReadLEInt (int & a_Value); - /// Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) + /** Reads VarInt, assigns it to anything that can be assigned from an UInt32 (unsigned short, char, Byte, double, ...) */ template bool ReadVarInt(T & a_Value) { UInt32 v; @@ -91,37 +91,37 @@ public: bool WriteVarUTF8String (const AString & a_Value); // string length as VarInt, then string as UTF-8 bool WriteLEInt (int a_Value); - /// Reads a_Count bytes into a_Buffer; returns true if successful + /** Reads a_Count bytes into a_Buffer; returns true if successful */ bool ReadBuf(void * a_Buffer, size_t a_Count); - /// Writes a_Count bytes into a_Buffer; returns true if successful + /** Writes a_Count bytes into a_Buffer; returns true if successful */ bool WriteBuf(const void * a_Buffer, size_t a_Count); - /// Reads a_Count bytes into a_String; returns true if successful + /** Reads a_Count bytes into a_String; returns true if successful */ bool ReadString(AString & a_String, size_t a_Count); - /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String + /** Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String */ bool ReadUTF16String(AString & a_String, int a_NumChars); - /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer + /** Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer */ bool SkipRead(size_t a_Count); - /// Reads all available data into a_Data + /** Reads all available data into a_Data */ void ReadAll(AString & a_Data); - /// Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success. + /** Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success. */ bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes); - /// Removes the bytes that have been read from the ringbuffer + /** Removes the bytes that have been read from the ringbuffer */ void CommitRead(void); - /// Restarts next reading operation at the start of the ringbuffer + /** Restarts next reading operation at the start of the ringbuffer */ void ResetRead(void); - /// Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication + /** Re-reads the data that has been read since the last commit to the current readpos. Used by ProtoProxy to duplicate communication */ void ReadAgain(AString & a_Out); - /// Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs + /** Checks if the internal state is valid (read and write positions in the correct bounds) using ASSERTs */ void CheckValid(void) const; protected: @@ -136,7 +136,7 @@ protected: size_t m_WritePos; // Where the data ends in the ringbuffer size_t m_ReadPos; // Where the next read will start in the ringbuffer - /// Advances the m_ReadPos by a_Count bytes + /** Advances the m_ReadPos by a_Count bytes */ void AdvanceReadPos(size_t a_Count); } ; diff --git a/src/PolarSSL++/BufferedSslContext.cpp b/src/PolarSSL++/BufferedSslContext.cpp index 885b30c68..2455d5781 100644 --- a/src/PolarSSL++/BufferedSslContext.cpp +++ b/src/PolarSSL++/BufferedSslContext.cpp @@ -20,6 +20,37 @@ cBufferedSslContext::cBufferedSslContext(size_t a_BufferSize): +size_t cBufferedSslContext::WriteIncoming(const void * a_Data, size_t a_NumBytes) +{ + size_t NumBytes = std::min(m_IncomingData.GetFreeSpace(), a_NumBytes); + if (NumBytes > 0) + { + m_IncomingData.Write(a_Data, NumBytes); + return a_NumBytes - NumBytes; + } + return 0; +} + + + + + +size_t cBufferedSslContext::ReadOutgoing(void * a_Data, size_t a_DataMaxSize) +{ + size_t NumBytes = std::min(m_OutgoingData.GetReadableSpace(), a_DataMaxSize); + if (NumBytes > 0) + { + m_OutgoingData.ReadBuf(a_Data, NumBytes); + m_OutgoingData.CommitRead(); + return a_DataMaxSize - NumBytes; + } + return 0; +} + + + + + int cBufferedSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) { // Called when PolarSSL wants to read encrypted data from the SSL peer From 47feb91e57f83c81722188ec3025c3109758dd33 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 00:28:27 +0200 Subject: [PATCH 02/41] cSslContext supports setting own cert / privkey. --- src/PolarSSL++/PublicKey.cpp | 82 ++++++++++++++++++++++++++++++++-- src/PolarSSL++/PublicKey.h | 32 ++++++++++++- src/PolarSSL++/RsaPrivateKey.h | 8 ++++ src/PolarSSL++/SslContext.cpp | 50 ++++++++++++++++++++- src/PolarSSL++/SslContext.h | 22 ++++++++- 5 files changed, 187 insertions(+), 7 deletions(-) diff --git a/src/PolarSSL++/PublicKey.cpp b/src/PolarSSL++/PublicKey.cpp index 49794a0c8..dae026082 100644 --- a/src/PolarSSL++/PublicKey.cpp +++ b/src/PolarSSL++/PublicKey.cpp @@ -10,15 +10,44 @@ -cPublicKey::cPublicKey(const AString & a_PublicKeyDER) +cPublicKey::cPublicKey(void) { pk_init(&m_Pk); - if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0) + m_CtrDrbg.Initialize("rsa_pubkey", 10); +} + + + + + +cPublicKey::cPublicKey(const AString & a_PublicKeyData) +{ + pk_init(&m_Pk); + m_CtrDrbg.Initialize("rsa_pubkey", 10); + int res = ParsePublic(a_PublicKeyData.data(), a_PublicKeyData.size()); + if (res != 0) { + LOGWARNING("Failed to parse public key: -0x%x", res); + ASSERT(!"Cannot parse PubKey"); + return; + } +} + + + + + +cPublicKey::cPublicKey(const AString & a_PrivateKeyData, const AString & a_Password) +{ + pk_init(&m_Pk); + m_CtrDrbg.Initialize("rsa_privkey", 11); + int res = ParsePrivate(a_PrivateKeyData.data(), a_PrivateKeyData.size(), a_Password); + if (res != 0) + { + LOGWARNING("Failed to parse private key: -0x%x", res); ASSERT(!"Cannot parse PubKey"); return; } - m_CtrDrbg.Initialize("rsa_pubkey", 10); } @@ -36,6 +65,8 @@ cPublicKey::~cPublicKey() int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) { + ASSERT(IsValid()); + size_t DecryptedLen = a_DecryptedMaxLength; int res = pk_decrypt(&m_Pk, a_EncryptedData, a_EncryptedLength, @@ -55,6 +86,8 @@ int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) { + ASSERT(IsValid()); + size_t EncryptedLength = a_EncryptedMaxLength; int res = pk_encrypt(&m_Pk, a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength, @@ -71,3 +104,46 @@ int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a + +int cPublicKey::ParsePublic(const void * a_Data, size_t a_NumBytes) +{ + ASSERT(!IsValid()); // Cannot parse a second key + + return pk_parse_public_key(&m_Pk, (const unsigned char *)a_Data, a_NumBytes); +} + + + + + + +int cPublicKey::ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password) +{ + ASSERT(!IsValid()); // Cannot parse a second key + + if (a_Password.empty()) + { + return pk_parse_key(&m_Pk, (const unsigned char *)a_Data, a_NumBytes, NULL, 0); + } + else + { + return pk_parse_key( + &m_Pk, + (const unsigned char *)a_Data, a_NumBytes, + (const unsigned char *)a_Password.c_str(), a_Password.size() + ); + } +} + + + + + +bool cPublicKey::IsValid(void) const +{ + return (pk_get_type(&m_Pk) != POLARSSL_PK_NONE); +} + + + + diff --git a/src/PolarSSL++/PublicKey.h b/src/PolarSSL++/PublicKey.h index 5a0a57147..df52a4143 100644 --- a/src/PolarSSL++/PublicKey.h +++ b/src/PolarSSL++/PublicKey.h @@ -18,9 +18,18 @@ class cPublicKey { + friend class cSslContext; + public: - /** Constructs the public key out of the DER-encoded pubkey data */ - cPublicKey(const AString & a_PublicKeyDER); + /** Constructs an empty key instance. Before use, it needs to be filled by ParsePublic() or ParsePrivate() */ + cPublicKey(void); + + /** Constructs the public key out of the DER- or PEM-encoded pubkey data */ + cPublicKey(const AString & a_PublicKeyData); + + /** Constructs the private key out of the DER- or PEM-encoded privkey data, with the specified password. + If a_Password is empty, no password is assumed. */ + cPublicKey(const AString & a_PrivateKeyData, const AString & a_Password); ~cPublicKey(); @@ -33,6 +42,20 @@ public: Both a_EncryptedData and a_DecryptedData must be at least bytes large. Returns the number of bytes decrypted, or negative number for error. */ int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); + + /** Parses the specified data into a public key representation. + The key can be DER- or PEM-encoded. + Returns 0 on success, PolarSSL error code on failure. */ + int ParsePublic(const void * a_Data, size_t a_NumBytes); + + /** Parses the specified data into a private key representation. + If a_Password is empty, no password is assumed. + The key can be DER- or PEM-encoded. + Returns 0 on success, PolarSSL error code on failure. */ + int ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password); + + /** Returns true if the contained key is valid. */ + bool IsValid(void) const; protected: /** The public key PolarSSL representation */ @@ -40,8 +63,13 @@ protected: /** The random generator used in encryption and decryption */ cCtrDrbgContext m_CtrDrbg; + + + /** Returns the internal context ptr. Only use in PolarSSL API calls. */ + pk_context * GetInternal(void) { return &m_Pk; } } ; +typedef SharedPtr cPublicKeyPtr; diff --git a/src/PolarSSL++/RsaPrivateKey.h b/src/PolarSSL++/RsaPrivateKey.h index ffacde11b..4d03f27df 100644 --- a/src/PolarSSL++/RsaPrivateKey.h +++ b/src/PolarSSL++/RsaPrivateKey.h @@ -19,6 +19,8 @@ /** Encapsulates an RSA private key used in PKI cryptography */ class cRsaPrivateKey { + friend class cSslContext; + public: /** Creates a new empty object, the key is not assigned */ cRsaPrivateKey(void); @@ -51,8 +53,14 @@ protected: /** The random generator used for generating the key and encryption / decryption */ cCtrDrbgContext m_CtrDrbg; + + + /** Returns the internal context ptr. Only use in PolarSSL API calls. */ + rsa_context * GetInternal(void) { return &m_Rsa; } } ; +typedef SharedPtr cRsaPrivateKeyPtr; + diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index 1994cf844..3d2b8cef7 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -40,7 +40,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & if (m_IsValid) { LOGWARNING("SSL: Double initialization is not supported."); - return POLARSSL_ERR_SSL_MALLOC_FAILED; // There is no return value well-suited for this, reuse this one. + return POLARSSL_ERR_SSL_BAD_INPUT_DATA; // There is no return value well-suited for this, reuse this one. } // Set the CtrDrbg context, create a new one if needed: @@ -80,8 +80,56 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & +void cSslContext::SetOwnCert(const cX509CertPtr & a_OwnCert, const cRsaPrivateKeyPtr & a_OwnCertPrivKey) +{ + ASSERT(m_IsValid); // Call Initialize() first + + // Check that both the cert and the key is valid: + if ((a_OwnCert.get() == NULL) || (a_OwnCertPrivKey.get() == NULL)) + { + LOGWARNING("SSL: Own certificate is not valid, skipping the set."); + return; + } + + // Make sure we have the cert stored for later, PolarSSL only uses the cert later on + m_OwnCert = a_OwnCert; + m_OwnCertPrivKey = a_OwnCertPrivKey; + + // Set into the context: + ssl_set_own_cert_rsa(&m_Ssl, m_OwnCert->GetInternal(), m_OwnCertPrivKey->GetInternal()); +} + + + + + +void cSslContext::SetOwnCert(const cX509CertPtr & a_OwnCert, const cPublicKeyPtr & a_OwnCertPrivKey) +{ + ASSERT(m_IsValid); // Call Initialize() first + + // Check that both the cert and the key is valid: + if ((a_OwnCert.get() == NULL) || (a_OwnCertPrivKey.get() == NULL)) + { + LOGWARNING("SSL: Own certificate is not valid, skipping the set."); + return; + } + + // Make sure we have the cert stored for later, PolarSSL only uses the cert later on + m_OwnCert = a_OwnCert; + m_OwnCertPrivKey2 = a_OwnCertPrivKey; + + // Set into the context: + ssl_set_own_cert(&m_Ssl, m_OwnCert->GetInternal(), m_OwnCertPrivKey2->GetInternal()); +} + + + + + void cSslContext::SetCACerts(const cX509CertPtr & a_CACert, const AString & a_ExpectedPeerName) { + ASSERT(m_IsValid); // Call Initialize() first + // Store the data in our internal buffers, to avoid losing the pointers later on // PolarSSL will need these after this call returns, and the caller may move / delete the data before that: m_ExpectedPeerName = a_ExpectedPeerName; diff --git a/src/PolarSSL++/SslContext.h b/src/PolarSSL++/SslContext.h index 85add5f8b..273939b9f 100644 --- a/src/PolarSSL++/SslContext.h +++ b/src/PolarSSL++/SslContext.h @@ -11,6 +11,8 @@ #include "polarssl/ssl.h" #include "../ByteBuffer.h" +#include "PublicKey.h" +#include "RsaPrivateKey.h" #include "X509Cert.h" @@ -47,7 +49,16 @@ public: /** Returns true if the object has been initialized properly. */ bool IsValid(void) const { return m_IsValid; } - /** Sets a cert chain as the trusted cert store for this context. + /** Sets the certificate to use as our own. Must be used when representing a server, optional when client. + Must be called after Initialize(). */ + void SetOwnCert(const cX509CertPtr & a_OwnCert, const cRsaPrivateKeyPtr & a_OwnCertPrivKey); + + /** Sets the certificate to use as our own. Must be used when representing a server, optional when client. + Must be called after Initialize(). + Despite the class name, a_OwnCertPrivKey is a PRIVATE key. */ + void SetOwnCert(const cX509CertPtr & a_OwnCert, const cPublicKeyPtr & a_OwnCertPrivKey); + + /** Sets a cert chain as the trusted cert store for this context. Must be called after Initialize(). Calling this will switch the context into strict cert verification mode. a_ExpectedPeerName is the CommonName that we expect the SSL peer to have in its cert, if it is different, the verification will fail. An empty string will disable the CN check. */ @@ -93,6 +104,15 @@ protected: /** The SSL context that PolarSSL uses. */ ssl_context m_Ssl; + /** The certificate that we present to the peer. */ + cX509CertPtr m_OwnCert; + + /** Private key for m_OwnCert, if initialized from a cRsaPrivateKey */ + cRsaPrivateKeyPtr m_OwnCertPrivKey; + + /** Private key for m_OwnCert, if initialized from a cPublicKey. Despite the class name, this is a PRIVATE key. */ + cPublicKeyPtr m_OwnCertPrivKey2; + /** True if the SSL handshake has been completed. */ bool m_HasHandshaken; From e2bf3783e8c8f7855532e04203bb1b46b976cfee Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 11:32:25 +0200 Subject: [PATCH 03/41] Fixed BufferedSslContext's buffer reading and writing. --- src/PolarSSL++/BufferedSslContext.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PolarSSL++/BufferedSslContext.cpp b/src/PolarSSL++/BufferedSslContext.cpp index 2455d5781..9f7caeb8a 100644 --- a/src/PolarSSL++/BufferedSslContext.cpp +++ b/src/PolarSSL++/BufferedSslContext.cpp @@ -26,7 +26,7 @@ size_t cBufferedSslContext::WriteIncoming(const void * a_Data, size_t a_NumBytes if (NumBytes > 0) { m_IncomingData.Write(a_Data, NumBytes); - return a_NumBytes - NumBytes; + return NumBytes; } return 0; } @@ -42,7 +42,7 @@ size_t cBufferedSslContext::ReadOutgoing(void * a_Data, size_t a_DataMaxSize) { m_OutgoingData.ReadBuf(a_Data, NumBytes); m_OutgoingData.CommitRead(); - return a_DataMaxSize - NumBytes; + return NumBytes; } return 0; } From e1b6a169457b267c3e11bbdb9e58e9ab7b3f0136 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 11:33:29 +0200 Subject: [PATCH 04/41] Added a (disabled) test of low-security ciphersuites. Enabling this allows the connection to be sniffed and decoded using Wireshark, when given the SSL private key. --- src/PolarSSL++/SslContext.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index 3d2b8cef7..df0219610 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -70,6 +70,18 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this); ssl_set_verify(&m_Ssl, &SSLVerifyCert, this); */ + + /* + // Set ciphersuite to the easiest one to decode, so that the connection can be wireshark-decoded: + static const int CipherSuites[] = + { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + 0, // Must be 0-terminated! + }; + ssl_set_ciphersuites(&m_Ssl, CipherSuites); + */ #endif m_IsValid = true; From a04cb6d146dfb8f04b0a8943c1e185fc25bf2829 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 11:34:15 +0200 Subject: [PATCH 05/41] Fixed HTTP message parsing, prepared for SSL. --- src/HTTPServer/HTTPConnection.cpp | 5 +++-- src/HTTPServer/HTTPMessage.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp index da4df0e34..8e95eff2d 100644 --- a/src/HTTPServer/HTTPConnection.cpp +++ b/src/HTTPServer/HTTPConnection.cpp @@ -26,6 +26,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) : cHTTPConnection::~cHTTPConnection() { + // LOGD("HTTP: Connection deleting: %p", this); delete m_CurrentRequest; } @@ -183,11 +184,11 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) // Process the rest of the incoming data into the request body: if (a_Size > BytesConsumed) { - DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); } else { - DataReceived("", 0); // If the request has zero body length, let it be processed right-away + cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away } break; } diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp index 4a3611050..44feda469 100644 --- a/src/HTTPServer/HTTPMessage.cpp +++ b/src/HTTPServer/HTTPMessage.cpp @@ -201,7 +201,7 @@ size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size) return AString::npos; } // Check that there's HTTP/version at the end - if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0) + if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0) { m_IsValid = false; return AString::npos; From 272c232efb645c9f7d75556aeb047e13b244c9ed Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 11:48:03 +0200 Subject: [PATCH 06/41] Implemented SSL connection for WebAdmin. Fixes FS-319. --- MCServer/.gitignore | 4 ++ src/HTTPServer/HTTPServer.cpp | 35 ++++++++- src/HTTPServer/HTTPServer.h | 10 +++ src/HTTPServer/SslHTTPConnection.cpp | 103 +++++++++++++++++++++++++++ src/HTTPServer/SslHTTPConnection.h | 45 ++++++++++++ 5 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 src/HTTPServer/SslHTTPConnection.cpp create mode 100644 src/HTTPServer/SslHTTPConnection.h diff --git a/MCServer/.gitignore b/MCServer/.gitignore index 32a634a03..6da9aa7c7 100644 --- a/MCServer/.gitignore +++ b/MCServer/.gitignore @@ -30,3 +30,7 @@ motd.txt *.xml mcserver_api.lua +# Ignore the webadmin certs / privkey, so that no-one commits theirs by accident: +webadmin/httpscert.crt +webadmin/httpskey.pem + diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index eaf8405a3..9e3e0a17b 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -8,6 +8,7 @@ #include "HTTPMessage.h" #include "HTTPConnection.h" #include "HTTPFormParser.h" +#include "SslHTTPConnection.h" @@ -123,8 +124,30 @@ class cDebugCallbacks : cHTTPServer::cHTTPServer(void) : m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), - m_Callbacks(NULL) + m_Callbacks(NULL), + m_Cert(new cX509Cert), + m_CertPrivKey(new cPublicKey) { + AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); + AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem"); + if (!CertFile.empty() && !KeyFile.empty()) + { + int res = m_Cert->Parse(CertFile.data(), CertFile.size()); + if (res == 0) + { + int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), ""); + if (res2 != 0) + { + // Reading the private key failed, reset the cert: + LOGWARNING("WebAdmin: Cannot read HTTPS certificate private key: -0x%x", -res2); + m_Cert.reset(); + } + } + else + { + LOGWARNING("WebAdmin: Cannot read HTTPS certificate: -0x%x", -res); + } + } } @@ -195,7 +218,15 @@ void cHTTPServer::Stop(void) void cHTTPServer::OnConnectionAccepted(cSocket & a_Socket) { - cHTTPConnection * Connection = new cHTTPConnection(*this); + cHTTPConnection * Connection; + if (m_Cert.get() != NULL) + { + Connection = new cSslHTTPConnection(*this, m_Cert, m_CertPrivKey); + } + else + { + Connection = new cHTTPConnection(*this); + } m_SocketThreads.AddClient(a_Socket, Connection); cCSLock Lock(m_CSConnections); m_Connections.push_back(Connection); diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index 8eff7d879..eb91dd5a3 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -12,6 +12,9 @@ #include "../OSSupport/ListenThread.h" #include "../OSSupport/SocketThreads.h" #include "inifile/iniFile.h" +#include "PolarSSL++/RsaPrivateKey.h" +#include "PolarSSL++/PublicKey.h" +#include "PolarSSL++/X509Cert.h" @@ -66,6 +69,7 @@ public: protected: friend class cHTTPConnection; + friend class cSslHTTPConnection; cListenThread m_ListenThreadIPv4; cListenThread m_ListenThreadIPv6; @@ -78,6 +82,12 @@ protected: /// The callbacks to call for various events cCallbacks * m_Callbacks; + /** The server certificate to use for the SSL connections */ + cX509CertPtr m_Cert; + + /** The private key for m_Cert. Despite the class name, this is the PRIVATE key. */ + cPublicKeyPtr m_CertPrivKey; + // cListenThread::cCallback overrides: virtual void OnConnectionAccepted(cSocket & a_Socket) override; diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp new file mode 100644 index 000000000..fff96bb2e --- /dev/null +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -0,0 +1,103 @@ + +// SslHTTPConnection.cpp + +// Implements the cSslHTTPConnection class representing a HTTP connection made over a SSL link + +#include "Globals.h" +#include "SslHTTPConnection.h" +#include "HTTPServer.h" + + + + + +cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cPublicKeyPtr & a_PrivateKey) : + super(a_HTTPServer), + m_Ssl(64000), + m_Cert(a_Cert), + m_PrivateKey(a_PrivateKey) +{ + m_Ssl.Initialize(false); + m_Ssl.SetOwnCert(a_Cert, a_PrivateKey); +} + + + + + +void cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +{ + // If there is outgoing data in the queue, notify the server that it should write it out: + if (!m_OutgoingData.empty()) + { + m_HTTPServer.NotifyConnectionWrite(*this); + } + + // Process the received data: + const char * Data = a_Data; + size_t Size = a_Size; + for (;;) + { + // Try to write as many bytes into Ssl's "incoming" buffer as possible: + size_t BytesWritten = 0; + if (Size > 0) + { + BytesWritten = m_Ssl.WriteIncoming(Data, Size); + Data += BytesWritten; + Size -= BytesWritten; + } + + // Try to read as many bytes from SSL's decryption as possible: + char Buffer[32000]; + int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); + if (NumRead > 0) + { + super::DataReceived(Buffer, (size_t)NumRead); + } + + // If both failed, bail out: + if ((BytesWritten == 0) && (NumRead <= 0)) + { + return; + } + } +} + + + + + +void cSslHTTPConnection::GetOutgoingData(AString & a_Data) +{ + for (;;) + { + // Write as many bytes from our buffer to SSL's encryption as possible: + int NumWritten = 0; + if (!m_OutgoingData.empty()) + { + NumWritten = m_Ssl.WritePlain(m_OutgoingData.data(), m_OutgoingData.size()); + if (NumWritten > 0) + { + m_OutgoingData.erase(0, (size_t)NumWritten); + } + } + + // Read as many bytes from SSL's "outgoing" buffer as possible: + char Buffer[32000]; + size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer)); + if (NumBytes > 0) + { + a_Data.append(Buffer, NumBytes); + } + + // If both failed, bail out: + if ((NumWritten <= 0) && (NumBytes == 0)) + { + return; + } + } +} + + + + diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h new file mode 100644 index 000000000..2a648e8c8 --- /dev/null +++ b/src/HTTPServer/SslHTTPConnection.h @@ -0,0 +1,45 @@ + +// SslHTTPConnection.h + +// Declared the cSslHTTPConnection class representing a HTTP connection made over a SSL link + + + + + +#pragma once + +#include "HTTPConnection.h" +#include "PolarSSL++/BufferedSslContext.h" + + + + + +class cSslHTTPConnection : + public cHTTPConnection +{ + typedef cHTTPConnection super; + +public: + /** Creates a new connection on the specified server; sends the specified cert as the server certificate, + uses the private key for decryption. a_Private key is, despite the class name, a PRIVATE key for the cert. */ + cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cPublicKeyPtr & a_PrivateKey); + +protected: + cBufferedSslContext m_Ssl; + + /** The certificate to send to the client */ + cX509CertPtr m_Cert; + + /** The private key used for the certificate */ + cPublicKeyPtr m_PrivateKey; + + // cHTTPConnection overrides: + virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client +} ; + + + + From cf821bc9a4b156d6ffb8a79502936a1de36bb074 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 11:48:45 +0200 Subject: [PATCH 07/41] Added scripts for generating HTTPS cert and key. --- .../GenerateSelfSignedHTTPSCertUsingOpenssl.cmd | 9 +++++++++ .../GenerateSelfSignedHTTPSCertUsingOpenssl.sh | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd create mode 100644 MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd new file mode 100644 index 000000000..5257e1bd6 --- /dev/null +++ b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd @@ -0,0 +1,9 @@ +echo This script generates the certificate and private key for the https webadmin +echo Note that the generated certificate is self-signed, and therefore not trusted by browsers +echo Note that this script requires openssl to be installed and in PATH +echo. +echo When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz +echo. + +openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes +pause \ No newline at end of file diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh new file mode 100644 index 000000000..0c8594f1a --- /dev/null +++ b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo "This script generates the certificate and private key for the https webadmin" +echo "Note that the generated certificate is self-signed, and therefore not trusted by browsers" +echo "Note that this script requires openssl to be installed and in PATH" +echo "" +echo "When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz" +echo "" + +openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.pem -days 3650 -nodes \ No newline at end of file From dc2d2ce53c2f751f72217a26d4f68d72f5f0f5f0 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 12:27:07 +0200 Subject: [PATCH 08/41] Added a mention to run as admin. --- MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd index 5257e1bd6..661cc34e8 100644 --- a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd +++ b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd @@ -4,6 +4,8 @@ echo Note that this script requires openssl to be installed and in PATH echo. echo When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz echo. +echo If OpenSSL fails with an error, "WARNING: can't open config file: /usr/local/ssl/openssl.cnf", you need to run this script as an administrator +echo. openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes pause \ No newline at end of file From 60850fe3e8da936d5b24460f33a1bf8f4d321ace Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 15:08:15 +0200 Subject: [PATCH 09/41] Fixed crashes in the SSL HTTP connection. --- src/ClientHandle.cpp | 3 ++- src/ClientHandle.h | 2 +- src/HTTPServer/HTTPConnection.cpp | 14 +++++++------- src/HTTPServer/HTTPConnection.h | 12 +++++++++--- src/HTTPServer/SslHTTPConnection.cpp | 10 +++++++--- src/HTTPServer/SslHTTPConnection.h | 2 +- src/OSSupport/SocketThreads.h | 6 ++++-- src/RCONServer.cpp | 8 ++++---- src/RCONServer.h | 2 +- 9 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 2362abe1e..a2bfdd381 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -2649,12 +2649,13 @@ void cClientHandle::PacketError(unsigned char a_PacketType) -void cClientHandle::DataReceived(const char * a_Data, size_t a_Size) +bool cClientHandle::DataReceived(const char * a_Data, size_t a_Size) { // Data is received from the client, store it in the buffer to be processed by the Tick thread: m_TimeSinceLastPacket = 0; cCSLock Lock(m_CSIncomingData); m_IncomingData.append(a_Data, a_Size); + return false; } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 9f8d44129..ac44e43dc 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -387,7 +387,7 @@ private: void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length); // cSocketThreads::cCallback overrides: - virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client + virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client virtual void SocketClosed (void) override; // The socket has been closed for any reason }; // tolua_export diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp index 8e95eff2d..b127e7091 100644 --- a/src/HTTPServer/HTTPConnection.cpp +++ b/src/HTTPServer/HTTPConnection.cpp @@ -145,7 +145,7 @@ void cHTTPConnection::Terminate(void) -void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +bool cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) { switch (m_State) { @@ -163,12 +163,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) m_CurrentRequest = NULL; m_State = wcsInvalid; m_HTTPServer.CloseConnection(*this); - return; + return true; } if (m_CurrentRequest->IsInHeaders()) { // The request headers are not yet complete - return; + return false; } // The request has finished parsing its headers successfully, notify of it: @@ -184,13 +184,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) // Process the rest of the incoming data into the request body: if (a_Size > BytesConsumed) { - cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); + return cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed); } else { - cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away + return cHTTPConnection::DataReceived("", 0); // If the request has zero body length, let it be processed right-away } - break; } case wcsRecvBody: @@ -210,7 +209,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) { m_State = wcsInvalid; m_HTTPServer.CloseConnection(*this); - return; + return true; } delete m_CurrentRequest; m_CurrentRequest = NULL; @@ -224,6 +223,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) break; } } + return false; } diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h index fc11f1ba6..6ea8a1ae8 100644 --- a/src/HTTPServer/HTTPConnection.h +++ b/src/HTTPServer/HTTPConnection.h @@ -91,9 +91,15 @@ protected: // cSocketThreads::cCallback overrides: - virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client - virtual void SocketClosed (void) override; // The socket has been closed for any reason + /** Data is received from the client. + Returns true if the connection has been closed as the result of parsing the data. */ + virtual bool DataReceived(const char * a_Data, size_t a_Size) override; + + /** Data can be sent to client */ + virtual void GetOutgoingData(AString & a_Data) override; + + /** The socket has been closed for any reason */ + virtual void SocketClosed(void) override; } ; typedef std::vector cHTTPConnections; diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp index fff96bb2e..b6b222b47 100644 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -25,7 +25,7 @@ cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509Ce -void cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) +bool cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) { // If there is outgoing data in the queue, notify the server that it should write it out: if (!m_OutgoingData.empty()) @@ -52,13 +52,17 @@ void cSslHTTPConnection::DataReceived(const char * a_Data, size_t a_Size) int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer)); if (NumRead > 0) { - super::DataReceived(Buffer, (size_t)NumRead); + if (super::DataReceived(Buffer, (size_t)NumRead)) + { + // The socket has been closed, and the object is already deleted. Bail out. + return true; + } } // If both failed, bail out: if ((BytesWritten == 0) && (NumRead <= 0)) { - return; + return false; } } } diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h index 2a648e8c8..653acbfce 100644 --- a/src/HTTPServer/SslHTTPConnection.h +++ b/src/HTTPServer/SslHTTPConnection.h @@ -36,7 +36,7 @@ protected: cPublicKeyPtr m_PrivateKey; // cHTTPConnection overrides: - virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client + virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client } ; diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h index 679e374e1..944f5f3bc 100644 --- a/src/OSSupport/SocketThreads.h +++ b/src/OSSupport/SocketThreads.h @@ -63,8 +63,10 @@ public: // Force a virtual destructor in all subclasses: virtual ~cCallback() {} - /** Called when data is received from the remote party */ - virtual void DataReceived(const char * a_Data, size_t a_Size) = 0; + /** Called when data is received from the remote party. + SocketThreads does not care about the return value, others can use it for their specific purpose - + for example HTTPServer uses it to signal if the connection was terminated as a result of the data received. */ + virtual bool DataReceived(const char * a_Data, size_t a_Size) = 0; /** Called when data can be sent to remote party The function is supposed to *set* outgoing data to a_Data (overwrite) */ diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index d7083ff2b..fd4b26cab 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -169,7 +169,7 @@ cRCONServer::cConnection::cConnection(cRCONServer & a_RCONServer, cSocket & a_So -void cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) +bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) { // Append data to the buffer: m_Buffer.append(a_Data, a_Size); @@ -187,12 +187,12 @@ void cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) m_RCONServer.m_SocketThreads.RemoveClient(this); m_Socket.CloseSocket(); delete this; - return; + return false; } if (Length > (int)(m_Buffer.size() + 4)) { // Incomplete packet yet, wait for more data to come - return; + return false; } int RequestID = IntFromBuffer(m_Buffer.data() + 4); @@ -202,7 +202,7 @@ void cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) m_RCONServer.m_SocketThreads.RemoveClient(this); m_Socket.CloseSocket(); delete this; - return; + return false; } m_Buffer.erase(0, Length + 4); } // while (m_Buffer.size() >= 14) diff --git a/src/RCONServer.h b/src/RCONServer.h index b964852ab..47c746736 100644 --- a/src/RCONServer.h +++ b/src/RCONServer.h @@ -65,7 +65,7 @@ protected: // cSocketThreads::cCallback overrides: - virtual void DataReceived(const char * a_Data, size_t a_Size) override; + virtual bool DataReceived(const char * a_Data, size_t a_Size) override; virtual void GetOutgoingData(AString & a_Data) override; virtual void SocketClosed(void) override; From 1587b21edded56dbfb88150500336c2853b460c6 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 15:21:41 +0200 Subject: [PATCH 10/41] Renamed cPublicKey to cCryptoKey. The class can hold both the private key and the public key, bad naming on PolarSSL's part. Also somewhat fixed the cert and key loading in cHTTPServer. --- src/HTTPServer/HTTPServer.cpp | 6 ++--- src/HTTPServer/HTTPServer.h | 6 ++--- src/HTTPServer/SslHTTPConnection.cpp | 2 +- src/HTTPServer/SslHTTPConnection.h | 8 +++---- .../{PublicKey.cpp => CryptoKey.cpp} | 24 +++++++++---------- src/PolarSSL++/{PublicKey.h => CryptoKey.h} | 18 +++++++------- src/PolarSSL++/CtrDrbgContext.h | 2 +- src/PolarSSL++/SslContext.cpp | 2 +- src/PolarSSL++/SslContext.h | 13 +++++----- 9 files changed, 40 insertions(+), 41 deletions(-) rename src/PolarSSL++/{PublicKey.cpp => CryptoKey.cpp} (77%) rename src/PolarSSL++/{PublicKey.h => CryptoKey.h} (85%) diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index 9e3e0a17b..c45044c66 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -124,17 +124,17 @@ class cDebugCallbacks : cHTTPServer::cHTTPServer(void) : m_ListenThreadIPv4(*this, cSocket::IPv4, "WebServer IPv4"), m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), - m_Callbacks(NULL), - m_Cert(new cX509Cert), - m_CertPrivKey(new cPublicKey) + m_Callbacks(NULL) { AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem"); if (!CertFile.empty() && !KeyFile.empty()) { + m_Cert.reset(new cX509Cert); int res = m_Cert->Parse(CertFile.data(), CertFile.size()); if (res == 0) { + m_CertPrivKey.reset(new cCryptoKey); int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), ""); if (res2 != 0) { diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h index eb91dd5a3..522b7da62 100644 --- a/src/HTTPServer/HTTPServer.h +++ b/src/HTTPServer/HTTPServer.h @@ -13,7 +13,7 @@ #include "../OSSupport/SocketThreads.h" #include "inifile/iniFile.h" #include "PolarSSL++/RsaPrivateKey.h" -#include "PolarSSL++/PublicKey.h" +#include "PolarSSL++/CryptoKey.h" #include "PolarSSL++/X509Cert.h" @@ -85,8 +85,8 @@ protected: /** The server certificate to use for the SSL connections */ cX509CertPtr m_Cert; - /** The private key for m_Cert. Despite the class name, this is the PRIVATE key. */ - cPublicKeyPtr m_CertPrivKey; + /** The private key for m_Cert. */ + cCryptoKeyPtr m_CertPrivKey; // cListenThread::cCallback overrides: diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp index b6b222b47..d237089d9 100644 --- a/src/HTTPServer/SslHTTPConnection.cpp +++ b/src/HTTPServer/SslHTTPConnection.cpp @@ -11,7 +11,7 @@ -cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cPublicKeyPtr & a_PrivateKey) : +cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey) : super(a_HTTPServer), m_Ssl(64000), m_Cert(a_Cert), diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h index 653acbfce..c2c1585cd 100644 --- a/src/HTTPServer/SslHTTPConnection.h +++ b/src/HTTPServer/SslHTTPConnection.h @@ -22,9 +22,9 @@ class cSslHTTPConnection : typedef cHTTPConnection super; public: - /** Creates a new connection on the specified server; sends the specified cert as the server certificate, - uses the private key for decryption. a_Private key is, despite the class name, a PRIVATE key for the cert. */ - cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cPublicKeyPtr & a_PrivateKey); + /** Creates a new connection on the specified server. + Sends the specified cert as the server certificate, uses the private key for decryption. */ + cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey); protected: cBufferedSslContext m_Ssl; @@ -33,7 +33,7 @@ protected: cX509CertPtr m_Cert; /** The private key used for the certificate */ - cPublicKeyPtr m_PrivateKey; + cCryptoKeyPtr m_PrivateKey; // cHTTPConnection overrides: virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client diff --git a/src/PolarSSL++/PublicKey.cpp b/src/PolarSSL++/CryptoKey.cpp similarity index 77% rename from src/PolarSSL++/PublicKey.cpp rename to src/PolarSSL++/CryptoKey.cpp index dae026082..0763c387b 100644 --- a/src/PolarSSL++/PublicKey.cpp +++ b/src/PolarSSL++/CryptoKey.cpp @@ -1,16 +1,16 @@ -// PublicKey.cpp +// CryptoKey.cpp -// Implements the cPublicKey class representing a RSA public key in PolarSSL +// Implements the cCryptoKey class representing a RSA public key in PolarSSL #include "Globals.h" -#include "PublicKey.h" +#include "CryptoKey.h" -cPublicKey::cPublicKey(void) +cCryptoKey::cCryptoKey(void) { pk_init(&m_Pk); m_CtrDrbg.Initialize("rsa_pubkey", 10); @@ -20,7 +20,7 @@ cPublicKey::cPublicKey(void) -cPublicKey::cPublicKey(const AString & a_PublicKeyData) +cCryptoKey::cCryptoKey(const AString & a_PublicKeyData) { pk_init(&m_Pk); m_CtrDrbg.Initialize("rsa_pubkey", 10); @@ -37,7 +37,7 @@ cPublicKey::cPublicKey(const AString & a_PublicKeyData) -cPublicKey::cPublicKey(const AString & a_PrivateKeyData, const AString & a_Password) +cCryptoKey::cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Password) { pk_init(&m_Pk); m_CtrDrbg.Initialize("rsa_privkey", 11); @@ -54,7 +54,7 @@ cPublicKey::cPublicKey(const AString & a_PrivateKeyData, const AString & a_Passw -cPublicKey::~cPublicKey() +cCryptoKey::~cCryptoKey() { pk_free(&m_Pk); } @@ -63,7 +63,7 @@ cPublicKey::~cPublicKey() -int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) +int cCryptoKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) { ASSERT(IsValid()); @@ -84,7 +84,7 @@ int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, -int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) +int cCryptoKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) { ASSERT(IsValid()); @@ -105,7 +105,7 @@ int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a -int cPublicKey::ParsePublic(const void * a_Data, size_t a_NumBytes) +int cCryptoKey::ParsePublic(const void * a_Data, size_t a_NumBytes) { ASSERT(!IsValid()); // Cannot parse a second key @@ -117,7 +117,7 @@ int cPublicKey::ParsePublic(const void * a_Data, size_t a_NumBytes) -int cPublicKey::ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password) +int cCryptoKey::ParsePrivate(const void * a_Data, size_t a_NumBytes, const AString & a_Password) { ASSERT(!IsValid()); // Cannot parse a second key @@ -139,7 +139,7 @@ int cPublicKey::ParsePrivate(const void * a_Data, size_t a_NumBytes, const AStri -bool cPublicKey::IsValid(void) const +bool cCryptoKey::IsValid(void) const { return (pk_get_type(&m_Pk) != POLARSSL_PK_NONE); } diff --git a/src/PolarSSL++/PublicKey.h b/src/PolarSSL++/CryptoKey.h similarity index 85% rename from src/PolarSSL++/PublicKey.h rename to src/PolarSSL++/CryptoKey.h index df52a4143..9c298e501 100644 --- a/src/PolarSSL++/PublicKey.h +++ b/src/PolarSSL++/CryptoKey.h @@ -1,7 +1,7 @@ -// PublicKey.h +// CryptoKey.h -// Declares the cPublicKey class representing a RSA public key in PolarSSL +// Declares the cCryptoKey class representing a RSA public key in PolarSSL @@ -16,22 +16,22 @@ -class cPublicKey +class cCryptoKey { friend class cSslContext; public: /** Constructs an empty key instance. Before use, it needs to be filled by ParsePublic() or ParsePrivate() */ - cPublicKey(void); + cCryptoKey(void); /** Constructs the public key out of the DER- or PEM-encoded pubkey data */ - cPublicKey(const AString & a_PublicKeyData); + cCryptoKey(const AString & a_PublicKeyData); /** Constructs the private key out of the DER- or PEM-encoded privkey data, with the specified password. If a_Password is empty, no password is assumed. */ - cPublicKey(const AString & a_PrivateKeyData, const AString & a_Password); + cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Password); - ~cPublicKey(); + ~cCryptoKey(); /** Decrypts the data using the stored public key Both a_EncryptedData and a_DecryptedData must be at least bytes large. @@ -58,7 +58,7 @@ public: bool IsValid(void) const; protected: - /** The public key PolarSSL representation */ + /** The PolarSSL representation of the key data */ pk_context m_Pk; /** The random generator used in encryption and decryption */ @@ -69,7 +69,7 @@ protected: pk_context * GetInternal(void) { return &m_Pk; } } ; -typedef SharedPtr cPublicKeyPtr; +typedef SharedPtr cCryptoKeyPtr; diff --git a/src/PolarSSL++/CtrDrbgContext.h b/src/PolarSSL++/CtrDrbgContext.h index 65e9a2374..230db8753 100644 --- a/src/PolarSSL++/CtrDrbgContext.h +++ b/src/PolarSSL++/CtrDrbgContext.h @@ -26,7 +26,7 @@ class cCtrDrbgContext { friend class cSslContext; friend class cRsaPrivateKey; - friend class cPublicKey; + friend class cCryptoKey; public: /** Constructs the context with a new entropy context. */ diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index df0219610..bc397b655 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -115,7 +115,7 @@ void cSslContext::SetOwnCert(const cX509CertPtr & a_OwnCert, const cRsaPrivateKe -void cSslContext::SetOwnCert(const cX509CertPtr & a_OwnCert, const cPublicKeyPtr & a_OwnCertPrivKey) +void cSslContext::SetOwnCert(const cX509CertPtr & a_OwnCert, const cCryptoKeyPtr & a_OwnCertPrivKey) { ASSERT(m_IsValid); // Call Initialize() first diff --git a/src/PolarSSL++/SslContext.h b/src/PolarSSL++/SslContext.h index 273939b9f..a4ad1a345 100644 --- a/src/PolarSSL++/SslContext.h +++ b/src/PolarSSL++/SslContext.h @@ -11,7 +11,7 @@ #include "polarssl/ssl.h" #include "../ByteBuffer.h" -#include "PublicKey.h" +#include "CryptoKey.h" #include "RsaPrivateKey.h" #include "X509Cert.h" @@ -54,9 +54,8 @@ public: void SetOwnCert(const cX509CertPtr & a_OwnCert, const cRsaPrivateKeyPtr & a_OwnCertPrivKey); /** Sets the certificate to use as our own. Must be used when representing a server, optional when client. - Must be called after Initialize(). - Despite the class name, a_OwnCertPrivKey is a PRIVATE key. */ - void SetOwnCert(const cX509CertPtr & a_OwnCert, const cPublicKeyPtr & a_OwnCertPrivKey); + Must be called after Initialize(). */ + void SetOwnCert(const cX509CertPtr & a_OwnCert, const cCryptoKeyPtr & a_OwnCertPrivKey); /** Sets a cert chain as the trusted cert store for this context. Must be called after Initialize(). Calling this will switch the context into strict cert verification mode. @@ -107,11 +106,11 @@ protected: /** The certificate that we present to the peer. */ cX509CertPtr m_OwnCert; - /** Private key for m_OwnCert, if initialized from a cRsaPrivateKey */ + /** Private key for m_OwnCert, if initialized from a cRsaPrivateKey. */ cRsaPrivateKeyPtr m_OwnCertPrivKey; - /** Private key for m_OwnCert, if initialized from a cPublicKey. Despite the class name, this is a PRIVATE key. */ - cPublicKeyPtr m_OwnCertPrivKey2; + /** Private key for m_OwnCert, if initialized from a cCryptoKey. */ + cCryptoKeyPtr m_OwnCertPrivKey2; /** True if the SSL handshake has been completed. */ bool m_HasHandshaken; From d9e5dbf16526aa102abdc818d3515928663a973c Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 1 May 2014 18:06:23 +0200 Subject: [PATCH 11/41] Renamed PublicKey to CryptoKey in CMakeLists.txt --- src/PolarSSL++/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PolarSSL++/CMakeLists.txt b/src/PolarSSL++/CMakeLists.txt index b0a592760..9a59cdb2c 100644 --- a/src/PolarSSL++/CMakeLists.txt +++ b/src/PolarSSL++/CMakeLists.txt @@ -1,4 +1,3 @@ - cmake_minimum_required (VERSION 2.6) project (MCServer) @@ -11,8 +10,8 @@ set(SOURCES BufferedSslContext.cpp CallbackSslContext.cpp CtrDrbgContext.cpp + CryptoKey.cpp EntropyContext.cpp - PublicKey.cpp RsaPrivateKey.cpp Sha1Checksum.cpp SslContext.cpp @@ -26,8 +25,8 @@ set(HEADERS BufferedSslContext.h CallbackSslContext.h CtrDrbgContext.h + CryptoKey.h EntropyContext.h - PublicKey.h RsaPrivateKey.h SslContext.h Sha1Checksum.h From 3e854bc5960dccbcecc32bf2f280f1fb04f67307 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 20:20:12 +0200 Subject: [PATCH 12/41] ProtoProxy: Renamed PublicKey to CryptoKey. --- Tools/ProtoProxy/CMakeLists.txt | 4 ++-- Tools/ProtoProxy/Connection.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt index a136c95df..f0796363c 100644 --- a/Tools/ProtoProxy/CMakeLists.txt +++ b/Tools/ProtoProxy/CMakeLists.txt @@ -38,9 +38,9 @@ set(SHARED_SRC ../../src/MCLogger.cpp ../../src/PolarSSL++/AesCfb128Decryptor.cpp ../../src/PolarSSL++/AesCfb128Encryptor.cpp + ../../src/PolarSSL++/CryptoKey.cpp ../../src/PolarSSL++/CtrDrbgContext.cpp ../../src/PolarSSL++/EntropyContext.cpp - ../../src/PolarSSL++/PublicKey.cpp ../../src/PolarSSL++/RsaPrivateKey.cpp ) set(SHARED_HDR @@ -50,9 +50,9 @@ set(SHARED_HDR ../../src/MCLogger.h ../../src/PolarSSL++/AesCfb128Decryptor.h ../../src/PolarSSL++/AesCfb128Encryptor.h + ../../src/PolarSSL++/CryptoKey.h ../../src/PolarSSL++/CtrDrbgContext.h ../../src/PolarSSL++/EntropyContext.h - ../../src/PolarSSL++/PublicKey.h ../../src/PolarSSL++/RsaPrivateKey.h ) set(SHARED_OSS_SRC diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index 26aed2206..c5916c1ca 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -7,7 +7,7 @@ #include "Connection.h" #include "Server.h" #include -#include "PolarSSL++/PublicKey.h" +#include "PolarSSL++/CryptoKey.h" #ifdef _WIN32 #include // For _mkdir() @@ -2900,7 +2900,7 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c Byte SharedSecret[16]; Byte EncryptedSecret[128]; memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret - cPublicKey PubKey(a_ServerPublicKey); + cCryptoKey PubKey(a_ServerPublicKey); int res = PubKey.Encrypt(SharedSecret, sizeof(SharedSecret), EncryptedSecret, sizeof(EncryptedSecret)); if (res < 0) { From 9221b458989512e41f8502b56ed738d143599093 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 21:23:37 +0200 Subject: [PATCH 13/41] cSslContext has virtual destructor now. --- src/PolarSSL++/SslContext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PolarSSL++/SslContext.h b/src/PolarSSL++/SslContext.h index a4ad1a345..6b4f2c1e7 100644 --- a/src/PolarSSL++/SslContext.h +++ b/src/PolarSSL++/SslContext.h @@ -40,7 +40,7 @@ public: /** Creates a new uninitialized context */ cSslContext(void); - ~cSslContext(); + virtual ~cSslContext(); /** Initializes the context for use as a server or client. Returns 0 on success, PolarSSL error on failure. */ From 30e81156eb6de46d7a374c044e26b35d83af4244 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 1 May 2014 22:02:45 +0200 Subject: [PATCH 14/41] Added a missing return statement. --- src/RCONServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RCONServer.cpp b/src/RCONServer.cpp index fd4b26cab..cd5292ac3 100644 --- a/src/RCONServer.cpp +++ b/src/RCONServer.cpp @@ -206,6 +206,7 @@ bool cRCONServer::cConnection::DataReceived(const char * a_Data, size_t a_Size) } m_Buffer.erase(0, Length + 4); } // while (m_Buffer.size() >= 14) + return false; } From e3d850b24a66cb926fced7b98eea6ed4d12b5f6f Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 2 May 2014 21:35:30 +0000 Subject: [PATCH 15/41] Made the cert generation script executable on Linux --- MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh old mode 100644 new mode 100755 From 3942ebdcb6e62d8b2fb85fad9144a993a78477a6 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Fri, 2 May 2014 23:46:06 +0200 Subject: [PATCH 16/41] WebAdmin outputs a log message about HTTP / HTTPS status. --- src/HTTPServer/HTTPServer.cpp | 57 +++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index c45044c66..b78a538a2 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -126,28 +126,6 @@ cHTTPServer::cHTTPServer(void) : m_ListenThreadIPv6(*this, cSocket::IPv6, "WebServer IPv6"), m_Callbacks(NULL) { - AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); - AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem"); - if (!CertFile.empty() && !KeyFile.empty()) - { - m_Cert.reset(new cX509Cert); - int res = m_Cert->Parse(CertFile.data(), CertFile.size()); - if (res == 0) - { - m_CertPrivKey.reset(new cCryptoKey); - int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), ""); - if (res2 != 0) - { - // Reading the private key failed, reset the cert: - LOGWARNING("WebAdmin: Cannot read HTTPS certificate private key: -0x%x", -res2); - m_Cert.reset(); - } - } - else - { - LOGWARNING("WebAdmin: Cannot read HTTPS certificate: -0x%x", -res); - } - } } @@ -165,6 +143,41 @@ cHTTPServer::~cHTTPServer() bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_PortsIPv6) { + // Read the HTTPS cert + key: + AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt"); + AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem"); + if (!CertFile.empty() && !KeyFile.empty()) + { + m_Cert.reset(new cX509Cert); + int res = m_Cert->Parse(CertFile.data(), CertFile.size()); + if (res == 0) + { + m_CertPrivKey.reset(new cCryptoKey); + int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), ""); + if (res2 != 0) + { + // Reading the private key failed, reset the cert: + LOGWARNING("WebServer: Cannot read HTTPS certificate private key: -0x%x", -res2); + m_Cert.reset(); + } + } + else + { + LOGWARNING("WebServer: Cannot read HTTPS certificate: -0x%x", -res); + } + } + + // Notify the admin about the HTTPS / HTTP status + if (m_Cert.get() == NULL) + { + LOGWARNING("WebServer: The server is running in unsecure HTTP mode."); + } + else + { + LOGINFO("WebServer: The server is running in secure HTTPS mode."); + } + + // Open up requested ports: bool HasAnyPort; HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; From ac1c3ba5d9ee407110700e2c5e9ce1d87e191e29 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Tue, 6 May 2014 21:43:31 +0200 Subject: [PATCH 17/41] Fixed an extra space. --- src/HTTPServer/HTTPServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index b78a538a2..d288c83c9 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -150,7 +150,7 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port { m_Cert.reset(new cX509Cert); int res = m_Cert->Parse(CertFile.data(), CertFile.size()); - if (res == 0) + if (res == 0) { m_CertPrivKey.reset(new cCryptoKey); int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), ""); From 45ca5bb857dbe418d59bdfcf9109b5aab2144b59 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 7 May 2014 15:09:25 +0200 Subject: [PATCH 18/41] Fixed cert filename in Linux script. --- MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd | 2 +- MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd index 661cc34e8..3ea6963b4 100644 --- a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd +++ b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.cmd @@ -8,4 +8,4 @@ echo If OpenSSL fails with an error, "WARNING: can't open config file: /usr/loca echo. openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes -pause \ No newline at end of file +pause diff --git a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh index 0c8594f1a..5cf1237c8 100755 --- a/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh +++ b/MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh @@ -7,4 +7,4 @@ echo "" echo "When OpenSSL asks you for Common Name, you need to enter the fully qualified domain name of the server, that is, e. g. gallery.xoft.cz" echo "" -openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.pem -days 3650 -nodes \ No newline at end of file +openssl req -x509 -newkey rsa:2048 -keyout httpskey.pem -out httpscert.crt -days 3650 -nodes From c8631d9a9b924c4c4d030bac3ed4e4a869ec2fd8 Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 9 May 2014 23:10:02 +0200 Subject: [PATCH 19/41] Add DIG_STATUS_CANCELLED packet and add item resend, when a block can't place/break. --- src/ClientHandle.cpp | 61 ++++++++++++++++++++++++++++++++------------ src/ClientHandle.h | 3 +++ src/Inventory.cpp | 10 ++++++++ src/Inventory.h | 41 +++++++++++++++-------------- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 94f031ed6..1535b0482 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -878,6 +878,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB case DIG_STATUS_CANCELLED: { // Block breaking cancelled by player + HandleBlockDigCancel(); return; } @@ -916,7 +917,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc // It is a duplicate packet, drop it right away return; } - + if ( m_Player->IsGameModeCreative() && ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) @@ -925,14 +926,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc // Players can't destroy blocks with a Sword in the hand. return; } - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) - { - // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - + // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: m_HasStartedDigging = true; m_LastDigBlockX = a_BlockX; @@ -1004,16 +998,16 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo return; } - m_HasStartedDigging = false; - if (m_BlockDigAnimStage != -1) - { - // End dig animation - m_BlockDigAnimStage = -1; - // It seems that 10 ends block animation - m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this); - } + HandleBlockDigCancel(); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) + { + // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows: + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } if (a_OldBlock == E_BLOCK_AIR) { @@ -1036,6 +1030,36 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo +void cClientHandle::HandleBlockDigCancel() +{ + if ( + !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet + (m_LastDigBlockX == -1) || + (m_LastDigBlockY == -1) || + (m_LastDigBlockZ == -1) + ) + { + return; + } + + m_HasStartedDigging = false; + if (m_BlockDigAnimStage != -1) + { + // End dig animation + m_BlockDigAnimStage = -1; + // It seems that 10 ends block animation + m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_LastDigBlockX, m_LastDigBlockY, m_LastDigBlockZ, 10, this); + } + + m_BlockDigAnimX = -1; + m_BlockDigAnimY = -1; + m_BlockDigAnimZ = -1; +} + + + + + void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem) { LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s", @@ -1058,6 +1082,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); //2 block high things + m_Player->GetInventory().SendEquippedSlot(); } return; } @@ -1246,6 +1271,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e { // Handler refused the placement, send that information back to the client: World->SendBlockTo(a_BlockX, a_BlockY, a_BlockY, m_Player); + m_Player->GetInventory().SendEquippedSlot(); return; } @@ -1255,6 +1281,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e { // A plugin doesn't agree with placing the block, revert the block on the client: World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + m_Player->GetInventory().SendEquippedSlot(); return; } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 4dc6ab074..03a716657 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -374,6 +374,9 @@ private: /** Handles the DIG_FINISHED dig packet: */ void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + /** Handles the DIG_CANCELLED dig packet: */ + void HandleBlockDigCancel(); + /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ AStringVector BreakApartPluginChannels(const AString & a_PluginChannels); diff --git a/src/Inventory.cpp b/src/Inventory.cpp index a365e4ed4..bce882c88 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -243,6 +243,16 @@ void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item) +void cInventory::SendEquippedSlot() +{ + int EquippedSlotNum = cInventory::invArmorCount + cInventory::invInventoryCount + GetEquippedSlotNum(); + SendSlot(EquippedSlotNum); +} + + + + + const cItem & cInventory::GetSlot(int a_SlotNum) const { if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots)) diff --git a/src/Inventory.h b/src/Inventory.h index 1ad7c4776..39aef1538 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -56,13 +56,13 @@ public: // tolua_begin - /// Removes all items from the entire inventory + /** Removes all items from the entire inventory */ void Clear(void); - /// Returns number of items out of a_ItemStack that can fit in the storage + /** Returns number of items out of a_ItemStack that can fit in the storage */ int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots); - /// Returns how many items of the specified type would fit into the slot range specified + /** Returns how many items of the specified type would fit into the slot range specified */ int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots); /** Adds as many items out of a_ItemStack as can fit. @@ -86,33 +86,36 @@ public: */ int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst); - /// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed + /** Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed */ bool RemoveOneEquippedItem(void); - /// Returns the number of items of type a_Item that are stored + /** Returns the number of items of type a_Item that are stored */ int HowManyItems(const cItem & a_Item); - /// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack + /** Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack */ bool HasItems(const cItem & a_ItemStack); + + /** Sends the equipped item slot to the client */ + void SendEquippedSlot(); - /// Returns the cItemGrid object representing the armor slots + /** Returns the cItemGrid object representing the armor slots */ cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; } - /// Returns the cItemGrid object representing the main inventory slots + /** Returns the cItemGrid object representing the main inventory slots */ cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; } - /// Returns the cItemGrid object representing the hotbar slots + /** Returns the cItemGrid object representing the hotbar slots */ cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; } - /// Returns the player associated with this inventory + /** Returns the player associated with this inventory */ cPlayer & GetOwner(void) { return m_Owner; } - /// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents + /** Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents */ void CopyToItems(cItems & a_Items); // tolua_end - /// Returns the player associated with this inventory (const version) + /** Returns the player associated with this inventory (const version) */ const cPlayer & GetOwner(void) const { return m_Owner; } // tolua_begin @@ -136,10 +139,10 @@ public: */ int ChangeSlotCount(int a_SlotNum, int a_AddToCount); - /// Adds the specified damage to the specified item; deletes the item and returns true if the item broke. + /** Adds the specified damage to the specified item; deletes the item and returns true if the item broke. */ bool DamageItem(int a_SlotNum, short a_Amount); - /// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. + /** Adds the specified damage to the currently held item; deletes the item and returns true if the item broke. */ bool DamageEquippedItem(short a_Amount = 1); const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); } @@ -149,13 +152,13 @@ public: // tolua_end - /// Sends the slot contents to the owner + /** Sends the slot contents to the owner */ void SendSlot(int a_SlotNum); - /// Update items (e.g. Maps) + /** Update items (e.g. Maps) */ void UpdateItems(void); - /// Converts an armor slot number into the ID for the EntityEquipment packet + /** Converts an armor slot number into the ID for the EntityEquipment packet */ static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum); void SaveToJson(Json::Value & a_Value); @@ -172,10 +175,10 @@ protected: cPlayer & m_Owner; - /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + /** Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum */ const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const; - /// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum + /** Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum */ cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum); // cItemGrid::cListener override: From eb0f713b6a5a7107a97284d1728858b4b2882c3d Mon Sep 17 00:00:00 2001 From: Howaner Date: Fri, 9 May 2014 23:43:00 +0200 Subject: [PATCH 20/41] Add block place/break distance check. --- src/ClientHandle.cpp | 21 +++++++++++++++++++++ src/Defines.h | 10 ++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 1535b0482..d1962506d 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1016,6 +1016,17 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo } cWorld * World = m_Player->GetWorld(); + + if ( + (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || + (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || + (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) + ) + { + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); // The ItemHandler is also responsible for spawning the pickups cChunkInterface ChunkInterface(World->GetChunkMap()); @@ -1191,6 +1202,16 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e // The block is being placed outside the world, ignore this packet altogether (#128) return; } + + if ( + (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || + (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || + (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) + ) + { + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); diff --git a/src/Defines.h b/src/Defines.h index 9fa3b3a8e..b0b209934 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -3,6 +3,7 @@ #include "ChatColor.h" #include +#include @@ -528,6 +529,15 @@ inline float GetSpecialSignf( float a_Val ) +template inline int Diff(T a_Val1, T a_Val2) +{ + return std::abs(a_Val1 - a_Val2); +} + + + + + // tolua_begin enum eMessageType From 683b839e2b3e634dd1a0a5b85327efe4ffa968fd Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sat, 10 May 2014 09:21:29 +0200 Subject: [PATCH 21/41] Client cert is not requested. --- src/PolarSSL++/SslContext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp index bc397b655..c3074f197 100644 --- a/src/PolarSSL++/SslContext.cpp +++ b/src/PolarSSL++/SslContext.cpp @@ -59,7 +59,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr & return res; } ssl_set_endpoint(&m_Ssl, a_IsClient ? SSL_IS_CLIENT : SSL_IS_SERVER); - ssl_set_authmode(&m_Ssl, SSL_VERIFY_OPTIONAL); + ssl_set_authmode(&m_Ssl, a_IsClient ? SSL_VERIFY_OPTIONAL : SSL_VERIFY_NONE); // Clients ask for server's cert but don't verify strictly; servers don't ask clients for certs by default ssl_set_rng(&m_Ssl, ctr_drbg_random, &m_CtrDrbg->m_CtrDrbg); ssl_set_bio(&m_Ssl, ReceiveEncrypted, this, SendEncrypted, this); From 5c9f89526aeca7b32014cd78e323f74baddf1e36 Mon Sep 17 00:00:00 2001 From: Howaner Date: Sun, 11 May 2014 11:56:15 +0200 Subject: [PATCH 22/41] Rename HandleBlockDigCancel to FinishDigAnimtion. --- src/ClientHandle.cpp | 6 +++--- src/ClientHandle.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index d1962506d..9238418a4 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -878,7 +878,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB case DIG_STATUS_CANCELLED: { // Block breaking cancelled by player - HandleBlockDigCancel(); + FinishDigAnimation(); return; } @@ -998,7 +998,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo return; } - HandleBlockDigCancel(); + FinishDigAnimation(); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); @@ -1041,7 +1041,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo -void cClientHandle::HandleBlockDigCancel() +void cClientHandle::FinishDigAnimation() { if ( !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 03a716657..85d348eee 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -374,8 +374,8 @@ private: /** Handles the DIG_FINISHED dig packet: */ void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - /** Handles the DIG_CANCELLED dig packet: */ - void HandleBlockDigCancel(); + /** The clients will receive a finished dig animation */ + void FinishDigAnimation(); /** Converts the protocol-formatted channel list (NUL-separated) into a proper string vector. */ AStringVector BreakApartPluginChannels(const AString & a_PluginChannels); From c7c3724a3ee0e7a77fe9924ad25c36b6ec8fdd14 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 11 May 2014 14:57:06 +0300 Subject: [PATCH 23/41] Statistic Manager --- src/Bindings/AllToLua.pkg | 1 + src/ClientHandle.cpp | 19 ++++- src/ClientHandle.h | 2 + src/Entities/Player.cpp | 21 ++++- src/Entities/Player.h | 7 ++ src/Protocol/Protocol.h | 2 + src/Protocol/Protocol125.cpp | 28 +++++++ src/Protocol/Protocol125.h | 1 + src/Protocol/Protocol17x.cpp | 48 +++++++++-- src/Protocol/Protocol17x.h | 1 + src/Protocol/ProtocolRecognizer.cpp | 10 +++ src/Protocol/ProtocolRecognizer.h | 1 + src/Statistics.cpp | 123 +++++++++++++++++++-------- src/Statistics.h | 48 +++++++++++ src/WorldStorage/StatSerializer.cpp | 126 ++++++++++++++++++++++++++++ src/WorldStorage/StatSerializer.h | 53 ++++++++++++ 16 files changed, 448 insertions(+), 43 deletions(-) create mode 100644 src/WorldStorage/StatSerializer.cpp create mode 100644 src/WorldStorage/StatSerializer.h diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 1cd7c74f8..4fe86e1c5 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -76,6 +76,7 @@ $cfile "../CompositeChat.h" $cfile "../Map.h" $cfile "../MapManager.h" $cfile "../Scoreboard.h" +$cfile "../Statistics.h" diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 94f031ed6..03b1b0dc2 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -31,6 +31,8 @@ #include "CompositeChat.h" #include "Items/ItemSword.h" +#include "WorldStorage/StatSerializer.h" + #include "md5/md5.h" @@ -336,7 +338,13 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) // Send scoreboard data World->GetScoreBoard().SendTo(*this); - + +#if 0 + // Load stats + cStatSerializer StatSerializer(World->GetName(), m_Player->GetName(), &m_Player->GetStatManager()); + StatSerializer.Load(); +#endif + // Delay the first ping until the client "settles down" // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client cTimer t1; @@ -2437,6 +2445,15 @@ void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTy +void cClientHandle::SendStatistics(const cStatManager & a_Manager) +{ + m_Protocol->SendStatistics(a_Manager); +} + + + + + void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results) { m_Protocol->SendTabCompletionResults(a_Results); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 4dc6ab074..0236d38fb 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -39,6 +39,7 @@ class cFallingBlock; class cItemHandler; class cWorld; class cCompositeChat; +class cStatManager; @@ -160,6 +161,7 @@ public: void SendSpawnMob (const cMonster & a_Mob); void SendSpawnObject (const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch); void SendSpawnVehicle (const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType = 0); + void SendStatistics (const cStatManager & a_Manager); void SendTabCompletionResults(const AStringVector & a_Results); void SendTeleportEntity (const cEntity & a_Entity); void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 6ac11c270..a42fe89cf 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -16,6 +16,8 @@ #include "../Items/ItemHandler.h" #include "../Vector3.h" +#include "../WorldStorage/StatSerializer.h" + #include "inifile/iniFile.h" #include "json/json.h" @@ -131,6 +133,15 @@ cPlayer::~cPlayer(void) SaveToDisk(); +#if 0 + /* Save statistics. */ + cStatSerializer StatSerializer(m_World->GetName(), m_PlayerName, &m_Stats); + if (!StatSerializer.Save()) + { + LOGERROR("Could not save stats for player %s", m_PlayerName.c_str()); + } +#endif + m_World->RemovePlayer( this ); m_ClientHandle = NULL; @@ -871,9 +882,13 @@ void cPlayer::KilledBy(cEntity * a_Killer) } else if (a_Killer->IsPlayer()) { - GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), ((cPlayer *)a_Killer)->GetName().c_str())); + cPlayer* Killer = (cPlayer*)a_Killer; - m_World->GetScoreBoard().AddPlayerScore(((cPlayer *)a_Killer)->GetName(), cObjective::otPlayerKillCount, 1); + GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); + + Killer->GetStatManager().AddValue(statPlayerKills); + + m_World->GetScoreBoard().AddPlayerScore(Killer->GetName(), cObjective::otPlayerKillCount, 1); } else { @@ -883,6 +898,8 @@ void cPlayer::KilledBy(cEntity * a_Killer) GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str())); } + m_Stats.AddValue(statDeaths); + m_World->GetScoreBoard().AddPlayerScore(GetName(), cObjective::otDeathCount, 1); } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 6fc7e2875..82a138290 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -7,6 +7,8 @@ #include "../World.h" #include "../ClientHandle.h" +#include "../Statistics.h" + @@ -174,6 +176,9 @@ public: cTeam * UpdateTeam(void); // tolua_end + + /** Return the associated statistic and achievement manager. */ + cStatManager & GetStatManager() { return m_Stats; } void SetIP(const AString & a_IP); @@ -487,6 +492,8 @@ protected: cTeam * m_Team; + cStatManager m_Stats; + void ResolvePermissions(void); diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 8f152ad37..a543c6361 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -31,6 +31,7 @@ class cMonster; class cChunkDataSerializer; class cFallingBlock; class cCompositeChat; +class cStatManager; @@ -111,6 +112,7 @@ public: 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; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index e7873cf7a..f3bdae3ac 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -99,6 +99,7 @@ enum 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, @@ -992,6 +993,33 @@ void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp +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 diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 423e58d67..18a626a2d 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -84,6 +84,7 @@ public: 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; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 443723e40..3b21f7821 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -11,13 +11,19 @@ Implements the 1.7.x protocol classes: #include "json/json.h" #include "Protocol17x.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 "../StringCompression.h" + #include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" @@ -25,15 +31,15 @@ Implements the 1.7.x protocol classes: #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/CommandBlockEntity.h" #include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/FlowerPotEntity.h" -#include "../CompositeChat.h" -#include "../Entities/ArrowEntity.h" -#include "../Entities/FireworkEntity.h" -#include "PolarSSL++/Sha1Checksum.h" @@ -1169,6 +1175,28 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp +void cProtocol172::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 (unsigned int i = 0; i < (unsigned int)statCount; ++i) + { + StatValue Value = a_Manager.GetValue((eStatistic) i); + + const AString & StatName = cStatInfo::GetName((eStatistic) i); + + Pkt.WriteString(StatName); + Pkt.WriteVarInt(Value); + } +} + + + + + void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) { ASSERT(m_State == 3); // In game mode? @@ -1843,13 +1871,19 @@ void cProtocol172::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer) case 1: { // Request stats - // TODO + const cStatManager & Manager = m_Client->GetPlayer()->GetStatManager(); + SendStatistics(Manager); + break; } case 2: { // Open Inventory achievement - // TODO + cStatManager & Manager = m_Client->GetPlayer()->GetStatManager(); + Manager.AddValue(achOpenInv); + + SendStatistics(Manager); + break; } } diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index dc111e737..3c6a8c085 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -116,6 +116,7 @@ public: 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; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 667fb5cef..b0cbb6def 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -675,6 +675,16 @@ void cProtocolRecognizer::SendSpawnVehicle(const cEntity & a_Vehicle, char a_Veh +void cProtocolRecognizer::SendStatistics(const cStatManager & a_Manager) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendStatistics(a_Manager); +} + + + + + void cProtocolRecognizer::SendTabCompletionResults(const AStringVector & a_Results) { ASSERT(m_Protocol != NULL); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 37f47379d..3a291bf7a 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -119,6 +119,7 @@ public: 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; diff --git a/src/Statistics.cpp b/src/Statistics.cpp index 2c980d98e..94076f828 100644 --- a/src/Statistics.cpp +++ b/src/Statistics.cpp @@ -13,39 +13,39 @@ cStatInfo cStatInfo::ms_Info[statCount] = { // http://minecraft.gamepedia.com/Achievements /* Type | Name | Prerequisite */ - cStatInfo(achOpenInv, "openInventory"), - cStatInfo(achMineWood, "mineWood", achOpenInv), - cStatInfo(achCraftWorkbench, "buildWorkBench", achMineWood), - cStatInfo(achCraftPickaxe, "buildPickaxe", achCraftWorkbench), - cStatInfo(achCraftFurnace, "buildFurnace", achCraftPickaxe), - cStatInfo(achAcquireIron, "acquireIron", achCraftFurnace), - cStatInfo(achCraftHoe, "buildHoe", achCraftWorkbench), - cStatInfo(achMakeBread, "makeBread", achCraftHoe), - cStatInfo(achBakeCake, "bakeCake", achCraftHoe), - cStatInfo(achCraftBetterPick, "buildBetterPickaxe", achCraftPickaxe), - cStatInfo(achCookFish, "cookFish", achAcquireIron), - cStatInfo(achOnARail, "onARail", achAcquireIron), - cStatInfo(achCraftSword, "buildSword", achCraftWorkbench), - cStatInfo(achKillMonster, "killEnemy", achCraftSword), - cStatInfo(achKillCow, "killCow", achCraftSword), - cStatInfo(achFlyPig, "flyPig", achKillCow), - cStatInfo(achSnipeSkeleton, "snipeSkeleton", achKillMonster), - cStatInfo(achDiamonds, "diamonds", achAcquireIron), - cStatInfo(achEnterPortal, "portal", achDiamonds), - cStatInfo(achReturnToSender, "ghast", achEnterPortal), - cStatInfo(achBlazeRod, "blazeRod", achEnterPortal), - cStatInfo(achBrewPotion, "potion", achBlazeRod), - cStatInfo(achEnterTheEnd, "theEnd", achBlazeRod), - cStatInfo(achDefeatDragon, "theEnd2", achEnterTheEnd), - cStatInfo(achCraftEnchantTable, "enchantments", achDiamonds), - cStatInfo(achOverkill, "overkill", achCraftEnchantTable), - cStatInfo(achBookshelf, "bookcase", achCraftEnchantTable), - cStatInfo(achExploreAllBiomes, "exploreAllBiomes", achEnterTheEnd), - cStatInfo(achSpawnWither, "spawnWither", achDefeatDragon), - cStatInfo(achKillWither, "killWither", achSpawnWither), - cStatInfo(achFullBeacon, "fullBeacon", achKillWither), - cStatInfo(achBreedCow, "breedCow", achKillCow), - cStatInfo(achThrowDiamonds, "diamondsToYou", achDiamonds), + cStatInfo(achOpenInv, "achievement.openInventory"), + cStatInfo(achMineWood, "achievement.mineWood", achOpenInv), + cStatInfo(achCraftWorkbench, "achievement.buildWorkBench", achMineWood), + cStatInfo(achCraftPickaxe, "achievement.buildPickaxe", achCraftWorkbench), + cStatInfo(achCraftFurnace, "achievement.buildFurnace", achCraftPickaxe), + cStatInfo(achAcquireIron, "achievement.acquireIron", achCraftFurnace), + cStatInfo(achCraftHoe, "achievement.buildHoe", achCraftWorkbench), + cStatInfo(achMakeBread, "achievement.makeBread", achCraftHoe), + cStatInfo(achBakeCake, "achievement.bakeCake", achCraftHoe), + cStatInfo(achCraftBetterPick, "achievement.buildBetterPickaxe", achCraftPickaxe), + cStatInfo(achCookFish, "achievement.cookFish", achAcquireIron), + cStatInfo(achOnARail, "achievement.onARail", achAcquireIron), + cStatInfo(achCraftSword, "achievement.buildSword", achCraftWorkbench), + cStatInfo(achKillMonster, "achievement.killEnemy", achCraftSword), + cStatInfo(achKillCow, "achievement.killCow", achCraftSword), + cStatInfo(achFlyPig, "achievement.flyPig", achKillCow), + cStatInfo(achSnipeSkeleton, "achievement.snipeSkeleton", achKillMonster), + cStatInfo(achDiamonds, "achievement.diamonds", achAcquireIron), + cStatInfo(achEnterPortal, "achievement.portal", achDiamonds), + cStatInfo(achReturnToSender, "achievement.ghast", achEnterPortal), + cStatInfo(achBlazeRod, "achievement.blazeRod", achEnterPortal), + cStatInfo(achBrewPotion, "achievement.potion", achBlazeRod), + cStatInfo(achEnterTheEnd, "achievement.theEnd", achBlazeRod), + cStatInfo(achDefeatDragon, "achievement.theEnd2", achEnterTheEnd), + cStatInfo(achCraftEnchantTable, "achievement.enchantments", achDiamonds), + cStatInfo(achOverkill, "achievement.overkill", achCraftEnchantTable), + cStatInfo(achBookshelf, "achievement.bookcase", achCraftEnchantTable), + cStatInfo(achExploreAllBiomes, "achievement.exploreAllBiomes", achEnterTheEnd), + cStatInfo(achSpawnWither, "achievement.spawnWither", achDefeatDragon), + cStatInfo(achKillWither, "achievement.killWither", achSpawnWither), + cStatInfo(achFullBeacon, "achievement.fullBeacon", achKillWither), + cStatInfo(achBreedCow, "achievement.breedCow", achKillCow), + cStatInfo(achThrowDiamonds, "achievement.diamondsToYou", achDiamonds), // http://minecraft.gamepedia.com/Statistics @@ -57,6 +57,7 @@ cStatInfo cStatInfo::ms_Info[statCount] = { cStatInfo(statDistFallen, "stat.fallOneCm"), cStatInfo(statDistClimbed, "stat.climbOneCm"), cStatInfo(statDistFlown, "stat.flyOneCm"), + cStatInfo(statDistDove, "stat.diveOneCm"), cStatInfo(statDistMinecart, "stat.minecartOneCm"), cStatInfo(statDistBoat, "stat.boatOneCm"), cStatInfo(statDistPig, "stat.pigOneCm"), @@ -137,3 +138,59 @@ eStatistic cStatInfo::GetPrerequisite(const eStatistic a_Type) +cStatManager::cStatManager() +{ + Reset(); +} + + + + + +StatValue cStatManager::GetValue(const eStatistic a_Stat) const +{ + ASSERT((a_Stat > statInvalid) && (a_Stat < statCount)); + + return m_MainStats[a_Stat]; +} + + + + + +void cStatManager::SetValue(const eStatistic a_Stat, const StatValue a_Value) +{ + ASSERT((a_Stat > statInvalid) && (a_Stat < statCount)); + + m_MainStats[a_Stat] = a_Value; +} + + + + + +StatValue cStatManager::AddValue(const eStatistic a_Stat, const StatValue a_Delta) +{ + ASSERT((a_Stat > statInvalid) && (a_Stat < statCount)); + + m_MainStats[a_Stat] += a_Delta; + + return m_MainStats[a_Stat]; +} + + + + + +void cStatManager::Reset(void) +{ + for (unsigned int i = 0; i < (unsigned int)statCount; ++i) + { + m_MainStats[i] = 0; + } +} + + + + + diff --git a/src/Statistics.h b/src/Statistics.h index 540df38cc..f37f32e1e 100644 --- a/src/Statistics.h +++ b/src/Statistics.h @@ -9,6 +9,7 @@ +// tolua_begin enum eStatistic { // The order must match the order of cStatInfo::ms_Info @@ -77,6 +78,7 @@ enum eStatistic statCount }; +// tolua_end @@ -114,3 +116,49 @@ private: + +/* Signed (?) integral value. */ +typedef int StatValue; // tolua_export + + + + +/** Class that manages the statistics and achievements of a single player. */ +// tolua_begin +class cStatManager +{ +public: + // tolua_end + + cStatManager(); + + // tolua_begin + + /** Return the value of the specified stat. */ + StatValue GetValue(const eStatistic a_Stat) const; + + /** Set the value of the specified stat. */ + void SetValue(const eStatistic a_Stat, const StatValue a_Value); + + /** Reset everything. */ + void Reset(); + + /** Increment the specified stat. + * + * Returns the new value. + */ + StatValue AddValue(const eStatistic a_Stat, const StatValue a_Delta = 1); + + // tolua_end + +private: + + StatValue m_MainStats[statCount]; + + // TODO 10-05-2014 xdot: Use, mine, craft statistics + + +}; // tolua_export + + + diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp new file mode 100644 index 000000000..5c6724c60 --- /dev/null +++ b/src/WorldStorage/StatSerializer.cpp @@ -0,0 +1,126 @@ + +// StatSerializer.cpp + + +#include "Globals.h" +#include "StatSerializer.h" + +#include "../Statistics.h" + +#include + + + + + +cStatSerializer::cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager) + : m_Manager(a_Manager) +{ + AString StatsPath; + Printf(StatsPath, "%s/stats", a_WorldName.c_str()); + + m_Path = StatsPath + "/" + a_PlayerName + ".dat"; + + /* Ensure that the directory exists. */ + cFile::CreateFolder(FILE_IO_PREFIX + StatsPath); +} + + + + + +bool cStatSerializer::Load(void) +{ + AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path); + if (Data.empty()) + { + return false; + } + + Json::Value Root; + Json::Reader Reader; + + if (Reader.parse(Data, Root, false)) + { + return LoadStatFromJSON(Root); + } + + return false; +} + + + + + +bool cStatSerializer::Save(void) +{ + Json::Value Root; + SaveStatToJSON(Root); + + cFile File; + if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite)) + { + return false; + } + + Json::StyledWriter Writer; + AString JsonData = Writer.write(Root); + + File.Write(JsonData.data(), JsonData.size()); + File.Close(); + + return true; +} + + + + + +void cStatSerializer::SaveStatToJSON(Json::Value & a_Out) +{ + for (unsigned int i = 0; i < (unsigned int)statCount; ++i) + { + StatValue Value = m_Manager->GetValue((eStatistic) i); + + if (Value != 0) + { + const AString & StatName = cStatInfo::GetName((eStatistic) i); + + a_Out[StatName] = Value; + } + } +} + + + + + +bool cStatSerializer::LoadStatFromJSON(const Json::Value & a_In) +{ + m_Manager->Reset(); + + for (Json::ValueIterator it = a_In.begin() ; it != a_In.end() ; ++it) + { + AString StatName = it.key().asString(); + + eStatistic StatType = cStatInfo::GetType(StatName); + + if (StatType == statInvalid) + { + LOGWARNING("Invalid statistic type %s", StatName.c_str()); + continue; + } + + m_Manager->SetValue(StatType, (*it).asInt()); + } + + return true; +} + + + + + + + + diff --git a/src/WorldStorage/StatSerializer.h b/src/WorldStorage/StatSerializer.h new file mode 100644 index 000000000..43514465b --- /dev/null +++ b/src/WorldStorage/StatSerializer.h @@ -0,0 +1,53 @@ + +// StatSerializer.h + +// Declares the cStatSerializer class that is used for saving stats into JSON + + + + + +#pragma once + +#include "json/json.h" + + + + + +// fwd: +class cStatManager; + + + + +class cStatSerializer +{ +public: + + cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager); + + bool Load(void); + + bool Save(void); + + +protected: + + void SaveStatToJSON(Json::Value & a_Out); + + bool LoadStatFromJSON(const Json::Value & a_In); + + +private: + + cStatManager* m_Manager; + + AString m_Path; + + +} ; + + + + From e3c6c8f3ddffdc368ebbc1a7688de2ca1b97167c Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 11 May 2014 20:30:54 +0300 Subject: [PATCH 24/41] Fixed stat serialization --- src/ClientHandle.cpp | 8 -------- src/Entities/Player.cpp | 31 +++++++++++++++++++---------- src/Statistics.cpp | 2 +- src/WorldStorage/StatSerializer.cpp | 27 ++++++++++++++++++++----- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 03b1b0dc2..c4f4e496a 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -31,8 +31,6 @@ #include "CompositeChat.h" #include "Items/ItemSword.h" -#include "WorldStorage/StatSerializer.h" - #include "md5/md5.h" @@ -339,12 +337,6 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) // Send scoreboard data World->GetScoreBoard().SendTo(*this); -#if 0 - // Load stats - cStatSerializer StatSerializer(World->GetName(), m_Player->GetName(), &m_Player->GetStatManager()); - StatSerializer.Load(); -#endif - // Delay the first ping until the client "settles down" // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client cTimer t1; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index a42fe89cf..1df473eb2 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -133,15 +133,6 @@ cPlayer::~cPlayer(void) SaveToDisk(); -#if 0 - /* Save statistics. */ - cStatSerializer StatSerializer(m_World->GetName(), m_PlayerName, &m_Stats); - if (!StatSerializer.Save()) - { - LOGERROR("Could not save stats for player %s", m_PlayerName.c_str()); - } -#endif - m_World->RemovePlayer( this ); m_ClientHandle = NULL; @@ -1638,6 +1629,12 @@ bool cPlayer::LoadFromDisk() m_Inventory.LoadFromJson(root["inventory"]); m_LoadedWorldName = root.get("world", "world").asString(); + + /* Load the player stats. + * We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + */ + cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); + StatSerializer.Load(); LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() @@ -1709,6 +1706,17 @@ bool cPlayer::SaveToDisk() LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); return false; } + + /* Save the player stats. + * We use the default world name (like bukkit) because stats are shared between dimensions/worlds. + */ + cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), m_PlayerName, &m_Stats); + if (!StatSerializer.Save()) + { + LOGERROR("Could not save stats for player %s", m_PlayerName.c_str()); + return false; + } + return true; } @@ -1723,7 +1731,10 @@ cPlayer::StringList cPlayer::GetResolvedPermissions() const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) { - if( itr->second ) Permissions.push_back( itr->first ); + if (itr->second) + { + Permissions.push_back( itr->first ); + } } return Permissions; diff --git a/src/Statistics.cpp b/src/Statistics.cpp index 94076f828..65addb0dd 100644 --- a/src/Statistics.cpp +++ b/src/Statistics.cpp @@ -114,7 +114,7 @@ eStatistic cStatInfo::GetType(const AString & a_Name) { for (unsigned int i = 0; i < ARRAYCOUNT(ms_Info); ++i) { - if (NoCaseCompare(ms_Info[i].m_Name, a_Name)) + if (NoCaseCompare(ms_Info[i].m_Name, a_Name) == 0) { return ms_Info[i].m_Type; } diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp index 5c6724c60..66e5e1e9e 100644 --- a/src/WorldStorage/StatSerializer.cpp +++ b/src/WorldStorage/StatSerializer.cpp @@ -7,8 +7,6 @@ #include "../Statistics.h" -#include - @@ -19,7 +17,7 @@ cStatSerializer::cStatSerializer(const AString& a_WorldName, const AString& a_Pl AString StatsPath; Printf(StatsPath, "%s/stats", a_WorldName.c_str()); - m_Path = StatsPath + "/" + a_PlayerName + ".dat"; + m_Path = StatsPath + "/" + a_PlayerName + ".json"; /* Ensure that the directory exists. */ cFile::CreateFolder(FILE_IO_PREFIX + StatsPath); @@ -88,6 +86,8 @@ void cStatSerializer::SaveStatToJSON(Json::Value & a_Out) a_Out[StatName] = Value; } + + // TODO 2014-05-11 xdot: Save "progress" } } @@ -107,11 +107,28 @@ bool cStatSerializer::LoadStatFromJSON(const Json::Value & a_In) if (StatType == statInvalid) { - LOGWARNING("Invalid statistic type %s", StatName.c_str()); + LOGWARNING("Invalid statistic type \"%s\"", StatName.c_str()); continue; } - m_Manager->SetValue(StatType, (*it).asInt()); + Json::Value & Node = *it; + + if (Node.isInt()) + { + m_Manager->SetValue(StatType, Node.asInt()); + } + else if (Node.isObject()) + { + StatValue Value = Node.get("value", 0).asInt(); + + // TODO 2014-05-11 xdot: Load "progress" + + m_Manager->SetValue(StatType, Value); + } + else + { + LOGWARNING("Invalid statistic value for type \"%s\"", StatName.c_str()); + } } return true; From 6cb348395468020ebac978d5a4968953e49d8b90 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 11 May 2014 21:41:25 +0300 Subject: [PATCH 25/41] Fixed compilation --- src/Entities/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Entities/CMakeLists.txt b/src/Entities/CMakeLists.txt index c9ca44d38..205cb2cca 100644 --- a/src/Entities/CMakeLists.txt +++ b/src/Entities/CMakeLists.txt @@ -10,3 +10,5 @@ file(GLOB SOURCE ) add_library(Entities ${SOURCE}) + +target_link_libraries(Entities WorldStorage) From 6c57b38b741787cd53a42b905d27e2d137ae194b Mon Sep 17 00:00:00 2001 From: archshift Date: Sun, 11 May 2014 13:44:30 -0700 Subject: [PATCH 26/41] Fixed a warning and a complaint about a never-read variable. --- src/BlockArea.cpp | 25 +++++++++++-------------- src/WorldStorage/WorldStorage.cpp | 1 - 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 9e20a0983..aff0bf9c6 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -2123,7 +2123,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msOverwrite case cBlockArea::msFillAir: @@ -2137,7 +2137,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msFillAir case cBlockArea::msImprint: @@ -2151,7 +2151,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msImprint case cBlockArea::msLake: @@ -2165,7 +2165,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msLake case cBlockArea::msSpongePrint: @@ -2179,7 +2179,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msSpongePrint case cBlockArea::msDifference: @@ -2193,7 +2193,7 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msDifference case cBlockArea::msMask: @@ -2207,16 +2207,13 @@ void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_Rel a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), m_Size.x, m_Size.y, m_Size.z ); - break; + return; } // case msMask - - default: - { - LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); - ASSERT(!"Unknown block area merge strategy"); - break; - } } // switch (a_Strategy) + + LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); + ASSERT(!"Unknown block area merge strategy"); + return; } diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 54eaaca5c..6867ad5bc 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -243,7 +243,6 @@ void cWorldStorage::Execute(void) bool Success; do { - Success = false; if (m_ShouldTerminate) { return; From 3f9e00a3f3486c2845115e495d66f438f90825f3 Mon Sep 17 00:00:00 2001 From: archshift Date: Sun, 11 May 2014 16:25:21 -0700 Subject: [PATCH 27/41] Fixed a few more switch warnings. --- src/Generating/StructGen.cpp | 28 +++++++++---------- src/Mobs/Monster.cpp | 2 ++ src/WorldStorage/NBTChunkSerializer.cpp | 37 +++++++++++++------------ 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index db9d5578c..636364e17 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -596,24 +596,22 @@ void cStructGenDirectOverhangs::GenFinish(cChunkDesc & a_ChunkDesc) // Interpolate between FloorLo and FloorHi: for (int z = 0; z < 16; z++) for (int x = 0; x < 16; x++) { - switch (a_ChunkDesc.GetBiome(x, z)) + EMCSBiome biome = a_ChunkDesc.GetBiome(x, z); + + if ((biome == biExtremeHills) || (biome == biExtremeHillsEdge)) { - case biExtremeHills: - case biExtremeHillsEdge: + int Lo = FloorLo[x + 17 * z] / 256; + int Hi = FloorHi[x + 17 * z] / 256; + for (int y = 0; y < SEGMENT_HEIGHT; y++) { - int Lo = FloorLo[x + 17 * z] / 256; - int Hi = FloorHi[x + 17 * z] / 256; - for (int y = 0; y < SEGMENT_HEIGHT; y++) + int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; + if (Val < 0) { - int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT; - if (Val < 0) - { - a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR); - } - } // for y - break; - } - } // switch (biome) + a_ChunkDesc.SetBlockType(x, y + Segment, z, E_BLOCK_AIR); + } + } // for y + break; + } // if (biome) } // for z, x // Swap the floors: diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 62670907f..5832edb9f 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -355,6 +355,8 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) InStateEscaping(a_Dt); break; } + + case ATTACKING: break; } // switch (m_EMState) BroadcastMovementUpdate(); diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 2ac1d7962..ab80dd7c8 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -391,38 +391,41 @@ void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock) void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart) { - const char * EntityClass = NULL; - switch (a_Minecart->GetPayload()) - { - case cMinecart::mpNone: EntityClass = "MinecartRideable"; break; - case cMinecart::mpChest: EntityClass = "MinecartChest"; break; - case cMinecart::mpFurnace: EntityClass = "MinecartFurnace"; break; - case cMinecart::mpTNT: EntityClass = "MinecartTNT"; break; - case cMinecart::mpHopper: EntityClass = "MinecartHopper"; break; - default: - { - ASSERT(!"Unhandled minecart payload type"); - return; - } - } // switch (payload) - m_Writer.BeginCompound(""); - AddBasicEntity(a_Minecart, EntityClass); + switch (a_Minecart->GetPayload()) { case cMinecart::mpChest: { + AddBasicEntity(a_Minecart, "MinecartChest"); // Add chest contents into the Items tag: AddMinecartChestContents((cMinecartWithChest *)a_Minecart); break; } - case cMinecart::mpFurnace: { + AddBasicEntity(a_Minecart, "MinecartFurnace"); // TODO: Add "Push" and "Fuel" tags break; } + case cMinecart::mpHopper: + { + AddBasicEntity(a_Minecart, "MinecartHopper"); + // TODO: Add hopper contents? + break; + } + case cMinecart::mpTNT: + { + AddBasicEntity(a_Minecart, "MinecartTNT"); + break; + } + case cMinecart::mpNone: + { + AddBasicEntity(a_Minecart, "MinecartRideable"); + break; + } } // switch (Payload) + m_Writer.EndCompound(); } From 3a5e04d118a88dc33930866546007f59fdba951f Mon Sep 17 00:00:00 2001 From: archshift Date: Sun, 11 May 2014 16:54:42 -0700 Subject: [PATCH 28/41] More switch warnings. --- src/Generating/Trees.cpp | 34 ++++++++++++++++++++----- src/WorldStorage/NBTChunkSerializer.cpp | 7 +++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index 4f1553c36..522f45148 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -174,7 +174,7 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No { GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); } - break; + return; } case biTaiga: @@ -184,14 +184,14 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No { // Conifers GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; + return; } case biSwampland: { // Swamp trees: GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; + return; } case biJungle: @@ -207,21 +207,21 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No { GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); } - break; + return; } case biBirchForest: case biBirchForestHills: { GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; + return; } case biBirchForestM: case biBirchForestHillsM: { GetTallBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; + return; } case biRoofedForest: @@ -257,9 +257,29 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No { // TODO: These need their special trees GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); - break; + return; + } + + case biDesert: + case biDesertHills: + case biRiver: + case biBeach: + case biHell: + case biSky: + case biOcean: + case biFrozenOcean: + case biFrozenRiver: + case biVariant: + case biNumBiomes: + case biNumVariantBiomes: + case biInvalidBiome: + { + // These biomes have no trees, or are non-biome members of the enum. + return; } } + + ASSERT(!"Invalid biome type!"); } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index ab80dd7c8..7b7cd3c7e 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -653,6 +653,13 @@ void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging) case BLOCK_FACE_YP: m_Writer.AddByte("Dir", (unsigned char)1); break; case BLOCK_FACE_ZM: m_Writer.AddByte("Dir", (unsigned char)0); break; case BLOCK_FACE_ZP: m_Writer.AddByte("Dir", (unsigned char)3); break; + + case BLOCK_FACE_XM: + case BLOCK_FACE_XP: + case BLOCK_FACE_NONE: + { + break; + } } } From b3d2b5b2c94193fd9364b26293b7d96b748ff96d Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 12 May 2014 17:05:09 +0300 Subject: [PATCH 29/41] cEntity::Killed(cEntity *) Handler; Achievement triggers; cPlayer::AwardAchievement() --- src/Entities/Entity.cpp | 5 +++ src/Entities/Entity.h | 3 ++ src/Entities/Pickup.cpp | 10 ++++++ src/Entities/Player.cpp | 70 +++++++++++++++++++++++++++++++++--- src/Entities/Player.h | 11 ++++++ src/Protocol/Protocol17x.cpp | 6 +--- src/UI/SlotArea.cpp | 42 +++++++++++++++++++++- src/UI/SlotArea.h | 6 ++++ 8 files changed, 143 insertions(+), 10 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 4cf10a219..9eb03acde 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -370,6 +370,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if (m_Health <= 0) { KilledBy(a_TDI.Attacker); + + if (a_TDI.Attacker != NULL) + { + a_TDI.Attacker->Killed(this); + } } return true; } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index df03d635b..a111b128d 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -299,6 +299,9 @@ public: /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) virtual void KilledBy(cEntity * a_Killer); + /// Called when the entity kills another entity + virtual void Killed(cEntity * a_Victim) {} + /// Heals the specified amount of HPs void Heal(int a_HitPoints); diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 497b41683..0fd006485 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -192,6 +192,16 @@ bool cPickup::CollectedBy(cPlayer * a_Dest) int NumAdded = a_Dest->GetInventory().AddItem(m_Item); if (NumAdded > 0) { + // Check achievements + switch (m_Item.m_ItemType) + { + case E_BLOCK_LOG: a_Dest->AwardAchievement(achMineWood); break; + case E_ITEM_LEATHER: a_Dest->AwardAchievement(achKillCow); break; + case E_ITEM_DIAMOND: a_Dest->AwardAchievement(achDiamonds); break; + case E_ITEM_BLAZE_ROD: a_Dest->AwardAchievement(achBlazeRod); break; + default: break; + } + m_Item.m_ItemCount -= NumAdded; m_World->BroadcastCollectPickup(*this, *a_Dest); // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 1df473eb2..48bb509b9 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -17,6 +17,7 @@ #include "../Vector3.h" #include "../WorldStorage/StatSerializer.h" +#include "../CompositeChat.h" #include "inifile/iniFile.h" #include "json/json.h" @@ -876,10 +877,6 @@ void cPlayer::KilledBy(cEntity * a_Killer) cPlayer* Killer = (cPlayer*)a_Killer; GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); - - Killer->GetStatManager().AddValue(statPlayerKills); - - m_World->GetScoreBoard().AddPlayerScore(Killer->GetName(), cObjective::otPlayerKillCount, 1); } else { @@ -898,6 +895,33 @@ void cPlayer::KilledBy(cEntity * a_Killer) +void cPlayer::Killed(cEntity * a_Victim) +{ + cScoreboard & ScoreBoard = m_World->GetScoreBoard(); + + if (a_Victim->IsPlayer()) + { + m_Stats.AddValue(statPlayerKills); + + ScoreBoard.AddPlayerScore(GetName(), cObjective::otPlayerKillCount, 1); + } + else if (a_Victim->IsMob()) + { + if (((cMonster *)a_Victim)->GetMobFamily() == cMonster::mfHostile) + { + AwardAchievement(achKillMonster); + } + + m_Stats.AddValue(statMobKills); + } + + ScoreBoard.AddPlayerScore(GetName(), cObjective::otTotalKillCount, 1); +} + + + + + void cPlayer::Respawn(void) { m_Health = GetMaxHealth(); @@ -1116,6 +1140,44 @@ void cPlayer::SetIP(const AString & a_IP) +unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) +{ + eStatistic Prerequisite = cStatInfo::GetPrerequisite(a_Ach); + + if (Prerequisite != statInvalid) + { + if (m_Stats.GetValue(Prerequisite) == 0) + { + return 0; + } + } + + StatValue Old = m_Stats.GetValue(a_Ach); + + if (Old > 0) + { + return m_Stats.AddValue(a_Ach); + } + else + { + cCompositeChat Msg; + Msg.AddTextPart(m_PlayerName + " has just earned the achievement "); + Msg.AddTextPart(cStatInfo::GetName(a_Ach)); // TODO 2014-05-12 xdot: Use the proper cCompositeChat submessage type and send the actual title + m_World->BroadcastChat(Msg); + + StatValue New = m_Stats.AddValue(a_Ach); + + /* Achievement Get! */ + m_ClientHandle->SendStatistics(m_Stats); + + return New; + } +} + + + + + void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { SetPosition(a_PosX, a_PosY, a_PosZ); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 82a138290..b5c9d75cc 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -179,6 +179,15 @@ public: /** Return the associated statistic and achievement manager. */ cStatManager & GetStatManager() { return m_Stats; } + + /** Awards the player an achievement. + * + * If all prerequisites are met, this method will award the achievement and will broadcast a chat message. + * If the achievement has been already awarded to the player, this method will just increment the stat counter. + * + * Returns the _new_ stat value. (0 = Could not award achievement) + */ + unsigned int AwardAchievement(const eStatistic a_Ach); void SetIP(const AString & a_IP); @@ -311,6 +320,8 @@ public: void AbortEating(void); virtual void KilledBy(cEntity * a_Killer) override; + + virtual void Killed(cEntity * a_Victim) override; void Respawn(void); // tolua_export diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 3b21f7821..39feee16f 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -1879,11 +1879,7 @@ void cProtocol172::HandlePacketClientStatus(cByteBuffer & a_ByteBuffer) case 2: { // Open Inventory achievement - cStatManager & Manager = m_Client->GetPlayer()->GetStatManager(); - Manager.AddValue(achOpenInv); - - SendStatistics(Manager); - + m_Client->GetPlayer()->AwardAchievement(achOpenInv); break; } } diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 788974f9c..1e3d8eaa4 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -496,6 +496,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem = Result; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } else if (DraggingItem.IsEqual(Result)) { @@ -505,6 +507,8 @@ void cSlotAreaCrafting::ClickedResult(cPlayer & a_Player) DraggingItem.m_ItemCount += Result.m_ItemCount; Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); } } @@ -594,6 +598,27 @@ cCraftingRecipe & cSlotAreaCrafting::GetRecipeForPlayer(cPlayer & a_Player) +void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Player) +{ + switch (a_Result.m_ItemType) + { + case E_BLOCK_WORKBENCH: a_Player.AwardAchievement(achCraftWorkbench); break; + case E_BLOCK_FURNACE: a_Player.AwardAchievement(achCraftFurnace); break; + case E_BLOCK_CAKE: a_Player.AwardAchievement(achBakeCake); break; + case E_BLOCK_ENCHANTMENT_TABLE: a_Player.AwardAchievement(achCraftEnchantTable); break; + case E_BLOCK_BOOKCASE: a_Player.AwardAchievement(achBookshelf); break; + case E_ITEM_WOODEN_PICKAXE: a_Player.AwardAchievement(achCraftPickaxe); break; + case E_ITEM_WOODEN_SWORD: a_Player.AwardAchievement(achCraftSword); break; + case E_ITEM_STONE_PICKAXE: a_Player.AwardAchievement(achCraftBetterPick); break; + case E_ITEM_WOODEN_HOE: a_Player.AwardAchievement(achCraftHoe); break; + case E_ITEM_BREAD: a_Player.AwardAchievement(achMakeBread); break; + default: break; + } +} + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaAnvil: @@ -1393,7 +1418,7 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) { // Something has changed in the window, broadcast the entire window to all clients ASSERT(a_ItemGrid == &(m_Furnace->GetContents())); - + m_ParentWindow.BroadcastWholeWindow(); } @@ -1401,6 +1426,21 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player) +{ + /** TODO 2014-05-12 xdot: Figure out when to call this method. */ + switch (a_Result.m_ItemType) + { + case E_ITEM_IRON: a_Player.AwardAchievement(achAcquireIron); break; + case E_ITEM_COOKED_FISH: a_Player.AwardAchievement(achCookFish); break; + default: break; + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cSlotAreaInventoryBase: diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 4da6a672f..e297bcff7 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -254,6 +254,9 @@ protected: /// Retrieves the recipe for the specified player from the map, or creates one if not found cCraftingRecipe & GetRecipeForPlayer(cPlayer & a_Player); + + /// Called after an item has been crafted to handle statistics e.t.c. + void HandleCraftItem(const cItem & a_Result, cPlayer & a_Player); } ; @@ -397,6 +400,9 @@ protected: // cItemGrid::cListener overrides: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; + + /// Called after an item has been smelted to handle statistics e.t.c. + void HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player); } ; From aea866f5b10d5ab0226260b4d25c70b1cfd31d2a Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 12 May 2014 21:38:52 +0300 Subject: [PATCH 30/41] Movement Statistics --- src/Entities/Entity.cpp | 15 ++++++- src/Entities/Player.cpp | 86 ++++++++++++++++++++++++++++++++++++----- src/Entities/Player.h | 6 +++ src/Item.h | 2 +- src/Mobs/Monster.cpp | 4 +- src/Statistics.cpp | 2 +- 6 files changed, 101 insertions(+), 14 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 9eb03acde..c393f89fd 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -312,12 +312,16 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) { + cPlayer * Player = (cPlayer *)a_TDI.Attacker; + // IsOnGround() only is false if the player is moving downwards - if (!((cPlayer *)a_TDI.Attacker)->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + if (!Player->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) { a_TDI.FinalDamage += 2; m_World->BroadcastEntityAnimation(*this, 4); // Critical hit } + + Player->GetStatManager().AddValue(statDamageDealt, round(a_TDI.FinalDamage * 10)); } m_Health -= (short)a_TDI.FinalDamage; @@ -580,9 +584,16 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk) if (m_AttachedTo != NULL) { - if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) + Vector3d DeltaPos = m_Pos - m_AttachedTo->GetPosition(); + if (DeltaPos.Length() > 0.5) { SetPosition(m_AttachedTo->GetPosition()); + + if (IsPlayer()) + { + cPlayer * Player = (cPlayer *)this; + Player->UpdateMovementStats(DeltaPos); + } } } else diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 48bb509b9..3df7c4c34 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -194,6 +194,8 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) return; } } + + m_Stats.AddValue(statMinutesPlayed, 1); if (!a_Chunk.IsValid()) { @@ -835,6 +837,8 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) // Any kind of damage adds food exhaustion AddFoodExhaustion(0.3f); SendHealth(); + + m_Stats.AddValue(statDamageTaken, round(a_TDI.FinalDamage * 10)); return true; } return false; @@ -865,6 +869,8 @@ void cPlayer::KilledBy(cEntity * a_Killer) Pickups.Add(cItem(E_ITEM_RED_APPLE)); } + m_Stats.AddValue(statItemsDropped, Pickups.Size()); + m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! @@ -1262,6 +1268,9 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos ) // TODO: should do some checks to see if player is not moving through terrain // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + Vector3d DeltaPos = a_NewPos - GetPosition(); + UpdateMovementStats(DeltaPos); SetPosition( a_NewPos ); SetStance(a_NewPos.y + 1.62); @@ -1492,10 +1501,7 @@ void cPlayer::TossEquippedItem(char a_Amount) Drops.push_back(DroppedItem); } - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player + TossItems(Drops); } @@ -1511,6 +1517,7 @@ void cPlayer::TossHeldItem(char a_Amount) char OriginalItemAmount = Item.m_ItemCount; Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); Drops.push_back(Item); + if (OriginalItemAmount > a_Amount) { Item.m_ItemCount = OriginalItemAmount - a_Amount; @@ -1521,10 +1528,7 @@ void cPlayer::TossHeldItem(char a_Amount) } } - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player + TossItems(Drops); } @@ -1536,10 +1540,21 @@ void cPlayer::TossPickup(const cItem & a_Item) cItems Drops; Drops.push_back(a_Item); + TossItems(Drops); +} + + + + + +void cPlayer::TossItems(const cItems & a_Items) +{ + m_Stats.AddValue(statItemsDropped, a_Items.Size()); + double vX = 0, vY = 0, vZ = 0; EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY); vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player + m_World->SpawnItemPickups(a_Items, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player } @@ -1935,6 +1950,59 @@ void cPlayer::HandleFloater() +void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) +{ + StatValue Value = round(a_DeltaPos.Length() * 100); + + if (m_AttachedTo == NULL) + { + int PosX = POSX_TOINT; + int PosY = POSY_TOINT; + int PosZ = POSZ_TOINT; + + BLOCKTYPE Block; + NIBBLETYPE Meta; + if (!m_World->GetBlockTypeMeta(PosX, PosY, PosZ, Block, Meta)) + { + return; + } + + if ((Block == E_BLOCK_LADDER) && (a_DeltaPos.y > 0.0)) // Going up + { + m_Stats.AddValue(statDistClimbed, round(a_DeltaPos.y * 100)); + } + else + { + // TODO 2014-05-12 xdot: Other types + m_Stats.AddValue(statDistWalked, Value); + } + } + else + { + switch (m_AttachedTo->GetEntityType()) + { + case cEntity::etMinecart: m_Stats.AddValue(statDistMinecart, Value); break; + case cEntity::etBoat: m_Stats.AddValue(statDistBoat, Value); break; + case cEntity::etMonster: + { + cMonster * Monster = (cMonster *)m_AttachedTo; + switch (Monster->GetMobType()) + { + case cMonster::mtPig: m_Stats.AddValue(statDistPig, Value); break; + case cMonster::mtHorse: m_Stats.AddValue(statDistHorse, Value); break; + default: break; + } + break; + } + default: break; + } + } +} + + + + + void cPlayer::ApplyFoodExhaustionFromMovement() { if (IsGameModeCreative()) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index b5c9d75cc..3de5e9c68 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -391,6 +391,9 @@ public: /** If true the player can fly even when he's not in creative. */ void SetCanFly(bool a_CanFly); + /** Update movement-related statistics. */ + void UpdateMovementStats(const Vector3d & a_DeltaPos); + /** Returns wheter the player can fly or not. */ virtual bool CanFly(void) const { return m_CanFly; } // tolua_end @@ -524,6 +527,9 @@ protected: /** Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item. */ void HandleFloater(void); + /** Tosses a list of items. */ + void TossItems(const cItems & a_Items); + /** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */ void ApplyFoodExhaustionFromMovement(); diff --git a/src/Item.h b/src/Item.h index 2f65d5344..acbc880dc 100644 --- a/src/Item.h +++ b/src/Item.h @@ -228,7 +228,7 @@ public: void Add (const cItem & a_Item) {push_back(a_Item); } void Delete(int a_Idx); void Clear (void) {clear(); } - size_t Size (void) {return size(); } + size_t Size (void) const { return size(); } void Set (int a_Idx, short a_ItemType, char a_ItemCount, short a_ItemDamage); void Add (short a_ItemType, char a_ItemCount, short a_ItemDamage) diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 62670907f..f3b408e68 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -464,8 +464,10 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } - if((m_SoundHurt != "") && (m_Health > 0)) + if ((m_SoundHurt != "") && (m_Health > 0)) + { m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + } if (a_TDI.Attacker != NULL) { diff --git a/src/Statistics.cpp b/src/Statistics.cpp index 65addb0dd..5950eb96c 100644 --- a/src/Statistics.cpp +++ b/src/Statistics.cpp @@ -64,7 +64,7 @@ cStatInfo cStatInfo::ms_Info[statCount] = { cStatInfo(statDistHorse, "stat.horseOneCm"), cStatInfo(statJumps, "stat.jump"), cStatInfo(statItemsDropped, "stat.drop"), - cStatInfo(statDamageDealt, "stat.damageDealth"), + cStatInfo(statDamageDealt, "stat.damageDealt"), cStatInfo(statDamageTaken, "stat.damageTaken"), cStatInfo(statDeaths, "stat.deaths"), cStatInfo(statMobKills, "stat.mobKills"), From 0685fb5258c6e4bdca310a3d22f8f997a2a556bb Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 13 May 2014 08:48:33 +0200 Subject: [PATCH 31/41] Added doxy-comment requirement --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c36be8b7..03481ec48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ Code Stuff - The only exception: a `switch` statement with all `case` statements being a single short statement is allowed to use the short brace-less form. - These two rules really mean that indent is governed by braces * Add an empty last line in all source files (GCC and GIT can complain otherwise) - * Use doxy-comments for functions in the header file, format as `/** Description */` + * All new public functions in all classes need documenting comments on what they do and what behavior they follow, use doxy-comments formatted as `/** Description */`. Do not use asterisks on additional lines in multi-line comments. * Use spaces after the comment markers: `// Comment` instead of `//Comment` From 466ff2204f18fda5f4f0f0b3e19f671d57747c24 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 13 May 2014 14:53:15 +0300 Subject: [PATCH 32/41] Fixes --- src/Entities/Player.cpp | 21 +++++++++++---------- src/Entities/Player.h | 9 +++------ src/Mobs/Monster.cpp | 2 +- src/WorldStorage/StatSerializer.cpp | 7 +++++-- src/WorldStorage/StatSerializer.h | 4 +++- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 3df7c4c34..632c41936 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -820,7 +820,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) { - cPlayer* Attacker = (cPlayer*) a_TDI.Attacker; + cPlayer * Attacker = (cPlayer *)a_TDI.Attacker; if ((m_Team != NULL) && (m_Team == Attacker->m_Team)) { @@ -880,7 +880,7 @@ void cPlayer::KilledBy(cEntity * a_Killer) } else if (a_Killer->IsPlayer()) { - cPlayer* Killer = (cPlayer*)a_Killer; + cPlayer * Killer = (cPlayer *)a_Killer; GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); } @@ -1150,6 +1150,7 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) { eStatistic Prerequisite = cStatInfo::GetPrerequisite(a_Ach); + // Check if the prerequisites are met if (Prerequisite != statInvalid) { if (m_Stats.GetValue(Prerequisite) == 0) @@ -1166,14 +1167,16 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) } else { + // First time, announce it cCompositeChat Msg; Msg.AddTextPart(m_PlayerName + " has just earned the achievement "); - Msg.AddTextPart(cStatInfo::GetName(a_Ach)); // TODO 2014-05-12 xdot: Use the proper cCompositeChat submessage type and send the actual title + Msg.AddTextPart(cStatInfo::GetName(a_Ach)); // TODO 2014-05-12 xdot: Use the proper cCompositeChat part (cAchievement) m_World->BroadcastChat(Msg); + // Increment the statistic StatValue New = m_Stats.AddValue(a_Ach); - /* Achievement Get! */ + // Achievement Get! m_ClientHandle->SendStatistics(m_Stats); return New; @@ -1707,9 +1710,8 @@ bool cPlayer::LoadFromDisk() m_LoadedWorldName = root.get("world", "world").asString(); - /* Load the player stats. - * We use the default world name (like bukkit) because stats are shared between dimensions/worlds. - */ + // Load the player stats. + // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); @@ -1784,9 +1786,8 @@ bool cPlayer::SaveToDisk() return false; } - /* Save the player stats. - * We use the default world name (like bukkit) because stats are shared between dimensions/worlds. - */ + // Save the player stats. + // We use the default world name (like bukkit) because stats are shared between dimensions/worlds. cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), m_PlayerName, &m_Stats); if (!StatSerializer.Save()) { diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 3de5e9c68..78b534d83 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -181,12 +181,9 @@ public: cStatManager & GetStatManager() { return m_Stats; } /** Awards the player an achievement. - * - * If all prerequisites are met, this method will award the achievement and will broadcast a chat message. - * If the achievement has been already awarded to the player, this method will just increment the stat counter. - * - * Returns the _new_ stat value. (0 = Could not award achievement) - */ + If all prerequisites are met, this method will award the achievement and will broadcast a chat message. + If the achievement has been already awarded to the player, this method will just increment the stat counter. + Returns the _new_ stat value. (0 = Could not award achievement) */ unsigned int AwardAchievement(const eStatistic a_Ach); void SetIP(const AString & a_IP); diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index f3b408e68..b2e42445b 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -464,7 +464,7 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } - if ((m_SoundHurt != "") && (m_Health > 0)) + if (!m_SoundHurt.empty() && (m_Health > 0)) { m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); } diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp index 66e5e1e9e..74113941c 100644 --- a/src/WorldStorage/StatSerializer.cpp +++ b/src/WorldStorage/StatSerializer.cpp @@ -11,15 +11,18 @@ -cStatSerializer::cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager) +cStatSerializer::cStatSerializer(const AString & a_WorldName, const AString & a_PlayerName, cStatManager * a_Manager) : m_Manager(a_Manager) { + // Even though stats are shared between worlds, they are (usually) saved + // inside the folder of the default world. + AString StatsPath; Printf(StatsPath, "%s/stats", a_WorldName.c_str()); m_Path = StatsPath + "/" + a_PlayerName + ".json"; - /* Ensure that the directory exists. */ + // Ensure that the directory exists. cFile::CreateFolder(FILE_IO_PREFIX + StatsPath); } diff --git a/src/WorldStorage/StatSerializer.h b/src/WorldStorage/StatSerializer.h index 43514465b..72f8d74f1 100644 --- a/src/WorldStorage/StatSerializer.h +++ b/src/WorldStorage/StatSerializer.h @@ -25,10 +25,12 @@ class cStatSerializer { public: - cStatSerializer(const AString& a_WorldName, const AString& a_PlayerName, cStatManager* a_Manager); + cStatSerializer(const AString & a_WorldName, const AString & a_PlayerName, cStatManager * a_Manager); + /* Try to load the player statistics. Returns whether the operation was successful or not. */ bool Load(void); + /* Try to save the player statistics. Returns whether the operation was successful or not. */ bool Save(void); From 6c5ff597bbbb3abd2985eda6bf35ea2d98cf69a4 Mon Sep 17 00:00:00 2001 From: Howaner Date: Thu, 15 May 2014 19:58:48 +0200 Subject: [PATCH 33/41] Move radius check. --- src/ClientHandle.cpp | 65 +++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 9238418a4..cf9d0bfeb 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -815,6 +815,16 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB return; } + if ( + (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || + (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || + (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) + ) + { + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status)) { @@ -927,6 +937,16 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc return; } + if ( + (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || + (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || + (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) + ) + { + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, this); + return; + } + // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig/aim bug in the client: m_HasStartedDigging = true; m_LastDigBlockX = a_BlockX; @@ -1000,6 +1020,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo FinishDigAnimation(); + cWorld * World = m_Player->GetWorld(); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta)) @@ -1008,24 +1029,12 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); return; } - + if (a_OldBlock == E_BLOCK_AIR) { LOGD("Dug air - what the function?"); return; } - - cWorld * World = m_Player->GetWorld(); - - if ( - (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || - (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || - (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) - ) - { - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } ItemHandler->OnBlockDestroyed(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ); // The ItemHandler is also responsible for spawning the pickups @@ -1078,7 +1087,23 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e ); cWorld * World = m_Player->GetWorld(); - + + if ( + (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || + (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || + (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) + ) + { + if (a_BlockFace != BLOCK_FACE_NONE) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things + m_Player->GetInventory().SendEquippedSlot(); + } + return; + } + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) { @@ -1092,7 +1117,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); //2 block high things + World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); // 2 block high things m_Player->GetInventory().SendEquippedSlot(); } return; @@ -1202,16 +1227,6 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e // The block is being placed outside the world, ignore this packet altogether (#128) return; } - - if ( - (Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) || - (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) || - (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) - ) - { - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); From 28a9c16db2212d57cca823a2b122530fdb70f210 Mon Sep 17 00:00:00 2001 From: Howaner Date: Thu, 15 May 2014 19:59:52 +0200 Subject: [PATCH 34/41] Fix compile error. --- src/ClientHandle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index cf9d0bfeb..0c897374b 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -943,7 +943,7 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6) ) { - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, this); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); return; } From 5724dcc2c199a5d7aec1bf60a160106770a2a63a Mon Sep 17 00:00:00 2001 From: tonibm19 Date: Fri, 16 May 2014 19:33:57 +0200 Subject: [PATCH 35/41] Fixed anvil exp removing --- src/UI/SlotArea.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 788974f9c..8833a767a 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -760,7 +760,7 @@ void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player) { if (!a_Player.IsGameModeCreative()) { - a_Player.DeltaExperience(cPlayer::XpForLevel(m_MaximumCost)); + a_Player.DeltaExperience(-cPlayer::XpForLevel(m_MaximumCost)); } SetSlot(0, a_Player, cItem()); From 6951b68aaf208c7efdcc954fbc54420966fafa4a Mon Sep 17 00:00:00 2001 From: Julian Laubstein Date: Sat, 17 May 2014 18:38:33 +0200 Subject: [PATCH 36/41] Added load command in the cServer class --- src/Server.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Server.cpp b/src/Server.cpp index bfb1b1cbb..91bc6853e 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -476,6 +476,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac a_Output.Finished(); return; } + if (split[0] == "load" && !split[1].empty()) + { + cPluginManager::Get()->LoadPlugin(split[1]); + a_Output.Out("Plugin " + split[1] + " added and activated!"); + a_Output.Finished(); + } // There is currently no way a plugin can do these (and probably won't ever be): if (split[0].compare("chunkstats") == 0) From 19f4cd05470785a1afdc764b79db3777f62fe65d Mon Sep 17 00:00:00 2001 From: Julian Laubstein Date: Sat, 17 May 2014 19:39:16 +0200 Subject: [PATCH 37/41] Added load cmd --- src/Server.cpp | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Server.cpp b/src/Server.cpp index 91bc6853e..8e222b743 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -476,13 +476,34 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac a_Output.Finished(); return; } - if (split[0] == "load" && !split[1].empty()) + if (split[0] == "load") { - cPluginManager::Get()->LoadPlugin(split[1]); - a_Output.Out("Plugin " + split[1] + " added and activated!"); - a_Output.Finished(); + if (split.size() > 1) + { + cPluginManager::Get()->LoadPlugin(split[1]); + return; + } + else + { + a_Output.Out("No plugin given! Command: load "); + a_Output.Finished(); + return; + } } - + /* + * TODO: Declare unload command + if (split[0] == "unload") + { + if (split.size() > 1) + { + + } + else + { + + } + } + */ // There is currently no way a plugin can do these (and probably won't ever be): if (split[0].compare("chunkstats") == 0) { @@ -573,6 +594,9 @@ void cServer::BindBuiltInConsoleCommands(void) PlgMgr->BindConsoleCommand("restart", NULL, " - Restarts the server cleanly"); PlgMgr->BindConsoleCommand("stop", NULL, " - Stops the server cleanly"); PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics"); + PlgMgr->BindConsoleCommand("load ", NULL, " - Adds and enables the specified plugin"); + PlgMgr->BindConsoleCommand("unload ", NULL, " - Disables the specified plugin"); + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); #endif From cfd3c33dfa38c946151a1c4d3ba780f7733963c0 Mon Sep 17 00:00:00 2001 From: Julian Laubstein Date: Sun, 18 May 2014 17:16:02 +0200 Subject: [PATCH 38/41] Added unload command --- src/Server.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Server.cpp b/src/Server.cpp index 8e222b743..e71e728a6 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -29,6 +29,7 @@ #include #include #include +#include extern "C" { #include "zlib/zlib.h" @@ -481,6 +482,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac if (split.size() > 1) { cPluginManager::Get()->LoadPlugin(split[1]); + return; } else @@ -490,20 +492,22 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac return; } } - /* - * TODO: Declare unload command + if (split[0] == "unload") { if (split.size() > 1) { - + cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1])); + return; } else { - + a_Output.Out("No plugin given! Command: unload "); + a_Output.Finished(); + return; } } - */ + // There is currently no way a plugin can do these (and probably won't ever be): if (split[0].compare("chunkstats") == 0) { From 40ce737a7e372b5f3aef51cad7362a199351cb27 Mon Sep 17 00:00:00 2001 From: Julian Laubstein Date: Sun, 18 May 2014 17:30:21 +0200 Subject: [PATCH 39/41] removed the include --- src/Server.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Server.cpp b/src/Server.cpp index e71e728a6..aa731cdd2 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -29,7 +29,6 @@ #include #include #include -#include extern "C" { #include "zlib/zlib.h" From e09a04a23aaa14598bff6692393f4187f59d3824 Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 18 May 2014 22:36:17 +0200 Subject: [PATCH 40/41] Fixed datatype truncation in Diff() template. --- src/Defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Defines.h b/src/Defines.h index b0b209934..563fc308c 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -529,7 +529,7 @@ inline float GetSpecialSignf( float a_Val ) -template inline int Diff(T a_Val1, T a_Val2) +template inline T Diff(T a_Val1, T a_Val2) { return std::abs(a_Val1 - a_Val2); } From a651c865e40ad80b52ddf69004b40a580e7069ea Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Sun, 18 May 2014 22:49:27 +0200 Subject: [PATCH 41/41] There's no "round" function in MSVC2008. --- src/Entities/Entity.cpp | 2 +- src/Entities/Player.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index c393f89fd..31ad66779 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -321,7 +321,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastEntityAnimation(*this, 4); // Critical hit } - Player->GetStatManager().AddValue(statDamageDealt, round(a_TDI.FinalDamage * 10)); + Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5)); } m_Health -= (short)a_TDI.FinalDamage; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 632c41936..c3b763278 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -838,7 +838,7 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) AddFoodExhaustion(0.3f); SendHealth(); - m_Stats.AddValue(statDamageTaken, round(a_TDI.FinalDamage * 10)); + m_Stats.AddValue(statDamageTaken, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5)); return true; } return false; @@ -1953,7 +1953,7 @@ void cPlayer::HandleFloater() void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) { - StatValue Value = round(a_DeltaPos.Length() * 100); + StatValue Value = (StatValue)floor(a_DeltaPos.Length() * 100 + 0.5); if (m_AttachedTo == NULL) { @@ -1970,7 +1970,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) if ((Block == E_BLOCK_LADDER) && (a_DeltaPos.y > 0.0)) // Going up { - m_Stats.AddValue(statDistClimbed, round(a_DeltaPos.y * 100)); + m_Stats.AddValue(statDistClimbed, (StatValue)floor(a_DeltaPos.y * 100 + 0.5)); } else {