mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
585 lines
15 KiB
C++
585 lines
15 KiB
C++
#include "ht.h"
|
|
|
|
#ifdef MP_STRESS
|
|
|
|
bool gfMPStress;
|
|
int gnMPPos;
|
|
|
|
int FindGobs(dword ffGob, WCoord wxNear, WCoord wyNear, State st, bool fAlly, int cgob, Gob **apgob)
|
|
{
|
|
Gob *apgobSort[kcpgobMax];
|
|
int anDistSort[kcpgobMax];
|
|
int cgobSort = 0;
|
|
int cgobReturn = 0;
|
|
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if ((pgob->GetFlags() & ffGob) != ffGob)
|
|
continue;
|
|
if (st != kstReservedNull && pgob->m_st != st)
|
|
continue;
|
|
if (pgob->GetFlags() & kfGobUnit) {
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
if (fAlly) {
|
|
if (!punt->IsAlly(gpplrLocal->GetSide()))
|
|
continue;
|
|
} else {
|
|
if (punt->IsAlly(gpplrLocal->GetSide()))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (wxNear == kwxInvalid) {
|
|
apgob[cgobReturn++] = pgob;
|
|
if (cgobReturn == cgob)
|
|
break;
|
|
} else {
|
|
// Remember so we can sort
|
|
|
|
WPoint wpt;
|
|
pgob->GetPosition(&wpt);
|
|
apgobSort[cgobSort] = pgob;
|
|
anDistSort[cgobSort] = (wxNear - wpt.wx) * (wxNear - wpt.wx) + (wyNear - wpt.wy) * (wyNear - wpt.wy);
|
|
cgobSort++;
|
|
}
|
|
}
|
|
|
|
if (wxNear == kwxInvalid)
|
|
return cgobReturn;
|
|
|
|
// Sort what we've found
|
|
|
|
for (int i = cgobSort - 1; i >= 0; i--) {
|
|
for (int j = 1; j <= i; j++) {
|
|
if (anDistSort[j - 1] > anDistSort[j]) {
|
|
Gob *pgobT = apgobSort[j];
|
|
apgobSort[j] = apgobSort[j - 1];
|
|
apgobSort[j - 1] = pgobT;
|
|
int nT = anDistSort[j];
|
|
anDistSort[j] = anDistSort[j - 1];
|
|
anDistSort[j - 1] = nT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the first cgob's worth
|
|
|
|
int igob = 0;
|
|
for (; igob < cgob && igob < cgobSort; igob++)
|
|
apgob[igob] = apgobSort[igob];
|
|
return igob;
|
|
}
|
|
|
|
bool MPWait(int *pcupdWait, int cupdInterval)
|
|
{
|
|
if (*pcupdWait == 0) {
|
|
*pcupdWait = cupdInterval;
|
|
return false;
|
|
}
|
|
(*pcupdWait)--;
|
|
return true;
|
|
}
|
|
|
|
void MPMineStressUpdate()
|
|
{
|
|
static int s_cupdWait;
|
|
if (MPWait(&s_cupdWait, 36))
|
|
return;
|
|
|
|
// Find all the miners, see what they are doing. If moving or guard state, give it a mine command
|
|
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if (pgob->GetType() != kgtGalaxMiner || !(pgob->GetFlags() & kfGobActive))
|
|
continue;
|
|
MinerGob *pgobMiner = (MinerGob *)pgob;
|
|
if (pgobMiner->m_fHidden)
|
|
continue;
|
|
if (pgobMiner->m_st != kstGuard && pgobMiner->m_st != kstMove)
|
|
continue;
|
|
|
|
Message msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.mid = kmidMineCommand;
|
|
msg.MineCommand.gidTarget = kgidNull;
|
|
msg.MineCommand.wptTarget.wx = kwxInvalid;
|
|
msg.MineCommand.wptTarget.wy = kwxInvalid;
|
|
msg.smidReceiver = pgobMiner->GetId();
|
|
gcmdq.Enqueue(&msg);
|
|
}
|
|
}
|
|
|
|
void MPAttackStressUpdate()
|
|
{
|
|
static int s_cupdWait;
|
|
if (MPWait(&s_cupdWait, 72))
|
|
return;
|
|
|
|
// Find an enemy - mobile first
|
|
|
|
#if 0
|
|
Gob *apgobEnemy[2];
|
|
int cEnemies = FindGobs(kfGobUnit | kfGobActive | kfGobMobileUnit, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy);
|
|
if (cEnemies == 0) {
|
|
// Find an enemy - anything
|
|
|
|
cEnemies = FindGobs(kfGobUnit | kfGobActive, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy);
|
|
if (cEnemies == 0)
|
|
return;
|
|
}
|
|
#else
|
|
Gob *apgobEnemy[2];
|
|
int cEnemies = FindGobs(kfGobUnit | kfGobActive, kwxInvalid, kwxInvalid, kstReservedNull, false, ARRAYSIZE(apgobEnemy), apgobEnemy);
|
|
if (cEnemies == 0)
|
|
return;
|
|
#endif
|
|
|
|
// Attack the headquarters only as a last resort
|
|
|
|
UnitGob *puntEnemy = (UnitGob *)apgobEnemy[0];
|
|
if (puntEnemy->GetUnitType() == kutHeadquarters) {
|
|
if (cEnemies > 1)
|
|
puntEnemy = (UnitGob *)apgobEnemy[1];
|
|
}
|
|
|
|
// Find a number of grouped allies that are idle
|
|
|
|
WPoint wpt;
|
|
puntEnemy->GetPosition(&wpt);
|
|
Gob *apgob[kcpgobMax];
|
|
int cUnits = FindGobs(kfGobMobileUnit | kfGobActive, wpt.wx, wpt.wy, kstGuard, true, ARRAYSIZE(apgob), apgob);
|
|
if (cUnits == 0)
|
|
return;
|
|
|
|
// Send these units to attack this enemy
|
|
|
|
#define kcpgobAttack 25
|
|
|
|
int cAttacking = 0;
|
|
for (int n = 0; n < cUnits && cAttacking <= kcpgobAttack; n++) {
|
|
// Limit # of attackers. Already attacking?
|
|
|
|
Gob *pgob = apgob[n];
|
|
if (pgob->m_st == kstAttack) {
|
|
cAttacking++;
|
|
continue;
|
|
}
|
|
|
|
// Queue attack command
|
|
|
|
Message msgT;
|
|
msgT.mid = kmidAttackCommand;
|
|
msgT.smidSender = apgob[n]->GetId();
|
|
msgT.smidReceiver = apgob[n]->GetId();
|
|
msgT.AttackCommand.wptTarget.wx = wpt.wx;
|
|
msgT.AttackCommand.wptTarget.wy = wpt.wy;
|
|
msgT.AttackCommand.gidTarget = puntEnemy->GetId();
|
|
msgT.AttackCommand.wptTargetCenter.wx = wpt.wx;
|
|
msgT.AttackCommand.wptTargetCenter.wy = wpt.wy;
|
|
msgT.AttackCommand.tcTargetRadius = 1;
|
|
msgT.AttackCommand.wcMoveDistPerUpdate = 0;
|
|
gcmdq.Enqueue(&msgT);
|
|
cAttacking++;
|
|
}
|
|
}
|
|
|
|
void MPMoveStressUpdate()
|
|
{
|
|
static int s_cupdWait;
|
|
if (MPWait(&s_cupdWait, 24))
|
|
return;
|
|
|
|
// Find a number of grouped allies that are idle
|
|
|
|
Gob *apgob[1];
|
|
int cUnits = FindGobs(kfGobMobileUnit | kfGobActive, kwxInvalid, kwxInvalid, kstGuard, true, ARRAYSIZE(apgob), apgob);
|
|
if (cUnits == 0)
|
|
return;
|
|
|
|
// Send this unit to some random place that won't cause an attack
|
|
|
|
TCoord ctx, cty;
|
|
ggobm.GetMapSize(&ctx, &cty);
|
|
TCoord tx = GetAsyncRandom() % ctx;
|
|
TCoord ty = GetAsyncRandom() % cty;
|
|
WPoint wpt;
|
|
FindNearestFreeTile(tx, ty, &wpt);
|
|
|
|
// Queue move command
|
|
|
|
Message msgT;
|
|
msgT.mid = kmidMoveCommand;
|
|
msgT.smidSender = apgob[0]->GetId();
|
|
msgT.smidReceiver = apgob[0]->GetId();
|
|
msgT.MoveCommand.wptTarget.wx = wpt.wx;
|
|
msgT.MoveCommand.wptTarget.wy = wpt.wy;
|
|
msgT.MoveCommand.gidTarget = kgidNull;
|
|
msgT.MoveCommand.wptTargetCenter.wx = wpt.wx;
|
|
msgT.MoveCommand.wptTargetCenter.wy = wpt.wy;
|
|
msgT.MoveCommand.tcTargetRadius = 1;
|
|
msgT.MoveCommand.wcMoveDistPerUpdate = 0;
|
|
gcmdq.Enqueue(&msgT);
|
|
}
|
|
|
|
struct UnitsMaintain { // unm
|
|
UnitType utBuilder;
|
|
int cUnits;
|
|
};
|
|
|
|
//Max unit counts
|
|
//#define kcStructGobsHumanMax 55
|
|
//#define kcStructGobsComputerMax 72
|
|
//#define kcMuntGobsHumanMax 88
|
|
//#define kcMuntGobsComputerMax 117
|
|
|
|
UnitsMaintain gaunm[] = {
|
|
{ kutHumanResourceCenter, 5}, // kutShortRangeInfantry
|
|
{ kutHumanResourceCenter, 10}, // kutLongRangeInfantry
|
|
{ kutHumanResourceCenter, 4}, // kutTakeoverSpecialist
|
|
{ kutVehicleTransportStation, 5}, // kutMachineGunVehicle
|
|
{ kutVehicleTransportStation, 10}, // kutLightTank
|
|
{ kutVehicleTransportStation, 10}, // kutRocketVehicle
|
|
{ kutVehicleTransportStation, 10}, // kutMediumTank
|
|
{ kutVehicleTransportStation, 4}, // kutGalaxMiner
|
|
{ kutVehicleTransportStation, 2}, // kutMobileHeadquarters
|
|
{ kutHeadquarters, 10}, // kutReactor
|
|
{ kutHeadquarters, 1}, // kutProcessor
|
|
{ kutHeadquarters, 8}, // kutWarehouse
|
|
{ kutHeadquarters, 2}, // kutHumanResourceCenter
|
|
{ kutHeadquarters, 2}, // kutVehicleTransportStation
|
|
{ kutHeadquarters, 1}, // kutRadar
|
|
{ kutHeadquarters, 1}, // kutResearchCenter
|
|
{ kutHeadquarters, 1}, // kutHeadquarters
|
|
{ kutHeadquarters, 10}, // kutMachineGunTower
|
|
{ kutHeadquarters, 10}, // kutRocketTower
|
|
{ kutHumanResourceCenter, 1}, // kutAndy
|
|
{ kutVehicleTransportStation, 5}, // kutArtillery
|
|
{ kutHeadquarters, 0}, // kutReplicator
|
|
{ kutHumanResourceCenter, 5}, // kutFox
|
|
};
|
|
|
|
WPoint FindInitPositionForStructure(BuilderGob *pbldr, int ctxReserve, int ctyReserve)
|
|
{
|
|
WPoint wpt;
|
|
pbldr->GetPosition(&wpt);
|
|
TCoord txStart = TcFromWc(wpt.wx);
|
|
TCoord tyStart = TcFromWc(wpt.wy);
|
|
TCoord ctx, cty;
|
|
ggobm.GetMapSize(&ctx, &cty);
|
|
|
|
for (TCoord ty = 0; ty < cty - ctyReserve; ty++) {
|
|
for (TCoord tx = 0; tx < ctx - ctxReserve; tx++) {
|
|
TCoord txT = (tx + txStart) % ctx;
|
|
TCoord tyT = (ty + tyStart) % cty;
|
|
|
|
bool fOpen = true;
|
|
for (TCoord tyS = 0; tyS < ctyReserve; tyS++) {
|
|
for (TCoord txS = 0; txS < ctxReserve; txS++) {
|
|
if (!IsTileFree(txS + txT, tyS + tyT, kbfReserved | kbfStructure | kbfMobileUnit)) {
|
|
fOpen = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!fOpen)
|
|
break;
|
|
}
|
|
if (fOpen) {
|
|
wpt.wx = WcFromTc(txT);
|
|
wpt.wy = WcFromTc(tyT);
|
|
return wpt;
|
|
}
|
|
}
|
|
}
|
|
|
|
return wpt;
|
|
}
|
|
|
|
void BuildUnits(UnitType ut, int cUnits, UnitType utBuilder, int *acUnitCapacityRemaining)
|
|
{
|
|
// Find all instances of the desired unit type either built and active or queued to be built
|
|
|
|
int cUnitsActive = gpplrLocal->GetUnitCount(ut);
|
|
if (cUnitsActive >= cUnits)
|
|
return;
|
|
|
|
// Find all builders of type utBuilder
|
|
|
|
int cUnitsQueued = 0;
|
|
UnitGob *apunt[kcpgobMax];
|
|
UnitGob **ppuntT = apunt;
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if ((pgob->GetFlags() & (kfGobActive | kfGobStructure)) != (kfGobActive | kfGobStructure))
|
|
continue;
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
if (punt->GetOwner() != gpplrLocal)
|
|
continue;
|
|
if (punt->GetUnitType() != utBuilder)
|
|
continue;
|
|
|
|
// # of queue units?
|
|
|
|
BuilderGob *pbldr = ((BuilderGob *)punt);
|
|
cUnitsQueued += pbldr->m_bq.GetUnitCount(ut);
|
|
*ppuntT++ = punt;
|
|
}
|
|
|
|
// If # of active and queued units are met, return
|
|
|
|
int cUnitsBuild = cUnits - (cUnitsActive + cUnitsQueued);
|
|
if (cUnitsBuild <= 0)
|
|
return;
|
|
|
|
// If no builders for this unit type, wait for one to be built
|
|
|
|
int cBuilders = ppuntT - apunt;
|
|
if (cBuilders == 0)
|
|
return;
|
|
|
|
// Build!
|
|
|
|
for (int n = 0; n < cBuilders; n++) {
|
|
// Is this builder full?
|
|
|
|
BuilderGob *pbldr = (BuilderGob *)apunt[n];
|
|
int *pcOpen = &acUnitCapacityRemaining[pbldr->GetId() / sizeof(Gob *)];
|
|
if (*pcOpen == 0)
|
|
continue;
|
|
|
|
while (*pcOpen > 0 && cUnitsBuild > 0) {
|
|
// Find coordinates for this unit
|
|
|
|
WPoint wpt;
|
|
if (!((1UL << ut) & kumStructures)) {
|
|
pbldr->FindInitPosition(&wpt);
|
|
} else {
|
|
StructConsts *pstruc = (StructConsts *)gapuntc[ut];
|
|
wpt = FindInitPositionForStructure(pbldr, pstruc->ctxReserve, pstruc->ctyReserve);
|
|
}
|
|
|
|
Message msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.smidSender = ksmidNull;
|
|
msg.mid = kmidBuildOtherCommand;
|
|
msg.BuildOtherCommand.ut = (UnitType)ut;
|
|
msg.BuildOtherCommand.wpt.wx = wpt.wx;
|
|
msg.BuildOtherCommand.wpt.wy = wpt.wy;
|
|
msg.smidReceiver = pbldr->GetId();
|
|
gcmdq.Enqueue(&msg);
|
|
|
|
(*pcOpen)--;
|
|
cUnitsBuild--;
|
|
}
|
|
if (cUnitsBuild == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MPBuildStressUpdate()
|
|
{
|
|
static int s_cupdWait;
|
|
if (MPWait(&s_cupdWait, 36))
|
|
return;
|
|
|
|
// Get the capacities of all builder gobs. Need to calc this up front since capacities don't change
|
|
// when build orders are queued
|
|
|
|
int acUnitCapacityRemaining[kcpgobMax + 1];
|
|
memset(acUnitCapacityRemaining, 0, sizeof(acUnitCapacityRemaining));
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if ((pgob->GetFlags() & (kfGobActive | kfGobStructure)) != (kfGobActive | kfGobStructure))
|
|
continue;
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
if (!(punt->GetConsts()->wf & (kfUntcStructureBuilder | kfUntcMobileUnitBuilder)))
|
|
continue;
|
|
BuilderGob *pbldr = (BuilderGob *)punt;
|
|
acUnitCapacityRemaining[pbldr->GetId() / sizeof(Gob *)] = pbldr->m_bq.GetRemainingCapacity();
|
|
}
|
|
|
|
// First ensure the builders are accounted for
|
|
|
|
BuildUnits(kutHeadquarters, 1, kutHeadquarters, acUnitCapacityRemaining);
|
|
for (int ut = 0; ut < ARRAYSIZE(gaunm); ut++) {
|
|
if (gaunm[ut].cUnits == 0)
|
|
continue;
|
|
UnitType utBuilder = gaunm[ut].utBuilder;
|
|
BuildUnits(utBuilder, gaunm[utBuilder].cUnits, kutHeadquarters, acUnitCapacityRemaining);
|
|
}
|
|
|
|
// Now ensure other units are accounted for. Enumerate in a random order
|
|
|
|
UnitType autOrder[kutMax];
|
|
memset(autOrder, 0xff, sizeof(autOrder));
|
|
for (int ut = 0; ut < ARRAYSIZE(autOrder); ut++) {
|
|
int n;
|
|
while (true) {
|
|
n = GetAsyncRandom() % kutMax;
|
|
if (autOrder[n] == kutNone)
|
|
break;
|
|
}
|
|
autOrder[n] = ut;
|
|
}
|
|
|
|
for (int n = 0; n < ARRAYSIZE(autOrder); n++) {
|
|
UnitType ut = autOrder[n];
|
|
if (gaunm[ut].cUnits == 0)
|
|
continue;
|
|
if (ut == kutHeadquarters)
|
|
continue;
|
|
BuildUnits(ut, gaunm[ut].cUnits, gaunm[ut].utBuilder, acUnitCapacityRemaining);
|
|
}
|
|
|
|
// UNDONE: kmidAbortBuildOtherCommand
|
|
}
|
|
|
|
void MPStressUpdate()
|
|
{
|
|
if (!gfMPStress)
|
|
return;
|
|
|
|
MPMineStressUpdate(); // kmidMineCommand
|
|
MPAttackStressUpdate(); // kmidAttackCommand
|
|
MPMoveStressUpdate(); // kmidMoveCommand
|
|
MPBuildStressUpdate(); // kmidBuildOtherCommand, kmidAbortBuildOtherCommand
|
|
|
|
#if 0
|
|
MPUpgradeStressUpdate(); // kmidUpgradeCommand, kmidAbortUpgradeCommand
|
|
MPTransformStressUpdate(); // kmidTransformCommand
|
|
MPSelfDestructStressUpdate(); // kmidSelfDestructCommand
|
|
MPRepairStressUpdate(); // kmidRepairCommand
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef MP_DEBUG_SHAREDMEM
|
|
|
|
HANDLE ghProcessServer;
|
|
HANDLE ghSharedMem;
|
|
SharedMemWindow *gpsmw;
|
|
bool gfMPServer;
|
|
Side gsideCurrent;
|
|
Gid ggidCurrent;
|
|
long gcupdCurrent;
|
|
GobType ggtCurrent;
|
|
|
|
#ifndef DEBUG
|
|
#error Currently requires DEBUG since Assert is used functionally
|
|
#endif
|
|
|
|
void MPInitSharedMemoryWindow(bool fServer)
|
|
{
|
|
if (fServer) {
|
|
ghSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 8192, "wi_shared_mem");
|
|
if (ghSharedMem == NULL)
|
|
return;
|
|
gpsmw = (SharedMemWindow *)MapViewOfFile(ghSharedMem, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
gpsmw->pidProcess = GetCurrentProcessId();
|
|
#ifdef DETECT_SYNC_ERRORS
|
|
gpsmw->fDetectSyncErrors = gfLockStep;
|
|
#else
|
|
gpsmw->fDetectSyncErrors = false;
|
|
#endif
|
|
} else {
|
|
ghSharedMem = OpenFileMapping(FILE_MAP_READ, FALSE, "wi_shared_mem");
|
|
if (ghSharedMem == NULL)
|
|
return;
|
|
gpsmw = (SharedMemWindow *)MapViewOfFile(ghSharedMem, FILE_MAP_READ, 0, 0, 0);
|
|
ghProcessServer = OpenProcess(PROCESS_VM_READ, FALSE, gpsmw->pidProcess);
|
|
}
|
|
gfMPServer = fServer;
|
|
}
|
|
|
|
void MPExitSharedMemoryWindow(bool fAmServer)
|
|
{
|
|
UnmapViewOfFile(gpsmw);
|
|
gpsmw = NULL;
|
|
CloseHandle(ghSharedMem);
|
|
ghSharedMem = NULL;
|
|
if (!gfMPServer) {
|
|
CloseHandle(ghProcessServer);
|
|
ghProcessServer = NULL;
|
|
}
|
|
}
|
|
|
|
void MPUpdateState()
|
|
{
|
|
if (gfMPServer)
|
|
memcpy(gpsmw->apgobMaster, ggobm.m_apgobMaster, (kcpgobMax + 1) * sizeof(Gob *));
|
|
}
|
|
|
|
void MPValidateState()
|
|
{
|
|
if (!gfMPServer && gpsmw->fDetectSyncErrors) {
|
|
for (Gob *pgob = ggobm.GetFirstGob(); pgob != NULL; pgob = ggobm.GetNextGob(pgob)) {
|
|
if (!(pgob->GetFlags() & kfGobUnit))
|
|
continue;
|
|
UnitGob *punt = (UnitGob *)pgob;
|
|
punt->MPValidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MPCopyMem(void *pvTo, void *pvFrom, int cb)
|
|
{
|
|
Assert(!gfMPServer);
|
|
SIZE_T cbRead;
|
|
BOOL f = ReadProcessMemory(ghProcessServer, pvFrom, pvTo, cb, &cbRead);
|
|
Assert(f && cb == cbRead);
|
|
}
|
|
|
|
Gob *MPGetGobPtr(Gid gid)
|
|
{
|
|
Assert(!gfMPServer);
|
|
Assert(gid >= 0 && gid < (kcpgobMax + 1) * sizeof(Gob *));
|
|
Gob *pgob = *((Gob **)((byte *)gpsmw->apgobMaster + (gid)));
|
|
Assert(((dword)pgob & 1) == 0);
|
|
return pgob;
|
|
}
|
|
|
|
void MPValidateMemory(void *pvRemote, void *pvLocal, int cb)
|
|
{
|
|
if (gfMPServer)
|
|
return;
|
|
|
|
byte abT[512];
|
|
Assert(cb <= sizeof(abT));
|
|
|
|
SIZE_T cbRead;
|
|
BOOL f = ReadProcessMemory(ghProcessServer, pvRemote, abT, cb, &cbRead);
|
|
Assert(f && cb == cbRead);
|
|
Assert(memcmp(abT, pvLocal, cb) == 0);
|
|
}
|
|
|
|
byte gabRemoteCache[16 * 1024];
|
|
void *gpvRemoteCache;
|
|
int gcbRemoteCache;
|
|
long gcupdRemoteCache;
|
|
bool gfInsideUpdate;
|
|
|
|
void MPValidateMember2(void *pvLocal, void *pvRemote, int cbThis, int cbOffsetMem, int cbMem)
|
|
{
|
|
if (gfMPServer)
|
|
return;
|
|
|
|
// If inside an update, check state directly
|
|
|
|
if (gfInsideUpdate) {
|
|
MPValidateMemory(((byte *)pvLocal) + cbOffsetMem, ((byte *)pvRemote) + cbOffsetMem, cbMem);
|
|
return;
|
|
}
|
|
|
|
// Update our speedup cache
|
|
|
|
if (gcupdRemoteCache == 0 || gcupdCurrent != gcupdRemoteCache || pvRemote != gpvRemoteCache || cbOffsetMem + cbMem > gcbRemoteCache) {
|
|
// Update the cache
|
|
|
|
Assert(cbOffsetMem + cbMem <= cbThis);
|
|
MPCopyMem(gabRemoteCache, pvRemote, cbThis);
|
|
gpvRemoteCache = pvRemote;
|
|
gcbRemoteCache = cbThis;
|
|
gcupdRemoteCache = gcupdCurrent;
|
|
}
|
|
|
|
// Check memory
|
|
|
|
Assert(memcmp(&gabRemoteCache[cbOffsetMem], &((byte *)pvLocal)[cbOffsetMem], cbMem) == 0);
|
|
}
|
|
#endif
|