/// <summary> /// When the client receives a greeting from the server. /// This makes us aware that the server is running VChat. /// </summary> private static void OnClientMessage(long senderId, string version) { if (senderId == ZNet.instance.GetServerPeer()?.m_uid) { VChatPlugin.Log($"Received a greeting from the server ({senderId}) that's running on {VChatPlugin.Name} {version}."); // Property to determine if both sides have VChat installed. if (!HasLocalPlayerReceivedGreetingFromServer) { HasLocalPlayerReceivedGreetingFromServer = true; ServerVersion = version; } // Send a response to the server if we haven't yet done so. if (!HasLocalPlayerGreetedToServer) { SendToServer(); HasLocalPlayerGreetedToServer = true; } } else { VChatPlugin.Log($"Ignoring a greeting received from a client with id {senderId} and version {version}."); } }
public static void Prefix(ref ZNet __instance) { // Reset the server greeting variables when the client disconnects. // These will be set once the client connects to a server. if (!ZNet.m_isServer) { VChatPlugin.Log("Resetting variables for local player"); PlayerPatchOnSpawned.HasSentServerPluginStatusMessage = false; GreetingMessage.ResetClientVariables(); } }
/// <summary> /// Register the global message commands, this should be called when ZNet initialises. /// </summary> public static void Register() { VChatPlugin.Log($"Registering custom routed messages for global chat."); if (ZNet.m_isServer) { ZRoutedRpc.instance.Register(GlobalChatHashName, new RoutedMethod <Vector3, int, string, string>(OnGlobalMessage_Server).m_action); } else { ZRoutedRpc.instance.Register(GlobalChatHashName, new RoutedMethod <Vector3, int, string, string>(OnGlobalMessage_Client).m_action); } }
/// <summary> /// Register the global message commands, this should be called when ZNet initialises. /// </summary> public static void Register() { VChatPlugin.Log($"Registering custom routed messages for greetings."); if (ZNet.m_isServer) { ZRoutedRpc.instance.Register(GreetingHashName, new RoutedMethod <string>(OnServerMessage).m_action); } else { ZRoutedRpc.instance.Register(GreetingHashName, new RoutedMethod <string>(OnClientMessage).m_action); } }
public static bool Prefix(ref ZRoutedRpc __instance, ref RoutedRPCData data) { if (ZNet.m_isServer && data?.m_methodHash == GlobalMessages.TalkerSayHashCode) { // Read local say chat messages for users not connected to VChat. // Messages that fit the global chat command name will be redirected as global chat messages. if (GreetingMessage.PeerInfo.TryGetValue(data.m_senderPeerID, out GreetingMessagePeerInfo peerInfo) && !peerInfo.HasReceivedGreeting) { try { var senderPeer = ZNet.instance.GetPeer(data.m_senderPeerID); var package = new ZPackage(data.m_parameters.GetArray()); var ctype = package.ReadInt(); var playerName = package.ReadString(); var text = package.ReadString(); if (ctype == (int)Talker.Type.Normal) { var globalChatCommand = VChatPlugin.CommandHandler.FindCommand(PluginCommandType.SendGlobalMessage); if (VChatPlugin.CommandHandler.IsValidCommandString(text, globalChatCommand, out text)) { VChatPlugin.Log($"Redirecting local message to global chat from peer {data.m_senderPeerID} \"({senderPeer?.m_playerName})\" with message \"{text}\"."); // Redirect this message to the global chat channel. foreach (var peer in ZNet.instance.GetConnectedPeers()) { // Exclude the sender, otherwise it'd probably just be annoying. if (peer.m_uid != data.m_senderPeerID) { GlobalMessages.SendGlobalMessageToPeer(peer.m_uid, (int)GlobalMessageType.RedirectedGlobalMessage, senderPeer?.m_refPos ?? new Vector3(), senderPeer?.m_playerName ?? playerName, text); } } // Intercept message so that other connected users won't receive the same message twice. data.m_methodHash = GlobalMessages.InterceptedSayHashCode; return(false); } } } catch (Exception ex) { VChatPlugin.LogError($"Error reading Talker.Say message for unconnected VChat user ({data.m_senderPeerID}): {ex}"); } } } return(true); }
/// <summary> /// Triggered when the server receives a global message from a client . /// </summary> /// <remarks>Player name and position are primarily here for future work, player aliases for example.</remarks> /// <param name="senderId">The peer id of the sender</param> /// <param name="pos">The reported position</param> /// <param name="type">Reserved for future use</param> /// <param name="callerName">The reported player name</param> /// <param name="text">the message, without a playername or formatting.</param> private static void OnGlobalMessage_Server(long senderId, Vector3 pos, int type, string callerName, string text) { if (senderId != ZNet.instance.GetServerPeer()?.m_uid) { var globalMessageType = (GlobalMessageType)type; if (globalMessageType == GlobalMessageType.StandardMessage || globalMessageType == GlobalMessageType.RedirectedGlobalMessage) { try { // Sender should always be found but who knows what can happen within a few milliseconds, though I bet its still cached should that player disconnect.. safety first. // We simply apply the position and player name the server knows rather than the reported values first. var peer = ZRoutedRpc.instance?.GetPeer(senderId); if (peer?.m_server == false) { // Loop through every connected peer and redirect the received message, including the original sender because the code is currently set so that the client knows that it's been sent. foreach (var connectedPeer in ZNet.instance.GetConnectedPeers()) { if (connectedPeer != null && !connectedPeer.m_server && connectedPeer.IsReady() && connectedPeer.m_socket?.IsConnected() == true) { VChatPlugin.Log($"Routing global message to peer {connectedPeer.m_uid} \"({connectedPeer.m_playerName})\" with message \"{text}\"."); SendGlobalMessageToPeer(connectedPeer.m_uid, type, peer?.m_refPos ?? pos, peer?.m_playerName ?? callerName, text); } } } else { VChatPlugin.LogWarning($"Received a global chat message from a peer identified as a server, id {senderId} \"{peer.m_playerName}\""); } } catch (Exception ex) { VChatPlugin.LogError($"Failed to InvokeRoutedRPC for global message ({senderId}|{text}): {ex}"); } } else { VChatPlugin.LogWarning($"A global message type with value of {type} could not be parsed. Please check if there are any updates available."); } } else { VChatPlugin.LogWarning($"Received a greeting from a peer with the server id..."); } }
public static void Prefix(ref ZNet __instance, ref ZRpc rpc, ref ZDOID characterID) { // Client sends the character id to the server before it spawns. if (ZNet.m_isServer) { ZNetPeer peer = __instance.GetPeer(rpc); if (peer != null) { if (!SpawnedPlayers.Contains(peer.m_uid)) { bool isPlayerConnectedWithVChat = false; if (GreetingMessage.PeerInfo.TryGetValue(peer.m_uid, out GreetingMessagePeerInfo peerInfo)) { isPlayerConnectedWithVChat = peerInfo.HasReceivedGreeting; } // Send a local chat message to the player if it does not have VChat installed. if (!isPlayerConnectedWithVChat) { VChatPlugin.Log($"Player \"{peer.m_playerName}\" ({peer.m_uid}) spawned that does not have VChat installed, sending mod information..."); var messages = new[] { $"This server runs {VChatPlugin.Name} {VChatPlugin.Version}, We detected that you do not have this mod installed.", $"You can find the latest version on {VChatPlugin.RepositoryUrl}", "Global chat messages will be sent in your local chat channel.", $"Type {VChatPlugin.Settings.CommandPrefix}{VChatPlugin.Settings.GlobalChatCommandName.FirstOrDefault()} [text] to send a message to the global chat.", }; foreach (var message in messages) { object[] parameters = new object[] { peer.GetRefPos(), (int)Talker.Type.Normal, VChatPlugin.Name, message }; ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "ChatMessage", parameters); } } SpawnedPlayers.Add(peer.m_uid); } } } }
/// <summary> /// Triggered when the client receives a global message from the server. /// </summary> /// <remarks>Player name and position are primarily here for future work, player aliases for example.</remarks> /// <param name="senderId">The peer id of the sender</param> /// <param name="pos">The position</param> /// <param name="type">Reserved for future use</param> /// <param name="playerName">The player name</param> /// <param name="text">the message, without a playername or formatting.</param> private static void OnGlobalMessage_Client(long senderId, Vector3 pos, int type, string playerName, string text) { // If the client is connected to a server-sided instance of VChat then only accept messages from the server. if (!GreetingMessage.HasLocalPlayerReceivedGreetingFromServer || senderId == ZNet.instance.GetServerPeer()?.m_uid) { VChatPlugin.Log($"Received a global message from {playerName} ({senderId}) on location {pos} with message \"{text}\"."); if (Chat.instance != null) { var formattedMessage = VChatPlugin.GetFormattedMessage(new CombinedMessageType(CustomMessageType.Global), playerName, text); Chat.instance?.AddString(formattedMessage); } else { VChatPlugin.LogWarning($"Received a message but Chat instance is undefined."); } } else { VChatPlugin.LogWarning($"Ignoring a global message received from a client, reported values: {senderId} \"{playerName}\" on location {pos} with message \"{text}\"."); } }
/// <summary> /// When the server receives a greeting from the client. /// This makes us aware that the user has VChat installed. /// </summary> private static void OnServerMessage(long senderId, string version) { if (senderId != ZNet.instance.GetServerPeer()?.m_uid) { var peer = ZRoutedRpc.instance?.GetPeer(senderId); if (peer != null) { VChatPlugin.Log($"Greeting received from client \"{peer?.m_playerName}\" ({senderId}) with version {version}."); GreetingMessagePeerInfo peerInfo; if (PeerInfo.TryGetValue(senderId, out GreetingMessagePeerInfo previousGreeting)) { peerInfo = previousGreeting; peerInfo.Version = version; peerInfo.HasReceivedGreeting = true; } else { peerInfo = new GreetingMessagePeerInfo() { PeerId = senderId, Version = version, HasReceivedGreeting = true, HasSentGreeting = false, }; } PeerInfo.AddOrUpdate(senderId, peerInfo, (long oldKey, GreetingMessagePeerInfo oldValue) => peerInfo); } else { VChatPlugin.LogWarning($"Received greeting from an unconnected peer with id {senderId}."); } } else { VChatPlugin.LogWarning($"Received a greeting from a peer with the server id..."); } }