mirror of
https://git.code.sf.net/p/quake/quakeforge-old
synced 2024-11-29 07:02:08 +00:00
c3f5581b0a
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified, Unity, etc. You know the drill. This takes care of the "standalone" problem with the wrong name, and the recent snafu with multiple developers working on the same files simultaneously...expect me (and probably others) to start locking dirs when updates are taking place. And yes, this update is really as large as it looks. Software only at the moment, but I will have the makefile updated to build the GL builds as well.
951 lines
20 KiB
C
951 lines
20 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// net_ser.c
|
|
|
|
#include "quakedef.h"
|
|
#include "net_ser.h"
|
|
#include "dosisms.h"
|
|
#include "crc.h"
|
|
|
|
#include "net_comx.c"
|
|
|
|
// serial protocol
|
|
|
|
#define SERIAL_PROTOCOL_VERSION 3
|
|
|
|
// The serial protocol is message oriented. The high level message format is
|
|
// a one byte message type (MTYPE_xxx), data, and a 16-bit checksum. All
|
|
// multi-byte fields are sent in network byte order. There are currently 4
|
|
// MTYPEs defined. Their formats are as follows:
|
|
//
|
|
// MTYPE_RELIABLE sequence data_length data checksum eom
|
|
// MTYPE_UNRELIABLE sequence data_length data checksum eom
|
|
// MTYPE_ACK sequence checksum eom
|
|
// MTYPE_CONTROL data_length data checksum eom
|
|
//
|
|
// sequence is an 8-bit unsigned value starting from 0
|
|
// data_length is a 16-bit unsigned value; it is the length of the data only
|
|
// the checksum is a 16-bit value. the CRC formula used is defined in crc.h.
|
|
// the checksum covers the entire messages, excluding itself
|
|
// eom is a special 2 byte sequence used to mark the End Of Message. This is
|
|
// needed for error recovery.
|
|
//
|
|
// A lot of behavior is based on knowledge of the upper level Quake network
|
|
// layer. For example, only one reliable message can be outstanding (pending
|
|
// reception of an MTYPE_ACK) at a time.
|
|
//
|
|
// The low level routines used to communicate with the modem are not part of
|
|
// this protocol.
|
|
//
|
|
// The CONTROL messages are only used for session establishment. They are
|
|
// not reliable or sequenced.
|
|
|
|
#define MTYPE_RELIABLE 0x01
|
|
#define MTYPE_UNRELIABLE 0x02
|
|
#define MTYPE_CONTROL 0x03
|
|
#define MTYPE_ACK 0x04
|
|
#define MTYPE_CLIENT 0x80
|
|
|
|
#define ESCAPE_COMMAND 0xe0
|
|
#define ESCAPE_EOM 0x19
|
|
|
|
static qboolean listening = false;
|
|
|
|
|
|
typedef struct SerialLine_s
|
|
{
|
|
struct SerialLine_s *next;
|
|
qsocket_t *sock;
|
|
int lengthStated;
|
|
int lengthFound;
|
|
int tty;
|
|
qboolean connected;
|
|
qboolean connecting;
|
|
qboolean client;
|
|
double connect_time;
|
|
unsigned short crcStated;
|
|
unsigned short crcValue;
|
|
byte currState;
|
|
byte prevState;
|
|
byte mtype;
|
|
byte sequence;
|
|
} SerialLine;
|
|
|
|
#define STATE_READY 0
|
|
#define STATE_SEQUENCE 1
|
|
#define STATE_LENGTH1 2
|
|
#define STATE_LENGTH2 3
|
|
#define STATE_DATA 4
|
|
#define STATE_CRC1 5
|
|
#define STATE_CRC2 6
|
|
#define STATE_EOM 7
|
|
#define STATE_ESCAPE 8
|
|
#define STATE_ABORT 9
|
|
|
|
SerialLine serialLine[NUM_COM_PORTS];
|
|
|
|
int myDriverLevel;
|
|
|
|
static void Serial_SendACK (SerialLine *p, byte sequence);
|
|
|
|
|
|
static void ResetSerialLineProtocol (SerialLine *p)
|
|
{
|
|
p->connected = false;
|
|
p->connecting = false;
|
|
p->currState = STATE_READY;
|
|
p->prevState = STATE_READY;
|
|
p->lengthFound = 0;
|
|
}
|
|
|
|
|
|
static int ProcessInQueue(SerialLine *p)
|
|
{
|
|
int b;
|
|
|
|
while (1)
|
|
{
|
|
b = TTY_ReadByte(p->tty);
|
|
if (b == ERR_TTY_NODATA)
|
|
break;
|
|
|
|
if (b == ERR_TTY_LINE_STATUS)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
continue;
|
|
}
|
|
if (b == ERR_TTY_MODEM_STATUS)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
return -1;
|
|
}
|
|
|
|
if (b == ESCAPE_COMMAND)
|
|
if (p->currState != STATE_ESCAPE)
|
|
{
|
|
p->prevState = p->currState;
|
|
p->currState = STATE_ESCAPE;
|
|
continue;
|
|
}
|
|
|
|
if (p->currState == STATE_ESCAPE)
|
|
{
|
|
if (b == ESCAPE_EOM)
|
|
{
|
|
if (p->prevState == STATE_ABORT)
|
|
{
|
|
p->currState = STATE_READY;
|
|
p->lengthFound = 0;
|
|
continue;
|
|
}
|
|
|
|
if (p->prevState != STATE_EOM)
|
|
{
|
|
p->currState = STATE_READY;
|
|
p->lengthFound = 0;
|
|
Con_DPrintf("Serial: premature EOM\n");
|
|
continue;
|
|
}
|
|
|
|
switch (p->mtype)
|
|
{
|
|
case MTYPE_RELIABLE:
|
|
Con_DPrintf("Serial: sending ack %u\n", p->sequence);
|
|
Serial_SendACK (p, p->sequence);
|
|
if (p->sequence == p->sock->receiveSequence)
|
|
{
|
|
p->sock->receiveSequence = (p->sequence + 1) & 0xff;
|
|
p->sock->receiveMessageLength += p->lengthFound;
|
|
}
|
|
else
|
|
Con_DPrintf("Serial: reliable out of order; got %u wanted %u\n", p->sequence, p->sock->receiveSequence);
|
|
break;
|
|
|
|
case MTYPE_UNRELIABLE:
|
|
p->sock->unreliableReceiveSequence = (p->sequence + 1) & 0xff;
|
|
p->sock->receiveMessageLength += p->lengthFound;
|
|
break;
|
|
|
|
case MTYPE_ACK:
|
|
Con_DPrintf("Serial: got ack %u\n", p->sequence);
|
|
if (p->sequence == p->sock->sendSequence)
|
|
{
|
|
p->sock->sendSequence = (p->sock->sendSequence + 1) & 0xff;
|
|
p->sock->canSend = true;
|
|
}
|
|
else
|
|
Con_DPrintf("Serial: ack out of order; got %u wanted %u\n",p->sequence, p->sock->sendSequence);
|
|
break;
|
|
|
|
case MTYPE_CONTROL:
|
|
p->sock->receiveMessageLength += p->lengthFound;
|
|
break;
|
|
}
|
|
|
|
p->currState = STATE_READY;
|
|
p->lengthFound = 0;
|
|
continue;
|
|
}
|
|
|
|
|
|
if (b != ESCAPE_COMMAND)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: Bad escape sequence\n");
|
|
continue;
|
|
}
|
|
|
|
// b == ESCAPE_COMMAND
|
|
p->currState = p->prevState;
|
|
}
|
|
|
|
p->prevState = p->currState;
|
|
|
|
//DEBUG
|
|
if (p->sock->receiveMessageLength + p->lengthFound > NET_MAXMESSAGE)
|
|
{
|
|
Con_DPrintf("Serial blew out receive buffer: %u\n", p->sock->receiveMessageLength + p->lengthFound);
|
|
p->currState = STATE_ABORT;
|
|
}
|
|
if (p->sock->receiveMessageLength + p->lengthFound == NET_MAXMESSAGE)
|
|
{
|
|
Con_DPrintf("Serial hit receive buffer limit: %u\n", p->sock->receiveMessageLength + p->lengthFound);
|
|
p->currState = STATE_ABORT;
|
|
}
|
|
//end DEBUG
|
|
|
|
switch (p->currState)
|
|
{
|
|
case STATE_READY:
|
|
CRC_Init(&p->crcValue);
|
|
CRC_ProcessByte(&p->crcValue, b);
|
|
if (p->client)
|
|
{
|
|
if ((b & MTYPE_CLIENT) != 0)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: client got own message\n");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((b & MTYPE_CLIENT) == 0)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: server got own message\n");
|
|
break;
|
|
}
|
|
b &= 0x7f;
|
|
}
|
|
p->mtype = b;
|
|
if (b != MTYPE_CONTROL)
|
|
p->currState = STATE_SEQUENCE;
|
|
else
|
|
p->currState = STATE_LENGTH1;
|
|
if (p->mtype < MTYPE_ACK)
|
|
{
|
|
p->sock->receiveMessage[p->sock->receiveMessageLength] = b;
|
|
p->lengthFound++;
|
|
}
|
|
break;
|
|
|
|
case STATE_SEQUENCE:
|
|
p->sequence = b;
|
|
CRC_ProcessByte(&p->crcValue, b);
|
|
if (p->mtype != MTYPE_ACK)
|
|
p->currState = STATE_LENGTH1;
|
|
else
|
|
p->currState = STATE_CRC1;
|
|
break;
|
|
|
|
case STATE_LENGTH1:
|
|
p->lengthStated = b * 256;
|
|
CRC_ProcessByte(&p->crcValue, b);
|
|
p->currState = STATE_LENGTH2;
|
|
break;
|
|
|
|
case STATE_LENGTH2:
|
|
p->lengthStated += b;
|
|
CRC_ProcessByte(&p->crcValue, b);
|
|
if (p->mtype == MTYPE_RELIABLE && p->lengthStated > MAX_MSGLEN)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: bad reliable message length %u\n", p->lengthStated);
|
|
}
|
|
else if (p->mtype == MTYPE_UNRELIABLE && p->lengthStated > MAX_DATAGRAM)
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: bad unreliable message length %u\n", p->lengthStated);
|
|
}
|
|
else
|
|
{
|
|
p->currState = STATE_DATA;
|
|
if (p->mtype < MTYPE_ACK)
|
|
{
|
|
*(short *)&p->sock->receiveMessage [p->sock->receiveMessageLength + 1] = p->lengthStated;
|
|
p->lengthFound += 2;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_DATA:
|
|
p->sock->receiveMessage[p->sock->receiveMessageLength + p->lengthFound] = b;
|
|
p->lengthFound++;
|
|
CRC_ProcessByte(&p->crcValue, b);
|
|
if (p->lengthFound == p->lengthStated + 3)
|
|
p->currState = STATE_CRC1;
|
|
break;
|
|
|
|
case STATE_CRC1:
|
|
p->crcStated = b * 256;
|
|
p->currState = STATE_CRC2;
|
|
break;
|
|
|
|
case STATE_CRC2:
|
|
p->crcStated += b;
|
|
if (p->crcStated == CRC_Value(p->crcValue))
|
|
{
|
|
p->currState = STATE_EOM;
|
|
}
|
|
else
|
|
{
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: Bad crc\n");
|
|
}
|
|
break;
|
|
|
|
case STATE_EOM:
|
|
p->currState = STATE_ABORT;
|
|
Con_DPrintf("Serial: Bad message format\n");
|
|
break;
|
|
|
|
case STATE_ABORT:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Serial_Init (void)
|
|
{
|
|
int n;
|
|
|
|
// LATER do Win32 serial support
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#endif
|
|
|
|
if (COM_CheckParm("-nolan"))
|
|
return -1;
|
|
if (COM_CheckParm ("-noserial"))
|
|
return -1;
|
|
|
|
myDriverLevel = net_driverlevel;
|
|
|
|
if (TTY_Init())
|
|
return -1;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
|
{
|
|
serialLine[n].tty = TTY_Open(n);
|
|
ResetSerialLineProtocol (&serialLine[n]);
|
|
}
|
|
|
|
Con_Printf("Serial driver initialized\n");
|
|
serialAvailable = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Serial_Shutdown (void)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
|
{
|
|
if (serialLine[n].connected)
|
|
Serial_Close(serialLine[n].sock);
|
|
}
|
|
|
|
TTY_Shutdown();
|
|
}
|
|
|
|
|
|
void Serial_Listen (qboolean state)
|
|
{
|
|
listening = state;
|
|
}
|
|
|
|
|
|
qboolean Serial_CanSendMessage (qsocket_t *sock)
|
|
{
|
|
return sock->canSend;
|
|
}
|
|
|
|
|
|
qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock)
|
|
{
|
|
return TTY_OutputQueueIsEmpty(((SerialLine *)sock->driverdata)->tty);
|
|
}
|
|
|
|
|
|
int Serial_SendMessage (qsocket_t *sock, sizebuf_t *message)
|
|
{
|
|
SerialLine *p;
|
|
int n;
|
|
unsigned short crc;
|
|
byte b;
|
|
|
|
p = (SerialLine *)sock->driverdata;
|
|
CRC_Init (&crc);
|
|
|
|
// message type
|
|
b = MTYPE_RELIABLE;
|
|
if (p->client)
|
|
b |= MTYPE_CLIENT;
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// sequence
|
|
b = p->sock->sendSequence;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data length
|
|
b = message->cursize >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
b = message->cursize & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data
|
|
for (n = 0; n < message->cursize; n++)
|
|
{
|
|
b = message->data[n];
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
}
|
|
|
|
// checksum
|
|
b = CRC_Value (crc) >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
b = CRC_Value (crc) & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
|
|
// end of message
|
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
|
|
|
TTY_Flush(p->tty);
|
|
|
|
// mark sock as busy and save the message for possible retransmit
|
|
sock->canSend = false;
|
|
Q_memcpy(sock->sendMessage, message->data, message->cursize);
|
|
sock->sendMessageLength = message->cursize;
|
|
sock->lastSendTime = net_time;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void ReSendMessage (qsocket_t *sock)
|
|
{
|
|
sizebuf_t temp;
|
|
|
|
Con_DPrintf("Serial: re-sending reliable\n");
|
|
temp.data = sock->sendMessage;
|
|
temp.maxsize = sock->sendMessageLength;
|
|
temp.cursize = sock->sendMessageLength;
|
|
Serial_SendMessage (sock, &temp);
|
|
}
|
|
|
|
|
|
int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *message)
|
|
{
|
|
SerialLine *p;
|
|
int n;
|
|
unsigned short crc;
|
|
byte b;
|
|
|
|
p = (SerialLine *)sock->driverdata;
|
|
|
|
if (!TTY_OutputQueueIsEmpty(p->tty))
|
|
{
|
|
TTY_Flush(p->tty);
|
|
return 1;
|
|
}
|
|
|
|
CRC_Init (&crc);
|
|
|
|
// message type
|
|
b = MTYPE_UNRELIABLE;
|
|
if (p->client)
|
|
b |= MTYPE_CLIENT;
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// sequence
|
|
b = p->sock->unreliableSendSequence;
|
|
p->sock->unreliableSendSequence = (b + 1) & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data length
|
|
b = message->cursize >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
b = message->cursize & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data
|
|
for (n = 0; n < message->cursize; n++)
|
|
{
|
|
b = message->data[n];
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
}
|
|
|
|
// checksum
|
|
b = CRC_Value (crc) >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
b = CRC_Value (crc) & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
|
|
// end of message
|
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
|
|
|
TTY_Flush(p->tty);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void Serial_SendACK (SerialLine *p, byte sequence)
|
|
{
|
|
unsigned short crc;
|
|
byte b;
|
|
|
|
CRC_Init (&crc);
|
|
|
|
// message type
|
|
b = MTYPE_ACK;
|
|
if (p->client)
|
|
b |= MTYPE_CLIENT;
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// sequence
|
|
b = sequence;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// checksum
|
|
b = CRC_Value (crc) >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
b = CRC_Value (crc) & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
|
|
// end of message
|
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
|
|
|
TTY_Flush(p->tty);
|
|
}
|
|
|
|
|
|
static void Serial_SendControlMessage (SerialLine *p, sizebuf_t *message)
|
|
{
|
|
unsigned short crc;
|
|
int n;
|
|
byte b;
|
|
|
|
CRC_Init (&crc);
|
|
|
|
// message type
|
|
b = MTYPE_CONTROL;
|
|
if (p->client)
|
|
b |= MTYPE_CLIENT;
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data length
|
|
b = message->cursize >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
b = message->cursize & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
|
|
// data
|
|
for (n = 0; n < message->cursize; n++)
|
|
{
|
|
b = message->data[n];
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
CRC_ProcessByte (&crc, b);
|
|
}
|
|
|
|
// checksum
|
|
b = CRC_Value (crc) >> 8;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
b = CRC_Value (crc) & 0xff;
|
|
TTY_WriteByte(p->tty, b);
|
|
if (b == ESCAPE_COMMAND)
|
|
TTY_WriteByte(p->tty, b);
|
|
|
|
// end of message
|
|
TTY_WriteByte(p->tty, ESCAPE_COMMAND);
|
|
TTY_WriteByte(p->tty, ESCAPE_EOM);
|
|
|
|
TTY_Flush(p->tty);
|
|
}
|
|
|
|
|
|
static int _Serial_GetMessage (SerialLine *p)
|
|
{
|
|
byte ret;
|
|
short length;
|
|
|
|
if (ProcessInQueue(p))
|
|
return -1;
|
|
|
|
if (p->sock->receiveMessageLength == 0)
|
|
return 0;
|
|
|
|
ret = p->sock->receiveMessage[0];
|
|
length = *(short *)&p->sock->receiveMessage[1];
|
|
if (ret == MTYPE_CONTROL)
|
|
ret = 1;
|
|
|
|
SZ_Clear (&net_message);
|
|
SZ_Write (&net_message, &p->sock->receiveMessage[3], length);
|
|
|
|
length += 3;
|
|
p->sock->receiveMessageLength -= length;
|
|
|
|
if (p->sock->receiveMessageLength + p->lengthFound)
|
|
Q_memcpy(p->sock->receiveMessage, &p->sock->receiveMessage[length], p->sock->receiveMessageLength + p->lengthFound);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int Serial_GetMessage (qsocket_t *sock)
|
|
{
|
|
SerialLine *p;
|
|
int ret;
|
|
|
|
p = (SerialLine *)sock->driverdata;
|
|
|
|
ret = _Serial_GetMessage (p);
|
|
|
|
if (ret == 1)
|
|
messagesReceived++;
|
|
|
|
if (!sock->canSend)
|
|
if ((net_time - sock->lastSendTime) > 1.0)
|
|
{
|
|
ReSendMessage (sock);
|
|
sock->lastSendTime = net_time;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void Serial_Close (qsocket_t *sock)
|
|
{
|
|
SerialLine *p = (SerialLine *)sock->driverdata;
|
|
TTY_Close(p->tty);
|
|
ResetSerialLineProtocol (p);
|
|
}
|
|
|
|
|
|
char *com_types[] = {"direct", "modem"};
|
|
unsigned com_bauds[] = {9600, 14400, 19200, 28800, 57600};
|
|
|
|
void Serial_SearchForHosts (qboolean xmit)
|
|
{
|
|
int n;
|
|
SerialLine *p;
|
|
|
|
if (sv.active)
|
|
return;
|
|
|
|
if (hostCacheCount == HOSTCACHESIZE)
|
|
return;
|
|
|
|
// see if we've already answered
|
|
for (n = 0; n < hostCacheCount; n++)
|
|
if (Q_strcmp (hostcache[n].cname, "#") == 0)
|
|
return;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
|
if (TTY_IsEnabled(n))
|
|
break;
|
|
if (n == NUM_COM_PORTS)
|
|
return;
|
|
p = &serialLine[n];
|
|
|
|
if (TTY_IsModem(p->tty))
|
|
return;
|
|
|
|
sprintf(hostcache[hostCacheCount].name, "COM%u", n+1);
|
|
Q_strcpy(hostcache[hostCacheCount].map, "");
|
|
hostcache[hostCacheCount].users = 0;
|
|
hostcache[hostCacheCount].maxusers = 0;
|
|
hostcache[hostCacheCount].driver = net_driverlevel;
|
|
Q_strcpy(hostcache[hostCacheCount].cname, "#");
|
|
hostCacheCount++;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static qsocket_t *_Serial_Connect (char *host, SerialLine *p)
|
|
{
|
|
int ret;
|
|
double start_time;
|
|
double last_time;
|
|
|
|
p->client = true;
|
|
if (TTY_Connect(p->tty, host))
|
|
return NULL;
|
|
|
|
p->sock = NET_NewQSocket ();
|
|
p->sock->driver = myDriverLevel;
|
|
if (p->sock == NULL)
|
|
{
|
|
Con_Printf("No sockets available\n");
|
|
return NULL;
|
|
}
|
|
p->sock->driverdata = p;
|
|
|
|
// send the connection request
|
|
start_time = SetNetTime();
|
|
last_time = 0.0;
|
|
|
|
SZ_Clear(&net_message);
|
|
MSG_WriteByte(&net_message, CCREQ_CONNECT);
|
|
MSG_WriteString(&net_message, "QUAKE");
|
|
do
|
|
{
|
|
SetNetTime();
|
|
if ((net_time - last_time) >= 1.0)
|
|
{
|
|
Serial_SendControlMessage (p, &net_message);
|
|
last_time = net_time;
|
|
Con_Printf("trying...\n"); SCR_UpdateScreen ();
|
|
}
|
|
ret = _Serial_GetMessage (p);
|
|
}
|
|
while (ret == 0 && (net_time - start_time) < 5.0);
|
|
|
|
if (ret == 0)
|
|
{
|
|
Con_Printf("Unable to connect, no response\n");
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if (ret == -1)
|
|
{
|
|
Con_Printf("Connection request error\n");
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
MSG_BeginReading ();
|
|
ret = MSG_ReadByte();
|
|
if (ret == CCREP_REJECT)
|
|
{
|
|
Con_Printf(MSG_ReadString());
|
|
goto ErrorReturn;
|
|
}
|
|
if (ret != CCREP_ACCEPT)
|
|
{
|
|
Con_Printf("Unknown connection response\n");
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
p->connected = true;
|
|
p->sock->lastMessageTime = net_time;
|
|
|
|
Con_Printf ("Connection accepted\n");
|
|
|
|
return p->sock;
|
|
|
|
ErrorReturn:
|
|
TTY_Disconnect(p->tty);
|
|
return NULL;
|
|
}
|
|
|
|
qsocket_t *Serial_Connect (char *host)
|
|
{
|
|
int n;
|
|
qsocket_t *ret = NULL;
|
|
|
|
// see if this looks like a phone number
|
|
if (*host == '#')
|
|
host++;
|
|
for (n = 0; n < Q_strlen(host); n++)
|
|
if (host[n] == '.' || host[n] == ':')
|
|
return NULL;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
|
if (TTY_IsEnabled(n) && !serialLine[n].connected)
|
|
if ((ret = _Serial_Connect (host, &serialLine[n])))
|
|
break;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static qsocket_t *_Serial_CheckNewConnections (SerialLine *p)
|
|
{
|
|
int command;
|
|
|
|
p->client = false;
|
|
if (!TTY_CheckForConnection(p->tty))
|
|
return NULL;
|
|
|
|
if (TTY_IsModem(p->tty))
|
|
{
|
|
if (!p->connecting)
|
|
{
|
|
p->connecting = true;
|
|
p->connect_time = net_time;
|
|
}
|
|
else if ((net_time - p->connect_time) > 15.0)
|
|
{
|
|
p->connecting = false;
|
|
TTY_Disconnect(p->tty);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
p->sock = NET_NewQSocket ();
|
|
p->sock->driver = myDriverLevel;
|
|
if (p->sock == NULL)
|
|
{
|
|
Con_Printf("No sockets available\n");
|
|
return NULL;
|
|
}
|
|
p->sock->driverdata = p;
|
|
|
|
SZ_Clear(&net_message);
|
|
if (_Serial_GetMessage(p) != 1)
|
|
{
|
|
NET_FreeQSocket(p->sock);
|
|
return NULL;
|
|
}
|
|
|
|
MSG_BeginReading ();
|
|
command = MSG_ReadByte();
|
|
|
|
if (command == CCREQ_SERVER_INFO)
|
|
{
|
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
|
return NULL;
|
|
|
|
if (MSG_ReadByte() != SERIAL_PROTOCOL_VERSION)
|
|
return NULL;
|
|
|
|
SZ_Clear(&net_message);
|
|
MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
|
|
MSG_WriteString(&net_message, hostname.string);
|
|
MSG_WriteString(&net_message, sv.name);
|
|
MSG_WriteByte(&net_message, net_activeconnections);
|
|
MSG_WriteByte(&net_message, svs.maxclients);
|
|
Serial_SendControlMessage (p, &net_message);
|
|
SZ_Clear(&net_message);
|
|
return NULL;
|
|
}
|
|
|
|
if (command != CCREQ_CONNECT)
|
|
return NULL;
|
|
|
|
if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0)
|
|
return NULL;
|
|
|
|
// send him back the info about the server connection he has been allocated
|
|
SZ_Clear(&net_message);
|
|
MSG_WriteByte(&net_message, CCREP_ACCEPT);
|
|
Serial_SendControlMessage (p, &net_message);
|
|
SZ_Clear(&net_message);
|
|
|
|
p->connected = true;
|
|
p->connecting = false;
|
|
p->sock->lastMessageTime = net_time;
|
|
sprintf(p->sock->address, "COM%u", (int)((p - serialLine) + 1));
|
|
|
|
return p->sock;
|
|
}
|
|
|
|
qsocket_t *Serial_CheckNewConnections (void)
|
|
{
|
|
int n;
|
|
qsocket_t *ret = NULL;
|
|
|
|
for (n = 0; n < NUM_COM_PORTS; n++)
|
|
if (TTY_IsEnabled(n) && !serialLine[n].connected)
|
|
if ((ret = _Serial_CheckNewConnections (&serialLine[n])))
|
|
break;
|
|
return ret;
|
|
}
|