diff --git a/Runtime/NetworkObject.cs b/Runtime/NetworkObject.cs index 086ea6d..2004090 100644 --- a/Runtime/NetworkObject.cs +++ b/Runtime/NetworkObject.cs @@ -27,7 +27,15 @@ namespace VelNet public string networkId; /// - /// This is generated at editor time and used to generated the network id at runtime. + /// This is used internally to handle spawning of objects for players that joined late. + /// This way objects can be spawned in a static location + /// + internal bool instantiatedWithTransform = false; + internal Vector3 initialPosition; + internal Quaternion initialRotation; + + /// + /// This is generated at editor time and used to generate the network id at runtime. /// This is needed because finding all objects of type at runtime doesn't have a guaranteed order. /// public int sceneNetworkId; diff --git a/Runtime/VelNetManager.cs b/Runtime/VelNetManager.cs index 20aab53..06cc374 100644 --- a/Runtime/VelNetManager.cs +++ b/Runtime/VelNetManager.cs @@ -51,6 +51,7 @@ namespace VelNet ObjectSync, TakeOwnership, Instantiate, + InstantiateWithTransform, Destroy, DeleteSceneObjects, Custom @@ -1089,6 +1090,7 @@ namespace VelNet Debug.LogError("Joining room before logging in.", instance); return; } + MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); @@ -1235,36 +1237,15 @@ namespace VelNet /// The NetworkObject for the instantiated object. public static NetworkObject NetworkInstantiate(string prefabName) { - VelNetPlayer localPlayer = LocalPlayer; - NetworkObject prefab = instance.prefabs.Find(p => p.name == prefabName); - if (prefab == null) - { - VelNetLogger.Error("Couldn't find a prefab with that name: " + prefabName + "\nMake sure to add the prefab to list of prefabs in VelNetManager"); - return null; - } - - string networkId = localPlayer.userid + "-" + localPlayer.lastObjectId++; + VelNetPlayer owner = LocalPlayer; + string networkId = AllocateNetworkId(); if (instance.objects.ContainsKey(networkId)) { VelNetLogger.Error("Can't instantiate object. Obj with that network ID was already instantiated.", instance.objects[networkId]); return null; } - NetworkObject newObject = Instantiate(prefab); - newObject.networkId = networkId; - newObject.prefabName = prefabName; - newObject.owner = localPlayer; - try - { - newObject.OwnershipChanged?.Invoke(localPlayer); - } - catch (Exception e) - { - VelNetLogger.Error("Error in event handling.\n" + e); - } - - instance.objects.Add(newObject.networkId, newObject); - + NetworkObject newObject = ActuallyInstantiate(networkId, prefabName, owner); // only sent to others, as I already instantiated this. Nice that it happens immediately. using MemoryStream mem = new MemoryStream(); @@ -1277,10 +1258,54 @@ namespace VelNet return newObject; } - public static void SomebodyInstantiatedNetworkObject(string networkId, string prefabName, VelNetPlayer owner) + /// + /// Instantiates a prefab for all players at a specific location + /// + /// This prefab *must* by added to the list of prefabs in the scene's VelNetManager for all players. + /// + /// + /// The NetworkObject for the instantiated object. + public static NetworkObject NetworkInstantiate(string prefabName, Vector3 position, Quaternion rotation) + { + VelNetPlayer owner = LocalPlayer; + string networkId = AllocateNetworkId(); + if (instance.objects.ContainsKey(networkId)) + { + VelNetLogger.Error("Can't instantiate object. Obj with that network ID was already instantiated.", instance.objects[networkId]); + return null; + } + + NetworkObject newObject = ActuallyInstantiate(networkId, prefabName, owner, position, rotation); + + // only sent to others, as I already instantiated this. Nice that it happens immediately. + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)MessageType.InstantiateWithTransform); + writer.Write(newObject.networkId); + writer.Write(prefabName); + writer.Write(position); + writer.Write(rotation); + SendToRoom(mem.ToArray(), include_self: false, reliable: true); + + return newObject; + } + + /// + /// This happens locally on all clients + /// + /// + /// + /// + /// + internal static NetworkObject ActuallyInstantiate(string networkId, string prefabName, VelNetPlayer owner) { NetworkObject prefab = instance.prefabs.Find(p => p.name == prefabName); - if (prefab == null) return; + if (prefab == null) + { + VelNetLogger.Error("Couldn't find a prefab with that name: " + prefabName + "\nMake sure to add the prefab to list of prefabs in VelNetManager"); + return null; + } + NetworkObject newObject = Instantiate(prefab); newObject.networkId = networkId; newObject.prefabName = prefabName; @@ -1295,6 +1320,42 @@ namespace VelNet } instance.objects.Add(newObject.networkId, newObject); + return newObject; + } + + + internal static NetworkObject ActuallyInstantiate(string networkId, string prefabName, VelNetPlayer owner, Vector3 position, Quaternion rotation) + { + NetworkObject prefab = instance.prefabs.Find(p => p.name == prefabName); + if (prefab == null) + { + VelNetLogger.Error("Couldn't find a prefab with that name: " + prefabName + "\nMake sure to add the prefab to list of prefabs in VelNetManager"); + return null; + } + + NetworkObject newObject = Instantiate(prefab, position, rotation); + newObject.instantiatedWithTransform = true; + newObject.initialPosition = position; + newObject.initialRotation = rotation; + newObject.networkId = networkId; + newObject.prefabName = prefabName; + newObject.owner = owner; + try + { + newObject.OwnershipChanged?.Invoke(owner); + } + catch (Exception e) + { + VelNetLogger.Error("Error in event handling.\n" + e); + } + + instance.objects.Add(newObject.networkId, newObject); + return newObject; + } + + private static string AllocateNetworkId() + { + return LocalPlayer.userid + "-" + LocalPlayer.lastObjectId++; } public static void NetworkDestroy(NetworkObject obj) diff --git a/Runtime/VelNetPlayer.cs b/Runtime/VelNetPlayer.cs index 6480347..de993a8 100644 --- a/Runtime/VelNetPlayer.cs +++ b/Runtime/VelNetPlayer.cs @@ -18,7 +18,7 @@ namespace VelNet private VelNetManager manager; /// - /// For instantiation + /// For instantiation. This is not synced across the network /// internal int lastObjectId; @@ -41,12 +41,27 @@ namespace VelNet { if (kvp.Value.owner == this && kvp.Value.prefabName != "") { - using MemoryStream mem = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(mem); - writer.Write((byte)VelNetManager.MessageType.Instantiate); - writer.Write(kvp.Value.networkId); - writer.Write(kvp.Value.prefabName); - VelNetManager.SendToRoom(mem.ToArray(), false, true); + if (kvp.Value.instantiatedWithTransform) + { + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.InstantiateWithTransform); + writer.Write(kvp.Value.networkId); + writer.Write(kvp.Value.prefabName); + writer.Write(kvp.Value.initialPosition); + writer.Write(kvp.Value.initialRotation); + VelNetManager.SendToRoom(mem.ToArray(), false, true); + } + else + { + using MemoryStream mem = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(mem); + writer.Write((byte)VelNetManager.MessageType.Instantiate); + writer.Write(kvp.Value.networkId); + writer.Write(kvp.Value.prefabName); + VelNetManager.SendToRoom(mem.ToArray(), false, true); + } + } } @@ -75,12 +90,12 @@ namespace VelNet //individual message parameters separated by comma VelNetManager.MessageType messageType = (VelNetManager.MessageType)reader.ReadByte(); - if(messageType == VelNetManager.MessageType.Custom) + if (messageType == VelNetManager.MessageType.Custom) { // Custom packets. These are global data that can be sent from anywhere. // Any script can subscribe to the callback to receive the message data. // todo: strange hack that any player can handle the custom message, which then simply calls velnetmanager. - + int len = reader.ReadInt32(); try { @@ -90,6 +105,7 @@ namespace VelNet { VelNetLogger.Error(e.ToString()); } + return; } @@ -103,7 +119,7 @@ namespace VelNet { // sync update for an object "I" may own // "I" being the person sending - case VelNetManager.MessageType.ObjectSync: + case VelNetManager.MessageType.ObjectSync: { string objectKey = reader.ReadString(); byte componentIdx = reader.ReadByte(); @@ -113,7 +129,7 @@ namespace VelNet { bool isRpc = (componentIdx & 1) == 1; componentIdx = (byte)(componentIdx >> 1); - + // rpcs can be sent by non-owners if (isRpc || manager.objects[objectKey].owner == this) { @@ -151,7 +167,22 @@ namespace VelNet break; //we already have this one, ignore } - VelNetManager.SomebodyInstantiatedNetworkObject(networkId, prefabName, this); + VelNetManager.ActuallyInstantiate(networkId, prefabName, this); + + break; + } + case VelNetManager.MessageType.InstantiateWithTransform: // I'm trying to instantiate an object + { + string networkId = reader.ReadString(); + string prefabName = reader.ReadString(); + Vector3 position = reader.ReadVector3(); + Quaternion rotation = reader.ReadQuaternion(); + if (manager.objects.ContainsKey(networkId)) + { + break; //we already have this one, ignore + } + + VelNetManager.ActuallyInstantiate(networkId, prefabName, this, position, rotation); break; } @@ -170,7 +201,7 @@ namespace VelNet break; } - + default: throw new ArgumentOutOfRangeException(); } diff --git a/package.json b/package.json index 9f11729..7f81eb0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "edu.uga.engr.vel.velnet", "displayName": "VelNet", - "version": "1.1.7", + "version": "1.1.8", "unity": "2019.1", "description": "A custom networking library for Unity.", "keywords": [ @@ -35,4 +35,4 @@ "path": "Samples~/DissonanceExample" } ] -} \ No newline at end of file +}