HN user

iliketrains

1,289 karma
Posts4
Comments79
View on HN

Author here, thanks for your comment. I am glad to hear that things are progressing!

Can you share how large is the team responsible for .NET Modernization?

migrating legacy native C++ code to C# backed by CoreCLR.

Yes please! Surely things like Quaternion.Lerp don't have to be C++ code under CoreCLR.

Feel free to get in touch in case I could be of any help :)

Re serialization: We have custom binary serialization that essentially dumps the game state into a binary stream. No allocations, no copies, no conversions. Our saves can be big, >100 MB uncompressed, so there is no room for waste.

The big advantage is that it reads data directly from game's classes, so there is no boilerplate needed, no prorobufs, no schema. And it supports versioning, adding or removing members mostly without limitations.

I think it's a cool system, maybe I should write a blog post about it :)

Sure, we could use Burst to speed up some strategic parts, but that would not help with the core of the game.

To give some context, things are very complex in our game, we have fully dynamic terrain with terrain physics (land-slides), advanced path-finding of hundreds of vehicles (each entity has its own width and height clearance), trains, conveyors and pipes carrying tens or even hundreds of thousands of individual products, machines, rockets, ships, automated logistics, etc. There is no one thing that could be bursted to get 3x gain. At this point, we'd have to rewrite the entire game in C++.

So what's the reason we use C#? Productivity, ease of debugging and testing, and resilience to bugs (e.g. null dereference won't kill the program). Messing with C++ or even burst would cost us more time and to be honest, the game would possibly not even exist at that point.

Could you share some details about your custom thread pool that got 3x speedup? What was the speedup from? It is highly unlikely that a custom thread pool would have any significant impact on the benchmark in our case. As you can see from Figure 3, threaded tasks run for about 25% of the total time and even with Mono, all tasks are reasonably well balanced between threads. Threads utilization is surely over 90% (there is always slight inefficiency towards the end as threads are finishing up, but that's 100's of ms). An "oracle" thread pool could speed tings up by 10% of 25%, so that is not it.

Vectorization could help too but majority of the code is not easily vectorizable. It's all kinds of workloads, loading data, deserialization, initialization of entities, map generation, precomputation of various things. I highly doubt that automatic vectorization from code generated by IL2CPP would bring more than 20% speedup here. The speedup from burst would mostly come from elimination of inefficient code generated by Mono's JIT, not from vectorization.

For now, we are accepting the Mono tax to be more productive. But I am hoping that Unity will deliver on the CoreCLR dream. In the meantime, my post was meant raise awareness and stir up some discussion, like this one, which is great. I've read lots of interesting thoughts in this comments section.

Going to completely disagree that Burst and HPC# are unnecessary and messy.

Making a managed code burst-compatible comes with real constraints that go beyond "write performant C#". In Burstable code, you generally can't interact with managed objects/GC-dependent APIs, so the design is pushed towards unmanaged structs in native collections. And this design spreads. The more logic is to be covered by Burst, the more things has to be broken down to native containers of unmanaged structs.

I agree that designing things in data-oriented way is good, but why to force this additional boundary and special types on devs instead of just letting them write it in C#? Writing burstable code can increase complexity, one has to manage memory/lifetimes, data layout, and job-friendly boundaries, copying data between native and managed collections, etc., not just "writing fast C#".

In a complex simulation game, my experience is that there are definitely things that fit the "raw data, batch processing" model, but not all gameplay/simulation logic does. Things like inheritance, events, graphs, AI (the dumb "game" version, no NN), UI, exceptions, etc. And on top of it all, debugging complications.

Wouldn't you be relieved with announcement: "C# is now as fast as Burst, have fun!"? You'd be able to do the same data-oriented design where necessary, but keep all the other tings handy standing by when needed. It's so close, yet, so far!

The fragmentation you mention

What you say makes sense. I've actually spent a lot of time debugging this and I did find some "leaks" where references to "dead objects" were keeping them from being GC'd. But after sorting all these out, Unity's memory profiler was showing that "Empty Heap Space" was the culprit, that one kept increasing after every iteration. My running theory is that the heap is just more and more fragmented, and some static objects randomly scattered around it are keeping it from being shrunk. ¯\_(ツ)_/¯

You can also install new DLLs and force the player to restart if you want downloadable mod support.

I am not aware of an easy way to load (managed) mods as DLLs to IL2CPP-compiled game. I am thinking about `Assembly.LoadFrom("Mod.dll")`.

Can you elaborate how this is done?

there are better, more performant alternatives for that use case, in my experience.

We actually use reflection to emit optimal code for generic serializers that avoid boxing and increase performance.

There may be alternatives, we explored things like FlatBuffers and their variants, but nothing came close to our system in terms of ease of use, versioning support, and performance.

If you have some suggestions, I'd be interested to see what options are out there for C#.

FieldOffset is supported by IL2CPP at compile time

You are right, I miss-remembered this one, you cannot get it via reflection, but it works.

Author here, thanks for your perspective. Here some thoughts:

approach of separating the simulation and presentation layers isn't all that uncommon

I agree that some level of separation is is not that uncommon, but games usually depend on things from their respective engine, especially on things like datatypes (e.g. Vector3) or math libraries. The reason I mention that our game is unique in this way is that its non-rendering code does not depend on any Unity types or DLLs. And I think that is quite uncommon, especially for a game made in Unity.

Most games don't ship on the mono backend, but instead on il2cpp

I think this really depends. If we take absolute numbers, roughly 20% of Unity games on Steam use IL2CPP [1]. Of course many simple games won't be using it so the sample is skewed is we want to measure "how many players play games with IL2CPP tech". But there are still many and higher perf of managed code would certainly have an impact.

We don't use IL2CPP because we use many features that are not compatible with it. For example DLC and mods loading at runtime via DLLs, reflection for custom serialization, things like [FieldOffset] for efficient struct packing and for GPU communication, etc.

Also, having managed code makes the game "hackabe". Some modders use IL injection to be able to hook to places where our APIs don't allow. This is good and bad, but so far this allowed modders to progress faster than we expected so it's a net positive.

In modern Unity, if you want to achieve performance, you'd be better off taking the approach of utilizing the burst compiler and HPC#

Yeah, and I really wish we would not need to do that. Burst and HPC# are messy and add a lot of unnecessary complexity and artificial limitations.

The thing is, if Mono and .NET were both equally "slow", then sure, let's do some HPC# tricks to get high performance, but it is not! Modern .NET is fast, but Unity devs cannot take advantage of it, which is frustrating.

By the way, the final trace with parallel workers was just C#'s workers threads and thread pool.

Profiling the editor is always a fools errand

Maybe, but we (devs) spend 99% of our time in the editor. And perf gains from editor usually translate to the Release build with very similar percentage gains (I know this is generally not true, but in my experience it is). We have done many significant optimizations before and measurements from the editor were always useful indicator.

What is not very useful is Unity's profiler, especially with "deep profile" enabled. It adds constant cost per method, highly exaggerating cost of small methods. So we have our own tracing system that does not do this.

I've seen a lot of mention around GC through this comment section, and professional Unity projects tend to go out of their way to minimize these at runtime

Yes, minimizing allocations is key, but there are many cases where they are hard to avoid. Things like strings processing for UI generates a lot of garbage every frame. And there are APIs that simply don't have an allocation-free options. CoreCLR would allow to further cut down on allocations and have better APIs available.

Just the fact that the current GC is non-moving means that the memory consumption goes up over time due to fragmentation. We have had numerous reports of "memory" leaks where players report that after periodic load/quit-to-menu loops, memory consumption goes up over time.

Even if we got fast CoreCLR C# code execution, these issues would prevail, so improved CG would be the next on the list.

[1] https://steamdb.info/stats/releases/?tech=SDK.UnityIL2CPP

From my experience, performance gains seen in Debug builds in Unity/C#/Mono nearly always translate in gains in Release mode. I know that this is not always true, but in this context that's my experience.

Setting up release benchmarks is much more complex and we develop the game in Debug mode, so it is very natural to get the first results there, and if promising, validate them in Release.

Also, since our team works in Debug mode, even gains that only speed things up in Debug mode are valuable for us, but I haven't encountered a case where I would see 20%+ perf gain in Debug mode that would not translate to Release mode.

MaFi Games | Senior ASP.NET (full-stack) | Contract | Remote

Hi, I'm the co-founder of MaFi Games, the indie studio behind Captain of Industry. We're looking for an experienced full-stack ASP.NET engineer to help us grow our community website, including features like a modding database, blog, and forum.

Some reasons you'd enjoy working with us:

- A multicultural, collaborative, and innovative work environment where your voice is heard.

- Fully remote job with flexible working hours and vacation schedule.

- High quality C# code base, code reviews, tests.

- High work satisfaction, work with a talented team on a popular video game with a wonderful community.

If interested, please see the detailed info, requirements, and application instructions at https://www.captain-of-industry.com/jobs-swe-hn, thanks!

Note: We are also looking for Senior Game devs, 2D artists, and SFX artists.

MaFi Games | Senior SWE/Game dev | Contract preferred | Remote | C#

Hi, I’m the co-founder of MaFi Games – an indie studio behind the game Captain of Industry. We are a small but passionate team who gave up their jobs at Google and Nvidia to pursue building the best factory simulation game possible, and we need more hands!

We are looking for an experienced software engineer to grow the team and accelerate our progress. We strongly prefer candidates with a background in game development, experience with 3D graphics, and performance optimizations.

Some reasons you’d enjoy working with us:

* A multicultural, collaborative, and innovative work environment where your voice is heard.

* Fully remote job with flexible working hours and vacation schedule.

* Long-term full-time collaboration (not a fixed-term contract).

* High quality C# code base, code reviews, tests.

* High work satisfaction, work on a popular video game with a wonderful community.

* No bureaucracy, 1 regular meeting per week.

As an example of our technical work see https://www.captain-of-industry.com/post/cd-31. We also have a unique signal-free train system: https://www.captain-of-industry.com/post/cd-46

If interested, please see the detailed info,requirements, and application instructions at https://www.captain-of-industry.com/jobs, thanks!

Note: We are also looking for a 2D game UI/UX designer and a SFX artist.

A new PNG spec 1 year ago

Official support for animations, yes! This feels so nostalgic to me, I have written an L-system generator with support for exporting animated PNGs 11 years ago! They were working only in Firefox, and Chrome used to have an extension for them. Too bad I had to take the website down.

Back then, there were no libraries in C# for it, but it's actually quite easy to make APNG from PNGs directly by writing chunks with correct headers, no encoders needed (assuming PNGs are already encoded as input).

https://github.com/NightElfik/Malsys/blob/master/src/Malsys....

https://marekfiser.com/projects/malsys-mareks-lsystems/

I see, integrating image inputs can be very challenging in this case as the models work with text input. I was not even thinking about the full isometric image, but just some simple 2D map where each pixel can be color-coded based on the entity type. I guess the problem is that these maps would look like nothing the models were trained on, so as you say, it might not provide any value.

The reason I was suggesting this is that I worked in robotics making RL policies, and supplying image data (be it maps, lidar scans, etc.) was a common practice. But our networks were custom made to ingest these data and trained from scratch, which is quite different from this approach.

This is awesome! I like the idea of abstracting the factory building with a code-like structure. I wonder if supplemental 2D image (mini-map style) as an input to the policy would help with the spatial reasoning?

I work on a similar factory game (Captain of Industry) and I have always wanted an agent that can play the game for testing and balancing reasons. However, pixels-to-mouse-actions RL policy (similar to Deep Mind's StarCraft agent) always seemed like a very hard and inefficient approach. Using code-like API seems so much better! I might try to find some time to port this framework to COI :) Thanks for sharing!

MaFi Games | Senior SWE/game dev | Contract or full-time | $70-110k | Remote | C#

EDIT: This posting is no longer active, thanks for all the applicants for applying!

I’m the co-founder of MaFi Games – an indie studio behind the game Captain of Industry. We are a small but passionate team who gave up their jobs at Google/Nvidia to pursue building the best factory simulation game possible, and we need more hands!

We are looking for an experienced software engineer to grow the team and accelerate our progress. We strongly prefer candidates with a background in game development or with experience in desktop UI, 3D graphics, and performance optimizations.

Some reasons you’d enjoy working with us:

* A multicultural, collaborative, and innovative work environment where your voice is heard.

* Fully remote job with flexible working hours and vacation schedule.

* High quality C# code base, code reviews, tests.

* High work satisfaction, work on a popular video game with a wonderful community.

As an example of our technical work see https://www.captain-of-industry.com/post/cd-31.

If interested, please see the detailed info and requirements at https://www.captain-of-industry.com/jobs, thanks!

Note that this is a fully remote job and we are happy to consider candidates from any country around the world!

MaFi Games | Senior SWE/game dev | Contract or full-time | $70-110k | Remote (World) | C#

EDIT: This posting is no longer active, thanks for all the applicants for applying!

I’m the co-founder of MaFi Games – an indie studio behind the game Captain of Industry. We are a small but passionate team who gave up their jobs at Google/Nvidia to pursue building the best factory simulation game possible, and we need more hands!

We are looking for an experienced software engineer or game developer to grow the team and accelerate our progress. We are also looking for part-time UI/UX designers.

Some reasons you’d enjoy working with us:

* A multicultural, collaborative, and innovative work environment where your voice is heard.

* Fully remote job with flexible working hours and vacation schedule.

* High quality C# code base, code reviews, tests.

* High work satisfaction, work on a popular video game with a wonderful community.

* No bureaucracy, no politics, no perf, 1 regular meeting per week.

If interested, please see the detailed info and requirements at https://www.captain-of-industry.com/jobs

Note that this is a fully remote job and we are happy to consider candidates from any country around the world!

This is not about "unwilling to pay for quality tools", but completely changing the way they charge for the tools, which gets applied to all legacy software that ever used their tools, despite their previous (now deleted) clauses that new TOS won't apply unless you use the new version, is just ridiculous to me.

Even if one stopped using Unity to develop new things before this change, they are still on the hook for product installs (even if they are free games), which are by the way tracked by Unity "proprietary data model".

Example: https://www.reddit.com/r/Unity3D/comments/16hgmqm/unity_want...

A few quotes from the FAQ:

Q: If a user reinstalls/redownloads a game / changes their hardware, will that count as multiple installs?

A: Yes. The creator will need to pay for all future installs. The reason is that Unity doesn’t receive end-player information, just aggregate data.

Q: Are these fees going to apply to games which have been out for years already? If you met the threshold 2 years ago, you'll start owing for any installs monthly from January, no? (in theory). It says they'll use previous installs to determine threshold eligibility & then you'll start owing them for the new ones.

A: Yes, assuming the game is eligible and distributing the Unity Runtime then runtime fees will apply. We look at a game's lifetime installs to determine eligibility for the runtime fee. Then we bill the runtime fee based on all new installs that occur after January 1, 2024.

I do the same as KMnO4 and I think I have never received an email to a random address that I haven't shared before. Currently the biggest volume of spam is coming to adobe@ and github@ (along with some dating sites).

Actually, I did receive one, from my friend who typed the entire message in the name before the @ and left the body and subject empty :D

Game dev here. The reason is simple - performance. Unlike Java, C# has zero-overhead structs (custom primitive types if you will), so say a Vector3f is just 3 floats in memory, 12 bytes, no object overhead, no pointer indirection. This is a BIG deal. Array of vectors can be just raw data in C#, not an array of pointers like in Java. You can fairly easily map C# structs to C/C++ structs that GPU drivers need. C# even allows fixing struct fields to certain offsets to ensure identical memory layout with other languages.

Other features are also critical to performance such as true generics. Say List<Vector3f> in C# is specialized for that type, no overhead from objects, no extra pointers, no casting.

In C#, if you are very careful, you can get close to the performance of C++. I don't think the same can be said about Java, because the language does not give you the tools to achieve that.

Yes! Nuclear reactor needs enriched fuel and produces heat. You connect water and it will turn it to steam that you can pipe to do what you want. Usually you want to use it in turbines for electricity production, but you can also use it for desalination, or in oil refinery.

If you don't supply water to the operational reactor, it overheats, and you know what that means... Just kidding, no explosions (yet), it just gets damaged and you loose all loaded fuel.

Thanks! I'd say that the most important "resource" is dedication. Anything else is just matter of doing it until it works. We've been chipping away for so many years without an end in sight and if just one of us gave up, that would be probably the end of it.

Of course my degree in computer science and computer graphics helps, but with dedication and you can just search your way through most problems.

I'd also recommend to limit the scope as much as possible. We might have been a little too ambitious. Don't design features that you cant make. For example, in one iteration we had a vision of global economy where you can buy/sell materials, competing with AI players. On paper, that's great. When we started coding, we had no idea how to make this all work, and after a few months of failed attempts we completely scratched it.

Multiplayer is a big topic and it is obviously on our minds. The big questions is what type of multiplayer would be the best for this kind of game?

Would it be coop where each player would start on their corner of the map? Or would it be shared factory? Of maybe each player with its own island + trading?

Also, it is technically very challenging, since the entire simulation must be perfectly deterministic. We actually have most of the things deterministic, maybe like 95%, fixing the last 5% is a lot of work.