/// <summary> /// Write player state into the network message. /// </summary> /// <param name="msg">Message to write to.</param> public void WriteSpawnState(Messages.FullWorldSyncMessage msg) { msg.spawnPosition = Utils.GameVec3ToNet(GetPosition()); msg.spawnRotation = Utils.GameQuatToNet(GetRotation()); msg.pickedUpObject = NetPickupable.INVALID_ID; }
/// <summary> /// Handle full world sync message. /// </summary> /// <param name="msg">The message to handle.</param> public void HandleFullWorldSync(Messages.FullWorldSyncMessage msg) { // Doors. foreach (Messages.DoorsInitMessage door in msg.doors) { Vector3 position = Utils.NetVec3ToGame(door.position); Game.Objects.GameDoor doors = Game.GameDoorsManager.Instance.FindGameDoors(position); Client.Assert(doors != null, $"Unable to find doors at: {position}."); if (doors.IsOpen != door.open) { doors.Open(door.open); } } // Vehicles. foreach (Messages.VehicleInitMessage vehicleMsg in msg.vehicles) { Vector3 pos = Utils.NetVec3ToGame(vehicleMsg.transform.position); Quaternion rot = Utils.NetQuatToGame(vehicleMsg.transform.rotation); NetVehicle vehicle = GetVehicle(vehicleMsg.id); Client.Assert(vehicle != null, $"Received info about non existing vehicle {vehicleMsg.id} in full world sync. (pos: {pos}, rot: {rot})"); vehicle.Teleport(pos, rot); } // Pickupables foreach (Messages.PickupableSpawnMessage pickupableMsg in msg.pickupables) { SpawnPickupable(pickupableMsg); } }
/// <summary> /// Write player state into the network message. /// </summary> /// <param name="msg">Message to write to.</param> public void WriteSpawnState(Messages.FullWorldSyncMessage msg) { msg.spawnPosition = Utils.GameVec3ToNet(GetPosition()); msg.spawnRotation = Utils.GameQuatToNet(GetRotation()); if (state == State.OnFoot) { msg.occupiedVehicleId = NetVehicle.INVALID_ID; } else { msg.occupiedVehicleId = currentVehicle.NetId; msg.passenger = state == State.Passenger; } msg.pickedUpObject = NetPickupable.INVALID_ID; GamePlayer player = GameWorld.Instance.Player; if (player != null) { // It is possible the local player is not spawned yet. If so we do not // care about those values as those can be only set when player is actually spawned. var pickedUpObject = player.PickedUpObject; if (pickedUpObject != null) { msg.pickedUpObject = netWorld.GetPickupableNetId(pickedUpObject); } } }
/// <summary> /// Write full world synchronization message. /// </summary> /// <param name="msg">The message to write to.</param> public void WriteFullWorldSync(Messages.FullWorldSyncMessage msg) { // Write doors List <Game.Objects.GameDoor> doors = Game.GameDoorsManager.Instance.doors; int doorsCount = doors.Count; msg.doors = new Messages.DoorsInitMessage[doorsCount]; for (int i = 0; i < doorsCount; ++i) { var doorMsg = new Messages.DoorsInitMessage(); Game.Objects.GameDoor door = doors[i]; doorMsg.position = Utils.GameVec3ToNet(door.Position); doorMsg.open = door.IsOpen; msg.doors[i] = doorMsg; } // Write vehicles. int vehiclesCount = vehicles.Count; msg.vehicles = new Messages.VehicleInitMessage[vehiclesCount]; for (int i = 0; i < vehiclesCount; ++i) { var vehicleMsg = new Messages.VehicleInitMessage(); NetVehicle vehicle = vehicles[i]; vehicleMsg.id = vehicle.NetId; vehicleMsg.transform.position = Utils.GameVec3ToNet(vehicle.GetPosition()); vehicleMsg.transform.rotation = Utils.GameQuatToNet(vehicle.GetRotation()); msg.vehicles[i] = vehicleMsg; } // Write pickupables. msg.pickupables = new Messages.PickupableSpawnMessage[netPickupables.Count]; int idx = 0; foreach (var kv in netPickupables) { NetPickupable pickupable = kv.Value; var pickupableMsg = new Messages.PickupableSpawnMessage(); pickupableMsg.id = pickupable.NetId; // Logger.Log($"Serializing pickupable: {pickupableMsg.id}. {pickupable.gameObject}"); var metaData = pickupable.gameObject.GetComponent <Game.Components.PickupableMetaDataComponent>(); pickupableMsg.prefabId = metaData.prefabId; Transform transform = pickupable.gameObject.transform; pickupableMsg.transform.position = Utils.GameVec3ToNet(transform.position); pickupableMsg.transform.rotation = Utils.GameQuatToNet(transform.rotation); msg.pickupables[idx++] = pickupableMsg; } }
/// <summary> /// Handle full world sync message. /// </summary> /// <param name="msg">The message to handle.</param> public void HandleFullWorldSync(Messages.FullWorldSyncMessage msg) { // Read time Game.GameWorld gameWorld = Game.GameWorld.Instance; gameWorld.WorldTime = msg.dayTime; gameWorld.WorldDay = msg.day; // Read mailbox name gameWorld.PlayerLastName = msg.mailboxName; // Doors. foreach (Messages.DoorsInitMessage door in msg.doors) { Vector3 position = Utils.NetVec3ToGame(door.position); Game.Objects.GameDoor doors = Game.GameDoorsManager.Instance.FindGameDoors(position); Client.Assert(doors != null, $"Unable to find doors at: {position}."); if (doors.IsOpen != door.open) { doors.Open(door.open); } } // Lights. foreach (Messages.LightSwitchMessage light in msg.lights) { Vector3 position = Utils.NetVec3ToGame(light.pos); Game.Objects.LightSwitch lights = Game.LightSwitchManager.Instance.FindLightSwitch(position); Client.Assert(lights != null, $"Unable to find light switch at: {position}."); if (lights.SwitchStatus != light.toggle) { lights.TurnOn(light.toggle); } } // Weather. Game.GameWeatherManager.Instance.SetWeather(msg.currentWeather); // Vehicles. foreach (Messages.VehicleInitMessage vehicleMsg in msg.vehicles) { Vector3 pos = Utils.NetVec3ToGame(vehicleMsg.transform.position); Quaternion rot = Utils.NetQuatToGame(vehicleMsg.transform.rotation); NetVehicle vehicle = GetVehicle(vehicleMsg.id); Client.Assert(vehicle != null, $"Received info about non existing vehicle {vehicleMsg.id} in full world sync. (pos: {pos}, rot: {rot})"); vehicle.Teleport(pos, rot); } // Pickupables List <ushort> pickupablesIds = new List <ushort>(); foreach (var kv in netPickupables) { pickupablesIds.Add(kv.Key); } foreach (Messages.PickupableSpawnMessage pickupableMsg in msg.pickupables) { SpawnPickupable(pickupableMsg); pickupablesIds.Remove(pickupableMsg.id); } // Remove spawned (and active) pickupables that we did not get info about. foreach (ushort id in pickupablesIds) { GameObject gameObject = netPickupables[id].gameObject; if (gameObject && !gameObject.activeSelf) { continue; } DestroyPickupableLocal(id); } }
/// <summary> /// Write full world synchronization message. /// </summary> /// <param name="msg">The message to write to.</param> public void WriteFullWorldSync(Messages.FullWorldSyncMessage msg) { // 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 <Game.Objects.GameDoor> doors = Game.GameDoorsManager.Instance.doors; int doorsCount = doors.Count; msg.doors = new Messages.DoorsInitMessage[doorsCount]; for (int i = 0; i < doorsCount; ++i) { var doorMsg = new Messages.DoorsInitMessage(); Game.Objects.GameDoor door = doors[i]; doorMsg.position = Utils.GameVec3ToNet(door.Position); doorMsg.open = door.IsOpen; msg.doors[i] = doorMsg; } // Write light switches. List <Game.Objects.LightSwitch> lights = Game.LightSwitchManager.Instance.lightSwitches; int lightCount = lights.Count; msg.lights = new Messages.LightSwitchMessage[lightCount]; for (int i = 0; i < lightCount; i++) { var lightMsg = new Messages.LightSwitchMessage(); Game.Objects.LightSwitch light = lights[i]; lightMsg.pos = Utils.GameVec3ToNet(light.Position); lightMsg.toggle = light.SwitchStatus; msg.lights[i] = lightMsg; } // Write weather Game.GameWeatherManager.Instance.WriteWeather(msg.currentWeather); // Write vehicles. int vehiclesCount = vehicles.Count; msg.vehicles = new Messages.VehicleInitMessage[vehiclesCount]; for (int i = 0; i < vehiclesCount; ++i) { var vehicleMsg = new Messages.VehicleInitMessage(); NetVehicle vehicle = vehicles[i]; vehicleMsg.id = vehicle.NetId; vehicleMsg.transform.position = Utils.GameVec3ToNet(vehicle.GetPosition()); vehicleMsg.transform.rotation = Utils.GameQuatToNet(vehicle.GetRotation()); msg.vehicles[i] = vehicleMsg; } // Write pickupables. var pickupableMessages = new List <Messages.PickupableSpawnMessage>(); foreach (var kv in netPickupables) { NetPickupable pickupable = kv.Value; if (pickupable.gameObject == null) { Logger.Log($"Null ptr of the pickupable game object {pickupable.NetId}"); continue; } var pickupableMsg = new Messages.PickupableSpawnMessage(); pickupableMsg.id = pickupable.NetId; var metaData = pickupable.gameObject.GetComponent <Game.Components.PickupableMetaDataComponent>(); pickupableMsg.prefabId = metaData.prefabId; Transform transform = pickupable.gameObject.transform; pickupableMsg.transform.position = Utils.GameVec3ToNet(transform.position); pickupableMsg.transform.rotation = Utils.GameQuatToNet(transform.rotation); pickupableMsg.active = pickupable.gameObject.activeSelf; List <float> data = new List <float>(); //Beercases if (metaData.PrefabDescriptor.type == Game.GamePickupableDatabase.PrefabType.BeerCase && pickupable.gameObject.name != "beer case") { Game.Objects.BeerCase beer = Game.BeerCaseManager.Instance.FindBeerCase(pickupable.gameObject); data.Add(Game.BeerCaseManager.Instance.FullCaseBottles - beer.UsedBottles); } if (data.Count != 0) { pickupableMsg.Data = data.ToArray(); } pickupableMessages.Add(pickupableMsg); } msg.pickupables = pickupableMessages.ToArray(); netManager.GetLocalPlayer().WriteSpawnState(msg); }
/// <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(netPickupables.ContainsKey(msg.id), $"Tried to move pickupable that is not spawned {msg.id}."); GameObject gameObject = netPickupables[msg.id].gameObject; gameObject.transform.position = Utils.NetVec3ToGame(msg.position); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupableActivateMessage msg) => { GameObject gameObject = null; if (netPickupables.ContainsKey(msg.id)) { gameObject = netPickupables[msg.id].gameObject; } if (msg.activate) { Client.Assert(gameObject != null, "Tried to activate pickupable but its not spawned!"); 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 (!netPickupables.ContainsKey(msg.id)) { return; } NetPickupable pickupable = netPickupables[msg.id]; GameObject.Destroy(pickupable.gameObject); netPickupables.Remove(msg.id); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.WorldPeriodicalUpdateMessage msg) => { // Game reports 'next hour' - we want to have transition so correct it. Game.GameWorld.Instance.WorldTime = (float)msg.sunClock - 2.0f; Game.GameWorld.Instance.WorldDay = (int)msg.worldDay; Game.GameWeatherManager.Instance.SetWeather(msg.currentWeather); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.RemoveBottleMessage msg) => { GameObject beerGO = GetPickupableGameObject(msg.netId); Game.Objects.BeerCase beer = Game.BeerCaseManager.Instance.FindBeerCase(beerGO); if (beer == null) { Logger.Log($"Player tried to drink beer, however, the beercase cannot be found."); return; } beer.RemoveBottles(1); }); 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.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; } Game.Objects.GameDoor doors = Game.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.occupiedVehicleId != NetVehicle.INVALID_ID) { var vehicle = GetVehicle(msg.occupiedVehicleId); Client.Assert(vehicle != null, $"Player {player.GetName()} ({player.SteamId}) you tried to join reported that he drives car that does not exists in your game. Vehicle id: {msg.occupiedVehicleId}, passenger: {msg.passenger}"); player.EnterVehicle(vehicle, msg.passenger); } 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; } NetVehicle vehicle = GetVehicle(msg.vehicleId); if (vehicle == null) { Logger.Error("Player " + player.SteamId + " tried to enter vehicle " + msg.vehicleId + " 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.VehicleSyncMessage 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.HandleVehicleSync(msg); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupObjectMessage 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.PickupObject(msg.netId); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.ReleaseObjectMessage 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.ReleaseObject(msg.drop); }); netMessageHandler.BindMessageHandler((Steamworks.CSteamID sender, Messages.LightSwitchMessage msg) => { Game.Objects.LightSwitch light = Game.LightSwitchManager.Instance.FindLightSwitch(Utils.NetVec3ToGame(msg.pos)); light.TurnOn(msg.toggle); }); }
/// <summary> /// Handle full world sync message. /// </summary> /// <param name="msg">The message to handle.</param> public void HandleFullWorldSync(Messages.FullWorldSyncMessage msg) { Logger.Debug("Handling full world synchronization message."); var watch = System.Diagnostics.Stopwatch.StartNew(); // Read time Game.GameWorld gameWorld = Game.GameWorld.Instance; gameWorld.WorldTime = msg.dayTime; gameWorld.WorldDay = msg.day; // Read mailbox name gameWorld.PlayerLastName = msg.mailboxName; // Doors. foreach (Messages.DoorsInitMessage door in msg.doors) { Vector3 position = Utils.NetVec3ToGame(door.position); Game.Objects.GameDoor doors = Game.GameDoorsManager.Instance.FindGameDoors(position); Client.Assert(doors != null, $"Unable to find doors at: {position}."); if (doors.IsOpen != door.open) { doors.Open(door.open); } } // Lights. foreach (Messages.LightSwitchMessage light in msg.lights) { Vector3 position = Utils.NetVec3ToGame(light.pos); Game.Objects.LightSwitch lights = Game.LightSwitchManager.Instance.FindLightSwitch(position); Client.Assert( lights != null, $"Unable to find light switch at: {position}."); if (lights.SwitchStatus != light.toggle) { lights.TurnOn(light.toggle); } } // Weather. GameWeatherManager.Instance.SetWeather(msg.currentWeather); // Pickupables foreach (Messages.PickupableSpawnMessage pickupableMsg in msg.pickupables) { SpawnPickupable(pickupableMsg); } // Remove spawned (and active) pickupables that we did not get info about. foreach (var kv in GamePickupableDatabase.Instance.Pickupables) { if (kv.Value.GetComponent <ObjectSyncComponent>() == null) { GameObject.Destroy(kv.Value); } } GamePickupableDatabase.Instance.Pickupables.Clear(); playerIsLoading = false; watch.Stop(); Logger.Debug("Full world synchronization message has been handled. Took " + watch.ElapsedMilliseconds + "ms"); }
/// <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> /// 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); } } }); }
public NetManager() { this.netManagerCreationTime = DateTime.UtcNow; netWorld = new NetWorld(this); // Setup local player. players[0] = new NetLocalPlayer(this, netWorld, Steamworks.SteamUser.GetSteamID()); p2pSessionRequestCallback = Steamworks.Callback <Steamworks.P2PSessionRequest_t> .Create((Steamworks.P2PSessionRequest_t result) => { if (!Steamworks.SteamNetworking.AcceptP2PSessionWithUser(result.m_steamIDRemote)) { Logger.Log("Accepted p2p session with " + result.m_steamIDRemote.ToString()); } }); gameLobbyJoinRequestedCallback = Steamworks.Callback <Steamworks.GameLobbyJoinRequested_t> .Create(OnGameLobbyJoinRequested); lobbyCreatedCallResult = new Steamworks.CallResult <Steamworks.LobbyCreated_t>((Steamworks.LobbyCreated_t result, bool ioFailure) => { if (result.m_eResult != Steamworks.EResult.k_EResultOK) { Logger.Log("Oh my f*****g god i failed to create a lobby for you. Please forgive me. (result: " + result.m_eResult + ")"); MPGUI.Instance.ShowMessageBox("Failed to create lobby due to steam error.\n" + result.m_eResult, () => { MPController.Instance.LoadLevel("MainMenu"); }); return; } Logger.Log("Hey you! I have lobby id for you! " + result.m_ulSteamIDLobby); mode = Mode.Host; state = State.Playing; currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby); netWorld.RegisterPickupables(); }); lobbyEnterCallResult = new Steamworks.CallResult <Steamworks.LobbyEnter_t>((Steamworks.LobbyEnter_t result, bool ioFailure) => { if (result.m_EChatRoomEnterResponse != (uint)Steamworks.EChatRoomEnterResponse.k_EChatRoomEnterResponseSuccess) { Logger.Log("Oh my f*****g god i failed to join the lobby for you. Please forgive me. (reponse: " + result.m_EChatRoomEnterResponse + ")"); players[1] = null; return; } Logger.Log("Oh hello! " + result.m_ulSteamIDLobby); mode = Mode.Player; state = State.LoadingGameWorld; currentLobbyId = new Steamworks.CSteamID(result.m_ulSteamIDLobby); SendHandshake(); }); // TODO: Move message handlers to some class. BindMessageHandler((Steamworks.CSteamID sender, Messages.HandshakeMessage msg) => { remoteClock = msg.clock; HandleHandshake(sender, msg); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.PlayerSyncMessage msg) => { if (players[1] == null) { Logger.Log("Received synchronization packet but no remote player is currently connected."); return; } players[1].HandleSynchronize(msg); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatMessage msg) => { var message = new Messages.HeartbeatResponseMessage(); message.clientClock = msg.clientClock; message.clock = GetNetworkClock(); BroadcastMessage(message, Steamworks.EP2PSend.k_EP2PSendReliable); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.HeartbeatResponseMessage msg) => { ping = (uint)(GetNetworkClock() - msg.clientClock); // TODO: Some smart lag compensation. remoteClock = msg.clock; timeSinceLastHeartbeat = 0.0f; }); BindMessageHandler((Steamworks.CSteamID sender, Messages.DisconnectMessage msg) => { HandleDisconnect(false); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.OpenDoorsMessage msg) => { NetPlayer player = players[1]; // 1.5 is a length of the ray used to check interaction with doors in game scripts. Vector3 interactionPosition = player.GetPosition() + player.GetRotation() * Vector3.forward * 1.5f; Game.Objects.GameDoor doors = Game.GameDoorsManager.Instance.FindGameDoors(interactionPosition); if (doors == null) { Logger.Log($"Player tried to open doors however he is not close to any: {interactionPosition}."); return; } doors.Open(msg.open); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.FullWorldSyncMessage msg) => { netWorld.HandleFullWorldSync(msg); // Now spawn player. if (players[1] != null) { players[1].Spawn(); } // World loaded we are playing! state = State.Playing; }); BindMessageHandler((Steamworks.CSteamID sender, Messages.AskForWorldStateMessage msg) => { var msgF = new Messages.FullWorldSyncMessage(); netWorld.WriteFullWorldSync(msgF); BroadcastMessage(msgF, Steamworks.EP2PSend.k_EP2PSendReliable); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleEnterMessage msg) => { NetPlayer player = players[1]; if (player == null) { return; } NetVehicle vehicle = netWorld.GetVehicle(msg.vehicleId); if (vehicle == null) { Logger.Log("Player " + player.SteamId + " tried to enter vehicle " + msg.vehicleId + " but there is no vehicle with such id."); return; } player.EnterVehicle(vehicle, msg.passenger); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleLeaveMessage msg) => { NetPlayer player = players[1]; if (player == null) { return; } player.LeaveVehicle(); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.VehicleSyncMessage msg) => { NetPlayer player = players[1]; if (player == null) { return; } player.HandleVehicleSync(msg); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.PickupObjectMessage msg) => { NetPlayer player = players[1]; if (player == null) { return; } player.PickupObject(msg.netId); }); BindMessageHandler((Steamworks.CSteamID sender, Messages.ReleaseObjectMessage msg) => { NetPlayer player = players[1]; if (player == null) { return; } player.ReleaseObject(msg.drop); }); }
/// <summary> /// Handle full world sync message. /// </summary> /// <param name="msg">The message to handle.</param> public void HandleFullWorldSync(Messages.FullWorldSyncMessage msg) { Logger.Debug("Handling full world synchronization message."); var watch = System.Diagnostics.Stopwatch.StartNew(); // Read time Game.GameWorld gameWorld = GameWorld.Instance; gameWorld.WorldTime = msg.dayTime; gameWorld.WorldDay = msg.day; // Read mailbox name gameWorld.PlayerLastName = msg.mailboxName; // Doors. foreach (Messages.DoorsInitMessage door in msg.doors) { Vector3 position = Utils.NetVec3ToGame(door.position); GameDoor doors = GameDoorsManager.Instance.FindGameDoors(position); Client.Assert(doors != null, $"Unable to find doors at: {position}."); if (doors.IsOpen != door.open) { doors.Open(door.open); } } // Lights. foreach (Messages.LightSwitchMessage light in msg.lights) { Vector3 position = Utils.NetVec3ToGame(light.pos); LightSwitch lights = LightSwitchManager.Instance.FindLightSwitch(position); Client.Assert(lights != null, $"Unable to find light switch at: {position}."); if (lights.SwitchStatus != light.toggle) { lights.TurnOn(light.toggle); } } // Pickupables foreach (Messages.PickupableSpawnMessage pickupableMsg in msg.pickupables) { SpawnPickupable(pickupableMsg); } // Remove spawned (and active) pickupables that we did not get info about. foreach (var kv in GamePickupableDatabase.Instance.Pickupables) { if (kv.Value.GetComponent <ObjectSyncComponent>() == null) { GameObject.Destroy(kv.Value); } } // Connected players. int i = 0; foreach (int newPlayerID in msg.connectedPlayers.playerIDs) { Steamworks.CSteamID localSteamID = Steamworks.SteamUser.GetSteamID(); Steamworks.CSteamID newPlayerSteamID = new Steamworks.CSteamID(msg.connectedPlayers.steamIDs[i]); // If player is not host or local player, setup new player. if (newPlayerSteamID != netManager.GetHostPlayer().SteamId&& newPlayerSteamID != localSteamID) { netManager.players.Add(newPlayerID, new NetPlayer(netManager, this, newPlayerSteamID)); netManager.players[newPlayerID].Spawn(); Logger.Debug("Setup new player at ID: " + newPlayerID); } i++; } // Object owners. foreach (Messages.ObjectOwnerSync syncMsg in msg.objectOwners) { if (syncMsg.ownerPlayerID != -1) { ObjectSyncManager.Instance.ObjectIDs[syncMsg.objectID].Owner = netManager.GetPlayerByPlayerID(syncMsg.ownerPlayerID); } } GamePickupableDatabase.Instance.Pickupables.Clear(); playerIsLoading = false; watch.Stop(); Logger.Debug("Full world synchronization message has been handled. Took " + watch.ElapsedMilliseconds + "ms"); }
/// <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> /// 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; }