diff --git a/bin/data/scp/gui_charselect_text.scp b/bin/data/scp/gui_charselect_text.scp index 64329cc..d8e8ef5 100644 --- a/bin/data/scp/gui_charselect_text.scp +++ b/bin/data/scp/gui_charselect_text.scp @@ -15,3 +15,6 @@ 0=Realmlist 1=Create New Character +[3] +0=Creating new Character... + diff --git a/bin/scripts/database_loader.def b/bin/scripts/database_loader.def index daf8c81..120f9ff 100644 --- a/bin/scripts/database_loader.def +++ b/bin/scripts/database_loader.def @@ -36,6 +36,9 @@ LoadDB sound LoadDB gui_login_text LoadDB gui_charselect_text +// misc data +LoadDB generic_text + log ** Databases loaded. diff --git a/src/Client/GUI/Scene.cpp b/src/Client/GUI/Scene.cpp index 63bb92e..b641a4c 100644 --- a/src/Client/GUI/Scene.cpp +++ b/src/Client/GUI/Scene.cpp @@ -60,10 +60,11 @@ Scene::~Scene() DEBUG(logdebug("Scene::~Scene()")); } -core::stringw Scene::GetStringFromDB(u32 index, u32 entry) +core::stringw Scene::GetStringFromDB(u32 index, u32 entry, SCPDatabase *other_db /* = NULL */) { core::stringw r = ""; - if(!textdb) + SCPDatabase *db = other_db ? other_db : textdb; + if(!db) { r += L"GetString(index, buf); + r += db->GetString(index, buf); return r; } diff --git a/src/Client/GUI/Scene.h b/src/Client/GUI/Scene.h index d619408..9cca068 100644 --- a/src/Client/GUI/Scene.h +++ b/src/Client/GUI/Scene.h @@ -39,7 +39,7 @@ class Scene public: Scene(PseuGUI *g); ~Scene(); - core::stringw GetStringFromDB(u32 index, u32 entry); + core::stringw GetStringFromDB(u32 index, u32 entry, SCPDatabase *other_db = NULL); inline void SetState(SceneState sc) { _scenestate = sc; } inline SceneState GetState(void) { return _scenestate; } virtual void OnUpdate(s32); @@ -99,9 +99,12 @@ class SceneCharSelection : public Scene public: SceneCharSelection(PseuGUI *gui); void OnUpdate(s32); + void OnManualUpdate(void); void OnDelete(void); void OnResize(void); + void FillCharlist(void); + private: GUIEventReceiver *eventrecv; IGUIWindow *realmwin; @@ -112,6 +115,8 @@ private: IGUIComboBox *raceselect; IGUIComboBox *classselect; IGUIEditBox *charname; + IGUIElement *msgbox; // display status/result of character creation + uint32 msgbox_textid; // stores old textid std::map racemap, classmap; // maps DB IDs in db to IDs in the combobox, because irrlicht does not allow custom ids in comboboxes }; diff --git a/src/Client/GUI/SceneCharselection.cpp b/src/Client/GUI/SceneCharselection.cpp index e0d6ad0..d68b216 100644 --- a/src/Client/GUI/SceneCharselection.cpp +++ b/src/Client/GUI/SceneCharselection.cpp @@ -50,7 +50,22 @@ SceneCharSelection::SceneCharSelection(PseuGUI *gui) : Scene(gui) rect clb_rect = CalcRelativeScreenPos(driver, 0.65f, 0.12f, 0.34f, 0.67f); charlistbox = guienv->addListBox(clb_rect); - mutex.acquire(); + FillCharlist(); + + if(soundengine) + { + ISoundSource *main_theme = soundengine->getSoundSource("data/misc/main_theme.ogg"); + if(main_theme && !soundengine->isCurrentlyPlaying(main_theme)) + { + soundengine->play2D(main_theme,true); + } + } +} + +void SceneCharSelection::FillCharlist(void) +{ + ZThread::Guard g(mutex); + charlistbox->clear(); WorldSession *ws = instance->GetWSession(); if(ws) { @@ -75,29 +90,19 @@ SceneCharSelection::SceneCharSelection(PseuGUI *gui) : Scene(gui) charlistbox->addItem(entry.c_str()); uint32 faction = racedb->GetInt(c.p._race, ffaction); - + SColor col; switch(faction) { - case 1: col.set(0xFF, 0xFF, 0x30, 0x30); break; - case 7: col.set(0xFF, 0x30, 0x30, 0xFF); break; - default: col.set(0xFFFFFFFF); + case 1: col.set(0xFF, 0xFF, 0x30, 0x30); break; + case 7: col.set(0xFF, 0x30, 0x30, 0xFF); break; + default: col.set(0xFFFFFFFF); } charlistbox->setItemOverrideColor(i,EGUI_LBC_TEXT,col); charlistbox->setItemOverrideColor(i,EGUI_LBC_TEXT_HIGHLIGHT,col); } } - mutex.release(); - - if(soundengine) - { - ISoundSource *main_theme = soundengine->getSoundSource("data/misc/main_theme.ogg"); - if(main_theme && !soundengine->isCurrentlyPlaying(main_theme)) - { - soundengine->play2D(main_theme,true); - } - } } void SceneCharSelection::OnUpdate(s32 timepassed) @@ -182,10 +187,11 @@ void SceneCharSelection::OnUpdate(s32 timepassed) { guienv->addMessageBox(L"Not yet implemented!", L"Deleting a character does not yet work!"); } - if(eventrecv->buttons & BUTTON_NEW_CHARACTER) + if(eventrecv->buttons & BUTTON_NEW_CHARACTER && !newcharwin) { dimension2d dim; rect pos; + msgbox_textid = 0; newcharwin = guienv->addWindow(CalcRelativeScreenPos(driver, 0.2f, 0.2f, 0.6f, 0.6f), true, GetStringFromDB(ISCENE_CHARSEL_LABELS, DSCENE_CHARSEL_LABEL_NEWCHARWIN).c_str()); pos = newcharwin->getAbsolutePosition(); // get absolute position and transform to absolute in-window position @@ -213,7 +219,7 @@ void SceneCharSelection::OnUpdate(s32 timepassed) //newcharwin->addChild(classselect); guienv->addStaticText(L"Char Name", CalcRelativeScreenPos(dim,0.1f,0.3f,0.8f,0.05f),false,true,newcharwin); charname = guienv->addEditBox(L"", CalcRelativeScreenPos(dim,0.1f,0.35f,0.8f,0.05f),true, newcharwin); - //guienv->addMessageBox(L"Not yet implemented!", L"Creating a new character does not yet work!"); + msgbox = guienv->addStaticText(L"",CalcRelativeScreenPos(dim,0.2f,0.6f,0.6f,0.1f), true, true, newcharwin); } if(eventrecv->buttons & BUTTON_SELECT_REALM || scenedata[ISCENE_CHARSEL_REALMFIRST]) { @@ -299,21 +305,20 @@ void SceneCharSelection::OnUpdate(s32 timepassed) if(eventrecv->buttons & BUTTON_NEWCHARWIN_OK && newcharwin) { - core::stringc tmp=charname->getText(); + core::stringc chname = charname->getText(); u8 race = racemap[raceselect->getSelected()]; u8 cclass = classmap[classselect->getSelected()]; - log("Creating character Race %i Class %i Name %s",race,cclass,tmp.c_str()); - if(tmp.size() && race && cclass) + if(chname.size() && race && cclass) { WorldSession *ws=instance->GetWSession(); if(ws) { - WorldPacket packet(CMSG_CHAR_CREATE,(tmp.size()+1)+1+1+1+1+1+1+1+1+1); - packet<AddSendWorldPacket(packet); - eventrecv->buttons |= BUTTON_NEWCHARWIN_CANCEL; // easiest way to close the window without much additional code + ws->SendCharCreate(chname.c_str(), race, cclass); + + msgbox->setText(GetStringFromDB(3,0).c_str()); + msgbox_textid = 0; + + // do not close window until character created (will when getting response code 0) } else logerror("Trying to create new Character, but no WorldSession exists."); @@ -323,18 +328,31 @@ void SceneCharSelection::OnUpdate(s32 timepassed) } // realmlist window - if(eventrecv->buttons & BUTTON_REALMWIN_CANCEL && realmwin) { realmwin->remove(); - realmwin=NULL; + realmwin = NULL; } // new character window if(eventrecv->buttons & BUTTON_NEWCHARWIN_CANCEL && newcharwin) { newcharwin->remove(); - newcharwin=NULL; + newcharwin = NULL; + } + + if(newcharwin && msgbox_textid != scenedata[ISCENE_CHARSEL_ERRMSG]) + { + msgbox_textid = scenedata[ISCENE_CHARSEL_ERRMSG]; + if(SCPDatabase *generictext = instance->dbmgr.GetDB("generic_text")) + { + msgbox->setText(GetStringFromDB(0, msgbox_textid, generictext).c_str()); + } + if(scenedata[ISCENE_CHARSEL_ERRMSG] == CHAR_CREATE_SUCCESS) + { + newcharwin->remove(); + newcharwin = NULL; + } } eventrecv->buttons = 0; @@ -351,3 +369,10 @@ void SceneCharSelection::OnResize(void) } +// called when receiving SMSG_CHAR_ENUM +void SceneCharSelection::OnManualUpdate(void) +{ + Scene::OnManualUpdate(); + FillCharlist(); +} + diff --git a/src/Client/GUI/SceneData.h b/src/Client/GUI/SceneData.h index dd5dd57..8bfdd4b 100644 --- a/src/Client/GUI/SceneData.h +++ b/src/Client/GUI/SceneData.h @@ -49,6 +49,7 @@ enum SceneCharSelectDataIndexes { ISCENE_CHARSEL_BUTTONS = 1, // text ISCENE_CHARSEL_LABELS = 2, // text + ISCENE_CHARSEL_ERRMSG = 3, // uint32 response code, see enum ResponseCodes in SharedDefines.h for IDs ISCENE_CHARSEL_REALMFIRST = 255, // flag that is set when connecting to a realm wasnt possible and the realm list must be shown first ISCENE_CHARSEL_END }; @@ -72,6 +73,7 @@ enum SceneCharSelectLabels DSCENE_CHARSEL_LABEL_NEWCHARWIN = 1, }; + #endif diff --git a/src/Client/World/CMSGConstructor.cpp b/src/Client/World/CMSGConstructor.cpp index 0888ceb..cfa0445 100644 --- a/src/Client/World/CMSGConstructor.cpp +++ b/src/Client/World/CMSGConstructor.cpp @@ -207,5 +207,23 @@ void WorldSession::SendQueryGameobject(uint32 entry, uint64 guid) SendWorldPacket(wp); } +void WorldSession::SendCharCreate(std::string name, uint8 race, uint8 class_, // below here all values default is 0 + uint8 gender, uint8 skin, uint8 face, + uint8 hairstyle, uint8 haircolor, uint8 facial, uint8 outfit) +{ + log("Creating Character '%s', race=%u, class=%u gender=%u", name.c_str(), race, class_, gender); + WorldPacket wp(CMSG_CHAR_CREATE, name.length()+1 + 9); + wp << name; + wp << race; + wp << class_; + wp << gender; + wp << skin; + wp << face; + wp << hairstyle; + wp << haircolor; + wp << facial; + wp << outfit; + AddSendWorldPacket(wp); +} diff --git a/src/Client/World/CacheHandler.cpp b/src/Client/World/CacheHandler.cpp index 263f0e5..42afc0a 100644 --- a/src/Client/World/CacheHandler.cpp +++ b/src/Client/World/CacheHandler.cpp @@ -17,58 +17,44 @@ uint32 GOTEMPLATES_CACHE_VERSION = 0; PlayerNameCache::~PlayerNameCache() { - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) - { - delete *i; - } } -bool PlayerNameCache::AddInfo(uint64 guid, std::string name){ - PlayerNameCacheItem *cacheItem=new PlayerNameCacheItem; - cacheItem->_name=name; - cacheItem->_guid=guid; - return AddInfo(cacheItem); +void PlayerNameCache::Add(uint64 guid, std::string name) +{ + _cache.erase(guid); // drop old data if present + _cache[guid] = name; } bool PlayerNameCache::IsKnown(uint64 guid) { - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) - if(guid==(*i)->_guid) - return true; + PlayerNameMap::iterator it = _cache.find(guid); + if(it != _cache.end()) + return true; return false; } -bool PlayerNameCache::AddInfo(PlayerNameCacheItem* cacheItem) -{ - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) - if(cacheItem->_guid==(*i)->_guid) - { - delete cacheItem; - return false; - } - _cache.push_back(cacheItem); - return true; -} - std::string PlayerNameCache::GetName(uint64 guid) { - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) - if(guid==(*i)->_guid) - return (*i)->_name; + PlayerNameMap::iterator it = _cache.find(guid); + if(it != _cache.end()) + return it->second; return ""; } uint64 PlayerNameCache::GetGuid(std::string name) { - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) - if(name==(*i)->_name) - return (*i)->_guid; + for(PlayerNameMap::iterator it = _cache.begin(); it != _cache.end(); it++) + if(it->second == name) + return it->first; return 0; } bool PlayerNameCache::SaveToFile(void) { - log("Saving PlayerNameCache..."); + if(_cache.empty()) + return true; // no data to save, so we are fine + + logdebug("Saving PlayerNameCache..."); char *fn="./cache/playernames.cache"; std::fstream fh; fh.open(fn, std::ios_base::out | std::ios_base::binary); @@ -77,22 +63,23 @@ bool PlayerNameCache::SaveToFile(void) logerror("PlayerNameCache: Could not write to file '%s'!",fn); return false; } - uint32 size=_cache.size(); - if(size==0) + uint32 size = _cache.size(); + if(!size) return false; - uint8 len; - fh.write((char*)&size,sizeof(uint32)); - for(std::vector::iterator i=_cache.begin(); i!=_cache.end(); i++) + fh.write((char*)&size,sizeof(uint32)); + ByteBuffer bb; + + bb << (uint32)_cache.size(); + for(PlayerNameMap::iterator i=_cache.begin(); i!=_cache.end(); i++) { - fh.write( (char*)&((*i)->_guid),sizeof(uint64) ); - len=(*i)->_name.length(); - fh.write( (char*)&len,sizeof(uint8) ); - fh.write( (char*)(*i)->_name.c_str(),len ); - DEBUG(log( "PlayerNameCache << " I64FMT " -> %s", (*i)->_guid, (*i)->_name.c_str());); + bb << i->first; + bb << (uint8)i->second.length(); + bb.append(i->second.c_str(), i->second.length()); // do not append '\0' } + fh.write((char*)bb.contents(), bb.size()); fh.close(); - log("PlayerNameCache saved successfully."); + logdebug("PlayerNameCache saved successfully."); return true; } @@ -102,49 +89,45 @@ bool PlayerNameCache::ReadFromFile(void) log("Loading PlayerNameCache..."); bool success=true; std::fstream fh; - fh.open(fn, std::ios_base::in | std::ios_base::binary); - if(!fh.is_open()) + uint32 size = GetFileSize(fn); + if(!size) { logerror("PlayerNameCache: Could not open file '%s'!",fn); return false; } - if(fh.eof()) - { - logdetail("PlayerNameCache: Can't load empty file '%s'",fn); - return false; - } - uint32 size; - fh.read((char*)&size,sizeof(uint32)); - std::string tmp; + + // do NOT use MemoryDataHolder, since the file can change during runtime and may be loaded again + fh.open(fn, std::ios_base::in | std::ios_base::binary); + ByteBuffer bb; + bb.resize(size); + fh.read((char*)bb.contents(), size); + fh.close(); + uint8 len; - char *nameptr=new char[13]; - for(unsigned int i=0;i> count; // entries count + + for(uint32 i = 0; i < count; i++) { - len=255; - memset(nameptr,0,13); - PlayerNameCacheItem *cacheItem=new PlayerNameCacheItem; - fh.read((char*)&(cacheItem->_guid),sizeof(uint64)); - fh.read((char*)&len,sizeof(uint8)); - if(len > MAX_PLAYERNAME_LENGTH || len < MIN_PLAYERNAME_LENGTH) + bb >> guid; + bb >> len; + if(len > MAX_PLAYERNAME_LENGTH || len < MIN_PLAYERNAME_LENGTH || !guid) { logerror("PlayerNameCache data seem corrupt [namelength=%d, should be <=%u]",len,MAX_PLAYERNAME_LENGTH); log("-> Clearing cache, creating new."); _cache.clear(); - success=false; + success = false; break; } - fh.read(nameptr,len); - cacheItem->_name=nameptr; - AddInfo(cacheItem); - printf("\rPlayerNameCache [ %u / %u ] items loaded",i+1,size); - DEBUG(printf( " >> " I64FMT " -> %s\n", cacheItem->_guid, nameptr);); + memset(namebuf,0,MAX_PLAYERNAME_LENGTH + 1); + bb.read((uint8*)namebuf, len); + Add(guid, namebuf); } - printf("\n"); - delete nameptr; - fh.flush(); - fh.close(); if(success) - log("PlayerNameCache successfully loaded."); + logdebug("PlayerNameCache successfully loaded."); return success; } @@ -215,7 +198,7 @@ void ItemProtoCache_InsertDataToSession(WorldSession *session) buf >> proto->Stackable; buf >> proto->ContainerSlots; buf >> proto->StatsCount; - for(int i = 0; i < proto->StatsCount; i++) + for(uint32 i = 0; i < proto->StatsCount; i++) { buf >> proto->ItemStat[i].ItemStatType; buf >> proto->ItemStat[i].ItemStatValue; @@ -349,7 +332,7 @@ void ItemProtoCache_WriteDataToCache(WorldSession *session) buf << proto->Stackable; buf << proto->ContainerSlots; buf << proto->StatsCount; - for(int i = 0; i < proto->StatsCount; i++) + for(uint32 i = 0; i < proto->StatsCount; i++) { buf << proto->ItemStat[i].ItemStatType; buf << proto->ItemStat[i].ItemStatValue; diff --git a/src/Client/World/CacheHandler.h b/src/Client/World/CacheHandler.h index 3ae5e9c..d872868 100644 --- a/src/Client/World/CacheHandler.h +++ b/src/Client/World/CacheHandler.h @@ -1,25 +1,22 @@ #ifndef _CACHEHANDLER_H #define _CACHEHANDLER_H -struct PlayerNameCacheItem { - uint64 _guid; - std::string _name; -}; +typedef std::map PlayerNameMap; -class PlayerNameCache { +class PlayerNameCache +{ public: ~PlayerNameCache(); std::string GetName(uint64); bool IsKnown(uint64); uint64 GetGuid(std::string); - bool AddInfo(uint64 guid, std::string name); - bool AddInfo(PlayerNameCacheItem*); + void Add(uint64 guid, std::string name); bool SaveToFile(void); bool ReadFromFile(void); uint32 GetSize(void); private: - std::vector _cache; + PlayerNameMap _cache; }; void ItemProtoCache_InsertDataToSession(WorldSession *session); diff --git a/src/Client/World/Item.cpp b/src/Client/World/Item.cpp index e602b9f..2c94d56 100644 --- a/src/Client/World/Item.cpp +++ b/src/Client/World/Item.cpp @@ -40,7 +40,7 @@ void WorldSession::_HandleItemQuerySingleResponseOpcode(WorldPacket& recvPacket) recvPacket >> proto->Stackable; recvPacket >> proto->ContainerSlots; recvPacket >> proto->StatsCount; - for(int i = 0; i < proto->StatsCount; i++) + for(uint32 i = 0; i < proto->StatsCount; i++) { recvPacket >> proto->ItemStat[i].ItemStatType; recvPacket >> proto->ItemStat[i].ItemStatValue; diff --git a/src/Client/World/SharedDefines.h b/src/Client/World/SharedDefines.h index 00ecb2b..8001264 100644 --- a/src/Client/World/SharedDefines.h +++ b/src/Client/World/SharedDefines.h @@ -1388,6 +1388,113 @@ enum SpellCastTargetFlags TARGET_FLAG_UNK2 = 0x00010000 // pguid }; +enum ResponseCodes +{ + RESPONSE_SUCCESS = 0x00, + RESPONSE_FAILURE = 0x01, + RESPONSE_CANCELLED = 0x02, + RESPONSE_DISCONNECTED = 0x03, + RESPONSE_FAILED_TO_CONNECT = 0x04, + RESPONSE_CONNECTED = 0x05, + RESPONSE_VERSION_MISMATCH = 0x06, + + CSTATUS_CONNECTING = 0x07, + CSTATUS_NEGOTIATING_SECURITY = 0x08, + CSTATUS_NEGOTIATION_COMPLETE = 0x09, + CSTATUS_NEGOTIATION_FAILED = 0x0A, + CSTATUS_AUTHENTICATING = 0x0B, + + AUTH_OK = 0x0C, + AUTH_FAILED = 0x0D, + AUTH_REJECT = 0x0E, + AUTH_BAD_SERVER_PROOF = 0x0F, + AUTH_UNAVAILABLE = 0x10, + AUTH_SYSTEM_ERROR = 0x11, + AUTH_BILLING_ERROR = 0x12, + AUTH_BILLING_EXPIRED = 0x13, + AUTH_VERSION_MISMATCH = 0x14, + AUTH_UNKNOWN_ACCOUNT = 0x15, + AUTH_INCORRECT_PASSWORD = 0x16, + AUTH_SESSION_EXPIRED = 0x17, + AUTH_SERVER_SHUTTING_DOWN = 0x18, + AUTH_ALREADY_LOGGING_IN = 0x19, + AUTH_LOGIN_SERVER_NOT_FOUND = 0x1A, + AUTH_WAIT_QUEUE = 0x1B, + AUTH_BANNED = 0x1C, + AUTH_ALREADY_ONLINE = 0x1D, + AUTH_NO_TIME = 0x1E, + AUTH_DB_BUSY = 0x1F, + AUTH_SUSPENDED = 0x20, + AUTH_PARENTAL_CONTROL = 0x21, + AUTH_LOCKED_ENFORCED = 0x22, + + REALM_LIST_IN_PROGRESS = 0x23, + REALM_LIST_SUCCESS = 0x24, + REALM_LIST_FAILED = 0x25, + REALM_LIST_INVALID = 0x26, + REALM_LIST_REALM_NOT_FOUND = 0x27, + + ACCOUNT_CREATE_IN_PROGRESS = 0x28, + ACCOUNT_CREATE_SUCCESS = 0x29, + ACCOUNT_CREATE_FAILED = 0x2A, + + CHAR_LIST_RETRIEVING = 0x2B, + CHAR_LIST_RETRIEVED = 0x2C, + CHAR_LIST_FAILED = 0x2D, + + CHAR_CREATE_IN_PROGRESS = 0x2E, + CHAR_CREATE_SUCCESS = 0x2F, + CHAR_CREATE_ERROR = 0x30, + CHAR_CREATE_FAILED = 0x31, + CHAR_CREATE_NAME_IN_USE = 0x32, + CHAR_CREATE_DISABLED = 0x33, + CHAR_CREATE_PVP_TEAMS_VIOLATION = 0x34, + CHAR_CREATE_SERVER_LIMIT = 0x35, + CHAR_CREATE_ACCOUNT_LIMIT = 0x36, + CHAR_CREATE_SERVER_QUEUE = 0x37, + CHAR_CREATE_ONLY_EXISTING = 0x38, + CHAR_CREATE_EXPANSION = 0x39, + CHAR_CREATE_EXPANSION_CLASS = 0x3A, + CHAR_CREATE_LEVEL_REQUIREMENT = 0x3B, + CHAR_CREATE_UNIQUE_CLASS_LIMIT = 0x3C, + + CHAR_DELETE_IN_PROGRESS = 0x3D, + CHAR_DELETE_SUCCESS = 0x3E, + CHAR_DELETE_FAILED = 0x3F, + CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x40, + CHAR_DELETE_FAILED_GUILD_LEADER = 0x41, + CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x42, + + CHAR_LOGIN_IN_PROGRESS = 0x43, + CHAR_LOGIN_SUCCESS = 0x44, + CHAR_LOGIN_NO_WORLD = 0x45, + CHAR_LOGIN_DUPLICATE_CHARACTER = 0x46, + CHAR_LOGIN_NO_INSTANCES = 0x47, + CHAR_LOGIN_FAILED = 0x48, + CHAR_LOGIN_DISABLED = 0x49, + CHAR_LOGIN_NO_CHARACTER = 0x4A, + CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x4B, + CHAR_LOGIN_LOCKED_BY_BILLING = 0x4C, + + CHAR_NAME_SUCCESS = 0x4D, + CHAR_NAME_FAILURE = 0x4E, + CHAR_NAME_NO_NAME = 0x4F, + CHAR_NAME_TOO_SHORT = 0x50, + CHAR_NAME_TOO_LONG = 0x51, + CHAR_NAME_INVALID_CHARACTER = 0x52, + CHAR_NAME_MIXED_LANGUAGES = 0x53, + CHAR_NAME_PROFANE = 0x54, + CHAR_NAME_RESERVED = 0x55, + CHAR_NAME_INVALID_APOSTROPHE = 0x56, + CHAR_NAME_MULTIPLE_APOSTROPHES = 0x57, + CHAR_NAME_THREE_CONSECUTIVE = 0x58, + CHAR_NAME_INVALID_SPACE = 0x59, + CHAR_NAME_CONSECUTIVE_SPACES = 0x5A, + CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 0x5B, + CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x5C, + CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5D +}; + #define MAX_PLAYERNAME_LENGTH 12 #define MIN_PLAYERNAME_LENGTH 2 diff --git a/src/Client/World/WorldSession.cpp b/src/Client/World/WorldSession.cpp index d0aaa36..6f3b8b4 100644 --- a/src/Client/World/WorldSession.cpp +++ b/src/Client/World/WorldSession.cpp @@ -347,6 +347,7 @@ OpcodeHandler *WorldSession::_GetOpcodeHandlerTable() const {SMSG_WHO, &WorldSession::_HandleWhoOpcode}, {SMSG_CREATURE_QUERY_RESPONSE, &WorldSession::_HandleCreatureQueryResponseOpcode}, {SMSG_GAMEOBJECT_QUERY_RESPONSE, &WorldSession::_HandleGameobjectQueryResponseOpcode}, + {SMSG_CHAR_CREATE, &WorldSession::_HandleCharCreateOpcode}, // table termination { 0, NULL } @@ -556,11 +557,11 @@ void WorldSession::_HandleAuthResponseOpcode(WorldPacket& recvPacket) recvPacket >> dummy32 >> dummy8 >> dummy32; recvPacket >> expansion; - if(errcode == 0xC) + // TODO: add data to generic_text.scp and use the strings here + if(errcode == AUTH_OK) { logdetail("World Authentication successful, preparing for char list request..."); - WorldPacket pkt; - pkt.SetOpcode(CMSG_CHAR_ENUM); + WorldPacket pkt(CMSG_CHAR_ENUM, 0); SendWorldPacket(pkt); } else @@ -591,7 +592,6 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket) else { logdetail("Chars in list: %u",num); - _LoadCache(); // we are about to login, so we need cache data // TODO: load cache on loadingscreen for(unsigned int i=0;i> plr[i]._items[inv].displayId >> plr[i]._items[inv].inventorytype >> dummy32; } - plrNameCache.AddInfo(plr[i]._guid, plr[i]._name); // TODO: set after loadingscreen, after loading cache + plrNameCache.Add(plr[i]._guid, plr[i]._name); // TODO: set after loadingscreen, after loading cache } char_found=false; @@ -747,6 +747,7 @@ void WorldSession::EnterWorldWithCharacter(std::string name) void WorldSession::PreloadDataBeforeEnterWorld(PlayerEnum& pl) { log("Loading data before entering world..."); + _LoadCache(); // we are about to login, so we need cache data GetWorld()->GetMapMgr()->Update(pl._x, pl._y, pl._mapId); // make it load the map files // preload additional map data only when the GUI is enabled @@ -1023,12 +1024,8 @@ void WorldSession::_HandleNameQueryResponseOpcode(WorldPacket& recvPacket) if(pname.length()>MAX_PLAYERNAME_LENGTH || pname.length()> unk; if(unk==1) { + // TODO: Implement this!! SendChatMessage(CHAT_MSG_SAY,0,"It has no sense trying to trade with me, that feature is not yet implemented!",""); WorldPacket pkt; pkt.SetOpcode(CMSG_CANCEL_TRADE); @@ -1679,6 +1677,35 @@ void WorldSession::_HandleGameobjectQueryResponseOpcode(WorldPacket& recvPacket) objmgr.AssignNameToObj(entry, TYPEID_GAMEOBJECT, go->name); } +void WorldSession::_HandleCharCreateOpcode(WorldPacket& recvPacket) +{ + uint8 response; + recvPacket >> response; + if(response == CHAR_CREATE_SUCCESS) + { + log("Character created successfully."); + WorldPacket pkt(CMSG_CHAR_ENUM, 0); + SendWorldPacket(pkt); + logdebug("Requested new CMSG_CHAR_ENUM"); + } + else + { + logerror("Character creation error, response=%u", response); + } + if(SCPDatabase *db = GetInstance()->dbmgr.GetDB("generic_text")) + { + // convert response number to field name (simple int to string) + char buf[20]; + sprintf(buf,"%u",response); + std::string response_str = db->GetString(0, buf); // data are expected to be at index 0 + log("Response String: '%s'",response_str.c_str()); + } + + if(PseuGUI *gui = GetInstance()->GetGUI()) + gui->SetSceneData(ISCENE_CHARSEL_ERRMSG, response); +} + + // TODO: delete world on LogoutComplete once implemented diff --git a/src/Client/World/WorldSession.h b/src/Client/World/WorldSession.h index 4556ba2..6a57fb2 100644 --- a/src/Client/World/WorldSession.h +++ b/src/Client/World/WorldSession.h @@ -104,6 +104,7 @@ public: void SendWhoListRequest(uint32 minlvl=0, uint32 maxlvl=100, uint32 racemask=-1, uint32 classmask=-1, std::string name="", std::string guildname="", std::vector *zonelist=NULL, std::vector *strlist=NULL); void SendQueryCreature(uint32 entry, uint64 guid = 0); void SendQueryGameobject(uint32 entry, uint64 guid = 0); + void SendCharCreate(std::string name, uint8 race, uint8 class_, uint8 gender=0, uint8 skin=0, uint8 face=0, uint8 hairstyle=0, uint8 haircolor=0, uint8 facial=0, uint8 outfit=0); void HandleWorldPacket(WorldPacket*); @@ -161,6 +162,7 @@ private: void _HandleWhoOpcode(WorldPacket& recvPacket); void _HandleCreatureQueryResponseOpcode(WorldPacket& recvPacket); void _HandleGameobjectQueryResponseOpcode(WorldPacket& recvPacket); + void _HandleCharCreateOpcode(WorldPacket& recvPacket); // helper functions to keep SMSG_(COMPRESSED_)UPDATE_OBJECT easy to handle void _MovementUpdate(uint8 objtypeid, uint64 guid, WorldPacket& recvPacket); // Helper for _HandleUpdateObjectOpcode