/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#ifndef STORAGE_H_
#define STORAGE_H_
#ifdef WIN32
#pragma warning(disable:4312)
#endif
/** Base iterator class, returned by MakeIterator() functions.
*/
template
class SERVER_DECL StorageContainerIterator
{
protected:
/** Currently referenced object
*/
T * Pointer;
public:
virtual ~StorageContainerIterator() {}
/** Returns the currently stored object
*/
inline T * Get() { return Pointer; }
/** Sets the current object to P
*/
inline void Set(T * P) { Pointer = P; }
/** Are we at the end of the storage container?
*/
inline bool AtEnd() { return (Pointer == 0); }
/** Virtual function to increment to the next element
*/
virtual bool Inc() = 0;
/** Virtual function to destroy the iterator
*/
virtual void Destruct() = 0;
};
template
class SERVER_DECL ArrayStorageContainer
{
public:
/** This is where the magic happens :P
*/
T ** _array;
/** Maximum possible entry
*/
uint32 _max;
/** Returns an iterator currently referencing the start of the container
*/
StorageContainerIterator * MakeIterator();
/** Do we need to get the max?
*/
bool NeedsMax()
{
return true;
}
/** Creates the array with specified maximum
*/
void Setup(uint32 Max)
{
_array = new T*[Max];
_max = Max;
memset(_array, 0, sizeof(T*) * Max);
}
/** Sets up the array with a different maximum
*/
void Resetup(uint32 Max)
{
if(Max <= _max)
return; // no need to realloc
T ** a = new T*[Max];
memcpy(a, _array, sizeof(T*) * _max);
delete [] _array;
_array = a;
_max = Max;
}
/** Frees the container array and all elements inside it
*/
~ArrayStorageContainer()
{
for(uint32 i = 0; i < _max; ++i)
if(_array[i] != 0)
delete _array[i];
delete [] _array;
}
/** Allocates entry Entry in the array and sets the pointer, and returns
* the allocated memory.
*/
T * AllocateEntry(uint32 Entry)
{
if(Entry >= _max || _array[Entry] != 0)
return reinterpret_cast(0);
_array[Entry] = new T;
return _array[Entry];
}
/** Deallocates the entry Entry in the array and sets the pointer to null.
*/
bool DeallocateEntry(uint32 Entry)
{
if(Entry >= _max || _array[Entry] == 0)
return false;
delete _array[Entry];
_array[Entry] = 0;
return true;
}
/** Looks up entry Entry and returns the pointer if it is existant, otherwise null.
*/
T * LookupEntry(uint32 Entry)
{
if(Entry >= _max)
return reinterpret_cast(0);
else
return _array[Entry];
}
/** Sets the pointer to entry Entry to Pointer, and if it already exists frees the existing
* element.
*/
bool SetEntry(uint32 Entry, T * Pointer)
{
if(Entry > _max)
return false;
if(_array[Entry] != 0)
delete _array[Entry];
_array[Entry] = Pointer;
return true;
}
/** Returns the current pointer if it exists, otherwise allocates it.
*/
T * LookupEntryAllocate(uint32 Entry)
{
T * ret = LookupEntry(Entry);
if(!ret)
ret = AllocateEntry(Entry);
return ret;
}
/** Deletes all entries in the container.
*/
void Clear()
{
for(uint32 i = 0; i < _max; ++i)
{
if(_array[i] != 0)
{
delete _array[i];
}
_array[i] = 0;
}
}
};
template
class SERVER_DECL HashMapStorageContainer
{
public:
typename HM_NAMESPACE::hash_map _map;
/** Returns an iterator currently referencing the start of the container
*/
StorageContainerIterator * MakeIterator();
/** Frees the container array and all elements inside it
*/
~HashMapStorageContainer()
{
for(typename HM_NAMESPACE::hash_map::iterator itr = _map.begin(); itr != _map.end(); ++itr)
delete itr->second;
}
/** Do we need to get the max?
*/
bool NeedsMax()
{
return false;
}
/** Creates the array with specified maximum
*/
void Setup(uint32 Max)
{
}
void Resetup(uint32 Max)
{
}
/** Allocates entry Entry in the array and sets the pointer, and returns
* the allocated memory.
*/
T * AllocateEntry(uint32 Entry)
{
if(_map.find(Entry) != _map.end())
return reinterpret_cast(0);
T * n = new T;
_map.insert( make_pair( Entry, n ) );
return n;
}
/** Deallocates the entry Entry in the array and sets the pointer to null.
*/
bool DeallocateEntry(uint32 Entry)
{
typename HM_NAMESPACE::hash_map::iterator itr = _map.find(Entry);
if(itr == _map.end())
return false;
delete itr->second;
_map.erase(itr);
return true;
}
T * LookupEntry(uint32 Entry)
{
typename HM_NAMESPACE::hash_map::iterator itr = _map.find(Entry);
if(itr == _map.end())
return reinterpret_cast(0);
return itr->second;
}
/** Sets the pointer to entry Entry to Pointer, and if it already exists frees the existing
* element.
*/
bool SetEntry(uint32 Entry, T * Pointer)
{
typename HM_NAMESPACE::hash_map::iterator itr = _map.find(Entry);
if(itr == _map.end())
{
_map.insert( make_pair( Entry, Pointer ) );
return true;
}
delete itr->second;
itr->second = Pointer;
return true;
}
/** Returns the current pointer if it exists, otherwise allocates it.
*/
T * LookupEntryAllocate(uint32 Entry)
{
T * ret = LookupEntry(Entry);
if(!ret)
ret = AllocateEntry(Entry);
return ret;
}
/** Deletes all entries in the container.
*/
void Clear()
{
typename HM_NAMESPACE::hash_map::iterator itr = _map.begin();
for(; itr != _map.end(); ++itr)
delete itr->second;
_map.clear();
}
};
template
class SERVER_DECL ArrayStorageIterator : public StorageContainerIterator
{
ArrayStorageContainer * Source;
uint32 MyIndex;
public:
/** Increments the iterator
*/
bool Inc()
{
GetNextElement();
if(StorageContainerIterator::Pointer != 0)
return true;
else
return false;
}
/** Frees the memory occupied by this iterator
*/
void Destruct()
{
delete this;
}
/** Constructor
*/
ArrayStorageIterator(ArrayStorageContainer * S) : StorageContainerIterator(), Source(S), MyIndex(0)
{
GetNextElement();
}
/** Sets the next element pointer, or to 0 if we reached the end
*/
void GetNextElement()
{
while(MyIndex < Source->_max)
{
if(Source->_array[MyIndex] != 0)
{
StorageContainerIterator::Set(Source->_array[MyIndex]);
++MyIndex;
return;
}
++MyIndex;
}
// reached the end of the array
StorageContainerIterator::Set(reinterpret_cast(0));
}
};
template
class SERVER_DECL HashMapStorageIterator : public StorageContainerIterator
{
HashMapStorageContainer * Source;
typename HM_NAMESPACE::hash_map::iterator itr;
public:
/** Constructor
*/
HashMapStorageIterator(HashMapStorageContainer * S) : StorageContainerIterator(), Source(S)
{
itr = S->_map.begin();
if(itr == S->_map.end())
StorageContainerIterator::Set(0);
else
StorageContainerIterator::Set(itr->second);
}
/** Gets the next element, or if we reached the end sets it to 0
*/
void GetNextElement()
{
++itr;
if(itr == Source->_map.end())
StorageContainerIterator::Set(0);
else
StorageContainerIterator::Set(itr->second);
}
/** Returns true if we're not at the end, otherwise false.
*/
bool Inc()
{
GetNextElement();
if(StorageContainerIterator::Pointer != 0)
return true;
else
return false;
}
/** Frees the memory occupied by this iterator
*/
void Destruct()
{
delete this;
}
};
#ifndef SCRIPTLIB
template
StorageContainerIterator * ArrayStorageContainer::MakeIterator()
{
return new ArrayStorageIterator(this);
}
template
StorageContainerIterator * HashMapStorageContainer::MakeIterator()
{
return new HashMapStorageIterator(this);
}
#endif
template
class SERVER_DECL Storage
{
protected:
StorageType _storage;
char * _indexName;
char * _formatString;
public:
inline char * GetIndexName() { return _indexName; }
inline char * GetFormatString() { return _formatString; }
/** False constructor to fool compiler
*/
Storage() {}
virtual ~Storage() {}
/** Makes an iterator, w00t!
*/
StorageContainerIterator * MakeIterator()
{
return _storage.MakeIterator();
}
/** Calls the storage container lookup function.
*/
T * LookupEntry(uint32 Entry)
{
return _storage.LookupEntry(Entry);
}
/** Reloads the content in this container.
*/
virtual void Reload() = 0;
/** Loads the container using the specified name and format string
*/
virtual void Load(const char * IndexName, const char * FormatString)
{
_indexName = strdup(IndexName);
_formatString = strdup(FormatString);
}
/** Frees the duplicated strings and all entries inside the storage container
*/
virtual void Cleanup()
{
printf("Deleting database cache of `%s`...\n", _indexName);
StorageContainerIterator * itr = _storage.MakeIterator();
while(!itr->AtEnd())
{
FreeBlock(itr->Get());
if(!itr->Inc())
break;
}
itr->Destruct();
_storage.Clear();
free(_indexName);
free(_formatString);
}
/** Frees any string elements inside blocks.
*/
void FreeBlock(T * Allocated)
{
char * p = _formatString;
char * structpointer = (char*)Allocated;
for(; *p != 0; ++p)
{
switch(*p)
{
case 's': // string is the only one we have to actually do anything for here
free((void*)(*(uint32*)structpointer));
structpointer += sizeof(char*);
break;
case 'u':
case 'i':
case 'f':
structpointer += sizeof(uint32);
break;
case 'h':
structpointer += sizeof(uint16);
break;
case 'c':
structpointer += sizeof(uint8);
break;
}
}
}
};
template
class SERVER_DECL SQLStorage : public Storage
{
public:
SQLStorage() : Storage() {}
~SQLStorage() {}
/** Loads the block using the format string.
*/
inline void LoadBlock(Field * fields, T * Allocated)
{
char * p = Storage::_formatString;
char * structpointer = (char*)Allocated;
Field * f = fields;
for(; *p != 0; ++p, ++f)
{
switch(*p)
{
case 'u': // Unsigned integer
*(uint32*)structpointer = f->GetUInt32();
structpointer += sizeof(uint32);
break;
case 'i': // Signed integer
*(int32*)structpointer = f->GetInt32();
structpointer += sizeof(uint64);
break;
case 's': // Null-terminated string
*(uint32*)structpointer = (uint32)strdup(f->GetString());
structpointer += sizeof(char*);
break;
case 'x': // Skip
break;
case 'f': // Float
*(float*)structpointer = f->GetFloat();
structpointer += sizeof(float);
break;
case 'c': // Char
*(uint8*)structpointer = f->GetUInt8();
structpointer += sizeof(uint8);
break;
case 'h': // Short
*(uint16*)structpointer = f->GetUInt16();
structpointer += sizeof(uint16);
break;
default: // unknown
printf("Unknown field type in string: `%c`\n", *p);
break;
}
}
}
/** Loads from the table.
*/
void Load(const char * IndexName, const char * FormatString)
{
//printf("Loading database cache from `%s`...\n", IndexName);
Storage::Load(IndexName, FormatString);
QueryResult * result;
if(Storage::_storage.NeedsMax())
{
result = WorldDatabase.Query("SELECT MAX(entry) FROM %s", IndexName);
uint32 Max = 999999;
if(result)
{
Max = result->Fetch()[0].GetUInt32() + 1;
delete result;
}
Storage::_storage.Setup(Max);
}
size_t cols = strlen(FormatString);
result = WorldDatabase.Query("SELECT * FROM %s", IndexName);
if (!result)
return;
Field * fields = result->Fetch();
if(result->GetFieldCount() != cols)
{
if(result->GetFieldCount() > cols)
{
Log.Warning("Storage", "Invalid format in %s (%u/%u), loading anyway because we have enough data\n", IndexName, (unsigned int)cols, (unsigned int)result->GetFieldCount());
}
else
{
Log.Error("Storage", "Invalid format in %s (%u/%u), not enough data to proceed.\n", IndexName, (unsigned int)cols, (unsigned int)result->GetFieldCount());
delete result;
return;
}
}
uint32 Entry;
T * Allocated;
do
{
Entry = fields[0].GetUInt32();
Allocated = Storage::_storage.AllocateEntry(Entry);
if(!Allocated)
continue;
LoadBlock(fields, Allocated);
} while(result->NextRow());
Log.Notice("Storage", "%u entries loaded from table %s.", result->GetRowCount(), IndexName);
delete result;
//Log.Success("Storage", "Loaded database cache from `%s`.", IndexName);
}
/** Reloads the storage container
*/
void Reload()
{
Log.Notice("Storage", "Reloading database cache from `%s`...\n", Storage::_indexName);
QueryResult * result = WorldDatabase.Query("SELECT MAX(entry) FROM %s", Storage::_indexName);
if(result == 0)
return;
uint32 Max = result->Fetch()[0].GetUInt32();
delete result;
if(!Max)
return;
Storage::_storage.Resetup(Max);
size_t cols = strlen(Storage::_formatString);
result = WorldDatabase.Query("SELECT * FROM %s", Storage::_indexName);
if (!result)
return;
Field * fields = result->Fetch();
if(result->GetFieldCount() != cols)
{
Log.Error("Storage", "Invalid format in %s (%u/%u).", Storage::_indexName, (unsigned int)cols, (unsigned int)result->GetFieldCount());
delete result;
return;
}
uint32 Entry;
T * Allocated;
do
{
Entry = fields[0].GetUInt32();
Allocated = Storage::_storage.LookupEntryAllocate(Entry);
LoadBlock(fields, Allocated);
} while(result->NextRow());
delete result;
}
};
#endif