pp and difficulty calculator for osu! in pure javascript


Keywords
osu!, pp, beatmap, parser, difficulty, ppv2
License
Unlicense
Install
npm install ojsama@2.2.0

Documentation

this is free and unencumbered software released into the public domain. refer to the attached UNLICENSE or http://unlicense.org/

Build Status

pure javascript implementation of https://github.com/Francesco149/oppai-ng intended to be easier to use and set up for js developers as well as more portable than straight up bindings at the cost of some performance

installation:

since this is a single-file library, you can just drop the file into your project:

cd my/project
curl https://waa.ai/ojsama > ojsama.js

or include it directly in a html page:

<script type="text/javascript" src="ojsama.min.js"></script>

it's also available as a npm package:

npm install ojsama

you can find full documentation of the code at http://hnng.moe/stuff/ojsama.html or simply read ojsama.js

usage (nodejs):

(change ./ojsama to ojsama if you installed through npm)

var readline = require("readline");
var osu = require("./ojsama");

var parser = new osu.parser();
readline.createInterface({
  input: process.stdin, terminal: falsei
})
.on("line", parser.feed_line.bind(parser))
.on("close", function() {
  console.log(osu.ppv2({map: parser.map}).toString());
});
$ curl https://osu.ppy.sh/osu/67079 | node minexample.js
133.24 pp (36.23 aim, 40.61 speed, 54.42 acc)

advanced usage (nodejs with acc, mods, combo...):

var readline = require("readline");
var osu = require("./ojsama");

var mods = osu.modbits.none;
var acc_percent;
var combo;
var nmiss;

// get mods, acc, combo, misses from command line arguments
// format: +HDDT 95% 300x 1m
var argv = process.argv;

for (var i = 2; i < argv.length; ++i)
{
  if (argv[i].startsWith("+")) {
    mods = osu.modbits.from_string(argv[i].slice(1) || "");
  }

  else if (argv[i].endsWith("%")) {
    acc_percent = parseFloat(argv[i]);
  }

  else if (argv[i].endsWith("x")) {
    combo = parseInt(argv[i]);
  }

  else if (argv[i].endsWith("m")) {
    nmiss = parseInt(argv[i]);
  }
}

var parser = new osu.parser();
readline.createInterface({
  input: process.stdin, terminal: false
})
.on("line", parser.feed_line.bind(parser))
.on("close", function() {
  var map = parser.map;
  console.log(map.toString());

  if (mods) {
    console.log("+" + osu.modbits.string(mods));
  }

  var stars = new osu.diff().calc({map: map, mods: mods});
  console.log(stars.toString());

  var pp = osu.ppv2({
    stars: stars,
    combo: combo,
    nmiss: nmiss,
    acc_percent: acc_percent,
  });

  var max_combo = map.max_combo();
  combo = combo || max_combo;

  console.log(pp.computed_accuracy.toString());
  console.log(combo + "/" + max_combo + "x");

  console.log(pp.toString());
});
$ curl https://osu.ppy.sh/osu/67079 | node example.js
TERRA - Tenjou no Hoshi ~Reimeiki~ [BMax] mapped by ouranhshc

AR5 OD8 CS4 HP8
262 circles, 69 sliders, 5 spinners
469 max combo

4.33 stars (2.09 aim, 2.19 speed)
100.00% 0x100 0x50 0xmiss
469/469x
133.24 pp (36.23 aim, 40.61 speed, 54.42 acc)

$ curl https://osu.ppy.sh/osu/67079 \
| node example.js +HDDT 98% 400x 1m
...
+HDDT
6.13 stars (2.92 aim, 3.11 speed)
97.92% 9x100 0x50 1xmiss
400/469x
266.01 pp (99.70 aim, 101.68 speed, 60.41 acc)

usage (in the browser)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <script type="text/javascript" src="ojsama.min.js"></script>
  <script type="text/javascript">
  function load_file()
  {
    var frame = document.getElementById("osufile");
    var contents = frame.contentWindow
      .document.body.childNodes[0].innerHTML;

    var parser = new osu.parser().feed(contents);
    console.log(parser.toString());

    var str = parser.map.toString();
    str += osu.ppv2({map: parser.map}).toString();

    document.getElementById("result").innerHTML = str;
  }
  </script>
</head>
<body>
  <iframe id="osufile" src="test.osu" onload="load_file();"
  style="display: none;">
  </iframe>
  <blockquote><pre id="result">calculating...</pre></blockquote>
</body>
</html>

(this example assumes you have a test.osu beatmap in the same directory)

performance

this is around 50-60% slower than the C implementation and uses ~10 times more memory.

$ busybox time -v node --use_strict test.js
...
User time (seconds): 16.58
System time (seconds): 0.43
Percent of CPU this job got: 101%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0m 16.70s
...
Maximum resident set size (kbytes): 314080
Minor (reclaiming a frame) page faults: 20928
Voluntary context switches: 72138
Involuntary context switches: 16689