/// <summary> /// Send object sync. /// </summary> /// <param name="objectID">The Object ID of the object.</param> /// <param name="setOwner">Set owner of the object.</param> public void SendObjectSync(int objectID, Vector3 pos, Quaternion rot, ObjectSyncManager.SyncTypes syncType, float[] syncedVariables, ObjectSyncManager.Flags flags) { if (NetManager.Instance.IsHost) { SendObjectSyncHost(objectID, pos, rot, syncType, syncedVariables, flags); return; } Network.Messages.ObjectSyncMessage msg = new Network.Messages.ObjectSyncMessage(); msg.objectID = objectID; msg.SyncType = (int)syncType; if (syncedVariables != null) { msg.SyncedVariables = syncedVariables; } switch (flags) { case ObjectSyncManager.Flags.Full: msg.Position = Utils.GameVec3ToNet(pos); msg.Rotation = Utils.GameQuatToNet(rot); break; case ObjectSyncManager.Flags.RotationOnly: msg.Rotation = Utils.GameQuatToNet(rot); break; case ObjectSyncManager.Flags.PositionOnly: msg.Position = Utils.GameVec3ToNet(pos); break; } NetManager.Instance.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); }
/// <summary> /// Sends a sync update of the object. /// </summary> public void SendObjectSync(ObjectSyncManager.SyncTypes type, bool sendVariables, bool syncWasRequested) { if (sendVariables) { NetLocalPlayer.Instance.SendObjectSync(ObjectID, syncedObject.ObjectTransform().position, syncedObject.ObjectTransform().rotation, type, syncedObject.ReturnSyncedVariables(true)); } else { NetLocalPlayer.Instance.SendObjectSync(ObjectID, syncedObject.ObjectTransform().position, syncedObject.ObjectTransform().rotation, type, null); } }
/// <summary> /// Send object sync. /// </summary> /// <param name="objectID">The Object ID of the object.</param> /// <param name="setOwner">Set owner of the object.</param> public void SendObjectSync(int objectID, Vector3 pos, Quaternion rot, ObjectSyncManager.SyncTypes syncType, float[] syncedVariables) { Messages.ObjectSyncMessage msg = new Messages.ObjectSyncMessage(); msg.objectID = objectID; msg.position = Utils.GameVec3ToNet(pos); msg.rotation = Utils.GameQuatToNet(rot); msg.SyncType = (int)syncType; if (syncedVariables != null) { msg.SyncedVariables = syncedVariables; } netManager.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); }
/// <summary> /// Sends a sync update of the object. /// </summary> public void SendObjectSync(ObjectSyncManager.SyncTypes type, bool sendVariables, bool syncWasRequested) { if (ObjectType == ObjectSyncManager.ObjectTypes.Weather) { SendObjectSync(ObjectID, syncedObject.ObjectTransform().localPosition, syncedObject.ObjectTransform().localRotation, type, syncedObject.ReturnSyncedVariables(true), syncedObject.flags()); } if (sendVariables) { SendObjectSync(ObjectID, syncedObject.ObjectTransform().position, syncedObject.ObjectTransform().rotation, type, syncedObject.ReturnSyncedVariables(true), syncedObject.flags()); } else { SendObjectSync(ObjectID, syncedObject.ObjectTransform().position, syncedObject.ObjectTransform().rotation, type, null, syncedObject.flags()); } }
/// <summary> /// Send object sync as the host. /// </summary> public void SendObjectSyncHost(int objectID, Vector3 pos, Quaternion rot, ObjectSyncManager.SyncTypes syncType, float[] syncedVariables, ObjectSyncManager.Flags flags) { Network.Messages.ObjectSyncMessage msg = new Network.Messages.ObjectSyncMessage(); msg.objectID = objectID; msg.SyncType = (int)syncType; if (syncedVariables != null) { msg.SyncedVariables = syncedVariables; } switch (flags) { case ObjectSyncManager.Flags.Full: msg.Position = Utils.GameVec3ToNet(pos); msg.Rotation = Utils.GameQuatToNet(rot); break; case ObjectSyncManager.Flags.RotationOnly: msg.Rotation = Utils.GameQuatToNet(rot); break; case ObjectSyncManager.Flags.PositionOnly: msg.Position = Utils.GameVec3ToNet(pos); break; } if (syncType != ObjectSyncManager.SyncTypes.PeriodicSync && syncType != ObjectSyncManager.SyncTypes.GenericSync) { Network.Messages.ObjectSyncMessage msgBroadcast = new Network.Messages.ObjectSyncMessage(); // Set owner as host. if (syncType == ObjectSyncManager.SyncTypes.SetOwner) { if (Owner == null) { Owner = NetManager.Instance.GetLocalPlayer(); SyncEnabled = true; msgBroadcast.SyncType = (int)ObjectSyncManager.SyncTypes.SetOwner; } } // Remove owner as host. else if (syncType == ObjectSyncManager.SyncTypes.RemoveOwner) { Owner = null; OwnerRemoved(); msgBroadcast.SyncType = (int)ObjectSyncManager.SyncTypes.RemoveOwner; } // Force take sync control as host. else if (syncType == ObjectSyncManager.SyncTypes.ForceSetOwner) { SyncEnabled = true; msgBroadcast.SyncType = (int)ObjectSyncManager.SyncTypes.ForceSetOwner; } // Send updated ownership info to other clients. msgBroadcast.objectID = msg.objectID; msgBroadcast.OwnerPlayerID = NetManager.Instance.GetPlayerIDBySteamID(Steamworks.SteamUser.GetSteamID()); NetManager.Instance.BroadcastMessage(msgBroadcast, Steamworks.EP2PSend.k_EP2PSendReliable); } else { NetManager.Instance.BroadcastMessage(msg, Steamworks.EP2PSend.k_EP2PSendReliable); } }
/// <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> /// 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); } }); }