Giter Club home page Giter Club logo

py-starbound's Introduction

Starbound utilities for Python

This is a library to parse Starbound's file formats which are used to store worlds, player characters, assets, etc.

Feel free to contribute either via submitting pull requests or writing up issues with suggestions and/or bugs.

File & data formats

Check out FORMATS.md for technical information on Starbound's file and data formats.

Installation

py-starbound can be installed (either to your system, user account, or virtualenv) using the usual setup.py script:

$ python setup.py install

After installation, the commandline utilities (described below) should be available in your $PATH can can be run like any other app:

$ pystarbound-export [args]
$ pystarbound-region [args]

If you wish to run these utilities from the git checkout itself (without installing first), the syntax is slightly more verbose:

$ python -m starbound.cliexport [args]
$ python -m starbound.cliregion [args]

Command line utilities

Extracting .pak files

You can use the pystarbound-export script to extract all the files in a .pak (or .modpak) file.

Example:

$ pystarbound-export -d assets /Starbound/assets/packed.pak

Or from the git checkout directly:

$ python -m starbound.cliexport -d assets /Starbound/assets/packed.pak

Getting world info

If you want information about a region in a world (planet or ship), you can use the region.py script. For example, here's how to pretty print the tiles in a region:

$ pystarbound-region /Starbound/storage/universe/-382912739_-582615456_-73870035_3.world
World size:        3000×2000
Spawn point:       (1224.0, 676.0)
Outputting region: (37, 21)
Outputting value:  foreground_material

Or from the git checkout directly:

$ python -m starbound.cliregion /Starbound/storage/universe/-382912739_-582615456_-73870035_3.world

Outputs something like this:

If you don't provide X and Y coordinates after the path, it will default to the region that the spawn point is in.

You can also output specific tile values (instead of the foreground) using --value-index (or -v):

$ pystarbound-region --value-index=12 /Starbound/storage/universe/-382912739_-582615456_-73870035_3.world 69 27
World size:        3000×2000
Spawn point:       (1224.0, 676.0)
Outputting region: (69, 27)
Outputting value:  liquid_pressure

Outputs something like this:

And here's how to print the entities in a region:

$ pystarbound-region --entities /Starbound/storage/universe/-382912739_-582615456_-73870035_3.world 69 27
World size:        3000×2000
Spawn point:       (1224.0, 676.0)
Outputting region: (69, 27)

[
  [
    "ObjectEntity",
    8,
    {
      "direction": "left",
      "inputWireNodes": [],
      "interactive": true,
      "name": "wiringstation",
      "orientationIndex": 0,
      "outputWireNodes": [],
      "parameters": {
        "owner": "916d5878483e3a40d10467dc419982c2"
      },
      "scriptStorage": {},
...

Using the Python package

The Python package lets you read data from Starbound's various file formats. The classes and functions expect file objects to read from.

You can use the mmap package to improve performance for large files, such as packed.pak and world files.

Example: Reading a player file

Here's how to print the name of a player:

import starbound

with open('player/11475cedd80ead373c19a91de2e2c4d3.player', 'rb') as fh:
  player = starbound.read_sbvj01(fh)
  print('Hello, {}!'.format(player.data['identity']['name']))

Example: World files

In the following example the mmap package is used for faster access:

import mmap, starbound

with open('universe/43619853_198908799_-9440367_6_3.world', 'rb') as fh:
  mm = mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ)

  world = starbound.World(mm)
  world.read_metadata()

  print('World size: {}×{}'.format(world.width, world.height))
  x, y = world.metadata['playerStart']
  print('Player spawns at ({}, {})'.format(x, y))

  # Regions consist of 32×32 tiles.
  rx, ry = x // 32, y // 32
  print('An entity: {}'.format(world.get_entities(rx, ry)[0]))

Example: Easy access to various world attributes

A vast amount of information about loaded Worlds is available via the metadata attribute (as seen in the above section), but some information is also abstracted out into an info attribute. For instance:

world = starbound.World(fh)
print('World Name: {}'.format(world.info.name))
print('World Description: {}'.format(world.info.description))
print('World Coordinates: ({}, {})'.format(world.info.coords[0], world.info.coords[1]))

The full list of attributes currently available are:

Attribute Description
biomes The full set of biomes found on the world. This should be a complete list, regardless of how much of the world has been explored.
coords World coordinates, as a tuple. The first two elements are the in-map coordinates of the system, the third is effectively random but describes the world itself.
description The internal description of the world. Will often include text describing the tier of the world.
dungeons The full set of dungeons found on the world. This should be a complete list, regardless of how much of the world has been explored.
name The name of the world. Will often include Starbound coloration markup.
size A tuple describing the width and height of the world.
world_biomes A set of the main biome IDs of the world, of the sort reported in the ingame navigation screen.

Example: Finding an entity by UUID/ID

Many entities in Starbound, such as bookmarked flags, mech beacons, quest markers, etc, have UUIDs or IDs which the game can use to find where they are in the map without having to have all regions loaded. Player bookmark UUIDs can be found in the player.data['universeMap'] dict, underneath teleportBookmarks. One object type which does not use UUIDs is a level's mech beacon, which instead uses the magic string mechbeacon. To find the ingame coordinates for a level's beacon (if one is present), this can be used:

mechbeacon_coords = world.get_entity_uuid_coords('mechbeacon')
if mechbeacon_coords:
  print('Mech beacon found at ({}, {})'.format(*mechbeacon_coords))
else:
  print('No mech beacon in level!')

Example: Getting assets from packed.pak

Starbound keeps most of the assets (images, configuration files, dungeons, etc.) in a file called packed.pak. This file uses a special format which can be read by py-starbound, as you can see below.

import starbound

with open('assets/packed.pak', 'rb') as fh:
  package = starbound.SBAsset6(fh)

  # Print the contents of a file in the asset package.
  print(package.get('/lighting.config'))

Example: Modifying Starbound files

Currently, only the SBVJ01 file format can be written by py-starbound. This means player files, client context files, and the statistics file.

Here's an example that renames a player (WARNING: Always back up files before writing to them!):

import starbound

with open('player/420ed511f83b3760dead42a173339b3e.player', 'r+b') as fh:
  player = starbound.read_sbvj01(fh)

  old_name = player.data['identity']['name']
  new_name = old_name.encode('rot13')
  player.data['identity']['name'] = new_name
  print('Updating name: {} -> {}'.format(old_name, new_name))

  # Go back to the beginning of the file and write the updated data.
  fh.seek(0)
  starbound.write_sbvj01(fh, player)
  # If the file got shorter, truncate away the remaining content.
  fh.truncate()

License

MIT License

py-starbound's People

Contributors

6-lasers avatar apocalyptech avatar blixt avatar chrmoritz avatar damianb avatar mk-pmb avatar scott-lewis-nsidc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

py-starbound's Issues

No API for saving changes made

I went through all the files and could not find a way to dump changes to either a new file or the same one (changes as in changing contents of dictionary)

Reading from btree during BTreeDB5.get_all_keys and World.get_all_regions_with_tiles

So I was writing some stuff to figure out where exactly the bookmark locations are kept (seems to be a combination of some json'd data in the player file, plus the use of layers 3 and 4 in the map itself, btw) and came across an issue where I was attempting to read data from the btree while in the middle of the get_all_keys loop. Since I converted that to being a generator, it was yielding results as it went, but then my subsequent reads from the table screwed up the loop processing (since it does some relative seeks inside the loop).

So the way I see it there's three options:

  1. Write the loop to only do absolute seeks, so any data read by the user mid-loop won't screw up the processing
  2. Revert the switch to yield so that it's no longer a generator, so it never actually returns until it's through processing. (When I'd been doing that originally, fwiw, the processing time to loop over entire map files was basically negligible)
  3. Just document that the user Shouldn't Do That while it's looping.

Without actually looking at the code, I suspect number 1 would be the best bet, though IMO 2 isn't awful either, even though we'd be using more memory than we'd need to be doing. Mostly I just wanted to get this in here so I didn't lose track of it myself; I'll whip up a PR within a few days.

SBBF header version updated in nightly

Hey @blixt

The latest nightlies have changed the version string in asset files from SBBF02 to SBBF03. I've got it working fine again by just updating the assert on line 89 in sbbf02.py and I have seen no issues in starcheat which should be working it pretty extensively.

I'd submit a patch but you're referencing this specific version quite a bit, not sure how you want to handle it but it doesn't feel right just updating that assert.

Newbie Issue

I have downloaded Python and your own files to repair .fail files. However, I am not quite sure how to run repair.py under the python interface.
I have tried cmd with python interactive running and I've also tried though python shell.

How is the following command supposed to be used?
./repair.py --output=repaired.world /path/to/world.fail

For example, I use this command when typing in python shell:
repair.py --output=repaired.world \games\Starbound\universe\alpha_62296467_18614490_8504722_3_1.world.1401379889199.fail

and I get this error: SyntaxError: unexpected character after line continuation character
while python highlights a blank space after the end of the command as invalid.

I am obviously doing something very wrong here. Can you point me to the right direction?

Add packets

This library supports parsing everything but packets right now, so we might as well add support for that as well. That way this library can be used for prototyping anything Starbound related.

Problem

i always get

init.py", line 105
if stream.read(1) == b'\x00'
^
SyntaxError: invalid syntax

Figure out final fields in SBBF02 format

There are a few fields in SBBF02 that are unknown still.

  • Header byte 15 (boolean: "is free block dirty")
  • Header byte 61 (boolean for alternating between root node references – toggled when committing)
  • Header byte 62 (unused byte)
  • Header bytes 63–66 (root node)
  • Header byte 67 (boolean: true = root node is leaf, false = root node is index)
  • Header bytes 68–70 (unused bytes)
  • Header bytes 71–74 (root node)
  • Header byte 75 (boolean: true = root node is leaf, false = root node is index)

Most of the above bytes seem insignificant to reading the format (besides the index pointers and the boolean that chooses which index pointer to use). But we should still try to figure them out.

StarNub Plugin

Would you be interested in porting this into a Java plugin to work in part with StarNub server tools.

StarNub integration would allow servers to use a plugin to repair worlds, add and remove objects and even render into a website or image the worlds ect.

ts3.free-universe.com and www.starnub.org

Is this still alive

because my world was reset and i was wondering what to do because i dont understand anything here

Support for reading bookmarks, etc, from world data (types/layers 3 and 4)

I'm putting this one in here more for documentation and brainstorming than anything else, in case someone else feels like puzzling this out. I've got it Good Enough for my own purposes, and was planning on just implementing what I've got in my own app. It'd be nice to have a "real" API for this, though whether it's worth the work is another question.

Anyway, I wanted to find out how to find user bookmarks, though this seems to also be a method to (probably) find where any Entity is, based on UUID (though I haven't really looked into that too far). The starting point for bookmarks, at least, is in the player JSON data, underneath universeMap.(uuid).teleportBookmarks, and includes the following data:

  1. The user-defined name for the bookmark
  2. The UUID of the object it links to
  3. A colon-separated string describing the world it links to

The colon-separated string contains one of:

CelestialWorld:<coord[0]>:<coord[1]>:<coord[2]>:<planet_num>[:<moon_num]
InstanceWorld:<filename_part>:<instance_uuid>:<suffix_num>
ClientShipWorld:<player_uuid>

CelestialWorld entries point to the universe/*.world files, as you'd expect, InstanceWorld entries point to universe/unique-<filename_part>-<instance_uuid>-<suffix_num>.world files, and ClientShipWorld points to player/<player_uuid>.shipworld.

Notably absent from that info is the actual world coordinates for the bookmark - for that we need to look at the map data itself. One brute-force way would be to loop through all regions, get_entities on them, and look for entities whose uniqueId attribute matches the bookmark UUID (and then use that entity's tilePosition).

To avoid having to loop through all entities in the map, it looks like the map data types/layers 3 and 4 could be used. Layer 4 is the simplest, and seems to be a simple map of all entity UUIDs in a region, so you can get those with just world.get(4, rx, ry), though of course finding that would require looping through all keys, since you don't know the rx, ry yet. The data itself in there starts out with a single unsigned byte which is the number of IDs included in the data, and then a length-prefixed string for each ID (the length is another single unsigned byte - ordinarily 0x20/32 for UUIDs).

Layer 3 is a bit more complex, and I suspect that this is the one which is intended to be used for this sort of lookup. My first big unknown is that I have no idea what the key actually represents. The layer/type is 4, of course, but I've yet to figure out what in the world the other eight bytes are meant to describe. It's definitely not just rx, ry, and may not even be considered two shorts.

As for the data inside layer 3 nodes, it starts off with an unsigned short specifying the number of entries. Each entry starts out with a length-prefixed string, as with the layer 4 entry. Then there's two shorts which give you the region X, Y, and a further eight bytes I don't have a clue how to interpret.

So right now, for both of those layers, the best I can manage is looping through all the keys in the DB, matching on the UUID, and then using the region X,Y to grab entities in that region, and match on the UUID in there as well. I assume there's got to be a way to generate the keys for region 3 data using what exists in the Player bookmark data, but I've not figured that out yet. Among the things I tried was checking to see if the key happened to be a crc32 of the UUID, but no such luck there.

I know this is already super long-winded, but for thoroughness's sake, here's various bookmarks and their layer 3 keys/unknown data:

Processing Diana
 * Found bookmark "Space Flag" in unique-playerstation-ada79096eb153ccfd6768413dd6edeb8-1.world, at region (2, 8), coords (91, 279)
  - Node Key: 03 1C FD B1 24
    * As bytes: 28 253 177 36
    * As shorts: 7421 45348
    * As int: 486388004
  - Unknown Data: 42 B6 00 00 43 8B 80 00
    * As bytes: 66 182 0 0 | 67 139 128 0
    * As shorts: 17078 0 | 17291 32768
    * As ints: 1119223808 | 1133215744

 * Found bookmark "Station Porter" in unique-playerstation-ada79096eb153ccfd6768413dd6edeb8-1.world, at region (2, 8), coords (73, 267)
  - Node Key: 03 ED 42 3F 40
    * As bytes: 237 66 63 64
    * As shorts: 60738 16192
    * As int: 3980541760
  - Unknown Data: 42 92 00 00 43 85 80 00
    * As bytes: 66 146 0 0 | 67 133 128 0
    * As shorts: 17042 0 | 17285 32768
    * As ints: 1116864512 | 1132822528

Processing Destructicus
 * Found bookmark "Human Campground" in -452761967_-908966405_-81331760_3.world, at region (146, 31), coords (4679, 1000)
  - Node Key: 03 60 52 71 34
    * As bytes: 96 82 113 52
    * As shorts: 24658 28980
    * As int: 1616015668
  - Unknown Data: 45 92 38 00 44 7A 00 00
    * As bytes: 69 146 56 0 | 68 122 0 0
    * As shorts: 17810 14336 | 17530 0
    * As ints: 1167210496 | 1148846080

 * Found bookmark "Apex Camp" in -452761929_-908966412_-25356524_11.world, at region (121, 32), coords (3900, 1026)
  - Node Key: 03 01 1D 69 97
    * As bytes: 1 29 105 151
    * As shorts: 285 27031
    * As int: 18704791
  - Unknown Data: 45 73 C0 00 44 80 40 00
    * As bytes: 69 115 192 0 | 68 128 64 0
    * As shorts: 17779 49152 | 17536 16384
    * As ints: 1165213696 | 1149255680

 * Found bookmark "Home Base" in -452761926_-908966428_-252630074_5.world, at region (95, 31), coords (3055, 1022)
  - Node Key: 03 43 AE F5 FF
    * As bytes: 67 174 245 255
    * As shorts: 17326 62975
    * As int: 1135539711
  - Unknown Data: 45 3E F0 00 44 7F 80 00
    * As bytes: 69 62 240 0 | 68 127 128 0
    * As shorts: 17726 61440 | 17535 32768
    * As ints: 1161752576 | 1149206528

 * Found bookmark "Home Base Backup" in -452761926_-908966428_-252630074_5.world, at region (95, 31), coords (3070, 1022)
  - Node Key: 03 7E E0 50 73
    * As bytes: 126 224 80 115
    * As shorts: 32480 20595
    * As int: 2128629875
  - Unknown Data: 45 3F E0 00 44 7F 80 00
    * As bytes: 69 63 224 0 | 68 127 128 0
    * As shorts: 17727 57344 | 17535 32768
    * As ints: 1161814016 | 1149206528

 * Found bookmark "On A Moon" in -452761882_-908966422_-48613115_10_2.world, at region (47, 21), coords (1504, 679)
  - Node Key: 03 69 FD 4C 2B
    * As bytes: 105 253 76 43
    * As shorts: 27133 19499
    * As int: 1778207787
  - Unknown Data: 44 BC 00 00 44 29 C0 00
    * As bytes: 68 188 0 0 | 68 41 192 0
    * As shorts: 17596 0 | 17449 49152
    * As ints: 1153171456 | 1143586816

 * Found bookmark "Exploring" in -452761882_-908966422_-48613115_11.world, at region (25, 16), coords (802, 529)
  - Node Key: 03 0C 1F 56 16
    * As bytes: 12 31 86 22
    * As shorts: 3103 22038
    * As int: 203380246
  - Unknown Data: 44 48 80 00 44 04 40 00
    * As bytes: 68 72 128 0 | 68 4 64 0
    * As shorts: 17480 32768 | 17412 16384
    * As ints: 1145602048 | 1141129216

 * Found bookmark "On the ship!" in 1d6a362efdf17303b77e33c75f73114f.shipworld, at region (31, 32), coords (1003, 1024)
  - Node Key: 03 B1 23 15 04
    * As bytes: 177 35 21 4
    * As shorts: 45347 5380
    * As int: 2971866372
  - Unknown Data: 44 7A C0 00 44 80 00 00
    * As bytes: 68 122 192 0 | 68 128 0 0
    * As shorts: 17530 49152 | 17536 0
    * As ints: 1148895232 | 1149239296

Support directory-like "browsing" of SBAsset6 objects?

(Very excellent library you've created, btw - thanks a bunch!)

While writing a little utility with this library, I came across the need to get "directory listings" of paths within the pakfile. Something for a possible enhancement to the SBAsset6 class, perhaps?

In my own app I added it with an ugly little class like so:

class PakTree(object):
    """
    Tree-based dict so we can "browse" pak contents by directory.
    Makes no distinction between directories and files.
    """

    def __init__(self):
        self.top = {}

    def add_path(self, pathname):
        """
        Adds a path to our tree
        """
        parts = pathname.split('/')[1:]
        cur = self.top
        for part in parts:
            if part not in cur:
                cur[part] = {}
            cur = cur[part]

    def get_all_in_path(self, path):
        """
        Gets all "files" within the given path.
        """
        parts = path.split('/')[1:]
        cur = self.top
        for part in parts:
            if part not in cur:
                return []
            cur = cur[part]
        return sorted(cur.keys())

    def get_all_matching_ext(self, path, ext):
        """
        Gets all "files" within the given path which match the given
        extension.  Note that "extension" is being used here a bit
        generously - be sure to pass in a leading dot if you want it
        to *actually* be an extension.
        """
        to_ret = []
        for item in self.get_all_in_path(path):
            if item.endswith(ext):
                to_ret.append(item)
        return to_ret

Which gets populated via:

            paktree = PakTree()
            pakdata = starbound.SBAsset6(pakdf)
            pakdata.read_index()
            for path in pakdata.index.keys():
                paktree.add_path(path)

... but that's pretty ugly, and the get_all_matching_ext method may not be something you'd want in the base package anyway. If this is something you'd be interested in having, I could try my hand at a more-suitable-for-public-consumption version.

SBON to YAML conversion?

Hi, very nice work!
Would it seem easy (or maybe interesting?) to you to add conversion from SBON to YAML and back? If I tried myself, would you see some caveats that I should avoid?

Figure out the tile format

Findings so far:

  • Tiles are stored as regions consisting of 32x32 (= 1,024) tiles
  • To get a region, load B-tree key 0x01XXXXYYYY where XXXX corresponds to horizontal region number (X coordinate of left-most tile divided by 32) and YYYY corresponds to vertical region number (Y coordinate of bottom-most tile divided by 32)
  • If you change the first byte in the B-tree key to 0x02, you will get a data structure containing all the entities in the region
  • Note that the coordinate system used in Starbound counts (0, 0) as the bottom left corner and increments to the right and up
  • The region data is zlib compressed
  • The region data uncompressed is 23,555 bytes (3 byte header + 23 bytes * number of tiles)
  • The data for each tile is a fixed structure consisting of 23 bytes, some stored as signed shorts and some stored as unsigned bytes
  • The variant number for each tile is calculated using a Fowler/Noll/Vo hash function, using world seed, X, Y, and a constant (based on whether tile is foreground or background) as variables

Add saving

This library only reads right now. Being able to save would be a great boon. The biggest hurdle to completing this task is to be able to expand the B-tree database.

SBAsset6 files intended to be case-insensitive?

I suspect that the internal path structure in the SBAsset6 is intended to be case-insensitive. For instance, /tiles/materials/ironblock.material references ironblock.png, but the path name actually specified by the Pak index is ironBlock.png (with a capital B). An app programmatically following that chain will currently get an exception while trying to .get() the all-lowercase version.

I applied this on my local checkout, which seems to do fine, though I haven't actually done much testing. I'll whip it up into a proper PR in a bit; just wanted to get this in here before I forgot:

diff --git a/starbound/sbasset6.py b/starbound/sbasset6.py
index dd3dae7..0aecf87 100644
--- a/starbound/sbasset6.py
+++ b/starbound/sbasset6.py
@@ -27,7 +27,7 @@ class SBAsset6(object):
     def get(self, path):
         if not hasattr(self, 'index'):
             self.read_index()
-        offset, length = self.index[path]
+        offset, length = self.index[path.lower()]
         self.stream.seek(offset)
         return self.stream.read(length)
 
@@ -50,6 +50,6 @@ class SBAsset6(object):
         self.stream.seek(self.index_offset)
         self.index = {}
         for i in range(self.file_count):
-            path = sbon.read_string(self.stream)
+            path = sbon.read_string(self.stream).lower()
             offset, length = struct.unpack('>QQ', self.stream.read(16))
             self.index[path] = IndexEntry(offset, length)

setup.py packaging / PyPI?

So how would you feel about wrapping up py-starbound in a "proper" setup.py, which would also include entry points for the CLI utilities. Assuming that you're okay with that, how would you feel about having this available in PyPI? I'd certainly be happy to whip up the setup.py and rearrange things a bit, in a PR.

One of the downsides of doing that, of course, is that running the CLI utils directly from a git checkout wouldn't be as simple as just executing a script directly. Instead, if you did something like git mv region.py starbound/cliregion.py, to run from a git checkout you'd have to do: python -m starbound.cliregion [filename]. Less convenient, but it'd allow for proper installation into a venv or system.

Anyway, let me know if that's something you'd be interested in! (Mostly just asking because I'd like to get my own map viewer properly-setup.py'd as well.)

Extract a or all files

Currently you can only print a single file contents or list the names/directories of all files.

I would like to see and extract function that saves the data out to the correct file structure (like it use to be)

An easy way to extract all files would be great

Possible explanation of "magic byte" in SBVJ01 files

Per Kawa in the Starbound Discord, 0x01 is used if the SBVJ01 file is versioned. If it isn't versioned, it'll be 0x00 and will skip the Int32 field for the entity version, immediately going straight to the entity data itself (the SBON dynamic type structure).

Needs to be verified, but if this is the case, that's more for the formats file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.