1
0
Fork 0
mirror of https://github.com/galaxyhaxz/devilution synced 2025-02-23 12:44:54 +00:00
devilution/2020_03_31/Source/multi.cpp
PrisonOfMirrors b720183846 initial up
2020-11-28 18:24:54 -06:00

1109 lines
24 KiB
C++

#include "diablo.h"
#include "../3rdParty/Storm/Source/storm.h"
#include "../DiabloUI/diabloui.h"
char gbSomebodyWonGameKludge; // weak
#ifdef _DEBUG
DWORD gdwHistTicks;
#endif
TBuffer sgHiPriBuf;
char szPlayerDescript[128];
WORD sgwPackPlrOffsetTbl[MAX_PLRS];
PkPlayerStruct netplr[MAX_PLRS];
BYTE sgbPlayerTurnBitTbl[MAX_PLRS];
char sgbPlayerLeftGameTbl[MAX_PLRS];
int sgbSentThisCycle; // idb
int dword_678628; // weak
char gbActivePlayers; // weak
char gbGameDestroyed; // weak
char sgbSendDeltaTbl[MAX_PLRS];
_gamedata sgGameInitInfo;
char gbSelectProvider; // weak
int sglTimeoutStart; // weak
int sgdwPlayerLeftReasonTbl[MAX_PLRS];
TBuffer sgLoPriBuf;
unsigned int sgdwGameLoops; // idb
unsigned char gbMaxPlayers; // weak
char sgbTimeout; // weak
char szPlayerName[128];
BYTE gbDeltaSender; // weak
int sgbNetInited; // weak
DWORD player_state[MAX_PLRS];
const int event_types[3] =
{
EVENT_TYPE_PLAYER_LEAVE_GAME,
EVENT_TYPE_PLAYER_CREATE_GAME,
EVENT_TYPE_PLAYER_MESSAGE
};
#ifdef _DEBUG
void __cdecl dumphist(const char *pszFmt, ...)
{
static FILE *sgpHistFile = NULL;
DWORD dwTicks;
va_list va;
va_start(va, pszFmt);
if(sgpHistFile == NULL) {
sgpHistFile = fopen("c:\\dumphist.txt", "wb");
if(sgpHistFile == NULL) {
return;
}
}
dwTicks = GetTickCount();
fprintf(sgpHistFile, "%4u.%02u ", (dwTicks - gdwHistTicks) / 1000, (dwTicks - gdwHistTicks) % 1000 / 10);
vfprintf(sgpHistFile, pszFmt, va);
fprintf(
sgpHistFile,
"\r\n (%d,%d)(%d,%d)(%d,%d)(%d,%d)\r\n",
plr[0].plractive,
player_state[0],
plr[1].plractive,
player_state[1],
plr[2].plractive,
player_state[2],
plr[3].plractive,
player_state[3]);
fflush(sgpHistFile);
}
#endif
void multi_msg_add(const BYTE *pbMsg, BYTE bLen)
{
/// ASSERT: assert(sgbNetInited);
if(pbMsg != NULL && bLen != 0) {
/// ASSERT: assert(bLen <= gdwNormalMsgSize - sizeof(TPktHdr));
tmsg_add(pbMsg, bLen);
}
}
void NetSendLoPri(const BYTE *pbMsg, BYTE bLen)
{
/// ASSERT: assert(sgbNetInited);
if(pbMsg != NULL && bLen != 0) {
/// ASSERT: assert(bLen <= gdwNormalMsgSize - sizeof(TPktHdr));
multi_copy_packet(&sgLoPriBuf, pbMsg, bLen);
multi_send_packet(pbMsg, bLen);
}
}
void multi_copy_packet(TBuffer *pBuf, const BYTE *pbMsg, BYTE bLen)
{
BYTE *p;
if(pBuf->dwNextWriteOffset + bLen + 2 > sizeof(pBuf->bData)) {
#ifdef _DEBUG
app_fatal("msg buffer failure");
#endif
return;
}
p = &pBuf->bData[pBuf->dwNextWriteOffset];
pBuf->dwNextWriteOffset += bLen + 1;
*p = bLen;
p++;
memcpy(p, pbMsg, bLen);
p += bLen;
*p = 0;
}
void multi_send_packet(const BYTE *pbMsg, BYTE bLen)
{
TPkt pkt;
/// ASSERT: assert(sgbNetInited);
/// ASSERT: assert(pbMsg);
/// ASSERT: assert(bLen);
/// ASSERT: assert(bLen <= gdwNormalMsgSize - sizeof(TPktHdr));
NetRecvPlrData(&pkt);
pkt.hdr.wLen = bLen + sizeof(TPktHdr);
memcpy(pkt.body, pbMsg, bLen);
if(!SNetSendMessage(myplr, &pkt.hdr, pkt.hdr.wLen)) {
nthread_terminate_game("SNetSendMessage0");
}
#if 0
debug_plr_tbl[myplr]++;
#endif
}
void NetRecvPlrData(TPkt *pkt)
{
/// ASSERT: assert(pkt);
pkt->hdr.wCheck = 'ip';
pkt->hdr.px = plr[myplr].WorldX;
pkt->hdr.py = plr[myplr].WorldY;
pkt->hdr.targx = plr[myplr]._ptargx;
pkt->hdr.targy = plr[myplr]._ptargy;
pkt->hdr.php = plr[myplr]._pHitPoints;
pkt->hdr.pmhp = plr[myplr]._pMaxHP;
pkt->hdr.bstr = plr[myplr]._pBaseStr;
pkt->hdr.bmag = plr[myplr]._pBaseMag;
pkt->hdr.bdex = plr[myplr]._pBaseDex;
}
void NetSendHiPri(const BYTE *pbMsg, BYTE bLen)
{
DWORD dwLeft, dwLen;
BYTE *p;
TPkt pkt;
/// ASSERT: assert(sgbNetInited);
if(pbMsg != NULL && bLen != 0) {
/// ASSERT: assert(bLen <= gdwNormalMsgSize - sizeof(TPktHdr));
multi_copy_packet(&sgHiPriBuf, pbMsg, bLen);
multi_send_packet(pbMsg, bLen);
}
if(dword_678628) {
return;
}
dword_678628 = 1;
/// ASSERT: assert((DWORD) myplr < MAX_PLRS);
NetRecvPlrData(&pkt);
dwLeft = gdwNormalMsgSize - sizeof(TPktHdr);
p = multi_recv_packet(&sgHiPriBuf, pkt.body, &dwLeft);
p = multi_recv_packet(&sgLoPriBuf, p, &dwLeft);
dwLeft = sync_all_monsters(p, dwLeft);
dwLen = gdwNormalMsgSize - dwLeft;
pkt.hdr.wLen = dwLen;
if(!SNetSendMessage(SNPLAYER_OTHERS, &pkt.hdr, dwLen)) {
nthread_terminate_game("SNetSendMessage");
}
#if 0
if(myplr != 0) {
debug_plr_tbl[0]++;
}
if(myplr != 1) {
debug_plr_tbl[1]++;
}
if(myplr != 2) {
debug_plr_tbl[2]++;
}
if(myplr != 3) {
debug_plr_tbl[3]++;
}
#endif
}
BYTE *multi_recv_packet(TBuffer *pBuf, BYTE *p, DWORD *dwSize)
{
BYTE bLen;
BYTE *pbMsg;
if(pBuf->dwNextWriteOffset == 0) {
return p;
}
pbMsg = pBuf->bData;
while(1) {
bLen = *pbMsg;
if(bLen == 0 || bLen > *dwSize) {
break;
}
pbMsg++;
memcpy(p, pbMsg, bLen);
pbMsg += bLen;
p += bLen;
*dwSize -= bLen;
}
memcpy(pBuf->bData, pbMsg, pBuf->dwNextWriteOffset - (pbMsg - pBuf->bData) + 1);
pBuf->dwNextWriteOffset -= pbMsg - pBuf->bData;
return p;
}
void multi_send_msg_packet(int msk, const BYTE *pbMsg, BYTE bLen)
{
int m, pnum;
DWORD dwSendBytes;
TPkt pkt;
/// ASSERT: assert(sgbNetInited);
/// ASSERT: assert(pbMsg);
/// ASSERT: assert(bLen);
NetRecvPlrData(&pkt);
dwSendBytes = bLen + sizeof(TPktHdr);
/// ASSERT: assert(dwSendBytes < gdwNormalMsgSize);
pkt.hdr.wLen = dwSendBytes;
memcpy(pkt.body, pbMsg, bLen);
m = 1;
pnum = 0;
while((DWORD)pnum < MAX_PLRS) {
if(m & msk) {
if(SNetSendMessage(pnum, &pkt.hdr, dwSendBytes)) {
#if 0
debug_plr_tbl[pnum]++;
#endif
} else if(SErrGetLastError() != STORM_ERROR_INVALID_PLAYER) {
nthread_terminate_game("SNetSendMessage");
return;
}
}
pnum++;
m <<= 1;
}
}
void multi_msg_countdown()
{
int i;
for(i = 0; i < MAX_PLRS; i++) {
if(player_state[i] & 0x20000) {
/// ASSERT: assert(glpMsgTbl[i]);
if(gdwMsgLenTbl[i] == sizeof(DWORD)) {
multi_parse_turn(i, *glpMsgTbl[i]);
}
}
}
}
void multi_parse_turn(int pnum, DWORD turn)
{
if(turn & 0x80000000) {
multi_handle_turn_upper_bit(pnum);
}
turn &= ~0x80000000;
if(sgbSentThisCycle < gdwTurnsInTransit + turn) {
if(turn >= ~0x80000000) {
turn &= 0xFFFF;
}
sgbSentThisCycle = turn + gdwTurnsInTransit;
turn *= gbDeltaTurnsSec;
turn <<= 2;
sgdwGameLoops = turn;
}
}
void multi_handle_turn_upper_bit(int pnum)
{
int i;
for(i = 0; i < MAX_PLRS; i++) {
if(player_state[i] & 0x10000 && i != pnum) {
break;
}
}
if(myplr == i) {
sgbSendDeltaTbl[pnum] = 1;
} else if(myplr == pnum) {
gbDeltaSender = i;
}
}
void multi_player_left(int pnum, DWORD reason)
{
sgbPlayerLeftGameTbl[pnum] = 1;
sgdwPlayerLeftReasonTbl[pnum] = reason;
multi_clear_left_tbl();
}
void multi_clear_left_tbl()
{
int i;
for(i = 0; i < MAX_PLRS; i++) {
if(sgbPlayerLeftGameTbl[i]) {
if(gbBufferMsgs == 1) {
#ifdef _DEBUG
dumphist("(%d) buffering -- player %d left game due to %d", myplr, i, sgdwPlayerLeftReasonTbl[i]);
#endif
msg_send_drop_pkt(i, sgdwPlayerLeftReasonTbl[i]);
} else {
#ifdef _DEBUG
dumphist("(%d) player %d left game due to %d", myplr, i, sgdwPlayerLeftReasonTbl[i]);
#endif
multi_player_left_msg(i, TRUE);
}
sgbPlayerLeftGameTbl[i] = 0;
sgdwPlayerLeftReasonTbl[i] = 0;
}
}
}
void multi_player_left_msg(int pnum, BOOL left)
{
char *pszLeft;
if(!plr[pnum].plractive) {
return;
}
#ifdef _DEBUG
dumphist("(%d) player %d --> inactive", myplr, pnum);
#endif
RemovePlrFromMap(pnum);
RemovePortalMissile(pnum);
DeactivatePortal(pnum);
delta_close_portal(pnum);
RemovePlrMissiles(pnum);
if(left) {
pszLeft = "Player '%s' just left the game";
switch(sgdwPlayerLeftReasonTbl[pnum]) {
case 0x40000006:
pszLeft = "Player '%s' dropped due to timeout";
break;
case 0x40000004:
pszLeft = "Player '%s' killed Diablo and left the game!";
gbSomebodyWonGameKludge = 1;
break;
}
EventPlrMsg(pszLeft, plr[pnum]._pName);
}
plr[pnum].plractive = 0;
plr[pnum]._pName[0] = '\0';
gbActivePlayers--;
}
void multi_net_ping()
{
sgbTimeout = 1;
sglTimeoutStart = GetTickCount();
}
BOOL multi_handle_delta()
{
int i;
BOOL fSendAsync;
/// ASSERT: assert(sgbNetInited);
if(gbGameDestroyed) {
gbRunGame = 0;
return FALSE;
}
for(i = 0; i < MAX_PLRS; i++) {
if(sgbSendDeltaTbl[i]) {
sgbSendDeltaTbl[i] = 0;
DeltaExportData(i);
}
}
sgbSentThisCycle = nthread_send_and_recv_turn(sgbSentThisCycle, 1);
if(!nthread_recv_turns(&fSendAsync)) {
multi_begin_timeout();
return FALSE;
}
sgbTimeout = 0;
if(fSendAsync) {
if(!dword_678628) {
NetSendHiPri(NULL, 0);
dword_678628 = 0;
} else {
dword_678628 = 0;
if(!multi_check_pkt_valid(&sgHiPriBuf)) {
NetSendHiPri(NULL, 0);
}
}
}
multi_mon_seeds();
return TRUE;
}
BOOL multi_check_pkt_valid(TBuffer *pBuf)
{
return pBuf->dwNextWriteOffset == 0;
}
void multi_mon_seeds()
{
int i;
DWORD s;
sgdwGameLoops++;
s = _rotr(sgdwGameLoops, 8);
for(i = 0; i < MAXMONSTERS; i++) {
monster[i]._mAISeed = i + s;
}
}
void multi_begin_timeout()
{
int i, nTicks, nState, nLowestActive, nLowestPlayer;
BYTE bGroupPlayers, bGroupCount;
if(!sgbTimeout) {
return;
}
#ifdef _DEBUG
if(debug_mode_key_i) {
return;
}
#endif
nTicks = GetTickCount() - sglTimeoutStart;
if(nTicks > 20000) {
gbRunGame = FALSE;
return;
}
if(nTicks < 10000) {
return;
}
nLowestActive = -1;
nLowestPlayer = -1;
bGroupPlayers = 0;
bGroupCount = 0;
for(i = 0; i < MAX_PLRS; i++) {
nState = player_state[i];
if(nState & 0x10000) {
if(nLowestPlayer == -1) {
nLowestPlayer = i;
}
if(nState & 0x40000) {
bGroupPlayers++;
if(nLowestActive == -1) {
nLowestActive = i;
}
} else {
bGroupCount++;
}
}
}
/// ASSERT: assert(bGroupPlayers);
/// ASSERT: assert(nLowestActive != -1);
/// ASSERT: assert(nLowestPlayer != -1);
#ifdef _DEBUG
dumphist(
"(%d) grp:%d ngrp:%d lowp:%d lowa:%d",
myplr,
bGroupPlayers,
bGroupCount,
nLowestPlayer,
nLowestActive);
#endif
if(bGroupPlayers < bGroupCount) {
gbGameDestroyed = TRUE;
} else if(bGroupPlayers == bGroupCount) {
if(nLowestPlayer != nLowestActive) {
gbGameDestroyed = TRUE;
} else if(nLowestActive == myplr) {
multi_check_drop_player();
}
} else if(nLowestActive == myplr) {
multi_check_drop_player();
}
}
void multi_check_drop_player()
{
int i;
for(i = 0; i < MAX_PLRS; i++) {
if(!(player_state[i] & 0x40000) && player_state[i] & 0x10000) {
#ifdef _DEBUG
dumphist("(%d) dropping player %d state %d", myplr, i, player_state[i]);
#endif
SNetDropPlayer(i, 0x40000006);
}
}
}
void multi_process_network_packets()
{
int dx, dy;
DWORD dwID, dwMsgSize;
char *p;
TPkt *pkt;
multi_clear_left_tbl();
multi_process_tmsgs();
/// ASSERT: assert(sgbNetInited);
while(SNetReceiveMessage(&dwID, &p, &dwMsgSize)) {
dwRecCount++; /* unused, probably debug to track total packets received */
multi_clear_left_tbl();
pkt = (TPkt *)p;
/// ASSERT: assert(dwMsgSize >= sizeof(TPktHdr));
/// ASSERT: assert(dwID < MAX_PLRS);
/// ASSERT: assert(pkt->hdr.wCheck == 0x6970);
/// ASSERT: assert(pkt->hdr.wLen == dwMsgSize);
if(dwMsgSize < sizeof(TPktHdr)) {
continue;
}
if(dwID >= MAX_PLRS) {
continue;
}
if(pkt->hdr.wCheck != 'ip') {
continue;
}
if(pkt->hdr.wLen != dwMsgSize) {
continue;
}
plr[dwID]._pownerx = pkt->hdr.px;
plr[dwID]._pownery = pkt->hdr.py;
#if 0
debug_act_plrs[dwID]++;
#endif
if(dwID != myplr) {
/// ASSERT: assert(gbBufferMsgs != BUFFER_PROCESS);
plr[dwID]._pHitPoints = pkt->hdr.php;
plr[dwID]._pMaxHP = pkt->hdr.pmhp;
plr[dwID]._pBaseStr = pkt->hdr.bstr;
plr[dwID]._pBaseMag = pkt->hdr.bmag;
plr[dwID]._pBaseDex = pkt->hdr.bdex;
if(gbBufferMsgs != 1 && plr[dwID].plractive && plr[dwID]._pHitPoints != 0) {
if(currlevel == plr[dwID].plrlevel && !plr[dwID]._pLvlChanging) {
dx = abs(plr[dwID].WorldX - pkt->hdr.px);
dy = abs(plr[dwID].WorldY - pkt->hdr.py);
if((dx > 3 || dy > 3) && dPlayer[pkt->hdr.px][pkt->hdr.py] == 0) {
FixPlrWalkTags(dwID);
plr[dwID]._poldx = plr[dwID].WorldX;
plr[dwID]._poldy = plr[dwID].WorldY;
FixPlrWalkTags(dwID);
plr[dwID].WorldX = pkt->hdr.px;
plr[dwID].WorldY = pkt->hdr.py;
plr[dwID]._px = pkt->hdr.px;
plr[dwID]._py = pkt->hdr.py;
dPlayer[plr[dwID].WorldX][plr[dwID].WorldY] = dwID + 1;
}
dx = abs(plr[dwID]._px - plr[dwID].WorldX);
dy = abs(plr[dwID]._py - plr[dwID].WorldY);
if(dx > 1 || dy > 1) {
plr[dwID]._px = plr[dwID].WorldX;
plr[dwID]._py = plr[dwID].WorldY;
}
MakePlrPath(dwID, pkt->hdr.targx, pkt->hdr.targy, TRUE);
} else {
plr[dwID].WorldX = pkt->hdr.px;
plr[dwID].WorldY = pkt->hdr.py;
plr[dwID]._px = pkt->hdr.px;
plr[dwID]._py = pkt->hdr.py;
plr[dwID]._ptargx = pkt->hdr.targx;
plr[dwID]._ptargy = pkt->hdr.targy;
}
}
}
multi_handle_all_packets(dwID, pkt->body, dwMsgSize - sizeof(TPktHdr));
}
if(SErrGetLastError() != STORM_ERROR_NO_MESSAGES_WAITING) {
nthread_terminate_game("SNetReceiveMsg");
}
}
void multi_handle_all_packets(int pnum, BYTE *pData, DWORD dwSize)
{
DWORD dwLen;
while(dwSize != 0) {
dwLen = ParseCmd(pnum, (TCmd *)pData);
if(dwLen == 0) {
break;
}
pData += dwLen;
dwSize -= dwLen;
}
}
void multi_process_tmsgs()
{
DWORD dwSize;
TPkt pkt;
while(dwSize = tmsg_get((BYTE *)&pkt, sizeof(pkt))) {
multi_handle_all_packets(myplr, (BYTE *)&pkt, dwSize);
}
}
void multi_send_zero_packet(int pnum, BYTE bCmd, BYTE *pbSrc, DWORD dwLen)
{
DWORD dwOffset, dwBody, dwMsg;
TCmdPlrInfoHdr *p;
TPkt pkt;
/// ASSERT: assert(pnum != myplr);
/// ASSERT: assert(pbSrc);
/// ASSERT: assert(dwLen <= 0x0ffff);
dwOffset = 0;
while(dwLen != 0) {
pkt.hdr.wCheck = 'ip';
pkt.hdr.px = 0;
pkt.hdr.py = 0;
pkt.hdr.targx = 0;
pkt.hdr.targy = 0;
pkt.hdr.php = 0;
pkt.hdr.pmhp = 0;
pkt.hdr.bstr = 0;
pkt.hdr.bmag = 0;
pkt.hdr.bdex = 0;
p = (TCmdPlrInfoHdr *)pkt.body;
p->bCmd = bCmd;
p->wOffset = dwOffset;
dwBody = gdwLargestMsgSize - sizeof(TPktHdr) - sizeof(*p);
if(dwLen < dwBody) {
dwBody = dwLen;
}
/// ASSERT: assert(dwBody <= 0x0ffff);
p->wBytes = dwBody;
memcpy(&pkt.body[sizeof(*p)], pbSrc, p->wBytes);
dwMsg = sizeof(TPktHdr);
dwMsg += sizeof(*p);
dwMsg += p->wBytes;
pkt.hdr.wLen = dwMsg;
if(!SNetSendMessage(pnum, &pkt, dwMsg)) {
nthread_terminate_game("SNetSendMessage2");
return;
}
#if 0
if((DWORD)pnum < MAX_PLRS) {
debug_plr_tbl[pnum]++;
} else {
if(myplr != 0) {
debug_plr_tbl[0]++;
}
if(myplr != 1) {
debug_plr_tbl[1]++;
}
if(myplr != 2) {
debug_plr_tbl[2]++;
}
if(myplr != 3) {
debug_plr_tbl[3]++;
}
}
#endif
pbSrc += p->wBytes;
dwLen -= p->wBytes;
dwOffset += p->wBytes;
}
}
void NetClose()
{
if(sgbNetInited) {
sgbNetInited = 0;
nthread_cleanup();
dthread_cleanup();
tmsg_cleanup();
multi_event_handler(FALSE);
SNetLeaveGame(3);
msgcmd_cmd_cleanup();
#ifdef _DEBUG
dumphist("(%d) NetClose", myplr);
#endif
if(gbMaxPlayers > 1) {
Sleep(2000);
}
}
}
void multi_event_handler(BOOL reg)
{
int i;
BOOL (__stdcall *func)(int, void (__stdcall *)(_SNETEVENT *));
if(reg) {
func = SNetRegisterEventHandler;
} else {
func = SNetUnregisterEventHandler;
}
for(i = 0; (DWORD)i < 3; i++) {
if(!func(event_types[i], multi_handle_events) && reg) {
app_fatal("SNetRegisterEventHandler:\n%s", TraceLastError());
}
}
}
void __stdcall multi_handle_events(_SNETEVENT *pEvt)
{
DWORD reason;
_gamedata *pGame;
switch(pEvt->eventid) {
case EVENT_TYPE_PLAYER_CREATE_GAME:
/// ASSERT: assert(pEvt->data);
/// ASSERT: assert(pEvt->databytes >= sizeof(DWORD));
pGame = (_gamedata *)pEvt->data;
sgGameInitInfo.dwSeed = pGame->dwSeed;
sgGameInitInfo.bDiff = pGame->bDiff;
sgbPlayerTurnBitTbl[pEvt->playerid] = 1;
break;
case EVENT_TYPE_PLAYER_LEAVE_GAME:
/// ASSERT: assert(pEvt->playerid >= 0 && pEvt->playerid < MAX_PLRS);
sgbPlayerLeftGameTbl[pEvt->playerid] = 1;
sgbPlayerTurnBitTbl[pEvt->playerid] = 0;
reason = 0;
if(pEvt->data != NULL && pEvt->databytes >= 4) {
reason = ((DWORD *)pEvt->data)[0];
}
sgdwPlayerLeftReasonTbl[pEvt->playerid] = reason;
if(reason == 0x40000004) {
gbSomebodyWonGameKludge = 1;
}
sgbSendDeltaTbl[pEvt->playerid] = 0;
dthread_remove_player(pEvt->playerid);
if(gbDeltaSender == pEvt->playerid) {
gbDeltaSender = 4;
}
#ifdef _DEBUG
dumphist("(%d) callback: player %d left game due to %d", myplr, pEvt->playerid, reason);
#endif
break;
case EVENT_TYPE_PLAYER_MESSAGE:
/// ASSERT: assert(pEvt->data);
ErrorPlrMsg((char *)pEvt->data);
break;
}
}
BOOL NetInit(BOOL bSinglePlayer, BOOL *pfExitProgram)
{
int i;
DWORD dwBytes;
_SNETPROGRAMDATA progdata;
_SNETPLAYERDATA plrdata;
_SNETUIDATA uidata;
while(1) {
/// ASSERT: assert(pfExitProgram);
*pfExitProgram = FALSE;
SetRndSeed(0);
sgGameInitInfo.dwSeed = time(NULL);
sgGameInitInfo.bDiff = gnDifficulty;
memset(&progdata, 0, sizeof(progdata));
progdata.size = sizeof(progdata);
progdata.programname = "Diablo Retail";
progdata.programdescription = gszVersionNumber;
progdata.programid = 'DRTL';
progdata.versionid = 42;
progdata.maxplayers = MAX_PLRS;
progdata.initdata = &sgGameInitInfo;
progdata.initdatabytes = sizeof(sgGameInitInfo);
progdata.optcategorybits = 15;
progdata.lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
memset(&plrdata, 0, sizeof(plrdata));
plrdata.size = sizeof(plrdata);
memset(&uidata, 0, sizeof(uidata));
uidata.size = sizeof(uidata);
uidata.parentwindow = SDrawGetFrameWindow(0);
uidata.artcallback = (void (*)())UiArtCallback;
uidata.createcallback = (void (*)())UiCreateGameCallback;
uidata.drawdesccallback = (void (*)())UiDrawDescCallback;
uidata.messageboxcallback = (void (*)())UiMessageBoxCallback;
uidata.soundcallback = (void (*)())UiSoundCallback;
uidata.authcallback = (void (*)())UiAuthCallback;
uidata.getdatacallback = (void (*)())UiGetDataCallback;
uidata.categorycallback = (void (*)())UiCategoryCallback;
uidata.selectnamecallback = (void (*)())mainmenu_select_hero_dialog;
uidata.changenamecallback = (void (*)())mainmenu_create_hero;
uidata.profilebitmapcallback = (void (*)())UiProfileDraw;
uidata.profilecallback = (void (*)())UiProfileCallback;
uidata.profilefields = UiProfileGetString();
memset(sgbPlayerTurnBitTbl, 0, sizeof(sgbPlayerTurnBitTbl));
gbGameDestroyed = 0;
memset(sgbPlayerLeftGameTbl, 0, sizeof(sgbPlayerLeftGameTbl));
memset(sgdwPlayerLeftReasonTbl, 0, sizeof(sgdwPlayerLeftReasonTbl));
memset(sgbSendDeltaTbl, 0, sizeof(sgbSendDeltaTbl));
memset(plr, 0, sizeof(plr));
memset(sgwPackPlrOffsetTbl, 0, sizeof(sgwPackPlrOffsetTbl));
SNetSetBasePlayer(0);
if(bSinglePlayer) {
if(!multi_init_single(&progdata, &plrdata, &uidata)) {
return FALSE;
}
} else {
if(!multi_init_multi(&progdata, &plrdata, &uidata, pfExitProgram)) {
return FALSE;
}
}
#ifdef _DEBUG
gdwHistTicks = GetTickCount();
dumphist("(%d) new game started", myplr);
#endif
sgbNetInited = 1;
sgbTimeout = 0;
delta_init();
InitPlrMsg();
buffer_init(&sgHiPriBuf);
buffer_init(&sgLoPriBuf);
dword_678628 = 0;
sync_init();
nthread_start(sgbPlayerTurnBitTbl[myplr]);
dthread_start();
tmsg_start();
sgdwGameLoops = 0;
sgbSentThisCycle = 0;
gbDeltaSender = myplr;
gbSomebodyWonGameKludge = 0;
nthread_send_and_recv_turn(0, 0);
SetupLocalCoords();
multi_send_pinfo(SNPLAYER_OTHERS, CMD_SEND_PLRINFO);
plr[myplr].plractive = 1;
gbActivePlayers = 1;
if(sgbPlayerTurnBitTbl[myplr] == 0 || msg_wait_resync()) {
break;
}
NetClose();
gbSelectProvider = 0;
}
gnDifficulty = sgGameInitInfo.bDiff;
SetRndSeed(sgGameInitInfo.dwSeed);
for(i = 0; i < NUMLEVELS; i++) {
glSeedTbl[i] = GetRndSeed();
gnLevelTypeTbl[i] = InitLevelType(i);
}
if(!SNetGetGameInfo(GAMEINFO_NAME, szPlayerName, sizeof(szPlayerName), &dwBytes)) {
nthread_terminate_game("SNetGetGameInfo1");
}
if(!SNetGetGameInfo(GAMEINFO_PASSWORD, szPlayerDescript, sizeof(szPlayerDescript), &dwBytes)) {
nthread_terminate_game("SNetGetGameInfo2");
}
return TRUE;
}
void buffer_init(TBuffer *pBuf)
{
pBuf->dwNextWriteOffset = 0;
pBuf->bData[0] = 0;
}
void multi_send_pinfo(int pnum, BYTE cmd)
{
PkPlayerStruct pkplr;
PackPlayer(&pkplr, myplr, TRUE);
dthread_send_delta(pnum, cmd, (BYTE *)&pkplr, sizeof(pkplr));
}
int InitLevelType(int l)
{
if(l == 0) {
return DTYPE_TOWN;
}
if(l >= 1 && l <= 4) {
return DTYPE_CATHEDRAL;
}
if(l >= 5 && l <= 8) {
return DTYPE_CATACOMBS;
}
if(l >= 9 && l <= 12) {
return DTYPE_CAVES;
}
return DTYPE_HELL;
}
void SetupLocalCoords()
{
int x, y;
if(!leveldebug || gbMaxPlayers > 1) {
currlevel = 0;
leveltype = 0;
setlevel = 0;
}
x = 75;
y = 68;
#ifdef _DEBUG
if(debug_mode_key_inverted_v || debug_mode_key_d) {
x = 49;
y = 23;
}
#endif
x += plrxoff[myplr];
y += plryoff[myplr];
plr[myplr].WorldX = x;
plr[myplr].WorldY = y;
plr[myplr]._px = x;
plr[myplr]._py = y;
plr[myplr]._ptargx = x;
plr[myplr]._ptargy = y;
plr[myplr].plrlevel = currlevel;
plr[myplr]._pLvlChanging = 1;
plr[myplr].pLvlLoad = 0;
plr[myplr]._pmode = PM_NEWLVL;
plr[myplr].destAction = -1;
}
BOOL multi_init_single(_SNETPROGRAMDATA *progdata, _SNETPLAYERDATA *plrdata, _SNETUIDATA *uidata)
{
DWORD dwID;
if(!SNetInitializeProvider(0, progdata, plrdata, uidata, &fileinfo)) {
SErrGetLastError();
return FALSE;
}
dwID = 0;
if(!SNetCreateGame("local", "local", "local", 0, &sgGameInitInfo, sizeof(sgGameInitInfo), 1, "local", "local", &dwID)) {
app_fatal("SNetCreateGame1:\n%s", TraceLastError());
}
/// ASSERT: assert(dwID == 0);
myplr = 0;
gbMaxPlayers = 1;
return TRUE;
}
BOOL multi_init_multi(_SNETPROGRAMDATA *progdata, _SNETPLAYERDATA *plrdata, _SNETUIDATA *uidata, BOOL *pfExitProgram)
{
DWORD dwProvider, dwID;
BOOL fUpgrade;
fUpgrade = TRUE;
while(1) {
dwProvider = 0;
if(gbSelectProvider) {
if(!UiSelectProvider(0, progdata, plrdata, uidata, &fileinfo, &dwProvider)) {
if(!fUpgrade) {
return FALSE;
}
if(SErrGetLastError() != STORM_ERROR_REQUIRES_UPGRADE) {
return FALSE;
}
if(!multi_upgrade(pfExitProgram)) {
return FALSE;
}
}
if(dwProvider == 'BNET') {
plr[0].pBattleNet = 1;
}
}
multi_event_handler(TRUE);
if(UiSelectGame(1, progdata, plrdata, uidata, &fileinfo, &dwID)) {
break;
}
/// ASSERT: assert(! *pfExitProgram);
gbSelectProvider = 1;
fUpgrade = FALSE;
}
if(dwID >= MAX_PLRS) {
return FALSE;
} else {
myplr = dwID;
gbMaxPlayers = MAX_PLRS;
pfile_read_player_from_save();
if(dwProvider == 'BNET') {
plr[myplr].pBattleNet = 1;
}
return TRUE;
}
}
BOOL multi_upgrade(BOOL *pfExitProgram)
{
DWORD dwStatus;
SNetPerformUpgrade(&dwStatus);
switch(dwStatus) {
case 0xFFFFFFFF:
DrawDlg("Network upgrade failed");
break;
case 0:
return TRUE;
case 1:
return TRUE;
case 2:
*pfExitProgram = TRUE;
break;
}
return FALSE;
}
void recv_plrinfo(int pnum, TCmdPlrInfoHdr *p, BOOL recv)
{
char *szEvent;
if(myplr == pnum) {
return;
}
/// ASSERT: assert((DWORD)pnum < MAX_PLRS);
if(sgwPackPlrOffsetTbl[pnum] != p->wOffset) {
sgwPackPlrOffsetTbl[pnum] = 0;
if(p->wOffset != 0) {
return;
}
}
if(!recv && sgwPackPlrOffsetTbl[pnum] == 0) {
multi_send_pinfo(pnum, CMD_ACK_PLRINFO);
}
memcpy((char *)&netplr[pnum] + p->wOffset, &p[1], p->wBytes); /* todo: cast? */
sgwPackPlrOffsetTbl[pnum] += p->wBytes;
if(sgwPackPlrOffsetTbl[pnum] != sizeof(*netplr)) {
return;
}
sgwPackPlrOffsetTbl[pnum] = 0;
multi_player_left_msg(pnum, 0);
plr[pnum]._pGFXLoad = 0;
UnPackPlayer(&netplr[pnum], pnum, 1);
if(!recv) {
#ifdef _DEBUG
dumphist("(%d) received all %d plrinfo", myplr, pnum);
#endif
return;
}
plr[pnum].plractive = 1;
gbActivePlayers++;
if(sgbPlayerTurnBitTbl[pnum] != 0) {
szEvent = "Player '%s' (level %d) just joined the game";
} else {
szEvent = "Player '%s' (level %d) is already in the game";
}
EventPlrMsg(szEvent, plr[pnum]._pName, plr[pnum]._pLevel);
LoadPlrGFX(pnum, 1);
SyncInitPlr(pnum);
if(plr[pnum].plrlevel == currlevel) {
if(plr[pnum]._pHitPoints >> 6 > 0) {
StartStand(pnum, 0);
} else {
plr[pnum]._pgfxnum = 0;
LoadPlrGFX(pnum, 128);
plr[pnum]._pmode = PM_DEATH;
NewPlrAnim(pnum, plr[pnum]._pDAnim[0], plr[pnum]._pDFrames, 1, plr[pnum]._pDWidth);
plr[pnum]._pAnimFrame = plr[pnum]._pAnimLen - 1;
plr[pnum]._pVar8 = 2 * plr[pnum]._pAnimLen;
dFlags[plr[pnum].WorldX][plr[pnum].WorldY] |= 4;
}
}
#ifdef _DEBUG
dumphist("(%d) making %d active -- recv_plrinfo", myplr, pnum);
#endif
}