There was previous effort into a direct transpiling from Golang to C# for the shared dynamics part of DelayNoMore v1.0.13.
It was experimented using go2cs, and is halted due to the untolerable buggy handling of the following issues by go2cs
.
- When a
Go struct
has a pointer field, e.g. https://github.com/genxium/DelayNoMore/blob/v1.0.13/resolv_tailored/collision.go#L13 - By the time of testing, its own go-src-converted is not fully compilable by
.NET 6.0/7.0 SDK
. - By the time of testing, its own golib of gocore is compilable into a
golib.lib/dll
file, but when referenced by a transpiledresolv
C# project (transpiled by the compiledgo2cs
binary in same codebase as thegolib
), many types are not found or not correctly formatted.
There're still 2 planned routes for moving on when go2cs
becomes more usable.
1. Golang -(compile directly)-> game_dynamic_export.lib/dll: very possibly won't work even on Windows, because this lib relies on Golang's "garbage collector" to work for heap management, and even if it's bundled in the lib it'd be very inefficient to run together with .NET runtime
2. Golang -(go2cs)-> C# v10 source code -(compile by .NET 6.0 SDK but targets .NET 4.0 runtime)-> game_dynamic_export.lib/dll: possibly can work in Unity runtime because this lib knows how to cooperate with .NET runtime garbage collector
By the time of starting this project, I'm much more familiar with C++ than C#, hence this is really a considered option but there're a few serious reasons that stopped it from happening.
- Unreal Engine IDE is a resource monster, my laptop just couldn't run it smoothly. Unfortunately this is the major reason T_T
- Transpiling Golang to C++ is non-trivial and the difficulties are very similar to that of
Golang to C#
- What's more, as there's no default
auto garbage collection
mechanism for C++, whenever the transpiled version of any heap RAM allocating method in DelayNoMore/jsexport/main.go is invoked, the allocated instance must be- either manually deallocated later, e.g. what's already done for DelayNoMore frontend UDP session management, or
- bound to framework specific gc system, e.g. UObject of UE4 on frontend & Boost Smart Pointer on backend
In short, it's all feasible but too expensive for me.
Now that I've decided to rewrite the whole project in C#
(i.e. both backend & frontend) to favor the use of Unity3D on frontend, there're some improvements planned to be made as using a single language removes many inconveniences previously encountered by Golang backend + JavaScript/C++ frontend
.
- On backend, deprecate the use of Redis for just keeping captchas like BuildingAndCraftingAndTowerDefenseGame.
- On backend, .NET framework supports WebSocket.SendAsync which can be the ideal way of implementing Room.downsyncToAllPlayers. Kindly note that not all .NET versions support the same set of APIs, on backend I'd use
.NET 7.0
which is not supported by Unity yet. - For the shared dynamics, per the garbage collection concern above for C++, I should eliminate the lazy init of
RoomDownsyncFrame
inbattle.ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame
to make it completely free from any heap RAM allocation. - For the shared dynamics, per the garbage collection concern above for C++, I should rewrite the init & return of
overlapResult
inbattle.calcPushbacks
using "output pointer in function parameter" style to make it completely free from any heap RAM allocation. AsoverlapResult
is currently returned bybattle.calcPushbacks
, there's definitely a heap RAM allocation according to Golang's doc -- and any C# compiler possibly follows the same rule. - For the shared dynamics, there could be a chance to eliminate the duplication of serialization models and dynamics models, i.e. by generating only 1 set of models from protobuf files.
However, there're also challenges to keep in mind even if both backend & frontend are using C#
.
- Unity supports only a subset of C# 9.0 w/ Roslyn compiler as of version 2021.3
- Unity runs in .NET 4.x equivalent runtime as of version 2019.1, but the runtime .NET equivalence is not explicitly specified as of version 2021.3 -- and .NET 4.x would be a safe assumption.
Language level and runtime version are relatively low compared to what's available for the backend, thus I'd start writing the shared dynamics on frontend for better compatibilty as well as visual testability.
I'm aware of the existence of some "claimed to be rollback friendly collision detection libraries", such as VolatilePhysics and even a few discussions of use cases of Godot Rollback Netcode addon -- yet still decide to stick with my own implementation.
As described previously, heap RAM allocation is to be avoided as much as possible, and my resolv_tailored has already been written carefully to yield 0 heap RAM allocation during active battle. This is non-trivial and many tricks are only available in 2D scenario where the whole battle field is dividable to only hundreds of cells such that a full traversal of cells is more efficient than the generic Broad Phase Collision Detection approach.
For why heap RAM allocation is a performance concern to me, please refer to this note for what's actually going on upon heap RAM allocation/deallocation.
Of all the Broad Phase Collision Detection implementations I've encountered, heap RAM allocation/deallocation is inevitable, e.g. that of BulletPhysics/bullet3 node creation and removal in C++ -- as discussed above there's no default auto garbage collection
mechanism for C++, yet the good part of BulletPhysics
is that it allows the binding to framework specific gc system.
The current gameplay of DelayNoMore
doesn't quite require a generic Broad Phase Collision Detection
. As shown in ApplyInputFrameDownsyncDynamicsOnSingleRenderFrame at each render frame calculation only collisions of specified groups are concerned, i.e. Character
, Bullet
-- where all instances in these groups should be traversed just for moving by velocity anyway -- thus adding collision detection for each instance on the way doesn't increase the order of total time complexity. If the generic Broad Phase Collision Detection
is used here, it might either traverse all spatially separated cells or all non-moving Barrier
s which is not necessary.
That said, I'm still open to the use of Broad Phase Collision Detection in rollback because it allows a much larger battle field, i.e. which couldn't be divided to just hundreds of fixed cells where each cell is about the size of the largest movable object -- and with the help of modern gc system, framerate might not be impacted badly.