Progress

General discussion for players of Oolite.

Moderators: winston, another_commander

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

Post by JensAyton »

Following my fine tradition of running off on a tangent, I’ve hacked together a basic but working command-line version of the JavaScript console, communicating over TCP, and with no Objective-C in it.

Code: Select all

- Waiting for connection.
- Client connected.
- Connection established to Oolite 1.69.2.

5 + 3
  > 5 + 3
  8
player
  > player
  [Player "Cobra Mark III" ID: 100 position: (-49515.3, 60769.4, 427622) scanClass: CLASS_PLAYER status: STATUS_DOCKED]
banana
  > banana
  Exception: ReferenceError: banana is not defined
    Active script: "oolite-mac-js-console" 1.69.2
    oolite-mac-js-console.js, line 147:
    	let result = eval(command)
- Connection closed (Oolite is terminating.).
It should be portable to Windows. Linux may be harder. I’ll try to slap together some documentation tomorrow, and integrate the Oolite-side part into Oolite itself rather than, as now, the Mac-only debug OXP.

Edit: I should probably add that the code is designed so that the bit for communicating with Oolite can easily be reused in, say, a GUI debug console.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Console protocol is now documented. Writing Windows and Linux consoles that don’t suck is considered a third-party opportunity. The command-line implementation in the repository now has build instructions, of sorts.
User avatar
Arexack_Heretic
Dangerous Subversive Element
Dangerous Subversive Element
Posts: 1876
Joined: Tue Jun 07, 2005 7:32 pm
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Contact:

Post by Arexack_Heretic »

Just for those that don't understand code like their native language...
what are the possibilities for this console?

Will it enable us to directly execute commands in the game?
Will it make possible for non-adept coders to build methods without using C?
Will it enable better debugging tools?
Will it do all this and more? :D
Riding the Rocket!
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

The console allows you to issue any JavaScript command that you could usually use in a script, but allows you to do it interactively rather than having to quit, modify the script and run Oolite again. This means that you can fiddle with and examine the game state as it is in any give moment, rather than having to try to recreate the state each time. For instance, you can manipulate ships in the game world:

Code: Select all

player.target.call("becomeExplosion")
player.setPosition(player.position.add(0, 100, 0))
Spawn ships for testing:

Code: Select all

system.legacy_addSystemShips("myShipRole", 1, 1)
Examine mission state:

Code: Select all

missionVariables["trumbles"]
// Console prints "TRUMBLE_BOUGHT"
Write your own debug tools:

Code: Select all

:setM startLogging this.loggingTimer = new Timer(this, function() { Log("player position: " + player.position) } , 0, PARAM)
:setM stopLogging if (this.loggingTimer) { this.loggingTimer.stop(); delete this.loggingTimer }
:startLogging 1
// Player position is logged at one-second intervals
Make scripts do things:

Code: Select all

system.mainStation.script.doTestStuff()
Examine and modify the scripts of live objects:

Code: Select all

> systemScripts["oolite-cloaking-device"].willExitWitchSpace
// Console prints:
this.willExitWitchSpace = function()
{
    if (galaxyNumber == 4)
    {
        if (missionVariables.cloak == null)
        {
            var cloakCounter = missionVariables.cloakcounter;
            if (cloakCounter == null)  cloakCounter = 1;
            else  cloakCounter++;
            missionVariables.cloakcounter = cloakCounter;
            if (cloakCounter > 6 && system.countShipsWithRole("asp-cloaked") == 0)
            {
                system.legacy_addShips("asp-cloaked", 1);
                system.legacy_addShips("asp-pirate", 2);
            }
        }
    }
}
> systemScripts["oolite-cloaking-device"].willExitWitchSpace = function() { player.credits += 5000 }
// Console prints:
function()
{
	player.credits += 5000
}
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Oh, here’s another one:

Code: Select all

> printList(system.allShips)
// Console prints:
72 items:
  [Player "Cobra Mark III" ID: 100 position: (-49515.3, 60769.4, 427622) scanClass: CLASS_PLAYER status: STATUS_START_GAME]
  [Station "Coriolis Station" "Coriolis Station" ID: 105 position: (-49515.3, 60769.4, 427622) scanClass: CLASS_STATION status: STATUS_ACTIVE]
  [Ship "Cobra Mark III" ID: 174 position: (0, 0, 261.843) scanClass: CLASS_NO_DRAW status: STATUS_COCKPIT_DISPLAY]
  [Ship "GalCop Viper" ID: 175 position: (-48975.8, 60107.3, 427897) scanClass: CLASS_POLICE status: STATUS_LAUNCHING]
  [Ship "Navigation Buoy" ID: 172 position: (-43500.3, 53387.3, 430676) scanClass: CLASS_BUOY status: STATUS_IN_FLIGHT]
...
  [Ship "Asteroid" ID: 170 position: (27558.5, 7192.02, 865128) scanClass: CLASS_ROCK status: STATUS_IN_FLIGHT]
  [Ship "Asteroid" ID: 171 position: (-16116.1, 20510.8, 880110) scanClass: CLASS_ROCK status: STATUS_IN_FLIGHT]

> printList(system.planets)
2 items:
  [Planet ID: 103 position: (0, 0, 452760) scanClass: CLASS_NO_DRAW status: STATUS_ACTIVE type: PLANET_TYPE_GREEN radius: 41.160km]
  [Planet ID: 104 position: (-782892, -340254, 754874) scanClass: CLASS_NO_DRAW status: STATUS_ACTIVE type: PLANET_TYPE_SUN radius: 84.449km]
printList() is a JavaScript function defined by the console script, which looks like this:

Code: Select all

this.printList = function(l)
{
    let length = l.length
    
    ConsoleMessage("printList", length.toString() + " items:")
    for (let i = 0; i != length; i++)
    {
        ConsoleMessage("printList", "  " + l[i].toString())
    }
}
User avatar
Arexack_Heretic
Dangerous Subversive Element
Dangerous Subversive Element
Posts: 1876
Joined: Tue Jun 07, 2005 7:32 pm
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Contact:

Post by Arexack_Heretic »

How about a command that enables realtime viewing of the AI states/commands and behaviour of the current player.target?
Riding the Rocket!
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Like shift-H, you mean?

As it stands, you can get player.target.AIState. I don’t want to expose the behaviour directly as it’s an implementation detail that may change. Adding it to the description (what you get if you type player.target, the stuff that’s in the list in my previous post) is an option, though. Possibly additional attributes like behaviour and the AI stack could be exposed when the debug OXP is installed.
User avatar
Arexack_Heretic
Dangerous Subversive Element
Dangerous Subversive Element
Posts: 1876
Joined: Tue Jun 07, 2005 7:32 pm
Location: [%H] = Earth surface, Lattitude 52°10'58.19"N, longtitude 4°30'0.25"E.
Contact:

Post by Arexack_Heretic »

I'm currently in a code-to-keyboard battle with a recalcitrant AI...the explosiveAI.
You are saying using [shift-H] would have saved me from using an entity dump (pause+zero) and reloading the stderr file again and again?

....where can these nuggets of unfathomable knowledge be found?!

I find: pauseAI: s does not work except in the UPDATE = (); line.
commsMessage is flaky.
I'm not yet sure what is going wrong, but for now I'm assuming it is my code.

Relevant code and stuff here: https://bb.oolite.space/viewtopic.php?t=3905&start=45
Riding the Rocket!
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Arexack_Heretic wrote:
I'm currently in a code-to-keyboard battle with a recalcitrant AI...the explosiveAI.
You are saying using [shift-H] would have saved me from using an entity dump (pause+zero) and reloading the stderr file again and again?
I’m referring to key_dump_target_state, which is not enabled by default in as-yet-released versions of Oolite. You can find out about it by reading various bits of the forum. :-)

You’ll still need to reload the log file, although there are probably log-watching programs for Windows that’ll show updates as they’re written. Echoing the key_dump_target_state dump to the debug console might be a reasonable thing to do.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

  • Implemented Planet JavaScript class. Not very exciting; it’s an http://wiki.alioth.net/index.php/Oolite ... ass/Entity with the additional attributes isMainPlanet, isSun, hasAtmosphere and radius.
  • Added System attributes sun, mainPlanet, planets and allShips.
  • Added System methods shipsWithPrimaryRole(), shipsWithRole() and entitiesWithScanClass().
  • Did not add method filteredEntities(), because it exposed a structural problem that I was aware of but was skirting around. The problem will also arise when a ship with a JS script is created from within a JS script. I think there’s a relatively painless fix, but I’m not certain.
system.filteredEntities() is particularly nice. It lets you get a list of entities based on arbitrary criteria. For instance, if you want to get all police ships that are within scanner range of the player and are not currently occupied, you might do this:

Code: Select all

this.findIdlePolice = function()
{
    function isIdlePolice(entity)
    {
        return entity.isShip && entity.isPolice && !entity.target
    }
    
    return system.filteredEntities(this, isIdlePolice, player, 25600)
}
The function isIdlePolice() (the filter predicate) will be called for each entity within 25600 metres of the player, and those for which it returns true will be put in a list. The second two parameters, the reference entity and range (in this case player and 25600), are optional; if they’re not specified, the entire system is searched. The methods shipsWithPrimaryRole(), shipsWithRole() and entitiesWithScanClass() also have optional reference entity and range parameters which work in the same way.

Edit: Updated isIdlePolice() example to actually check for policehood.
Last edited by JensAyton on Mon Oct 08, 2007 3:05 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:

Post by JensAyton »

Intended additional semantics: the search-for-entities functions should return a list sorted by distance from the reference entity, if specified. They should also filter out ships with STATUS_COCKPIT_DISPLAY (those are used on the demo screen, shipyard screen etc.).
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Sorting and range limitation work.

Code: Select all

> this.wp = system.shipsWithRole("buoy-witchpoint")[0]
[Ship "Witchpoint Beacon" ID: 132 position: (-8700, -6700, -2500) scanClass: CLASS_BUOY status: STATUS_IN_FLIGHT]

> this.printDistance = function (l) { let length = l.length; for (let i = 0; i != length; ++i)  ConsoleMessage("printList", l[i].ID.toString() + ": " + l[i].position.distanceTo(wp)) }
function (l) {
    let length = l.length;
    for (let i = 0; i != length; ++i) {
        ConsoleMessage("printList", l[i].ID.toString() + ": " + l[i].position.distanceTo(wp));
    }
}

> this.printDistance(system.shipsWithRole("asteroid"))
124: 703063.1875
130: 713221.6875
128: 706114.75
127: 704948.375
129: 711866.8125
125: 714886.8125
126: 714351.5625

> this.printDistance(system.shipsWithRole("asteroid", wp))
124: 703063.1875
127: 704948.375
128: 706114.75
129: 711866.8125
130: 713221.6875
126: 714351.5625
125: 714886.8125

> this.printDistance(system.shipsWithRole("asteroid", wp, 710000))
124: 703063.1875
127: 704948.375
128: 706114.75
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

  • Fixed bug where calling JavaScript code from Objective-C code that was itself called from JavaScript code would break the JavaScript engine. This was preventing System.filtereEntities() from working, and also breaking when a ship with a ship script was created from another script.
  • Added Ship.hasRole() method, after needing it in a function and wondering for a second what sort of idiot would leave out such an obvious method. (‘ship.roles.indexOf("somerole") > -1’ works, but is somewhat lacking in elegance.)
Things holding up next release:
Well, infinitely many things, of course. But the most important ones:
  • Need to go through and document the event handlers for ship scripts. Some handlers need to be moved from PlayerEntity to ShipEntity. I think there are some with dodgy names.
  • Getting the client side of the debug console stuff working on Windows would be nice. Currently another_commander can connect to a console running on my machine, but doesn’t see any input coming back, limiting its usefulness. Making the command-line console, at least, run on Windows is a secondary concern – that can be released separately without a full Oolite update.
  • I should probably look at at least a few bug reports at some point.
  • At least one very important thing I can’t think of just at the moment.
So, maybe a 1.70 alpha release in a couple of weeks. (This one is likely to be… interesting… so we’ll probably be recommending that people don’t switch over to it immediately, but obviously it needs testing.) Oh, and it still won’t have the new “multi-nebula” sky, but the sky/nebula bug is fixed.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Implemented access to world scripts:

Code: Select all

> worldScriptNames
oolite-trumbles,ahruman-billboard-populate,Pi-Forty-Two Con stores,oolite-thargoid-plans,oolite-nova,oolite-constrictor-hunt,oolite-cloaking-device
> :d worldScripts
> dumpObjectLong(eval("worldScripts"))
[object WorldScripts]:
    oolite-trumbles = [Script "oolite-trumbles" version 1.69.2]
    ahruman-billboard-populate = [Script "ahruman-billboard-populate" version 1.0]
    Pi-Forty-Two Con stores = null
    oolite-thargoid-plans = null
    oolite-nova = null
    oolite-constrictor-hunt = null
    oolite-cloaking-device = [Script "oolite-cloaking-device" version 1.69.2]
The null ones are legacy scripts. The JavaScript ones are Script objects, which can be interacted with, primarily by calling methods on the script. See also: Are sub-OXPs possible?.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

  • Applied Eric Walch’s AI fixes.
  • Had several mostly-successful remote JavaScript debugging sessions with another_commander. The Oolite side of the JavaScript console basically works, but is sensitive to bad input. On the face of it, this appears to be a GNUstep bug.
Post Reply