/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
// Filename : OERRCTRL.H
// Descrition : Error control
#include
#include
#include
#include
#include
#define DEBUG_LOG_LOCAL 1
#include
// ---------- define constant -----------//
// 0 don't display log, 1 display exception, 2 display more detail, 3 display all
#define DEBUG_LOG_LEVEL 1
const int SHORT_TIME_OUT = 100;
const int TIME_OUT = 2000; // 2 sec
const int CONNECT_LOST_TIME = 20000; // 20 sec
static String debugStr;
void ErrorControl::init(MultiPlayerType *mp, char ecPlayerId )
{
// ---------- initialize dp_id array ---------- //
memset(dp_id, 0, sizeof(dp_id) );
connecting_player_count = 0;
self_ec_player_id = ecPlayerId;
mp_ptr = mp;
// ---------- initialize head and tail of queues ---------- //
send_head = send_tail = 0;
recv_head = recv_tail = 0;
}
void ErrorControl::deinit()
{
}
void ErrorControl::set_dp_id(char ecPlayerId, long unsigned int dpPlayerId )
{
if( ecPlayerId != self_ec_player_id )
{
err_when( ecPlayerId < 1 || ecPlayerId > MAX_PLAYER );
dp_id[ecPlayerId-1] = dpPlayerId;
wait_to_receive[ecPlayerId-1] = 0;
memset(recv_flag[ecPlayerId-1], 0, MAX_QUEUE );
// next_send[ecPlayerId-1] = 0;
// next_ack_send[ecPlayerId-1] = 0;
// retrans_state[ecPlayerId-1] = 0;
// ------- update connecting_player_count --------//
connecting_player_count = 0;
for(int p = 0; p < MAX_PLAYER; ++p)
if( dp_id[p] )
connecting_player_count++;
}
}
// return ec_player_id, 0 for not found (can't found own dpPlayerId)
char ErrorControl::get_ec_player_id( long unsigned int dpPlayerId )
{
if( dpPlayerId == BROADCAST_PID || dpPlayerId == 0)
return 0;
for( char ecPlayerId = 1; ecPlayerId <= MAX_PLAYER; ++ecPlayerId )
{
if( dpPlayerId == dp_id[ecPlayerId-1] )
return ecPlayerId;
}
return 0;
}
// return 1 on success, -1 if queue is_full, 0 for other failure
int ErrorControl::send(char ecPlayerId, void *dataPtr, long unsigned int dataLen)
{
if( connecting_player_count == 0)
return 1;
if( send_queue_space() < MAX_QUEUE/2)
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 1)
DEBUG_LOG("ec_remote.send() fail, buffer half full");
#endif
return -1;
}
int frameId = en_send_queue();
if( frameId < 0)
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 1)
DEBUG_LOG("ec_remote.send() fail, buffer full");
#endif
return frameId;
}
else
{
// -------- add buffer to queue --------- //
err_when( frameId >= MAX_QUEUE );
VLenQueue &sq = send_queue[frameId];
sq.clear();
char *ecMsg = sq.reserve( sizeof(EcMsgHeader) + dataLen + CRC_LEN );
((EcMsgHeader *)ecMsg)->init( FIRST_SEND, self_ec_player_id, frameId );
memcpy( ecMsg + sizeof(EcMsgHeader), dataPtr, dataLen );
*((CRC_TYPE *) (ecMsg + sizeof(EcMsgHeader) + dataLen) ) = crc8((unsigned char *)ecMsg, sizeof(EcMsgHeader) + dataLen);
// ------- clear all ack flags of that frame --------//
PID_TYPE toDPid = BROADCAST_PID;
clear_ack( frameId );
if( ecPlayerId != 0)
{
toDPid = dp_id[ecPlayerId];
err_when(toDPid == BROADCAST_PID);
for(char p = 1; p <= MAX_PLAYER; ++p)
if( p != ecPlayerId )
set_ack(p, frameId);
}
// ------- try to send the data for the first time ------- //
if( mp_ptr->send(toDPid, sq.queue_buf, sq.length()) )
{
// mark send time
mark_send_time(frameId, TIME_OUT );
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 3)
debugStr = "ec_remote.send() successful, frame ";
debugStr += frameId;
DEBUG_LOG(debugStr);
#endif
// mark the func_id of the message RE_SEND
((EcMsgHeader *)ecMsg)->func_id = RE_SEND;
// recalculate CRC
*((CRC_TYPE *) (ecMsg + sizeof(EcMsgHeader) + dataLen) ) = crc8((unsigned char *)ecMsg, sizeof(EcMsgHeader) + dataLen);
}
else
{
// mark send time, mark a shorter time
mark_send_time(frameId, SHORT_TIME_OUT);
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2)
debugStr = "ec_remote.send() fail, frame ";
debugStr += frameId;
DEBUG_LOG(debugStr);
#endif
// still return true to sender, as it will be re-send later
}
}
mp_ptr->after_send();
return 1;
}
char *ErrorControl::receive(char *sendEcPlayerId, long unsigned int *dataLen)
{
// ----- draw the head of recv_queue ----- //
if( is_recv_empty() )
{
return NULL;
}
else
{
char *dataPtr = receive_queue[recv_head].queue_buf;
DWORD len = receive_queue[recv_head].length();
err_when( len < sizeof(EcMsgHeader) + CRC_LEN);
if( sendEcPlayerId )
*sendEcPlayerId = ((EcMsgHeader *)dataPtr)->sender_id;
if( dataLen )
*dataLen = len - sizeof(EcMsgHeader) - CRC_LEN;
return dataPtr + sizeof(EcMsgHeader);
}
}
int ErrorControl::is_player_valid(char ecPlayerId)
{
return dp_id[ecPlayerId-1] != 0 || ecPlayerId == self_ec_player_id;
}
void ErrorControl::set_player_lost(char ecPlayerId)
{
dp_id[ecPlayerId-1] = 0;
clear_acked_frame(); // some send_queue message may be waiting this player's ack
}
void ErrorControl::yield()
{
// -------- receive any frame from dplay -----------//
char *recvPtr;
DWORD recvLen;
PID_TYPE from, to;
static int simError = 1;
// check any player lost
// ##### begin Gilbert 2/5 ########//
int sysMsgCount;
int p;
// detect any player lost, detected previous mp_ptr->send
for( p = 1; p <= MAX_PLAYER; ++p)
{
if( dp_id[p-1] && !mp_ptr->is_player_connecting(dp_id[p-1]) )
{
set_player_lost(p);
connecting_player_count--;
}
}
mp_ptr->before_receive();
while( (recvPtr = mp_ptr->receive(&from, &to, &recvLen, &sysMsgCount)) != NULL
|| sysMsgCount != 0)
{
// -------- detect any player lost ---------//
if( sysMsgCount )
{
for( p = 1; p <= MAX_PLAYER; ++p)
{
if( dp_id[p-1] && !mp_ptr->is_player_connecting(dp_id[p-1]) )
{
set_player_lost(p);
connecting_player_count--;
}
}
}
if( !recvPtr )
break; // only received system message from direct play
// ##### end Gilbert 2/5 ########//
// -------- receive the message ----------//
#ifdef DEBUG
// simulate crc error
// if( ++simError >= 10)
// simError = 0;
#endif
if( simError && !crc8((unsigned char *)recvPtr, recvLen))
{
// crc correct
EcMsgHeader ecMsg = *(EcMsgHeader *)recvPtr;
switch( ecMsg.func_id )
{
case FIRST_SEND:
// accept except frameId is wait_to_receive -1 or recv_flag is set
if( is_waiting_receive( ecMsg.sender_id, ecMsg.frame_id ) )
{
err_when( ecMsg.sender_id <= 0 || ecMsg.sender_id > MAX_PLAYER);
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 3 )
debugStr = "ec_remote : FIRST_SEND received, from:";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " accepted";
DEBUG_LOG(debugStr);
#endif
if( !is_recv_full() )
{
if( dp_id[ecMsg.sender_id-1] )
{
// mark recv_flag
set_recv_flag(ecMsg.sender_id, ecMsg.frame_id);
// send ACK
char replyMsg[sizeof(EcMsgHeader) + CRC_LEN];
((EcMsgHeader *)replyMsg)->init(ACKNOW, self_ec_player_id, ecMsg.frame_id);
*((CRC_TYPE *)(replyMsg + sizeof(EcMsgHeader))) = crc8((unsigned char *)replyMsg, sizeof(EcMsgHeader));
mp_ptr->send( dp_id[ecMsg.sender_id-1], replyMsg, sizeof(replyMsg) );
if( ecMsg.frame_id == wait_to_receive[ecMsg.sender_id-1] )
{
// clear recv_flag, until it is zero
char &scanFrame = wait_to_receive[ecMsg.sender_id-1];
for( ; recv_flag[ecMsg.sender_id-1][scanFrame]; inc_frame_id(scanFrame) )
clear_recv_flag( ecMsg.sender_id, prev_frame_id(scanFrame) );
}
}
// append the queue to receive queue
en_recv_queue(recvPtr, recvLen);
}
else
{
// drop the message if the receive queue is full
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
DEBUG_LOG("ec_remote : but receive_queue is full, discard message");
#endif
}
}
else
{
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : FIRST_SEND received, from:";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " discarded";
DEBUG_LOG(debugStr);
#endif
// some frame before are missing, wait resend
// discard the frame, but reply, for the sender not to
// send the frame again
if( dp_id[ecMsg.sender_id-1] )
{
// send ACK
char replyMsg[sizeof(EcMsgHeader) + CRC_LEN];
((EcMsgHeader *)replyMsg)->init(ACKNOW, self_ec_player_id, ecMsg.frame_id);
*((CRC_TYPE *)(replyMsg + sizeof(EcMsgHeader))) = crc8((unsigned char *)replyMsg, sizeof(EcMsgHeader));
mp_ptr->send( dp_id[ecMsg.sender_id-1], replyMsg, sizeof(replyMsg) );
}
}
break;
case RE_SEND:
// accept except frameId is wait_to_receive -1 or recv_flag is set
if( is_waiting_receive( ecMsg.sender_id, ecMsg.frame_id ) )
{
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : RE_SEND received, from:";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " accepted";
DEBUG_LOG(debugStr);
#endif
err_when( ecMsg.sender_id <= 0 || ecMsg.sender_id > MAX_PLAYER);
if( !is_recv_full() )
{
if( dp_id[ecMsg.sender_id-1] )
{
// mark recv_flag
set_recv_flag(ecMsg.sender_id, ecMsg.frame_id);
// send ACK
char replyMsg[sizeof(EcMsgHeader) + CRC_LEN];
((EcMsgHeader *)replyMsg)->init(ACKNOW, self_ec_player_id, ecMsg.frame_id);
*((CRC_TYPE *)(replyMsg + sizeof(EcMsgHeader))) = crc8((unsigned char *)replyMsg, sizeof(EcMsgHeader));
mp_ptr->send( dp_id[ecMsg.sender_id-1], replyMsg, sizeof(replyMsg) );
if( ecMsg.frame_id == wait_to_receive[ecMsg.sender_id-1] )
{
// clear recv_flag, until it is zero
char &scanFrame = wait_to_receive[ecMsg.sender_id-1];
for( ; recv_flag[ecMsg.sender_id-1][scanFrame]; inc_frame_id(scanFrame) )
clear_recv_flag( ecMsg.sender_id, prev_frame_id(scanFrame) );
}
}
// append the queue to receive queue
en_recv_queue(recvPtr, recvLen);
}
else
{
// drop the message if the receive queue is full
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
DEBUG_LOG("ec_remote : but receive_queue is full, discard message");
#endif
}
}
else
{
#if ( defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : RE_SEND received, from:";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " discarded";
DEBUG_LOG(debugStr);
#endif
// re-dundant frame, discard, but still reply ACK,
// for the sender not to send the frame again
if( dp_id[ecMsg.sender_id-1] )
{
// send ACK
char replyMsg[sizeof(EcMsgHeader) + CRC_LEN];
((EcMsgHeader *)replyMsg)->init(ACKNOW, self_ec_player_id, ecMsg.frame_id);
*((CRC_TYPE *)(replyMsg + sizeof(EcMsgHeader))) = crc8((unsigned char *)replyMsg, sizeof(EcMsgHeader));
mp_ptr->send( dp_id[ecMsg.sender_id-1], replyMsg, sizeof(replyMsg) );
}
}
break;
case ACKNOW:
// mark the frame ack
if( is_waiting_ack(ecMsg.sender_id, ecMsg.frame_id) )
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 3 )
debugStr = "ec_remote : ACKNOW received";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " accepted";
DEBUG_LOG(debugStr);
#endif
set_ack(ecMsg.sender_id, ecMsg.frame_id);
clear_acked_frame();
}
else
{
// discard the frame
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : ACKNOW received";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " discarded";
DEBUG_LOG(debugStr);
#endif
}
break;
case NEGACK:
// re-send only the frameId
if( is_waiting_ack(ecMsg.sender_id, ecMsg.frame_id) )
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : NEGACK received";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " accepted";
DEBUG_LOG(debugStr);
#endif
char *replyMsg = send_queue[ecMsg.frame_id].queue_buf;
DWORD replyLen = send_queue[ecMsg.frame_id].length();
mp_ptr->send( dp_id[ecMsg.sender_id-1], replyMsg, replyLen );
err_when( replyLen <= sizeof(EcMsgHeader) );
// don't mark re-send time
// mark the func_id of the message RE_SEND
((EcMsgHeader *)replyMsg)->func_id = RE_SEND;
// recalculate CRC
*((CRC_TYPE *) (replyMsg + replyLen - CRC_LEN) ) = crc8((unsigned char *)replyMsg, replyLen - CRC_LEN);
DEBUG_LOG("ec_remote : frame retransmitted");
}
else
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2 )
debugStr = "ec_remote : NEGACK received";
debugStr += ecMsg.sender_id;
debugStr += " frame:";
debugStr += ecMsg.frame_id;
debugStr += " discarded";
DEBUG_LOG(debugStr);
#endif
}
break;
default:
err_here();
}
}
else
{
// crc incorrect
if( recvLen > sizeof(EcMsgHeader) + CRC_LEN )
{
char senderId = get_ec_player_id(from);
if( senderId)
{
// send NEGACK frame
char replyMsg[sizeof(EcMsgHeader) + CRC_LEN];
((EcMsgHeader *)replyMsg)->init(NEGACK, self_ec_player_id, wait_to_receive[senderId-1]);
*((CRC_TYPE *)(replyMsg + sizeof(EcMsgHeader))) = crc8((unsigned char *)replyMsg, sizeof(EcMsgHeader));
mp_ptr->send( dp_id[senderId-1], replyMsg, sizeof(replyMsg) );
}
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2)
DEBUG_LOG("ec_remote : long packet corrupted" );
#endif
}
else
{
// it is probably, ACKNOW/ NEGACK frame, discard it
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2)
DEBUG_LOG("ec_remote : short packet corrupted" );
#endif
}
}
}
// ------ retransmit any un-acked and time-out-ed frame -------//
clear_acked_frame();
re_transmit();
mp_ptr->after_send(); // re_transmit() will call after_send
}
int ErrorControl::is_send_empty()
{
return ( send_head == send_tail );
}
int ErrorControl::is_send_full()
{
return ( send_tail + 1 == send_head || send_tail + 1 == send_head + MAX_QUEUE );
}
int ErrorControl::send_queue_space()
{
// the queue can hold at most MAX_QUEUE-1 item
return send_tail >= send_head ? MAX_QUEUE-1 - (send_tail - send_head) : send_head - send_tail -1 ;
}
// return frameId
int ErrorControl::en_send_queue()
{
if( is_send_full() )
return -1;
else
{
char f = send_tail;
err_when( f < 0 || f >= MAX_QUEUE);
inc_frame_id( send_tail );
return f;
}
}
// free queue Id
void ErrorControl::de_send_queue()
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 3)
debugStr = "ec_remote.de_send_queue(), frame:";
debugStr += send_head;
DEBUG_LOG(debugStr);
#endif
inc_frame_id(send_head);
}
int ErrorControl::is_recv_empty()
{
return (recv_head == recv_tail);
}
int ErrorControl::is_recv_full()
{
return (recv_tail+1 == recv_head || recv_tail+1 == recv_head + MAX_RECV_QUEUE );
}
int ErrorControl::recv_queue_space()
{
// the queue can hold at most MAX_RECV_QUEUE-1 item
return recv_tail >= recv_head ? MAX_RECV_QUEUE-1 - (recv_tail - recv_head) : recv_head - recv_tail -1 ;
}
void ErrorControl::en_recv_queue(void *dataPtr, long unsigned int dataLen)
{
if( is_recv_full() )
{
err_here(); // receive queue is full
}
else
{
char f = recv_tail;
err_when( recv_tail < 0 || recv_tail >= MAX_RECV_QUEUE);
if( ++recv_tail >= MAX_RECV_QUEUE )
recv_tail = 0;
receive_queue[f].clear();
memcpy( receive_queue[f].reserve(dataLen), dataPtr, dataLen);
}
}
void ErrorControl::de_recv_queue()
{
if( ++recv_head >= MAX_RECV_QUEUE )
recv_head = 0;
}
int ErrorControl::is_waiting_ack(char ecPlayerId, char frameId)
{
// true if frameId is between send_head (inclusive) and send_tail (non-inclusive)
return is_between( send_head, frameId, send_tail );
}
void ErrorControl::set_ack(char ecPlayerId, char frameId)
{
ack_flag[frameId][ecPlayerId-1] = 1;
}
void ErrorControl::clear_ack(char frameId)
{
memset( ack_flag[frameId], 0, MAX_PLAYER );
}
void ErrorControl::mark_send_time(char frameId, long unsigned int duration)
{
send_time[frameId] = m.get_time();
re_send_after[frameId] = duration;
}
// larger the promptFactor, earlier to re-send
int ErrorControl::need_re_send(char frameId, int promptFactor)
{
return ((m.get_time() - send_time[frameId]) * promptFactor) >= re_send_after[frameId];
// do not use m.get_time() >= re_send_after[frameId] + send_time[frameId]
// assume m.get_time() may count again from zero
}
int ErrorControl::are_all_acked(char frameId)
{
for( char ecPlayerId = 1; ecPlayerId <= MAX_PLAYER; ++ecPlayerId )
{
if( dp_id[ecPlayerId-1] && !ack_flag[frameId][ecPlayerId-1] )
return 0;
}
return 1;
}
void ErrorControl::clear_acked_frame()
{
err_when( send_head < 0 || send_head >= MAX_QUEUE );
for( ; !is_send_empty() && are_all_acked(send_head); de_send_queue() );
}
int ErrorControl::is_waiting_receive(char ecPlayerId, char frameId)
{
err_when( frameId < 0 || frameId >= MAX_QUEUE );
// the first waiting frame is wait_to_receive, but may receive MAX_QUEUE-2
// e.g wait_to_receive is 0, then 1 to MAX_QUEUE/2-1 are also acceptable
return is_between(wait_to_receive[ecPlayerId-1], frameId,
(wait_to_receive[ecPlayerId-1] + MAX_QUEUE/2) % MAX_QUEUE)
&& !recv_flag[ecPlayerId-1][frameId];
}
void ErrorControl::set_recv_flag(char ecPlayerId, char frameId)
{
recv_flag[ecPlayerId-1][frameId] = 1;
}
void ErrorControl::clear_recv_flag(char ecPlayerId, char frameId)
{
recv_flag[ecPlayerId-1][frameId] = 0;
}
void ErrorControl::re_transmit(int promptFactor)
{
for( char f = send_head; f != send_tail; inc_frame_id(f) )
{
if( !are_all_acked(f) && need_re_send(f, promptFactor) )
{
// count no. of remote player to re_send
int resendSuccess = 0;
int resendFail = 0;
char *ecMsg = send_queue[f].queue_buf;
DWORD ecMsgLen = send_queue[f].length();
for( char ecPlayerId = 1; ecPlayerId <= MAX_PLAYER; ++ecPlayerId )
{
// resend to specific remote player
if( dp_id[ecPlayerId-1] && !ack_flag[f][ecPlayerId-1] )
{
#if (defined(DEBUG) && DEBUG_LOG_LEVEL >= 2)
debugStr = "ec.remote : time-out retransmit frame ";
debugStr += f;
debugStr += " to ";
debugStr += ecPlayerId;
DEBUG_LOG(debugStr);
#endif
if( mp_ptr->send(dp_id[ecPlayerId-1], ecMsg, ecMsgLen))
resendSuccess++;
else
resendFail++;
}
if( resendSuccess > 0)
{
if( resendFail > 0)
{
// some resend fail, mark short resend time
mark_send_time(f, SHORT_TIME_OUT);
}
else
{
// all resend success, mark longer resend time
mark_send_time(f, TIME_OUT );
}
// mark the func_id of the message RE_SEND
((EcMsgHeader *)ecMsg)->func_id = RE_SEND;
// recalculate CRC
*((CRC_TYPE *) (ecMsg + ecMsgLen - CRC_LEN )) = crc8((unsigned char *)ecMsg, ecMsgLen - CRC_LEN);
}
else if( resendFail > 0)
{
// all fail, mark a shorter time
// mark send time, mark a shorter time
mark_send_time(f, SHORT_TIME_OUT);
}
}
}
}
mp_ptr->after_send();
}