Giter Club home page Giter Club logo

arch's Introduction

Arch

Maintenance Nuget License C#

A high-performance C# based Archetype & Chunks Entity Component System (ECS) for game development and data-oriented programming.

  • ๐Ÿš€ FAST > Best cache efficiency, iteration, and allocation speed. Plays in the same league as C++/Rust ECS Libs!
  • ๐Ÿš€๐Ÿš€ FASTER > Arch is on average quite faster than other ECS implemented in C#. Check out this Benchmark!
  • ๐Ÿค BARE MINIMUM > Not bloated, it's small and only provides the essentials for you!
  • โ˜•๏ธ SIMPLE > Promotes a clean, minimal, and self-explanatory API that is simple by design. Check out the Wiki!
  • ๐Ÿ’ช MAINTAINED > It's actively being worked on, maintained, and comes along several Extensions!
  • ๐Ÿšข SUPPORT > Supports .NetStandard 2.1, .Net Core 6 and 7, and therefore you may use it with Unity or Godot!

Download the package, get started today and join the Discord!

dotnet add PROJECT package Arch --version 1.2.8.1-alpha

Code Sample

Arch is bare minimum, easy to use, and efficient. Let's say you want to create some game entities and make them move based on their velocity... sounds complicated? It's not! Arch does everything for you, you only need to define the entities and the logic.

// Components ( ignore the formatting, this saves space )
public struct Position{ float X, Y };
public struct Velocity{ float Dx, Dy };

public sealed class Game 
{
    public static void Main(string[] args) 
    {     
        // Create a world and entities with position and velocity.
        var world = World.Create();
        for (var index = 0; index < 1000; index++) 
            world.Create(new Position{ X = 0, Y = 0}, new Velocity{ Dx = 1, Dy = 1});
        
        // Query and modify entities ( There are also alternatives without lambdas ;) ) 
        var query = new QueryDescription().WithAll<Position,Velocity>(); // Targets entities with Position AND Velocity.
        world.Query(in query, (ref Position pos, ref Velocity vel) => {
            pos.X += vel.Dx;
            pos.Y += vel.Dy;
        });
    }
}

Contents

Quick start

I bet you don't want to read tons of documentation, theory, and other boring stuff right?
Let's just ignore all that deep knowledge and jump in directly to get something done.

For more detailed API and features, check out the wiki!

ECS

Entity Component System (ECS) is a software architectural pattern mostly used for the representation of game world objects or data-oriented design in general. An ECS comprises entities composed of components of data, with systems or queries which operate on entities' components.

ECS follows the principle of composition over inheritance, meaning that every entity is defined not by a type hierarchy, but by the components that are associated with it.

World

The world acts as a management class for all its entities, it contains methods to create, destroy and query them and handles all the internal mechanics.
Therefore, it is the most important class, you will use the world heavily.
Multiple worlds can be used in parallel, each instance and its entities are completely encapsulated from other worlds. Currently, worlds and their content can not interact with each other, however, this feature is already planned.

Worlds are created and destroyed like this...

var world = World.Create();
World.Destroy(world);

There can be up to 2,147,483,647 possible worlds with up to 2,147,483,647 entities each.

Entity

An entity represents your game entity.
It is a simple struct with some metadata acting as a key to access and manage its components.

Entities are being created by a world and will "live" in the world in which they were created.
When an entity is created, you need to specify the components it will have. Components are the additional data or structure the entity will have. This is called "Archetype".

var otherEntity = world.Create<Transform, Collider, PowerUp>(... optional);

or

var archetype = new ComponentType[]{ typeof(Position), typeof(Velocity), ... };
var entity = world.Create(archetype);

world.Destroy(entity);

Component

Components are data assigned to your entity. With them you define how an entity looks and behaves, they define the game logic with pure data.
It's recommended to use struct components since they offer better speed.

To ease writing code, you can access the entity directly to modify its components or to check its metadata.
A small example could look like this...

var entity = world.Create<Position, Velocity>();

ref var position = ref entity.Get<Position>();    // Get reference to the position.
position.X++;                                     // Update x.
position.Y++;                                     // Update y.

if(entity.Has<Position>())                        // Make sure that entity has a position (Optional).
    entity.Set(new Position{ X = 10, Y = 10 };    // Replaces the old position .

entity.Remove<Velocity>();                         // Removes a velocity component and moves it to a new archetype.
entity.Add<Velocity>(new Velocity{ X = 1, Y = 1);  // Adds a velocity component and moves the entity back to the previous archetype. 

System aka. Query

Queries aka. Systems are used to iterate over a set of entities to apply logic and behavior based on their components.

This is performed by using the world (remember, it manages your created entities) and by defining a description of which entities we want to iterate over.

// Define a description of which entities you want to query
var query = new QueryDescription().    
            WithAll<Position,Velocity>().      // Should have all specified components
            WithAny<Player,Projectile>().      // Should have any of those
            WithNone<AI>();                    // Should have none of those

// Execute the query
world.Query(in query, (Entity entity) => { /* Do something */ });

// Execute the query and modify components in the same step, up to 10 generic components at the same time. 
world.Query(in query, (ref Position pos, ref Velocity vel) => {
    pos.X += vel.Dx;
    pos.Y += vel.Dy;
});

In the example above, we want to move our entities based on their Position and Velocity components. To perform this operation, we need to iterate over all entities having both a Position and Velocity component (All). We also want that our entity either is a Player or a Projectile (Any). However, we do not want to iterate and perform that calculation on entities that are controlled by an AI (None).

The world.Query method then smartly searches for entities having both a Position and Velocity, either a Player or Projectile, and no AI component and executes the defined logic for all of those fitting entities.

Besides All, Any, and None, QueryDescription can also target an exclusive set of components via Exclusive. If that's set, it will ignore All, Any, and None and only target entities with an exactly defined set of components. It's also important to know that there are multiple different overloads to perform such a query.

The less you query in terms of components and the size of components... the faster the query is!

More features and Outlook

This is all you need to know, with this little knowledge you are already able to bring your worlds to life.
However, if you want to take a closer look at Arch's features and performance techniques, check out the Wiki! There's more to explore, for example...

  • Bulk Entity Creation
  • Batch Operations
  • High-performance Queries
  • Archetypes
  • Chunks
  • Parallel / Multithreaded Queries
  • Enumerators
  • CommandBuffers
  • Pure ECS
  • Events
  • Monogame, Unity, Godot Integration guides
  • More API

Extensions

Arch has some extensions that add more features and tools. Among them for example :

  • ๐Ÿ› ๏ธ Arch.Extended > Adds a set of tools and features to save boilerplate code!
  • ๐Ÿ”Ž Godot Entity Debugger > An Arch Entity debugger for the Godot engine!
  • ๐Ÿ”Ž Stride Entity Debugger > An example of Arch in the Stride engine, with additional entity and system inspector!
  • ๐Ÿ”Ž Arch.Unity > A library that makes the integration of Arch in Unity much easier, with many cool new features!
  • โ“ Your Tool-Library? > If you develop more tools and features for Arch, let us know and we'll list them here!

Performance

Well... it's fast, like REALLY fast.
However, the iteration speed depends, the less you query, the faster it is.
This rule targets the amount of queried components as well as their size.

Based on https://github.com/Doraku/Ecs.CSharp.Benchmark - Benchmark, it is among the fastest ECS frameworks in terms of allocation and iteration.

Benchmark

The current Benchmark tested a bunch of different iterations and access techniques. However, the most interesting one is the QueryBenchmark. In the benchmark, a set of entities was iterated over using the framework to access their transform and velocity and calculate a new position each iteration.

Their components looked like this:

public struct Transform{ float x; float y; float z; }
public struct Velocity { float x; float y; float z; }

The following performance was achieved with Arch for the scenario under heavy load and different amount of entities.

Method Amount Mean Error StdDev CacheMisses/Op Allocated
WorldEntityQuery 10000 111.003 us 13.2838 us 0.7281 us 409 -
Query 10000 20.159 us 1.4188 us 0.0778 us 103 -
EntityQuery 10000 17.711 us 1.1311 us 0.0620 us 49 -
StructQuery 10000 7.767 us 0.1572 us 0.0086 us 7 -
StructEntityQuery 10000 7.338 us 1.7188 us 0.0942 us 12 -
WorldEntityQuery 100000 1,326.959 us 3,058.5935 us 167.6518 us 5,753 -
Query 100000 203.555 us 4.6038 us 0.2523 us 2,977 -
EntityQuery 100000 228.222 us 17.4030 us 0.9539 us 2,708 -
StructQuery 100000 115.466 us 8.8355 us 0.4843 us 2,726 -
StructEntityQuery 100000 76.823 us 2.1875 us 0.1199 us 2,544 -
WorldEntityQuery 1000000 12,519.798 us 4,491.2760 us 246.1820 us 45,604 -
Query 1000000 2,679.153 us 35.1696 us 1.9278 us 28,579 -
EntityQuery 1000000 2,462.296 us 322.4767 us 17.6760 us 28,113 -
StructQuery 1000000 1,514.479 us 296.5311 us 16.2539 us 29,723 -
StructEntityQuery 1000000 1,483.142 us 329.9446 us 18.0854 us 31,272 -

Projects using Arch

Arch is already used in some projects, for a more detailed look, take a look at the wiki!

https://github.com/genaray/Arch/wiki/Projects-using-Arch

Space Station 14 is inspired by the cult classic Space Station 13 and tells the extraordinary story of everything that can go wrong on a shift at a space station. You take on a role and complete your tasks so that the space station doesn't go to the dogs... or do the exact opposite. Prepare yourself for chaos and the finest roleplay. Best of all, SS14 is open-source and anyone can play!

An action-packed c# clone of the hit "vampire survivor" based on monogame and arch! Fight your way through hordes of different enemies, level up your character, collect permanent items and explore various maps! Try it out!

Equilibrium Engine is a data-oriented C# game engine that takes advantage of ECS pattern followed by Hot-Reloading of your libraries which allows you to quickly iterate on different aspects of your projects.

arch's People

Contributors

andreakarasho avatar arhinzi avatar beancheeseburrito avatar clibequilibrium avatar donaut avatar drsmugleaf avatar emelrad12 avatar epicguru avatar genaray avatar hertzole avatar lilithsilver avatar martindevans avatar metalgearsloth avatar mikhail-dcl avatar nathanrw avatar pheubel avatar reeseschultz avatar richdog avatar royconskylands 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  avatar  avatar  avatar  avatar  avatar

arch's Issues

Empty structs / Tag components should not consume any memory

Tag components ( Empty structs ) do actually consume 1 byte per instance and are actually being allocated in each chunk.
Instead we could just ignore them, however this means that that some query like this...

world.Query(in desc, (ref Transform t, ref SomeTag tag) => {});

Would throw an exception since the entity is tagged with it, but since its a non allocated tag component it can not be acessed.
Debug.Assert could probably help here to avoid such errors.

IndexOutOfRangeException if LastChunk.Size on Archetype is 0

When removing Entities from an Archetype either directly or via the World it sometimes throws an IndexOutOfRangeException, this is because for some reason the Archetype's LastChunk.Size is 0. An easy reproduction is the following:

Inside EnumeratorTest.cs add a new Test:

[Test]
public void QueryRemove()
{
    var entities = new List<Entity>();
    world.GetEntities(description, entities);

    for (var i = 0; i < entities.Count; i++)
    {
        if (i == 5500)
            ;

        var entity = entities[i];
        world.Destroy(in entity);
    }
}

It will throw the exception when trying to remove the 5500th Entity, hence the if statement to set a breakpoint on the empty statement inside it

Set and enforce a consistent code style

Arch is currently using unconventional code styles in a lot of places. I would like to propose some more consistent styles, as well as some other things to potentially look at. Enforcing a consistent code style allows for a better overall coding and reading experience.

Major things this project needs to tackle:

  • Misspellings in both documentation and code.
  • Using one file for multiple types.
  • Misuse of the in modifier.
  • Overuse of the MethodImpl attribute.
  • Difficult readability because of single-line statements.

Below, I've listed some conventions this project can enforce. These are up for discussion though. I'm happy to help implementing this.

The below can also be found here as an .editorconfig file.


Naming Conventions and Accessibility Modifiers

  • private, protected, private protected, and protected internal fields should be _camelCase. The same applies when they're static.
  • internal and public properties should be PascalCase. The same applies when they're static.
  • const locals and fields should be PascalCase.
  • Parameters should be camelCase.
  • Methods, enums, structs and classes (and their record equivalents), and namespaces should be PascalCase.
  • Type parameters should be TPascalCase.
  • Interfaces should be IPascalCase.

Fields should never be internal or public. Properties should never be private, protected, private protected, or protected internal.
Accessibility modifiers should be required at all times.
readonly should be added whenever possible.

var Preferences

  • Do not use var anywhere.

.NET Conventions

  • System using directives should always be sorted first.
  • .this qualification should never be used.
  • Predefined types (int) should always be used over framework types (Int32).
  • Parentheses should be used for clarity:
    • In arithmetic operators: a + (b * c)
    • In other binary operators: a || (b && c)
    • In relational operators: (a < b) == (c > d)

Expressions

  • Should prefer index and range operators.
  • Should prefer simple default expression.
  • Should prefer object and collection initializers.
  • Should prefer conditional expression (?:) over if with assignments and returns.
  • Should prefer local function over anonymous function (lambda).
  • Should prefer implicit object creation when type is apparent (target-typed new).
  • Do not use inferred tuple element and anonymous type member names.

  • Do not use expression body for constructors.
  • Do not use expression body for local functions.
  • Do not use expression body for methods.
  • Do not use expression body for operators.
  • Do not use expression body for indexers.
  • Do not use expression body for properties.
  • Should prefer expression body for accessors when on single line.
  • Should prefer expression body for lambdas when on single line.

Pattern Matching and null Checking

  • Should prefer pattern matching when possible.
  • Do not use throw-expression (obj ?? throw new ArgumentNullException(nameof(obj));).
  • Should prefer conditional delegate call.
  • Should prefer coalesce expression.
  • Should prefer null propagation.
  • Should prefer is null over ReferenceEquals or == null.

New Line and Braces Preferences

  • Do not allow multiple blank lines.
  • Do not allow embedded statements on same line.
  • Do not allow blank lines between consecutive braces.
  • Do not allow statements immediately after block.
  • Do not allow blank line after colon in constructor initializer.
  • Should prefer opening brace after if, else, case blocks.

Ref iterators for `Query`

Would be neat to have one more alternative to the existing query APIs. A iterator directly received from the query, replacing world.Query(in, (...) => {});

Like this :

var query = world.Query(in desc); 
foreach(var refs in query.GetIterator<Position, Velocity,...>()){
   refs.t0.x += refs.t1.x;
   refs.t0.y += refs.t1.y;
}

foreach(var refs in query.GetEntityIterator<Position, Velocity,...>()){
   Console.WriteLine(refs.entity);
   refs.t0.x += refs.t1.x;
   refs.t0.y += refs.t1.y;
}

Those could probably even replace world.Query(in, (...) => {}) calls completly, since they offer more advantages. They will not closure copy, offer greater controll over the iteration flow and are probably easier to maintain.

@andreakarasho Probably already had a first draft of this according to #35 ( if i understood that correctly ) ^^

Resource manager: Handle<T>

Implement a Handle<T> to manage managed resources as struct fields as discussed in #44

Prototype

public readonly struct Handle<T>
{
    public readonly int ID;

    internal Handle(int id) => ID = id;
}

public sealed class EcsResourceMap<T>
{
    private readonly List<T> _list = new List<T>();

    public Handle<T> Add(T item)
    {
        var handle = new Handle<T>(_list.Count);
        _list.Add(item);

        return handle;
    }

    public ref T Get(in Handle<T> handle)
    {
        return ref CollectionsMarshal.AsSpan(_list)[handle.ID];
    }
}

Sample

struct Sprite
{
   public Handle<string> Name; // Name.ID as integer
   public Handle<Texture2D> Texture; // Texture.ID as integer
   public Vector2 Position;
   public Vector2 Scale;
}

Key not found when calling world.Destroy

         struct T { };
         struct K { };

          var arch = new Type[] { typeof(T), typeof(K) };

            for (int i = 0; i< 100; ++i)
                _world.Create(arch);

            var rng = new Random();
            while (true)
            {
                _world.Query(new QueryDescription() { All = arch }, (in Entity e, ref T t, ref K k) =>
                {
                    if (rng.Next() % 2 == 0)
                    {
                        _world.Destroy(in e);
                    }
                });
            }

At the exception throwing the _world.Size is > 0

Faster Lookups

Entity lookups are still pretty "slow" compared to regular iterations... this is because the archetype architecture favors iterations above lookups. However there still some tricks to make lookups optimal.

One idea is a simple lookup array inside the world itself, that one could map a entity index directly to its archetype, version and chunk. This would reduce dictionary lookups and would be optimal.

entity.Add(cmp) works differently than World.Add(entity, cmp)

Hey, things have been running pretty good so far with the latest changes. Found a minor one that seems to be reproducible:

entity.Add(new AbcComponent { Field = 1 });

Uses the default uninitialized AbcComponent component instead of the initialized component.

this does seem to work:

World.Add(entity, new AbcComponent { Field = 1 });

Null error in QueryArchetypeEnumerator

Hope you're doing well!

I'm not sure how it gets into this state, but occasionally, all of the entries in _archetypes in QueryArchetypeEnumerator become null. The enumerator Current returns null and it throws on archetype.Size. I think it might be related to adding a component to my player entity (which maybe is special in the number of components it has), only seems to happen to that entity.

In my testing, it seems like either all the entries are populated or all null when it encounters the error, so maybe it can break after encountering a null archetype?

Enumerators.cs, QueryArchetypeEnumerator struct:

[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
    while (_archetypes.MoveNext())
    {
        ref var archetype = ref _archetypes.Current;
        if (archetype.Size > 0 && _query.Valid(archetype.BitSet))
        {
            return true;
        }
    }

    return false;
}

Entity Events

Being able to listen for entities event is also probably a nice feature. Many ECS frameworks actually do this.
However, this should be an optional feature since it would heavily conflict with the "performance first" approach of this project.

world.OnEntityAdded += (in Entity en) => {};
world.OnEntityRemoved += (in Entity en) => {};
world.OnAdded<T> += (in Entity en, ref T cmp) => {};
...

Query BitSet comparison issue

Found an issue when my component count increased past 32. It started failing on components being "found" in Chunks not containing them causing memory access errors.

In BitSet All method, when the bitset for the new component extends to 2 uint's, but it checks against a bitset with 1 uint, there is an extra check to handle bitset's 2nd uint:

    var extra = _bits.Length - count;
    for (var i = count; i < extra; i++)

Since extra is subtracting "count", and starting the loop from "count", the loop won't run and always returns true. It can use the bits.Length to loop through the extra uint.

    for (var i = count; i < _bits.Length; i++)

A unit test for it:

[Test]
public void AllWithDifferentLengthBitSet()
{
    var bitSet1 = new BitSet();
    bitSet1.SetBit(5);
    var bitSet2 = new BitSet();
    bitSet2.SetBit(33);

    bool result = bitSet2.All(bitSet1);

    That(result, Is.EqualTo(false));
}

Up to 25 or 50 generic overloads ?

Currently there always 10 different generic overloads for all important methods. This could potentially be limiting...

world.Create<T0...T9>(...);

Might be not enough to describe complex entities... Especially players, mobs or similar entities could become very complex in no time. This of course depends on the way the user designs its components.

It would probably make sense to increase the amount of generic overloads to 25 or even 50 for each operation.

world.Create<T0...T25>(...);

Add `World.Create(object[])`

Discussed in #78

Originally posted by Mathetis April 11, 2023
Hi, I'm curious if the following behavior is by design, if I am misusing it, or if this is a bug.

I have a use case where the array of components arrived as an object[], which contains references to struct components (boxing isn't problematic for my case here). I want to create an entity from those components. Here's a simple example:

struct SomeComponent { ... }

object[] components = new object[] { new SomeComponent() { ... } };

Entity entity = world.Create(components);

bool hasComponent = entity.Has<SomeComponent>();  // <-- this will be false.

We should probably add another World.Create overload to create entities by a object[] array :)
This should be done quickly though.

(new Entity()).IsAlive() is maybe true

This a surprising behavior.

{
   World w = World.Create();
   bool e_alive_1 = (new Entity()).IsAlive(); //Returns false
   w.Create();
   bool e_alive_2 = (new Entity()).IsAlive(); //Returns true
}

this should probably return false in both situations. This leads to surprising results if you are trying to store an entity in a component and want to check if the parameter has been initialized or not.

CommandBuffer suggestions

I found the commandbuffers implementation not very user-friendly and about this I suggest to merge all the cmdbuffer types into an unique class (not a struct) to emulate the World class.
I see multiple advatanges:

  • a better and tiny API
  • users will work with Entity struct only [all the internal changes should be tracked into Arch]

Ugly pseudo code:

struct Transform { public Vector3 Position; }

var world = World.Create();
var cmd = new CommandBuffer(world);

var entity = cmd.Create<Transform>();
ref var transform = ref cmd.Get<Transform>(in entity);
transform.X = 20;

world.Query(in query, (in Entity e, ref Transform t) => {
   if (t.Position == Vector3.Zero)
      cmd.Destroy(in e); // will mark the entity as destroyed and next queries will not grab this entity until the real destruction
  else
    --t.X;
   
});

Add serialization and deserialization mechanisms

I find myself iterating the entities with an open QueryDescription then serializing all the components on each one, but there should probably be a better (faster?) way. And one that captures only what is needed to rebuild the state. The use-case would be both for syncing around (network or between processes) as well as saving down to disk or database.

Any plans to implement something first class in the near future or any ideas on how to accomplish this that I am missing?

Arch.Extended `ParallelQuery`?

Discussed in #62

Originally posted by stgeorge February 4, 2023
This is more for the Arch.Extended project but it didn't have a Discussions section.

I noticed there's not an easy way to change a [Query] to parallel other than kind of pulling it out of the source generator and using the Arch scheme. While this is OK, it would be pretty cool to just be able to say [ParallelQuery] and have it work without much fuss. I am liking the source generator approach in terms of less boilerplate.

Alternative to Dictionary<T, int>

In dotnext they created a generic class with a static read-only int field each time somebody accesses the generic class with a new type the static field is incremented. And thus creating a compile-time dictionary. TypeMap There was a discussion about it too somewhere in the c# runtime repository but sadly I lost that.

Exception when adding component to entity in query

For reasons I'm not sure of, when adding a component to an entity within a query. It seems to run into this problem (when processing a lot of entities, it doesn't seem to happen when there's not a lot going on):

in Archetype.cs:

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    [Pure]
    internal int Transfer(int index, ref Chunk chunk)
    {
        // Get last entity
        var lastIndex = chunk.Size - 1;
        var lastEntity = chunk.Entities[lastIndex];

chunk.Size is 0, so it tries to access lastIndex = -1.

So I'm not sure if it is because the chunk being transferred into might be empty once in a while? Not sure if it makes sense, but thought I'd post it in case you knew the cause, I can not find a clear way to repro it unfortunately without just throwing a ton of entities and adding components within the query (I use mostly inline non-parallel, but in this case, it happened with both parallel or non-parallel).

Issue with .dll hot-reloading and Type change

Hello,

I am using Arch in my game engine but facing an issue when reloading a .dll. My dll contains a Component struct and Arch would fail to recognize that the component from a new reloaded .dll is the same as from the old one. It wouldn't match any queries and I would be able to assign duplicate components of the "same" type to an entity.

I see that you use direct matching of Types in a dictionary to match the components, do you think it's possible to change it to a more robust approach, or do you happen to have tips on how to make Arch compatible with hot-reloading ?

 /// <summary>
    ///     Trys to get a component if it is registered.
    /// </summary>
    /// <param name="type">Its <see cref="Type"/>.</param>
    /// <param name="componentType">Its <see cref="ComponentType"/>, if it is registered.</param>
    /// <returns>True if it registered, otherwhise false.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool TryGet(Type type, out ComponentType componentType)
    {
        return _types.TryGetValue(type, out componentType);
    }

Thanks

16KB Chunks should scale up when entities are too big in size...

When entities are getting too big in size ( in terms of components and the sum of their total bytes ), the archetypes should increase their total chunk capacity to like 32,64 or 128 KB. This would improve performance for huge entities and datasets.

E.g :

var archetype = new Type[]{
   typeof(Transform),
   typeof(Rotation),
   typeof(Stats),         // Like 10 floats or whatever
   typeof(Health),
   typeof(Inventory),
   typeof(Equipment),
  .... // More components
};

Important entities like players are mostly getting pretty big... there always ways to reduce the size of components, however its not alwayss possible. In such a case above only a handfull of entities fit into each chunk... which means more chunk switches and therefore more cache misses.

By increasing the chunk capacity for bigger entities, this could be avoided and still provide the best possible performance.

Compile time static Queries, GroupDescriptions and more ?

Theres still a lot of mapping under the hood, often used with Dictionarys... and we all know that Dictionarys are kinda slow. You may ask why ?

Since we often need to find the certain archetype, query or bitset for a Type[]. So we need to map it, which is hella expensive.

world.TryGetArchetype();
world.Query();
world.Move();
entity.Add/Remove

Make heavily use of that mapping.
An alternative to this approach is to somehow use compile time static magic to prevent additional dictionary lookups. Which also increases the performance a lot.

public static class Archetype{
	
	public static int Id;
}

public static class Archetype<T>{
	
	public static readonly int Id;
	public static readonly Type[] BitSet; // BitSet wรผrde sich durch ComponentMeta von T erstellen lassen durch code generierung. 
	static Archetype(){	
		Id = ++Archetype.Id; 
		BitSet = new Type[]{ typeof(T) };					  
	}

	public static class Add<A>{
		
		public static readonly int InnerId;
		public static readonly Type[] AddedBitSet;
		
		static Add(){ 
			InnerId = Archetype<T,A>.Id;
			AddedBitSet = Archetype<T,A>.BitSet;
		}
	}
}

public static class Archetype<T,TT>{
	
	public static readonly int Id;
	public static readonly Type[] BitSet; 
	static Archetype(){	
		Id = ++Archetype.Id;
		BitSet = new Type[]{ typeof(T), typeof(TT)};
	}
	
	public static class Add<A>{
		
		public static readonly int InnerId;
		public static readonly Type[] AddedBitSet; 
		static Add(){ 
			InnerId = Id;
			AddedBitSet = new Type[]{ typeof(T), typeof(TT), typeof(A) };
		}
	}
	
	public static class Remove<T>{
		
		public static readonly int InnerId;
		public static readonly Type[] RemovedBitSet;
		
		static Remove(){ 
			InnerId = Archetype<TT>.Id;
			RemovedBitSet = Archetype<TT>.BitSet;
		}
	}
}

This is compile time static and could be used to receive bitsets quickly. The queryDesc could additional receive those ids or the bitset directly to avoid that query dictionary lookup. The old queryDesc would stay since its more readable.

var bitSet = Archetype<Health, Transform>.BitSet; // Compile time static, instant, no lookups required. 
var queryDesc = new QueryDescription{
  All = Archetype<Health, Transform>.Bitset,
  None = Archetype<AI>.Bitset
};

// Or 
var queryDesc = new QueryDescription().All<Health, Transform>().None<AI>();  // Acesses Archetype<T,...> under the hood

// No lookups needed since BitSets are compiletime static generated and our query would know instantly everything it needs. 
world.Query(in queryDesc,....);

Would also be great for structural changes... since we can easily calculate the archetype an entity would be moved to.

Array.CreateInstance

Today a chunk needs to allocate an array of array of struct. This is achieved using the API Array.CreateInstance which creates an opaque managed array of objects[].
I see some downside:

  • the compiler cannot predict the bytes size in memory, this is done during the runtime. So we could have a waste of resources here
  • accessing to a component might be slower when using the Unsafe.As API for the reason above (things get done runtime side)
  • the app cannot use the nativeaot optimizations (disable reflection) if the users wants (need to try out tbh)

I was wondering if it's worth to replace the managed Array.CreateInstance with an opaque unmanaged pointers pool.

Chunk Lookup-Table instead of Dictionary

After some profiling i noticed that the ComponentIdToArrayIndex Dictionary inside the Chunk is slow... which makes sense since dictionarys are only effective for high amount of items.

Therefore it would make sense to replace the dictionary by a efficient lookup table, that one could be stored in the archetype to minimize memory overhead. Using this lookup table to map the component id to the array inside the chunk would be more efficient.

Add Query Unique

We need a method to query singleton components. We will use this method in many scenarios, such as Player Input, making the code look more concise and readable.

    private QueryDescription mDescription = new QueryDescription{Any = new ComponentType[]{typeof(C_PlayerInput)}};
    public void Query()
    {
        world.Query(mDescription, (ref C_PlayerInput input) =>
        {
            if (input.IsClicked)
            {
                //Query another entity....
            }
        });
    }
    public void QueryUnique()
    {
        var playerInput = world.QueryUnique<C_PlayerInput>();
        //Query another entity...
    }

Exclusive Queries

Add a way to query for Entities that contain an exact match of components, so say we execute a Query with Exclusive = { Transform, Roation } in a world with an Entity (A) with components Transform and Rotation and another Entity (B) with Transform, Rotation and Ai, only Entity A would be returned.

Initially proposed in a comment on the Share your ideas and wishes discussion

Possible reference issue?

I found what might be another issue due to moving archetypes.

If I make changes using entity.Remove<>(), it seems to change the archetype of the entity immediately, so then the refs become incorrect. The Console.WriteLine("Hello") should never be reached, but I hit the break point locally.

world.Query(in enemyQuery, (in Entity entity, ref EntityStatus entityStatus, ref SpriteSheet spriteSheet, ref Animation animation) =>
            {
                if (entityStatus.State == State.ReadyToDie)
                {
                    entityStatus.State = State.Dying;

                    if (entity.Has<Burn>())
                    {
                        entity.Remove<Burn>();
                    }
                    if (entity.Has<Slow>())
                    {
                        entity.Remove<Slow>();
                    }
                    if (entity.Has<Shock>())
                    {
                        entity.Remove<Shock>();
                    }

                    if (spriteSheet.Width == 16)
                    {
                        if(entity.Get<Health>().Current > 0)
                        {
                            Console.WriteLine("hello");
                        }
                        int bloodToUse = random.Next(1, 9);
                        spriteSheet = new SpriteSheet(textures["MiniBlood" + bloodToUse], "MiniBlood" + bloodToUse, getMiniBloodNumFrames(bloodToUse), 1, 0, .5f);
                        animation = new Animation(0, getMiniBloodNumFrames(bloodToUse) - 1, 1 / 60f, 1, false);
                    }
                    else
                    {
                        int bloodToUse = random.Next(1, 5);
                        spriteSheet = new SpriteSheet(textures["Blood" + bloodToUse], "Blood" + bloodToUse, 30, 1, 0, .5f);
                        animation = new Animation(0, 29, 1 / 60f, 1, false);
                    }

                    if (entityStatus.State == State.Alive)
                    {
                        Console.WriteLine("Hello");
                    }
                }
                else if (entityStatus.State == State.Dying && animation.CurrentFrame == animation.LastFrame)
                {
                    entityStatus.State = State.Dead;
                }
            });

My solution for now will be to move the removal to a separate query because the sprite/animation change later in the loop is affecting enemies that haven't been killed yet. Oddly enough, I already had to do this to fix some issues in removing the physics component.

world.Query(in enemyQuery, (ref EntityStatus entityStatus, ref SpriteSheet spriteSheet, ref Animation animation) =>
            {
                if (entityStatus.State == State.ReadyToDie)
                {
                    entityStatus.State = State.Dying;

                    if (spriteSheet.Width == 16)
                    {
                        int bloodToUse = random.Next(1, 9);
                        spriteSheet = new SpriteSheet(textures["MiniBlood" + bloodToUse], "MiniBlood" + bloodToUse, getMiniBloodNumFrames(bloodToUse), 1, 0, .5f);
                        animation = new Animation(0, getMiniBloodNumFrames(bloodToUse) - 1, 1 / 60f, 1, false);
                    }
                    else
                    {
                        int bloodToUse = random.Next(1, 5);
                        spriteSheet = new SpriteSheet(textures["Blood" + bloodToUse], "Blood" + bloodToUse, 30, 1, 0, .5f);
                        animation = new Animation(0, 29, 1 / 60f, 1, false);
                    }
                }
                else if (entityStatus.State == State.Dying && animation.CurrentFrame == animation.LastFrame)
                {
                    entityStatus.State = State.Dead;
                }
            });

            world.Query(in query, (in Entity entity, ref EntityStatus entityStatus, ref Body body) => 
            { 
                if(entityStatus.State != State.Alive)
                {
                    physicsWorld.DestroyBody(body);
                    entity.RemoveRange(typeof(Body), typeof(Burn), typeof(Slow), typeof(Shock));
                }
            });

I may also just be trying to do too much in my loops, so feel free to tell me I'm doing things a dumb way ๐Ÿ˜„

Either way, keep of the great work on Arch!

Source generator does not properly build outside windows

C# 11 added raw string literals and although they would be a perfect fit for source generators, it seems that due to source generators needing to be compatible with .net standard 2.0 it causes weird behavior.

When compiling from visual studio on my pc, it works like a charm. however, when building from linux or a windows workflow through github, it makes mistakes in the string literal.

The terminal gets filled with errors that look like:
Error: /home/runner/work/Arch/Arch/src/Arch/Arch.SourceGen/Arch.SourceGen.QueryGenerator/Acessors.g.cs(7974,86): error CS1026: ) expected [/home/runner/work/Arch/Arch/src/Arch/Arch.csproj::TargetFramework=netstandard2.1]

As a test i decided to change StructuralChangesExtensions.AppendWorldAdd(this StringBuilder, int)

var template =
    $$"""
    [SkipLocalsInit]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Add<{{generics}}>(in Entity entity, {{parameters}})
    {
        var oldArchetype = EntityInfo[entity.Id].Archetype;
        // Create a stack array with all component we now search an archetype for.
        Span<int> ids = stackalloc int[oldArchetype.Types.Length + {{amount + 1}}];
        oldArchetype.Types.WriteComponentIds(ids);
        {{setIds}}
        if (!TryGetArchetype(ids, out var newArchetype))
            newArchetype = GetOrCreate(oldArchetype.Types.Add({{types}}));
        Move(in entity, oldArchetype, newArchetype, out var newSlot);
        newArchetype.Set<{{generics}}>(ref newSlot, {{inParameters}});
    }
    """;

to

string template = string.Format(
@"[SkipLocalsInit]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<{0}>(in Entity entity, {1})
{
    var oldArchetype = EntityInfo[entity.Id].Archetype;
    // Create a stack array with all component we now search an archetype for.
    Span<int> ids = stackalloc int[oldArchetype.Types.Length + {2}];
    oldArchetype.Types.WriteComponentIds(ids);
    {3}

    if (!TryGetArchetype(ids, out var newArchetype))
        newArchetype = GetOrCreate(oldArchetype.Types.Add({4}));
    Move(in entity, oldArchetype, newArchetype, out var newSlot);
    newArchetype.Set<{0}>(ref newSlot, {5});
}", generics, parameters, amount + 1, setIds, types, inParameters);

As a result the errors that looked like the one i described earlier went away.

Edit

the actual issue was due to the stringbuilder being trimmed too far on Linux due to a different newline standard.

For any future souls getting haunted by source generators, temporarily adding

<PropertyGroup>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

to your project might help with debugging the code that gets generated. that way you can a view into what actually gets generated.

More generic overloads for `world.` and `entity.` methods

Would be probably a cool addition to the existing API to allow some generic methods to have more generic overloads...

var entity = world.Create<Health,Transform>();
var entity = world.Create(in Health(), in Transform(), ...);

entity.Set(new Health(), new Transform(),...);
entity.Get<Health, Transform>(out var health, out var transform);
entity.Add<Stats,AI>();
entity.Remove<Stats,AI>();

// The same required for world. since entity. relies on them
world.Set(new Health(), new Transform(),...);
world.Get<Health, Transform>(out var health, out var transform);
world.Add<Stats,AI>();
world.Remove<Stats,AI>();

This would reduce boilerplate code and would also speed up some stuff :)
Probably #19 needed, since the generic overloads would likely need to target the fitting archetype ( atleast for creating, adding and removing ).

GetEntites throws a NullReferenceException if the world doesn't contain any entities that match the query

Repro/ UnitTest for it:

[Test]
public void GetEntitesTest()
{
	world = World.Create();

	var archTypes = new Type[] { typeof(Transform) };
	var query = new QueryDescription { All = archTypes };

	var entity = world.Create(archTypes);

	var entites = new List<Entity>();
	world.GetEntities(query, entites);

	Assert.That(entites.Count, Is.EqualTo(1));

	entites.Clear();
	world.Destroy(entity);
	world.GetEntities(query, entites);

	Assert.That(entites.Count, Is.EqualTo(0));
}

Reduce shared files for classes

Right now, files such as Arch/Core/World.cs host multiple classes that are not that tightly related. It would be beneficial if in such cases the classes could be broken into their own files so that it becomes easier to maintain and look for.

This is similar to #41 , but tackles a different area in order to improve the code base.

Sorting entities

Great library! I ditched my naive one and used this and tripled the raw frame rate.

I'd like to get a set of entities sorted by their Y position to draw them back to front, is there a performant way to do this?

Make the World class IDisposable

Looks like whenever a new World is created it is added to the World.Worlds static list (among many other things). Then, when calling Destroy(), it is removed from that list. Seems like this should happen with an IDisposable interface as well, so that there is a common way for this to occur (and convenient with using statement). Perhaps Entity should also have this method of disposable as well.

Bulk operations for `QueryDescriptions`

Would probably make sense to add "bulk" operations for Set, Add, Remove components by not targeting a single entity... instead targeting whole chunks and archetypes by queries directly. This looks simplified like this :

world.Set(in queryDescription, new Velocity(1,0));  // Every single entity targeted by the query description gets that transform set.
world.Add(in queryDescription, new AI());  // Every single entity targeted by the query description gets that new component.
world.Remove<Sprite>(in queryDescription);  // You can imagine what this does. 
world.Destroy(in ...);

This would ease the development even more and is extremely usefull for tagging components. Its also way more efficient since it allows us to do bulk copy operations.

Since this is a more advanced feature, its not that highly priorized yet.

Godot engine is not supported by this repo?

Hi, I'm a Godot and C# beginner, In the README.md file, it says as follows:

  • ๐Ÿšข SUPPORT > Supports .NetStandard 2.1, .Net Core 6 and 7 and therefore you may use it with Unity or Godot!

However, I found there are some C# 11 syntaxes, like this (using the scoped keyword):

Arch/Arch/Core/Archetype.cs

Lines 200 to 204 in b08cc53

internal unsafe ref T Get<T>(scoped ref Slot slot)
{
ref var chunk = ref GetChunk(slot.ChunkIndex);
return ref chunk.Get<T>(in slot.Index);
}

As far as I know, the newest Godot 4.0 support only .Net 6.0 and C# 10:

With the move to .NET 6, users can now target a newer framework that brings optimizations and new APIs. With .NET 6, projects use C# 10 by default and all features are available.

Does it mean the Godot engine is not supported by this project?

Entity Templates

Background

Would be pretty neat if we could clone entities and its components based on a template which defines its values.
Basically a way to create entities with set values. Has many advantages, can be serialized easily and is more effective in many ways.

Idea

Either:

var template = new Template(
   new Position(10,10),
   new Mesh(5, Mode.Batched),
   ...
);

var clone = world.Create(template);

Or using source generation:

[Template]
public struct PlayerTemplate{

   public Transform Transform;
   public Velocity Velocity;
   public Model Modell;
   public ...
   ....
}

var template = new PlayerTemplate(whateverData);
var clone = world.Create(template);

When we choose source generators we could even build more cool constructs around this. Like an extremely efficient identifier mechanic to clone certain templates:

[TemplateStorage]
public struct Templates{

   [Identifier("1:1")]  // Or raw ints e.g. for networking? 
   public PlayerTemplate playerTemplate;

   [Identifier("1:2")]
   public MobTemplate mobTemplate;

   [Identifier("2:1")]
   public FireGolemTemplate fireGolemTemplate;
}

var cloneByIdentifier = world.Create("1:1");
var fireGolem = world.Create("2:1");

`in Entity` vs `ref Entity` in `Query()` overloads

Hi, I managed to shoot myself in the foot earlier and it took me a little while to work out what I had done wrong.

Bad

            var qd = QueryDescription().WithAll<MyComponent>();
            World.Query(qd, (ref Entity e, ref MyComponent c) =>
            {
                // Do something
            });

This code crashes inside Arch with an access violation.

Good

            var qd = QueryDescription().WithAll<MyComponent>();
            World.Query(qd, (in Entity e, ref MyComponent c) =>
            {
                // Do something
            });

This code works.

This issue is that in the 'bad' example, the lambda is a ForEach and so Entity is actually treated as a component type, and naturally there are no such components.

In the 'good' example, the lambda is a ForEachWithEntity so it works as expected. But the only difference is in Entity vs ref Entity. I wonder if something could be done to catch the problem case?

Replace Dictionary EntityInfo with a simple array.

Currently, in the code, you have a EntityInfo property which is a dictionary but you can make it into an array because all the entities ids just increasing numbers starting from 0. So if you have an entity with an id 5 it can be inside the array at index 5. I'm planning to make a pull request about this, but the array resize logic needs to live somewhere should I place an EnsureCapacity method inside the World class or have an Array Wrapper?

`WithNone<Component>()` doesn't remove those entities from the query

I'm guessing that something is making it so that the WithNone and WithAll are conflicting with each other.

Here's the example code:

QueryDescription groundSpritesQuery = new QueryDescription()
                                                .WithNone<Aura, CanFly>()
                                                .WithAll<EntityStatus, Position, SpriteSheet, Animation>();

world.Query(in groundSpritesQuery, (in Entity entity, ref EntityStatus entityStatus, ref Position pos, ref Animation anim, ref SpriteSheet sprite) =>
                {
                    if (entity.Has<Aura>())
                    {
                        Console.WriteLine("hello");
                    }
                    if (entityStatus.State != State.Dead)
                    {
                        Vector2 position = pos.XY - playerPosition;
                        renderEntity(spriteBatch, textures, sprite, anim, position, offset);
                    }
                });

TryGetArchetype uses reference equality instead of SequenceEquals

In the World method TryGetArchetype(Type[], out Archetype) the backing PooledDictionary uses reference equality, which leads to unexpected behavior such as this:

var archTypes1 = new Type[] { typeof(Transform) };
var archTypes2 = new Type[] { typeof(Transform) };

var entity1 = world.Create(archTypes1);
var entity2 = world.Create(archTypes2);

Creating 2 Archetypes in the World as archTypes1 and archTypes2 don't reference the same instance of Type[].

The fix is quite easy, implement an IEqualityComparer<Type[]> that uses SequenceEquals and pass it to the PooledDictionary to use.

macOS arm64 support

SDK: 7.0.202 arm64
Maybe arm64 is not supported?

Arch/src/Arch.Samples/bin/Debug/net7.0/Arch.Samples
Sample App starts...
Unhandled exception. System.TypeInitializationException: The type initializer for 'Arch.Core.Utils.Group`1' threw an exception.
 ---> System.TypeInitializationException: The type initializer for 'Arch.Core.Utils.Component`1' threw an exception.
 ---> System.ArgumentException: Type 'Arch.Samples.Sprite' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
   at System.Runtime.InteropServices.Marshal.SizeOfHelper(Type t, Boolean throwIfNotMarshalable)
   at Arch.Core.Utils.Component`1..cctor() in /Users/Kanbaru/GitWorkspace/Arch/src/Arch/Core/Utils/CompileTimeStatics.cs:line 290
   --- End of inner exception stack trace ---
   at Arch.Core.Utils.Group`1..cctor() in /Users/Kanbaru/GitWorkspace/Arch/src/Arch/Arch.SourceGen/Arch.SourceGen.QueryGenerator/CompileTimeStatics.g.cs:line 12
   --- End of inner exception stack trace ---
   at Arch.Samples.ColorSystem..ctor(World world) in /Users/Kanbaru/GitWorkspace/Arch/src/Arch.Samples/Systems.cs:line 162
   at Arch.Samples.Game.BeginRun() in /Users/Kanbaru/GitWorkspace/Arch/src/Arch.Samples/Game.cs:line 67
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at Microsoft.Xna.Framework.Game.Run()
   at Program.<Main>$(String[] args) in /Users/Kanbaru/GitWorkspace/Arch/src/Arch.Samples/Program.cs:line 15

Process finished with exit code 134.

Command Buffer

It is not possible to Query and add, destroy or add/remove an entity at the same time. Such a operation breaks the query aswell as the underlying world.

To solve this issue we need to "record" or "buffer" such critical changes during the query to transfer those to the world after the query has finished. This would also allow us to leverage the parallel queries to the next level.

A first API draft would look like this :

var cb = new CommandBuffer(world);
var entityWhichIsBuffered = cb.Create();
cb.Destroy(someExistingEntity);
cb.Set<T>(someEntity, cmp);
cb.Add<T>(someEntity, cmp);
cb.Remove<T>(someEntity);

Questions & Problems

Structs with object references or objects themself can not be written to unmanaged memory... therefore we would probably need to find an efficient solution to write and record such a broad variation of different items and types.

cb.Set<int>(someEntity, 1);
cb.Set<MyStruct>(someEntity, new MyStruct());
cb.Set...

Parallel Queries and multiple threads may write to the command buffer at the same time, this needs to work somehow. We could lock the cb and its internals, however then the parallelqueries will become a bottleneck and pretty much obsolete.

How could we do this ?

System API

There should be an additional System API for the world where we can register and process systems. Those systems act as a group of queries and entity/world related operations known from other ECS frameworks.

It probably even could feature code generation...

world.AddSystem<MovementSystem, HealthSystem>();
world.AddSystem(new RangeSystem(10.0f));
world.Update();

public class MovementSystem{

   [Update] 
   [Any<Character, Mob, Entity>]
   [None<Dead, AI>]
   public void Move(ref Transform transform, ref Velocity velocity){
      ....
   }

   [Update] 
   public void CheckCollision(ref Transform transform){
     ...
   }
}

Entity pointing to re-used entry

I might be doing this wrong, as I was keeping references to an Entity in a component.

It had an id of 100, then that entity got killed and its entry (100) was reused by some other kind of entity with a new Id (5000). So then it fails hilariously as IsAlive() is now true and has a different set of components.

So I was expecting that entity's IsAlive() to return false since that id no longer exists...

I'm ok with just keeping the entity id, but doesn't seem like there's an easy way to get an entity (or null) by entity Id from World?

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.