Oolite 1.91.0.7646-241128-10e222e
Loading...
Searching...
No Matches
AI Class Reference

#include <AI.h>

+ Inheritance diagram for AI:
+ Collaboration diagram for AI:

Instance Methods

(NSString *) - name
 
(NSString *) - associatedJS
 
(NSString *) - state
 
(void) - setStateMachine:withJSScript:
 
(void) - setState:
 
(void) - setStateMachine:afterDelay:
 
(void) - setState:afterDelay:
 
(id) - initWithStateMachine:andState:
 
(ShipEntity *) - owner
 
(void) - setOwner:
 
(void) - preserveCurrentStateMachine
 
(void) - restorePreviousStateMachine
 
(BOOL) - hasSuspendedStateMachines
 
(void) - exitStateMachineWithMessage:
 
(NSUInteger) - stackDepth
 
(void) - reactToMessage:context:
 
(void) - takeAction:
 
(void) - think
 
(void) - message:
 
(void) - dropMessage:
 
(NSSet *) - pendingMessages
 
(void) - debugDumpPendingMessages
 
(void) - setNextThinkTime:
 
(OOTimeAbsolute- nextThinkTime
 
(void) - setThinkTimeInterval:
 
(OOTimeDelta- thinkTimeInterval
 
(void) - clearStack
 
(void) - clearAllData
 
(void) - dumpState
 
(id) - init [implementation]
 
(void) - dealloc [implementation]
 
(NSString *) - descriptionComponents [implementation]
 
(NSString *) - shortDescriptionComponents [implementation]
 
(void) - reportStackOverflow [implementation]
 
(NSString *) - inspBasicIdentityLine [implementation]
 
(NSArray *) - debugInspectorModules [implementation]
 
(void) - performDeferredCall:withObject:afterDelay: [implementation]
 
(void) - refreshOwnerDesc [implementation]
 
(void) - directSetStateMachine:name: [implementation]
 
(void) - directSetState: [implementation]
 
(NSDictionary *) - loadStateMachine:jsName: [implementation]
 
(NSDictionary *) - cleanHandlers:forState:stateMachine: [implementation]
 
(NSArray *) - cleanActions:forHandler:state:stateMachine: [implementation]
 
- Instance Methods inherited from OOWeakRefObject
(id) - weakSelf
 
(id) - weakRetain [implementation]
 
(void) - weakRefDied: [implementation]
 
- Instance Methods inherited from <OOWeakReferenceSupport>
(id) - OO_RETURNS_RETAINED
 

Class Methods

(AI *) + currentlyRunningAI
 
(NSString *) + currentlyRunningAIDescription
 
(void) + deferredCallTrampolineWithInfo: [implementation]
 

Private Attributes

id _owner
 
NSString * ownerDesc
 
NSDictionary * stateMachine
 
NSString * stateMachineName
 
NSString * currentState
 
NSMutableSet * pendingMessages
 
NSMutableArray * aiStack
 
OOTimeAbsolute nextThinkTime
 
OOTimeDelta thinkTimeInterval
 
NSString * jsScript
 

Additional Inherited Members

- Protected Attributes inherited from OOWeakRefObject
OOWeakReferenceweakSelf
 

Detailed Description

Definition at line 37 of file AI.h.

Method Documentation

◆ associatedJS

- (NSString *) associatedJS

Definition at line 87 of file AI.m.

371{
372 return [stateMachine objectForKey:@"jsScript"];
373}

◆ cleanActions:forHandler:state:stateMachine:

- (NSArray *) cleanActions: (NSArray *) actions
forHandler: (NSString *) handlerKey
state: (NSString *) stateKey
stateMachine: (NSString *) smName 
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

914 :(NSArray *)actions forHandler:(NSString *)handlerKey state:(NSString *)stateKey stateMachine:(NSString *)smName
915{
916 NSEnumerator *actionEnum = nil;
917 NSString *action = nil;
918 NSRange spaceRange;
919 NSString *selector = nil;
920 id aliasedSelector = nil;
921 NSMutableArray *result = nil;
922 static NSSet *whitelist = nil;
923 static NSDictionary *aliases = nil;
924 NSArray *whitelistArray1 = nil;
925 NSArray *whitelistArray2 = nil;
926
927 if (whitelist == nil)
928 {
929 whitelistArray1 = [[ResourceManager whitelistDictionary] oo_arrayForKey:@"ai_methods"];
930 if (whitelistArray1 == nil) whitelistArray1 = [NSArray array];
931 whitelistArray2 = [[ResourceManager whitelistDictionary] oo_arrayForKey:@"ai_and_action_methods"];
932 if (whitelistArray2 != nil) whitelistArray1 = [whitelistArray1 arrayByAddingObjectsFromArray:whitelistArray2];
933
934 whitelist = [[NSSet alloc] initWithArray:whitelistArray1];
935 aliases = [[[ResourceManager whitelistDictionary] oo_dictionaryForKey:@"ai_method_aliases"] retain];
936 }
937
938 result = [NSMutableArray arrayWithCapacity:[actions count]];
939 for (actionEnum = [actions objectEnumerator]; (action = [actionEnum nextObject]); )
940 {
941 if (![action isKindOfClass:[NSString class]])
942 {
943 OOLogWARN(@"ai.invalidFormat.action", @"An action in handler \"%@\" for state \"%@\" in AI \"%@\" is not a string, ignoring.", handlerKey, stateKey, smName);
944 continue;
945 }
946
947 // Trim spaces from beginning and end.
948 action = [action stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
949
950 // Cut off parameters.
951 spaceRange = [action rangeOfString:@" "];
952 if (spaceRange.location == NSNotFound) selector = action;
953 else selector = [action substringToIndex:spaceRange.location];
954
955 // Look in alias table.
956 aliasedSelector = [aliases objectForKey:selector];
957 if (aliasedSelector != nil)
958 {
959 if ([aliasedSelector isKindOfClass:[NSString class]])
960 {
961 // Change selector and action to use real method name.
962 selector = aliasedSelector;
963 if (spaceRange.location == NSNotFound) action = aliasedSelector;
964 else action = [aliasedSelector stringByAppendingString:[action substringFromIndex:spaceRange.location]];
965 }
966 else if ([aliasedSelector isKindOfClass:[NSArray class]] && [aliasedSelector count] != 0)
967 {
968 // Alias is complete expression, pretokenized in anticipation of a tokenized future.
969 action = [aliasedSelector componentsJoinedByString:@" "];
970 selector = [[aliasedSelector objectAtIndex:0] description];
971 }
972 }
973
974 // Check for selector in whitelist.
975 if (![whitelist containsObject:selector])
976 {
977 OOLog(@"ai.unpermittedMethod", @"Handler \"%@\" for state \"%@\" in AI \"%@\" uses \"%@\", which is not a permitted AI method.", handlerKey, stateKey, smName, selector);
978 continue;
979 }
980
981 [result addObject:action];
982 }
983
984 // Return immutable copy.
985 return [[result copy] autorelease];
986}
#define OOLogWARN(class, format,...)
Definition OOLogging.h:113
#define OOLog(class, format,...)
Definition OOLogging.h:88
unsigned count
return nil
NSDictionary * whitelistDictionary()

◆ cleanHandlers:forState:stateMachine:

- (NSDictionary *) cleanHandlers: (NSDictionary *) handlers
forState: (NSString *) stateKey
stateMachine: (NSString *) smName 
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

888 :(NSDictionary *)handlers forState:(NSString *)stateKey stateMachine:(NSString *)smName
889{
890 NSEnumerator *handlerEnum = nil;
891 NSString *handlerKey = nil;
892 NSArray *handlerActions = nil;
893 NSMutableDictionary *result = nil;
894
895 result = [NSMutableDictionary dictionaryWithCapacity:[handlers count]];
896 for (handlerEnum = [handlers keyEnumerator]; (handlerKey = [handlerEnum nextObject]); )
897 {
898 handlerActions = [handlers objectForKey:handlerKey];
899 if (![handlerActions isKindOfClass:[NSArray class]])
900 {
901 OOLogWARN(@"ai.invalidFormat.handler", @"Handler \"%@\" for state \"%@\" in AI \"%@\" is not an array, ignoring.", handlerKey, stateKey, smName);
902 continue;
903 }
904
905 handlerActions = [self cleanActions:handlerActions forHandler:handlerKey state:stateKey stateMachine:smName];
906 [result setObject:handlerActions forKey:handlerKey];
907 }
908
909 // Return immutable copy.
910 return [[result copy] autorelease];
911}

◆ clearAllData

- (void) clearAllData

Definition at line 400 of file AI.m.

692{
693 [aiStack removeAllObjects];
694 [pendingMessages removeAllObjects];
695
696 nextThinkTime += 36000.0; // should dealloc in under ten hours!
697}
OOTimeAbsolute nextThinkTime
Definition AI.h:50

References AIStackElement::aiName, AIStackElement::back, AIStackElement::context, count, currentState, kOOLogException, kRecursionLimiter, AIStackElement::message, nil, NO_TARGET, OOLog, OOLogERR, owner, AIStackElement::owner, sCurrentlyRunningAI, sStack, AIStackElement::state, stateMachine, stateMachineName, and takeAction:.

+ Here is the call graph for this function:

◆ clearStack

- (void) clearStack

Definition at line 400 of file AI.m.

686{
687 [aiStack removeAllObjects];
688}

◆ currentlyRunningAI

+ (AI *) currentlyRunningAI

Definition at line 87 of file AI.m.

108{
109 return sCurrentlyRunningAI;
110}
static AI * sCurrentlyRunningAI
Definition AI.m:52

◆ currentlyRunningAIDescription

+ (NSString *) currentlyRunningAIDescription

Definition at line 87 of file AI.m.

114{
116 {
117 return [NSString stringWithFormat:@"%@ in state %@", [sCurrentlyRunningAI name], [sCurrentlyRunningAI state]];
118 }
119 else
120 {
121 return @"<no AI running>";
122 }
123}

◆ dealloc

- (void) dealloc
implementation

Reimplemented from OOWeakRefObject.

Definition at line 87 of file AI.m.

153{
154 if (sCurrentlyRunningAI == self)
155 {
157 }
158
167
168 [super dealloc];
169}
#define DESTROY(x)
Definition OOCocoa.h:77
NSString * currentState
Definition AI.h:45
NSDictionary * stateMachine
Definition AI.h:43
id _owner
Definition AI.h:40
NSMutableSet * pendingMessages
Definition AI.h:46
NSMutableArray * aiStack
Definition AI.h:48
NSString * ownerDesc
Definition AI.h:41
NSString * stateMachineName
Definition AI.h:44
NSString * jsScript
Definition AI.h:53

◆ debugDumpPendingMessages

- (void) debugDumpPendingMessages

Definition at line 400 of file AI.m.

640{
641 NSArray *sortedMessages = nil;
642 NSString *displayMessages = nil;
643
644 if ([pendingMessages count] > 0)
645 {
646 sortedMessages = [[pendingMessages allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
647 displayMessages = [sortedMessages componentsJoinedByString:@", "];
648 }
649 else
650 {
651 displayMessages = @"none";
652 }
653
654 OOLog(@"ai.debug.pendingMessages", @"Pending messages for AI %@: %@", [self descriptionComponents], displayMessages);
655}
NSString * descriptionComponents()
Definition AI.m:172

◆ debugInspectorModules

- (NSArray *) debugInspectorModules
implementation

Provided by category AI(OOAIDebugInspectorModule).

Definition at line 1 of file OOAIDebugInspectorModule.m.

103{
104 return [[super debugInspectorModules] arrayByAddingInspectorModuleOfClass:[OOAIDebugInspectorModule class]
105 forObject:(id)self];
106}

◆ deferredCallTrampolineWithInfo:

+ (void) deferredCallTrampolineWithInfo: (NSValue *) info
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

740 :(NSValue *)info
741{
743
744 if (info != nil)
745 {
746 assert(strcmp([info objCType], @encode(OOAIDeferredCallTrampolineInfo)) == 0);
747 [info getValue:&infoStruct];
748
749 [infoStruct.ai performSelector:infoStruct.selector withObject:infoStruct.parameter];
750
751 [infoStruct.ai release];
752 [infoStruct.parameter release];
753 }
754}

◆ descriptionComponents

- (NSString *) descriptionComponents
implementation

Definition at line 87 of file AI.m.

173{
174 return [NSString stringWithFormat:@"\"%@\" in state: \"%@\" for %@", stateMachineName, currentState, ownerDesc];
175}

◆ directSetState:

- (void) directSetState: (NSString *) state
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

791 :(NSString *)state
792{
793 if (currentState != state)
794 {
795 [currentState release];
796 currentState = [state copy];
797 }
798}

◆ directSetStateMachine:name:

- (void) directSetStateMachine: (NSDictionary *) newSM
name: (NSString *) name 
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

776 :(NSDictionary *)newSM name:(NSString *)name
777{
778 if (stateMachine != newSM)
779 {
780 [stateMachine release];
781 stateMachine = [newSM copy];
782 }
783 if (stateMachineName != name)
784 {
785 [stateMachineName release];
786 stateMachineName = [name copy];
787 }
788}

◆ dropMessage:

- (void) dropMessage: (NSString *) ms

Definition at line 400 of file AI.m.

620 :(NSString *)ms
621{
622 [pendingMessages removeObject:ms];
623}

◆ dumpState

- (void) dumpState

Definition at line 400 of file AI.m.

701{
702 OOLog(@"dumpState.ai", @"State machine name: %@", stateMachineName);
703 OOLog(@"dumpState.ai", @"Current state: %@", currentState);
704 OOLog(@"dumpState.ai", @"Next think time: %g", nextThinkTime);
705 OOLog(@"dumpState.ai", @"Next think interval: %g", thinkTimeInterval);
706}
OOTimeDelta thinkTimeInterval
Definition AI.h:51

◆ exitStateMachineWithMessage:

- (void) exitStateMachineWithMessage: (NSString *) message

Definition at line 87 of file AI.m.

296 :(NSString *)message
297{
298 if ([aiStack count] != 0)
299 {
300 [self restorePreviousStateMachine];
301 if (message == nil) message = @"RESTARTED";
302 [self reactToMessage:message context:@"suspended AI restart"];
303 }
304}

Referenced by ShipExitAI().

+ Here is the caller graph for this function:

◆ hasSuspendedStateMachines

- (BOOL) hasSuspendedStateMachines

Definition at line 87 of file AI.m.

291{
292 return [aiStack count] != 0;
293}

◆ init

- (id) init
implementation

Definition at line 87 of file AI.m.

127{
128 if ((self = [super init]))
129 {
130 nextThinkTime = INFINITY; // don't think for a while
132
133 stateMachineName = @"<no AI>"; // no initial brain
134 }
135
136 return self;
137}
#define AI_THINK_INTERVAL
Definition AI.h:31
id init()
Definition AI.m:126

◆ initWithStateMachine:andState:

- (id) initWithStateMachine: (NSString *) smName
andState: (NSString *) stateName 

Definition at line 87 of file AI.m.

140 :(NSString *)smName andState:(NSString *)stateName
141{
142 if ((self = [self init]))
143 {
144 if (smName != nil) [self setStateMachine:smName withJSScript:@"oolite-nullAI.js"];
145 if (stateName != nil) currentState = [stateName retain];
146 }
147
148 return self;
149}

◆ inspBasicIdentityLine

- (NSString *) inspBasicIdentityLine
implementation

Provided by category AI(OOAIDebugInspectorModule).

Definition at line 1 of file OOAIDebugInspectorModule.m.

96{
97 if ([self owner] != nil) return [NSString stringWithFormat:@"AI for %@", [[self owner] inspBasicIdentityLine]];
98 return [super inspBasicIdentityLine];
99}

◆ loadStateMachine:jsName:

- (NSDictionary *) loadStateMachine: (NSString *) smName
jsName: (NSString *) script 
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

801 :(NSString *)smName jsName:(NSString *)script
802{
803 NSDictionary *newSM = nil;
804 NSMutableDictionary *cleanSM = nil;
806 NSEnumerator *stateEnum = nil;
807 NSString *stateKey = nil;
808 NSDictionary *stateHandlers = nil;
809 NSAutoreleasePool *pool = nil;
810
811 if (![smName isEqualToString:@"nullAI.plist"])
812 {
813 // don't cache nullAI since they're different depending on associated JS AI
814 newSM = [cacheMgr objectForKey:smName inCache:@"AIs"];
815 if (newSM != nil && ![newSM isKindOfClass:[NSDictionary class]]) return nil; // catches use of @"nil" to indicate no AI found.
816 }
817
818 if (newSM == nil)
819 {
820 pool = [[NSAutoreleasePool alloc] init];
821 OOLog(@"ai.load", @"Loading and sanitizing AI \"%@\"", smName);
823 OOLogIndentIf(@"ai.load");
824
825 @try
826 {
827 // Load state machine and validate against whitelist.
828 NSString *aiPath = [ResourceManager pathForFileNamed:smName inFolder:@"AIs"];
829 if (aiPath != nil)
830 {
831 newSM = OODictionaryFromFile(aiPath);
832 }
833 if (newSM == nil)
834 {
835 [cacheMgr setObject:@"nil" forKey:smName inCache:@"AIs"];
836 NSString *fromString = @"";
837 if ([self state] != nil)
838 {
839 fromString = [NSString stringWithFormat:@" from %@:%@", [self name], [self state]];
840 }
841 OOLog(@"ai.load.failed.unknownAI", @"Can't switch AI for %@%@ to \"%@\" - could not load file.", [[self owner] shortDescription], fromString, smName);
842 return nil;
843 }
844
845 cleanSM = [NSMutableDictionary dictionaryWithCapacity:[newSM count]];
846
847 for (stateEnum = [newSM keyEnumerator]; (stateKey = [stateEnum nextObject]); )
848 {
849 stateHandlers = [newSM objectForKey:stateKey];
850 if (![stateHandlers isKindOfClass:[NSDictionary class]])
851 {
852 OOLogWARN(@"ai.invalidFormat.state", @"State \"%@\" in AI \"%@\" is not a dictionary, ignoring.", stateKey, smName);
853 continue;
854 }
855
856 stateHandlers = [self cleanHandlers:stateHandlers forState:stateKey stateMachine:smName];
857 [cleanSM setObject:stateHandlers forKey:stateKey];
858 }
859 [cleanSM setObject:script forKey:@"jsScript"];
860
861 // Make immutable.
862 newSM = [[cleanSM copy] autorelease];
863
864#if DEBUG_GRAPHVIZ
865 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"generate-ai-graphviz"])
866 {
867 GenerateGraphVizForAIStateMachine(newSM, smName);
868 }
869#endif
870
871 // Cache.
872 [cacheMgr setObject:newSM forKey:smName inCache:@"AIs"];
873 }
874 @finally
875 {
877 }
878
879 [newSM retain];
880 [pool release];
881 [newSM autorelease];
882 }
883
884 return newSM;
885}
void OOLogPushIndent(void)
Definition OOLogging.m:316
void OOLogPopIndent(void)
Definition OOLogging.m:340
#define OOLogIndentIf(class)
Definition OOLogging.h:101
NSDictionary * OODictionaryFromFile(NSString *path)
void setObject:forKey:inCache:(id inElement,[forKey] NSString *inKey,[inCache] NSString *inCacheKey)
id objectForKey:inCache:(NSString *inKey,[inCache] NSString *inCacheKey)
OOCacheManager * sharedCache()
NSString * pathForFileNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)

◆ message:

- (void) message: (NSString *) ms

Definition at line 400 of file AI.m.

600 :(NSString *)ms
601{
602 if ([[self owner] universalID] == NO_TARGET) return; // don't think until launched
603
604 if (EXPECT_NOT([pendingMessages count] > 32))
605 {
606 // Generate the error, but don't crash Oolite! Fixes bug #18055 - Pending message overflow for thargoids, -> crash !
607 OOLogERR(@"ai.message.failed.overflow", @"AI message \"%@\" received by '%@' AI while pending messages stack full; message discarded. Pending messages:\n%@", ms, ownerDesc, pendingMessages);
608 }
609 else
610 {
611 if (pendingMessages == nil)
612 {
613 pendingMessages = [[NSMutableSet alloc] init];
614 }
615 [pendingMessages addObject:ms];
616 }
617}
#define EXPECT_NOT(x)
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
@ NO_TARGET
Definition OOTypes.h:194
ShipEntity * owner()
Definition AI.m:184

◆ name

- (NSString *) name

Definition at line 87 of file AI.m.

365{
366 return [[stateMachineName retain] autorelease];
367}

Referenced by AI(OOAIDebugInspectorModule)::debugInspectorModules, and ShipGetProperty().

+ Here is the caller graph for this function:

◆ nextThinkTime

- (OOTimeAbsolute) nextThinkTime

◆ owner

- (ShipEntity *) owner

Definition at line 87 of file AI.m.

185{
186 ShipEntity *owner = [_owner weakRefUnderlyingObject];
187 if (owner == nil)
188 {
189 [_owner release];
190 _owner = nil;
191 }
192
193 return owner;
194}

Referenced by clearAllData, and AI(OOAIDebugInspectorModule)::debugInspectorModules.

+ Here is the caller graph for this function:

◆ pendingMessages

- (NSSet *) pendingMessages

◆ performDeferredCall:withObject:afterDelay:

- (void) performDeferredCall: (SEL) selector
withObject: (id) object
afterDelay: (NSTimeInterval) delay 
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

719 :(SEL)selector withObject:(id)object afterDelay:(NSTimeInterval)delay
720{
722 NSValue *info = nil;
723
724 if (selector != NULL)
725 {
726 infoStruct.ai = [self retain];
727 infoStruct.selector = selector;
728 infoStruct.parameter = object;
729
730 info = [[NSValue alloc] initWithBytes:&infoStruct objCType:@encode(OOAIDeferredCallTrampolineInfo)];
731
732 [[AI class] performSelector:@selector(deferredCallTrampolineWithInfo:)
733 withObject:info
734 afterDelay:delay];
735 [info release];
736 }
737}
Definition AI.h:38

◆ preserveCurrentStateMachine

- (void) preserveCurrentStateMachine

Definition at line 87 of file AI.m.

232{
233 if (stateMachine == nil) return;
234
235 if (aiStack == nil)
236 {
237 aiStack = [[NSMutableArray alloc] init];
238 }
239
240 if ([aiStack count] >= kStackLimiter)
241 {
242 [self reportStackOverflow];
243
244 [NSException raise:@"OoliteException"
245 format:@"AI stack overflow for %@", _owner];
246 }
247
248 OOPreservedAIStateMachine *preservedMachine = [[OOPreservedAIStateMachine alloc]
249 initWithStateMachine:stateMachine
250 name:stateMachineName
251 state:currentState
252 pendingMessages:pendingMessages
253 jsScript:[stateMachine objectForKey:@"jsScript"]];
254
255#ifndef NDEBUG
256 if ([[self owner] reportAIMessages]) OOLog(@"ai.stack.push", @"Pushing state machine for %@", self);
257#endif
258
259 [aiStack addObject:preservedMachine]; // PUSH
260
261 [preservedMachine release];
262}
@ kStackLimiter
Definition AI.m:40

◆ reactToMessage:context:

- (void) reactToMessage: (NSString *) message
context: (NSString *) debugContext 

Definition at line 400 of file AI.m.

404 :(NSString *) message context:(NSString *)debugContext
405{
406 unsigned i;
407 NSArray *actions = nil;
408 NSDictionary *messagesForState = nil;
409 ShipEntity *owner = [self owner];
410 static unsigned recursionLimiter = 0;
411 AI *previousRunning = sCurrentlyRunningAI;
412
413 /* CRASH in _freedHandler when called via -setState: __NSFireDelayedPerform (1.69, OS X/x86).
414 Analysis: owner invalid.
415 Fix: make owner an OOWeakReference.
416 -- Ahruman, 20070706
417 */
418 if (message == nil || owner == nil || [owner universalID] == NO_TARGET) return;
419
420#ifndef NDEBUG
421 // Push debug stack frame.
422 if (debugContext == nil) debugContext = @"unspecified";
423 AIStackElement stackElement =
424 {
425 .back = sStack,
426 .owner = owner,
427 .aiName = [[stateMachineName retain] autorelease],
428 .state = [[currentState retain] autorelease],
429 .message = message,
430 .context = debugContext
431 };
432 sStack = &stackElement;
433#endif
434
435 /* CRASH when calling reactToMessage: FOO in state FOO causes infinite
436 recursion. (NB: there are other ways of triggering this.)
437 FIX: recursion limiter. Alternative is to explicitly catch this case
438 in takeAction:, but that could potentially miss indirect recursion via
439 scripts.
440 */
441 if (recursionLimiter > kRecursionLimiter)
442 {
443 OOLogERR(@"ai.error.recursion", @"AI dispatch: hit stack depth limit in AI %@, state %@ handling message %@ in context \"%@\", aborting.", stateMachineName, currentState, message, debugContext);
444
445#ifndef NDEBUG
446 AIStackElement *stack = sStack;
447 unsigned depth = 0;
448 while (stack != NULL)
449 {
450 OOLog(@"ai.error.recursion.stackTrace", @"%4u %@ - %@:%@.%@ (%@)", depth++, [stack->owner shortDescription], stack->aiName, stack->state, stack->message, stack->context);
451 stack = stack->back;
452 }
453
454 // unwind.
455 if (sStack != NULL) sStack = sStack->back;
456#endif
457
458 return;
459 }
460
461 messagesForState = [stateMachine objectForKey:currentState];
462 if (messagesForState == nil) return;
463
464#ifndef NDEBUG
465 if (currentState != nil && ![message isEqual:@"UPDATE"] && [owner reportAIMessages])
466 {
467 OOLog(@"ai.message.receive", @"AI %@ for %@ in state '%@' receives message '%@'. Context: %@, stack depth: %u", stateMachineName, ownerDesc, currentState, message, debugContext, recursionLimiter);
468 }
469#endif
470
471 actions = [[[messagesForState objectForKey:message] copy] autorelease];
472
473 sCurrentlyRunningAI = self;
474 if ([actions count] > 0)
475 {
476 ++recursionLimiter;
477 @try
478 {
479 for (i = 0; i < [actions count]; i++)
480 {
481 [self takeAction:[actions objectAtIndex:i]];
482 }
483 }
484 @catch (NSException *exception)
485 {
486 OOLog(kOOLogException, @"Squashing exception %@:%@ in AI handler %@:%@.%@", [exception name], [exception reason], stateMachineName, currentState, message);
487 }
488
489 --recursionLimiter;
490 }
491 else
492 {
493 if (currentState != nil)
494 {
495 if ([owner respondsToSelector:@selector(interpretAIMessage:)])
496 {
497 [owner performSelector:@selector(interpretAIMessage:) withObject:message];
498 }
499 }
500 }
501
502 sCurrentlyRunningAI = previousRunning;
503#ifndef NDEBUG
504 // Unwind stack.
505 if (sStack != NULL) sStack = sStack->back;
506#endif
507}
static AIStackElement * sStack
Definition AI.m:400
@ kRecursionLimiter
Definition AI.m:39
NSString *const kOOLogException
Definition OOLogging.m:651
NSString * name()
Definition AI.m:364
ShipEntity * owner
Definition AI.m:393
AIStackElement * back
Definition AI.m:392
NSString * aiName
Definition AI.m:394
NSString * context
Definition AI.m:397
NSString * message
Definition AI.m:396
NSString * state
Definition AI.m:395

◆ refreshOwnerDesc

- (void) refreshOwnerDesc
implementation

Provided by category AI(OOPrivate).

Definition at line 397 of file AI.m.

758{
759 ShipEntity *owner = [self owner];
760 [ownerDesc release];
761 if ([owner isPlayer])
762 {
763 ownerDesc = @"player autopilot";
764 }
765 else if (owner != nil)
766 {
767 ownerDesc = [[NSString alloc] initWithFormat:@"%@ %d", [owner name], [owner universalID]];
768 }
769 else
770 {
771 ownerDesc = @"no owner";
772 }
773}
OOUniversalID universalID
Definition Entity.h:89
NSString * name
Definition ShipEntity.h:327

◆ reportStackOverflow

- (void) reportStackOverflow
implementation

Definition at line 87 of file AI.m.

206{
207 if (OOLogWillDisplayMessagesInClass(@"ai.error.stackOverflow"))
208 {
209 BOOL stackDump = OOLogWillDisplayMessagesInClass(@"ai.error.stackOverflow.dump");
210
211 NSString *trailer = stackDump ? @" -- stack:" : @".";
212 OOLogERR(@"ai.error.stackOverflow", @"AI stack overflow for %@ in %@: %@%@\n", [_owner shortDescription], stateMachineName, currentState, trailer);
213
214 if (stackDump)
215 {
216 OOLogIndent();
217
218 NSUInteger count = [aiStack count];
219 while (count--)
220 {
221 OOPreservedAIStateMachine *preservedMachine = [aiStack objectAtIndex:count];
222 OOLog(@"ai.error.stackOverflow.dump", @"%3lu: %@: %@", count, [preservedMachine name], [preservedMachine state]);
223 }
224
225 OOLogOutdent();
226 }
227 }
228}
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
Definition OOLogging.m:144
void OOLogOutdent(void)
Definition OOLogging.m:376
void OOLogIndent(void)
Definition OOLogging.m:366
NSString * state()
Definition AI.m:376

◆ restorePreviousStateMachine

- (void) restorePreviousStateMachine

Definition at line 87 of file AI.m.

266{
267 if ([aiStack count] == 0) return;
268
269 OOPreservedAIStateMachine *preservedMachine = [aiStack lastObject];
270
271#ifndef NDEBUG
272 if ([[self owner] reportAIMessages]) OOLog(@"ai.stack.pop", @"Popping previous state machine for %@", self);
273#endif
274
275 [self directSetStateMachine:[preservedMachine stateMachine]
276 name:[preservedMachine name]];
277
278 [self directSetState:[preservedMachine state]];
279
280 // restore JS script
281 [[self owner] setAIScript:[preservedMachine jsScript]];
282
283 [pendingMessages release];
284 pendingMessages = [[preservedMachine pendingMessages] mutableCopy]; // restore a MUTABLE set
285
286 [aiStack removeLastObject]; // POP
287}
NSString * name()
Definition AI.m:1030
NSString * jsScript()
Definition AI.m:1047
NSSet * pendingMessages()
Definition AI.m:1042
NSString * state()
Definition AI.m:1036
NSDictionary * stateMachine()
Definition AI.m:1024

◆ setNextThinkTime:

- (void) setNextThinkTime: (OOTimeAbsolute) ntt

Definition at line 400 of file AI.m.

658 :(OOTimeAbsolute) ntt
659{
660 nextThinkTime = ntt;
661}
double OOTimeAbsolute
Definition OOTypes.h:223

◆ setOwner:

- (void) setOwner: (ShipEntity *) ship

Definition at line 87 of file AI.m.

197 :(ShipEntity *)ship
198{
199 [_owner release];
200 _owner = [ship weakRetain];
201 [self refreshOwnerDesc];
202}

◆ setState:

- (void) setState: (NSString *) stateName

Definition at line 87 of file AI.m.

334 :(NSString *) stateName
335{
336 if ([stateMachine objectForKey:stateName])
337 {
338 /* CRASH in objc_msgSend, apparently on [self reactToMessage:@"EXIT"] (1.69, OS X/x86).
339 Analysis: self corrupted. We're being called by __NSFireDelayedPerform, which doesn't go
340 through -[NSObject performSelector:withObject:], suggesting it's using IMP caching. An
341 invalid self is therefore possible.
342 Attempted fix: new delayed dispatch with trampoline, see -[AI setState:afterDelay:].
343 -- Ahruman, 20070706
344 */
345 [self reactToMessage:@"EXIT" context:@"changing state"];
346 [self directSetState:stateName];
347 [self reactToMessage:@"ENTER" context:@"changing state"];
348 }
349}

Referenced by ShipSetProperty().

+ Here is the caller graph for this function:

◆ setState:afterDelay:

- (void) setState: (NSString *) stateName
afterDelay: (NSTimeInterval) delay 

Definition at line 87 of file AI.m.

358 :(NSString *)stateName afterDelay:(NSTimeInterval)delay
359{
360 [self performDeferredCall:@selector(setState:) withObject:stateName afterDelay:delay];
361}

◆ setStateMachine:afterDelay:

- (void) setStateMachine: (NSString *) smName
afterDelay: (NSTimeInterval) delay 

Definition at line 87 of file AI.m.

352 :(NSString *)smName afterDelay:(NSTimeInterval)delay
353{
354 [self performDeferredCall:@selector(setStateMachine:) withObject:smName afterDelay:delay];
355}

◆ setStateMachine:withJSScript:

- (void) setStateMachine: (NSString *) smName
withJSScript: (NSString *) script 

Definition at line 87 of file AI.m.

307 :(NSString *)smName withJSScript:(NSString *)script
308{
309 NSDictionary *newSM = [self loadStateMachine:smName jsName:script];
310
311 if (newSM)
312 {
313 [self preserveCurrentStateMachine];
314 [self directSetStateMachine:newSM name:smName];
315 [self directSetState:@"GLOBAL"];
316
317 nextThinkTime = 0.0; // think at next tick
318
319 /* CRASH in objc_msgSend, apparently on [self reactToMessage:@"ENTER"] (1.69, OS X/x86).
320 Analysis: self corrupted. We're being called by __NSFireDelayedPerform, which doesn't go
321 through -[NSObject performSelector:withObject:], suggesting it's using IMP caching. An
322 invalid self is therefore possible.
323 Attempted fix: new delayed dispatch with trampoline, see -[AI setStateMachine:afterDelay:].
324 -- Ahruman, 20070706
325 */
326 [self reactToMessage:@"ENTER" context:@"changing AI"];
327
328 // refresh name
329 [self refreshOwnerDesc];
330 }
331}

◆ setThinkTimeInterval:

- (void) setThinkTimeInterval: (OOTimeDelta) tti

Definition at line 400 of file AI.m.

673 :(OOTimeDelta) tti
674{
675 thinkTimeInterval = tti;
676}
double OOTimeDelta
Definition OOTypes.h:224

◆ shortDescriptionComponents

- (NSString *) shortDescriptionComponents
implementation

Definition at line 87 of file AI.m.

179{
180 return [NSString stringWithFormat:@"%@:%@ / %@", stateMachineName, currentState, [stateMachine objectForKey:@"jsScript"]];
181}

◆ stackDepth

- (NSUInteger) stackDepth

Definition at line 87 of file AI.m.

383{
384 return [aiStack count];
385}

Referenced by AI(OOAIDebugInspectorModule)::debugInspectorModules.

+ Here is the caller graph for this function:

◆ state

- (NSString *) state

Definition at line 87 of file AI.m.

377{
378 return [[currentState retain] autorelease];
379}

Referenced by AI(OOAIDebugInspectorModule)::debugInspectorModules, and ShipGetProperty().

+ Here is the caller graph for this function:

◆ takeAction:

- (void) takeAction: (NSString *) action

Definition at line 400 of file AI.m.

510 :(NSString *)action
511{
512 ShipEntity *owner = [self owner];
513
514#ifndef NDEBUG
515 BOOL report = [owner reportAIMessages];
516 if (report)
517 {
518 OOLog(@"ai.takeAction", @"%@ to take action %@", ownerDesc, action);
519 OOLogIndent();
520 }
521#endif
522
523 NSArray *tokens = ScanTokensFromString(action);
524 NSUInteger tokenCount = [tokens count];
525
526 if (tokenCount != 0)
527 {
528 NSString *selectorStr = [tokens objectAtIndex:0];
529
530 if (owner != nil)
531 {
532 NSString *dataString = nil;
533
534 if (tokenCount == 2)
535 {
536 dataString = [tokens objectAtIndex:1];
537 }
538 else if ([tokens count] > 1)
539 {
540 dataString = [[tokens subarrayWithRange:NSMakeRange(1, tokenCount - 1)] componentsJoinedByString:@" "];
541 }
542
543 SEL selector = NSSelectorFromString(selectorStr);
544 if ([owner respondsToSelector:selector])
545 {
546 if (dataString != nil) [owner performSelector:selector withObject:dataString];
547 else [owner performSelector:selector];
548 }
549 else
550 {
551 OOLogERR(@"ai.takeAction.badSelector", @"in AI %@ in state %@: %@ does not respond to %@", stateMachineName, currentState, ownerDesc, selectorStr);
552 }
553 }
554 else
555 {
556 OOLog(@"ai.takeAction.orphaned", @"***** AI %@, trying to perform %@, is orphaned (no owner)", stateMachineName, selectorStr);
557 }
558 }
559 else
560 {
561#ifndef NDEBUG
562 if (report) OOLog(@"ai.takeAction.noAction", @"DEBUG: - no action '%@'", action);
563#endif
564 }
565
566#ifndef NDEBUG
567 if (report)
568 {
569 OOLogOutdent();
570 }
571#endif
572}
NSMutableArray * ScanTokensFromString(NSString *values)
unsigned reportAIMessages
Definition ShipEntity.h:255

Referenced by clearAllData.

+ Here is the caller graph for this function:

◆ think

- (void) think

Definition at line 400 of file AI.m.

576{
577 NSArray *ms_list = nil;
578 unsigned i;
579
580 if ([[self owner] universalID] == NO_TARGET || stateMachine == nil) return; // don't think until launched
581
582 [self reactToMessage:@"UPDATE" context:@"periodic update"];
583
584 if ([pendingMessages count] > 0)
585 {
586 ms_list = [pendingMessages allObjects];
587 [pendingMessages removeAllObjects];
588 }
589
590 if (ms_list != nil)
591 {
592 for (i = 0; i < [ms_list count]; i++)
593 {
594 [self reactToMessage:[ms_list objectAtIndex:i] context:@"handling deferred message"];
595 }
596 }
597}

◆ thinkTimeInterval

- (OOTimeDelta) thinkTimeInterval

Member Data Documentation

◆ _owner

- (id) _owner
private

Definition at line 40 of file AI.h.

◆ aiStack

- (NSMutableArray*) aiStack
private

Definition at line 48 of file AI.h.

◆ currentState

- (NSString*) currentState
private

Definition at line 45 of file AI.h.

Referenced by clearAllData.

◆ jsScript

- (NSString*) jsScript
private

Definition at line 53 of file AI.h.

◆ nextThinkTime

- (OOTimeAbsolute) nextThinkTime
private

Definition at line 50 of file AI.h.

Referenced by AI(OOAIDebugInspectorModule)::debugInspectorModules.

◆ ownerDesc

- (NSString*) ownerDesc
private

Definition at line 41 of file AI.h.

◆ pendingMessages

- (NSSet *) pendingMessages
private

Definition at line 46 of file AI.h.

Referenced by AI(OOAIDebugInspectorModule)::debugInspectorModules.

◆ stateMachine

- (NSDictionary*) stateMachine
private

Definition at line 43 of file AI.h.

Referenced by clearAllData.

◆ stateMachineName

- (NSString*) stateMachineName
private

Definition at line 44 of file AI.h.

Referenced by clearAllData.

◆ thinkTimeInterval

- (OOTimeDelta) thinkTimeInterval
private

Definition at line 51 of file AI.h.


The documentation for this class was generated from the following files: