ddnet
TypeScript icon, indicating that this package has built-in type declarations

0.7.4 • Public • Published

DDNet Readme

NPM Version NPM Downloads GitHub Issues or Pull Requests

A typescript npm package for interacting with data from ddnet.org

Why?

I was bored, and also I needed a decent package to use for interacting with ddnet.org programatically. It is also my first package and I tried my best to make it as good as possible.

Installation

Using your node package manager of choice, for example pnpm:

$ pnpm add ddnet

Example Usage

Player class examples

Import the Player class from the package and create a new instance of it.

import { Player } from 'ddnet';

const me = await Player.new('Sans3108');

console.log(me);

/*
Player {
  name: 'Sans3108',
  url: 'https://ddnet.org/players/Sans3108',
  globalLeaderboard: GlobalLeaderboard {
    completionist: { placement: 8462, points: 2740 },
    team: null,
    rank: null,
    completionistLastMonth: { placement: 3003, points: 224 },
    completionistLastWeek: { placement: 1570, points: 89 }
  },
  totalCompletionistPoints: 32136,
  favoriteServer: 'GER',
  finishes: Finishes {
    first: Finish {
      timestamp: 1628418102000,
      mapName: 'Multeasymap',
      timeSeconds: 2484.68,
      timeString: '41:24',
      rank: [Object],
      region: 'UNK',
      players: [Array]
    },
    recent: [
      [RecentFinish],
      ...
    ]
  },
  ... (lots of other data)
}
*/

You probably wouldn't want to log everything, just what you need. Examples below show how to log out the points of the player, the first finished map's name and the map with fastest finish time out of all the novice maps:

// Points
import { Player } from 'ddnet';
const player = await Player.new('Sans3108');

console.log(player.globalLeaderboard.completionist?.points); // 2740
// First finish
import { Player } from 'ddnet';
const player = await Player.new('Sans3108');

console.log(player.finishes.first.mapName); // "Multeasymap"
// Fastest finish time of all novice maps
import { Player, CompletedMapStats } from 'ddnet';
const player = await Player.new('Sans3108');

const completed = player.serverTypes.novice.maps.filter(map => map.finishCount > 0) as CompletedMapStats[];
const fastestTime = completed.sort((a, b) => a.bestTimeSeconds - b.bestTimeSeconds)[0];

console.log(`${fastestTime.mapName} ${fastestTime.bestTimeString}`); // "Tangerine 00:42"

Map class examples

Let's say you're not interested in player data that much, and you want to check out on some maps, to do that:

import { Map } from 'ddnet';
const map = await Map.new('Kobra 4');

console.log(map);
/*
Map {
  name: 'Kobra 4',
  url: 'https://ddnet.org/maps/Kobra-32-4',
  thumbnailUrl: 'https://ddnet.org/ranks/maps/Kobra_4.png',
  webPreviewUrl: 'https://ddnet.org/mappreview/?map=Kobra+4',
  type: 'Novice',
  points: 4,
  difficulty: 4,
  mappers: [
    Author {
      name: 'Zerodin',
      mapShowcaseUrl: 'https://ddnet.org/mappers/Zerodin',
      playerUrl: 'https://ddnet.org/players/Zerodin'
    }
  ],
  releasedTimestamp: 1438538340000,
  ... (lots of other data)
}
*/

Like before, this may be a bit much, so let's see some examples. Here are some showcasing how to get a map's author, the number of points it awards upon completion, and the current time record:

// Author

import { Map } from 'ddnet';

const map = await Map.new('Grandma');

console.log(map.mappers[0].name); // "Fňokurka oo7"

// If the map has multiple authors, then:
const map = await Map.new('Nagi');

console.log(map.mappers.map(a => a.name).join(' & ')); // "Cøke & Arrow"
// Points
import { Map } from 'ddnet';

const map = await Map.new('EasyRight');

console.log(map.points); // 4
// Current record
import { Map } from 'ddnet';

const map = await Map.new('Baby Aim 1.0');

console.log(`${map.finishes[0].players[0].name} ${map.finishes[0].timeString}`); // "Cireme 06:25"

TW 0.6 Skin rendering

I tried my hand at making a renderer based on TeeAssembler-2.0 by AlexIsTheGuy, and I think I nailed it.

Example usage:

import { TeeSkin6 } from 'ddnet';

const mySkin = new TeeSkin6({ skinResource: 'CrystalCat' });
const rendered = await mySkin.render({ eyeVariant: 'eye-happy' });
// Do something with the rendered skin buffer, like sending it in a message on discord as an attachment

// Or optionally, save it to a file by providing a file path, like this:
await mySkin.render({ eyeVariant: 'eye-happy', saveFilePath: 'my-skin.png' }); // Still returns a buffer

my-skin.png

Skin Render Output

TW 0.7 Skin rendering

I also tried creating a TW 0.7 skin renderer, which is similar to the 0.6 renderer but with a few changes.

Example usage:

import { TeeSkin7 } from 'ddnet';

const skin = new TeeSkin7({
  body: 'fox',
  marking: 'fox',
  eyes: 'colorable'
});

const rendered = await skin.render({
  customColors: {
    bodyTWcode: 1102443,
    markingTWcode: -485425166,
    feetTWcode: 1102450,
    eyesTWcode: 1441632
  },
  eyeVariant: 'eye-evil'
});
// Do something with the rendered skin buffer, like sending it in a message on discord as an attachment

// Or optionally, save it to a file by providing a file path, like this:
await skin.render({
  customColors: {
    bodyTWcode: 1102443,
    markingTWcode: -485425166,
    feetTWcode: 1102450,
    eyesTWcode: 1441632
  },
  saveFilePath: 'fox.png',
  eyeVariant: 'eye-evil'
}); // Still returns a buffer

fox.png

Skin Render Output

Cache

Most classes cache results, this is done in using the keyv and @keyv/sqlite packages. Cache data is stored in the ddnet_cache.sqlite file.

It's not really wise to mess with the cache, but there's a couple things you can do, mainly changing the TTL (Time-To-Live) of the cached items* the class is responsible for, and clearing different cache pools.

* Applies to newly added items

Building

For building this package yourself you will need at least Node v18.20.2, and some knowledge of typescript. Package manager choice should not matter, but for smooth operations I recommend pnpm.

First, clone the repository and navigate to it:

$ git clone https://github.com/Sans3108/DDNet.git
$ cd DDNet

Install the dependencies with:

$ pnpm install

After that, you're ready to open up the project in your code editor of choice, or, if you don't wish to change anything, simply run the following command to build the project:

$ pnpm build

Additionaly you may re-build the typedoc documentation website with:

$ pnpm typedoc

And after that everything in the /docs directory should be up to date with your changes.

Contributions & Notes

Help is always appreciated, if you are able to contribute and have the know-how, please do! I will look over every PR and potentially we can integrate your changes!

This readme may not showcase everything, but that's why the documentation website exists! Please check it out, explore and find what you need there if it wasn't shown here.

If something is missing or you would like to suggest something please submit an issue about it!

If you've made it this far and you consider this package useful, please consider starring this repository so more people can see it!

GitHub Repo stars

Any bugs can be reported here or on discord by adding me: Sans#0001.

Package Sidebar

Install

npm i ddnet

Homepage

ddnet.js.org

Weekly Downloads

84

Version

0.7.4

License

ISC

Unpacked Size

345 kB

Total Files

111

Last publish

Collaborators

  • thatboisans