Looks like much of the speedmodding in Puzzle Pirates comes from futzing with

the speed of the system timer, but that leaves System.currentTimeMillis alone.
If the precise timer is moving way faster or way slower than currentTimeMillis,
tack our timing to currentTimeMillis.  This will make timing precision blow,
but that's what you get for cheating. 



git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@424 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Charlie Groves
2008-02-20 02:28:57 +00:00
parent 16e17444c7
commit 4c002c5682
3 changed files with 114 additions and 45 deletions
@@ -0,0 +1,103 @@
package com.threerings.media.timer;
import com.samskivert.Log;
/**
* Calibrates timing values from a subclass' implementation of current against those returned by
* System.currentTimeMillis. If the subclass timer is moving 1.25 times faster or .75 times slower
* than currentTimeMillis, hold it to the values from currentTimeMillis.
*/
public abstract class CalibratingTimer
implements MediaTimer
{
/**
* Initializes this timer. Must be called before the timer is used.
*
* @param milliDivider - value by which current() must be divided to get milliseconds
* @param microDivider - value by which current() must be divided to get microseconds
*/
protected void init (long milliDivider, long microDivider)
{
_milliDivider = milliDivider;
_microDivider = microDivider;
reset();
Log.info("Using " + getClass() + " timer [mfreq=" + _milliDivider + ", ufreq="
+ _microDivider + ", start=" + _startStamp + "].");
}
// documentation inherited from interface
public long getElapsedMicros ()
{
return elapsed() / _microDivider;
}
// documentation inherited from interface
public long getElapsedMillis ()
{
return elapsed() / _milliDivider;
}
// documentation inherited from interface
public void reset ()
{
_startStamp = current();
_driftMilliStamp = System.currentTimeMillis();
_driftTimerStamp = current();
_callsSinceCalibration = 0;
}
/** Returns the difference between _startStamp and current() */
protected long elapsed ()
{
if (_callsSinceCalibration++ > 1000) {
calibrate();
}
return (long)(((current() - _startStamp)) / _driftFactor);
}
/** Calculates the drift factor from the time elapsed from the last calibrate call. */
protected void calibrate ()
{
long elapsedNanos = (current() - _driftTimerStamp);
double elapsedMillis = System.currentTimeMillis() - _driftMilliStamp;
double drift = (elapsedNanos / 1000000L) / elapsedMillis;
if (drift > 1.25 || drift < 0.75) {
if (_driftFactor == 1.0) {
Log.warning("Calibrating [drift=" + drift + "]");
}
_driftFactor = drift;
} else if (_driftFactor != 1.0) {
// If we're within bounds now but we weren't before, reset _driftFactor and log it
_driftFactor = 1.0;
Log.warning("Calibrating [drift=" + drift + "]");
}
_driftMilliStamp = System.currentTimeMillis();
_driftTimerStamp = current();
_callsSinceCalibration = 0;
}
/** Return the current value for this timer. */
public abstract long current ();
/** current() value when the timer was started. */
protected long _startStamp;
/** Amount by which current() should be divided to get milliseconds. */
protected long _milliDivider;
/** Amount by which current() should be divided to get microseconds. */
protected long _microDivider;
/** currentTimeMillis() value from the last time we called calibrate. */
protected long _driftMilliStamp = System.currentTimeMillis();
/** current() value from the last time we called calibrate. */
protected long _driftTimerStamp;
/** Factor by which the timer values are drifting from currentTimeMillis. */
protected double _driftFactor = 1.0;
/** Number of calls to elapsed since we've called calibrate. */
protected int _callsSinceCalibration = 0;
}
@@ -25,26 +25,16 @@ package com.threerings.media.timer;
* Uses the {@link System#nanoTime} method introduced in 1.5 to try to obtain higher resolution
* timestamps than those available via {@link System#currentTimeMillis}.
*/
public class NanoTimer implements MediaTimer
public class NanoTimer extends CalibratingTimer
{
// documentation inherited from interface
public void reset ()
public NanoTimer ()
{
_resetStamp = System.nanoTime();
init(1000000, 1000);
}
// documentation inherited from interface
public long getElapsedMillis ()
@Override
public long current ()
{
return (System.nanoTime() - _resetStamp) / 1000000L;
return System.nanoTime();
}
// documentation inherited from interface
public long getElapsedMicros ()
{
return (System.nanoTime() - _resetStamp) / 1000L;
}
/** The time at which this timer was last reset. */
protected long _resetStamp = System.nanoTime();
}
@@ -23,47 +23,23 @@ package com.threerings.media.timer;
import sun.misc.Perf;
import com.threerings.media.Log;
/**
* A timer that uses the performance clock exposed by Sun in JDK 1.4.2.
*/
public class PerfTimer implements MediaTimer
public class PerfTimer extends CalibratingTimer
{
public PerfTimer ()
{
_timer = Perf.getPerf();
_mfrequency = _timer.highResFrequency() / 1000;
_ufrequency = _timer.highResFrequency() / 1000000;
_startStamp = _timer.highResCounter();
Log.info("Using high performance timer [mfreq=" + _mfrequency +
", ufreq=" + _ufrequency + ", start=" + _startStamp + "].");
init(_timer.highResFrequency() / 1000, _timer.highResFrequency() / 1000000);
}
// documentation inherited from interface
public void reset ()
@Override
public long current ()
{
_startStamp = _timer.highResCounter();
}
// documentation inherited from interface
public long getElapsedMillis ()
{
return (_timer.highResCounter() - _startStamp) / _mfrequency;
}
// documentation inherited from interface
public long getElapsedMicros ()
{
return (_timer.highResCounter() - _startStamp) / _ufrequency;
return _timer.highResCounter();
}
/** A performance timer object. */
protected Perf _timer;
/** The time at which this timer was last reset. */
protected long _startStamp;
/** The timer frequencies. */
protected long _mfrequency, _ufrequency;
}