MuckyFoot-UrbanChaos/thrust/net.cpp
2017-05-20 11:14:17 +10:00

631 lines
9.5 KiB
C++

//
// Direct play stuff.
//
#include <dplay.h>
#include <dplobby.h>
#include <stdlib.h>
#include "always.h"
#include "net.h"
//
// Our unique application GUID.
//
static const GUID NET_application_guid = { 0xfb222ea3, 0x6565, 0x11d3, { 0xa7, 0xd8, 0x0, 0xa0, 0xc9, 0x1d, 0x60, 0x9e } };
//
// The main interface to DirectPlay4 and the lobby interface we need
// to create addresses.
//
LPDIRECTPLAY4A NET_dp;
LPDIRECTPLAYLOBBY3A NET_lobby;
//
// The server player and the local player.
//
DPID NET_player_server;
DPID NET_player_local;
//
// The buffer into which messages are received.
//
UBYTE NET_buffer[NET_MAX_MESSAGE_LENGTH];
//
// The current connection.
//
#define NET_CONNECTION_NONE 0
#define NET_CONNECTION_LAN 1
#define NET_CONNECITON_INTERNET 2
SLONG NET_connection;
SLONG NET_init(void)
{
HRESULT res;
//
// Initialise the COM module.
//
CoInitialize(NULL);
//
// Create the DirectPlay3 interface.
//
res = CoCreateInstance(CLSID_DirectPlay, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay4A, (LPVOID*)&NET_dp);
if (res != S_OK)
{
CoUninitialize();
return FALSE;
}
res = CoCreateInstance(CLSID_DirectPlayLobby, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayLobby3A, (LPVOID *) &NET_lobby);
if (res != S_OK)
{
CoUninitialize();
return FALSE;
}
return TRUE;
}
void NET_kill(void)
{
if (NET_dp)
{
NET_dp->Release();
}
if (NET_lobby)
{
NET_lobby->Release();
}
//
// Get rid of the tossy COM module.
//
CoUninitialize();
}
SLONG NET_connection_lan()
{
DPCOMPOUNDADDRESSELEMENT address_element[1];
SLONG num_elements;
void *address;
ULONG address_size;
HRESULT res;
if (NET_connection == NET_CONNECTION_LAN)
{
//
// Already connected to the LAN!
//
return TRUE;
}
//
// Create an IPX address.
//
address_element[0].guidDataType = DPAID_ServiceProvider;
address_element[0].dwDataSize = sizeof(GUID);
address_element[0].lpData = (LPVOID) &DPSPGUID_IPX;
num_elements = 1;
//
// How big is the address going to be?
//
res = NET_lobby->CreateCompoundAddress(address_element, num_elements, NULL, &address_size);
if (res != DPERR_BUFFERTOOSMALL)
{
return FALSE;
}
//
// Allocate memory for the address.
//
address = (void *) malloc(address_size);
//
// Actually create the address now.
//
res = NET_lobby->CreateCompoundAddress(address_element, num_elements, address, &address_size);
if (res != DP_OK)
{
return FALSE;
}
//
// Make the connection.
//
res = NET_dp->InitializeConnection(address, 0);
if (res != DP_OK)
{
return FALSE;
}
NET_connection = NET_CONNECTION_LAN;
return TRUE;
}
SLONG NET_session_create(CBYTE *session_name, SLONG max_players)
{
SLONG i;
UBYTE ans;
HRESULT res;
DPNAME dpname;
DPSESSIONDESC2 session;
memset(&session, 0, sizeof(session));
session.dwSize = sizeof(session);
session.dwFlags = DPSESSION_CLIENTSERVER | DPSESSION_KEEPALIVE | DPSESSION_OPTIMIZELATENCY | DPSESSION_DIRECTPLAYPROTOCOL;
session.guidApplication = NET_application_guid;
session.dwMaxPlayers = max_players + 1; // Add one because the server counts as a player.
session.lpszSessionNameA = session_name;
res = NET_dp->Open(&session, DPOPEN_CREATE);
if (res == DP_OK)
{
//
// Create a server player.
//
res = NET_dp->CreatePlayer(
&NET_player_server,
NULL, // No player name.
NULL, // No event,
NULL, // No data,
NULL, // No data,
DPPLAYER_SERVERPLAYER);
if (res == DP_OK)
{
return TRUE;
}
else
{
//
// Close the session.
//
NET_dp->Close();
return FALSE;
}
}
else
{
return FALSE;
}
}
//
// All the available sessions
//
typedef struct
{
CBYTE name[NET_MAX_NAME_LENGTH + 8];
SLONG num_players;
SLONG max_players;
GUID guidInstance;
GUID guidApplication;
} NET_Session;
#define NET_MAX_SESSIONS 16
NET_Session NET_session[NET_MAX_SESSIONS];
SLONG NET_session_upto;
//
// The session enumeration function.
//
BOOL FAR PASCAL NET_enum_sessions(
LPCDPSESSIONDESC2 lpThisSD,
LPDWORD lpdwTimeOut,
DWORD dwFlags,
LPVOID lpContext)
{
NET_Session *ns;
if (dwFlags & DPESC_TIMEDOUT)
{
return FALSE;
}
if (!WITHIN(NET_session_upto, 0, NET_MAX_SESSIONS - 1))
{
return FALSE;
}
ns = &NET_session[NET_session_upto];
ns->num_players = lpThisSD->dwCurrentPlayers; // Subtract one because the server counts as a player.
ns->max_players = lpThisSD->dwMaxPlayers - 1; // Subtract one because the server counts as a player.
ns->guidInstance = lpThisSD->guidInstance;
ns->guidApplication = lpThisSD->guidApplication;
sprintf(ns->name, "%s (%d/%d)", lpThisSD->lpszSessionNameA, ns->num_players, ns->max_players);
NET_session_upto += 1;
return TRUE;
}
SLONG NET_session_get_number()
{
DPSESSIONDESC2 desc;
memset(&desc, 0, sizeof(desc));
desc.dwSize = sizeof(desc);
desc.guidApplication = NET_application_guid;
NET_session_upto = 0;
NET_dp->EnumSessions(&desc, 0, NET_enum_sessions, NULL, 0);
return NET_session_upto;
}
NET_Sinfo NET_session_get_info(SLONG session)
{
NET_Sinfo ans;
ASSERT(WITHIN(session, 0, NET_MAX_SESSIONS - 1));
strncpy(ans.name, NET_session[session].name, NET_MAX_NAME_LENGTH - 1);
ans.num_players = NET_session[session].num_players;
ans.max_players = NET_session[session].max_players;
return ans;
}
SLONG NET_session_join(SLONG session)
{
HRESULT res;
DPSESSIONDESC2 desc;
ASSERT(WITHIN(session, 0, NET_MAX_SESSIONS - 1));
//
// Describe the session we want to join.
//
memset(&desc, 0, sizeof(desc));
desc.dwSize = sizeof(desc);
desc.guidInstance = NET_session[session].guidInstance;
res = NET_dp->Open(&desc, DPOPEN_JOIN);
if (res == DP_OK)
{
//
// Create a player.
//
res = NET_dp->CreatePlayer(
&NET_player_local,
NULL, // No name.
NULL, // No event.
NULL, // No data.
NULL, // No data.
0); // No special flags.
if (res == DP_OK)
{
return TRUE;
}
else
{
NET_dp->Close();
return FALSE;
}
}
else
{
return FALSE;
}
}
void NET_session_destroy()
{
NET_dp->Close();
}
void NET_session_leave()
{
NET_dp->Close();
}
void NET_player_message_send(SLONG num_bytes, void *data, SLONG guaranteed)
{
HRESULT res;
UBYTE *u = (UBYTE *) data;
if (u[4] == 4 && num_bytes == 6)
{
u = u + 1;
}
/*
if (!guaranteed)
{
if (rand() & 0x1)
{
return;
}
}
*/
res = NET_dp->Send(NET_player_local, DPID_SERVERPLAYER, (guaranteed) ? DPSEND_GUARANTEED : 0, data, num_bytes);
}
SLONG NET_player_message_receive(SLONG *num_bytes, void **data)
{
tail_recurse:;
ULONG buffer_length = NET_MAX_MESSAGE_LENGTH;
DPID from;
DPID to;
HRESULT res;
res = NET_dp->Receive(&from, &to, 0, (void *) NET_buffer, &buffer_length);
if (res == DPERR_NOMESSAGES)
{
//
// There were no pending messages in the receive queue.
//
return NET_PLAYER_MESSAGE_NONE;
}
ASSERT(res == DP_OK);
if (from == DPID_SERVERPLAYER)
{
//
// This is a message from the server.
//
*num_bytes = buffer_length;
*data = (void *) NET_buffer;
return NET_PLAYER_MESSAGE_FROM_SERVER;
}
if (from == DPID_SYSMSG)
{
//
// We have got a system message.
//
DPMSG_GENERIC *sysmsg = (DPMSG_GENERIC *) NET_buffer;
if (sysmsg->dwType == DPSYS_SESSIONLOST)
{
return NET_PLAYER_MESSAGE_LOST_CONNECTION;
}
else
{
//
// Ignore all other system messages?
//
goto tail_recurse;
}
}
//
// We've got a message from some random entity!
//
ASSERT(0);
return NET_PLAYER_MESSAGE_NONE;
}
void NET_server_message_to_player(NET_Player player, SLONG num_bytes, void *data, SLONG guaranteed)
{
HRESULT res;
/*
if (!guaranteed)
{
if (rand() & 0x1)
{
return;
}
}
*/
res = NET_dp->Send(NET_player_server, (DPID) player, (guaranteed) ? DPSEND_GUARANTEED : 0, data, num_bytes);
}
SLONG NET_server_message_receive(NET_Player *player, SLONG *num_bytes, void **data)
{
tail_recurse:;
ULONG buffer_length = NET_MAX_MESSAGE_LENGTH;
DPID from;
DPID to;
HRESULT res;
res = NET_dp->Receive(&from, &to, 0, (void *) NET_buffer, &buffer_length);
if (res == DPERR_NOMESSAGES)
{
//
// There were no pending messages in the receive queue.
//
return NET_SERVER_MESSAGE_NONE;
}
ASSERT(res == DP_OK);
if (from == DPID_SYSMSG)
{
//
// We have got a system message.
//
DPMSG_GENERIC *sysmsg = (DPMSG_GENERIC *) NET_buffer;
switch(sysmsg->dwType)
{
case DPSYS_SESSIONLOST:
return NET_SERVER_MESSAGE_LOST_CONNECTION;
case DPSYS_CREATEPLAYERORGROUP:
{
DPMSG_CREATEPLAYERORGROUP *cpmsg = (DPMSG_CREATEPLAYERORGROUP *) sysmsg;
//
// We don't support groups...
//
ASSERT(cpmsg->dwPlayerType == DPPLAYERTYPE_PLAYER);
*player = (SLONG) cpmsg->dpId;
return NET_SERVER_MESSAGE_PLAYER_JOINED;
}
case DPSYS_DESTROYPLAYERORGROUP:
{
DPMSG_DESTROYPLAYERORGROUP *dpmsg = (DPMSG_DESTROYPLAYERORGROUP *) sysmsg;
//
// We don't support groups...
//
ASSERT(dpmsg->dwPlayerType == DPPLAYERTYPE_PLAYER);
*player = (SLONG) dpmsg->dpId;
return NET_SERVER_MESSAGE_PLAYER_LEFT;
}
default:
//
// Ignore all other system messages?
//
goto tail_recurse;
}
}
else
{
//
// This is a message from a player.
//
*player = (SLONG) from;
*num_bytes = buffer_length;
*data = (void *) NET_buffer;
return NET_SERVER_MESSAGE_FROM_PLAYER;
}
//
// We've got a message from some random entity!
//
ASSERT(0);
return NET_SERVER_MESSAGE_NONE;
}