last changes before 2.1.3:

* added script to support /me-like emotes (scriptname is "me")
* changed the permission system: now a script cant be used as game command f no permission is explicitly set
* implemented threadsafe CLI queue to solve crashes with short-intervalled events that ran on 2 threads
* fixed name return at "getitemprotovalue" script
* iplemented DrawObject class and a Mgr for those objects; they will ease object drawing once implemented. the Mgr works under control of the GUI thread and is threadsafe.
* implemented auto-loading of SCP files if a name-tag is present somewhere in the file ("#dbname=...") and no explicit db name was passed to "loadscp" script.
* changed internal ObjMgr storage to std::map (instead of list) for faster access
* fixed call of "_enterworld" script
* fixed handling of MyCharacter(), which could cause crashes after newly changes
* fixed GetFileList() func in tools.cpp (this fixes also related "lgetfiles" script func). now it will only parse files, not directories. might still need some fixing for linux.
This commit is contained in:
False.Genesis 2007-08-01 19:09:17 +00:00
parent 15e24961ce
commit 3100f68a30
31 changed files with 1315 additions and 824 deletions

View File

@ -1,23 +1,29 @@
#permission=255
// PSEUWOW DEF_SCRIPT STARTUP FILE
LOG * DefScript StartUp [${@version_short}]...
// this is for debugging which scripts are loaded
// set to false to disable output
SET,LoadDebug true
// first, load all scripts in patch 'scripts' with extension .def
LOG *** DefScript StartUp [${@version_short}]...
// first, load all scripts in path 'scripts' with extension .def
SET,fcount ?{LGETFILES,scriptlist,def scripts}
LSORT scriptlist
LOGDETAIL * Loading ${fcount} scripts.
LOG *** Loading ${fcount} script files.
// iterate over all files and load them; if counter i is equal to the amount of files we are done.
SET,i 0
SET,errors 0
LOOP
IF ?{EQUAL,${i} ${fcount}}
EXITLOOP
ENDIF
SET,fn ./scripts/?{LINDEX,scriptlist ${i}}
LOG *** Loading script file [${fn}]
IF ?{AND,?{IsSet LoadDebug} ${LoadDebug}}
LOG * Loading script file [${fn}]
ENDIF
IF ?{NOT ?{LOADDEF ${fn}}}
LOGERROR Can't load script [${fn}]
LOGERROR * Error loading [${fn}]
ADD,errors 1
ENDIF
ADD,i 1
ENDLOOP
@ -28,12 +34,16 @@ UNSET i
UNSET fn
LDELETE scriptlist
IF ?{NOT ${errors}}
LOG *** All files loaded without errors.
ELSE
LOG *** All files loaded, ${errors} errors.
ENDIF
UNSET errors
// loads & applies the configuration
CONFIG
// set permissions for internal functions
INTERNAL_PERM
// Load some SCP files
LOADALLSCP

View File

@ -208,3 +208,22 @@ ELSE
YELL,{${@0}} ${@def}
RETURN true
ENDIF
//----------------------------------------------------------
#script=me
#permission=0
//----------------------------------------------------------
// Send text emote. example: "me feels lolo" will result in "Pseuwow feels lolo" at other clients
SET,lang ${@0}
SET,msg ${@def}
DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} faction}
DEFAULT,lang ${#DEFAULTLANG}
LOGDEBUG * Me ${msg} (in lang ${lang})
SENDCHATMESSAGE,8,${lang},{${msg}}
UNSET lang
UNSET msg

View File

@ -6,7 +6,6 @@
//--------------------------------------------------------
#script=append
#permission=255
//--------------------------------------------------------
// usage: append text to a string.
// args:
@ -53,13 +52,13 @@ LOADSCP,race data/scp/race.scp
// load extended databases if present.
EXLOADSCP,sound data/scp/sound.scp
EXLOADSCP,emote data/scp/emote.scp
EXLOADSCP,area data/scp/area.scp
LOG * SCP loaded.
//-------------------------------------------------
#script=string_is_command
#permission=255
//-------------------------------------------------
// purpose: detect if @def might be a server command.
@ -80,7 +79,6 @@ return false
//--------------------------------------------------------
#script=normalize_name
#permission=255
//--------------------------------------------------------
// uppercases first char, lowercases rest
set,name ?{uppercase ?{substr,1 ${@def}}}
@ -91,7 +89,6 @@ return ${name}
//---------------------------------------------------------
#script=globname
#permission=255
//---------------------------------------------------------
// purpose: returns the global name of a variable
// args: @def: var name, @0 (optional): name of the intended parent func
@ -107,7 +104,6 @@ return #${top}::${@def}
//---------------------------------------------------------
#script=getvar
#permission=255
//---------------------------------------------------------
// purpose: returns the value of a variable. if the variable hasnt been set return empty string.
set,top ${@caller}
@ -120,4 +116,26 @@ if ?{isset ${v}}
endif
return
//---------------------------------------------------------
#script=reverse
//---------------------------------------------------------
// purpose: reverse a srtring
// args: @def: string to reverse
// returns: reversed string
set,outstr
set,elems ?{lsplit,mylist ${@def}}
set,i ${elems}
loop
sub,i 1
if ?{equal,${i} -1}
exitloop
endif
append,outstr ?{lindex,mylist ${i}}
endloop
ldelete mylist
return ${outstr}

View File

@ -35,7 +35,6 @@ return ${t}
//------------------------------------------------
#script=toleet
#permission=255
//------------------------------------------------
// purpose: convert any text into leetspeak [1337sp34k]
// args: @def: the text to convert

View File

@ -39,6 +39,7 @@ LOGDETAIL * Dangerous variables removed.
//-----------------------------------------------
// Load all conf files from directory /conf/
// get all *.conf file names from /conf/ directory, store the names in list 'fl' and the amount in 'fcount'
LOG ** Configuring PseuWoW...
SET,fcount ?{LGETFILES,fl,conf conf}
// sort the list, load conf files alphabetically.
LSORT fl
@ -76,9 +77,14 @@ APPLYPERMISSIONS
// remove dangerous variables
CLEANUPVARS
// set permissions for internal functions
INTERNAL_PERM
// if its not set in the conf file, default it to "." (WoWEmu & MaNGOS style)
DEFAULT,#CMDCHAR .
LOG ** All Config done.
//--------------------------------------------------
#script=internal_perm

View File

@ -2,7 +2,6 @@
//--------------------------------------------
#script=lcontains
#permission=255
//--------------------------------------------
// return true if any list element matches @def, else return false.
// @0: list name, @def: search string
@ -18,7 +17,6 @@ endif
set,result false
loop
out eq:?{equal,${i} ?{llen ${l}}} i:${i} len:?{llen ${l}}
if ?{equal,${i} ?{llen ${l}}}
exitloop
endif

View File

@ -2,6 +2,7 @@
//------------------------------------------------
#script=gc
#permission=0
//------------------------------------------------
// example script to write into channel "generalchat"
// usage: "gc bla bla..."

View File

@ -1,6 +1,4 @@
#permission=255
// GETS EXECUTED IF A PLAYER TRIES TO GIVE PSEUWOW A COMMAND AND IS NOT ALLOWED TO
// this script gets executed if a player tries to give pseuwow a command but is not allowed to
// Arguments:
// ==========

View File

@ -1,5 +1,3 @@
#permission=255
// EXECUTED EVERYTIME A WHISPER IS BEEING RECIEVED
// comment out the following line if you dont need it

View File

@ -32,16 +32,11 @@ void CliRunnable::run(void)
if(in[0]=='!')
cur = &in[1];
else
{
try
{
out = cur.empty() ? in : (cur+" "+in);
_instance->GetScripts()->RunSingleLine(out);
}
catch (...)
{
printf("Exception while executing CLI command!\n");
}
_instance->AddCliCommand(out);
// add delay just if necessary
//ZThread::Thread::sleep(50);
}
}
}

View File

@ -146,7 +146,7 @@ bool VarSet::ReadVarsFromFile(std::string fn)
unsigned int pos=line.find("=");
if(pos!=std::string::npos)
{
std::string v=line.substr(0,pos);;
std::string v=line.substr(0,pos);
if(upper)
v=toUpper(v);

View File

@ -297,12 +297,22 @@ DefReturnResult DefScriptPackage::SCtarget(CmdSet& Set)
DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
{
DefReturnResult r;
if(Set.arg[0].empty() || Set.defaultarg.empty())
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
if(Set.defaultarg.empty()) // exit if no filename was given
return false;
std::string dbname = stringToLower(Set.arg[0]);
// TODO: remove db if loading was not successful
uint32 sections=((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).LoadFromFile((char*)Set.defaultarg.c_str());
uint32 sections = 0;
if(dbname.empty()) // if no db name was supplied, try to find it automatically in the file ("#dbname=...")
{
sections = dbmgr.AutoLoadFile((char*)Set.defaultarg.c_str());
if(!sections)
{
logerror("Tried to auto-load SCP file: \"%s\", but no name tag found. skipped.");
}
}
else
{
sections = dbmgr.GetDB(dbname).LoadFromFile((char*)Set.defaultarg.c_str());
if(sections)
{
logdetail("Loaded SCP: \"%s\" [%s] (%u sections)",dbname.c_str(),Set.defaultarg.c_str(),sections);
@ -311,8 +321,8 @@ DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
{
logerror("Failed to load SCP: \"%s\" [%s]",dbname.c_str(),Set.defaultarg.c_str());
}
r.ret=toString((uint64)sections);
return r;
}
return toString((uint64)sections);;
}
DefReturnResult DefScriptPackage::SCScpExists(CmdSet& Set)
@ -519,10 +529,10 @@ DefReturnResult DefScriptPackage::SCGetItemProtoValue(CmdSet& Set)
uint32 tmp=0;
if(t=="class") r.ret=toString(proto->Class);
else if(t=="subclass") r.ret=toString(proto->SubClass);
else if(t=="name1" || t=="name") proto->Name[0];
else if(t=="name2") proto->Name[1];
else if(t=="name3") proto->Name[2];
else if(t=="name4") proto->Name[3];
else if(t=="name1" || t=="name") r.ret=proto->Name[0];
else if(t=="name2") r.ret=proto->Name[1];
else if(t=="name3") r.ret=proto->Name[2];
else if(t=="name4") r.ret=proto->Name[3];
else if(t=="model" || t=="displayid") r.ret=toString(proto->DisplayInfoID);
else if(t=="quality") r.ret=toString(proto->Quality);
else if(t=="flags") r.ret=toString(proto->Flags);
@ -766,7 +776,7 @@ void DefScriptPackage::My_LoadUserPermissions(VarSet &vs)
void DefScriptPackage::My_Run(std::string line, std::string username)
{
uint8 scperm=255; // builtin functions that have no explicit req. permission level assigned should be secure from beeing called
int16 scperm = -1; // builtin functions that have no explicit req. permission level assigned should be secure from beeing called from ingame
uint8 usrperm=0; // users/players that have no explicit permission assigned should have minimal permission
for (std::map<std::string,unsigned char>::iterator i = my_usrPermissionMap.begin(); i != my_usrPermissionMap.end(); i++)
@ -778,9 +788,8 @@ void DefScriptPackage::My_Run(std::string line, std::string username)
}
DefXChgResult final;
if(usrperm < 255)
if(usrperm < 255 && line.find("?{")!=std::string::npos)
{
if(line.find("?{")!=std::string::npos)
logerror("WARNING: %s wanted to exec \"%s\"",username.c_str(),line.c_str());
final=ReplaceVars(line,NULL,0,false); // prevent execution of embedded scripts (= using return values) that could trigger dangerous stuff.
}
@ -799,6 +808,9 @@ void DefScriptPackage::My_Run(std::string line, std::string username)
}
}
if(scperm > 0) // skip "invisible" scripts (without any permission set) completely.
{
if(usrperm < scperm)
{
CmdSet Set;
@ -809,6 +821,8 @@ void DefScriptPackage::My_Run(std::string line, std::string username)
RunScript("_nopermission",&Set);
return;
}
}
Interpret(curSet);
}

View File

@ -0,0 +1,67 @@
#include "common.h"
#include "log.h"
#include "DrawObject.h"
#include "DrawObjMgr.h"
DrawObjMgr::DrawObjMgr()
{
DEBUG( logdebug("DrawObjMgr created") );
}
DrawObjMgr::~DrawObjMgr()
{
DEBUG( logdebug("~DrawObjMgr(), deleting %u DrawObjects...", _storage.size() ) );
for(DrawObjStorage::iterator i = _storage.begin(); i != _storage.end(); i++)
{
DEBUG( logdebug("del for guid "I64FMT, i->first) );
delete i->second; // this can be done safely, since the object ptrs are not accessed
}
}
void DrawObjMgr::Add(uint64 objguid, DrawObject *o)
{
_add.add(std::pair<uint64,DrawObject*>(objguid,o));
}
void DrawObjMgr::Delete(uint64 guid)
{
_del.add(guid);
}
void DrawObjMgr::Update(void)
{
ZThread::FastMutex mut;
// now for the threadsafe part: lock every thread except this one
// to prevent obj ptr corruption caused by other running threads
// TODO: lock only main thread (that should be the only one to delete objects anyway!)
mut.acquire();
// add objects waiting on the add queue to the real storage
while(_add.size())
{
std::pair<uint64,DrawObject*> p = _add.next();
_storage[p.first] = p.second;
}
// same for objects that should be deleted
while(_del.size())
{
uint64 guid = _del.next();
if(_storage.find(guid) != _storage.end())
{
DrawObject *o = _storage[guid];
_storage.erase(guid);
delete o;
}
}
// now draw everything
for(DrawObjStorage::iterator i = _storage.begin(); i != _storage.end(); i++)
{
i->second->Draw();
}
mut.release();
}

View File

@ -0,0 +1,27 @@
#ifndef DRAWOBJMGR_H
#define DRAWOBJMGR_H
#include <utility>
class DrawObject;
typedef std::map<uint64,DrawObject*> DrawObjStorage;
class DrawObjMgr
{
public:
DrawObjMgr();
~DrawObjMgr();
void Add(uint64,DrawObject*);
void Delete(uint64);
void Update(void); // Threadsafe! delete code must be called from here!
uint32 StorageSize(void) { return _storage.size(); }
private:
DrawObjStorage _storage;
ZThread::LockedQueue<uint64,ZThread::FastMutex> _del;
ZThread::LockedQueue<std::pair<uint64,DrawObject*>,ZThread::FastMutex > _add;
};
#endif

View File

@ -0,0 +1,21 @@
#include "common.h"
#include "PseuGui.h"
#include "DrawObject.h"
#include "PseuWoW.h"
DrawObject::DrawObject(irr::scene::ISceneManager *smgr, Object *obj)
{
_smgr = smgr;
_obj = obj;
DEBUG( logdebug("create DrawObject() this=%X obj=%X smgr=%X",this,_obj,_smgr) );
}
DrawObject::~DrawObject()
{
DEBUG( logdebug("~DrawObject() this=%X obj=%X smgr=%X",this,_obj,_smgr) );
}
void DrawObject::Draw(void)
{
}

View File

@ -0,0 +1,23 @@
#ifndef DRAWOBJECT_H
#define DRAWOBJECT_H
#include "common.h"
#include "irrlicht/irrlicht.h"
class Object;
class DrawObject
{
public:
DrawObject(irr::scene::ISceneManager*, Object*);
~DrawObject();
void Draw(void); // call only in threadsafe environment!! (ensure the obj ptr is still valid!)
// additionally, we dont use a GetObject() func - that would fuck things up if the object was already deleted.
private:
Object *_obj;
irr::scene::ISceneManager *_smgr;
};
#endif

View File

@ -1,5 +1,7 @@
#include "common.h"
#include "irrlicht/irrlicht.h"
#include "Object.h"
#include "DrawObject.h"
#include "PseuWoW.h"
#include "PseuGUI.h"
@ -55,12 +57,8 @@ PseuGUI::PseuGUI()
PseuGUI::~PseuGUI()
{
this->Shutdown();
}
void PseuGUI::SetInstance(PseuInstance *i)
{
_instance = i;
this->Cancel();
_instance->DeleteGUI(); // this makes the instance set its gui ptr to NULL
}
void PseuGUI::SetDriver(uint8 driverId)
@ -114,9 +112,9 @@ void PseuGUI::_Init(void)
_initialized = true;
}
void PseuGUI::Shutdown(void)
void PseuGUI::Cancel(void)
{
DEBUG(logdebug("PseuGUI::Shutdown()"));
DEBUG(logdebug("PseuGUI::Cancel()"));
_mustdie = true;
if(_device)
{
@ -125,6 +123,12 @@ void PseuGUI::Shutdown(void)
}
}
void PseuGUI::Shutdown(void)
{
DEBUG(logdebug("PseuGUI::Shutdown()"));
_mustdie = true;
}
void PseuGUI::Run(void)
{
if(!_initialized)
@ -140,11 +144,21 @@ void PseuGUI::Run(void)
{
_device->sleep(10); // save cpu & gpu power if not focused
}
try
{
_driver->beginScene(true, true, 0);
domgr.Update(); // iterate over DrawObjects, draw them and clean up
_smgr->drawAll();
_driver->endScene();
}
catch(...)
{
logerror("Unhandled exception in PseuGUI::Run() device=%X smgr=%X objects:%u", _device, _smgr, domgr.StorageSize());
}
fps = _driver->getFPS();
@ -163,7 +177,25 @@ void PseuGUI::Run(void)
}
DEBUG(logdebug("PseuGUI::Run() finished"));
Shutdown();
Cancel(); // already got shut down somehow, we can now safely cancel and drop the device
}
// called from ObjMgr::Remove(guid)
void PseuGUI::NotifyObjectDeletion(uint64 guid)
{
domgr.Delete(guid);
}
// called from ObjMgr::Add(Object*)
void PseuGUI::NotifyObjectCreation(Object *o)
{
DrawObject *d = new DrawObject(_smgr,o);
domgr.Add(o->GetGUID(),d);
}
void PseuGUI::SetInstance(PseuInstance* in)
{
_instance = in;
}

View File

@ -2,11 +2,12 @@
#define PSEUGUI_H
#include "irrlicht/irrlicht.h"
#include "DrawObjMgr.h"
class PseuGUI;
class Object;
class PseuInstance;
class PseuGUIRunnable : public ZThread::Runnable
{
public:
@ -25,26 +26,33 @@ public:
PseuGUI();
~PseuGUI();
void Run(void);
void SetInstance(PseuInstance*);
void Run(void);
void SetDriver(uint8);
void SetResolution(uint16 x, uint16 y, uint16 depth=32);
void SetWindowed(bool);
void SetVSync(bool);
void UseShadows(bool);
void Cancel(void);
void Shutdown(void);
inline bool MustDie(void) { return _mustdie; }
// interfaces to tell the gui what to draw
void NotifyObjectDeletion(uint64 guid);
void NotifyObjectCreation(Object *o);
private:
void _Init(void);
uint16 _xres,_yres,_colordepth;
bool _windowed,_vsync,_shadows;
bool _initialized,_mustdie;
PseuInstance *_instance;
irr::IrrlichtDevice *_device;
irr::video::IVideoDriver* _driver;
irr::scene::ISceneManager* _smgr;
irr::gui::IGUIEnvironment* _guienv;
irr::video::E_DRIVER_TYPE _driverType;
DrawObjMgr domgr;
PseuInstance *_instance;
};

View File

@ -33,7 +33,7 @@ void PseuInstanceRunnable::run(void)
}
else
{
getchar();
getchar(); // if init failed, wait for keypress before exit
}
delete _i;
}
@ -54,6 +54,8 @@ PseuInstance::PseuInstance(PseuInstanceRunnable *run)
_conf=NULL;
_cli=NULL;
_rmcontrol=NULL;
_gui=NULL;
_guithread=NULL;
_stop=false;
_fastquit=false;
_startrealm=true;
@ -61,7 +63,6 @@ PseuInstance::PseuInstance(PseuInstanceRunnable *run)
_error=false;
_initialized=false;
}
PseuInstance::~PseuInstance()
@ -72,6 +73,11 @@ PseuInstance::~PseuInstance()
// delete _cli; // ok this is a little mem leak... can be fixed sometime in future
}
if(_gui)
_gui->Shutdown();
if(_guithread)
_guithread->wait();
if(_rmcontrol)
delete _rmcontrol;
if(_rsession)
@ -94,7 +100,7 @@ bool PseuInstance::Init(void) {
if(_confdir.empty())
_confdir="./conf/";
if(_scpdir.empty())
_scpdir="./scp/";
_scpdir="./scripts/";
srand((unsigned)time(NULL));
RAND_set_rand_method(RAND_SSLeay()); // init openssl randomizer
@ -138,13 +144,13 @@ bool PseuInstance::Init(void) {
if(x>0 && y>0 && (depth==16 || depth==32) && driver>0 && driver<=5)
{
PseuGUIRunnable *rgui = new PseuGUIRunnable();
PseuGUI *gui = rgui->GetGUI();
gui->SetInstance(this);
gui->SetDriver(driver);
gui->SetResolution(x,y,depth);
gui->SetVSync(vsync);
gui->UseShadows(shadows);
ZThread::Thread *t = new ZThread::Thread(rgui);
_gui = rgui->GetGUI();
_gui->SetInstance(this);
_gui->SetDriver(driver);
_gui->SetResolution(x,y,depth);
_gui->SetVSync(vsync);
_gui->UseShadows(shadows);
_guithread = new ZThread::Thread(rgui);
}
else
logerror("GUI: incorrect settings!");
@ -232,6 +238,10 @@ void PseuInstance::Run(void)
void PseuInstance::Update()
{
// if the user typed anything into the console, process it before anything else
if(_cli) // need to to process only if cli exists
ProcessCliQueue();
// delete sessions if they are no longer needed
if(_rsession && _rsession->MustDie())
{
@ -290,6 +300,28 @@ void PseuInstance::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)
{
//...
@ -306,6 +338,12 @@ void PseuInstance::Sleep(uint32 msecs)
GetRunnable()->sleep(msecs);
}
void PseuInstance::DeleteGUI(void)
{
_gui = NULL;
_guithread = NULL;
}
PseuInstanceConf::PseuInstanceConf()
{
enablecli=false;

View File

@ -9,6 +9,7 @@
#include "DefScript/DefScript.h"
#include "Network/SocketHandler.h"
#include "SCPDatabase.h"
#include "GUI/PseuGUI.h"
class RealmSession;
class WorldSession;
@ -64,45 +65,51 @@ class PseuInstanceConf
class PseuInstance
{
public:
public:
PseuInstance(PseuInstanceRunnable *run);
~PseuInstance();
WorldSession *GetWSession(void) { return _wsession; }
RealmSession *GetRSession(void) { return _rsession; }
PseuInstanceConf *GetConf(void) { return _conf; }
DefScriptPackage *GetScripts(void) { return _scp; }
PseuInstanceRunnable *GetRunnable(void) { return _runnable; }
void SetConfDir(std::string dir) { _confdir = dir; }
std::string GetConfDir(void) { return _confdir; }
void SetScpDir(std::string dir) { _scpdir = dir; }
void SetSessionKey(BigNumber key) { _sessionkey = key; }
BigNumber GetSessionKey(void) { return _sessionkey; }
void SetError(void) { _error = true; }
inline WorldSession *GetWSession(void) { return _wsession; }
inline RealmSession *GetRSession(void) { return _rsession; }
inline PseuInstanceConf *GetConf(void) { return _conf; }
inline DefScriptPackage *GetScripts(void) { return _scp; }
inline PseuInstanceRunnable *GetRunnable(void) { return _runnable; }
inline PseuGUI *GetGUI(void) { return _gui; }
void DeleteGUI(void);
inline void SetConfDir(std::string dir) { _confdir = dir; }
inline std::string GetConfDir(void) { return _confdir; }
inline void SetScpDir(std::string dir) { _scpdir = dir; }
inline void SetSessionKey(BigNumber key) { _sessionkey = key; }
inline BigNumber GetSessionKey(void) { return _sessionkey; }
inline void SetError(void) { _error = true; }
SCPDatabaseMgr dbmgr;
bool Init();
void SaveAllCache(void);
void Stop(void) { _stop = true; }
bool Stopped(void) { return _stop; }
void SetFastQuit(bool q=true) { _fastquit=true; }
inline void Stop(void) { _stop = true; }
inline bool Stopped(void) { return _stop; }
inline void SetFastQuit(bool q=true) { _fastquit=true; }
void Quit(void);
void Run(void);
void Update(void);
void Sleep(uint32 msecs);
void CreateWorldSession(void) { _createws = true; }
inline void CreateWorldSession(void) { _createws = true; }
private:
void ProcessCliQueue(void);
void AddCliCommand(std::string);
private:
PseuInstanceRunnable *_runnable;
RealmSession *_rsession;
WorldSession *_wsession;
PseuInstanceConf *_conf;
DefScriptPackage *_scp;
std::string _confdir,_scpdir;
std::string _confdir,_scpdir; // _scpdir is the scipts dir, and NOT where SCP files are stored!!
bool _initialized;
bool _stop,_fastquit;
bool _startrealm;
@ -114,6 +121,9 @@ class PseuInstance
CliRunnable *_cli;
ZThread::Thread _clithread;
RemoteController *_rmcontrol;
ZThread::LockedQueue<std::string,ZThread::FastMutex> _cliQueue;
PseuGUI *_gui;
ZThread::Thread *_guithread;
};
@ -123,7 +133,7 @@ public:
PseuInstanceRunnable();
void run(void);
void sleep(uint32);
PseuInstance *GetInstance(void) { return _i; }
inline PseuInstance *GetInstance(void) { return _i; }
private:
PseuInstance *_i;

View File

@ -2,30 +2,48 @@
#include "common.h"
#include "SCPDatabase.h"
uint32 SCPDatabase::LoadFromFile(char *fn)
{
std::fstream fh;
std::string line,value,entry,storage;
uint32 id=0,sections=0;
char c;
uint32 size = GetFileSize(fn);
if(!size)
return 0;
fh.open(fn,std::ios_base::in);
fh.open(fn,std::ios_base::in | std::ios_base::binary);
if( !fh.is_open() )
return 0;
while(!fh.eof())
char *buf = new char[size];
fh.read(buf,size);
fh.close();
uint32 sections = LoadFromMem(buf,size);
delete [] buf;
return sections;
}
uint32 SCPDatabase::LoadFromMem(char *buf, uint32 size)
{
std::string line,value,entry,storage;
uint32 id=0,sections=0;
for(uint32 pos = 0; pos < size; pos++)
{
c=fh.get();
if(c=='\n' || fh.eof())
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[1]=='/')) )
if(line.empty() || (line.length() > 1 && (line[0]=='#' || (line[0]=='/' && line[1]=='/'))) )
{
line.clear();
continue;
}
if(line[line.size()-1] == 13) // 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 '['
@ -45,9 +63,8 @@ uint32 SCPDatabase::LoadFromFile(char *fn)
line.clear();
}
else
line+=c; // fill up line until a newline is reached (see above)
line += buf[pos]; // fill up line until a newline is reached (see above)
}
fh.close();
return sections;
}
@ -73,10 +90,7 @@ bool SCPField::HasEntry(std::string e)
std::string SCPField::GetString(std::string entry)
{
//return HasEntry(entry) ? _map[entry] : "";
if(HasEntry(entry))
return _map[entry];
else
return "";
}
// note that this can take a while depending on the size of the database!
@ -101,11 +115,64 @@ SCPDatabase& SCPDatabaseMgr::GetDB(std::string n)
return _map[n];
}
uint32 SCPDatabaseMgr::AutoLoadFile(char *fn)
{
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();
std::string line,dbname;
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[0] == '#')
{
uint32 eq = line.find("=");
if(eq != std::string::npos)
{
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;
}
}
}
}
else
line += buf[pos];
}
delete [] buf;
if(dbname.empty())
return 0;
uint32 sections = GetDB(dbname).LoadFromMem(buf,size);
return sections;
}
// -- helper functions -- //
std::string SCPDatabaseMgr::GetZoneName(uint32 id)
std::string SCPDatabaseMgr::GetAreaName(uint32 id)
{
return GetDB("zone").GetField(id).GetString("name");
return GetDB("area").GetField(id).GetString("name");
}
std::string SCPDatabaseMgr::GetRaceName(uint32 id)

View File

@ -3,21 +3,66 @@
#include <map>
struct SCPEntry
/*
* Some preparations for compiled databases. i will continue this later. [FG]
enum SCPTypes
{
std::string entry;
std::string value;
SCP_TYPE_STRING = 0,
SCP_TYPE_INT,
SCP_TYPE_UINT,
SCP_TYPE_FLOAT
};
class SCPEntry
{
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;
}
}
};
*/
typedef std::map<std::string,std::string> SCPEntryMap;
class SCPField
{
public:
std::string GetString(std::string);
uint64 GetInteger(std::string entry) { return toInt(GetString(entry)); }
double GetDouble(std::string entry) { return strtod(GetString(entry).c_str(),NULL); }
void Set(std::string entry,std::string value) { _map[entry]=value; }
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:
@ -30,9 +75,10 @@ typedef std::map<uint32,SCPField> SCPFieldMap;
class SCPDatabase
{
public:
SCPField& GetField(uint32 id) { return _map[id]; }
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);
private:
@ -47,11 +93,13 @@ class SCPDatabaseMgr
public:
bool HasDB(std::string);
SCPDatabase& GetDB(std::string);
uint32 AutoLoadFile(char *fn);
inline void DropDB(std::string s) { _map.erase(stringToLower(s)); }
//////////////////////
// helper functions //
//////////////////////
std::string GetZoneName(uint32 id);
std::string GetAreaName(uint32 id);
std::string GetRaceName(uint32 id);
std::string GetClassName_(uint32 id);
std::string GetGenderName(uint32 id);

View File

@ -1,46 +1,66 @@
#include "common.h"
#include "log.h"
#include "PseuWoW.h"
#include "ObjMgr.h"
#include "GUI/PseuGUI.h"
ObjMgr::ObjMgr()
{
DEBUG(logdebug("DEBUG: ObjMgr created"));
}
ObjMgr::~ObjMgr()
{
RemoveAll();
}
void ObjMgr::SetInstance(PseuInstance *i)
{
_instance = i;
DEBUG(logdebug("DEBUG: ObjMgr instance set to 0x%X",i));
}
void ObjMgr::RemoveAll(void)
{
for(ItemProtoList::iterator i = _iproto.begin(); i!=_iproto.end(); i++)
{
delete *i;
}
for(ObjectList::iterator i = _obj.begin(); i!=_obj.end(); i++)
while(_obj.size())
{
delete *i;
Remove(_obj.begin()->first);
}
}
void ObjMgr::Remove(uint64 guid)
{
for(ObjectList::iterator i = _obj.begin(); i!=_obj.end(); i++)
if((*i)->GetGUID() == guid)
Object *o = GetObj(guid);
if(o)
{
delete *i;
_obj.erase(i);
return;
PseuGUI *gui = _instance->GetGUI();
if(gui)
gui->NotifyObjectDeletion(guid); // we have a gui, which must delete linked DrawObject
_obj.erase(guid); // now delete the obj from the mgr
delete o; // and delete the obj itself
}
}
void ObjMgr::Add(Object *o)
{
_obj.push_back(o);
_obj[o->GetGUID()] = o;
PseuGUI *gui = _instance->GetGUI();
if(gui)
gui->NotifyObjectCreation(o);
}
Object *ObjMgr::GetObj(uint64 guid)
{
if(!guid)
return NULL;
for(ObjectList::iterator i = _obj.begin(); i!=_obj.end(); i++)
if((*i)->GetGUID() == guid)
return (*i);
for(ObjectMap::iterator i = _obj.begin(); i!=_obj.end(); i++)
if(i->second->GetGUID() == guid)
return i->second;
return NULL;
}

View File

@ -7,12 +7,16 @@
#include "Item.h"
typedef std::vector<ItemProto*> ItemProtoList;
typedef std::list<Object*> ObjectList;
typedef std::map<uint64,Object*> ObjectMap;
class PseuInstance;
class ObjMgr
{
public:
ObjMgr();
~ObjMgr();
void SetInstance(PseuInstance*);
void RemoveAll(void); // TODO: this needs to be called on SMSG_LOGOUT_COMPLETE once implemented.
// Item Prototype functions
@ -29,11 +33,13 @@ public:
void Add(Object*);
void Remove(uint64); // remove all objects with that guid (should be only 1 object in total anyway)
Object *GetObj(uint64 guid);
inline uint32 GetObjectCount(void) { return _obj.size(); }
private:
ItemProtoList _iproto;
ObjectList _obj;
ObjectMap _obj;
std::vector<uint32> _noitem;
PseuInstance *_instance;
};

View File

@ -23,9 +23,15 @@ void Player::Create(uint64 guid)
MyCharacter::MyCharacter() : Player()
{
DEBUG(logdebug("MyCharacter() constructor, this=0x%x",this));
SetTarget(0);
}
MyCharacter::~MyCharacter()
{
DEBUG(logdebug("~MyCharacter() destructor, this=0x%X guid="I64FMT,this,GetGUID())); // this _could_ crash if Player::Create(guid) wasnt called before!
}
void MyCharacter::SetActionButtons(WorldPacket &data)
{

View File

@ -200,6 +200,7 @@ class MyCharacter : public Player
{
public:
MyCharacter();
~MyCharacter();
void SetActionButtons(WorldPacket &data);
void AddSpell(uint32 spellid, uint16 spellslot);

View File

@ -64,8 +64,8 @@ void WorldSession::_HandleUpdateObjectOpcode(WorldPacket& recvPacket)
}
break;
case UPDATETYPE_CREATE_OBJECT:
case UPDATETYPE_CREATE_OBJECT2:
case UPDATETYPE_CREATE_OBJECT2: // will be sent when our very own character is created
case UPDATETYPE_CREATE_OBJECT: // will be sent on any other object creation
{
uguid = recvPacket.GetPackedGuid();
uint8 objtypeid;
@ -73,12 +73,22 @@ void WorldSession::_HandleUpdateObjectOpcode(WorldPacket& recvPacket)
logdebug("Create Object type %u with guid "I64FMT,objtypeid,uguid);
// dont create objects if already present in memory.
// recreate every object except ourself!
if( uguid != GetGuid() && objmgr.GetObj(uguid))
if(objmgr.GetObj(uguid))
{
if(uguid != GetGuid())
{
logdev("- already exists, deleting old , creating new object");
objmgr.Remove(uguid);
}
else
{
logdev("- already exists, but not deleted (has our current GUID)");
}
}
// only if the obj didnt exist or was just deleted above, create it....
if(!objmgr.GetObj(uguid))
{
switch(objtypeid)
{
case TYPEID_OBJECT: // no data to read
@ -135,16 +145,28 @@ void WorldSession::_HandleUpdateObjectOpcode(WorldPacket& recvPacket)
break;
}
}
}
else
{
logdebug("Obj "I64FMT" not created, already exists",uguid);
}
// ...regardless if it was freshly created or already present, update its values and stuff now...
this->_MovementUpdate(objtypeid, uguid, recvPacket);
this->_ValuesUpdate(uguid, recvPacket);
// ...and ask the server for eventually missing data.
_QueryObjectInfo(uguid);
// if our own character got finally created, we have successfully entered the world,
// and should have gotten all info about our char already.
// TODO: make called script function like "_enterworld"
//if(uguid==GetGuid())
// _OnCharCreate();
}
break;
case UPDATETYPE_OUT_OF_RANGE_OBJECTS:
{
recvPacket >> usize;
for(uint16 i=0;i<usize;i++)
{
@ -152,12 +174,15 @@ void WorldSession::_HandleUpdateObjectOpcode(WorldPacket& recvPacket)
logdebug("GUID "I64FMT" out of range",uguid);
objmgr.Remove(uguid);
}
}
break;
default:
{
logerror("UPDATE_OBJECT: Got unk updatetype 0x%X",utype);
logerror("UPDATE_OBJECT: Read %u / %u bytes, skipped rest",recvPacket.rpos(),recvPacket.size());
return;
}
} // switch
} // while
@ -303,7 +328,7 @@ void WorldSession::_ValuesUpdate(uint64 uguid, WorldPacket& recvPacket)
obj->SetUInt32Value(i, value);
//}
// still nee to find out which values to interpret as floats
// still need to find out which values to interpret as floats
logdev("-> Field[%u] = %u",i,value);
}
}

View File

@ -22,7 +22,7 @@ struct OpcodeHandler
WorldSession::WorldSession(PseuInstance *in)
{
logdebug("-> Starting WorldSession from instance 0x%X",in); // should never output a null ptr
logdebug("-> Starting WorldSession 0x%X from instance 0x%X",this,in); // should never output a null ptr
_instance = in;
_mustdie=false;
_logged=false;
@ -31,7 +31,10 @@ WorldSession::WorldSession(PseuInstance *in)
_channels = new Channel(this);
_world = NULL;
_sh.SetAutoCloseSockets(false);
objmgr.SetInstance(in);
//...
DEBUG(logdebug("WorldSession 0x%X constructor finished",this));
}
WorldSession::~WorldSession()
@ -50,6 +53,7 @@ WorldSession::~WorldSession()
delete _socket;
if(_world)
delete _world;
DEBUG(logdebug("~WorldSession() this=0x%X _instance=0x%X",this,_instance));
}
void WorldSession::Start(void)
@ -328,8 +332,9 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
GetInstance()->SetError();
return;
}
logdetail("Chars in list: %u\n",num);
_LoadCache(); // we are about to login, so we need cache data
logdetail("W: Chars in list: %u\n",num);
for(unsigned int i=0;i<num;i++){
recvPacket >> plr[i]._guid;
recvPacket >> plr[i]._name;
@ -363,13 +368,13 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
bool char_found=false;
for(unsigned int i=0;i<num;i++){
logcustom(0,LGREEN,"## %s (%u) [%s/%s] Map: %s; Zone: %s",
logcustom(0,LGREEN,"## %s (%u) [%s/%s] Map: %s; Area: %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());
GetDBMgr().GetAreaName(plr[i]._zoneId).c_str());
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);
for(unsigned int inv=0;inv<20;inv++)
@ -391,7 +396,10 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
return;
} else {
log("Entering World with Character \"%s\"...",GetInstance()->GetConf()->charname.c_str());
// create the character and add it to the objmgr.
// note: this is the only object that has to stay in memory unless its explicitly deleted by the server!
// that means even if the server sends create object with that guid, do NOT recreate it!!
MyCharacter *my = new MyCharacter();
my->Create(_myGUID);
objmgr.Add(my);
@ -408,12 +416,18 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
void WorldSession::_HandleSetProficiencyOpcode(WorldPacket& recvPacket)
{
_OnEnterWorld();
if(recvPacket.size())
{
DEBUG(
logdebug("SetProficiency: Hexdump:");
logdebug(toHexDump((uint8*)recvPacket.contents(),recvPacket.size(),true).c_str());
);
}
}
void WorldSession::_HandleAccountDataMD5Opcode(WorldPacket& recvPacket)
{
_OnEnterWorld();
// packet structure not yet known
}
void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
@ -438,7 +452,8 @@ void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
recvPacket >> target_guid;
std::string plrname;
if(target_guid){
if(target_guid)
{
plrname=plrNameCache.GetName(target_guid);
if(plrname.empty())
{
@ -488,7 +503,7 @@ void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
}
else if (type==CHAT_MSG_EMOTE )
{
logcustom(0,WHITE,"EMOTE: %s [%s]: %s",plrname.c_str(),ln,msg.c_str());
logcustom(0,WHITE,"EMOTE [%s]: %s %s",ln,plrname.c_str(),msg.c_str());
}
else
{
@ -560,12 +575,15 @@ void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
pos++;
}
id = atoi(itemid.c_str());
if(id)
{
logdebug("Found Item in chat message: %u",id);
if(objmgr.GetItemProto(id)==NULL)
SendQueryItem(id,0);
}
}
}
}
}
void WorldSession::_HandleNameQueryResponseOpcode(WorldPacket& recvPacket)
@ -658,12 +676,14 @@ void WorldSession::_HandleTelePortAckOpcode(WorldPacket& recvPacket)
void WorldSession::_HandleNewWorldOpcode(WorldPacket& recvPacket)
{
DEBUG(logdebug("DEBUG: _HandleNewWorldOpcode() objs:%u mychar: ptr=0x%X, guid="I64FMT,objmgr.GetObjectCount(),GetMyChar(),GetMyChar()->GetGUID()));
uint32 mapid;
float x,y,z,o;
// we assume we are NOT on a transport!
// else we had to do the following before:
// recvPacket >> tmapid >> tx >> ty >> tz >> to;
recvPacket >> mapid >> x >> y >> z >> o;
if(GetMyChar())
GetMyChar()->ClearSpells(); // will be resent by server
// TODO: clear action buttons
if(_world)
@ -857,6 +877,7 @@ void WorldSession::_HandleLoginVerifyWorldOpcode(WorldPacket& recvPacket)
recvPacket >> m >> x >> y >> z >> o;
// for now, init the world as soon as the server confirmed that we are where we are.
logdebug("LoginVerifyWorld: map=%u x=%f y=%f z=%f o=%f",m,x,y,z,o);
_OnEnterWorld();
if(_world)
delete _world;
_world = new World(this);

View File

@ -408,6 +408,18 @@
<Filter
Name="GUI"
Filter="">
<File
RelativePath=".\Client\Gui\DrawObject.cpp">
</File>
<File
RelativePath=".\Client\Gui\DrawObject.h">
</File>
<File
RelativePath=".\Client\Gui\DrawObjMgr.cpp">
</File>
<File
RelativePath=".\Client\Gui\DrawObjMgr.h">
</File>
<File
RelativePath=".\Client\Gui\PseuGUI.cpp">
</File>

View File

@ -74,12 +74,13 @@ public:
inline void SetTile(MapTile* tile, uint32 x, uint32 y) { SetTile(tile, y*64 + x); }
inline void SetTile(MapTile* tile, uint32 pos)
{
if(pos < 4096)
_tiles[pos] = tile;
}
inline void UnloadMapTile(uint32 x, uint32 y) { UnloadMapTile(y*64 + x); }
inline void UnloadMapTile(uint32 pos)
{
if(_tiles[pos])
if(pos < 4096 && _tiles[pos])
{
delete _tiles[pos];
_tiles[pos] = NULL;
@ -88,12 +89,12 @@ public:
inline bool TileExists(uint32 x, uint32 y) { return TileExists(y*64 + x); }
inline bool TileExists(uint32 pos)
{
return _hasTile[pos];
return pos < 4096 ? _hasTile[pos] : false;
}
inline MapTile *GetTile(uint32 x, uint32 y) { return GetTile(y*64 + x); }
inline MapTile *GetTile(uint32 pos)
{
return _tiles[pos];
return pos < 4096 ? _tiles[pos] : NULL;
}
void _DebugDump(void);

View File

@ -130,15 +130,17 @@ std::deque<std::string> GetFileList(std::string path)
HANDLE hFil=FindFirstFile(p,&fil);
if(hFil!=INVALID_HANDLE_VALUE)
{
if( !(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
files.push_back(std::string(fil.cFileName));
while(FindNextFile(hFil,&fil))
{
if( !(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
files.push_back(std::string(fil.cFileName));
}
}
# endif
while(files.size() && (files.front()=="." || files.front()==".."))
files.pop_front();
return files;
}