MessagingService Emulator: Solving Hacky Cross-Server Messaging Test Methods

Roblox library model: https://create.roblox.com/store/asset/17785565361/MessagingServiceEmulator
GitHub repository: GitHub - httpsKingPie/MessagingServiceEmulator

Introduction

Gone are the days when MessagingService required cumbersome and inefficient testing with two laptops. MessagingService Emulator is a utility module that allows you to test cross-server messaging within a controlled environment in Studio. It works as a 1:1 replacement for existing MessagingService references by returning MessagingService actual in non-Studio environments and returning MessagingService Emulator in Studio. The module is well-documented and compatible with @evaera’s moonwave.

Some examples where this would be especially helpful:

  • Stress-testing server browser UI with information provided by various simulated servers (ex: number of players, game mode, server location, etc.)
  • Design critical protocols for server hierarchy, for schemes where one server (among all) must be a controller and disseminator of information (e.g., specific analytic compilation, custodian of critical data)
  • Compiling and displaying global leaderboards composed only of live players - e.g., display the top players online at any moment
  • And of course, any instance of cross-server messaging in your game

It is my strong belief that better testing tools that mitigate limitations of the engine will allow for more ambitious applications of systems and protocols that depend on cross-server messaging.

Key Features

  • 1:1 plug in - MessagingServiceEmulator exactly mimics the actual MessagingService API allowing for 1:1 direction substitution. This means your code in Studio will work in a live server - expediting testing, easing integration with existing projects, and ensuring that you are not duplicating code because ‘it has to be done one way for Studio tests and another for actual games’.

  • Simulated DataModels (aka ‘game’) for Testing - MessagingService Emulator creates a simulated DataModel allowing you to create virtual test servers that behave like an actual server would (from the POV interacting with that server through cross-server messaging - to be clear, we aren’t talking about weird instance replication here, this is all virtual in an abstract sense). Simulated DataModels come with a JobId property (DataModels in Studio do not have this GUID, but real servers do, meaning you can better simulate and communicate across virtual servers) and a :SimulateClose method, allowing you to fire all functions previously connected to :BindToClose and test what happens to your servers when they shut down.

  • Control Test Conditions - Simulate messages dropping, errors arbitrarily being thrown, message latency, the times for simulated servers to generate, and more to achieve realistic testing conditions to fully stress test your game’s use of MessagingService before pushing an update to a production build. Executing test code by simulated servers is exceedingly easy too - just modify the ServerSimulation sub-module. Example code is already provided. Don’t forget to configure the amount of servers to be created in Settings!

  • Handy Debug Settings - Optionally debug and display when messages are sent, what their contents are, how long it took to arrive, what server sent them, what server received them, and more. Additionally, enjoy a quality of life toggle that appends an English name to the server’s JobId (used to distinguish servers). It’s much easier to debug your output seeing that 87923815-cd70-4460-b2e5-696d4c8ca3fa (John) sent a message to bc253bd3-c7f4-40c6-8199-5448a706c658 (Mike) and two seconds later 87923815-cd70-4460-b2e5-696d4c8ca3fa (John) received a separate message from 575bed77-272d-4590-a685-1d378283e853 (Sarah) versus doing the same mental tracking with just Hex strings.

API Overview

Note: in a non-Studio environment, MessagingServiceEmulator returns MessagingService. The API is nearly 1:1 and the API overview is explicit about difference.

MessagingServiceEmulator

MessagingServiceEmulator

Class: An emulator for MessagingService.
Description: Automatically returned when requiring the module in Studio. There is no constructor method.

MessagingServiceEmulator:GetSimulatedDataModel() -> nil

Description: (This is not a method in the actual MessagingService) Returns the simulated DataModel for Studio testing. You should never need this in a real-game instance, because you would just reference the global variable game.

MessagingServiceEmulator:PublishAsync(Topic: string, MessageData: any) -> nil

This function sends the provided message to all subscribers to the topic, triggering their registered callbacks to be invoked. Yields until the message is received by the backend.
(documentation copied from MessagingService)

MessagingServiceEmulator:SubscribeAsync(Topic: string, Callback: function) -> RBXScriptConnection

This function sends the provided message to all subscribers to the topic, triggering their registered callbacks to be invoked. Yields until the message is received by the backend.
(documentation copied from MessagingService)

Note: in actuality a Signal will be returned, but this has been tailored to work the same as an RBXScriptConnection, to include Quenty Maid compatibility

SimulatedDataModel

SimulatedDataModel.JobId string

This property is a unique identifier for the running game server instance. It is a universally unique identifier (UUID), meaning that no two servers, past or present, will ever have the same ID.
(documentation copied from DataModel)

Using SimulatedDataModel, you will have a JobId in Studio

SimulatedDataModel:SimulateClose() -> nil

Simulates the DataModel closing and fires all functions bound via SimulatedDataModel:BindToClose

All other properties and functions for both MessagingService and DataModel classes are either hooked or included, if not listed. This API Overview is intended to cover what most developers will interface with.

Settings

Studio Only: reminder, the actual MessagingService is returned if the module is required in a live server

Debug Mode Publish: boolean
Outputs whenever a message is published (outputs the message dictionary)

Debug Subscription: boolean
Outputs whenever a message is received and outputs if a message is sent by no servers are subscribed to that topic

Failure Rate: number
Number 0-100 representing the percentage that messages are not received (this can be set to 0), to practice catching errors. Remember: “Delivery is best effort and not guaranteed. Make sure to architect your experience so delivery failures are not critical.” (MessagingService | Documentation - Roblox Creator Hub)

Latency Lower Bound: number
The fastest a message can be sent

Latency Upper Bound: number
The slowest a message can be sent

Output Display Names: boolean
Provides an English name for display purposes only, to make it easier to track messages sent from various servers (vs scrutinizing pure hexadecimal UID strings)

Output Topic Subscription: boolean
Outputs when a server subscribes to a given topic

Output When Simulated Servers Are Created: boolean
Outputs via warning (for visual distinction, in case of print spam from other debug settings)

Servers To Simulate: number
Servers (excluding the actual one running) that MessagingServiceEmulator will simulate generation of

Server Generation Time Lower Bound: number
The shortest time a server can be generated (simulated)

Server Generation Time Upper Bound: number
The longest time a server can be generated (simulated)

Wait Before Automatic Server Generation: number
Once this time amount elapses, the server will begin auto generation of simulated servers.

7 Likes

Your GitHub link is unfortunately dead, could you fix this, please?

Also, a bit of silliness in your script, I think those methods do actually accept arguments. :wink:


2 Likes

Waiting for your dead GitHub link to be resurrected

1 Like

I think it’s there just for parity.

cc: @batteryday
Fixed the GitHub link - appreciate the heads up on that.

Just a comment typo on that from duplicating handling. It doesn’t affect the method’s function since the arguments are passed regardless.

1 Like

if I wanted to get a PrivateServerId would I just add another GUID to this table?
image

Yeah, you can do that. I’ll plan to add simulated private server and reserve server toggles in a future update, but in the meantime that would work.