Home > Software engineering >  C Allow Program to Accept Arbitrary Mods
C Allow Program to Accept Arbitrary Mods

Time:08-17

I am working on a game that I want to have very good mod support. The game is programmed in C and will be completely open source. I would like to be able to have users add and remove mods easily. The game will be open source so modders will know how the code looks, but I would like for modders to be able to override functions in the compiled version of the code and to be able to add new functions. I would not like to create an explicit API or use another language for mods as those are necessarily restrictive on what can be done with them. The ideal situation is for the user to download a mod and put it in a mod folder, run the game once to load the mods, and then close it and run it again to load the game with the mods included. How could this be done?

An example would be, that if I had the program:

#include <iostream>

char const* getName();

int main() {
    std::cout << getName() << std::endl;
}

char const* getName()
{
    return "Robert";
}

I would be able to write a mod that has

char const* getName()
{
    return "Steve";
}

in the same namespace, so that the result of the original code would be "Steve".

CodePudding user response:

I'm going to assume by "run the game once to load the mods" you mean "have the user go into a menu and indicate the location of a dynamic library".

Rather than a function getName, you'd have a function pointer getName, and some code to set that to some function loaded from the dynamic library.

As a sketch:

struct Library {
    template<typename Signature>
    Signature getSymbol(std::string name) // get the function named name from this dynamic library
}

std::vector<Library> getMods(); // read from some configuration all the mod file paths, and load them into Library objects

const char* defaultGetName()
{
    return "Robert";
}

using Namer = const char * (*)();

Namer getName = defaultGetName;

int main()
{
    for(auto & lib : getMods()) {
        if (auto namer = lib.getSymbol<Namer>("getName")) {
            getName = namer;
        } // etc for other "overridable" functions
    }
    std::cout << getName() << std::endl;
}

The Library parts are going to be platform-specific. Unix has dlopen / dlsym, Windows has LoadLibrary(Ex) / GetProcAddress.

CodePudding user response:

If you put the whole game (except for the part which loads mods) into a class, and make every function be virtual, the mod can have a derived class which replaces some of the functions. Then you have written a list of replaceable functions, as they are all inside the class and marked virtual.

You can only load one mod at a time this way. Adding new functions or deleting some functions will break all mods - mods will probably only work on the exact game version they were written for.

You'll probably find that some functions don't make sense to be moddable (e.g. calculate the minimum of two numbers) and some functions don't belong in one big class but should be in other classes instead (e.g. Building class if your game has buildings).

  •  Tags:  
  • c
  • Related