Merged branch 'master' into VillageGen.
This commit is contained in:
commit
e69a11012f
@ -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`
|
||||
|
||||
|
||||
|
4
MCServer/.gitignore
vendored
4
MCServer/.gitignore
vendored
@ -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
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
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.
|
||||
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
|
10
MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh
Executable file
10
MCServer/webadmin/GenerateSelfSignedHTTPSCertUsingOpenssl.sh
Executable file
@ -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.crt -days 3650 -nodes
|
@ -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
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "Connection.h"
|
||||
#include "Server.h"
|
||||
#include <iostream>
|
||||
#include "PolarSSL++/PublicKey.h"
|
||||
#include "PolarSSL++/CryptoKey.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h> // 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)
|
||||
{
|
||||
|
@ -76,6 +76,7 @@ $cfile "../CompositeChat.h"
|
||||
$cfile "../Map.h"
|
||||
$cfile "../MapManager.h"
|
||||
$cfile "../Scoreboard.h"
|
||||
$cfile "../Statistics.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 <typename T> 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, size_t 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);
|
||||
} ;
|
||||
|
||||
|
@ -336,7 +336,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
|
||||
|
||||
// Send scoreboard data
|
||||
World->GetScoreBoard().SendTo(*this);
|
||||
|
||||
|
||||
// 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;
|
||||
@ -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))
|
||||
{
|
||||
@ -878,6 +888,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
|
||||
case DIG_STATUS_CANCELLED:
|
||||
{
|
||||
// Block breaking cancelled by player
|
||||
FinishDigAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -916,7 +927,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 +936,17 @@ 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))
|
||||
|
||||
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)
|
||||
)
|
||||
{
|
||||
// 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,24 +1018,24 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
|
||||
return;
|
||||
}
|
||||
|
||||
m_HasStartedDigging = false;
|
||||
if (m_BlockDigAnimStage != -1)
|
||||
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))
|
||||
{
|
||||
// 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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
|
||||
|
||||
if (a_OldBlock == E_BLOCK_AIR)
|
||||
{
|
||||
LOGD("Dug air - what the function?");
|
||||
return;
|
||||
}
|
||||
|
||||
cWorld * World = m_Player->GetWorld();
|
||||
|
||||
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());
|
||||
@ -1036,6 +1050,36 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
|
||||
|
||||
|
||||
|
||||
void cClientHandle::FinishDigAnimation()
|
||||
{
|
||||
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",
|
||||
@ -1043,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))
|
||||
{
|
||||
@ -1057,7 +1117,8 @@ 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;
|
||||
}
|
||||
@ -1246,6 +1307,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 +1317,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;
|
||||
}
|
||||
|
||||
@ -2437,6 +2500,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);
|
||||
@ -2676,12 +2748,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
@ -374,6 +376,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);
|
||||
|
||||
/** 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);
|
||||
|
||||
@ -390,7 +395,7 @@ private:
|
||||
void HandleAnvilItemName(const char * a_Data, size_t 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
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "ChatColor.h"
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
|
||||
@ -528,6 +529,15 @@ inline float GetSpecialSignf( float a_Val )
|
||||
|
||||
|
||||
|
||||
template<class T> inline T Diff(T a_Val1, T a_Val2)
|
||||
{
|
||||
return std::abs(a_Val1 - a_Val2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
enum eMessageType
|
||||
|
@ -10,3 +10,5 @@ file(GLOB SOURCE
|
||||
)
|
||||
|
||||
add_library(Entities ${SOURCE})
|
||||
|
||||
target_link_libraries(Entities WorldStorage)
|
||||
|
@ -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, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
|
||||
}
|
||||
|
||||
m_Health -= (short)a_TDI.FinalDamage;
|
||||
@ -370,6 +374,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;
|
||||
}
|
||||
@ -575,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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 ;)
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "../Items/ItemHandler.h"
|
||||
#include "../Vector3.h"
|
||||
|
||||
#include "../WorldStorage/StatSerializer.h"
|
||||
#include "../CompositeChat.h"
|
||||
|
||||
#include "inifile/iniFile.h"
|
||||
#include "json/json.h"
|
||||
|
||||
@ -191,6 +194,8 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_Stats.AddValue(statMinutesPlayed, 1);
|
||||
|
||||
if (!a_Chunk.IsValid())
|
||||
{
|
||||
@ -815,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))
|
||||
{
|
||||
@ -832,6 +837,8 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
// Any kind of damage adds food exhaustion
|
||||
AddFoodExhaustion(0.3f);
|
||||
SendHealth();
|
||||
|
||||
m_Stats.AddValue(statDamageTaken, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -862,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 !
|
||||
|
||||
@ -871,9 +880,9 @@ 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()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -883,6 +892,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);
|
||||
}
|
||||
|
||||
@ -890,6 +901,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();
|
||||
@ -1108,6 +1146,47 @@ void cPlayer::SetIP(const AString & a_IP)
|
||||
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
StatValue Old = m_Stats.GetValue(a_Ach);
|
||||
|
||||
if (Old > 0)
|
||||
{
|
||||
return m_Stats.AddValue(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 part (cAchievement)
|
||||
m_World->BroadcastChat(Msg);
|
||||
|
||||
// Increment the statistic
|
||||
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);
|
||||
@ -1192,6 +1271,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);
|
||||
@ -1422,10 +1504,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);
|
||||
}
|
||||
|
||||
|
||||
@ -1441,6 +1520,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;
|
||||
@ -1451,10 +1531,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);
|
||||
}
|
||||
|
||||
|
||||
@ -1466,10 +1543,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
|
||||
}
|
||||
|
||||
|
||||
@ -1621,6 +1709,11 @@ 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()
|
||||
@ -1692,6 +1785,16 @@ 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;
|
||||
}
|
||||
|
||||
@ -1706,7 +1809,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;
|
||||
@ -1845,6 +1951,59 @@ void cPlayer::HandleFloater()
|
||||
|
||||
|
||||
|
||||
void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
|
||||
{
|
||||
StatValue Value = (StatValue)floor(a_DeltaPos.Length() * 100 + 0.5);
|
||||
|
||||
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, (StatValue)floor(a_DeltaPos.y * 100 + 0.5));
|
||||
}
|
||||
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())
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "../World.h"
|
||||
#include "../ClientHandle.h"
|
||||
|
||||
#include "../Statistics.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@ -174,6 +176,15 @@ public:
|
||||
cTeam * UpdateTeam(void);
|
||||
|
||||
// tolua_end
|
||||
|
||||
/** 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);
|
||||
|
||||
@ -306,6 +317,8 @@ public:
|
||||
void AbortEating(void);
|
||||
|
||||
virtual void KilledBy(cEntity * a_Killer) override;
|
||||
|
||||
virtual void Killed(cEntity * a_Victim) override;
|
||||
|
||||
void Respawn(void); // tolua_export
|
||||
|
||||
@ -375,6 +388,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
|
||||
@ -487,6 +503,8 @@ protected:
|
||||
|
||||
cTeam * m_Team;
|
||||
|
||||
cStatManager m_Stats;
|
||||
|
||||
|
||||
|
||||
void ResolvePermissions(void);
|
||||
@ -506,6 +524,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();
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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!");
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
|
||||
|
||||
cHTTPConnection::~cHTTPConnection()
|
||||
{
|
||||
// LOGD("HTTP: Connection deleting: %p", this);
|
||||
delete m_CurrentRequest;
|
||||
}
|
||||
|
||||
@ -144,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)
|
||||
{
|
||||
@ -162,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:
|
||||
@ -183,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)
|
||||
{
|
||||
DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
|
||||
return cHTTPConnection::DataReceived(a_Data + BytesConsumed, a_Size - BytesConsumed);
|
||||
}
|
||||
else
|
||||
{
|
||||
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:
|
||||
@ -209,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;
|
||||
@ -223,6 +223,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<cHTTPConnection *> cHTTPConnections;
|
||||
|
@ -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;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "HTTPMessage.h"
|
||||
#include "HTTPConnection.h"
|
||||
#include "HTTPFormParser.h"
|
||||
#include "SslHTTPConnection.h"
|
||||
|
||||
|
||||
|
||||
@ -142,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;
|
||||
@ -195,7 +231,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);
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include "../OSSupport/ListenThread.h"
|
||||
#include "../OSSupport/SocketThreads.h"
|
||||
#include "inifile/iniFile.h"
|
||||
#include "PolarSSL++/RsaPrivateKey.h"
|
||||
#include "PolarSSL++/CryptoKey.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. */
|
||||
cCryptoKeyPtr m_CertPrivKey;
|
||||
|
||||
|
||||
// cListenThread::cCallback overrides:
|
||||
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
|
||||
|
107
src/HTTPServer/SslHTTPConnection.cpp
Normal file
107
src/HTTPServer/SslHTTPConnection.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
// 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 cCryptoKeyPtr & 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
45
src/HTTPServer/SslHTTPConnection.h
Normal file
45
src/HTTPServer/SslHTTPConnection.h
Normal file
@ -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. */
|
||||
cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey);
|
||||
|
||||
protected:
|
||||
cBufferedSslContext m_Ssl;
|
||||
|
||||
/** The certificate to send to the client */
|
||||
cX509CertPtr m_Cert;
|
||||
|
||||
/** The private key used for the certificate */
|
||||
cCryptoKeyPtr m_PrivateKey;
|
||||
|
||||
// cHTTPConnection overrides:
|
||||
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
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -355,6 +355,8 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
InStateEscaping(a_Dt);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTACKING: break;
|
||||
} // switch (m_EMState)
|
||||
|
||||
BroadcastMovementUpdate();
|
||||
@ -464,8 +466,10 @@ 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);
|
||||
}
|
||||
|
||||
if (a_TDI.Attacker != NULL)
|
||||
{
|
||||
|
@ -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) */
|
||||
|
@ -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 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 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
|
||||
|
@ -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
|
||||
|
149
src/PolarSSL++/CryptoKey.cpp
Normal file
149
src/PolarSSL++/CryptoKey.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
|
||||
// CryptoKey.cpp
|
||||
|
||||
// Implements the cCryptoKey class representing a RSA public key in PolarSSL
|
||||
|
||||
#include "Globals.h"
|
||||
#include "CryptoKey.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCryptoKey::cCryptoKey(void)
|
||||
{
|
||||
pk_init(&m_Pk);
|
||||
m_CtrDrbg.Initialize("rsa_pubkey", 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCryptoKey::cCryptoKey(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCryptoKey::cCryptoKey(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCryptoKey::~cCryptoKey()
|
||||
{
|
||||
pk_free(&m_Pk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cCryptoKey::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,
|
||||
a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
|
||||
ctr_drbg_random, m_CtrDrbg.GetInternal()
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)DecryptedLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cCryptoKey::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,
|
||||
ctr_drbg_random, m_CtrDrbg.GetInternal()
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)EncryptedLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cCryptoKey::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 cCryptoKey::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 cCryptoKey::IsValid(void) const
|
||||
{
|
||||
return (pk_get_type(&m_Pk) != POLARSSL_PK_NONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
76
src/PolarSSL++/CryptoKey.h
Normal file
76
src/PolarSSL++/CryptoKey.h
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
// CryptoKey.h
|
||||
|
||||
// Declares the cCryptoKey class representing a RSA public key in PolarSSL
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CtrDrbgContext.h"
|
||||
#include "polarssl/pk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCryptoKey
|
||||
{
|
||||
friend class cSslContext;
|
||||
|
||||
public:
|
||||
/** Constructs an empty key instance. Before use, it needs to be filled by ParsePublic() or ParsePrivate() */
|
||||
cCryptoKey(void);
|
||||
|
||||
/** Constructs the public key out of the DER- or PEM-encoded pubkey data */
|
||||
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. */
|
||||
cCryptoKey(const AString & a_PrivateKeyData, const AString & a_Password);
|
||||
|
||||
~cCryptoKey();
|
||||
|
||||
/** Decrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
|
||||
|
||||
/** Encrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> 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 PolarSSL representation of the key data */
|
||||
pk_context m_Pk;
|
||||
|
||||
/** 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<cCryptoKey> cCryptoKeyPtr;
|
||||
|
||||
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -1,73 +0,0 @@
|
||||
|
||||
// PublicKey.cpp
|
||||
|
||||
// Implements the cPublicKey class representing a RSA public key in PolarSSL
|
||||
|
||||
#include "Globals.h"
|
||||
#include "PublicKey.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPublicKey::cPublicKey(const AString & a_PublicKeyDER)
|
||||
{
|
||||
pk_init(&m_Pk);
|
||||
if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0)
|
||||
{
|
||||
ASSERT(!"Cannot parse PubKey");
|
||||
return;
|
||||
}
|
||||
m_CtrDrbg.Initialize("rsa_pubkey", 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPublicKey::~cPublicKey()
|
||||
{
|
||||
pk_free(&m_Pk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
|
||||
{
|
||||
size_t DecryptedLen = a_DecryptedMaxLength;
|
||||
int res = pk_decrypt(&m_Pk,
|
||||
a_EncryptedData, a_EncryptedLength,
|
||||
a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
|
||||
ctr_drbg_random, m_CtrDrbg.GetInternal()
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)DecryptedLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
|
||||
{
|
||||
size_t EncryptedLength = a_EncryptedMaxLength;
|
||||
int res = pk_encrypt(&m_Pk,
|
||||
a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
|
||||
ctr_drbg_random, m_CtrDrbg.GetInternal()
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)EncryptedLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
|
||||
// PublicKey.h
|
||||
|
||||
// Declares the cPublicKey class representing a RSA public key in PolarSSL
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CtrDrbgContext.h"
|
||||
#include "polarssl/pk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPublicKey
|
||||
{
|
||||
public:
|
||||
/** Constructs the public key out of the DER-encoded pubkey data */
|
||||
cPublicKey(const AString & a_PublicKeyDER);
|
||||
|
||||
~cPublicKey();
|
||||
|
||||
/** Decrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
|
||||
|
||||
/** Encrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> 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);
|
||||
|
||||
protected:
|
||||
/** The public key PolarSSL representation */
|
||||
pk_context m_Pk;
|
||||
|
||||
/** The random generator used in encryption and decryption */
|
||||
cCtrDrbgContext m_CtrDrbg;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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<cRsaPrivateKey> cRsaPrivateKeyPtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> &
|
||||
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:
|
||||
@ -59,7 +59,7 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> &
|
||||
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);
|
||||
|
||||
@ -70,6 +70,18 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> &
|
||||
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;
|
||||
@ -80,8 +92,56 @@ int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> &
|
||||
|
||||
|
||||
|
||||
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 cCryptoKeyPtr & 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;
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include "polarssl/ssl.h"
|
||||
#include "../ByteBuffer.h"
|
||||
#include "CryptoKey.h"
|
||||
#include "RsaPrivateKey.h"
|
||||
#include "X509Cert.h"
|
||||
|
||||
|
||||
@ -38,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. */
|
||||
@ -47,7 +49,15 @@ 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(). */
|
||||
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.
|
||||
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 +103,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 cCryptoKey. */
|
||||
cCryptoKeyPtr m_OwnCertPrivKey2;
|
||||
|
||||
/** True if the SSL handshake has been completed. */
|
||||
bool m_HasHandshaken;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,15 @@ 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
|
||||
m_Client->GetPlayer()->AwardAchievement(achOpenInv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,10 +202,11 @@ 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)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -476,7 +476,37 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (split[0] == "load")
|
||||
{
|
||||
if (split.size() > 1)
|
||||
{
|
||||
cPluginManager::Get()->LoadPlugin(split[1]);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
a_Output.Out("No plugin given! Command: load <pluginname>");
|
||||
a_Output.Finished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 <pluginname>");
|
||||
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)
|
||||
{
|
||||
@ -567,6 +597,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 <pluginname>", NULL, " - Adds and enables the specified plugin");
|
||||
PlgMgr->BindConsoleCommand("unload <pluginname>", 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
|
||||
|
@ -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,13 +57,14 @@ 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"),
|
||||
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"),
|
||||
@ -113,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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
@ -760,7 +785,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());
|
||||
|
||||
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -650,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
146
src/WorldStorage/StatSerializer.cpp
Normal file
146
src/WorldStorage/StatSerializer.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
// StatSerializer.cpp
|
||||
|
||||
|
||||
#include "Globals.h"
|
||||
#include "StatSerializer.h"
|
||||
|
||||
#include "../Statistics.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO 2014-05-11 xdot: Save "progress"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
55
src/WorldStorage/StatSerializer.h
Normal file
55
src/WorldStorage/StatSerializer.h
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
// 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void SaveStatToJSON(Json::Value & a_Out);
|
||||
|
||||
bool LoadStatFromJSON(const Json::Value & a_In);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
cStatManager* m_Manager;
|
||||
|
||||
AString m_Path;
|
||||
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -243,7 +243,6 @@ void cWorldStorage::Execute(void)
|
||||
bool Success;
|
||||
do
|
||||
{
|
||||
Success = false;
|
||||
if (m_ShouldTerminate)
|
||||
{
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user