Necroing a sticky? More likely than you think.LOrDo wrote:Epic freaking ressurection?
Global AI Interface
Moderators: hoijui, Moderators
Re: Global AI Interface
Re: Global AI Interface
if fifteen months is epic....
http://spring.clan-sy.com/phpbb/viewtop ... 19#p241219
http://spring.clan-sy.com/phpbb/viewtop ... 19#p241219
- Aether_0001
- Posts: 228
- Joined: 25 Feb 2008, 03:41
Re: Global AI Interface
That's a huge list, so i didn't read all of it. I have one, but i'm not sure if it has been said before: DPS/cost ratio. It would help the AI calculate which units would be best to spam and kill things with.
Re: Global AI Interface
Since lua based mods came along, DPS is not something you can calculate from unit/weapon properties...
Re: Global AI Interface
One missing callback feature is the ability to give away resources (metal/energy), and units. I'm currently building AIs that work with allies, it would be nice to be able to do this, given that human players can.
On that note make sure UnitCreated() and UnitFinished() are called when a unit is given to an AI player, and if transferring units is allowed, make sure UnitDestroyed() gets called too.
I realize this thread is pretty old now: is this still the right place to post such suggestions to the devs?
On that note make sure UnitCreated() and UnitFinished() are called when a unit is given to an AI player, and if transferring units is allowed, make sure UnitDestroyed() gets called too.
I realize this thread is pretty old now: is this still the right place to post such suggestions to the devs?
Re: Global AI Interface
AI's are told when a unit is given through the HandleEvent() call-in,
but you have to call UnitCreated() and UnitFinished() manually. I'm
not sure why you'd want UnitDestroyed() to be called when sharing
units though, since the AI that performs the sharing action already
knows about their impending "destruction" (unless the command
fails for some reason, but I think the chance of that happening can
be neglected).
Sharing resources sounds like a very good addition, would a simple
callback suffice for your needs?
PS. yeah IMO using this thread is fine, it's still about the global AI
interface after all.
but you have to call UnitCreated() and UnitFinished() manually. I'm
not sure why you'd want UnitDestroyed() to be called when sharing
units though, since the AI that performs the sharing action already
knows about their impending "destruction" (unless the command
fails for some reason, but I think the chance of that happening can
be neglected).

Sharing resources sounds like a very good addition, would a simple
Code: Select all
void ShareResource(int receivingTeam, int type, float amount);
PS. yeah IMO using this thread is fine, it's still about the global AI
interface after all.
Re: Global AI Interface
Thanks for the fast response!
since that's in keeping with
and
where the player/zone are the last argument, and the object sent/received is in the first. Also, using the word Send rather than Share makes it a little clearer which way the resource is going. Incidentally, maybe GotChatMsg and SendTextMsg should be a bit more uniform - either both use Chat or Text, but not mixed. I'm also pretty fond of dual words used maybe GotChatMsg should become ReceiveChatMsg (since send and receive go together).
Finally, is there any good reason why ChangeTeamEvent is the way to handle a unit transfer? Shouldn't it be like receiving a message: having a RecieveUnit() function seems more natural to me.
Ah, I assumed the HandleEvent function was still unimplemented, the source has a comment next to it that's a bit misleading:Kloot wrote:AI's are told when a unit is given through the HandleEvent() call-in
Code: Select all
virtual int HandleEvent (int msg, const void *data)=0; // general messaging function to be used for future API extensions.
I'm guessing this means I detect a ChangeTeamEvent in the HandleEvent function, and act accordingly ... fine :)Kloot wrote: but you have to call UnitCreated() and UnitFinished() manually.
Well, I thought it would be good practice - when a unit disappears from a team, the callback mechanism is usually through UnitDestroyed(); having this called when a transfer is done would mean that whatever cleanup is normally done would be performed as per normal. You're right though, the UnitDestroyed() could be manually called if so desired, since some side-effects might not be wanted after a transfer.Kloot wrote: I'm
not sure why you'd want UnitDestroyed() to be called when sharing
units though, since the AI that performs the sharing action already
knows about their impending "destruction".![]()
Yes, that would be fine, though I recommendKloot wrote: Sharing resources sounds like a very good addition, would a simplecallback suffice for your needs?Code: Select all
void ShareResource(int receivingTeam, int type, float amount);
Code: Select all
virtual void SendResource(int type, float amount, int receivingPlayer)
virtual void SendUnit(int unit, int receivingPlayer)
Code: Select all
virtual void SendTextMsg(const char* text, int zone)
Code: Select all
virtual void GotChatMsg(const char* msg,int player)=0;
Finally, is there any good reason why ChangeTeamEvent is the way to handle a unit transfer? Shouldn't it be like receiving a message: having a RecieveUnit() function seems more natural to me.
Re: Global AI Interface
The interface doesn't have one strict naming convention no.
But,
because it's an interface, renaming all those functions would affect
existing AI's, so they're pretty much just left alone.
The HandleEvent() call-in was added so that the interface could be
extended in the future without breaking binary compatibility all the
time (which requires AI's to be recompiled), but there haven't been
many since then. ChangeTeamEvent was simply one of the few new
extensions that made use of it.

because it's an interface, renaming all those functions would affect
existing AI's, so they're pretty much just left alone.
The HandleEvent() call-in was added so that the interface could be
extended in the future without breaking binary compatibility all the
time (which requires AI's to be recompiled), but there haven't been
many since then. ChangeTeamEvent was simply one of the few new
extensions that made use of it.
Re: Global AI Interface
CAICallback::SendStartPos(bool ready, float3 pos)
but no
IAICallback::SendStartPos(bool ready, float3 pos)
how is it meant to be used?
has it to do with struct AIHCSendStartPos (in IAICallback.h)?
but no
IAICallback::SendStartPos(bool ready, float3 pos)
how is it meant to be used?
has it to do with struct AIHCSendStartPos (in IAICallback.h)?
Re: Global AI Interface
Like this:
Code: Select all
AIHCSendStartPos myStartPos;
myStartPos.ready = true;
myStartPos.pos = float3(1.0f, 2.0f, 3.0f);
cb->HandleCommand(AIHCSendStartPosId, &myStartPos);
Re: Global AI Interface
UFF!
thanks!
thanks!
Re: Global AI Interface
is it true, that UnitDefs never change during a game?
cause if so, it would be good to load all their values into Java at the beginning. This would save a JNI call every time one wants to fetch anything from a UnitDef.
an other, comparable question, concerning float3:
should the Java interface spit out and accept something like Vector3f instead of float3, so the implementation can do vector calculations Java internally, without JNI calls?
(edit: added second part about float3)
cause if so, it would be good to load all their values into Java at the beginning. This would save a JNI call every time one wants to fetch anything from a UnitDef.
an other, comparable question, concerning float3:
should the Java interface spit out and accept something like Vector3f instead of float3, so the implementation can do vector calculations Java internally, without JNI calls?
(edit: added second part about float3)
Re: Global AI Interface
That's a good question -- I've assumed that the answer is yes, but confirmation from up above would be good!
I think that the Java interface ought to be able to handle float3 calculations itself, so yes, a Vector3f would make sense. If this wasn't provided, I'd expect that people would start implementing their own anyway, so it may as well be supported at the JNI level.
I think that the Java interface ought to be able to handle float3 calculations itself, so yes, a Vector3f would make sense. If this wasn't provided, I'd expect that people would start implementing their own anyway, so it may as well be supported at the JNI level.
Re: Global AI Interface
ok...
am currently working on the Vector3f thing.
some things that float3 supports are not available in Vector3f, namely:
i could write/use a class that extends Vector3, and adds these things.
the max-positions get set when the map is loaded, so i could set them when the AI loads, as the map is loaded already then.
so this seems to be possible, with .. in my eyes.. better overall performance in the end, maybe.
The problem with UnitDef is...
whenever something changes in UnitDef, i have to adjust the Java class as well then, as i have to write one by hand, which reflects the C++ one, if i want to keep the values in Java.
am currently working on the Vector3f thing.
some things that float3 supports are not available in Vector3f, namely:
Code: Select all
float Distance2D();
float Length2D();
float SqLength2D(); // squared length
static float maxxpos
static float maxypos
static float maxzpos
bool CheckInBounds()
the max-positions get set when the map is loaded, so i could set them when the AI loads, as the map is loaded already then.
so this seems to be possible, with .. in my eyes.. better overall performance in the end, maybe.
The problem with UnitDef is...
whenever something changes in UnitDef, i have to adjust the Java class as well then, as i have to write one by hand, which reflects the C++ one, if i want to keep the values in Java.
Re: Global AI Interface
Yes a UnitDef *should* be constant and never change once it's set, however when trepan was fiddling with lua gadgets he added the ability to change unit definitions via lua.
So far AIs are not notified when they change and AIs work under the assumption that they never change. I don't know of anyone that uses the callbacks in lua to change unit definition unless they're modders and they're using it for development purposes.
If possible extend Vector3f as float3 and give it an identical interface/API so its easier to shift code across between java and c++
So far AIs are not notified when they change and AIs work under the assumption that they never change. I don't know of anyone that uses the callbacks in lua to change unit definition unless they're modders and they're using it for development purposes.
If possible extend Vector3f as float3 and give it an identical interface/API so its easier to shift code across between java and c++
Re: Global AI Interface
thanks AF
(will do that with Vector3f)
well...
With UnitDef...
i think i will do it this way:
when the first AI is loaded, it loads all UnitDefs. this list will contains UnitDef Java objects, which call native methods whenever something is requested from them. then i'll make an other list, containing JUnitDef objects, which have fields (in Java) for all the things contained in a UnitDef. and then fill second lit with values gotten from the first one.
if .. somehow, somewhen, it will be needed to get access to changed values, it could still be done relatively easy through the UnitDef objects, which would then call to native.
(will do that with Vector3f)
well...
With UnitDef...
i think i will do it this way:
when the first AI is loaded, it loads all UnitDefs. this list will contains UnitDef Java objects, which call native methods whenever something is requested from them. then i'll make an other list, containing JUnitDef objects, which have fields (in Java) for all the things contained in a UnitDef. and then fill second lit with values gotten from the first one.
if .. somehow, somewhen, it will be needed to get access to changed values, it could still be done relatively easy through the UnitDef objects, which would then call to native.
Re: Global AI Interface
the values in UnitResourceInfo do change frequently, right?
Re: Global AI Interface
I would assume so depending on which unit is being queried
Re: Global AI Interface
I know I'm coming to this discussion late, and that it's already been taken over by a 5 page rant on the merits of disclosure but since I intend to begin work on a Spring AI later this year I would like to see a useable API in place so I can work mostly in Lua.
I am currently working on an AI framework for Supreme Commander. SupCom exposes a lot of the engine to Lua so it serves as a good example (though it is far from perfect):
SupCom Lua Docs
Unfortunately it is only useful as an overview because it is almost entirely undocumented. Most of my work has been through guessing and trial-and-error.
I realise a lot of the Spring AI code is currently in C++ however I would like to see these functions included as a Lua interface as well. It should work something like this:
event -> C_Callback -> Lua_Callback.
Both the C++ and Lua interfaces should be passed the same data (or nearest approximation). Most of the time this will be at minimum a pointer to a unit. I also recommend the callbacks be implemented, where possible, as methods to avoid name conflicts between, unit, squad and army functions.
Anyway here are my recommendations. I've stuck to common functions that require the speed of C++. More complex functions can be implemented by the AI author in C++ or Lua as needed. My apologises if some/most of these have been suggested or implemented already:
UNIT CALLBACKS
Unit callbacks can be used for unit or squad AIs. They should account for the possibility that the events may be triggered by a player or AI sharing control of the army (support AI). In other words don't assume the AI will know why the event happened (particularly with OnCommand). In the case of events under AI control (like issuing orders) the AI must be able to abort the event by returning false from the handler. In some cases (like OnHit) the army may not have seen the attacker on radar. In this case the unit passed as 'attacker' should be a special value to indicate 'unknown'.
OnCreate(unit) -- Unit built or spawned
OnActivate(unit) -- Unit switched on
OnDeactivate(unit) -- Unit switched off
OnCaptured(unit) -- Unit captured from enemy
OnKilled(unit, killer)
OnHit(unit, attacker) -- attacker may be unknown
OnFire(unit, target) -- target may be ground
OnMove(unit) -- not just move order but any reason (like getting out the way)
OnBuild(unit, unitBeingBuilt) -- For shift/alt build call once per structure
OnBuildComplete(unit, unitBeingBuilt)
OnBuildCancelled(unit, unitBeingBuilt)
OnOrder(unit, order, args...)
OnOrderComplete(unit, order_id)
OnOrderCancelled(unit, order_id)
OnIdle(unit) -- Idle means orders queue finished
OnGrouped(unit,group_id)
OnUngrouped(unit,group_id)
OnLoad(unit, transport)
OnUnload(unit, transport)
ARMY CALLBACKS
OnFrame() -- called when game state changes
OnStartGame()
OnEndGame()
OnEnemySighted(enemy)
OnEnemyDetectedByRadar(blip)
OnAllianceChanged(army1, army2, old, new) -- when diplomacy happens
OnArmyKilled(army)
OnIntelUpdate() -- every time radar/jamming is calculated
INTEL FUNCTIONS
Functions to query intel data from the engine. Intel 'maps' should be defined as a grid of 100m2 rectangles (cells). The functions below should combine in many ways to form precise queries like ('find the combined threat level of all anti-air units or structures in the top right corner of the map owned by enemy armies') or ('find all enemies along this route')
GetThreatMap(type) -- Array containing each map grid square with a relative threat level. Type should be something like 'air', 'surface' or 'sub' to indicate what units are being threatened.
GetResourceMap(type) -- Array containing the desirability of a location for a given resource type (metal or energy or mod-defined type)
FilterMapByThreat(map, level, comparison) -- Filter a threat or resource map by threat level, ie. only return cells matching the query
FilterMapByTerrainType(map, type)
FilterMapByRect(map, rect) -- return cells inside or touching rect
FilterMapByCircle(map, pos, radius) -- return cells inside or touching circle
FilterMapByLine(map, pos1, pos2) -- only return cells that line passes through
FilterMapByPoint(pos) -- given a standard position vector return the cell surrounding it.
SortUnitsByThreat(map, dir) -- where direction is 'ASC' or 'DESC'
SortMapByThreat(map, dir) -- Reorder map cells based on threat instead of grid position
UNIT SELECTION
The AI will need to find or group units according to a range of properties. SupCom enumerates these properties in the unit definition and then allows
them to be combined into queries eg. TECH2 + LAND - ANTIAIR gives you all tier 2 tanks except anti-air.
GetUnitsByType(type) -- All units matching category or class id.
GetUnitsInRect(type, rect) -- List of units in a rectangle
GetUnitsInCircle(type, pos, radius) -- List of units in a circle
GetNearestUnit(unit_list) -- Get closest unit based on category or class id.
FilterUnitsByType(unit_list, type) -- Return units of 'type' only. Types should be 'land','water','air' or a more complex enumeration such as 'structure|missile|water' (a floating missile launcher).
FilterUnitsByAllegiance(unit_list, alliegance) -- Return only 'allied' or 'enemy' or 'own' units
FilterUnitsByThreat(unit_list, level, comparison) -- Return units related to the threat level by comparison where comparison is a string operator like '=', '>', etc..
PATHING
Path calculations are always slow. The AI should always leave it to the engine.
CanMoveTo(unit, pos, terrain_only) -- true if unit can reach destination (optionally ignoring barriers and structures)
GetRouteTo(unit, pos, terrain_only) -- Return a set of lines indicating best guess at unit route to reach pos.
GetETA(unit, pos) -- best guess at time to arrive at pos (assuming uninterrupted move)
GetTerrainType(pos) -- given a location return 'land' or 'water'
GetTerrainHeight(pos)
GetNearestTerrainByType(type) -- given 'land' or 'water' get the closest point. Useful for transports or starting naval construction.
I am currently working on an AI framework for Supreme Commander. SupCom exposes a lot of the engine to Lua so it serves as a good example (though it is far from perfect):
SupCom Lua Docs
Unfortunately it is only useful as an overview because it is almost entirely undocumented. Most of my work has been through guessing and trial-and-error.
I realise a lot of the Spring AI code is currently in C++ however I would like to see these functions included as a Lua interface as well. It should work something like this:
event -> C_Callback -> Lua_Callback.
Both the C++ and Lua interfaces should be passed the same data (or nearest approximation). Most of the time this will be at minimum a pointer to a unit. I also recommend the callbacks be implemented, where possible, as methods to avoid name conflicts between, unit, squad and army functions.
Anyway here are my recommendations. I've stuck to common functions that require the speed of C++. More complex functions can be implemented by the AI author in C++ or Lua as needed. My apologises if some/most of these have been suggested or implemented already:
UNIT CALLBACKS
Unit callbacks can be used for unit or squad AIs. They should account for the possibility that the events may be triggered by a player or AI sharing control of the army (support AI). In other words don't assume the AI will know why the event happened (particularly with OnCommand). In the case of events under AI control (like issuing orders) the AI must be able to abort the event by returning false from the handler. In some cases (like OnHit) the army may not have seen the attacker on radar. In this case the unit passed as 'attacker' should be a special value to indicate 'unknown'.
OnCreate(unit) -- Unit built or spawned
OnActivate(unit) -- Unit switched on
OnDeactivate(unit) -- Unit switched off
OnCaptured(unit) -- Unit captured from enemy
OnKilled(unit, killer)
OnHit(unit, attacker) -- attacker may be unknown
OnFire(unit, target) -- target may be ground
OnMove(unit) -- not just move order but any reason (like getting out the way)
OnBuild(unit, unitBeingBuilt) -- For shift/alt build call once per structure
OnBuildComplete(unit, unitBeingBuilt)
OnBuildCancelled(unit, unitBeingBuilt)
OnOrder(unit, order, args...)
OnOrderComplete(unit, order_id)
OnOrderCancelled(unit, order_id)
OnIdle(unit) -- Idle means orders queue finished
OnGrouped(unit,group_id)
OnUngrouped(unit,group_id)
OnLoad(unit, transport)
OnUnload(unit, transport)
ARMY CALLBACKS
OnFrame() -- called when game state changes
OnStartGame()
OnEndGame()
OnEnemySighted(enemy)
OnEnemyDetectedByRadar(blip)
OnAllianceChanged(army1, army2, old, new) -- when diplomacy happens
OnArmyKilled(army)
OnIntelUpdate() -- every time radar/jamming is calculated
INTEL FUNCTIONS
Functions to query intel data from the engine. Intel 'maps' should be defined as a grid of 100m2 rectangles (cells). The functions below should combine in many ways to form precise queries like ('find the combined threat level of all anti-air units or structures in the top right corner of the map owned by enemy armies') or ('find all enemies along this route')
GetThreatMap(type) -- Array containing each map grid square with a relative threat level. Type should be something like 'air', 'surface' or 'sub' to indicate what units are being threatened.
GetResourceMap(type) -- Array containing the desirability of a location for a given resource type (metal or energy or mod-defined type)
FilterMapByThreat(map, level, comparison) -- Filter a threat or resource map by threat level, ie. only return cells matching the query
FilterMapByTerrainType(map, type)
FilterMapByRect(map, rect) -- return cells inside or touching rect
FilterMapByCircle(map, pos, radius) -- return cells inside or touching circle
FilterMapByLine(map, pos1, pos2) -- only return cells that line passes through
FilterMapByPoint(pos) -- given a standard position vector return the cell surrounding it.
SortUnitsByThreat(map, dir) -- where direction is 'ASC' or 'DESC'
SortMapByThreat(map, dir) -- Reorder map cells based on threat instead of grid position
UNIT SELECTION
The AI will need to find or group units according to a range of properties. SupCom enumerates these properties in the unit definition and then allows
them to be combined into queries eg. TECH2 + LAND - ANTIAIR gives you all tier 2 tanks except anti-air.
GetUnitsByType(type) -- All units matching category or class id.
GetUnitsInRect(type, rect) -- List of units in a rectangle
GetUnitsInCircle(type, pos, radius) -- List of units in a circle
GetNearestUnit(unit_list) -- Get closest unit based on category or class id.
FilterUnitsByType(unit_list, type) -- Return units of 'type' only. Types should be 'land','water','air' or a more complex enumeration such as 'structure|missile|water' (a floating missile launcher).
FilterUnitsByAllegiance(unit_list, alliegance) -- Return only 'allied' or 'enemy' or 'own' units
FilterUnitsByThreat(unit_list, level, comparison) -- Return units related to the threat level by comparison where comparison is a string operator like '=', '>', etc..
PATHING
Path calculations are always slow. The AI should always leave it to the engine.
CanMoveTo(unit, pos, terrain_only) -- true if unit can reach destination (optionally ignoring barriers and structures)
GetRouteTo(unit, pos, terrain_only) -- Return a set of lines indicating best guess at unit route to reach pos.
GetETA(unit, pos) -- best guess at time to arrive at pos (assuming uninterrupted move)
GetTerrainType(pos) -- given a location return 'land' or 'water'
GetTerrainHeight(pos)
GetNearestTerrainByType(type) -- given 'land' or 'water' get the closest point. Useful for transports or starting naval construction.
Re: Global AI Interface
You might want to take a ook at System/EventHandler.h and
System/EventClient.h in SVN. The lua scripts are subclassed
from EventClient, which could also be done for AIs (that's why
LuaCallInHandler was moved to EventHandler).
The version in SVN only separates permissions based on
the 'synced' state. My local version separates permissions
based on canCtrlSynced and canReadUnsynced (such that
you could add EventClients that handle both synced and
unsynced data).
Something else to look at:
http://spring.clan-sy.com/wiki/Lua_Scripting
The interface does not use objects for units, etc... That was
done intentionally to keep things simple (and fast).
System/EventClient.h in SVN. The lua scripts are subclassed
from EventClient, which could also be done for AIs (that's why
LuaCallInHandler was moved to EventHandler).
The version in SVN only separates permissions based on
the 'synced' state. My local version separates permissions
based on canCtrlSynced and canReadUnsynced (such that
you could add EventClients that handle both synced and
unsynced data).
Something else to look at:
http://spring.clan-sy.com/wiki/Lua_Scripting
The interface does not use objects for units, etc... That was
done intentionally to keep things simple (and fast).