From 56f5e760cecb4894c4271df9bf96f3a860570fe9 Mon Sep 17 00:00:00 2001 From: Jens Ayton Date: Sat, 8 Jan 2011 17:03:04 +0000 Subject: [PATCH] Implemented frame callbacks. git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4035 127b21dd-08f5-0310-b4b7-95ae10353056 --- GNUmakefile | 3 +- Oolite.xcodeproj/project.pbxproj | 6 + src/Core/Entities/PlayerEntity.m | 4 +- src/Core/GameController.m | 3 + src/Core/OOCocoa.h | 9 + src/Core/Scripting/OOJSFrameCallbacks.h | 36 ++ src/Core/Scripting/OOJSFrameCallbacks.m | 417 ++++++++++++++++++++++++ src/Core/Scripting/OOJavaScriptEngine.m | 2 + src/Core/Universe.m | 8 + 9 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 src/Core/Scripting/OOJSFrameCallbacks.h create mode 100644 src/Core/Scripting/OOJSFrameCallbacks.m diff --git a/GNUmakefile b/GNUmakefile index 87e10778..bc074ab9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -339,7 +339,8 @@ OOLITE_SCRIPTING_FILES = \ OOLegacyScriptWhitelist.m \ OOPListScript.m \ OOScript.m \ - OOScriptTimer.m + OOScriptTimer.m \ + OOJSFrameCallbacks.m OOLITE_SOUND_FILES = \ OOBasicSoundReferencePoint.m \ diff --git a/Oolite.xcodeproj/project.pbxproj b/Oolite.xcodeproj/project.pbxproj index 326382ae..a0a9ab69 100644 --- a/Oolite.xcodeproj/project.pbxproj +++ b/Oolite.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 1A0729DA0EF56D1200B0F925 /* OOConvertSystemDescriptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0729D80EF56D1200B0F925 /* OOConvertSystemDescriptions.m */; }; 1A0729FE0EF5796500B0F925 /* OldSchoolPropertyListWriting.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A0729FC0EF5796500B0F925 /* OldSchoolPropertyListWriting.h */; }; 1A0729FF0EF5796500B0F925 /* OldSchoolPropertyListWriting.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0729FD0EF5796500B0F925 /* OldSchoolPropertyListWriting.m */; }; + 1A0942CE12D7D5B9003B6273 /* OOJSFrameCallbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0942C812D7C011003B6273 /* OOJSFrameCallbacks.m */; }; 1A09EF4412BD0BCA00BF7F48 /* PlayerEntityStickMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A26D09E0BCF9CF70073F257 /* PlayerEntityStickMapper.m */; }; 1A09EF5812BD0C4100BF7F48 /* Oolite Leopard support.bundle in Copy Plug-ins */ = {isa = PBXBuildFile; fileRef = 1A09EF5412BD0C1900BF7F48 /* Oolite Leopard support.bundle */; }; 1A0BF3D510DAE2B30099984D /* cobra1_redux1.dat in Copy Models */ = {isa = PBXBuildFile; fileRef = 1A0BF3D310DAE2B30099984D /* cobra1_redux1.dat */; }; @@ -1262,6 +1263,8 @@ 1A0729D80EF56D1200B0F925 /* OOConvertSystemDescriptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOConvertSystemDescriptions.m; sourceTree = ""; }; 1A0729FC0EF5796500B0F925 /* OldSchoolPropertyListWriting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OldSchoolPropertyListWriting.h; sourceTree = ""; }; 1A0729FD0EF5796500B0F925 /* OldSchoolPropertyListWriting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OldSchoolPropertyListWriting.m; sourceTree = ""; }; + 1A0942C712D7C011003B6273 /* OOJSFrameCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSFrameCallbacks.h; sourceTree = ""; }; + 1A0942C812D7C011003B6273 /* OOJSFrameCallbacks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSFrameCallbacks.m; sourceTree = ""; }; 1A09EF4F12BD0C1900BF7F48 /* LeopardFeatures.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LeopardFeatures.xcodeproj; path = "Mac-specific/LeopardFeatures/LeopardFeatures.xcodeproj"; sourceTree = ""; }; 1A0BF3D310DAE2B30099984D /* cobra1_redux1.dat */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = cobra1_redux1.dat; sourceTree = ""; }; 1A0BF3D410DAE2B30099984D /* cobra1_redux2.dat */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = cobra1_redux2.dat; sourceTree = ""; }; @@ -2581,6 +2584,8 @@ 1AC27A0E0EA7E9940054E5F0 /* OOJSEquipmentInfo.m */, 1A11F8490F35F60C001C886C /* OOJSShipGroup.h */, 1A11F8480F35F60C001C886C /* OOJSShipGroup.m */, + 1A0942C712D7C011003B6273 /* OOJSFrameCallbacks.h */, + 1A0942C812D7C011003B6273 /* OOJSFrameCallbacks.m */, ); name = JavaScript; sourceTree = ""; @@ -3851,6 +3856,7 @@ 1A7038A212BB9F5A0015CCDC /* dummy.cpp in Sources */, 1A09EF4412BD0BCA00BF7F48 /* PlayerEntityStickMapper.m in Sources */, 1AB5E1F012BD628500C334DD /* OOJoystickManager.m in Sources */, + 1A0942CE12D7D5B9003B6273 /* OOJSFrameCallbacks.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/Core/Entities/PlayerEntity.m b/src/Core/Entities/PlayerEntity.m index 69531c20..57389f7c 100644 --- a/src/Core/Entities/PlayerEntity.m +++ b/src/Core/Entities/PlayerEntity.m @@ -63,11 +63,12 @@ MA 02110-1301, USA. #import "OOShipRegistry.h" #import "OOEquipmentType.h" #import "OOCamera.h" +#import "NSFileManagerOOExtensions.h" #import "OOScript.h" #import "OOScriptTimer.h" #import "OOJavaScriptEngine.h" -#import "NSFileManagerOOExtensions.h" +#import "OOJSFrameCallbacks.h" #import "OOJoystickManager.h" #import "PlayerEntityStickMapper.h" @@ -1269,6 +1270,7 @@ static GLfloat sBaseMass = 0.0; [[OOMusicController sharedController] stop]; [OOScriptTimer noteGameReset]; + OOJSFrameCallbacksRemoveAll(); } diff --git a/src/Core/GameController.m b/src/Core/GameController.m index 6bad99af..84939387 100644 --- a/src/Core/GameController.m +++ b/src/Core/GameController.m @@ -36,6 +36,7 @@ MA 02110-1301, USA. #import "NSFileManagerOOExtensions.h" #import "OOLogOutputHandler.h" #import "OODebugFlags.h" +#import "OOJSFrameCallbacks.h" #define kOOLogUnconvertedNSLog @"unclassified.GameController" @@ -335,6 +336,8 @@ static BOOL _switchRez = NO, _switchRezDeferred = NO; [UNIVERSE update:delta_t]; [OOSound update]; + + OOJSFrameCallbacksInvoke(delta_t); #if OOLITE_HAVE_APPKIT if (fullscreen) diff --git a/src/Core/OOCocoa.h b/src/Core/OOCocoa.h index 72c57d68..badab7cb 100644 --- a/src/Core/OOCocoa.h +++ b/src/Core/OOCocoa.h @@ -351,6 +351,15 @@ enum { #endif +#if OOLITE_FAST_ENUMERATION +#define foreach(VAR,ARR) for(VAR in ARR) +#define foreachkey(VAR,DICT) for(VAR in DICT) +#else +#define foreach(VAR,OBJ) for (NSEnumerator *ooForEachEnum = [(OBJ) objectEnumerator]; ((VAR) = [ooForEachEnum nextObject]); ) +#define foreachkey(VAR,DICT) for (NSEnumerator *ooForEachEnum = [(DICT) keyEnumerator]; ((VAR) = [ooForEachEnum nextObject]); ) +#endif + + /* Speech synthesis */ #if OOLITE_MAC_OS_X || defined(HAVE_LIBESPEAK) diff --git a/src/Core/Scripting/OOJSFrameCallbacks.h b/src/Core/Scripting/OOJSFrameCallbacks.h new file mode 100644 index 00000000..91de9d63 --- /dev/null +++ b/src/Core/Scripting/OOJSFrameCallbacks.h @@ -0,0 +1,36 @@ +/* + +OOJSFrameCallbacks.h + +Support for JavaScript callbacks to be invoked on every frame. + + +Copyright (C) 2011 Jens Ayton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#import "OOJavaScriptEngine.h" + +void InitOOJSFrameCallbacks(JSContext *context, JSObject *global); + +void OOJSFrameCallbacksInvoke(OOTimeDelta delta); + +void OOJSFrameCallbacksRemoveAll(void); diff --git a/src/Core/Scripting/OOJSFrameCallbacks.m b/src/Core/Scripting/OOJSFrameCallbacks.m new file mode 100644 index 00000000..ce5bafb3 --- /dev/null +++ b/src/Core/Scripting/OOJSFrameCallbacks.m @@ -0,0 +1,417 @@ +/* + +OOJSFrameCallbacks.m + + +Copyright (C) 2011 Jens Ayton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +#import "OOJSFrameCallbacks.h" +#import "OOCollectionExtractors.h" + + +enum +{ + kMinCount = 16 +}; + + +typedef struct +{ + jsval callback; + uint32 trackingID; + uint32 _padding; +} CallbackEntry; + + +static CallbackEntry *sCallbacks; +static OOUInteger sCount; // Number of slots in use. +static OOUInteger sSpace; // Number of slots allocated. +static OOUInteger sHighWaterMark; // Number of slots which are GC roots. +static NSMutableArray *sDeferredOps; // Deferred adds/removes while running. +static uint32 sNextID; +static BOOL sRunning; + + +// Methods +static JSBool GlobalAddFrameCallback(OOJS_NATIVE_ARGS); +static JSBool GlobalRemoveFrameCallback(OOJS_NATIVE_ARGS); +static JSBool GlobalIsValidFrameCallback(OOJS_NATIVE_ARGS); + + +// Internals +static BOOL AddCallback(JSContext *context, jsval callback, uint32 trackingID, NSString **errorString); +static BOOL GrowCallbackList(JSContext *context, NSString **errorString); + +static BOOL GetIndexForTrackingID(uint32 trackingID, OOUInteger *outIndex); + +static BOOL RemoveCallbackWithTrackingID(JSContext *context, uint32 trackingID); +static void RemoveCallbackAtIndex(JSContext *context, OOUInteger index); + +static void QueueDeferredOperation(NSString *opType, uint32 trackingID, OOJSValue *value); +static void RunDeferredOperations(JSContext *context); + + +// MARK: Public + +void InitOOJSFrameCallbacks(JSContext *context, JSObject *global) +{ + JS_DefineFunction(context, global, "addFrameCallback", GlobalAddFrameCallback, 1, 0); + JS_DefineFunction(context, global, "removeFrameCallback", GlobalRemoveFrameCallback, 1, 0); + JS_DefineFunction(context, global, "isValidFrameCallback", GlobalIsValidFrameCallback, 1, 0); + + // Set randomish initial ID to catch bad habits. + sNextID = [[NSDate date] timeIntervalSinceReferenceDate]; +} + + +void OOJSFrameCallbacksInvoke(OOTimeDelta delta) +{ + NSCAssert1(!sRunning, @"%s cannot be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + if (sCount != 0) + { + OOJavaScriptEngine *jsEng = [OOJavaScriptEngine sharedEngine]; + JSContext *context = [jsEng acquireContext]; + jsval deltaVal, result; + OOUInteger i; + + JS_BeginRequest(context); + + if (EXPECT_NOT(!JS_NewDoubleValue(context, delta, &deltaVal))) return; + + // Block mutations. + sRunning = YES; + + /* + The watchdog timer only fires once per second in deployment builds, + but in testrelease builds at least we can keep them on a short leash. + */ + OOJSStartTimeLimiterWithTimeLimit(0.1); + + for (i = 0; i < sCount; i++) + { + JS_CallFunctionValue(context, NULL, sCallbacks[i].callback, 1, &deltaVal, &result); + } + + OOJSStopTimeLimiter(); + sRunning = NO; + + if (EXPECT_NOT(sDeferredOps != NULL)) + { + RunDeferredOperations(context); + DESTROY(sDeferredOps); + } + + JS_EndRequest(context); + [jsEng releaseContext:context]; + } +} + + +void OOJSFrameCallbacksRemoveAll(void) +{ + NSCAssert1(!sRunning, @"%s cannot be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + if (sCount != 0) + { + OOJavaScriptEngine *jsEng = [OOJavaScriptEngine sharedEngine]; + JSContext *context = [jsEng acquireContext]; + JS_BeginRequest(context); + + while (sCount != 0) RemoveCallbackAtIndex(context, sCount - 1); + + JS_EndRequest(context); + [jsEng releaseContext:context]; + } +} + + +// MARK: Methods + +// addFrameCallback(callback : Function) : Number +static JSBool GlobalAddFrameCallback(OOJS_NATIVE_ARGS) +{ + OOJS_NATIVE_ENTER(context) + + // Get callback argument and verify that it's a function. + jsval callback = OOJS_ARG(0); + if (EXPECT_NOT(!JSVAL_IS_OBJECT(callback) || !JS_ObjectIsFunction(context, JSVAL_TO_OBJECT(callback)))) + { + OOJSReportBadArguments(context, nil, @"addFrameCallback", 1, OOJS_ARGV, nil, @"function"); + return NO; + } + + // Assign a tracking ID. + uint32 trackingID = sNextID; + + /* Increment by a large prime number to produce a non-obvious sequence + which still uses all 2^32 values. + */ + sNextID += 992699; + + if (EXPECT(!sRunning)) + { + // Add to list immediately. + NSString *errorString = nil; + if (EXPECT_NOT(!AddCallback(context, callback, trackingID, &errorString))) + { + OOJSReportError(context, @"%@", errorString); + return NO; + } + } + else + { + // Defer mutations during callback invocation. + QueueDeferredOperation(@"add", trackingID, [OOJSValue valueWithJSValue:callback inContext:context]); + } + + OOJS_RETURN_INT(trackingID); + + OOJS_NATIVE_EXIT +} + + +// removeFrameCallback(trackingID : Number) +static JSBool GlobalRemoveFrameCallback(OOJS_NATIVE_ARGS) +{ + OOJS_NATIVE_ENTER(context) + + // Get tracking ID argument. + uint32 trackingID; + if (EXPECT_NOT(!JS_ValueToECMAUint32(context, OOJS_ARG(0), &trackingID))) + { + OOJSReportBadArguments(context, nil, @"removeFrameCallback", 1, OOJS_ARGV, nil, @"frame callback tracking ID"); + return NO; + } + + if (EXPECT(!sRunning)) + { + // Remove it. + if (EXPECT_NOT(!RemoveCallbackWithTrackingID(context, trackingID))) + { + OOJSReportWarning(context, @"removeFrameCallback(): invalid tracking ID."); + } + } + else + { + // Defer mutations during callback invocation. + QueueDeferredOperation(@"remove", trackingID, nil); + } + + OOJS_RETURN_VOID; + + OOJS_NATIVE_EXIT +} + + +// isValidFrameCallback(trackingID : Number) +static JSBool GlobalIsValidFrameCallback(OOJS_NATIVE_ARGS) +{ + OOJS_NATIVE_ENTER(context) + + // Get tracking ID argument. + uint32 trackingID; + if (EXPECT_NOT(!JS_ValueToECMAUint32(context, OOJS_ARG(0), &trackingID))) + { + OOJS_RETURN_BOOL(NO); + } + + OOUInteger index; + OOJS_RETURN_BOOL(GetIndexForTrackingID(trackingID, &index)); + + OOJS_NATIVE_EXIT +} + + +// MARK: Internals + +static BOOL AddCallback(JSContext *context, jsval callback, uint32 trackingID, NSString **errorString) +{ + NSCParameterAssert(context != NULL && JS_IsInRequest(context)); + NSCParameterAssert(errorString != NULL); + NSCAssert1(!sRunning, @"%s cannot be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + if (EXPECT_NOT(sCount == sSpace)) + { + if (!GrowCallbackList(context, errorString)) return NO; + } + + sCallbacks[sCount].callback = callback; + if (sCount >= sHighWaterMark) + { + // If we haven't used this slot before, root it. + + if (EXPECT_NOT(!OOJSAddGCValueRoot(context, &sCallbacks[sCount].callback, "frame callback"))) + { + *errorString = @"Failed to add GC root for frame callback."; + return NO; + } + } + + sCallbacks[sCount].trackingID = trackingID; + sCount++; + + return YES; +} + + +static BOOL GrowCallbackList(JSContext *context, NSString **errorString) +{ + NSCParameterAssert(context != NULL && JS_IsInRequest(context)); + NSCParameterAssert(errorString != NULL); + + OOUInteger newSpace = MAX(sSpace * 2, (OOUInteger)kMinCount); + + CallbackEntry *newCallbacks = calloc(sizeof (CallbackEntry), newSpace); + if (newCallbacks == NULL) return NO; + + CallbackEntry *oldCallbacks = sCallbacks; + + // Root and copy occupied slots. + OOUInteger newHighWaterMark = sCount; + OOUInteger i; + for (i = 0; i < newHighWaterMark; i++) + { + if (EXPECT_NOT(!OOJSAddGCValueRoot(context, &newCallbacks[i].callback, "frame callback"))) + { + // If we can't root them all, we fail; unroot all entries to date, free the buffer and return NO. + OOUInteger j; + for (j = 0; i < i; j++) + { + JS_RemoveValueRoot(context, &newCallbacks[j].callback); + } + free(newCallbacks); + + *errorString = @"Failed to add GC root for frame callback."; + return NO; + } + newCallbacks[i] = oldCallbacks[i]; + } + + // Unroot old array's slots. + for (i = 0; i < sHighWaterMark; i++) + { + JS_RemoveValueRoot(context, &oldCallbacks[i].callback); + } + + // We only rooted the occupied slots, so reset high water mark. + sHighWaterMark = newHighWaterMark; + + // Replace array. + sCallbacks = newCallbacks; + free(oldCallbacks); + sSpace = newSpace; + + return YES; +} + + +static BOOL GetIndexForTrackingID(uint32 trackingID, OOUInteger *outIndex) +{ + NSCParameterAssert(outIndex != 0); + + /* It is assumed that few frame callbacks will be active at once, so a + linear search is reasonable. If they become unexpectedly popular, we + can switch to a sorted list or a separate lookup table without changing + the API. + */ + OOUInteger i; + for (i = 0; i < sCount; i++) + { + if (sCallbacks[i].trackingID == trackingID) + { + *outIndex = i; + return YES; + } + } + + return NO; +} + + +static BOOL RemoveCallbackWithTrackingID(JSContext *context, uint32 trackingID) +{ + NSCParameterAssert(context != NULL && JS_IsInRequest(context)); + NSCAssert1(!sRunning, @"%s cannot be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + OOUInteger index; + if (GetIndexForTrackingID(trackingID, &index)) + { + RemoveCallbackAtIndex(context, index); + return YES; + } + + return NO; +} + + +static void RemoveCallbackAtIndex(JSContext *context, OOUInteger index) +{ + NSCParameterAssert(context != NULL && JS_IsInRequest(context)); + NSCParameterAssert(index < sCount && sCallbacks != NULL); + NSCAssert1(!sRunning, @"%s cannot be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + // Overwrite entry to be removed with last entry, and decrement count. + sCallbacks[index] = sCallbacks[sCount - 1]; + sCount--; +} + + +static void QueueDeferredOperation(NSString *opType, uint32 trackingID, OOJSValue *value) +{ + NSCAssert1(sRunning, @"%s can only be called while frame callbacks are running.", __PRETTY_FUNCTION__); + + if (sDeferredOps == nil) sDeferredOps = [[NSMutableArray alloc] init]; + [sDeferredOps addObject:[NSDictionary dictionaryWithObjectsAndKeys: + opType, @"operation", + [NSNumber numberWithInt:trackingID], @"trackingID", + value, @"value", + nil]]; +} + + +static void RunDeferredOperations(JSContext *context) +{ + NSDictionary *operation = nil; + + foreach(operation, sDeferredOps) + { + NSString *opType = [operation objectForKey:@"operation"]; + uint32 trackingID = [operation oo_intForKey:@"trackingID"]; + + if ([opType isEqualToString:@"add"]) + { + OOJSValue *callbackObj = [operation objectForKey:@"value"]; + NSString *errorString = nil; + + if (!AddCallback(context, [callbackObj oo_jsValueInContext:context], trackingID, &errorString)) + { + OOLogWARN(@"script.frameCallback.deferredAdd.failed", @"Deferred frame callback insertion failed: %@", errorString); + } + } + else if ([opType isEqualToString:@"remove"]) + { + RemoveCallbackWithTrackingID(context, trackingID); + } + } +} diff --git a/src/Core/Scripting/OOJavaScriptEngine.m b/src/Core/Scripting/OOJavaScriptEngine.m index 035e2005..05cf9a4e 100644 --- a/src/Core/Scripting/OOJavaScriptEngine.m +++ b/src/Core/Scripting/OOJavaScriptEngine.m @@ -59,6 +59,7 @@ MA 02110-1301, USA. #import "OOJSSystemInfo.h" #import "OOJSEquipmentInfo.h" #import "OOJSShipGroup.h" +#import "OOJSFrameCallbacks.h" #import "OOProfilingStopwatch.h" #import "OOLoggingExtended.h" @@ -332,6 +333,7 @@ static void ReportJSError(JSContext *context, const char *message, JSErrorReport InitOOJSSystemInfo(mainContext, globalObject); InitOOJSEquipmentInfo(mainContext, globalObject); InitOOJSShipGroup(mainContext, globalObject); + InitOOJSFrameCallbacks(mainContext, globalObject); JS_EndRequest(mainContext); diff --git a/src/Core/Universe.m b/src/Core/Universe.m index 346b5c73..d396f6c5 100644 --- a/src/Core/Universe.m +++ b/src/Core/Universe.m @@ -4424,6 +4424,8 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range) Vector p1; NSMutableArray *result = nil; + OOJSPauseTimeLimiter(); + if (predicate == NULL) predicate = YESPredicate; result = [NSMutableArray arrayWithCapacity:n_entities]; @@ -4443,6 +4445,8 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range) } } + OOJSResumeTimeLimiter(); + return result; OOJS_PROFILE_EXIT @@ -4455,6 +4459,8 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range) unsigned i; Entity *candidate = nil; + OOJSPauseTimeLimiter(); + if (predicate == NULL) predicate = YESPredicate; for (i = 0; i < n_entities; i++) @@ -4463,6 +4469,8 @@ OOINLINE BOOL EntityInRange(Vector p1, Entity *e2, float range) if (predicate(candidate, parameter)) return candidate; } + OOJSResumeTimeLimiter(); + return nil; }