SimplePeer でパソコン通信

SDK のサンプルプログラム(SimplePeer) をコンパイルしてパソコン通信を行います。
このプログラムは Windows XP & Visual C++ Ver 6.0 & DirectX 9.0 で作成しています。
正直に言ってパソコン通信のプログラムを DirectX で組むよりは、Windows で直接ソケットを使って組む方がわかり易いかも知れません。 (^_^;)

前田稔(Maeda Minoru)の超初心者のプログラム入門

SimplePeer プロジェクトの作成

  1. SDK のサンプルプログラムをフォルダーごとコピーしてきて HOST となる SimplePeer を作成します。
    DirectX9 をインストールしたときに格納されるサンプルプログラムをフォルダーごと別の場所にコピーして下さい。
    C:\DXSDK\Samples\C++\DirectPlay\SimplePeer
  2. simplepeer.dsp をダブルクリックしてVCを立ち上げて下さい。
    FileView のタブをクリックしてプロジェクトのファイル一覧を表示します。
  3. FileView から Common のフォルダーを開いて次のファイルを削除して下さい。
    これらのファイルは SDK の Common からリンクされているのでフォルダーを移すと使えなくなります。
    Common のフォルダーを選択して Delete キーでまとめて削除することもできます。
    dxutil.cpp
    dxutil.h
    NetConnect.cpp
    NetConnect.h
    netconnectres.h
    SessionInfo.cpp
    SessionInfo.h
  4. 次のフォルダーから削除したファイルをプロジェクトのフォルダーにコピーしてきて下さい。
    [プロジェクト][既存項目の追加] からコピーしてきたファイルをプロジェクトに追加します。
    *.h は次のフォルダーに格納されています。
    C:\DXSDK\Samples\C++\Common\Include
    *.cpp は次のフォルダーに格納されています。
    C:\DXSDK\Samples\C++\Common\Src
  5. directx.ico をプロジェクトのフォルダーにコピーしてきて下さい。
    C:\DXSDK\Samples\C++\Common\directx.ico
  6. simplepeer.rc のアイコン定義をエディタなどで修正して下さい。
    この方法とは別に、リソースからアイコンを削除して、新たにインポートすることもできます。
        /////////////////////////////////////////////////////////////////////////////
        //
        // Icon
        //
    
        // Icon with lowest ID value placed first to ensure application icon
        // remains consistent on all systems.
        IDI_MAIN                ICON    DISCARDABLE     "..\\..\\Common\\directx.ico"
        #endif    // English (U.S.) resources
        
    次のように修正します。
        IDI_MAIN                ICON    DISCARDABLE     "directx.ico"
        
  7. プロジェクトをビルドして下さい。

プロジェクトの実行方法です

  1. ネットワークの接続にはIPアドレスが必要になります。
    IPCONFIG であなたが使っているパソコンの IP アドレスを調べることができます。
    今回は simplepeer.exe を起動すると、IPアドレスが画面に表示されます。
    画面下部に表示されている 169.254.45.41 が IP アドレスです。
    複数のパソコンがネットで接続されていれば申し分ないのですが、一台だけでもテストはできます。
  2. 作成された実行形式のプログラム simplepeer.exe をダブルクリックで起動します。
    そのまま[OK]ボタンをクリックします。
  3. Create ボタンをクリックして新規セションを開設します。
  4. Create Game 画面が表示されます。
    そのまま[OK]ボタンをクリックします。
  5. しばらくすると HOST が起動されて待機状態に入ります。
    [Info] ボタンをクリックするとステータスが表示されます。
  6. クライアントを起動してホストに接続します。
    もう一度 simplepeer.exe をダブルクリックで起動します。
    そのまま[OK]ボタンをクリックします。
  7. Start Search ボタンをクリックすると IP アドレス設定の画面が表示されます。
  8. IP アドレスをタイプして[OK]をクリックすると接続先が検索されみつかれば[Join]が有効になります。
  9. Join ボタンをクリックしてセションに参加します。
    Wave to other players! ボタンをクリックすると相手方のウインドウにメッセージが送信されます。
  10. Exit ボタンをクリックしてウインドウを閉じて下さい。

Simplepeer の Message 転送

SimplePeer で送信・受信される Message 転送の詳細を説明します。

☆Message 転送に使用される構造体

  1. GAMEMSG_GENERIC の定義。
    送信するデータの構造体です。
    Simplepeer で送信するのは dwType だけで?、画面に表示されているのは付属として送られる プレーヤーのコンテキスト情報です。
        struct GAMEMSG_GENERIC
        {
            DWORD dwType;
        };
        
  2. APP_PLAYER_INFO の定義。
    プレーヤーの情報を格納する構造体です。
    strPlayerName が画面に表示されます。
        struct APP_PLAYER_INFO
        {
            LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
                                                    // are done w/ this object
            DPNID dpnidPlayer;                      // DPNID of player
            TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
        };
        
  3. GAME_MSGID_WAVE の定義。
    dwType に格納する識別 ID です。
        #define GAME_MSGID_WAVE        1
        


☆Message の送信

  1. 送信ボタンのクリックで呼ばれる CALLBACK 関数。
    実際の Message 送信処理は WaveToAllPlayers() 関数で行っています。
            case IDC_WAVE:
                if( FAILED( g_hrDialog = WaveToAllPlayers() ) )
                {
                    DXTRACE_ERR_MSGBOX( TEXT("WaveToAllPlayers"), g_hrDialog );
                    EndDialog( hDlg, 0 );
                }
                return TRUE;
        
  2. WaveToAllPlayers() 関数の記述。
    dwType に GAME_MSGID_WAVE(Message を識別する ID)を設定します。
    DPN_BUFFER_DESC 構造体には dwBufferSize と pBufferData だけが含まれています。(DirectX8 186P)
    pBufferData に GAMEMSG_GENERIC のポインタを設定して g_pDP->SendTo() で送信します。
    SendTo() の詳細は DirectX9 のドキュメントを起動して SendTo メソッド(IDirectPlay8Peer) を参照して下さい。
        HRESULT WaveToAllPlayers()
        {
            // This is called by the dialog UI thread.  This will send a message to all
            // the players or inform the player that there is no one to wave at.
            if( g_lNumberOfActivePlayers == 1 )
            {
                MessageBox( NULL, TEXT("No one is around to wave at! :("), 
                            TEXT("SimplePeer"), MB_OK );
            }
            else
            {
                // Send a message to all of the players
                GAMEMSG_GENERIC msgWave;
                msgWave.dwType = GAME_MSGID_WAVE;
                DPN_BUFFER_DESC bufferDesc;
                bufferDesc.dwBufferSize = sizeof(GAMEMSG_GENERIC);
                bufferDesc.pBufferData  = (BYTE*) &msgWave;
                DPNHANDLE hAsync;
                g_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
                               0, NULL, &hAsync, DPNSEND_NOLOOPBACK | DPNSEND_GUARANTEED );
            }
            return S_OK;
        }
        


☆Message の受信

  1. DirectPlayMessageHandler() の記述。
    Message の受信は DPN_MSGID_RECEIVE: で通知され、PDPNMSG_RECEIVE 構造体で渡されます。(DirectX8 193P)
    pReceiveMsg->pvPlayerContext でプレーヤーのコンテキストにアクセスします。
    pReceiveMsg->pReceiveData で送信データの構造体にアクセスします。
    pPlayerInfo->dpnidPlayer を引数として WM_APP_DISPLAY_WAVE で DialogBox の CALLBACK を呼びます。
            case DPN_MSGID_RECEIVE:
            {
                PDPNMSG_RECEIVE pReceiveMsg;
                pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
                APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
                if( NULL == pPlayerInfo )   break;
                // Validate incoming data: A malicious user could modify or create an application
                // to send bogus information; to help guard against logical errors and denial 
                // of service attacks, the size of incoming data should be checked against what
                // is expected.
                if( pReceiveMsg->dwReceiveDataSize < sizeof(GAMEMSG_GENERIC) )    break;
                GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
                if( pMsg->dwType == GAME_MSGID_WAVE )
                {
                    // This message is sent when a player has waved to us, so 
                    // post a message to the dialog thread to update the UI.  
                    // This keeps the DirectPlay threads from blocking, and also
                    // serializes the recieves since DirectPlayMessageHandler can
                    // be called simultaneously from a pool of DirectPlay threads.
                    PostMessage( g_hDlg, WM_APP_DISPLAY_WAVE, pPlayerInfo->dpnidPlayer, 0 );
                }
                break;
            }
        
  2. WM_APP_DISPLAY_WAVE: の記述。
    GetPlayerContext() で送信したプレーヤーの構造体を取得します。
    pPlayerInfo->strPlayerName が画面に表示するプレーヤーの名前です。
            case WM_APP_DISPLAY_WAVE:
            {
                HRESULT          hr;
                DPNID            dpnidPlayer = (DWORD)wParam;
                APP_PLAYER_INFO* pPlayerInfo = NULL;
                
                PLAYER_LOCK(); // enter player context CS
                // Get the player context accosicated with this DPNID
                hr = g_pDP->GetPlayerContext( dpnidPlayer, 
                                              (LPVOID* const) &pPlayerInfo, 0);
                PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
                PLAYER_UNLOCK(); // leave player context CS
                if( FAILED(hr) || pPlayerInfo == NULL )
                {
                    // The player who sent this may have gone away before this 
                    // message was handled, so just ignore it
                    break;
                }
                // Make wave message and display it.
                TCHAR szWaveMessage[MAX_PATH];
                _sntprintf( szWaveMessage, MAX_PATH-1, TEXT("%s just waved at you, %s!\r\n"), 
                            pPlayerInfo->strPlayerName, g_strLocalPlayerName );
                szWaveMessage[ MAX_PATH-1 ] = 0;
                PLAYER_LOCK();
                PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
                PLAYER_UNLOCK();
                AppendTextToEditControl( hDlg, szWaveMessage );
                break;
            }
        

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)