本篇是创建游戏内核(23)【OO改良版】的续篇,关于该内核的细节说明请参阅创建游戏内核(24)。
接口:
//====================================================================================
// This class encapsulate server how to communicate with client.
//====================================================================================
typedef class NETWORK_SERVER
{
public:
NETWORK_SERVER();
virtual ~NETWORK_SERVER();
IDirectPlay8Server* get_server();
BOOL init();
BOOL shutdown();
BOOL host(const GUID* guid_adapter, long port,
const char* session_name, const char* session_pwd,
long max_players);
BOOL disconnect();
BOOL is_connected();
BOOL send_data(DPNID player_id, void* data, ulong size, ulong flags);
BOOL send_text(DPNID player_id, char* text, ulong flags);
BOOL disconnect_player(long player_id);
BOOL get_ip(char* player_ip, ulong player_id);
BOOL get_player_name(char* player_name, ulong player_id);
long get_port();
BOOL get_session_name(char* session_name);
BOOL get_session_pwd(char* session_pwd);
long get_max_players();
long get_cur_players_num();
static HRESULT WINAPI network_msg_handler(PVOID user_context, DWORD msg_id, PVOID msg_buffer);
protected:
virtual BOOL _add_player_to_group(DPNMSG_ADD_PLAYER_TO_GROUP* msg) { return TRUE; }
virtual BOOL _async_op_complete(DPNMSG_ASYNC_OP_COMPLETE* msg) { return TRUE; }
virtual BOOL _client_info(DPNMSG_CLIENT_INFO* msg) { return TRUE; }
virtual BOOL _connect_complete(DPNMSG_CONNECT_COMPLETE* msg) { return TRUE; }
virtual BOOL _create_group(DPNMSG_CREATE_GROUP* msg) { return TRUE; }
virtual BOOL _create_player(DPNMSG_CREATE_PLAYER* msg) { return TRUE; }
virtual BOOL _destroy_group(DPNMSG_DESTROY_GROUP* msg) { return TRUE; }
virtual BOOL _destroy_player(DPNMSG_DESTROY_PLAYER* msg) { return TRUE; }
virtual BOOL _enum_hosts_query(DPNMSG_ENUM_HOSTS_QUERY* msg) { return TRUE; }
virtual BOOL _enum_hosts_response(DPNMSG_ENUM_HOSTS_RESPONSE* msg) { return TRUE; }
virtual BOOL _group_info(DPNMSG_GROUP_INFO* msg) { return TRUE; }
virtual BOOL _host_migrate(DPNMSG_HOST_MIGRATE* msg) { return TRUE; }
virtual BOOL _indicate_connect(DPNMSG_INDICATE_CONNECT* msg) { return TRUE; }
virtual BOOL _indicated_connect_aborted(DPNMSG_INDICATED_CONNECT_ABORTED* msg) { return TRUE; }
virtual BOOL _peer_info(DPNMSG_PEER_INFO* msg) { return TRUE; }
virtual BOOL _receive(DPNMSG_RECEIVE* msg) { return TRUE; }
virtual BOOL _remove_player_from_group(DPNMSG_REMOVE_PLAYER_FROM_GROUP* msg) { return TRUE; }
virtual BOOL _return_buffer(DPNMSG_RETURN_BUFFER* msg) { return TRUE; }
virtual BOOL _send_complete(DPNMSG_SEND_COMPLETE* msg) { return TRUE; }
virtual BOOL _server_info(DPNMSG_SERVER_INFO* msg) { return TRUE; }
virtual BOOL _terminate_session(DPNMSG_TERMINATE_SESSION* msg) { return TRUE; }
protected:
IDirectPlay8Server* m_server;
BOOL m_is_connected;
long m_port;
char m_session_name[MAX_PATH];
char m_session_pwd[MAX_PATH];
long m_max_players;
long m_cur_players_num;
} *NETWORK_SERVER_PTR;
实现:
//---------------------------------------------------------------------------------
// Constructor, zero member data.
//---------------------------------------------------------------------------------
NETWORK_SERVER::NETWORK_SERVER()
{
memset(this, 0, sizeof(*this) - 4);
}
//---------------------------------------------------------------------------------
// Destructor, shutdown server.
//---------------------------------------------------------------------------------
NETWORK_SERVER::~NETWORK_SERVER()
{
shutdown();
}
//---------------------------------------------------------------------------------
// Shutdown server.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::shutdown()
{
if(! disconnect())
return FALSE;
m_server = NULL;
return TRUE;
}
//---------------------------------------------------------------------------------
// Disconnect server to all clients.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::disconnect()
{
if(m_server)
{
// closes the open connection to a session
if(FAILED(m_server->Close(0)))
return FALSE;
}
m_is_connected = FALSE;
m_port = 0;
m_session_name[0] = NULL;
m_session_pwd[0] = NULL;
m_max_players = 0;
m_cur_players_num = 0;
return TRUE;
}
//---------------------------------------------------------------------------------
// Return pointer to IDirectPlay8Server object.
//---------------------------------------------------------------------------------
IDirectPlay8Server* NETWORK_SERVER::get_server()
{
return m_server;
}
//---------------------------------------------------------------------------------
// Create IDirectPlay8Server object.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::init()
{
// shutdown old server object first
shutdown();
// create the server object
if(FAILED(CoCreateInstance(CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC, IID_IDirectPlay8Server,
(void**) &m_server)))
{
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------------
// Host server.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::host(const GUID* guid_adapter, long port,
const char* session_name, const char* session_pwd,
long max_players)
{
// disconnect from current connection
disconnect();
m_port = port;
// error checking
if(m_server == NULL || session_name == NULL || port == 0)
return FALSE;
// initialize the server object
if(FAILED(m_server->Initialize((PVOID)this, network_msg_handler, 0)))
return FALSE;
IDirectPlay8Address* _dp_address;
// create an address object
if(FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC, IID_IDirectPlay8Address,
(void**) &_dp_address)))
return FALSE;
DPN_APPLICATION_DESC _app_desc;
WCHAR _w_session_name[MAX_PATH], _w_session_pwd[MAX_PATH];
// set protocol
if(FAILED(_dp_address->SetSP(&CLSID_DP8SP_TCPIP)))
goto fail;
// set the port - must not be 0
if(FAILED(_dp_address->AddComponent(DPNA_KEY_PORT, &m_port, sizeof(DWORD), DPNA_DATATYPE_DWORD)))
goto fail;
// set adapter (if any)
if(guid_adapter)
{
if(FAILED(_dp_address->AddComponent(DPNA_KEY_DEVICE, guid_adapter, sizeof(GUID), DPNA_DATATYPE_GUID)))
goto fail;
}
// record the session name and password
strcpy(m_session_name, session_name);
mbstowcs(_w_session_name, session_name, MAX_PATH);
if(session_pwd)
{
strcpy(m_session_pwd, session_pwd);
mbstowcs(_w_session_pwd, session_pwd, MAX_PATH);
}
// setup the application description structure
ZeroMemory(&_app_desc, sizeof(DPN_APPLICATION_DESC));
_app_desc.dwSize = sizeof(DPN_APPLICATION_DESC);
_app_desc.dwFlags = DPNSESSION_CLIENT_SERVER;
_app_desc.guidApplication = g_app_guid;
_app_desc.pwszSessionName = _w_session_name;
if(session_pwd)
{
_app_desc.pwszPassword = _w_session_pwd;
_app_desc.dwFlags |= DPNSESSION_REQUIREPASSWORD;
}
m_max_players = max_players;
_app_desc.dwMaxPlayers = max_players;
// start the hosting
if(FAILED(m_server->Host(&_app_desc, &_dp_address, 1, NULL, NULL, NULL, 0)))
goto fail;
_dp_address->Release();
m_is_connected = TRUE;
return TRUE;
fail:
_dp_address->Release();
return FALSE;
}
//---------------------------------------------------------------------------------
// Check whether server is hosting now.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::is_connected()
{
return m_is_connected;
}
//---------------------------------------------------------------------------------
// Send data to client.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::send_data(DPNID player_id, void* data, ulong size, ulong flags)
{
// error checking
if(m_server == NULL || size == 0)
return FALSE;
DPNHANDLE _async_handle;
DPN_BUFFER_DESC _buf_desc;
_buf_desc.dwBufferSize = size;
_buf_desc.pBufferData = (BYTE*) data;
if(FAILED(m_server->SendTo(player_id, &_buf_desc, 1, 0, NULL, &_async_handle, flags)))
return FALSE;
return TRUE;
}
//---------------------------------------------------------------------------------
// Send string to client.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::send_text(DPNID player_id, char* text, ulong flags)
{
// error checking
if(m_server == NULL || text == NULL)
return FALSE;
return send_data(player_id, text, (ulong)strlen(text)+1, flags);
}
//---------------------------------------------------------------------------------
// Disconnect specified player.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::disconnect_player(long player_id)
{
// error checking
if(m_server == NULL)
return FALSE;
// try to disconnect the specified player
if(FAILED(m_server->DestroyClient(player_id, NULL, 0, 0)))
return FALSE;
return TRUE;
}
//---------------------------------------------------------------------------------
// Get ip address, include server and client ip address.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::get_ip(char* ip, ulong player_id)
{
// get host ip address if player_id == 0
if(player_id == 0)
{
WSADATA _wsa_data;
// initiates use of ws2_32.dll by a process
if(WSAStartup(MAKEWORD(1, 0), &_wsa_data))
return FALSE;
char _local_name[MAX_PATH];
// retrieves the standard host name for the local computer
gethostname(_local_name, MAX_PATH);
// store information about a given host, such as host name, ip address, and so forth.
HOSTENT* _host_ent;
// retrieves host information corresponding to a host name from a host database
if((_host_ent = gethostbyname(_local_name)) == NULL)
{
// terminates use of the ws2_32.dll
WSACleanup();
return FALSE;
}
// get localip
char* _local_ip = inet_ntoa(*(in_addr*)_host_ent->h_addr_list[0]);
WSACleanup();
if(_local_ip == NULL)
return FALSE;
strcpy(ip, _local_ip);
return TRUE;
}
if(m_server == NULL || !m_is_connected)
return FALSE;
IDirectPlay8Address* _dp_address;
// retrieve the player information
if(FAILED(m_server->GetClientAddress((DPNID)player_id, &_dp_address, 0)))
return FALSE;
DWORD _size = MAX_PATH;
DWORD _flags = DPNA_DATATYPE_STRING;
WCHAR _client_address[MAX_PATH];
// retrieves information on the component at the specified key
if(FAILED(_dp_address->GetComponentByName(DPNA_KEY_HOSTNAME, _client_address, &_size, &_flags)))
{
_dp_address->Release();
return FALSE;
}
wcstombs(ip, _client_address, MAX_PATH);
_dp_address->Release();
return TRUE;
}
//---------------------------------------------------------------------------------
// Get player name.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::get_player_name(char* player_name, ulong player_id)
{
if(m_server == NULL)
return FALSE;
DPN_PLAYER_INFO* _player_info = NULL;
DWORD _size = 0;
// retrieves the client information set for the specified client
HRESULT _rv = m_server->GetClientInfo(player_id, _player_info, &_size, 0);
if(FAILED(_rv) && _rv != DPNERR_BUFFERTOOSMALL)
return FALSE;
_player_info = (DPN_PLAYER_INFO*) new char[_size];
_player_info->dwSize = sizeof(DPN_PLAYER_INFO);
if(FAILED(m_server->GetClientInfo(player_id, _player_info, &_size, 0)))
{
delete[] _player_info;
return FALSE;
}
wcstombs(player_name, _player_info->pwszName, MAX_PATH);
delete[] _player_info;
return TRUE;
}
//---------------------------------------------------------------------------------
// Return port of server.
//---------------------------------------------------------------------------------
long NETWORK_SERVER::get_port()
{
return m_port;
}
//---------------------------------------------------------------------------------
// Get session name.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::get_session_name(char* session_name)
{
if(session_name == NULL)
return FALSE;
strcpy(session_name, m_session_name);
return TRUE;
}
//---------------------------------------------------------------------------------
// Get session password.
//---------------------------------------------------------------------------------
BOOL NETWORK_SERVER::get_session_pwd(char* session_pwd)
{
if(session_pwd == NULL)
return FALSE;
strcpy(session_pwd, m_session_pwd);
return TRUE;
}
//---------------------------------------------------------------------------------
// Return max players permitted to login.
//---------------------------------------------------------------------------------
long NETWORK_SERVER::get_max_players()
{
return m_max_players;
}
//---------------------------------------------------------------------------------
// Return current player number.
//---------------------------------------------------------------------------------
long NETWORK_SERVER::get_cur_players_num()
{
return m_cur_players_num;
}
//---------------------------------------------------------------------------------
// Callback function to handler message for server.
//---------------------------------------------------------------------------------
HRESULT WINAPI NETWORK_SERVER::network_msg_handler(PVOID user_context, DWORD msg_id, PVOID msg_buffer)
{
NETWORK_SERVER* _server;
if((_server = (NETWORK_SERVER*) user_context) == NULL)
return E_FAIL;
// pass message to user-defined function
switch(msg_id)
{
case DPN_MSGID_ADD_PLAYER_TO_GROUP:
if(_server->_add_player_to_group((DPNMSG_ADD_PLAYER_TO_GROUP*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_ASYNC_OP_COMPLETE:
if(_server->_async_op_complete((DPNMSG_ASYNC_OP_COMPLETE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_CLIENT_INFO:
if(_server->_client_info((DPNMSG_CLIENT_INFO*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_CONNECT_COMPLETE:
if(_server->_connect_complete((DPNMSG_CONNECT_COMPLETE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_CREATE_GROUP:
if(_server->_create_group((DPNMSG_CREATE_GROUP*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_CREATE_PLAYER:
if(_server->_create_player((DPNMSG_CREATE_PLAYER*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_DESTROY_GROUP:
if(_server->_destroy_group((DPNMSG_DESTROY_GROUP*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_DESTROY_PLAYER:
if(_server->_destroy_player((DPNMSG_DESTROY_PLAYER*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_ENUM_HOSTS_QUERY:
if(_server->_enum_hosts_query((DPNMSG_ENUM_HOSTS_QUERY*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_ENUM_HOSTS_RESPONSE:
if(_server->_enum_hosts_response((DPNMSG_ENUM_HOSTS_RESPONSE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_GROUP_INFO:
if(_server->_group_info((DPNMSG_GROUP_INFO*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_HOST_MIGRATE:
if(_server->_host_migrate((DPNMSG_HOST_MIGRATE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_INDICATE_CONNECT:
if(_server->_indicate_connect((DPNMSG_INDICATE_CONNECT*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_INDICATED_CONNECT_ABORTED:
if(_server->_indicated_connect_aborted((DPNMSG_INDICATED_CONNECT_ABORTED*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_PEER_INFO:
if(_server->_peer_info((DPNMSG_PEER_INFO*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_RECEIVE:
if(_server->_receive((DPNMSG_RECEIVE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_REMOVE_PLAYER_FROM_GROUP:
if(_server->_remove_player_from_group((DPNMSG_REMOVE_PLAYER_FROM_GROUP*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_RETURN_BUFFER:
if(_server->_return_buffer((DPNMSG_RETURN_BUFFER*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_SEND_COMPLETE:
if(_server->_send_complete((DPNMSG_SEND_COMPLETE*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_SERVER_INFO:
if(_server->_server_info((DPNMSG_SERVER_INFO*) msg_buffer))
return S_OK;
break;
case DPN_MSGID_TERMINATE_SESSION:
if(_server->_terminate_session((DPNMSG_TERMINATE_SESSION*) msg_buffer))
return S_OK;
break;
}
return E_FAIL;
}