55@interface AI (OOPrivate)
58- (void) performDeferredCall:(
SEL)selector withObject:(
id)object afterDelay:(NSTimeInterval)delay;
59+ (void) deferredCallTrampolineWithInfo:(NSValue *)info;
64- (void) directSetStateMachine:(NSDictionary *)newSM name:(NSString *)name;
65- (void) directSetState:(NSString *)state;
68- (NSDictionary *) loadStateMachine:(NSString *)smName jsName:(NSString *)script;
69- (NSDictionary *) cleanHandlers:(NSDictionary *)handlers forState:(NSString *)stateKey stateMachine:(NSString *)smName;
70- (NSArray *) cleanActions:(NSArray *)actions forHandler:(NSString *)handlerKey state:(NSString *)stateKey stateMachine:(NSString *)smName;
76extern void GenerateGraphVizForAIStateMachine(NSDictionary *stateMachine, NSString *name);
90- (id) initWithStateMachine:(NSDictionary *)stateMachine
92 state:(NSString *)state
93 pendingMessages:(NSSet *)pendingMessages
94 jsScript:(NSString *)script;
96- (NSDictionary *) stateMachine;
99- (NSSet *) pendingMessages;
100- (NSString *) jsScript;
121 return @"<no AI running>";
128 if ((
self = [super
init]))
140- (id) initWithStateMachine:(NSString *)smName andState:(NSString *)stateName
142 if ((
self = [
self init]))
140- (id) initWithStateMachine:(NSString *)smName andState:(NSString *)stateName {
…}
174 return [
NSString stringWithFormat:@"\"%@\" in state: \"%@\" for %@", stateMachineName, currentState, ownerDesc];
180 return [
NSString stringWithFormat:@"%@:%@ / %@", stateMachineName, currentState, [stateMachine objectForKey:@"jsScript"]];
211 NSString *trailer = stackDump ?
@" -- stack:" :
@".";
218 NSUInteger
count = [aiStack count];
222 OOLog(
@"ai.error.stackOverflow.dump",
@"%3lu: %@: %@",
count, [preservedMachine
name], [preservedMachine
state]);
245 format:@"AI stack overflow for %@", _owner];
249 initWithStateMachine:stateMachine
250 name:stateMachineName
252 pendingMessages:pendingMessages
253 jsScript:[stateMachine objectForKey:@"jsScript"]];
256 if ([[
self owner] reportAIMessages])
OOLog(
@"ai.stack.push",
@"Pushing state machine for %@",
self);
259 [aiStack addObject:preservedMachine];
272 if ([[
self owner] reportAIMessages])
OOLog(
@"ai.stack.pop",
@"Popping previous state machine for %@",
self);
283 [pendingMessages release];
286 [aiStack removeLastObject];
292 return [aiStack count] != 0;
296- (void) exitStateMachineWithMessage:(NSString *)message
301 if (message ==
nil) message =
@"RESTARTED";
296- (void) exitStateMachineWithMessage:(NSString *)message {
…}
307- (void) setStateMachine:(NSString *)smName withJSScript:(NSString *)script
307- (void) setStateMachine:(NSString *)smName withJSScript:(NSString *)script {
…}
334- (void) setState:(NSString *) stateName
334- (void) setState:(NSString *) stateName {
…}
352- (void) setStateMachine:(NSString *)smName afterDelay:(NSTimeInterval)delay
352- (void) setStateMachine:(NSString *)smName afterDelay:(NSTimeInterval)delay {
…}
358- (void) setState:(NSString *)stateName afterDelay:(NSTimeInterval)delay
358- (void) setState:(NSString *)stateName afterDelay:(NSTimeInterval)delay {
…}
366 return [[stateMachineName retain] autorelease];
372 return [stateMachine objectForKey:@"jsScript"];
378 return [[currentState retain] autorelease];
384 return [aiStack count];
404- (void) reactToMessage:(NSString *) message context:(NSString *)debugContext
407 NSArray *actions =
nil;
408 NSDictionary *messagesForState =
nil;
410 static unsigned recursionLimiter = 0;
422 if (debugContext ==
nil) debugContext =
@"unspecified";
427 .aiName = [[stateMachineName retain] autorelease],
428 .state = [[currentState retain] autorelease],
430 .context = debugContext
443 OOLogERR(
@"ai.error.recursion",
@"AI dispatch: hit stack depth limit in AI %@, state %@ handling message %@ in context \"%@\
", aborting.",
stateMachineName,
currentState, message, debugContext);
448 while (stack != NULL)
461 messagesForState = [stateMachine objectForKey:currentState];
462 if (messagesForState ==
nil)
return;
474 if ([actions
count] > 0)
479 for (i = 0; i < [
actions count]; i++)
484 @catch (NSException *exception)
495 if ([
owner respondsToSelector:
@selector(interpretAIMessage:)])
497 [
owner performSelector:@selector(interpretAIMessage:) withObject:message];
404- (void) reactToMessage:(NSString *) message context:(NSString *)debugContext {
…}
510- (void) takeAction:(NSString *)action
518 OOLog(
@"ai.takeAction",
@"%@ to take action %@",
ownerDesc, action);
524 NSUInteger tokenCount = [
tokens count];
528 NSString *selectorStr = [
tokens objectAtIndex:0];
532 NSString *dataString =
nil;
536 dataString = [
tokens objectAtIndex:1];
538 else if ([tokens
count] > 1)
540 dataString = [[
tokens subarrayWithRange:NSMakeRange(1, tokenCount - 1)] componentsJoinedByString:@" "];
543 SEL selector = NSSelectorFromString(selectorStr);
544 if ([
owner respondsToSelector:selector])
546 if (dataString !=
nil) [
owner performSelector:selector withObject:dataString];
547 else [
owner performSelector:selector];
556 OOLog(
@"ai.takeAction.orphaned",
@"***** AI %@, trying to perform %@, is orphaned (no owner)",
stateMachineName, selectorStr);
562 if (report)
OOLog(
@"ai.takeAction.noAction",
@"DEBUG: - no action '%@'", action);
510- (void) takeAction:(NSString *)action {
…}
577 NSArray *ms_list =
nil;
586 ms_list = [pendingMessages allObjects];
587 [pendingMessages removeAllObjects];
592 for (i = 0; i < [
ms_list count]; i++)
600- (void) message:(NSString *)ms
607 OOLogERR(
@"ai.message.failed.overflow",
@"AI message \"%@\
" received by '%@' AI while pending messages stack full; message discarded. Pending messages:\n%@", ms,
ownerDesc,
pendingMessages);
615 [pendingMessages addObject:ms];
600- (void) message:(NSString *)ms {
…}
620- (void) dropMessage:(NSString *)ms
622 [pendingMessages removeObject:ms];
620- (void) dropMessage:(NSString *)ms {
…}
630 return [[pendingMessages copy] autorelease];
641 NSArray *sortedMessages =
nil;
642 NSString *displayMessages =
nil;
646 sortedMessages = [[pendingMessages allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
647 displayMessages = [
sortedMessages componentsJoinedByString:@", "];
651 displayMessages =
@"none";
687 [aiStack removeAllObjects];
693 [aiStack removeAllObjects];
694 [pendingMessages removeAllObjects];
717@implementation AI (OOPrivate)
719- (void)performDeferredCall:(
SEL)selector withObject:(
id)object afterDelay:(NSTimeInterval)delay
724 if (selector != NULL)
726 infoStruct.
ai = [
self retain];
730 info = [[
NSValue alloc] initWithBytes:&infoStruct objCType:@encode(OOAIDeferredCallTrampolineInfo)];
732 [[
AI class] performSelector:@selector(deferredCallTrampolineWithInfo:)
719- (void)performDeferredCall:(
SEL)selector withObject:(
id)object afterDelay:(NSTimeInterval)delay {
…}
740+ (void)deferredCallTrampolineWithInfo:(NSValue *)info
747 [
info getValue:&infoStruct];
749 [
infoStruct.
ai performSelector:infoStruct.selector withObject:infoStruct.parameter];
740+ (void)deferredCallTrampolineWithInfo:(NSValue *)info {
…}
761 if ([owner isPlayer])
763 ownerDesc =
@"player autopilot";
765 else if (owner !=
nil)
771 ownerDesc =
@"no owner";
776- (void) directSetStateMachine:(NSDictionary *)newSM name:(NSString *)name
778 if (stateMachine != newSM)
781 stateMachine = [
newSM copy];
783 if (stateMachineName != name)
786 stateMachineName = [
name copy];
776- (void) directSetStateMachine:(NSDictionary *)newSM name:(NSString *)name {
…}
791- (void) directSetState:(NSString *)state
793 if (currentState != state)
796 currentState = [
state copy];
791- (void) directSetState:(NSString *)state {
…}
801- (NSDictionary *) loadStateMachine:(NSString *)smName jsName:(NSString *)script
803 NSDictionary *newSM =
nil;
804 NSMutableDictionary *cleanSM =
nil;
806 NSEnumerator *stateEnum =
nil;
807 NSString *stateKey =
nil;
808 NSDictionary *stateHandlers =
nil;
809 NSAutoreleasePool *pool =
nil;
811 if (![smName isEqualToString:
@"nullAI.plist"])
815 if (newSM !=
nil && ![newSM isKindOfClass:[NSDictionary
class]])
return nil;
821 OOLog(
@"ai.load",
@"Loading and sanitizing AI \"%@\
"", smName);
836 NSString *fromString =
@"";
837 if ([
self state] !=
nil)
839 fromString = [
NSString stringWithFormat:@" from %@:%@", [
self name], [
self state]];
841 OOLog(
@"ai.load.failed.unknownAI",
@"Can't switch AI for %@%@ to \"%@\
" - could not load file.", [[
self owner] shortDescription], fromString, smName);
847 for (stateEnum = [newSM keyEnumerator]; (stateKey = [
stateEnum nextObject]); )
849 stateHandlers = [
newSM objectForKey:stateKey];
850 if (![stateHandlers isKindOfClass:[NSDictionary
class]])
852 OOLogWARN(
@"ai.invalidFormat.state",
@"State \"%@\
" in AI \"%@\" is not a dictionary, ignoring.", stateKey, smName);
857 [
cleanSM setObject:stateHandlers forKey:stateKey];
859 [
cleanSM setObject:script forKey:@"jsScript"];
862 newSM = [[
cleanSM copy] autorelease];
865 if ([[NSUserDefaults standardUserDefaults] boolForKey:
@"generate-ai-graphviz"])
867 GenerateGraphVizForAIStateMachine(newSM, smName);
801- (NSDictionary *) loadStateMachine:(NSString *)smName jsName:(NSString *)script {
…}
888- (NSDictionary *) cleanHandlers:(NSDictionary *)handlers forState:(NSString *)stateKey stateMachine:(NSString *)smName
890 NSEnumerator *handlerEnum =
nil;
891 NSString *handlerKey =
nil;
892 NSArray *handlerActions =
nil;
893 NSMutableDictionary *result =
nil;
896 for (handlerEnum = [handlers keyEnumerator]; (handlerKey = [
handlerEnum nextObject]); )
898 handlerActions = [
handlers objectForKey:handlerKey];
899 if (![handlerActions isKindOfClass:[NSArray
class]])
901 OOLogWARN(
@"ai.invalidFormat.handler",
@"Handler \"%@\
" for state \"%@\" in AI \"%@\" is not an array, ignoring.", handlerKey, stateKey, smName);
906 [
result setObject:handlerActions forKey:handlerKey];
910 return [[
result copy] autorelease];
888- (NSDictionary *) cleanHandlers:(NSDictionary *)handlers forState:(NSString *)stateKey stateMachine:(NSString *)smName {
…}
914- (NSArray *) cleanActions:(NSArray *)actions forHandler:(NSString *)handlerKey state:(NSString *)stateKey stateMachine:(NSString *)smName
916 NSEnumerator *actionEnum =
nil;
917 NSString *action =
nil;
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;
927 if (whitelist ==
nil)
930 if (whitelistArray1 ==
nil) whitelistArray1 = [
NSArray array];
932 if (whitelistArray2 !=
nil) whitelistArray1 = [
whitelistArray1 arrayByAddingObjectsFromArray:whitelistArray2];
934 whitelist = [[
NSSet alloc] initWithArray:whitelistArray1];
939 for (actionEnum = [actions objectEnumerator]; (action = [
actionEnum nextObject]); )
941 if (![action isKindOfClass:[NSString
class]])
943 OOLogWARN(
@"ai.invalidFormat.action",
@"An action in handler \"%@\
" for state \"%@\" in AI \"%@\" is not a string, ignoring.", handlerKey, stateKey, smName);
951 spaceRange = [
action rangeOfString:@" "];
952 if (spaceRange.location == NSNotFound) selector = action;
953 else selector = [
action substringToIndex:spaceRange.location];
956 aliasedSelector = [
aliases objectForKey:selector];
957 if (aliasedSelector !=
nil)
959 if ([aliasedSelector isKindOfClass:[NSString
class]])
962 selector = aliasedSelector;
963 if (spaceRange.location == NSNotFound) action = aliasedSelector;
964 else action = [
aliasedSelector stringByAppendingString:[
action substringFromIndex:spaceRange.location]];
966 else if ([aliasedSelector isKindOfClass:[NSArray
class]] && [aliasedSelector
count] != 0)
975 if (![whitelist containsObject:selector])
977 OOLog(
@"ai.unpermittedMethod",
@"Handler \"%@\
" for state \"%@\" in AI \"%@\" uses \"%@\", which is not a permitted AI method.", handlerKey, stateKey, smName, selector);
981 [
result addObject:action];
985 return [[
result copy] autorelease];
914- (NSArray *) cleanActions:(NSArray *)actions forHandler:(NSString *)handlerKey state:(NSString *)stateKey stateMachine:(NSString *)smName {
…}
993- (id) initWithStateMachine:(NSDictionary *)stateMachine
994 name:(NSString *)name
995 state:(NSString *)state
996 pendingMessages:(NSSet *)pendingMessages
997 jsScript:(NSString *)script
999 if ((
self = [super init]))
1002 _name = [name copy];
993- (id) initWithStateMachine:(NSDictionary *)stateMachine {
…}
1014 [_stateMachine autorelease];
1015 [_name autorelease];
1016 [_state autorelease];
1017 [_pendingMessages autorelease];
1018 [_jsScript autorelease];
#define AI_THINK_INTERVAL
static AI * sCurrentlyRunningAI
static AIStackElement * sStack
void OOLogPushIndent(void)
#define OOLogWARN(class, format,...)
#define OOLogERR(class, format,...)
NSString *const kOOLogException
void OOLogPopIndent(void)
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
#define OOLog(class, format,...)
#define OOLogIndentIf(class)
NSDictionary * OODictionaryFromFile(NSString *path)
NSMutableArray * ScanTokensFromString(NSString *values)
void setStateMachine:withJSScript:(NSString *smName,[withJSScript] NSString *script)
NSDictionary * stateMachine
NSString * descriptionComponents()
NSArray * cleanActions:forHandler:state:stateMachine:(NSArray *actions,[forHandler] NSString *handlerKey,[state] NSString *stateKey,[stateMachine] NSString *smName)
NSString * currentlyRunningAIDescription()
void reportStackOverflow()
void directSetState:(NSString *state)
NSMutableSet * pendingMessages
NSDictionary * loadStateMachine:jsName:(NSString *smName,[jsName] NSString *script)
void directSetStateMachine:name:(NSDictionary *newSM,[name] NSString *name)
void debugDumpPendingMessages()
void preserveCurrentStateMachine()
AI * currentlyRunningAI()
void restorePreviousStateMachine()
NSString * shortDescriptionComponents()
OOTimeDelta thinkTimeInterval
NSDictionary * cleanHandlers:forState:stateMachine:(NSDictionary *handlers,[forState] NSString *stateKey,[stateMachine] NSString *smName)
void takeAction:(NSString *action)
BOOL hasSuspendedStateMachines()
OOTimeAbsolute nextThinkTime
NSString * stateMachineName
NSString * associatedJS()
void performDeferredCall:withObject:afterDelay:(SEL selector,[withObject] id object,[afterDelay] NSTimeInterval delay)
void reactToMessage:context:(NSString *message,[context] NSString *debugContext)
OOUniversalID universalID
void setObject:forKey:inCache:(id inElement,[forKey] NSString *inKey,[inCache] NSString *inCacheKey)
id objectForKey:inCache:(NSString *inKey,[inCache] NSString *inCacheKey)
OOCacheManager * sharedCache()
NSSet * pendingMessages()
NSDictionary * stateMachine()
NSMutableSet * _pendingMessages
NSDictionary * _stateMachine
NSString * pathForFileNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)
NSDictionary * whitelistDictionary()
unsigned reportAIMessages
void setAIScript:(NSString *aiString)