Process huge amount of data with dynamic code

108 views Asked by At

I want to be able to change the code of my game dynamicly. For example lets say my game is structured this way:

class GameState {
    public int SomeData;
    public Entity[] EntityPool;
}

interface IServices {
    IRenderer Renderer { get; }
}

interface IGameCode {
    void RenderAndUpdate(GameState currentState, IServices serviceProvider);
}

I now want be able to write code like this:

 void MainLoop() {
    IGameCode gameCode = new DefaultGameCode();
    while(true) {
       // Handle Plattform things
       if(shouldUseNewGameCode) {
          UnloadCode(gameCode);

          gameCode = LoadCode("new.dll");
          // or
          gameCode = LoadCode("new.cs");
       }
       // Call GameTick
       gameCode.RenderAndUpdate(gameState, services);
    }
 }

I already used AppDomains and a Proxyclass but it is too slow to serialize every frame. I tried to just pass a pointer but since AppDomains use their own virtual address space i cant access the GameState Object. My other idea was to use Reflection to get the IL from the compiled method via GetMethodBody() and pass it to an DynamicMethod but this would limit the way how I could write the RenderAndUpdate method since I can not use Submethods or Variables in the IGameCode implementation.

So how can I achive what I want to do?

1

There are 1 answers

0
Peter Duniho On

As you've seen, you really don't want to be crossing AppDomain boundaries on every frame, especially if that code has to call back to the main code e.g. IServices a bunch of times. Even with MarshalByRefObject, which can improve things a little, it's going to be too slow. So you need a solution that doesn't involve the AppDomain.

How big is your assembly? How often do you expect to change it?

Noting that .NET assemblies are generally fairly space-efficient, and that in your scenario it seems unlikely a user would switch assemblies more than a few times in a session, I would just read your DLL into memory as a byte[] and then use Assembly.Load(byte[]); to load it.

Alternatively, if you really can't tolerate a dead assembly in your process memory space, I think it would be better to use a helper process, aka "launcher": when you want to switch implementations, start up the helper process (or just leave it running all the time if you want), which in turn will wait for the current game process to exit, and then will start a new one with the new settings.

This will be slower to switch, but of course is a one-time cost for each switch and then the program can run full-speed during the actual gameplay.