diff options
Diffstat (limited to 'src/shared')
86 files changed, 2381 insertions, 2 deletions
diff --git a/src/shared/Auth/AuthCrypt.cpp b/src/shared/Auth/AuthCrypt.cpp index 56d70d4c14e..e8126ad9f73 100644 --- a/src/shared/Auth/AuthCrypt.cpp +++ b/src/shared/Auth/AuthCrypt.cpp @@ -17,48 +17,65 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "AuthCrypt.h" #include "Hmac.h" #include "Log.h" #include "BigNumber.h" + AuthCrypt::AuthCrypt() { _initialized = false; } + AuthCrypt::~AuthCrypt() { + } + void AuthCrypt::Init(BigNumber *K) { uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0x22, 0xBE, 0xE5, 0xCF, 0xBB, 0x07, 0x64, 0xD9, 0x00, 0x45, 0x1B, 0xD0, 0x24, 0xB8, 0xD5, 0x45 }; HmacHash serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey); uint8 *encryptHash = serverEncryptHmac.ComputeHash(K); + uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xF4, 0x66, 0x31, 0x59, 0xFC, 0x83, 0x6E, 0x31, 0x31, 0x02, 0x51, 0xD5, 0x44, 0x31, 0x67, 0x98 }; HmacHash clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey); uint8 *decryptHash = clientDecryptHmac.ComputeHash(K); + //SARC4 _serverDecrypt(encryptHash); _clientDecrypt.Init(decryptHash); _serverEncrypt.Init(encryptHash); //SARC4 _clientEncrypt(decryptHash); + uint8 syncBuf[1024]; + memset(syncBuf, 0, 1024); + _serverEncrypt.UpdateData(1024, syncBuf); //_clientEncrypt.UpdateData(1024, syncBuf); + memset(syncBuf, 0, 1024); + //_serverDecrypt.UpdateData(1024, syncBuf); _clientDecrypt.UpdateData(1024, syncBuf); + _initialized = true; } + void AuthCrypt::DecryptRecv(uint8 *data, size_t len) { if (!_initialized) return; + _clientDecrypt.UpdateData(len, data); } + void AuthCrypt::EncryptSend(uint8 *data, size_t len) { if (!_initialized) return; + _serverEncrypt.UpdateData(len, data); } diff --git a/src/shared/Auth/AuthCrypt.h b/src/shared/Auth/AuthCrypt.h index 7f885e563ab..226fde018ae 100644 --- a/src/shared/Auth/AuthCrypt.h +++ b/src/shared/Auth/AuthCrypt.h @@ -17,20 +17,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _AUTHCRYPT_H #define _AUTHCRYPT_H + #include <Common.h> #include "SARC4.h" + class BigNumber; + class AuthCrypt { public: AuthCrypt(); ~AuthCrypt(); + void Init(BigNumber *K); void DecryptRecv(uint8 *, size_t); void EncryptSend(uint8 *, size_t); + bool IsInitialized() { return _initialized; } + private: SARC4 _clientDecrypt; SARC4 _serverEncrypt; diff --git a/src/shared/Auth/BigNumber.cpp b/src/shared/Auth/BigNumber.cpp index 837b75804df..303687c266c 100644 --- a/src/shared/Auth/BigNumber.cpp +++ b/src/shared/Auth/BigNumber.cpp @@ -17,143 +17,182 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Auth/BigNumber.h" #include <openssl/bn.h> #include <algorithm> + BigNumber::BigNumber() { _bn = BN_new(); _array = NULL; } + BigNumber::BigNumber(const BigNumber &bn) { _bn = BN_dup(bn._bn); _array = NULL; } + BigNumber::BigNumber(uint32 val) { _bn = BN_new(); BN_set_word(_bn, val); _array = NULL; } + BigNumber::~BigNumber() { BN_free(_bn); if(_array) delete[] _array; } + void BigNumber::SetDword(uint32 val) { BN_set_word(_bn, val); } + void BigNumber::SetQword(uint64 val) { BN_add_word(_bn, (uint32)(val >> 32)); BN_lshift(_bn, _bn, 32); BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF)); } + void BigNumber::SetBinary(const uint8 *bytes, int len) { uint8 t[1000]; for (int i = 0; i < len; i++) t[i] = bytes[len - 1 - i]; BN_bin2bn(t, len, _bn); } + void BigNumber::SetHexStr(const char *str) { BN_hex2bn(&_bn, str); } + void BigNumber::SetRand(int numbits) { BN_rand(_bn, numbits, 0, 1); } + BigNumber BigNumber::operator=(const BigNumber &bn) { BN_copy(_bn, bn._bn); return *this; } + BigNumber BigNumber::operator+=(const BigNumber &bn) { BN_add(_bn, _bn, bn._bn); return *this; } + BigNumber BigNumber::operator-=(const BigNumber &bn) { BN_sub(_bn, _bn, bn._bn); return *this; } + BigNumber BigNumber::operator*=(const BigNumber &bn) { BN_CTX *bnctx; + bnctx = BN_CTX_new(); BN_mul(_bn, _bn, bn._bn, bnctx); BN_CTX_free(bnctx); + return *this; } + BigNumber BigNumber::operator/=(const BigNumber &bn) { BN_CTX *bnctx; + bnctx = BN_CTX_new(); BN_div(_bn, NULL, _bn, bn._bn, bnctx); BN_CTX_free(bnctx); + return *this; } + BigNumber BigNumber::operator%=(const BigNumber &bn) { BN_CTX *bnctx; + bnctx = BN_CTX_new(); BN_mod(_bn, _bn, bn._bn, bnctx); BN_CTX_free(bnctx); + return *this; } + BigNumber BigNumber::Exp(const BigNumber &bn) { BigNumber ret; BN_CTX *bnctx; + bnctx = BN_CTX_new(); BN_exp(ret._bn, _bn, bn._bn, bnctx); BN_CTX_free(bnctx); + return ret; } + BigNumber BigNumber::ModExp(const BigNumber &bn1, const BigNumber &bn2) { BigNumber ret; BN_CTX *bnctx; + bnctx = BN_CTX_new(); BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx); BN_CTX_free(bnctx); + return ret; } + int BigNumber::GetNumBytes(void) { return BN_num_bytes(_bn); } + uint32 BigNumber::AsDword() { return (uint32)BN_get_word(_bn); } + bool BigNumber::isZero() const { return BN_is_zero(_bn)!=0; } + uint8 *BigNumber::AsByteArray(int minSize) { int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes(); + if (_array) { delete[] _array; _array = NULL; } _array = new uint8[length]; + // If we need more bytes than length of BigNumber set the rest to 0 if (length > GetNumBytes()) memset((void*)_array, 0, length); + BN_bn2bin(_bn, (unsigned char *)_array); + std::reverse(_array, _array + length); + return _array; } + const char *BigNumber::AsHexStr() { return BN_bn2hex(_bn); } + const char *BigNumber::AsDecStr() { return BN_bn2dec(_bn); diff --git a/src/shared/Auth/BigNumber.h b/src/shared/Auth/BigNumber.h index 898f53c2a05..f1b3a0beda2 100644 --- a/src/shared/Auth/BigNumber.h +++ b/src/shared/Auth/BigNumber.h @@ -17,10 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _AUTH_BIGNUMBER_H #define _AUTH_BIGNUMBER_H + #include "Common.h" + struct bignum_st; + class BigNumber { public: @@ -28,12 +32,16 @@ class BigNumber BigNumber(const BigNumber &bn); BigNumber(uint32); ~BigNumber(); + void SetDword(uint32); void SetQword(uint64); void SetBinary(const uint8 *bytes, int len); void SetHexStr(const char *str); + void SetRand(int numbits); + BigNumber operator=(const BigNumber &bn); + BigNumber operator+=(const BigNumber &bn); BigNumber operator+(const BigNumber &bn) { @@ -64,15 +72,22 @@ class BigNumber BigNumber t(*this); return t %= bn; } + bool isZero() const; + BigNumber ModExp(const BigNumber &bn1, const BigNumber &bn2); BigNumber Exp(const BigNumber &); + int GetNumBytes(void); + struct bignum_st *BN() { return _bn; } + uint32 AsDword(); uint8* AsByteArray(int minSize = 0); + const char *AsHexStr(); const char *AsDecStr(); + private: struct bignum_st *_bn; uint8 *_array; diff --git a/src/shared/Auth/Hmac.cpp b/src/shared/Auth/Hmac.cpp index ed68ce8c5df..985b4fb9a56 100644 --- a/src/shared/Auth/Hmac.cpp +++ b/src/shared/Auth/Hmac.cpp @@ -17,32 +17,40 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Auth/Hmac.h" #include "BigNumber.h" + HmacHash::HmacHash(uint32 len, uint8 *seed) { ASSERT(len == SEED_KEY_SIZE); + HMAC_CTX_init(&m_ctx); HMAC_Init_ex(&m_ctx, seed, SEED_KEY_SIZE, EVP_sha1(), NULL); } + HmacHash::~HmacHash() { HMAC_CTX_cleanup(&m_ctx); } + void HmacHash::UpdateBigNumber(BigNumber *bn) { UpdateData(bn->AsByteArray(), bn->GetNumBytes()); } + void HmacHash::UpdateData(const uint8 *data, int length) { HMAC_Update(&m_ctx, data, length); } + void HmacHash::Finalize() { uint32 length = 0; HMAC_Final(&m_ctx, (uint8*)m_digest, &length); ASSERT(length == SHA_DIGEST_LENGTH) } + uint8 *HmacHash::ComputeHash(BigNumber *bn) { HMAC_Update(&m_ctx, bn->AsByteArray(), bn->GetNumBytes()); diff --git a/src/shared/Auth/Hmac.h b/src/shared/Auth/Hmac.h index a039617f801..76a302d68de 100644 --- a/src/shared/Auth/Hmac.h +++ b/src/shared/Auth/Hmac.h @@ -17,13 +17,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _AUTH_HMAC_H #define _AUTH_HMAC_H + #include "Common.h" #include <openssl/hmac.h> #include <openssl/sha.h> + class BigNumber; + #define SEED_KEY_SIZE 16 + class HmacHash { public: diff --git a/src/shared/Auth/SARC4.cpp b/src/shared/Auth/SARC4.cpp index 5687deece59..f59bb7f0c53 100644 --- a/src/shared/Auth/SARC4.cpp +++ b/src/shared/Auth/SARC4.cpp @@ -15,14 +15,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Auth/SARC4.h" #include <openssl/sha.h> + SARC4::SARC4() { EVP_CIPHER_CTX_init(&m_ctx); EVP_EncryptInit_ex(&m_ctx, EVP_rc4(), NULL, NULL, NULL); EVP_CIPHER_CTX_set_key_length(&m_ctx, SHA_DIGEST_LENGTH); } + SARC4::SARC4(uint8 *seed) { EVP_CIPHER_CTX_init(&m_ctx); @@ -30,14 +33,17 @@ SARC4::SARC4(uint8 *seed) EVP_CIPHER_CTX_set_key_length(&m_ctx, SHA_DIGEST_LENGTH); EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL); } + SARC4::~SARC4() { EVP_CIPHER_CTX_cleanup(&m_ctx); } + void SARC4::Init(uint8 *seed) { EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL); } + void SARC4::UpdateData(int len, uint8 *data) { int outlen = 0; diff --git a/src/shared/Auth/SARC4.h b/src/shared/Auth/SARC4.h index 350b4ad51e6..3f15328d6cb 100644 --- a/src/shared/Auth/SARC4.h +++ b/src/shared/Auth/SARC4.h @@ -15,10 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _AUTH_SARC4_H #define _AUTH_SARC4_H + #include "Common.h" #include <openssl/evp.h> + class SARC4 { public: diff --git a/src/shared/Auth/Sha1.cpp b/src/shared/Auth/Sha1.cpp index 1cb1ea5fb40..802f1bbcdff 100644 --- a/src/shared/Auth/Sha1.cpp +++ b/src/shared/Auth/Sha1.cpp @@ -17,29 +17,36 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Auth/Sha1.h" #include "Auth/BigNumber.h" #include <stdarg.h> + Sha1Hash::Sha1Hash() { SHA1_Init(&mC); } + Sha1Hash::~Sha1Hash() { SHA1_Init(&mC); } + void Sha1Hash::UpdateData(const uint8 *dta, int len) { SHA1_Update(&mC, dta, len); } + void Sha1Hash::UpdateData(const std::string &str) { UpdateData((uint8 const*)str.c_str(), str.length()); } + void Sha1Hash::UpdateBigNumbers(BigNumber *bn0, ...) { va_list v; BigNumber *bn; + va_start(v, bn0); bn = bn0; while (bn) @@ -49,10 +56,12 @@ void Sha1Hash::UpdateBigNumbers(BigNumber *bn0, ...) } va_end(v); } + void Sha1Hash::Initialize() { SHA1_Init(&mC); } + void Sha1Hash::Finalize(void) { SHA1_Final(mDigest, &mC); diff --git a/src/shared/Auth/Sha1.h b/src/shared/Auth/Sha1.h index 9b6dad84234..bd2b1afa876 100644 --- a/src/shared/Auth/Sha1.h +++ b/src/shared/Auth/Sha1.h @@ -17,25 +17,34 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _AUTH_SHA1_H #define _AUTH_SHA1_H + #include "Common.h" #include <openssl/sha.h> #include <openssl/crypto.h> + class BigNumber; + class Sha1Hash { public: Sha1Hash(); ~Sha1Hash(); + void UpdateFinalizeBigNumbers(BigNumber *bn0, ...); void UpdateBigNumbers(BigNumber *bn0, ...); + void UpdateData(const uint8 *dta, int len); void UpdateData(const std::string &str); + void Initialize(); void Finalize(); + uint8 *GetDigest(void) { return mDigest; }; int GetLength(void) { return SHA_DIGEST_LENGTH; }; + private: SHA_CTX mC; uint8 mDigest[SHA_DIGEST_LENGTH]; diff --git a/src/shared/ByteBuffer.h b/src/shared/ByteBuffer.h index 9f454425327..b22dbdee729 100644 --- a/src/shared/ByteBuffer.h +++ b/src/shared/ByteBuffer.h @@ -17,12 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _BYTEBUFFER_H #define _BYTEBUFFER_H + #include "Common.h" #include "Errors.h" #include "Log.h" #include "Utilities/ByteConverter.h" + class ByteBufferException { public: @@ -31,6 +34,7 @@ class ByteBufferException { PrintPosError(); } + void PrintPosError() const { sLog.outError("ERROR: Attempted to %s in ByteBuffer (pos: " SIZEFMTD " size: "SIZEFMTD") value with size: " SIZEFMTD, @@ -42,157 +46,188 @@ class ByteBufferException size_t esize; size_t size; }; + class ByteBuffer { public: const static size_t DEFAULT_SIZE = 0x1000; + // constructor ByteBuffer(): _rpos(0), _wpos(0) { _storage.reserve(DEFAULT_SIZE); } + // constructor ByteBuffer(size_t res): _rpos(0), _wpos(0) { _storage.reserve(res); } + // copy constructor ByteBuffer(const ByteBuffer &buf): _rpos(buf._rpos), _wpos(buf._wpos), _storage(buf._storage) { } + void clear() { _storage.clear(); _rpos = _wpos = 0; } + template <typename T> void append(T value) { EndianConvert(value); append((uint8 *)&value, sizeof(value)); } + template <typename T> void put(size_t pos,T value) { EndianConvert(value); put(pos,(uint8 *)&value,sizeof(value)); } + ByteBuffer &operator<<(uint8 value) { append<uint8>(value); return *this; } + ByteBuffer &operator<<(uint16 value) { append<uint16>(value); return *this; } + ByteBuffer &operator<<(uint32 value) { append<uint32>(value); return *this; } + ByteBuffer &operator<<(uint64 value) { append<uint64>(value); return *this; } + // signed as in 2e complement ByteBuffer &operator<<(int8 value) { append<int8>(value); return *this; } + ByteBuffer &operator<<(int16 value) { append<int16>(value); return *this; } + ByteBuffer &operator<<(int32 value) { append<int32>(value); return *this; } + ByteBuffer &operator<<(int64 value) { append<int64>(value); return *this; } + // floating points ByteBuffer &operator<<(float value) { append<float>(value); return *this; } + ByteBuffer &operator<<(double value) { append<double>(value); return *this; } + ByteBuffer &operator<<(const std::string &value) { append((uint8 const *)value.c_str(), value.length()); append((uint8)0); return *this; } + ByteBuffer &operator<<(const char *str) { append((uint8 const *)str, str ? strlen(str) : 0); append((uint8)0); return *this; } + ByteBuffer &operator>>(bool &value) { value = read<char>() > 0 ? true : false; return *this; } + ByteBuffer &operator>>(uint8 &value) { value = read<uint8>(); return *this; } + ByteBuffer &operator>>(uint16 &value) { value = read<uint16>(); return *this; } + ByteBuffer &operator>>(uint32 &value) { value = read<uint32>(); return *this; } + ByteBuffer &operator>>(uint64 &value) { value = read<uint64>(); return *this; } + //signed as in 2e complement ByteBuffer &operator>>(int8 &value) { value = read<int8>(); return *this; } + ByteBuffer &operator>>(int16 &value) { value = read<int16>(); return *this; } + ByteBuffer &operator>>(int32 &value) { value = read<int32>(); return *this; } + ByteBuffer &operator>>(int64 &value) { value = read<int64>(); return *this; } + ByteBuffer &operator>>(float &value) { value = read<float>(); return *this; } + ByteBuffer &operator>>(double &value) { value = read<double>(); return *this; } + ByteBuffer &operator>>(std::string& value) { value.clear(); @@ -205,36 +240,45 @@ class ByteBuffer } return *this; } + uint8 operator[](size_t pos) const { return read<uint8>(pos); } + size_t rpos() const { return _rpos; } + size_t rpos(size_t rpos_) { _rpos = rpos_; return _rpos; } + size_t wpos() const { return _wpos; } + size_t wpos(size_t wpos_) { _wpos = wpos_; return _wpos; } + template<typename T> void read_skip() { read_skip(sizeof(T)); } + void read_skip(size_t skip) { if(_rpos + skip > size()) throw ByteBufferException(false, _rpos, skip, size()); _rpos += skip; } + template <typename T> T read() { T r = read<T>(_rpos); _rpos += sizeof(T); return r; } + template <typename T> T read(size_t pos) const { if(pos + sizeof(T) > size()) @@ -243,6 +287,7 @@ class ByteBuffer EndianConvert(val); return val; } + void read(uint8 *dest, size_t len) { if(_rpos + len > size()) @@ -250,67 +295,85 @@ class ByteBuffer memcpy(dest, &_storage[_rpos], len); _rpos += len; } + bool readPackGUID(uint64& guid) { if(rpos() + 1 > size()) return false; + guid = 0; + uint8 guidmark = 0; (*this) >> guidmark; + for(int i = 0; i < 8; ++i) { if(guidmark & (uint8(1) << i)) { if(rpos() + 1 > size()) return false; + uint8 bit; (*this) >> bit; guid |= (uint64(bit) << (i * 8)); } } + return true; } + const uint8 *contents() const { return &_storage[0]; } + size_t size() const { return _storage.size(); } bool empty() const { return _storage.empty(); } + void resize(size_t newsize) { _storage.resize(newsize); _rpos = 0; _wpos = size(); } + void reserve(size_t ressize) { if (ressize > size()) _storage.reserve(ressize); } + void append(const std::string& str) { append((uint8 const*)str.c_str(), str.size() + 1); } + void append(const char *src, size_t cnt) { return append((const uint8 *)src, cnt); } + template<class T> void append(const T *src, size_t cnt) { return append((const uint8 *)src, cnt * sizeof(T)); } + void append(const uint8 *src, size_t cnt) { if (!cnt) return; + ASSERT(size() < 10000000); + if (_storage.size() < _wpos + cnt) _storage.resize(_wpos + cnt); memcpy(&_storage[_wpos], src, cnt); _wpos += cnt; } + void append(const ByteBuffer& buffer) { if(buffer.wpos()) append(buffer.contents(), buffer.wpos()); } + // can be used in SMSG_MONSTER_MOVE opcode void appendPackXYZ(float x, float y, float z) { @@ -320,10 +383,12 @@ class ByteBuffer packed |= ((int)(z / 0.25f) & 0x3FF) << 22; *this << packed; } + void appendPackGUID(uint64 guid) { if (_storage.size() < _wpos + sizeof(guid) + 1) _storage.resize(_wpos + sizeof(guid) + 1); + size_t mask_position = wpos(); *this << uint8(0); for(uint8 i = 0; i < 8; ++i) @@ -333,39 +398,48 @@ class ByteBuffer _storage[mask_position] |= uint8(1 << i); *this << uint8(guid & 0xFF); } + guid >>= 8; } } + void put(size_t pos, const uint8 *src, size_t cnt) { if(pos + cnt > size()) throw ByteBufferException(true, pos, cnt, size()); memcpy(&_storage[pos], src, cnt); } + void print_storage() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; + sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); for(uint32 i = 0; i < size(); ++i) sLog.outDebugInLine("%u - ", read<uint8>(i) ); sLog.outDebug(" "); } + void textlike() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; + sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); for(uint32 i = 0; i < size(); ++i) sLog.outDebugInLine("%c", read<uint8>(i) ); sLog.outDebug(" "); } + void hexlike() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; + uint32 j = 1, k = 1; sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); + for(uint32 i = 0; i < size(); ++i) { if ((i == (j * 8)) && ((i != (k * 16)))) @@ -385,13 +459,16 @@ class ByteBuffer if (read<uint8>(i) < 0x10) { sLog.outDebugInLine("\n"); + sLog.outDebugInLine("0%X ", read<uint8>(i) ); } else { sLog.outDebugInLine("\n"); + sLog.outDebugInLine("%X ", read<uint8>(i) ); } + ++k; ++j; } @@ -409,10 +486,12 @@ class ByteBuffer } sLog.outDebugInLine("\n"); } + protected: size_t _rpos, _wpos; std::vector<uint8> _storage; }; + template <typename T> inline ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v) { @@ -423,6 +502,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v) } return b; } + template <typename T> inline ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v) { @@ -437,6 +517,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v) } return b; } + template <typename T> inline ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v) { @@ -447,6 +528,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v) } return b; } + template <typename T> inline ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v) { @@ -461,6 +543,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v) } return b; } + template <typename K, typename V> inline ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m) { @@ -471,6 +554,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m) } return b; } + template <typename K, typename V> inline ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m) { @@ -486,6 +570,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m) } return b; } + // TODO: Make a ByteBuffer.cpp and move all this inlining to it. template<> inline std::string ByteBuffer::read<std::string>() { @@ -493,17 +578,20 @@ template<> inline std::string ByteBuffer::read<std::string>() *this >> tmp; return tmp; } + template<> inline void ByteBuffer::read_skip<char*>() { std::string temp; *this >> temp; } + template<> inline void ByteBuffer::read_skip<char const*>() { read_skip<char*>(); } + template<> inline void ByteBuffer::read_skip<std::string>() { diff --git a/src/shared/Common.cpp b/src/shared/Common.cpp index 17bd0a90174..27ae9184d69 100644 --- a/src/shared/Common.cpp +++ b/src/shared/Common.cpp @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" + char const* localeNames[MAX_LOCALE] = { "enUS", "koKR", @@ -29,11 +31,13 @@ char const* localeNames[MAX_LOCALE] = { "esMX", "ruRU" }; + LocaleConstant GetLocaleByName(const std::string& name) { for(uint32 i = 0; i < MAX_LOCALE; ++i) if(name==localeNames[i]) return LocaleConstant(i); + return LOCALE_enUS; // including enGB case } diff --git a/src/shared/Common.h b/src/shared/Common.h index bfe243b34ab..8c948fb1d6d 100644 --- a/src/shared/Common.h +++ b/src/shared/Common.h @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef TRINITYCORE_COMMON_H #define TRINITYCORE_COMMON_H + // config.h needs to be included 1st // TODO this thingy looks like hack ,but its not, need to // make separate header however, because It makes mess here. @@ -56,7 +58,9 @@ #undef PACKAGE_VERSION #undef VERSION #endif //HAVE_CONFIG_H + #include "Platform/Define.h" + #if COMPILER == COMPILER_MICROSOFT # pragma warning(disable:4996) // 'function': was declared deprecated #ifndef __SHOW_STUPID_WARNINGS__ @@ -71,6 +75,7 @@ # pragma warning(disable:4522) //warning when class has 2 constructors #endif // __SHOW_STUPID_WARNINGS__ #endif // __GNUC__ + #include "Utilities/UnorderedMap.h" #include <stdio.h> #include <stdlib.h> @@ -80,11 +85,13 @@ #include <errno.h> #include <signal.h> #include <assert.h> + #if PLATFORM == PLATFORM_WINDOWS #define STRCASECMP stricmp #else #define STRCASECMP strcasecmp #endif + #include <set> #include <list> #include <string> @@ -92,13 +99,16 @@ #include <queue> #include <sstream> #include <algorithm> + #include "LockedQueue.h" #include "Threading.h" + #include <ace/Basic_Types.h> #include <ace/Guard_T.h> #include <ace/RW_Thread_Mutex.h> #include <ace/Thread_Mutex.h> + #if PLATFORM == PLATFORM_WINDOWS # define FD_SETSIZE 4096 # include <ace/config-all.h> @@ -115,8 +125,11 @@ # include <unistd.h> # include <netdb.h> #endif + #if COMPILER == COMPILER_MICROSOFT + #include <float.h> + #define I32FMT "%08I32X" #define I64FMT "%016I64X" #define snprintf _snprintf @@ -124,20 +137,30 @@ #define vsnprintf _vsnprintf #define strdup _strdup #define finite(X) _finite(X) + #else + #define stricmp strcasecmp #define strnicmp strncasecmp #define I32FMT "%08X" #define I64FMT "%016llX" + #endif + #define UI64FMTD ACE_UINT64_FORMAT_SPECIFIER #define UI64LIT(N) ACE_UINT64_LITERAL(N) + #define SI64FMTD ACE_INT64_FORMAT_SPECIFIER #define SI64LIT(N) ACE_INT64_LITERAL(N) + #define SIZEFMTD ACE_SIZE_T_FORMAT_SPECIFIER + inline float finiteAlways(float f) { return finite(f) ? f : 0.0f; } + #define atol(a) strtoul( a, NULL, 10) + #define STRINGIZE(a) #a + enum TimeConstants { MINUTE = 60, @@ -147,6 +170,7 @@ enum TimeConstants YEAR = MONTH*12, IN_MILISECONDS = 1000 }; + enum AccountTypes { SEC_PLAYER = 0, @@ -155,6 +179,7 @@ enum AccountTypes SEC_ADMINISTRATOR = 3, SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also }; + enum LocaleConstant { LOCALE_enUS = 0, @@ -167,18 +192,25 @@ enum LocaleConstant LOCALE_esMX = 7, LOCALE_ruRU = 8 }; + #define MAX_LOCALE 9 + extern char const* localeNames[MAX_LOCALE]; + LocaleConstant GetLocaleByName(const std::string& name); + // we always use stdlibc++ std::max/std::min, undefine some not C++ standard defines (Win API and some other platforms) #ifdef max #undef max #endif + #ifdef min #undef min #endif + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif + #endif diff --git a/src/shared/Config/Config.cpp b/src/shared/Config/Config.cpp index 5636e17fd6d..b56b804b50a 100644 --- a/src/shared/Config/Config.cpp +++ b/src/shared/Config/Config.cpp @@ -17,38 +17,49 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "ConfigEnv.h" #include "Policies/SingletonImp.h" + INSTANTIATE_SINGLETON_1(Config); + Config::Config() : mIgnoreCase(true), mConf(NULL) { } + Config::~Config() { delete mConf; } + bool Config::SetSource(const char *file, bool ignorecase) { mIgnoreCase = ignorecase; mFilename = file; + return Reload(); } + bool Config::Reload() { delete mConf; + mConf = new DOTCONFDocument(mIgnoreCase ? DOTCONFDocument::CASEINSENSETIVE : DOTCONFDocument::CASESENSETIVE); + if (mConf->setContent(mFilename.c_str()) == -1) { delete mConf; mConf = NULL; return false; } + return true; } + std::string Config::GetStringDefault(const char * name, std::string def) { if(!mConf) @@ -58,6 +69,7 @@ std::string Config::GetStringDefault(const char * name, std::string def) return std::string(def); return std::string(node->getValue()); }; + bool Config::GetBoolDefault(const char * name, const bool def) { if(!mConf) @@ -73,6 +85,7 @@ bool Config::GetBoolDefault(const char * name, const bool def) else return false; }; + int32 Config::GetIntDefault(const char * name, const int32 def) { if(!mConf) @@ -82,6 +95,7 @@ int32 Config::GetIntDefault(const char * name, const int32 def) return def; return atoi(node->getValue()); }; + float Config::GetFloatDefault(const char * name, const float def) { if(!mConf) diff --git a/src/shared/Config/Config.h b/src/shared/Config/Config.h index 582c5266a8c..7070e6180c0 100644 --- a/src/shared/Config/Config.h +++ b/src/shared/Config/Config.h @@ -17,28 +17,37 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef CONFIG_H #define CONFIG_H + #include <Policies/Singleton.h> #include "Platform/Define.h" + class DOTCONFDocument; + class TRINITY_DLL_SPEC Config { public: Config(); ~Config(); + bool SetSource(const char *file, bool ignorecase = true); bool Reload(); + std::string GetStringDefault(const char * name, std::string def); bool GetBoolDefault(const char * name, const bool def); int32 GetIntDefault(const char * name, const int32 def); float GetFloatDefault(const char * name, const float def); + std::string GetFilename() const { return mFilename; } private: std::string mFilename; bool mIgnoreCase; DOTCONFDocument *mConf; }; + #define sConfig Trinity::Singleton<Config>::Instance() + #endif diff --git a/src/shared/Config/ConfigEnv.h b/src/shared/Config/ConfigEnv.h index c98fe6b38b0..75209a7fc6b 100644 --- a/src/shared/Config/ConfigEnv.h +++ b/src/shared/Config/ConfigEnv.h @@ -17,10 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(CONFIGENVIRONMENT_H) + #define CONFIGENVIRONMENT_H + #include "Common.h" #include "dotconfpp/dotconfpp.h" #include "Config.h" + #endif diff --git a/src/shared/Config/dotconfpp/dotconfpp.cpp b/src/shared/Config/dotconfpp/dotconfpp.cpp index 0492c69f805..e779637256e 100644 --- a/src/shared/Config/dotconfpp/dotconfpp.cpp +++ b/src/shared/Config/dotconfpp/dotconfpp.cpp @@ -1,6 +1,8 @@ #include "Common.h" + #include "dotconfpp.h" + #ifdef WIN32 #define PATH_MAX _MAX_PATH #define strcasecmp stricmp @@ -12,14 +14,17 @@ #include <stdint.h> #include <strings.h> #endif + #if !defined(R_OK) #define R_OK 04 #endif + DOTCONFDocumentNode::DOTCONFDocumentNode():previousNode(NULL), nextNode(NULL), parentNode(NULL), childNode(NULL), values(NULL), valuesCount(0), name(NULL), lineNum(0), fileName(NULL), closed(true) { } + DOTCONFDocumentNode::~DOTCONFDocumentNode() { free(name); @@ -30,12 +35,14 @@ DOTCONFDocumentNode::~DOTCONFDocumentNode() free(values); } } + void DOTCONFDocumentNode::pushValue(char * _value) { ++valuesCount; values = (char**)realloc(values, valuesCount*sizeof(char*)); values[valuesCount-1] = strdup(_value); } + const char* DOTCONFDocumentNode::getValue(int index) const { if(index >= valuesCount){ @@ -43,6 +50,7 @@ const char* DOTCONFDocumentNode::getValue(int index) const } return values[index]; } + DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity): mempool(NULL), curParent(NULL), curPrev(NULL), curLine(0), file(NULL), fileName(NULL) @@ -52,9 +60,11 @@ DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity) } else { cmp_func = strcasecmp; } + mempool = new AsyncDNSMemPool(1024); mempool->initialize(); } + DOTCONFDocument::~DOTCONFDocument() { for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i != nodeTree.end(); ++i){ @@ -69,6 +79,7 @@ DOTCONFDocument::~DOTCONFDocument() free(fileName); delete mempool; } + int DOTCONFDocument::cleanupLine(char * line) { char * start = line; @@ -76,12 +87,15 @@ int DOTCONFDocument::cleanupLine(char * line) bool multiline = false; bool concat = false; char * word = NULL; + if(!words.empty() && quoted) concat = true; + while(*line){ if((*line == '#' || *line == ';') && !quoted){ *bg = 0; if(strlen(start)){ + if(concat){ word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); strcpy(word, words.back()); @@ -98,6 +112,7 @@ int DOTCONFDocument::cleanupLine(char * line) if(*line == '=' && !quoted){ *line = ' ';continue; } + // Allowing \" in there causes problems with directory paths // like "C:\TrinIty\" //if(*line == '\\' && (*(line+1) == '"' || *(line+1) == '\'')){ @@ -116,6 +131,7 @@ int DOTCONFDocument::cleanupLine(char * line) if(*line == '\\' && (*(line+1) == '\n' || *(line+1) == '\r')){ *bg = 0; if(strlen(start)){ + if(concat){ word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); strcpy(word, words.back()); @@ -137,6 +153,7 @@ int DOTCONFDocument::cleanupLine(char * line) if(isspace((unsigned char)*line) && !quoted){ *bg++ = 0; if(strlen(start)){ + if(concat){ word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); strcpy(word, words.back()); @@ -150,16 +167,20 @@ int DOTCONFDocument::cleanupLine(char * line) } start = bg; while(isspace((unsigned char)*++line)) {} + continue; } *bg++ = *line++; } + if(quoted && !multiline){ error(curLine, fileName, "unterminated quote"); return -1; } + return multiline?1:0; } + int DOTCONFDocument::parseLine() { char * word = NULL; @@ -167,21 +188,26 @@ int DOTCONFDocument::parseLine() char * nodeValue = NULL; DOTCONFDocumentNode * tagNode = NULL; bool newNode = false; + for(std::list<char*>::iterator i = words.begin(); i != words.end(); ++i) { word = *i; + if(*word == '<'){ newNode = true; } + if(newNode){ nodeValue = NULL; nodeName = NULL; newNode = false; } + size_t wordLen = strlen(word); if(word[wordLen-1] == '>'){ word[wordLen-1] = 0; newNode = true; } + if(nodeName == NULL){ nodeName = word; bool closed = true; @@ -216,9 +242,11 @@ int DOTCONFDocument::parseLine() if(!nodeTree.empty()){ DOTCONFDocumentNode * prev = nodeTree.back(); if(prev->closed){ + curPrev->nextNode = tagNode; tagNode->previousNode = curPrev; tagNode->parentNode = curParent; + } else { prev->childNode = tagNode; tagNode->parentNode = prev; @@ -232,6 +260,7 @@ int DOTCONFDocument::parseLine() tagNode->pushValue(nodeValue); } } + return 0; } int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent) @@ -240,8 +269,10 @@ int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent) int ret = 0; curLine = 0; curParent = _parent; + quoted = false; size_t slen = 0; + while(fgets(str, 511, file)){ ++curLine; slen = strlen(str); @@ -266,11 +297,14 @@ int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent) } } } + return ret; } + int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator & from) { int ret = 0; + DOTCONFDocumentNode * tagNode = NULL; int vi = 0; for(std::list<DOTCONFDocumentNode*>::iterator i = from; i != nodeTree.end(); ++i){ @@ -282,6 +316,7 @@ int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator } vi = 0; while( vi < tagNode->valuesCount ){ + if(strstr(tagNode->values[vi], "${") && strchr(tagNode->values[vi], '}') ){ ret = macroSubstitute(tagNode, vi ); mempool->free(); @@ -295,18 +330,24 @@ int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator break; } } + return ret; } + int DOTCONFDocument::setContent(const char * _fileName) { int ret = 0; char realpathBuf[PATH_MAX]; + if(realpath(_fileName, realpathBuf) == NULL){ error(0, NULL, "realpath(%s) failed: %s", _fileName, strerror(errno)); return -1; } + fileName = strdup(realpathBuf); + processedFiles.push_back(strdup(realpathBuf)); + if(( file = fopen(fileName, "r")) == NULL){ error(0, NULL, "failed to open file '%s': %s", fileName, strerror(errno)); return -1; @@ -316,12 +357,17 @@ int DOTCONFDocument::setContent(const char * _fileName) fgets((char*)&utf8header, 4, file); // Try read header if (utf8header!=0x00BFBBEF) // If not exist fseek(file, 0, SEEK_SET); // Reset read position + ret = parseFile(); + (void) fclose(file); + if(!ret){ + if( (ret = checkConfig(nodeTree.begin())) == -1){ return -1; } + std::list<DOTCONFDocumentNode*>::iterator from; DOTCONFDocumentNode * tagNode = NULL; int vi = 0; @@ -338,6 +384,7 @@ int DOTCONFDocument::setContent(const char * _fileName) error(tagNode->lineNum, tagNode->fileName, "realpath(%s) failed: %s", tagNode->values[vi], strerror(errno)); return -1; } + bool processed = false; for(std::list<char*>::const_iterator itInode = processedFiles.begin(); itInode != processedFiles.end(); ++itInode){ if(!strcmp(*itInode, realpathBuf)){ @@ -348,14 +395,18 @@ int DOTCONFDocument::setContent(const char * _fileName) if(processed){ break; } + processedFiles.push_back(strdup(realpathBuf)); + file = fopen(tagNode->values[vi], "r"); if(file == NULL){ error(tagNode->lineNum, fileName, "failed to open file '%s': %s", tagNode->values[vi], strerror(errno)); return -1; } + fileName = strdup(realpathBuf); from = nodeTree.end(); --from; + ret = parseFile(); (void) fclose(file); if(ret == -1) @@ -368,11 +419,14 @@ int DOTCONFDocument::setContent(const char * _fileName) } } + if(!requiredOptions.empty()) ret = checkRequiredOptions(); } + return ret; } + int DOTCONFDocument::checkRequiredOptions() { for(std::list<char*>::const_iterator ci = requiredOptions.begin(); ci != requiredOptions.end(); ++ci){ @@ -390,30 +444,40 @@ int DOTCONFDocument::checkRequiredOptions() } return 0; } + void DOTCONFDocument::error(int lineNum, const char * fileName_, const char * fmt, ...) { va_list args; va_start(args, fmt); + size_t len = (lineNum!=0?strlen(fileName_):0) + strlen(fmt) + 50; char * buf = (char*)mempool->alloc(len); + if(lineNum) (void) snprintf(buf, len, "DOTCONF++: file '%s', line %d: %s\n", fileName_, lineNum, fmt); else (void) snprintf(buf, len, "DOTCONF++: %s\n", fmt); + (void) vfprintf(stderr, buf, args); + va_end(args); } + char * DOTCONFDocument::getSubstitution(char * macro, int lineNum) { char * buf = NULL; char * variable = macro+2; + char * endBr = strchr(macro, '}'); + if(!endBr){ error(lineNum, fileName, "unterminated '{'"); return NULL; } *endBr = 0; + char * defaultValue = strchr(variable, ':'); + if(defaultValue){ *defaultValue++ = 0; if(*defaultValue != '-'){ @@ -428,6 +492,7 @@ char * DOTCONFDocument::getSubstitution(char * macro, int lineNum) } else { defaultValue = NULL; } + char * subs = getenv(variable); if( subs ){ buf = mempool->strdup(subs); @@ -454,6 +519,7 @@ char * DOTCONFDocument::getSubstitution(char * macro, int lineNum) } return buf; } + int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex) { int ret = 0; @@ -462,6 +528,7 @@ int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueInd char * value = (char*)mempool->alloc(valueLen); char * v = value; char * subs = NULL; + while(*macro){ if(*macro == '$' && *(macro+1) == '{'){ char * m = strchr(macro, '}'); @@ -481,10 +548,12 @@ int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueInd *v++ = *macro++; } *v = 0; + free(tagNode->values[valueIndex]); tagNode->values[valueIndex] = strdup(value); return ret; } + const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const { if ( !nodeTree.empty() ) { @@ -493,19 +562,25 @@ const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const return NULL; } } + const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode, const DOTCONFDocumentNode * startNode) const { + std::list<DOTCONFDocumentNode*>::const_iterator i = nodeTree.begin(); + if(startNode == NULL) startNode = parentNode; + if(startNode != NULL){ while( i != nodeTree.end() && (*i) != startNode ){ ++i; } if( i != nodeTree.end() ) ++i; } + for(; i!=nodeTree.end(); ++i){ + if((*i)->parentNode != parentNode){ continue; } @@ -515,6 +590,7 @@ const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, con } return NULL; } + void DOTCONFDocument::setRequiredOptionNames(const char ** requiredOptionNames) { while(*requiredOptionNames){ diff --git a/src/shared/Config/dotconfpp/dotconfpp.h b/src/shared/Config/dotconfpp/dotconfpp.h index 687753eb807..51455854ee7 100644 --- a/src/shared/Config/dotconfpp/dotconfpp.h +++ b/src/shared/Config/dotconfpp/dotconfpp.h @@ -1,17 +1,24 @@ + #ifndef DOTCONFPP_H #define DOTCONFPP_H + #include <list> + #include <stdarg.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> + #include <sys/types.h> #include <sys/stat.h> + #include "mempool.h" + class DOTCONFDocument; + class DOTCONFDocumentNode { friend class DOTCONFDocument; @@ -27,12 +34,16 @@ private: int lineNum; char * fileName; bool closed; + void pushValue(char * _value); + public: DOTCONFDocumentNode(); ~DOTCONFDocumentNode(); + const char * getConfigurationFileName()const { return fileName; } int getConfigurationLineNumber() const { return lineNum; } + const DOTCONFDocumentNode * getNextNode() const { return nextNode; } const DOTCONFDocumentNode * getPreviuosNode() const { return previousNode; } const DOTCONFDocumentNode * getParentNode() const { return parentNode; } @@ -41,6 +52,7 @@ public: const char * getName() const { return name; } const DOTCONFDocument * getDocument() const { return document; } }; + class DOTCONFDocument { public: @@ -59,6 +71,7 @@ private: char * fileName; std::list<char*> words; int (*cmp_func)(const char *, const char *); + int checkRequiredOptions(); int parseLine(); int parseFile(DOTCONFDocumentNode * _parent = NULL); @@ -66,15 +79,20 @@ private: int cleanupLine(char * line); char * getSubstitution(char * macro, int lineNum); int macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex); + protected: virtual void error(int lineNum, const char * fileName, const char * fmt, ...) ATTR_PRINTF(4,5); + public: DOTCONFDocument(CaseSensitive caseSensitivity = CASESENSETIVE); virtual ~DOTCONFDocument(); + int setContent(const char * _fileName); + void setRequiredOptionNames(const char ** requiredOptionNames); const DOTCONFDocumentNode * getFirstNode() const; const DOTCONFDocumentNode * findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode = NULL, const DOTCONFDocumentNode * startNode = NULL) const; }; + #endif diff --git a/src/shared/Config/dotconfpp/mempool.cpp b/src/shared/Config/dotconfpp/mempool.cpp index 019cfe3cb86..cec8e8d119f 100644 --- a/src/shared/Config/dotconfpp/mempool.cpp +++ b/src/shared/Config/dotconfpp/mempool.cpp @@ -1,20 +1,25 @@ + #include "mempool.h" + AsyncDNSMemPool::PoolChunk::PoolChunk(size_t _size): pool(NULL), pos(0), size(_size) { pool = ::malloc(size); } + AsyncDNSMemPool::PoolChunk::~PoolChunk() { ::free(pool); } + AsyncDNSMemPool::AsyncDNSMemPool(size_t _defaultSize): chunks(NULL), chunksCount(0), defaultSize(_defaultSize), poolUsage(0), poolUsageCounter(0) { } + AsyncDNSMemPool::~AsyncDNSMemPool() { for(size_t i = 0; i<chunksCount; ++i){ @@ -22,15 +27,19 @@ AsyncDNSMemPool::~AsyncDNSMemPool() } ::free(chunks); } + int AsyncDNSMemPool::initialize() { chunksCount = 1; chunks = (PoolChunk**)::malloc(sizeof(PoolChunk*)); if(chunks == NULL) return -1; + chunks[chunksCount-1] = new PoolChunk(defaultSize); + return 0; } + void AsyncDNSMemPool::addNewChunk(size_t size) { ++chunksCount; @@ -40,6 +49,7 @@ void AsyncDNSMemPool::addNewChunk(size_t size) else chunks[chunksCount-1] = new PoolChunk(size); } + void * AsyncDNSMemPool::alloc(size_t size) { PoolChunk * chunk = NULL; @@ -54,17 +64,20 @@ void * AsyncDNSMemPool::alloc(size_t size) chunks[chunksCount-1]->pos = size; return chunks[chunksCount-1]->pool; } + void AsyncDNSMemPool::free() { size_t pu = 0; size_t psz = 0; ++poolUsageCounter; + for(size_t i = 0; i<chunksCount; ++i){ pu += chunks[i]->pos; psz += chunks[i]->size; chunks[i]->pos = 0; } poolUsage=(poolUsage>pu)?poolUsage:pu; + if(poolUsageCounter >= 10 && chunksCount > 1){ psz -= chunks[chunksCount-1]->size; if(poolUsage < psz){ @@ -75,10 +88,12 @@ void AsyncDNSMemPool::free() poolUsageCounter = 0; } } + void * AsyncDNSMemPool::calloc(size_t size) { return ::memset(this->alloc(size), 0, size); } + char * AsyncDNSMemPool::strdup(const char *str) { return ::strcpy((char*)this->alloc(strlen(str)+1), str); diff --git a/src/shared/Config/dotconfpp/mempool.h b/src/shared/Config/dotconfpp/mempool.h index 6bf71eb6b54..81c01d15a58 100644 --- a/src/shared/Config/dotconfpp/mempool.h +++ b/src/shared/Config/dotconfpp/mempool.h @@ -1,13 +1,17 @@ + #ifndef ASYNC_DNS_MEMPOOL_H #define ASYNC_DNS_MEMPOOL_H + #include <stdlib.h> #include <string.h> #include <sys/types.h> + #undef free #undef calloc #undef strdup + class AsyncDNSMemPool { private: @@ -15,23 +19,29 @@ private: void * pool; size_t pos; size_t size; + PoolChunk(size_t _size); ~PoolChunk(); }; PoolChunk ** chunks; size_t chunksCount; size_t defaultSize; + size_t poolUsage; size_t poolUsageCounter; + void addNewChunk(size_t size); + public: AsyncDNSMemPool(size_t _defaultSize = 4096); virtual ~AsyncDNSMemPool(); + int initialize(); void free(); void * alloc(size_t size); void * calloc(size_t size); char * strdup(const char *str); }; + #endif diff --git a/src/shared/Database/DBCFileLoader.cpp b/src/shared/Database/DBCFileLoader.cpp index adf796fbce6..e7ebeedecfb 100644 --- a/src/shared/Database/DBCFileLoader.cpp +++ b/src/shared/Database/DBCFileLoader.cpp @@ -17,42 +17,59 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <stdio.h> #include <stdlib.h> #include <string.h> + #include "DBCFileLoader.h" + DBCFileLoader::DBCFileLoader() { data = NULL; fieldsOffset = NULL; } + bool DBCFileLoader::Load(const char *filename, const char *fmt) { + uint32 header; if(data) { delete [] data; data=NULL; } + FILE * f=fopen(filename,"rb"); if(!f)return false; + if(fread(&header,4,1,f)!=1) // Number of records return false; + EndianConvert(header); if(header!=0x43424457) return false; //'WDBC' + if(fread(&recordCount,4,1,f)!=1) // Number of records return false; + EndianConvert(recordCount); + if(fread(&fieldCount,4,1,f)!=1) // Number of fields return false; + EndianConvert(fieldCount); + if(fread(&recordSize,4,1,f)!=1) // Size of a record return false; + EndianConvert(recordSize); + if(fread(&stringSize,4,1,f)!=1) // String size return false; + EndianConvert(stringSize); + fieldsOffset = new uint32[fieldCount]; fieldsOffset[0] = 0; for(uint32 i = 1; i < fieldCount; i++) @@ -63,13 +80,17 @@ bool DBCFileLoader::Load(const char *filename, const char *fmt) else // 4 byte fields (int32/float/strings) fieldsOffset[i] += 4; } + data = new unsigned char[recordSize*recordCount+stringSize]; stringTable = data + recordSize*recordCount; + if(fread(data,recordSize*recordCount+stringSize,1,f)!=1) return false; + fclose(f); return true; } + DBCFileLoader::~DBCFileLoader() { if(data) @@ -77,11 +98,13 @@ DBCFileLoader::~DBCFileLoader() if(fieldsOffset) delete [] fieldsOffset; } + DBCFileLoader::Record DBCFileLoader::getRecord(size_t id) { assert(data); return Record(*this, data + id*recordSize); } + uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos) { uint32 recordsize = 0; @@ -107,10 +130,13 @@ uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos) recordsize += 1; break; } + if(index_pos) *index_pos = i; + return recordsize; } + char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable) { /* @@ -120,14 +146,18 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** float field1, int field2 }entry; + this func will generate entry[rows] data; */ + typedef char * ptr; if(strlen(format)!=fieldCount) return NULL; + //get struct size and index pos int32 i; uint32 recordsize=GetFormatRecordSize(format,&i); + if(i>=0) { uint32 maxi=0; @@ -137,9 +167,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** uint32 ind=getRecord(y).getUInt (i); if(ind>maxi)maxi=ind; } + // If higher index avalible from sql - use it instead of dbcs if (sqlHighestIndex > maxi) maxi = sqlHighestIndex; + ++maxi; records=maxi; indexTable=new ptr[maxi]; @@ -150,8 +182,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** records = recordCount + sqlRecordCount; indexTable = new ptr[recordCount+ sqlRecordCount]; } + char* dataTable= new char[(recordCount + sqlRecordCount)*recordsize]; + uint32 offset=0; + for(uint32 y =0;y<recordCount;++y) { if(i>=0) @@ -160,6 +195,7 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } else indexTable[y]=&dataTable[offset]; + for(uint32 x=0;x<fieldCount;x++) { switch(format[x]) @@ -185,15 +221,20 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } } sqlDataTable = dataTable + offset; + return dataTable; } + char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable) { if(strlen(format)!=fieldCount) return NULL; + char* stringPool= new char[stringSize]; memcpy(stringPool,stringTable,stringSize); + uint32 offset=0; + for(uint32 y =0;y<recordCount;y++) { for(uint32 x=0;x<fieldCount;x++) @@ -219,6 +260,7 @@ char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable) break; } } + return stringPool; } diff --git a/src/shared/Database/DBCFileLoader.h b/src/shared/Database/DBCFileLoader.h index fd1f5539ee3..ef29af84bc1 100644 --- a/src/shared/Database/DBCFileLoader.h +++ b/src/shared/Database/DBCFileLoader.h @@ -15,11 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DBC_FILE_LOADER_H #define DBC_FILE_LOADER_H #include "Platform/Define.h" #include "Utilities/ByteConverter.h" #include <cassert> + enum { FT_NA='x', //not used or unknown, 4 byte size @@ -34,12 +36,15 @@ enum FT_SQL_PRESENT='p', //Used in sql format to mark column present in sql dbc FT_SQL_ABSENT='a' //Used in sql format to mark column absent in sql dbc }; + class DBCFileLoader { public: DBCFileLoader(); ~DBCFileLoader(); + bool Load(const char *filename, const char *fmt); + class Record { public: @@ -62,6 +67,7 @@ class DBCFileLoader assert(field < file.fieldCount); return *reinterpret_cast<uint8*>(offset+file.GetOffset(field)); } + const char *getString(size_t field) const { assert(field < file.fieldCount); @@ -69,15 +75,20 @@ class DBCFileLoader assert(stringOffset < file.stringSize); return reinterpret_cast<char*>(file.stringTable + stringOffset); } + private: Record(DBCFileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) {} unsigned char *offset; DBCFileLoader &file; + friend class DBCFileLoader; + }; + // Get record by id Record getRecord(size_t id); /// Get begin iterator over records + uint32 GetNumRows() const { return recordCount;} uint32 GetRowSize() const { return recordSize;} uint32 GetCols() const { return fieldCount; } @@ -87,6 +98,7 @@ class DBCFileLoader char* AutoProduceStrings(const char* fmt, char* dataTable); static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); private: + uint32 recordSize; uint32 recordCount; uint32 fieldCount; diff --git a/src/shared/Database/DBCStore.h b/src/shared/Database/DBCStore.h index 60e533a88e1..e02265ec523 100644 --- a/src/shared/Database/DBCStore.h +++ b/src/shared/Database/DBCStore.h @@ -15,10 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DBCSTORE_H #define DBCSTORE_H + #include "DBCFileLoader.h" #include "Log.h" + struct SqlDbc { const std::string * formatString; @@ -38,6 +41,7 @@ struct SqlDbc else if (sqlTableName[i] == '.') sqlTableName[i] = '_'; } + // Get sql index position DBCFileLoader::GetFormatRecordSize(fmt, &indexPos); if (indexPos>=0) @@ -55,6 +59,7 @@ struct SqlDbc } } }; + template<class T> class DBCStorage { @@ -62,16 +67,19 @@ class DBCStorage public: explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } ~DBCStorage() { Clear(); } + T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } uint32 GetNumRows() const { return nCount; } char const* GetFormat() const { return fmt; } uint32 GetFieldCount() const { return fieldCount; } + bool Load(char const* fn, SqlDbc * sql) { DBCFileLoader dbc; // Check if load was sucessful, only then continue if(!dbc.Load(fn, fmt)) return false; + uint32 sqlRecordCount = 0; uint32 sqlHighestIndex = 0; Field *fields = NULL; @@ -83,6 +91,7 @@ class DBCStorage if (sql->indexPos >= 0) query +=" ORDER BY + " + *sql->indexName + " DESC"; query += ";"; + result = WorldDatabase.Query(query.c_str()); if (result) { @@ -103,7 +112,9 @@ class DBCStorage char * sqlDataTable; fieldCount = dbc.GetCols(); m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable, sqlRecordCount, sqlHighestIndex, sqlDataTable); + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + // Insert sql data into arrays if (result) { @@ -115,6 +126,7 @@ class DBCStorage { if (!fields) fields = result->Fetch(); + if(sql->indexPos >= 0) { uint32 id = fields[sql->sqlIndexPos].GetUInt32(); @@ -129,6 +141,7 @@ class DBCStorage indexTable[rowIndex]=(T*)&sqlDataTable[offset]; uint32 columnNumber = 0; uint32 sqlColumnNumber = 0; + for(;columnNumber < sql->formatString->size();++columnNumber) { if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT) @@ -198,35 +211,44 @@ class DBCStorage delete result; return false; } + fields = NULL; ++rowIndex; }while (result->NextRow()); } delete result; } + // error in dbc file at loading if NULL return indexTable!=NULL; } + bool LoadStringsFrom(char const* fn) { // DBC must be already loaded using Load if(!indexTable) return false; + DBCFileLoader dbc; // Check if load was successful, only then continue if(!dbc.Load(fn, fmt)) return false; + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + return true; } + void Clear() { if (!indexTable) return; + delete[] ((char*)indexTable); indexTable = NULL; delete[] ((char*)m_dataTable); m_dataTable = NULL; + while(!m_stringPoolList.empty()) { delete[] m_stringPoolList.front(); @@ -234,6 +256,7 @@ class DBCStorage } nCount = 0; } + private: char const* fmt; uint32 nCount; @@ -242,4 +265,5 @@ class DBCStorage T* m_dataTable; StringPoolList m_stringPoolList; }; + #endif diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp index 9bfae3479bf..572d3db6f1d 100644 --- a/src/shared/Database/Database.cpp +++ b/src/shared/Database/Database.cpp @@ -17,17 +17,22 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "DatabaseEnv.h" #include "Config/ConfigEnv.h" + #include "Common.h" #include "../../game/UpdateFields.h" + #include <ctime> #include <iostream> #include <fstream> + Database::~Database() { /*Delete objects*/ } + bool Database::Initialize(const char *) { // Enable logging of SQL commands (usally only GM commands) @@ -39,37 +44,46 @@ bool Database::Initialize(const char *) if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\')) m_logsDir.append("/"); } + return true; } + void Database::ThreadStart() { } + void Database::ThreadEnd() { } + void Database::escape_string(std::string& str) { if(str.empty()) return; + char* buf = new char[str.size()*2+1]; escape_string(buf,str.c_str(),str.size()); str = buf; delete[] buf; } + bool Database::PExecuteLog(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + if( m_logSQL ) { time_t curr; @@ -78,6 +92,7 @@ bool Database::PExecuteLog(const char * format,...) local=*(localtime(&curr)); // dereference and assign char fName[128]; sprintf( fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday ); + FILE* log_file; std::string logsDir_fname = m_logsDir+fName; log_file = fopen(logsDir_fname.c_str(), "a"); @@ -92,58 +107,74 @@ bool Database::PExecuteLog(const char * format,...) sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName); } } + return Execute(szQuery); } + void Database::SetResultQueue(SqlResultQueue * queue) { m_queryQueues[ACE_Based::Thread::current()] = queue; + } + QueryResult* Database::PQuery(const char *format,...) { if(!format) return NULL; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return Query(szQuery); } + QueryNamedResult* Database::PQueryNamed(const char *format,...) { if(!format) return NULL; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return QueryNamed(szQuery); } + bool Database::PExecute(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return Execute(szQuery); } + bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value) { return PExecute( @@ -153,6 +184,7 @@ bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const "' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u", field, field+1, value, -int32(PLAYER_END-field), guid); } + bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value) { return PExecute( @@ -161,22 +193,27 @@ bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const ui "%u,' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u", field, value, -int32(PLAYER_END-field), guid); } + bool Database::DirectPExecute(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return DirectExecute(szQuery); } + bool Database::CheckRequiredField( char const* table_name, char const* required_name ) { // check required field @@ -186,7 +223,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ delete result; return true; } + // check fail, prepare readabale error message + // search current required_* field in DB QueryNamedResult* result2 = PQueryNamed("SELECT * FROM %s LIMIT 1",table_name); if(result2) @@ -201,7 +240,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ break; } } + delete result2; + if(!reqName.empty()) sLog.outErrorDb("Table `%s` have field `%s` but expected `%s`! Not all sql updates applied?",table_name,reqName.c_str(),required_name); else @@ -209,5 +250,6 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ } else sLog.outErrorDb("Table `%s` fields list query fail but expected have `%s`! No records in `%s`?",table_name,required_name,table_name); + return false; } diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h index 34438d994dc..6172a61c5f9 100644 --- a/src/shared/Database/Database.h +++ b/src/shared/Database/Database.h @@ -17,35 +17,48 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DATABASE_H #define DATABASE_H + #include "Threading.h" #include "Utilities/UnorderedMap.h" #include "Database/SqlDelayThread.h" + class SqlTransaction; class SqlResultQueue; class SqlQueryHolder; + typedef UNORDERED_MAP<ACE_Based::Thread* , SqlTransaction*> TransactionQueues; typedef UNORDERED_MAP<ACE_Based::Thread* , SqlResultQueue*> QueryQueues; + #define MAX_QUERY_LEN 32*1024 + class TRINITY_DLL_SPEC Database { protected: Database() : m_threadBody(NULL), m_delayThread(NULL) {}; + TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads QueryQueues m_queryQueues; ///< Query queues from diff threads SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer (owned by m_delayThread) ACE_Based::Thread* m_delayThread; ///< Pointer to executer thread + public: + virtual ~Database(); + virtual bool Initialize(const char *infoString); virtual void InitDelayThread() = 0; virtual void HaltDelayThread() = 0; + virtual QueryResult* Query(const char *sql) = 0; QueryResult* PQuery(const char *format,...) ATTR_PRINTF(2,3); virtual QueryNamedResult* QueryNamed(const char *sql) = 0; QueryNamedResult* PQueryNamed(const char *format,...) ATTR_PRINTF(2,3); + /// Async queries and query holders, implemented in DatabaseImpl.h + // Query / member template<class Class> bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql); @@ -83,14 +96,18 @@ class TRINITY_DLL_SPEC Database bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder); template<class Class, typename ParamType1> bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1); + virtual bool Execute(const char *sql) = 0; bool PExecute(const char *format,...) ATTR_PRINTF(2,3); virtual bool DirectExecute(const char* sql) = 0; bool DirectPExecute(const char *format,...) ATTR_PRINTF(2,3); + bool _UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value); bool _SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value); + // Writes SQL commands to a LOG file (see Trinityd.conf "LogSQL") bool PExecuteLog(const char *format,...) ATTR_PRINTF(2,3); + virtual bool BeginTransaction() // nothing do if DB not support transactions { return true; @@ -103,15 +120,20 @@ class TRINITY_DLL_SPEC Database { return false; } + virtual operator bool () const = 0; + virtual unsigned long escape_string(char *to, const char *from, unsigned long length) { strncpy(to,from,length); return length; } void escape_string(std::string& str); + // must be called before first query in thread (one time for thread using one from existed Database objects) virtual void ThreadStart(); // must be called before finish thread run (one time for thread using one from existed Database objects) virtual void ThreadEnd(); + // sets the result queue of the current thread, be careful what thread you call this from void SetResultQueue(SqlResultQueue * queue); + bool CheckRequiredField(char const* table_name, char const* required_name); private: bool m_logSQL; diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h index 2e89d74e97c..d5d6867e82f 100644 --- a/src/shared/Database/DatabaseEnv.h +++ b/src/shared/Database/DatabaseEnv.h @@ -17,13 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(DATABASEENV_H) #define DATABASEENV_H + #include "Common.h" #include "Log.h" #include "Errors.h" + #include "Database/Field.h" #include "Database/QueryResult.h" + #ifdef DO_POSTGRESQL #include "Database/QueryResultPostgre.h" #include "Database/Database.h" @@ -43,8 +47,10 @@ typedef DatabaseMysql DatabaseType; #define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )" #define _OFFSET_ "LIMIT %d,1" #endif + extern DatabaseType WorldDatabase; extern DatabaseType CharacterDatabase; extern DatabaseType loginDatabase; + #endif diff --git a/src/shared/Database/DatabaseImpl.h b/src/shared/Database/DatabaseImpl.h index ac856664290..7cbd0ed8ba5 100644 --- a/src/shared/Database/DatabaseImpl.h +++ b/src/shared/Database/DatabaseImpl.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Database/Database.h" #include "Database/SqlOperations.h" + /// Function body definitions for the template function members of the Database class + #define ASYNC_QUERY_BODY(sql, queue_itr) \ if (!sql) return false; \ \ @@ -30,6 +33,7 @@ queue_itr = m_queryQueues.find(queryThread); \ if (queue_itr == m_queryQueues.end()) return false; \ } + #define ASYNC_PQUERY_BODY(format, szQuery) \ if(!format) return false; \ \ @@ -48,6 +52,7 @@ return false; \ } \ } + #define ASYNC_DELAYHOLDER_BODY(holder, queue_itr) \ if (!holder) return false; \ \ @@ -58,7 +63,9 @@ queue_itr = m_queryQueues.find(queryThread); \ if (queue_itr == m_queryQueues.end()) return false; \ } + // -- Query / member -- + template<class Class> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql) @@ -66,6 +73,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const c ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class>(object, method), itr->second)); } + template<class Class, typename ParamType1> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) @@ -73,6 +81,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1>(object, method, (QueryResult*)NULL, param1), itr->second)); } + template<class Class, typename ParamType1, typename ParamType2> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) @@ -80,6 +89,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2>(object, method, (QueryResult*)NULL, param1, param2), itr->second)); } + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) @@ -87,7 +97,9 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2, ParamType3>(object, method, (QueryResult*)NULL, param1, param2, param3), itr->second)); } + // -- Query / static -- + template<typename ParamType1> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) @@ -95,6 +107,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1 ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1>(method, (QueryResult*)NULL, param1), itr->second)); } + template<typename ParamType1, typename ParamType2> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) @@ -102,6 +115,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Param ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2>(method, (QueryResult*)NULL, param1, param2), itr->second)); } + template<typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) @@ -109,7 +123,9 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamT ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2, ParamType3>(method, (QueryResult*)NULL, param1, param2, param3), itr->second)); } + // -- PQuery / member -- + template<class Class> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) @@ -117,6 +133,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, szQuery); } + template<class Class, typename ParamType1> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) @@ -124,6 +141,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, szQuery); } + template<class Class, typename ParamType1, typename ParamType2> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) @@ -131,6 +149,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, param2, szQuery); } + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) @@ -138,7 +157,9 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, param2, param3, szQuery); } + // -- PQuery / static -- + template<typename ParamType1> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) @@ -146,6 +167,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, szQuery); } + template<typename ParamType1, typename ParamType2> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) @@ -153,6 +175,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Para ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, param2, szQuery); } + template<typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) @@ -160,7 +183,9 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, Param ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, param2, param3, szQuery); } + // -- QueryHolder -- + template<class Class> bool Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder) @@ -168,6 +193,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq ASYNC_DELAYHOLDER_BODY(holder, itr) return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult*)NULL, holder), m_threadBody, itr->second); } + template<class Class, typename ParamType1> bool Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1) @@ -175,6 +201,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq ASYNC_DELAYHOLDER_BODY(holder, itr) return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult*)NULL, holder, param1), m_threadBody, itr->second); } + #undef ASYNC_QUERY_BODY #undef ASYNC_PQUERY_BODY #undef ASYNC_DELAYHOLDER_BODY diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp index 235fb96f127..f08ea67cbbe 100644 --- a/src/shared/Database/DatabaseMysql.cpp +++ b/src/shared/Database/DatabaseMysql.cpp @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #include "Util.h" #include "Policies/SingletonImp.h" #include "Platform/Define.h" @@ -26,15 +28,19 @@ #include "Database/MySQLDelayThread.h" #include "Database/SqlOperations.h" #include "Timer.h" + void DatabaseMysql::ThreadStart() { mysql_thread_init(); } + void DatabaseMysql::ThreadEnd() { mysql_thread_end(); } + size_t DatabaseMysql::db_count = 0; + DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) { // before first connection @@ -42,6 +48,7 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) { // Mysql Library Init mysql_library_init(-1, NULL, NULL); + if (!mysql_thread_safe()) { sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe."); @@ -49,20 +56,26 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) } } } + DatabaseMysql::~DatabaseMysql() { if (m_delayThread) HaltDelayThread(); + if (mMysql) mysql_close(mMysql); + //Free Mysql library pointers for last ~DB if(--db_count == 0) mysql_library_end(); } + bool DatabaseMysql::Initialize(const char *infoString) { + if(!Database::Initialize(infoString)) return false; + tranThread = NULL; MYSQL *mysqlInit; mysqlInit = mysql_init(NULL); @@ -71,13 +84,19 @@ bool DatabaseMysql::Initialize(const char *infoString) sLog.outError( "Could not initialize Mysql connection" ); return false; } + InitDelayThread(); + Tokens tokens = StrSplit(infoString, ";"); + Tokens::iterator iter; + std::string host, port_or_socket, user, password, database; int port; char const* unix_socket; + iter = tokens.begin(); + if(iter != tokens.end()) host = *iter++; if(iter != tokens.end()) @@ -88,6 +107,7 @@ bool DatabaseMysql::Initialize(const char *infoString) password = *iter++; if(iter != tokens.end()) database = *iter++; + mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8"); #ifdef WIN32 if(host==".") // named pipe use option (Windows) @@ -117,14 +137,17 @@ bool DatabaseMysql::Initialize(const char *infoString) unix_socket = 0; } #endif + mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, unix_socket, 0); + if (mMysql) { sLog.outDetail( "Connected to MySQL database at %s", host.c_str()); sLog.outString( "MySQL client library: %s", mysql_get_client_info()); sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql)); + /*----------SET AUTOCOMMIT ON---------*/ // It seems mysql 5.0.x have enabled this feature // by default. In crash case you can lose data!!! @@ -138,10 +161,12 @@ bool DatabaseMysql::Initialize(const char *infoString) else sLog.outDetail("AUTOCOMMIT NOT SET TO 1"); /*-------------------------------------*/ + // set connection properties to UTF8 to properly handle locales for different // server configs - core sends data in UTF8, so MySQL must expect UTF8 too PExecute("SET NAMES `utf8`"); PExecute("SET CHARACTER SET `utf8`"); + #if MYSQL_VERSION_ID >= 50003 my_bool my_true = (my_bool)1; if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true)) @@ -155,6 +180,7 @@ bool DatabaseMysql::Initialize(const char *infoString) #else #warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem." #endif + return true; } else @@ -165,10 +191,12 @@ bool DatabaseMysql::Initialize(const char *infoString) return false; } } + bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount) { if (!mMysql) return 0; + { // guarded block for thread-safe mySQL request ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex); @@ -187,54 +215,72 @@ bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **p sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); #endif } + *pResult = mysql_store_result(mMysql); *pRowCount = mysql_affected_rows(mMysql); *pFieldCount = mysql_field_count(mMysql); // end guarded block } + if (!*pResult ) return false; + if (!*pRowCount) { mysql_free_result(*pResult); return false; } + *pFields = mysql_fetch_fields(*pResult); return true; } + QueryResult* DatabaseMysql::Query(const char *sql) { MYSQL_RES *result = NULL; MYSQL_FIELD *fields = NULL; uint64 rowCount = 0; uint32 fieldCount = 0; + if(!_Query(sql,&result,&fields,&rowCount,&fieldCount)) return NULL; + QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount); + queryResult->NextRow(); + return queryResult; } + QueryNamedResult* DatabaseMysql::QueryNamed(const char *sql) { MYSQL_RES *result = NULL; MYSQL_FIELD *fields = NULL; uint64 rowCount = 0; uint32 fieldCount = 0; + if(!_Query(sql,&result,&fields,&rowCount,&fieldCount)) return NULL; + QueryFieldNames names(fieldCount); for (uint32 i = 0; i < fieldCount; i++) names[i] = fields[i].name; + QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount); + queryResult->NextRow(); + return new QueryNamedResult(queryResult,names); } + bool DatabaseMysql::Execute(const char *sql) { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) return DirectExecute(sql); + tranThread = ACE_Based::Thread::current(); // owner of this transaction TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -246,15 +292,19 @@ bool DatabaseMysql::Execute(const char *sql) // Simple sql statement m_threadBody->Delay(new SqlStatement(sql)); } + return true; } + bool DatabaseMysql::DirectExecute(const char* sql) { if (!mMysql) return false; + { // guarded block for thread-safe mySQL request ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG uint32 _s = getMSTime(); #endif @@ -272,8 +322,10 @@ bool DatabaseMysql::DirectExecute(const char* sql) } // end guarded block } + return true; } + bool DatabaseMysql::_TransactionCmd(const char *sql) { if (mysql_query(mMysql, sql)) @@ -288,15 +340,18 @@ bool DatabaseMysql::_TransactionCmd(const char *sql) } return true; } + bool DatabaseMysql::BeginTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { if (tranThread == ACE_Based::Thread::current()) return false; // huh? this thread already started transaction + mMutex.acquire(); if (!_TransactionCmd("START TRANSACTION")) { @@ -305,19 +360,24 @@ bool DatabaseMysql::BeginTransaction() } return true; // transaction started } + tranThread = ACE_Based::Thread::current(); // owner of this transaction TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) // If for thread exists queue and also contains transaction // delete that transaction (not allow trans in trans) delete i->second; + m_tranQueues[tranThread] = new SqlTransaction(); + return true; } + bool DatabaseMysql::CommitTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { @@ -328,6 +388,7 @@ bool DatabaseMysql::CommitTransaction() mMutex.release(); return _res; } + tranThread = ACE_Based::Thread::current(); TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -339,10 +400,12 @@ bool DatabaseMysql::CommitTransaction() else return false; } + bool DatabaseMysql::RollbackTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { @@ -353,6 +416,7 @@ bool DatabaseMysql::RollbackTransaction() mMutex.release(); return _res; } + tranThread = ACE_Based::Thread::current(); TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -362,22 +426,28 @@ bool DatabaseMysql::RollbackTransaction() } return true; } + unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length) { if (!mMysql || !to || !from || !length) return 0; + return(mysql_real_escape_string(mMysql, to, from, length)); } + void DatabaseMysql::InitDelayThread() { assert(!m_delayThread); + //New delay thread for delay execute m_threadBody = new MySQLDelayThread(this); // will deleted at m_delayThread delete m_delayThread = new ACE_Based::Thread(m_threadBody); } + void DatabaseMysql::HaltDelayThread() { if (!m_threadBody || !m_delayThread) return; + m_threadBody->Stop(); //Stop event m_delayThread->wait(); //Wait for flush to DB delete m_delayThread; //This also deletes m_threadBody diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h index 3a7fa4f5def..4612ebfc462 100644 --- a/src/shared/Database/DatabaseMysql.h +++ b/src/shared/Database/DatabaseMysql.h @@ -17,13 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #ifndef _DATABASEMYSQL_H #define _DATABASEMYSQL_H + #include "Database.h" #include "Policies/Singleton.h" #include "ace/Thread_Mutex.h" #include "ace/Guard_T.h" + #ifdef WIN32 #define FD_SETSIZE 1024 #include <winsock2.h> @@ -31,12 +35,15 @@ #else #include <mysql.h> #endif + class TRINITY_DLL_SPEC DatabaseMysql : public Database { friend class Trinity::OperatorNew<DatabaseMysql>; + public: DatabaseMysql(); ~DatabaseMysql(); + //! Initializes Mysql and connects to a server. /*! infoString should be formated like hostname;username;password;database. */ bool Initialize(const char *infoString); @@ -49,18 +56,25 @@ class TRINITY_DLL_SPEC DatabaseMysql : public Database bool BeginTransaction(); bool CommitTransaction(); bool RollbackTransaction(); + operator bool () const { return mMysql != NULL; } + unsigned long escape_string(char *to, const char *from, unsigned long length); using Database::escape_string; + // must be call before first query in thread void ThreadStart(); // must be call before finish thread run void ThreadEnd(); private: ACE_Thread_Mutex mMutex; + ACE_Based::Thread * tranThread; + MYSQL *mMysql; + static size_t db_count; + bool _TransactionCmd(const char *sql); bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); }; diff --git a/src/shared/Database/Field.cpp b/src/shared/Database/Field.cpp index 2467eabd448..9a1fbfa5178 100644 --- a/src/shared/Database/Field.cpp +++ b/src/shared/Database/Field.cpp @@ -17,21 +17,28 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "DatabaseEnv.h" + Field::Field() : mValue(NULL), mType(DB_TYPE_UNKNOWN) { } + Field::Field(Field &f) { const char *value; + value = f.GetString(); + if (value && (mValue = new char[strlen(value) + 1])) strcpy(mValue, value); else mValue = NULL; + mType = f.GetType(); } + Field::Field(const char *value, enum Field::DataTypes type) : mType(type) { @@ -40,13 +47,16 @@ mType(type) else mValue = NULL; } + Field::~Field() { if(mValue) delete [] mValue; } + void Field::SetValue(const char *value) { if(mValue) delete [] mValue; + if (value) { mValue = new char[strlen(value) + 1]; diff --git a/src/shared/Database/Field.h b/src/shared/Database/Field.h index fd259423aef..d1238f838a5 100644 --- a/src/shared/Database/Field.h +++ b/src/shared/Database/Field.h @@ -17,11 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(FIELD_H) #define FIELD_H + class Field { public: + enum DataTypes { DB_TYPE_UNKNOWN = 0x00, @@ -30,11 +33,15 @@ class Field DB_TYPE_FLOAT = 0x03, DB_TYPE_BOOL = 0x04 }; + Field(); Field(Field &f); Field(const char *value, enum DataTypes type); + ~Field(); + enum DataTypes GetType() const { return mType; } + const char *GetString() const { return mValue; } std::string GetCppString() const { @@ -69,8 +76,11 @@ class Field else return 0; } + void SetType(enum DataTypes type) { mType = type; } + void SetValue(const char *value); + private: char *mValue; enum DataTypes mType; diff --git a/src/shared/Database/MySQLDelayThread.h b/src/shared/Database/MySQLDelayThread.h index f8dba08bddc..fcebe3fbd35 100644 --- a/src/shared/Database/MySQLDelayThread.h +++ b/src/shared/Database/MySQLDelayThread.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __MYSQLDELAYTHREAD_H #define __MYSQLDELAYTHREAD_H + #include "Database/SqlDelayThread.h" + class MySQLDelayThread : public SqlDelayThread { public: diff --git a/src/shared/Database/QueryResult.h b/src/shared/Database/QueryResult.h index 9d5bb57e4e9..f9f1a009833 100644 --- a/src/shared/Database/QueryResult.h +++ b/src/shared/Database/QueryResult.h @@ -17,39 +17,52 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(QUERYRESULT_H) #define QUERYRESULT_H + class TRINITY_DLL_SPEC QueryResult { public: QueryResult(uint64 rowCount, uint32 fieldCount) : mFieldCount(fieldCount), mRowCount(rowCount) {} + virtual ~QueryResult() {} + virtual bool NextRow() = 0; + Field *Fetch() const { return mCurrentRow; } + const Field & operator [] (int index) const { return mCurrentRow[index]; } + uint32 GetFieldCount() const { return mFieldCount; } uint64 GetRowCount() const { return mRowCount; } + protected: Field *mCurrentRow; uint32 mFieldCount; uint64 mRowCount; }; + typedef std::vector<std::string> QueryFieldNames; + class MANGOS_DLL_SPEC QueryNamedResult { public: explicit QueryNamedResult(QueryResult* query, QueryFieldNames const& names) : mQuery(query), mFieldNames(names) {} ~QueryNamedResult() { delete mQuery; } + // compatible interface with QueryResult bool NextRow() { return mQuery->NextRow(); } Field *Fetch() const { return mQuery->Fetch(); } uint32 GetFieldCount() const { return mQuery->GetFieldCount(); } uint64 GetRowCount() const { return mQuery->GetRowCount(); } Field const& operator[] (int index) const { return (*mQuery)[index]; } + // named access Field const& operator[] (const std::string &name) const { return mQuery->Fetch()[GetField_idx(name)]; } QueryFieldNames const& GetFieldNames() const { return mFieldNames; } + uint32 GetField_idx(const std::string &name) const { for(size_t idx = 0; idx < mFieldNames.size(); ++idx) @@ -60,9 +73,11 @@ class MANGOS_DLL_SPEC QueryNamedResult ASSERT(false && "unknown field name"); return uint32(-1); } + protected: QueryResult *mQuery; QueryFieldNames mFieldNames; }; + #endif diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp index ef8a77ec002..2e4738469c9 100644 --- a/src/shared/Database/QueryResultMysql.cpp +++ b/src/shared/Database/QueryResultMysql.cpp @@ -17,35 +17,47 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #include "DatabaseEnv.h" + QueryResultMysql::QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) : QueryResult(rowCount, fieldCount), mResult(result) { + mCurrentRow = new Field[mFieldCount]; ASSERT(mCurrentRow); + for (uint32 i = 0; i < mFieldCount; i++) mCurrentRow[i].SetType(ConvertNativeType(fields[i].type)); } + QueryResultMysql::~QueryResultMysql() { EndQuery(); } + bool QueryResultMysql::NextRow() { MYSQL_ROW row; + if (!mResult) return false; + row = mysql_fetch_row(mResult); if (!row) { EndQuery(); return false; } + for (uint32 i = 0; i < mFieldCount; i++) mCurrentRow[i].SetValue(row[i]); + return true; } + void QueryResultMysql::EndQuery() { if (mCurrentRow) @@ -53,12 +65,14 @@ void QueryResultMysql::EndQuery() delete [] mCurrentRow; mCurrentRow = 0; } + if (mResult) { mysql_free_result(mResult); mResult = 0; } } + enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysqlType) const { switch (mysqlType) @@ -75,6 +89,7 @@ enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysql case FIELD_TYPE_NULL: return Field::DB_TYPE_STRING; case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: case FIELD_TYPE_INT24: diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h index fb9d422ebf2..89aceb12b13 100644 --- a/src/shared/Database/QueryResultMysql.h +++ b/src/shared/Database/QueryResultMysql.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #if !defined(QUERYRESULTMYSQL_H) #define QUERYRESULTMYSQL_H + #ifdef WIN32 #define FD_SETSIZE 1024 #include <winsock2.h> @@ -27,15 +30,20 @@ #else #include <mysql.h> #endif + class QueryResultMysql : public QueryResult { public: QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount); + ~QueryResultMysql(); + bool NextRow(); + private: enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const; void EndQuery(); + MYSQL_RES *mResult; }; #endif diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp index e4de05c7c77..372baafe278 100644 --- a/src/shared/Database/SQLStorage.cpp +++ b/src/shared/Database/SQLStorage.cpp @@ -17,13 +17,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "SQLStorage.h" #include "SQLStorageImpl.h" + #ifdef DO_POSTGRESQL extern DatabasePostgre WorldDatabase; #else extern DatabaseMysql WorldDatabase; #endif + const char CreatureInfosrcfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiis"; const char CreatureInfodstfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiii"; const char CreatureDataAddonInfofmt[]="iiiiiiis"; @@ -37,6 +40,7 @@ const char ItemPrototypedstfmt[]="iiiisiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii const char PageTextfmt[]="isi"; const char InstanceTemplatesrcfmt[]="iiiiiiffffs"; const char InstanceTemplatedstfmt[]="iiiiiiffffi"; + SQLStorage sCreatureStorage(CreatureInfosrcfmt, CreatureInfodstfmt, "entry","creature_template"); SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon"); SQLStorage sCreatureModelStorage(CreatureModelfmt,"modelid","creature_model_info"); @@ -46,6 +50,7 @@ SQLStorage sGOStorage(GameObjectInfosrcfmt, GameObjectInfodstfmt, "entry","gameo SQLStorage sItemStorage(ItemPrototypesrcfmt, ItemPrototypedstfmt, "entry","item_template"); SQLStorage sPageTextStore(PageTextfmt,"entry","page_text"); SQLStorage sInstanceTemplate(InstanceTemplatesrcfmt, InstanceTemplatedstfmt, "map","instance_template"); + void SQLStorage::Free () { uint32 offset=0; @@ -55,6 +60,7 @@ void SQLStorage::Free () for(uint32 y=0;y<MaxEntry;y++) if(pIndex[y]) delete [] *(char**)((char*)(pIndex[y])+offset); + offset += sizeof(char*); } else if (dst_format[x]==FT_LOGIC) @@ -63,9 +69,11 @@ void SQLStorage::Free () offset += sizeof(char); else offset += 4; + delete [] pIndex; delete [] data; } + void SQLStorage::Load() { SQLStorageLoader loader; diff --git a/src/shared/Database/SQLStorage.h b/src/shared/Database/SQLStorage.h index 96f817c64e7..cc165af532e 100644 --- a/src/shared/Database/SQLStorage.h +++ b/src/shared/Database/SQLStorage.h @@ -17,21 +17,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef SQLSTORAGE_H #define SQLSTORAGE_H + #include "Common.h" #include "Database/DatabaseEnv.h" + class SQLStorage { template<class T> friend struct SQLStorageLoaderBase; + public: + SQLStorage(const char* fmt, const char * _entry_field, const char * sqlname) { src_format = fmt; dst_format = fmt; init(_entry_field, sqlname); } + SQLStorage(const char* src_fmt, const char* dst_fmt, const char * _entry_field, const char * sqlname) { src_format = src_fmt; @@ -39,10 +45,12 @@ class SQLStorage init(_entry_field, sqlname); } + ~SQLStorage() { Free(); } + template<class T> T const* LookupEntry(uint32 id) const { @@ -52,12 +60,16 @@ class SQLStorage return NULL; return reinterpret_cast<T const*>(pIndex[id]); } + uint32 RecordCount; uint32 MaxEntry; uint32 iNumFields; + char const* GetTableName() const { return table; } + void Load(); void Free(); + private: void init(const char * _entry_field, const char * sqlname) { @@ -68,7 +80,9 @@ class SQLStorage iNumFields = strlen(src_format); MaxEntry = 0; } + char** pIndex; + char *data; const char *src_format; const char *dst_format; @@ -76,11 +90,13 @@ class SQLStorage const char *entry_field; //bool HasString; }; + template <class T> struct SQLStorageLoaderBase { public: void Load(SQLStorage &storage); + template<class S, class D> void convert(uint32 field_pos, S src, D &dst); template<class S> @@ -88,13 +104,16 @@ struct SQLStorageLoaderBase template<class D> void convert_from_str(uint32 field_pos, char * src, D& dst); void convert_str_to_str(uint32 field_pos, char *src, char *&dst); + private: template<class V> void storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset); void storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset); }; + struct SQLStorageLoader : public SQLStorageLoaderBase<SQLStorageLoader> { }; + #endif diff --git a/src/shared/Database/SQLStorageImpl.h b/src/shared/Database/SQLStorageImpl.h index c229327a0ec..b511bdad68c 100644 --- a/src/shared/Database/SQLStorageImpl.h +++ b/src/shared/Database/SQLStorageImpl.h @@ -15,15 +15,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "ProgressBar.h" #include "Log.h" #include "DBCFileLoader.h" + template<class T> template<class S, class D> void SQLStorageLoaderBase<T>::convert(uint32 /*field_pos*/, S src, D &dst) { dst = D(src); } + template<class T> void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src, char *&dst) { @@ -39,6 +42,7 @@ void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src memcpy(dst, src, l); } } + template<class T> template<class S> void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, char * & dst) @@ -46,12 +50,14 @@ void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, ch dst = new char[1]; *dst = 0; } + template<class T> template<class D> void SQLStorageLoaderBase<T>::convert_from_str(uint32 /*field_pos*/, char * /*src*/, D& dst) { dst = 0; } + template<class T> template<class V> void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset) @@ -81,6 +87,7 @@ void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, in break; } } + template<class T> void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset) { @@ -109,6 +116,7 @@ void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char * break; } } + template<class T> void SQLStorageLoaderBase<T>::Load(SQLStorage &store) { @@ -120,8 +128,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) sLog.outError("Error loading %s table (not exist?)\n", store.table); exit(1); // Stop server at loading non exited table or not accessable table } + maxi = (*result)[0].GetUInt32()+1; delete result; + result = WorldDatabase.PQuery("SELECT COUNT(*) FROM %s", store.table); if(result) { @@ -131,15 +141,19 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) } else store.RecordCount = 0; + result = WorldDatabase.PQuery("SELECT * FROM %s", store.table); + if(!result) { sLog.outError("%s table is empty!\n", store.table); store.RecordCount = 0; return; } + uint32 recordsize = 0; uint32 offset = 0; + if(store.iNumFields != result->GetFieldCount()) { store.RecordCount = 0; @@ -147,6 +161,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) delete result; exit(1); // Stop server at loading broken or non-compatible table. } + //get struct size uint32 sc=0; uint32 bo=0; @@ -159,8 +174,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) else if (store.dst_format[x]==FT_BYTE) ++bb; recordsize=(store.iNumFields-sc-bo-bb)*4+sc*sizeof(char*)+bo*sizeof(bool)+bb*sizeof(char); + char** newIndex=new char*[maxi]; memset(newIndex,0,maxi*sizeof(char*)); + char * _data= new char[store.RecordCount *recordsize]; uint32 count=0; barGoLink bar( store.RecordCount ); @@ -170,6 +187,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) bar.step(); char *p=(char*)&_data[recordsize*count]; newIndex[fields[0].GetUInt32()]=p; + offset=0; for(uint32 x = 0; x < store.iNumFields; x++) switch(store.src_format[x]) @@ -187,7 +205,9 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) } ++count; }while( result->NextRow() ); + delete result; + store.pIndex = newIndex; store.MaxEntry = maxi; store.data = _data; diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp index bca8dcd8cd6..88b6b85df70 100644 --- a/src/shared/Database/SqlDelayThread.cpp +++ b/src/shared/Database/SqlDelayThread.cpp @@ -17,17 +17,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Database/SqlDelayThread.h" #include "Database/SqlOperations.h" #include "DatabaseEnv.h" + SqlDelayThread::SqlDelayThread(Database* db) : m_dbEngine(db), m_running(true) { } + void SqlDelayThread::run() { #ifndef DO_POSTGRESQL mysql_thread_init(); #endif + while (m_running) { // if the running state gets turned off while sleeping @@ -40,10 +44,12 @@ void SqlDelayThread::run() delete s; } } + #ifndef DO_POSTGRESQL mysql_thread_end(); #endif } + void SqlDelayThread::Stop() { m_running = false; diff --git a/src/shared/Database/SqlDelayThread.h b/src/shared/Database/SqlDelayThread.h index 422b01ac650..3c24d3525b7 100644 --- a/src/shared/Database/SqlDelayThread.h +++ b/src/shared/Database/SqlDelayThread.h @@ -17,26 +17,34 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __SQLDELAYTHREAD_H #define __SQLDELAYTHREAD_H + #include "ace/Thread_Mutex.h" #include "LockedQueue.h" #include "Threading.h" + class Database; class SqlOperation; + class SqlDelayThread : public ACE_Based::Runnable { typedef ACE_Based::LockedQueue<SqlOperation*, ACE_Thread_Mutex> SqlQueue; + private: SqlQueue m_sqlQueue; ///< Queue of SQL statements Database* m_dbEngine; ///< Pointer to used Database engine volatile bool m_running; + SqlDelayThread(); public: SqlDelayThread(Database* db); + ///< Put sql statement to delay queue bool Delay(SqlOperation* sql) { m_sqlQueue.add(sql); return true; } + virtual void Stop(); ///< Stop event virtual void run(); ///< Main Thread loop }; diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp index 9ca698d733e..396f2e36bc2 100644 --- a/src/shared/Database/SqlOperations.cpp +++ b/src/shared/Database/SqlOperations.cpp @@ -17,16 +17,20 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "SqlOperations.h" #include "SqlDelayThread.h" #include "DatabaseEnv.h" #include "DatabaseImpl.h" + /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + void SqlStatement::Execute(Database *db) { /// just do it db->DirectExecute(m_sql); } + void SqlTransaction::Execute(Database *db) { if(m_queue.empty()) @@ -36,6 +40,7 @@ void SqlTransaction::Execute(Database *db) { char const *sql = m_queue.front(); m_queue.pop(); + if(!db->DirectExecute(sql)) { free((void*)const_cast<char*>(sql)); @@ -47,11 +52,14 @@ void SqlTransaction::Execute(Database *db) } return; } + free((void*)const_cast<char*>(sql)); } db->DirectExecute("COMMIT"); } + /// ---- ASYNC QUERIES ---- + void SqlQuery::Execute(Database *db) { if(!m_callback || !m_queue) @@ -61,6 +69,7 @@ void SqlQuery::Execute(Database *db) /// add the callback to the sql result queue of the thread it originated from m_queue->add(m_callback); } + void SqlResultQueue::Update() { /// execute the callbacks waiting in the synchronization queue @@ -71,16 +80,19 @@ void SqlResultQueue::Update() delete callback; } } + bool SqlQueryHolder::Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue) { if(!callback || !thread || !queue) return false; + /// delay the execution of the queries, sync them with the delay thread /// which will in turn resync on execution (via the queue) and call back SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue); thread->Delay(holderEx); return true; } + bool SqlQueryHolder::SetQuery(size_t index, const char *sql) { if(m_queries.size() <= index) @@ -88,16 +100,19 @@ bool SqlQueryHolder::SetQuery(size_t index, const char *sql) sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,(uint32)m_queries.size(),sql); return false; } + if(m_queries[index].first != NULL) { sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])", index,m_queries[index].first,sql); return false; } + /// not executed yet, just stored (it's not called a holder for nothing) m_queries[index] = SqlResultPair(strdup(sql), NULL); return true; } + bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) { if(!format) @@ -105,18 +120,22 @@ bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) sLog.outError("Query (index: %u) is empty.",index); return false; } + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return SetQuery(index,szQuery); } + QueryResult* SqlQueryHolder::GetResult(size_t index) { if(index < m_queries.size()) @@ -133,12 +152,14 @@ QueryResult* SqlQueryHolder::GetResult(size_t index) else return NULL; } + void SqlQueryHolder::SetResult(size_t index, QueryResult *result) { /// store the result in the holder if(index < m_queries.size()) m_queries[index].second = result; } + SqlQueryHolder::~SqlQueryHolder() { for(size_t i = 0; i < m_queries.size(); i++) @@ -153,23 +174,28 @@ SqlQueryHolder::~SqlQueryHolder() } } } + void SqlQueryHolder::SetSize(size_t size) { /// to optimize push_back, reserve the number of queries about to be executed m_queries.resize(size); } + void SqlQueryHolderEx::Execute(Database *db) { if(!m_holder || !m_callback || !m_queue) return; + /// we can do this, we are friends std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries; + for(size_t i = 0; i < queries.size(); i++) { /// execute all queries in the holder and pass the results char const *sql = queries[i].first; if(sql) m_holder->SetResult(i, db->Query(sql)); } + /// sync with the caller thread m_queue->add(m_callback); } diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h index 164c7258ec3..e91d83b6611 100644 --- a/src/shared/Database/SqlOperations.h +++ b/src/shared/Database/SqlOperations.h @@ -17,16 +17,22 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __SQLOPERATIONS_H #define __SQLOPERATIONS_H + #include "Common.h" + #include "ace/Thread_Mutex.h" #include "LockedQueue.h" #include <queue> #include "Utilities/Callback.h" + /// ---- BASE --- + class Database; class SqlDelayThread; + class SqlOperation { public: @@ -34,7 +40,9 @@ class SqlOperation virtual void Execute(Database *db) = 0; virtual ~SqlOperation() {} }; + /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + class SqlStatement : public SqlOperation { private: @@ -44,6 +52,7 @@ class SqlStatement : public SqlOperation ~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); } void Execute(Database *db); }; + class SqlTransaction : public SqlOperation { private: @@ -53,18 +62,22 @@ class SqlTransaction : public SqlOperation void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } void Execute(Database *db); }; + /// ---- ASYNC QUERIES ---- + class SqlQuery; /// contains a single async query class QueryResult; /// the result of one class SqlResultQueue; /// queue for thread sync class SqlQueryHolder; /// groups several async quries class SqlQueryHolderEx; /// points to a holder, added to the delay thread + class SqlResultQueue : public ACE_Based::LockedQueue<MaNGOS::IQueryCallback* , ACE_Thread_Mutex> { public: SqlResultQueue() {} void Update(); }; + class SqlQuery : public SqlOperation { private: @@ -77,6 +90,7 @@ class SqlQuery : public SqlOperation ~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); } void Execute(Database *db); }; + class SqlQueryHolder { friend class SqlQueryHolderEx; @@ -93,6 +107,7 @@ class SqlQueryHolder void SetResult(size_t index, QueryResult *result); bool Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue); }; + class SqlQueryHolderEx : public SqlOperation { private: diff --git a/src/shared/Errors.h b/src/shared/Errors.h index a7784fb7c14..bb17b94cbd0 100644 --- a/src/shared/Errors.h +++ b/src/shared/Errors.h @@ -17,17 +17,22 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef TRINITYCORE_ERRORS_H #define TRINITYCORE_ERRORS_H + #include "Common.h" + #if PLATFORM != PLATFORM_WINDOWS #ifndef HAVE_CONFIG_H #include <config.h> #endif #endif + #ifdef HAVE_ACE_STACK_TRACE_H #include "ace/Stack_Trace.h" #endif + #ifdef HAVE_ACE_STACK_TRACE_H // old versions ACE not have Stack_Trace.h but used at some oS for better compatibility #define WPAssert( assertion ) { if (!(assertion)) { ACE_Stack_Trace st; fprintf( stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__,__FUNCTION__, #assertion, st.c_str()); assert( #assertion &&0 ); } } #else @@ -35,7 +40,9 @@ #endif #define WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( false ); } #define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); } + #define WPFatal( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( #assertion &&0 ); abort(); } + #define ASSERT WPAssert #endif diff --git a/src/shared/LockedQueue.h b/src/shared/LockedQueue.h index 7585989fdd0..6543487da81 100644 --- a/src/shared/LockedQueue.h +++ b/src/shared/LockedQueue.h @@ -15,13 +15,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef LOCKEDQUEUE_H #define LOCKEDQUEUE_H + #include <ace/Guard_T.h> #include <ace/Thread_Mutex.h> #include <deque> #include <assert.h> #include "Errors.h" + namespace ACE_Based { template <class T, class LockType, typename StorageType=std::deque<T> > @@ -29,41 +32,56 @@ namespace ACE_Based { //! Lock access to the queue. LockType _lock; + //! Storage backing the queue. StorageType _queue; + //! Cancellation flag. volatile bool _canceled; + public: + //! Create a LockedQueue. LockedQueue() : _canceled(false) {} + //! Destroy a LockedQueue. virtual ~LockedQueue() { } + //! Adds an item to the queue. void add(const T& item) { ACE_Guard<LockType> g(this->_lock); + //ASSERT(!this->_canceled); // throw Cancellation_Exception(); + _queue.push_back(item); } + //! Gets the next result in the queue, if any. bool next(T& result) { ACE_Guard<LockType> g(this->_lock); + if (_queue.empty()) return false; + //ASSERT (!_queue.empty() || !this->_canceled); // throw Cancellation_Exception(); result = _queue.front(); _queue.pop_front(); + return true; } + //! Cancels the queue. void cancel() { ACE_Guard<LockType> g(this->_lock); + _canceled = true; } + //! Checks if the queue is cancelled. bool cancelled() { diff --git a/src/shared/Log.cpp b/src/shared/Log.cpp index e842949d08e..89544a82426 100644 --- a/src/shared/Log.cpp +++ b/src/shared/Log.cpp @@ -17,14 +17,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "Log.h" #include "Policies/SingletonImp.h" #include "Config/ConfigEnv.h" #include "Util.h" + #include <stdarg.h> #include <stdio.h> + INSTANTIATE_SINGLETON_1( Log ); + Log::Log() : raLogfile(NULL), logfile(NULL), gmLogfile(NULL), charLogfile(NULL), dberLogfile(NULL), chatLogfile(NULL), m_gmlog_per_account(false), m_colored(false) @@ -32,54 +36,68 @@ Log::Log() : { Initialize(); } + Log::~Log() { if( logfile != NULL ) fclose(logfile); logfile = NULL; + if( gmLogfile != NULL ) fclose(gmLogfile); gmLogfile = NULL; + if (charLogfile != NULL) fclose(charLogfile); charLogfile = NULL; + if( dberLogfile != NULL ) fclose(dberLogfile); dberLogfile = NULL; + if (raLogfile != NULL) fclose(raLogfile); raLogfile = NULL; + if (chatLogfile != NULL) fclose(chatLogfile); chatLogfile = NULL; + if (arenaLogFile != NULL) fclose(arenaLogFile); arenaLogFile = NULL; } + void Log::SetLogLevel(char *Level) { int32 NewLevel =atoi((char*)Level); if ( NewLevel <0 ) NewLevel = 0; m_logLevel = NewLevel; + outString( "LogLevel is %u",m_logLevel ); } + void Log::SetLogFileLevel(char *Level) { int32 NewLevel =atoi((char*)Level); if ( NewLevel <0 ) NewLevel = 0; m_logFileLevel = NewLevel; + outString( "LogFileLevel is %u",m_logFileLevel ); } + void Log::SetDBLogLevel(char *Level) { int32 NewLevel = atoi((char*)Level); if ( NewLevel < 0 ) NewLevel = 0; m_dbLogLevel = NewLevel; + outString( "DBLogLevel is %u",m_dbLogLevel ); } + void Log::Initialize() { /// Check whether we'll log GM commands/RA events/character outputs/chat stuffs @@ -87,8 +105,10 @@ void Log::Initialize() m_dbRA = sConfig.GetBoolDefault("LogDB.RA", false); m_dbGM = sConfig.GetBoolDefault("LogDB.GM", false); m_dbChat = sConfig.GetBoolDefault("LogDB.Chat", false); + /// Realm must be 0 by default SetRealmID(0); + /// Common log files data m_logsDir = sConfig.GetStringDefault("LogsDir",""); if(!m_logsDir.empty()) @@ -96,10 +116,13 @@ void Log::Initialize() if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\')) m_logsDir.append("/"); } + m_logsTimestamp = "_" + GetTimestampStr(); + /// Open specific log files logfile = openLogFile("LogFile","LogTimestamp","w"); InitColors(sConfig.GetStringDefault("LogColors", "")); + m_gmlog_per_account = sConfig.GetBoolDefault("GmLogPerAccount",false); if(!m_gmlog_per_account) gmLogfile = openLogFile("GMLogFile","GmLogTimestamp","a"); @@ -110,32 +133,41 @@ void Log::Initialize() if(!m_gmlog_filename_format.empty()) { bool m_gmlog_timestamp = sConfig.GetBoolDefault("GmLogTimestamp",false); + size_t dot_pos = m_gmlog_filename_format.find_last_of("."); if(dot_pos!=m_gmlog_filename_format.npos) { if(m_gmlog_timestamp) m_gmlog_filename_format.insert(dot_pos,m_logsTimestamp); + m_gmlog_filename_format.insert(dot_pos,"_#%u"); } else { m_gmlog_filename_format += "_#%u"; + if(m_gmlog_timestamp) m_gmlog_filename_format += m_logsTimestamp; } + m_gmlog_filename_format = m_logsDir + m_gmlog_filename_format; } } + charLogfile = openLogFile("CharLogFile","CharLogTimestamp","a"); + dberLogfile = openLogFile("DBErrorLogFile",NULL,"a"); raLogfile = openLogFile("RaLogFile",NULL,"a"); chatLogfile = openLogFile("ChatLogFile","ChatLogTimestamp","a"); arenaLogFile = openLogFile("ArenaLogFile",NULL,"a"); + // Main log file settings m_logLevel = sConfig.GetIntDefault("LogLevel", LOGL_NORMAL); m_logFileLevel = sConfig.GetIntDefault("LogFileLevel", LOGL_NORMAL); m_dbLogLevel = sConfig.GetIntDefault("DBLogLevel", LOGL_NORMAL); + m_logFilter = 0; + if(sConfig.GetBoolDefault("LogFilter_TransportMoves", true)) m_logFilter |= LOG_FILTER_TRANSPORT_MOVES; if(sConfig.GetBoolDefault("LogFilter_CreatureMoves", true)) @@ -144,14 +176,18 @@ void Log::Initialize() m_logFilter |= LOG_FILTER_VISIBILITY_CHANGES; if(sConfig.GetBoolDefault("LogFilter_AchievementUpdates", true)) m_logFilter |= LOG_FILTER_ACHIEVEMENT_UPDATES; + // Char log settings m_charLog_Dump = sConfig.GetBoolDefault("CharLogDump", false); + } + FILE* Log::openLogFile(char const* configFileName,char const* configTimeStampFlag, char const* mode) { std::string logfn=sConfig.GetStringDefault(configFileName, ""); if(logfn.empty()) return NULL; + if(configTimeStampFlag && sConfig.GetBoolDefault(configTimeStampFlag,false)) { size_t dot_pos = logfn.find_last_of("."); @@ -160,16 +196,20 @@ FILE* Log::openLogFile(char const* configFileName,char const* configTimeStampFla else logfn += m_logsTimestamp; } + return fopen((m_logsDir+logfn).c_str(), mode); } + FILE* Log::openGmlogPerAccount(uint32 account) { if(m_gmlog_filename_format.empty()) return NULL; + char namebuf[TRINITY_PATH_MAX]; snprintf(namebuf,TRINITY_PATH_MAX,m_gmlog_filename_format.c_str(),account); return fopen(namebuf, "a"); } + void Log::outTimestamp(FILE* file) { time_t t = time(NULL); @@ -182,6 +222,7 @@ void Log::outTimestamp(FILE* file) // SS seconds (2 digits 00-59) fprintf(file,"%-4d-%02d-%02d %02d:%02d:%02d ",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); } + void Log::InitColors(const std::string& str) { if(str.empty()) @@ -189,20 +230,28 @@ void Log::InitColors(const std::string& str) m_colored = false; return; } + int color[4]; + std::istringstream ss(str); + for(uint8 i = 0; i < LogLevels; ++i) { ss >> color[i]; + if(!ss) return; + if(color[i] < 0 || color[i] >= Colors) return; } + for(uint8 i = 0; i < LogLevels; ++i) m_colors[i] = ColorTypes(color[i]); + m_colored = true; } + void Log::SetColor(bool stdout_stream, ColorTypes color) { #if PLATFORM == PLATFORM_WINDOWS @@ -230,6 +279,7 @@ void Log::SetColor(bool stdout_stream, ColorTypes color) // WHITE_BOLD FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY }; + HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE ); SetConsoleTextAttribute(hConsole, WinColorFG[color]); #else @@ -240,16 +290,19 @@ void Log::SetColor(bool stdout_stream, ColorTypes color) TA_BLINK=5, TA_REVERSE=7 }; + enum ANSIFgTextAttr { FG_BLACK=30, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, FG_MAGENTA, FG_CYAN, FG_WHITE, FG_YELLOW }; + enum ANSIBgTextAttr { BG_BLACK=40, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, BG_MAGENTA, BG_CYAN, BG_WHITE }; + static uint8 UnixColorFG[Colors] = { FG_BLACK, // BLACK @@ -268,9 +321,11 @@ void Log::SetColor(bool stdout_stream, ColorTypes color) FG_CYAN, // LCYAN FG_WHITE // LWHITE }; + fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm", UnixColorFG[color], (color >= YELLOW && color < Colors ? ";1" : "")); #endif } + void Log::ResetColor(bool stdout_stream) { #if PLATFORM == PLATFORM_WINDOWS @@ -280,6 +335,7 @@ void Log::ResetColor(bool stdout_stream) fprintf(( stdout_stream ? stdout : stderr ), "\x1b[0m"); #endif } + std::string Log::GetTimestampStr() { time_t t = time(NULL); @@ -294,27 +350,33 @@ std::string Log::GetTimestampStr() snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); return std::string(buf); } + void Log::outDB(LogTypes type, const char * str) { if (!str || type >= MAX_LOG_TYPES) return; + std::string new_str(str); if (new_str.empty()) return; loginDatabase.escape_string(new_str); + loginDatabase.PExecute("INSERT INTO logs (time, realm, type, string) " "VALUES (" UI64FMTD ", %u, %u, '%s');", uint64(time(0)), realm, type, new_str.c_str()); } + void Log::outString(const char * str, ...) { if (!str) return; + if (m_enableLogDB) { // we don't want empty strings in the DB std::string s(str); if (s.empty() || s == " ") return; + va_list ap2; va_start(ap2, str); char nnew_str[MAX_QUERY_LEN]; @@ -322,14 +384,19 @@ void Log::outString(const char * str, ...) outDB(LOG_TYPE_STRING, nnew_str); va_end(ap2); } + if (m_colored) SetColor(true,m_colors[LOGL_NORMAL]); + va_list ap; + va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if (m_colored) ResetColor(true); + printf("\n"); if(logfile) { @@ -338,10 +405,12 @@ void Log::outString(const char * str, ...) vfprintf(logfile, str, ap); fprintf(logfile, "\n"); va_end(ap); + fflush(logfile); } fflush(stdout); } + void Log::outString() { printf("\n"); @@ -353,10 +422,12 @@ void Log::outString() } fflush(stdout); } + void Log::outCrash(const char * err, ...) { if (!err) return; + if (m_enableLogDB) { va_list ap2; @@ -366,31 +437,40 @@ void Log::outCrash(const char * err, ...) outDB(LOG_TYPE_CRASH, nnew_str); va_end(ap2); } + if (m_colored) SetColor(false,LRED); + va_list ap; + va_start(ap, err); vutf8printf(stdout, err, &ap); va_end(ap); + if (m_colored) ResetColor(false); + fprintf(stderr, "\n"); if (logfile) { outTimestamp(logfile); fprintf(logfile, "CRASH ALERT: "); + va_start(ap, err); vfprintf(logfile, err, ap); va_end(ap); + fprintf(logfile, "\n"); fflush(logfile); } fflush(stderr); } + void Log::outError(const char * err, ...) { if (!err) return; + if (m_enableLogDB) { va_list ap2; @@ -400,31 +480,40 @@ void Log::outError(const char * err, ...) outDB(LOG_TYPE_ERROR, nnew_str); va_end(ap2); } + if (m_colored) SetColor(false,LRED); + va_list ap; + va_start(ap, err); vutf8printf(stderr, err, &ap); va_end(ap); + if (m_colored) ResetColor(false); + fprintf( stderr, "\n"); if (logfile) { outTimestamp(logfile); fprintf(logfile, "ERROR: "); + va_start(ap, err); vfprintf(logfile, err, ap); va_end(ap); + fprintf(logfile, "\n"); fflush(logfile); } fflush(stderr); } + void Log::outArena(const char * str, ...) { if (!str) return; + if (arenaLogFile) { va_list ap; @@ -437,44 +526,57 @@ void Log::outArena(const char * str, ...) } fflush(stdout); } + void Log::outErrorDb(const char * err, ...) { if (!err) return; + if (m_colored) SetColor(false,LRED); + va_list ap; + va_start(ap, err); vutf8printf(stderr, err, &ap); va_end(ap); + if (m_colored) ResetColor(false); + fprintf( stderr, "\n" ); + if (logfile) { outTimestamp(logfile); fprintf(logfile, "ERROR: " ); + va_start(ap, err); vfprintf(logfile, err, ap); va_end(ap); + fprintf(logfile, "\n" ); fflush(logfile); } + if (dberLogfile) { outTimestamp(dberLogfile); va_start(ap, err); vfprintf(dberLogfile, err, ap); va_end(ap); + fprintf(dberLogfile, "\n" ); fflush(dberLogfile); } fflush(stderr); } + void Log::outBasic(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbLogLevel > LOGL_NORMAL) { va_list ap2; @@ -484,17 +586,22 @@ void Log::outBasic(const char * str, ...) outDB(LOG_TYPE_BASIC, nnew_str); va_end(ap2); } + if (m_logLevel > LOGL_NORMAL) { if (m_colored) SetColor(true,m_colors[LOGL_BASIC]); + va_list ap; va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if (m_colored) ResetColor(true); + printf("\n"); + if (logfile) { outTimestamp(logfile); @@ -508,10 +615,12 @@ void Log::outBasic(const char * str, ...) } fflush(stdout); } + void Log::outDetail(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbLogLevel > LOGL_BASIC) { va_list ap2; @@ -521,17 +630,22 @@ void Log::outDetail(const char * str, ...) outDB(LOG_TYPE_DETAIL, nnew_str); va_end(ap2); } + if (m_logLevel > LOGL_BASIC) { if (m_colored) SetColor(true,m_colors[LOGL_DETAIL]); + va_list ap; va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if (m_colored) ResetColor(true); + printf("\n"); + if (logfile) { outTimestamp(logfile); @@ -539,24 +653,30 @@ void Log::outDetail(const char * str, ...) va_start(ap, str); vfprintf(logfile, str, ap); va_end(ap); + fprintf(logfile, "\n"); fflush(logfile); } } + fflush(stdout); } + void Log::outDebugInLine(const char * str, ...) { if (!str) return; + if (m_logLevel > LOGL_DETAIL) { va_list ap; va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + //if(m_colored) // ResetColor(true); + if (logfile) { va_list ap; @@ -566,10 +686,12 @@ void Log::outDebugInLine(const char * str, ...) } } } + void Log::outDebug(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbLogLevel > LOGL_DETAIL) { va_list ap2; @@ -579,17 +701,22 @@ void Log::outDebug(const char * str, ...) outDB(LOG_TYPE_DEBUG, nnew_str); va_end(ap2); } + if( m_logLevel > LOGL_DETAIL ) { if (m_colored) SetColor(true,m_colors[LOGL_DEBUG]); + va_list ap; va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if(m_colored) ResetColor(true); + printf( "\n" ); + if (logfile) { outTimestamp(logfile); @@ -597,20 +724,25 @@ void Log::outDebug(const char * str, ...) va_start(ap, str); vfprintf(logfile, str, ap); va_end(ap); + fprintf(logfile, "\n" ); fflush(logfile); } } fflush(stdout); } + void Log::outStringInLine(const char * str, ...) { if (!str) return; + va_list ap; + va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if (logfile) { va_start(ap, str); @@ -618,10 +750,12 @@ void Log::outStringInLine(const char * str, ...) va_end(ap); } } + void Log::outCommand(uint32 account, const char * str, ...) { if (!str) return; + // TODO: support accountid if (m_enableLogDB && m_dbGM) { @@ -632,17 +766,22 @@ void Log::outCommand(uint32 account, const char * str, ...) outDB(LOG_TYPE_GM, nnew_str); va_end(ap2); } + if (m_logLevel > LOGL_NORMAL) { if (m_colored) SetColor(true,m_colors[LOGL_BASIC]); + va_list ap; va_start(ap, str); vutf8printf(stdout, str, &ap); va_end(ap); + if (m_colored) ResetColor(true); + printf("\n"); + if (logfile) { outTimestamp(logfile); @@ -654,6 +793,7 @@ void Log::outCommand(uint32 account, const char * str, ...) fflush(logfile); } } + if (m_gmlog_per_account) { if (FILE* per_file = openGmlogPerAccount (account)) @@ -677,12 +817,15 @@ void Log::outCommand(uint32 account, const char * str, ...) va_end(ap); fflush(gmLogfile); } + fflush(stdout); } + void Log::outChar(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbChar) { va_list ap2; @@ -692,6 +835,7 @@ void Log::outChar(const char * str, ...) outDB(LOG_TYPE_CHAR, nnew_str); va_end(ap2); } + if (charLogfile) { outTimestamp(charLogfile); @@ -703,6 +847,7 @@ void Log::outChar(const char * str, ...) fflush(charLogfile); } } + void Log::outCharDump(const char * str, uint32 account_id, uint32 guid, const char * name) { if (charLogfile) @@ -711,10 +856,12 @@ void Log::outCharDump(const char * str, uint32 account_id, uint32 guid, const ch fflush(charLogfile); } } + void Log::outRemote(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbRA) { va_list ap2; @@ -724,6 +871,7 @@ void Log::outRemote(const char * str, ...) outDB(LOG_TYPE_RA, nnew_str); va_end(ap2); } + if (raLogfile) { outTimestamp(raLogfile); @@ -736,10 +884,12 @@ void Log::outRemote(const char * str, ...) } fflush(stdout); } + void Log::outChat(const char * str, ...) { if (!str) return; + if (m_enableLogDB && m_dbChat) { va_list ap2; @@ -749,6 +899,7 @@ void Log::outChat(const char * str, ...) outDB(LOG_TYPE_CHAT, nnew_str); va_end(ap2); } + if (chatLogfile) { outTimestamp(chatLogfile); @@ -761,59 +912,74 @@ void Log::outChat(const char * str, ...) } fflush(stdout); } + void outstring_log(const char * str, ...) { if (!str) return; + char buf[256]; va_list ap; va_start(ap, str); vsnprintf(buf,256, str, ap); va_end(ap); + Trinity::Singleton<Log>::Instance().outString(buf); } + void detail_log(const char * str, ...) { if (!str) return; + char buf[256]; va_list ap; va_start(ap, str); vsnprintf(buf,256, str, ap); va_end(ap); + Trinity::Singleton<Log>::Instance().outDetail(buf); } + void debug_log(const char * str, ...) { if (!str) return; + char buf[256]; va_list ap; va_start(ap, str); vsnprintf(buf,256, str, ap); va_end(ap); + Trinity::Singleton<Log>::Instance().outDebug(buf); } + void error_log(const char * str, ...) { if (!str) return; + char buf[256]; va_list ap; va_start(ap, str); vsnprintf(buf,256, str, ap); va_end(ap); + Trinity::Singleton<Log>::Instance().outError(buf); } + void error_db_log(const char * str, ...) { if (!str) return; + char buf[256]; va_list ap; va_start(ap, str); vsnprintf(buf,256, str, ap); va_end(ap); + Trinity::Singleton<Log>::Instance().outErrorDb(buf); } diff --git a/src/shared/Log.h b/src/shared/Log.h index ff6f3ad39ee..283b8819a10 100644 --- a/src/shared/Log.h +++ b/src/shared/Log.h @@ -17,12 +17,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef TRINITYCORE_LOG_H #define TRINITYCORE_LOG_H + #include "Common.h" #include "Policies/Singleton.h" #include "Database/DatabaseEnv.h" + class Config; + enum LogFilters { LOG_FILTER_TRANSPORT_MOVES = 1, @@ -30,6 +34,7 @@ enum LogFilters LOG_FILTER_VISIBILITY_CHANGES = 4, LOG_FILTER_ACHIEVEMENT_UPDATES = 8 }; + enum LogTypes { LOG_TYPE_STRING = 0, @@ -45,6 +50,7 @@ enum LogTypes LOG_TYPE_CHAT = 10, MAX_LOG_TYPES }; + enum LogLevel { LOGL_NORMAL = 0, @@ -52,7 +58,9 @@ enum LogLevel LOGL_DETAIL, LOGL_DEBUG }; + const int LogLevels = int(LOGL_DEBUG)+1; + enum ColorTypes { BLACK, @@ -71,17 +79,22 @@ enum ColorTypes LCYAN, WHITE }; + const int Colors = int(WHITE)+1; + class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_Thread_Mutex> > { friend class Trinity::OperatorNew<Log>; Log(); ~Log(); + public: void Initialize(); + void InitColors(const std::string& init_str); void SetColor(bool stdout_stream, ColorTypes color); void ResetColor(bool stdout_stream); + void outDB( LogTypes type, const char * str ); void outString( const char * str, ... ) ATTR_PRINTF(2,3); void outString( ); @@ -97,17 +110,21 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_ void outCommand( uint32 account, const char * str, ...) ATTR_PRINTF(3,4); void outRemote( const char * str, ... ) ATTR_PRINTF(2,3); void outChat( const char * str, ... ) ATTR_PRINTF(2,3); - void outArena( const char * str, ... ) ATTR_PRINTF(2,3); + void outArena( const char * str, ... ) ATTR_PRINTF(2,3); void outCharDump( const char * str, uint32 account_id, uint32 guid, const char * name ); + static void outTimestamp(FILE* file); static std::string GetTimestampStr(); + void SetLogLevel(char * Level); void SetLogFileLevel(char * Level); void SetDBLogLevel(char * Level); void SetRealmID(uint32 id) { realm = id; } + uint32 getLogFilter() const { return m_logFilter; } bool IsOutDebug() const { return m_logLevel > 2 || (m_logFileLevel > 2 && logfile); } bool IsOutCharDump() const { return m_charLog_Dump; } + bool GetLogDB() { return m_enableLogDB; } bool GetLogDBLater() { return m_enableLogDBLater; } void SetLogDB(bool enable) { m_enableLogDB = enable; } @@ -115,6 +132,7 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_ private: FILE* openLogFile(char const* configFileName,char const* configTimeStampFlag, char const* mode); FILE* openGmlogPerAccount(uint32 account); + FILE* raLogfile; FILE* logfile; FILE* gmLogfile; @@ -122,18 +140,23 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_ FILE* dberLogfile; FILE* chatLogfile; FILE* arenaLogFile; + // cache values for after initilization use (like gm log per account case) std::string m_logsDir; std::string m_logsTimestamp; + // gm log control bool m_gmlog_per_account; std::string m_gmlog_filename_format; + bool m_enableLogDBLater; bool m_enableLogDB; uint32 realm; + // log coloring bool m_colored; ColorTypes m_colors[4]; + // log levels: // 0 minimum/string, 1 basic/error, 2 detail, 3 full/debug uint8 m_dbLogLevel; @@ -146,12 +169,15 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_ bool m_dbChat; bool m_charLog_Dump; }; + #define sLog Trinity::Singleton<Log>::Instance() + #ifdef TRINITY_DEBUG #define DEBUG_LOG Trinity::Singleton<Log>::Instance().outDebug #else #define DEBUG_LOG #endif + // primary for script library void TRINITY_DLL_SPEC outstring_log(const char * str, ...) ATTR_PRINTF(1,2); void TRINITY_DLL_SPEC detail_log(const char * str, ...) ATTR_PRINTF(1,2); diff --git a/src/shared/ProgressBar.cpp b/src/shared/ProgressBar.cpp index 509e24b4a5b..2eba31c2c40 100644 --- a/src/shared/ProgressBar.cpp +++ b/src/shared/ProgressBar.cpp @@ -17,19 +17,24 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <stdio.h> + #include "ProgressBar.h" + char const* const barGoLink::empty = " "; #ifdef _WIN32 char const* const barGoLink::full = "\x3D"; #else char const* const barGoLink::full = "*"; #endif + barGoLink::~barGoLink() { printf( "\n" ); fflush(stdout); } + barGoLink::barGoLink( int row_count ) { rec_no = 0; @@ -49,9 +54,11 @@ barGoLink::barGoLink( int row_count ) #endif fflush(stdout); } + void barGoLink::step( void ) { int i, n; + if ( num_rec == 0 ) return; ++rec_no; n = rec_no * indic_len / num_rec; @@ -71,6 +78,7 @@ void barGoLink::step( void ) printf( "] %i%% \r[", (int)percent); #endif fflush(stdout); + rec_pos = n; } } diff --git a/src/shared/ProgressBar.h b/src/shared/ProgressBar.h index 29cca9b2a31..af7b5e03093 100644 --- a/src/shared/ProgressBar.h +++ b/src/shared/ProgressBar.h @@ -19,16 +19,21 @@ */ #ifndef TRINITYCORE_PROGRESSBAR_H #define TRINITYCORE_PROGRESSBAR_H + #include "Platform/Define.h" + class TRINITY_DLL_SPEC barGoLink { static char const * const empty; static char const * const full; + int rec_no; int rec_pos; int num_rec; int indic_len; + public: + void step( void ); barGoLink( int ); ~barGoLink(); diff --git a/src/shared/ServiceWin32.cpp b/src/shared/ServiceWin32.cpp index dc374a4ee41..5c68ba31954 100644 --- a/src/shared/ServiceWin32.cpp +++ b/src/shared/ServiceWin32.cpp @@ -17,12 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + #ifdef WIN32 + #include "Common.h" #include "Log.h" #include <cstring> #include <windows.h> #include <winsvc.h> + #if !defined(WINADVAPI) #if !defined(_ADVAPI32_) #define WINADVAPI DECLSPEC_IMPORT @@ -30,19 +33,26 @@ #define WINADVAPI #endif #endif + extern int main(int argc, char ** argv); extern char serviceLongName[]; extern char serviceName[]; extern char serviceDescription[]; + extern int m_ServiceStatus; + SERVICE_STATUS serviceStatus; + SERVICE_STATUS_HANDLE serviceStatusHandle = 0; + typedef WINADVAPI BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID); + bool WinServiceInstall() { CSD_T ChangeService_Config2; HMODULE advapi32; SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (serviceControlManager) { char path[_MAX_PATH + 10]; @@ -73,6 +83,7 @@ bool WinServiceInstall() CloseServiceHandle(serviceControlManager); return false; } + ChangeService_Config2 = (CSD_T) GetProcAddress(advapi32, "ChangeServiceConfig2A"); if (!ChangeService_Config2) { @@ -80,12 +91,14 @@ bool WinServiceInstall() CloseServiceHandle(serviceControlManager); return false; } + SERVICE_DESCRIPTION sdBuf; sdBuf.lpDescription = serviceDescription; ChangeService_Config2( service, // handle to service SERVICE_CONFIG_DESCRIPTION, // change: description &sdBuf); // new data + SC_ACTION _action[1]; _action[0].Type = SC_ACTION_RESTART; _action[0].Delay = 10000; @@ -98,16 +111,20 @@ bool WinServiceInstall() service, // handle to service SERVICE_CONFIG_FAILURE_ACTIONS, // information level &sfa); // new data + CloseServiceHandle(service); + } } CloseServiceHandle(serviceControlManager); } return true; } + bool WinServiceUninstall() { SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (serviceControlManager) { SC_HANDLE service = OpenService(serviceControlManager, @@ -122,32 +139,39 @@ bool WinServiceUninstall() } CloseServiceHandle(service); } + CloseServiceHandle(serviceControlManager); } return true; } + void WINAPI ServiceControlHandler(DWORD controlCode) { switch (controlCode) { case SERVICE_CONTROL_INTERROGATE: break; + case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); + m_ServiceStatus = 0; return; + case SERVICE_CONTROL_PAUSE: m_ServiceStatus = 2; serviceStatus.dwCurrentState = SERVICE_PAUSED; SetServiceStatus(serviceStatusHandle, &serviceStatus); break; + case SERVICE_CONTROL_CONTINUE: serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(serviceStatusHandle, &serviceStatus); m_ServiceStatus = 1; break; + default: if ( controlCode >= 128 && controlCode <= 255 ) // user defined control code @@ -156,8 +180,10 @@ void WINAPI ServiceControlHandler(DWORD controlCode) // unrecognized control code break; } + SetServiceStatus(serviceStatusHandle, &serviceStatus); } + void WINAPI ServiceMain(DWORD argc, char *argv[]) { // initialise service status @@ -168,42 +194,56 @@ void WINAPI ServiceMain(DWORD argc, char *argv[]) serviceStatus.dwServiceSpecificExitCode = NO_ERROR; serviceStatus.dwCheckPoint = 0; serviceStatus.dwWaitHint = 0; + serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler); + if ( serviceStatusHandle ) { char path[_MAX_PATH + 1]; unsigned int i, last_slash = 0; + GetModuleFileName(0, path, sizeof(path)/sizeof(path[0])); + for (i = 0; i < std::strlen(path); i++) { if (path[i] == '\\') last_slash = i; } + path[last_slash] = 0; + // service is starting serviceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); + // do initialisation here SetCurrentDirectory(path); + // running serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus( serviceStatusHandle, &serviceStatus ); + //////////////////////// // service main cycle // //////////////////////// + m_ServiceStatus = 1; argc = 1; main(argc , argv); + // service was stopped serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(serviceStatusHandle, &serviceStatus); + // do cleanup here + // service is now stopped serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); serviceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(serviceStatusHandle, &serviceStatus); } } + bool WinServiceRun() { SERVICE_TABLE_ENTRY serviceTable[] = @@ -211,6 +251,7 @@ bool WinServiceRun() { serviceName, ServiceMain }, { 0, 0 } }; + if (!StartServiceCtrlDispatcher(serviceTable)) { sLog.outError("StartService Failed. Error [%u]", ::GetLastError()); diff --git a/src/shared/ServiceWin32.h b/src/shared/ServiceWin32.h index a62526f4c70..18a52c396ea 100644 --- a/src/shared/ServiceWin32.h +++ b/src/shared/ServiceWin32.h @@ -17,12 +17,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + #ifdef WIN32 #ifndef _WIN32_SERVICE_ #define _WIN32_SERVICE_ + bool WinServiceInstall(); bool WinServiceUninstall(); bool WinServiceRun(); + #endif // _WIN32_SERVICE_ #endif // WIN32 diff --git a/src/shared/SystemConfig.h b/src/shared/SystemConfig.h index 0c13cd286ec..9d154b863d0 100644 --- a/src/shared/SystemConfig.h +++ b/src/shared/SystemConfig.h @@ -18,19 +18,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + // THIS FILE IS DEPRECATED + #ifndef TRINITY_SYSTEMCONFIG_H #define TRINITY_SYSTEMCONFIG_H + #include "Platform/Define.h" #include "revision.h" + #define _PACKAGENAME "TrinityCore2 " #define _CODENAME "YUME" + #if TRINITY_ENDIAN == TRINITY_BIGENDIAN # define _ENDIAN_STRING "big-endian" #else # define _ENDIAN_STRING "little-endian" #endif + #if PLATFORM == PLATFORM_WINDOWS # ifdef _WIN64 # define _FULLVERSION _PACKAGENAME "Rev: " _REVISION " Hash: " _HASH " (Win64," _ENDIAN_STRING ")" @@ -40,6 +46,7 @@ #else # define _FULLVERSION _PACKAGENAME "Rev: " _REVISION " Hash: " _HASH " (Unix," _ENDIAN_STRING ")" #endif + #define DEFAULT_PLAYER_LIMIT 100 #define DEFAULT_WORLDSERVER_PORT 8085 //8129 #define DEFAULT_REALMSERVER_PORT 3724 diff --git a/src/shared/Threading.cpp b/src/shared/Threading.cpp index e0c605ee9b4..90861b8f425 100644 --- a/src/shared/Threading.cpp +++ b/src/shared/Threading.cpp @@ -15,27 +15,36 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Threading.h" #include "Errors.h" #include <ace/OS_NS_unistd.h> #include <ace/Sched_Params.h> #include <vector> + using namespace ACE_Based; + ThreadPriority::ThreadPriority() { for (int i = Idle; i < MAXPRIORITYNUM; ++i) m_priority[i] = ACE_THR_PRI_OTHER_DEF; + m_priority[Idle] = ACE_Sched_Params::priority_min(ACE_SCHED_OTHER); m_priority[Realtime] = ACE_Sched_Params::priority_max(ACE_SCHED_OTHER); + std::vector<int> _tmp; + ACE_Sched_Params::Policy _policy = ACE_SCHED_OTHER; ACE_Sched_Priority_Iterator pr_iter(_policy); + while (pr_iter.more()) { _tmp.push_back(pr_iter.priority()); pr_iter.next(); } + ASSERT (!_tmp.empty()); + if(_tmp.size() >= MAXPRIORITYNUM) { const size_t max_pos = _tmp.size(); @@ -49,6 +58,7 @@ ThreadPriority::ThreadPriority() break; } } + //since we have only 7(seven) values in enum Priority //and 3 we know already (Idle, Normal, Realtime) so //we need to split each list [Idle...Normal] and [Normal...Realtime] @@ -57,103 +67,140 @@ ThreadPriority::ThreadPriority() size_t _div = (norm_pos - min_pos) / _divider; if(_div == 0) _div = 1; + min_pos = (norm_pos - 1); + m_priority[Low] = _tmp[min_pos -= _div]; m_priority[Lowest] = _tmp[min_pos -= _div ]; + _div = (max_pos - norm_pos) / _divider; if(_div == 0) _div = 1; + min_pos = norm_pos - 1; + m_priority[High] = _tmp[min_pos += _div]; m_priority[Highest] = _tmp[min_pos += _div]; } } + int ThreadPriority::getPriority(Priority p) const { if(p < Idle) p = Idle; + if(p > Realtime) p = Realtime; + return m_priority[p]; } + #define THREADFLAG (THR_NEW_LWP | THR_SCHED_DEFAULT| THR_JOINABLE) + Thread::Thread() : m_task(0), m_iThreadId(0), m_hThreadHandle(0) { + } + Thread::Thread(Runnable* instance) : m_task(instance), m_iThreadId(0), m_hThreadHandle(0) { // register reference to m_task to prevent it deeltion until destructor if (m_task) m_task->incReference(); + bool _start = start(); ASSERT (_start); } + Thread::~Thread() { //Wait(); + // deleted runnable object (if no other references) if (m_task) m_task->decReference(); } + //initialize Thread's class static member Thread::ThreadStorage Thread::m_ThreadStorage; ThreadPriority Thread::m_TpEnum; + bool Thread::start() { if (m_task == 0 || m_iThreadId != 0) return false; + bool res = (ACE_Thread::spawn(&Thread::ThreadTask, (void*)m_task, THREADFLAG, &m_iThreadId, &m_hThreadHandle) == 0); + if (res) m_task->incReference(); + return res; } + bool Thread::wait() { if (!m_hThreadHandle || !m_task) return false; + ACE_THR_FUNC_RETURN _value = ACE_THR_FUNC_RETURN(-1); int _res = ACE_Thread::join(m_hThreadHandle, &_value); + m_iThreadId = 0; m_hThreadHandle = 0; + return (_res == 0); } + void Thread::destroy() { if (!m_iThreadId || !m_task) return; + if (ACE_Thread::kill(m_iThreadId, -1) != 0) return; + m_iThreadId = 0; m_hThreadHandle = 0; + // reference set at ACE_Thread::spawn m_task->decReference(); } + void Thread::suspend() { ACE_Thread::suspend(m_hThreadHandle); } + void Thread::resume() { ACE_Thread::resume(m_hThreadHandle); } + ACE_THR_FUNC_RETURN Thread::ThreadTask(void * param) { Runnable * _task = (Runnable*)param; _task->run(); - // task execution complete, free referecne added at + + // task execution complete, free referecne added at _task->decReference(); + return (ACE_THR_FUNC_RETURN)0; } + ACE_thread_t Thread::currentId() { return ACE_Thread::self(); } + ACE_hthread_t Thread::currentHandle() { ACE_hthread_t _handle; ACE_Thread::self(_handle); + return _handle; } + Thread * Thread::current() { Thread * _thread = m_ThreadStorage.ts_object(); @@ -162,12 +209,15 @@ Thread * Thread::current() _thread = new Thread(); _thread->m_iThreadId = Thread::currentId(); _thread->m_hThreadHandle = Thread::currentHandle(); + Thread * _oldValue = m_ThreadStorage.ts_object(_thread); if(_oldValue) delete _oldValue; } + return _thread; } + void Thread::setPriority(Priority type) { int _priority = m_TpEnum.getPriority(type); @@ -175,6 +225,7 @@ void Thread::setPriority(Priority type) //remove this ASSERT in case you don't want to know is thread priority change was successful or not ASSERT (_ok == 0); } + void Thread::Sleep(unsigned long msecs) { ACE_OS::sleep(ACE_Time_Value(0, 1000 * msecs)); diff --git a/src/shared/Threading.h b/src/shared/Threading.h index 36d35ff9f79..574b9641aac 100644 --- a/src/shared/Threading.h +++ b/src/shared/Threading.h @@ -15,19 +15,24 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef THREADING_H #define THREADING_H + #include <ace/Thread.h> #include <ace/TSS_T.h> #include "ace/Atomic_Op.h" #include <assert.h> + namespace ACE_Based { + class Runnable { public: virtual ~Runnable() {} virtual void run() = 0; + void incReference() { ++m_refs; } void decReference() { @@ -37,6 +42,7 @@ namespace ACE_Based private: ACE_Atomic_Op<ACE_Thread_Mutex, int> m_refs; }; + enum Priority { Idle, @@ -47,43 +53,56 @@ namespace ACE_Based Highest, Realtime, }; + #define MAXPRIORITYNUM (Realtime + 1) + class ThreadPriority { public: ThreadPriority(); int getPriority(Priority p) const; + private: int m_priority[MAXPRIORITYNUM]; }; + class Thread { public: Thread(); explicit Thread(Runnable* instance); ~Thread(); + bool start(); bool wait(); void destroy(); + void suspend(); void resume(); + void setPriority(Priority type); + static void Sleep(unsigned long msecs); static ACE_thread_t currentId(); static ACE_hthread_t currentHandle(); static Thread * current(); + private: Thread(const Thread&); Thread& operator=(const Thread&); + static ACE_THR_FUNC_RETURN ThreadTask(void * param); + ACE_thread_t m_iThreadId; ACE_hthread_t m_hThreadHandle; Runnable * m_task; + typedef ACE_TSS<Thread> ThreadStorage; //global object - container for Thread class representation of every thread static ThreadStorage m_ThreadStorage; //use this object to determine current OS thread priority values mapped to enum Priority{} static ThreadPriority m_TpEnum; }; + } #endif diff --git a/src/shared/Timer.h b/src/shared/Timer.h index 03a379b2471..82f5be161d9 100644 --- a/src/shared/Timer.h +++ b/src/shared/Timer.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef TRINITY_TIMER_H #define TRINITY_TIMER_H + #include "Platform/CompilerDefs.h" + #if PLATFORM == PLATFORM_WINDOWS # include <ace/config-all.h> # include <mmsystem.h> @@ -31,6 +34,7 @@ # include <sys/time.h> # include <sys/timeb.h> #endif + #if PLATFORM == PLATFORM_WINDOWS inline uint32 getMSTime() { return GetTickCount(); } #else @@ -42,6 +46,7 @@ inline uint32 getMSTime() return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); } #endif + inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) { // getMSTime() have limited data range and this is case when it overflow in this tick @@ -50,21 +55,26 @@ inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) else return newMSTime - oldMSTime; } + class IntervalTimer { public: IntervalTimer() : _interval(0), _current(0) {} + void Update(time_t diff) { _current += diff; if(_current<0) _current=0;} bool Passed() { return _current >= _interval; } void Reset() { if(_current >= _interval) _current -= _interval; } + void SetCurrent(time_t current) { _current = current; } void SetInterval(time_t interval) { _interval = interval; } time_t GetInterval() const { return _interval; } time_t GetCurrent() const { return _current; } + private: time_t _interval; time_t _current; }; + struct TimeTracker { TimeTracker(time_t expiry) : i_expiryTime(expiry) {} @@ -74,6 +84,7 @@ struct TimeTracker time_t GetExpiry(void) const { return i_expiryTime; } time_t i_expiryTime; }; + struct TimeTrackerSmall { TimeTrackerSmall(int32 expiry) : i_expiryTime(expiry) {} @@ -83,5 +94,6 @@ struct TimeTrackerSmall int32 GetExpiry(void) const { return i_expiryTime; } int32 i_expiryTime; }; + #endif diff --git a/src/shared/Util.cpp b/src/shared/Util.cpp index 0f7386e4d33..ede7a27ea7b 100644 --- a/src/shared/Util.cpp +++ b/src/shared/Util.cpp @@ -17,14 +17,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Util.h" + #include "sockets/socket_include.h" #include "utf8cpp/utf8.h" #include "mersennetwister/MersenneTwister.h" #include <ace/TSS_T.h> + typedef ACE_TSS<MTRand> MTRandTSS; static MTRandTSS mtRand; + #ifdef MULTI_THREAD_MAP + int32 irand (int32 min, int32 max) { int32 result; @@ -34,6 +39,7 @@ int32 irand (int32 min, int32 max) } return result; } + uint32 urand (uint32 min, uint32 max) { uint32 result; @@ -43,6 +49,7 @@ uint32 urand (uint32 min, uint32 max) } return result; } + int32 rand32 () { int32 result; @@ -52,6 +59,7 @@ int32 rand32 () } return result; } + double rand_norm(void) { double result; @@ -61,6 +69,7 @@ double rand_norm(void) } return result; } + double rand_chance (void) { double result; @@ -70,28 +79,36 @@ double rand_chance (void) } return result; } + #else + int32 irand (int32 min, int32 max) { return int32 (mtRand->randInt (max - min)) + min; } + uint32 urand (uint32 min, uint32 max) { return mtRand->randInt (max - min) + min; } + int32 rand32 () { return mtRand->randInt (); } + double rand_norm(void) { return mtRand->randExc (); } + double rand_chance (void) { return mtRand->randExc (100.0); } + #endif + Tokens StrSplit(const std::string &src, const std::string &sep) { Tokens r; @@ -111,10 +128,13 @@ Tokens StrSplit(const std::string &src, const std::string &sep) if (s.length()) r.push_back(s); return r; } + void stripLineInvisibleChars(std::string &str) { static std::string invChars = " \t\7\n"; + size_t wpos = 0; + bool space = false; for(size_t pos = 0; pos < str.size(); ++pos) { @@ -135,17 +155,21 @@ void stripLineInvisibleChars(std::string &str) space = false; } } + if(wpos < str.size()) str.erase(wpos,str.size()); if(str.find("|TInterface")!=std::string::npos) str.clear(); + } + std::string secsToTimeString(uint32 timeInSecs, bool shortText, bool hoursOnly) { uint32 secs = timeInSecs % MINUTE; uint32 minutes = timeInSecs % HOUR / MINUTE; uint32 hours = timeInSecs % DAY / HOUR; uint32 days = timeInSecs / DAY; + std::ostringstream ss; if(days) ss << days << (shortText ? "d" : " Day(s) "); @@ -158,13 +182,16 @@ std::string secsToTimeString(uint32 timeInSecs, bool shortText, bool hoursOnly) if(secs || (!days && !hours && !minutes) ) ss << secs << (shortText ? "s" : " Second(s)."); } + return ss.str(); } + uint32 TimeStringToSecs(const std::string& timestring) { uint32 secs = 0; uint32 buffer = 0; uint32 multiplier = 0; + for(std::string::const_iterator itr = timestring.begin(); itr != timestring.end(); itr++ ) { if(isdigit(*itr)) @@ -187,8 +214,10 @@ uint32 TimeStringToSecs(const std::string& timestring) buffer=0; } } + return secs; } + std::string TimeToTimestampStr(time_t t) { tm* aTm = localtime(&t); @@ -202,30 +231,37 @@ std::string TimeToTimestampStr(time_t t) snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); return std::string(buf); } + /// Check if the string is a valid ip address representation bool IsIPAddress(char const* ipaddress) { if(!ipaddress) return false; + // Let the big boys do it. // Drawback: all valid ip address formats are recognized e.g.: 12.23,121234,0xABCD) return inet_addr(ipaddress) != INADDR_NONE; } + /// create PID file uint32 CreatePIDFile(const std::string& filename) { FILE * pid_file = fopen (filename.c_str(), "w" ); if (pid_file == NULL) return 0; + #ifdef WIN32 DWORD pid = GetCurrentProcessId(); #else pid_t pid = getpid(); #endif + fprintf(pid_file, "%d", pid ); fclose(pid_file); + return (uint32)pid; } + size_t utf8length(std::string& utf8str) { try @@ -238,6 +274,7 @@ size_t utf8length(std::string& utf8str) return 0; } } + void utf8truncate(std::string& utf8str,size_t len) { try @@ -245,6 +282,7 @@ void utf8truncate(std::string& utf8str,size_t len) size_t wlen = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size()); if(wlen <= len) return; + std::wstring wstr; wstr.resize(wlen); utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]); @@ -257,6 +295,7 @@ void utf8truncate(std::string& utf8str,size_t len) utf8str = ""; } } + bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize) { try @@ -269,6 +308,7 @@ bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize) wsize = 0; return false; } + wsize = len; utf8::utf8to16(utf8str,utf8str+csize,wstr); wstr[len] = L'\0'; @@ -280,14 +320,17 @@ bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize) wsize = 0; return false; } + return true; } + bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr) { try { size_t len = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size()); wstr.resize(len); + utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]); } catch(std::exception) @@ -295,14 +338,17 @@ bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr) wstr = L""; return false; } + return true; } + bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str) { try { std::string utf8str2; utf8str2.resize(size*4); // allocate for most long case + char* oend = utf8::utf16to8(wstr,wstr+size,&utf8str2[0]); utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail utf8str = utf8str2; @@ -312,14 +358,17 @@ bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str) utf8str = ""; return false; } + return true; } + bool WStrToUtf8(std::wstring wstr, std::string& utf8str) { try { std::string utf8str2; utf8str2.resize(wstr.size()*4); // allocate for most long case + char* oend = utf8::utf16to8(wstr.c_str(),wstr.c_str()+wstr.size(),&utf8str2[0]); utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail utf8str = utf8str2; @@ -329,15 +378,20 @@ bool WStrToUtf8(std::wstring wstr, std::string& utf8str) utf8str = ""; return false; } + return true; } + typedef wchar_t const* const* wstrlist; + std::wstring GetMainPartOfName(std::wstring wname, uint32 declension) { // supported only Cyrillic cases if(wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5) return wname; + // Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently) + static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430),wchar_t(0x0000)}; static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E),wchar_t(0x0000)}; static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F),wchar_t(0x0000)}; @@ -354,6 +408,7 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension) static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435),wchar_t(0x043C),wchar_t(0x0000)}; static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C),wchar_t(0x0000)}; static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439),wchar_t(0x0000)}; + static wchar_t const* const dropEnds[6][8] = { { &a_End[1], &o_End[1], &ya_End[1], &ie_End[1], &soft_End[1], &j_End[1], NULL, NULL }, { &a_End[1], &ya_End[1], &yeru_End[1], &i_End[1], NULL, NULL, NULL, NULL }, @@ -362,34 +417,42 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension) { &oj_End[1], &io_j_End[1], &ie_j_End[1], &o_m_End[1], &io_m_End[1], &ie_m_End[1], &yu_End[1], NULL }, { &ie_End[1], &i_End[1], NULL, NULL, NULL, NULL, NULL, NULL } }; + for(wchar_t const * const* itr = &dropEnds[declension][0]; *itr; ++itr) { size_t len = size_t((*itr)[-1]); // get length from string size field + if(wname.substr(wname.size()-len,len)==*itr) return wname.substr(0,wname.size()-len); } + return wname; } + bool utf8ToConsole(const std::string& utf8str, std::string& conStr) { #if PLATFORM == PLATFORM_WINDOWS std::wstring wstr; if(!Utf8toWStr(utf8str,wstr)) return false; + conStr.resize(wstr.size()); CharToOemBuffW(&wstr[0],&conStr[0],wstr.size()); #else // not implemented yet conStr = utf8str; #endif + return true; } + bool consoleToUtf8(const std::string& conStr,std::string& utf8str) { #if PLATFORM == PLATFORM_WINDOWS std::wstring wstr; wstr.resize(conStr.size()); OemToCharBuffW(&conStr[0],&wstr[0],conStr.size()); + return WStrToUtf8(wstr,utf8str); #else // not implemented yet @@ -397,17 +460,23 @@ bool consoleToUtf8(const std::string& conStr,std::string& utf8str) return true; #endif } + bool Utf8FitTo(const std::string& str, std::wstring search) { std::wstring temp; + if(!Utf8toWStr(str,temp)) return false; + // converting to lower case wstrToLower( temp ); + if(temp.find(search) == std::wstring::npos) return false; + return true; } + void utf8printf(FILE *out, const char *str, ...) { va_list ap; @@ -415,20 +484,25 @@ void utf8printf(FILE *out, const char *str, ...) vutf8printf(stdout, str, &ap); va_end(ap); } + void vutf8printf(FILE *out, const char *str, va_list* ap) { #if PLATFORM == PLATFORM_WINDOWS char temp_buf[32*1024]; wchar_t wtemp_buf[32*1024]; + size_t temp_len = vsnprintf(temp_buf, 32*1024, str, *ap); + size_t wtemp_len = 32*1024-1; Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len); + CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len+1); fprintf(out, temp_buf); #else vfprintf(out, str, *ap); #endif } + void hexEncodeByteArray(uint8* bytes, uint32 arrayLen, std::string& result) { std::ostringstream ss; diff --git a/src/shared/Util.h b/src/shared/Util.h index 7a0579e0a2f..04be6e93bed 100644 --- a/src/shared/Util.h +++ b/src/shared/Util.h @@ -17,47 +17,63 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _UTIL_H #define _UTIL_H + #include "Common.h" + #include <string> #include <vector> + typedef std::vector<std::string> Tokens; + Tokens StrSplit(const std::string &src, const std::string &sep); + void stripLineInvisibleChars(std::string &src); + std::string secsToTimeString(uint32 timeInSecs, bool shortText = false, bool hoursOnly = false); uint32 TimeStringToSecs(const std::string& timestring); std::string TimeToTimestampStr(time_t t); + inline uint32 secsToTimeBitFields(time_t secs) { tm* lt = localtime(&secs); return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min; } + /* Return a random number in the range min..max; (max-min) must be smaller than 32768. */ TRINITY_DLL_SPEC int32 irand(int32 min, int32 max); + /* Return a random number in the range min..max (inclusive). For reliable results, the difference * between max and min should be less than RAND32_MAX. */ TRINITY_DLL_SPEC uint32 urand(uint32 min, uint32 max); + /* Return a random number in the range 0 .. RAND32_MAX. */ TRINITY_DLL_SPEC int32 rand32(); + /* Return a random double from 0.0 to 1.0 (exclusive). Floats support only 7 valid decimal digits. * A double supports up to 15 valid decimal digits and is used internally (RAND32_MAX has 10 digits). * With an FPU, there is usually no difference in performance between float and double. */ TRINITY_DLL_SPEC double rand_norm(void); + /* Return a random double from 0.0 to 99.9999999999999. Floats support only 7 valid decimal digits. * A double supports up to 15 valid decimal digits and is used internaly (RAND32_MAX has 10 digits). * With an FPU, there is usually no difference in performance between float and double. */ TRINITY_DLL_SPEC double rand_chance(void); + /* Return true if a random roll fits in the specified chance (range 0-100). */ inline bool roll_chance_f(float chance) { return chance > rand_chance(); } + /* Return true if a random roll fits in the specified chance (range 0-100). */ inline bool roll_chance_i(int chance) { return chance > irand(0, 99); } + inline void ApplyModUInt32Var(uint32& var, int32 val, bool apply) { int32 cur = var; @@ -66,18 +82,21 @@ inline void ApplyModUInt32Var(uint32& var, int32 val, bool apply) cur = 0; var = cur; } + inline void ApplyModFloatVar(float& var, float val, bool apply) { var += (apply ? val : -val); if(var < 0) var = 0; } + inline void ApplyPercentModFloatVar(float& var, float val, bool apply) { if (val == -100.0f) // prevent set var to zero val = -99.99f; var *= (apply?(100.0f+val)/100.0f : 100.0f / (100.0f+val)); } + bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr); // in wsize==max size of buffer, out wsize==real string size bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize); @@ -85,11 +104,14 @@ inline bool Utf8toWStr(const std::string& utf8str, wchar_t* wstr, size_t& wsize) { return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize); } + bool WStrToUtf8(std::wstring wstr, std::string& utf8str); // size==real string size bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str); + size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence void utf8truncate(std::string& utf8str,size_t len); + inline bool isBasicLatinCharacter(wchar_t wchar) { if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z @@ -98,6 +120,7 @@ inline bool isBasicLatinCharacter(wchar_t wchar) return true; return false; } + inline bool isExtendedLatinCharacter(wchar_t wchar) { if(isBasicLatinCharacter(wchar)) @@ -118,6 +141,7 @@ inline bool isExtendedLatinCharacter(wchar_t wchar) return true; return false; } + inline bool isCyrillicCharacter(wchar_t wchar) { if(wchar >= 0x0410 && wchar <= 0x044F) // CYRILLIC CAPITAL LETTER A - CYRILLIC SMALL LETTER YA @@ -126,6 +150,7 @@ inline bool isCyrillicCharacter(wchar_t wchar) return true; return false; } + inline bool isEastAsianCharacter(wchar_t wchar) { if(wchar >= 0x1100 && wchar <= 0x11F9) // Hangul Jamo @@ -146,18 +171,22 @@ inline bool isEastAsianCharacter(wchar_t wchar) return true; return false; } + inline bool isNumeric(wchar_t wchar) { return (wchar >= L'0' && wchar <=L'9'); } + inline bool isNumeric(char c) { return (c >= '0' && c <='9'); } + inline bool isNumericOrSpace(wchar_t wchar) { return isNumeric(wchar) || wchar == L' '; } + inline bool isBasicLatinString(std::wstring wstr, bool numericOrSpace) { for(size_t i = 0; i < wstr.size(); ++i) @@ -165,6 +194,7 @@ inline bool isBasicLatinString(std::wstring wstr, bool numericOrSpace) return false; return true; } + inline bool isExtendedLatinString(std::wstring wstr, bool numericOrSpace) { for(size_t i = 0; i < wstr.size(); ++i) @@ -172,6 +202,7 @@ inline bool isExtendedLatinString(std::wstring wstr, bool numericOrSpace) return false; return true; } + inline bool isCyrillicString(std::wstring wstr, bool numericOrSpace) { for(size_t i = 0; i < wstr.size(); ++i) @@ -179,6 +210,7 @@ inline bool isCyrillicString(std::wstring wstr, bool numericOrSpace) return false; return true; } + inline bool isEastAsianString(std::wstring wstr, bool numericOrSpace) { for(size_t i = 0; i < wstr.size(); ++i) @@ -186,6 +218,7 @@ inline bool isEastAsianString(std::wstring wstr, bool numericOrSpace) return false; return true; } + inline wchar_t wcharToUpper(wchar_t wchar) { if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z @@ -205,12 +238,15 @@ inline wchar_t wcharToUpper(wchar_t wchar) return wchar_t(uint16(wchar)-0x0020); if(wchar == 0x0451) // CYRILLIC SMALL LETTER IO return wchar_t(0x0401); + return wchar; } + inline wchar_t wcharToUpperOnlyLatin(wchar_t wchar) { return isBasicLatinCharacter(wchar) ? wcharToUpper(wchar) : wchar; } + inline wchar_t wcharToLower(wchar_t wchar) { if(wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z @@ -230,33 +266,43 @@ inline wchar_t wcharToLower(wchar_t wchar) return wchar_t(0x0451); if(wchar >= 0x0410 && wchar <= 0x042F) // CYRILLIC CAPITAL LETTER A - CYRILLIC CAPITAL LETTER YA return wchar_t(uint16(wchar)+0x0020); + return wchar; } + inline void wstrToUpper(std::wstring& str) { std::transform( str.begin(), str.end(), str.begin(), wcharToUpper ); } + inline void wstrToLower(std::wstring& str) { std::transform( str.begin(), str.end(), str.begin(), wcharToLower ); } + std::wstring GetMainPartOfName(std::wstring wname, uint32 declension); + bool utf8ToConsole(const std::string& utf8str, std::string& conStr); bool consoleToUtf8(const std::string& conStr,std::string& utf8str); bool Utf8FitTo(const std::string& str, std::wstring search); void utf8printf(FILE *out, const char *str, ...); void vutf8printf(FILE *out, const char *str, va_list* ap); + bool IsIPAddress(char const* ipaddress); uint32 CreatePIDFile(const std::string& filename); + void hexEncodeByteArray(uint8* bytes, uint32 arrayLen, std::string& result); #endif + //handler for operations on large flags #ifndef _FLAG96 #define _FLAG96 + #ifndef PAIR64_HIPART #define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & UI64LIT(0x00000000FFFFFFFF)) #define PAIR64_LOPART(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF)) #endif + class flag96 { private: @@ -268,12 +314,14 @@ public: part[1]=p2; part[2]=p3; } + flag96(uint64 p1, uint32 p2) { part[0]=PAIR64_LOPART(p1); part[1]=PAIR64_HIPART(p1); part[2]=p2; } + inline bool IsEqual(uint32 p1=0, uint32 p2=0, uint32 p3=0) const { return ( @@ -281,6 +329,7 @@ public: part[1]==p2 && part[2]==p3); }; + inline bool HasFlag(uint32 p1=0, uint32 p2=0, uint32 p3=0) const { return ( @@ -288,12 +337,14 @@ public: part[1]&p2 || part[2]&p3); }; + inline void Set(uint32 p1=0, uint32 p2=0, uint32 p3=0) { part[0]=p1; part[1]=p2; part[2]=p3; }; + template<class type> inline bool operator < (type & right) { @@ -306,6 +357,7 @@ public: } return 0; }; + template<class type> inline bool operator < (type & right) const { @@ -318,6 +370,7 @@ public: } return 0; }; + template<class type> inline bool operator != (type & right) { @@ -327,6 +380,7 @@ public: return true; return false; } + template<class type> inline bool operator != (type & right) const { @@ -336,6 +390,7 @@ public: return true; return false; }; + template<class type> inline bool operator == (type & right) { @@ -345,6 +400,7 @@ public: return false; return true; }; + template<class type> inline bool operator == (type & right) const { @@ -354,6 +410,7 @@ public: return false; return true; }; + template<class type> inline void operator = (type & right) { @@ -361,6 +418,7 @@ public: part[1]=right.part[1]; part[2]=right.part[2]; }; + template<class type> inline flag96 operator & (type & right) { @@ -375,11 +433,13 @@ public: return ret; }; + template<class type> inline void operator &= (type & right) { *this=*this & right; }; + template<class type> inline flag96 operator | (type & right) { @@ -387,6 +447,7 @@ public: return ret; }; + template<class type> inline flag96 operator | (type & right) const { @@ -394,17 +455,20 @@ public: return ret; }; + template<class type> inline void operator |= (type & right) { *this=*this | right; }; + inline void operator ~ () { part[2]=~part[2]; part[1]=~part[1]; part[0]=~part[0]; }; + template<class type> inline flag96 operator ^ (type & right) { @@ -412,6 +476,7 @@ public: return ret; }; + template<class type> inline flag96 operator ^ (type & right) const { @@ -419,11 +484,13 @@ public: return ret; }; + template<class type> inline void operator ^= (type & right) { *this=*this^right; }; + inline operator bool() const { return( @@ -431,6 +498,7 @@ public: part[1] != 0 || part[2] != 0); }; + inline operator bool() { return( @@ -438,6 +506,7 @@ public: part[1] != 0 || part[2] != 0); }; + inline bool operator ! () const { return( @@ -445,6 +514,7 @@ public: part[1] == 0 && part[2] == 0); }; + inline bool operator ! () { return( @@ -452,10 +522,12 @@ public: part[1] == 0 && part[2] == 0); }; + inline uint32 & operator[](uint8 el) { return (part[el]); }; + inline const uint32 & operator[](uint8 el) const { return (part[el]); diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp index 9c537a20949..f2fd9b0f2e7 100644 --- a/src/shared/WheatyExceptionReport.cpp +++ b/src/shared/WheatyExceptionReport.cpp @@ -18,6 +18,7 @@ #include "revision.h" #define CrashFolder _T("Crashes") //#pragma comment(linker, "/defaultlib:dbghelp.lib") + inline LPTSTR ErrorMessage(DWORD dw) { LPVOID lpMsgBuf; @@ -31,7 +32,9 @@ inline LPTSTR ErrorMessage(DWORD dw) 0, NULL); return (LPTSTR)lpMsgBuf; } + //============================== Global Variables ============================= + // // Declare the static variables of the WheatyExceptionReport class // @@ -39,15 +42,19 @@ TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH]; LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter; HANDLE WheatyExceptionReport::m_hReportFile; HANDLE WheatyExceptionReport::m_hProcess; + // Declare global instance of class WheatyExceptionReport g_WheatyExceptionReport; + //============================== Class Methods ============================= + WheatyExceptionReport::WheatyExceptionReport() // Constructor { // Install the unhandled exception filter function m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); m_hProcess = GetCurrentProcess(); } + //============ // Destructor //============ @@ -56,6 +63,7 @@ WheatyExceptionReport::~WheatyExceptionReport() if (m_previousFilter) SetUnhandledExceptionFilter(m_previousFilter); } + //=========================================================== // Entry point where control comes on an unhandled exception //=========================================================== @@ -69,6 +77,7 @@ PEXCEPTION_POINTERS pExceptionInfo) return 0; pos[0] = '\0'; ++pos; + TCHAR crash_folder_path[MAX_PATH]; sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder); if (!CreateDirectory(crash_folder_path, NULL)) @@ -76,10 +85,12 @@ PEXCEPTION_POINTERS pExceptionInfo) if (GetLastError() != ERROR_ALREADY_EXISTS) return 0; } + SYSTEMTIME systime; GetLocalTime(&systime); sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt", crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond); + m_hReportFile = CreateFile(m_szLogFileName, GENERIC_WRITE, 0, @@ -87,22 +98,28 @@ PEXCEPTION_POINTERS pExceptionInfo) OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); + if (m_hReportFile) { SetFilePointer(m_hReportFile, 0, 0, FILE_END); + GenerateExceptionReport(pExceptionInfo); + CloseHandle(m_hReportFile); m_hReportFile = 0; } + if (m_previousFilter) return m_previousFilter(pExceptionInfo); else return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/; } + BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount) { if (!sProcessorName) return FALSE; + HKEY hKey; LONG lRet; lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), @@ -124,6 +141,7 @@ BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxco _tcsncpy(sProcessorName, psz, maxcount); return TRUE; } + BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) { // Try calling GetVersionEx using the OSVERSIONINFOEX structure. @@ -155,6 +173,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax); if (osvi.dwMajorVersion <= 4) _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax); + // Test for specific product on Windows NT 4.0 SP6 and later. if (bOsVersionInfoEx) { @@ -247,6 +266,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) { HKEY hKey; LONG lRet; + // Test for SP6 versus SP6a. lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey); if (lRet == ERROR_SUCCESS) @@ -280,12 +300,15 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) _tcsncat(szVersion, wszTmp, cntMax); break; } + return TRUE; } + void WheatyExceptionReport::PrintSystemInfo() { SYSTEM_INFO SystemInfo; ::GetSystemInfo(&SystemInfo); + MEMORYSTATUS MemoryStatus; MemoryStatus.dwLength = sizeof (MEMORYSTATUS); ::GlobalMemoryStatus(&MemoryStatus); @@ -297,24 +320,29 @@ void WheatyExceptionReport::PrintSystemInfo() else _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); + if (_GetWindowsVersion(sString, countof(sString))) _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString); else _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n")); } + //=========================================================================== void WheatyExceptionReport::printTracesForAllThreads() { HANDLE hThreadSnap = INVALID_HANDLE_VALUE; THREADENTRY32 te32; + DWORD dwOwnerPID = GetCurrentProcessId(); m_hProcess = GetCurrentProcess(); // Take a snapshot of all running threads hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) return; + // Fill in the size of the structure before using it. te32.dwSize = sizeof(THREADENTRY32); + // Retrieve information about the first thread, // and exit if unsuccessful if (!Thread32First(hThreadSnap, &te32)) @@ -323,6 +351,7 @@ void WheatyExceptionReport::printTracesForAllThreads() // snapshot object! return; } + // Now walk the thread list of the system, // and display information about each thread // associated with the specified process @@ -340,9 +369,12 @@ void WheatyExceptionReport::printTracesForAllThreads() CloseHandle(threadHandle); } } while(Thread32Next(hThreadSnap, &te32)); + // Don't forget to clean up the snapshot object. CloseHandle(hThreadSnap); } + + //=========================================================================== // Open the report file, and write the desired information to it. Called by // WheatyUnhandledExceptionFilter @@ -352,16 +384,19 @@ PEXCEPTION_POINTERS pExceptionInfo) { SYSTEMTIME systime; GetLocalTime(&systime); + // Start out with a banner _tprintf(_T("Revision: %s\r\n"), _FULLVERSION); _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + PrintSystemInfo(); // First print information about the type of fault _tprintf(_T("\r\n//=====================================================\r\n")); _tprintf(_T("Exception code: %08X %s\r\n"), pExceptionRecord->ExceptionCode, GetExceptionString(pExceptionRecord->ExceptionCode)); + // Now print information about where the fault occured TCHAR szFaultingModule[MAX_PATH]; DWORD section; @@ -370,6 +405,7 @@ PEXCEPTION_POINTERS pExceptionInfo) szFaultingModule, sizeof(szFaultingModule), section, offset); + #ifdef _M_IX86 _tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"), pExceptionRecord->ExceptionAddress, @@ -380,13 +416,17 @@ PEXCEPTION_POINTERS pExceptionInfo) pExceptionRecord->ExceptionAddress, section, offset, szFaultingModule); #endif + PCONTEXT pCtx = pExceptionInfo->ContextRecord; + // Show the registers #ifdef _M_IX86 // X86 Only! _tprintf(_T("\r\nRegisters:\r\n")); + _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi); + _tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip); _tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"), pCtx->SegSs, pCtx->Esp, pCtx->Ebp); @@ -394,6 +434,7 @@ PEXCEPTION_POINTERS pExceptionInfo) pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); #endif + #ifdef _M_X64 _tprintf(_T("\r\nRegisters:\r\n")); _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") @@ -407,30 +448,42 @@ PEXCEPTION_POINTERS pExceptionInfo) pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs); _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags); #endif + SymSetOptions(SYMOPT_DEFERRED_LOADS); + // Initialize DbgHelp if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), ErrorMessage(GetLastError())); } + CONTEXT trashableContext = *pCtx; + WriteStackDetails(&trashableContext, false, NULL); printTracesForAllThreads(); + // #ifdef _M_IX86 // X86 Only! + _tprintf(_T("========================\r\n")); _tprintf(_T("Local Variables And Parameters\r\n")); + trashableContext = *pCtx; WriteStackDetails(&trashableContext, true, NULL); + _tprintf(_T("========================\r\n")); _tprintf(_T("Global Variables\r\n")); + SymEnumSymbols(GetCurrentProcess(), (DWORD64)GetModuleHandle(szFaultingModule), 0, EnumerateSymbolsCallback, 0); // #endif // X86 Only! + SymCleanup(GetCurrentProcess()); + _tprintf(_T("\r\n")); } + //====================================================================== // Given an exception code, returns a pointer to a static string with a // description of the exception @@ -438,6 +491,7 @@ PEXCEPTION_POINTERS pExceptionInfo) LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode) { #define EXCEPTION(x) case EXCEPTION_##x: return _T(#x); + switch (dwCode) { EXCEPTION(ACCESS_VIOLATION) @@ -463,14 +517,19 @@ LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode) EXCEPTION(GUARD_PAGE) EXCEPTION(INVALID_HANDLE) } + // If not one of the "known" exceptions, try to get the string // from NTDLL.DLL's message table. + static TCHAR szBuffer[512] = { 0 }; + FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle(_T("NTDLL.DLL")), dwCode, 0, szBuffer, sizeof(szBuffer), 0); + return szBuffer; } + //============================================================================= // Given a linear address, locates the module, section, and offset containing // that address. @@ -482,17 +541,25 @@ BOOL WheatyExceptionReport::GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset) { MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr, &mbi, sizeof(mbi))) return FALSE; + DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase; + if (!GetModuleFileName((HMODULE)hMod, szModule, len)) return FALSE; + // Point to the DOS header in memory PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; + // From the DOS header, find the NT (PE) header PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew)); + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr); + DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address + // Iterate through the section table, looking for the one that encompasses // the linear address. for (unsigned i = 0; @@ -502,6 +569,7 @@ PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset) DWORD_PTR sectionStart = pSection->VirtualAddress; DWORD_PTR sectionEnd = sectionStart + DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize)); + // Is the address in this section??? if ((rva >= sectionStart) && (rva <= sectionEnd)) { @@ -513,8 +581,10 @@ PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset) return TRUE; } } + return FALSE; // Should never get here! } + // It contains SYMBOL_INFO structure plus additional // space for the name of the symbol struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE @@ -525,6 +595,7 @@ struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE si.MaxNameLen = sizeof(name); } }; + //============================================================ // Walks the stack, and writes the results to the report file //============================================================ @@ -533,11 +604,15 @@ PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output { _tprintf(_T("\r\nCall stack:\r\n")); + _tprintf(_T("Address Frame Function SourceFile\r\n")); + DWORD dwMachineType = 0; // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag + STACKFRAME64 sf; memset(&sf, 0, sizeof(sf)); + #ifdef _M_IX86 // Initialize the STACKFRAME structure for the first call. This is only // necessary for Intel CPUs, and isn't mentioned in the documentation. @@ -547,8 +622,10 @@ bool bWriteVariables, HANDLE pThreadHandle) sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Offset = pContext->Ebp; sf.AddrFrame.Mode = AddrModeFlat; + dwMachineType = IMAGE_FILE_MACHINE_I386; #endif + #ifdef _M_X64 sf.AddrPC.Offset = pContext->Rip; sf.AddrPC.Mode = AddrModeFlat; @@ -558,6 +635,7 @@ bool bWriteVariables, HANDLE pThreadHandle) sf.AddrFrame.Mode = AddrModeFlat; dwMachineType = IMAGE_FILE_MACHINE_AMD64; #endif + while (1) { // Get the next stack frame @@ -579,8 +657,10 @@ bool bWriteVariables, HANDLE pThreadHandle) #ifdef _M_X64 _tprintf(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset); #endif + DWORD64 symDisplacement = 0; // Displacement of the input address, // relative to the start of the symbol + // Get the name of the function for this stack frame entry CSymbolInfoPackage sip; if (SymFromAddr( @@ -590,12 +670,14 @@ bool bWriteVariables, HANDLE pThreadHandle) &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object) { _tprintf(_T("%hs+%I64X"), sip.si.Name, symDisplacement); + } else // No symbol found. Print out the logical address instead. { TCHAR szModule[MAX_PATH] = _T(""); DWORD section = 0; DWORD_PTR offset = 0; + GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset); #ifdef _M_IX86 @@ -605,6 +687,7 @@ bool bWriteVariables, HANDLE pThreadHandle) _tprintf(_T("%04X:%016I64X %s"), section, offset, szModule); #endif } + // Get the source line for this stack frame entry IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) }; DWORD dwLineDisplacement; @@ -613,7 +696,9 @@ bool bWriteVariables, HANDLE pThreadHandle) { _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber); } + _tprintf(_T("\r\n")); + // Write out the variables, if desired if (bWriteVariables) { @@ -621,22 +706,29 @@ bool bWriteVariables, HANDLE pThreadHandle) IMAGEHLP_STACK_FRAME imagehlpStackFrame; imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; SymSetContext(m_hProcess, &imagehlpStackFrame, 0); + // Enumerate the locals/parameters SymEnumSymbols(m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf); + _tprintf(_T("\r\n")); } } + } + ////////////////////////////////////////////////////////////////////////////// // The function invoked by SymEnumSymbols ////////////////////////////////////////////////////////////////////////////// + BOOL CALLBACK WheatyExceptionReport::EnumerateSymbolsCallback( PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext) { + char szBuffer[2048]; + __try { if (FormatSymbolValue(pSymInfo, (STACKFRAME*)UserContext, @@ -647,8 +739,10 @@ PVOID UserContext) { _tprintf(_T("punting on symbol %s\r\n"), pSymInfo->Name); } + return TRUE; } + ////////////////////////////////////////////////////////////////////////////// // Given a SYMBOL_INFO representing a particular variable, displays its // contents. If it's a user defined type, display the members and their @@ -661,15 +755,19 @@ char * pszBuffer, unsigned cbBuffer) { char * pszCurrBuffer = pszBuffer; + // Indicate if the variable is a local or parameter if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER) pszCurrBuffer += sprintf(pszCurrBuffer, "Parameter "); else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL) pszCurrBuffer += sprintf(pszCurrBuffer, "Local "); + // If it's a function, don't do anything. if (pSym->Tag == 5) // SymTagFunction from CVCONST.H from the DIA SDK return false; + DWORD_PTR pVariable = 0; // Will point to the variable's data in memory + if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE) { // if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1) @@ -688,11 +786,13 @@ unsigned cbBuffer) { pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable } + // Determine if the variable is a user defined type (UDT). IF so, bHandled // will return true. bool bHandled; pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex, 0, pVariable, bHandled, pSym->Name); + if (!bHandled) { // The symbol wasn't a UDT, so do basic, stupid formatting of the @@ -700,13 +800,17 @@ unsigned cbBuffer) // DWORD. BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase); pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]); + // Emit the variable name pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", pSym->Name); + pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size, (PVOID)pVariable); } + return true; } + ////////////////////////////////////////////////////////////////////////////// // If it's a user defined type (UDT), recurse through its members until we're // at fundamental types. When he hit fundamental types, return @@ -722,6 +826,7 @@ bool & bHandled, char* Name) { bHandled = false; + // Get the name of the symbol. This will either be a Type name (if a UDT), // or the structure member name. WCHAR * pwszTypeName; @@ -731,12 +836,15 @@ char* Name) pszCurrBuffer += sprintf(pszCurrBuffer, " %ls", pwszTypeName); LocalFree(pwszTypeName); } + // Determine how many children this type has. DWORD dwChildrenCount = 0; SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount); + if (!dwChildrenCount) // If no children, we're done return pszCurrBuffer; + // Prepare to get an array of "TypeIds", representing each of the children. // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this. @@ -745,29 +853,36 @@ char* Name) ULONG MoreChildIds[1024]; FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);} } children; + children.Count = dwChildrenCount; children.Start= 0; + // Get the array of TypeIds, one for each child type if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, &children)) { return pszCurrBuffer; } + // Append a line feed pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n"); + // Iterate through each of the children for (unsigned i = 0; i < dwChildrenCount; i++) { // Add appropriate indentation level (since this routine is recursive) for (unsigned j = 0; j <= nestingLevel+1; j++) pszCurrBuffer += sprintf(pszCurrBuffer, "\t"); + // Recurse for each of the child types bool bHandled2; BasicType basicType = GetBasicType(children.ChildId[i], modBase); pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]); + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, children.ChildId[i], nestingLevel+1, offset, bHandled2, ""/*Name */); + // If the child wasn't a UDT, format it appropriately if (!bHandled2) { @@ -775,30 +890,38 @@ char* Name) DWORD dwMemberOffset; SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_OFFSET, &dwMemberOffset); + // Get the real "TypeId" of the child. We need this for the // SymGetTypeInfo(TI_GET_TYPEID) call below. DWORD typeId; SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_TYPEID, &typeId); + // Get the size of the child member ULONG64 length; SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length); + // Calculate the address of the member DWORD_PTR dwFinalOffset = offset + dwMemberOffset; + // BasicType basicType = GetBasicType(children.ChildId[i], modBase); // // pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]); // // Emit the variable name // pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", Name); + pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, length, (PVOID)dwFinalOffset); + pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n"); } } + bHandled = true; return pszCurrBuffer; } + char * WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, @@ -840,8 +963,10 @@ PVOID pAddress) pszCurrBuffer += sprintf(pszCurrBuffer, " = %I64X", *(DWORD64*)pAddress); } + return pszCurrBuffer; } + BasicType WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) { @@ -851,6 +976,7 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) { return basicType; } + // Get the real "TypeId" of the child. We need this for the // SymGetTypeInfo(TI_GET_TYPEID) call below. DWORD typeId; @@ -862,8 +988,10 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) return basicType; } } + return btNoType; } + //============================================================================ // Helper function that writes to the report file, and allows the user to use // printf style formating @@ -874,10 +1002,13 @@ int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) int retValue; DWORD cbWritten; va_list argptr; + va_start(argptr, format); retValue = vsprintf(szBuff, format, argptr); va_end(argptr); + WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0); + return retValue; } diff --git a/src/shared/WheatyExceptionReport.h b/src/shared/WheatyExceptionReport.h index a2ff31aefa8..33fb4bc5b0e 100644 --- a/src/shared/WheatyExceptionReport.h +++ b/src/shared/WheatyExceptionReport.h @@ -1,12 +1,15 @@ #ifndef _WHEATYEXCEPTIONREPORT_ #define _WHEATYEXCEPTIONREPORT_ + #include <dbghelp.h> + #if _MSC_VER < 1400 # define countof(array) (sizeof(array) / sizeof(array[0])) #else # include <stdlib.h> # define countof _countof #endif // _MSC_VER < 1400 + enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK { btNoType = 0, @@ -28,6 +31,7 @@ enum BasicType // Stolen from CVCON btBSTR = 30, btHresult = 31 }; + const char* const rgBaseType[] = { " <user defined> ", // btNoType = 0, @@ -63,14 +67,18 @@ const char* const rgBaseType[] = " BSTR ", // btBSTR = 30, " HRESULT " // btHresult = 31 }; + class WheatyExceptionReport { public: + WheatyExceptionReport(); ~WheatyExceptionReport(); + // entry point where control comes on an unhandled exception static LONG WINAPI WheatyUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo); + static void printTracesForAllThreads(); private: // where report info is extracted and generated @@ -78,23 +86,33 @@ class WheatyExceptionReport static void PrintSystemInfo(); static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax); static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount); + // Helper functions static LPTSTR GetExceptionString(DWORD dwCode); static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset); + static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle); + static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO,ULONG, PVOID); + static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME *, char * pszBuffer, unsigned cbBuffer); + static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool & , char*); + static char * FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress); + static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase); + static int __cdecl _tprintf(const TCHAR * format, ...); + // Variables used by the class static TCHAR m_szLogFileName[MAX_PATH]; static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; static HANDLE m_hReportFile; static HANDLE m_hProcess; }; + extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class #endif //WheatyExceptionReport diff --git a/src/shared/WorldPacket.h b/src/shared/WorldPacket.h index 055cc0f6088..1eb3f12dd86 100644 --- a/src/shared/WorldPacket.h +++ b/src/shared/WorldPacket.h @@ -17,10 +17,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef TRINITYCORE_WORLDPACKET_H #define TRINITYCORE_WORLDPACKET_H + #include "Common.h" #include "ByteBuffer.h" + class WorldPacket : public ByteBuffer { public: @@ -33,14 +36,17 @@ class WorldPacket : public ByteBuffer WorldPacket(const WorldPacket &packet) : ByteBuffer(packet), m_opcode(packet.m_opcode) { } + void Initialize(uint16 opcode, size_t newres=200) { clear(); _storage.reserve(newres); m_opcode = opcode; } + uint16 GetOpcode() const { return m_opcode; } void SetOpcode(uint16 opcode) { m_opcode = opcode; } + protected: uint16 m_opcode; }; diff --git a/src/shared/vmap/AABSPTree.h b/src/shared/vmap/AABSPTree.h index 5fe9c59cf2b..ff4335b6774 100644 --- a/src/shared/vmap/AABSPTree.h +++ b/src/shared/vmap/AABSPTree.h @@ -1,14 +1,21 @@ /** @file AABSPTree.h + @maintainer Morgan McGuire, matrix@graphics3d.com + @created 2004-01-11 @edited 2007-02-16 + Copyright 2000-2007, Morgan McGuire. All rights reserved. + */ + #ifndef G3D_AABSPTREE_H #define G3D_AABSPTREE_H + #include "VMapTools.h" + #include "G3D/platform.h" #include "G3D/Array.h" #include "G3D/Table.h" @@ -26,47 +33,60 @@ #include "G3D/CollisionDetection.h" #include "G3D/GCamera.h" #include <algorithm> + // If defined, in debug mode the tree is checked for consistency // as a way of detecting corruption due to implementation bugs // #define VERIFY_TREE + inline void getBounds(const G3D::Vector3& v, G3D::AABox& out) { out = G3D::AABox(v); } + inline void getBounds(const G3D::AABox& a, G3D::AABox& out) { out = a; } + inline void getBounds(const G3D::Sphere& s, G3D::AABox& out) { s.getBounds(out); } + inline void getBounds(const G3D::Box& b, G3D::AABox& out) { b.getBounds(out); } + inline void getBounds(const G3D::Triangle& t, G3D::AABox& out) { t.getBounds(out); } + + inline void getBounds(const G3D::Vector3* v, G3D::AABox& out) { out = G3D::AABox(*v); } + inline void getBounds(const G3D::AABox* a, G3D::AABox& out) { getBounds(*a, out); } + inline void getBounds(const G3D::Sphere* s, G3D::AABox& out) { s->getBounds(out); } + inline void getBounds(const G3D::Box* b, G3D::AABox& out) { b->getBounds(out); } + inline void getBounds(const G3D::Triangle* t, G3D::AABox& out) { t->getBounds(out); } namespace G3D { namespace _internal { + /** Wraps a pointer value so that it can be treated as the instance itself; convenient for inserting pointers into a Table but using the @@ -76,50 +96,65 @@ namespace G3D { class Indirector { public: Type* handle; + inline Indirector(Type* h) : handle(h) {} + inline Indirector() : handle(NULL) {} + /** Returns true iff the values referenced by the handles are equivalent. */ inline bool operator==(const Indirector& m) { return *handle == *(m.handle); } + inline bool operator==(const Type& m) { return *handle == m; } + inline size_t hashCode() const { return handle->hashCode(); } }; } // namespace internal } // namespace G3D + template <class Handle> struct GHashCode< G3D::_internal::Indirector<Handle> > { size_t operator()(const G3D::_internal::Indirector<Handle>& key) const { return key.hashCode(); } }; + namespace G3D { + /** A set that supports spatial queries using an axis-aligned BSP tree for speed. + AABSPTree allows you to quickly find objects in 3D that lie within a box or along a ray. For large sets of objects it is much faster than testing each object for a collision. + AABSPTree is as powerful as but more general than a Quad Tree, Oct Tree, or KD Tree, but less general than an unconstrained BSP tree (which is much slower to create). + Internally, objects are arranged into an axis-aligned BSP-tree according to their axis-aligned bounds. This increases the cost of insertion to O(log n) but allows fast overlap queries. + <B>Template Parameters</B> <DT>The template parameter <I>T</I> must be one for which the following functions are all overloaded: + <P><CODE>void ::getBounds(const T&, G3D::AABox&);</CODE> <DT><CODE>bool ::operator==(const T&, const T&);</CODE> <DT><CODE>unsigned int ::hashCode(const T&);</CODE> <DT><CODE>T::T();</CODE> <I>(public constructor of no arguments)</I> + G3D provides these for common classes like G3D::Vector3 and G3D::Sphere. If you use a custom class, or a pointer to a custom class, you will need to define those functions. + <B>Moving %Set Members</B> <DT>It is important that objects do not move without updating the AABSPTree. If the axis-aligned bounds of an object are about @@ -130,21 +165,26 @@ namespace G3D { you can use the AABSPTree::update method as a shortcut to insert/remove an object in one step after it has moved. + Note: Do not mutate any value once it has been inserted into AABSPTree. Values are copied interally. All AABSPTree iterators convert to pointers to constant values to reinforce this. + If you want to mutate the objects you intend to store in a AABSPTree simply insert <I>pointers</I> to your objects instead of the objects themselves, and ensure that the above operations are defined. (And actually, because values are copied, if your values are large you may want to insert pointers anyway, to save space and make the balance operation faster.) + <B>Dimensions</B> Although designed as a 3D-data structure, you can use the AABSPTree for data distributed along 2 or 1 axes by simply returning bounds that are always zero along one or more dimensions. + */ namespace _AABSPTree { + /** Wrapper for a value that includes a cache of its bounds. Except for the test value used in a set-query operation, there is only ever one instance of the handle associated with any @@ -156,75 +196,97 @@ namespace _AABSPTree { public: /** The bounds of each object are constrained to AABox::maxFinite */ AABox bounds; + /** Center of bounds. We cache this value to avoid recomputing it during the median sort, and because MSVC 6 std::sort goes into an infinite loop if we compute the midpoint on the fly (possibly a floating point roundoff issue, where B<A and A<B both are true).*/ Vector3 center; + TValue value; + Handle<TValue>() {} + inline Handle<TValue>(const TValue& v) : value(v) { getBounds(v, bounds); bounds = bounds.intersect(AABox::maxFinite()); center = bounds.center(); } + inline bool operator==(const Handle<TValue>& other) const { return (*value).operator==(*other.value); } + inline size_t hashCode() const { return value->hashCode(); } }; + template<> class Handle<Triangle> { public: /** The bounds of each object are constrained to AABox::maxFinite */ AABox bounds; + /** Center of bounds. We cache this value to avoid recomputing it during the median sort, and because MSVC 6 std::sort goes into an infinite loop if we compute the midpoint on the fly (possibly a floating point roundoff issue, where B<A and A<B both are true).*/ Vector3 center; + Triangle value; + Handle<Triangle>() {} + inline Handle<Triangle>(const Triangle& v) : value(v) { getBounds(v, bounds); bounds = bounds.intersect(AABox::maxFinite()); center = bounds.center(); } + inline bool operator==(const Handle<Triangle>& other) const { return value.operator==(other.value); } + inline size_t hashCode() const { return value.hashCode(); } }; } + template<class T> class AABSPTree { protected: public: + /** Returns the bounds of the sub array. Used by makeNode. */ static AABox computeBounds( const Array<_AABSPTree::Handle<T>*>& point, int beginIndex, int endIndex) { + Vector3 lo = Vector3::inf(); Vector3 hi = -lo; + for (int p = beginIndex; p <= endIndex; ++p) { lo = lo.min(point[p]->bounds.low()); hi = hi.max(point[p]->bounds.high()); } + return AABox(lo, hi); } + /** Compares centers */ class CenterComparator { public: Vector3::Axis sortAxis; + CenterComparator(Vector3::Axis a) : sortAxis(a) {} + inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const { float a = A->center[sortAxis]; float b = B->center[sortAxis]; + if (a < b) { return 1; } else if (a > b) { @@ -235,14 +297,18 @@ public: } }; + /** Compares bounds for strict >, <, or overlap*/ class BoundsComparator { public: Vector3::Axis sortAxis; + BoundsComparator(Vector3::Axis a) : sortAxis(a) {} + inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const { const AABox& a = A->bounds; const AABox& b = B->bounds; + if (a.high()[sortAxis] < b.low()[sortAxis]) { return 1; } else if (a.low()[sortAxis] > b.high()[sortAxis]) { @@ -253,15 +319,19 @@ public: } }; + /** Compares bounds to the sort location */ class Comparator { public: Vector3::Axis sortAxis; float sortLocation; + Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {} + inline int operator()(_AABSPTree::Handle<T>* /*ignore*/, const _AABSPTree::Handle<T>* handle) const { const AABox& box = handle->bounds; debugAssert(ignore == NULL); + if (box.high()[sortAxis] < sortLocation) { // Box is strictly below the sort location return -1; @@ -274,31 +344,41 @@ public: } } }; + // Using System::malloc with this class provided no speed improvement. class Node { public: + /** Spatial bounds on all values at this node and its children, based purely on the parent's splitting planes. May be infinite. */ AABox splitBounds; + Vector3::Axis splitAxis; + /** Location along the specified axis */ float splitLocation; + /** child[0] contains all values strictly smaller than splitLocation along splitAxis. + child[1] contains all values strictly larger. + Both may be NULL if there are not enough values to bother recursing. */ Node* child[2]; + /** Array of values at this node (i.e., values straddling the split plane + all values if this is a leaf node). + This is an array of pointers because that minimizes data movement during tree building, which accounts for about 15% of the time cost of tree building. */ Array<_AABSPTree::Handle<T> * > valueArray; + /** For each object in the value array, a copy of its bounds. Packing these into an array at the node level instead putting them in the valueArray improves @@ -306,6 +386,7 @@ public: increase when performing intersection computations. */ Array<AABox> boundsArray; + /** Creates node with NULL children */ Node() { splitAxis = Vector3::X_AXIS; @@ -315,6 +396,7 @@ public: child[i] = NULL; } } + /** Doesn't clone children. */ @@ -326,6 +408,7 @@ public: child[i] = NULL; } } + /** Copies the specified subarray of pt into point, NULLs the children. Assumes a second pass will set splitBounds. */ Node(const Array<_AABSPTree::Handle<T> * >& pt) : valueArray(pt) { @@ -334,22 +417,26 @@ public: for (int i = 0; i < 2; ++i) { child[i] = NULL; } + boundsArray.resize(valueArray.size()); for (int i = 0; i < valueArray.size(); ++i) { boundsArray[i] = valueArray[i]->bounds; } } + /** Deletes the children (but not the values) */ ~Node() { for (int i = 0; i < 2; ++i) { delete child[i]; } } + /** Returns true if this node is a leaf (no children) */ inline bool isLeaf() const { return (child[0] == NULL) && (child[1] == NULL); } + /** Recursively appends all handles and children's handles to the array. @@ -362,35 +449,44 @@ public: } } } + void verifyNode(const Vector3& lo, const Vector3& hi) { // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n", // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); + debugAssert(lo == splitBounds.low()); debugAssert(hi == splitBounds.high()); + for (int i = 0; i < valueArray.length(); ++i) { const AABox& b = valueArray[i]->bounds; debugAssert(b == boundsArray[i]); + for(int axis = 0; axis < 3; ++axis) { debugAssert(b.low()[axis] <= b.high()[axis]); debugAssert(b.low()[axis] >= lo[axis]); debugAssert(b.high()[axis] <= hi[axis]); } } + if (child[0] || child[1]) { debugAssert(lo[splitAxis] < splitLocation); debugAssert(hi[splitAxis] > splitLocation); } + Vector3 newLo = lo; newLo[splitAxis] = splitLocation; Vector3 newHi = hi; newHi[splitAxis] = splitLocation; + if (child[0] != NULL) { child[0]->verifyNode(lo, newHi); } + if (child[1] != NULL) { child[1]->verifyNode(newLo, hi); } } + #if 0 /** Stores the locations of the splitting planes (the structure but not the content) @@ -410,6 +506,7 @@ public: } } } + /** Clears the member table */ static Node* deserializeStructure(BinaryInput& bi) { if (bi.readUInt8() == 0) { @@ -427,6 +524,7 @@ public: #endif /** Returns the deepest node that completely contains bounds. */ Node* findDeepestContainingNode(const AABox& bounds) { + // See which side of the splitting plane the bounds are on if (bounds.high()[splitAxis] < splitLocation) { // Bounds are on the low side. Recurse into the child @@ -441,11 +539,13 @@ public: return child[1]->findDeepestContainingNode(bounds); } } + // There was no containing child, so this node is the // deepest containing node. return this; } + /** Appends all members that intersect the box. If useSphere is true, members that pass the box test face a second test against the sphere. */ @@ -454,6 +554,7 @@ public: const Sphere& sphere, Array<T>& members, bool useSphere) const { + // Test all values at this node for (int v = 0; v < boundsArray.size(); ++v) { const AABox& bounds = boundsArray[v]; @@ -462,22 +563,27 @@ public: members.append(valueArray[v]->value); } } + // If the left child overlaps the box, recurse into it if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) { child[0]->getIntersectingMembers(box, sphere, members, useSphere); } + // If the right child overlaps the box, recurse into it if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) { child[1]->getIntersectingMembers(box, sphere, members, useSphere); } } + /** Recurse through the tree, assigning splitBounds fields. */ void assignSplitBounds(const AABox& myBounds) { splitBounds = myBounds; + AABox childBounds[2]; myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]); + # if defined(G3D_DEBUG) && defined(VERIFY_TREE) // Verify the split for (int v = 0; v < boundsArray.size(); ++v) { @@ -485,12 +591,14 @@ public: debugAssert(myBounds.contains(bounds)); } # endif + for (int c = 0; c < 2; ++c) { if (child[c]) { child[c]->assignSplitBounds(childBounds[c]); } } } + /** Returns true if the ray intersects this node */ bool intersects(const Ray& ray, float distance) const { // See if the ray will ever hit this node or its children @@ -499,10 +607,13 @@ public: bool rayWillHitBounds = VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox( ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds); + bool canHitThisNode = (alreadyInsideBounds || (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + return canHitThisNode; } + template<typename RayCallback> void intersectRay( const Ray& ray, @@ -511,13 +622,16 @@ public: bool pStopAtFirstHit, bool intersectCallbackIsFast) const { float enterDistance = distance; + if (! intersects(ray, distance)) { // The ray doesn't hit this node, so it can't hit the children of the node. return; } + // Test for intersection against every object at this node. for (int v = 0; v < valueArray.size(); ++v) { bool canHitThisObject = true; + if (! intersectCallbackIsFast) { // See if Vector3 location; @@ -526,9 +640,11 @@ public: bool rayWillHitBounds = VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox( ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + canHitThisObject = (alreadyInsideBounds || (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); } + if (canHitThisObject) { // It is possible that this ray hits this object. Look for the intersection using the // callback. @@ -538,24 +654,32 @@ public: if(pStopAtFirstHit && distance < enterDistance) return; } + // There are three cases to consider next: // // 1. the ray can start on one side of the splitting plane and never enter the other, // 2. the ray can start on one side and enter the other, and // 3. the ray can travel exactly down the splitting plane + enum {NONE = -1}; int firstChild = NONE; int secondChild = NONE; + if (ray.origin[splitAxis] < splitLocation) { + // The ray starts on the small side firstChild = 0; + if (ray.direction[splitAxis] > 0) { // The ray will eventually reach the other side secondChild = 1; } + } else if (ray.origin[splitAxis] > splitLocation) { + // The ray starts on the large side firstChild = 1; + if (ray.direction[splitAxis] < 0) { secondChild = 0; } @@ -569,12 +693,14 @@ public: firstChild = 1; } } + // Test on the side closer to the ray origin. if ((firstChild != NONE) && child[firstChild]) { child[firstChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); if(pStopAtFirstHit && distance < enterDistance) return; } + if (ray.direction[splitAxis] != 0) { // See if there was an intersection before hitting the splitting plane. // If so, there is no need to look on the far side and recursion terminates. @@ -586,16 +712,21 @@ public: return; } } + // Test on the side farther from the ray origin. if ((secondChild != NONE) && child[secondChild]) { child[secondChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); } + } }; + /** Recursively subdivides the subarray. + Clears the source array as soon as it is no longer needed. + Call assignSplitBounds() on the root node after making a tree. */ Node* makeNode( @@ -603,28 +734,40 @@ public: int valuesPerNode, int numMeanSplits, Array<_AABSPTree::Handle<T> * >& temp) { + Node* node = NULL; + if (source.size() <= valuesPerNode) { // Make a new leaf node node = new Node(source); + // Set the pointers in the memberTable for (int i = 0; i < source.size(); ++i) { memberTable.set(Member(source[i]), node); } source.clear(); + } else { // Make a new internal node node = new Node(); + const AABox bounds = computeBounds(source, 0, source.size() - 1); const Vector3 extent = bounds.high() - bounds.low(); + Vector3::Axis splitAxis = extent.primaryAxis(); + float splitLocation; + // Arrays for holding the children Array<_AABSPTree::Handle<T> * > lt, gt; + if (numMeanSplits <= 0) { + source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis)); + // Choose the split location to be the center of whatever fell in the center splitLocation = node->valueArray[0]->center[splitAxis]; + // Some of the elements in the lt or gt array might really overlap the split location. // Move them as needed. for (int i = 0; i < lt.size(); ++i) { @@ -636,6 +779,7 @@ public: lt.fastRemove(i); --i; } } + for (int i = 0; i < gt.size(); ++i) { const AABox& bounds = gt[i]->bounds; if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { @@ -645,6 +789,7 @@ public: gt.fastRemove(i); --i; } } + if ((node->valueArray.size() > (source.size() / 2)) && (source.size() > 6)) { // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the @@ -654,16 +799,21 @@ public: numMeanSplits = 1; } } + // Note: numMeanSplits may have been increased by the code in the previous case above in order to // force a re-partition. + if (numMeanSplits > 0) { // Split along the mean splitLocation = (bounds.high()[splitAxis] + bounds.low()[splitAxis]) / 2.0; + source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation)); + // The Comparator ensures that elements are strictly on the correct side of the split } + # if defined(G3D_DEBUG) && defined(VERIFY_TREE) debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size()); // Verify that all objects ended up on the correct side of the split. @@ -672,20 +822,25 @@ public: const AABox& bounds = lt[i]->bounds; debugAssert(bounds.high()[splitAxis] < splitLocation); } + for (int i = 0; i < gt.size(); ++i) { const AABox& bounds = gt[i]->bounds; debugAssert(bounds.low()[splitAxis] > splitLocation); } + for (int i = 0; i < node->valueArray.size(); ++i) { const AABox& bounds = node->valueArray[i]->bounds; debugAssert(bounds.high()[splitAxis] >= splitLocation); debugAssert(bounds.low()[splitAxis] <= splitLocation); } # endif + // The source array is no longer needed source.clear(); + node->splitAxis = splitAxis; node->splitLocation = splitLocation; + // Update the bounds array and member table node->boundsArray.resize(node->valueArray.size()); for (int i = 0; i < node->valueArray.size(); ++i) { @@ -693,15 +848,20 @@ public: node->boundsArray[i] = v->bounds; memberTable.set(Member(v), node); } + if (lt.size() > 0) { node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp); } + if (gt.size() > 0) { node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp); } + } + return node; } + /** Recursively clone the passed in node tree, setting pointers for members in the memberTable as appropriate. @@ -709,36 +869,47 @@ public: */ Node* cloneTree(Node* src) { Node* dst = new Node(*src); + // Make back pointers for (int i = 0; i < dst->valueArray.size(); ++i) { memberTable.set(Member(dst->valueArray[i]), dst); } + // Clone children for (int i = 0; i < 2; ++i) { if (src->child[i] != NULL) { dst->child[i] = cloneTree(src->child[i]); } } + return dst; } + /** Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but stores only Handle* internally to avoid memory copies. */ typedef _internal::Indirector<_AABSPTree::Handle<T> > Member; + typedef Table<Member, Node*> MemberTable; + /** Maps members to the node containing them */ MemberTable memberTable; + Node* root; + public: + /** To construct a balanced tree, insert the elements and then call AABSPTree::balance(). */ AABSPTree() : root(NULL) {} + AABSPTree(const AABSPTree& src) : root(NULL) { *this = src; } + AABSPTree& operator=(const AABSPTree& src) { delete root; // Clone tree takes care of filling out the memberTable. @@ -746,14 +917,17 @@ public: return *this; } + ~AABSPTree() { clear(); } + /** Throws out all elements of the set. */ void clear() { typedef typename Table<_internal::Indirector<_AABSPTree::Handle<T> >, Node* >::Iterator It; + // Delete all handles stored in the member table It cur = memberTable.begin(); It end = memberTable.end(); @@ -763,13 +937,16 @@ public: ++cur; } memberTable.clear(); + // Delete the tree structure itself delete root; root = NULL; } + size_t size() const { return memberTable.size(); } + /** Inserts an object into the set if it is not already present. O(log n) time. Does not @@ -780,19 +957,25 @@ public: // Already in the set return; } + _AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(value); + if (root == NULL) { // This is the first node; create a root node root = new Node(); } + Node* node = root->findDeepestContainingNode(h->bounds); + // Insert into the node node->valueArray.append(h); node->boundsArray.append(h->bounds); + // Insert into the node table Member m(h); memberTable.set(m, node); } + /** Inserts each elements in the array in turn. If the tree begins empty (no structure and no elements), this is faster than inserting each element in turn. You still need to balance @@ -815,6 +998,7 @@ public: root->boundsArray[j] = h->bounds; memberTable.set(Member(h), root); } + } else { // Insert at appropriate tree depth. for (int i = 0; i < valueArray.size(); ++i) { @@ -823,6 +1007,7 @@ public: } } + /** Returns true if this object is in the set, otherwise returns false. O(1) time. @@ -833,10 +1018,12 @@ public: return memberTable.containsKey(Member(&h)); } + /** Removes an object from the set in O(1) time. It is an error to remove members that are not already present. May unbalance the tree. + Removing an element never causes a node (split plane) to be removed... nodes are only changed when the tree is rebalanced. This behavior is desirable because it allows the split planes to be serialized, @@ -846,34 +1033,44 @@ public: debugAssertM(contains(value), "Tried to remove an element from a " "AABSPTree that was not present"); + // Get the list of elements at the node _AABSPTree::Handle<T> h(value); Member m(&h); + Array<_AABSPTree::Handle<T> * >& list = memberTable[m]->valueArray; + _AABSPTree::Handle<T>* ptr = NULL; + // Find the element and remove it for (int i = list.length() - 1; i >= 0; --i) { if (list[i]->value == value) { // This was the element. Grab the pointer so that // we can delete it below ptr = list[i]; + // Remove the handle from the node list.fastRemove(i); + // Remove the corresponding bounds memberTable[m]->boundsArray.fastRemove(i); break; } } + // Remove the member memberTable.remove(m); + // Delete the handle data structure delete ptr; ptr = NULL; } + /** If the element is in the set, it is removed. The element is then inserted. + This is useful when the == and hashCode methods on <I>T</I> are independent of the bounds. In that case, you may call update(v) to insert an @@ -888,18 +1085,22 @@ public: insert(value); } + /** Rebalances the tree (slow). Call when objects have moved substantially from their original positions (which unbalances the tree and causes the spatial queries to be slow). + @param valuesPerNode Maximum number of elements to put at a node. + @param numMeanSplits numMeanSplits = 0 gives a fully axis aligned BSP-tree, where the balance operation attempts to balance the tree so that every splitting plane has an equal number of left and right children (i.e. it is a <B>median</B> split along that axis). This tends to maximize average performance. + You can override this behavior by setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT creates a full oct-tree, which tends to optimize peak performance at the expense of @@ -911,33 +1112,41 @@ public: // Tree is empty return; } + // Get all handles and delete the old tree structure Node* oldRoot = root; for (int c = 0; c < 2; ++c) { if (root->child[c] != NULL) { root->child[c]->getHandles(root->valueArray); + // Delete the child; this will delete all structure below it delete root->child[c]; root->child[c] = NULL; } } + Array<_AABSPTree::Handle<T> * > temp; // Make a new root. Work with a copy of the value array because // makeNode clears the source array as it progresses Array<_AABSPTree::Handle<T> * > copy(oldRoot->valueArray); root = makeNode(copy, valuesPerNode, numMeanSplits, temp); + // Throw away the old root node delete oldRoot; oldRoot = NULL; + // Walk the tree, assigning splitBounds. We start with unbounded // space. This will override the current member table. root->assignSplitBounds(AABox::maxFinite()); + # ifdef _DEBUG // Ensure that the balanced tree is till correct root->verifyNode(Vector3::minFinite(), Vector3::maxFinite()); # endif } + protected: + /** @param parentMask The mask that this node returned from culledBy. */ @@ -946,12 +1155,15 @@ protected: Array<T>& members, Node* node, uint32 parentMask) { + int dummy; + if (parentMask == 0) { // None of these planes can cull anything for (int v = node->valueArray.size() - 1; v >= 0; --v) { members.append(node->valueArray[v]->value); } + // Iterate through child nodes for (int c = 0; c < 2; ++c) { if (node->child[c]) { @@ -959,13 +1171,16 @@ protected: } } } else { + // Test values at this node against remaining planes for (int v = node->boundsArray.size() - 1; v >= 0; --v) { if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) { members.append(node->valueArray[v]->value); } } + uint32 childMask = 0xFFFFFF; + // Iterate through child nodes for (int c = 0; c < 2; ++c) { if (node->child[c] && @@ -976,7 +1191,9 @@ protected: } } } + public: + /** Returns all members inside the set of planes. @param members The results are appended to this array. @@ -985,12 +1202,15 @@ public: if (root == NULL) { return; } + getIntersectingMembers(plane, members, root, 0xFFFFFF); } + /** Typically used to find all visible objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects <B>not<B> culled by frustum. + Example: <PRE> Array<Object*> visible; @@ -1001,11 +1221,14 @@ public: */ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const { Array<Plane> plane; + for (int i = 0; i < frustum.faceArray.size(); ++i) { plane.append(frustum.faceArray[i].plane); } + getIntersectingMembers(plane, members); } + /** C++ STL style iterator variable. See beginBoxIntersection(). The iterator overloads the -> (dereference) operator, so this @@ -1019,26 +1242,34 @@ public: class BoxIntersectionIterator { private: friend class AABSPTree<T>; + /** True if this is the "end" iterator instance */ bool isEnd; + /** The box that we're testing against. */ AABox box; + /** Node that we're currently looking at. Undefined if isEnd is true. */ Node* node; + /** Nodes waiting to be processed */ // We could use backpointers within the tree and careful // state management to avoid ever storing the stack-- but // it is much easier this way and only inefficient if the // caller uses post increment (which they shouldn't!). Array<Node*> stack; + /** The next index of current->valueArray to return. Undefined when isEnd is true.*/ int nextValueArrayIndex; + BoxIntersectionIterator() : isEnd(true) {} + BoxIntersectionIterator(const AABox& b, const Node* root) : isEnd(root == NULL), box(b), node(const_cast<Node*>(root)), nextValueArrayIndex(-1) { + // We intentionally start at the "-1" index of the current // node so we can use the preincrement operator to move // ourselves to element 0 instead of repeating all of the @@ -1046,10 +1277,13 @@ public: // cause us to become the "end" instance. ++(*this); } + public: + inline bool operator!=(const BoxIntersectionIterator& other) const { return ! (*this == other); } + bool operator==(const BoxIntersectionIterator& other) const { if (isEnd) { return other.isEnd; @@ -1064,41 +1298,49 @@ public: (stack.length() != other.stack.length())) { return false; } + // See if the stacks are the same for (int i = 0; i < stack.length(); ++i) { if (stack[i] != other.stack[i]) { return false; } } + // We failed to find a difference; they must be the same return true; } } + /** Pre increment. */ BoxIntersectionIterator& operator++() { ++nextValueArrayIndex; + bool foundIntersection = false; while (! isEnd && ! foundIntersection) { + // Search for the next node if we've exhausted this one while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) { // If we entered this loop, then the iterator has exhausted the elements at // node (possibly because it just switched to a child node with no members). // This loop continues until it finds a node with members or reaches // the end of the whole intersection search. + // If the right child overlaps the box, push it onto the stack for // processing. if ((node->child[1] != NULL) && (box.high()[node->splitAxis] > node->splitLocation)) { stack.push(node->child[1]); } + // If the left child overlaps the box, push it onto the stack for // processing. if ((node->child[0] != NULL) && (box.low()[node->splitAxis] < node->splitLocation)) { stack.push(node->child[0]); } + if (stack.length() > 0) { // Go on to the next node (which may be either one of the ones we // just pushed, or one from farther back the tree). @@ -1109,6 +1351,7 @@ public: isEnd = true; } } + // Search for the next intersection at this node until we run out of children while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) { if (box.intersects(node->boundsArray[nextValueArrayIndex])) { @@ -1120,8 +1363,10 @@ public: } } } + return *this; } + private: /** Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code. @@ -1132,19 +1377,23 @@ public: ++this; return old; }*/ + public: + /** Overloaded dereference operator so the iterator can masquerade as a pointer to a member */ const T& operator*() const { alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); return node->valueArray[nextValueArrayIndex]->value; } + /** Overloaded dereference operator so the iterator can masquerade as a pointer to a member */ T const * operator->() const { alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); return &(stack.last()->valueArray[nextValueArrayIndex]->value); } + /** Overloaded cast operator so the iterator can masquerade as a pointer to a member */ operator T*() const { @@ -1153,16 +1402,19 @@ public: } }; + /** Iterates through the members that intersect the box */ BoxIntersectionIterator beginBoxIntersection(const AABox& box) const { return BoxIntersectionIterator(box, root); } + BoxIntersectionIterator endBoxIntersection() const { // The "end" iterator instance return BoxIntersectionIterator(); } + /** Appends all members whose bounds intersect the box. See also AABSPTree::beginBoxIntersection. @@ -1174,18 +1426,25 @@ public: root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false); } + /** Invoke a callback for every member along a ray until the closest intersection is found. + @param callback either a function or an instance of a class with an overloaded operator() of the form: + <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to the intersection, otherwise leaves it unmodified. A common example is: + <pre> class Entity { public: + void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) { float d = maxDist; + // ... search for intersection distance d + if ((d > 0) && (d < maxDist)) { // Intersection occured maxDist = d; @@ -1194,24 +1453,30 @@ public: } } }; + // Finds the surface normal and location of the first intersection with the scene class Intersection { public: Entity* closestEntity; Vector3 hitLocation; Vector3 hitNormal; + void operator()(const Ray& ray, const Entity* entity, float& distance) { entity->intersect(ray, distance, hitLocation, hitNormal); } }; + AABSPTree<Entity*> scene; + Intersection intersection; float distance = inf(); scene.intersectRay(camera.worldRay(x, y), intersection, distance); </pre> + @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection. On return, this is set to the distance to the first intersection encountered. + @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked. If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true. */ @@ -1222,9 +1487,12 @@ public: float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false) const { + root->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); + } + /** @param members The results are appended to this array. */ @@ -1232,9 +1500,11 @@ public: if (root == NULL) { return; } + AABox box; sphere.getBounds(box); root->getIntersectingMembers(box, sphere, members, true); + } #if 0 /** @@ -1245,6 +1515,7 @@ public: void serializeStructure(BinaryOutput& bo) const { Node::serializeStructure(root, bo); } + /** Clears the member table */ void deserializeStructure(BinaryInput& bi) { clear(); @@ -1262,6 +1533,7 @@ public: } } + /** C++ STL style iterator variable. See begin(). Overloads the -> (dereference) operator, so this acts like a pointer @@ -1270,17 +1542,23 @@ public: class Iterator { private: friend class AABSPTree<T>; + // Note: this is a Table iterator, we are currently defining // Set iterator typename Table<Member, Node*>::Iterator it; + Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {} + public: + inline bool operator!=(const Iterator& other) const { return !(*this == other); } + bool operator==(const Iterator& other) const { return it == other.it; } + /** Pre increment. */ @@ -1288,6 +1566,7 @@ public: ++it; return *this; } + private: /** Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code. @@ -1298,17 +1577,21 @@ public: return old; }*/ public: + const T& operator*() const { return it->key.handle->value; } + T* operator->() const { return &(it->key.handle->value); } + operator T*() const { return &(it->key.handle->value); } }; + /** C++ STL style iterator method. Returns the first member. Use preincrement (++entry) to get to the next element (iteration @@ -1319,6 +1602,7 @@ public: return Iterator(memberTable.begin()); } + /** C++ STL style iterator method. Returns one after the last iterator element. @@ -1327,7 +1611,11 @@ public: return Iterator(memberTable.end()); } }; + } + #endif + + diff --git a/src/shared/vmap/BaseModel.cpp b/src/shared/vmap/BaseModel.cpp index 335a9a128e0..2ffd5672218 100644 --- a/src/shared/vmap/BaseModel.cpp +++ b/src/shared/vmap/BaseModel.cpp @@ -17,12 +17,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "BaseModel.h" #include "VMapTools.h" + using namespace G3D; + namespace VMAP { //========================================================== + void BaseModel::getMember(Array<TriangleBox>& pMembers) { for(unsigned int i=0; i<iNTriangles; i++) @@ -30,12 +34,15 @@ namespace VMAP pMembers.append(iTriangles[i]); } } + //========================================================== BaseModel::BaseModel(unsigned int pNNodes, unsigned int pNTriangles) { init(pNNodes, pNTriangles); }; + //========================================================== + void BaseModel::init(unsigned int pNNodes, unsigned int pNTriangles) { iNNodes = pNNodes; @@ -45,16 +52,21 @@ namespace VMAP if(iNNodes >0) iTreeNodes = new TreeNode[iNNodes]; if(iNTriangles >0) iTriangles = new TriangleBox[iNTriangles]; } + //========================================================== + void BaseModel::free() { if(getTriangles() != 0) delete [] getTriangles(); setNTriangles(0); if(getTreeNodes() != 0) delete [] getTreeNodes(); setNNodes(0); } + //========================================================== + void BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& /*pOutNormal*/) const { bool isInside = false; + float d = MyCollisionDetection::collisionLocationForMovingPointFixedAABox( pRay.origin, pRay.direction, pBox, @@ -64,7 +76,9 @@ namespace VMAP pMaxDist = d; } } + //========================================================== + bool BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const { // See if the ray will ever hit this node or its children @@ -73,9 +87,12 @@ namespace VMAP bool rayWillHitBounds = MyCollisionDetection::collisionLocationForMovingPointFixedAABox( pRay.origin, pRay.direction, pBox, location, alreadyInsideBounds); + bool canHitThisNode = (alreadyInsideBounds || (rayWillHitBounds && ((location - pRay.origin).squaredLength() < (pMaxDist * pMaxDist)))); + return canHitThisNode; } + } // VMAP diff --git a/src/shared/vmap/BaseModel.h b/src/shared/vmap/BaseModel.h index 1e896bb62ec..098e1d9381b 100644 --- a/src/shared/vmap/BaseModel.h +++ b/src/shared/vmap/BaseModel.h @@ -17,22 +17,29 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _BASEMODEL_H_ #define _BASEMODEL_H_ + #include <G3D/AABox.h> #include <G3D/Vector3.h> + #include "ShortVector.h" #include "ShortBox.h" #include "TreeNode.h" + /** A model is based on triangles. To be able to check intersection we need a BSP-Tree. This Class holds the array of triangles as well as the management information for the BSP-Tree. Both are stored in static array and index information is used instead of pointers. Therefore we can load the whole object as a binary block. + The vectors are relative to a base position. */ + namespace VMAP { + class BaseModel { protected: @@ -48,31 +55,48 @@ namespace VMAP iNNodes = pNNodes; iNTriangles = pNTriangles; iTriangles = pTriangleBox; iTreeNodes = pTreeNode; }; BaseModel(unsigned int pNNodes, unsigned int pNTriangles); + // destructor does nothing ! The subclass controles the array memory and knows when to free it ~BaseModel() {} + void free(); void init(unsigned int pNNodes, unsigned int pNTriangles); + void getMember(G3D::Array<TriangleBox>& pMembers); + inline const TriangleBox& getTriangle(int pPos) const { return(iTriangles[pPos]); } inline TriangleBox& getTriangle(int pPos) { return(iTriangles[pPos]); } + inline void setTriangle(const TriangleBox& pTriangleBox, int pPos) { iTriangles[pPos] = pTriangleBox; } + inline const TreeNode& getTreeNode(int pPos) const { return(getTreeNodes()[pPos]); } inline TreeNode& getTreeNode(int pPos) { return(getTreeNodes()[pPos]); } + inline void setTreeNode(const TreeNode& pTreeNode, int pPos) { getTreeNodes()[pPos] = pTreeNode; } + inline void setBasePosition(const G3D::Vector3& pBasePosition) { iBasePosition = pBasePosition; } + inline const G3D::Vector3& getBasePosition() const { return(iBasePosition); } + inline unsigned int getNNodes() const { return(iNNodes); } inline unsigned int getNTriangles() const { return(iNTriangles); } + inline void setNNodes(unsigned int pNNodes) { iNNodes = pNNodes; } inline void setNTriangles(unsigned int pNTriangles) { iNTriangles = pNTriangles; } + inline void setTriangleArray(TriangleBox *pGlobalTriangleArray ) { iTriangles = pGlobalTriangleArray ; } inline void setTreeNodeArray(TreeNode *pGlobalTreeNodeArray ) { iTreeNodes = pGlobalTreeNodeArray ; } + inline TriangleBox* getTriangles() const { return(iTriangles); } + inline TreeNode* getTreeNodes() const{ return(iTreeNodes); } + inline size_t getMemUsage() { return(iNTriangles * sizeof(TriangleBox) + iNNodes * sizeof(TreeNode) + sizeof(BaseModel)); } + void intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; bool intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const; }; + } #endif /*BASEMODEL_H_*/ diff --git a/src/shared/vmap/CoordModelMapping.cpp b/src/shared/vmap/CoordModelMapping.cpp index e4aa5e08893..39d1165f115 100644 --- a/src/shared/vmap/CoordModelMapping.cpp +++ b/src/shared/vmap/CoordModelMapping.cpp @@ -17,34 +17,45 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "CoordModelMapping.h" + #include <string.h> #include <stdio.h> + using namespace G3D; + namespace VMAP { + //============================================================ //============================================================ + void CMappingEntry::addFilename(char *pName) { std::string name = std::string(pName); if(!iFilenames.contains(name)) iFilenames.append(std::string(pName)); } + //============================================================ + const std::string CMappingEntry::getKeyString() const { return(CMappingEntry::getKeyString(iMapId,xPos, yPos)); } + const std::string CMappingEntry::getKeyString( unsigned int pMapId, int pXPos, int pYPos ) { char b[100]; sprintf(b,"%03u_%d_%d", pMapId, pXPos, pYPos); return(std::string(b)); } + //============================================================ //============================================================ //============================================================ + CoordModelMapping::~CoordModelMapping() { Array<std::string> keys = iMapObjectFiles.getKeys(); @@ -58,7 +69,9 @@ namespace VMAP } } } + //============================================================ + int findPosChar(const char *namebuffer, char pSearch, int pCount) { int result = -1; @@ -87,7 +100,9 @@ namespace VMAP printf("ERROR: Can't open file: %s\n",pDirectoryFileName.c_str()); return false; } + char buffer[500+1]; + CMappingEntry* cMappingEntry; while(fgets(buffer, 500, f)) { @@ -96,7 +111,9 @@ namespace VMAP int xpos, ypos, noVec; float scale; xpos = ypos = noVec = 0; + //sscanf(buffer, "%d %d %s %s %f %d", &xpos, &ypos, namebuffer,positionbuffer, &scale, &noVec); + // this is ugly, but the format has no read delimiter and a space could be in the first part of the name int nameStart = findPosChar(buffer, ' ', 2);// find the 2. space if(nameStart > -1 && (iFilterMethod == NULL || (*iFilterMethod)(buffer))) @@ -107,6 +124,7 @@ namespace VMAP // find the 1. space (after the name) nameEnd += findPosChar(&buffer[nameEnd], ' ', 1); buffer[nameEnd] = 0; // terminate the name + sscanf(buffer, "%d %d", &xpos, &ypos); sscanf(&buffer[nameEnd+1], "%s %f %d", positionbuffer, &scale, &noVec); unsigned int mapId = getMapIdFromFilename(std::string(&buffer[nameStart])); @@ -120,6 +138,7 @@ namespace VMAP xpos = 0; // store all files under the groupKey ypos = 0; } + std::string key = CMappingEntry::getKeyString(mapId, xpos, ypos); cMappingEntry = getCMappingEntry(key); if(cMappingEntry == 0) @@ -136,15 +155,19 @@ namespace VMAP fclose(f); return true; } + //============================================================ + const NameCollection CoordModelMapping::getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos) { NameCollection result; Array<std::string> rawNames; + CMappingEntry *entry = getCMappingEntry(CMappingEntry::getKeyString(pMapId, xPos, yPos)); if(entry != 0) { rawNames = entry->getFilenames(); + int pos = 0; while(pos < rawNames.size()) { @@ -170,6 +193,8 @@ namespace VMAP } return result; } + //================================================================= + } diff --git a/src/shared/vmap/CoordModelMapping.h b/src/shared/vmap/CoordModelMapping.h index 8600620cc99..7684bf1b373 100644 --- a/src/shared/vmap/CoordModelMapping.h +++ b/src/shared/vmap/CoordModelMapping.h @@ -17,32 +17,43 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _COORDMODELMAPPING_H_ #define _COORDMODELMAPPING_H_ + #include <cstdio> #include <G3D/Table.h> #include <G3D/Array.h> + /** This Class is a helper Class to convert the raw vector data into BSP-Trees. We read the directory file of the raw data output and build logical groups. Models with a lot of vectors are not merged into a resulting model, but separated into an additional file. */ + namespace VMAP { + #define MIN_VERTICES_FOR_OWN_CONTAINER_FILE 65000 + // if we are in an instance #define MIN_INST_VERTICES_FOR_OWN_CONTAINER_FILE 40000 + //===================================================== class NameCollection { public: G3D::Array<std::string> iMainFiles; G3D::Array<std::string> iSingeFiles; + void appendToMain(const std::string& pStr) { iMainFiles.append(pStr); } void appendToSingle(const std::string& pStr) { iSingeFiles.append(pStr); } + size_t size() { return (iMainFiles.size() + iSingeFiles.size()); } }; + //===================================================== + class CMappingEntry { private: @@ -50,6 +61,7 @@ namespace VMAP int yPos; unsigned int iMapId; G3D::Array<std::string> iFilenames; + public: CMappingEntry() { }; CMappingEntry(unsigned int pMapId, const int pXPos, const int pYPos) @@ -58,12 +70,17 @@ namespace VMAP xPos = pXPos; yPos = pYPos; }; ~CMappingEntry() {}; + void addFilename(char *pName); const std::string getKeyString() const; inline const G3D::Array<std::string>& getFilenames() const { return(iFilenames); } + static const std::string getKeyString(unsigned int pMapId, int pXPos, int pYPos); + }; + //===================================================== + class CoordModelMapping { private: @@ -72,10 +89,12 @@ namespace VMAP G3D::Array<unsigned int> iMapIds; G3D::Array<unsigned int> iWorldAreaGroups; bool (*iFilterMethod)(char *pName); + inline void addCMappingEntry(CMappingEntry* pCMappingEntry) { iMapObjectFiles.set(pCMappingEntry->getKeyString(), pCMappingEntry); } + inline CMappingEntry* getCMappingEntry(const std::string& pKey) { if(iMapObjectFiles.containsKey(pKey)) @@ -83,14 +102,19 @@ namespace VMAP else return 0; } + public: CoordModelMapping() { iFilterMethod = NULL; } virtual ~CoordModelMapping(); + bool readCoordinateMapping(const std::string& pDirectoryFileName); + const NameCollection getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos); + static unsigned int getMapIdFromFilename(const std::string& pName) { size_t spos; + spos = pName.find_last_of('/'); std::string basename = pName.substr(0, spos); spos = basename.find_last_of('/'); @@ -98,9 +122,11 @@ namespace VMAP unsigned int mapId = atoi(groupname.c_str()); return(mapId); } + const G3D::Array<unsigned int>& getMaps() const { return iMapIds; } bool isAlreadyProcessedSingleFile(const std::string& pName) const { return iProcesseSingleFiles.containsKey(pName); } void addAlreadyProcessedSingleFile(const std::string& pName) { iProcesseSingleFiles.set(pName,pName); } + inline void addWorldAreaMap(unsigned int pMapId) { if(!iWorldAreaGroups.contains(pMapId)) @@ -110,6 +136,7 @@ namespace VMAP } inline bool isWorldAreaMap(unsigned int pMapId) { return(iWorldAreaGroups.contains(pMapId)); } void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; } + }; } #endif /*_COORDMODELMAPPING_H_*/ diff --git a/src/shared/vmap/DebugCmdLogger.cpp b/src/shared/vmap/DebugCmdLogger.cpp index 5b5fbebdd59..c899606045b 100644 --- a/src/shared/vmap/DebugCmdLogger.cpp +++ b/src/shared/vmap/DebugCmdLogger.cpp @@ -17,12 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <cstdio> + #include "DebugCmdLogger.h" #include <stdio.h> + using namespace G3D; + namespace VMAP { + bool CommandFileRW::appendCmd(const Command& #ifdef _DEBUG pCommand @@ -50,7 +55,9 @@ namespace VMAP return true; #endif } + //========================================================= + bool CommandFileRW::appendCmds(const Array<Command>& #ifdef _DEBUG pCmdArray @@ -67,6 +74,7 @@ namespace VMAP else f = fopen(iFileName.c_str(), "ab"); resetfile = false; + if(f) { result = true; @@ -86,7 +94,9 @@ namespace VMAP return true; #endif } + //========================================================= + bool CommandFileRW::getNewCommands(Array<Command>& pCmdArray) { bool result = false; diff --git a/src/shared/vmap/DebugCmdLogger.h b/src/shared/vmap/DebugCmdLogger.h index f67687343c1..5493ab6f332 100644 --- a/src/shared/vmap/DebugCmdLogger.h +++ b/src/shared/vmap/DebugCmdLogger.h @@ -17,16 +17,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _DEBUGCMDLOGGER_H #define _DEBUGCMDLOGGER_H + #include <G3D/Vector3.h> #include <G3D/Array.h> + /** Class is used for debugging. We log activities into a file. With an external Class we read that log and display the activity in a graphical view. */ + namespace VMAP { + //========================================== enum C_TYPES { @@ -41,6 +46,7 @@ namespace VMAP TEST_HEIGHT, TEST_OBJECT_HIT, }; + class Command { int iType; @@ -48,11 +54,14 @@ namespace VMAP int ints[4]; char buffer[100]; public: + Command() { iType = STOP; } + inline int getType() { return iType; } inline G3D::Vector3 getVector(int pos) { return(G3D::Vector3(floats[pos*3+0], floats[pos*3+1], floats[pos*3+2])); } inline int getInt(int pos) { return(ints[pos]); } inline char* getBuffer() { return(buffer); } + void fillStopCmd() { iType = STOP; } void fillStartCmd() { iType = START; } void fillLoadTileCmd(int x, int y, G3D::uint32 pMapId) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; ints[2] = pMapId; } @@ -78,9 +87,12 @@ namespace VMAP floats[6] = pResultPos.x; floats[7]=pResultPos.y; floats[8]=pResultPos.z; ints[0] = result; ints[1] = pMapId; } + bool isCoreCmd() const { return(iType != TEST_VIS); } }; + //========================================== + class CommandFileRW { private: @@ -97,9 +109,11 @@ namespace VMAP void setFileName(const std::string& pName) { iFileName = pName; } bool getNewCommands(G3D::Array<Command>& commandArray); const G3D::Array<G3D::Array<Command> >& getFullCommandArray() { return iCommandArray; } + bool appendCmd(const Command& pCommand); bool appendCmds(const G3D::Array<Command>& pCmdArray); }; + } #endif diff --git a/src/shared/vmap/IVMapManager.h b/src/shared/vmap/IVMapManager.h index 40bb3164c0e..243a15aef73 100644 --- a/src/shared/vmap/IVMapManager.h +++ b/src/shared/vmap/IVMapManager.h @@ -17,36 +17,50 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _IVMAPMANAGER_H #define _IVMAPMANAGER_H + #include<string> + //=========================================================== + /** This is the minimum interface to the VMapMamager. */ + namespace VMAP { + enum VMAP_LOAD_RESULT { VMAP_LOAD_RESULT_ERROR, VMAP_LOAD_RESULT_OK, VMAP_LOAD_RESULT_IGNORED, }; + #define VMAP_INVALID_HEIGHT -100000.0f // for check #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case + //=========================================================== class IVMapManager { private: bool iEnableLineOfSightCalc; bool iEnableHeightCalc; + public: IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) {} + virtual ~IVMapManager(void) {} + virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; + virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; + virtual void unloadMap(unsigned int pMapId, int x, int y) = 0; virtual void unloadMap(unsigned int pMapId) = 0; + virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0; virtual float getHeight(unsigned int pMapId, float x, float y, float z) = 0; /** @@ -58,6 +72,7 @@ namespace VMAP send debug commands */ virtual bool processCommand(char *pCommand)= 0; + /** Enable/disable LOS calculation It is enabled by default. If it is enabled in mid game the maps have to loaded manualy @@ -68,9 +83,11 @@ namespace VMAP It is enabled by default. If it is enabled in mid game the maps have to loaded manualy */ void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; } + bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); } bool isHeightCalcEnabled() const { return(iEnableHeightCalc); } bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); } + virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0; /** Block maps from being used. @@ -79,6 +96,7 @@ namespace VMAP */ virtual void preventMapsFromBeingUsed(const char* pMapIdString) =0; }; + } #endif diff --git a/src/shared/vmap/ManagedModelContainer.cpp b/src/shared/vmap/ManagedModelContainer.cpp index a5258d86660..c7bfefe7179 100644 --- a/src/shared/vmap/ManagedModelContainer.cpp +++ b/src/shared/vmap/ManagedModelContainer.cpp @@ -17,16 +17,22 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "ManagedModelContainer.h" + using namespace G3D; + namespace VMAP { + ManagedModelContainer::ManagedModelContainer(void) : ModelContainer() { refCount = 0; } + ManagedModelContainer::~ManagedModelContainer(void) { } + } diff --git a/src/shared/vmap/ManagedModelContainer.h b/src/shared/vmap/ManagedModelContainer.h index c909aa878cf..e6862f915c7 100644 --- a/src/shared/vmap/ManagedModelContainer.h +++ b/src/shared/vmap/ManagedModelContainer.h @@ -17,16 +17,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _MANAGEDMODELCONTAINER_H #define _MANAGEDMODELCONTAINER_H + #include "ModelContainer.h" + //======================================================= /** This is a ModelContainer with reference count information. */ + namespace VMAP { //======================================================= + class ManagedModelContainer : public ModelContainer { @@ -35,10 +40,12 @@ namespace VMAP public: ManagedModelContainer(void) ; ~ManagedModelContainer(void); + void incRefCount() { ++refCount; } void decRefCount() { --refCount; if(refCount < 0) refCount = 0; } int getRefCount() { return refCount; } }; + //======================================================= } #endif diff --git a/src/shared/vmap/ModelContainer.cpp b/src/shared/vmap/ModelContainer.cpp index 94d453079d2..4a722d2585b 100644 --- a/src/shared/vmap/ModelContainer.cpp +++ b/src/shared/vmap/ModelContainer.cpp @@ -17,12 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <iostream> #include <fstream> + #include <string.h> + #include "ModelContainer.h" #include "VMapDefinitions.h" + using namespace G3D; + namespace VMAP { //========================================================== @@ -34,25 +39,32 @@ namespace VMAP return (pMc.getBasePosition() * pMc.getNTriangles()).hashCode(); } //========================================================== + ModelContainer::ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel) : BaseModel(pNNodes, pNTriangles) { + iNSubModel = pNSubModel; iSubModel = 0; if(pNSubModel > 0) iSubModel = new SubModel[iNSubModel]; } + //========================================================== + bool ModelContainer::operator==(const ModelContainer& pMc2) const { if (this->iNSubModel == 0 && pMc2.iNSubModel == 0 && this->iSubModel == 0 && pMc2.iSubModel == 0) return true; return this == &pMc2; } + //========================================================== + void ModelContainer::countSubModelsAndNodesAndTriangles(AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles) { // For this node we will need a TreeNode as well as for the internal nodes ++nNodes; + nSubModels += pNode.valueArray.size(); for(int i=0;i<pNode.valueArray.size(); i++) { @@ -62,6 +74,7 @@ namespace VMAP nNodes += m->getNNodes(); nTriangles += m->getNTriangles(); } + if(pNode.child[0] != 0) { countSubModelsAndNodesAndTriangles(*pNode.child[0], nSubModels, nNodes, nTriangles); @@ -72,6 +85,7 @@ namespace VMAP } } //========================================================== + void ModelContainer::fillContainer(const AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi, Vector3& pFinalLo, Vector3& pFinalHi) { // TreeNode for the SubModel @@ -79,20 +93,26 @@ namespace VMAP treeNode.setSplitAxis(pNode.splitAxis); treeNode.setSplitLocation(pNode.splitLocation); int currentTreeNodePos = pTreeNodePos++; + Vector3 lo = Vector3(inf(),inf(),inf()); Vector3 hi = Vector3(-inf(),-inf(),-inf()); + for(int i=0;i<pNode.valueArray.size(); i++) { G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i]; SubModel *m = h->value; + memcpy(&getTreeNodes()[pTreeNodePos], &m->getTreeNode(0), sizeof(TreeNode) * m->getNNodes()); memcpy(&getTriangles()[pTrianglePos], &m->getTriangle(0), sizeof(TriangleBox) * m->getNTriangles()); + SubModel newModel = SubModel(m->getNTriangles(), getTriangles(), pTrianglePos, m->getNNodes(), getTreeNodes(), pTreeNodePos); newModel.setReletiveBounds(m->getReletiveBounds().getLo(), m->getReletiveBounds().getHi()); newModel.setBasePosition(m->getBasePosition()); iSubModel[pSubModelPos++] = newModel; + pTreeNodePos += m->getNNodes(); pTrianglePos += m->getNTriangles(); + AABox box = m->getAABoxBounds(); lo = lo.min(box.low()); hi = hi.max(box.high()); @@ -105,6 +125,7 @@ namespace VMAP } */ // get absolute bounds + if(pNode.child[0] != 0) { treeNode.setChildPos(0, pTreeNodePos); @@ -115,45 +136,62 @@ namespace VMAP treeNode.setChildPos(1, pTreeNodePos); fillContainer(*pNode.child[1], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi); } + pLo = pLo.min(lo); pHi = pHi.max(hi); + treeNode.setBounds(lo,hi); + setTreeNode(treeNode, currentTreeNodePos); + } + //========================================================== /** Create the structure out of a AABSPTree */ + ModelContainer::ModelContainer(AABSPTree<SubModel *> *pTree) { + int nSubModels, nNodes, nTriangles; nSubModels = nNodes = nTriangles = 0; countSubModelsAndNodesAndTriangles(*pTree->root, nSubModels, nNodes, nTriangles); + init(nNodes, nTriangles); + iNSubModel = nSubModels; + iSubModel = new SubModel[iNSubModel]; + int subModelPos,treeNodePos, trianglePos; subModelPos = treeNodePos = trianglePos = 0; + Vector3 lo = Vector3(inf(),inf(),inf()); Vector3 hi = Vector3(-inf(),-inf(),-inf()); Vector3 finalLo, finalHi; finalLo = lo; finalHi = hi; + fillContainer(*pTree->root, subModelPos, treeNodePos, trianglePos, lo, hi, finalLo, finalHi); setBounds(finalLo, finalHi); } + //========================================================== + ModelContainer::~ModelContainer(void) { free(); if(iSubModel != 0) delete [] iSubModel; } //========================================================== + bool ModelContainer::writeFile(const char *filename) { bool result = false; unsigned int flags=0; unsigned int size; + FILE *wf =fopen(filename,"wb"); if(wf) { @@ -161,11 +199,13 @@ namespace VMAP result = true; if(result && fwrite("CTREE01",8,1,wf) != 1) result = false; if(result && fwrite(&flags,sizeof(unsigned int),1,wf) != 1) result = false; + if(result && fwrite("POS ",4,1,wf) != 1) result = false; size = sizeof(float)*3; if(result && fwrite(&size,4,1,wf) != 1) result = false; Vector3 basePos = getBasePosition(); if(result && fwrite(&basePos,sizeof(float),3,wf) != 3) result = false; + if(result && fwrite("BOX ",4,1,wf) != 1) result = false; size = sizeof(float)*6; if(result && fwrite(&size,4,1,wf) != 1) result = false; @@ -173,28 +213,35 @@ namespace VMAP if(result && fwrite(&low,sizeof(float),3,wf) != 3) result = false; Vector3 high = iBox.high(); if(result && fwrite(&high,sizeof(float),3,wf) != 3) result = false; + if(result && fwrite("NODE",4,1,wf) != 1) result = false; size = sizeof(unsigned int)+ sizeof(TreeNode)*getNNodes(); if(result && fwrite(&size,4,1,wf) != 1) result = false; unsigned int val = getNNodes(); if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false; if(result && fwrite(getTreeNodes(),sizeof(TreeNode),getNNodes(),wf) != getNNodes()) result = false; + if(result && fwrite("TRIB",4,1,wf) != 1) result = false; size = sizeof(unsigned int)+ sizeof(TriangleBox)*getNTriangles(); if(result && fwrite(&size,4,1,wf) != 1) result = false; val = getNTriangles(); if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false; if(result && fwrite(getTriangles(),sizeof(TriangleBox),getNTriangles(),wf) != getNTriangles()) result = false; + if(result && fwrite("SUBM",4,1,wf) != 1) result = false; size = sizeof(unsigned int)+ sizeof(SubModel)*iNSubModel; if(result && fwrite(&size,4,1,wf) != 1) result = false; if(result && fwrite(&iNSubModel,sizeof(unsigned int),1,wf) != 1) result = false; if(result && fwrite(iSubModel,sizeof(SubModel),iNSubModel,wf) != iNSubModel) result = false; + fclose(wf); } + return(result); } + //=============================================================== + bool ModelContainer::readFile(const char *filename) { bool result = false; @@ -207,6 +254,7 @@ namespace VMAP if(rf) { free(); + result = true; char magic[8]; // Ignore the added magic header fread(magic,1,8,rf); @@ -219,6 +267,7 @@ namespace VMAP Vector3 basePos; if(result && fread(&basePos,sizeof(float),3,rf) != 3) result = false; setBasePosition(basePos); + //---- Box if(result && fread(chunk,4,1,rf) != 1) result = false; if(result && fread(&size,4,1,rf) != 1) result = false; @@ -226,25 +275,32 @@ namespace VMAP if(result && fread(&low,sizeof(float),3,rf) != 3) result = false; if(result && fread(&high,sizeof(float),3,rf) != 3) result = false; setBounds(low, high); + //---- TreeNodes if(result && fread(chunk,4,1,rf) != 1) result = false; if(result && fread(&size,4,1,rf) != 1) result = false; + if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false; if(result) setNNodes(ival); if(result) setTreeNodeArray(new TreeNode[getNNodes()]); if(result && fread(getTreeNodes(),sizeof(TreeNode),getNNodes(),rf) != getNNodes()) result = false; + //---- TriangleBoxes if(result && fread(chunk,4,1,rf) != 1) result = false; if(result && fread(&size,4,1,rf) != 1) result = false; + if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false; setNTriangles(ival); if(result) setTriangleArray(new TriangleBox[getNTriangles()]); if(result && fread(getTriangles(),sizeof(TriangleBox),getNTriangles(),rf) != getNTriangles()) result = false; + //---- SubModel if(result && fread(chunk,4,1,rf) != 1) result = false; if(result && fread(&size,4,1,rf) != 1) result = false; + if(result && fread(&iNSubModel,sizeof(unsigned int),1,rf) != 1) result = false; if(result) iSubModel = new SubModel[iNSubModel]; + if(result) { for(unsigned int i=0;i<iNSubModel && result; ++i) @@ -260,12 +316,15 @@ namespace VMAP } return result; } + //================================================================= + size_t ModelContainer::getMemUsage() { // BaseModel is included in ModelContainer return(iNSubModel * sizeof(SubModel) + BaseModel::getMemUsage() + sizeof(ModelContainer) - sizeof(BaseModel)); } + //================================================================= #ifdef _DEBUG_VMAPS #ifndef gBoxArray @@ -275,6 +334,7 @@ namespace VMAP extern bool myfound; #endif #endif + void ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const { IntersectionCallBack<SubModel> intersectCallback; @@ -282,12 +342,16 @@ namespace VMAP Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin - getBasePosition(), pRay.direction); iTreeNodes[0].intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false); } + //========================================================== + bool ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist) const { return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist); } + //================================================================= + template<typename RayCallback> void ModelContainer::intersectRay(const G3D::Ray& pRay, RayCallback& intersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast) { @@ -302,7 +366,9 @@ namespace VMAP { pAABox = pMc.getAABoxBounds(); } + //================================================================= + void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox) { pAABox = pMc->getAABoxBounds(); diff --git a/src/shared/vmap/ModelContainer.h b/src/shared/vmap/ModelContainer.h index 6a595bec39f..abc96261050 100644 --- a/src/shared/vmap/ModelContainer.h +++ b/src/shared/vmap/ModelContainer.h @@ -17,18 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _MODELCONTAINER_H #define _MODELCONTAINER_H + // load our modified version first !! #include "AABSPTree.h" + #include <G3D/AABox.h> #include <G3D/Vector3.h> #include <G3D/Ray.h> + #include "ShortBox.h" #include "TreeNode.h" #include "VMapTools.h" #include "SubModel.h" #include "BaseModel.h" + namespace VMAP { /** @@ -39,41 +44,65 @@ namespace VMAP The references are done by indexes within these static arrays. Therefore we are able to just load a binary block and do not need to mess around with memory allocation and pointers. */ + //===================================================== + class ModelContainer : public BaseModel { private: unsigned int iNSubModel; SubModel *iSubModel; G3D::AABox iBox; + ModelContainer (const ModelContainer& c): BaseModel(c) {} ModelContainer& operator=(const ModelContainer& ) {} + public: ModelContainer() : BaseModel() { iNSubModel =0; iSubModel = 0; }; + // for the mainnode ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel); + ModelContainer(G3D::AABSPTree<SubModel *> *pTree); + ~ModelContainer(void); + inline const void setSubModel(const SubModel& pSubModel, int pPos) { iSubModel[pPos] = pSubModel; } + inline const SubModel& getSubModel(int pPos) const { return iSubModel[pPos]; } + inline unsigned int getNSubModel() const { return(iNSubModel); } + void countSubModelsAndNodesAndTriangles(G3D::AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles); + void fillContainer(const G3D::AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi, G3D::Vector3& pFinalLo, G3D::Vector3& pFinalHi); + bool readRawFile(const char *name); + inline const G3D::AABox& getAABoxBounds() const { return(iBox); } + inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBox.set(lo,hi); } + bool writeFile(const char *filename); + bool readFile(const char *filename); + size_t getMemUsage(); size_t hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); } + void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; bool intersect(const G3D::Ray& pRay, float& pMaxDist) const; + template<typename RayCallback> void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false); + bool operator==(const ModelContainer& pMc2) const; }; + //===================================================== + //===================================================== + size_t hashCode(const ModelContainer& pMc); void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox); void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox); diff --git a/src/shared/vmap/NodeValueAccess.h b/src/shared/vmap/NodeValueAccess.h index 01c085be834..54fc5ee99b6 100644 --- a/src/shared/vmap/NodeValueAccess.h +++ b/src/shared/vmap/NodeValueAccess.h @@ -17,25 +17,32 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _NODEVALUEACCESS_H #define _NODEVALUEACCESS_H + namespace VMAP { /** This is a helper Class to get access to SubModels or triangles when analyzing the BSP-Tree. */ + template<class TNode, class TValue> class NodeValueAccess { private: TNode const* iNodeArray; TValue const* iValueArray; + public: inline NodeValueAccess() : iNodeArray(NULL), iValueArray(NULL) {} + inline NodeValueAccess(TNode const* pNodeArray, TValue const* pValueArray) : iNodeArray(pNodeArray), iValueArray(pValueArray) {} inline TNode const* getNodePtr() const { return(iNodeArray); } inline TValue const* getValuePtr() const { return(iValueArray); } + inline TNode const& getNode(unsigned int pPos) const { return(iNodeArray[pPos]); } inline void setNode(const TNode& pNode, unsigned int pPos) { iNodeArray[pPos] = pNode; } + inline TValue const& getValue(unsigned int pPos) const { return(iValueArray[pPos]); } inline void setValue(const TValue& pValue, unsigned int pPos) { iValueArray[pPos] = pValue; } }; diff --git a/src/shared/vmap/ShortBox.h b/src/shared/vmap/ShortBox.h index f2d87bc6abe..0e98677aa9e 100644 --- a/src/shared/vmap/ShortBox.h +++ b/src/shared/vmap/ShortBox.h @@ -17,18 +17,24 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _SHORTBOX_H #define _SHORTBOX_H + #include <G3D/Vector3.h> #include <G3D/AABox.h> #include <G3D/Triangle.h> #include <G3D/Ray.h> + #include "ShortVector.h" + /** This is a box and a triangle Class using ShortVectors. Each vector has 16 bit an a fixed point 12.4 representation. */ + namespace VMAP { + class ShortBox { private: @@ -42,15 +48,18 @@ namespace VMAP inline void setHi(const ShortVector& pV){ iV2 = pV; } inline void setLo(const G3D::Vector3& pV){ iV1 = ShortVector(pV); } inline void setHi(const G3D::Vector3& pV){ iV2 = ShortVector(pV); } + inline bool operator==(const ShortBox& b) const { return ((iV1 == b.iV1) && (iV2 == b.iV2)); } + inline bool operator!=(const ShortBox& b) const { return !((iV1 == b.iV1) && (iV2 == b.iV2)); } }; + //===================================================================== #ifdef _DEBUG_VMAPS #ifndef gBoxArray @@ -61,7 +70,9 @@ namespace VMAP extern bool myfound; #endif #endif + static const G3D::Vector3 dummyZeroPosition = G3D::Vector3(0,0,0); + class TriangleBox { private: @@ -74,16 +85,20 @@ namespace VMAP _vertex[0] = pV1; _vertex[1] = pV2; _vertex[2] = pV3; + } inline const ShortVector& vertex (int n) const { return(_vertex[n]); } + inline const ShortBox getBounds()const { ShortBox box; + ShortVector lo = _vertex[0]; ShortVector hi = lo; + for (int i = 1; i < 3; ++i) { lo = lo.min(_vertex[i]); @@ -94,15 +109,19 @@ namespace VMAP return(box); } inline const G3D::Vector3& getBasePosition() { return(dummyZeroPosition); } + inline const G3D::AABox getAABoxBounds() const { ShortBox box = getBounds(); return(G3D::AABox(box.getLo().getVector3(), box.getHi().getVector3())); } + inline bool operator==(const TriangleBox& t) const { return ((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2])); } + inline bool operator!=(const TriangleBox& t) const { return !((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2])); } + inline void intersect(const G3D::Ray& pRay, float& pMaxDist, bool /*pStopAtFirstHitDummy*/, G3D::Vector3& /*pOutLocationDummy*/, G3D::Vector3& /*pOutNormalDummy*/) const { static const double epsilon = 0.00001; @@ -113,6 +132,7 @@ namespace VMAP else { testT = G3D::Triangle(vertex(2).getVector3(),vertex(1).getVector3(),vertex(0).getVector3()); + #ifdef _DEBUG_VMAPS { G3D::Triangle myt(testT.vertex(0)+p6, testT.vertex(1)+p6,testT.vertex(2)+p6); @@ -125,6 +145,7 @@ namespace VMAP } } }; + } #endif diff --git a/src/shared/vmap/ShortVector.h b/src/shared/vmap/ShortVector.h index 4de8cfde73d..5f0fb7d9fd8 100644 --- a/src/shared/vmap/ShortVector.h +++ b/src/shared/vmap/ShortVector.h @@ -17,25 +17,31 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _SHORTVECTOR_H #define _SHORTVECTOR_H + #include <G3D/Vector3.h> + namespace VMAP { /** Vector with 16 bit fix point values 12.4 bit. */ + class ShortVector { private: short iX; short iY; short iZ; + const static short maxvalue = 0x7fff; const static short minvalue = -0x7fff; const static int fixpointdiv = 16; const static short fixpoint_maxvalue = maxvalue / fixpointdiv; const static short fixpoint_minvalue = minvalue / fixpointdiv; + inline short float2Short(float fv) const { short sv; @@ -59,6 +65,7 @@ namespace VMAP fv = ((float)sv) / fixpointdiv; return fv; } + inline float getFX() const { return(short2Float(iX)); } inline float getFY() const { return(short2Float(iY)); } inline float getFZ() const { return(short2Float(iZ)); } @@ -70,6 +77,7 @@ namespace VMAP iY = float2Short(pVector.y); iZ = float2Short(pVector.z); } + inline ShortVector(float pX, float pY, float pZ) { iX = float2Short(pX); @@ -88,10 +96,13 @@ namespace VMAP iY = pShortVector.iY; iZ = pShortVector.iZ; } + inline float getX() const { return(iX); } inline float getY() const { return(iY); } inline float getZ() const { return(iZ); } + inline G3D::Vector3 getVector3() const { return(G3D::Vector3(getFX(), getFY(), getFZ())); } + inline ShortVector min(const ShortVector pShortVector) { ShortVector result = pShortVector; @@ -100,6 +111,7 @@ namespace VMAP if(pShortVector.iZ > iZ) { result.iZ = iZ; } return(result); } + inline ShortVector max(const ShortVector pShortVector) { ShortVector result = pShortVector; @@ -108,14 +120,17 @@ namespace VMAP if(pShortVector.iZ < iZ) { result.iZ = iZ; } return(result); } + inline bool operator==(const ShortVector& v) const { return (iX == v.iX && iY == v.iY && iZ == v.iZ); } + inline bool operator!=(const ShortVector& v) const { return !(iX == v.iX && iY == v.iY && iZ == v.iZ); } + }; } #endif diff --git a/src/shared/vmap/SubModel.cpp b/src/shared/vmap/SubModel.cpp index f04090c6269..370c80062d6 100644 --- a/src/shared/vmap/SubModel.cpp +++ b/src/shared/vmap/SubModel.cpp @@ -17,13 +17,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "SubModel.h" + #ifdef _ASSEMBLER_DEBUG extern FILE *::g_df; #endif + using namespace G3D; + namespace VMAP { + //========================================================== /** Functions to use ModelContainer with a AABSPTree @@ -32,16 +37,19 @@ namespace VMAP { return pSm.getNTriangles(); } + void getBounds(const SubModel& pSm, G3D::AABox& pAABox) { ShortBox box = pSm.getReletiveBounds(); pAABox.set(box.getLo().getVector3()+pSm.getBasePosition(), box.getHi().getVector3()+pSm.getBasePosition()); } + void getBounds(const SubModel* pSm, G3D::AABox& pAABox) { ShortBox box = pSm->getReletiveBounds(); pAABox.set(box.getLo().getVector3()+pSm->getBasePosition(), box.getHi().getVector3()+pSm->getBasePosition()); } + //========================================================== //========================================================== //========================================================== @@ -53,7 +61,9 @@ namespace VMAP iNodesPos = pNodesPos; iHasInternalMemAlloc = false; } + //========================================================== + SubModel::~SubModel(void) { if(iHasInternalMemAlloc) @@ -61,10 +71,13 @@ namespace VMAP free(); } } + //========================================================== + bool SubModel::operator==(const SubModel& pSm2) const { bool result = false; + if(getNNodes() == pSm2.getNNodes() && getNTriangles() == pSm2.getNTriangles() && getBasePosition() == pSm2.getBasePosition() && @@ -76,6 +89,7 @@ namespace VMAP return result; } //========================================================== + enum BIN_POSITIONS { BP_iNTriangles=8, @@ -99,14 +113,18 @@ namespace VMAP iHasInternalMemAlloc = *((bool *) (((char *) pBinBlock) + BP_iHasInternalMemAlloc)); iBox = *((ShortBox *) (((char *) pBinBlock) + BP_iBox)); } + //========================================================== + void SubModel::countNodesAndTriangles(AABSPTree<Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles) { ++pNNodes; pNTriabgles += pNode.valueArray.size(); + #ifdef _ASSEMBLER_DEBUG fprintf(::g_df, "Nodes: %d, Tris: %d\n",pNNodes, pNTriabgles); #endif + if(pNode.child[0] != 0) { countNodesAndTriangles(*pNode.child[0], pNNodes, pNTriabgles); @@ -116,15 +134,20 @@ namespace VMAP countNodesAndTriangles(*pNode.child[1], pNNodes, pNTriabgles); } } + //========================================================== + void SubModel::fillContainer(const AABSPTree<Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi) { TreeNode treeNode = TreeNode(pNode.valueArray.size(), pTrianglePos); treeNode.setSplitAxis(pNode.splitAxis); treeNode.setSplitLocation(pNode.splitLocation); + int currentTreeNodePos = pTreeNodePos++; + Vector3 lo = Vector3(inf(),inf(),inf()); Vector3 hi = Vector3(-inf(),-inf(),-inf()); + for(int i=0;i<pNode.valueArray.size(); i++) { G3D::_AABSPTree::Handle<Triangle>* h= pNode.valueArray[i]; @@ -132,8 +155,10 @@ namespace VMAP TriangleBox triangleBox = TriangleBox(t.vertex(0),t.vertex(1), t.vertex(2)); lo = lo.min(triangleBox.getBounds().getLo().getVector3()); hi = hi.max(triangleBox.getBounds().getHi().getVector3()); + getTriangles()[pTrianglePos++] = triangleBox; } + if(pNode.child[0] != 0) { treeNode.setChildPos(0, pTreeNodePos); @@ -144,29 +169,39 @@ namespace VMAP treeNode.setChildPos(1, pTreeNodePos); fillContainer(*pNode.child[1], pTreeNodePos, pTrianglePos, lo, hi); } + treeNode.setBounds(lo,hi); + // get absolute bounds pLo = pLo.min(lo); pHi = pHi.max(hi); + getTreeNodes()[currentTreeNodePos] = treeNode; } + //========================================================== + SubModel::SubModel(AABSPTree<Triangle> *pTree) { int nNodes, nTriangles; nNodes = nTriangles = 0; countNodesAndTriangles(*pTree->root, nNodes, nTriangles); + init(nNodes, nTriangles); + iTrianglesPos = 0; // this is the global array iNodesPos = 0; // this is the global array iHasInternalMemAlloc = true; int treeNodePos, trianglePos; treeNodePos = trianglePos = 0; + Vector3 lo = Vector3(inf(),inf(),inf()); Vector3 hi = Vector3(-inf(),-inf(),-inf()); + fillContainer(*pTree->root, treeNodePos, trianglePos, lo, hi); setReletiveBounds(lo, hi); } + //========================================================== #ifdef _DEBUG_VMAPS #ifndef gBoxArray @@ -177,6 +212,7 @@ namespace VMAP extern bool myfound; #endif #endif + //========================================================== void SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const { @@ -189,12 +225,16 @@ namespace VMAP #endif getTreeNode(0).intersectRay(relativeRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false); } + //========================================================== + bool SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist) const { return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist); } + //========================================================== + template<typename RayCallback> void SubModel::intersectRay(const Ray& pRay, RayCallback& pIntersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast) { @@ -206,5 +246,6 @@ namespace VMAP } } //========================================================== + } diff --git a/src/shared/vmap/SubModel.h b/src/shared/vmap/SubModel.h index d562f36d94e..89ea9b91d7b 100644 --- a/src/shared/vmap/SubModel.h +++ b/src/shared/vmap/SubModel.h @@ -17,15 +17,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _SUBMODEL_H #define _SUBMODEL_H + // load our modified version first !! #include "AABSPTree.h" + #include "ShortVector.h" #include "ShortBox.h" #include "TreeNode.h" #include "VMapTools.h" #include "BaseModel.h" + namespace VMAP { /** @@ -46,30 +50,44 @@ namespace VMAP #endif public: SubModel() : BaseModel(){ }; + SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos); SubModel(G3D::AABSPTree<G3D::Triangle> *pTree); ~SubModel(void); //Gets a 50 byte binary block void initFromBinBlock(void *pBinBlock); + void fillRenderArray(G3D::Array<TriangleBox> &pArray, const TreeNode* pTreeNode); + void countNodesAndTriangles(G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles); + void fillContainer(const G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi); + inline const ShortBox& getReletiveBounds() const { return(iBox); } + inline void setReletiveBounds(const ShortVector& lo, const ShortVector& hi) { iBox.setLo(lo); iBox.setHi(hi); } + inline const G3D::AABox getAABoxBounds() const { return(G3D::AABox(iBox.getLo().getVector3() + getBasePosition(), iBox.getHi().getVector3()+ getBasePosition())); } + // get start pos bases on the global array inline TriangleBox const* getTriangles() const { return &BaseModel::getTriangle(iTrianglesPos); } inline TriangleBox * getTriangles() { return &BaseModel::getTriangle(iTrianglesPos); } + // get start pos bases on the global array inline TreeNode const* getTreeNodes() const { return &BaseModel::getTreeNode(iNodesPos); } inline TreeNode * getTreeNodes() { return &BaseModel::getTreeNode(iNodesPos); } + // internal method usign internal offset inline const TreeNode& getTreeNode(int pPos) const { return(SubModel::getTreeNodes()[pPos]); } + // internal method usign internal offset inline const TriangleBox& getTriangle(int pPos) const { return(SubModel::getTriangles()[pPos]); } + inline unsigned int getNodesPos() const { return(iNodesPos); } inline unsigned int getTrianglesPos() const { return(iTrianglesPos); } + //unsigned int hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); } + void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; bool intersect(const G3D::Ray& pRay, float& pMaxDist) const; template<typename RayCallback> @@ -77,6 +95,7 @@ namespace VMAP bool operator==(const SubModel& pSm2) const; unsigned int hashCode() const { return BaseModel::getNTriangles(); } }; + unsigned int hashCode(const SubModel& pSm); void getBounds(const SubModel& pSm, G3D::AABox& pAABox); void getBounds(const SubModel* pSm, G3D::AABox& pAABox); diff --git a/src/shared/vmap/TileAssembler.cpp b/src/shared/vmap/TileAssembler.cpp index 509696f39a2..75997a847a2 100644 --- a/src/shared/vmap/TileAssembler.cpp +++ b/src/shared/vmap/TileAssembler.cpp @@ -17,20 +17,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <G3D/Vector3.h> #include <G3D/Triangle.h> + #include "TileAssembler.h" #include "CoordModelMapping.h" #include "ModelContainer.h" + #include <limits.h> #include <string.h> + #ifdef _ASSEMBLER_DEBUG FILE *g_df = NULL; #endif + using namespace G3D; + namespace VMAP { //================================================================= + Vector3 ModelPosition::transform(const Vector3& pIn) const { //return(pIn); @@ -39,8 +46,10 @@ namespace VMAP out = ixMatrix * out; out = iyMatrix * out; return(out); + } //================================================================= + TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName) { iCurrentUniqueNameId = 0; @@ -50,12 +59,16 @@ namespace VMAP //mkdir(iDestDir); init(); } + //================================================================= + TileAssembler::~TileAssembler() { delete iCoordModelMapping; } + //================================================================= + void TileAssembler::init() { iCoordModelMapping = new CoordModelMapping(); @@ -65,16 +78,20 @@ namespace VMAP addWorldAreaMapId(571); //Expansion02 } //================================================================= + std::string getModNameFromModPosName(const std::string& pModPosName) { size_t spos = pModPosName.find_first_of('#'); std::string modelFileName = pModPosName.substr(0,spos); return(modelFileName); } + //================================================================= + unsigned int TileAssembler::getUniqueNameId(const std::string pName) { unsigned int result; + if(!iUniqueNameIds.containsKey(pName)) { ++iCurrentUniqueNameId; @@ -83,11 +100,14 @@ namespace VMAP result = iUniqueNameIds.get(pName); return result; } + //================================================================= + std::string TileAssembler::getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName) { size_t spos; char buffer[20]; + std::string modelFileName = getModNameFromModPosName(pModPosName); //std::string fext = pModPosName.substr(modelFileName.length(),pModPosName.length()); unsigned int fextId = getUniqueNameId(pModPosName); @@ -105,7 +125,9 @@ namespace VMAP dirEntry.append(".vmap"); return(dirEntry); } + //================================================================= + void emptyArray(Array<ModelContainer*>& mc) { int no=mc.size(); @@ -116,6 +138,7 @@ namespace VMAP mc.remove(no); } } + //================================================================= bool TileAssembler::convertWorld() { @@ -126,22 +149,27 @@ namespace VMAP ::g_df = fopen("../TileAssembler_release.txt", "wb"); # endif #endif + std::string fname = iSrcDir; fname.append("/"); fname.append("dir"); iCoordModelMapping->setModelNameFilterMethod(iFilterMethod); + printf("Read coordinate mapping...\n"); if(!iCoordModelMapping->readCoordinateMapping(fname)) return false; + Array<unsigned int> mapIds = iCoordModelMapping->getMaps(); if(mapIds.size() == 0) { printf("Fatal error: empty map list!\n"); return false; } + for(int i=0; i<mapIds.size(); ++i) { unsigned int mapId = mapIds[i]; + #ifdef _ASSEMBLER_DEBUG if(mapId == 0) // "Azeroth" just for debug { @@ -172,12 +200,15 @@ namespace VMAP { sprintf(buffer, "%03u",mapId); dirname = std::string(buffer); + // prevent spam for small maps if(x==0 && y==0) printf("%s...\n",dirname.c_str()); } + bool result = fillModelContainerArray(dirname, mapId, x, y, mc); emptyArray(mc); + if(!result) return false; } @@ -187,15 +218,20 @@ namespace VMAP #ifdef _ASSEMBLER_DEBUG if(::g_df) fclose(::g_df); #endif + return true; } + //================================================================= + bool TileAssembler::fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, Array<ModelContainer*>& pMC) { ModelContainer* modelContainer; + NameCollection nameCollection = iCoordModelMapping->getFilenamesForCoordinate(pMapId, pXPos, pYPos); if(nameCollection.size() == 0) return true; // no data... + char dirfilename[500]; sprintf(dirfilename,"%s/%s.vmdir",iDestDir.c_str(),pDirFileName.c_str()); FILE *dirfile = fopen(dirfilename, "ab"); @@ -204,8 +240,10 @@ namespace VMAP printf("ERROR: Can't create file %s",dirfilename); return false; } + char destnamebuffer[500]; char fullnamedestnamebuffer[500]; + if(nameCollection.iMainFiles.size() >0) { sprintf(destnamebuffer,"%03u_%i_%i.vmap",pMapId, pYPos, pXPos); // flip it here too @@ -241,8 +279,10 @@ namespace VMAP iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr); fprintf(dirfile, "%s\n",dirEntryName.c_str()); destFileName.append(dirEntryName); + Array<std::string> positionarray; positionarray.append(nameCollection.iSingeFiles[pos]); + if(!iCoordModelMapping->isAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos])) { modelContainer = processNames(positionarray, destFileName.c_str()); @@ -255,10 +295,13 @@ namespace VMAP } ++pos; } + fclose(dirfile); return true; } + //================================================================= + void removeEntriesFromTree(AABSPTree<SubModel *>* pTree) { Array<SubModel *> submodelArray; @@ -270,18 +313,24 @@ namespace VMAP delete submodelArray[no]; } } + //================================================================= + ModelContainer* TileAssembler::processNames(const Array<std::string>& pPositions, const char* pDestFileName) { ModelContainer *modelContainer = 0; + Vector3 basepos = Vector3(0,0,0); AABSPTree<SubModel *>* mainTree = new AABSPTree<SubModel *>(); + int pos = 0; + bool result = true; while(result && (pos < pPositions.size())) { std::string modelPosString = pPositions[pos]; std::string modelFileName = getModNameFromModPosName(modelPosString); + if(!fillModelIntoTree(mainTree, basepos, modelPosString, modelFileName)) { result = false; @@ -296,9 +345,12 @@ namespace VMAP modelContainer->writeFile(pDestFileName); } removeEntriesFromTree(mainTree); + delete mainTree; + return(modelContainer); } + //================================================================= bool TileAssembler::readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, AABSPTree<SubModel *> *pMainTree) { @@ -317,14 +369,18 @@ namespace VMAP filename.append(baseModelFilename); rf = fopen(filename.c_str(), "rb"); } + if(!rf) { printf("ERROR: Can't open model file in form: %s",pModelFilename.c_str()); printf("... or form: %s",filename.c_str() ); return false; } + char ident[8]; + int trianglecount =0; + #ifdef _ASSEMBLER_DEBUG int startgroup = 0; //2; int endgroup = INT_MAX; //2; @@ -335,11 +391,13 @@ namespace VMAP int startgroup = 0; int endgroup = INT_MAX; #endif + // temporary use defines to simplify read/check code (close file and return at fail) #define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \ fclose(rf); return(false); } #define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \ fclose(rf); return(false); } + READ_OR_RETURN(&ident, 8); if(strcmp(ident, "VMAP001") == 0) { @@ -350,6 +408,7 @@ namespace VMAP // we have to read one int. This is needed during the export and we have to skip it here int tempNVectors; READ_OR_RETURN(&tempNVectors, sizeof(int)); + } else { @@ -361,13 +420,17 @@ namespace VMAP char blockId[5]; blockId[4] = 0; int blocksize; + READ_OR_RETURN(&groups, sizeof(G3D::uint32)); + for(int g=0;g<(int)groups;g++) { // group MUST NOT have more then 65536 indexes !! Array will have a problem with that !! (strange ...) Array<int> tempIndexArray; Array<Vector3> tempVertexArray; + AABSPTree<Triangle> *gtree = new AABSPTree<Triangle>(); + // add free gtree at fail #undef READ_OR_RETURN #undef CMP_OR_RETURN @@ -375,8 +438,10 @@ namespace VMAP fclose(rf); delete gtree; return(false); } #define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \ fclose(rf); delete gtree; return(false); } + G3D::uint32 flags; READ_OR_RETURN(&flags, sizeof(G3D::uint32)); + G3D::uint32 branches; READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "GRP "); @@ -388,6 +453,7 @@ namespace VMAP // indexes for each branch (not used jet) READ_OR_RETURN(&indexes, sizeof(G3D::uint32)); } + // ---- indexes READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "INDX"); @@ -405,13 +471,16 @@ namespace VMAP } delete[] indexarray; } + // ---- vectors READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "VERT"); READ_OR_RETURN(&blocksize, sizeof(int)); unsigned int nvectors; READ_OR_RETURN(&nvectors, sizeof(int)); + float *vectorarray = 0; + // add vectorarray free #undef READ_OR_RETURN #undef CMP_OR_RETURN @@ -419,6 +488,7 @@ namespace VMAP fclose(rf); delete gtree; delete[] vectorarray; return(false); } #define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \ fclose(rf); delete gtree; delete[] vectorarray; return(false); } + if(nvectors >0) { vectorarray = new float[nvectors*sizeof(float)*3]; @@ -434,22 +504,27 @@ namespace VMAP fseek(rf, blocksize, SEEK_CUR); } + for(unsigned int i=0, indexNo=0; indexNo<nvectors; indexNo++) { Vector3 v = Vector3(vectorarray[i+2], vectorarray[i+1], vectorarray[i+0]); i+=3; v = pModelPosition.transform(v); + float swapy = v.y; v.y = v.x; v.x = swapy; + tempVertexArray.append(v); } + // ---- calculate triangles int rest = nindexes%3; if(rest != 0) { nindexes -= rest; } + for(unsigned int i=0;i<(nindexes);) { Triangle t = Triangle(tempVertexArray[tempIndexArray[i+2]], tempVertexArray[tempIndexArray[i+1]], tempVertexArray[tempIndexArray[i+0]] ); @@ -460,13 +535,16 @@ namespace VMAP gtree->insert(t); } } + // drop of temporary use defines #undef READ_OR_RETURN #undef CMP_OR_RETURN + if(vectorarray != 0) { delete vectorarray; } + if(gtree->size() >0) { gtree->balance(); @@ -486,31 +564,40 @@ namespace VMAP fclose(rf); return true; } + //================================================================= + bool TileAssembler::fillModelIntoTree(AABSPTree<SubModel *> *pMainTree, const Vector3& pBasePos, std::string& pPos, std::string& pModelFilename) { ModelPosition modelPosition; getModelPosition(pPos, modelPosition); // all should be relative to object base position modelPosition.moveToBasePos(pBasePos); + modelPosition.init(); + return readRawFile(pModelFilename, modelPosition, pMainTree); } + //================================================================= void TileAssembler::getModelPosition(std::string& pPosString, ModelPosition& pModelPosition) { float vposarray[3]; float vdirarray[3]; float scale; + size_t spos = pPosString.find_first_of('#'); std::string stripedPosString = pPosString.substr(spos+1,pPosString.length()); + sscanf(stripedPosString.c_str(), "%f,%f,%f_%f,%f,%f_%f", &vposarray[0],&vposarray[1],&vposarray[2], &vdirarray[0],&vdirarray[1],&vdirarray[2], &scale); + pModelPosition.iPos = Vector3(vposarray[0], vposarray[1], vposarray[2]); pModelPosition.iDir = Vector3(vdirarray[0], vdirarray[1], vdirarray[2]); pModelPosition.iScale = scale; + } //========================================== } // VMAP diff --git a/src/shared/vmap/TileAssembler.h b/src/shared/vmap/TileAssembler.h index 27cddbc2c1c..d2183794a9e 100644 --- a/src/shared/vmap/TileAssembler.h +++ b/src/shared/vmap/TileAssembler.h @@ -17,14 +17,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _TILEASSEMBLER_H_ #define _TILEASSEMBLER_H_ + // load our modified version first !! #include "AABSPTree.h" + #include <G3D/Vector3.h> + #include "CoordModelMapping.h" #include "SubModel.h" #include "ModelContainer.h" + namespace VMAP { /** @@ -32,6 +37,7 @@ namespace VMAP To start the conversion call convertWorld(). */ //=============================================== + class ModelPosition { private: @@ -44,15 +50,18 @@ namespace VMAP float iScale; void init() { + // Swap x and y the raw data uses the axis differently ixMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitY(),-(G3D::pi()*iDir.x/180.0)); iyMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitX(),-(G3D::pi()*iDir.y/180.0)); izMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitZ(),-(G3D::pi()*iDir.z/180.0)); + } G3D::Vector3 transform(const G3D::Vector3& pIn) const; void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; } }; //=============================================== + class TileAssembler { private: @@ -62,13 +71,17 @@ namespace VMAP bool (*iFilterMethod)(char *pName); G3D::Table<std::string, unsigned int > iUniqueNameIds; unsigned int iCurrentUniqueNameId; + public: TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName); virtual ~TileAssembler(); + bool fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, G3D::Array<ModelContainer*>& pMC); ModelContainer* processNames(const G3D::Array<std::string>& pPosFileNames, const char* pDestFileName); + void init(); bool convertWorld(); + bool fillModelIntoTree(G3D::AABSPTree<SubModel *> *pMainTree, const G3D::Vector3& pBasePos, std::string& pPosFilename, std::string& pModelFilename); void getModelPosition(std::string& pPosString, ModelPosition& pModelPosition); bool readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, G3D::AABSPTree<SubModel *> *pMainTree); diff --git a/src/shared/vmap/TreeNode.cpp b/src/shared/vmap/TreeNode.cpp index d40f2acde25..c884f9b3b1d 100644 --- a/src/shared/vmap/TreeNode.cpp +++ b/src/shared/vmap/TreeNode.cpp @@ -17,10 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "TreeNode.h" + using namespace G3D; + namespace VMAP { + TreeNode const* TreeNode::getChild(TreeNode const* pValueArray,int pNo) const { if(iChilds[pNo] != -1) @@ -28,6 +32,7 @@ namespace VMAP else return(NULL); } + //================================================================= //================================================================= //================================================================= diff --git a/src/shared/vmap/TreeNode.h b/src/shared/vmap/TreeNode.h index 2b14de9ac33..1b9532001e5 100644 --- a/src/shared/vmap/TreeNode.h +++ b/src/shared/vmap/TreeNode.h @@ -17,14 +17,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _TREENODE_H #define _TREENODE_H + #include "ShortVector.h" #include "ShortBox.h" #include "NodeValueAccess.h" #include "VMapTools.h" + #include <G3D/Vector3.h> #include <G3D/AABox.h> + namespace VMAP { /** @@ -32,7 +36,9 @@ namespace VMAP It is the node within our static BSP-Trees. It does not use pointers but indexes to access the values and other nodes. */ + //===================================================== + class TreeNode { private: @@ -54,19 +60,30 @@ namespace VMAP iStartPosition = pStartPosition; iNumberOfValues = pNValues; } + bool hasChilds() const { return(iChilds[0] >= 0 || iChilds[1] >= 0); } + TreeNode const* getChild(TreeNode const* pValueArray, int pNo) const; // pChildNo = 0 or 1 inline void setChildPos(int pChildNo, int pChildPosInTreeNodeArray) { iChilds[pChildNo] = pChildPosInTreeNodeArray; } + inline G3D::Vector3::Axis getSplitAxis() const { return(iSplitAxis); } + inline void setSplitAxis(G3D::Vector3::Axis a) { iSplitAxis = a; } inline void setSplitLocation(float l) { iSplitLocation = l; } + inline void setBounds(const G3D::AABox& pBox) { iBounds = pBox; } + inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBounds.set(lo,hi); } + inline void getBounds(G3D::AABox& pBox) const { pBox.set(iBounds.low(),iBounds.high()); } + inline float getSplitLocation() const { return(iSplitLocation); } + inline unsigned short getNValues() const { return (iNumberOfValues); } + inline unsigned int getStartPosition() const { return(iStartPosition); } + inline bool operator==(const TreeNode& n) const { return ((iSplitLocation == n.iSplitLocation) && @@ -76,6 +93,7 @@ namespace VMAP (iBounds == n.iBounds) && (iNumberOfValues == n.iNumberOfValues)); } + inline bool operator!=(const TreeNode& n) const { return !((iSplitLocation == n.iSplitLocation) && @@ -85,6 +103,7 @@ namespace VMAP (iBounds == n.iBounds) && (iNumberOfValues == n.iNumberOfValues)); } + /** Returns true if the ray intersects this node */ bool intersects(const G3D::Ray& ray, float distance) const { // See if the ray will ever hit this node or its children @@ -93,10 +112,13 @@ namespace VMAP bool rayWillHitBounds = MyCollisionDetection::collisionLocationForMovingPointFixedAABox( ray.origin, ray.direction, iBounds, location, alreadyInsideBounds); + bool canHitThisNode = (alreadyInsideBounds || (rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance)))); + return canHitThisNode; } + template<typename RayCallback, typename TNode, typename TValue> void intersectRay( const G3D::Ray& ray, @@ -110,6 +132,7 @@ namespace VMAP // The ray doesn't hit this node, so it can't hit the children of the node. return; } + // Test for intersection against every object at this node. for (unsigned int v = iStartPosition; v < (iNumberOfValues+iStartPosition); ++v) { const TValue& nodeValue = pNodeValueAccess.getValue(v); @@ -122,9 +145,11 @@ namespace VMAP bool rayWillHitBounds = MyCollisionDetection::collisionLocationForMovingPointFixedAABox( ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + canHitThisObject = (alreadyInsideBounds || (rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance)))); } + if (canHitThisObject) { // It is possible that this ray hits this object. Look for the intersection using the // callback. @@ -133,24 +158,32 @@ namespace VMAP if(pStopAtFirstHit && distance < enterDistance) return; } + // There are three cases to consider next: // // 1. the ray can start on one side of the splitting plane and never enter the other, // 2. the ray can start on one side and enter the other, and // 3. the ray can travel exactly down the splitting plane + enum {NONE = -1}; int firstChild = NONE; int secondChild = NONE; + if (ray.origin[iSplitAxis] < iSplitLocation) { + // The ray starts on the small side firstChild = 0; + if (ray.direction[iSplitAxis] > 0) { // The ray will eventually reach the other side secondChild = 1; } + } else if (ray.origin[iSplitAxis] > iSplitLocation) { + // The ray starts on the large side firstChild = 1; + if (ray.direction[iSplitAxis] < 0) { secondChild = 0; } @@ -164,6 +197,7 @@ namespace VMAP firstChild = 1; } } + // Test on the side closer to the ray origin. if ((firstChild != NONE) && iChilds[firstChild]>0) { getChild(pNodeValueAccess.getNodePtr() , firstChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast); diff --git a/src/shared/vmap/VMapDefinitions.h b/src/shared/vmap/VMapDefinitions.h index 811546d44de..fd28a91d515 100644 --- a/src/shared/vmap/VMapDefinitions.h +++ b/src/shared/vmap/VMapDefinitions.h @@ -17,19 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _VMAPDEFINITIONS_H #define _VMAPDEFINITIONS_H #include <cstring> + namespace VMAP { //===================================== #define MAX_CAN_FALL_DISTANCE 10.0 const char VMAP_MAGIC[] = "VMAP_2.0"; + class VMapDefinitions { public: static const double getMaxCanFallDistance() { return(MAX_CAN_FALL_DISTANCE); } }; + //====================================== } #endif diff --git a/src/shared/vmap/VMapFactory.cpp b/src/shared/vmap/VMapFactory.cpp index c979a33ff82..5189f79daba 100644 --- a/src/shared/vmap/VMapFactory.cpp +++ b/src/shared/vmap/VMapFactory.cpp @@ -17,17 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <sys/types.h> #include "VMapFactory.h" #include "VMapManager.h" + using namespace G3D; + namespace VMAP { extern void chompAndTrim(std::string& str); + VMapManager *gVMapManager = 0; Table<unsigned int , bool>* iIgnoreSpellIds=0; + //=============================================== // result false, if no more id are found + bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) { bool result = false; @@ -49,10 +55,12 @@ namespace VMAP } return(result); } + //=============================================== /** parameter: String of map ids. Delimiter = "," */ + void VMapFactory::preventSpellsFromBeingTestedForLoS(const char* pSpellIdString) { if(!iIgnoreSpellIds) @@ -69,11 +77,14 @@ namespace VMAP } } } + //=============================================== + bool VMapFactory::checkSpellForLoS(unsigned int pSpellId) { return(!iIgnoreSpellIds->containsKey(pSpellId)); } + //=============================================== // just return the instance IVMapManager* VMapFactory::createOrGetVMapManager() @@ -82,6 +93,7 @@ namespace VMAP gVMapManager= new VMapManager(); // should be taken from config ... Please change if you like :-) return gVMapManager; } + //=============================================== // delete all internal data structures void VMapFactory::clear() diff --git a/src/shared/vmap/VMapFactory.h b/src/shared/vmap/VMapFactory.h index ca529bd2715..f3375d723c1 100644 --- a/src/shared/vmap/VMapFactory.h +++ b/src/shared/vmap/VMapFactory.h @@ -17,23 +17,30 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _VMAPFACTORY_H #define _VMAPFACTORY_H + #include "IVMapManager.h" + /** This is the access point to the VMapManager. */ + namespace VMAP { //=========================================================== + class VMapFactory { public: static IVMapManager* createOrGetVMapManager(); static void clear(); + static void preventSpellsFromBeingTestedForLoS(const char* pSpellIdString); static bool checkSpellForLoS(unsigned int pSpellId); }; + } #endif diff --git a/src/shared/vmap/VMapManager.cpp b/src/shared/vmap/VMapManager.cpp index 782c9d39c8a..342da0eb9e2 100644 --- a/src/shared/vmap/VMapManager.cpp +++ b/src/shared/vmap/VMapManager.cpp @@ -17,12 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "VMapManager.h" #include "VMapDefinitions.h" + using namespace G3D; + namespace VMAP { + //========================================================= + VMapManager::VMapManager() { #ifdef _VMAP_LOG_DEBUG @@ -30,7 +35,9 @@ namespace VMAP iCommandLogger.setResetFile(); #endif } + //========================================================= + VMapManager::~VMapManager(void) { Array<unsigned int > keyArray = iInstanceMapTrees.getKeys(); @@ -40,7 +47,9 @@ namespace VMAP iInstanceMapTrees.remove(keyArray[i]); } } + //========================================================= + Vector3 VMapManager::convertPositionToInternalRep(float x, float y, float z) const { float pos[3]; @@ -51,9 +60,12 @@ namespace VMAP double mid = full/2.0; pos[0] = full- (pos[0] + mid); pos[2] = full- (pos[2] + mid); + return(Vector3(pos)); } + //========================================================= + Vector3 VMapManager::convertPositionToTrinityRep(float x, float y, float z) const { float pos[3]; @@ -64,19 +76,24 @@ namespace VMAP double mid = full/2.0; pos[0] = -((mid+pos[0])-full); pos[1] = -((mid+pos[1])-full); + return(Vector3(pos)); } //========================================================= + std::string VMapManager::getDirFileName(unsigned int pMapId, int x, int y) const { char name[FILENAMEBUFFER_SIZE]; + sprintf(name, "%03u_%d_%d%s",pMapId, x, y, DIR_FILENAME_EXTENSION); return(std::string(name)); } + //========================================================= std::string VMapManager::getDirFileName(unsigned int pMapId) const { char name[FILENAMEBUFFER_SIZE]; + sprintf(name, "%03d%s",pMapId, DIR_FILENAME_EXTENSION); return(std::string(name)); } @@ -98,6 +115,7 @@ namespace VMAP } } //========================================================= + void chompAndTrim(std::string& str) { while(str.length() >0) @@ -125,8 +143,10 @@ namespace VMAP } } } + //========================================================= // result false, if no more id are found + bool getNextMapId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) { bool result = false; @@ -148,12 +168,14 @@ namespace VMAP } return(result); } + //========================================================= /** Block maps from being used. parameter: String of map ids. Delimiter = "," e.g.: "0,1,590" */ + void VMapManager::preventMapsFromBeingUsed(const char* pMapIdString) { if(pMapIdString != NULL) @@ -168,7 +190,9 @@ namespace VMAP } } } + //========================================================= + int VMapManager::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) { int result = VMAP_LOAD_RESULT_IGNORED; @@ -201,8 +225,10 @@ namespace VMAP } return result; } + //========================================================= // load one tile (internal use only) + bool VMapManager::_loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad) { bool result = false; @@ -223,6 +249,7 @@ namespace VMAP } else instanceTree = iInstanceMapTrees.get(pMapId); + unsigned int mapTileIdent = MAP_TILE_IDENT(x,y); result = instanceTree->loadMap(dirFileName, mapTileIdent); if(!result) // remove on fail @@ -235,7 +262,9 @@ namespace VMAP } return(result); } + //========================================================= + bool VMapManager::_existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad) { bool result = false; @@ -275,7 +304,9 @@ namespace VMAP } return result; } + //========================================================= + bool VMapManager::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) { std::string basePath = std::string(pBasePath); @@ -295,17 +326,22 @@ namespace VMAP } return found; } + //========================================================= + void VMapManager::unloadMap(unsigned int pMapId, int x, int y) { _unloadMap(pMapId, x, y); + #ifdef _VMAP_LOG_DEBUG Command c = Command(); c.fillUnloadTileCmd(pMapId, x,y); iCommandLogger.appendCmd(c); #endif } + //========================================================= + void VMapManager::_unloadMap(unsigned int pMapId, int x, int y) { if(iInstanceMapTrees.containsKey(pMapId)) @@ -329,7 +365,9 @@ namespace VMAP } } } + //========================================================= + void VMapManager::unloadMap(unsigned int pMapId) { if(iInstanceMapTrees.containsKey(pMapId)) @@ -350,6 +388,7 @@ namespace VMAP } } //========================================================== + bool VMapManager::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) { bool result = true; @@ -404,10 +443,12 @@ namespace VMAP } return result; } + //========================================================= /** get height or INVALID_HEIGHT if to height was calculated */ + //int gGetHeightCounter = 0; float VMapManager::getHeight(unsigned int pMapId, float x, float y, float z) { @@ -429,6 +470,7 @@ namespace VMAP } return(height); } + //========================================================= /** used for debugging @@ -440,6 +482,7 @@ namespace VMAP if(cmd == "startlog") { #ifdef _VMAP_LOG_DEBUG + iCommandLogger.enableWriting(true); #endif result = true; @@ -466,9 +509,11 @@ namespace VMAP } return result; } + //========================================================= //========================================================= //========================================================= + MapTree::MapTree(const char* pBaseDir) { iBasePath = std::string(pBaseDir); @@ -478,6 +523,7 @@ namespace VMAP } iTree = new AABSPTree<ModelContainer *>(); } + //========================================================= MapTree::~MapTree() { @@ -492,6 +538,7 @@ namespace VMAP delete iTree; } //========================================================= + // just for visual debugging with an external debug class #ifdef _DEBUG_VMAPS #ifndef gBoxArray @@ -501,10 +548,12 @@ namespace VMAP extern bool myfound; #endif #endif + //========================================================= /** return dist to hit or inf() if no hit */ + float MapTree::getIntersectionTime(const Ray& pRay, float pMaxDist, bool pStopAtFirstHit) { float firstDistance = inf(); @@ -527,6 +576,7 @@ namespace VMAP return firstDistance; } //========================================================= + bool MapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) { bool result = true; @@ -545,6 +595,7 @@ namespace VMAP When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one Return the hit pos or the original dest pos */ + bool MapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) { bool result; @@ -579,7 +630,9 @@ namespace VMAP } return result; } + //========================================================= + float MapTree::getHeight(const Vector3& pPos) { float height = inf(); @@ -593,12 +646,15 @@ namespace VMAP } return(height); } + //========================================================= + bool MapTree::PrepareTree() { iTree->balance(); return true; } + bool MapTree::loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent) { bool result = true; @@ -669,7 +725,9 @@ namespace VMAP } return (result); } + //========================================================= + void MapTree::unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce) { if(hasDirFile(dirFileName) && (pForce || containsLoadedMapTile(pMapTileIdent))) @@ -703,8 +761,10 @@ namespace VMAP } } } + //========================================================= //========================================================= + void MapTree::addModelContainer(const std::string& pName, ManagedModelContainer *pMc) { iLoadedModelContainer.set(pName, pMc); diff --git a/src/shared/vmap/VMapManager.h b/src/shared/vmap/VMapManager.h index 4e0e6bdfb18..bfeba3cfe67 100644 --- a/src/shared/vmap/VMapManager.h +++ b/src/shared/vmap/VMapManager.h @@ -17,8 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _VMAPMANAGER_H #define _VMAPMANAGER_H + // load our modified version first !! #include "AABSPTree.h" #include "ManagedModelContainer.h" @@ -27,9 +29,13 @@ #include "DebugCmdLogger.h" #endif #include <G3D/Table.h> + //=========================================================== + #define DIR_FILENAME_EXTENSION ".vmdir" + #define FILENAMEBUFFER_SIZE 500 + /** This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on. For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile. @@ -37,18 +43,22 @@ Each global map or instance has its own dynamic BSP-Tree. The loaded ModelContainers are included in one of these BSP-Trees. Additionally a table to match map ids and map names is used. */ + // Create a value describing the map tile #define MAP_TILE_IDENT(x,y) ((x<<8) + y) //=========================================================== + namespace VMAP { //=========================================================== + class FilesInDir { private: int iRefCount; G3D::Array<std::string> iFiles; public: + FilesInDir() { iRefCount = 0; } void append(const std::string& pName) { iFiles.append(pName); } void incRefCount() { ++iRefCount; } @@ -56,22 +66,28 @@ namespace VMAP int getRefCount() { return iRefCount; } const G3D::Array<std::string>& getFiles() const { return iFiles; } }; + //=========================================================== //=========================================================== //=========================================================== //=========================================================== + class MapTree { private: G3D::AABSPTree<ModelContainer *> *iTree; + // Key: filename, value ModelContainer G3D::Table<std::string, ManagedModelContainer *> iLoadedModelContainer; + // Key: dir file name, value FilesInDir G3D::Table<std::string, FilesInDir> iLoadedDirFiles; + // Store all the map tile idents that are loaded for that map // some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed G3D::Table<unsigned int, bool> iLoadedMapTiles; std::string iBasePath; + private: float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit); bool isAlreadyLoaded(const std::string& pName) { return(iLoadedModelContainer.containsKey(pName)); } @@ -86,17 +102,21 @@ namespace VMAP public: MapTree(const char *pBasePath); ~MapTree(); + bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2); bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist); float getHeight(const G3D::Vector3& pPos); + bool PrepareTree(); bool loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent); void addModelContainer(const std::string& pName, ManagedModelContainer *pMc); void unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce=false); + void getModelContainer(G3D::Array<ModelContainer *>& pArray ) { iTree->getMembers(pArray); } const void addDirFile(const std::string& pDirName, const FilesInDir& pFilesInDir) { iLoadedDirFiles.set(pDirName, pFilesInDir); } size_t size() { return(iTree->size()); } }; + //=========================================================== class MapIdNames { @@ -104,6 +124,7 @@ namespace VMAP std::string iDirName; std::string iMapGroupName; }; + //=========================================================== class VMapManager : public IVMapManager { @@ -112,6 +133,7 @@ namespace VMAP G3D::Table<unsigned int , MapTree *> iInstanceMapTrees; G3D::Table<unsigned int , bool> iMapsSplitIntoTiles; G3D::Table<unsigned int , bool> iIgnoreMapIds; + #ifdef _VMAP_LOG_DEBUG CommandFileRW iCommandLogger; #endif @@ -119,6 +141,7 @@ namespace VMAP bool _loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad=false); void _unloadMap(unsigned int pMapId, int x, int y); bool _existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad); + public: // public for debug G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const; @@ -129,17 +152,23 @@ namespace VMAP public: VMapManager(); ~VMapManager(void); + int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y); + bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y); + void unloadMap(unsigned int pMapId, int x, int y); void unloadMap(unsigned int pMapId); + bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ; /** fill the hit pos and return true, if an object was hit */ bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist); float getHeight(unsigned int pMapId, float x, float y, float z); + bool processCommand(char *pCommand); // for debug and extensions + void preventMapsFromBeingUsed(const char* pMapIdString); }; } diff --git a/src/shared/vmap/VMapTools.h b/src/shared/vmap/VMapTools.h index 2460bf9dcc4..3af3a29310d 100644 --- a/src/shared/vmap/VMapTools.h +++ b/src/shared/vmap/VMapTools.h @@ -17,16 +17,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef _VMAPTOOLS_H #define _VMAPTOOLS_H + #include <G3D/CollisionDetection.h> #include <G3D/AABox.h> + #include "NodeValueAccess.h" + /** The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure. This is an iterator that helps us analysing the BSP-Trees. The collision detection is modified to return true, if we are inside an object. */ + namespace VMAP { template<class TValue> @@ -35,17 +40,21 @@ namespace VMAP TValue* closestEntity; G3D::Vector3 hitLocation; G3D::Vector3 hitNormal; + void operator()(const G3D::Ray& ray, const TValue* entity, bool pStopAtFirstHit, float& distance) { entity->intersect(ray, distance, pStopAtFirstHit, hitLocation, hitNormal); } }; + //============================================================== //============================================================== //============================================================== + class MyCollisionDetection { private: public: + static bool collisionLocationForMovingPointFixedAABox( const G3D::Vector3& origin, const G3D::Vector3& dir, @@ -53,12 +62,15 @@ namespace VMAP G3D::Vector3& location, bool& Inside) { + // Integer representation of a floating-point value. #define IR(x) ((G3D::uint32&)x) + Inside = true; const G3D::Vector3& MinB = box.low(); const G3D::Vector3& MaxB = box.high(); G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f); + // Find candidate planes. for (int i = 0; i < 3; ++i) { @@ -66,6 +78,7 @@ namespace VMAP { location[i] = MinB[i]; Inside = false; + // Calculate T distances to candidate planes if (IR(dir[i])) { @@ -76,6 +89,7 @@ namespace VMAP { location[i] = MaxB[i]; Inside = false; + // Calculate T distances to candidate planes if (IR(dir[i])) { @@ -83,28 +97,33 @@ namespace VMAP } } } + if (Inside) { // definite hit location = origin; return true; } + // Get largest of the maxT's for final choice of intersection int WhichPlane = 0; if (MaxT[1] > MaxT[WhichPlane]) { WhichPlane = 1; } + if (MaxT[2] > MaxT[WhichPlane]) { WhichPlane = 2; } + // Check final candidate actually inside box if (IR(MaxT[WhichPlane]) & 0x80000000) { // Miss the box return false; } + for (int i = 0; i < 3; ++i) { if (i != WhichPlane) @@ -125,6 +144,7 @@ namespace VMAP normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0; */ return true; + #undef IR } }; |