25 Mar, 2011, RoFAdmin wrote in the 1st comment:
Votes: 0
Hey all-

So has anyone done any work relating to loading modules of code dynamically at run-time?

Ive been looking into this a little bit and came across information on dlopen, dlsym, and dlclose.
This appears to be the answer to my questions. I was wondering though if anyone has previously (and im sure some have)
have worked with loading code in this way.

Does it seem like a viable option?
Any disadvantages of this?
Thoughts, ideas, theories?

Is there any codebases out there that do this already?

Discuss
25 Mar, 2011, David Haley wrote in the 2nd comment:
Votes: 0
This is hardly a new concept for any number of projects. Several codebases (SmaugFUSS, if memory serves??) have used this. If you do a search on nearly any MUD forum for dlopen, you'll run into threads asking for help…

IMHO it's pointless if you're doing it at runtime without some kind of (potentially simple) module framework involving discovery. The point of dynamic loading is to be able to not know ahead of time what exactly you're loading. (Exception: there are cases where you defer loading for performance or some other unusual reason.)

For example, you might know that a command has a name, and takes two parameters: the command arguments and the person executing the command. But you might not know what commands will be made available to players; you might want that decision to be determined by configuration files. Or, you might want commands to be addable at runtime, so that you can throw in a dynamic library without rebooting the MUD.

Or, you might want to separate your game engine from game logic, and load up the latter as a dynamic library once at runtime.

You're asking a pretty huge question. :smile:
25 Mar, 2011, Nich wrote in the 3rd comment:
Votes: 0
I think one of the major benefits would be to compile parts of your code separately, and update at runtime without (fully) shutting the server down. Like, you could have your skill implementations in skills .dll, and add new ones, or change old ones, much more easily then you could otherwise.

I wouldn't implement it unless you can think of a really good reason for it. It seems like a complicating factor. Personally, I'm using a scripting language for commands/skills/objects/mobs to achieve the same thing.
25 Mar, 2011, Kline wrote in the 4th comment:
Votes: 0
I've done this to a good portion of one of my games mostly as a learning exercise in how it works. It has advantages, and disadvantages. Did I do this in the best way possible? Probably not; I was fairly new to coding and very new to tinkering with shared object files. It still works appreciably well though, and does let me update certain features without a Diku/Merc derived reboot; just an in-memory reload of the specified module. It's also only compiling a single file to do that, so it saves some time. There are still certain times when I do have to reboot the main game, though, as I have to add hooks into the shared object files in some static places, like the command table. Better ways to do this? Probably; just never wanted to invest the time for it, though :)

I released the codebase utilizing it in the past; but I don't think it's here @ MB. Let me go dig for it and I'll upload it for you to look at.
25 Mar, 2011, Vigud wrote in the 5th comment:
Votes: 0
From a C programmer's perspective, there are always at least three good reasons:

1. Maintainability - separated parts can be maintained by different, specialized teams. And you can reload a module that has changed instead of restarting the whole application,
2. Stability - errors in one module are not supposed to harm the rest of the process,
3. Flexibility - modules can be written in other languages, which is a HUGE advantage in the world of MUDs, where everybody seems to have their own favorite language and C is considered toilsome.
25 Mar, 2011, Kline wrote in the 6th comment:
Votes: 0
Once it is approved, here's the game I uploaded that utilizes this: [link=file]2808[/link]

Let me know if you need help sorting through any of it. It is code from 2003 and earlier :).
25 Mar, 2011, David Haley wrote in the 7th comment:
Votes: 0
Quote
1. Maintainability - separated parts can be maintained by different, specialized teams. And you can reload a module that has changed instead of restarting the whole application,
2. Stability - errors in one module are not supposed to harm the rest of the process,
3. Flexibility - modules can be written in other languages, which is a HUGE advantage in the world of MUDs, where everybody seems to have their own favorite language and C is considered toilsome.

These seem to be advantages in principle but rather difficult to get to in practice.

1. The real advantage here seems to be the reloading. Good code is already separated and maintaned (or maintainable) by separate teams.
2. Well, in theory. :wink:
3. This is true, although if you want to use scripting languages, you'll need a lot more work than just a dynamic module. (E.g., an API for the scripting language to get data from the host application.)


An elaboration of the point I made about separating engine and logic: you can distribute part of your code already compiled, and then let people compile their own game logic. This lets you keep your proprietary/secret/whatever code in binary format, while letting people mod your game with the game logic dynamic library source. (The Quake series of games did this, for example.)
25 Mar, 2011, RoFAdmin wrote in the 8th comment:
Votes: 0
Hey all-

Thanks for the input, i had a feeling i was on the right track.

@kline: i will certainly have to check out your upload.
@david: ill have to check out smaugfuss as well.

And here is how my code is setup, incase anyone has any ideas.

Pretty much i did like you were saying david and ive seperated the engine from the logic.
Pretty much what each module contains is a bunch of pre-defined functions that i will obviously already
now the name to with parameters i already know they accept.

These functions relate to certain events in the game engine.
Character connecting, disconnecting, state changes, incoming command to be interpreted, etc

once you have a module loaded up you call mod_name_register() and what it does is
calls back into the server, and registers with the server the list of events it wants to listen for.

Then when one of the events happens, the server calls modules_broadcast_event
and looks for modules registered for that event, that fit the criteria for the situation (connection state, sub-state, etc)
and then send the message off to the module via the proper call.

So say it was just a simple command i entered, and the mod_example module covered the command i entered.
modules_broadcast_event would end up calling mod_example_interpreter.


So that is my module system in a very very basic nutshell.


Right now im simpy using it to handle doing main game logic. For instance my olc system was designed using this modular system i setup.
Later i was thinking i might use it for doing "scripts" for all my rooms,objects, and npcs. Pretty much could make an ingame editor for the file. Once your done editing, have it tell the system to recompile the file, close the current loaded version and then load the new version, all without ever shutting down or rebooting the server. Sounds great to me!

Any other ideas, questions, comments? Would love to hear how other people have done such implimentations.


EDIT:
@david: Yeah that is one of my main ideas here is that i could setup a system where people wouldnt have to have the core source code in order to extend the game. They would just load and unload all sorts of modules for the game. I even have it setup so that modules can add a whole new list of variables to be stored, saved, and loaded with my rooms,objects, characters, etc etc.
25 Mar, 2011, RoFAdmin wrote in the 9th comment:
Votes: 0
Heya-


So i have to thank you again Kline for posting up your source there. Was very informative on what i was looking to do. Looked pretty much as simple as i thought it would be as far as the loading and such.

I do have a couple question though. More then likely fairly simplistic question to some, but as im learning as i go for some of this not so obvious to me.

1) can a single .so file be made from multiple C files?
Reason i was asking because for my own over organizational reasons each module is currently split up into several c files.

so my olc module is actually mod_olc.c, mod_olc.h, cmd_olc.c, cmd_olcobj.c, cmd_olcnpc.c, cmd_olcroom.c, cmd_olcarea.c
and of course i would like them all to be a single mod_olc.so file.

2) Assuming that question 1 is possible, i have absolutely no clue what the makefile would look like to generate such a .so file. Any help there would be greatly appreciated.

Thanks Again folks.
25 Mar, 2011, Kaz wrote in the 10th comment:
Votes: 0
Quote
1) can a single .so file be made from multiple C files?


With GCC, yes. Link it with: -o filename.so -shared
25 Mar, 2011, Kline wrote in the 11th comment:
Votes: 0
RoFAdmin said:
2) Assuming that question 1 is possible, i have absolutely no clue what the makefile would look like to generate such a .so file. Any help there would be greatly appreciated.


Should just be gcc -o filename.so file1.o file2.o file3.o etc; similar to how your core executable is linked.
25 Mar, 2011, RoFAdmin wrote in the 12th comment:
Votes: 0
Well that is indeed good to know.

I am however having a problem generating the .so file properly. Probably cause i have no clue what im doing, but that is just a shot in the dark.

I have several files that make up the module. They are as follows.

mod_olc.c
cmd_olc.c
cmd_oldarea.c
cmd_olcroom.c
cmd_olcobj.c
cmd_olcnpc.c
olc.h


I have tried several variations on the so: section of the makefile in Klines vortex source he posted to no avail.
It keeps complaining about undefined references to some of the basic functions in the core files. I obviously need to have it link
in my core.h file, which is ../core/core.h in relation to the module files.

I am horrible when it comes to makefiles and such, which im sure those of you who saw my questions on the multi-directory makefiles already know.
So i really have no clue as to how to do this properly. Any help there would be appreciated.
26 Mar, 2011, Kline wrote in the 13th comment:
Votes: 0
RoFAdmin said:
It keeps complaining about undefined references to some of the basic functions in the core files. I obviously need to have it link
in my core.h file, which is ../core/core.h in relation to the module files.


No, just #include your core.h within your .c files that comprise the .so you are trying to compile. Check some of mine; they all #include merc.h and some system headers.
26 Mar, 2011, RoFAdmin wrote in the 14th comment:
Votes: 0
Heya-

@Kline: yeah i saw that. They are included as normal, but i still get undefined reference errors.
the module files and the core files reside in separate directories. The #include statements in the
module directories point properly to the core h files they need.

Im at a loss.
26 Mar, 2011, RoFAdmin wrote in the 15th comment:
Votes: 0
So at the risk of looking as ignorant as i am when it comes to makefiles, i figured ill post up one of the attempts at a makefile i was trrying to use to get this to compile properly.

directory structure:
SRC directory.
Inside SRC there is a CORE and a MOD_OLC directory.
CORE contains all the core files, including core.h
MOD_OLC directory contains everything relating to olc

Files in MOD_OLC directory are
mod_olc.c
cmd_olc.c
cmd_oldarea.c
cmd_olcroom.c
cmd_olcobj.c
cmd_olcnpc.c
olc.h


and here is the makefile

CC      = g++
SO_I = -c -fpic
SO_O = -shared -lm -o

%.o: %.c
$(CC) $(SO_I) $<

mod_olc.so: mod_olc.o cmd_olc.o cmd_olcarea.o cmd_olcroom.o cmd_olcobj.o cmd_olcnpc.o
$(CC) $(SO_O) mod_olc.so mod_olc.o cmd_olc.o cmd_olcarea.o cmd_olcroom.o cmd_olcobj.o cmd_olcnpc.o


this generates the o files as it did in the vortex code that was linked.
and it attempts to create mod_olc.so but it flips out about undefined references to various functions that are in the core source.

I do most certainly have the required files #include section done properly and they point appropriatly to where the files are.

Im sure im doing something wrong, or something stupid.
Here is another attempt i tried simply because, well it was a wild guess. Same issue though.
CC      = g++
SO_I = -c -fpic
SO_O = -shared -lm -o

H_FILES := $(wildcard ../core/*.h) $(wildcard ../classes/*.h)


%.o: %.c $(H_FILES)
$(CC) $(SO_I) $<

mod_olc.so: mod_olc.o cmd_olc.o cmd_olcarea.o cmd_olcroom.o cmd_olcobj.o cmd_olcnpc.o
$(CC) $(SO_O) mod_olc.so mod_olc.o cmd_olc.o cmd_olcarea.o cmd_olcroom.o cmd_olcobj.o cmd_olcnpc.o


As i said i suck before at makefiles.

Anyone have any ideas or pointers?
27 Mar, 2011, RoFAdmin wrote in the 16th comment:
Votes: 0
Hey All-

Last Post On this one as im pretty sure i figured it out.

since im developing in cygwin, i can not have undefined references in my so files because it is really just creating a linux like interface on top of windows is pretty much what it boils down to.


So for anyone in the future developing on Cygwin, keep in mind you can not generate .so files which do not have ALL the included code they need.
Guess its time for me to run some version of linux inside windows here and continue to develop there.

Thanks for the input from everyone.
27 Mar, 2011, quixadhal wrote in the 17th comment:
Votes: 0
Nich said:
I think one of the major benefits would be to compile parts of your code separately, and update at runtime without (fully) shutting the server down. Like, you could have your skill implementations in skills .dll, and add new ones, or change old ones, much more easily then you could otherwise.


You're in very dangerous waters here. Unless all the rest of your code uses lookup functions, this is a time bomb waiting to explode all over you.

Consider, you have a set of skill functions. You use the dlsym library to dynamically unload/reload them as you make a change. Are you SURE there's nowhere in your code that had a direct pointer to one of those functions? Because if anything did, that pointer is now corrupt and is no longer pointing at the new version of the function, but rather at whatever random bit of memory happens to be in the old location.

Not saying you can't do it safely, but… most Dikurivative code keeps a mountain of pointers going all over the place, and trying to make sure everything is properly isolated and going through a clean API might be a lot more trouble than its worth.
27 Mar, 2011, Tyche wrote in the 18th comment:
Votes: 0
RoFAdmin said:
since im developing in cygwin, i can not have undefined references in my so files because it is really just creating a linux like interface on top of windows is pretty much what it boils down to.

So for anyone in the future developing on Cygwin, keep in mind you can not generate .so files which do not have ALL the included code they need.
Guess its time for me to run some version of linux inside windows here and continue to develop there.


Not exactly. If you are calling functions in your mud's exe from your dll, then you have to export those functions and create an export library for your main exec.

You can use a macro like this:
#ifdef __cygwin__
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

And then use this on any function from your main exe that you need available in your dll.

EXPORT int myfunc();

When you link your mud main executable you create the export library:

gcc -Wl,-out-implib,libmud.a -o mud.exe mud.o

And reference it when you link your dlls:

gcc -shared -o mydll.dll mydll.o -lmud


EDIT: Actually you may have to explicitly export symbols in gcc 4.x now under Linux anyway. Not sure.
27 Mar, 2011, RoFAdmin wrote in the 19th comment:
Votes: 0
Heya-

Very true, i did see some information on that. However it seemed like a very large hassle to get something to work on a platform that is not going to be my end platform. Especially since a majority of the functions in the core files are going to potentially be called by any given module that may be loaded.

So instead i just did vmware + ubuntu. Everything is working great. My mud still compiles fine, the so generated as it was supposed to. The world is grand.

Except, now i cant get dlopen to actually open the file. Keeps telling me that no such file or directory found.
the .so file is in the same location as the executable file, so i thought simply using "mod_olc.so" as the name would do the trick, but it didnt like that.
So then i tried putting the module files in their own directory. which would make the path from the exe to the modules so ../modules/mod_olc.so
still says it cant find it.

I saw in klines example he was using a path that started all the way back at /disk0/ and pointed down the directories to where the .so file was?
I tried this approach as well to no avail.

Any suggestions?

[EDIT] IGNORE ABOVE. I figured it out
27 Mar, 2011, RoFAdmin wrote in the 20th comment:
Votes: 0
Alright-

So this really is the last post i think. Im putting this up pretty much as an informational post for anyone who attempts to do the same thing. Hopefully they stumble across my post, and i save them a world of headache that is was for me figuring all this out.


Things to know about dlopen, dlsym, dlclose, and dlerror.
1) you will need #include <dlfcn.h> in the section you plan on using these. You will also have to compile your main program with -ldl

2) You will need to generate .so files from your module source to load into your main program via dlopen.

3) If you are attempting to do this on cygwin you will find it is a pain in the butt. Its not impossible, however i did not bother to try to decipher the cryptic ways in which you would need to accomplish this. My solution for this is to run vmware and a some linux flavor. Saves a whole lot of headaches in my opinion. However if you feel the need to pursue this route, perhaps you can persuade tyche to help you as he seemed to know what he was talking about above.

The problem being in that generating your .so files, the linker complains about undefined references to functions in your main program. This happens because of something to do with the way cygwin is built on top of windows if im not mistaken.


4) you will need to link your program during compiling with -Wl,-E
This tells it that it needs to make everything in your program available for the modules to call. Otherwise when you attempt to load your modules, if they call any functions, or variables, objects etc in your main program you get undefined symbol (i think that was it) errors.

5) If you are compiling with g++ any function(s) you plan on calling in your module via dlsym will need to be defined as extern "C". This prevents c++ name mangaling. Without doing this dlsym can not find the function you want cause its name is all jacked up. Using extern "C" tells it to keep it sane.

Suggestion: With this in mind i have one function per module that requires extern "C" on it. It is the module initalizer function. from the main program i use dlsym to call into the initializer function. This function calls back into the main program and registers all event listeners with the main program as well as the modules unload function. This limits calls needed to dlsym in the main program, and thus the need to use extern "C" on your functions.

6) Using dlysm may cause warnings to occur during compiling depending on your level of strictness to the C or C++ standard. From my understanding this has to do with the way posix defines the way dlsym should return the pointer the the functions and the way C and C++ define the way it wants it to be returned or something along those lines.

Current suggestion: Ignore the warnings the code does work, it just doesnt conform to the standard and thus the compiler is obligated to tell you "um hey, im not sure this is gonna do exactly what you think it is, but its your call have fun".
If anyone knows another way to get around this warning, i would be glad to hear it, this is the only warning i have in my whole codebase with very strict rules applied.

Also obviously dlsym has no clue what the return value of the function, or the arguments it needs are. You should know this, and cast and act appropriatly with it or its probably gonna freak balls on you.



Hope this saves someone a load of headaches.
Once i have everything all ironed out i think i will be releasing the framework for my module system for you all to enjoy. Its written in such a way it should work with any codebase i think.
0.0/21