Purple Martians
Technical Code Descriptions
Netgame - Client Control Change
Client's controls have changed
Server receives cdat
Client's controls have changed
Every frame, the client's controls are compared with their previous state.
If they have changed:
- an entry is put in the client's local game moves array (in case the change needs to be played back later)
- a 'cdat' packet with the control change and frame number is sent to the server
- the change is applied to the client's local game state
void mwPlayer::proc_player_input(void)
{
...
if (loc[p].comp_move != comp_move_from_players_current_controls(p)) // player's controls have changed
{
mGameMoves.add_game_move(mLoop.frame_num, 5, p, loc[p].comp_move);
if (cm == 4) // in client mode, send cdat packet, and apply move directly to controls
{
set_controls_from_comp_move(p, loc[p].comp_move);
mNetgame.client_send_cdat(p);
void mwNetgame::client_send_cdat(int p)
{
char data[1024] = {0}; int pos;
mPacketBuffer.PacketName(data, pos, "cdat");
mPacketBuffer.PacketPutInt1(data, pos, p);
mPacketBuffer.PacketPutInt4(data, pos, mLoop.frame_num);
mPacketBuffer.PacketPutInt1(data, pos, mPlayer.loc[p].comp_move);
ClientSend(data, pos);
mPlayer.loc[p].client_cdat_packets_tx++;
}
Server receives cdat
When the server receives a 'cdat' packet from a client, the server compares the frame number of the client's control change to the oldest state the server has a copy of.
If the cdat's frame number is earlier, it arrived too late to be applied and is discarded.
Otherwise the client's control change is added to the server's game moves array.
'server_dirty_frame' is also set. This tells the server how far back it needs to rewind to apply late input.
void mwNetgame::server_proc_cdat_packet(int i)
{
int p = mPacketBuffer.PacketGetInt1(i);
int cdat_frame_num = mPacketBuffer.PacketGetInt4(i);
int cm = mPacketBuffer.PacketGetInt1(i);
double timestamp = mPacketBuffer.rx_buf[i].timestamp;
mPlayer.loc[p].client_cdat_packets_tx++;
// calculate game_move_sync
mPlayer.loc[p].server_game_move_sync = cdat_frame_num - mLoop.frame_num;
// calculate game_move_dsync
mPlayer.loc[p].game_move_dsync = ( (double) mPlayer.loc[p].server_game_move_sync * 0.025) + mLoop.frame_start_timestamp - timestamp;
// add to average tally
mTally_game_move_dsync_avg_last_sec[p].add_data(mPlayer.loc[p].game_move_dsync);
// determine the cutoff frame for late cdats
int of = mStateHistory[0].oldest_state_frame_num;
if (cdat_frame_num < of)
{
mPlayer.syn[p].late_cdats++;
mTally_late_cdats_last_sec[p].add_data(1); // add to tally
mLog.addf(LOG_NET_cdat, p, "rx cdat p:%d fn:[%d] sync:[%d] late - droppped\n", p, cdat_frame_num, mPlayer.loc[p].server_game_move_sync);
}
else
{
mGameMoves.add_game_move(cdat_frame_num, 5, p, cm); // add to game_move array
mLog.addf(LOG_NET_cdat, p, "rx cdat p:%d fn:[%d] sync:[%d] gmep:[%d] - entered\n", p, cdat_frame_num, mPlayer.loc[p].server_game_move_sync, mGameMoves.entry_pos);
if (cdat_frame_num < server_dirty_frame) server_dirty_frame = cdat_frame_num;
}
}