示例#1
0
 public ClientState(Guid clientGuid, string userInfo)
 {
     ClientGuid     = clientGuid;
     UserInfo       = userInfo;
     AckSnapshotID  = 0;
     LastCommandID  = 0;
     CommandCounter = 0;
     SnapshotQueue  = new SnapshotQueue();
 }
			/// <summary>
			/// Creates instance of Active client state.
			/// </summary>
			/// <param name="gameClient"></param>
			/// <param name="snapshotId"></param>
			/// <param name="initialSnapshot"></param>
			public Active ( GameClient gameClient, uint snapshotId, byte[] initialSnapshot, long svTicks ) : base(gameClient, ClientState.Active)
			{
				queue	=	new SnapshotQueue(32);
				queue.Push( new Snapshot( new TimeSpan(0), snapshotId, initialSnapshot) );

				lastServerTicks		=	svTicks;
				lastSnapshotID		=	snapshotId;
				lastSnapshotFrame	=	snapshotId;

				Message				=	"";


				#if USE_DEJITTER
				jitter		=	new JitterBuffer( gameClient.Game, svTicks );
				#endif


				stopwatch	=	new Stopwatch();
				stopwatch.Start();
				clientTicks	=	stopwatch.Elapsed.Ticks;


				gameClient.FeedSnapshot( new GameTime(0,svTicks,0L), initialSnapshot, 0 );
			}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="msg"></param>
		void DispatchUserCommand ( GameTime gameTime, SnapshotQueue queue, NetIncomingMessage msg )
		{	
			var snapshotID	=	msg.ReadUInt32();
			var commandID	=	msg.ReadUInt32();
			var size		=	msg.ReadInt32();

			var data		=	msg.ReadBytes( size );

			//	we got user command and (command count=1)
			//	this means that client receives snapshot:
			if (msg.SenderConnection.GetCommandCount()==1) {
				ClientActivated( msg.SenderConnection.GetHailGuid() );
			}

			//	do not feed server with empty command.
			if (data.Length>0) {
				FeedCommand( msg.SenderConnection.GetHailGuid(), data, commandID, queue.GetLag(snapshotID, gameTime) );
			}

			//	set snapshot request when command get.
			msg.SenderConnection.SetRequestSnapshot( snapshotID, commandID );
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="msg"></param>
		void DispatchDataIM ( GameTime gameTime, SnapshotQueue queue, NetIncomingMessage msg )
		{
			var netCmd = (NetCommand)msg.ReadByte();

			switch (netCmd) {
				case NetCommand.UserCommand : 
					DispatchUserCommand( gameTime, queue, msg );
					break;

				case NetCommand.Notification :
					FeedNotification( msg.SenderConnection.GetHailGuid(), msg.ReadString() );
					break;
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="server"></param>
		void SendSnapshot ( NetServer server, SnapshotQueue queue, long serverTicks )
		{
			//	snapshot request is stored in connection's tag.s
			var debug	=	Game.Network.ShowSnapshots;
			var conns	=	server.Connections.Where ( c => c.IsSnapshotRequested() );

			var sw		=	new Stopwatch();

			foreach ( var conn in conns ) {

				sw.Reset();
				sw.Start();
					
				var frame		=	queue.LastFrame;
				var prevFrame	=	conn.GetRequestedSnapshotID();
				int size		=	0;
				var commandID	=	conn.GetLastCommandID();
				var snapshot	=	queue.Compress( ref prevFrame, out size);

				//	reset snapshot request :
				conn.ResetRequestSnapshot();

				var msg = server.CreateMessage( snapshot.Length + 4 * 4 + 8 + 1 );
			
				msg.Write( (byte)NetCommand.Snapshot );
				msg.Write( frame );
				msg.Write( prevFrame );
				msg.Write( commandID );
				msg.Write( serverTicks );
				msg.Write( snapshot.Length );
				msg.Write( snapshot ); 

				//	append atom table to first snapshot :
				if (commandID==0) {
					atoms.Write( msg );
				}

				//	Zero snapshot frame index means that client is waiting for first snapshot.
				//	and snapshot should reach the client.
				var delivery	=	prevFrame == 0 ? NetDeliveryMethod.ReliableOrdered : NetDeliveryMethod.UnreliableSequenced;

				if (prevFrame==0) {
					Log.Message("SV: Sending initial snapshot to {0}", conn.GetHailGuid().ToString() );
				}

				sw.Stop();

				server.SendMessage( msg, conn, delivery, 0 );

				if (debug) {
					Log.Message("Snapshot: #{0} - #{1} : {2} / {3} to {4} at {5} msec", 
						frame, prevFrame, snapshot.Length, size, conn.RemoteEndPoint.ToString(), sw.Elapsed.TotalMilliseconds );
				}
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="message"></param>
		void DispatchIM ( GameTime gameTime, SnapshotQueue queue, NetServer server )
		{
			NetIncomingMessage msg;
			while ((msg = server.ReadMessage()) != null)
			{
				switch (msg.MessageType)
				{
					case NetIncomingMessageType.VerboseDebugMessage:Log.Debug	("SV Net: " + msg.ReadString()); break;
					case NetIncomingMessageType.DebugMessage:		Log.Verbose	("SV Net: " + msg.ReadString()); break;
					case NetIncomingMessageType.WarningMessage:		Log.Warning	("SV Net: " + msg.ReadString()); break;
					case NetIncomingMessageType.ErrorMessage:		Log.Error	("SV Net: " + msg.ReadString()); break;

					case NetIncomingMessageType.ConnectionLatencyUpdated:
						if (Game.Network.ShowLatency) {
							float latency = msg.ReadFloat();
							Log.Verbose("...SV ping - {0} {1,6:0.00} ms", msg.SenderEndPoint, (latency*1000) );
						}

						break;

					case NetIncomingMessageType.DiscoveryRequest:
						Log.Message("Discovery request from {0}", msg.SenderEndPoint.ToString() );
						var response = server.CreateMessage( ServerInfo() );
						server.SendDiscoveryResponse( response, msg.SenderEndPoint );

						break;

					case NetIncomingMessageType.ConnectionApproval:
						
						var userGuid	=	msg.SenderConnection.GetHailGuid();
						var userInfo	=	msg.SenderConnection.GetHailUserInfo();
						var reason		=	"";
						var approve		=	ApproveClient( userGuid, userInfo, out reason );

						if (approve) {	
							msg.SenderConnection.Approve( server.CreateMessage( ServerInfo() ) );
						} else {
							msg.SenderConnection.Deny( reason );
						}

						break;

					case NetIncomingMessageType.StatusChanged:		
						DispatchStatusChange( msg );
						break;
					
					case NetIncomingMessageType.Data:
						DispatchDataIM( gameTime, queue, msg );
						break;
					
					default:
						Log.Warning("SV: Unhandled type: " + msg.MessageType);
						break;
				}
				server.Recycle(msg);
			}		
		}
		/// <summary>
		/// Updates everything related to network and game logic.
		/// </summary>
		void UpdateNetworkAndLogic ( GameTime svTime, NetServer server, SnapshotQueue snapshotQueue )
		{
			#if DEBUG
			server.Configuration.SimulatedLoss				=	Game.Network.SimulatePacketsLoss;
			server.Configuration.SimulatedMinimumLatency	=	Game.Network.SimulateMinLatency;
			server.Configuration.SimulatedRandomLatency		=	Game.Network.SimulateRandomLatency;
			#endif

			//	read input messages :
			DispatchIM( svTime, snapshotQueue, server );

			//	update pings :
			UpdatePings( server );

			//	update frame and get snapshot :
			var snapshot = Update( svTime );

			//	push snapshot to queue :
			snapshotQueue.Push( svTime.Total, snapshot );

			//	send snapshot to clients :
			SendSnapshot( server, snapshotQueue, svTime.Total.Ticks );

			//	send notifications to clients :
			SendNotifications( server );

			//	execute server's command queue :
			Game.Invoker.ExecuteQueue( svTime, CommandAffinity.Server );

			//	crash test for server :
			CrashServer.CrashTest();
			FreezeServer.FreezeTest();
			SlowdownServer.SlowTest();
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="map"></param>
		void ServerTaskFunc ( string map, string postCommand )
		{
			var netConfig		=	new NetPeerConfiguration(Game.GameID);
			netConfig.Port		=	Game.Network.Port;
			netConfig.MaximumConnections	=	32;
			netConfig.UnreliableSizeBehaviour = NetUnreliableSizeBehaviour.NormalFragmentation;
			
			if (Debugger.IsAttached) {
				netConfig.ConnectionTimeout		=	float.MaxValue;	
				Log.Message("SV: Debugger is attached: ConnectionTimeout = {0} sec", netConfig.ConnectionTimeout);
			}

			netConfig.EnableMessageType( NetIncomingMessageType.ConnectionApproval );
			netConfig.EnableMessageType( NetIncomingMessageType.DiscoveryRequest );
			netConfig.EnableMessageType( NetIncomingMessageType.DiscoveryResponse );
			netConfig.EnableMessageType( NetIncomingMessageType.ConnectionLatencyUpdated );

			var server		=	new NetServer( netConfig );
			notifications	=	new Queue<string>();

			Log.Message("SV: Start: {0} {1}", map, postCommand);

			var snapshotQueue	=	new SnapshotQueue(32);
			var serverFrames	=	0L;

			//var accumulatedElapsedTime = TimeSpan.FromTicks(0);
			//var maxElapsedTime = TimeSpan.FromMilliseconds(1000);
			//var previousTicks = (long)0;

			//
			//	configure & start server :
			//
			try {

				server.Start();

				//
				//	start game specific stuff :
				//
				atoms	=	new AtomCollection();
				LoadContent( map );
				atoms.Lock();

				//
				//	invoke post-start command :
				//
				if (postCommand!=null) {
					Game.Invoker.Push( postCommand );
				}


				//	Timer and fixed timestep stuff :
				//	http://gafferongames.com/game-physics/fix-your-timestep/
				var accumulator	=	TimeSpan.Zero;
				var stopwatch	=	new Stopwatch();
				stopwatch.Start();
				var currentTime	=	stopwatch.Elapsed;
				var time		=	stopwatch.Elapsed;

				//
				//	server loop :
				//	
				while ( !killToken.IsCancellationRequested ) {

				_retryTick:
					var targetDelta	=	TimeSpan.FromTicks( (long)(10000000 / TargetFrameRate) );
					var newTime		=	stopwatch.Elapsed;
					var frameTime	=	newTime - currentTime;
					currentTime		=	newTime;

					accumulator +=	 frameTime;

					if ( accumulator < targetDelta ) {
						Thread.Sleep(1);
						goto _retryTick;
					}

					while ( accumulator > targetDelta ) {

						//var svTime = new GameTime( time, targetDelta );
						var svTime = new GameTime( serverFrames, time, targetDelta );
						serverFrames++;

						//
						//	Do actual server stuff :
						//	
						UpdateNetworkAndLogic( svTime, server, snapshotQueue );

						accumulator	-= targetDelta;
						time		+= targetDelta;
					}
				}

				foreach ( var conn in server.Connections ) {
					conn.Disconnect("Server disconnected");
				}

			} catch ( Exception e ) {
				Log.PrintException( e, "Server error: {0}", e.Message );

				foreach ( var conn in server.Connections ) {
					conn.Disconnect(string.Format("Server error: {0}", e.Message));
				}

			} finally {

				//
				//	kill game specific stuff :
				//	try...catch???
				//
				UnloadContent();

				//
				//	shutdown connection :
				//
				server.Shutdown("Server shutdown");
				Log.Message("SV: Shutdown");

				notifications	=	null;

				killToken	=	null;
				serverTask	=	null;
				server		=	null;
				pings		=	null;
			}
		}