Multiplayer games require communication to inform each client of the position of other players. For Sailaway the communication involves also heel, dive, heading, course over ground, speed, speed over ground, state of mainsheet traveler, state of canting keel, state of foils and for each sail the angle, and 3 shape parameters.
For multi-crewed boats all the setting of trim lines are communicated as well.
Other communication involves the colors and textures of the boats and the in-game chat.
But the most crucial one is the communication of the position, speed, etc.
I believe there are 3 basic ways to handle this multiplayer communication in games:
– Send updates on positions and all other parameters as often as possible to a server and redistribute to the clients
– Make the clients communicate to each other directly using PtP communication.
– Compute everything on the server and distribute it to the clients
Sailaway has chosen for the first option.
In order to make the positions of other players on the screen as accurate as possible, the communication should occur as often as possible and as fast as possible. Many First Person Shooter games let their player’s computers send position update messages 3 to 5 times per second.
The biggest problem here is that when the number of players increases, the number of communication messages increases exponentially. What happens is: 1 player sends an update message to the server, the server sends X messages to X other players. This will get out of hand very fast:
2 players, 3x per second -> 2 x 2 x 3 = 12 messages per second
20 players, 3x per second -> 12,000 messages per second
200 players, 3x per second -> 120,000 messages per second
2000 players, 3x per second -> 12,000,000 messages per second
The capacity of the server, the bandwidth of the server connection would soon be consumed completely. And even if this would be limitless, the capacity and bandwidth of the player’s computer would become the bottleneck.
To overcome this problem players are often grouped together in “game rooms”. This way the number of players per game room remains under control. Sailaway takes a different approach, because there is only 1 game room with all players anywhere in the world online.
Another problem all games experience is internet latency. It can easily take up to 200ms or much more for a message to reach the server and from there another 200ms to reach another player. To overcome this problem game rooms are often assigned to specific servers that are located near to the players in that room.
But Sailaway uses 1 server for all players and therefor all these quickly described problems will apply and make it unplayable if nothing is done to prevent that.
In order to overcome these problems of flooding messages, server capacity, bandwidth and internet latency, Sailaway uses:
– a server that only sends messages to users that need it
– clients that compute, interpolate or compensate as much as they can without receiving input from the server
– clients that try to limit the number of messages they send out.
– clients that compensate for internet latency
Waves and wind
There is more than just the boat positions to synchronize in Sailaway. Since waves and wind shifts and gusts also influence the speed and behavior of the boats, it is important that these are identical on every computer as well.
Rather than sending messages back and forth to synchronize them, each computer creates it’s own waves, gusts and shifts. It uses an algorithm for this with 2 input parameters: world coordinates and time. Now if each computer uses the same algorithm and has the same input, all players will experience the exact same waves and wind.
It is vital that the time is correct, not just for waves and wind, but it is the basis of all multiplayer communication in Sailaway. It influences the positioning of boats and determines who passes the finish line of a race first. More about that later.
Since there is no guarantee that all computer clocks run exactly on time and are correctly set up, Sailaway uses it’s own internal UTC time. Basically the time from the server is requested upon login and the player’s computer updates this time constantly so that it is always up to date.
But this will result in discrepancies between players. To eliminate these, the internal clock is updated with the server time every 5 seconds. If the difference between the internal clock and the time on the server becomes smaller, the update frequency dynamically goes down to every 30 seconds. And when the discrepancy grows the frequency increases again.
The clients sends out a time request, the server receives it and sends the current time as an answer. When the server’s time arrives at the client, it is already outdated. To compensate for this latency, the client measures the duration between sending the request and receiving the answer, divides that by 2 and adds it to the time received from the server. The bigger the duration was, the less reliable the server time is. So instead of resetting the internal clock to the new server time, the new server time only nudges the internal clock to run faster or slower. The force of that nudge depends on the reliability of the newly received server time.
Together with a few other subtle calculations this makes the internal Sailaway clock of every player to run well within a margin of -20 to +20ms from the server time.
Instantiating other boats
When a client receives a position update or sails update from another boat, the first thing that happens is that the boat is looked up and if it doesn’t exist yet, it is instantiated.
Before the boat is actually shown in the world, it needs:
– information about its position (send periodically by the other boats)
– information about its sails and their settings (send periodically by the other boats)
– information about its appearance (requested once from the Sailaway server)
Before version 1.1.11 the boat would be instantiated upon a position update or sail update, but it would not store that information immediately. It therefor had to wait until the next update was received before it would become visible in the world. As of 1.1.12 the instantiated boat immediately stores any information available for later use. This has a significant effect on the time it takes before a boat appears on the screen.
If there are for instance 100 boats in the neighborhood, the client would have to send 100 requests to the server. Since that would be highly inefficient, the ids of the newly instantiated boats are collected and after max. 2 seconds a single request goes out the the server to provide the necessary information about the appearance of those boats in 1 go.
The sails info is also send periodically by the other boats and contains info like which sails are raised, at what angle are they, how curved are they how far open is the top of the sail, etc.
It will take between 0 and the the max. of:
– 2 second interval at which boat info is requested
– the interval at which position updates are send
– the interval at which sails updates are send
before all the necessary info is received, but then the model of the boat is placed in the world and the user can see the other boat on the screen.
The boats are removed from screen:
– when it is out of range,
– when the user logs off,
– when it has been longer than 2.5 times the interval for boat position updates since it last received any updates.
Receiving position updates
Each position update contains the time it was send in milliseconds. And since the internal clocks of all clients run in sync with each other, the message contains the exact location of a boat at an exact time. But also:
– speed over ground
– course over ground
– nose dive
– has the boat capsized Y/N
– has the boat ran aground Y/N
The copy of the other user’s boat behaves like any other boat. It has a speed and a course and it travels in a straight line even if no position updates are received. It moves with the waves, it speeds up when going down a wave and slows down when it climbs a wave (this works because all waves are computed with time and location as input and since the time is synchronized, the waves are synchronized as well).
When the client receives a new update, it knows when the message was send and it knows what time it is upon receive. Together with the course over ground and speed over ground, the received position is extrapolated to a new position for the boat. This should take away any delay caused by internet latency.
Now that an exact position is known, this new position update is compared to the current position of the boat on the receiving client. This copy of the boat has been sailing since the previous update and it is likely that the computed position is not identical to the newly received position.
The position difference and also the heading difference is gradually eliminated by speeding up/slowing down/turning the boat. As of version 1.1.11 this done by honoring the physical mass of the boat so that the correction will look as natural as possible.
Sending position updates
Each boat sends out it’s position every x seconds. This frequency is dynamic and varies between 1 and 10 seconds. The frequency increases when:
– other boats are in the neighborhood
– the distance to the nearest boat is smaller
– the boat accelerates or decelerates
– the boat changes course
The frequency decreases when there are a great many boats in the neighborhood to prevent flooding the clients with updates from all these boats.
Half the updates contain only the latitude, longitude, COG and SOG to minimize the bandwidth used. The other half of the updates also contain heel, nose dive, lift and a status whether the boat has ran aground or has capsized. When the boat is changing course a full update message is forced every time.
The messages are send to every boat within a rectangle of 1.2 degrees longitude and 0.8 degrees latitude.
Passing parameters of sails
Every boat sends out a summary of the parameters of each of it’s raised sails. This enables the other clients to update the visual appearance of the boat. It contains per sail:
– how far the sail is raised
– how much the sail is reefed or furled
– the angle of the sail or boom
– the depth of the curve of the sail
– the position of the curve of the sail
– how much the top of the sail is pulled close
– how intense the sail is flapping in the wind
The last one is not really a sail related parameter, but it is passed to other clients because it affects the appearance of the boat.
The message also contains:
– the position of the traveler
– angle of the keel
– position of the foils
The update is always for all the sails that are (partially) raised and it is send every 30 seconds.
This interval is rather big, but as soon as one of these values changes the interval is gradually reduced to a minimum interval of 1 second.
The messages are send to every boat within a rectangle of 1.2 degrees longitude and 0.8 degrees latitude.
A boat can not be shown on the screen of another user until this message is received at least once.
Multiple players on 1 boat
The computers of all online crew members compute their own speed, heel, etc. which is fine for the periods in between updates from the server, but only 1 client can determine the actual position and other parameters of the boat. This client is identified as the skipper. There can always be only 1 skipper. Usually this is the owner of the boat, but when he is not online a random other crew member will immediately take over. If the current skipper doesn’t log off correctly, the game will still assign a new skipper after a while.
The skipper has a few more authorizations than the other crew members. He can change the skills level (unless this is determined by the race or challenge), the auto navigation and the settings for offline sailing.
Only the owner of the boat (when online he is also the skipper) can change the appearance of the boat and can edit waypoints.
The skipper will send periodic position updates for the boat, that are received by other boats, but also by the crew members. The position updates are used by the crew’s computers to correct the position, heading, heel, dive, lift of the boat. These corrections will to be minimal since all computers use the same program, the same wind, the same waves and the same time. Any corrections are implemented with respect to physics and the mass of the boat.
Any crew member can change a control or steer the boat. But when they do, the control first needs to be locked. This lock message is broadcasted to the other crew members to notify them that the control or the helm is currently locked and prevent them from trying to pull the same line or steer at the same time.
When the control is locked, the client will send new settings to the rest of the crew while the line is pulled, the foils are extended or the rudder is turned. When the control is released, the control is made available again by a release message to the rest of the crew.
The skipper’s system will periodically (every 45 seconds, but never less than for 4 seconds after someone changed a control), send out a full set of all the control settings for the boat to the crew. This way it is guaranteed that all crew members share the same settings. The update also includes all sit positions of the crew members, the auto navigation settings, the skills level and the offline sailing mode.
Whenever a crew member comes online, the data for the boat is pulled from the Sailaway database. But as the crew is constantly changing settings, this information may be a bit behind. As soon as the current skipper of the boat receives a message that a crew member came online, a full parameters update, including the most recent position of the boat is send to the crew.