Skip to content

Glacier_EventSystemReverse

DronCode edited this page May 9, 2020 · 2 revisions

Glacier engine event system

The event bus reversed.

It's very interesting.

I guess a big part of code was generated by another tool in compile-time.

So, the event system in the glacier is divided into two sides:

  1. Event definition system. You may see this in assemly, something like that
 sub_742B80      proc near               ; DATA XREF: .data:007EFC60o
.text:00742B80                 push    offset aOperatedoor ; "OperateDoor"
.text:00742B85                 mov     ecx, offset unk_9AE068
.text:00742B8A                 call    ZEventSystem__DeclareEventType
.text:00742B8F                 push    offset sub_74E6B0 ; void (__cdecl *)()
.text:00742B94                 call    _atexit
.text:00742B99                 pop     ecx
.text:00742B9A                 retn
.text:00742B9A sub_742B80      endp

This is declaration of event type. Event definition is a simple structure looks like that:

struct ZEventDef
{
ZEventDef* pNext;
const char* pName;
uint16_t ID;
};

and the game operates IDs.

  1. Event bus system. Most game classes were derived from 4 things:
  • ZSerializationBase - base class for ZSerialization. The implementation of binary serialization/deserialization of objects (used in the content reading system, saves and etc).
  • ZSerialization - base implementation of ZSerializationBase
  • RTP::cBase - Runtime Type Propagation. Legacy system of types information (I guess it's not used or not).
  • ZGEOM - the place where objects operate events, in-game location, entity information, and other logical places.This method implements method ZGEOM::onEvent(uint16_t eventId, int* pResult). This method will be called by the game when in event bus spawned a new event. The derived class looking into eventId and comparing it. If the class can process this event it will do it, otherwise, it will be ignored.

And it's very good but in Hitman Blood Money created 3 action systems (thank you IOI, bastards):

  • ZLnkAction - legacy action system. Was created to implement basic scenarios of actor/player interaction with the world. It uses ZActionQueue and ZActionDispatcher. There are legacy but used sometimes.
  • ZHitmanAction - derived from ZLnkAction, implements ZLnkAction and provides more information about the self. Also, it works through ZActionQueue.
  • ZAction - literally different things. This method implements basic interaction methods between player and world objects. Like 'drag body', 'drop item', 'pick item', 'sedate agent from M04' and etc. So, ZLnkAction and ZHitmanAction operate objects and could be implemented as for player and as for actor (sometimes not) but why I'm talking about ZAction? Because ZAction works through event system.

How it works: Every level (except HitmanBloodMoney, because it has not owned GameData session) trying to create level control. It creates all custom actions on level. Each action was attached to ZSTDOBJ (representation of each object on level) with their own action string id (you may be seen this in .cfg file), their own translated text it, their own radius, base point and ... event id who will be emitted when user press specified key.

As example we have method CDoorBase::setupAction. This method was located at 0x005B55C0, it creating and assigning new action via

v1->m_action = ZAction::assignAction(
                       v6,
                       "AllLevels/Actions/OperateDoor",
                       "AllLevels/Actions/OperateDoor",
                       29,
                       v5,
                       0,
                       400,
                       0);

like this. Look for v5. Previously it's defined as

v5 = (unsigned __int16)dword_9AE070;

and we can look into dword_9AE070. We can see literally nothing here:

.data:009AE070 dword_9AE070    dd ?

But it's a trap. Look for 8 bytes before (because our dword is eventID, name and base pointer located at -8 bytes of eventID). And what we can see here:

.data:009AE068 unk_9AE068      db    ? ; [-8 bytes]
.data:009AE069                 db    ? ;
.data:009AE06A                 db    ? ;
.data:009AE06B                 db    ? ;
.data:009AE06C                 db    ? ;
.data:009AE06D                 db    ? ;
.data:009AE06E                 db    ? ;
.data:009AE06F                 db    ? ;
.data:009AE070 ; int dword_9AE070
.data:009AE070 dword_9AE070    dd ?        

and look for references to unk_9AE068 and the single one result is 00742B80.

It's the place, where created event definition for "OperateDoor".