2014-08-27 131 views
1

我正在嘗試實施客戶端預測和服務器對賬的一個示例,如果有更好的方法,請讓我知道!我試圖隱藏網絡遊戲的延遲。目前,我正在爲客戶端使用lidgren和XNA,並且僅使用服務器的控制檯應用程序。我將服務器端的lidgren設置爲模擬1.5秒的延遲。所以當我運行這個代碼時,它的作用大部分是這樣的,但似乎客戶端正在緩衝移動,然後最終在屏幕上移動字符,但根據我在下面引用的演示,我沒有看到緩衝類型的行爲只是難住它可能是什麼?如果您需要查看更多的代碼,請告訴我,我不想讓帖子氾濫太多,請提前感謝您提供的幫助。客戶端預測問題

在SendMovement方法中,我將用戶輸入和序列化命令發送到服務器,它將繼續並移動播放器並將移動命令存儲在隊列中。

private Queue<MovementCommand> _previousMovements = new Queue<MovementCommand>(5000); 

    public void SendMovement(float elapsed, byte direction) 
    { 
     MovementCommand movement = new MovementCommand(_id, _sequenceNumber++, elapsed, direction); 

     OutputCommand<MovementCommand> moveCommand = new OutputCommand<MovementCommand>(GamePacketTypes.Movement, movement); 

     byte[] msgData = moveCommand.Serialize(); 

     Send(msgData); 

     Console.WriteLine(string.Format("Elapsed Time = {0}, Sequence # = {1}", elapsed, _sequenceNumber)); 

     if (_clientSidePrediction == true) 
     { 
      _player.Move(movement); 

      _previousMovements.Enqueue(movement); 

     } 
    } 

當我收到一條消息來自我更新播放位置,然後檢查,看看有什麼是從服務器相比,本地序列號的最後輸入序列回服務器。

public void HandleMessages() 
    { 
     while (true) 
     { 
      if (mIncomingMessages.Count > 0) 
      { 
       for (int i = 0; i < mIncomingMessages.Count; i++) 
       { 
        byte command = mIncomingMessages[i].ReadByte(); 


        switch ((GamePacketTypes)command) 
        { 
         case GamePacketTypes.UpdateEntity: 


          EntityStateType stateObj = EntityStateType.Deserialize(mIncomingMessages[i].ReadBytes(mIncomingMessages[i].LengthBytes - 1)); 

          _player.Position(stateObj); 


          if (_serverReconciliation == true) 
          { 
           if (stateObj.ID == _id) 
           { 
            int j = 0; 

            while (j < _previousMovements.Count) 
            { 
             MovementCommand move = _previousMovements.Peek(); 

             if (move.InputSequence <= stateObj.LastProcessedInput) 
             { 
              _previousMovements.Dequeue(); 
             } 
             else 
             { 
              _player.Move(_previousMovements.Dequeue()); 
              j++; 
             } 

            } 
           } 
          } 

          break; 

        } 

       } 

       mIncomingMessages.Clear(); 
      } 

      Thread.Sleep(25); 
     } 
    } 

在服務器端,我只是把來自客戶端的命令,然後應用到自己的性格和對客戶端設置的最後處理序列時,下一個狀態更新熄滅。從加布裏埃爾Gamebetta

private async Task<bool> HandleMovement(MovementCommand move) 
    { 
     switch((DirectionHeading)move.Direction) 
     { 
      case DirectionHeading.North: 
       _player.Y -= (move.PressedTime * _player.Velocity); 
       break; 
      case DirectionHeading.East: 
       _player.X += (move.PressedTime * _player.Velocity); 
       break; 
      case DirectionHeading.South: 
       _player.Y += (move.PressedTime * _player.Velocity); 
       break; 
      case DirectionHeading.West: 
       _player.X -= (move.PressedTime * _player.Velocity); 

       break; 

     } 
     _player.Direction = move.Direction; 
     LastProcessedInput = move.InputSequence; 
     Console.WriteLine("Last Processed Input = {0}", LastProcessedInput); 
     return true; 
    } 

示例代碼(希望他不記...)

// Get inputs and send them to the server. 
// If enabled, do client-side prediction. 
Client.prototype.processInputs = function() { 
// Compute delta time since last update. 
var now_ts = +new Date(); 
var last_ts = this.last_ts || now_ts; 
var dt_sec = (now_ts - last_ts)/1000.0; 
this.last_ts = now_ts; 

// Package player's input. 
var input; 
if (this.key_right) { 
    input = { press_time: dt_sec }; 
} else if (this.key_left) { 
    input = { press_time: -dt_sec }; 
} else { 
    // Nothing interesting happened. 
    return; 
} 

// Send the input to the server. 
input.input_sequence_number = this.input_sequence_number++; 
input.entity_id = this.entity_id; 
this.server.network.send(client_server_lag, input); 

// Do client-side prediction. 
if (client_side_prediction) { 
    this.entity.applyInput(input); 
} 

// Save this input for later reconciliation. 
this.pending_inputs.push(input); 
} 


Server.prototype.processInputs = function() { 
// Process all pending messages from clients. 
while (true) { 
var message = this.network.receive(); 
if (!message) { 
    break; 
} 

// Update the state of the entity, based on its input. 
// We just ignore inputs that don't look valid; this is what prevents 
// clients from cheating. 
if (this.validateInput(message)) { 
    var id = message.entity_id; 
    this.entities[id].applyInput(message); 
    this.last_processed_input[id] = message.input_sequence_number; 
} 
} 

}

Client.prototype.processServerMessages = function() { 
while (true) { 
var message = this.network.receive(); 
if (!message) { 
    break; 
} 

// World state is a list of entity states. 
for (var i = 0; i < message.length; i++) { 
    var state = message[i]; 

    if (state.entity_id == this.entity_id) { 
    // Got the position of this client's entity. 

    if (!this.entity) { 
     // If this is the first server update, create a local entity. 
     this.entity = new Entity(); 
    } 

    // Set the position sent by the server. 
    this.entity.x = state.position; 

    if (server_reconciliation) { 
     // Server Reconciliation. Re-apply all the inputs not yet processed by 
     // the server. 
     var j = 0; 
     while (j < this.pending_inputs.length) { 
     var input = this.pending_inputs[j]; 
     if (input.input_sequence_number <= state.last_processed_input) { 
      // Already processed. Its effect is already taken into account 
      // into the world update we just got, so we can drop it. 
      this.pending_inputs.splice(j, 1); 
     } else { 
      // Not processed by the server yet. Re-apply it. 
      this.entity.applyInput(input); 
      j++; 
     } 
     } 
    } else { 
     // Reconciliation is disabled, so drop all the saved inputs. 
     this.pending_inputs = []; 
    } 
    } else { 
    // TO DO: add support for rendering other entities. 
    } 
} 
} 

回答

1

不,我不介意:)但也許添加一個鏈接給文章給其他讀者更多的上下文。

我不知道很多C#,但在HandleMessages()最裏面的循環你在兩個分支使用Dequeue(),和你在其中的一個遞增j - 我有一種感覺,這是不正確的。服務器更新後,您可能需要多次重新應用輸入。

+0

我試圖添加一個鏈接到您的網頁,它不會允許我:(我希望我可以。 – lakedoo 2014-08-27 13:52:32