Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First pass at merging Node.js support #54

Merged
merged 2 commits into from
Mar 3, 2015
Merged

Conversation

blinkdog
Copy link
Contributor

@blinkdog blinkdog commented Mar 2, 2015

Building

So this is rot.js with support for Node.js. It offers a new build target node:

make clean
make node

This will recreate package.json and create lib/rot.js.

Publishing

Once the Node version of rot.js is built, package.json specifies everything that npm needs in order to publish. Publishing to npm is just:

npm publish

I have added ondras as an owner on the rot-js npm module, so you should be able to publish and add/remove owners now. If you want me to publish, just let me know and I'll do so.

Usage

After publication, the most simple way to try out rot.js for Node would be:

npm install rot-js
node -e 'var ROT = require("rot-js"); console.log(ROT.isSupported());'

Notes

  • I did not delete the existing package.json file in order to keep the diff short and clear. I do think it should be deleted, or at least regenerated, as part of your merge back to master.
  • Let me know if there is anything else I can/should do. 😃

@blinkdog
Copy link
Contributor Author

blinkdog commented Mar 2, 2015

(CC @statico) for comments / suggestions.

@ondras
Copy link
Owner

ondras commented Mar 2, 2015

Very nice, thanks! I particularly like how the "if exports" node-ism (or, more correctly, commonjs-ism) is now moved into a dedicated node-exports.js file.

One question for node-shim.js, though -- the fourth lines says

if ((typeof process !== "undefined" && process !== null ? process.env : void 0) != null) {
  1. is this really necessary? This code is evaluated only in server context...
  2. you are returning void 0, which is undefined, but you test against null. While this works, I would say that not using type coercion would result in a more readable approach
  3. the first two values in the compound expression return bool; why not test agains false instead?
  4. actually, why is there the whole != null) part anyway? 😄 Testing against a falsy value would be sufficient imho? Never the less, see point 1.

Removed unnecessary conditionals from node-shim.js and node-export.js
Fixed jsdoc comments in node-shim.js and node-export.js
Added "rng" keyword to package.node
Removed "doc" directory from npm-published files in package.node
Replaced package.json with make generated package.json
@blinkdog
Copy link
Contributor Author

blinkdog commented Mar 3, 2015

is this really necessary? This code is evaluated only in server context...

You are right! 😃 The node build target is only for Node, so the conditional is unnecessary.
By the same logic, I also removed the "if exports" from node-export.js, and left the naked for loop.

I came up with the code for that conditional by going to Try CoffeeScript and putting in the following CoffeeScript code:

if process?.env?
  alert 'Node.js!'

It generates this JavaScript:

if ((typeof process !== "undefined" && process !== null ? process.env : void 0) != null) {
  alert('Node.js!');
}

I wanted to check that process.env existed (my original draft was code that worked in the browser and Node), and figured the CoffeeScript compiler would give me something sensible. But now we've got a Node-specific build, so that check isn't necessary.

I also regenerated package.json. I prefer to delete it, but I figured an updated package.json is better than an older one. If you are OK to delete it, I'll do that, just let me know.

Thanks! 😃

@ondras
Copy link
Owner

ondras commented Mar 3, 2015

Cool, will merge + delete package.json afterwards.

ondras added a commit that referenced this pull request Mar 3, 2015
First pass at merging Node.js support
@ondras ondras merged commit afa63d3 into ondras:master Mar 3, 2015
@ondras
Copy link
Owner

ondras commented Mar 3, 2015

package.json deleted, added .gitignore and .hgignore for package.json and lib/. I assume this concludes the node.js support?

@blinkdog
Copy link
Contributor Author

blinkdog commented Mar 3, 2015

I assume this concludes the node.js support?

Yep, that should do it! Would you like me to publish to npm? Or would you like to do it?
After make node it should be just npm publish, assuming Node is installed and on your path.

@ondras
Copy link
Owner

ondras commented Mar 3, 2015

Done, published :) Please let me know if anything goes wrong or this particular feature needs more attention in the future. Thanks for your assistance!

@blinkdog blinkdog deleted the node branch March 3, 2015 11:08
@statico
Copy link
Contributor

statico commented Mar 3, 2015

Awesome! Thanks for doing this :)

@Kodiologist
Copy link
Contributor

@blinkdog, I'm eager to try this, but I can't seem to get it to work. Could you provide a working example of a game running under Node.js? If you don't already have something, you could try porting the rot.js tutorial game (full source).

@statico
Copy link
Contributor

statico commented Mar 18, 2015

FWIW, I was specifically using the FOV and Map classes, not the whole framework. If it's useful, here are the snippets that use ROT within Node 0.10:

    ROT = require 'rot.js'
    {vec2} = require 'gl-matrix'

    ...

    @terrain = new DenseMap(@width, @height)
    pos = [0, 0]
    map = new ROT.Map.Digger(@width, @height)
    map.create (x, y, v) =>
      vec2.set pos, x, y
      @terrain.set pos, switch v
        when 0 then TILES.FLOOR
        when 1 then TILES.VOID

    for corridor in map.getCorridors()
      for x in [corridor._startX..corridor._endX]
        for y in [corridor._startY..corridor._endY]
          vec2.set pos, x, y
          @terrain.set pos, TILES.CORRIDOR if @terrain.get(pos) is TILES.FLOOR

    ...

    # computer what areas the player can see
    pos = [0, 0]
    diff = new SparseMap(@level.width, @level.height)
    isWalkable = (x, y) =>
      vec2.set pos, x, y
      @level.terrain.get(pos) of WALKABLE_TILES
    fov = new ROT.FOV.PreciseShadowcasting(isWalkable)
    for _, player of @playerActors
      [x, y] = player.pos
      fov.compute x, y, 10, (x, y, _, visible) =>
        # for now, just send terrain data to client
        vec2.set pos, x, y
        diff.set pos, terrain: @level.terrain.get pos

    ...

    steps = []
    path = new ROT.Path.AStar(target[0], target[1], isWalkable)
    path.compute @pos[0], @pos[1], (x, y) ->
      steps.push [x, y]

@Kodiologist
Copy link
Contributor

Thank you. What I'm puzzled about, mostly, is proper use of ROT.Display.Term.Xterm and getting keyboard input. Although, I guess the latter would need to accomplished with a different Node package.

@ondras
Copy link
Owner

ondras commented Mar 18, 2015

Right. There is basically no input handling in rot.js whatsoever, as regular DOM events are somewhat sufficient in this area.

@blinkdog
Copy link
Contributor Author

proper use of ROT.Display.Term.Xterm

"term" is another layout engine, similar to "rect" for rot.js in the browser.
In the browser, "rect" is default, so often code for the browser is written like this:

var display = new ROT.Display({width: 80, height: 25});

And this code would do the same thing, in the browser:

var display = new ROT.Display({width: 80, height: 25, layout: "rect"});

When working with Node.js, there is no DOM or browser, just your good old console with terminal emulation. So, when targeting Node.js you need to explicitly specify the "term" layout:

var display = new ROT.Display({width: 80, height: 25, layout: "term"});

Now, the tricky thing about terminal emulation is that there are a LOT of terminals to emulate. Each terminal had it's own sub/super set of ANSI Escape Codes. In order to make pretty colors on the display, the code needs to know how to tell the terminal "change to red background" and such.

That's where Xterm comes in. You see, like the "rect" default that rot.js provided for the browser, the Node.js port also pulls this trick with termColor:

var display = new ROT.Display({width: 80, height: 25, layout: "term", termColor: "xterm"});

The Node.js port assumes that you are running an xterm-compatible terminal emulation. If you are running Node.js on a Linux or MacOS machine, you're probably OK. If you are running Node.js on a Windows machine, the default console probably isn't going to cut it. You'll need something like MobaXterm or the like.

If there was enough demand, and I could find a Windows machine to test it on, I would consider making a ROT.Display.Term.Microsoft for the Windows folks. But, I don't see that demand yet.

I'm eager to try this, but I can't seem to get it to work.
Could you provide a working example of a game running under Node.js?
If you don't already have something, you could try porting the rot.js tutorial game (full source).

Sure. I'll see what I can come up with in a few hours.

@blinkdog
Copy link
Contributor Author

Okay @Kodiologist check out my repository here:

https://github.com/blinkdog/ananas-aus-caracas-node

It is the rot.js tutorial game with a shim for keyboard handling (addEventListener and removeEventListener), and a stub for the alert() function.

There are a few very tiny modifications to the game code itself, basically layout:"term" for the display and having the Node process exit nicely when the game ends.

I will do a better port (replace the browser-isms with Node-isms) when I get some free time this weekend. But for now, this should get you going with a working Node.js example.

@Kodiologist
Copy link
Contributor

Awesome, thanks! I linked to your repository from the tutorial.

I notice that the terminal cursor is visible when there's a floor square to the right of Pedro. That's probably easy to fix by hiding or moving the cursor, perhaps with an extra method of ROT.Display.Term.

@blinkdog
Copy link
Contributor Author

I made another small round of updates to the shim version:

  • The shim now hides the cursor when the program starts
  • The alert() function now displays its message and then kludges a redraw
  • The program shows the cursor when the program ends

perhaps with an extra method of ROT.Display.Term.

This is a good idea. I should code this as a permanent fix and offer another pull request to @ondras

@blinkdog
Copy link
Contributor Author

Now the repository contains a real Node version (game-node.js) that refactors all the browser-specific things into Node-specific things.

Some changes in this version:

  • The size of Pedro's warehouse depends on the size of your terminal window
  • Ctrl+C or ESC can be used to quit the game any time
  • Messages are displayed in the same yellow color as the player
  • Lots of comments around the things that I added/changed
  • Includes pretty screenshot referenced from README.md

Does it seem OK, @Kodiologist ?

@Kodiologist
Copy link
Contributor

@blinkdog Looks quite good to me. Well-commented, too.

You don't need the bind function, since its uses can be replaced with the built-in bind method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants