Advertisement
ArenMook

Steam.cs

Jan 17th, 2018
15,506
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.26 KB | None | 0 0
  1. //#if !UNITY_EDITOR
  2. #if !UNITY_IPHONE && !UNITY_ANDROID && !MODDING
  3. #define STEAM
  4. #endif
  5. //#endif
  6.  
  7. using System;
  8. using UnityEngine;
  9. using TNet;
  10. using Buffer = TNet.Buffer;
  11.  
  12. #if STEAM
  13. using System.IO;
  14. using Steamworks;
  15. #endif
  16.  
  17. /// <summary>
  18. /// Wrapper class that simplifies the functionality of Steamworks into one handy class that uses TNet.
  19. /// To use it, simply attach it to a game object present in your first game scene. This game object will become persistent.
  20. ///
  21. /// The OnConnect() function below automatically calls AllowFriendsToJoin(true), which enables the ability of your friends to
  22. /// right-click your player name and choose the Join Game option. If using matchmaking, you may want to control when
  23. /// AllowFriendsToJoin() is called instead -- to do this simply comment it out from the OnConnect(), then call it yourself.
  24. ///
  25. /// To connect to a friend manually by using their Steam ID, just call Steam.Connect("userID"), where 'userID' is their Steam ID,
  26. /// such as Steam.Connect("76561198012345678").
  27. /// </summary>
  28.  
  29. public class Steam : MonoBehaviour
  30. {
  31.     /// <summary>
  32.     /// Application ID we'll be working with.
  33.     /// TODO: SET THIS TO YOUR GAME'S OWN ID !!!
  34.     /// </summary>
  35.  
  36.     public const int appID = 655780;
  37.     static ulong mID = 0;
  38.  
  39. #if STEAM
  40.     static Steam mInst = null;
  41.     static bool mIsActive = false;
  42.     static string mUsername = null;
  43.     static CSteamID mUserID;
  44.  
  45.     /// <summary>
  46.     /// Whether the Steam API is available.
  47.     /// </summary>
  48.  
  49.     static public bool isActive { get { return mIsActive; } }
  50.  
  51.     /// <summary>
  52.     /// Steam ID is a fairly long number.
  53.     /// </summary>
  54.  
  55.     static public ulong userID
  56.     {
  57.         get
  58.         {
  59. #if UNITY_EDITOR && !MODDING
  60.             if (mID == 0) mID = mIsActive ? (ulong)mUserID : (ulong)SystemInfo.deviceUniqueIdentifier.GetHashCode();
  61. #else
  62.             if (mID == 0) mID = mIsActive ? (ulong)mUserID : (ulong)12345678901234567890; // Non-zero so that it can be banned by
  63. #endif
  64.             return mID;
  65.         }
  66.     }
  67.  
  68.     /// <summary>
  69.     /// Get the Steam username.
  70.     /// </summary>
  71.  
  72.     static public string username
  73.     {
  74.         get
  75.         {
  76.             if (string.IsNullOrEmpty(mUsername))
  77.             {
  78.                 mUsername = mIsActive ? SteamFriends.GetPersonaName() : "Guest";
  79.             }
  80.             return mUsername;
  81.         }
  82.     }
  83.  
  84.     /// <summary>
  85.     /// Command-line argument used to auto-connect an external server.
  86.     /// This will be set if Steam launches your game as the result of the "Join Game" choice on the player's list or player's profile.
  87.     /// </summary>
  88.  
  89.     static internal string autoConnectString
  90.     {
  91.         get
  92.         {
  93.             try
  94.             {
  95.                 bool connectStringIsNext = false;
  96.                 var args = Environment.GetCommandLineArgs();
  97.  
  98.                 if (args != null)
  99.                 {
  100.                     for (int i = 0; i < args.Length; ++i)
  101.                     {
  102.                         string val = args[i];
  103.                         if (connectStringIsNext) return val;
  104.                         else if (val == "+connect") connectStringIsNext = true;
  105.                     }
  106.                 }
  107.             }
  108.             catch (Exception) { }
  109.             return null;
  110.         }
  111.     }
  112.  
  113.     /// <summary>
  114.     /// Whether the specified steam account is a friend.
  115.     /// </summary>
  116.  
  117.     static internal bool HasFriend (ulong steamID)
  118.     {
  119.         if (isActive)
  120.         {
  121.             return SteamFriends.HasFriend(new CSteamID(steamID), EFriendFlags.k_EFriendFlagImmediate | EFriendFlags.k_EFriendFlagClanMember);
  122.         }
  123.         return false;
  124.     }
  125.  
  126.     /// <summary>
  127.     /// Whether the specified steam account is a friend.
  128.     /// </summary>
  129.  
  130.     static internal bool HasFriend (string steamID)
  131.     {
  132.         if (isActive)
  133.         {
  134.             ulong id;
  135.             if (ulong.TryParse(steamID, out id)) return SteamFriends.HasFriend(new CSteamID(id), EFriendFlags.k_EFriendFlagImmediate | EFriendFlags.k_EFriendFlagClanMember);
  136.         }
  137.         return false;
  138.     }
  139.  
  140.     /// <summary>
  141.     /// Whether the specified steam account is a friend.
  142.     /// </summary>
  143.  
  144.     static internal bool HasFriend (CSteamID steamID)
  145.     {
  146.         if (isActive)
  147.         {
  148.             return SteamFriends.HasFriend(steamID, EFriendFlags.k_EFriendFlagImmediate | EFriendFlags.k_EFriendFlagClanMember);
  149.         }
  150.         return false;
  151.     }
  152.  
  153.     /// <summary>
  154.     /// Initialize Steamworks.
  155.     /// </summary>
  156.  
  157.     void Awake ()
  158.     {
  159.         // Makes sure that only one instance of this object is in use at a time
  160.         if (mInst != null)
  161.         {
  162.             Destroy(gameObject);
  163.             return;
  164.         }
  165.  
  166.         DontDestroyOnLoad(gameObject);
  167.         mInst = this;
  168.  
  169.         if (!Packsize.Test())
  170.         {
  171.             // Likely using a Linux/OSX build on Windows or vice versa
  172.             Debug.LogError("Steamworks.Packsize.Test() fail!");
  173.             return;
  174.         }
  175.  
  176.         if (!DllCheck.Test())
  177.         {
  178.             Debug.LogError("Steamworks.DllCheck.Test() fail!");
  179.             return;
  180.         }
  181.  
  182.         mIsActive = false;
  183.  
  184.         try
  185.         {
  186.             // This would make Steam launch your game if you want
  187.             //if (SteamAPI.RestartAppIfNecessary(new AppId_t((uint)appID)))
  188.             //{
  189.             //  Application.Quit();
  190.             //  return;
  191.             //}
  192.  
  193.             mIsActive = SteamAPI.Init();
  194.         }
  195.         catch (Exception) { }
  196.  
  197.         if (!mIsActive)
  198.         {
  199.             mInst = null;
  200.             Destroy(this);
  201.             return;
  202.         }
  203.  
  204.         mUserID = SteamUser.GetSteamID();
  205.         Debug.Log("Steam " + mUserID);
  206.  
  207.         EnableNetworkingSupport();
  208.  
  209.         // Infrequently update Steam
  210.         InvokeRepeating("RunCallbacks", 0.001f, 0.1f);
  211.     }
  212.  
  213.     /// <summary>
  214.     /// Auto-connect to the chosen user. The 'autoConnectString' will only be valid if the game was launched from Steam as the
  215.     /// result of choosing the "Join Game" option. In this case Steam encodes the connect information in the launch arguments.
  216.     /// Steam.Connect("userID") returns 'false' if it's not a valid user ID to connect to, in which case is then passed
  217.     /// to TNManager.Connect instead, just in case the +connect string contains an IP instead.
  218.     /// </summary>
  219.  
  220.     void Start ()
  221.     {
  222.         var str = autoConnectString;
  223.         if (!string.IsNullOrEmpty(str) && !Connect(str)) TNManager.Connect(str);
  224.     }
  225.  
  226.     [TNet.DoNotObfuscate]
  227.     void RunCallbacks () { SteamAPI.RunCallbacks(); }
  228.  
  229.     [System.NonSerialized] static byte[] mTemp;
  230.     [System.NonSerialized] static System.Collections.Generic.Dictionary<CSteamID, TcpProtocol> mOpen = new System.Collections.Generic.Dictionary<CSteamID, TcpProtocol>();
  231.     [System.NonSerialized] static System.Collections.Generic.HashSet<CSteamID> mClosed = new System.Collections.Generic.HashSet<CSteamID>();
  232.  
  233.     class P2PConnection : IConnection
  234.     {
  235.         public CSteamID id;
  236.         public bool connecting = false;
  237.         public bool disconnected = false;
  238.  
  239.         public bool isConnected { get { return !disconnected; } }
  240.  
  241.         public bool SendPacket (Buffer buffer)
  242.         {
  243.             var offset = buffer.position;
  244.             var bufferSize = offset + buffer.size;
  245.             var packetSize = buffer.PeekInt(offset);
  246.             if (packetSize == 0) return false;
  247.  
  248.             var packetStart = offset + 4; // Skip past the size
  249.             int counter = 0;
  250.  
  251.             while (packetStart < bufferSize)
  252.             {
  253.                 if (packetStart + packetSize > bufferSize) break;
  254.  
  255.                 var fs = packetSize + 4;
  256.                 if (!Send(id, buffer.buffer, offset, fs)) return false;
  257.  
  258.                 ++counter;
  259.  
  260.                 // Advance past the packet that was just sent
  261.                 offset += fs;
  262.                 if (offset == bufferSize) break;
  263.  
  264.                 packetStart = offset + 4; // Skip past the size
  265.  
  266.                 // Packets can't be below 5 bytes (4 bytes for size + 1 byte for ID)
  267.                 if (packetStart + 5 < bufferSize)
  268.                 {
  269.                     packetSize = buffer.PeekInt(offset);
  270.                     continue;
  271.                 }
  272.                 break;
  273.             }
  274.             return true;
  275.         }
  276.  
  277.         public void ReceivePacket (out Buffer buffer) { buffer = null; }
  278.  
  279.         public void OnDisconnect ()
  280.         {
  281.             if (!disconnected)
  282.             {
  283.                 disconnected = true;
  284.  
  285.                 BeginSend(Packet.Disconnect);
  286.                 EndSend(id);
  287.  
  288.                 lock (mOpen)
  289.                 {
  290.                     mOpen.Remove(id);
  291.                     if (!mClosed.Contains(id)) mClosed.Add(id);
  292.                 }
  293.  
  294.                 if (TNManager.custom == this) TNManager.custom = null;
  295.             }
  296.         }
  297.     }
  298.  
  299.     // Callbacks are added to a list so they don't get discarded by GC
  300.     List<object> mCallbacks = new List<object>();
  301.  
  302.     /// <summary>
  303.     /// Event subscriptions needed to get networking to work.
  304.     /// </summary>
  305.  
  306.     void EnableNetworkingSupport ()
  307.     {
  308.         // P2P connection request
  309.         mCallbacks.Add(Callback<P2PSessionRequest_t>.Create(delegate (P2PSessionRequest_t val)
  310.         {
  311.             if (TNServerInstance.isListening)
  312.             {
  313.                 Debug.Log("P2P Request: " + val.m_steamIDRemote);
  314.  
  315.                 // Want only friends to join? Use this:
  316.                 // if (HasFriend(val.m_steamIDRemote))
  317.  
  318.                 SteamNetworking.AcceptP2PSessionWithUser(val.m_steamIDRemote);
  319.             }
  320.         }));
  321.  
  322.         // P2P connection error
  323.         mCallbacks.Add(Callback<P2PSessionConnectFail_t>.Create(delegate (P2PSessionConnectFail_t val)
  324.         {
  325.             Debug.LogError("P2P Error: " + val.m_steamIDRemote + " (" + val.m_eP2PSessionError + ")");
  326.  
  327.             if (TNServerInstance.isListening)
  328.             {
  329.                 var id = val.m_steamIDRemote;
  330.                 TcpProtocol tcp;
  331.  
  332.                 if (mOpen.TryGetValue(id, out tcp))
  333.                 {
  334.                     // Existing connection
  335.                     var p2p = tcp.custom as P2PConnection;
  336.                     if (p2p != null) p2p.OnDisconnect();
  337.                 }
  338.             }
  339.  
  340.             CancelInvoke("CancelConnect");
  341.             CancelConnect();
  342.         }));
  343.  
  344.         // Join a friend -- this is used when Steam launches the game as a result of choosing "Join Game" from the friend's list
  345.         mCallbacks.Add(Callback<GameRichPresenceJoinRequested_t>.Create(delegate (GameRichPresenceJoinRequested_t val)
  346.         {
  347.             Debug.Log("P2P Join " + val.m_rgchConnect);
  348.  
  349.             if (!TNManager.isConnected && !TNManager.isTryingToConnect)
  350.             {
  351.                 TNManager.playerName = username;
  352.                 var addr = val.m_rgchConnect;
  353.                 addr = addr.Replace("+connect ", "");
  354.                 if (!Connect(addr)) TNManager.Connect(addr);
  355.             }
  356.         }));
  357.  
  358.         TNManager.onDisconnect += OnDisconnect;
  359.         TNManager.onConnect += OnConnect;
  360.         TNManager.onUpdate += OnUpdate;
  361.     }
  362.  
  363.     /// <summary>
  364.     /// Connection notification: allow friends to join (if on a public server), and set the visible presence.
  365.     /// </summary>
  366.  
  367.     static void OnConnect (bool success, string msg)
  368.     {
  369.         // This is what makes it possible for your Steam friends to right-click your name and join your game.
  370.         // If you want to control when this joining will be allowed or disallowed instead, call this yourself from a more appropriate place.
  371.         AllowFriendsToJoin(success);
  372.     }
  373.  
  374.     /// <summary>
  375.     /// Notification of being disconnected from the game server.
  376.     /// </summary>
  377.  
  378.     static void OnDisconnect ()
  379.     {
  380.         if (mIsActive && TNServerInstance.isActive)
  381.         {
  382.             var p2p = TNManager.custom as P2PConnection;
  383.  
  384.             if (p2p != null)
  385.             {
  386.                 p2p.OnDisconnect();
  387.                 Debug.Log("OnDisconnect " + mIsActive);
  388.             }
  389.         }
  390.  
  391.         AllowFriendsToJoin(false);
  392.     }
  393.  
  394.     void CancelConnect ()
  395.     {
  396.         var p2p = TNManager.custom as P2PConnection;
  397.  
  398.         if (p2p != null && p2p.connecting)
  399.         {
  400.             Debug.Log("CancelConnect " + (p2p != null ? p2p.connecting.ToString() : "?"));
  401.             TNManager.client.stage = TcpProtocol.Stage.NotConnected;
  402.             TNManager.onConnect(false, "Unable to connect");
  403.             TNManager.custom = null;
  404.         }
  405.     }
  406.  
  407.     /// <summary>
  408.     /// Start a new P2P connection with the specified player.
  409.     /// </summary>
  410.  
  411.     static public bool Connect (string str)
  412.     {
  413.         ulong steamID;
  414.  
  415.         if (mInst != null && isActive && !str.Contains(".") && ulong.TryParse(str, out steamID))
  416.         {
  417.             mInst.ConnectP2P(new CSteamID(steamID));
  418.             return true;
  419.         }
  420.         return false;
  421.     }
  422.  
  423.     /// <summary>
  424.     /// Start a new P2P connection with the specified player.
  425.     /// </summary>
  426.  
  427.     void ConnectP2P (CSteamID id)
  428.     {
  429.         if (TNManager.custom == null && !TNManager.isConnected && !TNManager.isTryingToConnect && mInst != null)
  430.         {
  431.             CancelInvoke("CancelConnect");
  432.  
  433.             var p2p = new P2PConnection();
  434.             p2p.id = id;
  435.             p2p.connecting = true;
  436.             TNManager.custom = p2p;
  437.             TNManager.client.stage = TcpProtocol.Stage.Verifying;
  438.  
  439.             Debug.Log("Connecting to " + id);
  440.  
  441.             var writer = BeginSend(Packet.RequestID);
  442.             writer.Write(Player.version);
  443.             writer.Write(TNManager.playerName);
  444.             writer.Write(TNManager.playerData);
  445.             EndSend(id);
  446.  
  447.             Invoke("CancelConnect", 8f);
  448.         }
  449. #if UNITY_EDITOR
  450.         else Debug.Log("Already connecting, ignoring");
  451. #endif
  452.     }
  453.  
  454.     static bool Send (CSteamID id, byte[] data, int offset, int size)
  455.     {
  456.         if (offset == 0)
  457.         {
  458.             // Single packet -- send it as-is
  459.             var retVal = SteamNetworking.SendP2PPacket(id, data, (uint)size, EP2PSend.k_EP2PSendReliable);
  460.             if (!retVal) Debug.LogError("P2P failed to send the packet (" + size + " bytes)");
  461.             return retVal;
  462.         }
  463.         else
  464.         {
  465.             // Multiple packets in the same buffer: Steam's API doesn't offer an offset index option, so the packet must be copied
  466.             var temp = Buffer.Create();
  467.             var writer = temp.BeginWriting();
  468.             writer.Write(data, offset, size);
  469.             temp.EndWriting();
  470.  
  471.             var retVal = SteamNetworking.SendP2PPacket(id, temp.buffer, (uint)size, EP2PSend.k_EP2PSendReliable);
  472.             if (!retVal) Debug.LogError("P2P failed to send the packet (" + size + " bytes)");
  473.             temp.Recycle();
  474.             return retVal;
  475.         }
  476.     }
  477.  
  478.     [System.NonSerialized] static Buffer mTempBuffer;
  479.  
  480.     static BinaryWriter BeginSend (Packet packet)
  481.     {
  482.         mTempBuffer = Buffer.Create();
  483.         return mTempBuffer.BeginPacket(packet);
  484.     }
  485.  
  486.     static bool EndSend (CSteamID id)
  487.     {
  488.         if (mTempBuffer != null)
  489.         {
  490.             var size = mTempBuffer.EndPacket();
  491.             var retVal = Send(id, mTempBuffer.buffer, 0, size);
  492.  
  493.             mTempBuffer.Recycle();
  494.             mTempBuffer = null;
  495.             return retVal;
  496.         }
  497.         return false;
  498.     }
  499.  
  500.     /// <summary>
  501.     /// Receive all incoming P2P packets.
  502.     /// </summary>
  503.  
  504.     static void OnUpdate ()
  505.     {
  506.         // If Steam's multiplayer is being used, we should run the callbacks more often to ensure that there is minimal delay between packets
  507.         if (TNServerInstance.isListening || (TNManager.custom as P2PConnection != null))
  508.         {
  509.             UnityEngine.Profiling.Profiler.BeginSample("Steam.OnUpdate(Run callbacks)");
  510.             SteamAPI.RunCallbacks();
  511.             UnityEngine.Profiling.Profiler.EndSample();
  512.         }
  513.  
  514.         uint size;
  515.         if (!SteamNetworking.IsP2PPacketAvailable(out size)) return;
  516.  
  517.         UnityEngine.Profiling.Profiler.BeginSample("Steam.OnUpdate(Process packets)");
  518.         CSteamID id;
  519.  
  520.         lock (mOpen)
  521.         {
  522.             for (; ; )
  523.             {
  524.                 if (mTemp == null || mTemp.Length < size) mTemp = new byte[size < 4096 ? 4096 : size];
  525.  
  526.                 if (SteamNetworking.ReadP2PPacket(mTemp, size, out size, out id))
  527.                 {
  528.                     AddPacketP2P(id, mTemp, size);
  529.                 }
  530.  
  531.                 if (!SteamNetworking.IsP2PPacketAvailable(out size))
  532.                 {
  533.                     UnityEngine.Profiling.Profiler.EndSample();
  534.                     return;
  535.                 }
  536.             }
  537.         }
  538.     }
  539.  
  540.     /// <summary>
  541.     /// Add an incoming network packet to be processed.
  542.     /// </summary>
  543.  
  544.     static void AddPacketP2P (CSteamID id, byte[] data, uint size)
  545.     {
  546. #if UNITY_EDITOR
  547.         if (!Application.isPlaying) return;
  548. #endif
  549.         TcpProtocol tcp;
  550.  
  551.         if (mOpen.TryGetValue(id, out tcp))
  552.         {
  553.             // Existing connection
  554.             var p2p = tcp.custom as P2PConnection;
  555.             if (p2p != null && p2p.connecting) p2p.connecting = false;
  556.         }
  557.         else if (TNServerInstance.isListening)
  558.         {
  559.             // New connection
  560.             var p2p = new P2PConnection();
  561.             p2p.id = id;
  562.  
  563.             lock (mOpen)
  564.             {
  565.                 tcp = TNServerInstance.AddPlayer(p2p);
  566.                 mOpen[id] = tcp;
  567.                 mClosed.Remove(id);
  568.             }
  569.         }
  570.         else if (TNManager.custom != null)
  571.         {
  572.             // New connection
  573.             var p2p = TNManager.custom as P2PConnection;
  574.             if (p2p == null) return;
  575.  
  576.             p2p.id = id;
  577.             tcp = TNManager.client.protocol;
  578.  
  579.             lock (mOpen)
  580.             {
  581.                 mOpen[id] = tcp;
  582.                 mClosed.Remove(id);
  583.             }
  584.         }
  585.         else return;
  586.  
  587.         tcp.OnReceive(data, 0, (int)size);
  588.     }
  589.  
  590.     /// <summary>
  591.     /// Cleanup.
  592.     /// </summary>
  593.  
  594.     void OnDestroy ()
  595.     {
  596.         if (mInst == this)
  597.         {
  598.             TNManager.onDisconnect -= OnDisconnect;
  599.             TNManager.onConnect -= OnConnect;
  600.             TNManager.onUpdate -= OnUpdate;
  601.  
  602.             mInst = null;
  603.             foreach (var pair in mOpen) SteamNetworking.CloseP2PSessionWithUser(pair.Key);
  604.             foreach (var id in mClosed) SteamNetworking.CloseP2PSessionWithUser(id);
  605.             mClosed.Clear();
  606.             mOpen.Clear();
  607.  
  608.             SteamAPI.Shutdown();
  609.             mIsActive = false;
  610.         }
  611.     }
  612.  
  613.     /// <summary>
  614.     /// If set to 'true', friends will be able to right-click to join the game.
  615.     /// If the server is not actually listening for incoming connections, the 'allow' parameter is going to be treated as 'false'.
  616.     /// </summary>
  617.  
  618.     static public void AllowFriendsToJoin (bool allow)
  619.     {
  620.         if (mIsActive)
  621.         {
  622.             if (allow)
  623.             {
  624.                 if (TNServerInstance.isActive)
  625.                 {
  626.                     if (TNServerInstance.isListening)
  627.                     {
  628.                         // This would allow joining by IP:
  629.                         //SteamFriends.SetRichPresence("connect", "+connect " + Tools.externalAddress + ":" + TNServerInstance.listeningPort);
  630.  
  631.                         // This allows joining by player ID:
  632.                         SteamFriends.SetRichPresence("connect", "+connect " + mUserID);
  633.                     }
  634.                     else SteamFriends.SetRichPresence("connect", "");
  635.                 }
  636.                 else if (TNManager.client != null && TNManager.client.custom != null)
  637.                 {
  638.                     var p2p = TNManager.client.custom as P2PConnection;
  639.                     if (p2p != null) SteamFriends.SetRichPresence("connect", "+connect " + p2p.id);
  640.                     else SteamFriends.SetRichPresence("connect", "");
  641.                 }
  642.                 else if (TNManager.isConnected)
  643.                 {
  644.                     SteamFriends.SetRichPresence("connect", "+connect " + TNManager.tcpEndPoint);
  645.                 }
  646.                 else SteamFriends.SetRichPresence("connect", "");
  647.             }
  648.             else SteamFriends.SetRichPresence("connect", "");
  649.         }
  650.     }
  651. #else
  652.     static public bool isActive { get { return false; } }
  653.     static public ulong userID { get { if (mID == 0) mID = (ulong)SystemInfo.deviceUniqueIdentifier.GetHashCode(); return mID; } }
  654.     static public string username { get { return "Guest"; } }
  655.     static internal string autoConnectString { get { return null; } }
  656.  
  657.     static public bool Connect (string str) { return false; }
  658.     static public void AllowFriendsToJoin (bool allow) { }
  659.     static internal void SetPresence (string key, string text) { }
  660. #endif // STEAM
  661. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement