Skip to content

Commit 820c22e

Browse files
Fix Lua array detection and order preservation when using toJSON (PR #3369)
1 parent df28972 commit 820c22e

File tree

2 files changed

+62
-54
lines changed

2 files changed

+62
-54
lines changed

Client/mods/deathmatch/logic/lua/CLuaArguments.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -547,49 +547,53 @@ json_object* CLuaArguments::WriteTableToJSONObject(bool bSerialize, CFastHashMap
547547
bKnownTablesCreated = true;
548548
}
549549

550-
pKnownTables->insert(std::make_pair(this, pKnownTables->size()));
550+
pKnownTables->insert({this, pKnownTables->size()});
551551

552-
bool bIsArray = true;
553-
unsigned int iArrayPos = 1; // lua arrays are 1 based
554-
vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
552+
bool bIsArray = true;
553+
std::vector<std::pair<std::uint32_t, CLuaArgument*>> vecSortedArguments; // lua arrays are not necessarily sorted
554+
std::vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
555555
for (; iter != m_Arguments.end(); iter += 2)
556556
{
557557
CLuaArgument* pArgument = *iter;
558558
if (pArgument->GetType() == LUA_TNUMBER)
559559
{
560-
double num = pArgument->GetNumber();
561-
unsigned int iNum = static_cast<unsigned int>(num);
562-
if (num == iNum)
563-
{
564-
if (iArrayPos != iNum) // check if the value matches its index in the table
565-
{
566-
bIsArray = false;
567-
break;
568-
}
569-
}
570-
else
571-
{
572-
bIsArray = false;
573-
break;
574-
}
560+
double const num = pArgument->GetNumber();
561+
auto const iNum = static_cast<std::uint32_t>(num);
562+
563+
vecSortedArguments.push_back({iNum, *(iter + 1)});
575564
}
576565
else
577566
{
578567
bIsArray = false;
579568
break;
580569
}
581-
iArrayPos++;
582570
}
583571

584-
if (bIsArray)
572+
if (bIsArray && !vecSortedArguments.empty()) // the table could possibly be an array
585573
{
586-
json_object* my_array = json_object_new_array();
587-
vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
588-
for (; iter != m_Arguments.end(); iter++)
574+
// sort the table based on the keys (already handled correctly by std::pair)
575+
std::sort(vecSortedArguments.begin(), vecSortedArguments.end());
576+
577+
// only the first and last element are checked, everything else is correct by default because the vector was sorted
578+
// the last key should match the size of vecSortedArguments to ensure there are no gaps in this array-like table
579+
auto const iFirstKey = vecSortedArguments.front().first;
580+
auto const iLastKey = vecSortedArguments.back().first;
581+
582+
auto const iFirstArrayPos = 1U; // lua arrays are 1 based
583+
auto const iLastArrayPos = static_cast<std::uint32_t>(vecSortedArguments.size());
584+
585+
if (iFirstKey != iFirstArrayPos || iLastKey != iLastArrayPos)
589586
{
590-
iter++; // skip the key values
591-
CLuaArgument* pArgument = *iter;
592-
json_object* object = pArgument->WriteToJSONObject(bSerialize, pKnownTables);
587+
bIsArray = false;
588+
}
589+
}
590+
591+
if (bIsArray) // the table is definitely an array
592+
{
593+
json_object* my_array = json_object_new_array();
594+
for (auto const& [iKey, pArgument] : vecSortedArguments)
595+
{
596+
json_object* object = pArgument->WriteToJSONObject(bSerialize, pKnownTables);
593597
if (object)
594598
{
595599
json_object_array_add(my_array, object);

Server/mods/deathmatch/logic/lua/CLuaArguments.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -623,49 +623,53 @@ json_object* CLuaArguments::WriteTableToJSONObject(bool bSerialize, CFastHashMap
623623
bKnownTablesCreated = true;
624624
}
625625

626-
pKnownTables->insert(std::make_pair(this, pKnownTables->size()));
626+
pKnownTables->insert({this, pKnownTables->size()});
627627

628-
bool bIsArray = true;
629-
unsigned int iArrayPos = 1; // lua arrays are 1 based
630-
vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
628+
bool bIsArray = true;
629+
std::vector<std::pair<std::uint32_t, CLuaArgument*>> vecSortedArguments; // lua arrays are not necessarily sorted
630+
std::vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
631631
for (; iter != m_Arguments.end(); iter += 2)
632632
{
633633
CLuaArgument* pArgument = *iter;
634634
if (pArgument->GetType() == LUA_TNUMBER)
635635
{
636-
double num = pArgument->GetNumber();
637-
unsigned int iNum = static_cast<unsigned int>(num);
638-
if (num == iNum)
639-
{
640-
if (iArrayPos != iNum) // check if the value matches its index in the table
641-
{
642-
bIsArray = false;
643-
break;
644-
}
645-
}
646-
else
647-
{
648-
bIsArray = false;
649-
break;
650-
}
636+
double const num = pArgument->GetNumber();
637+
auto const iNum = static_cast<std::uint32_t>(num);
638+
639+
vecSortedArguments.push_back({iNum, *(iter + 1)});
651640
}
652641
else
653642
{
654643
bIsArray = false;
655644
break;
656645
}
657-
iArrayPos++;
658646
}
659647

660-
if (bIsArray)
648+
if (bIsArray && !vecSortedArguments.empty()) // the table could possibly be an array
661649
{
662-
json_object* my_array = json_object_new_array();
663-
vector<CLuaArgument*>::const_iterator iter = m_Arguments.begin();
664-
for (; iter != m_Arguments.end(); ++iter)
650+
// sort the table based on the keys (already handled correctly by std::pair)
651+
std::sort(vecSortedArguments.begin(), vecSortedArguments.end());
652+
653+
// only the first and last element are checked, everything else is correct by default because the vector was sorted
654+
// the last key should match the size of vecSortedArguments to ensure there are no gaps in this array-like table
655+
auto const iFirstKey = vecSortedArguments.front().first;
656+
auto const iLastKey = vecSortedArguments.back().first;
657+
658+
auto const iFirstArrayPos = 1U; // lua arrays are 1 based
659+
auto const iLastArrayPos = static_cast<std::uint32_t>(vecSortedArguments.size());
660+
661+
if (iFirstKey != iFirstArrayPos || iLastKey != iLastArrayPos)
665662
{
666-
iter++; // skip the key values
667-
CLuaArgument* pArgument = *iter;
668-
json_object* object = pArgument->WriteToJSONObject(bSerialize, pKnownTables);
663+
bIsArray = false;
664+
}
665+
}
666+
667+
if (bIsArray) // the table is definitely an array
668+
{
669+
json_object* my_array = json_object_new_array();
670+
for (auto const& [iKey, pArgument] : vecSortedArguments)
671+
{
672+
json_object* object = pArgument->WriteToJSONObject(bSerialize, pKnownTables);
669673
if (object)
670674
{
671675
json_object_array_add(my_array, object);

0 commit comments

Comments
 (0)