private static bool ProcessCoupleInternal(Guid vesselId, Guid coupledVesselId, uint partFlightId, uint coupledPartFlightId, CoupleTrigger trigger)
        {
            if (!VesselCommon.DoVesselChecks(vesselId))
            {
                return(false);
            }

            //If the coupling is against our OWN vessel we must FORCE the loading
            var forceLoad = FlightGlobals.ActiveVessel && (FlightGlobals.ActiveVessel.id == vesselId || FlightGlobals.ActiveVessel.id == coupledVesselId);

            _dominantVessel = FlightGlobals.FindVessel(vesselId);
            if (_dominantVessel == null)
            {
                return(false);
            }
            if (!_dominantVessel.loaded && forceLoad)
            {
                _dominantVessel.Load();
            }

            _weakVessel = FlightGlobals.FindVessel(coupledVesselId);
            if (_weakVessel == null)
            {
                return(false);
            }
            if (!_weakVessel.loaded && forceLoad)
            {
                _weakVessel.Load();
            }

            var protoPart        = _dominantVessel.protoVessel.GetProtoPart(partFlightId);
            var coupledProtoPart = _weakVessel.protoVessel.GetProtoPart(coupledPartFlightId);

            if (protoPart != null && coupledProtoPart != null)
            {
                if (protoPart.partRef && coupledProtoPart.partRef)
                {
                    VesselCoupleSystem.Singleton.IgnoreEvents = true;

                    //Remember! The weak vessel must couple with the DOMINANT vessel and not the other way around!

                    switch (trigger)
                    {
                    case CoupleTrigger.DockingNode:
                        var weakDockingNode = coupledProtoPart.partRef.FindModuleImplementing <ModuleDockingNode>();
                        if (weakDockingNode)
                        {
                            var dominantDockingNode = protoPart.partRef.FindModuleImplementing <ModuleDockingNode>();
                            if (dominantDockingNode)
                            {
                                weakDockingNode.DockToVessel(dominantDockingNode);
                            }
                        }
                        break;

                    case CoupleTrigger.GrappleNode:
                        var grappleModule = coupledProtoPart.partRef.FindModuleImplementing <ModuleGrappleNode>();
                        if (grappleModule)
                        {
                            GrappleMethod.Invoke(grappleModule, new object[] { coupledProtoPart, protoPart });
                        }
                        break;

                    case CoupleTrigger.Kerbal:
                        var kerbalEva = coupledProtoPart.partRef.FindModuleImplementing <KerbalEVA>();
                        if (kerbalEva)
                        {
                            var seat = KerbalSeatField.GetValue(kerbalEva) as KerbalSeat;
                            if (seat)
                            {
                                kerbalEva.fsm.RunEvent(kerbalEva.On_seatBoard);
                            }
                        }
                        break;

                    case CoupleTrigger.Other:
                        coupledProtoPart.partRef.Couple(protoPart.partRef);
                        break;
                    }
                    VesselCoupleSystem.Singleton.IgnoreEvents = false;

                    return(true);
                }
            }

            return(false);
        }
示例#2
0
        /// <summary>
        /// This method will take a vessel and update all it's parts and proto based on a protovessel we received
        /// Protovessel --------------> Vessel & ProtoVessel
        /// This way we avoid having to unload and reload a vessel with it's terrible performance
        /// </summary>
        public static void UpdateVesselPartsFromProtoVessel(Vessel vessel, ProtoVessel protoVessel, IEnumerable <uint> vesselPartsId = null)
        {
            if (vessel == null || protoVessel == null || vessel.state == Vessel.State.DEAD)
            {
                return;
            }

            if (vessel.id != protoVessel.vesselID)
            {
                LunaLog.LogError($"Tried to update a vessel id {vessel.id} with a protovessel of vessel id {protoVessel.vesselID}");
                return;
            }

            var vesselProtoPartIds = vesselPartsId ?? protoVessel.protoPartSnapshots.Select(p => p.flightID);

            //If vessel is UNLOADED it won't have parts so we must take them from the proto...
            var vesselPartsIds = vessel.loaded ? vessel.parts.Select(p => p.flightID) : vessel.protoVessel.protoPartSnapshots.Select(p => p.flightID);

            var hasMissingparts = vesselProtoPartIds.Except(vesselPartsIds).Any();

            if (hasMissingparts || !VesselCommon.IsSpectating && (!vessel.Landed && protoVessel.landed || !vessel.Splashed && protoVessel.splashed))
            {
                //Reload the whole vessel if vessel lands/splashes as otherwise map view puts the vessel next to the other player.
                //Better to reload if has missing parts as creating them dinamically is a PIA
                VesselLoader.ReloadVessel(protoVessel);
                return;
            }

            var hasCrewChanges = false;

            //Never do vessel.protoVessel = protoVessel; not even if the vessel is not loaded as when it gets loaded the parts are created in the active vessel
            //and not on the target vessel

            //Run trough all the vessel parts and protoparts.
            //Vessel.parts will be empty if vessel is unloaded.
            var protoPartsToRemove = new List <ProtoPartSnapshot>();

            for (var i = 0; i < vessel.protoVessel.protoPartSnapshots.Count; i++)
            {
                var protoPartToUpdate = vessel.protoVessel.protoPartSnapshots[i];
                var partSnapshot      = VesselCommon.FindProtoPartInProtovessel(protoVessel, protoPartToUpdate.flightID);
                if (partSnapshot == null) //Part does not exist in the protovessel definition so kill it
                {
                    protoPartsToRemove.Add(protoPartToUpdate);
                    continue;
                }

                AdjustCrewMembersInProtoPart(protoPartToUpdate, partSnapshot);
                protoPartToUpdate.state = partSnapshot.state;
                UpdatePartModulesInProtoPart(protoPartToUpdate, partSnapshot);
                UpdateProtoVesselResources(protoPartToUpdate, partSnapshot);

                var part = protoPartToUpdate.partRef;
                if (part != null) //Part can be null if the vessel is unloaded!!
                {
                    //Remove or add crew members in given part and detect if there have been any change
                    hasCrewChanges |= AdjustCrewMembersInPart(part, partSnapshot);

                    //Set part "state" field... Important for fairings for example...
                    StateField?.SetValue(part, partSnapshot.state);
                    part.ResumeState = part.State;

                    UpdatePartModules(partSnapshot, part);
                    UpdateVesselResources(partSnapshot, part);
                    UpdatePartFairings(partSnapshot, part);
                }
            }

            //Now kill both parts and protoparts that don't exist
            for (var i = 0; i < protoPartsToRemove.Count; i++)
            {
                //Part can be null if the vessel is unloaded.  In this case, no need to kill it as it's already gone from the game.
                protoPartsToRemove[i].partRef?.Die();
                vessel.protoVessel.protoPartSnapshots.Remove(protoPartsToRemove[i]);
            }

            if (hasCrewChanges)
            {
                //We must always refresh the crew in every part of the vessel, even if we don't spectate
                vessel.RebuildCrewList();

                //IF we are spectating we must fix the portraits of the kerbals
                if (FlightGlobals.ActiveVessel?.id == vessel.id)
                {
                    //If you don't call spawn crew and you do a crew transfer the transfered crew won't appear in the portraits...
                    Client.Singleton.StartCoroutine(CallbackUtil.DelayedCallback(0.25f, () => { FlightGlobals.ActiveVessel?.SpawnCrew(); }));
                    //If you don't call this the kerbal portraits appear in black...
                    Client.Singleton.StartCoroutine(CallbackUtil.DelayedCallback(0.5f, () => { KerbalPortraitGallery.Instance?.SetActivePortraitsForVessel(FlightGlobals.ActiveVessel); }));
                }
            }
        }
        /// <summary>
        /// If we get the Update lock, force the getting of the unloaded update lock.
        /// If we get a control lock, force getting the update and unloaded update
        /// </summary>
        public void LockAcquire(LockDefinition lockDefinition)
        {
            switch (lockDefinition.Type)
            {
            case LockType.Control:
                if (lockDefinition.PlayerName == SettingsSystem.CurrentSettings.PlayerName)
                {
                    if (VesselCommon.IsSpectating)
                    {
                        VesselLockSystem.Singleton.StopSpectating();
                    }
                    LockSystem.Singleton.AcquireUpdateLock(lockDefinition.VesselId, true);
                    LockSystem.Singleton.AcquireUnloadedUpdateLock(lockDefinition.VesselId, true);
                    LockSystem.Singleton.AcquireKerbalLock(lockDefinition.VesselId, true);

                    //As we got the lock of that vessel, remove its FS and position updates
                    //This is done so even if the vessel has queued updates, we ignore them as we are controlling it
                    VesselCommon.RemoveVesselFromSystems(lockDefinition.VesselId);
                }
                else
                {
                    //Somebody else got the control of our active vessel so start spectating
                    if (FlightGlobals.ActiveVessel && FlightGlobals.ActiveVessel.id == lockDefinition.VesselId)
                    {
                        System.StartSpectating(lockDefinition.VesselId);
                    }

                    //If some other player got the control lock release the update lock in case we have it
                    if (LockSystem.LockQuery.UpdateLockBelongsToPlayer(lockDefinition.VesselId, SettingsSystem.CurrentSettings.PlayerName))
                    {
                        LockSystem.LockStore.RemoveLock(LockSystem.LockQuery.GetUpdateLock(lockDefinition.VesselId));
                        LockSystem.LockStore.AddOrUpdateLock(new LockDefinition(LockType.UnloadedUpdate, lockDefinition.PlayerName, lockDefinition.VesselId));
                    }

                    //If some other player got the control lock release the unloaded update lock in case we have it
                    if (LockSystem.LockQuery.UnloadedUpdateLockBelongsToPlayer(lockDefinition.VesselId, SettingsSystem.CurrentSettings.PlayerName))
                    {
                        LockSystem.LockStore.RemoveLock(LockSystem.LockQuery.GetUnloadedUpdateLock(lockDefinition.VesselId));
                        LockSystem.LockStore.AddOrUpdateLock(new LockDefinition(LockType.UnloadedUpdate, lockDefinition.PlayerName, lockDefinition.VesselId));
                    }

                    //TODO:We should release the kerbals locks?
                }
                break;

            case LockType.Update:
                if (lockDefinition.PlayerName != SettingsSystem.CurrentSettings.PlayerName)
                {
                    //If some other player got the update lock release the unloaded update lock  just in case we have it
                    if (LockSystem.LockQuery.UnloadedUpdateLockBelongsToPlayer(lockDefinition.VesselId, SettingsSystem.CurrentSettings.PlayerName))
                    {
                        LockSystem.LockStore.RemoveLock(LockSystem.LockQuery.GetUnloadedUpdateLock(lockDefinition.VesselId));
                        LockSystem.LockStore.AddOrUpdateLock(new LockDefinition(LockType.UnloadedUpdate, lockDefinition.PlayerName, lockDefinition.VesselId));
                    }

                    //TODO:We should release the kerbals locks?
                }
                else
                {
                    LockSystem.Singleton.AcquireUnloadedUpdateLock(lockDefinition.VesselId, true);
                    LockSystem.Singleton.AcquireKerbalLock(lockDefinition.VesselId, true);
                }
                break;
            }
        }
示例#4
0
        public void HandleMessage(IMessageData messageData)
        {
            var msgData = messageData as VesselUpdateMsgData;

            if (msgData == null || !System.UpdateSystemBasicReady || VesselCommon.UpdateIsForOwnVessel(msgData.VesselId))
            {
                return;
            }

            var update = new VesselUpdate
            {
                Id                 = Guid.NewGuid(),
                ReceiveTime        = Time.fixedTime,
                PlanetTime         = msgData.PlanetTime,
                Stage              = msgData.Stage,
                SentTime           = msgData.GameSentTime,
                ActiveEngines      = msgData.ActiveEngines,
                StoppedEngines     = msgData.StoppedEngines,
                Decouplers         = msgData.Decouplers,
                AnchoredDecouplers = msgData.AnchoredDecouplers,
                Clamps             = msgData.Clamps,
                Docks              = msgData.Docks,
                VesselId           = msgData.VesselId,
                BodyName           = msgData.BodyName,
                Rotation           = msgData.Rotation,
                FlightState        = new FlightCtrlState
                {
                    mainThrottle      = msgData.MainThrottle,
                    wheelThrottleTrim = msgData.WheelThrottleTrim,
                    X              = msgData.X,
                    Y              = msgData.Y,
                    Z              = msgData.Z,
                    killRot        = msgData.KillRot,
                    gearUp         = msgData.GearUp,
                    gearDown       = msgData.GearDown,
                    headlight      = msgData.Headlight,
                    wheelThrottle  = msgData.WheelThrottle,
                    roll           = msgData.Roll,
                    yaw            = msgData.Yaw,
                    pitch          = msgData.Pitch,
                    rollTrim       = msgData.RollTrim,
                    yawTrim        = msgData.YawTrim,
                    pitchTrim      = msgData.PitchTrim,
                    wheelSteer     = msgData.WheelSteer,
                    wheelSteerTrim = msgData.WheelSteerTrim
                },
                ActionGrpControls = msgData.ActiongroupControls,
                IsSurfaceUpdate   = msgData.IsSurfaceUpdate
            };

            if (update.IsSurfaceUpdate)
            {
                update.Position     = msgData.Position;
                update.Velocity     = msgData.Velocity;
                update.Acceleration = msgData.Acceleration;
            }
            else
            {
                update.Orbit = msgData.Orbit;
            }

            if (!System.ReceivedUpdates.ContainsKey(update.VesselId))
            {
                System.ReceivedUpdates.Add(update.VesselId, new Queue <VesselUpdate>());
            }

            if (System.ReceivedUpdates[update.VesselId].Count + 1 > VesselUpdateInterpolationSystem.MaxTotalUpdatesInQueue)
            {
                System.ReceivedUpdates[update.VesselId].Dequeue();
            }

            System.ReceivedUpdates[update.VesselId].Enqueue(update);
        }
示例#5
0
 public VesselProtoUpdate(ConfigNode vessel, Guid vesselId)
 {
     VesselId    = vesselId;
     ProtoVessel = VesselCommon.CreateSafeProtoVesselFromConfigNode(vessel, vesselId);
 }
        public void ProcessPartFieldSync()
        {
            var vessel = FlightGlobals.FindVessel(VesselId);

            if (vessel == null)
            {
                return;
            }

            if (!VesselCommon.DoVesselChecks(VesselId))
            {
                return;
            }

            var part = vessel.protoVessel.GetProtoPart(PartFlightId);

            if (part != null)
            {
                var module = part.FindProtoPartModuleInProtoPart(ModuleName);
                if (module != null)
                {
                    switch (FieldType)
                    {
                    case PartSyncFieldType.Boolean:
                        module.moduleValues.SetValue(FieldName, BoolValue);
                        PartModuleEvent.onPartModuleBoolFieldProcessed.Fire(module, FieldName, BoolValue);
                        break;

                    case PartSyncFieldType.Integer:
                        module.moduleValues.SetValue(FieldName, IntValue);
                        PartModuleEvent.onPartModuleIntFieldProcessed.Fire(module, FieldName, IntValue);
                        break;

                    case PartSyncFieldType.Float:
                        module.moduleValues.SetValue(FieldName, FloatValue);
                        PartModuleEvent.onPartModuleFloatFieldProcessed.Fire(module, FieldName, FloatValue);
                        break;

                    case PartSyncFieldType.Double:
                        module.moduleValues.SetValue(FieldName, DoubleValue);
                        PartModuleEvent.onPartModuleDoubleFieldProcessed.Fire(module, FieldName, DoubleValue);
                        break;

                    case PartSyncFieldType.Vector3:
                        module.moduleValues.SetValue(FieldName, VectorValue);
                        PartModuleEvent.onPartModuleVectorFieldProcessed.Fire(module, FieldName, VectorValue);
                        break;

                    case PartSyncFieldType.Quaternion:
                        module.moduleValues.SetValue(FieldName, QuaternionValue);
                        PartModuleEvent.onPartModuleQuaternionFieldProcessed.Fire(module, FieldName, QuaternionValue);
                        break;

                    case PartSyncFieldType.String:
                        module.moduleValues.SetValue(FieldName, StrValue);
                        PartModuleEvent.onPartModuleStringFieldProcessed.Fire(module, FieldName, StrValue);
                        break;

                    case PartSyncFieldType.Enum:
                        module.moduleValues.SetValue(FieldName, StrValue);
                        PartModuleEvent.onPartModuleEnumFieldProcessed.Fire(module, FieldName, IntValue, StrValue);
                        break;

                    case PartSyncFieldType.Object:
                        module.moduleValues.SetValue(FieldName, StrValue);
                        PartModuleEvent.onPartModuleObjectFieldProcessed.Fire(module, FieldName, StrValue);
                        //We do not set the value of objects in the module as we cannot be sure if they can be transformed from a string back to the object
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }
示例#7
0
        /// <summary>
        /// The debris that is on the safety bubble SHOULD NEVER be synced with the server and at the same time
        /// it won't exist for any other player so here we just remove it in a routine
        /// </summary>
        private void RemoveSafetyBubbleDebris()
        {
            DebrisInSafetyBubbleToRemove.Clear();
            DebrisInSafetyBubbleToRemove.AddRange(FlightGlobals.Vessels.Where(v => v != null && v.state == Vessel.State.INACTIVE && v.vesselType != VesselType.Flag &&
                                                                              v.id != FlightGlobals.ActiveVessel?.id && VesselCommon.IsInSafetyBubble(v)));
            foreach (var vessel in DebrisInSafetyBubbleToRemove)
            {
                if (vessel == null)
                {
                    continue;
                }

                LunaLog.Log($"[LMP]: Vessel {vessel.id} name {vessel.vesselName} it's an inactive vessel inside the safety bubble.");
                KillVessel(vessel.id);
            }
        }
        /// <summary>
        /// Check vessels that must be reloaded
        /// </summary>
        private void CheckVesselsToRefresh()
        {
            try
            {
                if ((DateTime.UtcNow - LastReloadCheck).TotalMilliseconds > 1500 && ProtoSystemBasicReady && !VesselCommon.ActiveVesselIsInSafetyBubble())
                {
                    VesselsToRefresh.Clear();

                    //We get the vessels that already exist
                    VesselsToRefresh.AddRange(VesselsProtoStore.AllPlayerVessels
                                              .Where(pv => pv.Value.VesselExist && pv.Value.VesselHasUpdate)
                                              .Select(v => v.Key));

                    //Do not iterate directly trough the AllPlayerVessels dictionary as the collection can be modified in another threads!
                    foreach (var vesselIdToReload in VesselsToRefresh)
                    {
                        if (VesselRemoveSystem.VesselWillBeKilled(vesselIdToReload))
                        {
                            continue;
                        }

                        //Do not handle vessel proto updates over our OWN active vessel if we are not spectating
                        //If there is an undetected dock (our protovessel has been modified) it will be detected
                        //in the docksystem
                        if (vesselIdToReload == FlightGlobals.ActiveVessel?.id && !VesselCommon.IsSpectating)
                        {
                            continue;
                        }

                        if (VesselsProtoStore.AllPlayerVessels.TryGetValue(vesselIdToReload, out var vesselProtoUpdate))
                        {
                            CurrentlyUpdatingVesselId = vesselIdToReload;
                            ProtoToVesselRefresh.UpdateVesselPartsFromProtoVessel(vesselProtoUpdate.Vessel, vesselProtoUpdate.ProtoVessel, vesselProtoUpdate.VesselParts.Keys);
                            vesselProtoUpdate.VesselHasUpdate = false;
                            CurrentlyUpdatingVesselId         = Guid.Empty;
                        }
                    }

                    LastReloadCheck = DateTime.UtcNow;
                }
            }
            catch (Exception e)
            {
                LunaLog.LogError($"[LMP]: Error in CheckVesselsToReload {e}");
            }
        }
示例#9
0
 private static bool PositionUpdateIsTooOld(VesselPositionUpdate update)
 {
     return(update.GameTimeStamp < TimeSyncSystem.UniversalTime - VesselCommon.PositionAndFlightStateMessageOffsetSec(update.PingSec));
 }
 /// <summary>
 /// Kills a vessel.
 /// </summary>
 public void KillVessel(Guid vesselId, string reason)
 {
     VesselCommon.RemoveVesselFromSystems(vesselId);
     KillVessel(FlightGlobals.fetch.LmpFindVessel(vesselId), reason);
 }