1
0

Merged branch 'master' into VillageGen.

This commit is contained in:
madmaxoft 2014-05-18 23:10:23 +02:00
commit e69a11012f
60 changed files with 1589 additions and 328 deletions

View File

@ -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
View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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)
{

View File

@ -76,6 +76,7 @@ $cfile "../CompositeChat.h"
$cfile "../Map.h"
$cfile "../MapManager.h"
$cfile "../Scoreboard.h"
$cfile "../Statistics.h"

View File

@ -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;
}

View File

@ -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);
} ;

View File

@ -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;
}
@ -926,9 +937,12 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
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;
}
@ -1004,24 +1018,24 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
return;
}
m_HasStartedDigging = false;
if (m_BlockDigAnimStage != -1)
{
// End dig animation
m_BlockDigAnimStage = -1;
// It seems that 10 ends block animation
m_Player->GetWorld()->BroadcastBlockBreakAnimation(m_UniqueID, m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ, 10, this);
}
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))
{
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
if (a_OldBlock == E_BLOCK_AIR)
{
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",
@ -1044,6 +1088,22 @@ 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;
}

View File

@ -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

View File

@ -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

View File

@ -10,3 +10,5 @@ file(GLOB SOURCE
)
add_library(Entities ${SOURCE})
target_link_libraries(Entities WorldStorage)

View File

@ -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

View File

@ -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);

View File

@ -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 ;)

View File

@ -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"
@ -192,6 +195,8 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
}
}
m_Stats.AddValue(statMinutesPlayed, 1);
if (!a_Chunk.IsValid())
{
// This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
@ -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);
@ -1193,6 +1272,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
}
@ -1622,6 +1710,11 @@ bool cPlayer::LoadFromDisk()
m_LoadedWorldName = root.get("world", "world").asString();
// Load the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
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())

View File

@ -7,6 +7,8 @@
#include "../World.h"
#include "../ClientHandle.h"
#include "../Statistics.h"
@ -175,6 +177,15 @@ public:
// 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);
// Sets the current gamemode, doesn't check validity, doesn't send update packets to client
@ -307,6 +318,8 @@ public:
virtual void KilledBy(cEntity * a_Killer) override;
virtual void Killed(cEntity * a_Victim) override;
void Respawn(void); // tolua_export
void SetVisible( bool a_bVisible ); // 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();

View File

@ -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:

View File

@ -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!");
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View 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;
}
}
}

View 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
} ;

View File

@ -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))

View File

@ -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);
/// Returns the cItemGrid object representing the armor slots
/** Sends the equipped item slot to the client */
void SendEquippedSlot();
/** 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:

View File

@ -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)

View File

@ -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)
{

View File

@ -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) */

View File

@ -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

View File

@ -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

View 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);
}

View 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;

View File

@ -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. */

View File

@ -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;
}

View File

@ -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;
} ;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -476,6 +476,36 @@ 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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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());
@ -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:

View File

@ -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);
} ;

View File

@ -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;
}
}
}

View 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;
}

View 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;
} ;

View File

@ -243,7 +243,6 @@ void cWorldStorage::Execute(void)
bool Success;
do
{
Success = false;
if (m_ShouldTerminate)
{
return;