Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

DOTSNET - High Performance Unity ECS Networking from the creator of Mirror!

Discussion in 'Assets and Asset Store' started by mischa2k, May 1, 2020.

  1. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Last edited: Jun 10, 2020
    BitPax, Djayp, ConAim and 1 other person like this.
  2. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.7 pending review.
    2020-06-14_21-43-20.png
     
  3. matpaul

    matpaul

    Joined:
    Jul 24, 2019
    Posts:
    5
    Any updates when ios/android will be support ?
     
  4. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    This month should be doable.
    Feel free to ask again if I forgot.
     
  5. ziggyshawc

    ziggyshawc

    Joined:
    May 11, 2020
    Posts:
    4
    I see this impliments your Apathy transport. Would it require a lot of time to impliment steam p2p transport with something like FizzySteamworks ourselves?

    I know this is more aimed at MMOs but I'm making a 2 player RTS so this would be an instant buy if it came with steam p2p and steamworks authentication support out of the box.

    I hate the walled garden that is Unity's multiplayer. I know there are lots of developers also making ECS RTS/management games out there.
     
  6. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    More transports are very easy to add. You just need to fill out an abstract server/client transport class with abstract Send(), Connect(), etc. functions.

    We already have a steam transport for Mirror. You should be able to add that one to DOTSNET with 10 minutes of work.

    I will add at least one more transport this month too.
     
    dannyalgorithmic likes this.
  7. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.8 pending review. Deterministic transport update order, performance, and smaller improvements.

    2020-06-19_01-20-38.png
     
    Last edited: Jun 23, 2020
  8. JJ-Jabb

    JJ-Jabb

    Joined:
    Feb 6, 2015
    Posts:
    42
    How does this compare with unity’s dots netcode?
     
    lorddanger likes this.
  9. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    From what I understand, Unity's netcode is for first person shooters with around 80 players.
    DOTSNET is more general, not specific to any genre and targeting >1k players.
     
  10. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.9 pending review: small bug fix release.

    2020-06-23_22-49-48.png
     
  11. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.10 pending review:
    2020-06-25_14-33-47.png
     
  12. TheAxisx

    TheAxisx

    Joined:
    Apr 26, 2015
    Posts:
    1
    Hey, I've been wondering if DOTSNET can support multiple rooms/instances, each having different set of data depending on the client e.g. random dungeons, guilds halls, player's rooms(multiple clients could enter one). If not, are you planning to add such feature? Or maybe you could let me know if at least it is viable to implement it on our own.
    Thanks
     
  13. GambitMonkey

    GambitMonkey

    Joined:
    Apr 5, 2016
    Posts:
    65
    Vis2k
    Looks like you will have another networking library hit on your hands.

    Was just curious if there has been any more thoughts around Garbage Cats post on the following topics:

    what about snapshot interpolation?, lag compensation?, delta compression? (a must in this scales), events?,
    authoritative? (if so do you use HPC# physics or Havok or custom physics)
    entity freezing?, can set entity ownership ? does it have packet priority (for near and far entities)?


    As I would have to agree with him/her that these items are a must have for today's networking systems.

    I read above where you already acknowledged that you would look at Litenetlib transport implementation so woot. However it also is like adding a new transport is not that difficult so double woot.

    Thanks for your work in this area and I'm excited where you can take it.
     
    mischa2k likes this.
  14. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Will look into this in a few months. Should be doable since we can just add another ServerWorld.
    Will see :)

    Ok here is a longer answer:

    Snapshot Interpolation/Delta Compression/Lag Compensation
    DOTSNET is designed to be customizable. I don't want to hard code any particular synchronization/interpolation/compression method because different games have different requirements.
    • FPS games might want to use client side prediction and delta compression
    • MMOs will need rubberband movement and most likely CPU cycles are better spent on simulating a giant world instead of delta compression.
    Like with Mirror, the goal is to add default implementations (e.g. simple position movement with interpolation), while allowing you to replace it with your own (e.g. client side prediction with delta compression).

    The way you do this is by simply removing the NetworkTransformAuthoring component and adding your own system :)

    Events
    There are none yet. It's still very bare metal.
    Current goal is to scale the 50k Benchmark to 100k networked entities.
    Afterwards I want to focus on ease of use (e.g. easier state synchronization / helper functions / etc.)

    Authority
    Everything is fully server authoritative by default. Before working with Unity, I used to make MMO Bots. Any part of your game that isn't cheat safe will be exploited if it becomes successful. There was an MMO which had to shut down because they couldn't make it cheat safe, resulting in hyper inflation of the game's economy.

    That being said, it is customizable. You can add your own synchronization systems to have client authority if you wanted to.

    Physics
    Like with Mirror, DOTSNET doesn't really care about your physics. Nothing is hard coded. If you decide to use Unity.Physics package then you can add your own RigidbodySyncSystem.
    Of course, over time it would be valuable to add some of those systems by default.
    It comes with a very simple Unity.Physics demo by the way.

    Entity Freezing
    Not 100% sure what you mean with that.

    Entity Ownership
    Built in. There is a NetworkServerSystem.Spawn(prefab, ownerConnection) function. You would pass the owner connection parameter for player, player's pet, etc.

    Packet Priority
    I want it to be so fast that we don't need packet priority.
    Burst/Jobs are extremely fast and allow for high performance / high scale worlds when used right.
    DOTSNET already went from ~2k networked entities to ~100k entities in a few weeks.
    Packet Priority is how you can deal with a system that is too slow.
    I would rather have a system that is so fast that it doesn't need those workarounds.

    LitenetLib
    Yep, coming soon. Current goal is 100k Entities (which is almost done), afterwards quality of life improvements like transports.
    Note that transports aren't hard coded either. You can add/swap them any way you like.

    Modularity
    So while there are a lot of features to add, keep in mind that everything is very modular and you can add your own parts if you don't want to wait :)
     
    Last edited: Jul 3, 2020
    toomasio, babaqalex, TheAxisx and 2 others like this.
  15. GambitMonkey

    GambitMonkey

    Joined:
    Apr 5, 2016
    Posts:
    65
    Vis2k,
    Thank you for the lengthy reply and thought that might be the case. :)

    When Garbage Cat says Entity Freezing I interpret it as if you are building an area of interest solution for a big world, while calling Idle for each specific player's connection is correct, you might also want to call entity freeze when no players at all are near the entity in the world to save CPU resources on the server.

    Photon has this built in for example.

    Good luck with the new network library and will be sure to keep an eye on your progress.
     
  16. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    I see. This is probably not necessary anymore since Entities.ForEach calls are extremely fast (as in: millions of Entities). Manually worrying about one disabling a particular Entity would most likely be slower.

    ECS requires a whole new way of thinking.
    • Previously we used to worry about particular GameObjects.
    • Now it's about transforming large data sets. E.g. Entities => Transforms => TransformMessages => byte* => Socket
     
  17. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.11 pending review, now supporting LiteNetLib by @RevenantX :)
    It's probably the best UDP transport for Unity, you should give it a try!

    2020-07-12_20-20-57.png
     
  18. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Progress:

    libuv
    (https://github.com/libuv/libuv) Transport added.
    Will be in V1.12.
    Windows/Mac/Linux.
    C# bindings still slower than Apathy.
    Experimental for now.

    2020-07-14_18-33-31.png
     
    BitPax and Matt-Cranktrain like this.
  19. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Decided to fork the C# bindings and make my own, so it will take some time until the next update.

    I have high hopes for libuv. It's THE networking library behind Node.js, probably used a billion times today alone.
    Reading the book right now, they use non-blocking TCP just like Apathy. Very promising :)
     
  20. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    I know you've always argued that TCP is fine in this day and age, we had some bad experiences with it for mobile multiplayer games. Packets would sometimes be dropped, and everything will halt, stutter and lunge around after 100ms. It was surprisingly infrequent, but would be noticablet about 4-5 times per 2~ min match. So that was just unusable for us. Now with your new library supporting UDP and unreliable transmission, I am pretty excited to try it out. Nice work!
     
    mischa2k likes this.
  21. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Thanks. LiteNetLib works really well, can definitely recommend it.
     
  22. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    2020-07-20_14-02-28.png
    Finished **libuv** Transport. Fully uncorked, allocation free. Performance only limited by what libuv C code can handle.

    For DOTSNET, libuv can handle around 8k monsters sent to 1 connection before the buffers get full.
    Apathy can still handle an order of magnitude more :)
     
    dannyalgorithmic likes this.
  23. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.12 & V1.13 will come at the same time, pending review now:
    2020-07-21_17-42-26.png
     
    dannyalgorithmic likes this.
  24. dannyalgorithmic

    dannyalgorithmic

    Joined:
    Jul 22, 2018
    Posts:
    100
    Did you ever get to android/iOS support, brother?
     
  25. AlexHolderDev

    AlexHolderDev

    Joined:
    Jul 2, 2014
    Posts:
    35
    Through the LiteNetLib transport, yes, this can be compatible with those platforms.

    This means you can't just load up the example scenes & build to the platforms though -- you must replace Apathy server & client transport components with the LiteNetLib equivalent components. Then you can play around with the examples on your target platforms.
     
    dannyalgorithmic likes this.
  26. dannyalgorithmic

    dannyalgorithmic

    Joined:
    Jul 22, 2018
    Posts:
    100
    Thanks, bud!
     
  27. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Like others mentioned, it runs on all platforms except WebGL now thanks to LiteNetLib :)
     
    dannyalgorithmic likes this.
  28. dannyalgorithmic

    dannyalgorithmic

    Joined:
    Jul 22, 2018
    Posts:
    100
    You're cooler than the Tiger Woman, bro.
     
  29. Rom-

    Rom-

    Joined:
    Nov 26, 2008
    Posts:
    90
    Hello,

    I'm wondering if there are any plans to port this asset to Unity 2020.1? If not, is it possible to by done by a user?
     
  30. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    It runs fine in 2020.1, feel free to use that version if you need it.
    I try to stay on Unity LTS version as long as possible, because even LTS isn't very stable.
     
    hopeful likes this.
  31. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.14 pending review:
    2020-07-27_17-36-06.png
     
    florianhanke and AlexHolderDev like this.
  32. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    After reading the documentation (https://docs.google.com/document/d/...BiXyvToW_tO5jYcQg/edit#heading=h.vzk04s7s1zrm), which is very well written, I have two questions:
    1. Using a NetworkTransformAuthoring component, position and rotation are synced. How does one denote which other components and their fields are synced? (After adding a NetworkEntity, are all components and their fields synced?)
    2. How does DOTSNET handle entities with parent-child structures?
    Apologies if there questions are already answered in the docs.
     
  33. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    At the moment you need to create a broadcastsystem for each component that you want synced.
    Currently researching auto sync of all NetworkComponents, but there is no easy way yet. I will update to 2020.1 + latest Entities packages this weekend, so maybe there is news.

    It doesn't. If there is an important use case then I can look into it :)
     
    florianhanke likes this.
  34. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Got it, thank you! Personally, I prefer explicit code to auto sync. (Although not sure what NetworkComponent refers to – a concept or something in code? Can't find it in the docs)

    One of my use cases is an armored car which hs a parent charris and a child turret, which itself has a child weapon.

    As a simple case of this, a weapon can be blown off the turret. Using the BroadcastSystem, I probably could express it very simply as the following.

    With a parent, the weapon is attached, so only sends the rotation.
    Code (CSharp):
    1. using DOTSNET;
    2.  
    3. public class NetworkWeaponChildServerSystem : NetworkBroadcastSystem
    4. {
    5.     protected override void Broadcast()
    6.     {
    7.         // For each networked Child weapon.
    8.         Entities.WithAll<Weapon,Parent>().ForEach((Entity entity,
    9.                             DynamicBuffer<NetworkObserver> observers,
    10.                             ref NetworkEntity networkEntity,
    11.                           in Rotation rotation) =>
    12.         {
    13.             // Send state to each observer connection.
    14.             for (int i = 0; i < observers.Length; ++i)
    15.             {
    16.                 // Create the message.
    17.                 RotationMessage message = new RotationMessage(
    18.                         networkEntity.netId,
    19.                         rotation.Value
    20.                 );
    21.  
    22.                 // Send it.
    23.                 server.Send(message, observers[i]);
    24.             }
    25.         });
    26.     }
    27. }
    Without a parent, the weapon is not attached, so also sends the translation.
    Code (CSharp):
    1. using DOTSNET;
    2.  
    3. public class NetworkParentlessWeaponServerSystem : NetworkBroadcastSystem
    4. {
    5.     protected override void Broadcast()
    6.     {
    7.         // For each networked non-Child weapon.
    8.         Entities.WithAll<Weapon>().WithNone<Parent>().ForEach((Entity entity,
    9.                             DynamicBuffer<NetworkObserver> observers,
    10.                             ref NetworkEntity networkEntity,
    11.                           in Translation translation,
    12.                           in Rotation rotation) =>
    13.         {
    14.             // Send state to each observer connection.
    15.             for (int i = 0; i < observers.Length; ++i)
    16.             {
    17.                 // Create the message.
    18.                 TransformMessage message = new TransformMessage(
    19.                         networkEntity.netId,
    20.                       translation.Value,
    21.                         rotation.Value
    22.                 );
    23.  
    24.                 // Send it.
    25.                 server.Send(message, observers[i]);
    26.             }
    27.         });
    28.     }
    29. }
    For more complex examples, I assume one creates up to two NetworkBroadcastSystem per component, one with, one without Parent per component type (for those components which need different sync behaviours depending on whether they are parented).
     
  35. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    V1.15 pending review: 2020.1 + latest ECS.
    I hope you guys can update your projects without too much trouble. Unity deprecated/broke a few API calls. Won't be the last time either since ECS is still in preview :)

    2020-08-08_14-10-30.png
     
    Wobbers and florianhanke like this.
  36. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    That was the motivation yes. Right now, nothing is hard coded so you can use any sync you want.
    The idea was to have a NetworkComponent interface. Every component that implements it could be synced automatically. It would be pretty useful, but right now there is no way to do this without code gen.

    I see. Opened an issue here: https://github.com/vis2k/DOTSNET/issues/17 . Will need to add a child/parent test/example first and then go from there :)
     
    florianhanke likes this.
  37. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Thanks again – and I hope I expressed myself clearly: I personally prefer having more control and less code generation, and also do not need automatic handling of parent-child hierarchies. The above example was merely a try how I could cover the case in the current version of the framework as I understand it. And nice, getting to 2020.1 :)
     
    mischa2k likes this.
  38. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    @vis2k I've started using DOTSNET today, and I have to say I really like it! Is it ok to provide feedback here or what is the preferred channel?

    Anyway – I programmatically import and configure all network entities, and this is an issue when using
    NetworkTransform
    , since from code, I cannot add a
    NetworkTransformAuthoring
    component since it uses
    [GenerateAuthoringComponent]
    . Would it be ok for you to not generate it, but instead use the generated code in the
    NetworkTransform.cs
    , so it's possible to:
    go.AddComponent<NetworkTransformAuthoring>();
    ?

    Edit: Found the discord. Will repost there :)
     
    Last edited: Aug 11, 2020
    BitPax likes this.
  39. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Thanks. Sure, forum and discord are both ok.

    Could you maybe explain the use case a bit more in detail? You generate GameObjects at runtime and want to add NetworkTransformAuthoring? Or for existing scene objects? etc.
     
  40. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Sure! I generate Prefabs automatically from blender files and metadata files. When I do this, I add all sorts of GO Components to the Prefab. I'd like to add the NetworkTransformAuthoring component as well and set it correctly to SyncServerToClient (can't remember the exact attribute name). As far as I know, I cannot do this with authoring components that are generated via [GenerateAuthoringComponent].

    What I currently do (re networking), and commented out what I would like to do:
    Code (CSharp):
    1.         // Set convert and destroy.
    2.         var convert = go.AddComponent<ConvertToNetworkEntity>();
    3.         convert.targetWorld = TargetWorld.Both;
    4.         convert.ConversionMode = ConvertToNetworkEntity.Mode.ConvertAndDestroy;
    5.         go.AddComponent<NetworkEntityAuthoring>();
    6.         // Generally, we want to sync the transform.
    7.         // var networkTransform = go.AddComponent<NetworkTransformAuthoring>();
    Few people probably have this issue, but I prefer to be able to have my Prefabs be fully regenerated via code, and not having to add NetworkEntityAuthoring manually in the inspector.
    So whenever I notice I need to add a component to a prefab, I just check the DOTS compiler and copy the code.

    In this case:
    Screenshot 2020-08-11 at 23.11.11.png
    So, I know it's probably a rare case and I'd completely understand if you'd prefer using NetworkTransformAuthoring, but you could use the generated code in the assumption that it won't change much anymore. If not, my best bet is probably to copy the NetworkTransform, message and system.
    Thanks for reading this! :)

    Edit: After writing this, I noticed the real issue is that NetworkTransformAuthoring is not visible to my code (https://forum.unity.com/threads/wha...component-generates-an-internal-class.949950/).
     
    Last edited: Aug 11, 2020
  41. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Sure. NetworkTransformAuthoring will be in next version. Pending review now, should be out in 24 hours or so.
     
    Last edited: Aug 12, 2020
    AlexHolderDev and florianhanke like this.
  42. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Awesome, thank you! :)
     
    mischa2k likes this.
  43. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    @vis2k A small feedback re NetworkServerSystem#StartServer:

    I automatically start the server for testing purposes, and sometimes, the transport layer is not ready yet and throws an error.

    In that case, these two lines:
    Code (CSharp):
    1. state = ServerState.ACTIVE;
    2. transport.Start();
    already set the state to ACTIVE, even though the next line fails, which on subsequent calls results in an early return. I suggest to switch the two lines above:
    Code (CSharp):
    1. transport.Start();
    2. state = ServerState.ACTIVE;
    After that, a failed
    server.StartServer()
    call can be retried after the transport layer is initialized.
     
    Last edited: Aug 16, 2020
    AlexHolderDev and mischa2k like this.
  44. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Yep, seems like an obvious race condition.
    Nice find, thanks. That'll prevent a lot of bugs. Will fix in next version :)
     
    AlexHolderDev and florianhanke like this.
  45. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Nice, thanks!
     
    mischa2k likes this.
  46. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    A small feedback on the docs:

    When discussing BroadcastSystems (https://docs.google.com/document/d/...BiXyvToW_tO5jYcQg/edit#heading=h.7pqjy85hg90q), I think it would be useful to make a one-sentence reference to the client end. I think it would be good to at least mention the necessary client side counterpart for "to broadcast an entity’s state from the server to all observers in an interval" at the end of the section.
    For example "On the client side, apply the entity's server state via a NetworkClientMessageSystem. See TransformClientMessageSystem as an example on how we do the counterpart for NetworkTransformServerSystem."

    It's obvious that a client side counterpart to the broadcast system is needed, but it still stumped me for a while since I somehow expected it to be handled magically because the client side is not mentioned in the docs. But I think a sentence or two as in my example above would be completely sufficient :)
     
  47. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Another feedback: scene prefabs behave differently than regular prefabs regarding parent-child hierarchies.

    The case I have is that sometimes I'd like to test a very specific constellation of prefabs that I hand-place and rotate etc. in the scene, to debug. To avoid issues in the PrefabSystem, I remove the prefabs from the list of prefabs in the PrefabSystemAuthoring because it's not supported.

    In the
    NetworkClientSystem
    , calling
    EntityManager.Instantiate(prefab)
    will only instantiate the root entity. However, my prefabs have a hierarchy, so the root and all its children will have to be instantiated.
    EntityManager.Instantiate(prefab)
    does instantiate the whole hierarchy if a
    LinkedEntityGroup
    on the root contains all its children (see for example https://gametorrahod.com/game-object-conversion-and-subscene/).

    This works already for regular prefabs, since in
    PrefabSystem#ConvertGameObjectPrefabs
    ,
    GameObjectConversionUtility.ConvertGameObjectHierarchy
    is used, which already generates the
    LinkedEntityGroup
    .

    To solve this, on
    NetworkEntityAuthoring
    I added
    Code (CSharp):
    1. // Add a LinkedEntityGroup.
    2. conversionSystem.DeclareLinkedEntityGroup(transform.gameObject);
    at the end of the
    Convert
    method.

    Now the
    NetworkClientSystem
    instantiates the whole hierarchy :)
     
  48. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    This one is more of a wish/idea.

    The dragging regular prefabs into the scene is not supported at the moment – but I find myself often doing that in development mode to test certain configurations of prefabs etc.

    I understand that the issue is that "the one in the scene might have modifications", but I am wondering if the process for regular and scene prefabs could be more unified, and if the prefabId could be generated from hashes from all its components and all its children's components, therefore making the id generating processes for regular and scene prefabs the same.

    This would then allow for regular and scene prefabs be identical, and if so, allow the same prefabs (or all components etc. configured the same) in parallel.
     
  49. mischa2k

    mischa2k

    Joined:
    Sep 4, 2015
    Posts:
    4,347
    Thanks for feedback. Posted it on issue tracker so we don't forget: https://github.com/vis2k/DOTSNET/issues/26
    I usually go through one issue after another so replies might take a while until I get to it :)
     
    florianhanke likes this.
  50. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    Thanks! I have no expectations of anything being solved immediately btw :)
     
    mischa2k likes this.