Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Plugins/Tweaks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 23 additions & 12 deletions Plugins/Tweaks/SortCharListByLastPlayedDate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -36,22 +37,32 @@ struct CharacterInfoWithFiletime : NWPlayerCharacterList_st
void SortCharListByLastPlayedDate() __attribute__((constructor));
void SortCharListByLastPlayedDate()
{
if (!Config::Get<bool>("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<int>("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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -129,7 +140,7 @@ static BOOL SendServerToPlayerCharListHook(CNWSMessage* pMessage, CNWSPlayer* pP
{
BOOL bSuccess;
CResStruct topLevelStruct;

pRes->GetTopLevelStruct(&topLevelStruct);

CharacterInfoWithFiletime charInfo;
Expand All @@ -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"))
{
Expand All @@ -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());
}
}
Expand Down Expand Up @@ -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);
}
Expand Down