mirror of
https://github.com/galaxyhaxz/devilution
synced 2025-02-22 12:34:59 +00:00
1109 lines
24 KiB
C++
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
|
|
}
|