2
3
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-15 10:54:18 +00:00

Compare commits

..

58 Commits

Author SHA1 Message Date
4cc56d561b Merge branch 'ticket/54' into working 2024-03-06 10:14:20 -07:00
96facaa7bb Make Python.Cheat command more usable by not parsing its argument
The argument is now always treated as a plain string and passed directly
to the cheat. This is the behavior expected by all the functions in
xCheat.py, which parse the string argument manually where needed.

The previous implementation evaluated the argument value as a Python
expression. This theoretically allowed passing non-string arguments, but
none of the cheats use this (and it's also a potential code execution
issue). If no argument was passed, the function was called with no
arguments, which is also not supported by any of the cheats.

In practice, this required unintuitive syntax for calling the cheats
correctly, for example:

    Python.Cheat GetPlayerID None
    Python.Cheat GetSDL "'FirstWeekClothing'"

With the new argument handling, these have been simplified to:

    Python.Cheat GetPlayerID
    Python.Cheat GetSDL FirstWeekClothing
2023-12-15 01:03:25 +01:00
883495c35c Merge branch 'ticket/53' 2023-10-23 15:12:43 -06:00
e1306da736 Merge branch 'ticket/52' 2023-10-23 15:12:40 -06:00
8d1d813df5 Merge branch 'ticket/51' 2023-10-23 15:12:37 -06:00
65d50895bb closes #53 2023-10-23 15:12:15 -06:00
6a41bdbee7 closes #52 2023-10-23 15:11:45 -06:00
3dcf1c2f26 closes #51 2023-10-23 15:11:03 -06:00
70449244f4 Merge branch 'ticket/53' 2023-10-02 20:13:56 -06:00
82abcb2f07 remove clearReceivers() 2023-10-02 10:34:28 -07:00
870d0534df Merge branch 'ticket/53' 2023-09-29 10:49:27 -06:00
1f09d6c48a Try without armature changes 2023-09-28 11:48:33 -07:00
66ce08ed67 Merge branch 'ticket/53' 2023-09-26 10:08:18 -06:00
1c1d3dd82e Merge branch 'ticket/52' 2023-09-26 10:08:14 -06:00
4924156ad4 Fix 3 problems with plSubWorldMsg.
Co-authored-by: Adam Johnson <AdamJohnso@gmail.com>
2023-09-18 09:50:23 -07:00
13ea0a98de Don't trust the local machine's system time.
Co-authored-by: Adam Johnson <AdamJohnso@gmail.com>
2023-09-18 09:41:33 -07:00
f9723462d7 Merge branch 'ticket/51' 2023-07-05 15:00:20 -06:00
7d3774a732 Unload all keys of all pages before deleting them
(ported from H-uru/Plasma@04d0ac94ea)
2023-06-29 22:04:17 +02:00
ea7e4b2ab5 Fix previous commits not actually nulling out the key
I goofed while porting this from H'uru, where the variable is a
reference. The previous code actually caused the keys to not get deleted
at all.
2023-06-26 00:16:24 +02:00
5d5ba00f7d Also guard against accessing plRegistryKeyList currently being deleted
Co-authored-by: Adam Johnson <AdamJohnso@gmail.com>

(ported from H-uru/Plasma@725eeaa288)
2023-06-25 16:50:10 +02:00
af38137822 Fix possible use after free in plRegistryPageNode::UnloadKeys
Short explanation: the destructor of plRegistryKeyList may indirectly
access other entries of fKeyLists where the plRegistryKeyList has
already been deleted, but not yet removed from the map.

Long explanation:
* Deleting a plRegistryKeyList also deletes all plKeys inside it, which
  decrements the reference count of the objects they point to.
* If one of the deleted keys happens to be the last reference to an
  object, this also deletes the object itself.
* The object's destructor might in turn delete another plKey, which
  calls SetKeyUnused, which tries to look up the key in its page.
* If this second plKey belongs to the page that is currently being
  unloaded, then its plRegistryKeyList may be partially or completely
  deleted, but still listed in the fKeyLists map. In this case, the key
  lookup accesses already freed memory.

(ported from H-uru/Plasma@a529e35fd9)
2023-06-25 16:44:35 +02:00
4b0ab75a79 Merge branch 'ticket/49' 2022-11-12 10:30:24 -07:00
dcb0309a08 closes #49 2022-11-12 10:29:57 -07:00
7dde0855dc Merge branch 'ticket/48' 2022-11-12 10:13:57 -07:00
44c9b0694d Merge branch 'ticket/47' 2022-11-12 10:13:53 -07:00
dedf04ac43 Merge branch 'ticket/45' 2022-11-12 10:13:50 -07:00
eeb458817f Merge branch 'ticket/44' 2022-11-12 10:13:43 -07:00
68c7187155 closes #48 2022-11-12 10:13:17 -07:00
6fa695a2c3 closes #47 2022-11-12 10:12:53 -07:00
2a3c1b32ef closes #45 2022-11-12 10:11:59 -07:00
d3cbaa94ef closes #44 2022-11-12 10:11:24 -07:00
e7758ccc10 Merge branch 'ticket/49' 2022-10-23 10:46:15 -06:00
07b92234ad Fix bug for python SDL variable-length arrays 2022-10-03 21:48:57 -05:00
7dfa2ba56a Merge branch 'master' into working 2022-07-19 08:55:00 -06:00
1a091b7019 Merge branch 'ticket/48' 2022-06-05 15:14:09 -06:00
104a5ef4ee Merge branch 'ticket/47' 2022-06-05 15:13:55 -06:00
19cfaaaea9 Merge branch 'ticket/45' 2022-06-05 15:13:41 -06:00
dafd190083 Merge branch 'ticket/44' 2022-06-05 15:13:26 -06:00
fdee5a921b Merge branch 'ticket/47' of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata into ticket/47 2022-05-30 14:23:43 -07:00
b322ee094c Fix compile error 2022-05-30 14:23:20 -07:00
54c768d966 PR from Huru to prevent dynamics from moving during age initilization 2022-05-30 14:22:12 -07:00
82159bcb4a Merge branch 'ticket/47' of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata into ticket/47 2022-05-30 14:04:22 -07:00
b81eb21145 Fix compile error
Author: Adam Johnson <AdamJohnso@gmail.com>
2022-05-30 14:03:33 -07:00
e7b4f5c6ba Fix compile error
(cherry picked from commit 202e846a56)
2022-05-11 15:20:16 -06:00
202e846a56 Fix compile error 2022-05-11 14:16:44 -07:00
deb4858f3e Add subtitle and loc key message flags
(cherry picked from commit 126e7a76dd)
2022-05-11 13:58:51 -06:00
bff879abfd PR from Huru to prevent dynamics from moving during age initilization 2022-05-11 13:58:51 -06:00
fededb574b Change to prevent crashing when fCurStage is null 2022-05-11 13:58:51 -06:00
126e7a76dd Add subtitle and loc key message flags 2022-05-10 20:07:41 -05:00
26807a8d87 Merge branch 'ticket/47' 2022-05-05 09:45:42 -06:00
f54f14328e PR from Huru to prevent dynamics from moving during age initilization 2022-05-04 20:16:58 -07:00
311671e7a2 Merge branch 'ticket/46'
closes #46
2022-04-29 14:55:59 -06:00
8aaa98b8b7 Change to prevent crashing when fCurStage is null 2022-04-29 07:46:54 -07:00
3489e04463 Fix crash from plMouseDevice::HideCursor if it is called before cursor is created 2022-04-06 19:46:00 -05:00
33bbc490be Minor code quality correction 2022-04-06 19:32:30 -05:00
fd0c2a205e Fix nondeterministic conflicting page processing on startup 2022-04-06 19:18:05 -05:00
b2521e0777 Merge branch 'ticket/42'
closes #42
2022-03-23 09:22:29 -06:00
f9d629cfc5 Expand thread memory allocations for stack and heap to 1.25MB 2022-03-22 13:52:03 -06:00
16 changed files with 129 additions and 114 deletions

View File

@ -7061,17 +7061,12 @@ PF_CONSOLE_CMD( Python,
"string functions, ...", // Params "string functions, ...", // Params
"Run a cheat command" ) "Run a cheat command" )
{ {
std::string extraParms; const char* extraParms = "";
if (numParams > 1) if (numParams > 1)
{ {
extraParms = "("; extraParms = params[1];
extraParms.append(params[1]);
extraParms.append(",)");
} }
else PythonInterface::RunFunctionStringArg("xCheat", params[0], extraParms);
extraParms = "()";
PythonInterface::RunFunctionSafe("xCheat", params[0], extraParms.c_str());
std::string output; std::string output;
// get the messages // get the messages

View File

@ -165,6 +165,8 @@ class pfKIMsg : public plMessage
kUNUSED1 = 0x00000008, kUNUSED1 = 0x00000008,
kStatusMsg = 0x00000010, kStatusMsg = 0x00000010,
kNeighborMsg = 0x00000020, // sending to all the neighbors kNeighborMsg = 0x00000020, // sending to all the neighbors
kSubtitleMsg = 0x00000040,
kLocKeyMsg = 0x00000080,
kChannelMask = 0x0000ff00 kChannelMask = 0x0000ff00
}; };

View File

@ -2102,38 +2102,32 @@ PyObject* PythonInterface::RunFunction(PyObject* module, const char* name, PyObj
return result; return result;
} }
PyObject* PythonInterface::ParseArgs(const char* args) bool PythonInterface::RunFunctionStringArg(const char* module, const char* name, const char* arg)
{
PyObject* result = NULL;
PyObject* scope = PyDict_New();
if (scope)
{
//- Py_eval_input makes this function accept only single expresion (not statement)
//- When using empty scope, functions and classes like 'file' or '__import__' are not visible
result = PyRun_String(args, Py_eval_input, scope, NULL);
Py_DECREF(scope);
}
return result;
}
bool PythonInterface::RunFunctionSafe(const char* module, const char* function, const char* args)
{ {
PyObject* moduleObj = ImportModule(module); PyObject* moduleObj = ImportModule(module);
bool result = false; bool result = false;
if (moduleObj) if (moduleObj)
{ {
PyObject* argsObj = ParseArgs(args); PyObject* argObj = PyString_FromString(arg);
if (argsObj) if (argObj)
{ {
PyObject* callResult = RunFunction(moduleObj, function, argsObj); PyObject* argsObj = PyTuple_New(1);
if (callResult) if (argsObj)
{ {
result = true; // PyTuple_SET_ITEM steals the reference to argObj.
Py_DECREF(callResult); PyTuple_SET_ITEM(argsObj, 0, argObj);
PyObject* callResult = RunFunction(moduleObj, name, argsObj);
if (callResult)
{
result = true;
Py_DECREF(callResult);
}
Py_DECREF(argsObj);
}
else
{
Py_DECREF(argObj);
} }
Py_DECREF(argsObj);
} }
Py_DECREF(moduleObj); Py_DECREF(moduleObj);
} }

View File

@ -223,9 +223,7 @@ public:
static PyObject* RunFunction(PyObject* module, const char* name, PyObject* args); static PyObject* RunFunction(PyObject* module, const char* name, PyObject* args);
static PyObject* ParseArgs(const char* args); static bool RunFunctionStringArg(const char* module, const char* name, const char* arg);
static bool RunFunctionSafe(const char* module, const char* function, const char* args);
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //

View File

@ -474,6 +474,12 @@ void plPythonSDLModifier::IPythonVarToSDL(plStateDataRecord* state, const char*
int count = PyTuple_Size(pyVar); int count = PyTuple_Size(pyVar);
plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType(); plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType();
// Ensure that variable length arrays match.
if (var->GetSimpleVarDescriptor()->IsVariableLength()) {
if (var->GetCount() != count)
var->Alloc(count);
}
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
PyObject* pyVarItem = PyTuple_GetItem(pyVar, i); PyObject* pyVarItem = PyTuple_GetItem(pyVar, i);

View File

@ -326,7 +326,7 @@ bool plAvBrainClimb::IProcessExitStage(double time, float elapsed)
float curBlend = ai->GetBlend(); float curBlend = ai->GetBlend();
if(curBlend > .99) // reached peak strength if(fCurStage && curBlend > .99) // reached peak strength
{ {
fCurStage->Detach(fAvMod); // remove the (now completely masked) underlying anim fCurStage->Detach(fAvMod); // remove the (now completely masked) underlying anim
fCurStage = nil; fCurStage = nil;

View File

@ -481,8 +481,8 @@ void plMouseDevice::SetCursorY(hsScalar y)
void plMouseDevice::HideCursor(hsBool override) void plMouseDevice::HideCursor(hsBool override)
{ {
if( fInstance->fCursor != nil ) if (fInstance && fInstance->fCursor)
fInstance->fCursor->SetVisible( false ); fInstance->fCursor->SetVisible(false);
plMouseDevice::bCursorOverride = (override != 0); plMouseDevice::bCursorOverride = (override != 0);
plMouseDevice::bCursorHidden = true; plMouseDevice::bCursorHidden = true;
@ -499,9 +499,11 @@ void plMouseDevice::ShowCursor(hsBool override)
plMouseDevice::bCursorHidden = false; plMouseDevice::bCursorHidden = false;
plMouseDevice::bCursorOverride = false; plMouseDevice::bCursorOverride = false;
if( fInstance->fCursor == nil ) if (fInstance) {
fInstance->CreateCursor( fInstance->fCursorID ); if (!fInstance->fCursor)
fInstance->fCursor->SetVisible( true ); fInstance->CreateCursor(fInstance->fCursorID);
fInstance->fCursor->SetVisible(true);
}
} }
void plMouseDevice::NewCursor(char* cursor) void plMouseDevice::NewCursor(char* cursor)

View File

@ -217,6 +217,8 @@ bool plResponderModifier::IIsLocalOnlyCmd(plMessage* cmd)
return true; return true;
if (plCameraMsg::ConvertNoRef(cmd)) // don't want to change our camera if (plCameraMsg::ConvertNoRef(cmd)) // don't want to change our camera
return true; return true;
if (plSubWorldMsg::ConvertNoRef(cmd)) // don't want to enter a subworld (changes the avatar SDL)
return true;
plSoundMsg *snd = plSoundMsg::ConvertNoRef( cmd ); plSoundMsg *snd = plSoundMsg::ConvertNoRef( cmd );
if( snd != nil && snd->Cmd( plSoundMsg::kIsLocalOnly ) ) if( snd != nil && snd->Cmd( plSoundMsg::kIsLocalOnly ) )

View File

@ -211,7 +211,7 @@ void plNCAgeJoiner::Start () {
plNetClientMgr * nc = plNetClientMgr::GetInstance(); plNetClientMgr * nc = plNetClientMgr::GetInstance();
nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false); nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false);
nc->fServerTimeOffset = 0; // reset since we're connecting to a new server nc->ResetServerTimeOffset();
nc->fRequiredNumInitialSDLStates = 0; nc->fRequiredNumInitialSDLStates = 0;
nc->fNumInitialSDLStates = 0; nc->fNumInitialSDLStates = 0;
nc->SetFlagsBit(plNetClientApp::kNeedInitialAgeStateCount); nc->SetFlagsBit(plNetClientApp::kNeedInitialAgeStateCount);

View File

@ -133,9 +133,7 @@ plNetClientMgr::plNetClientMgr() :
// fProgressBar( nil ), // fProgressBar( nil ),
fTaskProgBar( nil ), fTaskProgBar( nil ),
fMsgRecorder(nil), fMsgRecorder(nil),
fServerTimeOffset(0), fLastLocalTime(),
fTimeSamples(0),
fLastTimeUpdate(0),
fListenListMode(kListenList_Distance), fListenListMode(kListenList_Distance),
fAgeSDLObjectKey(nil), fAgeSDLObjectKey(nil),
fExperimentalLevel(0), fExperimentalLevel(0),
@ -480,34 +478,23 @@ void plNetClientMgr::StartLinkInFX()
// //
void plNetClientMgr::UpdateServerTimeOffset(plNetMessage* msg) void plNetClientMgr::UpdateServerTimeOffset(plNetMessage* msg)
{ {
if ((hsTimer::GetSysSeconds() - fLastTimeUpdate) > 5) if (!msg->GetHasTimeSent())
{ return;
fLastTimeUpdate = hsTimer::GetSysSeconds(); if (msg->GetTimeSent().AtEpoch())
return;
const plUnifiedTime& msgSentUT = msg->GetTimeSent(); double localTime = hsTimer::GetSeconds();
if (!msgSentUT.AtEpoch()) if (localTime - fLastLocalTime < 1.0)
{ return;
double diff = plUnifiedTime::GetTimeDifference(msgSentUT, plClientUnifiedTime::GetCurrentTime());
if (fServerTimeOffset == 0) fLastServerTime = msg->GetTimeSent();
{ fLastLocalTime = localTime;
fServerTimeOffset = diff;
}
else
{
fServerTimeOffset = fServerTimeOffset + ((diff - fServerTimeOffset) / ++fTimeSamples);
}
DebugMsg("Setting server time offset to %f", fServerTimeOffset);
}
}
} }
void plNetClientMgr::ResetServerTimeOffset() void plNetClientMgr::ResetServerTimeOffset()
{ {
fServerTimeOffset = 0; fLastServerTime.ToEpoch();
fTimeSamples = 0; fLastLocalTime = 0.0;
fLastTimeUpdate = 0;
} }
// //
@ -515,14 +502,12 @@ void plNetClientMgr::ResetServerTimeOffset()
// //
plUnifiedTime plNetClientMgr::GetServerTime() const plUnifiedTime plNetClientMgr::GetServerTime() const
{ {
if ( fServerTimeOffset==0 ) // offline mode or before connecting/calibrating to a server if (fLastServerTime.AtEpoch()) {
WarningMsg("Someone asked for the server time, but we don't know it yet!");
return plUnifiedTime::GetCurrentTime(); return plUnifiedTime::GetCurrentTime();
}
plUnifiedTime serverUT;
if (fServerTimeOffset<0) return fLastServerTime + plUnifiedTime(hsTimer::GetSeconds() - fLastLocalTime);
return plUnifiedTime::GetCurrentTime() - plUnifiedTime(fabs(fServerTimeOffset));
else
return plUnifiedTime::GetCurrentTime() + plUnifiedTime(fServerTimeOffset);
} }
// //

View File

@ -166,9 +166,8 @@ private:
std::string fSPDesiredPlayerName; // SP: the player we want to load from vault. std::string fSPDesiredPlayerName; // SP: the player we want to load from vault.
// server info // server info
double fServerTimeOffset; // diff between our unified time and server's unified time plUnifiedTime fLastServerTime; // Last received time update from the server
UInt32 fTimeSamples; double fLastLocalTime; // Last monotonic time (in seconds) when the above update was received
double fLastTimeUpdate;
UInt8 fJoinOrder; // returned by the server UInt8 fJoinOrder; // returned by the server

View File

@ -1328,9 +1328,9 @@ void plPXPhysical::GetSyncState(hsPoint3& pos, hsQuat& rot, hsVector3& linV, hsV
void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV) void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV)
{ {
bool initialSync = plNetClientApp::GetInstance()->IsLoadingInitialAgeState() && bool isLoading = plNetClientApp::GetInstance()->IsLoadingInitialAgeState();
plNetClientApp::GetInstance()->GetJoinOrder() == 0; bool isFirstIn = plNetClientApp::GetInstance()->GetJoinOrder() == 0;
bool initialSync = isLoading && isFirstIn;
// If the physical has fallen out of the sim, and this is initial age state, and we're // If the physical has fallen out of the sim, and this is initial age state, and we're
// the first person in, reset it to the original position. (ie, prop the default state // the first person in, reset it to the original position. (ie, prop the default state
// we've got right now) // we've got right now)
@ -1350,6 +1350,13 @@ void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsV
SetLinearVelocitySim(*linV); SetLinearVelocitySim(*linV);
if (angV) if (angV)
SetAngularVelocitySim(*angV); SetAngularVelocitySim(*angV);
// If we're loading the age, then we should ensure the objects stay asleep if they're supposed to be asleep.
// NOTE: We should only do this if the objects are not at their initial locations. Otherwise, they might
// sleep inside each other and explode or float randomly in midair
if (isLoading && GetProperty(plSimulationInterface::kStartInactive) && !fActor->readBodyFlag(NX_BF_KINEMATIC)) {
if (!pos && !rot)
fActor->putToSleep();
}
SendNewLocation(false, true); SendNewLocation(false, true);
} }

View File

@ -59,8 +59,10 @@ plRegistryKeyList::~plRegistryKeyList()
for (int i = 0; i < fStaticKeys.size(); i++) for (int i = 0; i < fStaticKeys.size(); i++)
{ {
plKeyImp* keyImp = fStaticKeys[i]; plKeyImp* keyImp = fStaticKeys[i];
if (!keyImp->ObjectIsLoaded()) if (keyImp && !keyImp->ObjectIsLoaded()) {
fStaticKeys[i] = nullptr;
delete keyImp; delete keyImp;
}
} }
} }

View File

@ -203,6 +203,7 @@ void plRegistryPageNode::UnloadKeys()
for (; it != fKeyLists.end(); it++) for (; it != fKeyLists.end(); it++)
{ {
plRegistryKeyList* keyList = it->second; plRegistryKeyList* keyList = it->second;
it->second = nullptr;
delete keyList; delete keyList;
} }
fKeyLists.clear(); fKeyLists.clear();

View File

@ -219,14 +219,20 @@ void plResManager::IShutdown()
// Shut down the registry (finally!) // Shut down the registry (finally!)
ILockPages(); ILockPages();
PageSet::const_iterator it; // Unload all keys before actually deleting the pages.
for (it = fAllPages.begin(); it != fAllPages.end(); it++) // When a key's refcount drops to zero, IKeyUnreffed looks up the key's page.
delete *it; // If the page is already deleted at that point, this causes a use after free and potential crash.
fAllPages.clear(); for (PageSet::const_iterator it = fAllPages.begin(); it != fAllPages.end(); it++) {
(*it)->UnloadKeys();
}
fLoadedPages.clear(); fLoadedPages.clear();
fLastFoundPage = nil;
for (PageSet::const_iterator it = fAllPages.begin(); it != fAllPages.end(); it++) {
delete *it;
}
fAllPages.clear();
IUnlockPages(); IUnlockPages();
fLastFoundPage = nil;
// Now, kill off the Dispatcher // Now, kill off the Dispatcher
hsRefCnt_SafeUnRef(fDispatch); hsRefCnt_SafeUnRef(fDispatch);
@ -1299,7 +1305,7 @@ void plResManager::PageInAge(const char *age)
hsBool plResManager::VerifyPages() hsBool plResManager::VerifyPages()
{ {
hsTArray<plRegistryPageNode*> invalidPages, newerPages; PageSet invalidPages, newerPages;
// Step 1: verify major/minor version changes // Step 1: verify major/minor version changes
if (plResMgrSettings::Get().GetFilterNewerPageVersions() || if (plResMgrSettings::Get().GetFilterNewerPageVersions() ||
@ -1313,7 +1319,7 @@ hsBool plResManager::VerifyPages()
if (page->GetPageCondition() == kPageTooNew && plResMgrSettings::Get().GetFilterNewerPageVersions()) if (page->GetPageCondition() == kPageTooNew && plResMgrSettings::Get().GetFilterNewerPageVersions())
{ {
newerPages.Append(page); newerPages.insert(page);
fAllPages.erase(page); fAllPages.erase(page);
} }
else if ( else if (
@ -1321,21 +1327,21 @@ hsBool plResManager::VerifyPages()
page->GetPageCondition() == kPageOutOfDate) page->GetPageCondition() == kPageOutOfDate)
&& plResMgrSettings::Get().GetFilterOlderPageVersions()) && plResMgrSettings::Get().GetFilterOlderPageVersions())
{ {
invalidPages.Append(page); invalidPages.insert(page);
fAllPages.erase(page); fAllPages.erase(page);
} }
} }
} }
// Handle all our invalid pages now // Handle all our invalid pages now
if (invalidPages.GetCount() > 0) if (!invalidPages.empty())
{ {
if (!IDeleteBadPages(invalidPages, false)) if (!IDeleteBadPages(invalidPages, false))
return false; return false;
} }
// Warn about newer pages // Warn about newer pages
if (newerPages.GetCount() > 0) if (!newerPages.empty())
{ {
if (!IWarnNewerPages(newerPages)) if (!IWarnNewerPages(newerPages))
return false; return false;
@ -1343,22 +1349,35 @@ hsBool plResManager::VerifyPages()
// Step 2 of verification: make sure no sequence numbers conflict // Step 2 of verification: make sure no sequence numbers conflict
PageSet::iterator it = fAllPages.begin(); PageSet::iterator it = fAllPages.begin();
for (; it != fAllPages.end(); it++) for (; it != fAllPages.end();)
{ {
plRegistryPageNode* page = *it; plRegistryPageNode* page = *it;
PageSet::iterator itUp = it; PageSet::iterator itUp = it;
itUp++; itUp++;
for (; itUp != fAllPages.end(); itUp++) bool amValid = true;
for (; itUp != fAllPages.end();)
{ {
plRegistryPageNode* upPage = *itUp; plRegistryPageNode* upPage = *itUp;
if (page->GetPageInfo().GetLocation() == upPage->GetPageInfo().GetLocation()) if (page->GetPageInfo().GetLocation() == upPage->GetPageInfo().GetLocation()) {
{ invalidPages.insert(upPage);
invalidPages.Append(upPage); itUp = fAllPages.erase(itUp);
fAllPages.erase(itUp); amValid = false;
break; } else {
itUp++;
} }
} }
// Delete *all* conflicting pages, not just conflicting - 1.
// If we leave in a single conflicting page, a new conflict can trivially
// be introduced if the page we leave behind is defunct and a new version
// is later downloaded from the FileSrv.
if (!amValid) {
invalidPages.insert(page);
it = fAllPages.erase(it);
} else {
it++;
}
} }
// Redo our loaded pages list, since Verify() might force the page's keys to load or unload // Redo our loaded pages list, since Verify() might force the page's keys to load or unload
@ -1374,7 +1393,7 @@ hsBool plResManager::VerifyPages()
} }
// Handle all our conflicting pages now // Handle all our conflicting pages now
if (invalidPages.GetCount() > 0) if (!invalidPages.empty())
return IDeleteBadPages(invalidPages, true); return IDeleteBadPages(invalidPages, true);
return true; return true;
@ -1384,9 +1403,10 @@ hsBool plResManager::VerifyPages()
// Given an array of pages that are invalid (major version out-of-date or // Given an array of pages that are invalid (major version out-of-date or
// whatnot), asks the user what we should do about them. // whatnot), asks the user what we should do about them.
static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int bufSize) static void ICatPageNames(const std::set<plRegistryPageNode*>& pages, char* buf, int bufSize)
{ {
for (int i = 0; i < pages.GetCount(); i++) int i = 0;
for (auto it = pages.cbegin(); it != pages.cend(); ++it, ++i)
{ {
if (i >= 25) if (i >= 25)
{ {
@ -1394,7 +1414,7 @@ static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int b
break; break;
} }
const char* pagePath = pages[i]->GetPagePath(); const char* pagePath = (*it)->GetPagePath();
const char* pageFile = plFileUtils::GetFileName(pagePath); const char* pageFile = plFileUtils::GetFileName(pagePath);
if (strlen(buf) + strlen(pageFile) > bufSize - 5) if (strlen(buf) + strlen(pageFile) > bufSize - 5)
@ -1408,7 +1428,7 @@ static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int b
} }
} }
hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages, hsBool conflictingSeqNums) hsBool plResManager::IDeleteBadPages(PageSet& invalidPages, hsBool conflictingSeqNums)
{ {
#ifndef PLASMA_EXTERNAL_RELEASE #ifndef PLASMA_EXTERNAL_RELEASE
if (!hsMessageBox_SuppressPrompts) if (!hsMessageBox_SuppressPrompts)
@ -1430,12 +1450,12 @@ hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages
#endif // PLASMA_EXTERNAL_RELEASE #endif // PLASMA_EXTERNAL_RELEASE
// Delete 'em // Delete 'em
for (int i = 0; i < invalidPages.GetCount(); i++) for (PageSet::iterator it = invalidPages.begin(); it != invalidPages.end(); ++it)
{ {
invalidPages[i]->DeleteSource(); (*it)->DeleteSource();
delete invalidPages[i]; delete *it;
} }
invalidPages.Reset(); invalidPages.clear();
fLastFoundPage = nil; fLastFoundPage = nil;
@ -1447,7 +1467,7 @@ hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages
// than the "current" one), warns the user about them but does nothing to // than the "current" one), warns the user about them but does nothing to
// them. // them.
hsBool plResManager::IWarnNewerPages(hsTArray<plRegistryPageNode*> &newerPages) hsBool plResManager::IWarnNewerPages(PageSet &newerPages)
{ {
#ifndef PLASMA_EXTERNAL_RELEASE #ifndef PLASMA_EXTERNAL_RELEASE
if (!hsMessageBox_SuppressPrompts) if (!hsMessageBox_SuppressPrompts)
@ -1465,9 +1485,9 @@ hsBool plResManager::IWarnNewerPages(hsTArray<plRegistryPageNode*> &newerPages)
// Not deleting the files, just delete them from memory // Not deleting the files, just delete them from memory
for (int i = 0; i < newerPages.GetCount(); i++) for (PageSet::iterator it = newerPages.begin(); it != newerPages.end(); ++it)
delete newerPages[i]; delete *it;
newerPages.Reset(); newerPages.clear();
fLastFoundPage = nil; fLastFoundPage = nil;

View File

@ -61,6 +61,9 @@ typedef void(*plProgressProc)(plKey key);
class plResManager : public hsResMgr class plResManager : public hsResMgr
{ {
protected:
typedef std::set<plRegistryPageNode*> PageSet;
public: public:
plResManager(); plResManager();
virtual ~plResManager(); virtual ~plResManager();
@ -194,8 +197,8 @@ protected:
void IUnloadPageKeys(plRegistryPageNode* pageNode, hsBool dontClear = false); void IUnloadPageKeys(plRegistryPageNode* pageNode, hsBool dontClear = false);
hsBool IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages, hsBool conflictingSeqNums); hsBool IDeleteBadPages(PageSet& invalidPages, hsBool conflictingSeqNums);
hsBool IWarnNewerPages(hsTArray<plRegistryPageNode*>& newerPages); hsBool IWarnNewerPages(PageSet& newerPages);
void ILockPages(); void ILockPages();
void IUnlockPages(); void IUnlockPages();
@ -233,7 +236,6 @@ protected:
UInt8 fPageListLock; // Number of locks on the page lists. If it's greater than zero, they can't be modified UInt8 fPageListLock; // Number of locks on the page lists. If it's greater than zero, they can't be modified
hsBool fPagesNeedCleanup; // True if something modified the page lists while they were locked. hsBool fPagesNeedCleanup; // True if something modified the page lists while they were locked.
typedef std::set<plRegistryPageNode*> PageSet;
PageSet fAllPages; // All the pages, loaded or not PageSet fAllPages; // All the pages, loaded or not
PageSet fLoadedPages; // Just the loaded pages PageSet fLoadedPages; // Just the loaded pages