/// <summary> /// Adds new object to the ObjectIDs Dictionary. /// </summary> /// <param name="osc">Object to add.</param> /// <param name="objectID">Object ID to assign to object.</param> /// <returns>ObjectID of object.</returns> public int AddNewObject(ObjectSyncComponent osc, int objectID) { // Assign ObjectID automatically. if (objectID == AUTOMATIC_ID) { if (steamID.m_SteamID == 0) { steamID = Steamworks.SteamUser.GetSteamID(); } Logger.Debug($"Added new ObjectID at: {ObjectIDs.Count + 1}"); ObjectIDs.GetOrAdd(ObjectIDs.Count + 1, osc); return(ObjectIDs.Count); } // Assign object a specific ObjectID. else { Logger.Debug($"Force adding new ObjectID at: {objectID}"); if (ObjectIDs.ContainsKey(objectID)) { ObjectIDs[objectID] = osc; } else { ObjectIDs.GetOrAdd(objectID, osc); } return(objectID); } }
/// <summary> /// Constructor. /// </summary> public VehicleDoor(GameObject go, ObjectSyncComponent syncComponent) { gameObject = go; osc = syncComponent; // Rear door of van and vehicle trunks. if (go.name == "RearDoor" || go.name == "Bootlid") { doorType = DoorTypes.RearDoor; // Van, Old car. if (go.transform.FindChild("doorear")) { gameObject = go.transform.FindChild("doorear").gameObject; } // Ferndale. else { gameObject = go.transform.FindChild("Bootlid").gameObject; } } // Driver and passenger doors. else if (go.name == "doorl" || go.name == "doorr" || go.name == "door(leftx)" || go.name == "door(right)") { doorType = DoorTypes.DriverDoor; } hinge = gameObject.GetComponent <HingeJoint>(); lastRotation = hinge.angle; rigidbody = gameObject.GetComponent <Rigidbody>(); HookEvents(); }
/// <summary> /// Constructor. /// </summary> /// <param name="go"></param> public AIVehicle(GameObject go, ObjectSyncComponent osc) { gameObject = go; syncComponent = osc; parentGameObject = go.transform.parent.gameObject; // Set vehicle type, used to apply vehicle-specific event hooks. string goName = gameObject.transform.parent.gameObject.name; if (goName == "AMIS2" || goName == "KYLAJANI") { type = VehicleTypes.Amis; } else if (goName == "BUS") { type = VehicleTypes.Bus; } else if (goName == "FITTAN" && parentGameObject.transform.FindChild("Navigation") != null) { type = VehicleTypes.Fitan; } else if (parentGameObject.transform.FindChild("NavigationCW") != null || parentGameObject.transform.FindChild("NavigationCCW") != null) { type = VehicleTypes.TrafficDirectional; } else { type = VehicleTypes.Traffic; } rigidbody = parentGameObject.GetComponent <Rigidbody>(); dynamics = parentGameObject.GetComponent <CarDynamics>(); throttleFsm = Utils.GetPlaymakerScriptByName(parentGameObject, "Throttle"); if (type == VehicleTypes.TrafficDirectional) { if (parentGameObject.transform.FindChild("NavigationCW") != null) { navigationFsm = Utils.GetPlaymakerScriptByName(parentGameObject.transform.FindChild("NavigationCW").gameObject, "Navigation"); isClockwise = 1; } else { navigationFsm = Utils.GetPlaymakerScriptByName(parentGameObject.transform.FindChild("NavigationCCW").gameObject, "Navigation"); isClockwise = 0; } directionFsm = Utils.GetPlaymakerScriptByName(parentGameObject, "Direction"); } else { navigationFsm = Utils.GetPlaymakerScriptByName(parentGameObject.transform.FindChild("Navigation").gameObject, "Navigation"); } EventHooks(); }
/// <summary> /// Handle pickup of the object. /// </summary> private void PickupObject() { pickedUpGameObject = pickupFsm.Fsm.GetFsmGameObject("PickedObject").Value; ObjectSyncComponent osc = pickedUpGameObject.GetComponent <ObjectSyncComponent>(); osc.TakeSyncControl(); osc.SendConstantSync(true); Logger.Log("Picked up object: " + pickedUpGameObject); }
/// <summary> /// Write vehicle switch changes into vehicle switch message. /// </summary> /// <param name="state">The engine state to write.</param> public void WriteVehicleSwitchMessage(ObjectSyncComponent vehicle, PlayerVehicle.SwitchIDs switchID, bool newValue, float newValueFloat) { Network.Messages.VehicleSwitchMessage msg = new Network.Messages.VehicleSwitchMessage(); msg.objectID = vehicle.ObjectID; msg.switchID = (int)switchID; msg.switchValue = newValue; if (newValueFloat != -1) { msg.SwitchValueFloat = newValueFloat; } NetManager.Instance.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); }
/// <summary> /// Constructor. /// </summary> public Weather(GameObject go, ObjectSyncComponent syncComponent) { gameObject = go; osc = syncComponent; weatherFSM = gameObject.GetComponent <PlayMakerFSM>(); if (Network.NetManager.Instance.IsHost) { osc.TakeSyncControl(); } HookEvents(); }
/// <summary> /// Constructor. /// </summary> /// <param name="go"></param> public PlayerVehicle(GameObject go, ObjectSyncComponent osc) { gameObject = go; syncComponent = osc; ParentGameObject = go.transform.parent.parent.gameObject; rigidbody = ParentGameObject.GetComponent <Rigidbody>(); dynamics = ParentGameObject.GetComponent <CarDynamics>(); driveTrain = ParentGameObject.GetComponent <Drivetrain>(); axisCarController = ParentGameObject.GetComponent <AxisCarController>(); mpCarController = ParentGameObject.AddComponent <MPCarController>(); FindFSMs(); }
/// <summary> /// Handle destroy of pickupable game object. /// </summary> /// <param name="pickupable">The destroyed pickupable.</param> public void HandlePickupableDestroy(GameObject pickupable) { ObjectSyncComponent osc = GetPickupableByGameObject(pickupable); if (osc != null) { Messages.PickupableDestroyMessage msg = new Messages.PickupableDestroyMessage(); msg.id = osc.ObjectID; netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); Logger.Debug($"Handle pickupable destroy {pickupable.name}, Object ID: {osc.ObjectID}"); } else { Logger.Debug($"Unhandled pickupable has been destroyed {pickupable.name}"); Logger.Debug(Environment.StackTrace); } }
/// <summary> /// Constructor. /// </summary> /// <param name="go"></param> public PlayerVehicle(GameObject go, ObjectSyncComponent osc) { gameObject = go; syncComponent = osc; ParentGameObject = go.transform.parent.parent.gameObject; if (ParentGameObject.name.StartsWith("JONNEZ")) { isBike = true; steeringPivot = ParentGameObject.transform.FindChild("LOD/Suspension/Steering/SteeringPivot").gameObject; } rigidbody = ParentGameObject.GetComponent <Rigidbody>(); dynamics = ParentGameObject.GetComponent <CarDynamics>(); driveTrain = ParentGameObject.GetComponent <Drivetrain>(); axisCarController = ParentGameObject.GetComponent <AxisCarController>(); mpCarController = ParentGameObject.AddComponent <MPCarController>(); AddVehicleDoorSync(); FindFSMs(); }
/// <summary> /// Collect given objects. /// </summary> /// <param name="gameObject">The game object to collect.</param> public void CollectGameObject(GameObject gameObject) { if (gameObject.name == "SUN" && worldTimeFsm == null) { // Yep it's called "Color" :> worldTimeFsm = Utils.GetPlaymakerScriptByName(gameObject, "Color"); if (worldTimeFsm == null) { return; } // Register refresh world time event. if (!worldTimeFsm.Fsm.HasEvent(REFRESH_WORLD_TIME_EVENT)) { FsmEvent mpRefreshWorldTimeEvent = worldTimeFsm.Fsm.GetEvent(REFRESH_WORLD_TIME_EVENT); PlayMakerUtils.AddNewGlobalTransition(worldTimeFsm, mpRefreshWorldTimeEvent, "State 1"); } // Make sure world time is up-to-date with cache. WorldTime = worldTimeCached; } else if (Utils.IsGameObjectHierarchyMatching(gameObject, "mailbox_bottom_player/Name")) { SetupMailbox(gameObject); } else if (gameObject.name == "TRAFFIC") { new TrafficManager(gameObject); } else if (gameObject.name == "STORE") { new Shop(gameObject); } else if (gameObject.name == "BOAT") { ObjectSyncComponent osc = gameObject.transform.FindChild("GFX").FindChild("Colliders").FindChild("Collider").gameObject.AddComponent<ObjectSyncComponent>(); osc.Setup(ObjectSyncManager.ObjectTypes.Boat, ObjectSyncManager.AUTOMATIC_ID); } else if (gameObject.name == "GarageDoors") { ObjectSyncComponent oscLeft = gameObject.transform.FindChild("DoorLeft").FindChild("Coll").gameObject.AddComponent<ObjectSyncComponent>(); oscLeft.Setup(ObjectSyncManager.ObjectTypes.GarageDoor, ObjectSyncManager.AUTOMATIC_ID); ObjectSyncComponent oscRight = gameObject.transform.FindChild("DoorRight").FindChild("Coll").gameObject.AddComponent<ObjectSyncComponent>(); oscRight.Setup(ObjectSyncManager.ObjectTypes.GarageDoor, ObjectSyncManager.AUTOMATIC_ID); } }
/// <summary> /// Handle set owner messages on the host. /// </summary> public static void SetOwnerHandler(Network.Messages.ObjectSyncMessage msg, ObjectSyncComponent osc, Steamworks.CSteamID sender) { NetPlayer player = NetManager.Instance.GetPlayer(sender); // No one owns the object, accept ownership request. if (osc.Owner == null || osc.Owner == player) { osc.Owner = player; osc.OwnerSetToRemote(NetManager.Instance.GetPlayer(sender)); SendSyncResponse(player, msg.objectID, true); // Send updated ownership info to other clients. Network.Messages.ObjectSyncMessage msgBroadcast = new Network.Messages.ObjectSyncMessage(); msgBroadcast.objectID = msg.objectID; msgBroadcast.OwnerPlayerID = NetManager.Instance.GetPlayerIDBySteamID(sender); msgBroadcast.SyncType = (int)SyncTypes.SetOwner; NetManager.Instance.BroadcastMessage(msgBroadcast, Steamworks.EP2PSend.k_EP2PSendReliable); } // Someone else owns the object, deny ownership request. else { SendSyncResponse(player, msg.objectID, false); } }
/// <summary> /// Collect given objects. /// </summary> /// <param name="gameObject">The game object to collect.</param> public void CollectGameObject(GameObject gameObject) { if (gameObject.name == "SUN" && worldTimeFsm == null) { // Yep it's called "Color" :> worldTimeFsm = Utils.GetPlaymakerScriptByName(gameObject, "Color"); if (worldTimeFsm == null) { return; } // Register refresh world time event. if (!worldTimeFsm.Fsm.HasEvent(REFRESH_WORLD_TIME_EVENT)) { FsmEvent mpRefreshWorldTimeEvent = worldTimeFsm.Fsm.GetEvent(REFRESH_WORLD_TIME_EVENT); PlayMakerUtils.AddNewGlobalTransition(worldTimeFsm, mpRefreshWorldTimeEvent, "State 1"); } // Make sure world time is up-to-date with cache. WorldTime = worldTimeCached; } else if (Utils.IsGameObjectHierarchyMatching(gameObject, "mailbox_bottom_player/Name")) { SetupMailbox(gameObject); } else if (gameObject.name == "TRAFFIC") { trafficManager.Setup(gameObject); } else if (gameObject.name == "STORE") { shopManager.Setup(gameObject); } else if (gameObject.name == "BOAT") { ObjectSyncComponent osc = gameObject.transform.FindChild("GFX/Colliders/Collider").gameObject.AddComponent <ObjectSyncComponent>(); osc.Setup(ObjectSyncManager.ObjectTypes.Boat, ObjectSyncManager.AUTOMATIC_ID); } // Garage doors. else if (gameObject.name == "GarageDoors") { ObjectSyncComponent oscLeft = gameObject.transform.FindChild("DoorLeft/Coll").gameObject.AddComponent <ObjectSyncComponent>(); oscLeft.Setup(ObjectSyncManager.ObjectTypes.GarageDoor, ObjectSyncManager.AUTOMATIC_ID); ObjectSyncComponent oscRight = gameObject.transform.FindChild("DoorRight/Coll").gameObject.AddComponent <ObjectSyncComponent>(); oscRight.Setup(ObjectSyncManager.ObjectTypes.GarageDoor, ObjectSyncManager.AUTOMATIC_ID); } // Old car shed doors. else if (gameObject.name == "Doors" && gameObject.transform.parent.name == "Shed") { PlayMakerFSM doorLeft = gameObject.transform.FindChild("DoorLeft/Mesh").gameObject.GetComponent <PlayMakerFSM>(); EventHook.AddWithSync(doorLeft, "Open door"); EventHook.AddWithSync(doorLeft, "Close door"); PlayMakerFSM doorRight = gameObject.transform.FindChild("DoorRight/Mesh").gameObject.GetComponent <PlayMakerFSM>(); EventHook.AddWithSync(doorRight, "Open door"); EventHook.AddWithSync(doorRight, "Close door"); } // Weather system. else if (gameObject.name == "Clouds" && gameObject.transform.parent.name == "CloudSystem") { ObjectSyncComponent osc = gameObject.AddComponent <ObjectSyncComponent>(); osc.Setup(ObjectSyncManager.ObjectTypes.Weather, ObjectSyncManager.AUTOMATIC_ID); } // Sewage well jobs. else if (gameObject.name.StartsWith("HouseShit")) { ObjectSyncComponent osc = gameObject.AddComponent <ObjectSyncComponent>(); osc.Setup(ObjectSyncManager.ObjectTypes.SewageWell, ObjectSyncManager.AUTOMATIC_ID); } // Phone. else if (gameObject.name == "Ring") { phoneManager.Setup(gameObject); } // Map. else if (gameObject.name == "MAP" && gameObject.transform.FindChild("Darts")) { mapManager.Setup(gameObject); } }
/// <summary> /// Spawn pickupable from network message. /// </summary> /// <param name="msg">The message containing info about pickupable to /// spawn.</param> public void SpawnPickupable(Messages.PickupableSpawnMessage msg) { Vector3 position = Utils.NetVec3ToGame(msg.transform.position); Quaternion rotation = Utils.NetQuatToGame(msg.transform.rotation); if (ObjectSyncManager.Instance.ObjectIDs.ContainsKey(msg.id)) { ObjectSyncComponent osc = ObjectSyncManager.Instance.ObjectIDs[msg.id]; // Ignore spawn requests for items that are already spawned. if (osc.ObjectID == msg.id) { return; } GameObject gameObject = osc.gameObject; GamePickupableDatabase.PrefabDesc desc = GamePickupableDatabase.Instance.GetPickupablePrefab(msg.prefabId); if (gameObject != null) { var metaData = gameObject.GetComponent <PickupableMetaDataComponent>(); // Incorrect prefab found. if (msg.prefabId != metaData.prefabId) { bool resolved = false; foreach (var go in ObjectSyncManager.Instance.ObjectIDs) { if (go.Value.gameObject.GetComponent <PickupableMetaDataComponent>() .prefabId == msg.prefabId) { gameObject = go.Value.gameObject; Logger.Log("Prefab mismatch was resolved."); resolved = true; break; } } if (!resolved) { Client.Assert(true, "Prefab ID mismatch couldn't be resolved!"); } } gameObject.SetActive(msg.active); gameObject.transform.position = position; gameObject.transform.rotation = rotation; if (gameObject.GetComponent <ObjectSyncComponent>() != null) { GameObject.Destroy(gameObject.GetComponent <ObjectSyncComponent>()); } gameObject.AddComponent <ObjectSyncComponent>().Setup( ObjectSyncManager.ObjectTypes.Pickupable, msg.id); return; } DestroyPickupableLocal(msg.id); } GameObject pickupable = GameWorld.Instance.SpawnPickupable( msg.prefabId, position, rotation, msg.id); RegisterPickupable(pickupable, true); if (pickupable.GetComponent <ObjectSyncComponent>() != null) { GameObject.Destroy(pickupable.GetComponent <ObjectSyncComponent>()); } pickupable.AddComponent <ObjectSyncComponent>().Setup( ObjectSyncManager.ObjectTypes.Pickupable, msg.id); }
/// <summary> /// Write full world synchronization message. /// </summary> /// <param name="msg">The message to write to.</param> public void WriteFullWorldSync(Messages.FullWorldSyncMessage msg) { Logger.Debug("Writing full world synchronization message."); var watch = System.Diagnostics.Stopwatch.StartNew(); // 'Player is loading' is only applicable for remote client. playerIsLoading = false; // Write time Game.GameWorld gameWorld = Game.GameWorld.Instance; msg.dayTime = gameWorld.WorldTime; msg.day = gameWorld.WorldDay; // Write mailbox name msg.mailboxName = gameWorld.PlayerLastName; // Write doors List <GameDoor> doors = GameDoorsManager.Instance.doors; int doorsCount = doors.Count; msg.doors = new Messages.DoorsInitMessage[doorsCount]; Logger.Debug($"Writing state of {doorsCount} doors."); for (int i = 0; i < doorsCount; ++i) { var doorMsg = new Messages.DoorsInitMessage(); GameDoor door = doors[i]; doorMsg.position = Utils.GameVec3ToNet(door.Position); doorMsg.open = door.IsOpen; msg.doors[i] = doorMsg; } // Write light switches. List <LightSwitch> lights = Game.LightSwitchManager.Instance.lightSwitches; int lightCount = lights.Count; msg.lights = new Messages.LightSwitchMessage[lightCount]; Logger.Debug($"Writing light switches state of {lightCount}"); for (int i = 0; i < lightCount; i++) { var lightMsg = new Messages.LightSwitchMessage(); LightSwitch light = lights[i]; lightMsg.pos = Utils.GameVec3ToNet(light.Position); lightMsg.toggle = light.SwitchStatus; msg.lights[i] = lightMsg; } // Write weather GameWeatherManager.Instance.WriteWeather(msg.currentWeather); // Write objects. (Pickupables, Player vehicles, AI vehicles) var pickupableMessages = new List <Messages.PickupableSpawnMessage>(); Logger.Debug( $"Writing state of {ObjectSyncManager.Instance.ObjectIDs.Count} objects"); foreach (var kv in ObjectSyncManager.Instance.ObjectIDs) { ObjectSyncComponent osc = kv.Value; if (osc == null) { continue; } if (osc.ObjectType != ObjectSyncManager.ObjectTypes.Pickupable) { continue; } bool wasActive = true; if (!osc.gameObject.activeSelf) { wasActive = false; osc.gameObject.SetActive(true); } Logger.Log($"Writing object: {osc.gameObject.name}"); var pickupableMsg = new Messages.PickupableSpawnMessage(); var metaData = osc.gameObject.GetComponent <PickupableMetaDataComponent>(); Client.Assert(metaData != null && metaData.PrefabDescriptor != null, $"Object with broken meta data -- {osc.gameObject.name}."); pickupableMsg.prefabId = metaData.prefabId; Transform transform = osc.gameObject.transform; pickupableMsg.transform.position = Utils.GameVec3ToNet(transform.position); pickupableMsg.transform.rotation = Utils.GameQuatToNet(transform.rotation); pickupableMsg.active = osc.gameObject.activeSelf; // ObjectID pickupableMsg.id = osc.gameObject.GetComponent <ObjectSyncComponent>().ObjectID; List <float> data = new List <float>(); if (data.Count != 0) { pickupableMsg.Data = data.ToArray(); } if (!wasActive) { osc.gameObject.SetActive(false); } pickupableMessages.Add(pickupableMsg); } msg.pickupables = pickupableMessages.ToArray(); netManager.GetLocalPlayer().WriteSpawnState(msg); watch.Stop(); Logger.Debug( "World state has been written. Took " + watch.ElapsedMilliseconds + "ms"); }
/// <summary> /// Constructor. /// </summary> /// <param name="netManager">Network manager owning this network world.</param> public NetWorld(NetManager netManager) { this.netManager = netManager; Instance = this; GameCallbacks.onWorldUnload += () => { OnGameWorldUnload(); }; GameCallbacks.onWorldLoad += () => { OnGameWorldLoad(); }; GameCallbacks.onPlayMakerObjectCreate += (GameObject instance, GameObject prefab) => { if (!Game.GamePickupableDatabase.IsPickupable(instance)) { return; } var metaData = prefab.GetComponent <Game.Components.PickupableMetaDataComponent>(); Client.Assert(metaData != null, "Tried to spawn pickupable that has no meta data assigned."); RegisterPickupable(instance); Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage(); msg.prefabId = metaData.prefabId; msg.transform.position = Utils.GameVec3ToNet(instance.transform.position); msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation); msg.active = instance.activeSelf; // Check for multiple sync components from prefab ObjectSyncComponent oscOld = prefab.GetComponent <ObjectSyncComponent>(); if (instance.GetComponents <ObjectSyncComponent>().Length > 1) { foreach (ObjectSyncComponent osc in instance .GetComponents <ObjectSyncComponent>()) { if (osc.ObjectID == oscOld.ObjectID) { GameObject.Destroy(osc); } else { msg.id = osc.ObjectID; } } } else { msg.id = instance.GetComponent <ObjectSyncComponent>().ObjectID; } // Determine if object should be spawned on remote client. // (Helps to avoid duplicate objects spawning) bool sendToRemote = false; if (NetManager.Instance.IsHost) { Logger.Debug("Sending new object data to client!"); sendToRemote = true; } else { if (instance.name.StartsWith("BottleBeerFly")) { sendToRemote = true; } } if (sendToRemote) { netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); Logger.Debug("Sending new object data to client!"); } }; GameCallbacks.onPlayMakerObjectActivate += (GameObject instance, bool activate) => { if (playerIsLoading) { return; } if (activate == instance.activeSelf) { return; } if (!GamePickupableDatabase.IsPickupable(instance)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(instance); if (pickupable == null) { return; } if (activate) { var metaData = pickupable.gameObject.GetComponent <PickupableMetaDataComponent>(); Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage(); msg.id = pickupable.ObjectID; msg.prefabId = metaData.prefabId; msg.transform.position = Utils.GameVec3ToNet(instance.transform.position); msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation); netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } else { Messages.PickupableActivateMessage msg = new Messages.PickupableActivateMessage(); msg.id = pickupable.ObjectID; msg.activate = false; netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } }; GameCallbacks.onPlayMakerObjectDestroy += (GameObject instance) => { if (!Game.GamePickupableDatabase.IsPickupable(instance)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(instance); if (pickupable == null) { Logger.Debug( $"Pickupable {instance.name} has been destroyed however it is not registered, skipping removal."); return; } HandlePickupableDestroy(instance); }; GameCallbacks.onPlayMakerSetPosition += (GameObject gameObject, Vector3 position, Space space) => { if (!Game.GamePickupableDatabase.IsPickupable(gameObject)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(gameObject); if (pickupable == null) { return; } if (space == Space.Self) { position += gameObject.transform.position; } Messages.PickupableSetPositionMessage msg = new Messages.PickupableSetPositionMessage(); msg.id = pickupable.ObjectID; msg.position = Utils.GameVec3ToNet(position); netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); }; RegisterNetworkMessagesHandlers(netManager.MessageHandler); }
/// <summary> /// Register world related network message handlers. /// </summary> /// <param name="netMessageHandler">The network message handler to register /// messages to.</param> void RegisterNetworkMessagesHandlers(NetMessageHandler netMessageHandler) { netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.PickupableSetPositionMessage msg) => { Client.Assert(ObjectSyncManager.Instance.ObjectIDs.ContainsKey(msg.id), $"Tried to move pickupable that is not spawned {msg.id}."); GameObject gameObject = ObjectSyncManager.Instance.ObjectIDs[msg.id].gameObject; gameObject.transform.position = Utils.NetVec3ToGame(msg.position); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.PickupableActivateMessage msg) => { GameObject gameObject = null; if (ObjectSyncManager.Instance.ObjectIDs.ContainsKey(msg.id)) { gameObject = ObjectSyncManager.Instance.ObjectIDs[msg.id].gameObject; } Client.Assert(gameObject != null, "Tried to activate pickupable but its not spawned!"); if (msg.activate) { gameObject.SetActive(true); } else { if (gameObject != null) { gameObject.SetActive(false); } } }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.PickupableSpawnMessage msg) => { SpawnPickupable(msg); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.PickupableDestroyMessage msg) => { if (!ObjectSyncManager.Instance.ObjectIDs.ContainsKey(msg.id)) { return; } GameObject go; try { go = ObjectSyncManager.Instance.ObjectIDs[msg.id].gameObject; } catch { Logger.Error( "Failed to remove object: OSC found but can't get GameObject."); return; } GameObject.Destroy( ObjectSyncManager.Instance.ObjectIDs[msg.id].gameObject); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.WorldPeriodicalUpdateMessage msg) => { // Game reports 'next hour' - we want to have transition so correct it. GameWorld.Instance.WorldTime = (float)msg.sunClock - 2.0f; GameWorld.Instance.WorldDay = (int)msg.worldDay; GameWeatherManager.Instance.SetWeather(msg.currentWeather); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Log( $"Received synchronization packet from {sender} but there is not player registered using this id."); return; } player.HandleSynchronize(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.AnimSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Log( $"Received animation synchronization packet from {sender} but there is not player registered using this id."); return; } player.HandleAnimSynchronize(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.OpenDoorsMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Log( $"Received OpenDoorsMessage however there is no matching player {sender}! (open: {msg.open}"); return; } GameDoor doors = GameDoorsManager.Instance.FindGameDoors( Utils.NetVec3ToGame(msg.position)); if (doors == null) { Logger.Log( $"Player tried to open door, however, the door could not be found!"); return; } doors.Open(msg.open); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.FullWorldSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); // This one should never happen - if happens there is something done // miserably wrong. Client.Assert(player != null, $"There is no player matching given steam id {sender}."); // Handle full world state synchronization. HandleFullWorldSync(msg); // Spawn host character. player.Spawn(); // Set player state. player.Teleport(Utils.NetVec3ToGame(msg.spawnPosition), Utils.NetQuatToGame(msg.spawnRotation)); if (msg.pickedUpObject != NetPickupable.INVALID_ID) { player.PickupObject(msg.pickedUpObject); } // World is loaded! Notify network manager about that. netManager.OnNetworkWorldLoaded(); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.AskForWorldStateMessage msg) => { var msgF = new Messages.FullWorldSyncMessage(); WriteFullWorldSync(msgF); netManager.BroadcastMessage( msgF, Steamworks.EP2PSend.k_EP2PSendReliable); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleEnterMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error( $"Steam user of id {sender} send message however there is no active player matching this id."); return; } ObjectSyncComponent vehicle = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; if (vehicle == null) { Logger.Error("Player " + player.SteamId + " tried to enter vehicle with Object ID " + msg.objectID + " but there is no vehicle with such id."); return; } player.EnterVehicle(vehicle, msg.passenger); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleLeaveMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error( $"Steam user of id {sender} send message however there is no active player matching this id."); return; } player.LeaveVehicle(); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.VehicleStateMessage msg) => { float startTime = -1; ObjectSyncComponent vehicle = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; if (vehicle == null) { Logger.Log("Remote player tried to set state of vehicle " + msg.objectID + " but there is no vehicle with such id."); return; } if (msg.HasStartTime) { startTime = msg.StartTime; } PlayerVehicle subType = vehicle.GetObjectSubtype() as PlayerVehicle; subType.SetEngineState((PlayerVehicle.EngineStates)msg.state, (PlayerVehicle.DashboardStates)msg.dashstate, startTime); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.VehicleSwitchMessage msg) => { float newValueFloat = -1; PlayerVehicle vehicle = ObjectSyncManager.Instance.ObjectIDs[msg.objectID].GetObjectSubtype() as PlayerVehicle; if (vehicle == null) { Logger.Log("Remote player tried to change a switch in vehicle " + msg.objectID + " but there is no vehicle with such id."); return; } if (msg.HasSwitchValueFloat) { newValueFloat = msg.SwitchValueFloat; } vehicle.SetVehicleSwitch((PlayerVehicle.SwitchIDs)msg.switchID, msg.switchValue, newValueFloat); }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.LightSwitchMessage msg) => { LightSwitch light = Game.LightSwitchManager.Instance.FindLightSwitch( Utils.NetVec3ToGame(msg.pos)); light.TurnOn(msg.toggle); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ObjectSyncMessage msg) => { ObjectSyncComponent osc; ObjectSyncManager.SyncTypes type = (ObjectSyncManager.SyncTypes)msg.SyncType; try { osc = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; } catch { Logger.Log( $"Specified object is not yet added to the ObjectID's Dictionary! (Object ID: {msg.objectID})"); return; } if (osc != null) { // Set owner. if (type == ObjectSyncManager.SyncTypes.SetOwner) { if (osc.Owner == ObjectSyncManager.NO_OWNER || osc.Owner == sender.m_SteamID) { osc.OwnerSetToRemote(sender.m_SteamID); netManager.GetLocalPlayer().SendObjectSyncResponse(osc.ObjectID, true); } else { Logger.Debug( $"Set owner request rejected for object: {osc.transform.name} (Owner: {osc.Owner} Sender: {sender.m_SteamID})"); } } // Remove owner. else if (type == ObjectSyncManager.SyncTypes.RemoveOwner) { if (osc.Owner == sender.m_SteamID) { osc.OwnerRemoved(); } } // Force set owner. else if (type == ObjectSyncManager.SyncTypes.ForceSetOwner) { osc.Owner = sender.m_SteamID; netManager.GetLocalPlayer().SendObjectSyncResponse(osc.ObjectID, true); osc.SyncTakenByForce(); osc.SyncEnabled = false; } // Set object's position and variables if (osc.Owner == sender.m_SteamID || type == ObjectSyncManager.SyncTypes.PeriodicSync) { if (msg.HasSyncedVariables == true) { osc.HandleSyncedVariables(msg.SyncedVariables); } osc.SetPositionAndRotation(Utils.NetVec3ToGame(msg.position), Utils.NetQuatToGame(msg.rotation)); } } }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.ObjectSyncResponseMessage msg) => { ObjectSyncComponent osc = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; if (msg.accepted) { osc.SyncEnabled = true; osc.Owner = Steamworks.SteamUser.GetSteamID().m_SteamID; } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ObjectSyncRequestMessage msg) => { try { ObjectSyncComponent osc = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; osc.SendObjectSync(ObjectSyncManager.SyncTypes.GenericSync, true, true); } catch { Logger.Error( $"Remote client tried to request object sync of an unknown object, Object ID: {msg.objectID}"); } }); netMessageHandler.BindMessageHandler( (Steamworks.CSteamID sender, Messages.EventHookSyncMessage msg) => { if (msg.request) { EventHook.SendSync(msg.fsmID); } else { if (msg.HasFsmEventName) { EventHook.HandleEventSync( msg.fsmID, msg.fsmEventID, msg.FsmEventName); } else { EventHook.HandleEventSync(msg.fsmID, msg.fsmEventID); } } }); }
/// <summary> /// Handles sync taken by force messages on the host. /// </summary> public static void SyncTakenByForceHandler(Network.Messages.ObjectSyncMessage msg, ObjectSyncComponent osc, Steamworks.CSteamID sender) { if (osc.Owner == NetManager.Instance.GetLocalPlayer()) { osc.SyncTakenByForce(); osc.SyncEnabled = false; } osc.Owner = NetManager.Instance.GetPlayer(sender); if (osc.Owner == NetManager.Instance.GetLocalPlayer()) { osc.SyncEnabled = true; } // Send updated ownership info to other clients. Network.Messages.ObjectSyncMessage msgBroadcast = new Network.Messages.ObjectSyncMessage(); msgBroadcast.objectID = msg.objectID; msgBroadcast.OwnerPlayerID = NetManager.Instance.GetPlayerIDBySteamID(sender); msgBroadcast.SyncType = (int)SyncTypes.ForceSetOwner; NetManager.Instance.BroadcastMessage(msgBroadcast, Steamworks.EP2PSend.k_EP2PSendReliable); }
/// <summary> /// Handles remove owner messages on the host. /// </summary> public static void RemoveOwnerHandler(Network.Messages.ObjectSyncMessage msg, ObjectSyncComponent osc, Steamworks.CSteamID sender) { osc.Owner = null; osc.OwnerRemoved(); // Send updated ownership info to other clients. Network.Messages.ObjectSyncMessage msgBroadcast = new Network.Messages.ObjectSyncMessage(); msgBroadcast.objectID = msg.objectID; msgBroadcast.OwnerPlayerID = NetManager.Instance.GetPlayerIDBySteamID(sender); msgBroadcast.SyncType = (int)SyncTypes.RemoveOwner; NetManager.Instance.BroadcastMessage(msgBroadcast, Steamworks.EP2PSend.k_EP2PSendReliable); }
/// <summary> /// Write full world synchronization message. /// </summary> /// <param name="msg">The message to write to.</param> public void WriteFullWorldSync(Messages.FullWorldSyncMessage msg) { Logger.Debug("Writing full world synchronization message."); var watch = System.Diagnostics.Stopwatch.StartNew(); // 'Player is loading' is only applicable for remote client. playerIsLoading = false; // Write time GameWorld gameWorld = GameWorld.Instance; msg.dayTime = gameWorld.WorldTime; msg.day = gameWorld.WorldDay; // Write mailbox name msg.mailboxName = gameWorld.PlayerLastName; // Write doors List <GameDoor> doors = GameDoorsManager.Instance.doors; int doorsCount = doors.Count; msg.doors = new Messages.DoorsInitMessage[doorsCount]; Logger.Debug($"Writing state of {doorsCount} doors."); for (int i = 0; i < doorsCount; ++i) { var doorMsg = new Messages.DoorsInitMessage(); GameDoor door = doors[i]; doorMsg.position = Utils.GameVec3ToNet(door.Position); doorMsg.open = door.IsOpen; msg.doors[i] = doorMsg; } // Write light switches. List <LightSwitch> lights = Game.LightSwitchManager.Instance.lightSwitches; int lightCount = lights.Count; msg.lights = new Messages.LightSwitchMessage[lightCount]; Logger.Debug($"Writing light switches state of {lightCount}"); for (int i = 0; i < lightCount; i++) { var lightMsg = new Messages.LightSwitchMessage(); LightSwitch light = lights[i]; lightMsg.pos = Utils.GameVec3ToNet(light.Position); lightMsg.toggle = light.SwitchStatus; msg.lights[i] = lightMsg; } // Write connected players. msg.connectedPlayers = new Messages.ConnectedPlayersMessage(); int[] playerIDs = new int[netManager.players.Count]; ulong[] steamIDs = new ulong[netManager.players.Count]; int index2 = 0; foreach (var connectedPlayer in netManager.players) { playerIDs[index2] = connectedPlayer.Key; steamIDs[index2] = connectedPlayer.Value.SteamId.m_SteamID; index2++; } msg.connectedPlayers.playerIDs = playerIDs; msg.connectedPlayers.steamIDs = steamIDs; // Write objects. (Pickupables, Player vehicles, AI vehicles) var pickupableMessages = new List <Messages.PickupableSpawnMessage>(); Logger.Debug($"Writing state of {ObjectSyncManager.Instance.ObjectIDs.Count} objects"); foreach (var kv in ObjectSyncManager.Instance.ObjectIDs) { ObjectSyncComponent osc = kv.Value; if (osc == null) { continue; } if (osc.ObjectType != ObjectSyncManager.ObjectTypes.Pickupable) { continue; } bool wasActive = true; if (!osc.gameObject.activeSelf) { wasActive = false; osc.gameObject.SetActive(true); } if (NetWorld.DisplayObjectRegisteringDebug) { Logger.Debug($"Writing object: {osc.gameObject.name}"); } var pickupableMsg = new Messages.PickupableSpawnMessage(); var metaData = osc.gameObject.GetComponent <PickupableMetaDataComponent>(); Client.Assert(metaData != null && metaData.PrefabDescriptor != null, $"Object with broken meta data -- {osc.gameObject.name}."); pickupableMsg.prefabId = metaData.prefabId; Transform transform = osc.gameObject.transform; pickupableMsg.transform.position = Utils.GameVec3ToNet(transform.position); pickupableMsg.transform.rotation = Utils.GameQuatToNet(transform.rotation); pickupableMsg.active = osc.gameObject.activeSelf; // ObjectID pickupableMsg.id = osc.gameObject.GetComponent <ObjectSyncComponent>().ObjectID; List <float> data = new List <float>(); if (data.Count != 0) { pickupableMsg.Data = data.ToArray(); } if (!wasActive) { osc.gameObject.SetActive(false); } pickupableMessages.Add(pickupableMsg); } // Object owners. int objectsCount = ObjectSyncManager.Instance.ObjectIDs.Count; msg.objectOwners = new Messages.ObjectOwnerSync[objectsCount]; Dictionary <NetPlayer, int> playersReversed = new Dictionary <NetPlayer, int>(); foreach (var player in netManager.players) { playersReversed.Add(player.Value, player.Key); } Logger.Debug($"Writing owners of {objectsCount} objects!"); int index = 0; foreach (var objectId in ObjectSyncManager.Instance.ObjectIDs) { var objectMsg = new Messages.ObjectOwnerSync(); objectMsg.objectID = objectId.Key; if (objectId.Value.Owner != null) { objectMsg.ownerPlayerID = playersReversed[objectId.Value.Owner]; } else { objectMsg.ownerPlayerID = -1; } msg.objectOwners[index] = objectMsg; index++; } msg.pickupables = pickupableMessages.ToArray(); netManager.GetLocalPlayer().WriteSpawnState(msg); watch.Stop(); Logger.Debug("World state has been written. Took " + watch.ElapsedMilliseconds + "ms"); // Write player keys. int[] keys = { HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeyGifu").Value, HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeyHayosiko").Value, HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeyFerndale").Value, HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeyHome").Value, HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeyRuscko").Value, HutongGames.PlayMaker.FsmVariables.GlobalVariables.GetFsmInt("PlayerKeySatsuma").Value }; msg.playerKeys = keys; // Write uncle states. msg.uncleStage = UncleManager.Instance.UncleStage; msg.uncleTime = UncleManager.Instance.UncleTime; msg.uncleHome = UncleManager.Instance.UncleHome; }
/// <summary> /// Register world related network message handlers. /// </summary> /// <param name="netMessageHandler">The network message handler to register messages to.</param> void RegisterNetworkMessagesHandlers(NetMessageHandler netMessageHandler) { netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupableSetPositionMessage msg) => { Client.Assert(ObjectSyncManager.GetObjectByID(msg.id), $"Tried to move pickupable that is not spawned {msg.id}."); GameObject gameObject = ObjectSyncManager.GetObjectByID(msg.id); gameObject.transform.position = Utils.NetVec3ToGame(msg.position); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupableActivateMessage msg) => { GameObject gameObject = null; if (ObjectSyncManager.GetObjectByID(msg.id)) { gameObject = ObjectSyncManager.GetObjectByID(msg.id); } Client.Assert(gameObject != null, "Tried to activate a pickupable but it's not spawned! Does any connected client or you have other mods beside MSC:MP installed? Try uninstalling them!"); if (msg.activate) { gameObject.SetActive(true); } else { if (gameObject != null) { gameObject.SetActive(false); } } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupableSpawnMessage msg) => { SpawnPickupable(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupableDestroyMessage msg) => { if (!ObjectSyncManager.GetSyncComponentByID(msg.id)) { return; } ObjectSyncComponent osc = ObjectSyncManager.GetSyncComponentByID(msg.id); GameObject.Destroy(ObjectSyncManager.GetObjectByID(msg.id)); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.WorldPeriodicalUpdateMessage msg) => { // Game reports 'next hour' - we want to have transition so correct it. GameWorld.Instance.WorldTime = (float)msg.sunClock - 2.0f; GameWorld.Instance.WorldDay = (int)msg.worldDay; }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error($"Received synchronization packet from {sender} but there is not player registered using this id."); return; } player.HandleSynchronize(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.AnimSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error($"Received animation synchronization packet from {sender} but there is not player registered using this id."); return; } player.HandleAnimSynchronize(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.OpenDoorsMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error($"Received OpenDoorsMessage however there is no matching player {sender}! (open: {msg.open}"); return; } GameDoor doors = GameDoorsManager.Instance.FindGameDoors(Utils.NetVec3ToGame(msg.position)); if (doors == null) { Logger.Error($"Player tried to open door, however, the door could not be found!"); return; } doors.Open(msg.open); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.FullWorldSyncMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); // This one should never happen - if happens there is something done miserably wrong. Client.Assert(player != null, $"There is no player matching given steam id {sender}."); // Handle full world state synchronization. HandleFullWorldSync(msg); // Spawn host character. player.Spawn(); // Set player state. player.Teleport(Utils.NetVec3ToGame(msg.spawnPosition), Utils.NetQuatToGame(msg.spawnRotation)); if (msg.pickedUpObject != NetPickupable.INVALID_ID) { player.PickupObject(msg.pickedUpObject); } // World is loaded! Notify network manager about that. netManager.OnNetworkWorldLoaded(); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.AskForWorldStateMessage msg) => { var msgF = new Messages.FullWorldSyncMessage(); WriteFullWorldSync(msgF); netManager.SendMessage(netManager.GetPlayer(sender), msgF, Steamworks.EP2PSend.k_EP2PSendReliable); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleEnterMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error($"Steam user of id {sender} send message however there is no active player matching this id."); return; } ObjectSyncComponent vehicle = ObjectSyncManager.Instance.ObjectIDs[msg.objectID]; if (vehicle == null) { Logger.Error("Player " + player.SteamId + " tried to enter vehicle with Object ID " + msg.objectID + " but there is no vehicle with such id."); return; } player.EnterVehicle(vehicle, msg.passenger); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleLeaveMessage msg) => { NetPlayer player = netManager.GetPlayer(sender); if (player == null) { Logger.Error($"Steam user of id {sender} send message however there is no active player matching this id."); return; } player.LeaveVehicle(); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleSwitchMessage msg) => { float newValueFloat = -1; PlayerVehicle vehicle = ObjectSyncManager.Instance.ObjectIDs[msg.objectID].GetObjectSubtype() as PlayerVehicle; if (vehicle == null) { Logger.Debug("Remote player tried to change a switch in vehicle " + msg.objectID + " but there is no vehicle with such id."); return; } if (msg.HasSwitchValueFloat) { newValueFloat = msg.SwitchValueFloat; } vehicle.SetVehicleSwitch((PlayerVehicle.SwitchIDs)msg.switchID, msg.switchValue, newValueFloat); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.LightSwitchMessage msg) => { LightSwitch light = LightSwitchManager.Instance.FindLightSwitch(Utils.NetVec3ToGame(msg.pos)); light.TurnOn(msg.toggle); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ObjectSyncMessage msg) => { ObjectSyncComponent osc; ObjectSyncManager.SyncTypes type = (ObjectSyncManager.SyncTypes)msg.SyncType; if (ObjectSyncManager.GetObjectByID(msg.objectID)) { osc = ObjectSyncManager.GetSyncComponentByID(msg.objectID); } else { Logger.Error($"Specified object is not yet added to the ObjectID's Dictionary! (Object ID: {msg.objectID})"); return; } // This *should* never happen, but apparently it's possible. Client.Assert(osc != null, $"Object Sync Component wasn't found for object with ID {msg.objectID}, however, the object had a dictionary entry!"); // Host controls who owns an object. if (NetManager.Instance.IsHost) { // Set owner on host. if (type == ObjectSyncManager.SyncTypes.SetOwner) { ObjectSyncManager.SetOwnerHandler(msg, osc, sender); } // Remove owner on host. if (type == ObjectSyncManager.SyncTypes.RemoveOwner) { if (osc.Owner == netManager.GetLocalPlayer()) { ObjectSyncManager.RemoveOwnerHandler(msg, osc, sender); } } // Sync taken by force on host. if (type == ObjectSyncManager.SyncTypes.ForceSetOwner) { ObjectSyncManager.SyncTakenByForceHandler(msg, osc, sender); } } // Set ownership info on clients. else { NetPlayer player = netManager.GetPlayerByPlayerID(msg.OwnerPlayerID); // Check if player exists. if (player == null) { return; } // Set owner. if (type == ObjectSyncManager.SyncTypes.SetOwner) { if (osc.Owner != netManager.GetLocalPlayer()) { osc.OwnerSetToRemote(player); } osc.Owner = player; } // Remove owner. else if (type == ObjectSyncManager.SyncTypes.RemoveOwner) { if (osc.Owner != netManager.GetLocalPlayer()) { osc.OwnerRemoved(); } osc.Owner = null; } // Force set owner. else if (type == ObjectSyncManager.SyncTypes.ForceSetOwner) { if (osc.Owner != netManager.GetLocalPlayer()) { osc.SyncTakenByForce(); osc.SyncEnabled = false; } osc.Owner = player; if (osc.Owner == netManager.GetLocalPlayer()) { osc.SyncEnabled = true; } } } // Set object's position and variables. if ((osc.Owner == netManager.GetPlayer(sender)) || type == ObjectSyncManager.SyncTypes.PeriodicSync) { // Send synced variables, or variables only sync in some cases. if (msg.HasSyncedVariables) { osc.HandleSyncedVariables(msg.SyncedVariables); } // Full sync. if (msg.HasPosition && msg.HasRotation) { osc.SetPositionAndRotation(Utils.NetVec3ToGame(msg.Position), Utils.NetQuatToGame(msg.Rotation)); } // Position only sync. else if (msg.HasPosition) { Quaternion zero = new Quaternion(0, 0, 0, 0); osc.SetPositionAndRotation(Utils.NetVec3ToGame(msg.Position), zero); } // Rotation only sync. else if (msg.HasRotation) { osc.SetPositionAndRotation(Vector3.zero, Utils.NetQuatToGame(msg.Rotation)); } } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ObjectSyncResponseMessage msg) => { ObjectSyncComponent osc = ObjectSyncManager.GetSyncComponentByID(msg.objectID); if (msg.accepted) { osc.SyncEnabled = true; osc.Owner = NetManager.Instance.GetLocalPlayer(); } }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ObjectSyncRequestMessage msg) => { Client.Assert(ObjectSyncManager.GetObjectByID(msg.objectID), $"Remote client tried to request object sync of an unknown object, remote ObjectID was: {msg.objectID}"); ObjectSyncComponent osc = ObjectSyncManager.GetSyncComponentByID(msg.objectID); osc.SendObjectSync(ObjectSyncManager.SyncTypes.GenericSync, true, true); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.EventHookSyncMessage msg) => { if (msg.request) { EventHook.SendSync(msg.fsmID); return; } if (msg.HasFsmEventName) { EventHook.HandleEventSync(msg.fsmID, msg.fsmEventID, msg.FsmEventName); } else { EventHook.HandleEventSync(msg.fsmID, msg.fsmEventID); } }); }
/// <summary> /// Constructor. /// </summary> /// <param name="netManager">Network manager owning this network world.</param> public NetWorld(NetManager netManager) { this.netManager = netManager; Instance = this; GameCallbacks.onWorldUnload += () => { OnGameWorldUnload(); }; GameCallbacks.onWorldLoad += () => { OnGameWorldLoad(); }; GameCallbacks.onPlayMakerObjectCreate += (GameObject instance, GameObject prefab) => { if (!GamePickupableDatabase.IsPickupable(instance)) { return; } var metaData = prefab.GetComponent <PickupableMetaDataComponent>(); Client.Assert(metaData != null, "Tried to spawn pickupable that has no meta data assigned."); Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage(); msg.prefabId = metaData.prefabId; msg.transform.position = Utils.GameVec3ToNet(instance.transform.position); msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation); msg.active = instance.activeSelf; // Setup sync component on object. Client.Assert(instance.GetComponent <ObjectSyncComponent>(), $"Object created but no ObjectSyncComponent could be found! Object name: {instance.name}"); ObjectSyncComponent osc = instance.GetComponent <ObjectSyncComponent>(); msg.id = osc.Setup(osc.ObjectType, ObjectSyncManager.AUTOMATIC_ID); // Determine if object should be spawned on remote client. // (Helps to avoid duplicate objects spawning) bool sendToRemote = false; if (NetManager.Instance.IsHost) { Logger.Debug("Sending new object data to client!"); sendToRemote = true; } else { // This is a hack to workout beer bottles not spawning on the remote client due to items only spawning on the host. // This will be replaced in the future. if (instance.name.StartsWith("BottleBeerFly")) { sendToRemote = true; } } if (sendToRemote) { netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); Logger.Debug("Sending new object data to client!"); } }; GameCallbacks.onPlayMakerObjectActivate += (GameObject instance, bool activate) => { if (playerIsLoading) { return; } if (activate == instance.activeSelf) { return; } if (!GamePickupableDatabase.IsPickupable(instance)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(instance); if (pickupable == null) { return; } if (activate) { var metaData = pickupable.gameObject.GetComponent <PickupableMetaDataComponent>(); Messages.PickupableSpawnMessage msg = new Messages.PickupableSpawnMessage(); msg.id = pickupable.ObjectID; msg.prefabId = metaData.prefabId; msg.transform.position = Utils.GameVec3ToNet(instance.transform.position); msg.transform.rotation = Utils.GameQuatToNet(instance.transform.rotation); netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } else { Messages.PickupableActivateMessage msg = new Messages.PickupableActivateMessage(); msg.id = pickupable.ObjectID; msg.activate = false; netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } }; GameCallbacks.onPlayMakerObjectDestroy += (GameObject instance) => { if (!GamePickupableDatabase.IsPickupable(instance)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(instance); if (pickupable == null) { Logger.Debug($"Pickupable {instance.name} has been destroyed however it is not registered, skipping removal."); return; } HandlePickupableDestroy(instance); }; GameCallbacks.onPlayMakerSetPosition += (GameObject gameObject, Vector3 position, Space space) => { if (!GamePickupableDatabase.IsPickupable(gameObject)) { return; } ObjectSyncComponent pickupable = GetPickupableByGameObject(gameObject); if (pickupable == null) { return; } if (space == Space.Self) { position += gameObject.transform.position; } Messages.PickupableSetPositionMessage msg = new Messages.PickupableSetPositionMessage(); msg.id = pickupable.ObjectID; msg.position = Utils.GameVec3ToNet(position); netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); }; RegisterNetworkMessagesHandlers(netManager.MessageHandler); }