npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ddnet

v0.7.2

Published

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

Downloads

162

Readme

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, by default this is done in memory but there is an option to use sqlite to store things, for that you will need the @keyv/sqlite package, once installed cache will be stored on disk in the ddnet_cache.sqlite file.

$ pnpm install @keyv/sqlite

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.