deleting code at run time?

Started by
18 comments, last by Norman Barrows 10 years, 8 months ago

Note that changing code at run time is generally possible, but the ease/difficulty of doing so depends on what programming language you are using. For dynamic languages like Ruby replacing function definitions at run time is essentially trivial. For compiled languages, you have to do evil things like disabling data execution protection and rewriting code pages.

FWIW, there is a "slightly less evil" strategy of making use of function pointers.

the basic idea being that the logic is mostly broken down into pieces ("basic operations"), and then structs and function pointers are used to glue everything together. as things change around, then the various structures and function pointers can be changed around, ...

when executed, the logic can jump fairly directly from one operation to the next and essentially walk the structure.

if done well, this can be fairly flexible and actually a fair bit faster than the "general purpose" logic (lots of complex branching if/else chains or switches).

the drawback, however, is that it is typically much more bulky and nasty-looking, and potentially difficult to understand and debug.

generally, I think reorganizing code where possible is preferable to resorting to something like this.

generally, in my case, it is mostly confined to use in my scripting VM and dynamic type-system and object-system facilities (*1).

I had a few times tried using it in image codecs, but typically it is faster/easier just to write special-case versions of functions (say: "hey, we are using 4:2:0 subsampling and YCbCr and an RGBA image buffer" -> use a special version of the logic specifically for this case, falling back to the general case when encountering an unexpected combination of parameters).

the tradeoff is that specialized versions of code can also contribute a fair bit of bulk, and are inherently "not really all that flexible" since each only addresses a few specific parameters (say, if we had 3 subsampling modes, 4 color-space transforms, and 4 image-buffer layouts, naively this would mean 48 versions of the image-transform, which is impractical, so usually only 2 or 3 "very common" cases get special handling).

sometimes a "mix and match" compromise is possible, with some special-casing of the logic, mixed with the use of function-pointers in other places.

going beyond this, there is crafting specialized machine-code sequences at run-time, which can execute faster, but opens up a lot of issues (namely portability and complexity). however, machine-code can accomplish a few things which function-pointers can't (it is free from the constraints of the language and ABI, ...).

direct self-modifying code and writing into compiler-generated code generally seems like a bad idea IMO.

*1: actually, they tend to use a mixture of structs and function pointers, and directly-generated native code.

in some tests (for script code), using a straight-C route I have gotten within about 10x of native, and about 2x-3x of native C for cases where typically a form of "call-threaded code" is used (the native code = sequence of calls to C functions implementing the logic), with occasional operations being generated directly as machine code (typically things like variable loads/stores and arithmetic operations and similar).

typically, this is good enough.

a much more "advanced" strategy is to use (or implement) a full compiler, but, this is another level of pain, and adds its own costs.

using such a code generator for the original problem though would likely be severely overkill.

or such...

Advertisement

You might not have to check all the achievements from the beginning, which makes optimizing this not pointless.

The achievement "Killed second enemy" would be added after the first one is completed. Same with other things that have some preconditions.

I would make a container of achievement objects that check their condition when told to do so. They can then be removed and new ones can be added as the player progresses.

Polymorphism would be used.

o3o


thank you for the reply, but I don't really worry about "code exceeding ram"(sorry, I didn't state my question clearly enough). my problem is that I need to do many if statement to test whether the player has completed certain achievements, which is unnecessary after the player has completed those achievements.

that's nothing to worry about! <g>.

just code it. it'll run plenty fast. even if you do a couple hundred checks per frame.

you can nest the checks to make it run faster:

if (first opponent killed)

{

// run second opponent dead check

}

in general, the code will probably take the form of:

if (achievement A not awarded)

{

check for achievement A completion

}

else

{

check achievements that require A to be completed first

}

for example, you only check for kill #2 once kill #1 is competed.

if you have achievements for the first 10 kills, at first it just does it does one if (is A done already?), then does the check for achievement A. only at the end does it do 9 if's (A through I completed) and then check for achievement J.

you make be able to implement this in a generic manner, with flags for each achievement saying if they're completed. you might also use flags for stuff like "all 10 first kill achievements done". you'd check this before checking for kills. once the player had 10 kills, the code would just do that one check per frame.

i was thinking you might be able to write a slick generic routine that used the completed flags, dependency info (what achievements are prerequisites for which other achievements), and the various achievement checks to process any and all achievements. but that would require some sort of check function as a variable. granted you could possibly do function variables for each achievement check. i use this in CAVEMAN. one of the fields in an actions database record is a function variable that contains the address of the action handler code. another common way to implement this is with a big switch() statement. if i did it again, i'd just use a big switch:

int achievement_done(int achievement _num)

switch(achievement_num)

{

case 0:

// code to check completion of achievement #0 goes here

return(result)

case 1:

// code to check completion of achievement #1 goes here

return(result)

// add additional checks here

}

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

You could maintain a list of achievements you iterate over. When one has been completed remove it from the list to check. No need to hard code each achievement in a giant if/else or case statement.

thank you guys! your replies really helped me a lot. I think I've got the basic idea on how to do this.

You could maintain a list of achievements you iterate over. When one has been completed remove it from the list to check. No need to hard code each achievement in a giant if/else or case statement.

agreed.

using a list of objects of some sort would probably be better than hard-coding it, with a class method or similar to determine when the achievement has been completed.


if i did it again, i'd just use a big switch:

thank you for your advice. I will try it out.

Assuming C++/Java/C#:

I'll second the have some kind of container (vector, list) of objects which are derived from a base class which has a pure virtual

bool SatisfiedAchievement()

method which returns true if the achievement is satisfied, so it can be removed from the container after it has been completed.

But what you don't want is each achievement to be a separate class derived from the base class - most achievements are similar (kill enemy X, collect X items, kill X enemies of type Y, etc.), so you want to have the different types of achievements as classes which take parameters on construction (or a separate initialisation step) which provides the details.

You can still have really complicated achievement types to have a class of their own, but these should be an exception rather than the norm. Also consider compound achievements which can have several subtasks (such as kill X goblins and collect Y golden nostrils), you can have a compound achievement class which checks for both (so will contain 2 or more instances of concrete achievement classes as members) which only returns true from SatisifiedAchievement when both tasks are complete.

If you are using C, you can do the same with function pointers.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley


You could maintain a list of achievements you iterate over. When one has been completed remove it from the list to check. No need to hard code each achievement in a giant if/else or case statement.

This way, I need to do all the checks in one place which means I need to keep some static variables or database for those events that will trigger achievements, right?

generally speaking when an event occurs (such as player making 1st kill), you can process it immediately, or you must store the event (somehow - perhaps with an event_X_occurred flag) for later processing.

this is one of the design options i tend to ponder over with every game i do. should i process events immediately, or store them for later processing?

processing them immediately means i don't have the extra complexity of storing events which are pending processing.

storing them for later processing means i can process all at once, which may be more efficient and/or a more modular design.

but usually, storing them for later processing is more work and complexity, and less efficient as well. its not necessarily overhead that a game can't handle, but essentially, its more work to code, and runs slower, just for the benefit of a more modular design.

so i usually process events immediately "and be done with it".

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement