321 lines
9.1 KiB
C#
321 lines
9.1 KiB
C#
#if !BESTHTTP_DISABLE_SOCKETIO
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
using BestHTTP.SocketIO;
|
|
|
|
namespace BestHTTP.Examples
|
|
{
|
|
public sealed class SocketIOChatSample : MonoBehaviour
|
|
{
|
|
private readonly TimeSpan TYPING_TIMER_LENGTH = TimeSpan.FromMilliseconds(700);
|
|
|
|
private enum ChatStates
|
|
{
|
|
Login,
|
|
Chat
|
|
}
|
|
|
|
#region Fields
|
|
|
|
/// <summary>
|
|
/// The Socket.IO manager instance.
|
|
/// </summary>
|
|
private SocketManager Manager;
|
|
|
|
/// <summary>
|
|
/// Current state of the chat demo.
|
|
/// </summary>
|
|
private ChatStates State;
|
|
|
|
/// <summary>
|
|
/// The selected nickname
|
|
/// </summary>
|
|
private string userName = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Currently typing message
|
|
/// </summary>
|
|
private string message = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Sent and received messages.
|
|
/// </summary>
|
|
private string chatLog = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Position of the scroller
|
|
/// </summary>
|
|
private Vector2 scrollPos;
|
|
|
|
/// <summary>
|
|
/// True if the user is currently typing
|
|
/// </summary>
|
|
private bool typing;
|
|
|
|
/// <summary>
|
|
/// When the message changed.
|
|
/// </summary>
|
|
private DateTime lastTypingTime = DateTime.MinValue;
|
|
|
|
/// <summary>
|
|
/// Users that typing.
|
|
/// </summary>
|
|
private List<string> typingUsers = new List<string>();
|
|
|
|
#endregion
|
|
|
|
#region Unity Events
|
|
|
|
void Start()
|
|
{
|
|
// The current state is Login
|
|
State = ChatStates.Login;
|
|
|
|
// Change an option to show how it should be done
|
|
SocketOptions options = new SocketOptions();
|
|
options.AutoConnect = false;
|
|
options.ConnectWith = BestHTTP.SocketIO.Transports.TransportTypes.WebSocket;
|
|
|
|
// Create the Socket.IO manager
|
|
Manager = new SocketManager(new Uri("https://socket-io-chat.now.sh/socket.io/"), options);
|
|
|
|
// Set up custom chat events
|
|
Manager.Socket.On("login", OnLogin);
|
|
Manager.Socket.On("new message", OnNewMessage);
|
|
Manager.Socket.On("user joined", OnUserJoined);
|
|
Manager.Socket.On("user left", OnUserLeft);
|
|
Manager.Socket.On("typing", OnTyping);
|
|
Manager.Socket.On("stop typing", OnStopTyping);
|
|
|
|
// The argument will be an Error object.
|
|
Manager.Socket.On(SocketIOEventTypes.Error, (socket, packet, args) => Debug.LogError(string.Format("Error: {0}", args[0].ToString())));
|
|
// We set SocketOptions' AutoConnect to false, so we have to call it manually.
|
|
Manager.Open();
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
// Leaving this sample, close the socket
|
|
Manager.Close();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
// Go back to the demo selector
|
|
if (Input.GetKeyDown(KeyCode.Escape))
|
|
SampleSelector.SelectedSample.DestroyUnityObject();
|
|
|
|
// Stop typing if some time passed without typing
|
|
if (typing)
|
|
{
|
|
var typingTimer = DateTime.UtcNow;
|
|
var timeDiff = typingTimer - lastTypingTime;
|
|
if (timeDiff >= TYPING_TIMER_LENGTH)
|
|
{
|
|
Manager.Socket.Emit("stop typing");
|
|
typing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
switch (State)
|
|
{
|
|
case ChatStates.Login: DrawLoginScreen(); break;
|
|
case ChatStates.Chat: DrawChatScreen(); break;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Chat Logic
|
|
|
|
/// <summary>
|
|
/// Called from an OnGUI event to draw the Login Screen.
|
|
/// </summary>
|
|
void DrawLoginScreen()
|
|
{
|
|
GUIHelper.DrawArea(GUIHelper.ClientArea, true, () =>
|
|
{
|
|
GUILayout.BeginVertical();
|
|
GUILayout.FlexibleSpace();
|
|
|
|
GUIHelper.DrawCenteredText("What's your nickname?");
|
|
userName = GUILayout.TextField(userName);
|
|
|
|
if (GUILayout.Button("Join"))
|
|
SetUserName();
|
|
|
|
GUILayout.FlexibleSpace();
|
|
GUILayout.EndVertical();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from an OnGUI event to draw the Chat Screen.
|
|
/// </summary>
|
|
void DrawChatScreen()
|
|
{
|
|
GUIHelper.DrawArea(GUIHelper.ClientArea, true, () =>
|
|
{
|
|
GUILayout.BeginVertical();
|
|
scrollPos = GUILayout.BeginScrollView(scrollPos);
|
|
GUILayout.Label(chatLog, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
|
GUILayout.EndScrollView();
|
|
|
|
string typing = string.Empty;
|
|
|
|
if (typingUsers.Count > 0)
|
|
{
|
|
typing += string.Format("{0}", typingUsers[0]);
|
|
|
|
for (int i = 1; i < typingUsers.Count; ++i)
|
|
typing += string.Format(", {0}", typingUsers[i]);
|
|
|
|
if (typingUsers.Count == 1)
|
|
typing += " is typing!";
|
|
else
|
|
typing += " are typing!";
|
|
}
|
|
|
|
GUILayout.Label(typing);
|
|
|
|
GUILayout.Label("Type here:");
|
|
|
|
GUILayout.BeginHorizontal();
|
|
message = GUILayout.TextField(message);
|
|
|
|
if (GUILayout.Button("Send", GUILayout.MaxWidth(100)))
|
|
SendMessage();
|
|
GUILayout.EndHorizontal();
|
|
|
|
if (GUI.changed)
|
|
UpdateTyping();
|
|
|
|
GUILayout.EndVertical();
|
|
});
|
|
}
|
|
|
|
void SetUserName()
|
|
{
|
|
if (string.IsNullOrEmpty(userName))
|
|
return;
|
|
|
|
State = ChatStates.Chat;
|
|
|
|
Manager.Socket.Emit("add user", userName);
|
|
}
|
|
|
|
void SendMessage()
|
|
{
|
|
if (string.IsNullOrEmpty(message))
|
|
return;
|
|
|
|
Manager.Socket.Emit("new message", message);
|
|
|
|
chatLog += string.Format("{0}: {1}\n", userName, message);
|
|
message = string.Empty;
|
|
}
|
|
|
|
void UpdateTyping()
|
|
{
|
|
if (!typing)
|
|
{
|
|
typing = true;
|
|
Manager.Socket.Emit("typing");
|
|
}
|
|
|
|
lastTypingTime = DateTime.UtcNow;
|
|
}
|
|
|
|
void addParticipantsMessage(Dictionary<string, object> data)
|
|
{
|
|
int numUsers = Convert.ToInt32(data["numUsers"]);
|
|
|
|
if (numUsers == 1)
|
|
chatLog += "there's 1 participant\n";
|
|
else
|
|
chatLog += "there are " + numUsers + " participants\n";
|
|
}
|
|
|
|
void addChatMessage(Dictionary<string, object> data)
|
|
{
|
|
var username = data["username"] as string;
|
|
var msg = data["message"] as string;
|
|
|
|
chatLog += string.Format("{0}: {1}\n", username, msg);
|
|
}
|
|
|
|
void AddChatTyping(Dictionary<string, object> data)
|
|
{
|
|
var username = data["username"] as string;
|
|
|
|
typingUsers.Add(username);
|
|
}
|
|
|
|
void RemoveChatTyping(Dictionary<string, object> data)
|
|
{
|
|
var username = data["username"] as string;
|
|
|
|
int idx = typingUsers.FindIndex((name) => name.Equals(username));
|
|
if (idx != -1)
|
|
typingUsers.RemoveAt(idx);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Custom SocketIO Events
|
|
|
|
void OnLogin(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
chatLog = "Welcome to Socket.IO Chat — \n";
|
|
|
|
addParticipantsMessage(args[0] as Dictionary<string, object>);
|
|
}
|
|
|
|
void OnNewMessage(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
addChatMessage(args[0] as Dictionary<string, object>);
|
|
}
|
|
|
|
void OnUserJoined(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
var data = args[0] as Dictionary<string, object>;
|
|
|
|
var username = data["username"] as string;
|
|
|
|
chatLog += string.Format("{0} joined\n", username);
|
|
|
|
addParticipantsMessage(data);
|
|
}
|
|
|
|
void OnUserLeft(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
var data = args[0] as Dictionary<string, object>;
|
|
|
|
var username = data["username"] as string;
|
|
|
|
chatLog += string.Format("{0} left\n", username);
|
|
|
|
addParticipantsMessage(data);
|
|
}
|
|
|
|
void OnTyping(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
AddChatTyping(args[0] as Dictionary<string, object>);
|
|
}
|
|
|
|
void OnStopTyping(Socket socket, Packet packet, params object[] args)
|
|
{
|
|
RemoveChatTyping(args[0] as Dictionary<string, object>);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
#endif |