228 lines
8.0 KiB
C#
228 lines
8.0 KiB
C#
using System;
|
|
using MSCLoader;
|
|
using Steamworks;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
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; } }
|
|
|
|
/// <summary>Lobby erstellt (Host).</summary>
|
|
public event Action<CSteamID> Created;
|
|
/// <summary>Lobby betreten (Client): Lobby-ID und Host.</summary>
|
|
public event Action<CSteamID, CSteamID> Entered;
|
|
/// <summary>Einladung angenommen / "Spiel beitreten" im Freundesfenster.</summary>
|
|
public event Action<CSteamID> JoinRequested;
|
|
/// <summary>Jemand hat die Lobby verlassen (Steam-ID).</summary>
|
|
public event Action<CSteamID> MemberLeft;
|
|
/// <summary>Fehler (Text für die Statuszeile).</summary>
|
|
public event Action<string> Failed;
|
|
|
|
CallResult<LobbyCreated_t> createdResult;
|
|
CallResult<LobbyEnter_t> enterResult;
|
|
Callback<GameLobbyJoinRequested_t> joinRequestedCallback;
|
|
Callback<LobbyChatUpdate_t> chatUpdateCallback;
|
|
Callback<P2PSessionRequest_t> 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;
|
|
|
|
/// <summary>Steam-Anmeldeticket für dedizierte Server (der Server kann es bei Steam prüfen).</summary>
|
|
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<LobbyCreated_t>.Create(OnLobbyCreated);
|
|
enterResult = CallResult<LobbyEnter_t>.Create(OnLobbyEntered);
|
|
joinRequestedCallback = Callback<GameLobbyJoinRequested_t>.Create(OnJoinRequested);
|
|
chatUpdateCallback = Callback<LobbyChatUpdate_t>.Create(OnChatUpdate);
|
|
sessionRequestCallback = Callback<P2PSessionRequest_t>.Create(OnSessionRequest);
|
|
initialized = true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ModConsole.Error("[MSCO] Steam init failed: " + e.Message);
|
|
}
|
|
return initialized;
|
|
}
|
|
|
|
/// <summary>Jedes Frame aufrufen, damit Steam-Callbacks ankommen.</summary>
|
|
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.ModVersion);
|
|
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) && hostVersion != MSCO.ModVersion)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/// <summary>Wurde das Spiel über eine Steam-Einladung gestartet? ("+connect_lobby <id>")</summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|