- fix: in debug mode, Oolite would occasionally CTD after non-fatal linkedlist errors.

- fix: only call frameCallBacks when game is actually running.
- fix: js reset problems should not stall/crash the game anymore. Kept test harness (#if 0-ed) for further tests.
- yep, some code cleanup.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4664 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Marc 2011-11-27 12:42:09 +00:00
parent f44dede98e
commit 47786d1f79
9 changed files with 168 additions and 68 deletions

View File

@ -250,8 +250,6 @@ static NSString * const kOOLogEntityUpdateError = @"entity.linkedList.update.
{
OOLog(kOOLogEntityAddToListError, @"DEBUG LINKED LISTS - problem encountered while adding %@ to linked lists", self);
[UNIVERSE debugDumpEntities];
exit(-1);
}
}
#endif
@ -299,8 +297,6 @@ static NSString * const kOOLogEntityUpdateError = @"entity.linkedList.update.
{
OOLog(kOOLogEntityRemoveFromListError, @"DEBUG LINKED LISTS - problem encountered while removing %@ from linked lists", self);
[UNIVERSE debugDumpEntities];
exit(-1);
}
}
#endif
@ -401,8 +397,6 @@ static NSString * const kOOLogEntityUpdateError = @"entity.linkedList.update.
{
OOLog(kOOLogEntityVerificationError, @"DEBUG LINKED LISTS problem encountered before updating linked lists for %@", self);
[UNIVERSE debugDumpEntities];
exit(-1);
}
}
#endif
@ -484,8 +478,6 @@ static NSString * const kOOLogEntityUpdateError = @"entity.linkedList.update.
{
OOLog(kOOLogEntityUpdateError, @"DEBUG LINKED LISTS problem encountered after updating linked lists for %@", self);
[UNIVERSE debugDumpEntities];
exit(-1);
}
}
#endif

View File

@ -541,7 +541,8 @@ typedef enum
+ (PlayerEntity *)sharedPlayer;
- (void) deferredInit;
- (void) setUp;
- (BOOL) setUpAndConfirmOK:(BOOL)stopOnError;
- (BOOL) setUpAndConfirmOK:(BOOL)stopOnError saveGame:(BOOL)loadingGame;
- (void) completeSetUp;
- (void) completeSetUpAndSetTarget:(BOOL)setTarget;

View File

@ -97,6 +97,7 @@ enum
static NSString * const kOOLogBuyMountedOK = @"equip.buy.mounted";
static NSString * const kOOLogBuyMountedFailed = @"equip.buy.mounted.failed";
static float const kDeadResetTime = 30.0f;
PlayerEntity *gOOPlayer = nil;
static GLfloat sBaseMass = 0.0;
@ -739,7 +740,7 @@ static GLfloat sBaseMass = 0.0;
if ([dict oo_stringForKey:@"galaxy_coordinates"] == nil) return NO;
BOOL strict = [dict oo_boolForKey:@"strict" defaultValue:NO];
[UNIVERSE setStrict:strict fromSaveGame:YES];
if (![UNIVERSE setStrict:strict fromSaveGame:YES]) return NO;
//base ship description
[self setShipDataKey:[dict oo_stringForKey:@"ship_desc"]];
@ -1080,7 +1081,7 @@ static GLfloat sBaseMass = 0.0;
{
missile_entity[i] = nil;
}
[self setUp];
[self setUpAndConfirmOK:NO];
save_path = nil;
@ -1096,19 +1097,59 @@ static GLfloat sBaseMass = 0.0;
}
- (void) setUp
- (BOOL) setUpAndConfirmOK:(BOOL)stopOnError
{
return [self setUpAndConfirmOK:stopOnError saveGame:NO];
}
- (BOOL) setUpAndConfirmOK:(BOOL)stopOnError saveGame:(BOOL)saveGame
{
unsigned i;
Random_Seed gal_seed = {0x4a, 0x5a, 0x48, 0x02, 0x53, 0xb7};
showDemoShips = NO;
show_info_flag = NO;
// Reset JavaScript.
[OOScriptTimer noteGameReset];
[OOScriptTimer updateTimers];
[[OOJavaScriptEngine sharedEngine] reset];
if (EXPECT_NOT(![[OOJavaScriptEngine sharedEngine] reset] && stopOnError)) // always (try to) reset the engine, then find out if we need to stop.
{
/*
Occasionally there's a racing condition between timers being deleted
and the js engine needing to be reset: the engine reset stops the timers
from being deleted, and undeleted timers don't allow the engine to reset
itself properly.
If the engine can't reset, let's give ourselves an extra 20ms to allow the
timers to delete themselves.
We'll piggyback performDeadUpdates: when STATUS_DEAD, the engine waits until
kDeadResetTime then restarts Oolite via [UNIVERSE updateGameOver]
The variable shot_time is used to keep track of how long ago we were
shot.
*/
if (!saveGame)
{
// set up STATUS_DEAD
dockedStation = nil; // needed for STATUS_DEAD
[self setStatus:STATUS_DEAD];
}
else
{
OOLog(@"script.javascript.init.error", @"Retrying JavaScript reset.");
}
if ([self status] == STATUS_DEAD)
{
OOLog(@"script.javascript.init.error", @"Scheduling new JavaScript reset in 20ms.");
shot_time = kDeadResetTime - 0.02f; // schedule reinit 20 milliseconds from now.
}
return NO;
}
// Load locale script before any regular scripts.
[OOJSScript jsScriptFromFileNamed:@"oolite-locale-functions.js"
@ -1328,6 +1369,7 @@ static GLfloat sBaseMass = 0.0;
demoShip = nil;
[[OOMusicController sharedController] justStop];
return YES;
}
@ -2000,8 +2042,8 @@ static bool minShieldLevelPercentageInitialised = false;
[self setStatus:STATUS_DEAD];
//[self playGameOver]; // no death explosion sounds for player pods
// no shipDied events for player pods, either
[UNIVERSE displayMessage:DESC(@"gameoverscreen-escape-pod") forCount:30.0];
[UNIVERSE displayMessage:@"" forCount:30.0];
[UNIVERSE displayMessage:DESC(@"gameoverscreen-escape-pod") forCount:kDeadResetTime];
[UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
[self showGameOver];
}
}
@ -2536,7 +2578,7 @@ static bool minShieldLevelPercentageInitialised = false;
- (void) performDeadUpdates:(OOTimeDelta)delta_t
{
if ([self shotTime] > 30.0)
if ([self shotTime] > kDeadResetTime)
{
BOOL was_mouse_control_on = mouse_control_on;
[UNIVERSE handleGameOver]; // we restart the UNIVERSE
@ -2587,11 +2629,11 @@ static bool minShieldLevelPercentageInitialised = false;
NSString *scoreMS = [NSString stringWithFormat:ExpandDescriptionForCurrentSystem(@"[gameoverscreen-score-@]"),
KillCountToRatingAndKillString(ship_kills)];
[UNIVERSE displayMessage:ExpandDescriptionForCurrentSystem(@"[gameoverscreen-game-over]") forCount:30.0];
[UNIVERSE displayMessage:@"" forCount:30.0];
[UNIVERSE displayMessage:scoreMS forCount:30.0];
[UNIVERSE displayMessage:@"" forCount:30.0];
[UNIVERSE displayMessage:ExpandDescriptionForCurrentSystem(@"[gameoverscreen-press-space]") forCount:30.0];
[UNIVERSE displayMessage:ExpandDescriptionForCurrentSystem(@"[gameoverscreen-game-over]") forCount:kDeadResetTime];
[UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
[UNIVERSE displayMessage:scoreMS forCount:kDeadResetTime];
[UNIVERSE displayMessage:@"" forCount:kDeadResetTime];
[UNIVERSE displayMessage:ExpandDescriptionForCurrentSystem(@"[gameoverscreen-press-space]") forCount:kDeadResetTime];
[self resetShotTime];
}

View File

@ -462,8 +462,7 @@ static uint16_t PersonalityForCommanderDict(NSDictionary *dict);
if (loadedOK)
{
[self setUp];
if (![self setCommanderDataFromDictionary:fileDic])
if (![self setUpAndConfirmOK:YES saveGame:YES] || ![self setCommanderDataFromDictionary:fileDic])
{
fail_reason = DESC(@"loadfailed-could-not-set-up-player-ship");
loadedOK = NO;

View File

@ -340,9 +340,11 @@ static BOOL _switchRez = NO, _switchRezDeferred = NO;
}
[UNIVERSE update:delta_t];
[OOSound update];
OOJSFrameCallbacksInvoke(delta_t);
if (!gameIsPaused)
{
[OOSound update];
OOJSFrameCallbacksInvoke(delta_t);
}
#if OOLITE_HAVE_APPKIT
if (fullscreen)
@ -589,7 +591,7 @@ static NSComparisonResult CompareDisplayModes(id arg1, id arg2, void *context)
NSMutableData *attrData = [[[gameView pixelFormatAttributes] mutableCopy] autorelease];
NSOpenGLPixelFormatAttribute *attrs = [attrData mutableBytes];
NSAssert(attrs[0] == NSOpenGLPFAWindow, @"Pixel format does not meet my strenuous expectations!");
NSAssert(attrs[0] == NSOpenGLPFAWindow, @"Pixel format does not meet expectations. Exiting.");
attrs[0] = NSOpenGLPFAFullScreen;
GLint rendererID;

View File

@ -70,7 +70,7 @@ MA 02110-1301, USA.
/* Tear down context and global object and rebuild them from scratch. This
invalidates -globalObject and the main thread context.
*/
- (void) reset;
- (BOOL) reset;
// Call a JS function, setting up new contexts as necessary. Caller is responsible for ensuring the jsval passed really is a function.
- (BOOL) callJSFunction:(jsval)function

View File

@ -400,14 +400,39 @@ static void ReportJSError(JSContext *context, const char *message, JSErrorReport
}
- (void) reset
- (BOOL) reset
{
NSAssert(gOOJSMainThreadContext != NULL, @"JavaScript engine not active. Can't reset.");
OOJSFrameCallbacksRemoveAll();
# if 0
// deferred JS reset - test harness.
static int counter = 3; // loading a savegame with different strict mode calls js reset twice
if (counter-- == 0) {
counter = 3;
OOLog(@"script.javascript.init.error", @"JavaScript processes still pending. Can't reset JavaScript engine.");
return NO;
}
else
{
OOLog(@"script.javascript.init", @"JavaScript reset successful.");
}
#endif
#if JS_THREADSAFE
NSAssert(!JS_IsInRequest(gOOJSMainThreadContext), @"JavaScript processes still pending. Can't reset JavaScript engine.");
//NSAssert(!JS_IsInRequest(gOOJSMainThreadContext), @"JavaScript processes still pending. Can't reset JavaScript engine.");
if (JS_IsInRequest(gOOJSMainThreadContext))
{
// some threads are still pending, this should mean timers are still being removed.
OOLog(@"script.javascript.init.error", @"JavaScript processes still pending. Can't reset JavaScript engine.");
return NO;
}
else
{
OOLog(@"script.javascript.init", @"JavaScript reset successful.");
}
#endif
JSContext *context = OOJSAcquireContext();
@ -422,6 +447,7 @@ static void ReportJSError(JSContext *context, const char *message, JSErrorReport
OOJSRelinquishContext(context);
[self garbageCollectionOpportunity];
return YES;
}

View File

@ -314,8 +314,8 @@ typedef uint8_t OOEconomyID; // 0..7
- (void) setDoProcedurallyTexturedPlanets:(BOOL) value;
- (BOOL) strict;
- (void) setStrict:(BOOL) value;
- (void) setStrict:(BOOL)value fromSaveGame: (BOOL)saveGame;
- (BOOL) setStrict:(BOOL) value;
- (BOOL) setStrict:(BOOL)value fromSaveGame: (BOOL)saveGame;
- (void) reinitAndShowDemo:(BOOL)showDemo;

View File

@ -212,7 +212,7 @@ static OOComparisonResult comparePrice(id dict1, id dict2, void * context);
#endif
- (void) filterOutNonStrictEquipment;
- (void) reinitAndShowDemo:(BOOL) showDemo strictChanged:(BOOL) strictChanged;
- (BOOL) reinitAndShowDemo:(BOOL) showDemo strictChanged:(BOOL) strictChanged;
// Set shader effects level without logging or triggering a reset -- should only be used directly during startup.
- (void) setShaderEffectsLevelDirectly:(OOShaderSetting)value;
@ -230,6 +230,27 @@ static OOComparisonResult comparePrice(id dict1, id dict2, void * context);
@implementation Universe
// Flags needed when JS reset fails.
static int JSResetFlags = 0;
// track the position and status of the lights
BOOL object_light_on = NO;
BOOL demo_light_on = NO;
static GLfloat sun_off[4] = {0.0, 0.0, 0.0, 1.0};
GLfloat demo_light_position[4] = { DEMO_LIGHT_POSITION, 1.0 };
#define DOCKED_AMBIENT_LEVEL 0.2f // Was 0.05, 'temporarily' set to 0.2.
#define DOCKED_ILLUM_LEVEL 0.7f
GLfloat docked_light_ambient[4] = { DOCKED_AMBIENT_LEVEL, DOCKED_AMBIENT_LEVEL, DOCKED_AMBIENT_LEVEL, 1.0f };
GLfloat docked_light_diffuse[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, 1.0f }; // white
GLfloat docked_light_specular[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL * 0.75f, (GLfloat) 1.0f }; // yellow-white
// Weight of sun in ambient light calculation. 1.0 means only sun's diffuse is used for ambient, 0.0 means only sky colour is used.
// TODO: considering the size of the sun and the number of background stars might be worthwhile. -- Ahruman 20080322
#define SUN_AMBIENT_INFLUENCE 0.75
- (id) initWithGameView:(MyOpenGLView *)inGameView
{
PlayerEntity *player = nil;
@ -434,19 +455,19 @@ static OOComparisonResult comparePrice(id dict1, id dict2, void * context);
}
- (void) setStrict:(BOOL)value
- (BOOL) setStrict:(BOOL)value
{
[self setStrict:value fromSaveGame:NO];
return [self setStrict:value fromSaveGame:NO];
}
- (void) setStrict:(BOOL) value fromSaveGame:(BOOL) saveGame
- (BOOL) setStrict:(BOOL) value fromSaveGame:(BOOL) saveGame
{
if (strict == value) return;
if (strict == value) return YES;
strict = !!value;
[[NSUserDefaults standardUserDefaults] setBool:strict forKey:@"strict-gameplay"];
[self reinitAndShowDemo:!saveGame strictChanged:YES];
return [self reinitAndShowDemo:!saveGame strictChanged:YES];
}
@ -1154,22 +1175,6 @@ static OOComparisonResult comparePrice(id dict1, id dict2, void * context);
}
// track the position and status of the lights
BOOL object_light_on = NO;
BOOL demo_light_on = NO;
static GLfloat sun_off[4] = {0.0, 0.0, 0.0, 1.0};
GLfloat demo_light_position[4] = { DEMO_LIGHT_POSITION, 1.0 };
#define DOCKED_AMBIENT_LEVEL 0.2f // Was 0.05, 'temporarily' set to 0.2.
#define DOCKED_ILLUM_LEVEL 0.7f
GLfloat docked_light_ambient[4] = { DOCKED_AMBIENT_LEVEL, DOCKED_AMBIENT_LEVEL, DOCKED_AMBIENT_LEVEL, 1.0f };
GLfloat docked_light_diffuse[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, 1.0f }; // white
GLfloat docked_light_specular[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL * 0.75f, (GLfloat) 1.0f }; // yellow-white
// Weight of sun in ambient light calculation. 1.0 means only sun's diffuse is used for ambient, 0.0 means only sky colour is used.
// TODO: considering the size of the sun and the number of background stars might be worthwhile. -- Ahruman 20080322
#define SUN_AMBIENT_INFLUENCE 0.75
- (void) setLighting
{
/*
@ -2195,9 +2200,16 @@ GLfloat docked_light_specular[4] = { DOCKED_ILLUM_LEVEL, DOCKED_ILLUM_LEVEL, DOC
- (void) handleGameOver
{
// In unrestricted mode, reload last save game, if any. In strict mode, always restart as a fresh Jameson.
// NOTE: this is also called when loading a game fails.
if (![self strict] && [[gameView gameController] playerFileToLoad]) [[gameView gameController] loadPlayerIfRequired];
else [self reinitAndShowDemo:NO];
// NOTE: this is also called when loading a game fails, and when the js engine fails to reset properly.
if (![self strict] && [[gameView gameController] playerFileToLoad])
{
[[gameView gameController] loadPlayerIfRequired];
}
else
{
[self reinitAndShowDemo:NO];
}
}
@ -5057,7 +5069,7 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range)
volatile OOTimeDelta delta_t = inDeltaT * [self timeAccelerationFactor];
OOUInteger sessionID = _sessionID;
if (!no_update)
if (EXPECT(!no_update))
{
unsigned i, ent_count = n_entities;
Entity *my_entities[ent_count];
@ -5158,7 +5170,6 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range)
demo_stage = DEMO_FLY_IN;
demo_start_time=universal_time;
demo_stage_time = demo_start_time + DEMO2_FLY_IN_STAGE_TIME;
}
break;
}
@ -5190,7 +5201,7 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range)
}
[thing update:delta_t];
if (sessionID != _sessionID)
if (EXPECT_NOT(sessionID != _sessionID))
{
// Game was reset (in player update); end this update: cycle.
break;
@ -5288,6 +5299,11 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range)
[my_entities[i] release]; // explicitly release each one
}
}
else
{
// always perform player's dead updates: allows deferred JS resets.
if ([PLAYER status] == STATUS_DEAD) [PLAYER update:delta_t];
}
[entitiesDeadThisUpdate autorelease];
entitiesDeadThisUpdate = nil;
@ -8638,13 +8654,23 @@ Entity *gOOJSPlayerIfStale = nil;
}
// FIXME: needs less redundancy.
- (void) reinitAndShowDemo:(BOOL) showDemo strictChanged:(BOOL) strictChanged
// FIXME: needs less redundancy?
- (BOOL) reinitAndShowDemo:(BOOL) showDemo strictChanged:(BOOL) strictChanged
{
no_update = YES;
PlayerEntity* player = PLAYER;
assert(player != nil);
if (JSResetFlags != 0) // JS reset failed, remember previous settings
{
showDemo = (JSResetFlags & 2) > 0; // binary 10, a.k.a. 1 << 1
strictChanged = (JSResetFlags & 1) > 0; // binary 01
}
else
{
JSResetFlags = (showDemo << 1) | strictChanged;
}
[self removeAllEntitiesExceptPlayer];
[OOTexture clearCache];
[self resetSystemDataCache];
@ -8685,14 +8711,25 @@ Entity *gOOJSPlayerIfStale = nil;
[self setUpSettings];
[player setUp];
GameController *gc = [gameView gameController];
if (![gc inFullScreenMode]) [gc stopAnimationTimer]; // start of critical section
if (![player setUpAndConfirmOK:YES])
{
// reinitAndShowDemo rescheduled inside setUpAndConfirmOK...
if (![gc inFullScreenMode]) [gc startAnimationTimer]; // keep the game ticking over.
return NO; // Abort!
}
// end of critical section
if (![gc inFullScreenMode]) [gc startAnimationTimer];
JSResetFlags = 0;
[self addEntity:player];
demo_ship = nil;
[[gameView gameController] setPlayerFileToLoad:nil]; // reset Quicksave
[self setUpInitialUniverse];
autoSaveNow = NO; // don't autosave immediately after loading / restarting game!
autoSaveNow = NO; // don't autosave immediately after restarting a game
[[self station] initialiseLocalMarketWithRandomFactor:[player random_factor]];
@ -8718,6 +8755,7 @@ Entity *gOOJSPlayerIfStale = nil;
[self verifyEntitySessionIDs];
no_update = NO;
return YES;
}