MHServerEmu Progress Report - August 2025

Welcome to another episode of MHServerEmu Progress Report.

0.7.0 Status

Unfortunately, the backend changes needed to implement additional social features took longer than expected. The new instancing system is working, and I have just merged friend and ignore lists at the time of writing this. However, I have not had the opportunity to work on parties yet. Because this is probably the most highly anticipated social feature, I believe it is better to delay the next release by a few weeks and try to get it in. The goal is still to release 0.7.0 in September, but closer to the tail end of the month.

Although the new instancing system needed more time to cook, its deployment has been very successful so far. One of the big changes with the new system is how load is distributed across worker threads, which I covered in detail in the previous report. Thanks to this working out as well as I hoped, some of the popular community servers are now able to handle higher numbers of concurrent players with much fewer issues. We now also follow mostly the same rules as the original Gazillion implementation in terms of how instances are managed. One exception to this is how we handle hub regions (referred to internally as towns): the original data specifies a 20 player limit for most hub regions, which we are ignoring. As a result, we now have a single mega-instance of each hub on the server that can fit everyone. Performance-wise this is fine because most powers are disabled in hubs, and there are no enemies. The mood has gotten a lot more lively now:

Lively Hub

Communities

The most significant feature-enabling backend change is that we now have a way for game instances to communicate with each other through the Player Manager. The next step was to expand our implementation of communities.

A community is a collection of circles and members. A member represents a player that can be a part of one or more circles in a community. Each player entity has its own community instance. The data contained in your community is displayed in the Social panel, and each tab (Friends, Nearby, Ignored, Supergroup) represents a circle.

The biggest issue with communities is how overengineered the whole system is:

  • In addition to “System” circles represented by tabs in the Social panel, there are also “User” circles that were supposed to be customizable. This aspect was never fully implemented, but the entire serialization system, including the mode that is used to replicate this data to the client, is designed with customizable circles in mind, so we have to do it in a similar convoluted way.

  • The static data definitions for System circles are not integrated into the prototype system used by the rest of the game. Instead, all circle definitions are hardcoded, even though the system was apparently supposed to be fully customizable.

  • Even though it is not displayed in the Social tab, there is an additional hidden circle that contains your party members.

  • The Supergroup tab displays last online time for all players. This time is replicated to the client using the Windows FILETIME format, which is “the number of 100-nanosecond intervals since January 1, 1601 (UTC)”. This is the only place in the game that uses this time format, and it is immediately converted back to a regular Unix timestamp as soon as it is received by the client.

This unnecessary additional complexity makes it difficult to determine what is the essential functionality that we actually need to implement.

Each community member has a CommunityMemberBroadcast structure associated with it:

message CommunityMemberAvatarSlot {
    optional uint64    avatarRefId    = 1;
    optional uint64    costumeRefId    = 2;
    optional uint32    level    = 3;
    optional uint32    prestigeLevel    = 4;
    optional string    onlineId    = 5;
}

message CommunityMemberBroadcast {
    required uint64    memberPlayerDbId    = 1;
    optional uint64    currentRegionRefId    = 2 [default = 123];
    optional uint64    currentDifficultyRefId    = 3;
    repeated CommunityMemberAvatarSlot    slots    = 4;
    optional string    currentPlayerName    = 5;
    repeated uint64    memberPlayerDbIdIgnoreList    = 6;
    optional int32    isOnline    = 7 [default = -1];
    optional int64    lastLogoutTimeAsFileTimeUtc    = 8;
    optional uint64    currentCostumeRefId    = 9 [default = 123];
    optional uint64    consoleAccountId    = 10;
    optional uint64    secondaryConsoleAccountId    = 11;
    optional string    secondaryPlayerName    = 12;
}

A new CommunityMemberBroadcast instance is generated when a status of a player in-game changes (e.g. you enter a region or switch to a different avatar). This instance is first served to other players in the same game instance (e.g. to update the Nearby tab), and then it is sent to the Player Manager. Afterwards, it is routed to other game instances to update communities of other players that may be interested in it.

When the Social panel is open, the client polls the server for status updates every 10 seconds. Because of this, the Player Manager needs to not only route broadcast data, but also cache it for potential repeated queries, including those involving players that are currently offline.

We now have 3 out of 5 System circles functional now: Friends, Nearby, and Ignored. Parties are next on the list, and for Supergroups we are going to need even more backend changes to support features like, for example, managing member ranks while they are offline.

I wanted communities to be useful for something other than stalking your friends, so I did some work on the Grouping Manager and implemented tells (private chat messages). Now you can plan world domination or play out your Marvel fantasies without letting the whole server know about it.


This is it for today. Stay tuned!