Skip to main content

Online Emotes

With MCE Online, the emotion balloons a player shows are seen by the other players too: press F3 and a heart pops over your avatar on every screen that has you in range.

Emotes ride on the same realtime match as the position updates (see World Sync). They need no extra service, no RPC and no database: an emote is a tiny message that is relayed and forgotten.

Available in Pro and Source tiers.

The Flow​

 sender                         Nakama world match                     receiver
────── ────────────────── ────────
F3 pressed
PlayerEmotes.Played("heart")
OnlineEmoteRelay ──opcode 3──▶ relays it as it comes ──opcode 3──▶ WorldSyncService.HandleEmote
{"e":"heart"} (no inspection) OnlineEmotes.TryRead (checks the name)
OnPlayerEmote(userId, "heart")
PlayerSpawnManager.ApplyEmote
OnlineEmotes.ShowOver(avatar, "heart", gameSet)
  1. Sender. The local player's hotkey raises PlayerEmotes.Played (the same event local code listens to). OnlineEmoteRelay is attached to that event while the world sync service runs, and sends the balloon's name to the world match only while the service is running and connected with a world match joined. Attaching twice does not send twice.
  2. Server. The world match forwards opcode 3 to the other players as it comes, like the map-change (2) and follower (4) events. It does not interpret the body.
  3. Receiver. WorldSyncService reads the message strictly, raises OnPlayerEmote(userId, balloonName), and PlayerSpawnManager draws the balloon over that player's avatar -- if the avatar is spawned, which means only players inside your area of interest see it.

The Message​

Opcode3 (WorldSyncService.OP_EMOTE)
Body{"e":"heart"} -- a JSON object with the balloon's name in e
Build it withOnlineEmotes.Payload(name)

Safety: a Peer Cannot Make You Draw Anything​

What a peer sends is text from outside, so it is never trusted:

  • The name is checked by OnlineEmotes.IsValidName before anything uses it: lower case letters, digits and underscores, at most OnlineEmotes.MaxNameLength (32) characters. OnlineEmotes.TryRead(json, out name) returns false for anything else -- malformed JSON, an empty name, a long one -- and WorldSyncService logs Bad emote from <user>: not a balloon name. and drops it.
  • What is drawn is always the balloon of that name that your game or the engine already owns (EmotionBalloonSet.Resolve). The message carries a name, never a sprite. A name nobody has draws nothing.

This is also why the balloons need no content synchronization: if your game replaces heart with its own drawing, every player of your game sees your heart.

Observing Emotes​

MemberWhen
WorldSyncService.OnPlayerEmoteA valid emote arrived: (userId, balloonName)
OnlineEmotes.ShownA balloon was drawn over a remote avatar, already in place: (balloonName, avatarTransform). A listener that throws is logged and does not stop the balloon.
using OpenMon.MCE.Online.World;

OnlineEmotes.Shown += (name, avatar) =>
Debug.Log($"{avatar.name} shows '{name}'.");

Reference​

TypeNamespaceRole
OnlineEmotesOpenMon.MCE.Online.WorldMessage format, name validation, ShowOver(Transform avatar, string name, EmotionBalloonSet game), the Shown event
OnlineEmoteRelayOpenMon.MCE.Online.WorldCarries PlayerEmotes.Played to the socket
WorldSyncServiceOpenMon.MCE.Online.WorldOP_EMOTE, OnPlayerEmote
PlayerSpawnManagerOpenMon.MCE.Online.WorldDraws the balloon over the remote avatar