HkRename in version 1.6.0
-
I think there is a problem in the rename function in the latest version of hook. The problem is that the rename of the character in slot 5 of a player’s account will fail. At least it did for me on three different machines. I would appreciate it if some one could independently verify this.
Looking at the code I think the rename fails because the “new” character is created before removing the old character from the Player database and if the old character is in the last slot CreateNewCharacter fails because the account is full. I don’t understand why the CreateNewCharacter works when renaming a player in another slot, e.g. the account has 5 characters and renaming character 3 works but renaming character 5 fails.
I’ve restructured the rename function to delete the old character before calling CreateNewCharacter and it seems to work for all cases that I could think of. These changes have been running on the server for a little while so I think (hope) they work okay.
I also have modified the code to re-encode the character file so that ISFO can read the character file with out needing the player to launch or dock. I have applied a similar change to the HkAddCash function.
If possible (assuming these changes work) I would like them to find their way into the next official release of flhook.
HK_ERROR HkRename(wstring wscCharname, wstring wscNewCharname, bool bOnlyDelete) { HK_GET_CLIENTID(iClientID, wscCharname); if((iClientID == -1) && !HkGetAccountByCharname(wscCharname)) return HKE_CHAR_DOES_NOT_EXIST; if(!bOnlyDelete && HkGetAccountByCharname(wscNewCharname)) return HKE_CHARNAME_ALREADY_EXISTS; if(!bOnlyDelete && (wscNewCharname.length() > 23)) return HKE_CHARNAME_TOO_LONG; if(!bOnlyDelete && !wscNewCharname.length()) return HKE_CHARNAME_TOO_SHORT; CAccount *acc; wstring wscOldCharname; if(iClientID != -1) { acc = Players.FindAccountFromClientID(iClientID); wscOldCharname = Players.GetActiveCharacterName(iClientID); } else { wscOldCharname = wscCharname; acc = HkGetAccountByCharname(wscCharname); } wstring wscAccountDirname; HkGetAccountDirName(acc, wscAccountDirname); wstring wscNewFilename; HkGetCharFileName(wscNewCharname, wscNewFilename); wstring wscOldFilename; HkGetCharFileName(wscOldCharname, wscOldFilename); string scNewCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscNewFilename) + ".fl"; string scOldCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscOldFilename) + ".fl"; if(bOnlyDelete) { // delete character flstr *str = CreateWString(wscOldCharname.c_str()); HkLockAccountAccess(acc, true); // also kicks player on this account Players.DeleteCharacterFromName(*str); HkUnlockAccountAccess(acc); FreeWString(str); return HKE_OK; } HkLockAccountAccess(acc, true); // kick player if online HkUnlockAccountAccess(acc); // Copy existing char file into tmp string scTmpPath = scOldCharfilePath+".tmp"; DeleteFile(scTmpPath.c_str()); CopyFile(scOldCharfilePath.c_str(), scTmpPath.c_str(), FALSE); // Delete existing char otherwise a rename of the char in slot 5 fails. flstr *str = CreateWString(wscOldCharname.c_str()); Players.DeleteCharacterFromName(*str); FreeWString(str); // Emulate char create SLoginInfo logindata; wcsncpy(logindata.wszAccount, HkGetAccountID(acc).c_str(), 36); Players.login(logindata, Players.GetMaxPlayerCount()+1); SCreateCharacterInfo newcharinfo; wcsncpy(newcharinfo.wszCharname, wscNewCharname.c_str(), 23); // Fill struct with valid data (though it isnt used it is needed) newcharinfo.iDunno[0] = 2338219209; newcharinfo.iDunno[1] = 2804956685; newcharinfo.iDunno[2] = 2151746432; newcharinfo.iDunno[3] = 3089407751; newcharinfo.iDunno[4] = 65536; newcharinfo.iDunno[5] = 65538; newcharinfo.iDunno[6] = 0; newcharinfo.iDunno[7] = 1058642330; newcharinfo.iDunno[8] = 3206125978; newcharinfo.iDunno[9] = 65537; newcharinfo.iDunno[10] = 0; newcharinfo.iDunno[11] = 3206125978; newcharinfo.iDunno[12] = 65539; newcharinfo.iDunno[13] = 65540; newcharinfo.iDunno[14] = 65536; newcharinfo.iDunno[15] = 65538; Server.CreateNewCharacter(newcharinfo, Players.GetMaxPlayerCount()+1); HkSaveChar(wscNewCharname); Players.logout(Players.GetMaxPlayerCount()+1); // Decode the backup of the old char and overwrite the new char file if(!flc_decode(scTmpPath.c_str(), scNewCharfilePath.c_str())) { // file wasn't encoded, thus simply rename it DeleteFile(scNewCharfilePath.c_str()); // just to get sure... CopyFile(scTmpPath.c_str(), scNewCharfilePath.c_str(), FALSE); } DeleteFile(scTmpPath.c_str()); // Update the char name in the new char file. // Add a space to the value so the ini file line looks like " <key>= <value>" // otherwise IFSO can't decode the file correctly string scValue = " "; for(uint i = 0; (i < wscNewCharname.length()); i++) { char cHiByte = wscNewCharname[i] >> 8; char cLoByte = wscNewCharname[i] & 0xFF; char szBuf[8]; sprintf(szBuf, "%02X%02X", ((uint)cHiByte) & 0xFF, ((uint)cLoByte) & 0xFF); scValue += szBuf; } IniWrite(scNewCharfilePath, "Player", "Name", scValue); // Re-encode the char file if needed. if (!set_bDisableCharfileEncryption) if (!flc_encode(scNewCharfilePath.c_str(),scNewCharfilePath.c_str())) return HKE_COULD_NOT_ENCODE_CHARFILE; return HKE_OK; } [code] HK_ERROR HkAddCash(wstring wscCharname, int iAmount) { HK_GET_CLIENTID(iClientID, wscCharname); uint iClientIDAcc = 0; if(iClientID == -1) { CAccount *acc = HkGetAccountByCharname(wscCharname); if(!acc) return HKE_CHAR_DOES_NOT_EXIST; iClientIDAcc = HkGetClientIdFromAccount(acc); } else iClientIDAcc = iClientID; if((iClientID != -1) && bIdString && HkIsInCharSelectMenu(iClientID)) return HKE_NO_CHAR_SELECTED; else if((iClientID != -1) && !HkIsInCharSelectMenu(iClientID)) { // player logged in pub::Player::AdjustCash(iClientID, iAmount); return HKE_OK; } else { // player not logged in wstring wscDir; if(!HKHKSUCCESS(HkGetAccountDirName(wscCharname, wscDir))) return HKE_CHAR_DOES_NOT_EXIST; wstring wscFile; HkGetCharFileName(wscCharname, wscFile); string scCharFile = scAcctPath + wstos(wscDir) + "\\" + wstos(wscFile) + ".fl"; int iRet; if(HkIsEncoded(scCharFile)) { string scCharFileNew = scCharFile + ".ini"; if(!flc_decode(scCharFile.c_str(), scCharFileNew.c_str())) return HKE_COULD_NOT_DECODE_CHARFILE; iRet = IniGetI(scCharFileNew, "Player", "money", -1); // Add a space to the value so the ini file line looks like " <key>= <value>" // otherwise IFSO can't decode the file correctly IniWrite(scCharFileNew, "Player", "money", " "+itos(iRet + iAmount)); if (!set_bDisableCharfileEncryption) if(!flc_encode(scCharFileNew.c_str(), scCharFile.c_str())) return HKE_COULD_NOT_ENCODE_CHARFILE; DeleteFile(scCharFileNew.c_str()); } else { iRet = IniGetI(scCharFile, "Player", "money", -1); // Add a space to the value so the ini file line looks like " <key>= <value>" // otherwise IFSO can't decode the file correctly IniWrite(scCharFile, "Player", "money", " "+itos(iRet + iAmount)); } if(HkIsInCharSelectMenu(wscCharname) || (iClientIDAcc != -1)) { // money fix in case player logs in with this account bool bFound = false; foreach(ClientInfo[iClientIDAcc].lstMoneyFix, MONEY_FIX, i) { if((*i).wscCharname == wscCharname) { (*i).iAmount += iAmount; bFound = true; } } if(!bFound) { MONEY_FIX mf; mf.wscCharname = wscCharname; mf.iAmount = iAmount; ClientInfo[iClientIDAcc].lstMoneyFix.push_back(mf); } } return HKE_OK; } } [/code][/i][/i]</value></key></value></key></value></key>
-
It turns out that HkRename doesn’t work properly if the starting base is not Manhattan - the call to Server.CreateNewCharacter fails. Those mysterious numbers in the SCreateCharacterInfo structure do change when a different starting location is used. I’ve got a fix for this problem.
If any one wants a copy of the patched flhook.dll let me know and I’ll make it available.
Here’s the patched lines in the rename function. It’s a bit hacky but it works for me…
... SCreateCharacterInfo newcharinfo; wcsncpy(newcharinfo.wszCharname, wscNewCharname.c_str(), 23); // CHANGE STARTS HERE newcharinfo.iNickName = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","nickname","new_player").c_str()); newcharinfo.iBase = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","base", "Li01_01_Base").c_str()); newcharinfo.iPackage = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","Package","ge_fighter").c_str()); newcharinfo.iPilot = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","Pilot","trent").c_str()); // CHANGE ENDS HERE // Fill struct with valid data (though it isnt used it is needed) newcharinfo.iDunno[4] = 65536; ...
Here’s the SCreateCharacterInfo structure and the full rename function…
struct SCreateCharacterInfo { wchar_t wszCharname[23]; uint iNickName; // From [Faction] section of newcharacter.ini uint iBase; Â // From [Faction] section of newcharacter.ini uint iPackage; // From [Faction] section of newcharacter.ini uint iPilot; // From [Faction] section of newcharacter.ini uint iDunno[96]; }; HK_ERROR HkRename(wstring wscCharname, wstring wscNewCharname, bool bOnlyDelete) { HK_GET_CLIENTID(iClientID, wscCharname); if((iClientID == -1) && !HkGetAccountByCharname(wscCharname)) return HKE_CHAR_DOES_NOT_EXIST; if(!bOnlyDelete && HkGetAccountByCharname(wscNewCharname)) return HKE_CHARNAME_ALREADY_EXISTS; if(!bOnlyDelete && (wscNewCharname.length() > 23)) return HKE_CHARNAME_TOO_LONG; if(!bOnlyDelete && !wscNewCharname.length()) return HKE_CHARNAME_TOO_SHORT; CAccount *acc; wstring wscOldCharname; if(iClientID != -1) { acc = Players.FindAccountFromClientID(iClientID); wscOldCharname = Players.GetActiveCharacterName(iClientID); } else { wscOldCharname = wscCharname; acc = HkGetAccountByCharname(wscCharname); } wstring wscAccountDirname; HkGetAccountDirName(acc, wscAccountDirname); wstring wscNewFilename; HkGetCharFileName(wscNewCharname, wscNewFilename); wstring wscOldFilename; HkGetCharFileName(wscOldCharname, wscOldFilename); string scNewCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscNewFilename) + ".fl"; string scOldCharfilePath = scAcctPath + wstos(wscAccountDirname) + "\\" + wstos(wscOldFilename) + ".fl"; if(bOnlyDelete) { // delete character flstr *str = CreateWString(wscOldCharname.c_str()); HkLockAccountAccess(acc, true); // also kicks player on this account Players.DeleteCharacterFromName(*str); HkUnlockAccountAccess(acc); FreeWString(str); return HKE_OK; } HkLockAccountAccess(acc, true); // kick player if online HkUnlockAccountAccess(acc); // Copy existing char file into tmp string scTmpPath = scOldCharfilePath+".tmp"; DeleteFile(scTmpPath.c_str()); CopyFile(scOldCharfilePath.c_str(), scTmpPath.c_str(), FALSE); // Delete existing char otherwise a rename of the char in slot 5 fails. flstr *str = CreateWString(wscOldCharname.c_str()); Players.DeleteCharacterFromName(*str); FreeWString(str); // Emulate char create SLoginInfo logindata; wcsncpy(logindata.wszAccount, HkGetAccountID(acc).c_str(), 36); Players.login(logindata, Players.GetMaxPlayerCount()+1); SCreateCharacterInfo newcharinfo; wcsncpy(newcharinfo.wszCharname, wscNewCharname.c_str(), 23); newcharinfo.iNickName = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","nickname","").c_str()); newcharinfo.iBase = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","base", "").c_str()); newcharinfo.iPackage = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","Package","").c_str()); newcharinfo.iPilot = CreateID(IniGetS("..\\DATA\\CHARACTERS\\newcharacter.ini","Faction","Pilot","").c_str()); // Fill struct with valid data (though it isnt used it is needed) newcharinfo.iDunno[4] = 65536; newcharinfo.iDunno[5] = 65538; newcharinfo.iDunno[6] = 0; newcharinfo.iDunno[7] = 1058642330; newcharinfo.iDunno[8] = 3206125978; newcharinfo.iDunno[9] = 65537; newcharinfo.iDunno[10] = 0; newcharinfo.iDunno[11] = 3206125978; newcharinfo.iDunno[12] = 65539; newcharinfo.iDunno[13] = 65540; newcharinfo.iDunno[14] = 65536; newcharinfo.iDunno[15] = 65538; Server.CreateNewCharacter(newcharinfo, Players.GetMaxPlayerCount()+1); HkSaveChar(wscNewCharname); Players.logout(Players.GetMaxPlayerCount()+1); // Decode the backup of the old char and overwrite the new char file if(!flc_decode(scTmpPath.c_str(), scNewCharfilePath.c_str())) { // file wasn't encoded, thus simply rename it DeleteFile(scNewCharfilePath.c_str()); // just to get sure... CopyFile(scTmpPath.c_str(), scNewCharfilePath.c_str(), FALSE); } DeleteFile(scTmpPath.c_str()); // Update the char name in the new char file. // Add a space to the value so the ini file line looks like " <key>= <value>" // otherwise Ioncross Server Operator can't decode the file correctly string scValue = " "; for(uint i = 0; (i < wscNewCharname.length()); i++) { char cHiByte = wscNewCharname[i] >> 8; char cLoByte = wscNewCharname[i] & 0xFF; char szBuf[8]; sprintf(szBuf, "%02X%02X", ((uint)cHiByte) & 0xFF, ((uint)cLoByte) & 0xFF); scValue += szBuf; } IniWrite(scNewCharfilePath, "Player", "Name", scValue); // Re-encode the char file if needed. if (!set_bDisableCharfileEncryption) if (!flc_encode(scNewCharfilePath.c_str(),scNewCharfilePath.c_str())) return HKE_COULD_NOT_ENCODE_CHARFILE; return HKE_OK; } [/i][/i]</value></key>