/// <summary> /// Performs the specified spawn locally, for this client only. Must not be called on /// networked objects (objects with NetworkIdentity), or you will get unpredictable behavior. /// </summary> /// <returns></returns> public static SpawnResult Client(SpawnInfo info) { if (info == null) { Logger.LogError("Cannot spawn, info is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } if (info.SpawnableToSpawn is IClientSpawnable clientSpawnable) { List <GameObject> spawnedObjects = new List <GameObject>(); for (var i = 0; i < info.Count; i++) { var result = clientSpawnable.ClientSpawnAt(info.SpawnDestination); if (result.Successful) { spawnedObjects.Add(result.GameObject); } } if (spawnedObjects.Count == 1) { return(SpawnResult.Single(info, spawnedObjects[0])); } return(SpawnResult.Multiple(info, spawnedObjects)); } else { Logger.LogErrorFormat("Cannot spawn {0} client side, spawnable does not" + " implement IClientSpawnable", Category.ItemSpawn, info); return(SpawnResult.Fail(info)); } }
/// <summary> /// Server-side only. Performs the spawn and syncs it to all clients. /// </summary> /// <returns></returns> private static SpawnResult Server(SpawnInfo info, bool AutoOnSpawnServerHook = true) { if (info == null) { Logger.LogError("Cannot spawn, info is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } EnsureInit(); Logger.LogTraceFormat("Server spawning {0}", Category.ItemSpawn, info); List <GameObject> spawnedObjects = new List <GameObject>(); for (int i = 0; i < info.Count; i++) { var result = info.SpawnableToSpawn.SpawnAt(info.SpawnDestination); if (result.Successful) { spawnedObjects.Add(result.GameObject); //apply scattering if it was specified if (info.ScatterRadius != null) { foreach (var spawned in spawnedObjects) { var cnt = spawned.GetComponent <CustomNetTransform>(); var scatterRadius = info.ScatterRadius.GetValueOrDefault(0); if (cnt != null) { cnt.SetPosition(info.SpawnDestination.WorldPosition + new Vector3(Random.Range(-scatterRadius, scatterRadius), Random.Range(-scatterRadius, scatterRadius))); } } } } else { return(SpawnResult.Fail(info)); } } //fire hooks for all spawned objects SpawnResult spawnResult = null; if (spawnedObjects.Count == 1) { spawnResult = SpawnResult.Single(info, spawnedObjects[0]); } else { spawnResult = SpawnResult.Multiple(info, spawnedObjects); } if (AutoOnSpawnServerHook) { _ServerFireClientServerSpawnHooks(spawnResult); } return(spawnResult); }
/// <summary> /// Performs the specified spawn locally, for this client only. /// </summary> /// <returns></returns> public static SpawnResult Client(SpawnInfo info) { if (info == null) { Logger.LogError("Cannot spawn, info is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } if (info.SpawnableToSpawn is IClientSpawnable clientSpawnable) { List <GameObject> spawnedObjects = new List <GameObject>(); for (var i = 0; i < info.Count; i++) { var result = clientSpawnable.ClientSpawnAt(info.SpawnDestination); if (result.Successful) { spawnedObjects.Add(result.GameObject); } } //fire client side lifecycle hooks foreach (var spawnedObject in spawnedObjects) { var hooks = spawnedObject.GetComponents <IClientSpawn>(); if (hooks != null) { foreach (var hook in hooks) { hook.OnSpawnClient(ClientSpawnInfo.Default()); } } } if (spawnedObjects.Count == 1) { return(SpawnResult.Single(info, spawnedObjects[0])); } return(SpawnResult.Multiple(info, spawnedObjects)); } else { Logger.LogErrorFormat("Cannot spawn {0} client side, spawnable does not" + " implement IClientSpawnable", Category.ItemSpawn, info); return(SpawnResult.Fail(info)); } }
/// <summary> /// Performs the specified spawn locally, for this client only. /// </summary> /// <returns></returns> public static SpawnResult Client(SpawnInfo info) { if (info == null) { Logger.LogError("Cannot spawn, info is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } List <GameObject> spawnedObjects = new List <GameObject>(); for (var i = 0; i < info.Count; i++) { if (info.SpawnableType == SpawnableType.Cloth) { Logger.LogErrorFormat("Spawning cloths on client side is not currently supported. {0}", Category.ItemSpawn, info); return(SpawnResult.Fail(info)); } bool isPooled; // not used for Client-only instantiation var go = PoolInstantiate(info.PrefabUsed, info.WorldPosition, info.Rotation, info.Parent, out isPooled); spawnedObjects.Add(go); } //fire client side lifecycle hooks foreach (var spawnedObject in spawnedObjects) { var hooks = spawnedObject.GetComponents <IClientSpawn>(); if (hooks != null) { foreach (var hook in hooks) { hook.OnSpawnClient(ClientSpawnInfo.Default()); } } } if (spawnedObjects.Count == 1) { return(SpawnResult.Single(info, spawnedObjects[0])); } return(SpawnResult.Multiple(info, spawnedObjects)); }
/// <summary> /// Server-side only. Performs the spawn and syncs it to all clients. /// </summary> /// <returns></returns> private static SpawnResult Server(SpawnInfo info) { if (info == null) { Logger.LogError("Cannot spawn, info is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } EnsureInit(); Logger.LogTraceFormat("Server spawning {0}", Category.ItemSpawn, info); List <GameObject> spawnedObjects = new List <GameObject>(); for (int i = 0; i < info.Count; i++) { if (info.SpawnableType == SpawnableType.Prefab) { if (info.PrefabUsed == null) { Logger.LogError("Cannot spawn, prefab to use is null", Category.ItemSpawn); return(SpawnResult.Fail(info)); } if (info.CancelIfImpassable) { if (IsTotallyImpassable(info.WorldPosition.CutToInt())) { Logger.LogTraceFormat("Cancelling spawn of {0} because" + " the position being spawned to {1} is impassable", Category.ItemSpawn, info.PrefabUsed, info.WorldPosition.CutToInt()); return(SpawnResult.Fail(info)); } } bool isPooled; GameObject tempObject = PoolInstantiate(info.PrefabUsed, info.WorldPosition, info.Rotation, info.Parent, out isPooled); if (!isPooled) { Logger.LogTrace("Prefab to spawn was not pooled, spawning new instance.", Category.ItemSpawn); NetworkServer.Spawn(tempObject); tempObject.GetComponent <CustomNetTransform>() ?.NotifyPlayers(); //Sending clientState for newly spawned items } else { Logger.LogTrace("Prefab to spawn was pooled, reusing it...", Category.ItemSpawn); } spawnedObjects.Add(tempObject); } else if (info.SpawnableType == SpawnableType.Cloth) { var result = ServerCloth(info); if (result == null) { return(SpawnResult.Fail(info)); } spawnedObjects.Add(result); } else if (info.SpawnableType == SpawnableType.Clone) { var prefab = DeterminePrefab(info.ClonedFrom); if (prefab == null) { Logger.LogErrorFormat( "Object {0} cannot be cloned because it has no PoolPrefabTracker and its name" + " does not match a prefab name, so we cannot" + " determine the prefab to instantiate. Please fix this object so that it" + " has an attached PoolPrefabTracker or so its name matches the prefab it was created from.", Category.ItemSpawn, info.ClonedFrom); } GameObject tempObject = PoolInstantiate(prefab, info.WorldPosition, info.Rotation, info.Parent, out var isPooled); if (!isPooled) { NetworkServer.Spawn(tempObject); tempObject.GetComponent <CustomNetTransform>() ?.NotifyPlayers(); //Sending clientState for newly spawned items } spawnedObjects.Add(tempObject); } //apply scattering if it was specified if (info.ScatterRadius != null) { foreach (var spawned in spawnedObjects) { var cnt = spawned.GetComponent <CustomNetTransform>(); var scatterRadius = info.ScatterRadius.GetValueOrDefault(0); if (cnt != null) { cnt.SetPosition(info.WorldPosition + new Vector3(Random.Range(-scatterRadius, scatterRadius), Random.Range(-scatterRadius, scatterRadius))); } } } //fire hooks for all spawned objects if (spawnedObjects.Count == 1) { _ServerFireClientServerSpawnHooks(SpawnResult.Single(info, spawnedObjects[0])); } else { _ServerFireClientServerSpawnHooks(SpawnResult.Multiple(info, spawnedObjects)); } } return(SpawnResult.Multiple(info, spawnedObjects)); }