28#import "MyOpenGLView.h"
49#import <Sparkle/Sparkle.h>
54#elif (OOLITE_GNUSTEP && !defined(NDEBUG))
62@interface GameController (OOPrivate)
64- (void)reportUnhandledStartupException:(NSException *)exception;
88 [NSException raise:NSInternalInconsistencyException format:@"%s: expected only one GameController to exist at a time.", __PRETTY_FUNCTION__];
91 if ((
self = [super init]))
93 last_timeInterval = [NSDate timeIntervalSinceReferenceDate];
95 _animationTimerInterval = [[NSUserDefaults standardUserDefaults] oo_doubleForKey:@"animation_timer_interval" defaultValue:MINIMUM_ANIMATION_TICK];
99 ranrot_srand((uint32_t)[[NSDate date] timeIntervalSince1970]);
101 _splashStart = [[NSDate alloc] init];
111 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:UNIVERSE];
118 [playerFileToLoad release];
119 [playerFileDirectory release];
120 [expansionPathsToInclude release];
132- (void) setGamePaused:(BOOL)value
134 if (value && !gameIsPaused)
136 _resumeMode = [
self mouseInteractionMode];
137 [
self setMouseInteractionModeForUIWithMouseInteraction:NO];
139 [PLAYER doScriptEvent:OOJSID("gamePaused")];
141 else if (!value && gameIsPaused)
143 [
self setMouseInteractionMode:_resumeMode];
145 [PLAYER doScriptEvent:OOJSID("gameResumed")];
159 if (
mode == oldMode)
return;
164#if OO_USE_FULLSCREEN_CONTROLLER
165 if ([
self inFullScreenMode])
167 [_fullScreenController noteMouseInteractionModeChangedFrom:oldMode to:mode];
172 [[
self gameView] noteMouseInteractionModeChangedFrom:oldMode to:mode];
177- (void) setMouseInteractionModeForFlight
179 [
self setMouseInteractionMode:[PLAYER isMouseControlOn] ? MOUSE_MODE_FLIGHT_WITH_MOUSE_CONTROL : MOUSE_MODE_FLIGHT_NO_MOUSE_CONTROL];
183- (void) setMouseInteractionModeForUIWithMouseInteraction:(BOOL)interaction
185 [
self setMouseInteractionMode:interaction ? MOUSE_MODE_UI_SCREEN_WITH_INTERACTION : MOUSE_MODE_UI_SCREEN_NO_INTERACTION];
198 gameView = [view retain];
199 [gameView setGameController:self];
200 [UNIVERSE setGameView:gameView];
204- (void) applicationDidFinishLaunching:(NSNotification *)notification
206 NSAutoreleasePool *pool =
nil;
209 pool = [[NSAutoreleasePool alloc] init];
216#if OO_OXP_VERIFIER_ENABLED
220 [
self exitAppWithContext:@"OXP verifier run"];
224 [
self beginSplashScreen];
228 [
self beginSplashScreen];
236 [
self setUpDisplayModes];
239 if (expansionPathsToInclude)
241 for (i = 0; i < [expansionPathsToInclude count]; i++)
252 [[
Universe alloc] initWithGameView:gameView];
254 [
self loadPlayerIfRequired];
256 [
self logProgress:@""];
259 [
self startAnimationTimer];
261 [
self endSplashScreen];
263 @catch (NSException *exception)
265 [
self reportUnhandledStartupException:exception];
269 OOLog(
@"startup.complete",
@"========== Loading complete in %.2f seconds. ==========", -[_splashStart timeIntervalSinceNow]);
271#if OO_USE_FULLSCREEN_CONTROLLER
272 [
self setFullScreenMode:[[NSUserDefaults standardUserDefaults] boolForKey:@"fullscreen"]];
279 [[NSRunLoop currentRunLoop] run];
284- (void) loadPlayerIfRequired
286 if (playerFileToLoad !=
nil)
288 [
self logProgress:DESC(@"loading-player")];
291 [UNIVERSE useGUILightSource:YES];
292 [UNIVERSE useGUILightSource:NO];
293 [PLAYER loadPlayerFromFile:playerFileToLoad asNew:NO];
298- (void) beginSplashScreen
305 [gameView setGameController:self];
306 [gameView initSplashScreen];
309 [gameView updateScreen];
316- (void) performGameTick:(
id)sender
318 [gameView pollControls];
319 [
self doPerformGameTick];
324- (void) performGameTick:(
id)sender
326 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
328 [gameView pollControls];
329 [
self doPerformGameTick];
345 delta_t = [NSDate timeIntervalSinceReferenceDate] - last_timeInterval;
346 last_timeInterval += delta_t;
351 [UNIVERSE update:delta_t];
354 [UNIVERSE reinitAndShowDemo:YES];
362 @catch (
id exception)
364 OOLog(
@"exception.backtrace",
@"%@",[exception callStackSymbols]);
371 @catch (
id exception) {}
375- (void) startAnimationTimer
379 NSTimeInterval ti = _animationTimerInterval;
381 timer = [[NSTimer timerWithTimeInterval:ti target:self selector:@selector(performGameTick:) userInfo:nil repeats:YES] retain];
383 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
385 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
392- (void) stopAnimationTimer
405- (void) recenterVirtualJoystick
408 my_mouse_x = my_mouse_y = 0;
409 [gameView setVirtualJoystick:0.0 :0.0];
413- (IBAction) showLogAction:sender
415 [[NSWorkspace sharedWorkspace] openFile:[OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:@"Previous.log"]];
419- (IBAction) showLogFolderAction:sender
421 [[NSWorkspace sharedWorkspace] openFile:OOLogHandlerGetLogBasePath()];
428 id result = [[NSUserDefaults standardUserDefaults] objectForKey:key];
429 if (expectedClass != Nil && ![result isKindOfClass:expectedClass]) result =
nil;
437 [[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
441static void RemovePreference(NSString *key)
443 [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
447#define kSnapshotsDirRefKey @"snapshots-directory-reference"
448#define kSnapshotsDirNameKey @"snapshots-directory-name"
450- (NSURL *) snapshotsURLCreatingIfNeeded:(BOOL)create
455 NSString *name =
DESC(
@"snapshots-directory-name-mac");
457 if (snapshotDirDict !=
nil)
462 NSString *existingName = [[url path] lastPathComponent];
463 if ([existingName compare:name options:NSCaseInsensitiveSearch] != 0)
467 if (originalOldName ==
nil || [existingName compare:originalOldName options:NSCaseInsensitiveSearch] != 0)
474 Boolean inTrash =
false;
475 const UInt8 *utfPath = (
const UInt8 *)[[url path] UTF8String];
477 OSStatus err = DetermineIfPathIsEnclosedByFolder(kOnAppropriateDisk, kTrashFolderType, utfPath,
false, &inTrash);
479 if (err == noErr && inTrash ==
true) url =
nil;
485 NSString *path =
nil;
486 NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
487 if ([searchPaths
count] > 0)
489 path = [[searchPaths objectAtIndex:0] stringByAppendingPathComponent:name];
491 url = [NSURL fileURLWithPath:path];
498 NSFileManager *fmgr = [NSFileManager defaultManager];
499 if (![fmgr fileExistsAtPath:path])
501 [fmgr oo_createDirectoryAtPath:path attributes:nil];
510 if (snapshotDirDict !=
nil)
525- (IBAction) showSnapshotsAction:sender
527 [[NSWorkspace sharedWorkspace] openURL:[
self snapshotsURLCreatingIfNeeded:YES]];
531- (IBAction) showAddOnsAction:sender
536 for (NSString *path in paths) {
537 if ([
self addOnsExistAtPath:path]) {
538 [
self openPath:path];
544 for (NSString *path in paths) {
545 if ([
self isDirectoryAtPath:path]) {
546 [
self openPath:path];
552 [NSFileManager.defaultManager createDirectoryAtPath:[paths objectAtIndex:0]
553 withIntermediateDirectories:YES
556 [
self openPath:[paths objectAtIndex:0]];
560- (BOOL) isDirectoryAtPath:(NSString *)path
563 return [NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory;
567- (BOOL) addOnsExistAtPath:(NSString *)path
569 if (![
self isDirectoryAtPath:path])
return NO;
571 NSWorkspace *workspace = NSWorkspace.sharedWorkspace;
572 for (NSString *subPath in [NSFileManager.defaultManager enumeratorAtPath:path]) {
573 subPath = [path stringByAppendingPathComponent:subPath];
574 NSString *type = [workspace typeOfFile:subPath error:NULL];
575 if ([workspace type:type conformsToType:
@"org.aegidian.oolite.expansion"])
return YES;
582- (void) openPath:(NSString *)path
584 [NSWorkspace.sharedWorkspace openURL:[NSURL fileURLWithPath:path]];
588- (BOOL) validateMenuItem:(NSMenuItem *)menuItem
590 SEL action = menuItem.action;
592 if (action ==
@selector(showLogAction:))
595 return ([[NSFileManager defaultManager] fileExistsAtPath:[
OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:
@"Previous.log"]]);
598 if (action ==
@selector(showAddOnsAction:))
604 if (action ==
@selector(showSnapshotsAction:))
606 BOOL pathIsDirectory;
607 if(![[NSFileManager defaultManager] fileExistsAtPath:[
self snapshotsURLCreatingIfNeeded:NO].path isDirectory:&pathIsDirectory])
611 return pathIsDirectory;
614 if (action ==
@selector(toggleFullScreenAction:))
616 if (_fullScreenController.fullScreenMode)
619 menuItem.title = NSLocalizedString(
@"Exit Full Screen", NULL);
623 menuItem.title = NSLocalizedString(
@"Enter Full Screen", NULL);
632- (NSMenu *)applicationDockMenu:(NSApplication *)sender
639- (NSURL *) snapshotsURLCreatingIfNeeded:(BOOL)create
641 NSURL *url = [NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:DESC(@"snapshots-directory-name")]];
645 NSString *path = [url path];
646 NSFileManager *fmgr = [NSFileManager defaultManager];
647 if (![fmgr fileExistsAtPath:path])
649 [fmgr createDirectoryAtPath:path attributes:nil];
656 #error Unknown environment!
659- (void) logProgress:(NSString *)message
661 if (![
UNIVERSE doingStartUp])
return;
664 [splashProgressTextField setStringValue:message];
665 [splashProgressTextField display];
667 if([message length] > 0)
669 OOLog(
@"startup.progress",
@"===== [%.2f s] %@", -[_splashStart timeIntervalSinceNow], message);
676- (BOOL) debugMessageTrackingIsOn
678 return splashProgressTextField !=
nil;
682- (NSString *) debugMessageCurrentString
684 return [splashProgressTextField stringValue];
687- (BOOL) debugMessageTrackingIsOn
693- (NSString *) debugMessageCurrentString
699- (void) debugLogProgress:(NSString *)format, ...
702 va_start(args, format);
703 [
self debugLogProgress:format arguments:args];
708- (void) debugLogProgress:(NSString *)format arguments:(va_list)arguments
710 NSString *message = [[[NSString alloc] initWithFormat:format arguments:arguments] autorelease];
711 [
self logProgress:message];
715static NSMutableArray *sMessageStack;
717- (void) debugPushProgressMessage:(NSString *)format, ...
719 if ([
self debugMessageTrackingIsOn])
721 if (sMessageStack ==
nil) sMessageStack = [[NSMutableArray alloc] init];
722 [sMessageStack addObject:[
self debugMessageCurrentString]];
725 va_start(args, format);
726 [
self debugLogProgress:format arguments:args];
734- (void) debugPopProgressMessage
738 if ([sMessageStack
count] > 0)
740 NSString *message = [sMessageStack lastObject];
741 if ([message length] > 0) [
self logProgress:message];
742 [sMessageStack removeLastObject];
749- (void) endSplashScreen
755 splashProgressTextField =
nil;
758 [gameWindow setAcceptsMouseMovedEvents:YES];
759 [gameWindow setContentView:gameView];
760 [gameWindow makeFirstResponder:gameView];
762 [gameView endSplashScreen];
772 NSString *path =
nil;
775 path = [[NSBundle mainBundle] pathForResource:@"OoliteReadMe" ofType:@"pdf"];
778 PDFDocument *document = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:path]];
779 [helpView setDocument:document];
782 [helpView setBackgroundColor:[NSColor whiteColor]];
787- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
789 if ([[
filename pathExtension] isEqual:
@"oolite-save"])
791 [
self setPlayerFileToLoad:filename];
792 [
self setPlayerFileDirectory:filename];
795 if ([[
filename pathExtension] isEqualToString:
@"oxp"])
798 [[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&dir_test];
801 if (expansionPathsToInclude ==
nil)
803 expansionPathsToInclude = [[NSMutableArray alloc] init];
805 [expansionPathsToInclude addObject:filename];
813- (void) exitAppWithContext:(NSString *)context
815 [gameView.window orderOut:nil];
817 [NSApp terminate:self];
821- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
825 return NSTerminateNow;
830- (void) exitAppWithContext:(NSString *)context
832 OOLog(
@"exit.context",
@"Exiting: %@.", context);
833#if (OOLITE_GNUSTEP && !defined(NDEBUG))
839 if (![gameView atDesktopResolution])
841 OOLog(
@"gameController.exitApp",
@"%@",
@"Restoring desktop resolution.");
842 ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
845 [[NSUserDefaults standardUserDefaults] synchronize];
846 OOLog(
@"gameController.exitApp",
@"%@",
@".GNUstepDefaults synchronized.");
854 #error Unknown environment!
858- (void) exitAppCommandQ
860 [
self exitAppWithContext:@"Command-Q"];
864- (void)windowDidResize:(NSNotification *)aNotification
866 [gameView updateScreen];
870- (NSString *) playerFileToLoad
872 return playerFileToLoad;
876- (void) setPlayerFileToLoad:(NSString *)filename
878 if (playerFileToLoad)
879 [playerFileToLoad autorelease];
880 playerFileToLoad =
nil;
881 if ([[[
filename pathExtension] lowercaseString] isEqual:
@"oolite-save"])
882 playerFileToLoad = [filename copy];
886- (NSString *) playerFileDirectory
888 if (playerFileDirectory ==
nil)
890 playerFileDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"save-directory"];
891 if (playerFileDirectory !=
nil && ![[NSFileManager defaultManager] fileExistsAtPath:playerFileDirectory])
893 playerFileDirectory =
nil;
895 if (playerFileDirectory ==
nil) playerFileDirectory = [[NSFileManager defaultManager] defaultCommanderPath];
897 [playerFileDirectory retain];
900 return playerFileDirectory;
904- (void) setPlayerFileDirectory:(NSString *)filename
906 if (playerFileDirectory !=
nil)
908 [playerFileDirectory autorelease];
909 playerFileDirectory =
nil;
912 if ([[[
filename pathExtension] lowercaseString] isEqual:
@"oolite-save"])
914 filename = [filename stringByDeletingLastPathComponent];
917 playerFileDirectory = [filename retain];
918 [[NSUserDefaults standardUserDefaults] setObject:filename forKey:@"save-directory"];
922- (void)reportUnhandledStartupException:(NSException *)exception
924 OOLog(
@"startup.exception",
@"***** Unhandled exception during startup: %@ (%@).", [exception name], [exception reason]);
929 NSRunCriticalAlertPanel(
@"Oolite failed to start up, because an unhandled exception occurred.",
@"An exception of type %@ occurred. If this problem persists, please file a bug report.",
@"OK", NULL, NULL, [exception name]);
934- (void)setUpBasicOpenGLStateWithSize:(NSSize)viewSize
939 float aspect = viewSize.height/viewSize.width;
941 OOGL(glClearColor(0.0, 0.0, 0.0, 0.0));
942 OOGL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
944 OOGL(glClearDepth(1.0));
945 OOGL(glViewport(0, 0, viewSize.width, viewSize.height));
950 OOGL(glDepthFunc(GL_LESS));
954 [UNIVERSE setLighting];
958 GLfloat black[4] = {0.0, 0.0, 0.0, 1.0};
959 GLfloat white[] = {1.0, 1.0, 1.0, 1.0};
960 GLfloat stars_ambient[] = {0.25, 0.2, 0.25, 1.0};
962 OOGL(glLightfv(GL_LIGHT1, GL_AMBIENT, black));
963 OOGL(glLightfv(GL_LIGHT1, GL_SPECULAR, white));
964 OOGL(glLightfv(GL_LIGHT1, GL_DIFFUSE, white));
965 OOGL(glLightfv(GL_LIGHT1, GL_POSITION, black));
966 OOGL(glLightModelfv(GL_LIGHT_MODEL_AMBIENT, stars_ambient));
970 if ([extMgr usePointSmoothing])
OOGL(glEnable(GL_POINT_SMOOTH));
971 if ([extMgr useLineSmoothing])
OOGL(glEnable(GL_LINE_SMOOTH));
974#if GL_APPLE_transform_hint
975 if ([extMgr haveExtension:
@"GL_APPLE_transform_hint"])
977 OOGL(glHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST));
981 OOGL(glDisable(GL_NORMALIZE));
982 OOGL(glDisable(GL_RESCALE_NORMAL));
986 if ([extMgr versionIsAtLeastMajor:1 minor:2])
988 OOGL(glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR));
998- (BOOL) suppressClangStuff
1000 return pauseSelector &&
1012#define FEED_URL_BASE "http://www.oolite.org/updates/"
1013#define TEST_RELEASE_FEED_NAME "oolite-mac-test-release-appcast.xml"
1014#define DEPLOYMENT_FEED_NAME "oolite-mac-appcast.xml"
1016#define TEST_RELEASE_FEED_URL (@ FEED_URL_BASE TEST_RELEASE_FEED_NAME)
1017#define DEPLOYMENT_FEED_URL (@ FEED_URL_BASE DEPLOYMENT_FEED_NAME)
1021#define DEFAULT_TEST_RELEASE 0
1023#define DEFAULT_TEST_RELEASE 1
1026 BOOL useTestReleases = [[NSUserDefaults standardUserDefaults] oo_boolForKey:@"use-test-release-updates"
1027 defaultValue:DEFAULT_TEST_RELEASE];
1029 SUUpdater *updater = [SUUpdater sharedUpdater];
1030 [updater setFeedURL:[NSURL URLWithString:useTestReleases ? TEST_RELEASE_FEED_URL : DEPLOYMENT_FEED_URL]];
#define MINIMUM_GAME_TICK
static GameController * sSharedController
#define kSnapshotsDirNameKey
#define kSnapshotsDirRefKey
static void SetUpSparkle(void)
@ kJAPersistentFileReferenceWithoutUI
@ kJAPersistentFileReferenceWithoutMounting
NSURL * JAURLFromPersistentFileReference(NSDictionary *fileRef, JAPersistentFileReferenceResolveFlags flags, BOOL *isStale)
NSDictionary * JAPersistentFileReferenceFromURL(NSURL *url)
void OOJSFrameCallbacksInvoke(OOTimeDelta delta)
NSString * OOLogHandlerGetLogBasePath(void)
BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
#define OOLogOutdentIf(class)
#define OOLog(class, format,...)
#define OOLogIndentIf(class)
void OOLoggingTerminate(void)
void OOLogSetDisplayMessagesInClass(NSString *inClass, BOOL inFlag)
NSString * OOStringFromMouseInteractionMode(OOMouseInteractionMode mode)
void OOGLFrustum(double left, double right, double bottom, double top, double near, double far)
void OOGLResetProjection(void)
static id GetPreference(NSString *key, Class expectedClass)
static void SetPreference(NSString *key, id value)
void finishOngoingFlush()
OOCacheManager * sharedCache()
OODebugMonitor * sharedDebugMonitor()
BOOL setStickHandlerClass:(Class aClass)
OOOXZManager * sharedManager()
OOOpenALController * sharedController()
OOOpenGLExtensionManager * sharedManager()
void addExternalPath:(NSString *fileName)
NSArray * userRootPaths()
void ranrot_srand(uint32_t seed)