NETWORK.C

/*********************************************************************** 
File: Network.c

Abstract:

This is the network communications module for MazeLords. It contains functions
to implement mailslots, as well as the various protocols and so-on required
for communications.


Contents:

MemCRC() -- Generates a 'unique' LONG for different data structures
SendNetMessage() -- Transmit a pre-made structure to other players
MailWatchThread() -- Thread process to watch for new mail
InitNetwork() -- Create mailslot, Send out ID message to network.
ProcessNewMail() -- Process newly recieved data packets


************************************************************************/

#include <windows.h>
#include <mmsystem.h>
#include "winmaze.h"
#include "mazproto.h"
#include "net.h"
#include "crctable.h"



/*=====================================================================
Function: MemCRC()

Inputs: Buffer & buffer length to CRC

Outputs: Returns single Long corresponding to CRC for structure

Abstract:
The CRC should provide a unique ID for each player, provided ANY
information in their data structures is unique. Each player is
responsible for generating their ID & transmitting it to the other
players.
======================================================================*/

unsigned long MemCRC(
LPSTR buffer,
int bytes
)
{
unsigned long crc = 0xFFFFFFFF;
LPSTR p;

for ( p = buffer; bytes > 0; bytes-- ) {
crc = ( crc >> 8 ) ^ CRCtable[(unsigned char) ((unsigned char) crc ^ *p++ ) ];
}

return ( crc ^ 0xFFFFFFFF );
}


/*=====================================================================
Function: SendNetMessage()

Inputs: Source, destination, structure to send, packet type

Outputs: Returns success/failure

Abstract:
SendNetMessage will send a message to all mazelords mailslots in the
current domain. Note that this message is broadcast once. It is up
to the individual recipients to decide whether it is addressed to them,
or a general message (sent to -1).
======================================================================*/

BOOL SendNetMessage(
unsigned long ulSender,
unsigned long ulDest,
LPVOID lpStruct,
DWORD dwPacketType
)
{
BOOL bResult=TRUE;
HANDLE hFile;
DWORD dwScratch,dwLength;
char cBuff[MAX_PACKET_SIZE];
LPSTR lpStr;
NetPacketType FAR *lpN;
PlayerInfoType FAR *lppiInfo,FAR *lppiInfo2;
PositionType FAR *lpptPos,FAR *lpptPos2;
int FAR *iScore;
IntoneType FAR *itIntone;

if (!bNetworked) {
return(TRUE);
}

lpN = (NetPacketType FAR *) cBuff;
lpN->ulSender = (ulSender)? ulSender : ptSelf.ulID;
lpN->ulDest = ulDest;
lpN->dwPacketType = dwPacketType;
dwLength = sizeof(NetPacketType);

//
// determine size, type of packet and fill out info accordingly.
//
lpStr = (LPSTR) &lpN->cData;

switch(dwPacketType) {

case NP_NEWPLAYER:
lppiInfo = (PlayerInfoType FAR *) lpStr;
lppiInfo2 = (PlayerInfoType FAR *) lpStruct;
*lppiInfo = *lppiInfo2;
dwLength += sizeof(PlayerInfoType);
break;

case NP_REQUESTIDENTITY:
case NP_LEAVINGGAME:
case NP_HITCONFIRM:
break;

case NP_MOVETO:
case NP_SHOTFIRED:
lpptPos = (PositionType FAR *) lpStr;
lpptPos2 = (PositionType FAR *) lpStruct;
*lpptPos = *lpptPos2;
dwLength += sizeof(PositionType);
break;

case NP_SCORE:
iScore = (int FAR *) lpStr;
*iScore = ptSelf.iScore;
dwLength += sizeof(int);
break;

case NP_INTONE:
itIntone = (IntoneType FAR *) lpStr;
*itIntone = *((IntoneType FAR *) lpStruct);
dwLength += sizeof(IntoneType);
dwLength -= (131 - itIntone->dwLen);
break;

default:
MessageBox((HWND)NULL,GetStringRes(IDS_BADPCKTXMITFAIL),"SendNetMessage",
MB_ICONEXCLAMATION|MB_APPLMODAL);
break;
}

#ifdef WIN32
hFile = CreateFile("\\\\*\\mailslot\\mazemslt",
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);

bResult = (hFile != INVALID_HANDLE_VALUE);

bResult = bResult &&
WriteFile(hFile,cBuff,dwLength+1,&dwScratch,NULL);

return(bResult && CloseHandle(hFile));
#else // else it's any operating system except WIN32
return(TRUE);
#endif
}



/*=====================================================================
Function: MailWatchThread()

Inputs: Handle to mailslot to watch

Outputs: none

Abstract:
This is a thread which is spawned. As soon as it sees a mail message
is queued up, it sends a message to the main window notifying Maze
Lords that new mail has been received. This is done 5 times a second.
======================================================================*/

void MailWatchThread(
HANDLE hMailSlot
)
{
DWORD cbMaxMsg,cbNextMsg,cMsg,dwReadTimeout;
#ifdef WIN32
while(TRUE) {

Sleep(200);
if (GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout)) {
if (cMsg > 0) {
PostMessage(hWndMain,WM_COMMAND,IDT_NEWMAIL,0);
}
}
}
#endif

//
// We shouldn't ever get here.
//
return;
}



/*=====================================================================
Function: InitNetwork()

Inputs: none

Outputs: returns success/failure

Abstract:
InitNetwork takes care of creating a mailslot for this user, and
sending out a packet saying that we're joining the game.
FALSE indicates that there was a fatal error starting the network.
======================================================================*/

BOOL InitNetwork(
void
)
{
DWORD dwScratch;
BOOL bRet;
PlayerInfoType ptPlayScr;

bRet = TRUE;

//
// initialize player list
//
ptPlayers.next = (PlayerType FAR *)NULL;

if (!bNetworked) {
hMailSlot = NULL;
return(TRUE);
}

#ifdef WIN32
hMailSlot = CreateMailslot("\\\\.\\mailslot\\mazemslt",0,0,NULL);
if (hMailSlot == INVALID_HANDLE_VALUE) {
MessageBox(NULL,GetStringRes(IDS_CREMSLOTFAIL),
GetStringRes2(IDS_NETINIT), MB_ICONEXCLAMATION|MB_APPLMODAL);
bRet = FALSE;
}

if (! CreateThread((LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE)MailWatchThread,
(LPVOID)hMailSlot,
0,&dwScratch)) {
MessageBox(NULL,GetStringRes(IDS_CREMWATCHTHRFAIL),
GetStringRes2(IDS_NETINIT), MB_ICONEXCLAMATION|MB_APPLMODAL);
bRet = FALSE;
}
#endif
ptPlayScr.ulID = ptSelf.ulID;
ptPlayScr.dwPID = ptSelf.dwPID;
lstrcpy(ptPlayScr.cUserName,ptSelf.cUserName);
lstrcpy(ptPlayScr.cComputerName,ptSelf.cComputerName);
ptPlayScr.ix = ptSelf.Pos.ix;
ptPlayScr.iy = ptSelf.Pos.iy;
ptPlayScr.bFacing = ptSelf.Pos.Facing;
ptPlayScr.iScore = ptSelf.iScore;
ptPlayScr.iPicNum = ptSelf.iPicNum;
ptPlayScr.iGridNum = ptSelf.iGridNum;
ptPlayScr.pGridLoc = ptSelf.pGridLoc;

return(bRet&&SendNetMessage(0,0,(LPVOID) &ptPlayScr,NP_NEWPLAYER));
}


/*=====================================================================
Function: ProcessNewMail()

Inputs: none

Outputs: none

Abstract:
ProcessNewMail is responsible for updating character positions, asking
for more information, etc.
======================================================================*/

void ProcessNewMail(
void
)
{
DWORD cbMaxMsg,cbNextMsg,cMsg,dwReadTimeout;
NetPacketType FAR *NPkt;
PlayerInfoType FAR *piPlayerPacket,piScratch;
PlayerType FAR *ptTrav;
PositionType FAR *ptPosPacket;
BOOL bFound,bDrawMessageSent;
int ix,iy;
BYTE bSquare1, bSquare2,Facing;
char cBuff[257],cNetMessBuff[400];
int FAR *iScore;
IntoneType FAR *itIntone;

#ifdef WIN32
GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout);
bDrawMessageSent = FALSE;

while (cMsg) {
if (!ReadFile(hMailSlot,cNetMessBuff,cbNextMsg,&cbMaxMsg,(LPOVERLAPPED) NULL)) {
MessageBox(NULL,GetStringRes(IDS_RDMSLOTFAIL),"ProcessNewMail",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}

NPkt = (NetPacketType FAR *) cNetMessBuff;

//
// Don't process the message if it's one of our own
//
if ((NPkt->ulSender != ptSelf.ulID)&&
((NPkt->ulDest == ptSelf.ulID)||(NPkt->ulDest == 0))
) {

piPlayerPacket = (PlayerInfoType FAR *) &(NPkt->cData);
ptPosPacket = (PositionType FAR *) &(NPkt->cData);
iScore = (int FAR *) &(NPkt->cData);
itIntone = (IntoneType FAR *) &(NPkt->cData);

bFound = FALSE;
ptTrav = &ptPlayers;

while (ptTrav->next != NULL) {
if (ptTrav->next->ulID == NPkt->ulSender) {
bFound = TRUE;
break;
}
ptTrav = ptTrav->next;
}

if ((!bFound)&&(NPkt->dwPacketType != NP_NEWPLAYER)) {
if(!SendNetMessage(0,NPkt->ulSender,NULL,NP_REQUESTIDENTITY)) {
MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}
}

switch (NPkt->dwPacketType) {

case (NP_NEWPLAYER):
if (!bFound) {
ptTrav->next = NewPlayer(piPlayerPacket->ulID,
piPlayerPacket->dwPID,
piPlayerPacket->cUserName,
piPlayerPacket->cComputerName,
piPlayerPacket->ix,
piPlayerPacket->iy,
piPlayerPacket->bFacing,
piPlayerPacket->iScore,
piPlayerPacket->iPicNum,
piPlayerPacket->iGridNum,
piPlayerPacket->pGridLoc.x,
piPlayerPacket->pGridLoc.y,
ptTrav->next);
if (!AddPic(piPlayerPacket->iPicNum)) {
ptTrav->next->iPicNum = PIC_DEFAULT;
sprintf(cBuff, GetStringRes(IDS_FMT_BADPICNUM),
piPlayerPacket->iPicNum,piPlayerPacket->cComputerName);
PrintTextLine(cBuff);
AddPic(PIC_DEFAULT);
}

//
// After we add the user to our own database,
// let him know we exist.
//
sprintf(cBuff,GetStringRes(IDS_FMT_JOIN),ptTrav->next->cUserName);
PrintTextLine(cBuff);

piScratch.ulID = ptSelf.ulID;
piScratch.dwPID = ptSelf.dwPID;
strcpy(piScratch.cUserName,ptSelf.cUserName);
strcpy(piScratch.cComputerName,ptSelf.cComputerName);
piScratch.ix = ptSelf.Pos.ix;
piScratch.iy = ptSelf.Pos.iy;
piScratch.bFacing = ptSelf.Pos.Facing;
piScratch.iScore = ptSelf.iScore;
piScratch.iPicNum = ptSelf.iPicNum;
piScratch.iGridNum = ptSelf.iGridNum;
piScratch.pGridLoc = ptSelf.pGridLoc;
if (!SendNetMessage(0,NPkt->ulSender,&piScratch,NP_NEWPLAYER)) {
MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}

//
// Finally, do a redraw to make sure if he's in
// line of sight we see him. */
//
PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
PostMessage(hWndScore,WM_COMMAND,WC_ADDSCORE,ptTrav->next->ulID);
}
break;

case (NP_REQUESTIDENTITY):
piScratch.ulID = ptSelf.ulID;
piScratch.dwPID = ptSelf.dwPID;
strcpy(piScratch.cUserName,ptSelf.cUserName);
strcpy(piScratch.cComputerName,ptSelf.cComputerName);
piScratch.ix = ptSelf.Pos.ix;
piScratch.iy = ptSelf.Pos.iy;
piScratch.bFacing = ptSelf.Pos.Facing;
piScratch.iScore = ptSelf.iScore;
piScratch.iPicNum = ptSelf.iPicNum;
piScratch.iGridNum = ptSelf.iGridNum;
piScratch.pGridLoc = ptSelf.pGridLoc;
if (!SendNetMessage(0,NPkt->ulSender,&piScratch,NP_NEWPLAYER)) {
MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"ProcessNewMail",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}

break;

case (NP_MOVETO):
if (bFound) {
ptTrav->next->Pos = *ptPosPacket;
if (ptTrav->next->Drawn) {
if (!bBitmapDraw) {
InvalidateRect(hWndMaze,&ptTrav->next->rDrawn,TRUE);
}
//
//BUGBUG -- drawing problem for bitmaps
//
ptTrav->next->Drawn=FALSE;
}
else {
if (!bDrawMessageSent) {
PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
bDrawMessageSent = TRUE;
}
}
PostMessage(hWndScore,WM_COMMAND,WC_UPDATEDIRECTION,ptTrav->next->ulID);
}
break;

case (NP_SHOTFIRED):
if (bFound) {

PlaySound("othrlasr.wav",NULL,SND_FILENAME|SND_ASYNC);

//
// if the shot was fired in the same 'x' or 'y'
// plane, we may have been hit!
//
if ((ptPosPacket->ix == ptSelf.Pos.ix)||
(ptPosPacket->iy == ptSelf.Pos.iy)
) {

ix = ptPosPacket->ix;
iy = ptPosPacket->iy;
Facing = ptPosPacket->Facing;

bSquare2 = bMaze[ix][iy];

do {
//
// if we're hit, handle it!
//
if ((!InSanctuary(&ptSelf.Pos))&&(ptSelf.Pos.ix == ix)&&(ptSelf.Pos.iy == iy)) {
if (!SendNetMessage(0,NPkt->ulSender,NULL,NP_HITCONFIRM)) {
MessageBox(NULL,GetStringRes(IDS_SNDPCKTFAIL),"MazeWndProc",
MB_ICONEXCLAMATION|MB_APPLMODAL);
}
sprintf(cBuff,GetStringRes(IDS_FMT_ZAPPEDYOU),ptTrav->next->cUserName);
PrintTextLine(cBuff);
KillSelf();
break;
}
//
// otherwise, keep checking.
//
bSquare1 = bSquare2;
bSquare2 = bMaze[ix = ADJ_X(ix,Facing)][iy = ADJ_Y(iy,Facing)];
} while (!((bSquare1 & Facing)||(bSquare2 & BACK_TO_ABS(Facing))));
}
}

break;

case (NP_LEAVINGGAME):
//
// ***BUGBUG*** Might cause problems if character leaving is on screen
//
if (bFound) {
sprintf(cBuff,GetStringRes(IDS_FMT_LEFT),ptTrav->next->cUserName);
PrintTextLine(cBuff);
PostMessage(hWndScore,WM_COMMAND,WC_DELETESCORE,ptTrav->next->ulID);
ptTrav->next = DeletePlayer(ptTrav->next);
PostMessage(hWndMaze,WM_COMMAND,IDM_DRAWPLAYERS,0);
}
break;

case (NP_HITCONFIRM):
PlaySound("squish.wav",NULL,SND_FILENAME|SND_ASYNC);
if (bFound) {
FadePic(ptTrav->next->iPicNum,
ptTrav->next->Pos.Facing,
hMazeDC,
&ptTrav->next->rFrom,
&ptTrav->next->rDrawn);
sprintf(cBuff,GetStringRes(IDS_FMT_HIT),ptTrav->next->cUserName);
PrintTextLine(cBuff);
}
ptSelf.iScore += 50;
iPlayersKilled++;

SendNetMessage(0,0,NULL,NP_SCORE);
PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptSelf.ulID);
break;

case (NP_SCORE):
if (bFound) {
ptTrav->next->iScore = *iScore;
PostMessage(hWndScore,WM_COMMAND,WC_UPDATESCORE,ptTrav->next->ulID);
}
break;

case NP_INTONE:
if (bFound) {
ix = ptSelf.Pos.ix - itIntone->Pos.ix;
iy = ptSelf.Pos.iy - itIntone->Pos.iy;
if ((ix*ix + iy*iy) <= (itIntone->iLoudness * itIntone->iLoudness)) {
sprintf(cBuff,"[%s] - \"%s\"",ptTrav->next->cUserName,
itIntone->cBuff);
PrintTextLine(cBuff);
}
}
break;

default:
MessageBox(NULL,GetStringRes(IDS_BADMSG),"ProcessNewMail",
MB_ICONEXCLAMATION|MB_APPLMODAL);
break;
}
}

GetMailslotInfo(hMailSlot,&cbMaxMsg,&cbNextMsg,&cMsg,&dwReadTimeout);
}

#endif // end if WIN32. This whole function is not present for WIN16.

return;

}