0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonでSteamSDKを利用する

Last updated at Posted at 2025-01-29

SteamWorksPy

まず挙げられる方法としてはラッパを使用することだと思います。
こんなのがあります

https://github.com/philippj/SteamworksPy/tree/master
マッチメイキング機能から、基本的なsteamの情報を取得する機能までがある。
使い方はこの動画がとても参考になりました。
https://www.youtube.com/watch?v=Ka9LIGYm_b0

ただしp2p方式の通信機能までは備わっていなかったので(もしかしたらあるのかもしれない)
今後の拡張性を考慮して、C++から拡張しPythonで使えるようにする。

VS

VSを使う
まずはプロジェクトのプロパティーから、SteamSDKを読み込む
https://partner.steamgames.com/downloads/list
SteamSDKはこのサイトからダウンロードできる

  1. VC++ディレクトリー -> ライブラリディレクトリー -> C:\steamworks_sdk\redistributable_bin\win64\steam_api64.lib
    ... ライブラリの読
  2. C/C++ -> 追加のインクルードディレクトリ -> C:\steamworks_sdk\public
    これでおそらく、cppファイルで#include が読み込めるようになった。
  3. C/C++ -> SDLチェック -> いいえ
    これはC言語の低レベルな関数を使うためにチェックを外す必要がある...らしい?(これがTrueだとstring.hが読み込めなかった)
  4. リンカー -> 追加の依存ファイル -> steam_api64.lib
  5. リンカー -> 全般 -> 使いのライブラリディレクトリー -> C:\steamworks_sdk\redistributable_bin\win64
    これでデバッグ時に依存ファイルをチェックしてくれる。
    憶測なので確実な情報じゃないよ

また実行ファイルと同じディレクトリーにsteam_api64.dllをセットしておく必要がある。
スクリプトも例を乗せておく

#include <iostream>
#include <chrono>
#include <thread>
#include <steam/steam_api.h>
#include <vector>
#include <string>

static std::vector<uint64_t> friendLobbyIDs;
static std::vector<uint64_t> publicLobbyIDs;

class LobbyCreatedCallback {
public:
    STEAM_CALLBACK(LobbyCreatedCallback, OnLobbyCreated, LobbyCreated_t);
    CSteamID createdLobbyID;
    bool success = false;
};

void LobbyCreatedCallback::OnLobbyCreated(LobbyCreated_t* pCallback) {
    if (pCallback->m_eResult == k_EResultOK) {
        createdLobbyID.SetFromUint64(pCallback->m_ulSteamIDLobby);
        success = true;
    }
    else {
        success = false;
    }
}

class LobbyChatUpdateCallback {
public:
    STEAM_CALLBACK(LobbyChatUpdateCallback, OnLobbyChatUpdate, LobbyChatUpdate_t);
    uint64 lastJoinedSteamID = 0;
    uint64 lastLeftSteamID = 0;
    uint64 lastLobbyID = 0;
    bool joinedUpdated = false;
    bool leftUpdated = false;
};

void LobbyChatUpdateCallback::OnLobbyChatUpdate(LobbyChatUpdate_t* pCallback) {
    lastLobbyID = pCallback->m_ulSteamIDLobby;

    // **参加したプレイヤー**
    if (pCallback->m_rgfChatMemberStateChange & k_EChatMemberStateChangeEntered) {
        lastJoinedSteamID = pCallback->m_ulSteamIDUserChanged;
        joinedUpdated = true;
        std::cout << "Player " << lastJoinedSteamID << " joined lobby " << lastLobbyID << std::endl;
    }

    // **退室したプレイヤー**
    if (pCallback->m_rgfChatMemberStateChange & k_EChatMemberStateChangeLeft) {
        lastLeftSteamID = pCallback->m_ulSteamIDUserChanged;
        leftUpdated = true;
        std::cout << "Player " << lastLeftSteamID << " left lobby " << lastLobbyID << std::endl;
    }
}

class LobbyListCallback {
public:
    STEAM_CALLBACK(LobbyListCallback, OnLobbyMatchList, LobbyMatchList_t);
    int lobbyCount = 0;
    bool updated = false;
};

void LobbyListCallback::OnLobbyMatchList(LobbyMatchList_t* pCallback) {
    lobbyCount = pCallback->m_nLobbiesMatching;
    updated = true;
}
class LobbyEnterCallback {
public:
    STEAM_CALLBACK(LobbyEnterCallback, OnLobbyEnter, LobbyEnter_t);
    bool lobbyEnterSuccess = false;
    CSteamID joinedLobbyID;
    EChatRoomEnterResponse response;
};

void LobbyEnterCallback::OnLobbyEnter(LobbyEnter_t* pCallback) {
    joinedLobbyID = pCallback->m_ulSteamIDLobby;
    response = static_cast<EChatRoomEnterResponse>(pCallback->m_EChatRoomEnterResponse);

    if (response == k_EChatRoomEnterResponseSuccess) {
        lobbyEnterSuccess = true;
        std::cout << "[LobbyEnter] Successfully joined lobby "
            << joinedLobbyID.ConvertToUint64() << std::endl;
    }
    else {
        lobbyEnterSuccess = false;
        std::cout << "[LobbyEnter] Failed to enter lobby (Error code: "
            << static_cast<int>(response) << ")" << std::endl;
    }
}
LobbyEnterCallback       lobbyEnterCallback;

// **グローバル変数としてコールバックオブジェクトを登録**
LobbyChatUpdateCallback chatUpdateCallback;


// ロビー検索用のコールバックオブジェクト
LobbyListCallback lobbyListCallback;


// ロビー作成用のコールバックオブジェクト
LobbyCreatedCallback lobbyCallback;

extern "C" {
    // **P2P セッションリクエストを受け入れる**
    __declspec(dllexport) bool AcceptP2PSession(uint64 steamID) {
        return SteamNetworking()->AcceptP2PSessionWithUser(CSteamID(steamID));
    }
    __declspec(dllexport) void RunSteamCallbacks() {
        SteamAPI_RunCallbacks();
    }
    // Steam API の初期化
    __declspec(dllexport) bool InitializeSteam() {
        return SteamAPI_Init();
    }

    // Steam ID の取得
    __declspec(dllexport) uint64 GetSteamID() {
        if (SteamUser()) {
            CSteamID steamID = SteamUser()->GetSteamID();
            return steamID.ConvertToUint64();
        }
        return 0;
    }

    // P2P メッセージ送信
    __declspec(dllexport) bool SendP2PMessage(uint64 steamID, const char* message) {
        CSteamID targetID = CSteamID(steamID);
        SteamNetworking()->AllowP2PPacketRelay(true);

        return SteamNetworking()->SendP2PPacket(targetID, message, strlen(message) + 1, k_EP2PSendReliable);
    }

    // P2P メッセージ受信
    __declspec(dllexport) bool ReceiveP2PMessage(char* buffer, int bufferSize, uint64* senderSteamID) {
        uint32 msgSize;
        CSteamID senderID;
        SteamNetworking()->AllowP2PPacketRelay(true);
        if (SteamNetworking()->IsP2PPacketAvailable(&msgSize)) {
            SteamNetworking()->ReadP2PPacket(buffer, bufferSize, &msgSize, &senderID);
            *senderSteamID = senderID.ConvertToUint64();
            return true;
        }
        return false;
    }
    // **PING メッセージを送信**
    __declspec(dllexport) bool SendPing(uint64 steamID) {
        const char* pingMessage = "PING";
        return SteamNetworking()->SendP2PPacket(CSteamID(steamID), pingMessage, 4, k_EP2PSendReliable);
    }
    // **フレンドのロビーを検索→一覧を保存
    __declspec(dllexport) int RefreshFriendLobbies()
    {
        friendLobbyIDs.clear();        // 前回結果をクリア

        // 1) ロビーリストをリクエスト
        lobbyListCallback.updated = false;
        SteamMatchmaking()->AddRequestLobbyListResultCountFilter(100);
        SteamMatchmaking()->RequestLobbyList();

        // 2) コールバック完了まで待機
        for (int i = 0; i < 200; i++) {
            SteamAPI_RunCallbacks();
            if (lobbyListCallback.updated) {
                break;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        // 3) 結果を取得してフレンドチェック
        int count = lobbyListCallback.lobbyCount;
        for (int i = 0; i < count; i++) {
            CSteamID lobbyID = SteamMatchmaking()->GetLobbyByIndex(i);
            CSteamID ownerID = SteamMatchmaking()->GetLobbyOwner(lobbyID);

            // ownerが0なら無効
            if (ownerID.ConvertToUint64() == 0) continue;

            // オーナーがフレンドなら保存
            if (SteamFriends()->HasFriend(ownerID, k_EFriendFlagImmediate)) {
                friendLobbyIDs.push_back(lobbyID.ConvertToUint64());
            }
        }

        // 4) 見つかった数を返す
        return static_cast<int>(friendLobbyIDs.size());
    }
	//** 公開lobbyを検索して一覧を保存**
    __declspec(dllexport) int RefreshPublicLobbies()
    {
        publicLobbyIDs.clear();

        lobbyListCallback.updated = false;
        SteamMatchmaking()->AddRequestLobbyListResultCountFilter(300);
        SteamMatchmaking()->RequestLobbyList();

        for (int i = 0; i < 200; i++) {
            SteamAPI_RunCallbacks();
            if (lobbyListCallback.updated) {
                break;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        int count = lobbyListCallback.lobbyCount;
        for (int i = 0; i < count; i++) {
            CSteamID lobbyID = SteamMatchmaking()->GetLobbyByIndex(i);
            CSteamID ownerID = SteamMatchmaking()->GetLobbyOwner(lobbyID);

            // オーナー不在なら無視
            if (ownerID.ConvertToUint64() == 0) continue;

            // ここで必要があれば、さらにロビー人数などをチェックして除外もOK
            //int numMembers = SteamMatchmaking()->GetNumLobbyMembers(lobbyID);

            publicLobbyIDs.push_back(lobbyID.ConvertToUint64());
        }
        return static_cast<int>(publicLobbyIDs.size());
    }
    // **一般lobby一覧を取り出す
    __declspec(dllexport) uint64 GetPublicLobbyIDByIndex(int index)
    {
        if (index < 0 || index >= (int)publicLobbyIDs.size()) {
            return 0;
        }
        return publicLobbyIDs[index];
    }
	// **フレンドlobby一覧を取り出す**
    __declspec(dllexport) uint64 GetFriendLobbyIDByIndex(int index)
    {
        if (index < 0 || index >= (int)friendLobbyIDs.size()) {
            return 0;
        }
        return friendLobbyIDs[index];
    }
    // **指定したオーナーのロビー ID を取得**
    __declspec(dllexport) uint64 FindLobbyByOwner(uint64 ownerID) {
        lobbyListCallback.updated = false;
        SteamMatchmaking()->RequestLobbyList();

        // **コールバックが完了するまで待つ**
        for (int i = 0; i < 100; i++) {
            SteamAPI_RunCallbacks();
            if (lobbyListCallback.updated) {
                break;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 10ミリ秒待機
        }

        // **取得したロビーの数を確認**
        int count = lobbyListCallback.lobbyCount;
        for (int i = 0; i < count; i++) {
            CSteamID lobbyID = SteamMatchmaking()->GetLobbyByIndex(i);
            uint64 lobbyOwnerID = SteamMatchmaking()->GetLobbyOwner(lobbyID).ConvertToUint64();

            if (lobbyOwnerID == ownerID) {
                return lobbyID.ConvertToUint64(); // 該当するロビーの ID を返す
            }
        }

        return 0; // 該当するロビーが見つからなかった
    }

    // **ロビー ID をインデックスから取得**
    __declspec(dllexport) uint64 GetLobbyByIndex(int index) {
        if (index < 0 || index >= SteamMatchmaking()->GetFavoriteGameCount()) {
            return 0; // 無効なインデックス
        }
        return SteamMatchmaking()->GetLobbyByIndex(index).ConvertToUint64();
    }
    // **ロビーのオーナー(ホスト)の Steam ID を取得**
    __declspec(dllexport) uint64 GetLobbyOwner(uint64 lobbyID) {
        CSteamID lobby(lobbyID);
        CSteamID owner = SteamMatchmaking()->GetLobbyOwner(lobby);
        return owner.ConvertToUint64();
    }
    // **ロビーのメンバー数を取得**
    __declspec(dllexport) int GetNumLobbyMembers(uint64 lobbyID) {
        CSteamID lobby(lobbyID);
        return SteamMatchmaking()->GetNumLobbyMembers(lobby);
    }

    // **指定インデックスのロビーメンバーの Steam ID を取得**
    __declspec(dllexport) uint64 GetLobbyMemberByIndex(uint64 lobbyID, int index) {
        CSteamID lobby(lobbyID);
        CSteamID member = SteamMatchmaking()->GetLobbyMemberByIndex(lobby, index);
        return member.ConvertToUint64();
    }


    // **SteamID からプレイヤー名を取得**
    __declspec(dllexport) void GetSteamName(uint64 steamID, char* buffer, int bufferSize) {
        CSteamID playerID(steamID);
        const char* name = SteamFriends()->GetFriendPersonaName(playerID);
        strncpy(buffer, name, bufferSize - 1);
        buffer[bufferSize - 1] = '\0';  // Null 終端
    }



    // **ロビーを作成**
    __declspec(dllexport) uint64 CreateLobby(int lobbyType, int maxPlayers) {
        lobbyCallback.success = false;
        SteamMatchmaking()->CreateLobby((ELobbyType)lobbyType, maxPlayers);

        // 一定時間待機してロビーが作成されたかチェック
        for (int i = 0; i < 100; i++) {
            SteamAPI_RunCallbacks(); // コールバックを処理
            if (lobbyCallback.success) {
                return lobbyCallback.createdLobbyID.ConvertToUint64();
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        return 0; // 作成失敗
    }
    // ロビーに参加
    __declspec(dllexport) bool JoinLobby(uint64 lobbyID) {
        lobbyEnterCallback.lobbyEnterSuccess = false;
        SteamMatchmaking()->JoinLobby(CSteamID(lobbyID));

        // 参加完了を待機 (OnLobbyEnter コールバック)
        for (int i = 0; i < 100; i++) {
            SteamAPI_RunCallbacks();
            if (lobbyEnterCallback.lobbyEnterSuccess) {
                // 成功
                return true;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        // 失敗
        return false;
    }


    // **ロビー参加を検出**
    __declspec(dllexport) bool CheckLobbyJoin(uint64* joinedSteamID, uint64* lobbyID) {
        SteamAPI_RunCallbacks();  // **Steam コールバックを処理**
        if (chatUpdateCallback.joinedUpdated) {
            *joinedSteamID = chatUpdateCallback.lastJoinedSteamID;
            *lobbyID = chatUpdateCallback.lastLobbyID;
            chatUpdateCallback.joinedUpdated = false;
            return true;
        }
        return false;
    }

    // **ロビー退室を検出**
    __declspec(dllexport) bool CheckLobbyLeave(uint64* leftSteamID, uint64* lobbyID) {
        SteamAPI_RunCallbacks();  // **Steam コールバックを処理**
        if (chatUpdateCallback.leftUpdated) {
            *leftSteamID = chatUpdateCallback.lastLeftSteamID;
            *lobbyID = chatUpdateCallback.lastLobbyID;
            chatUpdateCallback.leftUpdated = false;
            return true;
        }
        return false;
    }

    // ========================
    // Rich Presence 関連
    // ========================

    // 自分が所属するロビーIDを Rich Presence にセット
    __declspec(dllexport) void SetLobbyRichPresence(uint64 lobbyID) {
        // key="steam_player_group" にロビーIDを文字列で入れるのが慣例
        char lobbyStr[32];
        sprintf(lobbyStr, "%llu", (unsigned long long)lobbyID);

        // ※ "steam_player_group" と "connect" の2つに同じ値をセットする手法が多い
        //    "connect" を見て参加しようとするタイトルもあるため
        SteamFriends()->SetRichPresence("steam_player_group", lobbyStr);
        SteamFriends()->SetRichPresence("connect", lobbyStr);

        // 任意でステータス文言をセットしてもOK
        SteamFriends()->SetRichPresence("status", "In a lobby!");
        std::cout << "[SetLobbyRichPresence] lobbyID=" << lobbyStr << std::endl;
    }

    // Rich Presence をクリア (ゲーム終了時やロビー退出時など)
    __declspec(dllexport) void ClearRichPresence() {
        SteamFriends()->ClearRichPresence();
        std::cout << "[ClearRichPresence] All presence data cleared.\n";
    }

    // フレンドの Rich Presence から "steam_player_group" を取得
    // (フレンドがロビーIDをセットしていれば、それが返ってくる)
    __declspec(dllexport) uint64 GetFriendLobbyRichPresence(uint64 friendSteamID) {
        CSteamID friendID(friendSteamID);
        const char* presence = SteamFriends()->GetFriendRichPresence(friendID, "steam_player_group");
        if (presence && presence[0] != '\0') {
            return std::stoull(presence);
        }
        return 0; // 見つからない場合は 0
    }
    __declspec(dllexport) int RefreshFriendLobbiesRichPresence()
    {
        friendLobbyIDs.clear();

        // (1) 今のフレンド数を取得
        int friendCount = SteamFriends()->GetFriendCount(k_EFriendFlagImmediate);
        if (friendCount < 1) {
            std::cout << "[RefreshFriendLobbiesRichPresence] No friends found.\n";
            return 0;
        }

        // (2) 各フレンドを走査し、Rich Presenceの "steam_player_group" をチェック
        for (int i = 0; i < friendCount; i++) {
            CSteamID friendID = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagImmediate);
            if (!friendID.IsValid()) continue;

            // ここでキー"steam_player_group"の値を取得 (ロビーIDが入っている可能性がある)
            const char* presence = SteamFriends()->GetFriendRichPresence(friendID, "steam_player_group");
            if (presence && presence[0] != '\0') {
                // 文字列 -> uint64 に変換
                uint64_t lobbyID = std::strtoull(presence, nullptr, 10);
                if (lobbyID != 0) {
                    friendLobbyIDs.push_back(lobbyID);
                    std::cout << "[RefreshFriendLobbiesRichPresence] Friend "
                        << friendID.ConvertToUint64()
                        << " has lobbyID=" << lobbyID << "\n";
                }
            }
        }

        // フレンドが参加中のロビー数を返す
        return static_cast<int>(friendLobbyIDs.size());
    }
    __declspec(dllexport) uint64_t GetFriendLobbyIDByIndex_RichPresence(int index)
    {
        if (index < 0 || index >= static_cast<int>(friendLobbyIDs.size())) {
            return 0; // 無効な場合は0
        }
        return friendLobbyIDs[index];
    }

}

仮のスクリプトですので、整理する必要があります。
私はこんな感じで分けてみました。
source - SteamLobby.cpp SteamNetworking.cpp SteamCallbacks.cpp
header - SteamLobby.h 以下同じ

cl.exe /LD /I "C:\steamworks_sdk\public" SteamCallbacks.cpp SteamNetworking.cpp SteamLobby.cpp "C:\steamworks_sdk\redistributable_bin\win64\steam_api64.lib" /FeSteamNetworkingWrapper.dll

Pythonではこんな感じで読み込みます。

import ctypes
import os

# DLL をロード
dll_path = os.path.abspath("./SteamNetworkingWrapper.dll")
steam_dll = ctypes.CDLL(dll_path)
# SteamCallbacks を実行
run_steam_callbacks = steam_dll.RunSteamCallbacks
run_steam_callbacks.restype = None

コールバックしかないんですけど全部こんな感じでかけます。
フレンドのルーム検索は二つあって、私のおすすめは、RichPresenceです

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?