* unload WMOs when unloading map tile * removed silly old chat AI * misc stuff & cleanups
567 lines
15 KiB
C++
567 lines
15 KiB
C++
#include "common.h"
|
|
#include "PseuWoW.h"
|
|
#include <time.h>
|
|
#include <openssl/rand.h>
|
|
|
|
#include "ByteBuffer.h"
|
|
#include "DefScript/DefScript.h"
|
|
#include "DefScriptInterface.h"
|
|
#include "Auth/BigNumber.h"
|
|
#include "DefScript/DefScript.h"
|
|
#include "RealmSession.h"
|
|
#include "WorldSession.h"
|
|
#include "CacheHandler.h"
|
|
#include "GUI/PseuGUI.h"
|
|
#include "RemoteController.h"
|
|
#include "Cli.h"
|
|
#include "GUI/SceneData.h"
|
|
#include "MemoryDataHolder.h"
|
|
|
|
|
|
//###### Start of program code #######
|
|
|
|
PseuInstanceRunnable::PseuInstanceRunnable()
|
|
{
|
|
}
|
|
|
|
void PseuInstanceRunnable::run(void)
|
|
{
|
|
_i = new PseuInstance(this);
|
|
_i->SetConfDir("./conf/");
|
|
_i->SetScpDir("./scripts/");
|
|
if(_i->Init())
|
|
{
|
|
_i->Run();
|
|
}
|
|
else
|
|
{
|
|
getchar(); // if init failed, wait for keypress before exit
|
|
}
|
|
delete _i;
|
|
}
|
|
|
|
void PseuInstanceRunnable::sleep(uint32 msecs)
|
|
{
|
|
ZThread::Thread::sleep(msecs);
|
|
}
|
|
|
|
PseuInstance::PseuInstance(PseuInstanceRunnable *run)
|
|
{
|
|
_runnable=run;
|
|
_ver="PseuWoW Alpha Build 13.51" DEBUG_APPENDIX;
|
|
_ver_short="A13.51" DEBUG_APPENDIX;
|
|
_wsession=NULL;
|
|
_rsession=NULL;
|
|
_scp=NULL;
|
|
_conf=NULL;
|
|
_cli=NULL;
|
|
_rmcontrol=NULL;
|
|
_gui=NULL;
|
|
_guithread=NULL;
|
|
_stop=false;
|
|
_fastquit=false;
|
|
_startrealm=true;
|
|
_createws=false;
|
|
_creaters=false;
|
|
_error=false;
|
|
_initialized=false;
|
|
for(uint32 i = 0; i < COND_MAX; i++)
|
|
{
|
|
_condition[i] = new ZThread::Condition(_mutex);
|
|
}
|
|
|
|
}
|
|
|
|
PseuInstance::~PseuInstance()
|
|
{
|
|
if(_cli)
|
|
{
|
|
_cli->stop();
|
|
// delete _cli; // ok this is a little mem leak... can be fixed sometime in future
|
|
}
|
|
|
|
if(_gui)
|
|
_gui->Shutdown();
|
|
logdebug("Waiting for GUI to quit...");
|
|
while(_gui)
|
|
Sleep(1);
|
|
|
|
if(_guithread)
|
|
_guithread->wait();
|
|
|
|
if(_rmcontrol)
|
|
delete _rmcontrol;
|
|
if(_rsession)
|
|
delete _rsession;
|
|
if(_wsession)
|
|
delete _wsession;
|
|
|
|
delete _scp;
|
|
delete _conf;
|
|
|
|
for(uint32 i = 0; i < COND_MAX; i++)
|
|
{
|
|
delete _condition[i];
|
|
}
|
|
|
|
log("--- Instance shut down ---");
|
|
}
|
|
|
|
bool PseuInstance::Init(void)
|
|
{
|
|
log_setloglevel(0);
|
|
log("");
|
|
log("--- Initializing Instance ---");
|
|
|
|
if(_confdir.empty())
|
|
_confdir="./conf/";
|
|
if(_scpdir.empty())
|
|
_scpdir="./scripts/";
|
|
|
|
srand((unsigned)time(NULL));
|
|
RAND_set_rand_method(RAND_SSLeay()); // init openssl randomizer
|
|
|
|
_scp=new DefScriptPackage();
|
|
_scp->SetParentMethod((void*)this);
|
|
_conf=new PseuInstanceConf();
|
|
|
|
_scp->SetPath(_scpdir);
|
|
|
|
CreateDir("cache");
|
|
|
|
dbmgr.AddSearchPath("./cache");
|
|
dbmgr.AddSearchPath("./data/scp");
|
|
dbmgr.SetCompression(6);
|
|
|
|
_scp->variables.Set("@version_short",_ver_short);
|
|
_scp->variables.Set("@version",_ver);
|
|
_scp->variables.Set("@inworld","false");
|
|
|
|
if(!_scp->LoadScriptFromFile("./_startup.def"))
|
|
{
|
|
logerror("Error loading '_startup.def'");
|
|
SetError();
|
|
}
|
|
else if(!_scp->BoolRunScript("_startup",NULL))
|
|
{
|
|
logerror("Error executing '_startup.def'");
|
|
SetError();
|
|
}
|
|
|
|
// TODO: find a better loaction where to place this block!
|
|
if(GetConf()->enablegui)
|
|
{
|
|
if(InitGUI())
|
|
logdebug("GUI: Init successful.");
|
|
else
|
|
logerror("GUI: Init failed!");
|
|
}
|
|
|
|
if(GetConf()->rmcontrolport)
|
|
{
|
|
_rmcontrol = new RemoteController(this,GetConf()->rmcontrolport);
|
|
}
|
|
|
|
#if !(PLATFORM == PLATFORM_WIN32 && !defined(_CONSOLE))
|
|
if(GetConf()->enablecli)
|
|
{
|
|
log("Starting CLI...");
|
|
_cli = new CliRunnable(this);
|
|
ZThread::Thread t(_cli);
|
|
}
|
|
#endif
|
|
|
|
if(_error)
|
|
{
|
|
logcritical("Errors while initializing!");
|
|
return false;
|
|
}
|
|
|
|
log("Init complete.");
|
|
_initialized=true;
|
|
return true;
|
|
}
|
|
|
|
bool PseuInstance::InitGUI(void)
|
|
{
|
|
if(GetGUI())
|
|
{
|
|
logerror("GUI: Aborting init, GUI already exists!");
|
|
return false;
|
|
}
|
|
|
|
/*if (!GetConf()->enablegui)
|
|
{
|
|
logdebug("GUI: Can't start, gui disabled in config");
|
|
return false;
|
|
}*/
|
|
|
|
uint16 x,y,depth;
|
|
uint8 driver;
|
|
bool shadows,vsync,win,usesound;
|
|
|
|
driver=(uint8)atoi(GetScripts()->variables.Get("GUI::DRIVER").c_str());
|
|
vsync=(bool)atoi(GetScripts()->variables.Get("GUI::VSYNC").c_str());
|
|
depth=(uint8)atoi(GetScripts()->variables.Get("GUI::DEPTH").c_str());
|
|
x=(uint16)atoi(GetScripts()->variables.Get("GUI::RESX").c_str());
|
|
y=(uint16)atoi(GetScripts()->variables.Get("GUI::RESY").c_str());
|
|
win=(bool)atoi(GetScripts()->variables.Get("GUI::WINDOWED").c_str());
|
|
shadows=(bool)atoi(GetScripts()->variables.Get("GUI::SHADOWS").c_str());
|
|
usesound=(bool)atoi(GetScripts()->variables.Get("GUI::USESOUND").c_str());
|
|
log("GUI settings: driver=%u, depth=%u, res=%ux%u, windowed=%u, shadows=%u sound=%u",driver,depth,x,y,win,shadows,usesound);
|
|
if(x>0 && y>0 && (depth==16 || depth==32) && driver>0 && driver<=5)
|
|
{
|
|
PseuGUIRunnable *rgui = new PseuGUIRunnable();
|
|
_gui = rgui->GetGUI();
|
|
_gui->SetInstance(this);
|
|
_gui->SetDriver(driver);
|
|
_gui->SetResolution(x,y,depth);
|
|
_gui->SetVSync(vsync);
|
|
_gui->UseShadows(shadows);
|
|
_gui->SetWindowed(win);
|
|
_gui->SetUseSound(usesound);
|
|
_guithread = new ZThread::Thread(rgui);
|
|
return true;
|
|
}
|
|
else
|
|
logerror("GUI: incorrect settings!");
|
|
return false;
|
|
}
|
|
|
|
void PseuInstance::Run(void)
|
|
{
|
|
if(!_initialized)
|
|
return;
|
|
|
|
logdetail("PseuInstance: Initialized and running!");
|
|
|
|
if(GetGUI())
|
|
{
|
|
while(!GetGUI()->IsInitialized())
|
|
Sleep(1); // wait until the gui is ready. it will crash otherwise
|
|
logdebug("GUI: switching to startup display...");
|
|
GetGUI()->SetSceneState(SCENESTATE_GUISTART);
|
|
}
|
|
// TODO: as soon as username and password can be inputted into the gui, wait until it was set by user.
|
|
|
|
if(GetConf()->realmlist.empty() || GetConf()->realmport==0)
|
|
{
|
|
logcritical("Realmlist address not set, can't connect.");
|
|
SetError();
|
|
}
|
|
else
|
|
{
|
|
|
|
if(!GetConf()->enablegui || !(GetConf()->accname.empty() || GetConf()->accpass.empty()) )
|
|
{
|
|
logdebug("GUI not active or Login data pre-entered, skipping Login GUI");
|
|
CreateRealmSession();
|
|
}
|
|
else
|
|
{
|
|
GetGUI()->SetSceneState(SCENESTATE_LOGINSCREEN);
|
|
}
|
|
|
|
// this is the mainloop
|
|
while(!_stop)
|
|
{
|
|
Update();
|
|
if(_error)
|
|
_stop=true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// fastquit is defined if we clicked [X] (on windows)
|
|
// If softquit is set, do not terminate forcefully, but shut it down instead
|
|
if(_fastquit && !_conf->softquit)
|
|
{
|
|
log("Aborting Instance...");
|
|
return;
|
|
}
|
|
|
|
log("Shutting down instance...");
|
|
|
|
// if there was an error, better dont save, as the data might be damaged
|
|
if(!_error)
|
|
{
|
|
SaveAllCache();
|
|
//...
|
|
}
|
|
|
|
if(GetScripts()->ScriptExists("_onexit"))
|
|
{
|
|
CmdSet Set;
|
|
Set.arg[0] = DefScriptTools::toString(_error);
|
|
GetScripts()->RunScript("_onexit",&Set);
|
|
}
|
|
|
|
if(GetConf()->exitonerror == false && _error)
|
|
{
|
|
log("Exiting on error is disabled, PseuWoW is now IDLE");
|
|
log("-- Press enter to exit --");
|
|
char crap[100];
|
|
fgets(crap,sizeof(crap),stdin); // workaround, need to press enter 2x for now
|
|
}
|
|
|
|
}
|
|
|
|
void PseuInstance::Update()
|
|
{
|
|
// if the user typed anything into the console, process it before anything else.
|
|
// note that it can also be used for simulated cli commands sent by other threads, so it needs to be checked even if cli is disabled
|
|
ProcessCliQueue();
|
|
|
|
// delete sessions if they are no longer needed
|
|
if(_rsession && _rsession->MustDie())
|
|
{
|
|
delete _rsession;
|
|
_rsession = NULL;
|
|
}
|
|
|
|
if(_wsession && _wsession->MustDie())
|
|
{
|
|
delete _wsession;
|
|
_wsession = NULL;
|
|
}
|
|
|
|
if(_createws)
|
|
{
|
|
_createws = false;
|
|
if(_wsession)
|
|
delete _wsession;
|
|
_wsession = new WorldSession(this);
|
|
_wsession->Start();
|
|
}
|
|
|
|
if(_creaters)
|
|
{
|
|
_creaters = false;
|
|
if(_rsession)
|
|
delete _rsession;
|
|
ConnectToRealm();
|
|
}
|
|
|
|
// if we have no active sessions, we may reconnect, if no GUI is active for login
|
|
if((!_rsession) && (!_wsession) && GetConf()->reconnect && !_gui)
|
|
{
|
|
if(GetConf()->accname.empty() || GetConf()->accpass.empty())
|
|
{
|
|
logdev("Skipping reconnect, acc name or password not set");
|
|
}
|
|
else
|
|
{ // everything fine, we have all data
|
|
logdetail("Waiting %u ms before reconnecting.",GetConf()->reconnect);
|
|
for(uint32 t = 0; t < GetConf()->reconnect && !this->Stopped(); t+=100) Sleep(100);
|
|
this->Sleep(1000); // wait 1 sec before reconnecting
|
|
CreateRealmSession();
|
|
}
|
|
}
|
|
if((!_rsession) && (!_wsession) && _gui)
|
|
{
|
|
if(_gui->GetSceneState() != SCENESTATE_LOGINSCREEN)
|
|
{
|
|
logdetail("Disconnected, switching GUI back to Loginscreen.");
|
|
_gui->SetSceneState(SCENESTATE_LOGINSCREEN);
|
|
while(_gui && _gui->GetSceneState() != SCENESTATE_LOGINSCREEN) // .. and wait until scenestate is set
|
|
Sleep(1);
|
|
}
|
|
}
|
|
|
|
// update currently existing/active sessions
|
|
if(_rsession)
|
|
_rsession->Update();
|
|
if(_wsession)
|
|
try { _wsession->Update(); } catch (...)
|
|
{
|
|
logerror("Unhandled exception in WorldSession::Update()");
|
|
}
|
|
|
|
|
|
if(_rmcontrol)
|
|
{
|
|
_rmcontrol->Update();
|
|
if(_rmcontrol->MustDie())
|
|
{
|
|
delete _rmcontrol;
|
|
_rmcontrol = NULL;
|
|
}
|
|
}
|
|
|
|
GetScripts()->GetEventMgr()->Update();
|
|
|
|
this->Sleep(GetConf()->networksleeptime);
|
|
}
|
|
|
|
void PseuInstance::ProcessCliQueue(void)
|
|
{
|
|
std::string cmd;
|
|
while(_cliQueue.size())
|
|
{
|
|
cmd = _cliQueue.next();
|
|
try
|
|
{
|
|
GetScripts()->RunSingleLine(cmd);
|
|
}
|
|
catch(...)
|
|
{
|
|
logerror("Exception while executing CLI command: \"%s\"",cmd.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PseuInstance::AddCliCommand(std::string cmd)
|
|
{
|
|
_cliQueue.add(cmd);
|
|
}
|
|
|
|
void PseuInstance::SaveAllCache(void)
|
|
{
|
|
//...
|
|
if(GetWSession())
|
|
{
|
|
GetWSession()->plrNameCache.SaveToFile();
|
|
ItemProtoCache_WriteDataToCache(GetWSession());
|
|
CreatureTemplateCache_WriteDataToCache(GetWSession());
|
|
GOTemplateCache_WriteDataToCache(GetWSession());
|
|
//...
|
|
}
|
|
}
|
|
|
|
void PseuInstance::Sleep(uint32 msecs)
|
|
{
|
|
GetRunnable()->sleep(msecs);
|
|
}
|
|
|
|
void PseuInstance::DeleteGUI(void)
|
|
{
|
|
_gui = NULL;
|
|
delete _guithread; // since it was allocated with new
|
|
_guithread = NULL;
|
|
if(GetScripts()->ScriptExists("_onguiclose"))
|
|
AddCliCommand("_onguiclose"); // since this func is called from another thread, use threadsafe variant via CLI
|
|
|
|
// if console mode is disabled in windows, closing the gui needs to close the app
|
|
#if PLATFORM == PLATFORM_WIN32 && !defined(_CONSOLE)
|
|
this->Stop();
|
|
#endif
|
|
}
|
|
|
|
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() || !_rsession->SocketGood()) // something failed. it will be deleted in next Update() call
|
|
{
|
|
logerror("PseuInstance: Connecting to Realm failed!");
|
|
if(_gui)
|
|
_gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_CONN_FAILED);
|
|
return false;
|
|
}
|
|
|
|
_rsession->SendLogonChallenge();
|
|
return true;
|
|
}
|
|
|
|
void PseuInstance::WaitForCondition(InstanceConditions c, uint32 timeout /* = 0 */)
|
|
{
|
|
_mutex.acquire();
|
|
if(timeout)
|
|
_condition[c]->wait(timeout);
|
|
else
|
|
_condition[c]->wait();
|
|
_mutex.release();
|
|
}
|
|
|
|
PseuInstanceConf::PseuInstanceConf()
|
|
{
|
|
enablecli=false;
|
|
enablegui=false;
|
|
exitonerror=false;
|
|
debug=0;
|
|
rmcontrolport=0;
|
|
}
|
|
|
|
void PseuInstanceConf::ApplyFromVarSet(VarSet &v)
|
|
{
|
|
debug=atoi(v.Get("DEBUG").c_str());
|
|
realmlist=v.Get("REALMLIST");
|
|
accname=v.Get("ACCNAME");
|
|
accpass=v.Get("ACCPASS");
|
|
exitonerror=(bool)atoi(v.Get("EXITONERROR").c_str());
|
|
reconnect=atoi(v.Get("RECONNECT").c_str());
|
|
realmport=atoi(v.Get("REALMPORT").c_str());
|
|
clientversion_string=v.Get("CLIENTVERSION");
|
|
clientbuild=atoi(v.Get("CLIENTBUILD").c_str());
|
|
clientlang=v.Get("CLIENTLANGUAGE");
|
|
realmname=v.Get("REALMNAME");
|
|
charname=v.Get("CHARNAME");
|
|
networksleeptime=atoi(v.Get("NETWORKSLEEPTIME").c_str());
|
|
showopcodes=atoi(v.Get("SHOWOPCODES").c_str());
|
|
hidefreqopcodes=(bool)atoi(v.Get("HIDEFREQOPCODES").c_str());
|
|
hideDisabledOpcodes=(bool)atoi(v.Get("HIDEDISABLEDOPCODES").c_str());
|
|
enablecli=(bool)atoi(v.Get("ENABLECLI").c_str());
|
|
allowgamecmd=(bool)atoi(v.Get("ALLOWGAMECMD").c_str());
|
|
notifyping=(bool)atoi(v.Get("NOTIFYPING").c_str());
|
|
showmyopcodes=(bool)atoi(v.Get("SHOWMYOPCODES").c_str());
|
|
disablespellcheck=(bool)atoi(v.Get("DISABLESPELLCHECK").c_str());
|
|
enablegui=(bool)atoi(v.Get("ENABLEGUI").c_str());
|
|
rmcontrolport=atoi(v.Get("RMCONTROLPORT").c_str());
|
|
rmcontrolhost=v.Get("RMCONTROLHOST");
|
|
rmcontrolpass=v.Get("RMCONTROLPASS");
|
|
useMaps=(bool)atoi(v.Get("USEMAPS").c_str());
|
|
skipaddonchat=(bool)atoi(v.Get("SKIPADDONCHAT").c_str());
|
|
dumpPackets=(uint8)atoi(v.Get("DUMPPACKETS").c_str());
|
|
softquit=(bool)atoi(v.Get("SOFTQUIT").c_str());
|
|
dataLoaderThreads=atoi(v.Get("DATALOADERTHREADS").c_str());
|
|
|
|
// clientversion is a bit more complicated to add
|
|
{
|
|
std::string opt=clientversion_string + ".";
|
|
std::string num;
|
|
uint8 p=0;
|
|
for(uint8 i=0;i<opt.length();i++)
|
|
{
|
|
if(!isdigit(opt.at(i)))
|
|
{
|
|
clientversion[p]=(unsigned char)atoi(num.c_str());
|
|
num.clear();
|
|
p++;
|
|
if(p>2)
|
|
break;
|
|
continue;
|
|
}
|
|
num+=opt.at(i);
|
|
}
|
|
}
|
|
|
|
// GUI related
|
|
terrainsectors = atoi(v.Get("GUI::TERRAINSECTORS").c_str());
|
|
terrainrendersize = atoi(v.Get("GUI::TERRAINRENDERSIZE").c_str());
|
|
terrainupdatestep = atoi(v.Get("GUI::TERRAINUPDATESTEP").c_str());
|
|
farclip = atof(v.Get("GUI::FARCLIP").c_str());
|
|
fogfar = atof(v.Get("GUI::FOGFAR").c_str());
|
|
fognear = atof(v.Get("GUI::FOGNEAR").c_str());
|
|
fov = atof(v.Get("GUI::FOV").c_str());
|
|
masterSoundVolume = atof(v.Get("GUI::MASTERSOUNDVOLUME").c_str());
|
|
|
|
// cleanups, internal settings, etc.
|
|
log_setloglevel(debug);
|
|
log_setlogtime((bool)atoi(v.Get("LOGTIME").c_str()));
|
|
MemoryDataHolder::SetThreadCount(dataLoaderThreads);
|
|
}
|
|
|
|
|
|
|
|
|
|
PseuInstanceConf::~PseuInstanceConf()
|
|
{
|
|
//...
|
|
}
|
|
|
|
|
|
|
|
|