Mary Sue Protocol

A common protocol for WoW roleplaying addons to communicate

The “Mary Sue Protocol” (“MSP” for short) is a simple challenge/response protocol for RP UI addons within WoW to communicate with each other and publish text information to other clients (such as RP character names and descriptions). It’s a complete, flexible and extensible replacement for the old “xtensionxtooltip2” chat channel method, without any of the associated problems (quoting/spam filter/evil person putting a password on the channel, etc…). It’s also much more efficient in actual testing, incorporating versioning for individual fields, group requests for tooltip data, and a throttle to avoid excessive communication.

LibMSP

LibMSP is an extremely lightweight (<400 line) public domain (CC0) embeddable library that WoW RP addons can embed to make supporting MSP very easy. It supports all the backend protocol internals, and lets you get on with the UI and frontend.

Its only dependency is ChatThrottleLib, which has no other dependencies (you should use v22 or later, as found for example in AceComm-3.0 or mirrored here).

Download: LibMSP.zip (version 7; 4KB, 12KB uncompressed) - a debug version which can display debugging info (LibMSP_Debug) is also available.

Protocol Overview

MSP is a “pull” protocol; instead of actively publishing, it waits for requests for data, and responds to requests as they arrive. It uses the Blizzard API SendAddonMessage() to send hidden WHISPERs to other players that will not appear on their chat.

Specifically: MSP uses the SendAddonMessage() prefix "MSP" to denote MSP communication. As packets, especially responses, can get longer than the 255 bytes that you can send in a single SendAddonMessage() message, multi-part messages are handled in the same fashion as AceComm3 does: If the packet is more than 255 bytes, split it into 255 byte parts, and use prefix "MSP\1" (where \1 is a 0x01 byte) for the first part, "MSP\2" (where \2 is a 0x02 byte) for subsequent parts (if there are 3 or more), and "MSP\3" (where \3 is a 0x03 byte) for the last part. Upon receiving the final part of a multi-part message chunk, the receiver should concatenate them together and treat them as a single message. (It’s up to the receiver if they want to parse chunks early, if they can, but the delay may well be less than the CPU overhead could cause, so it is suggested that you do not.) This uses just 4 prefixes out of the 64-limit “soft cap” in WoW 4.1, to handle a massive number of arbitary length textual metadata fields, with versioning, that can be used by more than one AddOn.

Packets (sent as one message with “MSP” or multiple parts, as above) can (but do not have to) contain multiple “chunks”, each separated with "\1" (a 0x01 byte). This value cannot be used within field text anywhere (nor can \0 i.e. null bytes); but you should never ever need \0 or \1 bytes, as they have no place in text fields. All other values are fine, yes even newlines (which should be sent as-is!); SendAddonMessage() doesn’t have any quoting issues (or problems with drunkenness). Text should be UTF-8 (as is normal within WoW); there are no special concerns with breaking a multi-part message mid-UTF8 character because SendAddonMessage() doesn’t choke on that either. There shouldn’t normally be a trailing or leading \1 in a packet, nor should there be consecutive \1 bytes, but malfunctioning clients may send this, and receiving clients should not break horribly if they receive such a packet.

How do you know if someone supports MSP? Well, the only way is to just go ahead and ask them for something (a ?TT for example): if you get a reply to your request, they support MSP! If you don’t get a reply in a timely manner (which may be vastly more likely on some realms i.e. non-RP realms), then they obviously don’t, but as it’s just a very short SendAddonMessage() for a prefix they are not registered for, it probably won’t even arrive (as the server will filter it), or if it does arrive (because they have more than 64 registered prefixes) it certainly won’t disturb them. Note: To keep things reasonable, if you have not received any MSP replies from a particular character you need to remember that: DO NOT send anything else to them for at least 5-6 minutes (300-360 seconds) to conserve your client bandwidth. People who don’t have an MSP-supporting AddOn installed will most likely not receive any data from you they don’t want, due to WoW 4.1’s server-side prefix filtering; unless they’ve hit the “soft cap” of 64 prefixes registered (MSP uses 4; MSP, MSP\1, MSP\2 and MSP\3). (Why 5-6 minutes? Well, they might have installed it in the meantime, and a few bytes every 5 minutes to someone who has a lot of AddOn communication going on anyway won’t have a significant impact.)

Doesn’t that use a lot of bandwidth? Nope. Less than you’re used to. Compared to xtensionxtooltip2, a O(n²) broadcast chat channel with 20 people sending simultaneous <DP> requests to random characters to thousands of users at the same moment, MSP is actually far more polite and delicate; in a universe where no-one supports it, blind ?TT probes account for just 35 outbound bytes to each character you mouse over, which will more than likely be dropped by the server if they haven’t registered the prefix, once every 5-6 minutes, compared to a constant torrent of incoming/parse-heavy data from a chat channel (plus chat hooks). In a universe where everyone supports it, it’s still quieter than xtensionxtooltip2 in the vast majority of cases; even if you write a 100KB novel in your description it might use more of your upstream bandwidth, but it’ll use less downstream for all the other people who don’t request it, as it won’t be spammed to everyone on a chat channel in case they might, but sent on demand. In the real world, widely tested by MyRolePlay 4.x, MSP wins on efficiency by a mile.

Note: You don’t actually have to send the same responses to everyone who asks. MSP in fact allows for an RP addon to send different RP profiles to friends, enemies, or confidants; maybe friends notice more, or you want your character’s name to be ? (i.e. NA1=?) to everyone who doesn’t know them? Of course, the problem of UI design of such a complex feature is left to the UI addon author…

A note on policies: GMs have confirmed that all addon data, including via channels or by other methods like this, are logged; data transferred via MSP should be in accordance with the policies. If user-supplied (and it almost always will be), users may face action against their account if they (for example) set offensive descriptions for their character that, by design, will be publically viewable.

AddOn Conflicts, and how to avoid them

Since all MSP AddOns, unless they’re specifically designed to be plugins to an existing one, will do the same basic jobs as another AddOn, there are likely to be conflicts between them. Maybe they’ll fight over who owns the tooltip; maybe they’ll both answer MSP requests inconsistently (which would be Very Bad, if you think about it), or even if they both use LibMSP unmodified, they might fight over who gets to update what fields.

To avoid very undesirable conflicts as often happened in the past running multiple RP AddOns, please can all MSP RP AddOns check the global variable msp_RPAddOn on load:

The following piece of code is placed in the public domain, and you can use it as you wish to do this. Please change MyAddOn where appropriate, of course. It checks for mrp as well because versions of MyRolePlay before 4.1.0.81 did not set msp_RPAddOn, but did set mrp so can still be detected.

-- Detect any other MSP AddOn and bail out in case of conflict
if _G.msp_RPAddOn or _G.mrp then
	StaticPopupDialogs[ "MYADDON_MSP_CONFLICT" ] = {
		text = format( "ERROR: You can only use one MSP AddOn at once, but you have both MyAddOn and %s loaded.\n\nAll MSP AddOns can communicate with each other, but please do not try to use more than one at once as conflicts will arise.", tostring(_G.msp_RPAddOn) or "another MSP AddOn" ),
		button1 = OKAY or "OK",
		whileDead = true,
		timeout = 0,
	}
	StaticPopup_Show( "MYADDON_MSP_CONFLICT" )
	return
end 
_G.msp_RPAddOn = "MyAddOn"

Chunk Commands

Each chunk contains one of three possible commands, as follows:—

Request

?<fieldname>
Unconditional field request for <fieldname>.
?<fieldname><version number>
A conditional field request (or unconditional if version number == 0)

A request from the sender for the contents of the specified field. The version number is optional, but if included, should specify the most recent version number that the sender has seen of the receiver’s field of this name.

An implementation of MSP really just has to either send these out as necessary, and also wait for these and respond to them as they come in. If there are multiple chunks in one packet, it’s best to wait until the packet is parsed and batch the replies into one packet separated with \1 (which might be multiple messages, but that’s still far more efficient than sending multiple messages for each chunk).

Please leave at least 10-15 seconds between consecutive conditional field requests for the same field from the same person.

Examples:
?TT
An unconditional request from the sender for your client’s TT (tooltip) data.
?TT0
Equivalent to the above, but longer (and therefore not recommended).
?DE1
A conditional request for your client’s DE (physical description) data. The sender indicates they have version 1 already; you should respond with a !DE1 chunk to let them know if that is still the latest version, else send them an updated version. (Note DE fields in particular are frequently long, so the packet you send in response may well be multi-part.)

Not Updated

!<fieldname>
The field <fieldname> has not been updated; please forget the version number (revert to 0).
!<fieldname><version number>
The field <fieldname> has not changed since <version number>, which was the most recent.

Sent in response to a conditional field request if the sender has the most recent version of that field and therefore doesn’t need an update. The version number is optional. If it appears, should be remembered as the most recent version of that field; normally it will be the same as the one you requested. If it doesn’t appear or it’s 0, remember that too, as that means versioning for this field has been abandoned and should be reset. Upon receiving one of these, please leave at least 10-15 seconds before making another request for an update of this field.

Example:

!DE1 - a reply indicating DEscription version 1 (which you presumably requested with ?DE1) is (still) the most recent version of this character’s description.

Response

<fieldname>
Field <fieldname>is empty and has version 0.
<fieldname>=
Exactly equivalent to the above, but longer; should not be sent, but should be understood if received.
<fieldname><field version>
Field is empty, and field version is the most recent version, remember it for future conditional requests, if any.
<fieldname><field version>=
Exactly equivalent to the above, but longer; should not be sent, but should be understood if received.
<fieldname>=<field contents>
Field has version 0 and contains everything in this chunk after the =.
<fieldname><field version>=<field contents>
(field has the contents specified, and field version is the most recent version, remember it for future conditional requests, if any)

If someone sends you a field request, and they haven’t indicated specifically that they have the latest version, you should respond with one of these at your earliest convenience (replies to multiple requests sent in one packet should also be batched into one packet).

If you receive one of these from someone, they’re sending you their most up-to-date information; remember it, and display it to the user or act accordingly. Remember the version number if present too, so if you request to see if there is an updated version (leave it at least 10-15 seconds after this), they know which version you saw last.

Examples:
NA=Mary Sue
A reply chunk simply indicating the character’s name should be displayed as “Mary Sue”. The version number is 0 (not specified).
NI
A reply chunk indicating the character’s NI (nickname) field is blank; they do not have a nickname defined, or otherwise do not recognise the field. The version number is 0 (not specified).
NI=
Exactly equivalent to the above, but an = longer than necessary.
RA1
A reply chunk indicating that version 1 of the character’s race field is blank; either RA isn’t supported or their race is intended to appear normally, as it does in-game.
NT2=Crystal Dragon Vampire Princess
A reply chunk indicating the character’s title is as stated. This is version 2 of their title, and is the latest version (one can only speculate on what version 1 might have been).

Defined Fields

MSP divides data into “fields”, each represented by a unique field code (which are always exactly two uppercase letters). Each field has an associated version (positive integer, normally counting up from 1; if not specified, the version number is 0, which is a special value used for unconditional requests/responses, or where you don’t wish to count versions, perhaps because the data in the field will never be the same twice?).

Implementations are asked to please try to stay compatible; if you want to add a field, please make a post on the development forums at http://moonshyne.org/ so that other people can possibly support it, and so that no collisions occur. Fields beginning with X, Y, and Z are currently reserved.

You don’t have to support any of these (although having a reply for VP is highly recommended, as is ?TT as that is likely to be the most common request by far; you may want to special-case ?TT). Remember, the contents of any fields you don’t support are, by definition, empty.

Currently defined field names are as follows:—

FieldContentsDetails
VPProtocol versionCurrently should always be set to "1", i.e. VP1=1.
This may be incremented to specify an extended protocol in the future, if that is needed.
VAAddon versionsSemicolon-separated list of Addon/Version pairs. The first should be the AddOn which is handling MSP support (i.e. you, if you set msp_RPAddOn).
Example: “flagRSP2/2.5.0”, or “MyRolePlay/4.1.0.81;GHI/1.0.4”
NANameThe character’s name (including all surnames, prefixes, etc), as they wish it to be displayed. May include spaces.
Example: “Varian Wrynn”
NHHouse NameThe house that the character belongs to (if any, for appropriate races). If non-empty, on display, please prepend this with “of ”.
Example: “the Elvoir Family” should be displayed as “of the Elvoir Family”.
NINicknameThe character’s nickname (if any). It’s up to the receiver how/when this should be displayed, if non-empty.
Example: “Bob”
NTTitleThe line that appears below the name, often used as a sort of subtitle; RSP’s <T> field.
Example: “Crystal Dragon Vampire Princess”, or “Perfectly normal cheese merchant”.
RARaceThe character’s race, if and only if they’re playing as a race not equal to their UnitRace (leave empty otherwise; don’t include if it’s the same, to allow for localisation).
(Please try to avoid wading chest-deep in half-elves! As always, addons may choose to not support this.)
(Sane) Example: “Broken”
FRRP Style class="details"Either free-text (displayed as-is, for a custom RP style), or a number for a default FlagRSP <RPx> common/legacy one:
0 (or empty)=undefined, 1=Normal, 2=Casual, 3=Full–time, 4=Beginner.
Note: While no value is included for 5 or beyond, you can have free text in here instead of a number.
Example: “3” (meaning full-time roleplayer), or “Godmoding” (well, it would be fair warning, I suppose…).
FCCharacter StatusEither free-text (displayed as-is, for a custom status) or a number for a default FlagRSP <CSx> common/legacy one:
0 (or empty)=undefined, 1=OOC, 2=IC, 3=Looking For Contact, 4=Storyteller
Example: “2” (in character), “3” (looking for contact), “Busy (farming)”.
CUCurrentlyA short, single-line description pertaining to the character’s current (publically-visible) status. Intended to be possibly suitable to put in tooltips (like Total RP 2 does). Useful to give an at-a-glance overview, for example, for something that should be immediately obvious to anyone looking at someone. Keep it terse. If, for example, the character is currently covered in blood, then “Covered in blood” would be fine (it’s suggested this is prefixed for display with Currently: or similar, but that shouldn’t be transmitted). If you were in a text adventure, and had TERSE mode on instead of VERBOSE, this is the field you’d get your description from.
TTTooltipSpecial case! This is the only “magic” field in MSP (v1). Tooltip requests are the most common, so this is short-hand for one.
Someone requesting your TT field should be sent all of the above fields.
Treat a ?TT request as if it were requesting: VP, VA, NA, NH, NI, NT, RA, FR, FC, CU and the TT field itself.
The TT field’s contents are empty, but you should increment the TT version as well whenever one or more of the above fields changes.
Example: “TT3” (3rd version of tooltip; the packet should also enclose replies to the other fields), “!TT4” (response to a ?TT4 specifying that version 4 of the tooltip is still the most recent).
DEPhysical DescriptionA textual description of the character’s appearance (not life history, that’s HI). FlagRSP’s <Dxx> field.
Think how an oldschool text adventure might describe your character. Can be short, descriptive but sweet, but often edges on the long side; some players like writing novellas in here, it's dependent on preference. There is no specific length restriction, and you should probably try to avoid having a maximum length. Classes in creative writing are not mandatory. Caveat lector. Other players may laugh at your crystal dragon vampire moon princess, Mary Sue.
Bad examples: Roleplayer’s Lament
AGAgeEither free-text (general short description, to be displayed as-is) or a number (in years).
Sensible ages for races are encouraged, but not mandatory.
Note this is the age your character appears to be to other characters. Actual age may vary.
Example: “450” (an elf, perhaps?), “9001”, “Young”, “Old”, …
AEEye ColourThe colour of the eyes’ iris (and/or glow, for applicable races).
Example: “Blue”, “Hazel”, “Golden”, …
AHHeightEither free-text (display as-is) or a number (which should be in centimetres, without units).
If it’s a number, it’s suggested that it’s displayed in appropriate units; players may prefer ft/in, or cm, depending on locale/preference.
Example: “198” (which is 198cm, or 6'5", depending on how you wish to display it), “Tall”, “Short”, …
AWWeightEither free-text (display as-is) or a number (which should be in kilograms, without units).
If it’s a number, it’s suggested that it’s displayed in appropriate units; players may prefer st/lb, just lb, or kg, depending on locale/preference.
Example: “52” (which is 52kg, 8st 2lb, or 115lb), “Slim”, “Stocky”, …
MOMottoThe character’s motto or favourite saying. Please do not include any surrounding quotes in the field. You may wish to put them in for display, however.
Example: “Not all who wander are lost.”
HIHistorySome background history about the character. Contents/length up to the player (another frequently lengthy field, if present).
It’s suggested only things other characters might generally know or that are rumoured about the character are included here.
It doesn’t necessarily have to be accurate, up-to-date, or very specific, or, like all fields, even there at all (a significant proportion of players prefer this to be discovered via RP rather than in a profile).
HHHomeWhere in the World of Warcraft does this character live at the moment (if, indeed, anywhere)?
Example: “Goldshire”, “Ironforge”, “Orgrimmar”, …
HBBirthplaceWhere in the World of Warcraft was this character born?
Example: “Stormwind”, “Telaar, Draenor”, “Auberdine”, …
GUGame, GUIDNot human-editable. Set it equal to your UnitGUID("player"); included to ease possible future expansion for units which are out of the Area of Interest.
Example: “0x0180000002CBBA59” …
GSGame, SexDefines your in-game character’s sex. Please set it equal to tostring( UnitSex("player") ); included to ease possible future expansion for units which are out of the Area of Interest.
Examples (in fact, the only values supported by WoW, but remember MSP always deals in strings): “1” (neuter), “2” (male), “3” (female)
GCGame, ClassNot human-editable. Defines your (real) class. Set it equal to the second parameter (that is, the non-localised version, for interoperability) of UnitClass("player") i.e. select( 2, UnitClass("player") ). Please do not change this at the user’s request; this is included so that it’s still possible to fall back on the UnitClass if a possible future Class field (if appropriate) isn’t defined (and look up this value for consistent localisation), even if the unit is not in the Area of Interest.
Examples: “WARRIOR”, “DEATHKNIGHT”, “DRUID”, …
GRGame, RaceNot human-editable. Set it equal to the second parameter (that is, the non-localised version, for interoperability) of UnitRace("player") i.e. select( 2, UnitRace("player") ). Please do not change this at the user’s request; this is included so that it’s still possible to fall back on the UnitRace if RA isn’t defined (and look up this value for consistent localisation), even if the unit is not in the Area of Interest.
Example: “Human”, “Troll”, “Scourge” (this is what Forsaken return) …