見出し画像

Steamでネット対戦を作る


自作ゲームのネット対戦をSteam側の機能でできるようになったのでまとめていきます、情報少なかったISteamNetworkingMessagesだけは書きたい
色々間違っていそうですが、Steamのネットワーク機能を使いたいときに参考になるかもしれません
気が向いた時に更新していきます

↓自作ゲーム

環境: VisualStudio2019/C++/DXライブラリ使用

参考サイト
・Steamworksのドキュメント
https://partner.steamgames.com/doc/features/multiplayer

・ISteamNetworkingMessagesの使い方
https://habr.com/ru/articles/597783/


テスト時の機能いろいろ

ユーザーの追加

開発チームにユーザーを追加しておけば、
チームが出しているゲームが無料でインストールできる
筆者はサブアカを作って2台のPCでテストしていた

サブアカや知り合いを追加してテストプレイ

ベータ版機能

ビルドの【新しいアプリブランチを作成】から
ベータ版を設定することができる
発売前のゲームでできるかは不明だが、出した後にネットワークを作り直していたので、ここでテストできた


ISteamNetworkingMessagesの使い方

UDPに近い使い方ができる通信機能
公式サンプルコードがなかったのでかなり手探り
大まかな流れは

1..Steamマッチメイキングとロビーで相手のCSteamIDを取得
2..相手のIDが合ってたら接続
3..データ送受信
4..ゲーム終わったら切断

相手のCSteamIDが必要なので、
おそらくSteamマッチメイキングとロビー機能が必須
ISteamNetworkingMessagesは
「ISteamNetworkingSocketsをUDPっぽく使える」ものらしい
細かいことしたい場合はSocketsを使ったほうが良い

メッセージ送信

// 送信データ
typedef struct
{
public:
	BYTE message[SENDLOG][GAMELOG_SIZE]; // キーログ
	BYTE gameTime[2];	// ゲームタイム
	BYTE gameMode;	// モード
}InputData_t;


void NetworkMessage::SendToMessage() {
// 多分これ使えばいい
SteamNetworkingIdentity identity;
identity.Clear();
identity.SetSteamID(clientID);	// サーバー名ではなくユーザーのID

InputData_t sendPac = InputData_t();	// 送信用データを生成
uint32 cubData = (uint32)sizeof(sendPac);	// サイズをuint32にする

// ゲーム用のデータ設定、Steam関係なし //
/*
// データ設定
// パッケージのデータを移動する
for (int logNum = 0; logNum < SENDLOG; logNum++) {
	for (int gameLog = 0; gameLog < GAMELOG_SIZE; gameLog++) {
		sendPac.message[logNum][gameLog] = myDataLog[logNum][gameLog];
		//printfDx("%d.", sendPac.message[i]);
	}
}
// 中身を設定
sendPac.gameMode = gameMode;
sendPac.gameTime[0] = gameTime[0];
sendPac.gameTime[1] = gameTime[1];
*/
// 関係なし〆 //

//送信フラグ、ブラクラは0だと思う
int sendFlag = 0;

// [相手][データ][データ量][メッセージの送信方法、UDPは0でよさそう][別の送るチャンネルを設定、0でよさそう]
EResult res = SteamNetworkingMessages()->SendMessageToUser(identity, &sendPac, cubData, sendFlag, 0);
// 成功 or 失敗メッセージ
if (res == k_EResultOK);//printfDx("UDP..ok!\n");//	成功時は黙っとく
else { 
    // 失敗時の番号を表示
	//printfDx("UDP..failed %d\n", res); 
}
}


メッセージ受信

// 受け取るメッセージの数、多分もっと
SteamNetworkingMessage_t* msgs[2];
//[チャンネル][受信したデータ][受信する最大数]
int countMessage = SteamNetworkingMessages()->ReceiveMessagesOnChannel(0, msgs, 2);

// メッセージが届いてない場合
//if (countMessage <= 0)printfDx("nomes..\n"); // メッセージなし
//else{ printfDx("mes:%d\n", countMessage); } // メッセージ数

for (int i = 0; i < countMessage; i++)
{
	// メッセージをmsgsから取得し、サイズも取得
	SteamNetworkingMessage_t* message = msgs[i];	// メッセージを1つもってくる
	uint32 cubMsgSize = message->GetSize();	// メッセージサイズを取得

	// メッセージの型を変換、サイズが違うならエラー
	if (cubMsgSize != sizeof(InputData_t))	// 入力データとサイズが違う
	{
		printfDx("bad InputData\n");
		message->Release();	// 失敗、リリース
		break;
	}
    // メッセージから受け取ったデータを設定
	InputData_t* package = (InputData_t*)message->GetData();

    // ゲーム用のデータ設定、Steam関係なし //
    /*
	// データ設定
	// データ設定テスト
	// パッケージのデータを移動する
	for (int logNum = 0; logNum < SENDLOG; logNum++) {
		for (int gameLog = 0; gameLog < GAMELOG_SIZE; gameLog++) {
			// 数値があったらもってくる
			//printfDx("(%d):%d ", keyNum, package->message[keyNum]);
			clientDataLog[logNum][gameLog] = package->message[logNum][gameLog];
		}
	}
	// タイムとモード
	p2GameTime[0] = package->gameTime[0];
	p2GameTime[1] = package->gameTime[1];
	p2GameMode = package->gameMode;
    */
    // 関係なし〆 //

	message->Release();	// 終了時にリリース
}

return;	// おわり
}


コールバック

class NetworkMessage {
private:
    // 関数定義出なくてOK、cppに記述
    STEAM_CALLBACK(NetworkMessage, SteamNetworkingMessagesSessionRequest, SteamNetworkingMessagesSessionRequest_t);	
//
// 省略
//

// idがクライアント側のものかチェック
bool ExpectingClient(CSteamID getId)
{
	bool flg = 0;
	if (clientID == getId)flg = 1;	// あってるなら1
	return flg;
}

}


// コールバック、相手が合っているか確認して接続を許可
void NetworkMessage::SteamNetworkingMessagesSessionRequest(SteamNetworkingMessagesSessionRequest_t* pCallback)
{
    //リクエストを送ってきた相手のSteamIDを取得 
	CSteamID getId = pCallback->m_identityRemote.GetSteamID(); 

	if (ExpectingClient(getId))	// IDが事前に確認したものと合っているか確認する
	{
		// セッションを許可
		SteamNetworkingMessages()->AcceptSessionWithUser(pCallback->m_identityRemote);
		//printfDx("ok:通信が来た");
	}
	else
	{
		//printfDx("error:知らない人から通信");
	}
}

相手から通信リクエストが来たら自動的に呼び出される関数
取得しておいた相手のIDと合ってるかチェックし、
合っていたら通信を許可する

切断

// 通信が来なかった時間等の条件で切断
// 接続を終了する
void EndConnection() {
	SteamNetworkingIdentity identity;
	identity.Clear();
	identity.SetSteamID(clientID);	// ユーザーID
    // 切断
	SteamNetworkingMessages()->CloseSessionWithUser(identity);
}


最後に

マッチメイキングとロビー、ISteamNetworkingMessagesを実装したいけど
マジでわからないって時に相談に乗れるかもしれません
↓筆者のtwitter
https://twitter.com/oka0015

この記事が気に入ったらサポートをしてみませんか?