public override int UniformRunLength(int irun) { TsRunInfo info; Tss.FetchRunInfo(irun, out info); return(info.ichLim - info.ichMin); }
/// <summary> /// Creates a new server listening on the specified port and allowing /// for the specified number of players. /// </summary> /// <param name="port">The port to listen on.</param> /// <param name="maxPlayers">The maximum number of players supported.</param> /// <param name="commandHandler">The command handler to use in the /// simulation.</param> public SimpleServerController(ushort port, int maxPlayers, CommandHandler commandHandler) : base(new HybridServerSession <TPlayerData>(port, maxPlayers)) { var simulation = new DefaultSimulation(); simulation.Command += commandHandler; Tss.Initialize(simulation); }
/// <summary> /// Creates a new game client, ready to connect to an open game. /// </summary> /// <param name="commandHandler">The command handler to use.</param> public SimpleClientController(CommandHandler commandHandler) : base(new HybridClientSession <TPlayerData>()) { var simulation = new DefaultSimulation(); simulation.Command += commandHandler; Tss.Initialize(simulation); }
public override string UniformRunText(int irun) { TsRunInfo info; Tss.FetchRunInfo(irun, out info); var result = Tss.GetChars(info.ichMin, info.ichLim); if (result == null) { return(""); } return(result); }
/// <summary> /// Return an otherwise equivalent Tss client run that has the specified Contents. /// Subclasses should override to return the appropriate subclass and copy any additional information. /// </summary> internal override TssClientRun CopyWithNewContents(ITsString newContents) { ITsString tss = newContents; int var; if (tss == null) { tss = TsStrFactoryClass.Create().EmptyString(Tss.get_Properties(0).GetIntPropValues((int)FwTextPropType.ktptWs, out var)); } var result = new SubstituteTssClientRun(tss, Style, m_substitute, m_substituteStyle); result.Hookup = Hookup; return(result); }
public override AssembledStyles UniformRunStyles(int irun) { ITsTextProps props = Tss.get_Properties(irun); return(Style.ApplyTextProps(props)); }
/// <summary> /// Takes care of client side TSS synchronization logic. /// </summary> protected override FrameCommand UnwrapDataForReceive(SessionDataEventArgs e) { var args = (ClientDataEventArgs)e; var type = (TssControllerMessage)args.Data.ReadByte(); switch (type) { case TssControllerMessage.Command: { // Normal command, forward it. var command = base.UnwrapDataForReceive(e); // Test if we got the message from the server, to mark the command accordingly. command.IsAuthoritative = args.IsAuthoritative; // Return the deserialized command. return(command); } case TssControllerMessage.Synchronize: { // Answer to a synchronization request. // Only accept these when they come from the server, and disregard if // we're waiting for a snapshot of the simulation. if (args.IsAuthoritative && !Tss.WaitingForSynchronization) { // This calculation follows algorithm described here: // http://www.mine-control.com/zack/timesync/timesync.html var sentFrame = args.Data.ReadInt64(); var serverFrame = args.Data.ReadInt64(); // We also adjust the game speed to accommodate slow // machines. That's the speed we get in this step. AdjustedSpeed = args.Data.ReadSingle(); var latency = (Tss.CurrentFrame - sentFrame) / 2; var clientServerDelta = (serverFrame - Tss.CurrentFrame); var frameDelta = clientServerDelta + latency / 2; _frameDiff.Put((int)frameDelta); var median = _frameDiff.Median(); var stdDev = _frameDiff.StandardDeviation(); if (System.Math.Abs(frameDelta) > 1 && frameDelta < (int)(median + stdDev)) { Logger.Trace("Correcting for {0} frames.", frameDelta); // Adjust the current frame of the simulation. ScheduleFrameskip(frameDelta); } } break; } case TssControllerMessage.HashCheck: { // Only accept these when they come from the server. if (args.IsAuthoritative) { // Get the frame this hash data is for. var hashFrame = args.Data.ReadInt64(); Debug.Assert(hashFrame > _lastServerHashedFrame); _lastServerHashedFrame = hashFrame; // Read hash values. var hashValue = args.Data.ReadUInt32(); // And perform hash check. PerformHashCheck(hashValue, hashFrame, Tss.TrailingFrame < _lastServerHashedFrame); } break; } case TssControllerMessage.GameState: { // Got a simulation snap shot (normally after requesting it due to // our simulation going out of scope for an older event). // Only accept these when they come from the server. if (args.IsAuthoritative) { // Read data. var serverHash = args.Data.ReadUInt32(); args.Data.ReadPacketizableInto(Tss); // Validate the data we got. var hasher = new Hasher(); hasher.Write(Tss.TrailingSimulation); if (hasher.Value != serverHash) { Logger.Error("Hash mismatch after deserialization."); Session.Leave(); } // Run to current frame to avoid slow interpolation to current frame. // Take into account the time we need to get there. var delta = 0L; do { // Remember when we started, see below. var started = DateTime.UtcNow; // Do the actual update, run to where we want to be. Tss.RunToFrame(Tss.CurrentFrame + delta); // See how long we took for this update, and compute the number // of updates we'd otherwise have made in that time. delta = (long)((DateTime.UtcNow - started).TotalMilliseconds / TargetElapsedMilliseconds); // Continue until we're close enough with the *actual* current // frame to what we want to be at. } while (delta > 10); } break; } case TssControllerMessage.RemoveGameObject: { // Only accept these when they come from the server. if (args.IsAuthoritative) { var removeFrame = args.Data.ReadInt64(); var entityUid = args.Data.ReadInt32(); Tss.RemoveEntity(entityUid, removeFrame); } break; } } return(null); }
/// <summary> /// A server sent us a response to our request to join his game. /// </summary> /// <param name="sender">the underlying session.</param> /// <param name="e">information of the type <c>JoinResponseEventArgs</c>.</param> private void HandleJoinResponse(object sender, JoinResponseEventArgs e) { // OK, we were allowed to join, invalidate our simulation to request // the current state. Tss.Invalidate(); }