diff --git a/engine/nqnet/net_dgrm.c b/engine/nqnet/net_dgrm.c new file mode 100644 index 000000000..1941a6ceb --- /dev/null +++ b/engine/nqnet/net_dgrm.c @@ -0,0 +1,1668 @@ +/* +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_dgrm.c + +#include "../client/quakedef.h" + + +#ifdef NQPROT + +#define NET_GAMENAME_NQ "QUAKE" +#define NET_GAMENAME_QW "QUAKEWORLD" + +// This is enables a simple IP banning mechanism +#define BAN_TEST + +#ifdef BAN_TEST +#if defined(_WIN32) +#include +#elif defined (NeXT) +#include +#include +#else +#define AF_INET 2 /* internet */ +struct in_addr +{ + union + { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +}; +#define s_addr S_un.S_addr /* can be used for most tcp & ip code */ +struct sockaddr_in +{ + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +char *inet_ntoa(struct in_addr in); +unsigned long inet_addr(const char *cp); +#endif +#endif // BAN_TEST + + +#include "net_dgrm.h" + +// these two macros are to make the code more readable +#define sfunc net_landrivers[sock->landriver] +#define dfunc net_landrivers[net_landriverlevel] + +static int net_landriverlevel; + +/* statistic counters */ +int packetsSent = 0; +int packetsReSent = 0; +int packetsReceived = 0; +int receivedDuplicateCount = 0; +int shortPacketCount = 0; +int droppedDatagrams; + +static int myDriverLevel; + +struct +{ + unsigned int length; + unsigned int sequence; + qbyte data[MAX_OVERALLDATAGRAM]; +} packetBuffer; + +#ifndef SERVERONLY +extern int m_return_state; +extern qboolean m_return_onerror; +extern char m_return_reason[32]; +#endif + +#ifdef DEBUG +char *StrAddr (struct qsockaddr *addr) +{ + static char buf[34]; + qbyte *p = (qbyte *)addr; + int n; + + for (n = 0; n < 16; n++) + sprintf (buf + n * 2, "%02x", *p++); + return buf; +} +#endif + + +#ifdef BAN_TEST +unsigned long banAddr = 0x00000000; +unsigned long banMask = 0xffffffff; + +void NET_Ban_f (void) +{ + char addrStr [32]; + char maskStr [32]; + + switch (Cmd_Argc ()) + { + case 1: + if (((struct in_addr *)&banAddr)->s_addr) + { + Q_strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr)); + Q_strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask)); + Con_Printf("Banning %s [%s]\n", addrStr, maskStr); + } + else + Con_Printf("Banning not active\n"); + break; + + case 2: + if (Q_strcasecmp(Cmd_Argv(1), "off") == 0) + banAddr = 0x00000000; + else + banAddr = inet_addr(Cmd_Argv(1)); + banMask = 0xffffffff; + break; + + case 3: + banAddr = inet_addr(Cmd_Argv(1)); + banMask = inet_addr(Cmd_Argv(2)); + break; + + default: + Con_Printf("BAN ip_address [mask]\n"); + break; + } +} +#endif + + +int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + +#ifdef DEBUG + if (data->cursize == 0) + Sys_Error("Datagram_SendMessage: zero length message\n"); + + if (data->cursize > NET_MAXMESSAGE) + Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize); + + if (sock->canSend == false) + Sys_Error("SendMessage: called with canSend == false\n"); +#endif + + Q_memcpy(sock->sendMessage, data->data, data->cursize); + sock->sendMessageLength = data->cursize; + + if (data->cursize <= MAX_NQDATAGRAM) + { + dataLen = data->cursize; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_NQDATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence++); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->canSend = false; + + if (sfunc.Write (sock->socket, (qbyte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsSent++; + return 1; +} + + +int SendMessageNext (qsocket_t *sock) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + + if (sock->sendMessageLength <= MAX_NQDATAGRAM) + { + dataLen = sock->sendMessageLength; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_NQDATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence++); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->sendNext = false; + + if (sfunc.Write (sock->socket, (qbyte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsSent++; + return 1; +} + + +int ReSendMessage (qsocket_t *sock) +{ + unsigned int packetLen; + unsigned int dataLen; + unsigned int eom; + + if (sock->sendMessageLength <= MAX_NQDATAGRAM) + { + dataLen = sock->sendMessageLength; + eom = NETFLAG_EOM; + } + else + { + dataLen = MAX_NQDATAGRAM; + eom = 0; + } + packetLen = NET_HEADERSIZE + dataLen; + + packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); + packetBuffer.sequence = BigLong(sock->sendSequence - 1); + Q_memcpy (packetBuffer.data, sock->sendMessage, dataLen); + + sock->sendNext = false; + + if (sfunc.Write (sock->socket, (qbyte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + sock->lastSendTime = net_time; + packetsReSent++; + return 1; +} + + +qboolean Datagram_CanSendMessage (qsocket_t *sock) +{ + if (sock->sendNext) + SendMessageNext (sock); + + return sock->canSend; +} + + +qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock) +{ + return true; +} + + +int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) +{ + int packetLen; + +#ifdef DEBUG + if (data->cursize == 0) + Sys_Error("Datagram_SendUnreliableMessage: zero length message\n"); + + if (data->cursize > MAX_NQDATAGRAM) + Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize); +#endif + + packetLen = NET_HEADERSIZE + data->cursize; + + packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE); + packetBuffer.sequence = BigLong(sock->unreliableSendSequence++); + Q_memcpy (packetBuffer.data, data->data, data->cursize); + + if (sfunc.Write (sock->socket, (qbyte *)&packetBuffer, packetLen, &sock->addr) == -1) + return -1; + + packetsSent++; + return 1; +} + + +int Datagram_GetMessage (qsocket_t *sock) +{ + unsigned int length; + unsigned int flags; + int ret = 0; + struct sockaddr_qstorage readaddr; + unsigned int sequence; + unsigned int count; + + if (!sock->canSend) + if ((net_time - sock->lastSendTime) > 1.0) + ReSendMessage (sock); + + for(;;) + { + length = sfunc.Read (sock->socket, (qbyte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); + +// if ((rand() & 255) > 220) +// continue; + + if (length == 0) + break; + + if (length == -1) + { + Con_Printf("Read error\n"); + return -1; + } + + if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) + { +#ifdef DEBUG + Con_DPrintf("Forged packet received\n"); + Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); + Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); +#endif + continue; + } + + if (length < NET_HEADERSIZE) + { + shortPacketCount++; + continue; + } + + length = BigLong(packetBuffer.length); + flags = length & (~NETFLAG_LENGTH_MASK); + length &= NETFLAG_LENGTH_MASK; + + if (flags & NETFLAG_CTL) + continue; + + sequence = BigLong(packetBuffer.sequence); + packetsReceived++; + + if (flags & NETFLAG_UNRELIABLE) + { + if (sequence < sock->unreliableReceiveSequence) + { + Con_DPrintf("Got a stale datagram\n"); + ret = 0; + break; + } + if (sequence != sock->unreliableReceiveSequence) + { + count = sequence - sock->unreliableReceiveSequence; + droppedDatagrams += count; + Con_DPrintf("Dropped %u datagram(s)\n", count); + } + sock->unreliableReceiveSequence = sequence + 1; + + length -= NET_HEADERSIZE; + + SZ_Clear (&net_message); + SZ_Write (&net_message, packetBuffer.data, length); + + ret = 2; + break; + } + + if (flags & NETFLAG_ACK) + { + if (sequence != (sock->sendSequence - 1)) + { + Con_DPrintf("Stale ACK received\n"); + continue; + } + if (sequence == sock->ackSequence) + { + sock->ackSequence++; + if (sock->ackSequence != sock->sendSequence) + Con_DPrintf("ack sequencing error\n"); + } + else + { + Con_DPrintf("Duplicate ACK received\n"); + continue; + } + sock->sendMessageLength -= MAX_NQDATAGRAM; + if (sock->sendMessageLength > 0) + { + Q_memcpy(sock->sendMessage, sock->sendMessage+MAX_NQDATAGRAM, sock->sendMessageLength); + sock->sendNext = true; + } + else + { + sock->sendMessageLength = 0; + sock->canSend = true; + } + continue; + } + + if (flags & NETFLAG_DATA) + { + packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); + packetBuffer.sequence = BigLong(sequence); + sfunc.Write (sock->socket, (qbyte *)&packetBuffer, NET_HEADERSIZE, &readaddr); + + if (sequence != sock->receiveSequence) + { + receivedDuplicateCount++; + continue; + } + sock->receiveSequence++; + + length -= NET_HEADERSIZE; + + if (flags & NETFLAG_EOM) + { + SZ_Clear(&net_message); + SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); + SZ_Write(&net_message, packetBuffer.data, length); + sock->receiveMessageLength = 0; + + ret = 1; + break; + } + + Q_memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); + sock->receiveMessageLength += length; + continue; + } + } + + if (sock->sendNext) + SendMessageNext (sock); + + return ret; +} + + +void PrintStats(qsocket_t *s) +{ + Con_Printf("canSend = %4u \n", s->canSend); + Con_Printf("sendSeq = %4u ", s->sendSequence); + Con_Printf("recvSeq = %4u \n", s->receiveSequence); + Con_Printf("\n"); +} + +void NET_Stats_f (void) +{ + qsocket_t *s; + + if (Cmd_Argc () == 1) + { + Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent); + Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived); + Con_Printf("reliable messages sent = %i\n", messagesSent); + Con_Printf("reliable messages received = %i\n", messagesReceived); + Con_Printf("packetsSent = %i\n", packetsSent); + Con_Printf("packetsReSent = %i\n", packetsReSent); + Con_Printf("packetsReceived = %i\n", packetsReceived); + Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount); + Con_Printf("shortPacketCount = %i\n", shortPacketCount); + Con_Printf("droppedDatagrams = %i\n", droppedDatagrams); + } + else if (Q_strcmp(Cmd_Argv(1), "*") == 0) + { + for (s = net_activeSockets; s; s = s->next) + PrintStats(s); + for (s = net_freeSockets; s; s = s->next) + PrintStats(s); + } + else + { + for (s = net_activeSockets; s; s = s->next) + if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + for (s = net_freeSockets; s; s = s->next) + if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + return; + PrintStats(s); + } +} + +/* +static qboolean testInProgress = false; +static int testPollCount; +static int testDriver; +static int testSocket; + +static void Test_Poll(void *arg); +PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; + +static void Test_Poll(void *arg) +{ + struct qsockaddr clientaddr; + int control; + int len; + char name[32]; + char address[64]; + int colors; + int frags; + int connectTime; + qbyte playerNumber; + + net_landriverlevel = testDriver; + + while (1) + { + len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + break; + + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + break; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + break; + if ((control & NETFLAG_LENGTH_MASK) != len) + break; + + if (MSG_ReadByte() != CCREP_PLAYER_INFO) + Sys_Error("Unexpected repsonse to Player Info request\n"); + + playerNumber = MSG_ReadByte(); + Q_strcpy(name, MSG_ReadString()); + colors = MSG_ReadLong(); + frags = MSG_ReadLong(); + connectTime = MSG_ReadLong(); + Q_strcpy(address, MSG_ReadString()); + + Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); + } + + testPollCount--; + if (testPollCount) + { + SchedulePollProcedure(&testPollProcedure, 0.1); + } + else + { + dfunc.CloseSocket(testSocket); + testInProgress = false; + } +} + +static void Test_f (void) +{ + char *host; + int n; + int max = MAX_SCOREBOARD; + struct qsockaddr sendaddr; + + if (testInProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + max = hostcache[n].maxusers; + Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); + break; + } + if (n < hostCacheCount) + goto JustDoIt; + } + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (!net_landrivers[net_landriverlevel].initialized) + continue; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) != -1) + break; + } + if (net_landriverlevel == net_numlandrivers) + return; + +JustDoIt: + testSocket = dfunc.OpenSocket(0); + if (testSocket == -1) + return; + + testInProgress = true; + testPollCount = 20; + testDriver = net_landriverlevel; + + for (n = 0; n < max; n++) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); + MSG_WriteByte(&net_message, n); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); + } + SZ_Clear(&net_message); + SchedulePollProcedure(&testPollProcedure, 0.1); +} + + +static qboolean test2InProgress = false; +static int test2Driver; +static int test2Socket; + +static void Test2_Poll(void *arg); +PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; + +static void Test2_Poll(void *arg) +{ + struct qsockaddr clientaddr; + int control; + int len; + char name[256]; + char value[256]; + + net_landriverlevel = test2Driver; + name[0] = 0; + + len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + goto Reschedule; + + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + goto Error; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + goto Error; + if ((control & NETFLAG_LENGTH_MASK) != len) + goto Error; + + if (MSG_ReadByte() != CCREP_RULE_INFO) + goto Error; + + Q_strcpy(name, MSG_ReadString()); + if (name[0] == 0) + goto Done; + Q_strcpy(value, MSG_ReadString()); + + Con_Printf("%-16.16s %-16.16s\n", name, value); + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, name); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + +Reschedule: + SchedulePollProcedure(&test2PollProcedure, 0.05); + return; + +Error: + Con_Printf("Unexpected repsonse to Rule Info request\n"); +Done: + dfunc.CloseSocket(test2Socket); + test2InProgress = false; + return; +} + +static void Test2_f (void) +{ + char *host; + int n; + struct qsockaddr sendaddr; + + if (test2InProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + Q_memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); + break; + } + if (n < hostCacheCount) + goto JustDoIt; + } + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (!net_landrivers[net_landriverlevel].initialized) + continue; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) != -1) + break; + } + if (net_landriverlevel == net_numlandrivers) + return; + +JustDoIt: + test2Socket = dfunc.OpenSocket(0); + if (test2Socket == -1) + return; + + test2InProgress = true; + test2Driver = net_landriverlevel; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_RULE_INFO); + MSG_WriteString(&net_message, ""); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + SchedulePollProcedure(&test2PollProcedure, 0.05); +} +*/ + +int Datagram_Init (void) +{ + int i; + int csock; + + myDriverLevel = net_driverlevel; + Cmd_AddCommand ("net_stats", NET_Stats_f); + + if (COM_CheckParm("-nolan")) + return -1; + + for (i = 0; i < net_numlandrivers; i++) + { + csock = net_landrivers[i].Init (); + if (csock == -1) + continue; + net_landrivers[i].initialized = true; + net_landrivers[i].controlSock = csock; + } + +#ifdef BAN_TEST + Cmd_AddCommand ("ban", NET_Ban_f); +#endif +// Cmd_AddCommand ("test", Test_f); +// Cmd_AddCommand ("test2", Test2_f); + + return 0; +} + + +void Datagram_Shutdown (void) +{ + int i; + +// +// shutdown the lan drivers +// + for (i = 0; i < net_numlandrivers; i++) + { + if (net_landrivers[i].initialized) + { + net_landrivers[i].Shutdown (); + net_landrivers[i].initialized = false; + } + } +} + + +void Datagram_Close (qsocket_t *sock) +{ + sfunc.CloseSocket(sock->socket); +} + + +void Datagram_Listen (qboolean state) +{ +#ifndef CLIENTONLY + int i; + + for (i = 0; i < net_numlandrivers; i++) + if (net_landrivers[i].initialized) + net_landrivers[i].Listen (state); +#endif +} + +#ifndef CLIENTONLY +static qsocket_t *_Datagram_CheckNewConnections (void) +{ + struct sockaddr_qstorage clientaddr; + struct sockaddr_qstorage newaddr; + int newsock; + int acceptsock; + qsocket_t *sock; + qsocket_t *s; + int len; + int command; + int control; + int ret; +#ifdef NET_GAMENAME_QW + char *gname; +#endif + + acceptsock = dfunc.CheckNewConnections(); + if (acceptsock == -1) + return NULL; + + SZ_Clear(&net_message); + + len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); + if (len < sizeof(int)) + return NULL; + net_message.cursize = len; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + return NULL; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + return NULL; + if ((control & NETFLAG_LENGTH_MASK) != len) + return NULL; + + command = MSG_ReadByte(); + if (command == CCREQ_SERVER_INFO) + { + int numcl; + +#ifdef NET_GAMENAME_QW + gname = MSG_ReadString(); + if (Q_strcmp(gname, NET_GAMENAME_QW)) + if (Q_strcmp(gname, NET_GAMENAME_NQ) ) + return NULL; +#else + if (Q_strcmp(MSG_ReadString(), NET_GAMENAME_NQ) != 0) + return NULL; +#endif + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_SERVER_INFO); + dfunc.GetSocketAddr(acceptsock, &newaddr); + MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); + MSG_WriteString(&net_message, hostname.string); + MSG_WriteString(&net_message, sv.name); + numcl = 0; + for (len = 0; len < MAX_CLIENTS; len++) + { + if(svs.clients[len].state>cs_zombie || *svs.clients[len].name) //client or bot. + numcl++; + } + MSG_WriteByte(&net_message, numcl); + MSG_WriteByte(&net_message, sv.allocated_client_slots); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + + if (command == CCREQ_PLAYER_INFO) + { + int playerNumber; + int activeNumber; + int clientNumber; + client_t *client; + + playerNumber = MSG_ReadByte(); + activeNumber = -1; + for (clientNumber = 0, client = svs.clients; clientNumber < sv.allocated_client_slots; clientNumber++, client++) + { + if (client->state>cs_zombie || *svs.clients[len].name) + { + activeNumber++; + if (activeNumber == playerNumber) + break; + } + } + if (clientNumber == sv.allocated_client_slots) + return NULL; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); + MSG_WriteByte(&net_message, playerNumber); + MSG_WriteString(&net_message, client->name); + MSG_WriteLong(&net_message, 0); + MSG_WriteLong(&net_message, (int)client->old_frags); + MSG_WriteLong(&net_message, (int)(net_time - client->connection_started)); + MSG_WriteString(&net_message, "WITHHELD"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return NULL; + } + + if (command == CCREQ_RULE_INFO) + { + extern cvar_group_t cvargroup_serverinfo; + char *prevCvarName; + cvar_t *var; + + // find the search start location + prevCvarName = MSG_ReadString(); + if (*prevCvarName) + { + var = Cvar_FindVar (prevCvarName); + if (!var) + return NULL; + var = var->next; + } + else + var = cvargroup_serverinfo.cvars; + + // search for the next server cvar + while (var) + { + if (var->flags&CVAR_SERVERINFO) + break; + var = var->next; + } + + // send the response + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_RULE_INFO); + if (var) + { + MSG_WriteString(&net_message, var->name); + MSG_WriteString(&net_message, var->string); + } + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return NULL; + } + + if (command != CCREQ_CONNECT) + return NULL; + +#ifdef NET_GAMENAME_QW + gname = MSG_ReadString(); + if (Q_strcmp(gname, NET_GAMENAME_QW)) + if (Q_strcmp(gname, NET_GAMENAME_NQ) ) + return NULL; +#else + if (Q_strcmp(MSG_ReadString(), NET_GAMENAME_NQ) != 0) + return NULL; +#endif + + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "Incompatible version.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + +#ifdef BAN_TEST + // check for a ban + if (clientaddr.sa_family == AF_INET) + { + unsigned long testAddr; + testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; + if ((testAddr & banMask) == banAddr) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "You have been banned.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + } +#endif + + // see if this guy is already connected + for (s = net_activeSockets; s; s = s->next) + { + if (s->driver != net_driverlevel) + continue; + ret = dfunc.AddrCompare(&clientaddr, &s->addr); + if (ret >= 0) + { + // is this a duplicate connection reqeust? + if (ret == 0 && net_time - s->connecttime < 2.0) + { + // yes, so send a duplicate reply + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_ACCEPT); + dfunc.GetSocketAddr(s->socket, &newaddr); + MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); + MSG_WriteString(&net_message, gname); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + // it's somebody coming back in from a crash/disconnect + // so close the old qsocket and let their retry get them back in + NET_Close(s); + return NULL; + } + } + + // allocate a QSocket + sock = NET_NewQSocket (); + if (sock == NULL) + { + // no room; try to let him know + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_REJECT); + MSG_WriteString(&net_message, "Server is full.\n"); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + return NULL; + } + + if (!Q_strcmp(gname, NET_GAMENAME_QW)) + sock->qwprotocol = true; + + // allocate a network socket + newsock = dfunc.OpenSocket(0); + if (newsock == -1) + { + NET_FreeQSocket(sock); + return NULL; + } + + // connect to the client + if (dfunc.Connect (newsock, &clientaddr) == -1) + { + dfunc.CloseSocket(newsock); + NET_FreeQSocket(sock); + return NULL; + } + + // everything is allocated, just fill in the details + sock->socket = newsock; + sock->landriver = net_landriverlevel; + sock->addr = clientaddr; + Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); + + // send him back the info about the server connection he has been allocated + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREP_ACCEPT); + dfunc.GetSocketAddr(newsock, &newaddr); + MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); + MSG_WriteString(&net_message, gname); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); + SZ_Clear(&net_message); + + return sock; +} + +qsocket_t *Datagram_CheckNewConnections (void) +{ + qsocket_t *ret = NULL; + + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_CheckNewConnections ()) != NULL) + break; + return ret; +} +#else +qsocket_t *Datagram_CheckNewConnections (void) +{ + return NULL; //client only can't. +} +#endif + +static void _Datagram_SearchForHosts (qboolean xmit) +{ + int ret; + int n; + int i; + struct sockaddr_qstorage readaddr; + struct sockaddr_qstorage myaddr; + int control; + + dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); + if (xmit) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); + MSG_WriteString(&net_message, NET_GAMENAME_QW); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); + SZ_Clear(&net_message); + + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); + MSG_WriteString(&net_message, NET_GAMENAME_NQ); //look for either sort of server + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); + SZ_Clear(&net_message); + } + + while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) + { + if (ret < sizeof(int)) + continue; + net_message.cursize = ret; + + // don't answer our own query +// if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) +// continue; + + // is the cache full? + if (hostCacheCount == HOSTCACHESIZE) + continue; + + MSG_BeginReading (); + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + continue; + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + continue; + if ((control & NETFLAG_LENGTH_MASK) != ret) + continue; + + if (MSG_ReadByte() != CCREP_SERVER_INFO) + continue; + + dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); + // search the cache for this server + for (n = 0; n < hostCacheCount; n++) + if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) + break; + + // is it already there? + if (n < hostCacheCount) + continue; + + // add it + hostCacheCount++; + Q_strncpyz(hostcache[n].name, MSG_ReadString(), sizeof(hostcache[n].name)); + Q_strncpyz(hostcache[n].map, MSG_ReadString(), sizeof(hostcache[n].map)); + hostcache[n].users = MSG_ReadByte(); + hostcache[n].maxusers = MSG_ReadByte(); + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + Q_strcpy(hostcache[n].cname, hostcache[n].name); + hostcache[n].cname[14] = 0; + Q_strcpy(hostcache[n].name, "*"); + Q_strcat(hostcache[n].name, hostcache[n].cname); + } + Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct sockaddr_qstorage)); + hostcache[n].driver = net_driverlevel; + hostcache[n].ldriver = net_landriverlevel; + Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); + + // check for a name conflict + for (i = 0; i < hostCacheCount; i++) + { + if (i == n) + continue; + if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) + { + i = Q_strlen(hostcache[n].name); + if (i < 15 && hostcache[n].name[i-1] > '8') + { + hostcache[n].name[i] = '0'; + hostcache[n].name[i+1] = 0; + } + else + hostcache[n].name[i-1]++; + i = -1; + } + } + } +} + +void Datagram_SearchForHosts (qboolean xmit) +{ + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + { + if (hostCacheCount == HOSTCACHESIZE) + break; + if (net_landrivers[net_landriverlevel].initialized) + _Datagram_SearchForHosts (xmit); + } +} + +#ifndef SERVERONLY +static qsocket_t *_Datagram_Connect (char *host) +{ + struct sockaddr_qstorage sendaddr; + struct sockaddr_qstorage readaddr; + qsocket_t *sock; + int newsock; + int ret=0; + int reps; + double start_time; + int control; + char *reason; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) == -1) + return NULL; + + newsock = dfunc.OpenSocket (0); + if (newsock == -1) + return NULL; + + sock = NET_NewQSocket (); + if (sock == NULL) + goto ErrorReturn2; + sock->socket = newsock; + sock->landriver = net_landriverlevel; + + // connect to the host + if (dfunc.Connect (newsock, &sendaddr) == -1) + goto ErrorReturn; + + // send the connection request + Con_Printf("trying...\n"); SCR_UpdateScreen (); + start_time = net_time; + + for (reps = 0; reps < 3; reps++) + { + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, NET_GAMENAME_QW); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, NET_GAMENAME_NQ); //eitehr will do + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + +/* { + int s = sock->landriver, d = net_landriverlevel; + SVNQ_CheckForNewClients(); + net_landriverlevel = d; + sock->landriver = s; + } +*/ + do + { + ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); + // if we got something, validate it + if (ret > 0) + { + // is it from the right place? + if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) + { +#ifdef DEBUG + Con_Printf("wrong reply address\n"); + Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); + Con_Printf("Received: %s\n", StrAddr (&readaddr)); + SCR_UpdateScreen (); +#endif + ret = 0; + continue; + } + + if (ret < sizeof(int)) + { + ret = 0; + continue; + } + + net_message.cursize = ret; + MSG_BeginReading (); + + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + { + ret = 0; + continue; + } + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + { + ret = 0; + continue; + } + if ((control & NETFLAG_LENGTH_MASK) != ret) + { + ret = 0; + continue; + } + } + } + while (ret == 0 && (SetNetTime() - start_time) < 2.5); + if (ret) + break; + Con_Printf("still trying...\n"); SCR_UpdateScreen (); + start_time = SetNetTime(); + } + + if (ret == 0) + { + reason = "No Response"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + if (ret == -1) + { + reason = "Network Error"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + reason = MSG_ReadString(); + Con_Printf(reason); + Q_strncpyz(m_return_reason, reason, sizeof(m_return_reason)); + goto ErrorReturn; + } + + if (ret == CCREP_ACCEPT) + { + Q_memcpy(&sock->addr, &sendaddr, sizeof(struct sockaddr_qstorage)); + dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); + if (!Q_strcmp(NET_GAMENAME_QW, MSG_ReadString())) + sock->qwprotocol = true; + } + else + { + reason = "Bad Response"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + dfunc.GetNameFromAddr (&sendaddr, sock->address); + + Con_Printf ("Connection accepted\n"); + sock->lastMessageTime = SetNetTime(); + + // switch the connection to the specified address + if (dfunc.Connect (newsock, &sock->addr) == -1) + { + reason = "Connect to Game failed"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + m_return_onerror = false; + return sock; + +ErrorReturn: + NET_FreeQSocket(sock); +ErrorReturn2: + dfunc.CloseSocket(newsock); + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + } + return NULL; +} +static int newsock; +static qsocket_t *sock; +static qsocket_t *_Datagram_FailConnect (char *host) +{ + if (sock) + NET_Close(sock); +// NET_FreeQSocket(sock); + + if (!newsock) + return NULL; + + dfunc.CloseSocket(newsock); + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + } + + sock = NULL; + + return NULL; +} +static qsocket_t *_Datagram_ContinueConnect (char *host) +{ + struct sockaddr_qstorage sendaddr; + struct sockaddr_qstorage readaddr; + int ret; +// int reps; + int control; + char *reason; + + // see if we can resolve the host name + if (!sock || dfunc.GetAddrFromName(host, &sendaddr) == -1) + return NULL; + + + do + { + ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); + // if we got something, validate it + if (ret > 0) + { + // is it from the right place? + if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) + { +#ifdef DEBUG + Con_Printf("wrong reply address\n"); + Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); + Con_Printf("Received: %s\n", StrAddr (&readaddr)); + SCR_UpdateScreen (); +#endif + ret = 0; + continue; + } + + if (ret < sizeof(int)) + { + ret = 0; + continue; + } + + net_message.cursize = ret; + MSG_BeginReading (); + + control = BigLong(*((int *)net_message.data)); + MSG_ReadLong(); + if (control == -1) + { + ret = 0; + continue; + } + if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) + { + ret = 0; + continue; + } + if ((control & NETFLAG_LENGTH_MASK) != ret) + { + ret = 0; + continue; + } + break; + } + } + while (ret > 0); + + + if (ret == 0) + { + reason = "No Response"; +// Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + return NULL; + } + + if (ret == -1) + { + reason = "Network Error"; +// Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + return _Datagram_FailConnect(host); + } + + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + reason = MSG_ReadString(); + Con_Printf(reason); + Q_strncpyz(m_return_reason, reason, sizeof(m_return_reason)); + return _Datagram_FailConnect(host); + } + + if (ret == CCREP_ACCEPT) + { + Q_memcpy(&sock->addr, &sendaddr, sizeof(struct sockaddr_qstorage)); + dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); + if (!Q_strcmp(NET_GAMENAME_QW, MSG_ReadString())) + sock->qwprotocol = true; + } + else + { + reason = "Bad Response"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + return _Datagram_FailConnect(host); + } + + dfunc.GetNameFromAddr (&sendaddr, sock->address); + + Con_Printf ("Connection accepted\n"); + sock->lastMessageTime = SetNetTime(); + + // switch the connection to the specified address + if (dfunc.Connect (newsock, &sock->addr) == -1) + { + reason = "Connect to Game failed"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + return _Datagram_FailConnect(host); + } + + m_return_onerror = false; + return sock; +} +static qsocket_t *_Datagram_BeginConnect (char *host) +{ + struct sockaddr_qstorage sendaddr; + double start_time; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) == -1) + return NULL; + + sock = _Datagram_ContinueConnect(host); + if (sock) + return sock; + + newsock = dfunc.OpenSocket (0); + if (newsock == -1) + return _Datagram_FailConnect(host); + + sock = NET_NewQSocket (); + if (sock == NULL) + return _Datagram_FailConnect(host); + sock->socket = newsock; + sock->landriver = net_landriverlevel; + + // connect to the host + if (dfunc.Connect (newsock, &sendaddr) == -1) + { + return _Datagram_FailConnect(host); + } + + // send the connection request + Con_Printf("trying...\n"); + start_time = net_time; + + SZ_Clear(&net_message); + // save space for the header, filled in later + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, NET_GAMENAME_QW); + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + + MSG_WriteLong(&net_message, 0); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, NET_GAMENAME_NQ); //either will do + MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); + *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); + SZ_Clear(&net_message); + + return NULL; +} +#endif +qsocket_t *Datagram_BeginConnect (char *host) +{ + qsocket_t *ret = NULL; +#ifndef SERVERONLY + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_BeginConnect (host)) != NULL) + break; +#endif + return ret; +} +qsocket_t *Datagram_ContinueConnect (char *host) +{ + qsocket_t *ret = NULL; +#ifndef SERVERONLY + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_ContinueConnect (host)) != NULL) + break; +#endif + return ret; +} +qsocket_t *Datagram_Connect (char *host) +{ + qsocket_t *ret = NULL; +#ifndef SERVERONLY + for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) + if (net_landrivers[net_landriverlevel].initialized) + if ((ret = _Datagram_Connect (host)) != NULL) + break; +#endif + return ret; +} +#endif + diff --git a/engine/nqnet/net_dgrm.h b/engine/nqnet/net_dgrm.h new file mode 100644 index 000000000..dde77a533 --- /dev/null +++ b/engine/nqnet/net_dgrm.h @@ -0,0 +1,37 @@ +/* +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_dgrm.h + + +int Datagram_Init (void); +void Datagram_Listen (qboolean state); +void Datagram_SearchForHosts (qboolean xmit); +qsocket_t *Datagram_Connect (char *host); +qsocket_t *Datagram_CheckNewConnections (void); +int Datagram_GetMessage (qsocket_t *sock); +int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data); +int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); +qboolean Datagram_CanSendMessage (qsocket_t *sock); +qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock); +void Datagram_Close (qsocket_t *sock); +void Datagram_Shutdown (void); + +qsocket_t *Datagram_BeginConnect (char *host); +qsocket_t *Datagram_ContinueConnect (char *host); diff --git a/engine/nqnet/net_loop.c b/engine/nqnet/net_loop.c new file mode 100644 index 000000000..f152ab149 --- /dev/null +++ b/engine/nqnet/net_loop.c @@ -0,0 +1,267 @@ +/* +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_loop.c + +#include "../client/quakedef.h" +#if !(defined(CLIENTONLY) || defined(SERVERONLY)) +#ifdef NQPROT +#include "net_loop.h" + +qboolean localconnectpending = false; +qsocket_t *loop_client = NULL; +qsocket_t *loop_server = NULL; + +int Loop_Init (void) +{ + if (isDedicated) + return -1; +// if (cls.state == ca_dedicated) +// return -1; + return 0; +} + + +void Loop_Shutdown (void) +{ +} + + +void Loop_Listen (qboolean state) +{ +} + + +void Loop_SearchForHosts (qboolean xmit) +{ + if (!sv.active) + return; + + hostCacheCount = 1; + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + Q_strcpy(hostcache[0].name, "local"); + else + Q_strcpy(hostcache[0].name, hostname.string); + Q_strcpy(hostcache[0].map, sv.name); + hostcache[0].users = net_activeconnections; + hostcache[0].maxusers = 32; + hostcache[0].driver = net_driverlevel; + Q_strcpy(hostcache[0].cname, "local"); +} + + +qsocket_t *Loop_Connect (char *host) +{ + if (Q_strcmp(host,"local") != 0) + return NULL; + + if (localconnectpending && loop_client && loop_server) + return loop_client; + + if (!loop_client) + { + if ((loop_client = NET_NewQSocket ()) == NULL) + { + Con_Printf("Loop_Connect: no qsocket available\n"); + return NULL; + } + Q_strcpy (loop_client->address, "localhost"); + } + else + loop_client->disconnected = false; + loop_client->receiveMessageLength = 0; + loop_client->sendMessageLength = 0; + loop_client->canSend = true; + + if (!loop_server) + { + if ((loop_server = NET_NewQSocket ()) == NULL) + { + Con_Printf("Loop_Connect: no qsocket available\n"); + return NULL; + } + Q_strcpy (loop_server->address, "LOCAL"); + } + else + loop_server->disconnected = false; + loop_server->receiveMessageLength = 0; + loop_server->sendMessageLength = 0; + loop_server->canSend = true; + + loop_client->driverdata = (void *)loop_server; + loop_server->driverdata = (void *)loop_client; + + loop_server->qwprotocol = loop_client->qwprotocol = true; + + localconnectpending = true; + + return loop_client; +} + + +qsocket_t *Loop_CheckNewConnections (void) +{ + if (!localconnectpending || !loop_server || !loop_client) + return NULL; + + localconnectpending = false; + loop_server->sendMessageLength = 0; + loop_server->receiveMessageLength = 0; + loop_server->canSend = true; + loop_client->sendMessageLength = 0; + loop_client->receiveMessageLength = 0; + loop_client->canSend = true; + return loop_server; +} + + +static int IntAlign(int value) +{ + return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1)); +} + + +int Loop_GetMessage (qsocket_t *sock) +{ + int ret; + int length; + + if (sock->receiveMessageLength == 0) + return 0; + + ret = sock->receiveMessage[0]; + length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8); + // alignment qbyte skipped here + SZ_Clear (&net_message); + SZ_Write (&net_message, &sock->receiveMessage[4], length); + + length = IntAlign(length + 4); + sock->receiveMessageLength -= length; + + if (sock->receiveMessageLength) + Q_memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength); + + if (sock->driverdata && ret == 1) + ((qsocket_t *)sock->driverdata)->canSend = true; + + return ret; +} + + +int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + qbyte *buffer; + int *bufferLength; + + if (!sock->driverdata) + return -1; + + bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; + + if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE) + Sys_Error("Loop_SendMessage: overflow\n"); + + buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; + + // message type + *buffer++ = 1; + + // length + *buffer++ = data->cursize & 0xff; + *buffer++ = data->cursize >> 8; + + // align + buffer++; + + // message + Q_memcpy(buffer, data->data, data->cursize); + *bufferLength = IntAlign(*bufferLength + data->cursize + 4); + + sock->canSend = false; + return 1; +} + + +int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) +{ + qbyte *buffer; + int *bufferLength; + + if (!sock->driverdata) + return -1; + + bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; + + if ((*bufferLength + data->cursize + sizeof(qbyte) + sizeof(short)) > NET_MAXMESSAGE) + return 0; + + buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; + + // message type + *buffer++ = 2; + + // length + *buffer++ = data->cursize & 0xff; + *buffer++ = data->cursize >> 8; + + // align + buffer++; + + // message + Q_memcpy(buffer, data->data, data->cursize); + *bufferLength = IntAlign(*bufferLength + data->cursize + 4); + return 1; +} + + +qboolean Loop_CanSendMessage (qsocket_t *sock) +{ + if (!sock->driverdata) + return false; + return sock->canSend; +} + + +qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock) +{ + return true; +} + + +void Loop_Close (qsocket_t *sock) +{ + localconnectpending = false; + + if (sock->driverdata) + ((qsocket_t *)sock->driverdata)->driverdata = NULL; + sock->receiveMessageLength = 0; + sock->sendMessageLength = 0; + sock->canSend = true; + if (sock == loop_client) + loop_client = NULL; + else + loop_server = NULL; + + if (loop_server) + loop_server->disconnected=2; + else if (loop_client) + loop_client->disconnected=2; +} +#endif +#endif diff --git a/engine/nqnet/net_loop.h b/engine/nqnet/net_loop.h new file mode 100644 index 000000000..1629f7082 --- /dev/null +++ b/engine/nqnet/net_loop.h @@ -0,0 +1,37 @@ +/* +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_loop.h + +#if !defined(SERVERONLY) && !defined(CLIENTONLY) + +int Loop_Init (void); +void Loop_Listen (qboolean state); +void Loop_SearchForHosts (qboolean xmit); +qsocket_t *Loop_Connect (char *host); +qsocket_t *Loop_CheckNewConnections (void); +int Loop_GetMessage (qsocket_t *sock); +int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data); +int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); +qboolean Loop_CanSendMessage (qsocket_t *sock); +qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock); +void Loop_Close (qsocket_t *sock); +void Loop_Shutdown (void); + +#endif diff --git a/engine/nqnet/net_main.c b/engine/nqnet/net_main.c new file mode 100644 index 000000000..425ab8b89 --- /dev/null +++ b/engine/nqnet/net_main.c @@ -0,0 +1,1111 @@ +/* +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_main.c + +#define NOCOM + +#include "../client/quakedef.h" +#ifdef NQPROT +#include "net_vcr.h" + + +#define Sys_FloatTime Sys_DoubleTime + +qsocket_t *net_activeSockets = NULL; +qsocket_t *net_freeSockets = NULL; +int net_numsockets = 0; + +#ifndef NOCOM +qboolean serialAvailable = false; +#endif +qboolean ipxAvailable = false; +qboolean tcpipAvailable = false; + +int net_hostport; +int DEFAULTnet_hostport = 26000; + +char my_ipx_address[NET_NAMELEN]; +char my_tcpip_address[NET_NAMELEN]; + +#ifndef NOCOM +void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +#endif + +static qboolean listening = false; + +qboolean slistInProgress = false; +qboolean slistSilent = false; +qboolean slistLocal = true; +static double slistStartTime; +static int slistLastShown; + +static void Slist_Send(void *arg); +static void Slist_Poll(void *arg); +PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; +PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll}; + + +extern sizebuf_t net_message; +int net_activeconnections = 0; + +int messagesSent = 0; +int messagesReceived = 0; +int unreliableMessagesSent = 0; +int unreliableMessagesReceived = 0; + +cvar_t net_messagetimeout = {"net_messagetimeout","300"}; + +#ifndef NOCOM +qboolean configRestored = false; +cvar_t config_com_port = {"_config_com_port", "0x3f8", CVAR_ARCHIVE}; +cvar_t config_com_irq = {"_config_com_irq", "4", CVAR_ARCHIVE}; +cvar_t config_com_baud = {"_config_com_baud", "57600", CVAR_ARCHIVE}; +cvar_t config_com_modem = {"_config_com_modem", "1", CVAR_ARCHIVE}; +cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", CVAR_ARCHIVE}; +cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", CVAR_ARCHIVE}; +cvar_t config_modem_init = {"_config_modem_init", "", CVAR_ARCHIVE}; +cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", CVAR_ARCHIVE}; +#endif + +#ifdef IDGODS +cvar_t idgods = {"idgods", "0"}; +#endif + +int vcrFile = -1; +qboolean recording = false; + +// these two macros are to make the code more readable +#define sfunc net_drivers[sock->driver] +#define dfunc net_drivers[net_driverlevel] + +int net_driverlevel; + + +double net_time; + +double SetNetTime(void) +{ + net_time = Sys_FloatTime(); + return net_time; +} + +#if defined(_WIN32) || defined(SERVERONLY) +int Sys_FileWrite (int handle, void *data, int count) +{ + return 0; +} +int Sys_FileRead (int handle, void *data, int count) +{ + return 0; +} +void Sys_FileClose (int handle) +{ + return; +} +#endif +/* +=================== +NET_NewQSocket + +Called by drivers when a new communications endpoint is required +The sequence and buffer fields will be filled in properly +=================== +*/ +qsocket_t *NET_NewQSocket (void) +{ + qsocket_t *sock; + + if (net_freeSockets == NULL) + return NULL; + + if (net_activeconnections >= 32) + return NULL; + + // get one from free list + sock = net_freeSockets; + net_freeSockets = sock->next; + + // add it to active list + sock->next = net_activeSockets; + net_activeSockets = sock; + + sock->disconnected = false; + sock->connecttime = net_time; + Q_strcpy (sock->address,"UNSET ADDRESS"); + sock->driver = net_driverlevel; + sock->socket = 0; + sock->driverdata = NULL; + sock->qwprotocol = false; + sock->canSend = true; + sock->sendNext = false; + sock->lastMessageTime = net_time; + sock->ackSequence = 0; + sock->sendSequence = 0; + sock->unreliableSendSequence = 0; + sock->sendMessageLength = 0; + sock->receiveSequence = 0; + sock->unreliableReceiveSequence = 0; + sock->receiveMessageLength = 0; + + return sock; +} + + +void NET_FreeQSocket(qsocket_t *sock) +{ + qsocket_t *s; + + // remove it from active list + if (sock == net_activeSockets) + net_activeSockets = net_activeSockets->next; + else + { + for (s = net_activeSockets; s; s = s->next) + if (s->next == sock) + { + s->next = sock->next; + break; + } + if (!s) + Sys_Error ("NET_FreeQSocket: not active\n"); + } + + // add it to free list + sock->next = net_freeSockets; + net_freeSockets = sock; + sock->disconnected = true; +} + + +static void NET_Listen_f (void) +{ + if (Cmd_Argc () != 2) + { + Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0); + return; + } + + listening = Q_atoi(Cmd_Argv(1)) ? true : false; + + for (net_driverlevel=0 ; net_driverlevel 65534) + { + Con_Printf ("Bad value, must be between 1 and 65534\n"); + return; + } + + DEFAULTnet_hostport = n; + net_hostport = n; + + if (listening) + { + // force a change to the new port + Cbuf_AddText ("listen 0\n", RESTRICT_LOCAL); + Cbuf_AddText ("listen 1\n", RESTRICT_LOCAL); + } +} + + +static void PrintSlistHeader(void) +{ + Con_Printf("Server Map Users\n"); + Con_Printf("--------------- --------------- -----\n"); + slistLastShown = 0; +} + + +static void PrintSlist(void) +{ + int n; + + for (n = slistLastShown; n < hostCacheCount; n++) + { + if (hostcache[n].maxusers) + Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); + else + Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map); + } + slistLastShown = n; +} + + +static void PrintSlistTrailer(void) +{ + if (hostCacheCount) + Con_Printf("== end list ==\n\n"); + else + Con_Printf("No Quake servers found.\n\n"); +} + + +void NET_Slist_f (void) +{ + if (slistInProgress) + return; + + if (! slistSilent) + { + Con_Printf("Looking for Quake servers...\n"); + PrintSlistHeader(); + } + + slistInProgress = true; + slistStartTime = Sys_FloatTime(); + + SchedulePollProcedure(&slistSendProcedure, 0.0); + SchedulePollProcedure(&slistPollProcedure, 0.1); + + hostCacheCount = 0; +} + + +static void Slist_Send(void *arg) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + continue; + if (net_drivers[net_driverlevel].initialized == false) + continue; + dfunc.SearchForHosts (true); + } + + if ((Sys_FloatTime() - slistStartTime) < 0.5) + SchedulePollProcedure(&slistSendProcedure, 0.75); +} + + +static void Slist_Poll(void *arg) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + continue; + if (net_drivers[net_driverlevel].initialized == false) + continue; + dfunc.SearchForHosts (false); + } + + if (! slistSilent) + PrintSlist(); + + if ((Sys_FloatTime() - slistStartTime) < 1.5) + { + SchedulePollProcedure(&slistPollProcedure, 0.1); + return; + } + + if (! slistSilent) + PrintSlistTrailer(); + slistInProgress = false; + slistSilent = false; + slistLocal = true; +} + + +/* +=================== +NET_Connect +=================== +*/ + +int hostCacheCount = 0; +hostcache_t hostcache[HOSTCACHESIZE]; + +qsocket_t *NET_Connect (char *host, qboolean continuation) +{ + qsocket_t *ret; + int n; + int numdrivers = net_numdrivers; + + SetNetTime(); + + if (host && *host == 0) + host = NULL; + + if (host) + { + if (Q_strcasecmp (host, "local") == 0) + { + numdrivers = 1; + goto JustDoIt; + } + + if (continuation != 1) + { + slistSilent = host ? true : false; + NET_Slist_f (); + + while(slistInProgress) + NET_Poll(); + } + + if (hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + host = hostcache[n].cname; + break; + } + if (n < hostCacheCount) + goto JustDoIt; + } + } + + if (host == NULL) + { + if (hostCacheCount != 1) + return NULL; + host = hostcache[0].cname; + Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host); + } + + if (hostCacheCount) + for (n = 0; n < hostCacheCount; n++) + if (Q_strcasecmp (host, hostcache[n].name) == 0) + { + host = hostcache[n].cname; + break; + } + +JustDoIt: + for (net_driverlevel=0 ; net_driverleveladdress, NET_NAMELEN); + } + return ret; + } + } + + if (recording) + { + vcrConnect.time = sv.time; + vcrConnect.op = VCR_OP_CONNECT; + vcrConnect.session = 0; + Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect)); + } +#endif + return NULL; +} + +/* +=================== +NET_Close +=================== +*/ +void NET_Close (qsocket_t *sock) +{ + if (!sock) + return; + + if (sock->disconnected==true) + return; + + SetNetTime(); + + // call the driver_Close function + sfunc.Close (sock); + + NET_FreeQSocket(sock); +} + + +/* +================= +NET_GetMessage + +If there is a complete message, return it in net_message + +returns 0 if no data is waiting +returns 1 if a message was received +returns -1 if connection is invalid +================= +*/ + +struct +{ + double time; + int op; + long session; + int ret; + int len; +} vcrGetMessage; + +extern void PrintStats(qsocket_t *s); + +int NET_GetMessage (qsocket_t *sock) +{ + int ret; + + if (!sock) + return -1; + + if (sock->disconnected) + { + Con_Printf("NET_GetMessage: disconnected socket\n"); + return -1; + } + + SetNetTime(); + + ret = sfunc.QGetMessage(sock); + + // see if this connection has timed out + if (ret == 0 && sock->driver) + { + if (net_time - sock->lastMessageTime > net_messagetimeout.value) + { + NET_Close(sock); + return -1; + } + } + + + if (ret > 0) + { + if (sock->driver) + { + sock->lastMessageTime = net_time; + if (ret == 1) + messagesReceived++; + else if (ret == 2) + unreliableMessagesReceived++; + } + + if (recording) + { + vcrGetMessage.time = realtime; + vcrGetMessage.op = VCR_OP_GETMESSAGE; + vcrGetMessage.session = (long)sock; + vcrGetMessage.ret = ret; + vcrGetMessage.len = net_message.cursize; + Sys_FileWrite (vcrFile, &vcrGetMessage, 24); + Sys_FileWrite (vcrFile, net_message.data, net_message.cursize); + } + } + else + { + if (recording) + { + vcrGetMessage.time = realtime; + vcrGetMessage.op = VCR_OP_GETMESSAGE; + vcrGetMessage.session = (long)sock; + vcrGetMessage.ret = ret; + Sys_FileWrite (vcrFile, &vcrGetMessage, 20); + } + } + + *(int *)net_from.ip = 0; + net_from.port = 0; + return ret; +} + + +/* +================== +NET_SendMessage + +Try to send a complete length+message unit over the reliable stream. +returns 0 if the message cannot be delivered reliably, but the connection + is still considered valid +returns 1 if the message was sent properly +returns -1 if the connection died +================== +*/ +struct +{ + double time; + int op; + long session; + int r; +} vcrSendMessage; + +int NET_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + int r; + + if (!sock) + return -1; + + if (sock->disconnected) + { + Con_Printf("NET_SendMessage: disconnected socket\n"); + return -1; + } + + SetNetTime(); + r = sfunc.QSendMessage(sock, data); + if (r == 1 && sock->driver) + messagesSent++; + + if (recording) + { + vcrSendMessage.time = realtime; + vcrSendMessage.op = VCR_OP_SENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + return r; +} + + +int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) +{ + int r; + + if (!sock) + return -1; + + if (sock->disconnected) + { + Con_Printf("NET_SendMessage: disconnected socket\n"); + return -1; + } + + SetNetTime(); + r = sfunc.SendUnreliableMessage(sock, data); + if (r == 1 && sock->driver) + unreliableMessagesSent++; + + if (recording) + { + vcrSendMessage.time = realtime; + vcrSendMessage.op = VCR_OP_SENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + return r; +} + + +/* +================== +NET_CanSendMessage + +Returns true or false if the given qsocket can currently accept a +message to be transmitted. +================== +*/ +qboolean NET_CanSendMessage (qsocket_t *sock) +{ + int r; + + if (!sock) + return false; + + if (sock->disconnected) + return false; + + SetNetTime(); + + r = sfunc.CanSendMessage(sock); + + if (recording) + { + vcrSendMessage.time = realtime; + vcrSendMessage.op = VCR_OP_CANSENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + return r; +} + + +int NET_SendToAll(sizebuf_t *data, int blocktime) +{ +#ifndef CLIENTONLY + double start; + int i; + int count = 0; + qboolean state1 [MAX_SCOREBOARD]; + qboolean state2 [MAX_SCOREBOARD]; + + for (i=0, host_client = svs.clients ; i<32 ; i++, host_client++) + { + if (!host_client->netchan.qsocket) + continue; + if (host_client->state > cs_zombie) + { + if (host_client->netchan.qsocket->driver == 0) + { + NET_SendMessage(host_client->netchan.qsocket, data); + state1[i] = true; + state2[i] = true; + continue; + } + count++; + state1[i] = false; + state2[i] = false; + } + else + { + state1[i] = true; + state2[i] = true; + } + } + + start = Sys_FloatTime(); + while (count) + { + count = 0; + for (i=0, host_client = svs.clients ; i<32 ; i++, host_client++) + { + if (! state1[i]) + { + if (NET_CanSendMessage (host_client->netchan.qsocket)) + { + state1[i] = true; + NET_SendMessage(host_client->netchan.qsocket, data); + } + else + { + NET_GetMessage (host_client->netchan.qsocket); + } + count++; + continue; + } + + if (! state2[i]) + { + if (NET_CanSendMessage (host_client->netchan.qsocket)) + { + state2[i] = true; + } + else + { + NET_GetMessage (host_client->netchan.qsocket); + } + count++; + continue; + } + } + if ((Sys_FloatTime() - start) > blocktime) + break; + } + return count; +#else + return 0; +#endif +} + + +//============================================================================= + +/* +==================== +NET_Init +==================== +*/ + +void NQ_NET_Init (void) +{ + int i; + int controlSocket; + qsocket_t *s; + + if (COM_CheckParm("-playback")) + { + net_numdrivers = 1; + net_drivers[0].Init = VCR_Init; + } + + if (COM_CheckParm("-record")) + recording = true; + + i = COM_CheckParm ("-nqport"); + if (!i) + i = COM_CheckParm ("-udpport"); + if (!i) + i = COM_CheckParm ("-ipxport"); + + if (i) + { + if (i < com_argc-1) + DEFAULTnet_hostport = Q_atoi (com_argv[i+1]); + else + Sys_Error ("NET_Init: you must specify a number after -port"); + } + net_hostport = DEFAULTnet_hostport; + +#ifndef CLIENTONLY +#ifndef SERVERONLY + if (COM_CheckParm("-listen") || isDedicated) +#endif + listening = true; + + net_numsockets = 32; +#ifndef SERVERONLY + if (!isDedicated) + net_numsockets++; +#endif + +#else + net_numsockets = 1; +#endif + + SetNetTime(); + + for (i = 0; i < net_numsockets; i++) + { + s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket"); + s->next = net_freeSockets; + net_freeSockets = s; + s->disconnected = true; + } + + // allocate space for network message buffer + net_message.data = Z_Malloc(NET_MAXMESSAGE); + net_message.maxsize = NET_MAXMESSAGE; + + Cvar_Register (&net_messagetimeout, "Networking"); +// Cvar_RegisterVariable (&hostname); +#ifndef NOCOM + Cvar_RegisterVariable (&config_com_port); + Cvar_RegisterVariable (&config_com_irq); + Cvar_RegisterVariable (&config_com_baud); + Cvar_RegisterVariable (&config_com_modem); + Cvar_RegisterVariable (&config_modem_dialtype); + Cvar_RegisterVariable (&config_modem_clear); + Cvar_RegisterVariable (&config_modem_init); + Cvar_RegisterVariable (&config_modem_hangup); +#endif +#ifdef IDGODS + Cvar_RegisterVariable (&idgods); +#endif + + Cmd_AddCommand ("slist", NET_Slist_f); + Cmd_AddCommand ("listen", NET_Listen_f); + Cmd_AddCommand ("port", NET_Port_f); + + // initialize all the drivers + for (net_driverlevel=0 ; net_driverlevelnext) + NET_Close(sock); + +// +// shutdown the drivers +// + for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (net_drivers[net_driverlevel].initialized == true) + { + net_drivers[net_driverlevel].Shutdown (); + net_drivers[net_driverlevel].initialized = false; + } + } + + if (vcrFile != -1) + { + Con_Printf ("Closing vcrfile.\n"); + Sys_FileClose(vcrFile); + } +} + + +static PollProcedure *pollProcedureList = NULL; + +void NET_Poll(void) +{ + PollProcedure *pp; +#ifndef NOCOM + qboolean useModem; + + if (!configRestored) + { + if (serialAvailable) + { + if (config_com_modem.value == 1.0) + useModem = true; + else + useModem = false; + SetComPortConfig (0, (int)config_com_port.value, (int)config_com_irq.value, (int)config_com_baud.value, useModem); + SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string); + } + configRestored = true; + } +#endif + + SetNetTime(); + + for (pp = pollProcedureList; pp; pp = pp->next) + { + if (pp->nextTime > net_time) + break; + pollProcedureList = pp->next; + pp->procedure(pp->arg); + } +} + + +void SchedulePollProcedure(PollProcedure *proc, double timeOffset) +{ + PollProcedure *pp, *prev; + + proc->nextTime = Sys_FloatTime() + timeOffset; + for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) + { + if (pp->nextTime >= proc->nextTime) + break; + prev = pp; + } + + if (prev == NULL) + { + proc->next = pollProcedureList; + pollProcedureList = proc; + return; + } + + proc->next = pp; + prev->next = proc; +} + + +#ifdef IDGODS +#define IDNET 0xc0f62800 + +qboolean IsID(struct qsockaddr *addr) +{ + if (idgods.value == 0.0) + return false; + + if (addr->sa_family != 2) + return false; + + if ((BigLong(*(int *)&addr->sa_data[2]) & 0xffffff00) == IDNET) + return true; + return false; +} +#endif + + +#ifndef SERVERONLY +void NQ_BeginConnect(char *to) +{ +netadr_t addr; + + if (cls.netcon) + { + if (cls.netcon->qwprotocol) + { + sizebuf_t msg; + char data[64]; + *(int*)data = -1; + strcpy(data+4, "getchallenge"); + msg.cursize = strlen(data); + msg.data = data; + NET_SendMessage(cls.netcon, &msg); + } + return; + } + + cls.netcon = NET_Connect(to, 0); + if (!cls.netcon) + { + Con_Printf ("CL_Connect: connect failed\n"); + return; + } + if (cls.netcon->qwprotocol) + { + extern double connect_time; + cls.netchan.qsocket = cls.netcon; + connect_time=-1; //a get chalenge is emulated by server. + } + else + { + SockadrToNetadr(&cls.netcon->addr, &addr); + Netchan_Setup(cls.netcon->socket, &cls.netchan, addr, cls.qport); + cls.netchan.qsocket = cls.netcon; + Con_DPrintf ("CL_EstablishConnection: connected to %s\n", cls.servername); + + cls.netchan.qsocket = cls.netcon; + + cls.demonum = -1; // not in the demo loop now + cls.state = ca_connected; + } + +// MSG_WriteByte(&cls.netchan.message, clc_stringcmd); +// MSG_WriteString(&cls.netchan.message, "new"); +} +void NQ_ContinueConnect(char *to) +{ +netadr_t addr; + if (cls.netcon) + return; //already got through + + cls.netcon = NET_Connect(to, 1); + if (!cls.netcon) + { + return; + } + if (cls.netcon->qwprotocol) + { + extern double connect_time; + cls.netchan.qsocket = cls.netcon; + connect_time=-1; //a get chalenge is emulated by server. + } + else + { + SockadrToNetadr(&cls.netcon->addr, &addr); + Netchan_Setup(cls.netcon->socket, &cls.netchan, addr, cls.qport); + cls.netchan.qsocket = cls.netcon; + Con_DPrintf ("CL_EstablishConnection: connected to %s\n", cls.servername); + + cls.netchan.qsocket = cls.netcon; + + cls.demonum = -1; // not in the demo loop now + cls.state = ca_connected; + } +// MSG_WriteByte(&cls.netchan.message, clc_stringcmd); +// MSG_WriteString(&cls.netchan.message, "new"); +} +void NQ_AbortConnect(char *to) +{ +} +#endif + + +#endif diff --git a/engine/nqnet/net_ser.h b/engine/nqnet/net_ser.h new file mode 100644 index 000000000..5885ab970 --- /dev/null +++ b/engine/nqnet/net_ser.h @@ -0,0 +1,33 @@ +/* +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.h + +int Serial_Init (void); +void Serial_Listen (qboolean state); +void Serial_SearchForHosts (qboolean xmit); +qsocket_t *Serial_Connect (char *host); +qsocket_t *Serial_CheckNewConnections (void); +int Serial_GetMessage (qsocket_t *sock); +int Serial_SendMessage (qsocket_t *sock, sizebuf_t *data); +int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); +qboolean Serial_CanSendMessage (qsocket_t *sock); +qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock); +void Serial_Close (qsocket_t *sock); +void Serial_Shutdown (void); diff --git a/engine/nqnet/net_vcr.c b/engine/nqnet/net_vcr.c new file mode 100644 index 000000000..afbbc2d82 --- /dev/null +++ b/engine/nqnet/net_vcr.c @@ -0,0 +1,170 @@ +/* +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_vcr.c + +#include "../client/quakedef.h" +#ifdef NQPROT +#include "net_vcr.h" + +extern int vcrFile; + +// This is the playback portion of the VCR. It reads the file produced +// by the recorder and plays it back to the host. The recording contains +// everything necessary (events, timestamps, and data) to duplicate the game +// from the viewpoint of everything above the network layer. + +static struct +{ + double time; + int op; + long session; +} next; + +int VCR_Init (void) +{ + net_drivers[0].Init = VCR_Init; + + net_drivers[0].SearchForHosts = VCR_SearchForHosts; + net_drivers[0].Connect = VCR_Connect; + net_drivers[0].CheckNewConnections = VCR_CheckNewConnections; + net_drivers[0].QGetMessage = VCR_GetMessage; + net_drivers[0].QSendMessage = VCR_SendMessage; + net_drivers[0].CanSendMessage = VCR_CanSendMessage; + net_drivers[0].Close = VCR_Close; + net_drivers[0].Shutdown = VCR_Shutdown; + + Sys_FileRead(vcrFile, &next, sizeof(next)); + return 0; +} + +void VCR_ReadNext (void) +{ + if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0) + { + next.op = 255; + Sys_Error ("=== END OF PLAYBACK===\n"); + } + if (next.op < 1 || next.op > VCR_MAX_MESSAGE) + Sys_Error ("VCR_ReadNext: bad op"); +} + + +void VCR_Listen (qboolean state) +{ +} + + +void VCR_Shutdown (void) +{ +} + + +int VCR_GetMessage (qsocket_t *sock) +{ + int ret; + + if (realtime != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + if (ret != 1) + { + VCR_ReadNext (); + return ret; + } + + Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int)); + Sys_FileRead(vcrFile, net_message.data, net_message.cursize); + + VCR_ReadNext (); + + return 1; +} + + +int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + int ret; + + if (realtime != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + + VCR_ReadNext (); + + return ret; +} + + +qboolean VCR_CanSendMessage (qsocket_t *sock) +{ + qboolean ret; + + if (realtime != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + + VCR_ReadNext (); + + return ret; +} + + +void VCR_Close (qsocket_t *sock) +{ +} + + +void VCR_SearchForHosts (qboolean xmit) +{ +} + + +qsocket_t *VCR_Connect (char *host) +{ + return NULL; +} + + +qsocket_t *VCR_CheckNewConnections (void) +{ + qsocket_t *sock; + + if (realtime != next.time || next.op != VCR_OP_CONNECT) + Sys_Error ("VCR missmatch"); + + if (!next.session) + { + VCR_ReadNext (); + return NULL; + } + + sock = NET_NewQSocket (); + *(long *)(&sock->driverdata) = next.session; + + Sys_FileRead (vcrFile, sock->address, NET_NAMELEN); + VCR_ReadNext (); + + return sock; +} +#endif + diff --git a/engine/nqnet/net_vcr.h b/engine/nqnet/net_vcr.h new file mode 100644 index 000000000..95c2f34c3 --- /dev/null +++ b/engine/nqnet/net_vcr.h @@ -0,0 +1,37 @@ +/* +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_vcr.h + +#define VCR_OP_CONNECT 1 +#define VCR_OP_GETMESSAGE 2 +#define VCR_OP_SENDMESSAGE 3 +#define VCR_OP_CANSENDMESSAGE 4 +#define VCR_MAX_MESSAGE 4 + +int VCR_Init (void); +void VCR_Listen (qboolean state); +void VCR_SearchForHosts (qboolean xmit); +qsocket_t *VCR_Connect (char *host); +qsocket_t *VCR_CheckNewConnections (void); +int VCR_GetMessage (qsocket_t *sock); +int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data); +qboolean VCR_CanSendMessage (qsocket_t *sock); +void VCR_Close (qsocket_t *sock); +void VCR_Shutdown (void); diff --git a/engine/nqnet/net_win.c b/engine/nqnet/net_win.c new file mode 100644 index 000000000..a3e260954 --- /dev/null +++ b/engine/nqnet/net_win.c @@ -0,0 +1,765 @@ +/* +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. + +*/ +#include "../client/quakedef.h" +#ifdef NQPROT +#include "../client/winquake.h" + +#include "net_loop.h" +#include "net_dgrm.h" +#include "net_ser.h" + +#define Sys_FloatTime Sys_DoubleTime + +net_driver_t net_drivers[MAX_NET_DRIVERS] = +{ +#if !defined(SERVERONLY) && !defined(CLIENTONLY) + { + "Loopback", + false, + Loop_Init, + Loop_Listen, + Loop_SearchForHosts, + Loop_Connect, + Loop_CheckNewConnections, + Loop_GetMessage, + Loop_SendMessage, + Loop_SendUnreliableMessage, + Loop_CanSendMessage, + Loop_CanSendUnreliableMessage, + Loop_Close, + Loop_Shutdown + } + , +#endif + { + "Datagram", + false, + Datagram_Init, + Datagram_Listen, + Datagram_SearchForHosts, + Datagram_Connect, + Datagram_CheckNewConnections, + Datagram_GetMessage, + Datagram_SendMessage, + Datagram_SendUnreliableMessage, + Datagram_CanSendMessage, + Datagram_CanSendUnreliableMessage, + Datagram_Close, + Datagram_Shutdown, + + Datagram_BeginConnect, + Datagram_ContinueConnect + } +}; + +#if !defined(SERVERONLY) && !defined(CLIENTONLY) +int net_numdrivers = 2; +#else +int net_numdrivers = 1; +#endif + + +#include "net_wins.h" +#include "net_wipx.h" + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "Winsock TCPIP", + false, + 0, + WINS_Init, + WINS_Shutdown, + WINS_Listen, + WINS_OpenSocket, + WINS_CloseSocket, + WINS_Connect, + WINS_CheckNewConnections, + WINS_Read, + WINS_Write, + WINS_Broadcast, + WINS_AddrToString, + WINS_StringToAddr, + WINS_GetSocketAddr, + WINS_GetNameFromAddr, + WINS_GetAddrFromName, + WINS_AddrCompare, + WINS_GetSocketPort, + WINS_SetSocketPort + } +#ifdef _WIN32 +, + { + "Winsock IPX", + false, + 0, + WIPX_Init, + WIPX_Shutdown, + WIPX_Listen, + WIPX_OpenSocket, + WIPX_CloseSocket, + WIPX_Connect, + WIPX_CheckNewConnections, + WIPX_Read, + WIPX_Write, + WIPX_Broadcast, + WIPX_AddrToString, + WIPX_StringToAddr, + WIPX_GetSocketAddr, + WIPX_GetNameFromAddr, + WIPX_GetAddrFromName, + WIPX_AddrCompare, + WIPX_GetSocketPort, + WIPX_SetSocketPort + } +#endif +}; + +#ifdef _WIN32 +int net_numlandrivers = 2; +#else +int net_numlandrivers = 1; +#endif + + + + + + + + + + + + + + + + + + + + + + +#ifndef CLIENTONLY +extern cvar_t hostname; +#endif + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_broadcastsocket = 0; +static struct sockaddr_qstorage broadcastaddr; + +static unsigned long myAddr; + +#ifdef _WIN32 +qboolean winsock_lib_initialized; + +int (PASCAL *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); +int (PASCAL *pWSACleanup)(void); +int (PASCAL *pWSAGetLastError)(void); +SOCKET (PASCAL *psocket)(int af, int type, int protocol); +int (PASCAL *pioctlsocket)(SOCKET s, long cmd, u_long *argp); +int (PASCAL *psetsockopt)(SOCKET s, int level, int optname, + const char * optval, int optlen); +int (PASCAL *precvfrom)(SOCKET s, char * buf, int len, int flags, + struct sockaddr *from, int * fromlen); +int (PASCAL *psendto)(SOCKET s, const char * buf, int len, int flags, + const struct sockaddr *to, int tolen); +int (PASCAL *pclosesocket)(SOCKET s); +int (PASCAL *pgethostname)(char * name, int namelen); +struct hostent * (PASCAL *pgethostbyname)(const char * name); +struct hostent * (PASCAL *pgethostbyaddr)(const char * addr, + int len, int type); +int (PASCAL *pgetsockname)(SOCKET s, struct sockaddr *name, + int * namelen); + +int winsock_initialized = 0; +WSADATA winsockdata; +#define qerrno pWSAGetLastError() + +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNREFUSED WSAECONNREFUSED +#else +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SOCKET_ERROR -1 + +#define qerrno errno + +#define psocket socket +#define pgethostbyaddrpsocket gethostbyaddrpsocket +#define pgethostbyaddrpioctlsocket gethostbyaddrpioctlsocket +#define psetsockopt setsockopt +#define precvfrom recvfrom +#define psendto sendto +#define pclosesocket close +#define pgethostname gethostname +#define pgethostbyname gethostbyname +#define pgethostbyaddr gethostbyaddr +#define pgetsockname getsockname +#define pioctlsocket ioctl + +#endif + +#include "net_wins.h" + +//============================================================================= + +static double blocktime; +#ifdef _WIN32 +BOOL PASCAL BlockingHook(void) +{ + MSG msg; + BOOL ret; + + if ((Sys_FloatTime() - blocktime) > 2.0) + { + WSACancelBlockingCall(); + return FALSE; + } + + /* get the next message, if any */ + ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + + /* if we got one, process it */ + if (ret) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* TRUE if we got a message */ + return ret; +} +#endif + +void WINS_GetLocalAddress() +{ + struct hostent *local = NULL; + char buff[MAXHOSTNAMELEN]; + unsigned long addr; + + if (myAddr != INADDR_ANY) + return; + + if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) + return; + + blocktime = Sys_FloatTime(); +#ifdef _WIN32 + WSASetBlockingHook(BlockingHook); +#endif + local = pgethostbyname(buff); +#ifdef _WIN32 + WSAUnhookBlockingHook(); +#endif + if (local == NULL) + return; + + myAddr = *(int *)local->h_addr_list[0]; + + addr = ntohl(myAddr); + sprintf(my_tcpip_address, "%d.%d.%d.%d", (qbyte)((addr >> 24) & 0xff), (qbyte)((addr >> 16) & 0xff), (qbyte)((addr >> 8) & 0xff), (qbyte)(addr & 0xff)); +} + + +int WINS_Init (void) +{ + int i; + char buff[MAXHOSTNAMELEN]; + char *p; + +#ifdef _WIN32 + int r; + WORD wVersionRequested; + HINSTANCE hInst; + +// initialize the Winsock function vectors (we do this instead of statically linking +// so we can run on Win 3.1, where there isn't necessarily Winsock) + hInst = LoadLibrary("wsock32.dll"); + + if (hInst == NULL) + { + Con_Printf ("Failed to load winsock.dll\n"); + winsock_lib_initialized = false; + return -1; + } + + winsock_lib_initialized = true; + + pWSAStartup = (void *)GetProcAddress(hInst, "WSAStartup"); + pWSACleanup = (void *)GetProcAddress(hInst, "WSACleanup"); + pWSAGetLastError = (void *)GetProcAddress(hInst, "WSAGetLastError"); + psocket = (void *)GetProcAddress(hInst, "socket"); + pioctlsocket = (void *)GetProcAddress(hInst, "ioctlsocket"); + psetsockopt = (void *)GetProcAddress(hInst, "setsockopt"); + precvfrom = (void *)GetProcAddress(hInst, "recvfrom"); + psendto = (void *)GetProcAddress(hInst, "sendto"); + pclosesocket = (void *)GetProcAddress(hInst, "closesocket"); + pgethostname = (void *)GetProcAddress(hInst, "gethostname"); + pgethostbyname = (void *)GetProcAddress(hInst, "gethostbyname"); + pgethostbyaddr = (void *)GetProcAddress(hInst, "gethostbyaddr"); + pgetsockname = (void *)GetProcAddress(hInst, "getsockname"); + + if (!pWSAStartup || !pWSACleanup || !pWSAGetLastError || + !psocket || !pioctlsocket || !psetsockopt || + !precvfrom || !psendto || !pclosesocket || + !pgethostname || !pgethostbyname || !pgethostbyaddr || + !pgetsockname) + { + Con_Printf ("Couldn't GetProcAddress from winsock.dll\n"); + return -1; + } +#endif + + if (COM_CheckParm ("-noudp")) + return -1; + +#ifdef _WIN32 + if (winsock_initialized == 0) + { + wVersionRequested = MAKEWORD(1, 1); + + r = pWSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + { + Con_Printf ("Winsock initialization failed.\n"); + return -1; + } + } + winsock_initialized++; +#endif + + // determine my name + if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) + { +#ifdef _WIN32 + Con_DPrintf ("Winsock TCP/IP Initialization failed.\n"); + if (--winsock_initialized == 0) + pWSACleanup (); +#else + Con_DPrintf ("TCP/IP failed to get hostname.\n"); +#endif + return -1; + } +#ifndef CLIENTONLY + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } + Cvar_Set (&hostname, buff); + } +#endif + i = COM_CheckParm ("-nqip"); + if (i) + { + if (i < com_argc-1) + { + myAddr = inet_addr(com_argv[i+1]); + if (myAddr == INADDR_NONE) + Sys_Error ("%s is not a valid IP address", com_argv[i+1]); + strcpy(my_tcpip_address, com_argv[i+1]); + } + else + { + Sys_Error ("NET_Init: you must specify an IP address after -ip"); + } + } + else + { + myAddr = INADDR_ANY; + strcpy(my_tcpip_address, "INADDR_ANY"); + } + + if ((net_controlsocket = WINS_OpenSocket (0)) == -1) + { + Con_Printf("WINS_Init: Unable to open control socket\n"); +#ifdef _WIN32 + if (--winsock_initialized == 0) + pWSACleanup (); +#endif + return -1; + } + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((unsigned short)net_hostport); + + Con_Printf("Winsock TCP/IP Initialized\n"); + tcpipAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void WINS_Shutdown (void) +{ + WINS_Listen (false); + WINS_CloseSocket (net_controlsocket); +#ifdef _WIN32 + if (--winsock_initialized == 0) + pWSACleanup (); +#endif +} + +//============================================================================= + +void WINS_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + WINS_GetLocalAddress(); + if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) + Con_Printf ("WINS_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WINS_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int WINS_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + u_long _true = 1; + + if ((newsocket = psocket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (pioctlsocket (newsocket, FIONBIO, &_true) == -1) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = myAddr; + address.sin_port = htons((unsigned short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == 0) + return newsocket; + + Con_Printf ("Unable to bind to %s\n", WINS_AddrToString((struct sockaddr_qstorage *)&address)); +ErrorReturn: + pclosesocket (newsocket); + return -1; +} + +//============================================================================= + +int WINS_CloseSocket (int socket) +{ + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + return pclosesocket (socket); +} + + +//============================================================================= +/* +============ +PartialIPAddress + +this lets you type only as much of the net address as required, using +the local network components to fill in the rest +============ +*/ +static int PartialIPAddress (char *in, struct sockaddr_qstorage *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + int run; + int port; + + buff[0] = '.'; + b = buff; + strcpy(buff+1, in); + if (buff[1] == '.') + b++; + + addr = 0; + mask=-1; + while (*b == '.') + { + b++; + num = 0; + run = 0; + while (!( *b < '0' || *b > '9')) + { + num = num*10 + *b++ - '0'; + if (++run > 3) + return -1; + } + if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) + return -1; + if (num < 0 || num > 255) + return -1; + mask<<=8; + addr = (addr<<8) + num; + } + + if (*b++ == ':') + port = Q_atoi(b); + else + port = net_hostport; + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} +//============================================================================= + +int WINS_Connect (int socket, struct sockaddr_qstorage *addr) +{ + return 0; +} + +//============================================================================= + +int WINS_CheckNewConnections (void) +{ + char buf[4096]; + + if (net_acceptsocket == -1) + return -1; + + if (precvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) > 0) + { + return net_acceptsocket; + } + return -1; +} + +//============================================================================= + +int WINS_Read (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr) +{ + int addrlen = sizeof (struct sockaddr_qstorage); + int ret; + + ret = precvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + if (qerrno == EWOULDBLOCK || qerrno == ECONNREFUSED) + return 0; + + } + return ret; +} + +//============================================================================= + +int WINS_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (psetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} + +//============================================================================= + +int WINS_Broadcast (int socket, qbyte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + WINS_GetLocalAddress(); + ret = WINS_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return WINS_Write (socket, buf, len, &broadcastaddr); +} + +//============================================================================= + +int WINS_Write (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr) +{ + int ret; + + ret = psendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_qstorage)); + if (ret == -1) + if (qerrno == EWOULDBLOCK) + return 0; + + return ret; +} + +//============================================================================= + +char *WINS_AddrToString (struct sockaddr_qstorage *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); + return buffer; +} + +//============================================================================= + +int WINS_StringToAddr (char *string, struct sockaddr_qstorage *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); + return 0; +} + +//============================================================================= + +int WINS_GetSocketAddr (int socket, struct sockaddr_qstorage *addr) +{ + int addrlen = sizeof(struct sockaddr_qstorage); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct sockaddr_qstorage)); + pgetsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; + + return 0; +} + +//============================================================================= + +int WINS_GetNameFromAddr (struct sockaddr_qstorage *addr, char *name) +{ + struct hostent *hostentry; + + hostentry = pgethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); + if (hostentry) + { + Q_strncpyz (name, (char *)hostentry->h_name, NET_NAMELEN); + return 0; + } + + Q_strcpy (name, WINS_AddrToString (addr)); + return 0; +} + +//============================================================================= + +int WINS_GetAddrFromName(char *name, struct sockaddr_qstorage *addr) +{ + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = pgethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} + +//============================================================================= + +int WINS_AddrCompare (struct sockaddr_qstorage *addr1, struct sockaddr_qstorage *addr2) +{ + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) + return 1; + + return 0; +} + +//============================================================================= + +int WINS_GetSocketPort (struct sockaddr_qstorage *addr) +{ + return ntohs(((struct sockaddr_in *)addr)->sin_port); +} + + +int WINS_SetSocketPort (struct sockaddr_qstorage *addr, int port) +{ + ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); + return 0; +} + +//============================================================================= +#endif diff --git a/engine/nqnet/net_wins.h b/engine/nqnet/net_wins.h new file mode 100644 index 000000000..604fd35e3 --- /dev/null +++ b/engine/nqnet/net_wins.h @@ -0,0 +1,39 @@ +/* +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_wins.h + +int WINS_Init (void); +void WINS_Shutdown (void); +void WINS_Listen (qboolean state); +int WINS_OpenSocket (int port); +int WINS_CloseSocket (int socket); +int WINS_Connect (int socket, struct sockaddr_qstorage *addr); +int WINS_CheckNewConnections (void); +int WINS_Read (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); +int WINS_Write (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); +int WINS_Broadcast (int socket, qbyte *buf, int len); +char *WINS_AddrToString (struct sockaddr_qstorage *addr); +int WINS_StringToAddr (char *string, struct sockaddr_qstorage *addr); +int WINS_GetSocketAddr (int socket, struct sockaddr_qstorage *addr); +int WINS_GetNameFromAddr (struct sockaddr_qstorage *addr, char *name); +int WINS_GetAddrFromName (char *name, struct sockaddr_qstorage *addr); +int WINS_AddrCompare (struct sockaddr_qstorage *addr1, struct sockaddr_qstorage *addr2); +int WINS_GetSocketPort (struct sockaddr_qstorage *addr); +int WINS_SetSocketPort (struct sockaddr_qstorage *addr, int port); diff --git a/engine/nqnet/net_wipx.c b/engine/nqnet/net_wipx.c new file mode 100644 index 000000000..58f37d848 --- /dev/null +++ b/engine/nqnet/net_wipx.c @@ -0,0 +1,441 @@ +/* +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_wipx.c + +#include "quakedef.h" +#ifdef NQPROT +#include "../client/winquake.h" +#include +#include "net_wipx.h" + +#ifndef CLIENTONLY +extern cvar_t hostname; +#endif + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static struct sockaddr_qstorage broadcastaddr; + +extern qboolean winsock_initialized; +extern WSADATA winsockdata; + +#define IPXSOCKETS 18 +static int ipxsocket[IPXSOCKETS]; +static int sequence[IPXSOCKETS]; + +#ifdef _WIN32 +#define qerrno pWSAGetLastError() +#else +#define qerrno errno +#endif + +//============================================================================= + +int WIPX_Init (void) +{ + int i; + char buff[MAXHOSTNAMELEN]; + struct sockaddr_qstorage addr; + char *p; + int r; + WORD wVersionRequested; + + if (COM_CheckParm ("-noipx")) + return -1; + +// make sure LoadLibrary has happened successfully + if (!winsock_lib_initialized) + return -1; + + if (winsock_initialized == 0) + { + wVersionRequested = MAKEWORD(1, 1); + + r = pWSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + { + Con_Printf ("Winsock initialization failed.\n"); + return -1; + } + } + winsock_initialized++; + + for (i = 0; i < IPXSOCKETS; i++) + ipxsocket[i] = 0; + +#ifndef CLIENTONLY + // determine my name & address + if (pgethostname(buff, MAXHOSTNAMELEN) == 0) + { + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } + Cvar_Set (&hostname, buff); + } + } +#endif + + if ((net_controlsocket = WIPX_OpenSocket (0)) == -1) + { + Con_Printf("WIPX_Init: Unable to open control socket\n"); + if (--winsock_initialized == 0) + pWSACleanup (); + return -1; + } + + ((struct sockaddr_ipx *)&broadcastaddr)->sa_family = AF_IPX; + memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_netnum, 0, 4); + memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_nodenum, 0xff, 6); + ((struct sockaddr_ipx *)&broadcastaddr)->sa_socket = htons((unsigned short)net_hostport); + + WIPX_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr)); + p = Q_strrchr (my_ipx_address, ':'); + if (p) + *p = 0; + + Con_Printf("Winsock IPX Initialized\n"); + ipxAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void WIPX_Shutdown (void) +{ + WIPX_Listen (false); + WIPX_CloseSocket (net_controlsocket); + if (--winsock_initialized == 0) + pWSACleanup (); +} + +//============================================================================= + +void WIPX_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == -1) + Con_Printf ("WIPX_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WIPX_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int WIPX_OpenSocket (int port) +{ + int handle; + int newsocket; + struct sockaddr_ipx address; + u_long _true = 1; + + for (handle = 0; handle < IPXSOCKETS; handle++) + if (ipxsocket[handle] == 0) + break; + if (handle == IPXSOCKETS) + return -1; + + if ((newsocket = psocket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET) + return -1; + + if (pioctlsocket (newsocket, FIONBIO, &_true) == -1) + goto ErrorReturn; + + if (psetsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) < 0) + goto ErrorReturn; + + address.sa_family = AF_IPX; + memset(address.sa_netnum, 0, 4); + memset(address.sa_nodenum, 0, 6);; + address.sa_socket = htons((unsigned short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == 0) + { + ipxsocket[handle] = newsocket; + sequence[handle] = 0; + return handle; + } + + Con_Printf ("Winsock IPX bind failed\n"); +ErrorReturn: + pclosesocket (newsocket); + return -1; +} + +//============================================================================= + +int WIPX_CloseSocket (int handle) +{ + int socket = ipxsocket[handle]; + int ret; + + ret = pclosesocket (socket); + ipxsocket[handle] = 0; + return ret; +} + + +//============================================================================= + +int WIPX_Connect (int handle, struct sockaddr_qstorage *addr) +{ + return 0; +} + +//============================================================================= + +int WIPX_CheckNewConnections (void) +{ + unsigned long available; + + if (net_acceptsocket == -1) + return -1; + + if (pioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == -1) + Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed\n"); + if (available) + return net_acceptsocket; + return -1; +} + +//============================================================================= + +static qbyte packetBuffer[NET_DATAGRAMSIZE + 4]; + +int WIPX_Read (int handle, qbyte *buf, int len, struct sockaddr_qstorage *addr) +{ + int addrlen = sizeof (struct sockaddr_qstorage); + int socket = ipxsocket[handle]; + int ret; + + ret = precvfrom (socket, packetBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + if (qerrno == WSAEWOULDBLOCK || qerrno == WSAECONNREFUSED) + return 0; + + } + + if (ret < 4) + return 0; + + // remove sequence number, it's only needed for DOS IPX + ret -= 4; + memcpy(buf, packetBuffer+4, ret); + + return ret; +} + +//============================================================================= + +int WIPX_Broadcast (int handle, qbyte *buf, int len) +{ + return WIPX_Write (handle, buf, len, &broadcastaddr); +} + +//============================================================================= + +int WIPX_Write (int handle, qbyte *buf, int len, struct sockaddr_qstorage *addr) +{ + int socket = ipxsocket[handle]; + int ret; + + // build packet with sequence number + *(int *)(&packetBuffer[0]) = sequence[handle]; + sequence[handle]++; + memcpy(&packetBuffer[4], buf, len); + len += 4; + + ret = psendto (socket, packetBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_qstorage)); + if (ret == -1) + if (pWSAGetLastError() == WSAEWOULDBLOCK) + return 0; + + return ret; +} + +//============================================================================= + +char *WIPX_AddrToString (struct sockaddr_qstorage *addr) +{ + static char buf[28]; + + sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u", + ((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff, + ((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff, + ntohs(((struct sockaddr_ipx *)addr)->sa_socket) + ); + return buf; +} + +//============================================================================= + +int WIPX_StringToAddr (char *string, struct sockaddr_qstorage *addr) +{ + int val; + char buf[3]; + + buf[2] = 0; + Q_memset(addr, 0, sizeof(struct sockaddr_qstorage)); + addr->sa_family = AF_IPX; + +#define DO(src,dest) \ + buf[0] = string[src]; \ + buf[1] = string[src + 1]; \ + if (sscanf (buf, "%x", &val) != 1) \ + return -1; \ + ((struct sockaddr_ipx *)addr)->dest = val + + DO(0, sa_netnum[0]); + DO(2, sa_netnum[1]); + DO(4, sa_netnum[2]); + DO(6, sa_netnum[3]); + DO(9, sa_nodenum[0]); + DO(11, sa_nodenum[1]); + DO(13, sa_nodenum[2]); + DO(15, sa_nodenum[3]); + DO(17, sa_nodenum[4]); + DO(19, sa_nodenum[5]); +#undef DO + + sscanf (&string[22], "%u", &val); + ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val); + + return 0; +} + +//============================================================================= + +int WIPX_GetSocketAddr (int handle, struct sockaddr_qstorage *addr) +{ + int socket = ipxsocket[handle]; + int addrlen = sizeof(struct sockaddr_qstorage); + + Q_memset(addr, 0, sizeof(struct sockaddr_qstorage)); + if(pgetsockname(socket, (struct sockaddr *)addr, &addrlen) != 0) + { + } + + return 0; +} + +//============================================================================= + +int WIPX_GetNameFromAddr (struct sockaddr_qstorage *addr, char *name) +{ + Q_strcpy(name, WIPX_AddrToString(addr)); + return 0; +} + +//============================================================================= + +int WIPX_GetAddrFromName(char *name, struct sockaddr_qstorage *addr) +{ + int n; + char buf[32]; + + n = Q_strlen(name); + + if (n == 12) + { + sprintf(buf, "00000000:%s:%u", name, net_hostport); + return WIPX_StringToAddr (buf, addr); + } + if (n == 21) + { + sprintf(buf, "%s:%u", name, net_hostport); + return WIPX_StringToAddr (buf, addr); + } + if (n > 21 && n <= 27) + return WIPX_StringToAddr (name, addr); + + return -1; +} + +//============================================================================= + +int WIPX_AddrCompare (struct sockaddr_qstorage *addr1, struct sockaddr_qstorage *addr2) +{ + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (*((struct sockaddr_ipx *)addr1)->sa_netnum && *((struct sockaddr_ipx *)addr2)->sa_netnum) + if (memcmp(((struct sockaddr_ipx *)addr1)->sa_netnum, ((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0) + return -1; + if (memcmp(((struct sockaddr_ipx *)addr1)->sa_nodenum, ((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0) + return -1; + + if (((struct sockaddr_ipx *)addr1)->sa_socket != ((struct sockaddr_ipx *)addr2)->sa_socket) + return 1; + + return 0; +} + +//============================================================================= + +int WIPX_GetSocketPort (struct sockaddr_qstorage *addr) +{ + return ntohs(((struct sockaddr_ipx *)addr)->sa_socket); +} + + +int WIPX_SetSocketPort (struct sockaddr_qstorage *addr, int port) +{ + ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port); + return 0; +} + +//============================================================================= +#endif + diff --git a/engine/nqnet/net_wipx.h b/engine/nqnet/net_wipx.h new file mode 100644 index 000000000..e3cc1d40e --- /dev/null +++ b/engine/nqnet/net_wipx.h @@ -0,0 +1,39 @@ +/* +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_wipx.h + +int WIPX_Init (void); +void WIPX_Shutdown (void); +void WIPX_Listen (qboolean state); +int WIPX_OpenSocket (int port); +int WIPX_CloseSocket (int socket); +int WIPX_Connect (int socket, struct sockaddr_qstorage *addr); +int WIPX_CheckNewConnections (void); +int WIPX_Read (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); +int WIPX_Write (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); +int WIPX_Broadcast (int socket, qbyte *buf, int len); +char *WIPX_AddrToString (struct sockaddr_qstorage *addr); +int WIPX_StringToAddr (char *string, struct sockaddr_qstorage *addr); +int WIPX_GetSocketAddr (int socket, struct sockaddr_qstorage *addr); +int WIPX_GetNameFromAddr (struct sockaddr_qstorage *addr, char *name); +int WIPX_GetAddrFromName (char *name, struct sockaddr_qstorage *addr); +int WIPX_AddrCompare (struct sockaddr_qstorage *addr1, struct sockaddr_qstorage *addr2); +int WIPX_GetSocketPort (struct sockaddr_qstorage *addr); +int WIPX_SetSocketPort (struct sockaddr_qstorage *addr, int port); diff --git a/engine/nqnet/nqnet.h b/engine/nqnet/nqnet.h new file mode 100644 index 000000000..442b2c638 --- /dev/null +++ b/engine/nqnet/nqnet.h @@ -0,0 +1,343 @@ +/* +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.h -- quake's interface to the networking layer + +#define NET_NAMELEN 64 + +#define NET_MAXMESSAGE 8192 +#define NET_HEADERSIZE (2 * sizeof(unsigned int)) +#define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE) + +// NetHeader flags +#define NETFLAG_LENGTH_MASK 0x0000ffff +#define NETFLAG_DATA 0x00010000 +#define NETFLAG_ACK 0x00020000 +#define NETFLAG_NAK 0x00040000 +#define NETFLAG_EOM 0x00080000 +#define NETFLAG_UNRELIABLE 0x00100000 +#define NETFLAG_CTL 0x80000000 + + +#define NET_PROTOCOL_VERSION 3 + +// This is the network info/connection protocol. It is used to find Quake +// servers, get info about them, and connect to them. Once connected, the +// Quake game protocol (documented elsewhere) is used. +// +// +// General notes: +// game_name is currently always "QUAKE", but is there so this same protocol +// can be used for future games as well; can you say Quake2? +// +// CCREQ_CONNECT +// string game_name "QUAKE" +// qbyte net_protocol_version NET_PROTOCOL_VERSION +// +// CCREQ_SERVER_INFO +// string game_name "QUAKE" +// qbyte net_protocol_version NET_PROTOCOL_VERSION +// +// CCREQ_PLAYER_INFO +// qbyte player_number +// +// CCREQ_RULE_INFO +// string rule +// +// +// +// CCREP_ACCEPT +// long port +// +// CCREP_REJECT +// string reason +// +// CCREP_SERVER_INFO +// string server_address +// string host_name +// string level_name +// qbyte current_players +// qbyte max_players +// qbyte protocol_version NET_PROTOCOL_VERSION +// +// CCREP_PLAYER_INFO +// qbyte player_number +// string name +// long colors +// long frags +// long connect_time +// string address +// +// CCREP_RULE_INFO +// string rule +// string value + +// note: +// There are two address forms used above. The short form is just a +// port number. The address that goes along with the port is defined as +// "whatever address you receive this reponse from". This lets us use +// the host OS to solve the problem of multiple host addresses (possibly +// with no routing between them); the host will use the right address +// when we reply to the inbound connection request. The long from is +// a full address and port in a string. It is used for returning the +// address of a server that is not running locally. + +#define CCREQ_CONNECT 0x01 +#define CCREQ_SERVER_INFO 0x02 +#define CCREQ_PLAYER_INFO 0x03 +#define CCREQ_RULE_INFO 0x04 + +#define CCREP_ACCEPT 0x81 +#define CCREP_REJECT 0x82 +#define CCREP_SERVER_INFO 0x83 +#define CCREP_PLAYER_INFO 0x84 +#define CCREP_RULE_INFO 0x85 + +typedef struct qsocket_s +{ + struct qsocket_s *next; + double connecttime; + double lastMessageTime; + double lastSendTime; + + qboolean disconnected; + qboolean canSend; + qboolean sendNext; + + int driver; + int landriver; + int socket; + void *driverdata; + + unsigned int ackSequence; + unsigned int sendSequence; + unsigned int unreliableSendSequence; + int sendMessageLength; + qbyte sendMessage [NET_MAXMESSAGE]; + + unsigned int receiveSequence; + unsigned int unreliableReceiveSequence; + int receiveMessageLength; + qbyte receiveMessage [NET_MAXMESSAGE]; + + struct sockaddr_qstorage addr; + char address[NET_NAMELEN]; + + int qwprotocol; + +} qsocket_t; + +extern qsocket_t *net_activeSockets; +extern qsocket_t *net_freeSockets; +extern int net_numsockets; + +typedef struct +{ + char *name; + qboolean initialized; + int controlSock; + int (*Init) (void); + void (*Shutdown) (void); + void (*Listen) (qboolean state); + int (*OpenSocket) (int port); + int (*CloseSocket) (int socket); + int (*Connect) (int socket, struct sockaddr_qstorage *addr); + int (*CheckNewConnections) (void); + int (*Read) (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); + int (*Write) (int socket, qbyte *buf, int len, struct sockaddr_qstorage *addr); + int (*Broadcast) (int socket, qbyte *buf, int len); + char * (*AddrToString) (struct sockaddr_qstorage *addr); + int (*StringToAddr) (char *string, struct sockaddr_qstorage *addr); + int (*GetSocketAddr) (int socket, struct sockaddr_qstorage *addr); + int (*GetNameFromAddr) (struct sockaddr_qstorage *addr, char *name); + int (*GetAddrFromName) (char *name, struct sockaddr_qstorage *addr); + int (*AddrCompare) (struct sockaddr_qstorage *addr1, struct sockaddr_qstorage *addr2); + int (*GetSocketPort) (struct sockaddr_qstorage *addr); + int (*SetSocketPort) (struct sockaddr_qstorage *addr, int port); +} net_landriver_t; + +#define MAX_NET_DRIVERS 8 +extern int net_numlandrivers; +extern net_landriver_t net_landrivers[MAX_NET_DRIVERS]; + +typedef struct +{ + //basic setup + char *name; + qboolean initialized; + int (*Init) (void); + void (*Listen) (qboolean state); + void (*SearchForHosts) (qboolean xmit); + qsocket_t *(*Connect) (char *host); + qsocket_t *(*CheckNewConnections) (void); + int (*QGetMessage) (qsocket_t *sock); + int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data); + int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data); + qboolean (*CanSendMessage) (qsocket_t *sock); + qboolean (*CanSendUnreliableMessage) (qsocket_t *sock); + void (*Close) (qsocket_t *sock); + void (*Shutdown) (void); + + //extra setup + qsocket_t *(*BeginConnect) (char *host); + qsocket_t *(*ContinueConnect) (char *host); + qsocket_t *(*FailConnect) (char *host); + + //extra info + int controlSock; +} net_driver_t; + +extern int net_numdrivers; +extern net_driver_t net_drivers[MAX_NET_DRIVERS]; + +extern int DEFAULTnet_hostport; +extern int net_hostport; + +extern int net_driverlevel; +extern cvar_t hostname; +extern char playername[]; +extern int playercolor; + +extern int messagesSent; +extern int messagesReceived; +extern int unreliableMessagesSent; +extern int unreliableMessagesReceived; + +qsocket_t *NET_NewQSocket (void); +void NET_FreeQSocket(qsocket_t *); +double SetNetTime(void); + + +#define HOSTCACHESIZE 8 + +typedef struct +{ + char name[16]; + char map[16]; + char cname[32]; + int users; + int maxusers; + int driver; + int ldriver; + struct sockaddr_qstorage addr; + + qboolean isqwprotocol; +} hostcache_t; + +extern int hostCacheCount; +extern hostcache_t hostcache[HOSTCACHESIZE]; + +#if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__) +#ifndef htonl +extern unsigned long htonl (unsigned long hostlong); +#endif +#ifndef htons +extern unsigned short htons (unsigned short hostshort); +#endif +#ifndef ntohl +extern unsigned long ntohl (unsigned long netlong); +#endif +#ifndef ntohs +extern unsigned short ntohs (unsigned short netshort); +#endif +#endif + +#ifdef IDGODS +qboolean IsID(struct qsockaddr *addr); +#endif + +//============================================================================ +// +// public network functions +// +//============================================================================ + +extern double net_time; +extern sizebuf_t net_message; +extern int net_activeconnections; + +void NQ_NET_Init (void); +void NQ_NET_Shutdown (void); + +struct qsocket_s *NET_CheckNewConnections (void); +// returns a new connection number if there is one pending, else -1 + +void NQ_Connect(char *to); +struct qsocket_s *NET_Connect (char *host, qboolean continuation); +// called by client to connect to a host. Returns -1 if not able to + +qboolean NET_CanSendMessage (qsocket_t *sock); +// Returns true or false if the given qsocket can currently accept a +// message to be transmitted. + +int NET_GetMessage (struct qsocket_s *sock); +// returns data in net_message sizebuf +// returns 0 if no data is waiting +// returns 1 if a message was received +// returns 2 if an unreliable message was received +// returns -1 if the connection died + +int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data); +int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data); +// returns 0 if the message connot be delivered reliably, but the connection +// is still considered valid +// returns 1 if the message was sent properly +// returns -1 if the connection died + +int NET_SendToAll(sizebuf_t *data, int blocktime); +// This is a reliable *blocking* send to all attached clients. + + +void NET_Close (struct qsocket_s *sock); +// if a dead connection is returned by a get or send function, this function +// should be called when it is convenient + +// Server calls when a client is kicked off for a game related misbehavior +// like an illegal protocal conversation. Client calls when disconnecting +// from a server. +// A netcon_t number will not be reused until this function is called for it + +void NET_Poll(void); + + +typedef struct _PollProcedure +{ + struct _PollProcedure *next; + double nextTime; + void (*procedure)(void *arg); + void *arg; +} PollProcedure; + +void SchedulePollProcedure(PollProcedure *pp, double timeOffset); + +extern qboolean serialAvailable; +extern qboolean ipxAvailable; +extern qboolean tcpipAvailable; +extern char my_ipx_address[NET_NAMELEN]; +extern char my_tcpip_address[NET_NAMELEN]; +extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); + +extern qboolean slistInProgress; +extern qboolean slistSilent; +extern qboolean slistLocal; + +void NET_Slist_f (void);