net.kano.joscar.snaccmd.conn
Class RateClassInfo

java.lang.Object
  extended byRateClassInfo
All Implemented Interfaces:
LiveWritable, Writable

public class RateClassInfo
extends java.lang.Object
implements Writable

A data structure containing rate limiting information for a specific "class" of SNAC commands.

Introduction to Rates

A rate class identifies a set of SNAC commands and limitations on how fast any sequence of those commands can be sent to the server. For example, one rate class normally contains the outgoing ICBM command and the info request command. You may have noticed that sometimes WinAIM will tell you you can't look at someone's info because your rate is too high; this is why. Gaim, of course, simply tells you to stop talking so fast. Good one, Gaim.

The RateClassInfo Fields

windowSize
The number of previously sent commands that will be included in the calculation of your current "rate average" (this value varies from rate class to rate class; normally ranges from 10 to 60)
currentAvg
Your current "rate average," which attempts to resemble a moving average of the times between each of your last windowSize commands
warnAvg
The "rate average" that will put you into the yellow part of WinAIM's rate limiting bar (normally 5000 ms)
limitedAvg
The "rate average" under which you will be "rate limited" until your rate average is back above clearAvg (normally 4000 ms)
clearAvg
The "rate average" above which you will stop being rate limited, if you are currently limited; this is normally equal to warnAvg plus 100 ms, or 5100 ms
disconnectAvg
The "rate average" below which you will be disconnected from the server (normally 3000 ms)
max
The maximum value for a rate average (normally 6000 ms)

Handling Rate-Related SNAC Commands

The values described above can be used to keep an accurate rate average for a rate class as follows:

Upon initial connection, the average should be ignored: all commands can be sent as quickly as possible until a RateAck is sent.

Upon receiving a RateInfoCmd, the current rate for each rate class within should be set to that rate class's maximum rate average. Note that a current average is sent, but should be ignored in the initial RateInfoCmd.

Upon receiving a RateChange, nothing must be modified (unless the maximum rate average has been decreased and the current rate is now above it). You may want to set your current average to the given "current average," but this is not advised, as the rate change command may have been sent in response to a command sent several commands ago (due to network lag). A good way to do this might be to only set your rate average to the given "current average" only if the given current average is lower than your client's computed average. This should be the most conservative and thus reliable way to handle rate changes.

Computing the Current Rate

Now the important part: how to compute the current average for a rate class.

The current average for a rate class is computed cumulatively. Essentially, the algorithm goes like this:
void computeNewAvg(long lastSent, long oldAvg,
            RateClassInfo rateClassInfo) {
    long curTime = System.currentTimeMillis();
    long diff = curTime - lastSent;

    long winSize = rateClassInfo.getWindowSize();
    long maxAvg = rateClassInfo.getMax();

    currentAvg = ((currentAvg * (winSize - 1))
            + diff) / winSize;

    if (currentAvg > maxAvg) currentAvg = maxAvg;
}
 
Using such an algorithm produces results almost exactly consistent with the "current averages" sent in RateChange packets, often within a margin of one or two milliseconds (out of an average of 5000 ms or lower). (This margin of error is surely due to network traffic and not an error in the algorithm's above implementation.)

Being "Rate Limited"

If commands are sent so fast as to bring the current rate average below the limited average, the server will send a RateChange with a change code of RateChange.CODE_LIMITED. After this happens, all of the commands sent in the associated rate class will be ignored by the server until the rate reaches the "clear average", at which point a RateChange with a change code of RateChange.CODE_LIMIT_CLEARED may be sent (though it is not usually sent). Once the average is above the clear average, however, all is back to normal, as if limiting had never taken place.


Constructor Summary
RateClassInfo(int rateClass, long windowSize, long clearAvg, long warnAvg, long limitedAvg, long disconnectAvg, long currentAvg, long max)
          Creates a new rate class information block with the given properties.
 
Method Summary
 long getClearAvg()
          Returns the rate average above which the user is no longer rate limited.
 CmdType[] getCommands()
          Returns the commands included in this rate class, or null if they were not sent (as in a RateChange).
 long getCurrentAvg()
          Returns the user's current rate average in this rate class.
 long getDisconnectAvg()
          Returns the rate average below which the user will be disconnected.
 long getLimitedAvg()
          Returns the rate average below which the user is rate-limited.
 long getMax()
          Returns the maximum rate average for this rate class.
 int getRateClass()
          Returns the ID of the rate class that holds this rate class info.
 long getWarnAvg()
          Returns the rate average below which the user is "warned."
 long getWindowSize()
          Returns the "window size" of this rate class.
 long getWritableLength()
          Returns the length of the data that was or will be written in a call to write.
static RateClassInfo readRateClassInfo(ByteBlock block)
          Generates a rate class information block from the given block of data.
 java.lang.String toString()
           
 void write(java.io.OutputStream out)
          Writes a representation of this object to the given stream.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

RateClassInfo

public RateClassInfo(int rateClass,
                     long windowSize,
                     long clearAvg,
                     long warnAvg,
                     long limitedAvg,
                     long disconnectAvg,
                     long currentAvg,
                     long max)
Creates a new rate class information block with the given properties. See above for details on what these mean.

Parameters:
rateClass - the rate class ID that this block describes
windowSize - the "window size"
clearAvg - the "not rate limited anymore" average
warnAvg - the "warned" average
limitedAvg - the "rate limited" average
disconnectAvg - the "disconnected" average
currentAvg - the current average
max - the maximum rate average
Method Detail

readRateClassInfo

public static RateClassInfo readRateClassInfo(ByteBlock block)
Generates a rate class information block from the given block of data. The total number of bytes read can be accessed by calling the getTotalSize method of the returned RateClassInfo.

Parameters:
block - a block of data containing a rate information block
Returns:
a rate class information object read from the given block of data

getRateClass

public final int getRateClass()
Returns the ID of the rate class that holds this rate class info.

Returns:
this rate class information block's rate class ID

getWindowSize

public final long getWindowSize()
Returns the "window size" of this rate class. See above for more details.

Returns:
the rate class's window size

getWarnAvg

public final long getWarnAvg()
Returns the rate average below which the user is "warned." See above for more details.

Returns:
the "warned rate average"

getLimitedAvg

public final long getLimitedAvg()
Returns the rate average below which the user is rate-limited. No commands should be sent to the server in this rate class until the rate average is above the clear average. See above for more details.

Returns:
the rate-limited rate average

getClearAvg

public final long getClearAvg()
Returns the rate average above which the user is no longer rate limited. See above for more details.

Returns:
the rate class's "cleared of rate limiting" average

getDisconnectAvg

public final long getDisconnectAvg()
Returns the rate average below which the user will be disconnected. See above for more details.

Returns:
the disconnect rate average

getCurrentAvg

public final long getCurrentAvg()
Returns the user's current rate average in this rate class. See above for more details.

Returns:
the user's current rate average.

getMax

public final long getMax()
Returns the maximum rate average for this rate class. See above for more details.

Returns:
the maximum rate average in this class

getCommands

public final CmdType[] getCommands()
Returns the commands included in this rate class, or null if they were not sent (as in a RateChange).

Returns:
the SNAC command types included in this rate class

getWritableLength

public long getWritableLength()
Description copied from interface: Writable
Returns the length of the data that was or will be written in a call to write. The value returned by this method must not change after its first invocation.

Specified by:
getWritableLength in interface Writable
Returns:
the length of the data to be written by write

write

public void write(java.io.OutputStream out)
           throws java.io.IOException
Description copied from interface: Writable
Writes a representation of this object to the given stream. The length of the data written by this function must match any value previously returned by getWritableLength.

Specified by:
write in interface Writable
Parameters:
out - the stream to which to write
Throws:
java.io.IOException - if an I/O error occurs

toString

public java.lang.String toString()