Added big fat deprecation warning for scriptActionOnTarget:. Added skanky code to avoid running world scripts more than once with certain player states (such as STATUS_EXITING_WITCHSPACE).

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1445 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2008-03-02 18:56:24 +00:00
parent 1a75b7f6bf
commit 959dc99d88
10 changed files with 145 additions and 30 deletions

View File

@ -2817,7 +2817,6 @@
0865424C06B8447D000CA0AB /* Resources */,
0865430306B8447D000CA0AB /* Sources */,
0865431B06B8447D000CA0AB /* Frameworks */,
1A5DB2D10BBEA46F00D57389 /* Sync logcontrol.plist.xml */,
1A5BF29C0916D49800BF238F /* Copy MDImporter */,
1A23153A0B9C773B00EF0852 /* Copy Images */,
1A2316C80B9CFAB800EF0852 /* Copy Config */,
@ -2954,22 +2953,6 @@
shellPath = /bin/sh;
shellScript = "TARGET_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/Contents/Resources/Schemata\"\n\nif [ $COPY_SCHEMATA ]\nthen\n\techo \"Copying schemata\"\n\t\n\tSRC_DIR=\"$SRCROOT/Schemata/\";\n\t\n\tif [ ! -e \"$TARGET_DIR\" ]\n\tthen\n\t\tmkdir \"$TARGET_DIR\"\n\tfi\n\tfor item in `ls \"$SRC_DIR\"`\n\tdo\n\t\tcp \"$SRC_DIR/$item\" \"$TARGET_DIR/\"\n\tdone\n\trm \"$TARGET_DIR/README.txt\"\nelse\n\tif [ -e \"$TARGET_DIR\" ]\n\tthen\n\t\trmdir \"$TARGET_DIR\"\n\tfi\nfi\n";
};
1A5DB2D10BBEA46F00D57389 /* Sync logcontrol.plist.xml */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$(SRCROOT)/Resources/Config/logcontrol.plist",
);
name = "Sync logcontrol.plist.xml";
outputPaths = (
"$(SRCROOT)/Resources/Config/logcontrol.plist.xml",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "plutil -convert xml1 -o $SRCROOT/Resources/Config/logcontrol.plist.xml $SRCROOT/Resources/Config/logcontrol.plist\necho Updated logcontrol.plist.xml.\n";
};
1AD0C7300C47BDEF0070BD23 /* Copy SCR Shim (test release only) */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

View File

@ -9,6 +9,7 @@
*/
{
// To be removed after 1.71
"shipSpawned" = "didSpawn";
"shipDied" = ("didBecomeDead", "didDie");
"shipWasScooped" = "wasScooped";
@ -30,4 +31,7 @@
"shipWillExitWitchspace" = "willExitWitchSpace";
"shipExitedWitchspace" = "didExitWitchSpace";
"shipWillEnterWitchspace" = "willEnterWitchSpace";
// To be removed after 1.72
"shipBeingAttacked" = "beingAttacked";
}

View File

@ -64,7 +64,7 @@
/*** Module-specific message classes ***/
ai = $entityState;
ai = yes; // AI messages are sent if a ship's reportAIMessages property is set, for instance through the JavaScript console.
ai.message.receive = inherit;
ai.takeAction.takeAction = inherit;
ai.takeAction.noAction = inherit;
@ -268,6 +268,8 @@
script.trace.plist.run = inherit;
script.trace.javaScript.call = inherit; // Prints selector and parameter string on Player.call()
script.deprecated.scriptActionOnTarget = $scriptError; // Warning not to use scriptActionOnTarget:
searchPaths.dumpAll = $troubleShootingDump;

View File

@ -51,6 +51,9 @@ MA 02110-1301, USA.
}
+ (AI *) currentlyRunningAI;
+ (NSString *) currentlyRunningAIDescription;
- (NSString *) name;
- (NSString *) state;

View File

@ -41,6 +41,9 @@ typedef struct
} OOAIDeferredCallTrampolineInfo;
static AI *sCurrentlyRunningAI = nil;
@interface AI (OOPrivate)
// Wrapper for performSelector:withObject:afterDelay: to catch/fix bugs.
@ -54,6 +57,25 @@ typedef struct
@implementation AI
+ (AI *) currentlyRunningAI
{
return sCurrentlyRunningAI;
}
+ (NSString *) currentlyRunningAIDescription
{
if (sCurrentlyRunningAI != nil)
{
return [NSString stringWithFormat:@"%@ in state %@", [sCurrentlyRunningAI name], [sCurrentlyRunningAI state]];
}
else
{
return @"<no AI running>";
}
}
- (id) init
{
self = [super init];
@ -91,6 +113,8 @@ typedef struct
[currentState release];
[pendingMessages release];
if (sCurrentlyRunningAI == self) sCurrentlyRunningAI = nil;
[super dealloc];
}
@ -149,7 +173,9 @@ typedef struct
format:@"AI stack overflow for %@", _owner];
}
#ifndef NDEBUG
if ([[self owner] reportAIMessages]) OOLog(@"ai.stack.push", @"Pushing state machine for %@", self);
#endif
[aiStack insertObject:pickledMachine atIndex:0]; // PUSH
}
@ -160,7 +186,9 @@ typedef struct
NSMutableDictionary *pickledMachine = [aiStack objectAtIndex:0];
#ifndef NDEBUG
if ([[self owner] reportAIMessages]) OOLog(@"ai.stack.pop", @"Popping previous state machine for %@", self);
#endif
[stateMachine release];
stateMachine = [[pickledMachine objectForKey:@"stateMachine"] retain];
@ -282,6 +310,8 @@ typedef struct
NSDictionary *messagesForState = nil;
ShipEntity *owner = [self owner];
static unsigned recursionLimiter = 0;
AI *previousRunning = sCurrentlyRunningAI;
/* CRASH in _freedHandler when called via -setState: __NSFireDelayedPerform (1.69, OS X/x86).
Analysis: owner invalid.
@ -306,15 +336,18 @@ typedef struct
messagesForState = [stateMachine objectForKey:currentState];
if (messagesForState == nil) return;
#ifndef NDEBUG
if (currentState != nil && ![message isEqual:@"UPDATE"] && [owner reportAIMessages])
{
OOLog(@"ai.message.receive", @"AI for %@ in state '%@' receives message '%@'", ownerDesc, currentState, message);
}
#endif
actions = [[[messagesForState objectForKey:message] copy] autorelease];
if (rulingInstinct != nil) [rulingInstinct freezeShipVars]; // preserve the pre-thinking state
sCurrentlyRunningAI = self;
if ([actions count] > 0)
{
NS_DURING
@ -338,6 +371,7 @@ typedef struct
}
}
}
sCurrentlyRunningAI = previousRunning;
if (rulingInstinct != nil)
{
@ -354,14 +388,15 @@ typedef struct
NSString *selectorStr;
SEL selector;
ShipEntity *owner = [self owner];
BOOL report = [owner reportAIMessages];
report = [owner reportAIMessages];
#ifndef NDEBUG
BOOL report = [owner reportAIMessages];
if (report)
{
OOLog(@"ai.takeAction.takeAction", @"%@ to take action %@", ownerDesc, action);
OOLogIndent();
}
#endif
if ([tokens count] != 0)
{
@ -400,13 +435,17 @@ typedef struct
}
else
{
#ifndef NDEBUG
if (report) OOLog(@"ai.takeAction.noAction", @" - no action '%@'", action);
#endif
}
#ifndef NDEBUG
if (report)
{
OOLogOutdent();
}
#endif
}

View File

@ -1056,15 +1056,21 @@ static GLfloat texture_uv_array[10400 * 2];
if ([ship isShuttle])
{
[ship landOnPlanet];
#ifndef NDEBUG
if ([ship reportAIMessages])
{
OOLog(@"planet.collide.shuttleLanded", @"DEBUG %@ landed on planet %@", other, self);
}
#endif
return NO;
}
#ifndef NDEBUG
if ([ship reportAIMessages])
{
Vector p1 = ship->position;
OOLog(@"planet.collide.shipHit", @"DEBUG %@ %d collided with planet at (%.1f,%.1f,%.1f)",[ship name], [ship universalID], p1.x,p1.y,p1.z);
}
#endif
}
return YES;

View File

@ -194,20 +194,73 @@ OOINLINE void PerformScriptActions(NSArray *actions, Entity *target)
}
OOINLINE OOEntityStatus RecursiveRemapStatus(OOEntityStatus status)
{
// Some player stutuses should only be seen once per "event". This remaps them to something innocuous in case of recursion.
if (status == STATUS_DOCKING ||
status == STATUS_LAUNCHING ||
status == STATUS_ENTERING_WITCHSPACE ||
status == STATUS_EXITING_WITCHSPACE)
{
return STATUS_IN_FLIGHT;
}
else
{
return status;
}
}
static BOOL sRunningScript = NO;
- (void) checkScript
{
NSEnumerator *scriptEnum = nil;
OOScript *theScript = nil;
BOOL wasRunningScript = sRunningScript;
OOEntityStatus restoreStatus;
[self setScriptTarget:self];
OOLog(@"script.trace.runWorld", @"----- Running world script with state %@", [self status_string]);
OOLogIndentIf(@"script.trace.runWorld");
for (scriptEnum = [worldScripts objectEnumerator]; (theScript = [scriptEnum nextObject]); )
/* World scripts can potentially be invoked recursively, through
scriptActionOnTarget: and possibly other mechanisms. This is bad, but
that's the way it is. Legacy world scripts rely on only seeing certain
player statuses once per "event". To ensure this, we must lie about
the player's status when invoked recursively.
Of course, there are also methods in the game that rely on status not
lying. However, I don't believe any that rely on these particular
statuses can be legitimately invoked by scripts. The alternative would
be to track the "status-as-seen-by-scripts" separately from the "real"
status, which'd risk synchronization problems.
In summary, scriptActionOnTarget: is bad, and calling it from scripts
rather than AIs is very bad.
-- Ahruman, 20080302
*/
restoreStatus = status;
if (sRunningScript)
{
[theScript runWithTarget:self];
status = RecursiveRemapStatus(status);
if (status != restoreStatus)
{
OOLog(@"script.trace.runWorld.recurse.lying", @"----- Running world script recursively and temporarily changing player status from %@ to %@.", EntityStatusToString(restoreStatus), EntityStatusToString(status));
}
else
{
OOLog(@"script.trace.runWorld.recurse", @"----- Running world script recursively.", EntityStatusToString(restoreStatus), EntityStatusToString(status));
}
}
sRunningScript = YES;
// After all that, actually running the scripts is trivial.
[[worldScripts allValues] makeObjectsPerformSelector:@selector(runWithTarget:) withObject:self];
// Restore anti-recursion measures.
sRunningScript = wasRunningScript;
status = restoreStatus;
OOLogOutdentIf(@"script.trace.runWorld");
}

View File

@ -1572,8 +1572,7 @@ ShipEntity* doOctreesCollide(ShipEntity* prime, ShipEntity* other)
if ([other isKindOfClass:[ShipEntity class]]) source = other;
else source = from;
[self doScriptEvent:@"beingAttacked" withArgument:source];
[shipAI reactToMessage:@"ATTACKED"];
[self doScriptEvent:@"shipBeingAttacked" withArgument:source andReactToAIMessage:@"ATTACKED"];
}
@ -6458,11 +6457,13 @@ BOOL class_masslocks(int some_class)
being_mined = NO;
ShipEntity *hunter = nil;
if ((other)&&(other->isShip))
if ([other isShip])
{
hunter = (ShipEntity *)other;
if ([hunter isCloaked])
{
[self doScriptEvent:@"shipBeingAttackedByCloaked" andReactToAIMessage:@"ATTACKED_BY_CLOAKED"];
// lose it!
other = nil;
hunter = nil;

View File

@ -1053,9 +1053,13 @@ WormholeEntity* whole;
ShipEntity *mother = [UNIVERSE entityForUniversalID:primaryTarget];
if (mother)
{
#ifndef NDEBUG
if (reportAIMessages)
{
OOLog(@"ai.suggestEscort", @"DEBUG %@ suggests escorting %@", self, mother);
}
#endif
if ([mother acceptAsEscort:self])
{
// copy legal status across
@ -1071,8 +1075,12 @@ WormholeEntity* whole;
return;
}
#ifndef NDEBUG
if (reportAIMessages)
{
OOLog(@"ai.suggestEscort.refused", @"DEBUG %@ refused by %@", self, mother);
}
#endif
}
[self setOwner:NULL];
@ -1758,6 +1766,17 @@ WormholeEntity* whole;
PlayerEntity *player = [PlayerEntity sharedPlayer];
Entity *targEnt = [UNIVERSE entityForUniversalID:primaryTarget];
ShipEntity *oldTarget = nil;
static BOOL deprecationWarning = NO;
if (!deprecationWarning)
{
deprecationWarning = YES;
OOLog(@"script.deprecated.scriptActionOnTarget", @"***** WARNING in AI %@: the AI method scriptActionOnTarget: is deprecated and should not be used. It is slow and has unpredictable side effects. The recommended alternative is to use sendScriptMessage: to call a function in a ship's JavaScript ship script instead. scriptActionOnTarget: should not be used at all from scripts.", [AI currentlyRunningAIDescription]);
}
else
{
OOLog(@"script.deprecated.scriptActionOnTarget.repeat", @"***** WARNING in AI %@: the AI method scriptActionOnTarget: is deprecated and should not be used.", [AI currentlyRunningAIDescription]);
}
if ([targEnt isShip])
{

View File

@ -275,7 +275,12 @@ static JSFunctionSpec sScriptMethods[] =
- (void)runWithTarget:(Entity *)target
{
OOLog(@"script.trace.js.run", @"Runing script \"%@\"", [self name]);
OOLogIndentIf(@"script.trace.js.run");
[self doEvent:@"tickle" withArguments:[NSArray arrayWithObject:[[PlayerEntity sharedPlayer] status_string]]];
OOLogOutdentIf(@"script.trace.js.run");
}