Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using HarmonyLib;
- using Steamworks;
- using UnityEngine;
- /// <summary>
- /// Example of sending messages through the raft network without the need to send them to blocks/behaviours directly.
- ///
- /// 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.
- /// If the message contains this type the custom deserialize method takes care of processing the request and overwrites the default behaviour.
- /// In order to send messages across the network we will need to hijack a carrier dto class preferable deriving from <see cref="Message"/> directly
- /// 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.
- /// As <see cref="Message_InitiateConnection"/> is never used on the game channel, our custom protocol should not alter the default vanilla behaviour.
- ///
- /// As the <see cref="Messages"/> type of the transfer object we use a short value <239 to not interfere with vanilla protocol types.
- /// Any negative non zero value or value greater 239 (state game version 12.01) can be used for custom protocol types.
- /// For better readability the mods wraps our custom protocol type in the <see cref="ECustomProtocolType"/> enum.
- ///
- /// 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.
- ///
- /// </summary>
- public class SendOnHandshake : Mod
- {
- private Harmony mi_harmony;
- private static short mi_defaultMessagesOffset;
- private void Start()
- {
- mi_harmony = new Harmony("pp.test");
- mi_harmony.PatchAll(Assembly.GetExecutingAssembly());
- //to quickly check if the incoming message is an internal raft one, we remember rafts built-in messages enum member count.
- //any message with a type lower this value is built-in and thus can be ignored by our mod
- mi_defaultMessagesOffset = (short)System.Enum.GetNames(typeof(Messages)).Length;
- Semih_Network.OnPlayerInitialized -= OnPlayerInitialized;
- //as an example we will hook into the player connection callback. After an incomming player connection has been approved the local player is initialized
- //we use the event being called to hook into the player connection.
- Semih_Network.OnPlayerInitialized += OnPlayerInitialized;
- }
- private void OnDestroy()
- {
- if(mi_harmony != null)
- {
- mi_harmony.UnpatchAll("pp.test");
- }
- Semih_Network.OnPlayerInitialized -= OnPlayerInitialized;
- }
- /// <summary>
- /// Called whenever a new network player joined the session. World has not been created at this point so be careful.
- /// Use later callbacks if you want to alter the scene directly.
- /// </summary>
- /// <param name="playerNetwork"></param>
- private void OnPlayerInitialized(Network_Player _playerNetwork)
- {
- 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
- //Sending example data to the connecting client, the string here could be anything e.g. a json string.
- Debug.Log("Player connected! Sending data...");
- SendData(_playerNetwork.steamID, ECustomProtocolType.DATA, "Sending you arbitrary data...This could be anything!");
- }
- /// <summary>
- /// Send string data to the specified client with the specified protocol type.
- /// </summary>
- private static void SendData(CSteamID _steamID, ECustomProtocolType _type, string _data)
- {
- //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.
- //Message_InitiateConnection fullfills all of those requirements.
- var network = ComponentManager<Semih_Network>.Value;
- network.SendP2P(
- _steamID,
- new Message_InitiateConnection((Messages)_type, -1, _data),
- EP2PSend.k_EP2PSendReliable,
- NetworkChannel.Channel_Game);
- }
- [HarmonyPatch(typeof(NetworkUpdateManager), "Deserialize")]
- public class CHarmonyPatch_NetworkUpdateManager_Deserialize
- {
- [HarmonyPrefix]
- 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
- {
- List<Message> resultMessages = packet.messages.ToList(); //copy messages again, those will be passed to the vanilla method
- List<Message> messages = packet.messages.ToList(); //get all transfer objects received
- foreach (Message package in messages)
- {
- if (package.t < mi_defaultMessagesOffset)
- {
- continue; //this is a raft message, ignore this package
- }
- var msg = package as Message_InitiateConnection;
- if (msg == null) continue; //check if this is our custom type
- resultMessages.Remove(package); //if we find a valid type remove it from the messages that are passed to the vanilla method
- switch ((ECustomProtocolType)msg.t) //raft is blocking type <=239 so we use anything higher than that
- {
- case ECustomProtocolType.DATA:
- if (!Semih_Network.IsHost)
- {
- //received data from host! we could react here on the client side with whatever we want
- //just keep in mind that the data is sent from the player initialized event and the world has not been loaded yet.
- //we access the received data through the password field. this member contains our data.
- Debug.Log("We received data from host \"" + msg.password + "\". Sending confirmation...");
- //as an example we send a confirmation back to the host
- SendData(remoteID, ECustomProtocolType.RECEIVED_DATA, "data received!");
- }
- break;
- case ECustomProtocolType.RECEIVED_DATA:
- if (Semih_Network.IsHost)
- {
- //client received our data and sent us a confirmation
- Debug.Log("Client received our data!");
- }
- break;
- }
- }
- if (resultMessages.Count == 0) return false; //no packages left, nothing todo. Dont even call the vanilla method
- //we remove all custom messages from the provided package and reassign the modified list so it is passed to the vanilla method.
- //this is to make sure we dont lose any vanilla packages
- packet.messages = resultMessages.ToArray();
- return true; //nothing for the mod left to do here, let the vanilla behaviour take over
- }
- }
- /// <summary>
- /// Deriving from short to match the raft <see cref="Messages"/> type.
- /// </summary>
- public enum ECustomProtocolType : short
- {
- DATA = short.MaxValue - 1, //just an example value >239
- RECEIVED_DATA = short.MaxValue - 2
- }
- }
Add Comment
Please, Sign In to add comment