Event stream abuse for fun and profit

I’m currently writing some World of Warcraft addons. In itself that’s a fun challenge, but not one that hasn’t been relatively exhaustively tackled over Warcraft’s 15-year lifetime to date. Even the subject area I was interested in - writing some simple analytics to keep track of in-game resource use has been done before. I wanted to make some notes, however, as it has me thinking about how we reconstruct context from a relatively thin, multi-grained 3rd party event sequence.

World of Warcraft

These addons extend the behaviour of the standard Warcraft UI, which provides an event stream and Lua API with the intent of allowing players to extend their UI, typically to enhance the display, provide warnings and alerts, and automatically trigger or chain actions. The events provided are quite extensive, but aren’t perfect.

The events published from the client engine vary in granularity, and focus (from “your cursor position updated” to “someone nearby has created something” to “a friend of yours has come online”, for example). Some, but not all, carry a payload, and depending on context that payload may be only partially filled. They can be multiply delivered, but delivery is not guaranteed. Finally, events are somewhat contextless, although the overall ordering of events seems consistent. For example - you might get an event telling you that the contents of a bag have been updated, but the event won’t tell you that it’s because you just traded with a merchant or another player, or whether it’s because you just decided to dump a lower value (“grey” in Warcraft terminology) item to make space. Reconstructing that player-action context is a task for the player-developer.

Unfortunately, I need that context in order to track where and why resources get used.

Following the event stream

At first the task appears impossible. The set of events is published, but when many are contextless it’s not clear how to determine the player’s intent. At that point there are two pragmatic options - get a handle on the API to cause additional context-rich events to be thrown as a result of player actions, or to study the event stream to determine whether context can be extracted from it. The latter mechanism has the advantage that you don’t need to modify the player’s UI experience to achieve the goal, but requires a bit of empirical investigation - or, more accurately, undertaking specific sets of actions and recording which events are published as a result.

Fortunately, Warcraft provides a debug tool (the event trace, /eventtrace command) that will monitor all published events and show them in a window. Unfortunately, the window is clunky to use and slow to update, doesn’t persist the data anywhere and doesn’t allow you to tag ranges of events. It’s not practical for a really structured survey.

Fortunately, Warcraft provides an API call that allows you to register for all, as well as specific events - which means I could write an EventTrace addon more suited to my needs. This addon can be turned on or off, with a label indicating the context you’re studying, e.g.

Anhgarad (apologies to any Welsh speakers) about to buy something
Fig 1. Anhgarad (apologies to any Welsh speakers) about to buy something

When toggled on all events are recorded - and all events recorded are persisted in a local database that can be studied afterwards. Here’s a comparison of the events recorded while buying something from a merchant - including one where the purchase failed because I had no bag space left.

A simple merchant purchase.
Fig 2. A simple merchant purchase. Many of these events can be disregarded, and by undertaking the same action a few times you can see the common structure.

Context and information

Finally, we want to fit those events and their payload into a mental model, along the lines of “buying from a merchant means: switching into a ‘merchant’ state; choosing an item; receiving the item; handing over payment; switching out of ‘merchant’ state”. We can identify specific events that consistently represent stages of that model, and determine whether we can extract any data required from the event, or need to go elsewhere for it.

Event Payload Comment
MERCHANT_SHOW   The player’s started talking to a merchant
ITEM_PUSH 23,134186 Almost useful - it gives us an item ID but doesn’t tell us how many. We could work this out by remembering what we had, but …
CHAT_MSG_LOOT You receive item: ... :::::|h[Delicious Cave Mold]|h|rx5 ... The name of the item we’ve purchased is buried in this Warcraft URI - useful for the UI to render, but we need to dig a bit to pick up the right information.
INVENTORY_UNIT_CHANGED player Just tells us the player’s inventory has changed – no details
BAG_UPDATE 4 The bag in slot 4 has been updated .. but what with? Who would care?
PLAYER_MONEY   There’s no payload – but we can get current cash with the API call GetMoney(). If we remember the money we had before the transaction then we can work out the cash outflow.
MERCHANT_CLOSE   The player’s stopped talking to a merchant.

Even the events that are related to the exchange vary in utility - from my point of view. What I need is an event:

Event Payload Comment
PLAYER_EXCHANGED Delicious Cave Mold,5,300,Merchant 5 portions of lovely cave mold, for at price 300, with a merchant

Instead I need to build a stateful event processor in order to

Determine the context in which an action takes place
Fine, but we can have nested actions (I can start talking to a merchant and realise that I need to make some space in my bags. Do I need a state stack?

Remember pre-action resource holdings
GetMoney is one thing, but I might also need a detailed snapshot of what I have in every bag slot for later comparison.

Build a single transaction over multiple events (e.g. CHAT_MSG_LOOT and PLAYER_MONEY)
Let’s hope that the client doesn’t helpfully bundle things up, like giving me an individual chat message for each item, but a single PLAYER_MONEY event.

Your events (partly) determine my app’s structure and complexity

The upshot is that we should think quite hard about the events we publish - and in particular about the uses they get put to. In the case I’m in it’s arguably not Warcraft’s fault, as the event stream seems to be designed to enable simple contextless UI updates, not rebuild a relatively sophisticated context-based transaction record. However, it’s easy to see how an event stream that’s designed without regard to the uses that it’ll be put might twist

Going back to my original goal, it looks like I need to determine which sets of player activities I’m interested in, and trace the events published while I’m undertaking then. After that I need to extract the common events and work out (from their payload) whether it’s possible to reconstruct the context and quantify the consequences.