Singlep plugin
=-=-=-=-=-=-=-=

This plugin incorporates code provided by Pierre-Marie Baty
(www.racc-ai.com/) for using Metamod with single-player Half-Life, which
came from discussions with Botman (planethalflife.com/botman/) on running
bots in single-player Half-Life.

Download at: http://metamod.org/files/singlep/ (metamod.org/files/singlep
/)


Use of singlep
==============

Install it as you would a normal Metamod plugin, and it should solve the
problems otherwise seen when trying to run Metamod with single-player
games (savegames not working, game triggers not activating, etc). For
further discussion, see below.

The plugin provides a single command ("splist") to print out the list of
function names and addresses, ie:
        ActiveThink@CBaseTurret 92364368
        AngularMoveDone@CBaseToggle 92322784
        Animate@CBMortar 91866864
        Animate@CSquidSpit 91875856
        AnimateThink@CControllerZapBall 91933600
        AnimateThink@CSprite 91960848
        AnimateUntilDead@CSprite 91960928


The problem
===========

References from Botman's forums:

  - about that singleplayer problem... (dynamic.gamespy.com/~botman/yabb/
    YaBB.pl?board=2&action=display&num=2854)
  - single player support in Hpb_bot? (dynamic.gamespy.com/~botman/yabb/
    YaBB.pl?board=2&action=display&num=2894)

Further discussion of the problem, from Pierre:

    Look at all the functions that are callable inside a game DLL (ie,
    Valve's HL.DLL). You'll note that you find :
      - GetEntityAPI
      - GetEntityAPI2
      - GiveFnptrsToDll
      - the list of entities built by LINK_ENTITY_TO_FUNC (ammo_357,
        player, env_beam, etc.)
    OK, given that, if you can build a DLL that fakes those exports, you
    have a "hook DLL", that is seen by the engine side as a valid GameDLL.
    That's what bot authors do. That's what you are doing in metamod too.
   
    The problem:
   
    The interfacing is not complete. With just the above, you are not
    exporting enough functions. You are missing 50% of the exported
    functions, ie:
        ?AccelerateThink@CApacheHVR@@AAEXXZ
        ?ActiveThink@CBaseTurret@@QAEXXZ
        ?AngularMoveDone@CBaseToggle@@QAEXXZ
        ?Animate@CBMortar@@QAEXXZ
    OK, those functions are MS-style exports (starting with '?',
    containing '@@' etc, would not compile under Unix), but they are
    required for single-player gaming. Not having them exported too is the
    cause of many bizarre behaviours you see in-game when you play
    single-player MODs with a hook DLL between your engine DLL and your
    MOD DLL. For example, in Half-Life, the barney at the end of the train
    ride (intro scene) won't come to open your door. In Opposing Force,
    the barrack's door will refuse to open. In Azure Sheep, you can't use
    eye scanners. Everywhere, you can't save a game correctly. I assume
    that's because some entities have special functions interacting on
    them, that are "outside" their respective class (the one interfaced to
    by the LINK_ENTITY_TO stuff). For example, I've found that the entity
    "func_tracktrain" must also react to calls to "?
    DeadEnd@CFuncTrackTrain@@QAEXXZ" for functioning correctly in
    single-player. How and why is that stuff implemented in the genuine
    DLLs, I dunno & I don't give a ****. But that is.
   
    My solution (which is basically the same as botman's)
   
    : When the engine and your hook DLL are exchanging the list of their
    functions, they build a table associating each function to an address
    in memory. Then, when the engine needs (or is asked for) a function,
    knowing its name, it calls it directly at the right adress. But since
    you are not exporting half the functions, the table is very
    incomplete. Instead of letting the engine look in its array for a
    function it obviously won't find, I am doing the work for it. When the
    hook DLL loads the game DLL using LoadLibrary, I get told what DLL
    file it is. So I open it, and build myself my own exports--address
    table. I look for each export, and store 1) its address, and 2) its
    name (without the MS C++ -style mangled case because the engine
    usually calls these functions by pure alphanumeric names). And so,
    when the engine asks for a function address or a function name of the
    game DLL using FUNCTION_FROM_NAME() or NAME_FOR_FUNCTION(), instead of
    letting those macros do the work, I feed it with my own addresses and
    my own names. Then the engine looks up at these addresses and finds
    the functions it expects to find. It doesn't know, and doesn't need
    to, that these functions in memory are located outside the segment
    where my hook DLL is actually loaded, actually instead of finding
    these functions inside the fake DLL that it believes to be the game
    DLL, it goes and find it directly in the right game DLL, because I
    told it the function it requested was located at such an address. If
    I'd let FunctionFromName() and NameForFunction() work, it would never
    find those functions since the hook DLL doesn't export them. And such
    a trick makes the thing work, because the interfacing is now fully
    realized.

--------------------------------------------------------------------------
