Progress

General discussion for players of Oolite.

Moderators: another_commander, winston

User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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 [EliteWiki] 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.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.)
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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).
Last edited by JensAyton on Tue Jan 18, 2011 5:07 pm, edited 2 times in total.
Reason: Updated with <console input>, which doesn’t really deserve its own post but is quite nice.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.)
Last edited by JensAyton on Thu Jan 20, 2011 7:53 pm, edited 1 time in total.
Reason: Now with actual names!
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Re: Progress

Post by Svengali »

Nice. And yes, entGetScreenModel() will be updated and marked as deprecated.

Oh, I guess you mean mission.displayModel right?

Edit: snip snap
Last edited by Svengali on Thu Jan 20, 2011 8:50 pm, edited 1 time in total.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
Last edited by JensAyton on Fri Jan 21, 2011 6:20 pm, edited 2 times in total.
Reason: Simplified code.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
User avatar
Commander McLane
---- E L I T E ----
---- E L I T E ----
Posts: 9520
Joined: Thu Dec 14, 2006 9:08 am
Location: a Hacker Outpost in a moderately remote area
Contact:

Re: Progress

Post by Commander McLane »

Very nice addition! :D
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Re: Progress

Post 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.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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).
User avatar
Killer Wolf
---- E L I T E ----
---- E L I T E ----
Posts: 2269
Joined: Tue Jan 02, 2007 12:38 pm

Re: Progress

Post 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?
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Re: Progress

Post 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.
Post Reply