maxp0wer789

CustomProtocol-RaftModding

Jan 17th, 2021 (edited)
505
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Reflection;
  4. using HarmonyLib;
  5. using Steamworks;
  6. using UnityEngine;
  7.  
  8. /// <summary>
  9. /// Example of sending messages through the raft network without the need to send them to blocks/behaviours directly.
  10. ///
  11. /// This mod hooks into the <see cref="NetworkUpdateManager.Deserialize(Packet_Multiple, Steamworks.CSteamID)"/> method and checks each network package received for a specific custom type.
  12. /// If the message contains this type the custom deserialize method takes care of processing the request and overwrites the default behaviour.
  13. /// In order to send messages across the network we will need to hijack a carrier dto class preferable deriving from <see cref="Message"/> directly
  14. /// which is not used in the game channel and contains a string for us to work with. <see cref="Message_InitiateConnection"/> fulfills all those requirements.
  15. /// As <see cref="Message_InitiateConnection"/> is never used on the game channel, our custom protocol should not alter the default vanilla behaviour.
  16. ///
  17. /// As the <see cref="Messages"/> type of the transfer object we use a short value <239 to not interfere with vanilla protocol types.
  18. /// Any negative non zero value or value greater 239 (state game version 12.01) can be used for custom protocol types.
  19. /// For better readability the mods wraps our custom protocol type in the <see cref="ECustomProtocolType"/> enum.
  20. ///
  21. /// Being able to send strings we can now send any data, even implement our custom protocol using e.g. json strings and only use the InitiateConnection message as a carrier.
  22. ///
  23. /// </summary>
  24. public class SendOnHandshake : Mod
  25. {
  26.     private Harmony mi_harmony;
  27.  
  28.     private static short mi_defaultMessagesOffset;
  29.  
  30.     private void Start()
  31.     {
  32.         mi_harmony = new Harmony("pp.test");
  33.         mi_harmony.PatchAll(Assembly.GetExecutingAssembly());
  34.  
  35.         //to quickly check if the incoming message is an internal raft one, we remember rafts built-in messages enum member count.
  36.         //any message with a type lower this value is built-in and thus can be ignored by our mod
  37.         mi_defaultMessagesOffset = (short)System.Enum.GetNames(typeof(Messages)).Length;
  38.  
  39.         Semih_Network.OnPlayerInitialized -= OnPlayerInitialized;
  40.         //as an example we will hook into the player connection callback. After an incomming player connection has been approved the local player is initialized
  41.         //we use the event being called to hook into the player connection.
  42.         Semih_Network.OnPlayerInitialized += OnPlayerInitialized;
  43.     }
  44.  
  45.     private void OnDestroy()
  46.     {
  47.         if(mi_harmony != null)
  48.         {
  49.             mi_harmony.UnpatchAll("pp.test");
  50.         }
  51.  
  52.         Semih_Network.OnPlayerInitialized -= OnPlayerInitialized;
  53.     }
  54.  
  55.     /// <summary>
  56.     /// Called whenever a new network player joined the session. World has not been created at this point so be careful.
  57.     /// Use later callbacks if you want to alter the scene directly.
  58.     /// </summary>
  59.     /// <param name="playerNetwork"></param>
  60.     private void OnPlayerInitialized(Network_Player _playerNetwork)
  61.     {
  62.         if (_playerNetwork.IsLocalPlayer || !Semih_Network.IsHost) return; //make sure we do not send stuff to ourself and make sure we are the host before proceeding
  63.  
  64.         //Sending example data to the connecting client, the string here could be anything e.g. a json string.
  65.         Debug.Log("Player connected! Sending data...");
  66.         SendData(_playerNetwork.steamID, ECustomProtocolType.DATA, "Sending you arbitrary data...This could be anything!");
  67.     }
  68.  
  69.     /// <summary>
  70.     /// Send string data to the specified client with the specified protocol type.
  71.     /// </summary>
  72.     private static void SendData(CSteamID _steamID, ECustomProtocolType _type,  string _data)
  73.     {
  74.         //we are hijacking any network object preferable deriving from message directly which is not used in the game channel and contains a string for us to work with.
  75.         //Message_InitiateConnection fullfills all of those requirements.
  76.         var network = ComponentManager<Semih_Network>.Value;
  77.         network.SendP2P(
  78.             _steamID,
  79.             new Message_InitiateConnection((Messages)_type, -1, _data),
  80.             EP2PSend.k_EP2PSendReliable,
  81.             NetworkChannel.Channel_Game);
  82.     }
  83.  
  84.     [HarmonyPatch(typeof(NetworkUpdateManager), "Deserialize")]
  85.     public class CHarmonyPatch_NetworkUpdateManager_Deserialize
  86.     {
  87.         [HarmonyPrefix]
  88.         private static bool NetworkUpdateManager_Deserialize(Packet_Multiple packet, CSteamID remoteID) //the method provides all packages being received and the steam id of the client from which the package originated
  89.         {
  90.             List<Message> resultMessages    = packet.messages.ToList(); //copy messages again, those will be passed to the vanilla method
  91.             List<Message> messages          = packet.messages.ToList(); //get all transfer objects received
  92.             foreach (Message package in messages)
  93.             {
  94.                 if (package.t < mi_defaultMessagesOffset)
  95.                 {
  96.                     continue; //this is a raft message, ignore this package
  97.                 }
  98.  
  99.                 var msg = package as Message_InitiateConnection;
  100.                 if (msg == null) continue; //check if this is our custom type
  101.  
  102.                 resultMessages.Remove(package); //if we find a valid type remove it from the messages that are passed to the vanilla method
  103.  
  104.                 switch ((ECustomProtocolType)msg.t) //raft is blocking type <=239 so we use anything higher than that
  105.                 {
  106.                     case ECustomProtocolType.DATA:
  107.                         if (!Semih_Network.IsHost)
  108.                         {
  109.                             //received data from host! we could react here on the client side with whatever we want
  110.                             //just keep in mind that the data is sent from the player initialized event and the world has not been loaded yet.
  111.                             //we access the received data through the password field. this member contains our data.
  112.                             Debug.Log("We received data from host \"" + msg.password + "\". Sending confirmation...");
  113.                             //as an example we send a confirmation back to the host
  114.                             SendData(remoteID, ECustomProtocolType.RECEIVED_DATA, "data received!");
  115.                         }
  116.                         break;
  117.                     case ECustomProtocolType.RECEIVED_DATA:
  118.                         if (Semih_Network.IsHost)
  119.                         {
  120.                             //client received our data and sent us a confirmation
  121.                             Debug.Log("Client received our data!");
  122.                         }
  123.                         break;
  124.                 }
  125.             }
  126.  
  127.             if (resultMessages.Count == 0) return false; //no packages left, nothing todo. Dont even call the vanilla method
  128.  
  129.             //we remove all custom messages from the provided package and reassign the modified list so it is passed to the vanilla method.
  130.             //this is to make sure we dont lose any vanilla packages
  131.             packet.messages = resultMessages.ToArray();
  132.             return true; //nothing for the mod left to do here, let the vanilla behaviour take over
  133.         }
  134.     }
  135.  
  136.     /// <summary>
  137.     /// Deriving from short to match the raft <see cref="Messages"/> type.
  138.     /// </summary>
  139.     public enum ECustomProtocolType : short
  140.     {
  141.         DATA            = short.MaxValue - 1, //just an example value >239
  142.         RECEIVED_DATA   = short.MaxValue - 2
  143.     }
  144. }
RAW Paste Data