diff --git a/CHANGELOG.md b/CHANGELOG.md index daa51dc77c..63d657d566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ https://github.com/nwnxee/unified/compare/build8193.37.13...HEAD ### Changed - Damage: Added bRangedAttack to the NWNX_Damage_AttackEventData struct. - Events: Added ID to the NWNX_ON_ITEMPROPERTY_EFFECT_* events data. +- `NWNX_TWEAKS_CHARLIST_SORT_BY_LAST_PLAYED_DATE` environment variable now takes a number of characters to sort by date instead of true/false. ### Deprecated - N/A diff --git a/Plugins/Tweaks/README.md b/Plugins/Tweaks/README.md index f1e8a85242..2a6c08d105 100644 --- a/Plugins/Tweaks/README.md +++ b/Plugins/Tweaks/README.md @@ -51,8 +51,8 @@ Tweaks stuff. See below. | `NWNX_TWEAKS_CUTSCENE_MODE_NO_TURD` | true or false | SetCutsceneMode() will not cause a TURD to be dropped. | | `NWNX_TWEAKS_CAN_USE_ITEMS_WHILE_POLYMORPHED` | true or false | Allow all items to be used while polymorphed. | | `NWNX_TWEAKS_RESIST_ENERGY_STACKS_WITH_EPIC_ENERGY_RESISTANCE` | true or false | Resist Energy feats stack with Epic Energy Resistance. | -| `NWNX_TWEAKS_UNHARDCODE_SPECIAL_ABILITY_TARGET_TYPE` | true or false | Allows special abilities to be used on target types other than creatures. | -| `NWNX_TWEAKS_CHARLIST_SORT_BY_LAST_PLAYED_DATE` | true or false | Servervault characters will be sorted by last time played date, instead of character name in the character list | +| `NWNX_TWEAKS_UNHARDCODE_SPECIAL_ABILITY_TARGET_TYPE` | true or false | Allows special abilities to be used on target types other than creatures. | +| `NWNX_TWEAKS_CHARLIST_SORT_BY_LAST_PLAYED_DATE` | Between 1 and 2097152 | The set number of servervault characters will be sorted by last time played date, instead of character name, in the character list. | ## Environment variable values diff --git a/Plugins/Tweaks/SortCharListByLastPlayedDate.cpp b/Plugins/Tweaks/SortCharListByLastPlayedDate.cpp index 13104e058a..1a016943a4 100644 --- a/Plugins/Tweaks/SortCharListByLastPlayedDate.cpp +++ b/Plugins/Tweaks/SortCharListByLastPlayedDate.cpp @@ -27,6 +27,7 @@ using namespace NWNXLib::API::Constants; static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pPlayer); static Hooks::Hook s_SendServerToPlayerCharListHook; +static size_t s_NumCharactersToSort; struct CharacterInfoWithFiletime : NWPlayerCharacterList_st { @@ -36,22 +37,32 @@ struct CharacterInfoWithFiletime : NWPlayerCharacterList_st void SortCharListByLastPlayedDate() __attribute__((constructor)); void SortCharListByLastPlayedDate() { - if (!Config::Get("CHARLIST_SORT_BY_LAST_PLAYED_DATE", false)) + const int MAX_SORT_VALUE = 2097152; // 2^7 * 2^7 * 2^7 = 2^21 = 2097152, see below in SetCharacterListIndex + + s_NumCharactersToSort = Config::Get("CHARLIST_SORT_BY_LAST_PLAYED_DATE", 0); + + if (s_NumCharactersToSort < 1) return; - LOG_INFO("Character list will be sorted by last played date"); + if (s_NumCharactersToSort > MAX_SORT_VALUE) + { + LOG_ERROR("Plugin NOT active! The maximum value for CHARLIST_SORT_BY_LAST_PLAYED_DATE is %d", MAX_SORT_VALUE); + return; + } + + LOG_INFO("Character list will be sorted by last played date for %d character(s)", s_NumCharactersToSort); s_SendServerToPlayerCharListHook = Hooks::HookFunction(&CNWSMessage::SendServerToPlayerCharList, &SendServerToPlayerCharListHook, Hooks::Order::Final); } static void SetCharacterListIndex(CharacterInfoWithFiletime* pCharInfo, int nIndex) { - // Set lowest bit of each rgb component to 1, reducing the range of available indices from 2^24 to 2^21 + // Set lowest bit of each rgb component to 1, reducing the range of available indices from 2^24 to 2^21 // while avoiding null bytes and preserving uniqueness + order nIndex = nIndex << 1; int b = (nIndex & 0xFF) | 1; int g = ((nIndex >> 7) & 0xFF) | 1; int r = ((nIndex >> 14) & 0xFF) | 1; - + auto& pStrList = pCharInfo->sLocFirstName.m_pExoLocStringInternal->m_lstString; for (auto *pNode = pStrList.GetHeadPos(); pNode; pNode = pNode->pNext) { @@ -65,7 +76,7 @@ static std::time_t GetBICFileTime(const CExoString& sVaultPath, const CResRef& s CExoString sFilename = sVaultPath + CExoString(sBicResRef) + ".bic"; struct stat fileStats; - if (stat(sFilename.c_str(), &fileStats) == 0) + if (stat(sFilename.c_str(), &fileStats) == 0) return fileStats.st_mtime; else return 0; @@ -129,7 +140,7 @@ static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pP { BOOL bSuccess; CResStruct topLevelStruct; - + pRes->GetTopLevelStruct(&topLevelStruct); CharacterInfoWithFiletime charInfo; @@ -140,7 +151,7 @@ static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pP charInfo.nPortraitId = pRes->ReadFieldWORD(&topLevelStruct, "PortraitId", bSuccess, 0xFFFF); charInfo.resPortrait = pRes->ReadFieldCResRef(&topLevelStruct, "Portrait", bSuccess); charInfo.fLastFileModified = GetBICFileTime(sPlayerServerVault, charInfo.resFileName); - + CResList classList; if (pRes->GetList(&classList, &topLevelStruct, "ClassList")) { @@ -163,8 +174,8 @@ static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pP else { LOG_ERROR("Corrupt BIC file in servervault of player: '%s' (%s) -> '%s.bic', character skipped.", - pPlayer->GetPlayerName().CStr(), - pPlayerInfo->m_cCDKey.sPublic.CStr(), + pPlayer->GetPlayerName().CStr(), + pPlayerInfo->m_cCDKey.sPublic.CStr(), sBicResRef.CStr()); } } @@ -235,13 +246,13 @@ static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pP } } - std::sort(lstChars.begin(), lstChars.end(), + std::sort(lstChars.begin(), lstChars.end(), [](const CharacterInfoWithFiletime& a, const CharacterInfoWithFiletime& b) { - return a.fLastFileModified > b.fLastFileModified; + return a.fLastFileModified > b.fLastFileModified; }); - for (size_t i=0; i < lstChars.size(); i++) + for (size_t i=0; i < std::min(s_NumCharactersToSort, lstChars.size()); i++) { SetCharacterListIndex(&lstChars[i], i); }