Giter VIP home page Giter VIP logo

jotunn's Introduction

Jötunn, the Valheim Library

Banner

Jötunn (/ˈjɔːtʊn/, "giant"), the Valheim Library was created with the intent to facilitate developer creativity, unify the communities problem solving efforts, and enhance developer productivity by curating a library of common helper utilities. Additionally, it supplies specific interfaces and abstractions which aid with mod interoperability, networked gameplay consistency, and remove the need to maintain valheim version specific code by acting as an interface between the developer and the games changing internals.

This project was originally derived from the base structure of JötunnLib, and had many entity abstractions and features from ValheimLib merged into it before we proceeded with further implementations. We have lots of features planned for the future, and we hope the community has many feature requests to suggest. I hope the features we have implemented thus far prove to be a useful base and provide an idea of the consistency we aim to deliver moving forwards.

Usage

Please refer to our documentation. We have gone to great lengths to ensure there is ample documentation to facilitate the developer's learning experience.

Installation

If you're using a mod installer, you can likely ignore this section.
For a more in-depth installation guide, please check out the manual installation guide in our documentation.
However, here is a quick run-down:

  1. Install BepInEx:
    Download BepInEx, extract everything inside BepInEx_Valheim into your Valheim folder (typically C:\<PathToYourSteamLibary>\steamapps\common\Valheim).

  2. Install Jötunn:
    Download from either Nexus / Thunderstore, extract the ZIP and place all content into BepInEx/plugins/Jotunn of your Valheim install.

That's it, launch the game and mod away!

Features

JVL provides three distinct groups of features. Entities, which abstract the game's own entities into easy-to-use interfaces. Managers, which act as interfaces between the entities and native collections or subsystems. Utilities, which are there to aid in generic/common functions that can span many different areas.

Entities

  • CustomCreature - Represents custom animals, enemies and NPCs.
  • CustomItem - Represents ingame items such as weapons, tools and consumables.
  • CustomItemConversion - Represents ingame item conversions for the CookingStation, Fermenter, Smelter and Incinerator in one abstraction.
  • CustomLocalization - Represents custom localizations for your mod.
  • CustomLocation - Represents custom locations from simple stone circles to complete villages.
  • CustomPiece - Represent ingame building pieces.
  • CustomPieceTable - Represent ingame building tables. Support for custom categories included.
  • CustomRecipe - Represents ingame recipes for managing crafting and upgrading of items.
  • CustomStatusEffect - Represents ingame status effects from weapon hit effects to guardian powers.
  • CustomVegetation - Represents vegetation spread throughout biomes from pickables to cosmetics.
  • KitbashObject - Represents a custom object assembled from various other prefabs' components.
  • Mocks - Fake any vanilla prefab and use it in your custom assets - Jötunn resolves the references to the vanilla objects at runtime.
  • Config classes - There are many more abstractions beside the main entities which allow for easy creation of things like key bindings, custom commands, skills and more.

Managers

  • Command Manager - Facilitates implementation of methods which can be registered as executable console commands.
  • CreatureManager - Add new creatures or copy and modify vanilla ones.
  • GUI Manager - Allows invocation of UI prefabs on the fly via code.
  • Input Manager - Provides an interface for binding keys via ZInput in a consistent manner, facilitating custom keybind hints.
  • Item Manager - Abstracts away implementation details of configurations applied to items/recipes to provide a consistent developer experience in adding new items. tl;dr items are easy!
  • Kitbash Manager - Create custom assets with individual pieces from vanilla prefabs.
  • KeyHint Manager - Create custom key hints for your weapons and tools, even down to the selected piece.
  • Localization Manager - Provides multiple methods of loading localization data into the game, as well as exposing an interface for adding additional languages to provide localizations to unsupported languages.
  • Minimap Manager - Alter map data or create overlays for the map.
  • Piece Manager - Very similar to the Item Manager, abstracting implementation details of configurations for pieces/recipe's.
  • Prefab Manager - Provides a cache of prefabs registered through other managers, mostly developers will only query the cache for prefabs added via other managers.
  • Render Manager - Provides a custom render queue to render visual GameObjects into a Sprite - Useful to generate icons for your custom items.
  • Skill Manager - Facilitates additional custom skills.
  • Undo Manager - Provides global undo/redo queues for mods to revert and replay any actions in the game.
  • Zone Manager - Create custom locations and vegetation to add in the world generation.

Utilities

  • Asset Helpers - Methods to facilitate referencing and loading of assets.
  • Bone Reorderer - Fixes bone ordering issues on SkinnedMeshRenderer's that have been ripped and imported into unity.
  • Network Compatibility - Allows plugins to define their own version requirements for clients connected to the server. Ensures a customisable level of interoperability with clients of differing mod configurations on a plugin-by-plugin basis.
  • Config Synchronisation - Allows administrators to adjust configuration values via an in game menu. Config setting is synced to connected clients.
  • Mod Registry - Query added content per Mod.
  • SimpleJSON - We have imported SimpleJSON into our library at the request of developers who would simply prefer to have this dependency taken care of already. We use the MIT Licensed SimpleJSON

Bugs, Support, Contributions

Please refer to our documentation before requesting support via discord. If there are any mod interoperability issues developers experience (not just exclusive JVL issues), we would like to hear from you! If we can facilitate better mod interoperability by providing a common interface, or exposing native valheim objects, including a utility which you have created, then please feel free to create a new feature request or pull request.

Roadmap

Check our projects for a more up to date list of features currently in development, or suggest your own features for inclusion by creating a new feature request

Changelog

See the full Changelog.

Contributors to Jötunn, the Valheim Library

These people have been integral to pushing JVL out of the door, and without them we could not have achieved nearly as much. Please give them some love on github, thunderstore, and nexus.

Core:

Jules#7950: github

Margmas#9562: github, thunderstore, nexus

iDeathHD#7866: github, thunderstore

Algorithman#6741: github

Quaesar#5604: github

radu#0571: github, thunderstore, nexus

paddy#1337: github, thunderstore

Contributors:

Cinnabun#0451: github

GoldenJude#8965: github, nexus

zarboz#7828: github, thunderstore, nexus

MarcoPogo#6095: github, nexus

blaxxun#9098: github

Tekla#1012: github

JoeyParrish#8644: github, thunderstore, nexus

Nosirrom#2626: github

Jere#0989: github, thunderstore, nexus

Searica: github, thunderstore

OrianaVenture: github, thunderstore

jotunn's People

Contributors

algorithman avatar bid-soft avatar defee avatar digitalroot avatar dominowood371 avatar donchad avatar github-actions[bot] avatar heinermann avatar igmat avatar joeyparrish avatar mathiasdecrock avatar mle-ii avatar mr-rageous avatar mschmoecker avatar orianaventure avatar paddywaan avatar probablykory avatar raduschirliu avatar ratikkapoor avatar redseiko avatar searica avatar sirskunkalot avatar spikehimself avatar t3kla avatar tiorthan avatar xiaoxiao921 avatar zarboz 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

jotunn's Issues

[FEATURE]Custome PieceTable Categories

User Story
As a plugin developer I want to specify piece categories, and add custom categories so that my piece additions are easy to find and use.

Envisioned Solution
When specifying a PieceConfig, I want to be able to set the category for a piece to a vanilla category, or a custom category.
The PieceManager should expose categories for adding additional custom tabs to specific piecetables.

Can ConsoleCommands be made public?

I'd like to check if a custom command has already been loaded, but the list of loaded console commands is marked as internal. Is this something that can/should be changed? I'm not too experienced with the library so I'm not sure if it's possible or if it even makes sense.

[FEATURE]public JSON library

User Story
As a lazy user I want to serialise objects and fields so that I don't have to acquire a json dependency for myself.

Envisioned Solution
Make simpleJSON public instead of internal

Additional context
requested by Juha686#9097

NetworkCompatibility display concise missing requirements.

Currently, the server's required mod list and the clients current mod list are presented to the user upon a failed connection.

We can make this more concise by simply listing the offending/required packages which are missing, or mis-versioned.

Nuget deployment

Things to do before making JotunnLib public on nuget.org:

  • check/fix nuget meta informations
  • nuget deploy (github action)
  • nuget auto versioning (github actions at main merge?)

For the meta informations: Core team please check info and report changes or missing entries.
Nuget deploy: Need to get API Key, add deploy to github workflow
Nuget auto versioning on production workflow: Implementation needed, GitVersionTask maybe would help here

NetworkCompatibility

A NetworkCompatibilityModule for plugin developers to "enforce" synchronisation between client's mods/versions, and potentially configs.

See: https://github.com/risk-of-thunder/R2API/blob/master/R2API/Utils/NetworkCompatibility.cs

  • Must ensure plugins can request clients to have a certain dependency
  • Must ensure plugins can request clients to have a certain version of that dependency
  • Must ensure plugins can be permissive with versioning
  • Must ensure synchronisation of specific configurations / bep whole configurations
  • Does NOT attempt to act as an anti-cheat.
  • Clients failing to meet conditions must be notified of the offending/lacking dependencies if/when connection is refused.

Potentially utilise/suggest https://github.com/BepInEx/BepInEx.ConfigurationManager for UI frontend for configs

Add transition documents for current JotunnLib/ValheimLib users

We should add a section to the documentation, with code examples, that explains how to transition existing mods from ValheimLib and JotunnLib to the new combined Jotunn lib.

Requirements:

  • Adding JVL to existing project
  • Transition guide for JotunnLib
  • Transition guide for ValheimLib

Singleton pattern implemented via MonoBehaviour's prevents unit testing abstractions

As mentioned: #32 (comment) we would need to remove this inheritance, and instead use interfaces in order to implement unit testing. Similarly if we really want to go down this route we should look into the Dependency Injection design pattern and integrate it into all managers so that we can pass our collections as dependencies via constructor, param, or method.

This would mean rewriting how we invoke our managers, however it should allow us to be more consistent in code quality.

Deconflict Custom Key Bindings

As more mods need custom key bindings it would be nice to have a common approach to coordinating / de-conflicting bindings. There are a few different ways I could see this working ranging in technical complexity from agreed upon standards although way to common code library for managing key bindings. While some of the less technical approaches may not need Jotunn library support for them it seems this wiki would still be a good place to document them.

I'm throwing some possible options below, likely some combination of them will end up making sense. I'm sure there may be other solutions that would work even better.

Option 1:
Mods standardize on using a ModKeyBinding section in BepinEx.Configuration. This would allow the user to easily see all of the custom key bindings they need to setup.

Option 2:
For common custom button bindings Jotunn sets up the key binding for them. An example of this would be several mods may want a Use2 binding. Jotunn's config could define the Use2 key binding so that mods wouldn't have to manage the configuration themselves. For something like Use2 we could also have an agreed upon interface where a method Interact2 gets called on the object being used.

Option 3:
Jotunn provides mechanism for mods to register key bindings needed and they get added to the actual Valheim configuration screen as opposed to just a BepinEx configuration file.

[BUG]Raising the skill of a custom skill throws NRE

RaiseSkill throws NRE when given a custom skill, as such:

        void addSkills()
        {
            // Test adding a skill with a texture
            Sprite testSkillSprite = Sprite.Create(testTex, new Rect(0f, 0f, testTex.width, testTex.height), Vector2.zero);
            //TestSkillType = SkillManager.Instance.AddSkill("com.jotunnlib.JotunnModExample.testskill", "TestingSkill", "A nice testing skill!", 1f, testSkillSprite);
            TestSkillType = SkillManager.Instance.AddSkill(new SkillConfig { Identifier = "com.jotunnlib.JotunnModExample.testskill",
                Name = "TestingSkill",
                Description = "A nice testing skill!",
                Icon = testSkillSprite,
                IncreaseStep = 1f
            }, true);
            Logger.LogDebug(TestSkillType);
            //if(!TestSkillType) Logger.
        }

private void Update()
        {
            if (Input.GetKeyDown(KeyCode.F8))
            { // Set a breakpoint here to break on F6 key press
                Player.m_localPlayer.RaiseSkill(TestSkillType, 1);
            }
        }

The first stack trace occurs on hitting F8. The second occurs upon opening the skills window.

[Error  : Unity Log] NullReferenceException: Object reference not set to an instance of an object
Stack trace:
Skills+Skill.Raise (System.Single factor) (at <6fd2223d69c147d18c77057ce0dedd7b>:0)
Skills.RaiseSkill (Skills+SkillType skillType, System.Single factor) (at <6fd2223d69c147d18c77057ce0dedd7b>:0)
Player.RaiseSkill (Skills+SkillType skill, System.Single value) (at <6fd2223d69c147d18c77057ce0dedd7b>:0)
JotunnModExample.JotunnModExample.Update ()

[Info   : Unity Log] 04/11/2021 23:21:19: Setting selected recipe 0

[Info   : Unity Log] 04/11/2021 23:21:19: UI Group status changed root = True

[Info   : Unity Log] 04/11/2021 23:21:19: UI Group status changed Player = True

[Error  : Unity Log] NullReferenceException: Object reference not set to an instance of an object
Stack trace:
SkillsDialog.Setup (Player player) (at <6fd2223d69c147d18c77057ce0dedd7b>:0)
InventoryGui.OnOpenSkills () (at <6fd2223d69c147d18c77057ce0dedd7b>:0)
UnityEngine.Events.InvokableCall.Invoke () (at <51725b129ecd4967a9a213c1ae65b295>:0)
UnityEngine.Events.UnityEvent.Invoke () (at <51725b129ecd4967a9a213c1ae65b295>:0)
UnityEngine.UI.Button.Press () (at <5abdc278a459476dabe36f8ad65ea091>:0)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at <5abdc278a459476dabe36f8ad65ea091>:0)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at <5abdc278a459476dabe36f8ad65ea091>:0)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at <5abdc278a459476dabe36f8ad65ea091>:0)
UnityEngine.EventSystems.EventSystem:Update()

When raise skill is performed, the sword skill is set to level 100:
image

[FEATURE] Prefab naming consistency and persistance

User Story

As a Mod developer I want to be able to refactor my code related to prefabs so that prefabs have unchanged identity unless I explicitly decided them to do so.

As a Player I want to have my custom items/pieces persist in world even after updating mod so that I'm able to take latest version of mods without losing progress in my saved world.

Envisioned Solution

Using InstantiateClone (or similar method) shouldn't alter name provided by developer in a way that prevents developer from any further changes around calling this method. Like it currently does in ValheimLib

Additional context

I've developed a mod that adds custom piece ‘SignedChest’. It is registered with following code:

GameObject signedLocker = assetBundle.LoadAsset<GameObject>("Assets/Pieces/SignedChest.prefab");
siqnedLocker.FixReferences();
GameObject cloned = signedLocker.InstantiateClone("SignedChest");

Which is called from namespace ChestReloaded.Pieces from class SignedLocker from method AddSignedChestPiece which ends up with the name of instance equal to SignedChest_ChestReloaded_SigqnedLocker_AddSignedChestPiece(Clone).
After small refactoring where this code was moved to method AddPrefab loading world with placed pieces from previous mod version causes them to be destroyed as invalid prefabs, because now mod registers prefab with name SignedChest_ChestReloaded_SigqnedLocker_AddPrefab(Clone) which differs from name of prefab in savegame.

It might be fixed with following line in mod code:

cloned.name = "SignedChest_ChestReloaded_SigqnedLocker_AddSignedChestPiece(Clone)";

But it seems like such problem shouldn't appear in first place.

Actions no longer work

Actions previously depended on env vars, which are no longer used and need to be updated.

Network compatibility more precise controls

Perhaps it would be worth it to add some more precise controls for the NetworkCompatibility.VersionStrictness enum. It may be useful to allow something along the lines of:

  • Versions do not matter
  • Everyone needs same major version, minor.patch does not matter
  • Everyone needs same major.minor version, patch does not matter
  • Everyone needs same mod version for major.minor.patch

Enhance interface for custom KeyHints

KeyHints are the "help" texts for the button config in the right corner. Basic implementation with ButtonConfigs for Keyboard is there, need to include Joystick hints as well.

[REFACTOR] The CustomRecipe dilemma

During documentation, there was an inconsistency which has jumped out at me, and we must resolve before we release to the public as this will be a "now or never" change which will affect all documentation, examples, and dependant plugins going forward.

I will preface this issue by detailing the vanilla game's implementation for two similar object interfaces.

First, we have two individual component systems within valheim, that live in the ObjectDB. We have the m_recipes collection, and the m_items collection. Recipe is comprised of two notable attributes: public ItemDrop m_item;, and public Piece.Requirement[] m_resources = new Piece.Requirement[0];. Meanwhile, the m_items collection is of generic GameObjects, which may have an ItemDrop component, the same ItemDrop which can be referenced from a recipe.

Secondly, we have Pieces, that short of their piecetables do not have a objectDB style collection. Pieces, have a subclass named Requirement, which is a basic structure that defines an public ItemDrop m_resItem; and a quantity as an ingredient.

At this point, its clear to see some similarities in these implementations. Now lets look forward at how we interface with these libraries within our JVL abstractions:

Item

private void CreateBlueprintRune()
{
    // Create and add a custom item
    // CustomItem can be instantiated with an AssetBundle and will load the prefab from there
    CustomItem rune = new CustomItem(BlueprintRuneBundle, "BlueprintRune", false);
    ItemManager.Instance.AddItem(rune);

    // Create and add a recipe for the custom item
    CustomRecipe runeRecipe = new CustomRecipe(new RecipeConfig()
    {
        Item = "BlueprintRune",
        Amount = 1,
        Requirements = new PieceRequirementConfig[]
        {
            new PieceRequirementConfig {Item = "Stone", Amount = 1}
        }
    });
    ItemManager.Instance.AddRecipe(runeRecipe);
}

In the above, we create a CustomItem, and a CustomRecipe which can take a RecipeConfig as a parameter, that also requires a PieceRequirementConfig property to define its ingredient list.

Piece

private void CreateRunePieces()
{
    // Create and add custom pieces
    GameObject makebp_prefab = BlueprintRuneBundle.LoadAsset<GameObject>("make_blueprint");
    CustomPiece makebp = new CustomPiece(makebp_prefab, new PieceConfig
    {
        PieceTable = "_BlueprintPieceTable",
        AllowedInDungeons = false
    });
    PieceManager.Instance.AddPiece(makebp);
    GameObject placebp_prefab = BlueprintRuneBundle.LoadAsset<GameObject>("piece_blueprint");
    CustomPiece placebp = new CustomPiece(placebp_prefab, new PieceConfig
    {
        PieceTable = "_BlueprintPieceTable",
        AllowedInDungeons = true,
        Requirements = new PieceRequirementConfig[]
        {
            new PieceRequirementConfig {Item = "Wood", Amount = 2}
        }
    });
    blueprintRuneLocalizations();
    PieceManager.Instance.AddPiece(placebp);
}

Inconsistency

In the above, we create a CustomPiece, which takes PieceConfig as a parameter, that also requires a PieceRequirementConfig property to define its ingredient list.

If we compare this to the item example:

In the above, we create a CustomItem, and a CustomRecipe which can take a RecipeConfig as a parameter, that also requires a PieceRequirementConfig property to define its ingredient list.

The problem:

The problem here is inconsistency. We are obfuscating native implementations via our abstractions using inconsistent naming schemas to accomplish the same operations, not to mention potentially having some redundant nesting of tightly coupled objects/types when we could just abstract them away with parameters, but that's a problem for another time.

Bellow, I will note our equivalent implementation names, and what i suggest they should become

ImplementationName Item Piece
CustomObject CustomItem CustomPiece
Recipe/Conf CustomRecipe PieceConfig
Ingredient PieceRequirementConfig PieceRequirementConfig

We can see that if we were to apply consistent naming schema, then the CustomItem should have an ItemConfig, not a CustomRecipe as a component parameter.

Furthermore, PieceRequirementConfig is a misnaming within the vanilla libraries, and just further adds to the confusing as now items have piece requirements, which makes 0 sense at all either.

We are better than this, there is no need to make this mistake and we can correct it before we release. I suggest that we do as such.

[Documentation]Wiki

All features and interfaces must be documented. A template project should also be provided to allow ease of use for new developers.

  • Refine documentation for deployment
  • example mod (In Progress, comprised of the following:)
  • getting started / ModStub & Config
  • importing assets
  • custom items
  • custom pieces & piece tables
  • custom status effects
  • custom skills
  • using mock references inside assets
  • VL Asset Creation Walkthrough
  • adding localisations
  • GUIManager

Utilities:

  • Configs
  • NetworkCompatibility
  • AssetUtils
  • BoneReorder
  • Input
  • Commands

[DOC]Remove unnecessary documentation from mod stub repo readme.md

Doc's such as debug information, unity setup etc are better directed toward the modding wiki than duplicated here. If we have any JVL specific implementations such as J/VLmock_ prefixing for asset creation then include these, but only link to generic information which is tangential to our project.

Changelog, features, roadmap

We're currently not keeping track of changes at all in this repo. We should create some form of changelog markdown file documenting what we've done up to the initial release, and what feature will be available upon release.

Additionally, we should clean up (or honestly, probably could even entirely remove) the "features & roadmap" section from the README.

  • Features
  • Roadmap
  • Thunderstore README.md
  • Nexus page ?????
  • Begin curated changelog via a featurelist

chore: Generate MMHOOK DLLs at runtime

Look into how we can generate the MMHOOK publicized assemblies at runtime, when Jotunn is loaded. This way, we won't need to ship the DLLs with the library, and they'll always be up-to-date for users. Additionally, it should be less cumbersome for users to deal with installing our library if there's less DLLs for them to fetch,

SkillConfig throws errors if UID is negative

Not sure if this is an issue or not yet, but wanted to repost an issue raised on one of the PRs on the JotunnLib repo:
jotunnlib/jotunnlib#21

It appears that some strings can have a negative hash code, which then results in a negative SkillConfg UID (used for Skill.SkillType).

EDIT: It appears it throws errors on negative UID. With the errors fixed, negative UIDs seem to work just fine :)

Cleanup LocalizationManager

  • Remove restrictions on requiring tokens to start with $ char
  • Disallow users from using any characters from Localization.m_endChars in their tokens, as they will not be usable

Redo TestMod using merged interfaces

#31 breaks the TestMod completely. We need to rebuild it with the new concepts and see along the way what should be next / changed in the merger in general. #31 shuold be the base of that, though.

Add(ButtonConfig) in InputManager

ButtonConfig instances are only generated on the fly. For KeyHints they should be created outside the InputManager and the manager needs an Add(ButtonConfig) method.

Standardizing naming conventions

Currently, there is a mix of various naming conventions being used. Naming conventions across all files should be changed to use:

  • lowerCamelCase for private/local member variables and methods
  • UpperCamelCase for classes, and public/internal methods and variables
  • Remove any uses for Hungarian notation, such as the m_ member variable prefix used by Valheim

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.