/// <summary>
        /// This event is called after a game is loaded. We use it to detect if the player has done a revert
        /// </summary>
        public void OnGameStatePostLoad(ConfigNode data)
        {
            if (FlightGlobals.ActiveVessel != null && !VesselCommon.IsSpectating)
            {
                LunaLog.Log("[LMP]: Detected a revert!");
                var vesselIdsToRemove = FlightGlobals.Vessels
                                        .Where(v => v.rootPart?.missionID == FlightGlobals.ActiveVessel.rootPart.missionID && v.id != FlightGlobals.ActiveVessel.id)
                                        .Select(v => v.id).Distinct();

                //We detected a revert, now pick all the vessel parts (debris) that came from our main active
                //vessel and remove them both from our game and server
                foreach (var vesselIdToRemove in vesselIdsToRemove)
                {
                    System.MessageSender.SendVesselRemove(vesselIdToRemove);
                    System.AddToKillList(vesselIdToRemove);
                }

                //Store it here so the delayed routine can access it!
                var activeVesselId = FlightGlobals.ActiveVessel.id;

                //Now tell the server to remove our old vessel
                CoroutineUtil.StartDelayedRoutine("SendProperVesselRemoveMsg", () =>
                {
                    //We delay the send vessel remove to wait until the proper scene is loaded.
                    //In case we revert to editor we must fully delete that vessel as when flying again we will get a new ID.
                    //Otherwise we say to not keep it in the vessels remove list as perhaps we are reverting to flight and then our vessel id will stay the same.
                    //If we set the keepvesselinremovelist to true then the server will ignore every change we do to our vessel!
                    System.MessageSender.SendVesselRemove(activeVesselId, HighLogic.LoadedSceneIsEditor);
                    if (HighLogic.LoadedSceneIsEditor)
                    {
                        System.AddToKillList(activeVesselId);
                    }
                }, 3);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Checks and sends if we took a screenshot
        /// </summary>
        public void CheckScreenshots()
        {
            if (GameSettings.TAKE_SCREENSHOT.GetKeyDown())
            {
                if (TimeUtil.IsInInterval(ref _lastTakenScreenshot, SettingsSystem.ServerSettings.MinScreenshotIntervalMs))
                {
                    var path = CommonUtil.CombinePaths(MainSystem.KspPath, "Screenshots");
                    CoroutineUtil.StartDelayedRoutine(nameof(CheckScreenshots), () =>
                    {
                        var photo = new DirectoryInfo(path).GetFiles().OrderByDescending(f => f.LastWriteTime).FirstOrDefault();
                        if (photo != null)
                        {
                            var imageData = ScaleScreenshot(File.ReadAllBytes(photo.FullName), 800, 600);
                            TaskFactory.StartNew(() =>
                            {
                                MessageSender.SendScreenshot(imageData);
                            });
                            LunaScreenMsg.PostScreenMessage(LocalizationContainer.ScreenText.ScreenshotTaken, 10f, ScreenMessageStyle.UPPER_CENTER);
                        }
                    }, 0.3f);
                }
                else
                {
                    var msg = LocalizationContainer.ScreenText.ScreenshotInterval.Replace("$1", TimeSpan.FromMilliseconds(SettingsSystem.ServerSettings.MinScreenshotIntervalMs).TotalSeconds
                                                                                          .ToString(CultureInfo.InvariantCulture));

                    LunaScreenMsg.PostScreenMessage(msg, 20f, ScreenMessageStyle.UPPER_CENTER);
                }
            }
        }
        /// <summary>
        /// Called when a vessel is initiated.
        /// </summary>
        public void VesselInitialized(Vessel vessel, bool fromShipAssembly)
        {
            if (vessel == null)
            {
                return;
            }

            //The vessel is being created by the loader
            if (VesselLoader.CurrentlyLoadingVesselId == vessel.id || fromShipAssembly)
            {
                return;
            }

            //This happens when the vessel you're spectating crashes
            if (VesselCommon.IsSpectating)
            {
                VesselRemoveSystem.Singleton.KillVessel(vessel.id, true, "Tried to create a new vessel while spectating");
                return;
            }

            //It's a debris vessel that we made it
            if (!LockSystem.LockQuery.UnloadedUpdateLockExists(vessel.id))
            {
                //We delay it a bit because we must wait until the vessel is named correctly and so on.
                CoroutineUtil.StartDelayedRoutine("VesselInitialized", () => System.MessageSender.SendVesselMessage(vessel), 0.5f);
                LockSystem.Singleton.AcquireUnloadedUpdateLock(vessel.id, true);
            }
        }
        /// <summary>
        /// Sends our vessel just when we start the flight
        /// </summary>
        public void FlightReady()
        {
            if (!VesselCommon.IsSpectating && FlightGlobals.ActiveVessel != null)
            {
                if (!System.CheckVessel(FlightGlobals.ActiveVessel))
                {
                    VesselRemoveSystem.Singleton.AddToKillList(FlightGlobals.ActiveVessel.id, "Vessel check not passed");
                    VesselRemoveSystem.Singleton.KillVessel(FlightGlobals.ActiveVessel.id, "Vessel check not passed");
                    return;
                }

                CoroutineUtil.StartDelayedRoutine(nameof(FlightReady), () =>
                {
                    if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.id == Guid.Empty)
                    {
                        return;
                    }

                    System.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel, true, false);
                }, 5f);

                //Only show safety bubble text if safety bubble is active and player is spawning a new vessel
                if (SettingsSystem.ServerSettings.SafetyBubbleDistance > 0 && FlightGlobals.ActiveVessel.vesselSpawning)
                {
                    LunaScreenMsg.PostScreenMessage(LocalizationContainer.ScreenText.SafetyBubble, 10f, ScreenMessageStyle.UPPER_CENTER);
                }
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Triggered when the vessel parts change.
        /// CAUTION!: When staging this method can be called a lot of times!
        /// Also, this method is called when docking/undocking and when reloading a vessel
        /// </summary>
        public void VesselPartCountChanged(Vessel vessel)
        {
            if (vessel == null)
            {
                return;
            }

            //The vessel is being created by the loader
            if (VesselLoader.CurrentlyLoadingVesselId == vessel.id)
            {
                return;
            }

            //When a vessel docks it's part count changes but we must not relay it as it's handled by the vessel dock system
            if (CurrentDockEvent.DominantVesselId == vessel.id || CurrentUndockEvent.UndockingVesselId == vessel.id)
            {
                return;
            }

            //This event is called when the vessel is being created and we don't want to send protos of vessels we don't own or while our vessel is not 100% loaded (FlightReady)
            if (vessel.vesselSpawning)
            {
                return;
            }

            //Vessel is scheduled to be killed so ignore this
            if (VesselRemoveSystem.Singleton.VesselWillBeKilled(vessel.id))
            {
                return;
            }

            //We are spectating and the vessel has been modified so trigger a reload
            if (VesselCommon.IsSpectating && FlightGlobals.ActiveVessel && FlightGlobals.ActiveVessel.id == vessel.id && vessel.protoVessel.protoPartSnapshots.Count != FlightGlobals.ActiveVessel.Parts.Count)
            {
                VesselLoader.LoadVessel(vessel.protoVessel);
                return;
            }

            if (!LockSystem.LockQuery.UpdateLockExists(vessel.id))
            {
                LockSystem.Singleton.AcquireUpdateLock(vessel.id, true);
                LockSystem.Singleton.AcquireUnloadedUpdateLock(vessel.id, true);
                VesselProtoSystem.Singleton.MessageSender.SendVesselMessage(vessel);
            }

            if (LockSystem.LockQuery.UpdateLockBelongsToPlayer(vessel.id, SettingsSystem.CurrentSettings.PlayerName))
            {
                //This method can be called a lot of times during staging (for every part that decouples)
                //For this reason we wait 0.5 seconds so we send all the changes at once.
                if (QueuedVessels.Contains(vessel.id))
                {
                    return;
                }

                QueuedVessels.Add(vessel.id);
                CoroutineUtil.StartDelayedRoutine("QueueVesselMessageAsPartsChanged", () => QueueNewVesselChange(vessel), 0.5f);
                VesselProtoSystem.Singleton.MessageSender.SendVesselMessage(vessel);
            }
        }
 /// <summary>
 /// Kills a vessel.
 /// </summary>
 public void DelayedKillVessel(Guid vesselId, bool addToKilledList, string reason, int delayInMs)
 {
     CoroutineUtil.StartDelayedRoutine("DelayedKillVessel", () =>
     {
         LunaLog.Log($"Delayed attempt to kill vessel {vesselId}");
         KillVessel(vesselId, addToKilledList, reason);
     }, (float)TimeSpan.FromMilliseconds(delayInMs).TotalSeconds);
 }
 /// <summary>
 /// This coroutine removes the vessels when switching to the KSC. We delay the removal of the vessels so
 /// in case we recover a vessel while in flight we correctly recover the crew, funds etc
 /// </summary>
 private static void DelayedClearVessels()
 {
     CoroutineUtil.StartDelayedRoutine(nameof(DelayedClearVessels), () =>
     {
         FlightGlobals.Vessels.Clear();
         HighLogic.CurrentGame?.flightState?.protoVessels?.Clear();
     }, 3);
 }
Esempio n. 8
0
 /// <summary>
 /// Release the given kerbal lock
 /// </summary>
 public void ReleaseKerbalLock(string kerbalName, float delayInSec)
 {
     if (delayInSec > 0)
     {
         CoroutineUtil.StartDelayedRoutine("ReleaseKerbalLock", () => ReleaseLock(new LockDefinition(LockType.Kerbal, SettingsSystem.CurrentSettings.PlayerName, kerbalName)), delayInSec);
     }
     else
     {
         ReleaseLock(new LockDefinition(LockType.Kerbal, SettingsSystem.CurrentSettings.PlayerName, kerbalName));
     }
 }
Esempio n. 9
0
 /// <summary>
 /// Release all the locks (unloaded update, update, control and kerbals) of a vessel
 /// </summary>
 public void ReleaseAllVesselLocks(IEnumerable <string> crewNames, Guid vesselId, float delayInSec = 0)
 {
     if (delayInSec > 0)
     {
         CoroutineUtil.StartDelayedRoutine("ReleaseAllVesselLocks", () => ReleaseAllVesselLocksImpl(crewNames, vesselId), delayInSec);
     }
     else
     {
         ReleaseAllVesselLocksImpl(crewNames, vesselId);
     }
 }
Esempio n. 10
0
        /// <summary>
        /// Sends a delayed vessel definition to the server.
        /// Call this method if you expect to do a lot of modifications to a vessel and you want to send it only once
        /// </summary>
        public void DelayedSendVesselMessage(Guid vesselId, float delayInSec, bool forceReload = false)
        {
            if (QueuedVesselsToSend.Contains(vesselId))
            {
                return;
            }

            QueuedVesselsToSend.Add(vesselId);
            CoroutineUtil.StartDelayedRoutine("QueueVesselMessageAsPartsChanged", () =>
            {
                QueuedVesselsToSend.Remove(vesselId);

                LunaLog.Log($"[LMP]: Sending delayed proto vessel {vesselId}");
                MessageSender.SendVesselMessage(FlightGlobals.FindVessel(vesselId));
            }, delayInSec);
        }
Esempio n. 11
0
        /// <summary>
        /// Sends our vessel just when we start the flight
        /// </summary>
        public void FlightReady()
        {
            if (!VesselCommon.IsSpectating && FlightGlobals.ActiveVessel != null)
            {
                CoroutineUtil.StartDelayedRoutine(nameof(FlightReady), () =>
                {
                    if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.id == Guid.Empty)
                    {
                        return;
                    }

                    System.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel, true);
                }, 5f);

                ScreenMessages.PostScreenMessage(LocalizationContainer.ScreenText.SafetyBubble, 10f, ScreenMessageStyle.UPPER_CENTER);
            }
        }
        /// <summary>
        /// Sends our vessel just when we start the flight
        /// </summary>
        public void FlightReady()
        {
            if (!VesselCommon.IsSpectating && FlightGlobals.ActiveVessel != null)
            {
                CoroutineUtil.StartDelayedRoutine(nameof(FlightReady), () =>
                {
                    if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.id == Guid.Empty)
                    {
                        return;
                    }

                    System.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel, true);
                }, 5f);

                ScreenMessages.PostScreenMessage("Remember!! While you're inside the safety bubble you won't see vessels that are close to you!!", 10f, ScreenMessageStyle.UPPER_CENTER);
            }
        }
Esempio n. 13
0
        public void LevelLoaded(GameScenes data)
        {
            if (data == GameScenes.SPACECENTER)
            {
                System.ClearVesselMarkers?.Invoke(KSCVesselMarkers.fetch, null);

                //Delay it to have time to recover the vessel, crew and funds
                CoroutineUtil.StartDelayedRoutine("ClearVesselsInKsc", () =>
                {
                    HighLogic.CurrentGame?.flightState?.protoVessels?.Clear();

                    if (KSCVesselMarkers.fetch != null)
                    {
                        System.ClearVesselMarkers?.Invoke(KSCVesselMarkers.fetch, null);
                    }
                }, 3);
            }
        }
Esempio n. 14
0
        /// <summary>
        /// Sends our vessel just when we start the flight
        /// </summary>
        public void FlightReady()
        {
            if (!VesselCommon.IsSpectating && FlightGlobals.ActiveVessel != null)
            {
                CoroutineUtil.StartDelayedRoutine(nameof(FlightReady), () =>
                {
                    if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.id == Guid.Empty)
                    {
                        return;
                    }

                    System.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel, true);
                    //Add our own vessel to the dictionary aswell
                    VesselsProtoStore.AddVesselToDictionary(FlightGlobals.ActiveVessel);
                }, 5f);

                ScreenMessages.PostScreenMessage("Remember!! While you're inside the safety bubble you won't see other players!!", 10f, ScreenMessageStyle.UPPER_CENTER);
            }
        }
        public void FlightReady()
        {
            //Only show safety bubble text if safety bubble is active and player is spawning a new vessel
            if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || !FlightGlobals.ActiveVessel.vesselSpawning || SettingsSystem.ServerSettings.SafetyBubbleDistance <= 0)
            {
                return;
            }

            if (System.IsInSafetyBubble(FlightGlobals.ActiveVessel) && FlightGlobals.ActiveVessel.situation == Vessel.Situations.PRELAUNCH)
            {
                System.DrawSafetyBubble();
            }

            if (FlightGlobals.ActiveVessel.vesselSpawning)
            {
                LunaScreenMsg.PostScreenMessage(LocalizationContainer.ScreenText.SafetyBubble, 10f, ScreenMessageStyle.UPPER_CENTER);
                CoroutineUtil.StartDelayedRoutine(nameof(SafetyBubbleEvents),
                                                  () => LunaScreenMsg.PostScreenMessage(LocalizationContainer.ScreenText.CheckParts, 15f, ScreenMessageStyle.UPPER_CENTER, Color.red), 25f);
            }
        }
Esempio n. 16
0
        public void OnDockingComplete(GameEvents.FromToAction <Part, Part> data)
        {
            LunaLog.Log(_ownDominantVessel ? $"[LMP]: Docking finished! We own the dominant vessel {CurrentDockEvent.DominantVesselId}" :
                        $"[LMP]: Docking finished! We DON'T own the dominant vessel {CurrentDockEvent.DominantVesselId}");

            JumpIfVesselOwnerIsInFuture(CurrentDockEvent.DominantVesselId);

            if (_ownDominantVessel)
            {
                System.MessageSender.SendDockInformation(CurrentDockEvent.WeakVesselId, FlightGlobals.ActiveVessel, WarpSystem.Singleton.CurrentSubspace);
                VesselProtoSystem.Singleton.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel);
            }
            else
            {
                CoroutineUtil.StartDelayedRoutine("OnDockingComplete", () => System.MessageSender.SendDockInformation(CurrentDockEvent.WeakVesselId,
                                                                                                                      FlightGlobals.ActiveVessel, WarpSystem.Singleton.CurrentSubspace), 3);
            }

            VesselRemoveSystem.Singleton.MessageSender.SendVesselRemove(CurrentDockEvent.WeakVesselId, false);
        }
Esempio n. 17
0
        public void EVAConstructionModePartDetached(Vessel vessel, Part part)
        {
            if (VesselCommon.IsSpectating)
            {
                return;
            }
            System.MessageSender.SendVesselMessage(vessel);

            _detachedPart = part;

            CoroutineUtil.StartDelayedRoutine("SendDetachedPartAsVessel", () =>
            {
                var newVessel = FlightGlobals.VesselsLoaded.FirstOrDefault(v => v.Parts.Contains(_detachedPart));

                if (newVessel != null)
                {
                    LockSystem.Singleton.AcquireUnloadedUpdateLock(newVessel.id, true, true);
                    LockSystem.Singleton.AcquireUpdateLock(newVessel.id, true, true);

                    System.MessageSender.SendVesselMessage(newVessel);
                }
            }, 0.50f);
        }
Esempio n. 18
0
        /// <summary>
        /// Sends our vessel just when we start the flight
        /// </summary>
        public void FlightReady()
        {
            if (!VesselCommon.IsSpectating && FlightGlobals.ActiveVessel != null)
            {
                if (!System.CheckVessel(FlightGlobals.ActiveVessel))
                {
                    VesselRemoveSystem.Singleton.AddToKillList(FlightGlobals.ActiveVessel.id);
                    VesselRemoveSystem.Singleton.KillVessel(FlightGlobals.ActiveVessel.id);
                    return;
                }

                CoroutineUtil.StartDelayedRoutine(nameof(FlightReady), () =>
                {
                    if (VesselCommon.IsSpectating || FlightGlobals.ActiveVessel == null || FlightGlobals.ActiveVessel.id == Guid.Empty)
                    {
                        return;
                    }

                    System.MessageSender.SendVesselMessage(FlightGlobals.ActiveVessel, true);
                }, 5f);

                LunaScreenMsg.PostScreenMessage(LocalizationContainer.ScreenText.SafetyBubble, 10f, ScreenMessageStyle.UPPER_CENTER);
            }
        }
Esempio n. 19
0
        /// <summary>
        /// Loads the vessel proto into the current game
        /// </summary>
        private static bool LoadVesselIntoGame(ProtoVessel vesselProto, bool forceReload)
        {
            if (HighLogic.CurrentGame?.flightState == null)
            {
                return(false);
            }

            var reloadingOwnVessel = FlightGlobals.ActiveVessel && vesselProto.vesselID == FlightGlobals.ActiveVessel.id;

            //In case the vessel exists, silently remove them from unity and recreate it again
            var existingVessel = FlightGlobals.FindVessel(vesselProto.vesselID);

            if (existingVessel != null)
            {
                if (existingVessel.Parts.Count == vesselProto.protoPartSnapshots.Count && !forceReload)
                {
                    return(true);
                }

                LunaLog.Log($"[LMP]: Reloading vessel {vesselProto.vesselID}");
                if (reloadingOwnVessel)
                {
                    existingVessel.RemoveAllCrew();
                }

                FlightGlobals.RemoveVessel(existingVessel);
                foreach (var part in existingVessel.parts)
                {
                    Object.Destroy(part.gameObject);
                }
                Object.Destroy(existingVessel.gameObject);
            }
            else
            {
                LunaLog.Log($"[LMP]: Loading vessel {vesselProto.vesselID}");
            }

            vesselProto.Load(HighLogic.CurrentGame.flightState);
            if (vesselProto.vesselRef == null)
            {
                LunaLog.Log($"[LMP]: Protovessel {vesselProto.vesselID} failed to create a vessel!");
                return(false);
            }

            VesselPositionSystem.Singleton.ForceUpdateVesselPosition(vesselProto.vesselRef.id);

            vesselProto.vesselRef.protoVessel = vesselProto;
            if (vesselProto.vesselRef.isEVA)
            {
                var evaModule = vesselProto.vesselRef.FindPartModuleImplementing <KerbalEVA>();
                if (evaModule != null && evaModule.fsm != null && !evaModule.fsm.Started)
                {
                    evaModule.fsm?.StartFSM("Idle (Grounded)");
                }
                vesselProto.vesselRef.GoOnRails();
            }

            if (vesselProto.vesselRef.situation > Vessel.Situations.PRELAUNCH)
            {
                vesselProto.vesselRef.orbitDriver.updateFromParameters();
            }

            if (double.IsNaN(vesselProto.vesselRef.orbitDriver.pos.x))
            {
                LunaLog.Log($"[LMP]: Protovessel {vesselProto.vesselID} has an invalid orbit");
                return(false);
            }

            if (reloadingOwnVessel)
            {
                vesselProto.vesselRef.Load();
                vesselProto.vesselRef.RebuildCrewList();

                //Do not do the setting of the active vessel manually, too many systems are dependant of the events triggered by KSP
                FlightGlobals.ForceSetActiveVessel(vesselProto.vesselRef);

                vesselProto.vesselRef.SpawnCrew();
                foreach (var crew in vesselProto.vesselRef.GetVesselCrew())
                {
                    if (crew.KerbalRef)
                    {
                        crew.KerbalRef.state = Kerbal.States.ALIVE;
                    }
                }

                CoroutineUtil.StartDelayedRoutine("ReloadOwnVessel", () =>
                {
                    if (KerbalPortraitGallery.Instance.ActiveCrew.Count == 0)
                    {
                        FlightGlobals.ActiveVessel.SpawnCrew();
                        foreach (var kerbal in KerbalPortraitGallery.Instance.ActiveCrew)
                        {
                            kerbal.state = Kerbal.States.ALIVE;
                        }
                        KerbalPortraitGallery.Instance.StartRefresh(FlightGlobals.ActiveVessel);
                    }
                }, 0.5f);
            }

            return(true);
        }