DIRECTX1 DirectPlay 4 サービスプロバイダを列挙してコネクションを張る

今まではボタン1個でやってきましたが、ここでサービスプロバイダを選択しなければいけません。すると、選択した次には別のボタンが必要になります。ボタンを一つ増やしてください。このボタンのハンドルから以下のようにメンバ関数を呼びます。
 

void CDxp3View::OnButton3()
{
 // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください

 // 選択したコネクションに接続する
 if(dxp.DxInitConnection(&m_List1) != DP_OK){
   return;
 }
}
 

ここで呼ばれているのが、選択したプロバイダとコネクションを張るメンバ関数です。
 

HRESULT CDxPlay::DxInitConnection(CListBox* listbox)
{
 // int posi = listbox->GetSel(0); // 削除
 for(int posi=0; posi<listbox->GetCount(); posi++) // 修正2000-3-6
  if(listbox->GetSel(posi) >0)
   break;
 
 if(posi >=0){
  // 選択されていたのでコネクションを張る
   HRESULT ret = lpDP4A->InitializeConnection(connections.lpConnections[posi], 0);
  if(ret != DP_OK){
    if(ret == DPERR_ALREADYINITIALIZED){
        AfxMessageBox("すでにコネクションを張っている");
        return FALSE;
    }
    AfxMessageBox("コネクションを張れなかった");
    return FALSE;
   }
   AfxMessageBox("コネクションを張った");
   return DP_OK;
  }
  return FALSE;
}
 

ビルド実行で、コネクションが張れたメッセージが出ればOK。

コネクションを張った後は、セッションに入りますが、この時に新しいセッションを作るか、誰かのセッションに参加するか選択する必要があります。まず、新しいセッションを作成する方からやってみます。ここで、ボタン2にコネクションを張ってすぐセッションを作成するように呼び出しをしましょう。呼び出しは以下の通り。ついでにその後のプレイヤの作成もついでにやってしまいます。
 

void CDxp3View::OnButton2()
{
 // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
 
 // 選択したコネクションに接続する
 if(dxp.DxInitConnection(&m_List1) != DP_OK){
  return;
 }
 
 // セッションを 作成
 if(dxp.DxCreateSession() != DP_OK){
  return;
 }
 
 // プレイヤを作成する
 if(dxp.MakePlayer() != DP_OK) return;
 
}
 

制作する新しいメンバ関数は、DxCreateSessionと、MakePlayerです。それぞれ以下の通り。赤部分に注意。
 

HRESULT CDxPlay::DxCreateSession()
{
HRESULT ret;

DPSESSIONDESC2 sessionDESC;
static const GUID myAppricationGUID=
// {52C07B2F-EF73-11d3-9566-004026174071}
//IMPLEMENT_OLECREATE(<<class>>, <<external_name>>,
{0x52c07b2f, 0xef73, 0x11d3,{ 0x95, 0x66, 0x0, 0x40, 0x26, 0x17, 0x40, 0x71}};
 
char * mySessionName = "MyNetGame";
//DSPSESSIONDESC2構造体の内容を0に初期化する
memset(&sessionDESC, 0, sizeof(DPSESSIONDESC2));
//DSPSESSION2構造体のメンバを設定する
sessionDESC.dwSize = sizeof(DPSESSIONDESC2);
sessionDESC.dwFlags = DPSESSION_MIGRATEHOST | DPSESSION_KEEPALIVE;
sessionDESC.guidApplication = myAppricationGUID;
sessionDESC.dwMaxPlayers = 100;
sessionDESC.lpszSessionName = (unsigned short *) mySessionName;
// Openメソッドを呼び出してセッションを作る
ret = lpDP4A->Open(&sessionDESC,DPOPEN_CREATE);
if(ret != DP_OK){
 AfxMessageBox("セッションのクリエイトに失敗しました");
 return FALSE;
}
AfxMessageBox("セッションのクリエイトに成功しました");
return DP_OK
 
HRESULT CDxPlay::MakePlayer()
{
HRESULT ret;
 
// プレイヤの名前を準備する
memset(&dpPlayerName, 0, sizeof(DPNAME));
dpPlayerName.dwSize = sizeof(DPNAME);
dpPlayerName.dwFlags = 0;
 
dpPlayerName.lpszShortNameA = playername;
dpPlayerName.lpszLongName = NULL;
 
// プレイヤを作る
ret = lpDP4A->CreatePlayer(&playerDPID, &dpPlayerName, hEvent, NULL, 0,0);
if(ret != DP_OK){
 AfxMessageBox("プレイヤの作成に失敗した");
 return FALSE;
}
AfxMessageBox("プレイヤの作成に成功した");
return TRUE;
}
 

プレイヤの作成まで成功しましかた?ここまで来るとすでにネットに接続している状態です。別なプレイヤがセッションに参加することが出来ます。しかし、このままでは、セッションに参加することができないので、セッションに参加するための関数とボタンをつくリます。

混乱しそうなので説明しておきますが、ここで言う別なプレイヤというのは、このプログラムの別な実行者の事です。ネット上で別な人が同じプログラムを立ち上げている人がセッションに参加することが出来るわけです。ということは、デバッグで他にもう一つ同じプログラムを立ち上げる必要があるということです。今は一つのコンピュータ上の同じデスクトップ上でかまいません。

現在の状態で、起動すると、自分一人だけのセッションが作成されています。ホストの状態ですね。実際にはピアツーピアで接続されているのでホストでは無いのですが、セッションを作ったので一応ホストとしておきましょう。

次にもう一つ、同じプログラムを立ち上げた場合、それは既存のセッションを探して接続するわけです。そうすると始めに立ち上げたプログラムと後から立ち上げたプログラムが通信できる状態になります。

ボタンは一つ追加して、これはボタン3になりますが、これはセッションに参加するためのボタンです。このハンドルから呼ぶのは以下の通り。
 

// 選択したコネクションに接続する
if(dxp.DxInitConnection(&m_List1) != DP_OK){
 return;
}
 
// セッションを選択 
if(dxp.DxSelectSession(&m_List1) != DP_OK){
 return;
}
 

セッションの新規作成に比較すると、プレイヤの作成がありません。プレイヤの作成は、セッションを選択してからになります。さらにプレイヤの作成ボタンを作ります。ではまず、セッションの選択でセッションの列挙を作ります。

この段階までをビルドして実行すると、セッションの一覧がでます。というか、でません。どっちなんだ?

始めにEXEファイルを直接起動してセッションを作成しておかないと、セッションが無いわけだから表示もされません。始めにEXEを実行してセッションを作成し、デバッガで実行してみてください。ちゃんとできていれば、MyNetGameという名前が表示されるはずです。それが表示されたらここまではちゃんと動いています。つぎはそれを選択してコネクションをはり、プレイヤを作ります。

プロバイダと同じように列挙するわけですが、まず、列挙に使用する構造体を宣言します。これはCDxPlay.hのConnection構造体と同じ場所に宣言しましょう。そしてその構造体のオブジェクトは、CDxPlayクラスの中に作ります。

Sessions sessions; という感じのCDxplayクラスのメンバ変数にします。

ボタンのハンドルから呼ぶメンバ関数は以下の通り。選択したコネクションを張るところからスタートですね。
 

//----- セッションを列挙する
void CDxptestView::OnButton3()
{
 // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください

 // コネクションを張る
 dxp.DxInitConnection(&m_List1);

 
 // セッションを選択 
 if(dxp.DxEnumSession(&m_List1) != DP_OK){
   return;
 }

}
 

DxEnumSessionは、新しいメンバ関数です。EnumSessionを呼び出すためのメンバ関数です。直接EnumSessionを呼び出しても良いのですが、なるべくクラスに入れる方針なのでこれも作ります。
 

//===== 既存のセッションに参加する場合 =========================
HRESULT CDxPlay::DxEnumSession(CListBox* list)
{
HRESULT ret;
 
list->ResetContent();
 
// セッションの列挙
DPSESSIONDESC2 sessionDESC;
static const GUID myAppricationGUID=
// {52C07B2F-EF73-11d3-9566-004026174071}
//IMPLEMENT_OLECREATE(<<class>>, <<external_name>>,
{0x52c07b2f, 0xef73, 0x11d3,{ 0x95, 0x66, 0x0, 0x40, 0x26, 0x17, 0x40, 0x71}};
 
//DSPSESSIONDESC2構造体の内容を0に初期化する
memset(&sessionDESC, 0, sizeof(DPSESSIONDESC2));

//DSPSESSION2構造体のメンバを設定する
sessionDESC.dwSize = sizeof(DPSESSIONDESC2);
sessionDESC.guidApplication = myAppricationGUID;
 
sessions.lpSessionDescs = NULL;
sessions.lpszSessionNames = NULL;
sessions.numSessions = 0;
sessions.list = list;
 
ret = lpDP4A->EnumSessions( &sessionDESC,
0,
EnumSessionCallback,
&sessions,
DPENUMSESSIONS_ALL);
 
if(ret != DP_OK){
 AfxMessageBox("セッションの列挙に失敗しました");
 return FALSE;
}
AfxMessageBox("セッションの列挙に成功しました");
return DP_OK;
}
//=========================================================
 

次にEnumSessionCallBack関数を作ります。これもDirectPlayEnumCBackと同じようにグローバル関数として宣言します。DirectPlayEnumCBackと同じ場所に作りましょう。
 

//===== セッション列挙用コールバック関数 =================
BOOL CALLBACK EnumSessionCallback(LPCDPSESSIONDESC2 lpThisSD,
LPDWORD lpdwTimeOut,
DWORD dwFlags,
LPVOID lpContext)
{
Sessions *sessions;
int num;
 
sessions = (Sessions*) lpContext;
num = sessions->numSessions;
// タイムアウトかどうか確認
if(dwFlags & DPESC_TIMEDOUT){
  AfxMessageBox("TimeOut");
  return FALSE;
}
 
// セッション情報を保存する
sessions->lpSessionDescs=(LPDPSESSIONDESC2*)
realloc(sessions->lpSessionDescs,
sizeof(LPDPSESSIONDESC2) * (num +1));
 
sessions->lpSessionDescs[num]=(LPDPSESSIONDESC2)
malloc(sizeof(DPSESSIONDESC2));
memcpy(sessions->lpSessionDescs[num],lpThisSD,sizeof(DPSESSIONDESC2));
 
// セッション名を保存する
sessions->lpszSessionNames=(LPWSTR*)realloc(sessions->lpszSessionNames,
sizeof(LPWSTR)*(num+1));
sessions->lpszSessionNames[num]=
(LPWSTR)malloc((wcslen(lpThisSD->lpszSessionName)
+1)*sizeof(wchar_t)+2);
memcpy(sessions->lpszSessionNames[num],lpThisSD->lpszSessionName,
(wcslen(lpThisSD->lpszSessionName+1))
* sizeof(wchar_t)+2);
 
// ListBoxに表示する
// sessions->list->AddString("test ok ??");
sessions->list->AddString((char *)sessions->lpszSessionNames[num]);
 
num ++;
sessions->numSessions = num;
return TRUE;
}
//===============================================================
 

これでセッションを列挙することが出来ました。うまく行くとリストボックスにMyNetGameというセッションの名前が出てきます。セッション名は固定になっていますが、実際に作るときにはセッション名をユーザが決められるようにしたほうが楽しいですね。他にもセッションがあれば2個以上のセッションが表示されます。これを選択して次はそのセッションに入らなければなりません。そこでさらにボタンを追加します。選択したセッションに参加するためのボタンです。
 

void CDxptestView::OnButton4()
{
 // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
 
 dxp.DxJointSession();
 
 // プレイヤを作成する
 if(dxp.MakePlayer() != DP_OK) return;
}
 

ボタン4から呼ぶDxJoinSession関数の中身は以下の通り。
 
 
HRESULT CDxPlay::DxJointSession()
{
HRESULT ret;
// 指定したセッションに参加する
if(sessions.numSessions > 0){
 ret = lpDP4A->Open( sessions.lpSessionDescs[0],
 DPOPEN_JOIN);
 
 if(ret != DP_OK){
  AfxMessageBox("セッションに参加できなかった");
   return FALSE;
 }
} else {
 AfxMessageBox("セッションが選択されていないか、無い\n選択");
 return FALSE;
}
return DP_OK;
}
 

これでコーディングは全部です。ビルドして実行してみましょう。始めに立ち上げた方でセッションを作成してから、他で立ち上げた方でセッションを選択します。途中でダイアルアップダイアログが出ますが、キャンセルしてください。プレイヤが作成できたらOKです。