librg»Blog
Dominik Madarász
Hello everyone!

Do you sometimes sit down and think about how things started, what the goals were when you went on a journey to code what you like and you experienced a new lands of knowledge? Well, we did. We went on a journey during which we have learnt to code better, to understand this field a lot more, but more importantly, to get to know each other even better. We often ended up using VoIP and remote screen software, so we could discuss what to work on and how to approach it. It's what I think made the goals fulfilled. Not the fact we finally made a piece of software that works as originally intended, but the fact we worked on something as a team, improving ourselves.

Librg enters a maintenance status, as we've reached the point when we're content with what this library offers. We still provide full support and QoL patches, so that it can be used at any time, improved over time and keep being fresh and aligned to what we think is a good status of the project. Time will tell where we end up.

I'd like to thank Handmade Network community, the staff members and librg users for any support provided. We're glad to be part of this community. I can say for sure it has changed how we approach programming today, it has changed us to think more about what the software really needs, why and when to sacrifice comfort for efficiency, it brought us to point where we discuss `why`s over following idioms blindly.
Dominik Madarász
Hello folks!

We hope you are doing well. Today we wanted to make a small update post with the current situation, and what is happening.
And the most important news - nothing happens! Or as you say in the development world, everything is stable.

Around a month ago we release minor update 3.1, which fixed a few bugs and added a few minor features, you can check out changelog on our github.

Our next planned minor update is gonna be focused around jitter buffer, or "how to make a world a nice discrete place".
We want to give users ability to setup a time, or a buffer size, which will average and distribute update packets as evenly as it can, by requiring minimal changes from their side.

And our next major update will be focued around splitting up currently hardcoded net library backend we are using, into a more modular-ish way.
So librg header will just define methods that will be used for network backend, and lets say, librg_enet.h will have them implemented.
This way users can easily can decide to use any other library backend they find better suits their needs, even TCP or HTTP solutions!
(I agree, HTTP in this case might be *slightly* weird, but hey, we are not gonna judge you! :D)

We will finally address an issue, where in heavily populated areas librg spawns a lot of branches, that might end up being unused after some time. This behavior can cause slight increase in memory usage, as those branches, that might never be used anymore, will end up in memory without any real purpose. To solve this issue, we need to make sure we join unused branches together and perform actual tree cleanup. This issue can also be temporarily avoided by increasing the minimum branch area size.

In any case, it's gonna be fun! If you have any questions or suggestions, fell free to comment.

Thank you guys for sticking with us, for reading the post, and for being awesome handmade people you are!

(To answer the question: It depends)
Dominik Madarász
Hi everyone,

It has been 2 months since we've released the second version of librg. When we've started working on 3.0 however, we've decided to revamp and re-design some parts of the library heavily:

  • We've decided to drop support of our component system in the core and make an extension out of it. This means, users aren't forced to use our component system anymore and they can pass their data using user_data pointer.
  • We've also finally brought library contexts to the codebase, so user can maintain multiple instances of librg in a single process.
  • The library itself is much easier to embed into an existing project as all our dependencies part of the same compilation unit used by librg.

  • World reconstruction is now a thing, while still not being perfect, it's already stable and improves the performance a lot, since we don't need to reconstruct the whole entity scene graph every tick anymore.

  • Built-in (or custom) events can now be rejected using librg_event_reject function.

  • After several thoughts, we've decided to not include entity cooling into the library's core. It is available as a library extension instead. Reason is, entity cooling is rather a specific algorithm that should be adapted based on user's needs. We've decided to implement a very basic template called librg_limiter which makes use of event rejection and should act as a template for user's needs.

  • We've improved the way we sync data across the network and split the stream into two channels: reliable and unreliable, where the former is used to describe entity creation/deletion (as it is important information) whereas the latter is used for entity updates.

  • As usual, we've made some tweaks and performance improvements.


  • Since 3.0 is out now, we currently don't have any specific goals for the next iteration. We might announce any plans at a later date.

    We've also tried librg out on different platforms and so far we've had a success with the iOS and Android builds.



    You can try the new version out at: librg GitHub site.

    Thank you for reading this, we appreciate any support or feedback.

    Have fun!
    inlife
    Hello guys,

    This update post will be short. Just want to quickly describe what we are currently doing, and what are our plans for the future.

    I think you remember from our last update that we were working on the version-3 update. And we planned to release it at the end of October 2017. However we've decided to slightly(or not) hold the update, and include some major changes to an api.

    Some of these changes we wanted to share with you guys:
    We are planning to extract and move our EC system to a separate optional module, considering the fact that not many people really need an built-in EC system in librg and/or they are using their own systems already.

    We are still working on some prototype, but here are our thoughts on how we can make a new entity might look like:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    typedef struct {
        u32 id;
        u32 type;
        u64 flags;
    
        f32 stream_range;
        zplm_vec3_t position;
    
        librg_peer_t *control_peer;
        librg_peer_t *client_peer;
    
        zpl_array_t(librg_entity_t) last_query;
    
        librg_table_t last_snapshot;
        librg_table_t ignored;
    
        zplc_t *branch;
        void *user_data;
    } librg_entity_tx;
    


    Maybe you guys have some ideas/propositions, we would be glad to hear them too.

    Thank you and have a nice day! :)
    inlife
    Welcome everybody to our second post.

    First of all we would like to give you guys updates about our current status, and what we have been doing for the past month. And oh boy, that was one hell of a month!

    Lest start with nice list of things that we planned to do:
    * Multi-threaded world reconstruction.
    * Re-visit and revamp all memory allocation strategies.
    * Add support of entity cooling, decreasing the number of updates sent.
    * Add ability to reject all possible built-in events.
    * Add fields: peer and "custom" pointer to the event structure, possibly giving them more potential.
    * Consider single-var context representation and context switching.
    * Add more assertions to possible user inputs.
    * And of course various performance optimizations.

    So, concluding the list above, we had 2 major tasks, refactor the library, to support contexts, or in other words, make it thread-safe. And second one, refactor the component system. The component system refactor was not actually mentioned above, but it was needed due to context implementation, and the way how components worked before.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    #define LIBRG_IMPLEMENTATION
    #define LIBRG_DEBUG
    #include <librg.h>
    
    typedef struct { u32 bar; } foo;
    
    void custom_components(librg_ctx_t *ctx) {
        librg_component_register(ctx, librg_component_last, sizeof(foo));
    }
    
    void on_connect_accepted(librg_event_t *event) {
        librg_log("someone connected to the server!\n");
    }
    
    int main() {
        // initialization
        librg_ctx_t ctx = {0};
    
        ctx.tick_delay   = 32;
        ctx.mode         = LIBRG_MODE_SERVER;
        ctx.world_size   = zplm_vec3(5000.0f, 5000.0f, 0.0f);
    
        librg_init(&ctx, custom_components);
    
        // adding event handlers
        librg_event_add(&ctx, LIBRG_CONNECTION_ACCEPT, on_connect_accepted);
    
        // starting server
        librg_address_t address = {0}; address.port = 27010;
        librg_network_start(&ctx, address);
    
        // starting main loop (run 100 times for test)
        for (int i = 0; i < 100; ++i) {
            librg_tick(&ctx);
            zpl_sleep_ms(1);
        }
    
        // stopping network and freeing resources
        librg_network_stop(&ctx);
        librg_free(&ctx);
    
        return 0;
    }
    


    New component system has a few benefits, comparing it to old one, such as: the core implementation is macro-less now (previous one required you to use macro constructions to generate needed methods for the needed component), it is slightly faster, and the most important thing is, it is possible to bind component methods to the different scripting languages/implementations now. We also got rid of so-called, lazy-initialization for component pools, which makes your memory consumption increase somewhat unexpected for occasions when your are not really familiar with what is going on under the hood.

    Next thing - context implementation.
    The idea is simple, and you saw it in the most of proper-made C libraries out-there: provide context to each method. So this is basically exactly what we did. Updating method-API part, changing data structures, and re-organizing code inside implementation part, to make everything work simply, and nicely.

    - Why do you need contexts, you can ask, they are just making the interface more complicated!
    Well it's a good question, first of all, with our previous approach you wouldn't be able to run multiple instances of librg inside one thread, and making let's say a server-client app/game bundled inside one application and inside one thread would be impossible.

    As soon as we finished those 2 things mentioned above, we wanted to check, how hard it would be to create a bindings for the library, so we made librg-odin. And then we stared doing performance optimizations, decreasing amount of time needed for creation of update packet for each client.



    We started from around point of 300 ms per update for 1k connected clients + 10k entities, and successfully decreased the update time to only 10-20 ms per update for same conditions by making optimizations for cycles, minimizing repeated memory allocations, etc. And, the most important, adding support for multi-threaded update and entity culling insertion, which made the most significant difference.

    We are currently also experimenting with world graph modification, to avoid re-constructing our k-d tree each time server updates our entities. This feature is currently volatile and requires further polishing, it may or may not be available in the 3.0 release.

    All these changes lead to our new version 3.0.
    Currently, we are still busy finishing some things, but we hope that we will be able to switch to 3.0 and mark it as stable within this month.

    Thank you for reading!