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
この記事が気に入ったらサポートをしてみませんか?