diff --git a/src/client/ClientServices.cpp b/src/client/ClientServices.cpp index fdffbdb..1754ffc 100644 --- a/src/client/ClientServices.cpp +++ b/src/client/ClientServices.cpp @@ -248,6 +248,11 @@ void ClientServices::CharacterDelete(uint64_t guid) { ClientServices::s_currentConnection->DeleteCharacter(guid); } +void ClientServices::RequestCharacterCreate(const CHARACTER_CREATE_INFO* info) { + STORM_ASSERT(ClientServices::s_currentConnection); + ClientServices::s_currentConnection->CharacterCreate(info); +} + void ClientServices::Initialize() { if (!g_clientConnection) { ClientServices::s_clientRealmResponse = NEW(ClientRealmResponseAdapter); @@ -427,6 +432,16 @@ int32_t ClientServices::GetExpansionLevel() { return ClientServices::Connection()->GetExpansionLevel(); } +uint32_t ClientServices::CharacterValidateName(const char* name) { + // WORKAROUND: + if (!name || *name == '\0') { + return CHAR_NAME_RESULT_START + NAME_NO_NAME; + } + + // TODO + return CHAR_NAME_SUCCESS; +} + void ClientServices::InitLoginServerCVars(int32_t overwrite, const char* locale) { if ((ClientServices::s_realmListBNVar == nullptr || ClientServices::s_realmListVar == nullptr) || overwrite != 0 ) { ClientServices::s_decorateAccountName = CVar::Register( diff --git a/src/client/ClientServices.hpp b/src/client/ClientServices.hpp index 0016d0a..efb2d3e 100644 --- a/src/client/ClientServices.hpp +++ b/src/client/ClientServices.hpp @@ -46,6 +46,7 @@ class ClientServices : public LoginResponse { static const char* GetSelectedRealmName(); static const REALM_INFO* GetSelectedRealm(); static void CharacterDelete(uint64_t guid); + static void RequestCharacterCreate(const CHARACTER_CREATE_INFO* info); static void Initialize(); static Login* LoginConnection(); static void Logon(const char* accountName, const char* password); @@ -57,6 +58,7 @@ class ClientServices : public LoginResponse { static const char* GetDefaultPatchListString(); static bool LoadCDKey(); static int32_t GetExpansionLevel(); + static uint32_t CharacterValidateName(const char* name); // Virtual member functions virtual int32_t GetLoginServerType(); diff --git a/src/glue/CCharacterComponent.cpp b/src/glue/CCharacterComponent.cpp index b494cac..45c0023 100644 --- a/src/glue/CCharacterComponent.cpp +++ b/src/glue/CCharacterComponent.cpp @@ -40,7 +40,7 @@ ComponentData::ComponentData(const CHARACTER_INFO& info) { this->m_info.faceID = info.faceID; this->m_info.hairStyleID = info.hairStyleID; this->m_info.hairColorID = info.hairColorID; - this->m_info.facialFeatureID = info.facialHairStyleID; + this->m_info.facialHairStyleID = info.facialHairStyleID; DefaultGeosets(); } @@ -210,7 +210,7 @@ void CCharacterComponent::GeosRenderPrep(int32_t a2) { } } -void CCharacterComponent::GetInfo(CHARACTER_CREATE_INFO* info) { +void CCharacterComponent::GetInfo(CHARACTER_PREFERENCES* info) { if (info) { *info = this->m_data.m_info; } diff --git a/src/glue/CCharacterComponent.hpp b/src/glue/CCharacterComponent.hpp index d7c6873..3f6a6a7 100644 --- a/src/glue/CCharacterComponent.hpp +++ b/src/glue/CCharacterComponent.hpp @@ -17,7 +17,7 @@ class ComponentData { void DefaultGeosets(); public: - CHARACTER_CREATE_INFO m_info; + CHARACTER_PREFERENCES m_info; CM2Model* m_model; uint32_t m_unkFlag; uint32_t m_geosets[19]; @@ -60,7 +60,7 @@ class CCharacterComponent { bool Init(ComponentData* data, const char* a3); bool RenderPrep(int32_t a2); void GeosRenderPrep(int32_t a2); - void GetInfo(CHARACTER_CREATE_INFO* info); + void GetInfo(CHARACTER_PREFERENCES* info); public: uint32_t m_handle; diff --git a/src/glue/CCharacterCreation.cpp b/src/glue/CCharacterCreation.cpp index b275e23..4b97063 100644 --- a/src/glue/CCharacterCreation.cpp +++ b/src/glue/CCharacterCreation.cpp @@ -1,15 +1,17 @@ #include "glue/CCharacterCreation.hpp" #include "glue/CCharacterComponent.hpp" #include "glue/CCharacterSelection.hpp" +#include "glue/CGlueMgr.hpp" #include "ui/CSimpleModelFFX.hpp" #include "model/CM2Model.hpp" #include "model/CM2Shared.hpp" #include "clientobject/Player_C.hpp" +#include "client/ClientServices.hpp" #include "db/Db.hpp" int32_t CCharacterCreation::m_selectedClassID; int32_t CCharacterCreation::m_existingCharacterIndex; -CHARACTER_CREATE_INFO* CCharacterCreation::m_charPreferences[44]; +CHARACTER_PREFERENCES* CCharacterCreation::m_charPreferences[44]; int32_t CCharacterCreation::m_raceIndex; CSimpleModelFFX* CCharacterCreation::m_charCustomizeFrame; float CCharacterCreation::m_charFacing; @@ -225,7 +227,7 @@ void CCharacterCreation::InitCharacterComponent(ComponentData* data, int32_t ran CCharacterCreation::m_prevFaceIndex = info.faceID; CCharacterCreation::m_prevHairColorIndex = info.hairColorID; CCharacterCreation::m_prevHairStyleIndex = info.hairStyleID; - CCharacterCreation::m_prevFacialFeatureIndex = info.facialFeatureID; + CCharacterCreation::m_prevFacialFeatureIndex = info.facialHairStyleID; CCharacterCreation::SetCharFacing(CCharacterCreation::m_charFacing); CCharacterCreation::Dress(); @@ -251,7 +253,7 @@ void CCharacterCreation::RandomizeCharFeatures() { CCharacterCreation::m_prevFaceIndex = info.faceID; CCharacterCreation::m_prevHairColorIndex = info.hairColorID; CCharacterCreation::m_prevHairStyleIndex = info.hairStyleID; - CCharacterCreation::m_prevFacialFeatureIndex = info.facialFeatureID; + CCharacterCreation::m_prevFacialFeatureIndex = info.facialHairStyleID; } void CCharacterCreation::SetSelectedRace(int32_t raceIndex) { @@ -266,7 +268,7 @@ void CCharacterCreation::SetSelectedRace(int32_t raceIndex) { auto preferences = CCharacterCreation::m_charPreferences[2 * previousRace + sexID]; if (!preferences) { - preferences = NEW(CHARACTER_CREATE_INFO); + preferences = NEW(CHARACTER_PREFERENCES); CCharacterCreation::m_charPreferences[2 * previousRace + sexID] = preferences; } @@ -285,7 +287,7 @@ void CCharacterCreation::SetSelectedRace(int32_t raceIndex) { data.m_info.skinID = display->m_characterInfo.skinID; data.m_info.hairStyleID = display->m_characterInfo.hairStyleID; data.m_info.hairColorID = display->m_characterInfo.hairColorID; - data.m_info.facialFeatureID = display->m_characterInfo.facialHairStyleID; + data.m_info.facialHairStyleID = display->m_characterInfo.facialHairStyleID; data.m_info.faceID = display->m_characterInfo.faceID; CCharacterCreation::InitCharacterComponent(&data, 0); @@ -340,7 +342,7 @@ void CCharacterCreation::SetSelectedSex(int32_t sexID) { auto preferences = CCharacterCreation::m_charPreferences[2 * raceID + previousSex]; if (!preferences) { - preferences = NEW(CHARACTER_CREATE_INFO); + preferences = NEW(CHARACTER_PREFERENCES); CCharacterCreation::m_charPreferences[2 * raceID + previousSex] = preferences; } @@ -361,7 +363,7 @@ void CCharacterCreation::SetSelectedSex(int32_t sexID) { data.m_info.skinID = display->m_characterInfo.skinID; data.m_info.hairStyleID = display->m_characterInfo.hairStyleID; data.m_info.hairColorID = display->m_characterInfo.hairColorID; - data.m_info.facialFeatureID = display->m_characterInfo.facialHairStyleID; + data.m_info.facialHairStyleID = display->m_characterInfo.facialHairStyleID; data.m_info.faceID = display->m_characterInfo.faceID; CCharacterCreation::InitCharacterComponent(&data, 0); @@ -424,7 +426,52 @@ void CCharacterCreation::SetCharFacing(float facing) { } void CCharacterCreation::CreateCharacter(const char* name) { - // TODO + uint64_t guid = 0; + CharacterSelectionDisplay* display = nullptr; + + auto index = CCharacterCreation::m_existingCharacterIndex; + if (index >= 0) { + display = CCharacterSelection::GetCharacterDisplay(index); + if (!display) { + return; + } + + guid = display->m_characterInfo.guid; + + if (display->m_characterInfo.name) { + if (SStrCmpI(name, display->m_characterInfo.name, STORM_MAX_STR)) { + name = display->m_characterInfo.name; + } + } + } + + auto validationResult = ClientServices::CharacterValidateName(name); + if (validationResult != CHAR_NAME_SUCCESS) { + auto token = ClientServices::GetErrorToken(validationResult); + auto text = FrameScript_GetText(token, -1, GENDER_NOT_APPLICABLE); + FrameScript_SignalEvent(3, "%s%s", "OKAY", text); + return; + } + + const auto& info = CCharacterCreation::m_character->m_data.m_info; + + CHARACTER_CREATE_INFO character; + SStrCopy(character.name, name, sizeof(character.name)); + character.raceID = info.raceID; + character.classID = CCharacterCreation::m_selectedClassID; + character.sexID = info.sexID; + character.skinID = info.skinID; + character.hairColorID = info.hairColorID; + character.hairStyleID = info.hairStyleID; + character.facialHairStyleID = info.facialHairStyleID; + character.faceID = info.faceID; + character.outfitID = 0; + + if (guid && display) { + // TODO + } else { + CGlueMgr::CreateCharacter(&character); + } } void CCharacterCreation::SetToExistingCharacter(uint32_t index) { diff --git a/src/glue/CCharacterCreation.hpp b/src/glue/CCharacterCreation.hpp index a9e322a..e566f09 100644 --- a/src/glue/CCharacterCreation.hpp +++ b/src/glue/CCharacterCreation.hpp @@ -26,7 +26,7 @@ class CCharacterCreation { // Static variables static int32_t m_selectedClassID; static int32_t m_existingCharacterIndex; - static CHARACTER_CREATE_INFO* m_charPreferences[44]; + static CHARACTER_PREFERENCES* m_charPreferences[44]; static int32_t m_raceIndex; static CSimpleModelFFX* m_charCustomizeFrame; static float m_charFacing; diff --git a/src/glue/CGlueMgr.cpp b/src/glue/CGlueMgr.cpp index 7124548..345ae21 100644 --- a/src/glue/CGlueMgr.cpp +++ b/src/glue/CGlueMgr.cpp @@ -1092,6 +1092,20 @@ void CGlueMgr::DeleteCharacter(uint64_t guid) { ClientServices::CharacterDelete(guid); } +void CGlueMgr::CreateCharacter(const CHARACTER_CREATE_INFO* character) { + if (!character) { + return; + } + + CGlueMgr::m_idleState = IDLE_CREATE_CHARACTER; + CGlueMgr::m_showedDisconnect = 0; + + auto errorText = ClientServices::GetErrorToken(46); + auto text = FrameScript_GetText(errorText, -1, GENDER_NOT_APPLICABLE); + FrameScript_SignalEvent(3u, "%s%s", "CANCEL", text); + ClientServices::RequestCharacterCreate(character); +} + void CGlueMgr::PollEnterWorld() { //if (!LoadingScreenDrawing()) // return; diff --git a/src/glue/CGlueMgr.hpp b/src/glue/CGlueMgr.hpp index f4d3cd3..5926b8a 100644 --- a/src/glue/CGlueMgr.hpp +++ b/src/glue/CGlueMgr.hpp @@ -104,6 +104,7 @@ class CGlueMgr { static void UpdateCurrentScreen(const char* screen); static bool HandleBattlenetDisconnect(); static void DeleteCharacter(uint64_t guid); + static void CreateCharacter(const CHARACTER_CREATE_INFO* character); static void PollEnterWorld(); diff --git a/src/net/Types.hpp b/src/net/Types.hpp index bc4fecb..6c44866 100644 --- a/src/net/Types.hpp +++ b/src/net/Types.hpp @@ -1218,6 +1218,34 @@ enum WOWCS_OPS { COP_WAIT_QUEUE = 10, }; +enum VALIDATE_NAME_RESULT { + NAME_SUCCESS = 0, + NAME_FAILURE, + NAME_NO_NAME, + NAME_TOO_SHORT, + NAME_TOO_LONG, + NAME_INVALID_CHARACTER, + NAME_MIXED_LANGUAGES, + NAME_PROFANE, + NAME_RESERVED, + NAME_INVALID_APOSTROPHE, + NAME_MULTIPLE_APOSTROPHES, + NAME_THREE_CONSECUTIVE, + NAME_INVALID_SPACE, + NAME_CONSECUTIVE_SPACES, + NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS, + NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END, + NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, + NUM_NAME_RESULTS, +}; + +enum CHAR_NAME_RESULT { + CHAR_NAME_RESULT_START = 87, + CHAR_NAME_SUCCESS = 87, + CHAR_NAME_NO_NAME = 89, + LAST_CHAR_NAME_RESULT = 103, +}; + struct LoginData { char m_account[1280]; int32_t m_loginServerID; @@ -1286,17 +1314,30 @@ struct CHARACTER_INFO { uint8_t firstLogin; }; -struct CHARACTER_CREATE_INFO { +struct CHARACTER_PREFERENCES { uint32_t raceID; uint32_t sexID; uint32_t classID; uint32_t hairColorID; uint32_t skinID; uint32_t faceID; - uint32_t facialFeatureID; + uint32_t facialHairStyleID; uint32_t hairStyleID; }; +struct CHARACTER_CREATE_INFO { + char name[48]; + uint8_t raceID; + uint8_t classID; + uint8_t sexID; + uint8_t skinID; + uint8_t faceID; + uint8_t hairStyleID; + uint8_t hairColorID; + uint8_t facialHairStyleID; + uint8_t outfitID; +}; + struct CLIENT_NETSTATS { uint32_t bytesSent; uint32_t messagesSent; diff --git a/src/net/connection/ClientConnection.cpp b/src/net/connection/ClientConnection.cpp index 6ac0b53..a9683ed 100644 --- a/src/net/connection/ClientConnection.cpp +++ b/src/net/connection/ClientConnection.cpp @@ -65,6 +65,28 @@ void ClientConnection::DeleteCharacter(uint64_t guid) { } } +void ClientConnection::CharacterCreate(const CHARACTER_CREATE_INFO* info) { + this->Initiate(COP_CREATE_CHARACTER, 46, nullptr); + if (this->m_connected) { + CDataStore msg; + msg.Put(static_cast(CMSG_CREATE_CHARACTER)); + msg.PutString(info->name); + msg.Put(info->raceID); + msg.Put(info->classID); + msg.Put(info->sexID); + msg.Put(info->skinID); + msg.Put(info->faceID); + msg.Put(info->hairStyleID); + msg.Put(info->hairColorID); + msg.Put(info->facialHairStyleID); + msg.Put(info->outfitID); + msg.Finalize(); + this->Send(&msg); + } else { + this->Cancel(4); + } +} + void ClientConnection::Cancel(int32_t errorCode) { this->Complete(0, errorCode); } diff --git a/src/net/connection/ClientConnection.hpp b/src/net/connection/ClientConnection.hpp index bb88880..5d988f6 100644 --- a/src/net/connection/ClientConnection.hpp +++ b/src/net/connection/ClientConnection.hpp @@ -31,6 +31,7 @@ class ClientConnection : public RealmConnection { void EnumerateCharacters(ENUMERATE_CHARACTERS_CALLBACK fcn, void* param); void CharacterLogin(uint64_t id); void DeleteCharacter(uint64_t guid); + void CharacterCreate(const CHARACTER_CREATE_INFO* info); void Cancel(int32_t errorCode); void Cleanup(); void Connect(); diff --git a/src/net/connection/RealmConnection.cpp b/src/net/connection/RealmConnection.cpp index de2d81b..c29d22c 100644 --- a/src/net/connection/RealmConnection.cpp +++ b/src/net/connection/RealmConnection.cpp @@ -20,7 +20,7 @@ int32_t RealmConnection::MessageHandler(void* param, NETMESSAGE msgId, uint32_t } case SMSG_CREATE_CHAR: { - // TODO + result = connection->HandleCharacterCreate(msgId, time, msg); break; } @@ -279,6 +279,13 @@ int32_t RealmConnection::HandleCharacterDelete(uint32_t msgId, uint32_t time, CD return 1; } +int32_t RealmConnection::HandleCharacterCreate(uint32_t msgId, uint32_t time, CDataStore* msg) { + uint8_t result; + msg->Get(result); + static_cast(this)->Complete(1, result); + return 1; +} + void RealmConnection::SetSelectedRealm(uint32_t a2, uint32_t a3, uint32_t a4) { // TODO } diff --git a/src/net/connection/RealmConnection.hpp b/src/net/connection/RealmConnection.hpp index 1fd3d06..2d323e0 100644 --- a/src/net/connection/RealmConnection.hpp +++ b/src/net/connection/RealmConnection.hpp @@ -42,6 +42,7 @@ class RealmConnection : public NetClient { int32_t HandleAuthResponse(uint32_t msgId, uint32_t time, CDataStore* msg); int32_t HandleCharEnum(uint32_t msgId, uint32_t time, CDataStore* msg); int32_t HandleCharacterDelete(uint32_t msgId, uint32_t time, CDataStore* msg); + int32_t HandleCharacterCreate(uint32_t msgId, uint32_t time, CDataStore* msg); void SetSelectedRealm(uint32_t a2, uint32_t a3, uint32_t a4); void RequestCharacterEnum(); void RequestCharacterLogin(uint64_t id);