/* Note - does not appear to like DPSESSION_MULTICASTSERVER very much! */ #include "std.h" #include "multiplay.h" #include "multiplay_setup.h" struct Player; static bool host; static map player_map; static list players,new_players; static int msg_type; static string msg_data; static DPID msg_from,msg_to; static char *recv_buff; static int recv_buff_sz; static char *send_buff; static int send_buff_sz; #pragma pack( push,1 ) struct bbMsg{ DPID from,to; char type; }; #pragma pack( pop ) struct Player{ DPID id; string name; bool remote; Player( DPID i,const string &n,bool r ):id(i),name(n),remote(r){ players.push_back( this ); if( remote ) new_players.push_back( this ); player_map.clear(); } Player::~Player(){ new_players.remove( this ); players.remove( this ); player_map.clear(); } }; static void chk(){ if( !dirPlay ){ RTEX( "Multiplayer game not started" ); } } static void clearPlayers(){ while( players.size() ) delete players.back(); new_players.clear(); player_map.clear(); } static Player *findPlayer( DPID id ){ if( !player_map.size() ){ list::iterator it; for( it=players.begin();it!=players.end();++it ){ player_map.insert( pair( (*it)->id,(*it) ) ); } } map::iterator it=player_map.find( id ); return it==player_map.end() ? 0 : it->second; } static BOOL FAR PASCAL enumPlayer( DPID id,DWORD type,LPCDPNAME name,DWORD flags,LPVOID context ){ Player *p=findPlayer( id );if( p ) return TRUE; p=d_new Player( id,string( name->lpszShortNameA ),true ); return TRUE; } void multiplay_link( void(*rtSym)(const char*,void*) ){ rtSym( "%StartNetGame",bbStartNetGame ); rtSym( "%HostNetGame$game_name",bbHostNetGame ); rtSym( "%JoinNetGame$game_name$ip_address",bbJoinNetGame ); rtSym( "StopNetGame",bbStopNetGame ); rtSym( "%CreateNetPlayer$name",bbCreateNetPlayer ); rtSym( "DeleteNetPlayer%player",bbDeleteNetPlayer ); rtSym( "$NetPlayerName%player",bbNetPlayerName ); rtSym( "%NetPlayerLocal%player",bbNetPlayerLocal ); rtSym( "%SendNetMsg%type$msg%from_player%to_player=0%reliable=1",bbSendNetMsg ); rtSym( "%RecvNetMsg",bbRecvNetMsg ); rtSym( "%NetMsgType",bbNetMsgType ); rtSym( "%NetMsgFrom",bbNetMsgFrom ); rtSym( "%NetMsgTo",bbNetMsgTo ); rtSym( "$NetMsgData",bbNetMsgData ); } bool multiplay_create(){ recv_buff_sz=send_buff_sz=1024; recv_buff=d_new char[recv_buff_sz]; send_buff=d_new char[send_buff_sz]; multiplay_setup_create(); return true; } bool multiplay_destroy(){ bbStopNetGame(); multiplay_setup_destroy(); delete[] recv_buff;recv_buff=0; delete[] send_buff;send_buff=0; return true; } static int startGame( int n ){ clearPlayers(); if( !n ) return 0; if( dirPlay->EnumPlayers( 0,enumPlayer,0,0 )>=0 ){ host=n==2; return n; } multiplay_setup_close(); return 0; } int bbStartNetGame(){ if( dirPlay ){ RTEX( "Multiplayer game already started" ); } return startGame( multiplay_setup_open() ); } int bbHostNetGame( BBStr *name ){ if( dirPlay ){ RTEX( "Multiplayer game already started" ); } string n=*name;delete name; return startGame( multiplay_setup_host( n ) ); } int bbJoinNetGame( BBStr *name,BBStr *address ){ if( dirPlay ){ RTEX( "Multiplayer game already started" ); } string n=*name,a=*address;delete name;delete address; return startGame( multiplay_setup_join( n,a ) ); } void bbStopNetGame(){ multiplay_setup_close(); clearPlayers(); } DPID bbCreateNetPlayer( BBStr *nm ){ chk(); string t=*nm; string t0=t+'\0'; delete nm; DPID id; DPNAME name; memset( &name,0,sizeof( name ) ); name.dwSize=sizeof(name);name.lpszShortNameA=(char*)t0.data(); if( dirPlay->CreatePlayer( &id,&name,0,0,0,0 )<0 ) return 0; Player *p=d_new Player( id,t,false ); if( players.size()==1 ){ if( dirPlay->EnumPlayers( 0,enumPlayer,0,0 )<0 ){ dirPlay->DestroyPlayer( id ); delete p; return 0; } } return id; } void bbDeleteNetPlayer( DPID player ){ chk(); if( Player *p=findPlayer( player ) ){ dirPlay->DestroyPlayer( player ); delete p; } } BBStr *bbNetPlayerName( DPID player ){ if( !player ) return d_new BBStr( "" ); Player *p=findPlayer( player ); return d_new BBStr( p ? p->name : "" ); } int bbNetPlayerLocal( DPID player ){ if( Player *p=findPlayer( player ) ) return p->remote ? 0 : 1; return 0; } int bbRecvNetMsg(){ chk(); msg_type=0; msg_data.resize(0); msg_from=DPID_UNKNOWN;msg_to=DPID_ALLPLAYERS; while( !msg_type ){ if( new_players.size() ){ msg_from=new_players.front()->id; new_players.pop_front(); msg_type=100; return 1; } DPID from,to; DWORD sz=recv_buff_sz; int n=dirPlay->Receive( &from,&to,0,recv_buff,&sz ); if( n==DPERR_BUFFERTOOSMALL ){ sz=recv_buff_sz=sz/2+sz; delete[] recv_buff;recv_buff=d_new char[recv_buff_sz]; n=dirPlay->Receive( &from,&to,0,recv_buff,&sz ); } if( n!=DP_OK ) return 0; if( from==DPID_SYSMSG ){ switch( *(DWORD*)recv_buff ){ case DPSYS_CREATEPLAYERORGROUP: if( DPMSG_CREATEPLAYERORGROUP *msg=(DPMSG_CREATEPLAYERORGROUP*)recv_buff ){ if( findPlayer( from=msg->dpId ) ) continue; d_new Player( from,string( msg->dpnName.lpszShortNameA ),true ); continue; } break; case DPSYS_DESTROYPLAYERORGROUP: if( DPMSG_DESTROYPLAYERORGROUP *msg=(DPMSG_DESTROYPLAYERORGROUP*)recv_buff ){ Player *p=findPlayer( msg->dpId );if( !p ) continue; delete p;msg_from=msg->dpId;msg_type=101; } break; case DPSYS_HOST: if( !host ){ host=true;msg_type=102; } break; case DPSYS_SESSIONLOST: msg_type=200; break; } }else{ bbMsg *m=(bbMsg*)recv_buff; Player *p=findPlayer( m->from ); if( p && !p->remote ) continue; msg_data=string( (char*)(m+1),sz-sizeof(bbMsg) ); msg_from=m->from;msg_to=m->to; msg_type=m->type; } } return 1; } int bbNetMsgType(){ return msg_type; } BBStr *bbNetMsgData(){ return d_new BBStr( msg_data ); } DPID bbNetMsgFrom(){ return msg_from; } DPID bbNetMsgTo(){ return msg_to; } int bbSendNetMsg( int type,BBStr *msg,DPID from,DPID to,int reliable ){ chk(); int sz=msg->size()+sizeof(bbMsg); if( sz>send_buff_sz ){ send_buff_sz=sz/2+sz; delete send_buff;send_buff=d_new char[send_buff_sz]; } bbMsg *m=(bbMsg*)send_buff; m->type=type;m->from=from;m->to=to; memcpy( m+1,msg->data(),msg->size() ); if( !to ) to=DPID_ALLPLAYERS; int n=dirPlay->Send( from,to,reliable ? DPSEND_GUARANTEED : 0,send_buff,sz ); delete msg; return n>=0; }