Skip to content

Commit

Permalink
Fixed issue #4 where outbound messages were being timestamped with
Browse files Browse the repository at this point in the history
System.nanoTime() even if that was not the time basis used in the
object update notifications.  Fixing this required threading a
server-side TimeSource from EtherealHost down through to StateWriter.
In the process, TimeSource was refactored to just be a TimeSource.
A new SynchedTimeSource extends it to provide the drift-related
methods... mostly in case clients care to monitor those values for
some reason.
RemoteTimeSource now implements SynchedTimeSource.
  • Loading branch information
pspeed42 committed Nov 17, 2018
1 parent c2e6993 commit c6272ff
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 60 deletions.
8 changes: 5 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ buildscript {
apply plugin: 'java'
apply plugin: 'maven'

version='1.2.2-SNAPSHOT'
version='1.3.0-SNAPSHOT'
group='com.simsilica'

ext.jmeVersion = "3.1.0-alpha4"
ext.jmeVersion = "3.1.0-stable"

// Version meta-data
ext {
Expand All @@ -34,7 +34,9 @@ ext {
issueTrackerUrl = 'https://github.com/Simsilica/SimEthereal/issues'
}

sourceCompatibility = 1.7
compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}

Expand All @@ -53,7 +55,7 @@ dependencies {
compile "org.jmonkeyengine:jme3-networking:$jmeVersion"

// Uses SimMath for bit streaming utilities and transition buffers
compile "com.simsilica:sim-math:1.1.2-SNAPSHOT"
compile "com.simsilica:sim-math:1.2.0-SNAPSHOT"

// Base logging
compile 'org.slf4j:slf4j-api:1.7.15'
Expand Down
11 changes: 9 additions & 2 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
Version 1.2.2 (unreleased)
Version 1.3.0 (unreleased)
--------------
* Fixed a bug where the newer state messages would fail if the game hadn't
already registered Vec3d as a serializable class.
* Fixed NetworkStateListener to properly pay attention to zone extents.
* Fixed EtherealHost to pass the specified client zone extents on to the
NetworkStateListener.
* Added additional trace logging to NetworkStateListener.
* Upgraded to sim-math 1.1.1
* Fixed an issue where raw nanoTime() was being used to timestamp messages.
Now applications can specify their own TimeSource on the server.
See issue #4.
* Refactored the TimeSource interface to be just a time provider and added
a SynchedTimeSource interface to provide the extra drift/offset methods
that it previously had. RemoteTimeSource now implements SynchedTimeSource.
* Upgraded to sim-math 1.2.0
* Set sourceCompatibility to 1.7


Version 1.2.1
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/simsilica/ethereal/EtherealHost.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ public class EtherealHost extends AbstractHostedConnectionService {

private SessionDataDelegator delegator;

/**
* This is the time source that will be used for timestamping outbound
* object state messages. This time source should be compatible with whatever
* is providing time to the object update messages. Otherwise, clients will
* see a wide difference between what they see from RemoteTimeSource and
* the timestamps in the object updates.
*/
private TimeSource timeSource = new NanoTimeSource();

public EtherealHost() {
this(new ObjectStateProtocol(8, 64, new Vec3Bits(-10, 42, 16), new QuatBits(12)),
new ZoneGrid(32), new Vec3i(1, 1, 1));
Expand All @@ -93,6 +102,23 @@ public EtherealHost( ObjectStateProtocol objectProtocol, ZoneGrid grid, Vec3i cl
Serializer.registerClass(Vec3d.class, new FieldSerializer());
}

/**
* Returns the aurhoritative time source on the server. This should match the timestamps
* that are being supplied in object update notifications.
*/
public TimeSource getTimeSource() {
return timeSource;
}

/**
* Returns the aurhoritative time source on the server. This should match the timestamps
* that are being supplied in object update notifications. Applications should set their
* own timesource if they are using a different time tracking basis than System.nanoTime().
*/
public void setTimeSource( TimeSource timeSource ) {
this.timeSource = timeSource == null ? new NanoTimeSource() : timeSource;
}

public ZoneManager getZones() {
return zones;
}
Expand Down
27 changes: 2 additions & 25 deletions src/main/java/com/simsilica/ethereal/NanoTimeSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,42 +38,19 @@


/**
* Provides local or networked time as needed with certain
* implied constraints (like time always moves forward, ie:
* never backwards, etc.)
* A TimeSource that directly returns System.nanoTime() as the time.
*
* @author Paul Speed
*/
public class NanoTimeSource implements TimeSource {

private long offset;

public NanoTimeSource() {
}

public NanoTimeSource( long offset ) {
this.offset = offset;
}

public void setOffset( long offset ) {
this.offset = offset;
}

public long getOffset() {
return offset;
}

/**
* Returns the current time in nanoseconds.
*/
public long getTime() {
return System.nanoTime() + offset;
}

/**
* Returns 0.
*/
public long getDrift() {
return 0;
return System.nanoTime();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public NetworkStateListener( EtherealHost host, HostedConnection conn, LocalZone
this.zoneIndex = zoneIndex;
this.idIndex = idIndex;
this.space = new SharedObjectSpace(host.getObjectProtocol());
this.stateWriter = new StateWriter(conn, host.getObjectProtocol(), stats);
this.stateWriter = new StateWriter(conn, host.getObjectProtocol(), host.getTimeSource(), stats);
}

public void setSelf( Long self, Vec3d startingPosition ) {
Expand Down
70 changes: 70 additions & 0 deletions src/main/java/com/simsilica/ethereal/SynchedTimeSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* $Id$
*
* Copyright (c) 2018, Simsilica, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.simsilica.ethereal;


/**
* A time source that represents time that has been synched with
* another time source relative to local system time. It has
* methods that indicate the amount of drift, offset, etc. that
* maps local system time to TimeSource time.
*
* @author Paul Speed
*/
public interface SynchedTimeSource extends TimeSource {

/**
* Returns the current drift if there is one. In a remote
* time source, the drift is the offset from current time
* and is related in a loose way to both the ping time of the
* server and the general time difference of the server.
*/
public long getDrift();

/**
* Sets a time that will be automatically added into the
* normally returned time. This allows getTime() to return
* time in the past for interpolating object state buffers.
*/
public void setOffset( long offset );

/**
* Returns the current time offset for this TimeSource.
*/
public long getOffset();

}
21 changes: 0 additions & 21 deletions src/main/java/com/simsilica/ethereal/TimeSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,4 @@ public interface TimeSource {
* Returns the current time in nanoseconds.
*/
public long getTime();

/**
* Returns the current drift if there is one. In a remote
* time source, the drift is the offset from current time
* and is related in a loose way to both the ping time of the
* server and the general time difference of the server.
*/
public long getDrift();

/**
* Sets a time that will be automatically added into the
* normally returned time. This allows getTime() to return
* time in the past for interpolating object state buffers.
*/
public void setOffset( long offset );

/**
* Returns the current time offset for this TimeSource.
*/
public long getOffset();

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import com.simsilica.ethereal.DebugUtils;
import com.simsilica.ethereal.Statistics;
import com.simsilica.ethereal.Statistics.Sequence;
import com.simsilica.ethereal.TimeSource;
import com.simsilica.ethereal.SynchedTimeSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -52,7 +52,7 @@
*
* @author Paul Speed
*/
public class RemoteTimeSource implements TimeSource {
public class RemoteTimeSource implements SynchedTimeSource {

static Logger log = LoggerFactory.getLogger(RemoteTimeSource.class);

Expand Down Expand Up @@ -102,7 +102,7 @@ public long getOffset() {
}

protected void updateDrift( long serverTime ) {

syncTime.add(serverTime);

this.lastServerTime = serverTime;
Expand Down
33 changes: 28 additions & 5 deletions src/main/java/com/simsilica/ethereal/net/StateWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,21 @@

package com.simsilica.ethereal.net;

import com.jme3.network.HostedConnection;
import com.simsilica.ethereal.ConnectionStats;
import com.simsilica.ethereal.zone.ZoneKey;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;

import org.slf4j.*;

import com.jme3.network.HostedConnection;

import com.simsilica.ethereal.ConnectionStats;
import com.simsilica.ethereal.TimeSource;
import com.simsilica.ethereal.zone.ZoneKey;


/**
* Buffers the state information, flushing it to real messages
Expand All @@ -55,6 +60,8 @@
*/
public class StateWriter {

static Logger log = LoggerFactory.getLogger(StateWriter.class);

/**
* The connection to send state through.
*/
Expand Down Expand Up @@ -91,12 +98,18 @@ public class StateWriter {
private int nextMessageId = 0;
private int headerBits;
private int estimatedSize;

// Time source we will use for timestamping outbound messages.
// This time should be compatible with the times provided by the
// frame updates or things will get weird.
private TimeSource timeSource;

private ConnectionStats stats;

public StateWriter( HostedConnection conn, ObjectStateProtocol objectProtocol, ConnectionStats stats ) {
public StateWriter( HostedConnection conn, ObjectStateProtocol objectProtocol, TimeSource timeSource, ConnectionStats stats ) {
this.conn = conn;
this.objectProtocol = objectProtocol;
this.timeSource = timeSource;
this.stats = stats;
}

Expand Down Expand Up @@ -187,6 +200,15 @@ public SentState ackSentState( int messageId ) {
}

public void startFrame( long time, ZoneKey centerZone ) throws IOException {

// Watchdog to check for mismatched time sources.
long delta = Math.abs(time - timeSource.getTime());
if( delta > 1000000000 ) {
// more then a second difference means they are waaaaaay off.
// Even a ms difference would be large.
log.warn("Mismatched time sources. Delta:" + (delta/1000000000.0) + " seconds");
}

// End any previous frame that we might be in the middle of
endFrame();

Expand Down Expand Up @@ -409,7 +431,8 @@ protected void endFrame() throws IOException {

protected void endMessage() throws IOException {

long timestamp = System.nanoTime();
//long timestamp = System.nanoTime();
long timestamp = timeSource.getTime();
int id = nextMessageId++;

ObjectStateMessage msg = new ObjectStateMessage(id, timestamp, null);
Expand Down

0 comments on commit c6272ff

Please sign in to comment.