Merge branch 'master' into chunksparsing/structs
This commit is contained in:
commit
4ab8288116
@ -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
|
@ -131,8 +131,8 @@ endmacro()
|
||||
macro(enable_profile)
|
||||
# Declare the flags used for profiling builds:
|
||||
if (MSVC)
|
||||
set (CXX_PROFILING "")
|
||||
set (LNK_PROFILING "/PROFILE")
|
||||
set (CXX_PROFILING "/Zi")
|
||||
set (LNK_PROFILING "/PROFILE /DEBUG")
|
||||
else()
|
||||
set (CXX_PROFILING "-pg")
|
||||
set (LNK_PROFILING "-pg")
|
||||
|
@ -22,6 +22,15 @@
|
||||
#define ALIGN_8
|
||||
#define ALIGN_16
|
||||
|
||||
#define FORMATSTRING(formatIndex, va_argsIndex)
|
||||
|
||||
// MSVC has its own custom version of zu format
|
||||
#define SIZE_T_FMT "%Iu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "Iu"
|
||||
#define SIZE_T_FMT_HEX "%Ix"
|
||||
|
||||
#define NORETURN __declspec(noreturn)
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
|
||||
@ -39,6 +48,12 @@
|
||||
#define stricmp strcasecmp
|
||||
|
||||
#define FORMATSTRING(formatIndex,va_argsIndex)
|
||||
|
||||
#define SIZE_T_FMT "%zu"
|
||||
#define SIZE_T_FMT_PRECISION(x) "%" #x "zu"
|
||||
#define SIZE_T_FMT_HEX "%zx"
|
||||
|
||||
#define NORETURN __attribute((__noreturn__))
|
||||
#else
|
||||
|
||||
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
||||
|
@ -2095,7 +2095,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:
|
||||
@ -2109,7 +2109,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:
|
||||
@ -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 msImprint
|
||||
|
||||
case cBlockArea::msLake:
|
||||
@ -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 msLake
|
||||
|
||||
case cBlockArea::msSpongePrint:
|
||||
@ -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 msSpongePrint
|
||||
|
||||
case cBlockArea::msDifference:
|
||||
@ -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 msDifference
|
||||
|
||||
case cBlockArea::msMask:
|
||||
@ -2179,16 +2179,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,8 +46,30 @@ public:
|
||||
bool IsWither(void) const { return m_IsWither; }
|
||||
|
||||
void Reset(void) { m_IsWither = false; }
|
||||
|
||||
} CallbackA, CallbackB;
|
||||
|
||||
class cPlayerCallback : public cPlayerListCallback
|
||||
{
|
||||
Vector3f m_Pos;
|
||||
|
||||
virtual bool Item(cPlayer * a_Player)
|
||||
{
|
||||
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
|
||||
double Dist = (a_Player->GetPosition() - m_Pos).Length();
|
||||
if (Dist < 50.0)
|
||||
{
|
||||
// If player is close, award achievement
|
||||
a_Player->AwardAchievement(achSpawnWither);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
|
||||
} PlayerCallback(Vector3f(a_BlockX, a_BlockY, a_BlockZ));
|
||||
|
||||
a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
|
||||
|
||||
if (!CallbackA.IsWither())
|
||||
@ -86,6 +108,9 @@ public:
|
||||
// Spawn the wither:
|
||||
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_World->ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -113,6 +138,9 @@ public:
|
||||
// Spawn the wither:
|
||||
a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_World->ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
@ -815,6 +815,17 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED)) && // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
|
||||
((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 +889,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 +938,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 +1019,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 +1051,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 +1089,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 +1118,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 +1308,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 +1318,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 +2501,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 +2749,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
|
||||
@ -601,6 +617,10 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
m_TicksSinceLastVoidDamage = 0;
|
||||
}
|
||||
|
||||
if (IsMob() || IsPlayer() || IsPickup() || IsExpOrb())
|
||||
{
|
||||
DetectCacti();
|
||||
}
|
||||
if (IsMob() || IsPlayer())
|
||||
{
|
||||
// Set swimming state
|
||||
@ -998,6 +1018,26 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
void cEntity::DetectCacti(void)
|
||||
{
|
||||
int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
|
||||
double w = m_Width / 2;
|
||||
if (
|
||||
(((X + 1) - GetPosX() < w) && (GetWorld()->GetBlock(X + 1, Y, Z) == E_BLOCK_CACTUS)) ||
|
||||
(((GetPosX() - (X - 1)) - 1 < w) && (GetWorld()->GetBlock(X - 1, Y, Z) == E_BLOCK_CACTUS)) ||
|
||||
(((Z + 1) - GetPosZ() < w) && (GetWorld()->GetBlock(X, Y, Z + 1) == E_BLOCK_CACTUS)) ||
|
||||
(((GetPosZ() - (Z - 1)) - 1 < w) && (GetWorld()->GetBlock(X, Y, Z - 1) == E_BLOCK_CACTUS)) ||
|
||||
(((Y > 0) && (Y < cChunkDef::Height)) && ((GetPosY() - Y < 1) && (GetWorld()->GetBlock(X, Y, Z) == E_BLOCK_CACTUS)))
|
||||
)
|
||||
{
|
||||
TakeDamage(dtCactusContact, NULL, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEntity::SetSwimState(cChunk & a_Chunk)
|
||||
{
|
||||
int RelY = (int)floor(GetPosY() + 0.1);
|
||||
|
@ -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);
|
||||
|
||||
@ -318,6 +321,9 @@ public:
|
||||
/// Updates the state related to this entity being on fire
|
||||
virtual void TickBurning(cChunk & a_Chunk);
|
||||
|
||||
/** Detects the time for application of cacti damage */
|
||||
virtual void DetectCacti(void);
|
||||
|
||||
/// Handles when the entity is in the void
|
||||
virtual void TickInVoid(cChunk & a_Chunk);
|
||||
|
||||
|
@ -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"
|
||||
|
||||
@ -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)
|
||||
@ -451,8 +456,18 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
|
||||
else
|
||||
{
|
||||
float Dist = (float)(m_LastGroundHeight - floor(GetPosY()));
|
||||
|
||||
if (Dist >= 2.0) // At least two blocks - TODO: Use m_LastJumpHeight instead of m_LastGroundHeight above
|
||||
{
|
||||
// Increment statistic
|
||||
m_Stats.AddValue(statDistFallen, (StatValue)floor(Dist * 100 + 0.5));
|
||||
}
|
||||
|
||||
int Damage = (int)(Dist - 3.f);
|
||||
if (m_LastJumpHeight > m_LastGroundHeight) Damage++;
|
||||
if (m_LastJumpHeight > m_LastGroundHeight)
|
||||
{
|
||||
Damage++;
|
||||
}
|
||||
m_LastJumpHeight = (float)GetPosY();
|
||||
|
||||
if (Damage > 0)
|
||||
@ -815,7 +830,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 +847,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 +879,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 +890,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 +902,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 +911,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 +1156,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 +1282,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 +1514,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 +1530,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 +1541,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 +1553,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 +1720,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 +1795,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 +1819,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 +1961,92 @@ void cPlayer::HandleFloater()
|
||||
|
||||
|
||||
|
||||
bool cPlayer::IsClimbing(void) const
|
||||
{
|
||||
int PosX = POSX_TOINT;
|
||||
int PosY = POSY_TOINT;
|
||||
int PosZ = POSZ_TOINT;
|
||||
|
||||
if ((PosY < 0) || (PosY >= cChunkDef::Height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE Block = m_World->GetBlock(PosX, PosY, PosZ);
|
||||
switch (Block)
|
||||
{
|
||||
case E_BLOCK_LADDER:
|
||||
case E_BLOCK_VINES:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos)
|
||||
{
|
||||
StatValue Value = (StatValue)floor(a_DeltaPos.Length() * 100 + 0.5);
|
||||
|
||||
if (m_AttachedTo == NULL)
|
||||
{
|
||||
if (IsClimbing())
|
||||
{
|
||||
if (a_DeltaPos.y > 0.0) // Going up
|
||||
{
|
||||
m_Stats.AddValue(statDistClimbed, (StatValue)floor(a_DeltaPos.y * 100 + 0.5));
|
||||
}
|
||||
}
|
||||
else if (IsSubmerged())
|
||||
{
|
||||
m_Stats.AddValue(statDistDove, Value);
|
||||
}
|
||||
else if (IsSwimming())
|
||||
{
|
||||
m_Stats.AddValue(statDistSwum, Value);
|
||||
}
|
||||
else if (IsOnGround())
|
||||
{
|
||||
m_Stats.AddValue(statDistWalked, Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Value >= 25) // Ignore small/slow movement
|
||||
{
|
||||
m_Stats.AddValue(statDistFlown, 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"
|
||||
|
||||
|
||||
|
||||
|
||||
@ -125,6 +127,9 @@ public:
|
||||
|
||||
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
|
||||
|
||||
/** Returns whether the player is climbing (ladders, vines e.t.c). */
|
||||
bool IsClimbing(void) const;
|
||||
|
||||
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
|
||||
|
||||
// tolua_begin
|
||||
@ -175,6 +180,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 +321,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 +391,9 @@ public:
|
||||
/** If true the player can fly even when he's not in creative. */
|
||||
void SetCanFly(bool a_CanFly);
|
||||
|
||||
/** Update movement-related statistics. */
|
||||
void UpdateMovementStats(const Vector3d & a_DeltaPos);
|
||||
|
||||
/** Returns wheter the player can fly or not. */
|
||||
virtual bool CanFly(void) const { return m_CanFly; }
|
||||
// tolua_end
|
||||
@ -487,6 +506,8 @@ protected:
|
||||
|
||||
cTeam * m_Team;
|
||||
|
||||
cStatManager m_Stats;
|
||||
|
||||
|
||||
|
||||
void ResolvePermissions(void);
|
||||
@ -506,6 +527,9 @@ protected:
|
||||
/** Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item. */
|
||||
void HandleFloater(void);
|
||||
|
||||
/** Tosses a list of items. */
|
||||
void TossItems(const cItems & a_Items);
|
||||
|
||||
/** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */
|
||||
void ApplyFoodExhaustionFromMovement();
|
||||
|
||||
|
@ -35,13 +35,6 @@ reduced in complexity in order for this generator to be useful, so the caves' sh
|
||||
|
||||
|
||||
|
||||
/// How many nests in each direction are generated for a given chunk. Must be an even number
|
||||
#define NEIGHBORHOOD_SIZE 8
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const int MIN_RADIUS = 3;
|
||||
const int MAX_RADIUS = 8;
|
||||
|
||||
@ -122,27 +115,19 @@ typedef std::vector<cCaveTunnel *> cCaveTunnels;
|
||||
|
||||
|
||||
/// A collection of connected tunnels, possibly branching.
|
||||
class cStructGenWormNestCaves::cCaveSystem
|
||||
class cStructGenWormNestCaves::cCaveSystem :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
// The generating block position; is read directly in cStructGenWormNestCaves::GetCavesForChunk()
|
||||
int m_BlockX;
|
||||
int m_BlockZ;
|
||||
|
||||
cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
|
||||
cCaveSystem(int a_OriginX, int a_OriginZ, int a_MaxOffset, int a_Size, cNoise & a_Noise);
|
||||
~cCaveSystem();
|
||||
|
||||
/// Carves the cave system into the chunk specified
|
||||
void ProcessChunk(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes,
|
||||
cChunkDef::HeightMap & a_HeightMap
|
||||
);
|
||||
|
||||
#ifdef _DEBUG
|
||||
AString ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const;
|
||||
#endif // _DEBUG
|
||||
|
||||
protected:
|
||||
int m_Size;
|
||||
cCaveTunnels m_Tunnels;
|
||||
@ -157,6 +142,9 @@ protected:
|
||||
|
||||
/// Returns a radius based on the location provided.
|
||||
int GetRadius(cNoise & a_Noise, int a_OriginX, int a_OriginY, int a_OriginZ);
|
||||
|
||||
// cGridStructGen::cStructure overrides:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override;
|
||||
} ;
|
||||
|
||||
|
||||
@ -586,17 +574,16 @@ AString cCaveTunnel::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) cons
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cStructGenWormNestCaves::cCaveSystem:
|
||||
|
||||
cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_BlockX, int a_BlockZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
|
||||
m_BlockX(a_BlockX),
|
||||
m_BlockZ(a_BlockZ),
|
||||
cStructGenWormNestCaves::cCaveSystem::cCaveSystem(int a_OriginX, int a_OriginZ, int a_MaxOffset, int a_Size, cNoise & a_Noise) :
|
||||
super(a_OriginX, a_OriginZ),
|
||||
m_Size(a_Size)
|
||||
{
|
||||
int Num = 1 + a_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) % 3;
|
||||
int Num = 1 + a_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) % 3;
|
||||
for (int i = 0; i < Num; i++)
|
||||
{
|
||||
int OriginX = a_BlockX + (a_Noise.IntNoise3DInt(13 * a_BlockX, 17 * a_BlockZ, 11 * i) / 19) % a_MaxOffset;
|
||||
int OriginZ = a_BlockZ + (a_Noise.IntNoise3DInt(17 * a_BlockX, 13 * a_BlockZ, 11 * i) / 23) % a_MaxOffset;
|
||||
int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_BlockX, 13 * a_BlockZ, 11 * i) / 17) % 20;
|
||||
int OriginX = a_OriginX + (a_Noise.IntNoise3DInt(13 * a_OriginX, 17 * a_OriginZ, 11 * i) / 19) % a_MaxOffset;
|
||||
int OriginZ = a_OriginZ + (a_Noise.IntNoise3DInt(17 * a_OriginX, 13 * a_OriginZ, 11 * i) / 23) % a_MaxOffset;
|
||||
int OriginY = 20 + (a_Noise.IntNoise3DInt(19 * a_OriginX, 13 * a_OriginZ, 11 * i) / 17) % 20;
|
||||
|
||||
// Generate three branches from the origin point:
|
||||
// The tunnels generated depend on X, Y, Z and Branches,
|
||||
@ -622,15 +609,15 @@ cStructGenWormNestCaves::cCaveSystem::~cCaveSystem()
|
||||
|
||||
|
||||
|
||||
void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes,
|
||||
cChunkDef::HeightMap & a_HeightMap
|
||||
)
|
||||
void cStructGenWormNestCaves::cCaveSystem::DrawIntoChunk(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cChunkDef::BlockTypes & BlockTypes = a_ChunkDesc.GetBlockTypes();
|
||||
cChunkDef::HeightMap & HeightMap = a_ChunkDesc.GetHeightMap();
|
||||
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
|
||||
{
|
||||
(*itr)->ProcessChunk(a_ChunkX, a_ChunkZ, a_BlockTypes, a_HeightMap);
|
||||
(*itr)->ProcessChunk(ChunkX, ChunkZ, BlockTypes, HeightMap);
|
||||
} // for itr - m_Tunnels[]
|
||||
}
|
||||
|
||||
@ -638,53 +625,6 @@ void cStructGenWormNestCaves::cCaveSystem::ProcessChunk(
|
||||
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
AString cStructGenWormNestCaves::cCaveSystem::ExportAsSVG(int a_Color, int a_OffsetX, int a_OffsetZ) const
|
||||
{
|
||||
AString SVG;
|
||||
SVG.reserve(512 * 1024);
|
||||
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
|
||||
{
|
||||
SVG.append((*itr)->ExportAsSVG(a_Color, a_OffsetX, a_OffsetZ));
|
||||
} // for itr - m_Tunnels[]
|
||||
|
||||
// Base point highlight:
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
|
||||
);
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
|
||||
);
|
||||
|
||||
// A gray line from the base point to the first point of the ravine, for identification:
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ,
|
||||
a_OffsetX + m_Tunnels.front()->m_Points.front().m_BlockX,
|
||||
a_OffsetZ + m_Tunnels.front()->m_Points.front().m_BlockZ
|
||||
);
|
||||
|
||||
// Offset guides:
|
||||
if (a_OffsetX > 0)
|
||||
{
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M %d,0 L %d,1024\"/>\n",
|
||||
a_OffsetX, a_OffsetX
|
||||
);
|
||||
}
|
||||
if (a_OffsetZ > 0)
|
||||
{
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#0000ff;stroke-width:1px;\"\nd=\"M 0,%d L 1024,%d\"/>\n",
|
||||
a_OffsetZ, a_OffsetZ
|
||||
);
|
||||
}
|
||||
|
||||
return SVG;
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenWormNestCaves::cCaveSystem::Clear(void)
|
||||
{
|
||||
for (cCaveTunnels::const_iterator itr = m_Tunnels.begin(), end = m_Tunnels.end(); itr != end; ++itr)
|
||||
@ -750,142 +690,9 @@ int cStructGenWormNestCaves::cCaveSystem::GetRadius(cNoise & a_Noise, int a_Orig
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cStructGenWormNestCaves:
|
||||
|
||||
cStructGenWormNestCaves::~cStructGenWormNestCaves()
|
||||
cGridStructGen::cStructurePtr cStructGenWormNestCaves::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenWormNestCaves::ClearCache(void)
|
||||
{
|
||||
for (cCaveSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - m_Cache[]
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenWormNestCaves::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cCaveSystems Caves;
|
||||
GetCavesForChunk(ChunkX, ChunkZ, Caves);
|
||||
for (cCaveSystems::const_iterator itr = Caves.begin(); itr != Caves.end(); ++itr)
|
||||
{
|
||||
(*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
|
||||
} // for itr - Caves[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenWormNestCaves::GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenWormNestCaves::cCaveSystems & a_Caves)
|
||||
{
|
||||
int BaseX = a_ChunkX * cChunkDef::Width / m_Grid;
|
||||
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Grid;
|
||||
if (BaseX < 0)
|
||||
{
|
||||
--BaseX;
|
||||
}
|
||||
if (BaseZ < 0)
|
||||
{
|
||||
--BaseZ;
|
||||
}
|
||||
BaseX -= NEIGHBORHOOD_SIZE / 2;
|
||||
BaseZ -= NEIGHBORHOOD_SIZE / 2;
|
||||
|
||||
// Walk the cache, move each cave system that we want into a_Caves:
|
||||
int StartX = BaseX * m_Grid;
|
||||
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Grid;
|
||||
int StartZ = BaseZ * m_Grid;
|
||||
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Grid;
|
||||
for (cCaveSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
|
||||
{
|
||||
if (
|
||||
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
|
||||
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
|
||||
)
|
||||
{
|
||||
// want
|
||||
a_Caves.push_back(*itr);
|
||||
itr = m_Cache.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't want
|
||||
++itr;
|
||||
}
|
||||
} // for itr - m_Cache[]
|
||||
|
||||
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
|
||||
{
|
||||
int RealX = (BaseX + x) * m_Grid;
|
||||
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
|
||||
{
|
||||
int RealZ = (BaseZ + z) * m_Grid;
|
||||
bool Found = false;
|
||||
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Found)
|
||||
{
|
||||
a_Caves.push_back(new cCaveSystem(RealX, RealZ, m_MaxOffset, m_Size, m_Noise));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a_Caves into m_Cache to the beginning:
|
||||
cCaveSystems CavesCopy(a_Caves);
|
||||
m_Cache.splice(m_Cache.begin(), CavesCopy, CavesCopy.begin(), CavesCopy.end());
|
||||
|
||||
// Trim the cache if it's too long:
|
||||
if (m_Cache.size() > 100)
|
||||
{
|
||||
cCaveSystems::iterator itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
for (cCaveSystems::iterator end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
m_Cache.erase(itr, m_Cache.end());
|
||||
}
|
||||
|
||||
/*
|
||||
// Uncomment this block for debugging the caves' shapes in 2D using an SVG export
|
||||
#ifdef _DEBUG
|
||||
AString SVG;
|
||||
SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
|
||||
SVG.reserve(2 * 1024 * 1024);
|
||||
for (cCaveSystems::const_iterator itr = a_Caves.begin(), end = a_Caves.end(); itr != end; ++itr)
|
||||
{
|
||||
int Color = 0x10 * abs((*itr)->m_BlockX / m_Grid);
|
||||
Color |= 0x1000 * abs((*itr)->m_BlockZ / m_Grid);
|
||||
SVG.append((*itr)->ExportAsSVG(Color, 512, 512));
|
||||
}
|
||||
SVG.append("</svg>\n");
|
||||
|
||||
AString fnam;
|
||||
Printf(fnam, "wnc\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
|
||||
cFile File(fnam, cFile::fmWrite);
|
||||
File.Write(SVG.c_str(), SVG.size());
|
||||
#endif // _DEBUG
|
||||
//*/
|
||||
return cStructurePtr(new cCaveSystem(a_OriginX, a_OriginZ, m_MaxOffset, m_Size, m_Noise));
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
@ -64,10 +64,12 @@ protected:
|
||||
|
||||
|
||||
class cStructGenWormNestCaves :
|
||||
public cFinishGen
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
public:
|
||||
cStructGenWormNestCaves(int a_Seed, int a_Size = 64, int a_Grid = 96, int a_MaxOffset = 128) :
|
||||
super(a_Seed, a_Grid, a_Grid, a_Size + a_MaxOffset, a_Size + a_MaxOffset, 100),
|
||||
m_Noise(a_Seed),
|
||||
m_Size(a_Size),
|
||||
m_MaxOffset(a_MaxOffset),
|
||||
@ -75,26 +77,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~cStructGenWormNestCaves();
|
||||
|
||||
protected:
|
||||
class cCaveSystem; // fwd: Caves.cpp
|
||||
typedef std::list<cCaveSystem *> cCaveSystems;
|
||||
|
||||
cNoise m_Noise;
|
||||
int m_Size; // relative size of the cave systems' caves. Average number of blocks of each initial tunnel
|
||||
int m_MaxOffset; // maximum offset of the cave nest origin from the grid cell the nest belongs to
|
||||
int m_Grid; // average spacing of the nests
|
||||
cCaveSystems m_Cache;
|
||||
|
||||
/// Clears everything from the cache
|
||||
void ClearCache(void);
|
||||
|
||||
/// Returns all caves that *may* intersect the given chunk. All the caves are valid until the next call to this function.
|
||||
void GetCavesForChunk(int a_ChunkX, int a_ChunkZ, cCaveSystems & a_Caves);
|
||||
|
||||
// cStructGen override:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
// cGridStructGen override:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
135
src/Generating/GridStructGen.cpp
Normal file
135
src/Generating/GridStructGen.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
// GridStructGen.cpp
|
||||
|
||||
// Implements the cGridStructGen class representing a common base class for structure generators that place structures in a semi-random grid
|
||||
|
||||
#include "Globals.h"
|
||||
#include "GridStructGen.h"
|
||||
|
||||
|
||||
|
||||
|
||||
cGridStructGen::cGridStructGen(
|
||||
int a_Seed,
|
||||
int a_GridSizeX, int a_GridSizeZ,
|
||||
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
|
||||
size_t a_MaxCacheSize
|
||||
) :
|
||||
m_Seed(a_Seed),
|
||||
m_GridSizeX(a_GridSizeX),
|
||||
m_GridSizeZ(a_GridSizeZ),
|
||||
m_MaxStructureSizeX(a_MaxStructureSizeX),
|
||||
m_MaxStructureSizeZ(a_MaxStructureSizeZ),
|
||||
m_MaxCacheSize(a_MaxCacheSize)
|
||||
{
|
||||
size_t NumStructuresPerQuery = (size_t)((m_MaxStructureSizeX / m_GridSizeX + 1) * (m_MaxStructureSizeZ / m_GridSizeZ + 1));
|
||||
if (NumStructuresPerQuery > m_MaxCacheSize)
|
||||
{
|
||||
m_MaxCacheSize = NumStructuresPerQuery * 4;
|
||||
LOGINFO(
|
||||
"cGridStructGen: The cache size is too small (%u), increasing the cache size to %u to avoid inefficiency.",
|
||||
(unsigned)a_MaxCacheSize, (unsigned)m_MaxCacheSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cGridStructGen::GetStructuresForChunk(int a_ChunkX, int a_ChunkZ, cStructurePtrs & a_Structures)
|
||||
{
|
||||
// Calculate the min and max grid coords of the structures to be returned:
|
||||
int MinBlockX = a_ChunkX * cChunkDef::Width - m_MaxStructureSizeX;
|
||||
int MinBlockZ = a_ChunkZ * cChunkDef::Width - m_MaxStructureSizeZ;
|
||||
int MaxBlockX = a_ChunkX * cChunkDef::Width + m_MaxStructureSizeX + cChunkDef::Width - 1;
|
||||
int MaxBlockZ = a_ChunkZ * cChunkDef::Width + m_MaxStructureSizeZ + cChunkDef::Width - 1;
|
||||
int MinGridX = MinBlockX / m_GridSizeX;
|
||||
int MinGridZ = MinBlockZ / m_GridSizeZ;
|
||||
int MaxGridX = (MaxBlockX + m_GridSizeX - 1) / m_GridSizeX;
|
||||
int MaxGridZ = (MaxBlockZ + m_GridSizeZ - 1) / m_GridSizeZ;
|
||||
int MinX = MinGridX * m_GridSizeX;
|
||||
int MaxX = MaxGridX * m_GridSizeX;
|
||||
int MinZ = MinGridZ * m_GridSizeZ;
|
||||
int MaxZ = MaxGridZ * m_GridSizeZ;
|
||||
|
||||
// Walk the cache, move each structure that we want into a_Structures:
|
||||
for (cStructurePtrs::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
|
||||
{
|
||||
if (
|
||||
((*itr)->m_OriginX >= MinX) && ((*itr)->m_OriginX < MaxX) &&
|
||||
((*itr)->m_OriginZ >= MinZ) && ((*itr)->m_OriginZ < MaxZ)
|
||||
)
|
||||
{
|
||||
// want
|
||||
a_Structures.push_back(*itr);
|
||||
itr = m_Cache.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't want
|
||||
++itr;
|
||||
}
|
||||
} // for itr - m_Cache[]
|
||||
|
||||
// Create those structures that haven't been in the cache:
|
||||
for (int x = MinGridX; x < MaxGridX; x++)
|
||||
{
|
||||
int OriginX = x * m_GridSizeX;
|
||||
for (int z = MinGridZ; z < MaxGridZ; z++)
|
||||
{
|
||||
int OriginZ = z * m_GridSizeZ;
|
||||
bool Found = false;
|
||||
for (cStructurePtrs::const_iterator itr = a_Structures.begin(), end = a_Structures.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_OriginX == OriginX) && ((*itr)->m_OriginZ == OriginZ))
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
} // for itr - a_Structures[]
|
||||
if (!Found)
|
||||
{
|
||||
a_Structures.push_back(CreateStructure(OriginX, OriginZ));
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
||||
// Copy a_Forts into m_Cache to the beginning:
|
||||
cStructurePtrs StructuresCopy (a_Structures);
|
||||
m_Cache.splice(m_Cache.begin(), StructuresCopy, StructuresCopy.begin(), StructuresCopy.end());
|
||||
|
||||
// Trim the cache if it's too long:
|
||||
size_t CacheSize = 0;
|
||||
for (cStructurePtrs::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
CacheSize += (*itr)->GetCacheCost();
|
||||
if (CacheSize > m_MaxCacheSize)
|
||||
{
|
||||
// Erase all items from this one till the cache end
|
||||
m_Cache.erase(itr, m_Cache.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cGridStructGen::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cStructurePtrs Structures;
|
||||
GetStructuresForChunk(ChunkX, ChunkZ, Structures);
|
||||
for (cStructurePtrs::const_iterator itr = Structures.begin(); itr != Structures.end(); ++itr)
|
||||
{
|
||||
(*itr)->DrawIntoChunk(a_ChunkDesc);
|
||||
} // for itr - Structures[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
124
src/Generating/GridStructGen.h
Normal file
124
src/Generating/GridStructGen.h
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
// GridStructGen.h
|
||||
|
||||
// Declares the cGridStructGen class representing a common base class for structure generators that place structures in a semi-random grid
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Generates structures in a semi-random grid.
|
||||
Defines a grid in the XZ space with predefined cell size in each direction. Each cell then receives exactly
|
||||
one structure (provided by the descendant class). The structure is placed within the cell, but doesn't need
|
||||
to be bounded by the cell, it can be well outside the cell; the generator uses the MaxStructureSize parameter
|
||||
to determine how far away from the cell the structure can be at most.
|
||||
This class provides a cache for the structures generated for successive chunks and manages that cache. It
|
||||
also provides the cFinishGen override that uses the cache to actually generate the structure into chunk data.
|
||||
|
||||
After generating each chunk the cache is checked for size, each item in the cache has a cost associated with
|
||||
it and the cache is trimmed (from its least-recently-used end) so that the sum of the cost in the cache is
|
||||
less than m_MaxCacheSize
|
||||
|
||||
To use this class, declare a descendant class that implements the overridable methods, then create an
|
||||
instance of that class. The descendant must provide the CreateStructure() function that is called to generate
|
||||
a structure at the specific grid cell.
|
||||
|
||||
The descendant must use a specific cStructure descendant to provide the actual structure that gets generated.
|
||||
The structure must provide the DrawIntoChunk() function that generates the structure into the chunk data, and
|
||||
can override the GetCacheCost() function that returns the cost of that structure in the cache.
|
||||
*/
|
||||
class cGridStructGen :
|
||||
public cFinishGen
|
||||
{
|
||||
public:
|
||||
cGridStructGen(
|
||||
int a_Seed,
|
||||
int a_GridSizeX, int a_GridSizeZ,
|
||||
int a_MaxStructureSizeX, int a_MaxStructureSizeZ,
|
||||
size_t a_MaxCacheSize
|
||||
);
|
||||
|
||||
protected:
|
||||
/** Represents a single structure that occupies the grid point. Knows how to draw itself into a chunk. */
|
||||
class cStructure
|
||||
{
|
||||
public:
|
||||
/** The origin (the coords of the gridpoint for which the structure is generated) */
|
||||
int m_OriginX, m_OriginZ;
|
||||
|
||||
|
||||
/** Creates a structure that has its originset at the specified coords. */
|
||||
cStructure (int a_OriginX, int a_OriginZ) :
|
||||
m_OriginX(a_OriginX),
|
||||
m_OriginZ(a_OriginZ)
|
||||
{
|
||||
}
|
||||
|
||||
// Force a virtual destructor in descendants:
|
||||
virtual ~cStructure() {}
|
||||
|
||||
/** Draws self into the specified chunk */
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) = 0;
|
||||
|
||||
/** Returns the cost of keeping this structure in the cache */
|
||||
virtual size_t GetCacheCost(void) const { return 1; }
|
||||
} ;
|
||||
typedef SharedPtr<cStructure> cStructurePtr;
|
||||
typedef std::list<cStructurePtr> cStructurePtrs;
|
||||
|
||||
|
||||
/** Seed for generating the semi-random grid. */
|
||||
int m_Seed;
|
||||
|
||||
/** The size of each grid's cell in the X axis */
|
||||
int m_GridSizeX;
|
||||
|
||||
/** The size of each grid's cell in the Z axis */
|
||||
int m_GridSizeZ;
|
||||
|
||||
/** Maximum theoretical size of the structure in the X axis.
|
||||
This limits the structures considered for a single chunk, so the lesser the number, the better performance.
|
||||
Structures large than this may get cropped. */
|
||||
int m_MaxStructureSizeX;
|
||||
|
||||
/** Maximum theoretical size of the structure in the Z axis.
|
||||
This limits the structures considered for a single chunk, so the lesser the number, the better performance.
|
||||
Structures large than this may get cropped. */
|
||||
int m_MaxStructureSizeZ;
|
||||
|
||||
/** Maximum allowed sum of costs for items in the cache. Items that are over this cost are removed from the
|
||||
cache, oldest-first */
|
||||
size_t m_MaxCacheSize;
|
||||
|
||||
/** Cache for the most recently generated structures, ordered by the recentness. */
|
||||
cStructurePtrs m_Cache;
|
||||
|
||||
|
||||
/** Clears everything from the cache */
|
||||
void ClearCache(void);
|
||||
|
||||
/** Returns all structures that may intersect the given chunk.
|
||||
The structures are considered as intersecting iff their bounding box (defined by m_MaxStructureSize)
|
||||
around their gridpoint intersects the chunk. */
|
||||
void GetStructuresForChunk(int a_ChunkX, int a_ChunkZ, cStructurePtrs & a_Structures);
|
||||
|
||||
// cFinishGen overrides:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
|
||||
// Functions for the descendants to override:
|
||||
/** Create a new structure at the specified gridpoint */
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -25,12 +25,6 @@ in a depth-first processing. Each of the descendants will branch randomly, if no
|
||||
|
||||
|
||||
|
||||
static const int NEIGHBORHOOD_SIZE = 3;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cMineShaft abstract
|
||||
{
|
||||
public:
|
||||
@ -234,10 +228,12 @@ protected:
|
||||
|
||||
|
||||
|
||||
class cStructGenMineShafts::cMineShaftSystem
|
||||
class cStructGenMineShafts::cMineShaftSystem :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
int m_BlockX, m_BlockZ; ///< The pivot point on which the system is generated
|
||||
int m_GridSize; ///< Maximum offset of the dirtroom from grid center, * 2, in each direction
|
||||
int m_MaxRecursion; ///< Maximum recursion level (initialized from cStructGenMineShafts::m_MaxRecursion)
|
||||
int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
|
||||
@ -249,17 +245,15 @@ public:
|
||||
cMineShafts m_MineShafts; ///< List of cMineShaft descendants that comprise this system
|
||||
cCuboid m_BoundingBox; ///< Bounding box into which all of the components need to fit
|
||||
|
||||
/// Creates and generates the entire system
|
||||
|
||||
/** Creates and generates the entire system */
|
||||
cMineShaftSystem(
|
||||
int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
|
||||
int a_OriginX, int a_OriginZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
|
||||
int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
|
||||
);
|
||||
|
||||
~cMineShaftSystem();
|
||||
|
||||
/// Carves the system into the chunk data
|
||||
void ProcessChunk(cChunkDesc & a_Chunk);
|
||||
|
||||
/** Creates new cMineShaft descendant connected at the specified point, heading the specified direction,
|
||||
if it fits, appends it to the list and calls its AppendBranches()
|
||||
*/
|
||||
@ -269,8 +263,11 @@ public:
|
||||
int a_RecursionLevel
|
||||
);
|
||||
|
||||
/// Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid
|
||||
/** Returns true if none of the objects in m_MineShafts intersect with the specified bounding box and the bounding box is valid */
|
||||
bool CanAppend(const cCuboid & a_BoundingBox);
|
||||
|
||||
// cGridStructGen::cStructure overrides:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_Chunk);
|
||||
} ;
|
||||
|
||||
|
||||
@ -281,11 +278,10 @@ public:
|
||||
// cStructGenMineShafts::cMineShaftSystem:
|
||||
|
||||
cStructGenMineShafts::cMineShaftSystem::cMineShaftSystem(
|
||||
int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
|
||||
int a_OriginX, int a_OriginZ, int a_GridSize, int a_MaxSystemSize, cNoise & a_Noise,
|
||||
int a_ProbLevelCorridor, int a_ProbLevelCrossing, int a_ProbLevelStaircase
|
||||
) :
|
||||
m_BlockX(a_BlockX),
|
||||
m_BlockZ(a_BlockZ),
|
||||
super(a_OriginX, a_OriginZ),
|
||||
m_GridSize(a_GridSize),
|
||||
m_MaxRecursion(8), // TODO: settable
|
||||
m_ProbLevelCorridor(a_ProbLevelCorridor),
|
||||
@ -330,7 +326,7 @@ cStructGenMineShafts::cMineShaftSystem::~cMineShaftSystem()
|
||||
|
||||
|
||||
|
||||
void cStructGenMineShafts::cMineShaftSystem::ProcessChunk(cChunkDesc & a_Chunk)
|
||||
void cStructGenMineShafts::cMineShaftSystem::DrawIntoChunk(cChunkDesc & a_Chunk)
|
||||
{
|
||||
for (cMineShafts::const_iterator itr = m_MineShafts.begin(), end = m_MineShafts.end(); itr != end; ++itr)
|
||||
{
|
||||
@ -409,15 +405,15 @@ cMineShaftDirtRoom::cMineShaftDirtRoom(cStructGenMineShafts::cMineShaftSystem &
|
||||
super(a_Parent, mskDirtRoom)
|
||||
{
|
||||
// Make the room of random size, min 10 x 4 x 10; max 18 x 12 x 18:
|
||||
int rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 0, a_Parent.m_BlockZ) / 7;
|
||||
int rnd = a_Noise.IntNoise3DInt(a_Parent.m_OriginX, 0, a_Parent.m_OriginZ) / 7;
|
||||
int OfsX = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
|
||||
rnd >>= 12;
|
||||
int OfsZ = (rnd % a_Parent.m_GridSize) - a_Parent.m_GridSize / 2;
|
||||
rnd = a_Noise.IntNoise3DInt(a_Parent.m_BlockX, 1000, a_Parent.m_BlockZ) / 11;
|
||||
m_BoundingBox.p1.x = a_Parent.m_BlockX + OfsX;
|
||||
rnd = a_Noise.IntNoise3DInt(a_Parent.m_OriginX, 1000, a_Parent.m_OriginZ) / 11;
|
||||
m_BoundingBox.p1.x = a_Parent.m_OriginX + OfsX;
|
||||
m_BoundingBox.p2.x = m_BoundingBox.p1.x + 10 + (rnd % 8);
|
||||
rnd >>= 4;
|
||||
m_BoundingBox.p1.z = a_Parent.m_BlockZ + OfsZ;
|
||||
m_BoundingBox.p1.z = a_Parent.m_OriginZ + OfsZ;
|
||||
m_BoundingBox.p2.z = m_BoundingBox.p1.z + 10 + (rnd % 8);
|
||||
rnd >>= 4;
|
||||
m_BoundingBox.p1.y = 20;
|
||||
@ -1287,6 +1283,7 @@ cStructGenMineShafts::cStructGenMineShafts(
|
||||
int a_Seed, int a_GridSize, int a_MaxSystemSize,
|
||||
int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
|
||||
) :
|
||||
super(a_Seed, a_GridSize, a_GridSize, a_MaxSystemSize, a_MaxSystemSize, 100),
|
||||
m_Noise(a_Seed),
|
||||
m_GridSize(a_GridSize),
|
||||
m_MaxSystemSize(a_MaxSystemSize),
|
||||
@ -1300,125 +1297,9 @@ cStructGenMineShafts::cStructGenMineShafts(
|
||||
|
||||
|
||||
|
||||
cStructGenMineShafts::~cStructGenMineShafts()
|
||||
cGridStructGen::cStructurePtr cStructGenMineShafts::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenMineShafts::ClearCache(void)
|
||||
{
|
||||
for (cMineShaftSystems::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - m_Cache[]
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenMineShafts::GetMineShaftSystemsForChunk(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cStructGenMineShafts::cMineShaftSystems & a_MineShafts
|
||||
)
|
||||
{
|
||||
int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize;
|
||||
int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize;
|
||||
if (BaseX < 0)
|
||||
{
|
||||
--BaseX;
|
||||
}
|
||||
if (BaseZ < 0)
|
||||
{
|
||||
--BaseZ;
|
||||
}
|
||||
BaseX -= NEIGHBORHOOD_SIZE / 2;
|
||||
BaseZ -= NEIGHBORHOOD_SIZE / 2;
|
||||
|
||||
// Walk the cache, move each cave system that we want into a_Mineshafts:
|
||||
int StartX = BaseX * m_GridSize;
|
||||
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
|
||||
int StartZ = BaseZ * m_GridSize;
|
||||
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
|
||||
for (cMineShaftSystems::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
|
||||
{
|
||||
if (
|
||||
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
|
||||
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
|
||||
)
|
||||
{
|
||||
// want
|
||||
a_MineShafts.push_back(*itr);
|
||||
itr = m_Cache.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't want
|
||||
++itr;
|
||||
}
|
||||
} // for itr - m_Cache[]
|
||||
|
||||
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
|
||||
{
|
||||
int RealX = (BaseX + x) * m_GridSize;
|
||||
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
|
||||
{
|
||||
int RealZ = (BaseZ + z) * m_GridSize;
|
||||
bool Found = false;
|
||||
for (cMineShaftSystems::const_iterator itr = a_MineShafts.begin(), end = a_MineShafts.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
} // for itr - a_Mineshafts
|
||||
if (!Found)
|
||||
{
|
||||
a_MineShafts.push_back(new cMineShaftSystem(RealX, RealZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase));
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
||||
// Copy a_MineShafts into m_Cache to the beginning:
|
||||
cMineShaftSystems MineShaftsCopy(a_MineShafts);
|
||||
m_Cache.splice(m_Cache.begin(), MineShaftsCopy, MineShaftsCopy.begin(), MineShaftsCopy.end());
|
||||
|
||||
// Trim the cache if it's too long:
|
||||
if (m_Cache.size() > 100)
|
||||
{
|
||||
cMineShaftSystems::iterator itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
for (cMineShaftSystems::iterator end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
m_Cache.erase(itr, m_Cache.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenMineShafts::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cMineShaftSystems MineShafts;
|
||||
GetMineShaftSystemsForChunk(ChunkX, ChunkZ, MineShafts);
|
||||
for (cMineShaftSystems::const_iterator itr = MineShafts.begin(); itr != MineShafts.end(); ++itr)
|
||||
{
|
||||
(*itr)->ProcessChunk(a_ChunkDesc);
|
||||
} // for itr - MineShafts[]
|
||||
return cStructurePtr(new cMineShaftSystem(a_OriginX, a_OriginZ, m_GridSize, m_MaxSystemSize, m_Noise, m_ProbLevelCorridor, m_ProbLevelCrossing, m_ProbLevelStaircase));
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
@ -17,16 +17,16 @@
|
||||
|
||||
|
||||
class cStructGenMineShafts :
|
||||
public cFinishGen
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
|
||||
public:
|
||||
cStructGenMineShafts(
|
||||
int a_Seed, int a_GridSize, int a_MaxSystemSize,
|
||||
int a_ChanceCorridor, int a_ChanceCrossing, int a_ChanceStaircase
|
||||
);
|
||||
|
||||
virtual ~cStructGenMineShafts();
|
||||
|
||||
protected:
|
||||
friend class cMineShaft;
|
||||
friend class cMineShaftDirtRoom;
|
||||
@ -34,26 +34,16 @@ protected:
|
||||
friend class cMineShaftCrossing;
|
||||
friend class cMineShaftStaircase;
|
||||
class cMineShaftSystem; // fwd: MineShafts.cpp
|
||||
typedef std::list<cMineShaftSystem *> cMineShaftSystems;
|
||||
|
||||
cNoise m_Noise;
|
||||
int m_GridSize; ///< Average spacing of the systems
|
||||
int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
|
||||
int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
|
||||
int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
|
||||
int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
|
||||
cMineShaftSystems m_Cache; ///< Cache of the most recently used systems. MoveToFront used.
|
||||
cNoise m_Noise;
|
||||
int m_GridSize; ///< Average spacing of the systems
|
||||
int m_MaxSystemSize; ///< Maximum blcok size of a mineshaft system
|
||||
int m_ProbLevelCorridor; ///< Probability level of a branch object being the corridor
|
||||
int m_ProbLevelCrossing; ///< Probability level of a branch object being the crossing, minus Corridor
|
||||
int m_ProbLevelStaircase; ///< Probability level of a branch object being the staircase, minus Crossing
|
||||
|
||||
/// Clears everything from the cache
|
||||
void ClearCache(void);
|
||||
|
||||
/** Returns all systems that *may* intersect the given chunk.
|
||||
All the systems are valid until the next call to this function (which may delete some of the pointers).
|
||||
*/
|
||||
void GetMineShaftSystemsForChunk(int a_ChunkX, int a_ChunkZ, cMineShaftSystems & a_MineShaftSystems);
|
||||
|
||||
// cFinishGen overrides:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -11,29 +11,24 @@
|
||||
|
||||
|
||||
|
||||
static const int NEIGHBORHOOD_SIZE = 3;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cNetherFortGen::cNetherFort:
|
||||
|
||||
class cNetherFortGen::cNetherFort
|
||||
class cNetherFortGen::cNetherFort :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
public:
|
||||
cNetherFortGen & m_ParentGen;
|
||||
int m_BlockX, m_BlockZ;
|
||||
int m_GridSize;
|
||||
int m_Seed;
|
||||
cPlacedPieces m_Pieces;
|
||||
|
||||
|
||||
cNetherFort(cNetherFortGen & a_ParentGen, int a_BlockX, int a_BlockZ, int a_GridSize, int a_MaxDepth, int a_Seed) :
|
||||
cNetherFort(cNetherFortGen & a_ParentGen, int a_OriginX, int a_OriginZ, int a_GridSize, int a_MaxDepth, int a_Seed) :
|
||||
super(a_OriginX, a_OriginZ),
|
||||
m_ParentGen(a_ParentGen),
|
||||
m_BlockX(a_BlockX),
|
||||
m_BlockZ(a_BlockZ),
|
||||
m_GridSize(a_GridSize),
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
@ -43,8 +38,8 @@ public:
|
||||
// Generate pieces:
|
||||
for (int i = 0; m_Pieces.size() < (size_t)(a_MaxDepth * a_MaxDepth / 8 + a_MaxDepth); i++)
|
||||
{
|
||||
cBFSPieceGenerator pg(m_ParentGen, a_Seed + i);
|
||||
pg.PlacePieces(a_BlockX, BlockY, a_BlockZ, a_MaxDepth, m_Pieces);
|
||||
cBFSPieceGenerator pg(cNetherFortGen::m_PiecePool, a_Seed + i);
|
||||
pg.PlacePieces(a_OriginX, BlockY, a_OriginZ, a_MaxDepth, m_Pieces);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +51,7 @@ public:
|
||||
|
||||
|
||||
/** Carves the system into the chunk data */
|
||||
void ProcessChunk(cChunkDesc & a_Chunk)
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_Chunk)
|
||||
{
|
||||
for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
@ -107,214 +102,30 @@ public:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cNetherFortGen:
|
||||
|
||||
cPrefabPiecePool cNetherFortGen::m_PiecePool(g_NetherFortPrefabs, g_NetherFortPrefabsCount, g_NetherFortStartingPrefabs, g_NetherFortStartingPrefabsCount);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cNetherFortGen::cNetherFortGen(int a_Seed, int a_GridSize, int a_MaxDepth) :
|
||||
m_Seed(a_Seed),
|
||||
m_Noise(a_Seed),
|
||||
m_GridSize(a_GridSize),
|
||||
super(a_Seed, a_GridSize, a_GridSize, a_MaxDepth * 10, a_MaxDepth * 10, 200),
|
||||
m_MaxDepth(a_MaxDepth)
|
||||
{
|
||||
// Initialize the prefabs:
|
||||
for (size_t i = 0; i < g_NetherFortPrefabsCount; i++)
|
||||
{
|
||||
cPrefab * Prefab = new cPrefab(g_NetherFortPrefabs[i]);
|
||||
m_AllPieces.push_back(Prefab);
|
||||
if (Prefab->HasConnectorType(0))
|
||||
{
|
||||
m_OuterPieces.push_back(Prefab);
|
||||
}
|
||||
if (Prefab->HasConnectorType(1))
|
||||
{
|
||||
m_InnerPieces.push_back(Prefab);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the starting piece prefabs:
|
||||
for (size_t i = 0; i < g_NetherFortStartingPrefabsCount; i++)
|
||||
{
|
||||
m_StartingPieces.push_back(new cPrefab(g_NetherFortStartingPrefabs[i]));
|
||||
}
|
||||
|
||||
/*
|
||||
// DEBUG: Try one round of placement:
|
||||
cPlacedPieces Pieces;
|
||||
cBFSPieceGenerator pg(*this, a_Seed);
|
||||
cBFSPieceGenerator pg(m_PiecePool, a_Seed);
|
||||
pg.PlacePieces(0, 64, 0, a_MaxDepth, Pieces);
|
||||
*/
|
||||
//*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cNetherFortGen::~cNetherFortGen()
|
||||
cGridStructGen::cStructurePtr cNetherFortGen::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
ClearCache();
|
||||
for (cPieces::iterator itr = m_AllPieces.begin(), end = m_AllPieces.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - m_AllPieces[]
|
||||
m_AllPieces.clear();
|
||||
return cStructurePtr(new cNetherFort(*this, a_OriginX, a_OriginZ, m_GridSizeX, m_MaxDepth, m_Seed));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetherFortGen::ClearCache(void)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetherFortGen::GetFortsForChunk(int a_ChunkX, int a_ChunkZ, cNetherForts & a_Forts)
|
||||
{
|
||||
int BaseX = a_ChunkX * cChunkDef::Width / m_GridSize;
|
||||
int BaseZ = a_ChunkZ * cChunkDef::Width / m_GridSize;
|
||||
if (BaseX < 0)
|
||||
{
|
||||
--BaseX;
|
||||
}
|
||||
if (BaseZ < 0)
|
||||
{
|
||||
--BaseZ;
|
||||
}
|
||||
BaseX -= NEIGHBORHOOD_SIZE / 2;
|
||||
BaseZ -= NEIGHBORHOOD_SIZE / 2;
|
||||
|
||||
// Walk the cache, move each cave system that we want into a_Forts:
|
||||
int StartX = BaseX * m_GridSize;
|
||||
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
|
||||
int StartZ = BaseZ * m_GridSize;
|
||||
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_GridSize;
|
||||
for (cNetherForts::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
|
||||
{
|
||||
if (
|
||||
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
|
||||
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
|
||||
)
|
||||
{
|
||||
// want
|
||||
a_Forts.push_back(*itr);
|
||||
itr = m_Cache.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't want
|
||||
++itr;
|
||||
}
|
||||
} // for itr - m_Cache[]
|
||||
|
||||
// Create those forts that haven't been in the cache:
|
||||
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
|
||||
{
|
||||
int RealX = (BaseX + x) * m_GridSize;
|
||||
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
|
||||
{
|
||||
int RealZ = (BaseZ + z) * m_GridSize;
|
||||
bool Found = false;
|
||||
for (cNetherForts::const_iterator itr = a_Forts.begin(), end = a_Forts.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
} // for itr - a_Mineshafts
|
||||
if (!Found)
|
||||
{
|
||||
a_Forts.push_back(new cNetherFort(*this, RealX, RealZ, m_GridSize, m_MaxDepth, m_Seed));
|
||||
}
|
||||
} // for z
|
||||
} // for x
|
||||
|
||||
// Copy a_Forts into m_Cache to the beginning:
|
||||
cNetherForts FortsCopy (a_Forts);
|
||||
m_Cache.splice(m_Cache.begin(), FortsCopy, FortsCopy.begin(), FortsCopy.end());
|
||||
|
||||
// Trim the cache if it's too long:
|
||||
if (m_Cache.size() > 100)
|
||||
{
|
||||
cNetherForts::iterator itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
for (cNetherForts::iterator end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
m_Cache.erase(itr, m_Cache.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetherFortGen::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cNetherForts Forts;
|
||||
GetFortsForChunk(ChunkX, ChunkZ, Forts);
|
||||
for (cNetherForts::const_iterator itr = Forts.begin(); itr != Forts.end(); ++itr)
|
||||
{
|
||||
(*itr)->ProcessChunk(a_ChunkDesc);
|
||||
} // for itr - Forts[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cNetherFortGen::GetPiecesWithConnector(int a_ConnectorType)
|
||||
{
|
||||
switch (a_ConnectorType)
|
||||
{
|
||||
case 0: return m_OuterPieces;
|
||||
case 1: return m_InnerPieces;
|
||||
default: return cPieces();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cNetherFortGen::GetStartingPieces(void)
|
||||
{
|
||||
return m_StartingPieces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cNetherFortGen::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece)
|
||||
{
|
||||
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetherFortGen::PiecePlaced(const cPiece & a_Piece)
|
||||
{
|
||||
UNUSED(a_Piece);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cNetherFortGen::Reset(void)
|
||||
{
|
||||
// Nothing needed
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -10,77 +10,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "PieceGenerator.h"
|
||||
#include "PrefabPiecePool.h"
|
||||
#include "GridStructGen.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cNetherFortGen :
|
||||
public cFinishGen,
|
||||
public cPiecePool
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
|
||||
public:
|
||||
cNetherFortGen(int a_Seed, int a_GridSize, int a_MaxDepth);
|
||||
|
||||
virtual ~cNetherFortGen();
|
||||
|
||||
protected:
|
||||
friend class cNetherFortPerfTest; // fwd: NetherFortGen.cpp
|
||||
class cNetherFort; // fwd: NetherFortGen.cpp
|
||||
typedef std::list<cNetherFort *> cNetherForts;
|
||||
|
||||
|
||||
/** The seed used for generating*/
|
||||
int m_Seed;
|
||||
|
||||
/** The noise used for generating */
|
||||
cNoise m_Noise;
|
||||
|
||||
/** Average spacing between the fortresses*/
|
||||
int m_GridSize;
|
||||
|
||||
/** Maximum depth of the piece-generator tree */
|
||||
int m_MaxDepth;
|
||||
|
||||
/** Cache of the most recently used systems. MoveToFront used. */
|
||||
cNetherForts m_Cache;
|
||||
|
||||
/** All the pieces that are allowed for building.
|
||||
This is the list that's used for memory allocation and deallocation for the pieces. */
|
||||
cPieces m_AllPieces;
|
||||
|
||||
/** The pieces that are used as starting pieces.
|
||||
This list is not shared and the pieces need deallocation. */
|
||||
cPieces m_StartingPieces;
|
||||
|
||||
/** The pieces that have an "outer" connector.
|
||||
The pieces are copies out of m_AllPieces and shouldn't be ever delete-d. */
|
||||
cPieces m_OuterPieces;
|
||||
|
||||
/** The pieces that have an "inner" connector.
|
||||
The pieces are copies out of m_AllPieces and shouldn't be ever delete-d. */
|
||||
cPieces m_InnerPieces;
|
||||
/** The pool of pieces to use for generating. Static, so that it's shared by multiple generators. */
|
||||
static cPrefabPiecePool m_PiecePool;
|
||||
|
||||
|
||||
/** Clears everything from the cache.
|
||||
Also invalidates the forst returned by GetFortsForChunk(). */
|
||||
void ClearCache(void);
|
||||
|
||||
/** Returns all forts that *may* intersect the given chunk.
|
||||
The returned forts live within m_Cache.They are valid until the next call
|
||||
to this function (which may delete some of the pointers). */
|
||||
void GetFortsForChunk(int a_ChunkX, int a_ChunkZ, cNetherForts & a_Forts);
|
||||
|
||||
// cFinishGen overrides:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
|
||||
// cPiecePool overrides:
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
|
||||
virtual cPieces GetStartingPieces(void) override;
|
||||
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override;
|
||||
virtual void Reset(void) override;
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
121
src/Generating/PrefabPiecePool.cpp
Normal file
121
src/Generating/PrefabPiecePool.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
// PrefabPiecePool.cpp
|
||||
|
||||
// Implements the cPrefabPiecePool class that represents a cPiecePool descendant that uses cPrefab instances as the pieces
|
||||
|
||||
#include "Globals.h"
|
||||
#include "PrefabPiecePool.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPrefabPiecePool::cPrefabPiecePool(
|
||||
const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs,
|
||||
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
|
||||
)
|
||||
{
|
||||
AddPieceDefs(a_PieceDefs, a_NumPieceDefs);
|
||||
if (a_StartingPieceDefs != NULL)
|
||||
{
|
||||
AddStartingPieceDefs(a_StartingPieceDefs, a_NumStartingPieceDefs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::AddPieceDefs(const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs)
|
||||
{
|
||||
ASSERT(a_PieceDefs != NULL);
|
||||
for (size_t i = 0; i < a_NumPieceDefs; i++)
|
||||
{
|
||||
cPrefab * Prefab = new cPrefab(a_PieceDefs[i]);
|
||||
m_AllPieces.push_back(Prefab);
|
||||
AddToPerConnectorMap(Prefab);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::AddStartingPieceDefs(const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs)
|
||||
{
|
||||
ASSERT(a_StartingPieceDefs != NULL);
|
||||
for (size_t i = 0; i < a_NumStartingPieceDefs; i++)
|
||||
{
|
||||
cPrefab * Prefab = new cPrefab(a_StartingPieceDefs[i]);
|
||||
m_StartingPieces.push_back(Prefab);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::AddToPerConnectorMap(cPrefab * a_Prefab)
|
||||
{
|
||||
cPiece::cConnectors Connectors = ((const cPiece *)a_Prefab)->GetConnectors();
|
||||
for (cPiece::cConnectors::const_iterator itr = Connectors.begin(), end = Connectors.end(); itr != end; ++itr)
|
||||
{
|
||||
m_PiecesByConnector[itr->m_Type].push_back(a_Prefab);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cPrefabPiecePool::GetPiecesWithConnector(int a_ConnectorType)
|
||||
{
|
||||
return m_PiecesByConnector[a_ConnectorType];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cPrefabPiecePool::GetStartingPieces(void)
|
||||
{
|
||||
if (m_StartingPieces.empty())
|
||||
{
|
||||
return m_AllPieces;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_StartingPieces;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cPrefabPiecePool::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece)
|
||||
{
|
||||
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::PiecePlaced(const cPiece & a_Piece)
|
||||
{
|
||||
// Do nothing
|
||||
UNUSED(a_Piece);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPrefabPiecePool::Reset(void)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
79
src/Generating/PrefabPiecePool.h
Normal file
79
src/Generating/PrefabPiecePool.h
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
// PrefabPiecePool.h
|
||||
|
||||
// Declares the cPrefabPiecePool class that represents a cPiecePool descendant that uses cPrefab instances as the pieces
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PieceGenerator.h"
|
||||
#include "Prefab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPrefabPiecePool :
|
||||
public cPiecePool
|
||||
{
|
||||
public:
|
||||
/** Creates an empty instance. Prefabs can be added by calling AddPieceDefs() and AddStartingPieceDefs(). */
|
||||
cPrefabPiecePool(void);
|
||||
|
||||
/** Creates a piece pool with prefabs from the specified definitions.
|
||||
If both a_PieceDefs and a_StartingPieceDefs are given, only the a_StartingPieceDefs are used as starting
|
||||
pieces for the pool, and they do not participate in the generation any further.
|
||||
If only a_PieceDefs is given, any such piece can be chosen as a starting piece, and all the pieces are used
|
||||
for generating.
|
||||
More pieces can be added to the instance afterwards by calling AddPieceDefs() and AddStartingPieceDefs(). */
|
||||
cPrefabPiecePool(
|
||||
const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs,
|
||||
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
|
||||
);
|
||||
|
||||
/** Adds pieces from the specified definitions into m_AllPieces. Also adds the pieces into
|
||||
the m_PiecesByConnector map.
|
||||
May be called multiple times with different PieceDefs, will add all such pieces. */
|
||||
void AddPieceDefs(const cPrefab::sDef * a_PieceDefs, size_t a_NumPieceDefs);
|
||||
|
||||
/** Adds pieces from the specified definitions into m_StartingPieces. Doesn't add them to
|
||||
the m_PiecesByConnector map.
|
||||
May be called multiple times with different PieceDefs, will add all such pieces. */
|
||||
void AddStartingPieceDefs(const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** The type used to map a connector type to the list of pieces with that connector */
|
||||
typedef std::map<int, cPieces> cPiecesMap;
|
||||
|
||||
/** All the pieces that are allowed for building.
|
||||
This is the list that's used for memory allocation and deallocation for the pieces. */
|
||||
cPieces m_AllPieces;
|
||||
|
||||
/** The pieces that are used as starting pieces.
|
||||
This list is not shared and the pieces need deallocation. */
|
||||
cPieces m_StartingPieces;
|
||||
|
||||
/** The map that has all pieces by their connector types
|
||||
The pieces are copies out of m_AllPieces and shouldn't be ever delete-d. */
|
||||
cPiecesMap m_PiecesByConnector;
|
||||
|
||||
|
||||
/** Adds the prefab to the m_PiecesByConnector map for all its connectors. */
|
||||
void AddToPerConnectorMap(cPrefab * a_Prefab);
|
||||
|
||||
// cPiecePool overrides:
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
|
||||
virtual cPieces GetStartingPieces(void) override;
|
||||
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override;
|
||||
virtual void Reset(void) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -9,9 +9,6 @@
|
||||
|
||||
|
||||
|
||||
/// How many ravines in each direction are generated for a given chunk. Must be an even number
|
||||
static const int NEIGHBORHOOD_SIZE = 8;
|
||||
|
||||
static const int NUM_RAVINE_POINTS = 4;
|
||||
|
||||
|
||||
@ -42,40 +39,38 @@ typedef std::vector<cRavDefPoint> cRavDefPoints;
|
||||
|
||||
|
||||
|
||||
class cStructGenRavines::cRavine
|
||||
class cStructGenRavines::cRavine :
|
||||
public cGridStructGen::cStructure
|
||||
{
|
||||
typedef cGridStructGen::cStructure super;
|
||||
|
||||
cRavDefPoints m_Points;
|
||||
|
||||
/// Generates the shaping defpoints for the ravine, based on the ravine block coords and noise
|
||||
|
||||
/** Generates the shaping defpoints for the ravine, based on the ravine block coords and noise */
|
||||
void GenerateBaseDefPoints(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
|
||||
|
||||
/// Refines (adds and smooths) defpoints from a_Src into a_Dst
|
||||
/** Refines (adds and smooths) defpoints from a_Src into a_Dst */
|
||||
void RefineDefPoints(const cRavDefPoints & a_Src, cRavDefPoints & a_Dst);
|
||||
|
||||
/// Does one round of smoothing, two passes of RefineDefPoints()
|
||||
/** Does one round of smoothing, two passes of RefineDefPoints() */
|
||||
void Smooth(void);
|
||||
|
||||
/// Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block
|
||||
/** Linearly interpolates the points so that the maximum distance between two neighbors is max 1 block */
|
||||
void FinishLinear(void);
|
||||
|
||||
public:
|
||||
// Coords for which the ravine was generated (not necessarily the center)
|
||||
int m_BlockX;
|
||||
int m_BlockZ;
|
||||
|
||||
cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise);
|
||||
|
||||
/// Carves the ravine into the chunk specified
|
||||
void ProcessChunk(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes,
|
||||
cChunkDef::HeightMap & a_HeightMap
|
||||
);
|
||||
|
||||
#ifdef _DEBUG
|
||||
/// Exports itself as a SVG line definition
|
||||
AString ExportAsSVG(int a_Color, int a_OffsetX = 0, int a_OffsetZ = 0) const;
|
||||
#endif // _DEBUG
|
||||
|
||||
protected:
|
||||
// cGridStructGen::cStructure overrides:
|
||||
virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override;
|
||||
} ;
|
||||
|
||||
|
||||
@ -86,6 +81,7 @@ public:
|
||||
// cStructGenRavines:
|
||||
|
||||
cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
|
||||
super(a_Seed, a_Size, a_Size, a_Size * 2, a_Size * 2, 100),
|
||||
m_Noise(a_Seed),
|
||||
m_Size(a_Size)
|
||||
{
|
||||
@ -95,139 +91,9 @@ cStructGenRavines::cStructGenRavines(int a_Seed, int a_Size) :
|
||||
|
||||
|
||||
|
||||
cStructGenRavines::~cStructGenRavines()
|
||||
cGridStructGen::cStructurePtr cStructGenRavines::CreateStructure(int a_OriginX, int a_OriginZ)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenRavines::ClearCache(void)
|
||||
{
|
||||
for (cRavines::const_iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - m_Cache[]
|
||||
m_Cache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenRavines::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int ChunkX = a_ChunkDesc.GetChunkX();
|
||||
int ChunkZ = a_ChunkDesc.GetChunkZ();
|
||||
cRavines Ravines;
|
||||
GetRavinesForChunk(ChunkX, ChunkZ, Ravines);
|
||||
for (cRavines::const_iterator itr = Ravines.begin(), end = Ravines.end(); itr != end; ++itr)
|
||||
{
|
||||
(*itr)->ProcessChunk(ChunkX, ChunkZ, a_ChunkDesc.GetBlockTypes(), a_ChunkDesc.GetHeightMap());
|
||||
} // for itr - Ravines[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGenRavines::cRavines & a_Ravines)
|
||||
{
|
||||
int BaseX = a_ChunkX * cChunkDef::Width / m_Size;
|
||||
int BaseZ = a_ChunkZ * cChunkDef::Width / m_Size;
|
||||
if (BaseX < 0)
|
||||
{
|
||||
--BaseX;
|
||||
}
|
||||
if (BaseZ < 0)
|
||||
{
|
||||
--BaseZ;
|
||||
}
|
||||
BaseX -= 4;
|
||||
BaseZ -= 4;
|
||||
|
||||
// Walk the cache, move each ravine that we want into a_Ravines:
|
||||
int StartX = BaseX * m_Size;
|
||||
int EndX = (BaseX + NEIGHBORHOOD_SIZE + 1) * m_Size;
|
||||
int StartZ = BaseZ * m_Size;
|
||||
int EndZ = (BaseZ + NEIGHBORHOOD_SIZE + 1) * m_Size;
|
||||
for (cRavines::iterator itr = m_Cache.begin(), end = m_Cache.end(); itr != end;)
|
||||
{
|
||||
if (
|
||||
((*itr)->m_BlockX >= StartX) && ((*itr)->m_BlockX < EndX) &&
|
||||
((*itr)->m_BlockZ >= StartZ) && ((*itr)->m_BlockZ < EndZ)
|
||||
)
|
||||
{
|
||||
// want
|
||||
a_Ravines.push_back(*itr);
|
||||
itr = m_Cache.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't want
|
||||
++itr;
|
||||
}
|
||||
} // for itr - m_Cache[]
|
||||
|
||||
for (int x = 0; x < NEIGHBORHOOD_SIZE; x++)
|
||||
{
|
||||
int RealX = (BaseX + x) * m_Size;
|
||||
for (int z = 0; z < NEIGHBORHOOD_SIZE; z++)
|
||||
{
|
||||
int RealZ = (BaseZ + z) * m_Size;
|
||||
bool Found = false;
|
||||
for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
|
||||
{
|
||||
if (((*itr)->m_BlockX == RealX) && ((*itr)->m_BlockZ == RealZ))
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Found)
|
||||
{
|
||||
a_Ravines.push_back(new cRavine(RealX, RealZ, m_Size, m_Noise));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a_Ravines into m_Cache to the beginning:
|
||||
cRavines RavinesCopy(a_Ravines);
|
||||
m_Cache.splice(m_Cache.begin(), RavinesCopy, RavinesCopy.begin(), RavinesCopy.end());
|
||||
|
||||
// Trim the cache if it's too long:
|
||||
if (m_Cache.size() > 100)
|
||||
{
|
||||
cRavines::iterator itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
for (cRavines::iterator end = m_Cache.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
itr = m_Cache.begin();
|
||||
std::advance(itr, 100);
|
||||
m_Cache.erase(itr, m_Cache.end());
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef _DEBUG
|
||||
// DEBUG: Export as SVG into a file specific for the chunk, for visual verification:
|
||||
AString SVG;
|
||||
SVG.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1024\" height = \"1024\">\n");
|
||||
for (cRavines::const_iterator itr = a_Ravines.begin(), end = a_Ravines.end(); itr != end; ++itr)
|
||||
{
|
||||
SVG.append((*itr)->ExportAsSVG(0, 512, 512));
|
||||
}
|
||||
SVG.append("</svg>\n");
|
||||
|
||||
AString fnam;
|
||||
Printf(fnam, "ravines\\%03d_%03d.svg", a_ChunkX, a_ChunkZ);
|
||||
cFile File(fnam, cFile::fmWrite);
|
||||
File.Write(SVG.c_str(), SVG.size());
|
||||
#endif // _DEBUG
|
||||
//*/
|
||||
return cStructurePtr(new cRavine(a_OriginX, a_OriginZ, m_Size, m_Noise));
|
||||
}
|
||||
|
||||
|
||||
@ -238,14 +104,13 @@ void cStructGenRavines::GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cStructGe
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cStructGenRavines::cRavine
|
||||
|
||||
cStructGenRavines::cRavine::cRavine(int a_BlockX, int a_BlockZ, int a_Size, cNoise & a_Noise) :
|
||||
m_BlockX(a_BlockX),
|
||||
m_BlockZ(a_BlockZ)
|
||||
cStructGenRavines::cRavine::cRavine(int a_OriginX, int a_OriginZ, int a_Size, cNoise & a_Noise) :
|
||||
super(a_OriginX, a_OriginZ)
|
||||
{
|
||||
// Calculate the ravine shape-defining points:
|
||||
GenerateBaseDefPoints(a_BlockX, a_BlockZ, a_Size, a_Noise);
|
||||
GenerateBaseDefPoints(a_OriginX, a_OriginZ, a_Size, a_Noise);
|
||||
|
||||
// Smooth the ravine. A two passes are needed:
|
||||
// Smooth the ravine. Two passes are needed:
|
||||
Smooth();
|
||||
Smooth();
|
||||
|
||||
@ -263,8 +128,8 @@ void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_Block
|
||||
a_Size = (512 + ((a_Noise.IntNoise3DInt(19 * a_BlockX, 11 * a_BlockZ, a_BlockX + a_BlockZ) / 17) % 512)) * a_Size / 1024;
|
||||
|
||||
// The complete offset of the ravine from its cellpoint, up to 2 * a_Size in each direction
|
||||
int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
|
||||
int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * m_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
|
||||
int OffsetX = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 0) / 9) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * a_BlockZ, 1000) / 7) % (2 * a_Size)) - 2 * a_Size) / 2;
|
||||
int OffsetZ = (((a_Noise.IntNoise3DInt(50 * a_BlockX, 30 * a_BlockZ, 2000) / 7) % (2 * a_Size)) + ((a_Noise.IntNoise3DInt(30 * a_BlockX, 50 * a_BlockZ, 3000) / 9) % (2 * a_Size)) - 2 * a_Size) / 2;
|
||||
int CenterX = a_BlockX + OffsetX;
|
||||
int CenterZ = a_BlockZ + OffsetZ;
|
||||
|
||||
@ -429,15 +294,15 @@ AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int
|
||||
|
||||
// Base point highlight:
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX - 5, a_OffsetZ + m_BlockZ, a_OffsetX + m_BlockX + 5, a_OffsetZ + m_BlockZ
|
||||
a_OffsetX + m_OriginX - 5, a_OffsetZ + m_OriginZ, a_OffsetX + m_OriginX + 5, a_OffsetZ + m_OriginZ
|
||||
);
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#ff0000;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ - 5, a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ + 5
|
||||
a_OffsetX + m_OriginX, a_OffsetZ + m_OriginZ - 5, a_OffsetX + m_OriginX, a_OffsetZ + m_OriginZ + 5
|
||||
);
|
||||
|
||||
// A gray line from the base point to the first point of the ravine, for identification:
|
||||
AppendPrintf(SVG, "<path style=\"fill:none;stroke:#cfcfcf;stroke-width:1px;\"\nd=\"M %d,%d L %d,%d\"/>\n",
|
||||
a_OffsetX + m_BlockX, a_OffsetZ + m_BlockZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
|
||||
a_OffsetX + m_OriginX, a_OffsetZ + m_OriginZ, a_OffsetX + m_Points.front().m_BlockX, a_OffsetZ + m_Points.front().m_BlockZ
|
||||
);
|
||||
|
||||
// Offset guides:
|
||||
@ -461,14 +326,10 @@ AString cStructGenRavines::cRavine::ExportAsSVG(int a_Color, int a_OffsetX, int
|
||||
|
||||
|
||||
|
||||
void cStructGenRavines::cRavine::ProcessChunk(
|
||||
int a_ChunkX, int a_ChunkZ,
|
||||
cChunkDef::BlockTypes & a_BlockTypes,
|
||||
cChunkDef::HeightMap & a_HeightMap
|
||||
)
|
||||
void cStructGenRavines::cRavine::DrawIntoChunk(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int BlockStartX = a_ChunkX * cChunkDef::Width;
|
||||
int BlockStartZ = a_ChunkZ * cChunkDef::Width;
|
||||
int BlockStartX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
|
||||
int BlockStartZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
|
||||
int BlockEndX = BlockStartX + cChunkDef::Width;
|
||||
int BlockEndZ = BlockStartZ + cChunkDef::Width;
|
||||
for (cRavDefPoints::const_iterator itr = m_Points.begin(), end = m_Points.end(); itr != end; ++itr)
|
||||
@ -494,7 +355,7 @@ void cStructGenRavines::cRavine::ProcessChunk(
|
||||
// DEBUG: Make the ravine shapepoints visible on a single layer (so that we can see with Minutor what's going on)
|
||||
if ((DifX + x == 0) && (DifZ + z == 0))
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, 4, z, E_BLOCK_LAPIS_ORE);
|
||||
a_ChunkDesc.SetBlockType(x, 4, z, E_BLOCK_LAPIS_ORE);
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
@ -504,7 +365,7 @@ void cStructGenRavines::cRavine::ProcessChunk(
|
||||
int Top = std::min(itr->m_Top, (int)(cChunkDef::Height)); // Stupid gcc needs int cast
|
||||
for (int y = std::max(itr->m_Bottom, 1); y <= Top; y++)
|
||||
{
|
||||
switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z))
|
||||
switch (a_ChunkDesc.GetBlockType(x, y, z))
|
||||
{
|
||||
// Only carve out these specific block types
|
||||
case E_BLOCK_DIRT:
|
||||
@ -522,7 +383,7 @@ void cStructGenRavines::cRavine::ProcessChunk(
|
||||
case E_BLOCK_REDSTONE_ORE:
|
||||
case E_BLOCK_REDSTONE_ORE_GLOWING:
|
||||
{
|
||||
cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
|
||||
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_AIR);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
@ -17,28 +17,22 @@
|
||||
|
||||
|
||||
class cStructGenRavines :
|
||||
public cFinishGen
|
||||
public cGridStructGen
|
||||
{
|
||||
typedef cGridStructGen super;
|
||||
|
||||
public:
|
||||
cStructGenRavines(int a_Seed, int a_Size);
|
||||
~cStructGenRavines();
|
||||
|
||||
protected:
|
||||
class cRavine; // fwd: Ravines.cpp
|
||||
typedef std::list<cRavine *> cRavines;
|
||||
|
||||
cNoise m_Noise;
|
||||
int m_Size; // Max size, in blocks, of the ravines generated
|
||||
cRavines m_Cache;
|
||||
cNoise m_Noise;
|
||||
int m_Size; // Max size, in blocks, of the ravines generated
|
||||
|
||||
/// Clears everything from the cache
|
||||
void ClearCache(void);
|
||||
|
||||
/// Returns all ravines that *may* intersect the given chunk. All the ravines are valid until the next call to this function.
|
||||
void GetRavinesForChunk(int a_ChunkX, int a_ChunkZ, cRavines & a_Ravines);
|
||||
|
||||
// cFinishGen override:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
// cGridStructGen overrides:
|
||||
virtual cStructurePtr CreateStructure(int a_OriginX, int a_OriginZ) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/// 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:
|
||||
|
@ -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)
|
||||
|
@ -142,6 +142,8 @@ public:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
a_Player->GetStatManager().AddValue(statTreasureFished, 1);
|
||||
}
|
||||
else if (ItemCategory <= 14) // Junk 10%
|
||||
{
|
||||
@ -190,6 +192,8 @@ public:
|
||||
{
|
||||
Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK));
|
||||
}
|
||||
|
||||
a_Player->GetStatManager().AddValue(statJunkFished, 1);
|
||||
}
|
||||
else // Fish
|
||||
{
|
||||
@ -210,6 +214,8 @@ public:
|
||||
{
|
||||
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH));
|
||||
}
|
||||
|
||||
a_Player->GetStatManager().AddValue(statFishCaught, 1);
|
||||
}
|
||||
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(*a_Player, Drops))
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -2,7 +2,9 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Wither.h"
|
||||
|
||||
#include "../World.h"
|
||||
#include "../Entities/Player.h"
|
||||
|
||||
|
||||
|
||||
@ -100,3 +102,35 @@ void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer)
|
||||
|
||||
|
||||
|
||||
|
||||
void cWither::KilledBy(cEntity * a_Killer)
|
||||
{
|
||||
super::KilledBy(a_Killer);
|
||||
|
||||
class cPlayerCallback : public cPlayerListCallback
|
||||
{
|
||||
Vector3f m_Pos;
|
||||
|
||||
virtual bool Item(cPlayer * a_Player)
|
||||
{
|
||||
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
|
||||
double Dist = (a_Player->GetPosition() - m_Pos).Length();
|
||||
if (Dist < 50.0)
|
||||
{
|
||||
// If player is close, award achievement
|
||||
a_Player->AwardAchievement(achKillWither);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
|
||||
} PlayerCallback(GetPosition());
|
||||
|
||||
m_World->ForEachPlayer(PlayerCallback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
|
||||
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
|
||||
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
|
||||
virtual void KilledBy(cEntity * a_Killer) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -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,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
|
||||
|
@ -13,57 +13,58 @@ 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
|
||||
|
||||
/* Type | Name */
|
||||
cStatInfo(statGamesQuit, "stat.leaveGame"),
|
||||
cStatInfo(statMinutesPlayed, "stat.playOneMinute"),
|
||||
cStatInfo(statDistWalked, "stat.walkOnCm"),
|
||||
cStatInfo(statDistWalked, "stat.walkOneCm"),
|
||||
cStatInfo(statDistSwum, "stat.swimOneCm"),
|
||||
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());
|
||||
|
||||
@ -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