using System; using Steamworks; namespace MSCO { /// /// Steam-Lobby für P2P: Host erstellt eine Freunde-Lobby, lädt über das Steam-Overlay ein, /// Freunde treten über die Einladung bzw. "Spiel beitreten" bei. Die eigentlichen Daten /// laufen danach über Steam P2P (siehe SteamConnection). /// public class SteamLobby { const string VersionKey = "msco_version"; const uint ChatMemberLeftMask = 2 | 4 | 8 | 16; // Left, Disconnected, Kicked, Banned public CSteamID LobbyId { get; private set; } public bool InLobby { get { return LobbyId.m_SteamID != 0; } } /// Lobby erstellt (Host). public event Action Created; /// Lobby betreten (Client): Lobby-ID und Host. public event Action Entered; /// Einladung angenommen / "Spiel beitreten" im Freundesfenster. public event Action JoinRequested; /// Jemand hat die Lobby verlassen (Steam-ID). public event Action MemberLeft; /// Fehler (Text für die Statuszeile). public event Action Failed; CallResult createdResult; CallResult enterResult; Callback joinRequestedCallback; Callback chatUpdateCallback; Callback sessionRequestCallback; bool initialized; public static bool IsAvailable { get { try { return SteamAPI.IsSteamRunning() && SteamUser.GetSteamID().m_SteamID != 0; } catch { return false; } } } public static string PersonaName { get { try { return SteamFriends.GetPersonaName(); } catch { return null; } } } public static ulong OwnSteamId { get { try { return SteamUser.GetSteamID().m_SteamID; } catch { return 0; } } } static HAuthTicket authTicket; static bool hasAuthTicket; /// Steam-Anmeldeticket für dedizierte Server (der Server kann es bei Steam prüfen). public static byte[] CreateAuthTicket() { try { CancelAuthTicket(); var buffer = new byte[1024]; uint length; authTicket = SteamUser.GetAuthSessionTicket(buffer, buffer.Length, out length); hasAuthTicket = true; var ticket = new byte[length]; Array.Copy(buffer, ticket, length); return ticket; } catch { return new byte[0]; } } public static void CancelAuthTicket() { if (!hasAuthTicket) return; hasAuthTicket = false; try { SteamUser.CancelAuthTicket(authTicket); } catch { } } public bool Init() { if (initialized) return true; if (!IsAvailable) return false; try { createdResult = CallResult.Create(OnLobbyCreated); enterResult = CallResult.Create(OnLobbyEntered); joinRequestedCallback = Callback.Create(OnJoinRequested); chatUpdateCallback = Callback.Create(OnChatUpdate); sessionRequestCallback = Callback.Create(OnSessionRequest); initialized = true; } catch (Exception e) { MscoLog.Error("[MSCO] Steam init failed: " + e.Message); } return initialized; } /// Jedes Frame aufrufen, damit Steam-Callbacks ankommen. public void Tick() { if (!initialized) return; try { SteamAPI.RunCallbacks(); } catch { } } public void Create(int maxPlayers) { if (!Init()) { Fail("Steam is not running"); return; } Leave(); createdResult.Set(SteamMatchmaking.CreateLobby(ELobbyType.k_ELobbyTypeFriendsOnly, maxPlayers)); } public void Join(CSteamID lobby) { if (!Init()) { Fail("Steam is not running"); return; } Leave(); enterResult.Set(SteamMatchmaking.JoinLobby(lobby)); } public void OpenInviteDialog() { if (InLobby) SteamFriends.ActivateGameOverlayInviteDialog(LobbyId); } public void Leave() { if (!InLobby) return; try { SteamMatchmaking.LeaveLobby(LobbyId); } catch { } LobbyId = new CSteamID(0); } public bool IsMember(CSteamID user) { if (!InLobby) return false; int count = SteamMatchmaking.GetNumLobbyMembers(LobbyId); for (int i = 0; i < count; i++) if (SteamMatchmaking.GetLobbyMemberByIndex(LobbyId, i) == user) return true; return false; } // ---------------------------------------------------------------- Callbacks void OnLobbyCreated(LobbyCreated_t result, bool ioFailure) { if (ioFailure || result.m_eResult != EResult.k_EResultOK) { Fail("Could not create Steam lobby (" + result.m_eResult + ")"); return; } LobbyId = new CSteamID(result.m_ulSteamIDLobby); SteamMatchmaking.SetLobbyData(LobbyId, VersionKey, MSCO.ProtocolVersion); // ältere Mods vergleichen genau if (Created != null) Created(LobbyId); } void OnLobbyEntered(LobbyEnter_t result, bool ioFailure) { if (ioFailure || result.m_EChatRoomEnterResponse != 1) // 1 = Success { Fail("Could not join the lobby (full or closed)"); return; } LobbyId = new CSteamID(result.m_ulSteamIDLobby); string hostVersion = SteamMatchmaking.GetLobbyData(LobbyId, VersionKey); if (!string.IsNullOrEmpty(hostVersion) && !MSCO.Compatible(hostVersion)) { Leave(); Fail("Version mismatch (host " + hostVersion + ", you " + MSCO.ModVersion + ")"); return; } CSteamID owner = SteamMatchmaking.GetLobbyOwner(LobbyId); if (Entered != null) Entered(LobbyId, owner); } void OnJoinRequested(GameLobbyJoinRequested_t request) { if (JoinRequested != null) JoinRequested(request.m_steamIDLobby); } void OnChatUpdate(LobbyChatUpdate_t update) { if (update.m_ulSteamIDLobby != LobbyId.m_SteamID) return; if ((update.m_rgfChatMemberStateChange & ChatMemberLeftMask) == 0) return; if (MemberLeft != null) MemberLeft(new CSteamID(update.m_ulSteamIDUserChanged)); } void OnSessionRequest(P2PSessionRequest_t request) { // Nur Verbindungen von Spielern aus unserer Lobby annehmen if (IsMember(request.m_steamIDRemote)) SteamNetworking.AcceptP2PSessionWithUser(request.m_steamIDRemote); } void Fail(string message) { if (Failed != null) Failed(message); } /// Wurde das Spiel über eine Steam-Einladung gestartet? ("+connect_lobby <id>") public static CSteamID LobbyFromCommandLine() { string[] args = Environment.GetCommandLineArgs(); for (int i = 0; i < args.Length - 1; i++) { ulong id; if (args[i] == "+connect_lobby" && ulong.TryParse(args[i + 1], out id)) return new CSteamID(id); } return new CSteamID(0); } } }