• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Sync Doc

Sync Documentation
(work in progress)

Table of Contents
  1. Introduction
  2. History
  3. How it Works
  4. Performance
  5. Things to Know

1. Introduction

Sync is a library that allows you to synchronize otherwise asynchronous data such as camera position or the contents of a local file. This is important because the game is likely to split up, or disconnect (desync) the players if you try to use data that differs from each player. The way we synchronize values in JASS is through the gamecache natives StoreX and SyncStoredX (where X is replaced with the variable type). These natives allow you to store primitive types such as integer real boolean and string which you can then send to the rest of the players in the map. There are also natives for units but because you can't create them locally there is likely no use in trying to sync them.

2. History

Past attempts were very slow and basically had no practical use. This is because of TriggerSyncReady and SyncSelections. These natives are supposed to hang the thread (and game time, at least for TSR) until the all players have the same data, but they are very slow and can hang for minutes at a time.

The implementation in Sync avoids both of those natives and provides fast speeds when syncing data.

3. How It Works

The system will store the values in the gamecache for only one player. This will ensure that only the syncing player has the value we want to sync. Then, once the start method is ran, SyncStoredInteger, SyncStoredReal, and SyncStoredBoolean are called locally for all the values in the cache. Because SyncStoredString does not work, we have to encode it into a series of integers. Next, a periodic timer is ran to check the last index of the cache, and if found the local player has finished syncing. This works because the data is received in the order it was sent, so once we get the last index we know we're done. The player who is syncing the data will be finished after the first timer callback because they already have all of the data.

Once a player has received the data he is the only one who will know, so we have to trigger a unit selection event to notify the rest of the players. When a unit is selected the event fires synchronously, meaning that it will run for all the players at the same time. This is how we signal that the local player received the data (See: SyncInteger).

Now that all players have the same data in their gamecache files, it's safe to use that data in your map script.

4. Performance

This section was written for patch 1.27. On the newest patches (1.28.5+) the performance is much better.

It is fast. You are able to sync about 1,000 integers in a second (4kb/s), or less in B.NET (tested with 2-6 players).

However, after that there seems to be throttling of about ~250-300 bytes a second. This means you should try not to exceed ~1kb/s for more than a few seconds or you will notice slower speeds. It is more than enough anyway. For example, syncing 1,000 integers at map start could take ~0.3 seconds, but 1,400 could take almost 4 seconds. You can send much more than a thousand at a time, but keeping it within that range will provide the fastest speeds.

Of course if a player has a bad connection there may be a longer delay since we have to wait for all players to receive the data. Although I don't think this should be a problem because the game already handles lag by pausing the game and showing the lagging players window (this has been tested). You may also set a timeout for your data which will throw an error if any player hasn't finished syncing in the specified time.

Another thing to consider is the game cache parameters string missionKey and string key. They are used to map values by strings, however those strings are likely also sent over the network. You can see how these values significantly affects performance below.


3 Player LAN Results

* results are in seconds

Tests results were with 1000 integers, 2 strings, and 2 booleans

Mission Keys
--------------------------------------
"A" = 0.375 - 0.500
"AB" = 0.375 - 0.500
"ABC" = 1.118 - 1.400
"ABCD" = 1.750 - 2.188

"ABCDEFGHIJKLMNOPQRSTUVWXYZ" = 20.000 - 20.063

Notes:
* Key length hardly made a difference when only syncing a "few" values (about 300 integers or less).
* These results were the same for the "mkey" and "key" paramter of the game cache natives


You can see after the key length exceeds 2 the performance drops significantly.

Fortunately, the Sync library handles this automatically by generating short keys. You never have to deal with keys when using Sync.

Note: This section is a WIP as more thorough testing needs to be done. All testing was done with ~5 players or less in battle.net and LAN

5. Things to Know

If you use the system wisely you shouldn't have any issues with the following.

*Note: These only occur while data is still syncing
  1. The local player who is syncing may briefly be unable to issue orders to units until all of the selections have occurred in syncing (usually very quick, one selection).
  2. TriggerSleepAction & SyncSelections - These native will be delayed as long as data is syncing.

--------------------------
Thanks to: killcide, wareditor, noctosphere, solu9, skargoth, nestharus, troll-brain
Last edited:
Top