Page 48 of 139
Re: Progress
Posted: Fri Jan 14, 2011 5:01 pm
by JensAyton
Fun with JavaScript stack traces:
- Stack traces now include the names and values of variables for each frame. (Specifically, it lists
this
, then any function parameters, then any local variables.)
- The logcontrol.plist entries for controlling stack traces have been removed. Instead, when the debug console is connected, they can be controlled with the properties
debugConsole.dumpStackForErrors
and debugConsole.dumpStackForWarnings
(default: true
; changes are persisted in preferences). Without the debug console, stack traces are not dumped by default but can be enabled with the
hidden settings “dump-stack-for-errors” and “dump-stack-for-warnings”.
- When the debug console is connected, a stack trace can be dumped at any time with the
debugger;
statement. Without the debug console, this does nothing and takes essentially zero time.
- Stack traces are always dumped when the time limiter kills a script.
Example:
Code: Select all
this.name = "debuggerTest";
this.version = "1.0";
this.shipWillLaunchFromStation = function shipWillLaunchFromStation(station)
{
function doStuff()
{
var somethingStrangeHappened = (station == system.mainStation);
if (somethingStrangeHappened)
{
log("debuggerTest.strangeness", "Something strange happened!");
debugger;
}
}
doStuff();
}
…logs the following:
Code: Select all
[debuggerTest.strangeness]: Something strange happened!
[script.javaScript.debugger]: debugger invoked during debuggerTest 1.0:
[script.javaScript.stackTrace]: 0 /Users/jayton/Library/Application Support/Oolite/AddOns/script.js:12 doStuff()
[script.javaScript.stackTrace]: this: [object Global]
[script.javaScript.stackTrace]: somethingStrangeHappened: true
[script.javaScript.stackTrace]: 1 /Users/jayton/Library/Application Support/Oolite/AddOns/script.js:16 shipWillLaunchFromStation()
[script.javaScript.stackTrace]: this: [Script "debuggerTest" version 1.0]
[script.javaScript.stackTrace]: station: [Station "Coriolis Station" "Coriolis Station" position: (-49515.3, 60769.4, 427622) scanClass: CLASS_STATION status: STATUS_ACTIVE]
[script.javaScript.stackTrace]: doStuff: function doStuff
The precise meaning of the “debugger” statement may change in future.
Re: Progress
Posted: Fri Jan 14, 2011 6:55 pm
by JensAyton
As a side note, native frames don’t appear in stack traces under the new JS engine. This could be somewhat confusing with callback methods like System.filteredEntities()
, where you’ll get a frame for the callback followed by a frame for the function that called System.filteredEntities()
with nothing between to indicate System.filteredEntities()
itself. (In the old regime, you’d get something like “1 <Oolite native>”, which doesn’t tell you much except that there’s something there.)
Re: Progress
Posted: Sun Jan 16, 2011 10:30 pm
by JensAyton
The profiler (
console.profile()
,
console.getProfile()
, the
:time
macro) now includes JavaScript functions and file names/lines under Mac OS X. For this feature to work, three things are required:
- The new JS engine (which excludes normal svn builds for Linux, but not nightlies)
- Native Objective-C exceptions (which excludes Windows, at least with the toolchain another_commander is using)
- SpiderMonkey must be built with
MOZ_TRACE_JSCALLS
defined (which probably excludes Linux nightlies, but should be fixable).
A typical JavaScript profile entry looks like this:
Code: Select all
NAME T COUNT TOTAL SELF TOTAL% SELF% SELFMAX
(oolite-nova-mission.js:173) <anonymous> J 1 0.11 0.09 41.2 31.9 0.09
The first part is the file name and line number of the function (well,
approximately the first line of the function; the details of this are complicated, but it’s enough to help find which anonymous function you’re looking at). The
<anonymous>
is where the function’s name would be, if it had one – like most script methods, it doesn’t. The J indicates that it’s a JavaScript function (as opposed to N for native code). The rest is as before.
Re: Progress
Posted: Tue Jan 18, 2011 3:10 pm
by JensAyton
I’ve added a new debugging toy using the same infrastructure as profiling (and hence with the same limitations/requirements): execution tracing. This simply logs the hierarchy of calls made, which can help track down where an error is occurring. (A similar facility exists for legacy scripts in form of the
script.debug.trace
log message class.)
To generate a trace, call
console.trace(<some function>, <optional this value>)
. For example:
Code: Select all
console.trace(function test() {
function inner (scale) {
var v = new Vector3D(-1, 2, 0);
return v.multiply(scale);
}
return inner(Math.random());
})
produces (with
logging-show-class
turned off):
Code: Select all
>>>> Beginning trace.
>> (<console input>) test(this: [Script "oolite-debug-console" version 1.75]) [JS]
>> random() [NW]
>> (<console input>) inner(this: [object Global], scale: 0.012386843307234607) [JS]
>> VectorConstruct [ON]
>> VectorFromArgumentListNoErrorInternal [ON]
>> multiply() [NW]
>> VectorMultiply [ON]
>> JSObjectGetVector [ON]
>> OOJSArgumentListGetNumberNoError [ON]
>> VectorToJSValue [ON]
>> JSVectorWithVector [ON]
<<<< End of trace.
There are three types of entries here, tagged with cryptic acronyms.
- [JS] entries are pure JavaScript function calls; there’s one for
test
and one for inner
, with this
and arguments listed. As in the trace results, these have a location; code from the console is now tagged “<console input>” instead of “oolite-debug-console.js:779”.
- [NW] entries are “native wrappers”, the JavaScript representation of functions implemented by Oolite or SpiderMonkey. In this example, there’s one for
random
(i.e. Math.random()
) and one for multiply
(i.e. Vector3D.prototype.multiply()
).
- [ON] entries are Oolite native functions with profiling support.
The return value from
console.trace()
is the return value of the function it was passed (so in the example above, the scaled vector produced by
test()
is echoed in the log).
Re: Progress
Posted: Thu Jan 20, 2011 3:17 pm
by JensAyton
- New
mission.runScreen()
parameter spinModel
(boolean, default true). If false (or a falsey value), and model
is specified, the model will not be rotated automatically (it will be created with orientation (1/√2, 1/√2, 0, 0), which shows it from the top with the front towards the top of the screen).
- New property
mission.displayModel
(Ship); while a mission screen is running, if it has a model
, this is it.
-
system.allShips
no longer includes ships with status STATUS_COCKPIT_DISPLAY
(the fact that they’re technically “in the universe” is an implementation detail that wasn’t meant to be exposed; system.filteredEntities()
always filtered them out). Cabal Common Functions’ entGetScreenModel()
relied on this bug, so it will no longer work. Use mission.displayModel
instead. (I assume Cabal Common Functions will be updated to do this in 1.75.)
Re: Progress
Posted: Thu Jan 20, 2011 6:31 pm
by Svengali
Nice. And yes, entGetScreenModel() will be updated and marked as deprecated.
Oh, I guess you mean mission.displayModel right?
Edit: snip snap
Re: Progress
Posted: Thu Jan 20, 2011 8:00 pm
by JensAyton
Svengali wrote:Oh, I guess you mean mission.displayModel right?
That’s the bunny.
Ship scripts may now specify escort formations. This is done with the method
coordinatesForEscortPosition(position)
in the mothership, which takes an integer specifying the index of an escort (starting from zero) and returns a vector or three-element array specifying its spacial position relative to the mothership. The default implementation (ported from the old hard-coded behaviour; rewritten as it would appear in a ship script) is:
Code: Select all
(function () {
const escortPositions =
[
// V-shape escort pattern
new Vector3D(-2, 0, -1),
new Vector3D( 2, 0, -1),
new Vector3D(-3, 0, -3),
new Vector3D( 3, 0, -3)
/*
// X-shape escort pattern
new Vector3D(-2, 0, 2),
new Vector3D( 2, 0, 2),
new Vector3D(-3, 0, -3),
new Vector3D( 3, 0, -3)
*/
];
this.coordinatesForEscortPosition = function (index)
{
var highPart = 1 + (index >> 2); // Equivalent to 1 + Math.floor(position / 4)
var lowPart = index % 4;
var spacing = this.ship.collisionRadius * 3 * highPart;
return escortPositions[lowPart].multiply(spacing);
}
}).call(this);
Currently, this is called every frame for each escort, but I want to add a cache. To support this, there is also a Ship method
updateEscortFormation()
, and an AI method also called
updateEscortFormation
. When the cache is added, these will cause it to be rebuilt by calling
coordinatesForEscortPosition()
for each escort.
Re: Progress
Posted: Fri Jan 21, 2011 10:25 pm
by JensAyton
Because of a bug in the prefix script,
coordinatesForEscortPosition()
overrides didn’t actually work. They’ll work in the next nightly.
Here is a test case. Spawn an “ahruman-escort-test-mother” and it will have six escorts flying in a circle (or hexagon) in front of it.
Re: Progress
Posted: Sat Jan 22, 2011 11:33 am
by Commander McLane
Very nice addition!

Re: Progress
Posted: Sun Jan 23, 2011 5:21 pm
by JensAyton
Screen backgrounds/overlays can now be scaled, allowing for more than 480 pixels vertically. Welcome to the 1990s!
This works by providing a dictionary/JS object instead of a string. The dictionary must have a “name” property, and may also have “width” and/or “height”; if only one dimension is specified, the texture is scaled proportionately. If neither is specified, they will default to the pixel dimensions of the image, which matches the old behaviour (unless your texture has a non-power-of-two size, in which case the behaviour now makes more sense).
The scaling works as follows: the Oolite display space is always 480 units high, while the width varies with the window’s aspect ratio. If the window is at a 4:3 aspect ratio, the width will be 640 units. If a background/overlay has dimensions specified as 640 by 480, it will fill up the screen.
The most common cases are likely to be:
- A higher-resolution version of your current background/overlay, with the same margins: use the dimensions of the old versions. For a full-screen backdrop, this will generally be width = 1024, height = 512 (or just height = 512).
- A proportionately scaled-up version of your current background/overlay with the margins trimmed: height = 480.
- An image scaled disproportionately to fit into a power-of-two aspect ratio (this is fiddly, but makes the best use of memory): the ideal dimensions of the image, scaled to a height of 480. For instance, for a 4:3 image, you’d use width = 640, height = 480. In this case, both dimensions must be specified.
I believe I’ve covered all the situations in which overlays can be specified, namely:
In the JavaScript cases, passing
null
or
""
will remove any existing overlay/background, overriding
screenbackgrounds.plist.
Re: Progress
Posted: Sun Jan 23, 2011 6:36 pm
by JensAyton
New JavaScript function defaultFont.measureString(<string>)
returns the width of a string, in ems, assuming no line breaks. (An em is the intrinsic unit size of a font; for a font at 12 points, 1 em = 12 pt. The mission screen text area is about 32 em wide. For reference, the sizes used in oolite-font.plist are in eigths of an em.)
The measurement takes the substitutions defined in oolite-font.plist into account.
The existence of the defaultFont
object does not signify any plan to add multiple font support, it’s just there for future-proofing.
Re: Progress
Posted: Sun Jan 23, 2011 6:56 pm
by Svengali
Hey cool! Muchas gracias .-)
This gave me always some headaches (e.g. when thinking about localization/fonts and .charCodeAt()'s Unicode usage). Rock'n'Roll! Will update Cabal_Common.
Re: Progress
Posted: Mon Jan 24, 2011 5:26 pm
by JensAyton
Since yesterday, coordinatesForEscortPosition()
is now passed a second parameter, which is the number of escort slots the ship has. I say “slots”, because the escort index and count passed don’t necessarily correspond to live ships; in particular, coordinatesForEscortPosition()
is called before each escort is added, and destroying an escort leaves a hole in the escort array (which can potentially be filled by acquiring a new escort that’s fishing for work).
Also, escort positions are now cached, so updateEscortFormation()
is required if you want to change the formation (or create animated formations; it is valid to call updateEscortFormation()
from within coordinatesForEscortPosition()
to cause an update in the next frame).
Re: Progress
Posted: Mon Jan 24, 2011 5:39 pm
by Killer Wolf
sounds interesting, but how would you animate the formations? is there an indicator that shows the first formation has been achieved, thereby letting the 2nd formation begin?
Re: Progress
Posted: Mon Jan 24, 2011 5:50 pm
by JensAyton
Nope. Since the ships are always chasing a moving target, the true formation can never really be achieved. However, purely time-based animation works, and the ships will continuously try to get where they
should be if they aren’t distracted by fighting, collision avoidance etc.
In fact, I have
here in my pocket a modified example where the escorts switch between a spinning circle in front of the mother and a static circle behind the mother.