static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, unsigned id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest, StructureBounds const &dstStructure) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); if (!worldOnMap(startX, startY) || !worldOnMap(tX, tY)) { debug(LOG_ERROR, "Droid trying to find path to/from invalid location (%d %d) -> (%d %d).", startX, startY, tX, tY); objTrace(id, "Invalid start/end."); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); auto const &I = pathResults.find(id); ASSERT(I != pathResults.end(), "Missing path result promise"); PATHRESULT result = I->second.get(); ASSERT(result.retval != FPR_OK || result.sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = result.sMove.destination; psMove->numPoints = result.sMove.numPoints; bool correctDestination = tX == result.originalDest.x && tY == result.originalDest.y; psMove->pathIndex = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = result.sMove.asPath; FPATH_RETVAL retval = result.retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(id); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); if (!correctDestination) { goto queuePathfinding; // Seems we got the result of an old pathfinding job for this droid, so need to pathfind again. } return retval; } queuePathfinding: // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.dstStructure = dstStructure; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); debug(LOG_NEVER, "starting new job for droid %d 0x%x", id, id); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); packagedPathJob task([job]() { return fpathExecute(job); }); pathResults[id] = task.get_future(); // Add to end of list wzMutexLock(fpathMutex); bool isFirstJob = pathJobs.empty(); pathJobs.push_back(std::move(task)); wzMutexUnlock(fpathMutex); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }
static FPATH_RETVAL fpathRoute(MOVE_CONTROL *psMove, int id, int startX, int startY, int tX, int tY, PROPULSION_TYPE propulsionType, DROID_TYPE droidType, FPATH_MOVETYPE moveType, int owner, bool acceptNearest) { objTrace(id, "called(*,id=%d,sx=%d,sy=%d,ex=%d,ey=%d,prop=%d,type=%d,move=%d,owner=%d)", id, startX, startY, tX, tY, (int)propulsionType, (int)droidType, (int)moveType, owner); // don't have to do anything if already there if (startX == tX && startY == tY) { // return failed to stop them moving anywhere objTrace(id, "Tried to move nowhere"); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_FAILED", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_FAILED; } // Check if waiting for a result while (psMove->Status == MOVEWAITROUTE) { objTrace(id, "Checking if we have a path yet"); wzMutexLock(fpathMutex); // psNext should be _declared_ here, after the mutex lock! Used to be a race condition, thanks to -Wdeclaration-after-statement style pseudocompiler compatibility. for (std::list<PATHRESULT>::iterator psResult = pathResults.begin(); psResult != pathResults.end(); ++psResult) { if (psResult->droidID != id) { continue; // Wrong result, try next one. } ASSERT(psResult->retval != FPR_OK || psResult->sMove.asPath, "Ok result but no path in list"); // Copy over select fields - preserve others psMove->destination = psResult->sMove.destination; psMove->numPoints = psResult->sMove.numPoints; psMove->Position = 0; psMove->Status = MOVENAVIGATE; free(psMove->asPath); psMove->asPath = psResult->sMove.asPath; FPATH_RETVAL retval = psResult->retval; ASSERT(retval != FPR_OK || psMove->asPath, "Ok result but no path after copy"); ASSERT(retval != FPR_OK || psMove->numPoints > 0, "Ok result but path empty after copy"); // Remove it from the result list pathResults.erase(psResult); wzMutexUnlock(fpathMutex); objTrace(id, "Got a path to (%d, %d)! Length=%d Retval=%d", psMove->destination.x, psMove->destination.y, psMove->numPoints, (int)retval); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = %d, path[%d] = %08X->(%d, %d)", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner, retval, psMove->numPoints, ~crcSumVector2i(0, psMove->asPath, psMove->numPoints), psMove->destination.x, psMove->destination.y); return retval; } objTrace(id, "No path yet. Waiting."); waitingForResult = true; waitingForResultId = id; wzMutexUnlock(fpathMutex); wzSemaphoreWait(waitingForResultSemaphore); // keep waiting } // We were not waiting for a result, and found no trivial path, so create new job and start waiting PATHJOB job; job.origX = startX; job.origY = startY; job.droidID = id; job.destX = tX; job.destY = tY; job.droidType = droidType; job.propulsion = propulsionType; job.moveType = moveType; job.owner = owner; job.acceptNearest = acceptNearest; job.deleted = false; fpathSetBlockingMap(&job); // Clear any results or jobs waiting already. It is a vital assumption that there is only one // job or result for each droid in the system at any time. fpathRemoveDroidData(id); wzMutexLock(fpathMutex); // Add to end of list bool isFirstJob = pathJobs.empty(); pathJobs.push_back(job); if (isFirstJob) { wzSemaphorePost(fpathSemaphore); // Wake up processing thread. } wzMutexUnlock(fpathMutex); objTrace(id, "Queued up a path-finding request to (%d, %d), at least %d items earlier in queue", tX, tY, isFirstJob); syncDebug("fpathRoute(..., %d, %d, %d, %d, %d, %d, %d, %d, %d) = FPR_WAIT", id, startX, startY, tX, tY, propulsionType, droidType, moveType, owner); return FPR_WAIT; // wait while polling result queue }