Share network identities throughout clients

This commit is contained in:
Sofia 2017-05-09 04:25:04 +03:00
parent 7134c406f3
commit 504b2a08d4
16 changed files with 350 additions and 13 deletions

View File

@ -1,6 +1,4 @@
using System.Collections; using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
using Cyber.Console; using Cyber.Console;
namespace Cyber.Entities { namespace Cyber.Entities {

View File

@ -0,0 +1,26 @@
using Cyber.Entities;
namespace Cyber.Networking.Clientside {
/// <summary>
/// Represents a connected player on the clientside. This class is used by clients.
/// The C stands for "Client".
/// </summary>
class CConnectedPlayer {
/// <summary>
/// Connection ID on the global perspective.
/// </summary>
public readonly int ConnectionID;
/// <summary>
/// The player's controlled character.
/// </summary>
public Character Character;
public CConnectedPlayer(int connectionID) {
ConnectionID = connectionID;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f30b47eaf72d54f4099f5690a67965e3
timeCreated: 1494285679
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,6 @@
using Cyber.Console; using Cyber.Console;
using Cyber.Networking.Messages; using Cyber.Networking.Messages;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
@ -9,6 +10,13 @@ namespace Cyber.Networking.Clientside {
/// Client-class used to connecting to a server and communicating to it. /// Client-class used to connecting to a server and communicating to it.
/// Also handles all events incoming from the server and forwards them where they should be handled. /// Also handles all events incoming from the server and forwards them where they should be handled.
/// </summary> /// </summary>
/// <remarks>
/// The pipeline of requests sent by the client:
/// 1. Player sends command to server (e.g. player presses 'w') and then processes it on the client.
/// 2. Server receives the command and determines weather it's possible
/// 3. Server then sends the results(along with a timestamp) to the clients
/// 4. Clients receive their results and if the action was possible, starts the action and catches up to the server with the timestamp
/// </remarks>
public class Client : MonoBehaviour { public class Client : MonoBehaviour {
private NetworkClient NetClient; private NetworkClient NetClient;
@ -16,6 +24,12 @@ namespace Cyber.Networking.Clientside {
private static Client Singleton; private static Client Singleton;
/// <summary>
/// The player of this client
/// </summary>
private CConnectedPlayer Player;
private List<CConnectedPlayer> Players = new List<CConnectedPlayer>();
/// <summary> /// <summary>
/// Creates the client and sets it as the signleton. /// Creates the client and sets it as the signleton.
/// </summary> /// </summary>
@ -88,6 +102,8 @@ namespace Cyber.Networking.Clientside {
Running = true; Running = true;
NetClient.RegisterHandler(PktType.TextMessage, HandlePacket); NetClient.RegisterHandler(PktType.TextMessage, HandlePacket);
NetClient.RegisterHandler(PktType.Identity, HandlePacket);
NetClient.RegisterHandler(PktType.MassIdentity, HandlePacket);
NetClient.RegisterHandler(MsgType.Connect, OnConnected); NetClient.RegisterHandler(MsgType.Connect, OnConnected);
NetClient.RegisterHandler(MsgType.Disconnect, OnDisconnected); NetClient.RegisterHandler(MsgType.Disconnect, OnDisconnected);
@ -104,10 +120,29 @@ namespace Cyber.Networking.Clientside {
private void HandlePacket(NetworkMessage msg) { private void HandlePacket(NetworkMessage msg) {
switch (msg.msgType) { switch (msg.msgType) {
case (PktType.TextMessage): case (PktType.TextMessage):
TextMessage TextMsg = new TextMessage(); TextMessagePkt TextMsg = new TextMessagePkt();
TextMsg.Deserialize(msg.reader); TextMsg.Deserialize(msg.reader);
Term.Println(TextMsg.Message); Term.Println(TextMsg.Message);
break; break;
case (PktType.Identity):
IdentityPkt Identity = new IdentityPkt();
Identity.Deserialize(msg.reader);
var Conn = new CConnectedPlayer(Identity.ConnectionID);
if (Identity.Owned) {
Player = Conn;
} else {
Debug.Log(Conn.ConnectionID + " connected!");
Term.Println(Conn.ConnectionID + " connected!");
}
Players.Add(Conn);
break;
case (PktType.MassIdentity):
MassIdentityPkt Identities = new MassIdentityPkt();
Identities.Deserialize(msg.reader);
foreach (int currId in Identities.IdList) {
Players.Add(new CConnectedPlayer(currId));
}
break;
default: default:
Debug.LogError("Received an unknown packet, id: " + msg.msgType); Debug.LogError("Received an unknown packet, id: " + msg.msgType);
Term.Println("Received an unknown packet, id: " + msg.msgType); Term.Println("Received an unknown packet, id: " + msg.msgType);
@ -123,7 +158,7 @@ namespace Cyber.Networking.Clientside {
Term.AddCommand("send (message)", "Send a message across the vastness of space and time!", (args) => { Term.AddCommand("send (message)", "Send a message across the vastness of space and time!", (args) => {
Term.Println("You: " + args[0]); Term.Println("You: " + args[0]);
NetClient.Send(PktType.TextMessage, new TextMessage("A Client: " + args[0])); NetClient.Send(PktType.TextMessage, new TextMessagePkt("A Client: " + args[0]));
}); });
} }

View File

@ -0,0 +1,54 @@
using UnityEngine.Networking;
namespace Cyber.Networking.Messages {
/// <summary>
/// Contains the Connection ID of the user and weather the ID belongs to the client or not.
/// </summary>
public class IdentityPkt : MessageBase {
/// <summary>
/// ID of the Connection
/// </summary>
public int ConnectionID;
/// <summary>
/// Weather the recieving client owns this ID or not.
/// </summary>
public bool Owned;
/// <summary>
/// Creates a packet containing an ID and the detail weather the recieving client owns this ID.
/// </summary>
/// <param name="connectionID">ID of the connection.</param>
/// <param name="owned">Weather the recieving client owns this ID.</param>
public IdentityPkt(int connectionID, bool owned) {
ConnectionID = connectionID;
Owned = owned;
}
/// <summary>
/// Parameter-less constructor using when deserializing the message.
/// </summary>
public IdentityPkt() {
}
/// <summary>
/// Used to deserialize a message received via networking.
/// </summary>
/// <param name="reader"></param>
public override void Deserialize(NetworkReader reader) {
ConnectionID = reader.ReadInt32();
Owned = reader.ReadBoolean();
}
/// <summary>
/// Used to serialize the message before it is sent.
/// </summary>
/// <param name="writer"></param>
public override void Serialize(NetworkWriter writer) {
writer.Write(ConnectionID);
writer.Write(Owned);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c30897b96fbb288409d685055ea44926
timeCreated: 1494285679
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@

using UnityEngine.Networking;
namespace Cyber.Networking.Messages {
/// <summary>
/// Packet containing a list of ID's of players currently connected.
/// </summary>
public class MassIdentityPkt : MessageBase {
public int[] IdList;
public MassIdentityPkt(int[] idList) {
IdList = idList;
}
public MassIdentityPkt() {
}
public override void Deserialize(NetworkReader reader) {
byte[][] ByteArray = new byte[4][];
ByteArray[0] = reader.ReadBytesAndSize();
ByteArray[1] = reader.ReadBytesAndSize();
ByteArray[2] = reader.ReadBytesAndSize();
ByteArray[3] = reader.ReadBytesAndSize();
IdList = NetworkHelper.DeserializeIntArray(ByteArray);
}
public override void Serialize(NetworkWriter writer) {
byte[][] ByteArray = NetworkHelper.SerializeIntArray(IdList);
writer.WriteBytesFull(ByteArray[0]);
writer.WriteBytesFull(ByteArray[1]);
writer.WriteBytesFull(ByteArray[2]);
writer.WriteBytesFull(ByteArray[3]);
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 97cd8f5bc9c011e4eb4b6f638b852656
timeCreated: 1494288982
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,7 +6,7 @@ namespace Cyber.Networking.Messages {
/// Generic Text Message for chat etc. /// Generic Text Message for chat etc.
/// To be removed later when no longer necessary. /// To be removed later when no longer necessary.
/// </summary> /// </summary>
public class TextMessage : MessageBase { public class TextMessagePkt : MessageBase {
/// <summary> /// <summary>
/// Message inside the Text Message. Does not container sender information. /// Message inside the Text Message. Does not container sender information.
@ -17,14 +17,14 @@ namespace Cyber.Networking.Messages {
/// Create a TextMessage containing the message to be sent. /// Create a TextMessage containing the message to be sent.
/// </summary> /// </summary>
/// <param name="message">Message to be sent.</param> /// <param name="message">Message to be sent.</param>
public TextMessage(string message) { public TextMessagePkt(string message) {
this.Message = message; this.Message = message;
} }
/// <summary> /// <summary>
/// Parameter-less constructor using when deserializing the message. /// Parameter-less constructor using when deserializing the message.
/// </summary> /// </summary>
public TextMessage() { public TextMessagePkt() {
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,55 @@

using System;
using UnityEngine;
namespace Cyber.Networking {
/// <summary>
/// Class that contains a few functions to help de/serialize some arrays.
/// </summary>
public class NetworkHelper {
/// <summary>
/// Divides an int-array into four byte-arrays.
/// </summary>
/// <param name="intArray">Int-array to divide</param>
/// <returns>Four byte-arrays</returns>
public static byte[][] SerializeIntArray(int[] intArray) {
byte[][] ByteArray = new byte[4][];
for (int i = 0; i < 4; i++) {
ByteArray[i] = new byte[intArray.Length];
}
for (int top = 0; top < 4; top++) {
for (int i = 0; i < intArray.Length; i++) {
byte B = (byte) ((intArray[i] & (0xFF << (top * 8))) >> (top * 8));
ByteArray[top][i] = B;
}
}
return ByteArray;
}
/// <summary>
/// Deserializes an int-array from four byte-arrays
/// </summary>
/// <param name="byteArray">Four byte arrays</param>
/// <returns>An int array</returns>
public static int[] DeserializeIntArray(byte[][] byteArray) {
if (byteArray.GetLength(0) != 4) {
throw new ArgumentException("Specified byte-array is invalid (First dimension should be of length 4).");
} else if (byteArray.Length <= 0 || byteArray[0] == null) {
return new int[0];
}
int[] IntArray = new int[byteArray[0].Length];
for (int i = 0; i < byteArray[0].Length; i++) {
int I = byteArray[0][i] + (byteArray[1][i] << 8) + (byteArray[2][i] << 16) + (byteArray[3][i] << 24);
IntArray[i] = I;
}
return IntArray;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 914f19b5c62c68c429667403d97f2837
timeCreated: 1494288981
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,6 +6,19 @@ namespace Cyber.Networking {
/// </summary> /// </summary>
public class PktType { public class PktType {
/// <summary>
/// Test Text Message for sending stuff like chat.
/// </summary>
public const short TextMessage = 200; public const short TextMessage = 200;
/// <summary>
/// Packet containing identification details, server's given ID and if the ID belongs to you or not.
/// </summary>
public const short Identity = 201;
/// <summary>
/// Packet containing the identification details about everyone on the server before the client connected.
/// </summary>
public const short MassIdentity = 202;
} }
} }

View File

@ -0,0 +1,31 @@

using Cyber.Entities;
namespace Cyber.Networking.Serverside {
/// <summary>
/// Represents a player on the server. This class is used by the server.
/// The S stands for "Server".
/// </summary>
class SConnectedPlayer {
/// <summary>
/// The player's connection ID.
/// </summary>
public readonly int ConnectionID;
/// <summary>
/// The player's controlled character
/// </summary>
public Character Character;
/// <summary>
/// Create a connected player and give it a connection ID;
/// </summary>
/// <param name="connectionID">The player's connection ID</param>
public SConnectedPlayer(int connectionID) {
ConnectionID = connectionID;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8e1569e7f6fe73a438a3ba0421ac5059
timeCreated: 1494282762
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,7 @@
using Cyber.Console; using Cyber.Console;
using Cyber.Entities;
using Cyber.Networking.Messages; using Cyber.Networking.Messages;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
@ -8,10 +10,14 @@ namespace Cyber.Networking.Serverside {
/// <summary> /// <summary>
/// Server-class used to host a server and communicate to clients. /// Server-class used to host a server and communicate to clients.
/// </summary> /// </summary>
/// \todo Change connection channels to Unreliable to optimize ping.
public class Server : MonoBehaviour { public class Server : MonoBehaviour {
private List<SConnectedPlayer> Players = new List<SConnectedPlayer>();
private static Server Singleton; private static Server Singleton;
private Spawner Spawner;
/// <summary> /// <summary>
/// Creates the server-component, and sets the singleton as itself. /// Creates the server-component, and sets the singleton as itself.
/// </summary> /// </summary>
@ -81,6 +87,8 @@ namespace Cyber.Networking.Serverside {
return false; return false;
} }
Spawner = GetComponent<Spawner>();
ConnectionConfig Config = new ConnectionConfig(); ConnectionConfig Config = new ConnectionConfig();
Config.AddChannel(QosType.ReliableSequenced); Config.AddChannel(QosType.ReliableSequenced);
Config.AddChannel(QosType.UnreliableSequenced); Config.AddChannel(QosType.UnreliableSequenced);
@ -99,7 +107,7 @@ namespace Cyber.Networking.Serverside {
Term.AddCommand("send (message)", "Howl at the darkness of space. Does it echo though?", (args) => { Term.AddCommand("send (message)", "Howl at the darkness of space. Does it echo though?", (args) => {
Term.Println("You: " + args[0]); Term.Println("You: " + args[0]);
SendToAll(PktType.TextMessage, new TextMessage("Server: " + args[0])); SendToAll(PktType.TextMessage, new TextMessagePkt("Server: " + args[0]));
}); });
return true; return true;
@ -109,7 +117,7 @@ namespace Cyber.Networking.Serverside {
switch (msg.msgType) { switch (msg.msgType) {
case PktType.TextMessage: case PktType.TextMessage:
TextMessage TextMsg = new TextMessage(); TextMessagePkt TextMsg = new TextMessagePkt();
TextMsg.Deserialize(msg.reader); TextMsg.Deserialize(msg.reader);
Term.Println(TextMsg.Message); Term.Println(TextMsg.Message);
break; break;
@ -122,9 +130,28 @@ namespace Cyber.Networking.Serverside {
// Internal built-in event handler // Internal built-in event handler
private void OnConnected(NetworkMessage msg) { private void OnConnected(NetworkMessage msg) {
Debug.Log("Someone connected!"); int Id = msg.conn.connectionId;
Term.Println("Someone connected!");
Debug.Log(Id + " connected!");
Term.Println(Id + " connected!");
int[] IdList = new int[Players.Count];
for (int i = 0; i < Players.Count; i++) {
SConnectedPlayer P = Players[i];
IdList[i] = P.ConnectionID;
NetworkServer.SendToClient(P.ConnectionID, PktType.Identity, new IdentityPkt(Id, false));
}
foreach (int id in IdList) {
Debug.Log("id: " + id);
}
NetworkServer.SendToClient(Id, PktType.MassIdentity, new MassIdentityPkt(IdList));
SConnectedPlayer Player = new SConnectedPlayer(msg.conn.connectionId);
Players.Add(Player);
NetworkServer.SendToClient(msg.conn.connectionId,
PktType.Identity, new IdentityPkt(msg.conn.connectionId, true));
} }
private void OnDisconnected(NetworkMessage msg) { private void OnDisconnected(NetworkMessage msg) {