* rewrote the SCP data storage. note that all database files must now be placed in a directory added with "AddDBPath <path>" and must contain a #dbname tag. the db now converts SCP (text) to SCC (binary) files, which increases access speed a lot. also less RAM used in most cases.

* replaced "LoadSCP" func with "LoadDB <dbname>" (different syntax!); removed all other scp db related funcs except "getscpvalue"
* the GUI can now show texts stored in databases
* added displaying status to SceneLogin
* misc fixes/cleanups
This commit is contained in:
false_genesis 2008-04-19 23:45:37 +00:00
parent f484de89a9
commit 76ebbe5cf3
24 changed files with 1149 additions and 395 deletions

View File

@ -41,11 +41,5 @@ ELSE
ENDIF
UNSET errors
// Load some SCP files
LOADALLSCP
// do more stuff here in future...
LOG * StartUp complete!
LOG *** StartUp complete!

View File

@ -1,3 +1,5 @@
#dbname=class
[1]
name=Warrior

View File

@ -1,4 +1,4 @@
// define names of genders
#dbname=gender
[0]
name=male

View File

@ -0,0 +1,25 @@
#dbname=gui_login_text
// check src/Client/GUI/SceneData.h for the fields/numbers.
// this file covers the texts for SceneLogin.
// connection info
[1]
0=Not connected.
1=Connecting...
2=Could not connect.
3=Account does not exist!
4=Account is already connected!
5=Wrong game version. (Try to set a different version info in PseuWoW.conf)
6=Logging in...
7=Authenticating...
8=Authentication failed! (Wrong password?)
9=Requesting realmlist...
10=Unknown error!
11=Incoming file transfer.
// msgbox texts. 0: header 1:body
[2]
0=Oh noes!
1=You have to enter account name and password!

View File

@ -1,3 +1,5 @@
#dbname=language
[0]
name=Global

View File

@ -21,45 +21,6 @@ SET,${v} ${${v}}${@def}
// remove the name placeholder
UNSET v
//--------------------------------------------------------
#script=exloadscp
#permission=255
//--------------------------------------------------------
if ?{fileexists ${@def}}
loadscp,{${@0}} ${@def}
else
logdebug skipped loading of non-existent file '${@def}'
endif
//--------------------------------------------------------
#script=loadallscp
#permission=255
//--------------------------------------------------------
LOG * Loading SCP data storages...
// example:
// LOADSCP,test data/test.scp
// load default databases
LOADSCP,class data/scp/class.scp
LOADSCP,gender data/scp/gender.scp
LOADSCP,language data/scp/language.scp
LOADSCP,map data/scp/map.scp
LOADSCP,race data/scp/race.scp
// load extended databases if present.
EXLOADSCP,sound data/scp/sound.scp
EXLOADSCP,emote data/scp/emote.scp
EXLOADSCP,zone data/scp/zone.scp
EXLOADSCP,creaturedisplayinfo data/scp/creaturedisplayinfo.scp
EXLOADSCP,creaturemodeldata data/scp/creaturemodeldata.scp
EXLOADSCP,npcsound data/scp/npcsound.scp
LOG * SCP loaded.
//-------------------------------------------------
#script=string_is_command
//-------------------------------------------------

View File

@ -0,0 +1,45 @@
#script=register_db_loader
if ?{not ?{IsHooked _startup}}
HookStart _startup
HookAdd db_loader_load_all
HookEnd
// set up paths; ./data/scp and ./cache are set in the core already
// the SCP files placed in this directory are used to override some values
// in already present SCP files or to add custom fields or content
// note: it DOES matter in which order paths are added!!
AddDBPath ./data/scp-patches
endif
//---------------------------------------------------------
#script=db_loader_load_all
log ** Loading / dyncompiling databases...
// game databases
LoadDB race
LoadDB class
LoadDB gender
LoadDB language
LoadDB emote
LoadDB map
LoadDB zone
LoadDB creaturedisplayinfo
LoadDB creaturemodeldata
// LoadDB itemdisplayinfo // not yet used
// LoadDB charsections // not yet used
// LoadDB sound // not yet used
// LoadDB npcsound // not yet used
// GUI related databases
LoadDB gui_login_text
log ** Databases loaded.
//----------------------------------------------------------
#script=dummy
#onload
register_db_loader
#/onload

View File

@ -29,6 +29,7 @@ struct DefReturnResult
DefReturnResult(bool b) { ok=true; mustreturn=false; ret=b?"true":"false"; }
DefReturnResult(std::string s) { ok=true; mustreturn=false; ret=s; }
DefReturnResult(const char *s) { DefReturnResult(std::string(s)); }
DefReturnResult(char *s) { DefReturnResult(std::string(s)); }
bool ok; // true if the execution of the current statement was successful
bool mustreturn;
std::string ret; // return value used by ?{..}

View File

@ -32,10 +32,6 @@ void DefScriptPackage::_InitDefScriptInterface(void)
AddFunc("castspell",&DefScriptPackage::SCcastspell);
AddFunc("queryitem",&DefScriptPackage::SCqueryitem);
AddFunc("target",&DefScriptPackage::SCtarget);
AddFunc("loadscp",&DefScriptPackage::SCloadscp);
AddFunc("scpexists",&DefScriptPackage::SCScpExists);
AddFunc("scpsectionexists",&DefScriptPackage::SCScpSectionExists);
AddFunc("scpentryexists",&DefScriptPackage::SCScpEntryExists);
AddFunc("getscpvalue",&DefScriptPackage::SCGetScpValue);
AddFunc("getplayerguid",&DefScriptPackage::SCGetPlayerGuid);
AddFunc("getname",&DefScriptPackage::SCGetName);
@ -61,6 +57,8 @@ void DefScriptPackage::_InitDefScriptInterface(void)
AddFunc("switchopcodehandler",&DefScriptPackage::SCSwitchOpcodeHandler);
AddFunc("opcodedisabled",&DefScriptPackage::SCOpcodeDisabled);
AddFunc("spoofworldpacket",&DefScriptPackage::SCSpoofWorldPacket);
AddFunc("loaddb",&DefScriptPackage::SCLoadDB);
AddFunc("adddbpath",&DefScriptPackage::SCAddDBPath);
}
DefReturnResult DefScriptPackage::SCshdn(CmdSet& Set)
@ -81,15 +79,17 @@ DefReturnResult DefScriptPackage::SCSendChatMessage(CmdSet& Set){
DEF_RETURN_ERROR;
}
std::stringstream ss;
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
uint32 type=atoi(Set.arg[0].c_str());
uint32 lang=atoi(Set.arg[1].c_str());
ss << lang;
if(ss.str()!=Set.arg[1]) // given lang is NOT a number
{
uint32 dblang;
dblang = ((PseuInstance*)parentMethod)->dbmgr.GetDB("language").GetFieldByValue("name",Set.arg[1]);
SCPDatabase *langdb = dbmgr.GetDB("language");
uint32 dblang = langdb->GetFieldByStringValue("name",(char*)Set.arg[1].c_str());
logdev("looking up language id for lang '%s', found %i",Set.arg[1].c_str(),dblang);
// TODO: comment this out to enable using addon language??!
if(dblang != -1)
lang = dblang;
}
@ -135,12 +135,13 @@ DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){
// this supports calls like "emote 126" and "emote ready"
uint32 id = uint32(-1);
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
if(dbmgr.HasDB("emote"))
SCPDatabase *emotedb = dbmgr.GetDB("emote");
if(emotedb)
{
SCPDatabase& db = dbmgr.GetDB("emote");
id = db.GetFieldByValue("name",DefScriptTools::stringToUpper(Set.defaultarg)); // emote names are always uppercased
id = emotedb->GetFieldByStringValue("name",(char*)DefScriptTools::stringToUpper(Set.defaultarg).c_str()); // emote names are always uppercased
}
if(id == uint32(-1))
if(id == SCP_INVALID_INT)
{
id=atoi(Set.defaultarg.c_str());
if(!id)
@ -339,6 +340,7 @@ DefReturnResult DefScriptPackage::SCtarget(CmdSet& Set)
return "";
}
/*
DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
{
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
@ -366,38 +368,9 @@ DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
logerror("Failed to load SCP: \"%s\" [%s]",dbname.c_str(),Set.defaultarg.c_str());
}
}
return DefScriptTools::toString((uint64)sections);;
return DefScriptTools::toString((uint64)sections);
}
DefReturnResult DefScriptPackage::SCScpExists(CmdSet& Set)
{
return (!Set.defaultarg.empty()) && ((PseuInstance*)parentMethod)->dbmgr.HasDB(Set.defaultarg);
}
DefReturnResult DefScriptPackage::SCScpSectionExists(CmdSet& Set)
{
static std::string dbname;
if(!Set.arg[0].empty())
dbname=Set.arg[0];
return (!Set.defaultarg.empty()) && (!dbname.empty())
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField((uint32)DefScriptTools::toUint64(Set.defaultarg));
}
DefReturnResult DefScriptPackage::SCScpEntryExists(CmdSet& Set)
{
static std::string dbname;
static uint32 keyid;
if(!Set.arg[0].empty())
dbname=Set.arg[0];
if(!Set.arg[1].empty())
keyid=(uint32)DefScriptTools::toUint64(Set.arg[1]);
return (!Set.defaultarg.empty()) && (!dbname.empty())
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField(keyid)
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).HasEntry(Set.defaultarg);
}
*/
// GetScpValue,db,key entry
// db & key will be stored, that multiple calls like GetScpValue entryxyz are possible
@ -406,7 +379,7 @@ DefReturnResult DefScriptPackage::SCGetScpValue(CmdSet& Set)
static std::string dbname;
static uint32 keyid;
std::string entry;
DefReturnResult r;
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
if(!Set.arg[0].empty())
dbname=Set.arg[0];
@ -414,18 +387,36 @@ DefReturnResult DefScriptPackage::SCGetScpValue(CmdSet& Set)
keyid=(uint32)DefScriptTools::toUint64(Set.arg[1]);
if(!Set.defaultarg.empty())
entry=Set.defaultarg;
if( (!entry.empty()) && (!dbname.empty())
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField(keyid)
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).HasEntry(entry))
if( (!entry.empty()) && (!dbname.empty()) )
{
r.ret = ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).GetString(entry);
SCPDatabase *db = dbmgr.GetDB(dbname);
if(db)
{
uint32 ftype = db->GetFieldType((char*)entry.c_str());
switch(ftype)
{
case SCP_TYPE_INT:
{
return DefScriptTools::toString(db->GetInt(keyid,(char*)entry.c_str()));
}
case SCP_TYPE_FLOAT:
{
return DefScriptTools::toString(db->GetFloat(keyid,(char*)entry.c_str()));
}
case SCP_TYPE_STRING:
{
return std::string(db->GetString(keyid,(char*)entry.c_str()));
}
default: logerror("GetSCPValue: field '%s' does not exist in DB '%s'!",entry.c_str(),dbname.c_str());
}
}
else
{
logerror("GetSCPValue: No such DB: '%s'",dbname.c_str());
}
}
else
{
r.ret = "";
}
return r;
return "";
}
DefReturnResult DefScriptPackage::SCGetPlayerGuid(CmdSet& Set)
@ -825,15 +816,16 @@ DefReturnResult DefScriptPackage::SCGetFileList(CmdSet& Set)
DefReturnResult DefScriptPackage::SCPrintScript(CmdSet &Set)
{
DefScript *sc = GetScript(DefScriptTools::stringToLower(Set.defaultarg));
if(sc)
if(!sc)
return false;
logcustom(0,GREEN,"== DefScript \"%s\", %u lines: ==",sc->GetName().c_str(),sc->GetLines());
for(uint32 i = 0; i < sc->GetLines(); i++)
{
logcustom(0,GREEN,"== DefScript \"%s\", %u lines: ==",sc->GetName().c_str(),sc->GetLines());
for(uint32 i = 0; i < sc->GetLines(); i++)
{
logcustom(0,GREEN,sc->GetLine(i).c_str());
}
logcustom(0,GREEN,sc->GetLine(i).c_str());
}
return "";
return true;
}
DefReturnResult DefScriptPackage::SCGetObjectValue(CmdSet &Set)
@ -1100,7 +1092,7 @@ DefReturnResult DefScriptPackage::SCSwitchOpcodeHandler(CmdSet &Set)
WorldSession *ws = ((PseuInstance*)parentMethod)->GetWSession();
if(!ws)
{
logerror("Invalid Script call: SCRemoveOpcodeHandler: WorldSession not valid");
logerror("Invalid Script call: SCSwitchOpcodeHandler: WorldSession not valid");
DEF_RETURN_ERROR;
}
uint16 opc = (uint16)DefScriptTools::toUint64(Set.arg[0]);
@ -1173,6 +1165,23 @@ DefReturnResult DefScriptPackage::SCSpoofWorldPacket(CmdSet &Set)
return false;
}
DefReturnResult DefScriptPackage::SCLoadDB(CmdSet &Set)
{
PseuInstance *ins = (PseuInstance*)parentMethod;
if(ins->dbmgr.GetDB(Set.defaultarg.c_str()))
return "exists";
logdetail("Loading database '%s'",Set.defaultarg.c_str());
uint32 result = ins->dbmgr.SearchAndLoad((char*)Set.defaultarg.c_str(), false);
return toString(result);
}
DefReturnResult DefScriptPackage::SCAddDBPath(CmdSet &Set)
{
PseuInstance *ins = (PseuInstance*)parentMethod;
ins->dbmgr.AddSearchPath((char*)Set.defaultarg.c_str());
return true;
}
void DefScriptPackage::My_LoadUserPermissions(VarSet &vs)
{
static char *prefix = "USERS::";

View File

@ -23,10 +23,6 @@ DefReturnResult SClogerror(CmdSet&);
DefReturnResult SCcastspell(CmdSet&);
DefReturnResult SCqueryitem(CmdSet&);
DefReturnResult SCtarget(CmdSet&);
DefReturnResult SCloadscp(CmdSet&);
DefReturnResult SCScpExists(CmdSet&);
DefReturnResult SCScpSectionExists(CmdSet&);
DefReturnResult SCScpEntryExists(CmdSet&);
DefReturnResult SCGetScpValue(CmdSet&);
DefReturnResult SCGetName(CmdSet&);
DefReturnResult SCGetPlayerGuid(CmdSet&);
@ -52,6 +48,8 @@ DefReturnResult SCGetObjectDistance(CmdSet&);
DefReturnResult SCSwitchOpcodeHandler(CmdSet&);
DefReturnResult SCOpcodeDisabled(CmdSet&);
DefReturnResult SCSpoofWorldPacket(CmdSet&);
DefReturnResult SCLoadDB(CmdSet &Set);
DefReturnResult SCAddDBPath(CmdSet &Set);
void my_print(const char *fmt, ...);

View File

@ -47,11 +47,11 @@ void DrawObject::_Init(void)
if(!cube && _obj->IsWorldObject()) // only world objects have coords and can be drawn
{
uint32 displayid = _obj->IsUnit() ? _obj->GetUInt32Value(UNIT_FIELD_DISPLAYID) : 0; // TODO: in case its GO get it from proto data
SCPDatabase& cdi = _instance->dbmgr.GetDB("creaturedisplayinfo");
SCPField& crdata = cdi.GetField(displayid);
uint32 modelid = crdata.GetInteger("model");
std::string modelfile = std::string("data/model/") + _instance->dbmgr.GetDB("creaturemodeldata").GetField(modelid).GetString("file");
uint32 opacity = crdata.GetInteger("opacity");
SCPDatabase *cdi = _instance->dbmgr.GetDB("creaturedisplayinfo");
SCPDatabase *cmd = _instance->dbmgr.GetDB("creaturemodeldata");
uint32 modelid = cdi && displayid ? cdi->GetUint32(displayid,"model") : 0;
std::string modelfile = std::string("data/model/") + cmd->GetString(modelid,"file");
uint32 opacity = cdi && displayid ? cdi->GetUint32(displayid,"opacity") : 255;
scene::IAnimatedMesh *mesh = _smgr->getMesh(modelfile.c_str());
if(mesh)
{

View File

@ -2,6 +2,7 @@
#define PSEUGUI_H
#include "irrlicht/irrlicht.h"
#include "SceneData.h"
#include "DrawObjMgr.h"
#include "World.h"

View File

@ -7,6 +7,7 @@
Scene::Scene(PseuGUI *g)
{
memset(scenedata, 0, sizeof(uint32) * SCENEDATA_SIZE);
textdb = NULL;
gui = g;
instance = gui->GetInstance();
device = gui->_device;
@ -45,3 +46,22 @@ Scene::~Scene()
{
DEBUG(logdebug("Scene::~Scene()"));
}
core::stringw Scene::GetStringFromDB(u32 index, u32 entry)
{
core::stringw r = "";
if(!textdb)
{
r += L"<string ";
r += index;
r += L"/";
r += entry;
r += L" not found>";
return r;
}
char buf[20];
sprintf(buf,"%u",entry);
r += textdb->GetString(index, buf);
textdb->DumpStructureToFile("DB_textdb.txt");
return r;
}

View File

@ -22,6 +22,7 @@ class Scene
public:
Scene(PseuGUI *g);
~Scene();
core::stringw GetStringFromDB(u32 index, u32 entry);
inline void SetState(SceneState sc) { _scenestate = sc; }
inline SceneState GetState(void) { return _scenestate; }
virtual void OnUpdate(s32);
@ -40,6 +41,7 @@ protected:
CCursorController *cursor;
SceneState _scenestate;
uint32 scenedata[SCENEDATA_SIZE]; // generic storage for anything the PseuInstance thread wants to tell us
SCPDatabase *textdb;
};
class SceneGuiStart : public Scene
@ -66,6 +68,8 @@ private:
IGUIImage *irrlogo, *background;
GUIEventReceiver *eventrecv;
PseuGUI* _gui;
gui::IGUIElement *msgbox;
uint32 msgbox_textid;
};
class ShTlTerrainSceneNode;

View File

@ -3,24 +3,31 @@
#define SCENEDATA_SIZE 255
// I: index
// I: index, enums should start with 1
// D: data value
// dummy indexes are used to keep space for misc text data in an SCP file
enum SceneLoginDataIndexes
{
ISCENE_LOGIN_CONN_STATUS,
ISCENE_LOGIN_END
ISCENE_LOGIN_CONN_STATUS = 1,
ISCENE_LOGIN_MSGBOX_DUMMY = 2,
ISCENE_LOGIN_END = 3
};
enum SceneLoginConnStatus
{
DSCENE_LOGIN_NOT_CONNECTED,
DSCENE_LOGIN_CONN_ATTEMPT,
DSCENE_LOGIN_CONN_FAILED,
DSCENE_LOGIN_LOGGING_IN,
DSCENE_LOGIN_AUTHENTICATING,
DSCENE_LOGIN_AUTH_FAILED,
DSCENE_LOGIN_AUTH_OK
DSCENE_LOGIN_NOT_CONNECTED = 0,
DSCENE_LOGIN_CONN_ATTEMPT = 1,
DSCENE_LOGIN_CONN_FAILED = 2,
DSCENE_LOGIN_ACC_NOT_FOUND = 3,
DSCENE_LOGIN_ALREADY_CONNECTED = 4,
DSCENE_LOGIN_WRONG_VERSION = 5,
DSCENE_LOGIN_LOGGING_IN = 6,
DSCENE_LOGIN_AUTHENTICATING = 7,
DSCENE_LOGIN_AUTH_FAILED = 8,
DSCENE_LOGIN_REQ_REALM = 9,
DSCENE_LOGIN_UNK_ERROR = 10,
DSCENE_LOGIN_FILE_TRANSFER = 11,
};

View File

@ -48,7 +48,8 @@ public:
SceneLogin::SceneLogin(PseuGUI *gui) : Scene(gui)
{
_gui=gui;
textdb = instance->dbmgr.GetDB("gui_login_text");
msgbox_textid = 0;
eventrecv = new GUIEventReceiver();
device->setEventReceiver(eventrecv);
root = guienv->getRootGUIElement();
@ -67,17 +68,21 @@ SceneLogin::SceneLogin(PseuGUI *gui) : Scene(gui)
guienv->addButton(rect<s32>(scrn.Width-120, scrn.Height-40, scrn.Width-10, scrn.Height-10), 0, 4, L"Quit");
guienv->addButton(rect<s32>(10, scrn.Height-40, 120, scrn.Height-10), 0, 8, L"Community Site");
guienv->addButton(rect<s32>((scrn.Width*0.5f)-60, (scrn.Height*0.3f)+100, (scrn.Width*0.5f)+60, (scrn.Height*0.3f)+130), 0, 16, L"Logon");
msgbox = guienv->addStaticText(GetStringFromDB(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_NOT_CONNECTED).c_str(),rect<s32>((scrn.Width*0.5f)-90, (scrn.Height*0.3f)+150, (scrn.Width*0.5f)+90, (scrn.Height*0.3f)+180),true,true);
}
void SceneLogin::OnUpdate(s32 timepassed)
{
if(msgbox_textid != scenedata[ISCENE_LOGIN_CONN_STATUS])
{
msgbox_textid = scenedata[ISCENE_LOGIN_CONN_STATUS];
msgbox->setText(GetStringFromDB(ISCENE_LOGIN_CONN_STATUS,msgbox_textid).c_str());
}
if(eventrecv->buttons & BUTTON_QUIT)
{
_gui->Shutdown();
_gui->GetInstance()->Stop();
eventrecv->buttons=0;
instance->Stop();
}
if(eventrecv->buttons & BUTTON_LOGON)
{
@ -90,18 +95,21 @@ void SceneLogin::OnUpdate(s32 timepassed)
std::string accpass=tmp.c_str();
if(accname.size() && accpass.size())
{
SetData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_CONN_ATTEMPT);
logdebug("Trying to set Logon Data %u, %u", accname.size(), accpass.size());
// we can safely override the conf settings
_gui->GetInstance()->GetConf()->accname = accname;
_gui->GetInstance()->GetConf()->accpass = accpass;
_gui->GetInstance()->CreateRealmSession();
instance->GetConf()->accname = accname;
instance->GetConf()->accpass = accpass;
instance->CreateRealmSession();
}
else
{
guienv->addMessageBox(L"Oh noes!", L"You have to enter account name and password!");
guienv->addMessageBox(GetStringFromDB(ISCENE_LOGIN_MSGBOX_DUMMY,0).c_str(),
GetStringFromDB(ISCENE_LOGIN_MSGBOX_DUMMY,1).c_str());
}
}
eventrecv->buttons = 0;
}
void SceneLogin::OnDelete(void)

View File

@ -117,6 +117,9 @@ bool PseuInstance::Init(void)
_scp->SetPath(_scpdir);
dbmgr.AddSearchPath("./cache");
dbmgr.AddSearchPath("./data/scp");
_scp->variables.Set("@version_short",_ver_short);
_scp->variables.Set("@version",_ver);
_scp->variables.Set("@inworld","false");
@ -412,9 +415,9 @@ bool PseuInstance::ConnectToRealm(void)
_rsession = new RealmSession(this);
_rsession->SetLogonData(); // get accname & accpass from PseuInstanceConfig and set it in the realm session
_rsession->Connect();
if(_rsession->MustDie()) // something failed. it will be deleted in next Update() call
if(_rsession->MustDie() || !_rsession->SocketGood()) // something failed. it will be deleted in next Update() call
{
logerror("Connecting to Realm failed!");
logerror("PseuInstance: Connecting to Realm failed!");
if(_gui)
_gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_CONN_FAILED);
return false;

View File

@ -315,6 +315,8 @@ void RealmSession::SendLogonChallenge(void)
GetInstance()->SetError();
return;
}
if(PseuGUI *gui = GetInstance()->GetGUI())
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_LOGGING_IN);
std::string acc = stringToUpper(_accname);
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_CHALLENGE;
@ -338,11 +340,14 @@ void RealmSession::SendLogonChallenge(void)
void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
{
PseuGUI *gui = GetInstance()->GetGUI();
logdebug("RealmSocket: Got AUTH_LOGON_CHALLENGE [%u of %u bytes]",pkt.size(),sizeof(sAuthLogonChallenge_S));
if(pkt.size() < 3)
{
logerror("AUTH_LOGON_CHALLENGE: Recieved incorrect/unknown packet. Hexdump:");
DumpInvalidPacket(pkt);
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
return;
}
@ -353,18 +358,25 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
{
case 4:
logerror("Realm Server did not find account \"%s\"!",_accname.c_str());
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_ACC_NOT_FOUND);
break;
case 6:
logerror("Account \"%s\" is already logged in!",_accname.c_str());
// TODO: wait a certain amount of time before reconnecting? conf option?
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_ALREADY_CONNECTED);
break;
case 9:
logerror("Realm Server doesn't accept this version!");
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_WRONG_VERSION);
break;
case 0:
{
pkt.read((uint8*)&lc, sizeof(sAuthLogonChallenge_S));
logdetail("Login successful, now calculating proof packet...");
if(PseuGUI *gui = GetInstance()->GetGUI())
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_AUTHENTICATING);
// now lets start calculating
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
@ -501,12 +513,15 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
{
PseuGUI *gui = GetInstance()->GetGUI();
logdebug("RealmSocket: Got AUTH_LOGON_PROOF [%u of %u bytes]",pkt.size(),26);
if(pkt.size() < 2)
{
logerror("AUTH_LOGON_PROOF: Recieved incorrect/unknown packet. Hexdump:");
DumpInvalidPacket(pkt);
DieOrReconnect(true);
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
return;
}
uint8 error = pkt[1];
@ -516,11 +531,15 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
{
case REALM_AUTH_UPDATE_CLIENT:
log("The realm server requested client update.");
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_WRONG_VERSION);
DieOrReconnect(true);
return;
case REALM_AUTH_NO_MATCH:
case REALM_AUTH_UNKNOWN2:
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_AUTH_FAILED);
logerror("Wrong password or invalid account information or authentication error");
DieOrReconnect(true);
return;
@ -530,6 +549,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
if(error != REALM_AUTH_SUCCESS)
{
logerror("AUTH_LOGON_PROOF: unk error = 0x%X",error);
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
pkt.rpos(2);
DieOrReconnect(true);
return;
@ -542,6 +563,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
//printchex((char*)&lp, sizeof(sAuthLogonProof_S),true);
if(!memcmp(lp.M2,this->_m2,20))
{
if(PseuGUI *gui = GetInstance()->GetGUI())
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_REQ_REALM);
// auth successful
ByteBuffer packet;
packet << (uint8)REALM_LIST;
@ -553,7 +576,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
logcritical("Auth failed, M2 differs!");
printf("My M2 :"); printchex((char*)_m2,20,true);
printf("Srv M2:"); printchex((char*)lp.M2,20,true);
if(gui)
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_AUTH_FAILED);
DieOrReconnect(true);
}
}
@ -576,6 +600,8 @@ void RealmSession::_HandleTransferInit(ByteBuffer& pkt)
pkt >> _file_size;
pkt.read(_file_md5,MD5_DIGEST_LENGTH);
logcustom(0,GREEN,"TransferInit [%s]: File size: "I64FMTD" KB (MD5: %s)", (char*)type_str, _file_size / 1024L, toHexDump(&_file_md5[0],MD5_DIGEST_LENGTH,false).c_str());
if(PseuGUI *gui = GetInstance()->GetGUI())
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_FILE_TRANSFER);
delete [] type_str;
ByteBuffer bb(1);
bb << uint8(XFER_ACCEPT);
@ -708,3 +734,8 @@ void RealmSession::DieOrReconnect(bool err)
GetInstance()->SetError();
}
}
bool RealmSession::SocketGood(void)
{
return _socket && _socket->IsOk();
}

View File

@ -21,7 +21,7 @@ public:
void SendLogonChallenge(void);
bool MustDie(void);
void SetMustDie(void);
bool SocketGood(void);
private:

View File

@ -1,210 +1,839 @@
#include <fstream>
#include "common.h"
#include "Auth/MD5Hash.h"
#include "SCPDatabase.h"
#define HEADER_SIZE (21*sizeof(uint32))
uint32 SCPDatabase::LoadFromFile(char *fn)
inline char *gettypename(uint32 ty)
{
std::fstream fh;
uint32 size = GetFileSize(fn);
if(!size)
return 0;
fh.open(fn,std::ios_base::in | std::ios_base::binary);
if( !fh.is_open() )
return 0;
char *buf = new char[size];
fh.read(buf,size);
fh.close();
uint32 sections = LoadFromMem(buf,size);
delete [] buf;
return sections;
return (ty==0 ? "INT" : (ty==1 ? "FLOAT" : "STRING"));
}
uint32 SCPDatabase::LoadFromMem(char *buf, uint32 size)
// file-globally declared pointer holder. NOT multi-instance-safe for now!!
struct memblock
{
std::string line,value,entry,storage;
uint32 id=0,sections=0;
memblock() : ptr(NULL), size(0) {}
memblock(uint8 *p, uint32 s) : size(s), ptr(p) {}
~memblock() { if(ptr) delete [] ptr; }
uint8 *ptr;
uint32 size;
};
TypeStorage<memblock> Pointers;
for(uint32 pos = 0; pos < size; pos++)
{
if(buf[pos] == '\n')
{
if(line.empty())
continue;
while(line[0]==' ' || line[0]=='\t')
line.erase(0,1);
if(line.empty() || (line.length() > 1 && (line[0]=='#' || (line[0]=='/' && line[1]=='/'))) )
{
line.clear();
continue;
}
if(line[line.size()-1] == 13 || line[line.size()-1] == 10) // this fixes the annoying newline problems on windows + binary mode
line[line.size()-1] = 0;
if(line[0]=='[')
{
id=(uint32)toInt(line.c_str()+1); // start reading after '['
sections++;
}
else
{
uint32 pos=line.find("=");
if(pos!=std::string::npos)
{
entry=stringToLower(line.substr(0,pos));
value=line.substr(pos+1,line.length()-1);
_map[id].Set(entry,value);
}
// else invalid line, must have '='
}
line.clear();
}
else
line += buf[pos]; // fill up line until a newline is reached (see above)
}
return sections;
SCPDatabase::~SCPDatabase()
{
DEBUG(logdebug("Deleting SCPDatabase '%s'",_name.c_str()));
DropAll();
}
bool SCPDatabase::HasField(uint32 id)
SCPDatabase::SCPDatabase()
{
for(SCPFieldMap::iterator i = _map.begin(); i != _map.end(); i++)
if(i->first == id)
return true;
return false;
_stringbuf = NULL;
_intbuf = NULL;
_compact = false;
}
bool SCPField::HasEntry(std::string e)
void SCPDatabase::DropAll(void)
{
for(SCPEntryMap::iterator i = _map.begin(); i != _map.end(); i++)
{
std::string ch = i->first;
if(ch == e)
return true;
}
return false;
DropTextData();
if(_stringbuf)
delete [] _stringbuf;
if(_intbuf)
delete [] _intbuf;
_indexes.clear();
_indexes_reverse.clear();
_fielddefs.clear();
_stringbuf = NULL;
_intbuf = NULL;
_stringsize = 0;
_compact = false;
}
std::string SCPField::GetString(std::string entry)
void SCPDatabase::DropTextData(void)
{
//return HasEntry(entry) ? _map[entry] : "";
return _map[entry];
DEBUG(logdebug("Dropping plaintext parts of DB '%s'",_name.c_str()));
for(SCPSourceList::iterator it = sources.begin(); it != sources.end(); it++)
Pointers.Delete(*it);
sources.clear();
fields.clear();
}
// note that this can take a while depending on the size of the database!
uint32 SCPDatabase::GetFieldByValue(std::string entry, std::string value)
void *SCPDatabase::GetPtr(uint32 index, char *entry)
{
for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++)
if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value)
return fm->first;
return uint32(-1);
std::map<uint32,uint32>::iterator it = _indexes.find(index);
if(it == _indexes.end())
return NULL;
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
if(fi == _fielddefs.end())
return NULL;
uint32 target_row = _indexes[index];
uint32 field_id = _fielddefs[entry].id;
return (void*)&_intbuf[(_fields_per_row * target_row) + field_id];
}
bool SCPDatabaseMgr::HasDB(std::string n)
void *SCPDatabase::GetPtrByField(uint32 index, uint32 entry)
{
for(SCPDatabaseMap::iterator i = _map.begin(); i != _map.end(); i++)
if(i->first == n)
return true;
return false;
std::map<uint32,uint32>::iterator it = _indexes.find(index);
if(it == _indexes.end())
return NULL;
uint32 target_row = _indexes[index];
return (void*)&_intbuf[(_fields_per_row * target_row) + entry];
}
SCPDatabase& SCPDatabaseMgr::GetDB(std::string n)
uint32 SCPDatabase::GetFieldByUint32Value(char *entry, uint32 val)
{
return _map[n];
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
if(fi == _fielddefs.end())
return SCP_INVALID_INT;
uint32 field_id = _fielddefs[entry].id;
return GetFieldByUint32Value(field_id,val);
}
uint32 SCPDatabase::GetFieldByUint32Value(uint32 entry, uint32 val)
{
for(uint32 row = 0; row < _rowcount; row++)
if(_intbuf[row * _fields_per_row + entry] == val)
return _indexes_reverse[row];
return SCP_INVALID_INT;
}
uint32 SCPDatabase::GetFieldByIntValue(char *entry, int32 val)
{
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
if(fi == _fielddefs.end())
return SCP_INVALID_INT;
uint32 field_id = _fielddefs[entry].id;
return GetFieldByIntValue(field_id,val);
}
uint32 SCPDatabase::GetFieldByIntValue(uint32 entry, int32 val)
{
for(uint32 row = 0; row < _rowcount; row++)
if((int)_intbuf[row * _fields_per_row + entry] == val)
return _indexes_reverse[row];
return (int)SCP_INVALID_INT;
}
uint32 SCPDatabase::GetFieldByStringValue(char *entry, char *val)
{
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
if(fi == _fielddefs.end())
return SCP_INVALID_INT;
uint32 field_id = _fielddefs[entry].id;
return GetFieldByStringValue(field_id,val);
}
uint32 SCPDatabase::GetFieldByStringValue(uint32 entry, char *val)
{
for(uint32 row = 0; row < _rowcount; row++)
if(!stricmp(GetStringByOffset(_intbuf[row * _fields_per_row + entry]), val))
return row;
return SCP_INVALID_INT;
}
uint32 SCPDatabase::GetFieldType(char *entry)
{
std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.find(entry);
if(it != _fielddefs.end())
return _fielddefs[entry].type;
return SCP_INVALID_INT;
}
uint32 SCPDatabase::GetFieldId(char *entry)
{
std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.find(entry);
if(it != _fielddefs.end())
return _fielddefs[entry].id;
return SCP_INVALID_INT;
}
SCPDatabase *SCPDatabaseMgr::GetDB(std::string n, bool create)
{
return create ? _map.Get(n) : _map.GetNoCreate(n);
}
uint32 SCPDatabaseMgr::AutoLoadFile(char *fn)
{
std::fstream fh;
uint32 size = GetFileSize(fn);
if(!size)
return 0;
char *buf;
uint32 size;
fh.open(fn,std::ios_base::in | std::ios_base::binary);
if( !fh.is_open() )
return 0;
// check if file was loaded before; use memory data if this is the case
if(memblock *mb = Pointers.GetNoCreate(fn))
{
size = mb->size;
buf = (char*)mb->ptr;
}
else // and if not, read the file from disk
{
std::fstream fh;
size = GetFileSize(fn);
if(!size)
return 0;
char *buf = new char[size];
fh.open(fn,std::ios_base::in | std::ios_base::binary);
if( !fh.is_open() )
return 0;
fh.read(buf,size);
fh.close();
buf = new char[size];
std::string line,dbname;
fh.read(buf,size);
fh.close();
// store the loaded file buffer so we can reuse it later if necessary
Pointers.Assign(fn, new memblock((uint8*)buf,size));
}
std::string line,dbname,entry,value;
SCPDatabase *db = NULL;
uint32 id = 0, sections = 0;
for(uint32 pos = 0; pos < size; pos++)
{
if(buf[pos] == '\n')
if(buf[pos] == '\n' || buf[pos] == 10 || buf[pos] == 13)
{
if(line.empty())
continue;
while(line[0]==' ' || line[0]=='\t')
while(line.size() && (line[0]==' ' || line[0]=='\t'))
line.erase(0,1);
if(line[0] == '#')
uint32 eq = line.find("=");
if(eq != std::string::npos)
{
uint32 eq = line.find("=");
if(eq != std::string::npos)
entry=stringToLower(line.substr(0,eq));
value=line.substr(eq+1,line.length()-1);
if(!stricmp(entry.c_str(),"#dbname") && value.size())
{
std::string info = stringToLower(line.substr(0,pos));
std::string value = stringToLower(line.substr(pos+1,line.length()-1));
if(info == "#dbname")
{
dbname = value;
break;
}
dbname = value;
db = GetDB(dbname,true); // create db if not existing
}
else if(db)
db->fields[id][entry] = value;
}
else if(line[0]=='[')
{
id=(uint32)toInt(line.c_str()+1); // start reading after '['
sections++;
}
line.clear();
}
else
line += buf[pos];
}
delete [] buf;
if(dbname.empty())
return 0;
uint32 sections = GetDB(dbname).LoadFromMem(buf,size);
db->sources.insert(fn);
return sections;
}
// -- helper functions -- //
////////////////////////////////////////////////////////
// SCP compiler
////////////////////////////////////////////////////////
std::string SCPDatabaseMgr::GetZoneName(uint32 id)
// check the datatype that will be used for this string value
uint32 SCPDatabaseMgr::GetDataTypeFromString(char *s)
{
return GetDB("zone").GetField(id).GetString("name");
bool isint = true, first = true;
for(;*s;s++) // check every char until \0 is reached
{
if(!isdigit(*s) && !(first && (*s=='+' || *s=='-'))) // if not in 0-9 or first beeing "+" or "-" it cant be int...
{
isint = false;
if(*s != '.') // and if the char beeing not int isnt a dot (3.1415), it cant be float either
return SCP_TYPE_STRING;
}
first = false;
}
return isint ? SCP_TYPE_INT : SCP_TYPE_FLOAT;
}
std::string SCPDatabaseMgr::GetRaceName(uint32 id)
bool SCPDatabaseMgr::Compact(char *dbname, char *outfile)
{
std::string r = GetDB("race").GetField(id).GetString("name");
//if(r.empty())
// r = raceName[id];
return r;
logdebug("Compacting database '%s' into file '%s'", dbname, outfile);
SCPDatabase *db = GetDB(dbname);
if(!db || db->fields.empty() || db->sources.empty())
{
logerror("Compact(\"%s\",\"%s\") failed, DB doesn't exist or is empty",dbname,outfile);
return false;
}
std::map<std::string, SCPFieldDef> fieldIdMap;
std::map<uint32,uint32> idToSectionMap;
ByteBuffer stringdata(5000);
uint32 cur_idx, pass = 0;
std::string line;
uint32 section=0;
uint32 *membuf = NULL; // stores ints, floats and string offsets from ByteBuffer stringdata
uint32 blocksize;
std::string entry,value;
uint32 field_id;
// some preparations
uint32 nStrings = 0, nRows = db->fields.size(), nFields; // pre-declared
stringdata << ""; // write an empty string so that offset 0 will always return empty string
// the whole process is divided into 2 passes:
// - pass 0 autodetects the datatypes stored in the dirfferent entries (SCPFieldDef)
// - pass 1 creates the data field holding the values and string offsets, and also stuffs string values into the ByteBuffer
while(pass < 2)
{
cur_idx = 1; // stores the current index of the last entry added. index 0 is always field_id!
section = 0; // stores how many sections are done already
for(SCPFieldMap::iterator fmit = db->fields.begin(); fmit != db->fields.end(); fmit++)
{
SCPEntryMap& emap = fmit->second;
field_id = fmit->first; // stores the field id (e.g. "[154]" in the scp file)
idToSectionMap[field_id] = section; // since not every field id exists, store field id and its section number
// for faster lookup, less overhead, and smaller files
// write the field id into row position 0 of the data field
if(pass == 1)
{
ASSERT(membuf != NULL);
uint32 pos = section * nFields;
ASSERT(pos < blocksize);
membuf[pos] = field_id;
}
for(SCPEntryMap::iterator eit = emap.begin(); eit != emap.end(); eit++)
{
// "entry=value" in the scp file
entry = eit->first;
value = eit->second;
// pass 0 - autodetect field types
if(pass == 0)
{
if(fieldIdMap.find(entry) == fieldIdMap.end())
{
SCPFieldDef d;
d.id = cur_idx++;
d.type = GetDataTypeFromString((char*)value.c_str());
fieldIdMap[entry] = d;
DEBUG(logdebug("Found new key: '%s' id: %u type: %s", entry.c_str(), d.id, gettypename(d.type) ));
}
else
{
SCPFieldDef& d = fieldIdMap[entry];
uint32 _oldtype = d.type;
// int can be changed to float or string, float only to string. changing back not allowed.
if(d.type == SCP_TYPE_INT)
{
d.type = GetDataTypeFromString((char*)value.c_str());
}
else if(d.type == SCP_TYPE_FLOAT)
{
uint32 newtype = GetDataTypeFromString((char*)value.c_str());
if(newtype == SCP_TYPE_STRING)
d.type = newtype;
}
if(_oldtype != d.type)
{
logdebug("Key '%s' id %u changed from %s to %s (field_id: %u)", entry.c_str(), d.id, gettypename(_oldtype), gettypename(d.type),field_id);
}
}
}
// pass 1 - create the data field
else if(pass == 1)
{
ASSERT(membuf != NULL);
SCPFieldDef d = fieldIdMap[entry];
uint32 pos = (section * nFields) + d.id;
ASSERT(pos < blocksize);
if(d.type == SCP_TYPE_STRING)
{
membuf[pos] = stringdata.wpos();
stringdata << value;
nStrings++;
}
else if(d.type == SCP_TYPE_INT)
{
membuf[pos] = atoi(value.c_str());
}
else if(d.type == SCP_TYPE_FLOAT)
{
((float*)membuf)[pos] = atof(value.c_str());
}
}
}
section++;
}
// if the first pass is done, allocate the data field for the second pass
if(!pass)
{
nFields = fieldIdMap.size() + 1; // add the one field used for the field ID
ASSERT(section == nRows);
blocksize = nRows * nFields; // +1 because we store the field id here also
DEBUG(logdebug("SCP: allocating %u*%u = %u integers (%u bytes)",nRows,fieldIdMap.size()+1,blocksize, blocksize*sizeof(uint32)));
membuf = new uint32[blocksize];
memset(membuf, 0, blocksize * sizeof(uint32));
}
pass++;
}
// used header fields, some are pre-declared above, if needed
uint32 offsMD5, nMD5, sizeMD5;
uint32 offsIndexes, nIndexes, sizeIndexes;
uint32 offsFields, sizeFields;
uint32 offsData;
uint32 offsStrings, sizeStrings;
// MD5 hashes of source files
SCPSourceList& src = db->sources;
ByteBuffer md5buf;
nMD5 = 0;
for(SCPSourceList::iterator it = src.begin(); it != src.end(); it++)
{
memblock *mb = Pointers.GetNoCreate(*it);
if(!mb)
{
// if we reach this point there was really some big f*** up
logerror("SCP Compact: memblock for file '%s' doesn't exist",it->c_str());
continue;
}
MD5Hash md5;
md5.Update(mb->ptr,mb->size);
md5.Finalize();
md5buf << *it;
md5buf.append(md5.GetDigest(),md5.GetLength());
nMD5++;
}
sizeMD5 = md5buf.size();
// field types, sorted by IDs
ByteBuffer fieldbuf;
// put the entries and their type into ByteBuffer, sorted by their position in the membuf rows
// note that the first field in the data row is always the field id, so the values start from 1
for(std::map<std::string,SCPFieldDef>::iterator itf = fieldIdMap.begin(); itf != fieldIdMap.end(); itf++)
{
fieldbuf << itf->first << itf->second.id << itf->second.type; // entry name, id, type.
}
sizeFields = fieldbuf.size();
// index -> ID lookup table, e.g. data with ID 500 will have field index 214, because some IDs in between are missing
// it *could* be calculated at load-time from the existing data field, but this way is faster when random-accessing the file itself
// (what we dont do anyway, for now)
ByteBuffer indexbuf;
for(std::map<uint32,uint32>::iterator itx = idToSectionMap.begin(); itx != idToSectionMap.end(); itx++)
{
indexbuf << itx->first << itx->second; // field id; row number
}
nIndexes = idToSectionMap.size();
sizeIndexes = indexbuf.size();
// string data
// -- most of it is handled somewhere above
sizeStrings = stringdata.size();
// precalc absolute offsets, header is 18 bytes large
offsMD5 = HEADER_SIZE;
offsIndexes = offsMD5 + sizeMD5;
offsFields = offsIndexes + sizeIndexes;
offsData = offsFields + sizeFields;
offsStrings = offsData + blocksize * sizeof(uint32);
// buffer the file header
ByteBuffer hbuf;
hbuf.append("SCPC",4); // identifier
hbuf << (uint32)0; // flags, not yet used
hbuf << (uint32)0 << (uint32)0 << (uint32)0 << (uint32)0; // padding, not yet used
hbuf << offsMD5 << nMD5 << sizeMD5;
hbuf << offsIndexes << nIndexes << sizeIndexes;
hbuf << offsFields << nFields << sizeFields;
hbuf << offsData << section << blocksize * sizeof(uint32);
hbuf << offsStrings << nStrings << sizeStrings;
FILE *fh = fopen(outfile,"wb");
if(!fh)
return false;
if(fh)
{
fwrite(hbuf.contents(),hbuf.size(),1,fh);
if(sizeMD5) // just in case no md5sums are stored
fwrite(md5buf.contents(),sizeMD5,1,fh);
fwrite(indexbuf.contents(),sizeIndexes,1,fh);
fwrite(fieldbuf.contents(),sizeFields,1,fh);
fwrite(membuf,sizeof(uint32),blocksize,fh);
fwrite(stringdata.contents(),sizeStrings,1,fh);
}
fclose(fh);
if(!db)
db = GetDB(dbname,true); // create if not exist
db->_compact = true;
db->_name = dbname;
// drop all data no longer needed if the database is compacted
db->DropTextData();
// we keep the membuf, since the compiled data are now usable as if loaded directly from a file
// associate it with the buffers used by the db accessing functions
db->_stringbuf = new char[sizeStrings];
db->_stringsize = sizeStrings;
memcpy(db->_stringbuf,stringdata.contents(),sizeStrings);
db->_intbuf = membuf; // <<-- do NOT drop the membuf, its still used and will be deleted with ~SCPDatabase()!!
db->_fields_per_row = nFields;
db->_rowcount = nRows;
db->_indexes = idToSectionMap;
db->_fielddefs = fieldIdMap;
for(std::map<uint32,uint32>::iterator it = idToSectionMap.begin(); it != idToSectionMap.end(); it++)
db->_indexes_reverse[it->second] = it->first;
return true;
}
std::string SCPDatabaseMgr::GetMapName(uint32 id)
uint32 SCPDatabaseMgr::SearchAndLoad(char *dbname, bool no_compiled)
{
return GetDB("map").GetField(id).GetString("name");
uint32 count = 0;
std::deque<std::string> goodfiles;
for(std::deque<std::string>::iterator it = _paths.begin(); it != _paths.end(); it++)
{
std::deque<std::string> files = GetFileList(*it);
sort(files.begin(),files.end()); // rough alphabetical sort
for(std::deque<std::string>::iterator itf = files.begin(); itf != files.end(); itf++)
{
std::string& fn = *itf;
if(fn.length() < 5)
continue;
std::string filepath = *it + fn;
// check for special case: <dbname>.ccp in this directory? load it and skip the rest
if(!no_compiled && !stricmp(std::string(dbname).append(".ccp").c_str(), fn.c_str()))
{
logdebug("Loading pre-compacted database '%s%s'", it->c_str(), fn.c_str());
DropDB(dbname); // if sth got loaded before, remove that
// load SCC database file and skip rest
if(LoadCompactSCP((char*)filepath.c_str(), dbname))
{
logdebug("Loaded '%s' -> %s, skipping scp files",filepath.c_str(),dbname);
return 1;
}
}
else if(!stricmp(fn.c_str() + fn.length() - 4, ".scp"))
{
// skip 0-byte files
if(GetFileSize(filepath.c_str()))
goodfiles.push_back(filepath);
}
}
}
logdetail("Pre-compacted SCC file for '%s' invalid, creating from SCP (%u files total)",dbname,goodfiles.size());
for(std::deque<std::string>::iterator it = goodfiles.begin(); it != goodfiles.end(); it++)
{
bool load_it = false;
std::fstream fh;
fh.open( it->c_str() , std::ios_base::in | std::ios_base::binary);
if( !fh.is_open() )
continue;
uint32 size = 1000; // search for #dbname tag in first 1000 bytes
char *buf = new char[size];
memset(buf,0,size);
fh.read(buf,size);
fh.close();
std::string line,dbn;
for(uint32 pos = 0; pos < size; pos++)
{
if(buf[pos] == '\n' || buf[pos] == 10 || buf[pos] == 13)
{
if(line.empty())
continue;
while(line.size() && (line[0]==' ' || line[0]=='\t'))
line.erase(0,1);
if(line[0] == '#')
{
if(!strnicmp(line.c_str(),"#dbname=",8) && !stricmp(line.c_str()+8, dbname))
{
load_it = true;
break;
}
}
line.clear();
}
else
line += buf[pos];
}
delete [] buf;
if(load_it)
{
logdebug("File '%s' matching database '%s', loading", it->c_str(), dbname);
count++;
uint32 sections = AutoLoadFile((char*)it->c_str());
}
}
char fn[100];
sprintf(fn,"./cache/%s.ccp",dbname);
Compact(dbname, fn);
return count;
}
std::string SCPDatabaseMgr::GetClassName_(uint32 id)
void SCPDatabaseMgr::AddSearchPath(char *path)
{
std::string r = GetDB("class").GetField(id).GetString("name");
//if(r.empty())
// r = className[id];
return r;
std::string p;
// normalize path, use '/' instead of '\'. needed for that check below
uint32 len = strlen(path);
for(uint32 i = 0; i < len; i++)
{
if(path[i] == '\\')
p += '/';
else
p += path[i];
}
if(p[p.size()-1] != '/')
p += '/';
for(std::deque<std::string>::iterator it = _paths.begin(); it != _paths.end(); it++)
{
// windows doesnt care about UPPER/lowercase, while *nix does;
// a path shouldnt be added twice or unexpected behavior is likely to occur
#if PLATFORM == PLATFORM_WIN32
if(!stricmp(p.c_str(), it->c_str()))
return;
#else
if(p == *it)
return;
#endif
}
_paths.push_back(p);
}
std::string SCPDatabaseMgr::GetGenderName(uint32 id)
bool SCPDatabaseMgr::LoadCompactSCP(char *fn, char *dbname)
{
return GetDB("gender").GetField(id).GetString("name");
uint32 filesize = GetFileSize(fn);
if(filesize < HEADER_SIZE)
{
logerror("Database file '%s' is too small!",fn);
return false;
}
std::fstream fh;
fh.open(fn, std::ios_base::in | std::ios_base::binary);
if(!fh.is_open())
{
logerror("Error opening '%s'",fn);
return false;
}
ByteBuffer bb;
bb.resize(filesize);
fh.read((char*)bb.contents(), filesize);
fh.close();
char tag[4];
uint32 flags, padding[4];
uint32 offsMD5, nMD5, sizeMD5;
uint32 offsIndexes, nIndexes, sizeIndexes;
uint32 offsFields, nFields, sizeFields;
uint32 offsData, nRows, sizeData;
uint32 offsStrings, nStrings, sizeStrings;
bb.read((uint8*)&tag[0],4);
if(memcmp(tag,"SCPC",4))
{
logerror("'%s' is not a compact database file!",fn);
return false;
}
bb >> flags;
bb >> padding[0] >> padding[1] >> padding[2] >> padding[3];
bb >> offsMD5 >> nMD5 >> sizeMD5;
bb >> offsIndexes >> nIndexes >> sizeIndexes;
bb >> offsFields >> nFields >> sizeFields;
bb >> offsData >> nRows >> sizeData;
bb >> offsStrings >> nStrings >> sizeStrings;
SCPDatabase *db = GetDB(dbname,true);
db->_name = dbname;
db->_compact = true;
ByteBuffer md5buf(sizeMD5);
md5buf.resize(sizeMD5);
// read MD5 block
bb.rpos(offsMD5);
if(bb.rpos() == offsMD5)
bb.read((uint8*)md5buf.contents(),sizeMD5);
else
{
logerror("'%s' has wrong MD5 offset, can't load",fn);
return false;
}
for(uint32 i = 0; i < nMD5; i++)
{
// read filename and MD5 hash from compiled database
uint8 buf[MD5_DIGEST_LENGTH];
std::string refFn;
md5buf >> refFn;
md5buf.read(buf,MD5_DIGEST_LENGTH);
// load the file referred to
uint32 refFileSize = GetFileSize(refFn.c_str());
FILE *refFile = fopen(refFn.c_str(), "rb");
if(!refFile)
{
logdebug("Not loading '%s', file doesn't exist",fn);
return false;
}
uint8 *refFileBuf = new uint8[refFileSize];
fread(refFileBuf,sizeof(uint8),refFileSize,refFile);
fclose(refFile);
db->sources.insert(refFn);
Pointers.Assign(refFn, new memblock(refFileBuf,refFileSize));
MD5Hash md5;
md5.Update(refFileBuf,refFileSize);
md5.Finalize();
if(memcmp(buf, md5.GetDigest(), MD5_DIGEST_LENGTH))
{
logdebug("MD5-check: '%s' has changed!", refFn.c_str());
return false;
}
else
{
logdebug("MD5-check: '%s' -> OK",refFn.c_str());
}
}
// everything good so far? we reached this point? then its likely that the rest of the file is ok, alloc remaining buffers
ByteBuffer indexbuf(sizeIndexes);
ByteBuffer fieldsbuf(sizeFields);
indexbuf.resize(sizeIndexes);
fieldsbuf.resize(sizeFields);
// read indexes block
bb.rpos(offsIndexes);
if(bb.rpos() == offsIndexes)
bb.read((uint8*)indexbuf.contents(),sizeIndexes);
else
{
logerror("'%s' has wrong indexes offset, can't load",fn);
return false;
}
// read field definitions buf
bb.rpos(offsFields);
if(bb.rpos() == offsFields)
bb.read((uint8*)fieldsbuf.contents(),sizeFields);
else
{
logerror("'%s' has wrong field defs offset, can't load",fn);
return false;
}
// main data and string blocks follow below
for(uint32 i = 0; i < nIndexes; i++)
{
uint32 field_id, row;
indexbuf >> field_id >> row;
db->_indexes[field_id] = row;
db->_indexes_reverse[row] = field_id;
}
for(uint32 i = 0; i < nFields - 1; i++) // the first field (index column) is never written to the file!
{
SCPFieldDef fieldd;
std::string fieldn;
fieldsbuf >> fieldn >> fieldd.id >> fieldd.type;
db->_fielddefs[fieldn] = fieldd;
}
// read main data block
ASSERT(nRows * nFields == sizeData / sizeof(uint32));
bb.rpos(offsData);
if(bb.rpos() == offsData)
{
db->_intbuf = new uint32[nRows * nFields];
bb.read((uint8*)db->_intbuf, sizeData); // load this somewhat fast and without a for loop
}
else
{
logerror("'%s' has wrong data offset, can't load",fn);
return false;
}
// read strings
bb.rpos(offsStrings);
if(bb.rpos() == offsStrings)
{
db->_stringbuf = new char[sizeStrings];
bb.read((uint8*)db->_stringbuf,sizeStrings);
}
else
{
logerror("'%s' has wrong strings offset, can't load",fn);
return false;
}
db->_stringsize = sizeStrings;
db->_rowcount = nRows;
db->_fields_per_row = nFields;
db->DropTextData(); // delete pointers to file content created at md5 comparison
logdebug("'%s' loaded successfully",fn);
return true;
}
std::string SCPDatabaseMgr::GetLangName(uint32 id)
// used only for debugging
void SCPDatabase::DumpStructureToFile(char *fn)
{
std::string r = GetDB("language").GetField(id).GetString("name");
//if(r.empty())
// r = LookupName(id,langNames);
return r;
std::ofstream f;
f.open(fn);
if(!f.is_open())
return;
uint32 *ftype = new uint32[_fields_per_row];
ftype[0] = SCP_TYPE_INT;
f << "Fields: (0 is always index field)\n";
for(std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.begin(); it != _fielddefs.end(); it++)
{
f << "-> Name: " << it->first << ", ID: " << it->second.id << ", type: " << gettypename(it->second.type) << "\n";
ftype[it->second.id] = it->second.type;
}
f << "\n";
for(uint32 row = 0; row < _rowcount; row++)
{
for(uint32 column = 0; column < _fields_per_row; column++)
{
if(ftype[column] == SCP_TYPE_INT)
f << *((int*)&_intbuf[row * _fields_per_row + column]) << "\t";
else if(ftype[column] == SCP_TYPE_FLOAT)
f << *((float*)&_intbuf[row * _fields_per_row + column]) << "\t";
else
f << "S_" << _intbuf[row * _fields_per_row + column] << "\t";
}
f << "\n";
}
f << "\nStrings:\n";
for(uint32 i = 0; i < _stringsize; i++)
{
if(!_stringbuf[i])
{
i++;
if(i >= _stringsize)
break;
f << "\n" << i << ": ";
}
f << _stringbuf[i];
}
}

View File

@ -1,113 +1,106 @@
#ifndef _SCPDATABASE_H
#define _SCPDATABASE_H
#include <map>
#include "DefScript/TypeStorage.h"
#include <set>
/*
* Some preparations for compiled databases. i will continue this later. [FG]
enum SCPTypes
enum SCPFieldTypes
{
SCP_TYPE_STRING = 0,
SCP_TYPE_INT,
SCP_TYPE_UINT,
SCP_TYPE_FLOAT
SCP_TYPE_INT = 0,
SCP_TYPE_FLOAT = 1,
SCP_TYPE_STRING = 2
};
class SCPEntry
struct SCPFieldDef
{
private:
char *str;
public:
SCPEntry() { str = NULL; }
~SCPEntry() { if(str) delete [] str; }
union
{
int32 intvalue
uint32 uintvalue;
float floatvalue;
};
inline char *GetString(uint8 type)
{
if(str)
return str;
else
{
char buf[25];
char *fmt;
switch (type)
{
case SCP_TYPE_INT: sprintf(buf,"%d",intvalue); break;
case SCP_TYPE_UINT: sprintf(buf,"%u",uintvalue); break;
case SCP_TYPE_FLOAT: sprintf(buf,"%f",floatvalue); break;
}
str = new char[strlen(buf) + 1];
memcpy(str,buf,strlen(buf) + 1);
return str;
}
}
uint32 id;
uint8 type;
};
*/
#define SCP_INVALID_INT 0xFFFFFFFF
typedef std::map<std::string,std::string> SCPEntryMap;
class SCPField
{
public:
std::string GetString(std::string);
inline uint64 GetInteger(std::string entry) { return toInt(GetString(entry)); }
inline double GetDouble(std::string entry) { return strtod(GetString(entry).c_str(),NULL); }
inline void Set(std::string entry,std::string value) { _map[entry]=value; }
bool HasEntry(std::string);
private:
SCPEntryMap _map;
};
typedef std::map<uint32,SCPField> SCPFieldMap;
typedef std::map<uint32,SCPEntryMap> SCPFieldMap;
typedef std::set<std::string> SCPSourceList;
class SCPDatabase
{
friend class SCPDatabaseMgr;
public:
inline SCPField& GetField(uint32 id) { return _map[id]; }
bool HasField(uint32 id);
uint32 LoadFromFile(char*);
uint32 LoadFromMem(char*,uint32);
uint32 GetFieldByValue(std::string entry, std::string value);
SCPDatabase();
~SCPDatabase();
// if the database is compacted, do not allow any modification from outside
SCPSourceList *GetSources(void) { return _compact ? NULL : &sources; }
SCPFieldMap *GetFields(void) { return _compact ? NULL : &fields; }
inline bool IsCompact(void) { return _compact; }
inline std::string GetName(void) { return _name; }
void DropAll(void);
void DropTextData(void);
// access funcs
void *GetPtr(uint32 index, char *entry);
void *GetPtrByField(uint32 index, uint32 entry);
inline char *GetStringByOffset(uint32 offs) { return (offs < _stringsize ? _stringbuf + offs : ""); }
inline char *GetString(uint32 index, char *entry) { return GetStringByOffset(GetUint32(index,entry)); }
inline char *GetString(uint32 index, uint32 entry) { return GetStringByOffset(GetUint32(index,entry)); }
inline uint32 GetUint32(uint32 index, char *entry) { uint32 *t = (uint32*)GetPtr(index,entry); return t ? *t : 0; }
inline uint32 GetUint32(uint32 index, uint32 entry) { uint32 *t = (uint32*)GetPtrByField(index,entry); return t ? *t : 0; }
inline int32 GetInt(uint32 index, char *entry) { int32 *t = (int32*)GetPtr(index,entry); return t ? *t : 0; }
inline int32 GetInt(uint32 index, uint32 entry) { int32 *t = (int32*)GetPtrByField(index,entry); return t ? *t : 0; }
inline float GetFloat(uint32 index, char *entry) { float *t = (float*)GetPtr(index,entry); return t ? *t : 0; }
inline float GetFloat(uint32 index, uint32 entry) { float *t = (float*)GetPtrByField(index,entry); return t ? *t : 0; }
uint32 GetFieldType(char *entry);
uint32 GetFieldId(char *entry);
inline void *GetRowByIndex(uint32 index) { return GetPtrByField(index,0); }
uint32 GetFieldByUint32Value(char *entry, uint32 val);
uint32 GetFieldByUint32Value(uint32 entry, uint32 val);
uint32 GetFieldByIntValue(char *entry, int32 val);
uint32 GetFieldByIntValue(uint32 entry, int32 val);
uint32 GetFieldByStringValue(char *entry, char *val);
uint32 GetFieldByStringValue(uint32 entry, char *val);
// float value lookup not necessary
inline uint32 GetFieldsCount(void) { return _fields_per_row; }
inline uint32 GetRowsCount(void) { return _rowcount; }
void DumpStructureToFile(char *fn);
private:
SCPFieldMap _map;
// text data related
SCPSourceList sources;
SCPFieldMap fields;
// binary data related
bool _compact;
std::string _name;
uint32 _rowcount;
uint32 _fields_per_row;
char *_stringbuf;
uint32 _stringsize;
uint32 *_intbuf;
std::map<uint32,uint32> _indexes, _indexes_reverse; // stores index-to-rowID, rowID-to-index
std::map<std::string,SCPFieldDef> _fielddefs;
};
typedef std::map<std::string,SCPDatabase> SCPDatabaseMap;
typedef TypeStorage<SCPDatabase> SCPDatabaseMap;
class SCPDatabaseMgr
{
friend class SCPDatabase;
public:
bool HasDB(std::string);
SCPDatabase& GetDB(std::string);
SCPDatabase *GetDB(std::string n, bool create = false);
uint32 AutoLoadFile(char *fn);
inline void DropDB(std::string s) { _map.erase(stringToLower(s)); }
//////////////////////
// helper functions //
//////////////////////
std::string GetZoneName(uint32 id);
std::string GetRaceName(uint32 id);
std::string GetClassName_(uint32 id);
std::string GetGenderName(uint32 id);
std::string GetMapName(uint32 id);
std::string GetLangName(uint32 id);
inline void DropDB(std::string s) { _map.Delete(stringToLower(s)); }
bool Compact(char *dbname, char *outfile);
static uint32 GetDataTypeFromString(char *s);
uint32 SearchAndLoad(char*,bool);
void AddSearchPath(char*);
bool LoadCompactSCP(char*, char*);
private:
SCPDatabaseMap _map;
std::deque<std::string> _paths;
};

View File

@ -577,15 +577,30 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
}
bool char_found=false;
SCPDatabase *zonedb = GetDBMgr().GetDB("zone"),
*racedb = GetDBMgr().GetDB("race"),
*mapdb = GetDBMgr().GetDB("map"),
*classdb = GetDBMgr().GetDB("class");
char *zonename, *racename, *mapname, *classname;
for(unsigned int i=0;i<num;i++)
{
if(zonedb)
zonename = zonedb->GetString(plr[i]._zoneId, "name");
if(racedb)
racename = racedb->GetString(plr[i]._race, "name");
if(mapdb)
mapname = mapdb->GetString(plr[i]._mapId, "name");
if(classdb)
classname = classdb->GetString(plr[i]._class, "name");
logcustom(0,LGREEN,"## %s (%u) [%s/%s] Map: %s; Zone: %s",
plr[i]._name.c_str(),
plr[i]._level,
GetDBMgr().GetRaceName(plr[i]._race).c_str(),
GetDBMgr().GetClassName_(plr[i]._class).c_str(),
GetDBMgr().GetMapName(plr[i]._mapId).c_str(),
GetDBMgr().GetZoneName(plr[i]._zoneId).c_str());
racename,
classname,
mapname,
zonename);
logdetail("-> coords: map=%u zone=%u x=%f y=%f z=%f",
plr[i]._mapId,plr[i]._zoneId,plr[i]._x,plr[i]._y,plr[i]._z);
@ -683,8 +698,10 @@ void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
}
recvPacket >> target_guid >> msglen >> msg;
std::string langname = GetDBMgr().GetLangName(lang);
const char* ln = langname.c_str();
SCPDatabase *langdb = GetDBMgr().GetDB("language");
const char* ln = "";
if(langdb)
langdb->GetString(lang,"name");
std::string name;
if(type == CHAT_MSG_MONSTER_SAY)
@ -1147,9 +1164,9 @@ void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket)
logdebug(I64FMT " Emote: name=%s text=%u variation=%i len=%u",guid,name.c_str(),emotetext,emotev,namelen);
SCPDatabaseMgr& dbmgr = GetInstance()->dbmgr;
if(dbmgr.HasDB("emote"))
SCPDatabase *emotedb = dbmgr.GetDB("emote");
if(emotedb)
{
SCPDatabase& db = dbmgr.GetDB("emote");
std::string target,target2;
bool targeted=false; // if the emote is directed to anyone around or a specific target
bool targeted_me=false; // if the emote was targeted to us if it was targeted
@ -1196,13 +1213,13 @@ void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket)
target += "general";
// not all emotes have a female version, so check if there is one in the database
if(female && db.GetField(emotetext).HasEntry(target + "female"))
if(female && emotedb->GetFieldId((char*)(target + "female").c_str()) != SCP_INVALID_INT)
target += "female";
logdebug("Looking up 'emote' SCP field %u entry '%s'",emotetext,target.c_str());
std::string etext;
etext = db.GetField(emotetext).GetString(target);
etext = emotedb->GetString(emotetext,(char*)target.c_str());
char out[300]; // should be enough
@ -1278,9 +1295,16 @@ void WorldSession::_HandleWhoOpcode(WorldPacket& recvPacket)
std::string zonename;
memset(classname,0,sizeof(classname));
memset(racename,0,sizeof(racename));
zonename = db.GetZoneName(wle.zoneId);
strcpy(classname, db.GetClassName_(wle.classId).c_str());
strcpy(racename, db.GetRaceName(wle.raceId).c_str());
SCPDatabase *zonedb = db.GetDB("zone"),
*racedb = db.GetDB("race"),
*classdb = db.GetDB("class");
if(zonedb)
zonename = zonedb->GetString(wle.zoneId, "name");
if(racedb)
strcpy(racename,racedb->GetString(wle.raceId, "name"));
if(classdb)
strcpy(classname,classdb->GetString(wle.classId, "name"));
for(uint8 i = strlen(classname); strlen(classname) < 10; i++)
classname[i] = ' ';

View File

@ -207,9 +207,6 @@
<File
RelativePath=".\Client\SCPDatabase.h">
</File>
<File
RelativePath=".\Client\SysDefs.h">
</File>
<Filter
Name="DefScript"
Filter="">

View File

@ -181,7 +181,7 @@ class ByteBuffer
size_t rpos(size_t rpos)
{
_rpos = rpos;
_rpos = rpos < _storage.capacity() ? rpos : _storage.capacity();
return _rpos;
};
@ -192,7 +192,7 @@ class ByteBuffer
size_t wpos(size_t wpos)
{
_wpos = wpos;
_wpos = wpos < _storage.capacity() ? wpos : _storage.capacity();
return _wpos;
}