Tracking changes - CitiesSkylinesMultiplayer/CSM GitHub Wiki

To be able to synchronize the game, all actions a player performs need to be tracked.

Two major types of listeners are used for that: The Extensions provided by the Cities API and our own Injections into the game code.

Extensions

Extensions are APIs provided by the ColossalOrder modding framework. Our extension implementations are placed in the src/Extensions folder. An overview of available extensions can be found here: Extensions. Every extension provides certain functions that are called by the game when some properties change. E.g. OnUnlockArea for the AreaExtension that is called when an area is unlocked.

Injections

For most of the features we need to synchronize, no extension exists. For this reason we patch the Cities code with the Harmony library. The library adds our code into the normal execution flow of the game. This is realized by adding prefix or postfix methods before or after a Cities method is called. Further explanations can be found here: Injections

Techniques

We use different techniques for synchronization of the game's features. The following paragraphs will provide an overview:

The IgnoreHelper

The IgnoreHelper serves multiple purposes. Most interaction handlers check if the current action (e.g. the creation of a street) must be ignored using the IgnoreHelper.IsIgnored() call. The current action should not be tracked in this case.

One use case for this behaviour is to prevent double synchronization. If for example a building automatically has streets attached (like the toll station), we only want to track the creation of the building but not the attached streets. When the building creation is applied, the streets are also created on the other clients. Additionally synchronizing the street creation would cause the street to be created two times at the client. For this reason, every interaction handler calls IgnoreHandler.StartIgnore() in the prefix and IgnoreHandler.EndIgnore() in the postfix. All interaction handlers that are called during this method's execution will ignore their action. StartIgnore calls can be nested.

Another use case is to prevent the synchronization of a remotely created element. If for example a street is created by one player, it is also created on the other clients. This create action should not be synchronized back as this would create an infinite loop. This means that when applying changes from other clients, StartIgnore and EndIgnore must also be called.

The IgnoreHelper can also be used for special cases where an action should be ignored. One example can be found in NetHandler::UpdateBuilding. The tracked NetNode::UpdateBuilding method may for example create power poles in certain situations, which already happens on both clients equally and should thus not be synchronized.

//TODO: IgnoreHelper exception functions (are they actually needed?)

The ArrayHandler

Tracking iterator methods