Analysis of WWC In Boats

This article is a preliminary analysis of the workings of boats that use the WWC wind system (Wind, Waves, Current). It’s purpose is to give an overview of what environmental calculation occurs as part of the WWC system and, in particular, to examine how the boats communicate with each other.

In the WWC system, all wind, wave and current calculation, as well as inter-boat communications, are handled by the WWC Interpreter script. The base environmental parameters are acquired from a WWC setter by the WWC Receiver script and passed to the Interpreter.

  • In the Interpreter, the running of calculation and communication procedures is managed by a timer which has a rate of 1 second.

In the timer event, there are calls to functions which calculate the wind, wave and current values at that moment in time. When the values have been calculated, they are sent to the boat’s sailing engine by one of two possible means…

  • A link message (intra-object communication).
  • Writing them into the object’s description field.

The sailing engine then either responds to the link message when its link_message event is triggered, or, during its own timer event, reads the data that has been written into the object description. Once it has acquired this data, the sailing engine does whatever it does to make the boat sail (applies physics to make it move at the desired speed, roll and pitch over waves, etc).

Also in the timer event, after sending the environmental data, is the broadcast in chat, containing data such as boat position – whatever is needed by the system to calculate inter-boat wind effects like shadow.

All boats within chat range listen on the same chat channel for broadcasts from other boats. When a broadcast is received, the inter-boat effects pertaining to that broadcast are calculated. Note that, although these effects are caclulated as they come in (the events are sequential – only one can be calculated at any given time), they are not applied to the boat immediately, but added to a list for later processing.

In the timer event, the sequence of the calls to the calculation procedures is as follows…

getLocalFluctuation   // every second timer cycle
calcCurrent           // every third timer cycle

Note that two procedures, getLocalFluctuation and calcCurrent, are not called every timer cycle. getLocalFluctuation is called every second cycle, and calcCurrent every third cycle, so every 2 seconds and every 3 seconds respectively. The results of the wind shadow calculations that were done as broadcasts from other boats were received are incorporated into the calcWind function. So, even though the broadcasts are processed as the script works through the chat queue, the resulting inter-boat wind effects are only applied once every second.

After doing the calculations, the Interpreter sends the resulting data to the sailing engine…

   llMessageLinked(LINK_SET, msgTypePassInfo, data, NULL_KEY);
   llSetObjectDesc( data );

msgTypePassInfo is a number.

  • If it is greater than zero, then a link message containing the data is sent to all prims in the boat.
  • If it equals zero, then the data is written into the object description field of the root prim. This will subsequently be read by the sailing engine when its own timer event next triggers.

The next part of the WWC Intrerpreter script’s timer event handles the chat broadcast to facilitate inter-boat wind effects. Here’s is the code directly from the script…

if(sailingMode==vehicleSailing && llFabs((float)apparentWindDir)>20)

Let’s break this down step by step.

if(sailingMode==vehicleSailing && llFabs((float)apparentWindDir)>20)

In this line, the script wants to know if the boat is sailing, and if it is not in irons. If both of these conditions are true, it then tests another condition…


++sendBtPosCounter adds 1 to a counter every timer cycle (1 second) and the script then tests to see if the new value of the counter is greater than the result of a calculation…

sendBtPosInterval + ( llGetListLength(shadowingBoats) / 4 )

sendBtPosInterval has a fixed value of 3. llGetListLength is an LSL function which tells us the number of items in a list. In this case, the list is called shadowingBoats, and it this that contains the results calculated from data received from other boats. For each boat that is shadowing our boat, this list has three elements. So, if we were being shadowed by two boats, llGetListLength would return a value of 6. Continuing with this example of two boats, the calculation works out like this…

3 + ( 6 / 4 )

The part in brackets is calculated first, and then added to the 3. In this case, the result is 3 + 1.5 = 4.5. Rather it would be, if it wasn’t for the fact that the values are stored in integers – a data type that has nothing after the decimal point. In other words, if the result of the calculation has non-zero numbers after the point, they are disregarded. Our value of 4.5 becomes 4.

So, if we are sailing and not in irons, the script increments the counter and tests to see if it is larger than the result of a calculation based on a fixed value plus a value derived from the number of shadowing boats. If the tested condition is true, the following two lines of code are executed…


The first line resets the counter to zero, ready for the next round of testing against the calculated value. The second line calls the function that sends our boat’s broadcast, to be received by any nearby boats that are within chat range.

From this, the frequency of the broadcasts can be determined – the counter is incremented once every timer cycle (if we are sailing and not in irons). Furthermore, because the counter is compared to a value derived from the number of shadowing boats, the value, and therefore the frequency of broadcasts, can vary.

It is important here to note two things. Firstly, the calculated value is never less than 3 – the result of the calculation is added to a constant. Secondly, the comparison that is made checks whether the counter is greater than the constant+calculation.

  • This means that the boat’s position broadcast can never happen more often than every fourth timer cycle – every 4 seconds.

When the number of shadowing boats is accounted for, the effect on the broadcast frequency can be determined…

Shadowing   Broadcast
Boats       Frequency
 0              4
 1              4
 2              5
 3              6
 4              7
 5              7
 6              8
 7              9
 8             10
 9             10
10             11

A number of conclusions can be drawn from this…

  • A WWC boat sailing on its own broadcasts every 4 seconds.
  •  If we are in a race start with 5 boats shadowing us, our boat broadcasts every 7 seconds.
  • If the boat immediately upwind of us is being shadowed by 4 boats, it broadcasts to our boat every 7 seconds.

Clearly, many similar possibilities can be derived.

It is reasonable to wonder why this ‘broadcast throttling’ has been implemented, and it seems reasonable to suppose that the purpose is to reduce lag, either in terms of server load due to chat traffic that might become excessive, or within the WWC Interpreter script as it tries to handle incoming broadcasts from other boats and calculate the results. (The reduced frequency of calculating the effects of local fluctuation and current may have been implemented for similar reasons.)

Whatever the reason, it does seem that the WWC broadcast frequency for inter-boat wind effects leans towards being somewhat granular. With a minimum frequency of 4 seconds, it would be possible for a boat to sail through another boat’s shadow, and only receive a broadcast when it is clear. In situations where several boats are shadowing each other, this effect would be exaggerated as their broadcasts become less frequent.



Comments are closed.