using UnityEngine; //This gives us monobehaviour
using UnityEngine.UI; //This gives us UI classes
using System.Collections; //Known
using System.Collections.Generic; //Known
using UnityEngine.Networking; //This gives us NetworkBehaviour and it's derived methods
using UnityEngine.Networking.NetworkSystem; //This gives us the MsgTypes
//Declaring the namespace all my scripts are found under
namespace Panzerwolf
{
//Declaring public class
//This will be directly attached to a physical object in the game,
//and thus derives from UnityEngine.MonoBehaviour
//If you see functions here that aren't being called through code,
//they are being called through UI elements in the program
public class gameChatClient : MonoBehaviour
{
public int xoffset = 0;
//Unity stuff
[SerializeField] InputField input;
[SerializeField]Transform chatWindow;
[SerializeField]Transform tabBar;
[SerializeField]GameObject textPrefab;
[SerializeField]GameObject tabPrefab;
//The timer that will count down towards a timeout
float serverConnectTimeoutCounter = 11;
//The timeout limit that the counter will reset to
public int timeoutLimit = 10;
//Self explanatory by now
public string chatServerHost = "127.0.0.1";
public int chatServerPort = 9999;
public string chatName = "someDude";
public ChatPerson me;
//See previous explanations
public Dictionary<ChatChannelId, ChatChannel> channels = new Dictionary<ChatChannelId, ChatChannel>();
public Dictionary<ChatPersonId, ChatPerson> people = new Dictionary<ChatPersonId, ChatPerson>();
public List<bool> channelTabs = new List<bool> ();
//NetworkClients are required to connect to NetworkServers(well, duh!)
//It is set to null by default, and created using code later.
NetworkClient client = null;
//This is the identifier I use for the code to know which channel is the active one
public ChatChannelId chatchannelint;
public string talkText = "Hello!";
const int kMaxChannelMessages = 5;
//The start function is called when an object is instantiated in Unity
void Start()
{
//Call the Setup function
Setup ();
}
//Setup the client
void Setup()
{
//Make client a new instance of type NetworkClient
client = new NetworkClient();
//Register all the handlers for NetworkMessages
//When the client receives a NetworkMessage,
//it will check the MsgType id (short int) of the NetworkMessage
//and compare it to its own check.
//If the comparison returns true, the delegate method hook is called
//The rest should with the given explanation explain itself
client.RegisterHandler(MsgType.Connect, OnConnect);
client.RegisterHandler(ChatMsg.Login, OnLogin);
client.RegisterHandler(ChatMsg.ChannelJoin, OnChannelJoin);
client.RegisterHandler(ChatMsg.ChannelLeave, OnChannelLeave);
client.RegisterHandler(ChatMsg.Talk, OnTalk);
client.RegisterHandler(ChatMsg.ChannelFailed, OnChannelJoinFailed);
//Connect the client using the variables chatServerHost and chatServerPort
client.Connect (chatServerHost, chatServerPort);
//Log to console
Debug.Log ("Client set up, trying to connect...");
//Set the timer to 0 so it begins counting up towards 10
serverConnectTimeoutCounter = 0;
}
//Hook methods are explained under Handlers.
//These get called by their corresponding handler.
void OnConnect(NetworkMessage netMsg)
{
//We have established a connection, now log in to the server
//Call the login method
Login();
}
void OnChannelJoinFailed(NetworkMessage netMsg)
{
Debug.LogError ("Failed to find channel! Check spelling and try again.");
}
void OnLogin(NetworkMessage netMsg)
{
//Decode the encrypted NetworkMessage using a reader of the correct type
var msg = netMsg.ReadMessage<LoginResponseMessage>();
//Give the clients instance of ChatPerson a name and an ID from the server
me = new ChatPerson(msg.personName, msg.personId);
//Add the client to his own people list
people[me.personId] = me;
//Here I've setup a select bunch of default channels
//the client will join automatically
JoinChannel ("Global");
JoinChannel ("Local");
JoinChannel ("Party");
JoinChannel ("Blades of Urdual");
}
void OnChannelJoin(NetworkMessage netMsg)
{
//See above
var msg = netMsg.ReadMessage<ChannelJoinResponseMessage>();
Debug.Log ("OnChannelJoin called");
//Declare TEMPORARY variable instance.
//This instance will only exist inside this method
ChatChannel channel;
//Check if we already have the channel on our list
if (channels.ContainsKey(msg.channelId))
{
//Set the channel to join equal to the channel on the list
channel = channels[msg.channelId];
}
//if not
else
{
//Set channel to be equal to a new client version reference instance copy of the channel from the server
channel = new ChatChannel(msg.channelName, msg.channelId, msg.channelColor, msg.channelAlias);
//Add it to list of joined channels
channels[channel.channelId] = channel;
}
//Check if people contains the person joining already
if (!people.ContainsKey(msg.personId))
{
//if not make a new reference instance copy
var newPerson = new ChatPerson(msg.personName, msg.personId);
people[newPerson.personId] = newPerson;
}
//Set temporary variable instance
var person = people[msg.personId];
channel.ClientAdd(person);
//If the channelname in question is "Global"
if(msg.channelName == "Global")
{
//Set it to be the currently active one
chatchannelint = msg.channelId;
}
//This will only happen once per gameChatClient run
//and servers as the way to make Global the default chat
//(as we cannot have it be null)
//Check if the client is the one joining the channel
if(msg.personId == me.personId)
{
//Unity stuff for GUI
if (!GameObject.Find (msg.channelName + " channel"))
{
GameObject tab = Instantiate (tabPrefab);
tab.name = msg.channelName + " channel";
tab.transform.SetParent (tabBar);
tab.transform.localScale = new Vector3 (1, 1, 1);
tab.transform.GetChild (0).GetComponent<Text> ().text = msg.channelAlias;
tab.GetComponent<Button> ().onClick.AddListener (() => ChangeChannel (msg.channelId));
}
}
//**NOT USED**
//This was for another version of the ChatClient that could leave and join channels at will
// leaveChannel.SetActive (true);
// joinChannel.SetActive (false);
//Log to console
Debug.Log("Client joined channel " + msg.channelId + " " + person.personId);
}
void OnChannelLeave(NetworkMessage netMsg)
{
//See above
var msg = netMsg.ReadMessage<ChannelLeaveResponseMessage>();
//chatChannelId = msg.channelId;
//See above
ChatChannel channel;
//See above
if (channels.ContainsKey(msg.channelId))
{
//See above
channel = channels[msg.channelId];
}
//See above
else
{
//See above
Debug.LogError("Leave channel not found:" + msg.channelId);
return;
}
//See above
if (msg.personId == me.personId)
{
//Remove channel from clients channel list
channels.Remove(msg.channelId);
//See above
Debug.Log("Client left channel " + msg.channelId );
return;
}
//See above
ChatPerson person;
//See above
if (people.ContainsKey(msg.personId))
{
person = people[msg.personId];
}
//See above
else
{
Debug.LogError("Leave person not found:" + msg.personId);
return;
}
//Remove the client from the channel
channel.ClientRemove(person);
Debug.Log("Other left channel " + msg.channelId + " " + person.personId);
}
void OnTalk(NetworkMessage netMsg)
{
//See above
var msg = netMsg.ReadMessage<TalkMessage>();
//See above
var channel = channels[msg.channelId];
//See above
var person = channel.people[msg.personId];
//Early debugging, I still keep it cause it looks cool in the console lol
Debug.Log("Client Talk: [" + channel.channelName + "] " + person.personName + ": " + msg.text);
//Add the message to the specified channels list of messages
channel.messages.Add(msg);
//If the total count of messages exceeds the maxcount
if (channel.messages.Count > kMaxChannelMessages)
{
//Remove the oldest message
channel.messages.RemoveAt(0);
}
//Draw the messages in Unity
DrawMsg ();
}
//Regular methods
public void Login()
{
//Successful Login, set the timer above 10 so it stops counting
serverConnectTimeoutCounter = 11;
//Create a new NetworkMessage of type LoginMessage
var msg = new LoginMessage();
msg.personName = chatName;
//client.Send will send to the server alone
client.Send(ChatMsg.Login, msg);
Debug.Log("Client login");
}
//This is all Unity Stuff
void ChangeChannel(ChatChannelId channelid)
{
for(int i = 0; i < chatWindow.childCount; i++)
{
Destroy (chatWindow.GetChild (i).gameObject);
}
GameObject.Find (channels [chatchannelint].channelName + " channel").GetComponent<Image> ().color = Color.white;
chatchannelint = channelid;
GameObject.Find (channels [chatchannelint].channelName + " channel").GetComponent<Image> ().color = Color.gray;
// Debug.Log ("ChangeChannel called");
//Draws old messages
foreach(var t in channels[chatchannelint].messages)
{
string Name = channels [chatchannelint].people [t.personId].personName + " msg nr " + channels[channelid].messages.FindIndex (x => x.text == t.text).ToString ();
if (GameObject.Find (Name))
Destroy (GameObject.Find (Name));
GameObject text = Instantiate (textPrefab);
text.name = Name;
text.transform.SetParent (chatWindow);
text.transform.GetChild (0).GetChild (0).GetComponent<Text> ().text = channels [chatchannelint].people [t.personId].personName;
text.transform.GetChild (1).GetComponent<Text> ().text = t.text;
text.transform.GetChild (1).GetComponent<Text> ().color = Color.grey;
}
}
void Update()
{
if(serverConnectTimeoutCounter <= 10)
{
serverConnectTimeoutCounter += Time.deltaTime;
if (serverConnectTimeoutCounter > 10 && serverConnectTimeoutCounter < 11)
{
Debug.LogError ("Connect failed, are you sure you have the right IP/PORT?");
StopClient ();
serverConnectTimeoutCounter = 11;
}
}
}
//**NOT USED**
//Also used for the previous client version where client could start and stop NetworkClient at will
// public void StartClient()
// {
// if (serverInput.text == string.Empty)
// {
// Debug.LogError ("Enter an IP adress!");
// return;
// }
//
// if(portInput.text == string.Empty)
// {
// Debug.LogError("Enter a port!");
// return;
// }
//
// stopClient.SetActive (true);
// nameInput.gameObject.SetActive (false);
// serverInput.gameObject.SetActive (false);
// portInput.gameObject.SetActive (false);
// startClient.SetActive (false);
// Setup ();
//
// }
public void StopClient()
{
//For each channel
foreach(var ch in channels.Values)
{
//Leave the channel(ServerSide)
ch.LeaveServer (me);
}
//Leave all channels(ClientSide)
LeaveChannel ();
//Reset timeout counter to above 10 so it doesn't count
serverConnectTimeoutCounter = 11;
//**NOT USED**
//Other client version
// nameInput.gameObject.SetActive (true);
// stopClient.SetActive (false);
// serverInput.gameObject.SetActive (true);
// portInput.gameObject.SetActive (true);
// channelInput.gameObject.SetActive (false);
// joinChannel.SetActive (false);
// startClient.SetActive (true);
//Disconnect the client from the server
client.Disconnect();
//Reset the client instance to null
client = null;
}
// public void JoinChannel()
// {
// Debug.Log ("JoinChannel called");
// var response = new ChannelJoinRequestMessage ();
//
// response = new ChannelJoinRequestMessage ();
// response.name = channelInput.text;
// response.personId = myChatPerson.personId;
//
// client.connection.Send (myChatMsg.ChannelJoin, response);
// channelInput.text = string.Empty;
// Debug.Log("Client Login myChatPerson " + myChatPerson.personId);
// }
//You should be able to understand this one now
public void JoinChannel(string name)
{
Debug.Log ("JoinChannel by name called");
var response = new ChannelJoinRequestMessage ();
response = new ChannelJoinRequestMessage ();
response.name = name;
response.personId = me.personId;
client.connection.Send (ChatMsg.ChannelJoin, response);
Debug.Log("Client Login myChatPerson " + me.personId);
}
//You should be able to understand this one too now
public void LeaveChannel()
{
Debug.Log ("Left channels");
foreach(var c in channels.Keys)
{
var leaveMsg = new ChannelLeaveRequestMessage();
leaveMsg.channelId = c;
leaveMsg.personId = me.personId;
client.Send(ChatMsg.ChannelLeave, leaveMsg);
}
}
//Draw the messages on screen
void DrawMsg()
{
//Set temporary variable
ChatChannel curchan = channels [chatchannelint];
//For each message T in the current channels list of messages
foreach(var t in curchan.messages)
{
//If client is currently connected to the channel
if(channels.ContainsKey(t.channelId))
{
var channel = channels [t.channelId];
var person = channel.people [t.personId];
string Name = person.personName + " msg nr " + t.count;
//This is just to prevent multiple in-game text GameObjects of the same message
//If the script can find a GameObject that is active with a name that matches the Name variable
if (GameObject.Find (Name))
{
}
//if not
else
{
//Make a temporary GameObject reference
GameObject text = Instantiate (textPrefab);
//Set it equal to a new instance of a textPrefabrication(Unity stuff)
//Set the name of the GameObject in the scene
text.name = Name;
//Set the parent of the transform of the gameObject (this will anchor it to the parent object)
text.transform.SetParent (chatWindow);
//Get the Script Component "Text" that is attached to the Child of the Child of the GameObject
//and set it's text value to be equal to the name of the person that wrote the message
text.transform.GetChild (0).GetChild (0).GetComponent<Text> ().text = person.personName;
//Set the text of the Text component on the Child to be equal to the message.text value
text.transform.GetChild (1).GetComponent<Text> ().text = t.text;
//Set the color of the Text component on the Child to be equal to the color of the channel of the message
text.transform.GetChild (1).GetComponent<Text> ().color = channel.channelColor;
}
}
}
}
//Send NetworkMessage for talk
public void SendTalk()
{
//Declare temporary variable reference and set it equal to the currently active channel
ChatChannel curChan = channels [chatchannelint];
//Get the text from InputField input (Unity stuff)
talkText = input.text;
if (talkText == null)
//If the talktext is null, return from the function(why would we want to send one of these)
return;
//This should be known by now
var talkMsg = new TalkMessage();
talkMsg.personId = me.personId;
talkMsg.channelId = curChan.channelId;
talkMsg.text = talkText;
//Reset the input text to empty
input.text = string.Empty;
//Send the message to the server
client.Send(ChatMsg.Talk, talkMsg);
}
}
}