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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user