diff --git a/Assets/Scripts/Entities/Spawner.cs b/Assets/Scripts/Entities/Spawner.cs index 54b8824..271fd9b 100644 --- a/Assets/Scripts/Entities/Spawner.cs +++ b/Assets/Scripts/Entities/Spawner.cs @@ -34,7 +34,7 @@ namespace Cyber.Entities { /// set if they exist already (eg. the server has sent them over). These /// ids should be from . To create new /// ids, leave as the default (null). - public GameObject Spawn(EntityType type, Vector3 position, uint[] ids = null) { + public GameObject Spawn(EntityType type, Vector3 position, int[] ids = null) { GameObject Spawned = null; switch (type) { case EntityType.PC: @@ -55,7 +55,6 @@ namespace Cyber.Entities { } private void Start() { - Spawn(EntityType.PC, new Vector3()); } private void Update() { diff --git a/Assets/Scripts/Entities/SyncBase.cs b/Assets/Scripts/Entities/SyncBase.cs index b541091..9b0bc5d 100644 --- a/Assets/Scripts/Entities/SyncBase.cs +++ b/Assets/Scripts/Entities/SyncBase.cs @@ -14,6 +14,6 @@ namespace Cyber.Entities { /// The ID this syncable component can be found with from its parent /// . /// - public uint ID; + public int ID; } } \ No newline at end of file diff --git a/Assets/Scripts/Entities/SyncDB.cs b/Assets/Scripts/Entities/SyncDB.cs index 9685026..5fc2d7e 100644 --- a/Assets/Scripts/Entities/SyncDB.cs +++ b/Assets/Scripts/Entities/SyncDB.cs @@ -15,8 +15,8 @@ namespace Cyber.Entities { typeof(Character) }; - private uint IDCounter = 0; - private Dictionary Database = new Dictionary(); + private int IDCounter = 0; + private Dictionary Database = new Dictionary(); /// /// Add an entity to the database with the given IDs. @@ -24,7 +24,7 @@ namespace Cyber.Entities { /// Game object. /// The IDs. Should be from or /// , since the order is important. - public void AddEntity(GameObject gameObject, uint[] ids) { + public void AddEntity(GameObject gameObject, int[] ids) { int Index = 0; for (int i = 0; i < SyncableClasses.Length; i++) { SyncBase Syncable = (SyncBase)gameObject.GetComponent(SyncableClasses[i]); @@ -44,8 +44,8 @@ namespace Cyber.Entities { /// Whether or not new IDs are created. /// is a shorthand for this function with /// this parameter set to true. - public uint[] GetEntityIDs(GameObject gameObject, bool newIDs = false) { - List IDs = new List(); + public int[] GetEntityIDs(GameObject gameObject, bool newIDs = false) { + List IDs = new List(); for (int i = 0; i < SyncableClasses.Length; i++) { SyncBase Syncable = (SyncBase)gameObject.GetComponent(SyncableClasses[i]); if (Syncable != null) { @@ -55,7 +55,7 @@ namespace Cyber.Entities { IDs.Add(Syncable.ID); } } - uint[] IDArray = new uint[IDs.Count]; + int[] IDArray = new int[IDs.Count]; for (int i = 0; i < IDs.Count; i++) { IDArray[i] = IDs[i]; } @@ -68,7 +68,7 @@ namespace Cyber.Entities { /// /// The new IDs. /// Game object. - public uint[] GetNewEntityIDs(GameObject gameObject) { + public int[] GetNewEntityIDs(GameObject gameObject) { return GetEntityIDs(gameObject, true); } @@ -76,7 +76,7 @@ namespace Cyber.Entities { /// Get a synced component by its ID. /// /// The ID. - public SyncBase Get(uint id) { + public SyncBase Get(int id) { return Database[id]; } @@ -84,17 +84,17 @@ namespace Cyber.Entities { /// Creates a new ID which isn't in use yet. /// /// A new, free ID. - public uint CreateID() { - uint ID; + public int CreateID() { + int ID; try { ID = IDCounter++; } catch (OverflowException) { ID = 0; IDCounter = 1; } - while (Database.ContainsKey(ID) && ID < uint.MaxValue) { + while (Database.ContainsKey(ID) && ID < int.MaxValue) { ID++; - if (ID < uint.MaxValue - 1) + if (ID < int.MaxValue - 1) IDCounter = ID + 1; } if (Database.ContainsKey(ID)) { diff --git a/Assets/Scripts/Networking/Clientside/Client.cs b/Assets/Scripts/Networking/Clientside/Client.cs index 954ed80..ef2af51 100644 --- a/Assets/Scripts/Networking/Clientside/Client.cs +++ b/Assets/Scripts/Networking/Clientside/Client.cs @@ -1,4 +1,5 @@ using Cyber.Console; +using Cyber.Entities; using Cyber.Networking.Messages; using System.Collections.Generic; using UnityEngine; @@ -24,11 +25,13 @@ namespace Cyber.Networking.Clientside { private static Client Singleton; + private Spawner Spawner; + /// /// The player of this client /// private CConnectedPlayer Player; - private List Players = new List(); + private Dictionary Players = new Dictionary(); /// /// Creates the client and sets it as the signleton. @@ -86,6 +89,11 @@ namespace Cyber.Networking.Clientside { return Singleton.NetClient.isConnected; } + + private void Start() { + Spawner = GetComponent(); + } + private bool LaunchClient(string ip, int port) { if (Running) { return false; @@ -104,6 +112,7 @@ namespace Cyber.Networking.Clientside { NetClient.RegisterHandler(PktType.TextMessage, HandlePacket); NetClient.RegisterHandler(PktType.Identity, HandlePacket); NetClient.RegisterHandler(PktType.MassIdentity, HandlePacket); + NetClient.RegisterHandler(PktType.SpawnEntity, HandlePacket); NetClient.RegisterHandler(MsgType.Connect, OnConnected); NetClient.RegisterHandler(MsgType.Disconnect, OnDisconnected); @@ -134,15 +143,27 @@ namespace Cyber.Networking.Clientside { Debug.Log(Conn.ConnectionID + " connected!"); Term.Println(Conn.ConnectionID + " connected!"); } - Players.Add(Conn); + Players.Add(Conn.ConnectionID, Conn); break; case (PktType.MassIdentity): MassIdentityPkt Identities = new MassIdentityPkt(); Identities.Deserialize(msg.reader); foreach (int currId in Identities.IdList) { - Players.Add(new CConnectedPlayer(currId)); + Players.Add(currId, new CConnectedPlayer(currId)); } break; + case (PktType.SpawnEntity): + SpawnEntityPkt SpawnPkt = new SpawnEntityPkt(); + SpawnPkt.Deserialize(msg.reader); + + EntityType EntityType = SpawnPkt.EntityType; + // Check if you are the owner and if they are spawning an NPC + if (SpawnPkt.OwnerID == Player.ConnectionID && EntityType == EntityType.NPC) { + // Change it into a PC instead. + EntityType = EntityType.PC; + } + Spawner.Spawn(EntityType, SpawnPkt.Position, SpawnPkt.SyncBaseIDList); + break; default: Debug.LogError("Received an unknown packet, id: " + msg.msgType); Term.Println("Received an unknown packet, id: " + msg.msgType); diff --git a/Assets/Scripts/Networking/Messages/MassIdentityPkt.cs b/Assets/Scripts/Networking/Messages/MassIdentityPkt.cs index ceb7e83..e9a4940 100644 --- a/Assets/Scripts/Networking/Messages/MassIdentityPkt.cs +++ b/Assets/Scripts/Networking/Messages/MassIdentityPkt.cs @@ -8,16 +8,30 @@ namespace Cyber.Networking.Messages { /// public class MassIdentityPkt : MessageBase { + /// + /// List of Connection ID's to send + /// public int[] IdList; + /// + /// Create a Mass Identity packet used to send a list of currently online connections. + /// + /// public MassIdentityPkt(int[] idList) { IdList = idList; } + /// + /// Parameter-less constructor using when deserializing the message. + /// public MassIdentityPkt() { } + /// + /// Used to deserialize a message received via networking. + /// + /// public override void Deserialize(NetworkReader reader) { byte[][] ByteArray = new byte[4][]; ByteArray[0] = reader.ReadBytesAndSize(); @@ -27,6 +41,10 @@ namespace Cyber.Networking.Messages { IdList = NetworkHelper.DeserializeIntArray(ByteArray); } + /// + /// Used to serialize the message before it is sent. + /// + /// public override void Serialize(NetworkWriter writer) { byte[][] ByteArray = NetworkHelper.SerializeIntArray(IdList); writer.WriteBytesFull(ByteArray[0]); diff --git a/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs b/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs new file mode 100644 index 0000000..fee555d --- /dev/null +++ b/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs @@ -0,0 +1,86 @@ + +using Cyber.Entities; +using UnityEngine; +using UnityEngine.Networking; + +namespace Cyber.Networking.Messages { + + /// + /// Packet which tells the client to spawn an entity corresponding to the data inside. + /// + public class SpawnEntityPkt : MessageBase { + + /// + /// The type of entity to spawn. + /// + public EntityType EntityType; + /// + /// The position of the entity, where it should be spawned + /// + public Vector3 Position; + /// + /// The List of Sync ID's for the entity. + /// + public int[] SyncBaseIDList; + + /// + /// Connection ID of the owner of this entity + /// + public int OwnerID; + + /// + /// Create a packet of information about spawning an entity to send the clients. + /// + /// Type of entity to spawn. + /// Position where the entity will be spawned. + /// List of Sync ID's for the entity. + /// The Connection ID who owns this entity (or -1). + public SpawnEntityPkt(EntityType entityType, Vector3 position, int[] syncIDList, int ownerID) { + EntityType = entityType; + Position = position; + SyncBaseIDList = syncIDList; + OwnerID = ownerID; + } + + /// + /// Parameter-less constructor using when deserializing the message. + /// + public SpawnEntityPkt() { + + } + + /// + /// Used to deserialize a message received via networking. + /// + /// + public override void Deserialize(NetworkReader reader) { + EntityType = (EntityType) reader.ReadInt16(); + Position = reader.ReadVector3(); + OwnerID = reader.ReadInt32(); + + byte[][] ByteArray = new byte[4][]; + ByteArray[0] = reader.ReadBytesAndSize(); + ByteArray[1] = reader.ReadBytesAndSize(); + ByteArray[2] = reader.ReadBytesAndSize(); + ByteArray[3] = reader.ReadBytesAndSize(); + SyncBaseIDList = NetworkHelper.DeserializeIntArray(ByteArray); + } + + /// + /// Used to serialize the message before it is sent. + /// + /// + public override void Serialize(NetworkWriter writer) { + writer.Write((short) EntityType); + writer.Write(Position); + writer.Write(OwnerID); + + byte[][] ByteArray = NetworkHelper.SerializeIntArray(SyncBaseIDList); + writer.WriteBytesFull(ByteArray[0]); + writer.WriteBytesFull(ByteArray[1]); + writer.WriteBytesFull(ByteArray[2]); + writer.WriteBytesFull(ByteArray[3]); + } + + } +} diff --git a/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs.meta b/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs.meta new file mode 100644 index 0000000..68cd034 --- /dev/null +++ b/Assets/Scripts/Networking/Messages/SpawnEntityPkt.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bcdedfecd6ba4a04397852ba038f0939 +timeCreated: 1494296640 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Networking/PktType.cs b/Assets/Scripts/Networking/PktType.cs index aece22c..1c897f8 100644 --- a/Assets/Scripts/Networking/PktType.cs +++ b/Assets/Scripts/Networking/PktType.cs @@ -20,5 +20,10 @@ namespace Cyber.Networking { /// Packet containing the identification details about everyone on the server before the client connected. /// public const short MassIdentity = 202; + + /// + /// Packet containing necessary information about spawning an entity. + /// + public const short SpawnEntity = 203; } } \ No newline at end of file diff --git a/Assets/Scripts/Networking/Serverside/Server.cs b/Assets/Scripts/Networking/Serverside/Server.cs index 4bfd73e..582e66a 100644 --- a/Assets/Scripts/Networking/Serverside/Server.cs +++ b/Assets/Scripts/Networking/Serverside/Server.cs @@ -11,9 +11,10 @@ namespace Cyber.Networking.Serverside { /// Server-class used to host a server and communicate to clients. /// /// \todo Change connection channels to Unreliable to optimize ping. + /// \todo Remove PC/NPC and make a Human-type entity instead. public class Server : MonoBehaviour { - private List Players = new List(); + private Dictionary Players = new Dictionary(); private static Server Singleton; private Spawner Spawner; @@ -130,28 +131,51 @@ namespace Cyber.Networking.Serverside { // Internal built-in event handler - private void OnConnected(NetworkMessage msg) { + private void OnConnected(NetworkMessage msg) { + // Get client's ID int Id = msg.conn.connectionId; Debug.Log(Id + " connected!"); Term.Println(Id + " connected!"); + // Send all other clients a notification and collect their id's int[] IdList = new int[Players.Count]; - for (int i = 0; i < Players.Count; i++) { - SConnectedPlayer P = Players[i]; - IdList[i] = P.ConnectionID; + int TempCounter = 0; + foreach (SConnectedPlayer P in Players.Values) { + IdList[TempCounter++] = P.ConnectionID; NetworkServer.SendToClient(P.ConnectionID, PktType.Identity, new IdentityPkt(Id, false)); } - foreach (int id in IdList) { - Debug.Log("id: " + id); - } + + // Then send the client a list of all other clients NetworkServer.SendToClient(Id, PktType.MassIdentity, new MassIdentityPkt(IdList)); + // Add the player to the list SConnectedPlayer Player = new SConnectedPlayer(msg.conn.connectionId); - Players.Add(Player); + Players.Add(Id, Player); + // Send the previously collected list to the player NetworkServer.SendToClient(msg.conn.connectionId, PktType.Identity, new IdentityPkt(msg.conn.connectionId, true)); + + // Spawn the player and collet it's IDs + Vector3 Position = new Vector3(0, 0, 0); + GameObject Obj = Spawner.Spawn(EntityType.NPC, Position); + int[] EntityIdList = Spawner.SyncDB.GetEntityIDs(Obj); + Player.Character = Obj.GetComponent(); + + NetworkServer.SendToAll(PktType.SpawnEntity, new SpawnEntityPkt(EntityType.NPC, Position, EntityIdList, Id)); + + // Send every entity to the player who just connected. + foreach (var Entry in Players) { + if (Entry.Key == Id) { + continue; + } + Character Char = Players[Entry.Key].Character; + GameObject CurrObj = Char.gameObject; + int[] CurrEntityIdList = Spawner.SyncDB.GetNewEntityIDs(CurrObj); + NetworkServer.SendToClient(Id, PktType.SpawnEntity, + new SpawnEntityPkt(EntityType.NPC, CurrObj.transform.position, CurrEntityIdList, Entry.Key)); + } } private void OnDisconnected(NetworkMessage msg) { diff --git a/ROADMAP.md b/ROADMAP.md index f62d9d9..e8b2101 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -47,5 +47,5 @@ Version 0.1.0 - Gigantic - [x] ...With movement capabilities. - [ ] Client-server connectivity, players can see each other moving around. - [x] 1. Client-server connectivity: the clients can connect to the server and send some messages back and forth. - - [ ] 2. Players can see eachothers capsule-characters and send commands to move them around. + - [x] 2. Players can see eachothers capsule-characters and send commands to move them around. - [ ] 3. Basic syncing (Send sync packages at certain intervals) in order to sync positions correctly between server and client.