NTai XE10.1b
Moderators: hoijui, Moderators
AF:
Now that your room has cool a little, I├óÔé¼Ôäóm warming this thread with a possible solution to Solobuild and SingleBuild
But first I need to clear some assumptions and misconceptions about the flow during a UnitIdle call.
I├óÔé¼Ôäóve tried to reconstruct the flow of Unitidle:
Is this a reasonable representation of the flow, for an idle construction unit with b_power as task
(Don't correct names, just tell me if I got the general idea)
Also I├óÔé¼Ôäóm assuming that a new thread is started every time a unit needs to find a position for building. An alternative might be that a task-thread is started during NTAI init, and this thread has a stack of position hunting orders, it process in FIFO.
(Eg. the Boost::Thread in above is substituted for SendMessage/SendOrder/SendParameters)
So which solution does NTAI use, one thread for each position hunt, or one single thread (not a spring thread, separate), doing all position hunting in sequence
Now that your room has cool a little, I├óÔé¼Ôäóm warming this thread with a possible solution to Solobuild and SingleBuild

But first I need to clear some assumptions and misconceptions about the flow during a UnitIdle call.
I├óÔé¼Ôäóve tried to reconstruct the flow of Unitidle:
Code: Select all
CManifacture:UnitIdle (unit)
If unit has NO tasklist then goto ├óÔé¼┼ôhandle attacker├óÔé¼┬Ø // CActions
If unit has unit name in tasklist then Goto CUnitConstructionTask::Init
CKeywordConstructionTask::Init
CUBuild::operator
Planning::feasable
CKeywordConstructionTask::Build
If (CUBuild::OkBuildSelection == false) //check for solobuild
{
Push(CMD_REPAIR);
exit; //Exit unitidle, do new order
}
Boost::Thread CManifacture->BuildPlacement(unit, unitpos, UnitDef, target, spacing)
If (CKeywordConstructionTask::RecieveMessage(CManifacture::BuildPlacement) == false) {
Return; // No BuildPos, exit Unitidle, do Next task
}
CKeywordConstructionTask::RecieveMessage: "G->OrderRouter -> Giveorder"
CAICallback::GiveOrder
CKeywordConstructionTask::RecieveMessage: "creating plan."
Push(G->Cached->solobuilds[name]) //lock solobuild
Return; //Wait for new idle

Also I├óÔé¼Ôäóm assuming that a new thread is started every time a unit needs to find a position for building. An alternative might be that a task-thread is started during NTAI init, and this thread has a stack of position hunting orders, it process in FIFO.
(Eg. the Boost::Thread in above is substituted for SendMessage/SendOrder/SendParameters)
So which solution does NTAI use, one thread for each position hunt, or one single thread (not a spring thread, separate), doing all position hunting in sequence

One thread for each search. 5 searches 5 threads and so on.
CManufcturer is now simply a helper class that manages construction plans and so on.
NTai recieves a UnitIdle() call so it looks up an array attatched to the "unitidle" string, creates a "unitidle" message and fires it off to each object in that array. These objects are all inherited from IModule, they could be tasks, helpers, etc.
Every unit has an instance of CUnit which is registered to recieve these events. CUnit manages the task cycle.
It will keep passing the first task in its list events untill that task is flagged as valid=false, after which it is discarded and the next task is moved up and initialized.
For a task to build a "corvlab" a core vehicle lab, it would spawn a CUnitConstructionTask.
It would then call Init() on that task.
Inside Init it would sort out exactly what needs building and wether to go ahead with it taking into account exclusion zones and other tags based on the builders location.
When that is done the task calls a function in the build placement algorithm passing a smart pointer, and the details necessary to find a build position.
The build algorithm then runs a series of checks to determine the placement algorithm, radar placement, mex placement, geothermal placement.
When the algorithm is finished it sends a message containing the position to the task object and exits.
If none of the checks checkout and no algorithm is ran, all the parameters are passed to a boost thread object and a buildalgorithm thread is spawned. The function then exits.
When the algorithm completes it sends the position back to the task and the thread ends.
When the task recieves the final position, it does one or two final checks incase an error value has been returned. It then prepares a command object with all the necessary data and sends it off to the command handler. If the command handler returns true a plan is created. If it returns false the task is flagged as invalid and gets deleted.
When the command handler recieves the command object it places it in a queue after a set of checks. If the command fails these checks then the command is discarded and a false is returned. At set intervals NTai will send a set amount of commands to NTai to prevent overflowing the engine with unit commands.
The task then waits. During the tasks Init function it will have registered itself to recieve unitidle and unitmovefailed and unitdestroyed events. It will wait for these events. When unitidle or unitmovefailed is recieved it will assume the task has completed and exit. If unitdestroyed is recieved and matches the unit it of the builder then the task ends too. The task is then flagged as invalid and CUnit moves onto the next task.
Perhaps it would be more efficient to handle build algorithm searches using a set of worker threads that works through a shared pool of points. But I havent given it much thought as NTai rarely gives my cpu much to think about, possibly one of the downsides to having a core 2 duo e6600.
CManufcturer is now simply a helper class that manages construction plans and so on.
NTai recieves a UnitIdle() call so it looks up an array attatched to the "unitidle" string, creates a "unitidle" message and fires it off to each object in that array. These objects are all inherited from IModule, they could be tasks, helpers, etc.
Every unit has an instance of CUnit which is registered to recieve these events. CUnit manages the task cycle.
It will keep passing the first task in its list events untill that task is flagged as valid=false, after which it is discarded and the next task is moved up and initialized.
For a task to build a "corvlab" a core vehicle lab, it would spawn a CUnitConstructionTask.
It would then call Init() on that task.
Inside Init it would sort out exactly what needs building and wether to go ahead with it taking into account exclusion zones and other tags based on the builders location.
When that is done the task calls a function in the build placement algorithm passing a smart pointer, and the details necessary to find a build position.
The build algorithm then runs a series of checks to determine the placement algorithm, radar placement, mex placement, geothermal placement.
When the algorithm is finished it sends a message containing the position to the task object and exits.
If none of the checks checkout and no algorithm is ran, all the parameters are passed to a boost thread object and a buildalgorithm thread is spawned. The function then exits.
When the algorithm completes it sends the position back to the task and the thread ends.
When the task recieves the final position, it does one or two final checks incase an error value has been returned. It then prepares a command object with all the necessary data and sends it off to the command handler. If the command handler returns true a plan is created. If it returns false the task is flagged as invalid and gets deleted.
When the command handler recieves the command object it places it in a queue after a set of checks. If the command fails these checks then the command is discarded and a false is returned. At set intervals NTai will send a set amount of commands to NTai to prevent overflowing the engine with unit commands.
The task then waits. During the tasks Init function it will have registered itself to recieve unitidle and unitmovefailed and unitdestroyed events. It will wait for these events. When unitidle or unitmovefailed is recieved it will assume the task has completed and exit. If unitdestroyed is recieved and matches the unit it of the builder then the task ends too. The task is then flagged as invalid and CUnit moves onto the next task.
Perhaps it would be more efficient to handle build algorithm searches using a set of worker threads that works through a shared pool of points. But I havent given it much thought as NTai rarely gives my cpu much to think about, possibly one of the downsides to having a core 2 duo e6600.
Thanks for the very elaborate answer, I just need to clear the question in red below, so I can surgest a solution.
(I need to know how many threads are involved in the task flow, from unitidle to new unit command, as each thread can lead to relaxed solobuild.)
OK so I got the general concept of the task flow, but messed all the names.AF wrote: One thread for each search. 5 searches 5 threads and so on.
CManufcturer is now simply a helper class that manages construction plans and so on.
When you say "spawn" do you mean a new boost::thread, or just an event/class call?AF wrote: For a task to build a "corvlab" a core vehicle lab, it would spawn a CUnitConstructionTask.
(I need to know how many threads are involved in the task flow, from unitidle to new unit command, as each thread can lead to relaxed solobuild.)
Basically it tries to do a simple buildplacment find, and if failed, will spawn a thread doing some serious buildplacment hunting.AF wrote: The build algorithm then runs a series of checks to determine the placement algorithm, radar placement, mex placement, geothermal placement.
When the algorithm is finished it sends a message containing the position to the task object and exits.
OK and it is this thread, than can be overtaken by another construction unit, leading to imperfect solobuild, rightAF wrote: If none of the checks checkout and no algorithm is ran, all the parameters are passed to a boost thread object and a buildalgorithm thread is spawned. The function then exits.
When the algorithm completes it sends the position back to the task and the thread ends.

Each solution has good and bad points, the current implementation works with 5% CPU usage on a 2GHz Athlon single core, this is GOOD performance.AF wrote: Perhaps it would be more efficient to handle build algorithm searches using a set of worker threads that works through a shared pool of points. But I havent given it much thought as NTai rarely gives my cpu much to think about, possibly one of the downsides to having a core 2 duo e6600.
OK I├óÔé¼Ôäóm going to assumeAF wrote:5 searches 5 threads

I├óÔé¼Ôäóve got 2 solutions for you:
1) Based on Boost::Mutex array
Bad:
- ├óÔé¼┬ó Only 1 thread of BuildPlacement per unittype seeking construction.
├óÔé¼┬ó Possible larger footprint of NTAI.dll (few kilobytes)
├óÔé¼┬ó Static max number of unit types. (eg. max 5000 different units in mod)
├óÔé¼┬ó Slightly less performance on quad cores.
- ├óÔé¼┬ó 100% reliable Solo and Singlebuild
├óÔé¼┬ó No deadlocks eg. No solobuild becomes singlebuild.
├óÔé¼┬ó Slightly less stutters on single core.
├óÔé¼┬ó No wasted BuildPlacment searches on solo- or singlebuilds.
- ├óÔé¼┬ó Reduced sequential Solobuilding of unittype as default behavior.
├óÔé¼┬ó Reduced assist; only some units trying to do a solo- or singebuilds will assist (others will do next task instead).
├óÔé¼┬ó Possible to implement Singlebuild, as: Make new one, if old is destroyed.
2) Fake/pre-emptive solobuild, using double bookkeeping.
Bad:
- ├óÔé¼┬ó Possible deadlock, eg. Solobuild becomes singlebuild, Singlebuild becomes zerobuild.
├óÔé¼┬ó Double size array needed for solo- and singlebuild (eg. Cached->singlebuilds with two elements per unitid)
├óÔé¼┬ó More complex to debug and error hunt.
├óÔé¼┬ó Massive sequential Solobuilding of unittype as ONLY behavior
- ├óÔé¼┬ó 99.9% reliable Solo- and Singlebuild
├óÔé¼┬ó No CPU usage hit/change in respect to current NTAI
├óÔé¼┬ó No wasted BuildPlacment searches on solo- or singlebuilds
- ├óÔé¼┬ó Possible to change Singlebuild into ├óÔé¼┼ôbuild no more than x├óÔé¼┬Ø
├óÔé¼┬ó Massive assist; all unit trying to do a solo- or singlebuild will assist.
├óÔé¼┬ó Possible to implement Singlebuild, as: Make new one, if old is destroyed or not finished.
I'll post solution 1 right away. Solution 2 needs some touch-up.
Solution 1:
Consider this example:BEWARE, I added the OkBuildSelection, after doing the final compile, so there might be syntax errors in this part.
It outputs:
Consider this example:
Code: Select all
#include <unistd.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>
boost::mutex io_mutex[5000]; //5000 equals max number of different units in a any given spring mod.
char *targetname[4]={"none","corsolar","corwin ","corllt "};
char *targetnum[5]={"zero", "1st ","2nd ","1st ","1st "};
//Above strings , are just for readability
bool OkBuildSelection(string name);
{
// FAKE! do NOT use this, instead use routine OkBuildSelection from ubuild.cpp
if (name == ├óÔé¼┬Øname_of_unit_to_solobuild├óÔé¼┬Ø)
return false;
return true;
}
void GetBuildPlacment(int unit, int targetid)
{
boost::mutex::scoped_lock lock(io_mutex[targetid]);
// The below routine does a second check, within a mutexed thread, to test if solobuild or singlebuild
if ((OKBuildSelection(targetname[targetid]) == false)
{
// SendMessage(UpVector); // replace with how u normally exit buildplacment thread with a: no buildpos found.
exit; //exit (kill) current thread;
}
for (int i = 0; i < 10; ++i)
{
usleep((300*unit*unit*unit*unit));
std::cout << "constructor number: " << unit << " find buildpos for: " << targetnum[unit] << targetname[targetid] << " trying pos: " << i << std::endl;
}
}
int main(int argc, char* argv[])
{
//Below call is how CUnitConstructionTask. Should spawn the BuildPlacement thread. The boost:bind is IMPORTANT!
boost::thread thrd1(boost::bind(&GetBuildPlacment, 1, 1));
boost::thread thrd2(boost::bind(&GetBuildPlacment, 2, 1));
boost::thread thrd3(boost::bind(&GetBuildPlacment, 3, 2));
thrd1.join();
thrd2.join();
thrd3.join();
return 0;
}
It outputs:
- constructor number: 2 find buildpos for: 2nd corsolar trying pos: 0
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 1
constructor number: 3 find buildpos for: 1st corwin trying pos: 0
constructor number: 3 find buildpos for: 1st corwin trying pos: 1
constructor number: 3 find buildpos for: 1st corwin trying pos: 2
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 2
constructor number: 3 find buildpos for: 1st corwin trying pos: 3
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 3
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 4
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 5
constructor number: 3 find buildpos for: 1st corwin trying pos: 4
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 6
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 7
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 8
constructor number: 3 find buildpos for: 1st corwin trying pos: 5
constructor number: 2 find buildpos for: 2nd corsolar trying pos: 9
constructor number: 1 find buildpos for: 1st corsolar trying pos: 0
constructor number: 1 find buildpos for: 1st corsolar trying pos: 1
constructor number: 3 find buildpos for: 1st corwin trying pos: 6
constructor number: 1 find buildpos for: 1st corsolar trying pos: 2
constructor number: 1 find buildpos for: 1st corsolar trying pos: 3
constructor number: 1 find buildpos for: 1st corsolar trying pos: 4
constructor number: 3 find buildpos for: 1st corwin trying pos: 7
constructor number: 1 find buildpos for: 1st corsolar trying pos: 5
constructor number: 1 find buildpos for: 1st corsolar trying pos: 6
constructor number: 1 find buildpos for: 1st corsolar trying pos: 7
constructor number: 3 find buildpos for: 1st corwin trying pos: 8
constructor number: 1 find buildpos for: 1st corsolar trying pos: 8
constructor number: 1 find buildpos for: 1st corsolar trying pos: 9
constructor number: 3 find buildpos for: 1st corwin trying pos: 9
3 Steps to realize this solution for NTAI
Important!. Please note that I do NOT know the exact name and accurate reference to BuildPlacement algorithme entry in the following, I call it: "GetBuildPlacement"
)
Important!. Please note that I do NOT know the exact name and accurate reference to BuildPlacement algorithme entry in the following, I call it: "GetBuildPlacement"
- 1) Insert a:Into the headerfile of BuildPlacement algorithm.
Code: Select all
boost::mutex io_mutex[5000]; //5000 equals max number of different units (max unit.id) in any given spring mod.
2) If you do NOT already use ├óÔé¼┼ôbind├óÔé¼┬Ø in boost::thread GetBuildPlacement. Then modify it to use this:3) Change start of BuildPlacment thread to include:Code: Select all
boost::thread thrdBP(boost::bind (&GetBuildPlacement, unit, unitpos, UnitDef, target, spacing));
The "OkBuildSelection", above should be replace with an accurate reference to this rutine in ubuild.cpp, probably somthing like G->CUBuild->OkBuildSelectionCode: Select all
void GetBuildPlacment(unit, unitpos, UnitDef, target, spacing) { boost::mutex::scoped_lock lock(io_mutex[target.id]); if ((OKBuildSelection(targetname[targetid]) == false) // Code here to send UpVector and end (terminate) thread) }

At the moment the build algorithm thread is a functor.
However I've made adjustments based on what you've said and here's a test build.
http://www.darkstars.co.uk/randomfiles/NTaiLALE.rar
However I've made adjustments based on what you've said and here's a test build.
http://www.darkstars.co.uk/randomfiles/NTaiLALE.rar
OK The mux thing seems to work. But somehow the call from inside the functor to OkBuildSelection makes OkBuildSelection always return false for everything but mex├óÔé¼Ôäós ??
I├óÔé¼Ôäóv tested with this simple setup:
This gives the log:
Investigating.....
I├óÔé¼Ôäóv tested with this simple setup:
- [AI]
{
singlebuild=corlab,corck;
solobuild=corhp;
antistall=0;
[NORMAL]
{
Corcom=corcom;
}
[LISTS]
{
corcom=b_mex,b_radar,b_factory,corlab,corhp;
}
This gives the log:
My hunch, is that this has somthing to do with the scope of variables used inside OkBuildSelection.LOG wrote: [-] < Frame: 0 >loading contents of tasklist :: corcom :: filling tasklist with #5 items
[-] < Frame: 0 >CUnitConstructionTask::CUnitConstructionTask building :: CORLAB using builder::CORCOM
[-] < Frame: 0 >CUnitConstructionTask::CUnitConstructionTask building :: CORHP using builder::CORCOM
[-] < Frame: 0 >loaded contents of tasklist :: corcom :: loaded tasklist at 5 items
[-] < Frame: 0 >CKeywordConstructionTask::Init b_mex
[-] < Frame: 0 >CKeywordConstructionTask::Build() :: CORMEX
[-] < Frame: 0 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: CORMEX
[-] < Frame: 0 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: CORMEX
[-] < Frame: 3 >issuing command in update()
[-] < Frame: 3 >Command: ID: -271 Timeout: 374 params: 328, 40.6084, 536, 0, source of command: CBuild
[-] < Frame: 3 >issuing command in update() succeeded
[00:15] < Frame: 476 >Value tasklists\normal\cormex missing in file buffer
[00:15] < Frame: 476 >Value tasklists\lists\cormex missing in file buffer
[00:15] < Frame: 476 > error loading tasklist :: CORMEX :: buffer empty, most likely because of an empty list
[00:18] < Frame: 541 >next task?
[00:18] < Frame: 541 >CKeywordConstructionTask::Init b_radar
[00:18] < Frame: 541 >CKeywordConstructionTask::Build() :: CORRAD
[00:18] < Frame: 541 >CKeywordConstructionTask::RecieveMessage BuildPlacement returned UpVector or some other nasty position
[00:18] < Frame: 555 >next task?
[00:18] < Frame: 555 >CKeywordConstructionTask::Init b_factory
[00:18] < Frame: 555 >CKeywordConstructionTask::Build() :: CORSY
[00:18] < Frame: 555 >CKeywordConstructionTask::RecieveMessage BuildPlacement returned UpVector or some other nasty position
[00:19] < Frame: 595 >next task?
[00:19] < Frame: 595 >CUnitConstructionTask::Init :: CORLAB
[00:19] < Frame: 595 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!
[00:19] < Frame: 597 >next task?
[00:19] < Frame: 597 >CUnitConstructionTask::Init :: CORHP
[00:19] < Frame: 597 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!
[00:23] < Frame: 719 >next task?
[00:23] < Frame: 719 >CUnitConstructionTask::Init :: CORHP
[00:23] < Frame: 719 >loading contents of tasklist :: corcom :: filling tasklist with #5 items
[00:23] < Frame: 719 >CUnitConstructionTask::CUnitConstructionTask building :: CORLAB using builder::CORCOM
[00:23] < Frame: 719 >CUnitConstructionTask::CUnitConstructionTask building :: CORHP using builder::CORCOM
[00:23] < Frame: 719 >loaded contents of tasklist :: corcom :: loaded tasklist at 5 items
[00:23] < Frame: 719 >CKeywordConstructionTask::Init b_mex
[00:23] < Frame: 719 >CKeywordConstructionTask::Build() :: CORMEX
[00:23] < Frame: 719 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: CORMEX
[00:23] < Frame: 719 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: CORMEX
[00:24] < Frame: 720 >issuing command in update()
[00:24] < Frame: 720 >Command: ID: -271 Timeout: 1093 params: 664, 36.0579, 1256, 0, source of command: CBuild
[00:24] < Frame: 720 >issuing command in update() succeeded
Investigating.....
Well the new redownload, solved the ├óÔé¼┼ônot build anything but mex├óÔé¼┬Ø problem.
And NTAI seems to be working as usual.
BUT:
This has had no impact on solobuild (and probably not on single build either)
First: I had expected to se some: (when CORHP is solobuild)
Also:
For this, and the previous 9.73 build is see this in the log:
Something tells me the issue with solo and single build is not only with the BuildPlacement-thread overtaking a previous solobuilded search.
Btw: in case you are interested in the mutex solution, it├óÔé¼Ôäós from this article:
http://www.ddj.com/dept/cpp/184401518
And NTAI seems to be working as usual.
BUT:
This has had no impact on solobuild (and probably not on single build either)
First: I had expected to se some: (when CORHP is solobuild)
- [00:19] < Frame: 597 >next task?
[00:19] < Frame: 597 >CUnitConstructionTask::Init :: CORHP
[00:19] < Frame: 597 >BuildPlacement returned UpVector or some other nasty position, a build location wasn't found!

Also:
For this, and the previous 9.73 build is see this in the log:
Here HeerMedVehYard is solobuild ├óÔé¼┼ôdetected├óÔé¼┬Ø (eg another one is being constructed) and yet the CKeywordConstructionTask, still calls Build() with this, whyLOG wrote:
[09:28] < Frame: 17063 >next task?
[09:28] < Frame: 17063 >CKeywordConstructionTask::Init b_estore
[09:28] < Frame: 17063 >Given the go ahead :: HeerLightVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerGunyard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerAssaultyard
[09:28] < Frame: 17063 >Given the go ahead :: SupplyBunker
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build() :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build solobuild HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage wiping and creaiing the plan :: HeerMedVehYard
[09:28] < Frame: 17064 >issuing command in update()
[09:28] < Frame: 17064 >Command: ID: -60 Timeout: 20163 params: 560, 169.602, 1264, 0, source of command: CBuild
[09:28] < Frame: 17064 >issuing command in update() succeeded
[09:28] < Frame: 17065 >next task?

Something tells me the issue with solo and single build is not only with the BuildPlacement-thread overtaking a previous solobuilded search.
Btw: in case you are interested in the mutex solution, it├óÔé¼Ôäós from this article:
http://www.ddj.com/dept/cpp/184401518
Code: Select all
t = building->name;
trim(t);
tolowercase(t);
if(G->Cached->solobuilds.find(t)!= G->Cached->solobuilds.end()){
NLOG("CKeywordConstructionTask::Build G->Cached->solobuilds.find(name)!= G->Cached->solobuilds.end()");
G->L.print("CKeywordConstructionTask::Build solobuild " + building->name);
if(G->Actions->Repair(unit,G->Cached->solobuilds[t])){// One is already being built, change to a repair order to go help it!
End();
return;
}
}
OK I got the message,
the problem is still due to multithreading (though I do not know what the ├óÔé¼┼ôEnd()├óÔé¼┬Ø statement does)
So basically in the log:
Anyway I owe you solution nr 2, though I├óÔé¼Ôäóm not very optimistic about this one.
- In fact I would suggest you do NOT spend time implementing this unless you think it has a good chance of success.
Solution nr 2.
(in the below, target refers to a unit that ntai tries to construct)
When CKeywordConstructionTask::Build() is called, it will first do the normal solobuild check.
After this it will check if the target is in solobuild list, and if, then always push the target to solobuld cache, this way making a pre-emptive reservation.
Then, if necessary, run the build algorithm thread.
If the build algorithm returns a UpVector or other BadPos messages then CKeywordConstructionTask will clear the solobuild cache item for the target. This way making it possible for other constructores to try to build the target again.
However the above has a big hole!
What happens if the construction unit is destroyed during build placement hunting (thread), then the solobuild will never be cleared!
The solution is to have UnitDestroyed remove this target from the solobuild cache.
But since UnitDestroyed does not know the target name, the solobuild cache will have to be expanded with a new element: the unit number of the constructor constructing the target.
This means that every part of ntai code that pushed a new element to the solobuild cache, must also push the unitnumber of the constructor to the cache.

So basically in the log:
Just after the ├óÔé¼┼ôBuild() solobuild├óÔé¼┬Ø, another thread must have overtaken, and the ├óÔé¼┼ôGiven the go ahead├óÔé¼┬Ø must come from this thread.[09:28] < Frame: 17063 >CKeywordConstructionTask::Build() :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::Build solobuild HeerMedVehYard
[09:28] < Frame: 17063 >Given the go ahead :: HeerMedVehYard
[09:28] < Frame: 17063 >CKeywordConstructionTask::RecieveMessage G->OrderRouter->GiveOrder(tc)== true :: HeerMedVehYard

Anyway I owe you solution nr 2, though I├óÔé¼Ôäóm not very optimistic about this one.
- In fact I would suggest you do NOT spend time implementing this unless you think it has a good chance of success.
Solution nr 2.
(in the below, target refers to a unit that ntai tries to construct)
When CKeywordConstructionTask::Build() is called, it will first do the normal solobuild check.
After this it will check if the target is in solobuild list, and if, then always push the target to solobuld cache, this way making a pre-emptive reservation.
Then, if necessary, run the build algorithm thread.
If the build algorithm returns a UpVector or other BadPos messages then CKeywordConstructionTask will clear the solobuild cache item for the target. This way making it possible for other constructores to try to build the target again.
However the above has a big hole!
What happens if the construction unit is destroyed during build placement hunting (thread), then the solobuild will never be cleared!
The solution is to have UnitDestroyed remove this target from the solobuild cache.
But since UnitDestroyed does not know the target name, the solobuild cache will have to be expanded with a new element: the unit number of the constructor constructing the target.
This means that every part of ntai code that pushed a new element to the solobuild cache, must also push the unitnumber of the constructor to the cache.
not necessarily.
If I specify the unit id of the unit being built for solobuild as -1, and switch a flag in the task then the task can listen for unitdeath and perform the removal if the value is still -1.
If the unit does manage to start construction though, then the targets ID will overwrite the -1.
This would eb an ideal solution because threading isnt the only delay were this could occur, ti could occur while the units enroute to the construction site.
If I specify the unit id of the unit being built for solobuild as -1, and switch a flag in the task then the task can listen for unitdeath and perform the removal if the value is still -1.
If the unit does manage to start construction though, then the targets ID will overwrite the -1.
This would eb an ideal solution because threading isnt the only delay were this could occur, ti could occur while the units enroute to the construction site.
Ok I├óÔé¼Ôäóm going back to solution 1 for a moment.
In the below I made two important assumptions:
First the boost::mutex MUST be followed by a test of solobuild, which sends a UpVector message and do return; (return from thread). Also the boost:mutex must be ├óÔé¼┼ôearly├óÔé¼┬Ø (first) in the thread, so no ├óÔé¼┼ôGiveorder├óÔé¼┬Ø is messaged from this thread before the boost:mutex is performed.
Second the ├óÔé¼┼ôtarget.id├óÔé¼┬Ø is the same number for every corsolar ever built in the game, and NOT a unit number.
Said in another way: the target.id is related directly to the target.name (corsolar). This is on a one-to-one basis eg. exactly one target.id for each target name.
eg:
This second assumption is VERY important. Is it correct in respect to the current implimentation? 
In the below I made two important assumptions:
Code: Select all
struct GetBuildPlacement
{
GetBuildPlacement(int unit,pos unitpos,unitdef UnitDef,int target,int spacing) : unit(unit){}
void operator()()
{
boost::mutex::scoped_lock lock(io_mutex[target.id]);
if ((OKBuildSelection(targetname[targetid]) == false)
// Code here to send UpVector and end (terminate) thread)
}
Second the ├óÔé¼┼ôtarget.id├óÔé¼┬Ø is the same number for every corsolar ever built in the game, and NOT a unit number.
Said in another way: the target.id is related directly to the target.name (corsolar). This is on a one-to-one basis eg. exactly one target.id for each target name.
eg:
Code: Select all
First corsolar build has unitnumber 235 and target.id: 344
Second corsolar build has unitnumber 244 and target.id 344
First corllt build has unitnumber 254 and target.id 235
Second corllt build has unitnumber 276 and target.id 235
321├óÔé¼Ôäóth corllt build has unitnumber xxx and target.id 235

Like this?
taken from the build already uploaded and tested by you.
Code: Select all
void operator()(){
boost::mutex::scoped_lock lock(io_mutex[building->id]);
if(!reciever->IsValid()) return;
CUBuild b;
b.Init(G,builder,0);
if(!b.OkBuildSelection(building->name)){
CMessage m("buildposition");
m.AddParameter(UpVector);
reciever->RecieveMessage(m);
return;
}