fix for the drone dance bug
Here’s how I rewrote WorkerManager::handleGasWorkers() to solve the drone dance bug whose cause Arrak located. It’s a deadly bug and not fixed in any released Steamhammer version, so if you’re running a fork you probably want this.
First, a pseudocode version to show the structure. The “resource depot” is the command center, nexus, or hatchery. If the resource depot is lost, we need to stop mining from the refinery because the enemy army is likely still around and will kill all our workers 3 at a time as we send them over. (Code elsewhere handles the case where the refinery is destroyed.)
if collecting gas
for each refinery
if its resource depot still exists
add workers to the refinery
else
remove workers from the refinery
else
remove workers from all refineries
Then the actual code. The pseudocode is good, but bugs may lurk in the details. It is new code and poorly tested, especially removing workers from a refinery whose depot is lost. Let me know if you find any remaining bugs.
// Move gas workers on or off gas as necessary.
// NOTE A worker inside a refinery does not accept orders.
void WorkerManager::handleGasWorkers()
{
if (_collectGas)
{
// Gather gas where possible. Check each refinery.
for (const auto refinery : BWAPI::Broodwar->self()->getUnits())
{
if (refinery->getType().isRefinery() && refinery->isCompleted())
{
if (refineryHasDepot(refinery))
{
// This is a good refinery. Gather from it.
// If too few workers are assigned, add more.
int numAssigned = workerData.getNumAssignedWorkers(refinery);
for (int i = 0; i < (Config::Macro::WorkersPerRefinery - numAssigned); ++i)
{
BWAPI::Unit gasWorker = getGasWorker(refinery);
if (gasWorker)
{
workerData.setWorkerJob(gasWorker, WorkerData::Gas, refinery);
}
else
{
return; // won't find any more, either for this refinery or others
}
}
}
else
{
// The refinery has no depot to return gas to. Remove any gas workers.
std::set gasWorkers;
workerData.getGasWorkers(gasWorkers);
for (const auto gasWorker : gasWorkers)
{
if (refinery == workerData.getWorkerResource(gasWorker) &&
gasWorker->getOrder() != BWAPI::Orders::HarvestGas) // not inside the refinery
{
workerData.setWorkerJob(gasWorker, WorkerData::Idle, nullptr);
}
}
}
}
}
}
else
{
// Don't gather gas: If workers are assigned to gas anywhere, take them off.
std::set gasWorkers;
workerData.getGasWorkers(gasWorkers);
for (const auto gasWorker : gasWorkers)
{
if (gasWorker->getOrder() != BWAPI::Orders::HarvestGas) // not inside the refinery
{
workerData.setWorkerJob(gasWorker, WorkerData::Idle, nullptr);
// An idle worker carrying gas will become a ReturnCargo worker,
// so gas will not be lost needlessly.
}
}
}
}
Comments
MicroDK on :