/// <summary> /// Do some basic validations over the protovessel /// </summary> private static bool ProtoVesselValidationsPassed(ProtoVessel vesselProto) { if (vesselProto == null) { LunaLog.LogError("[LMP]: protoVessel is null!"); return(false); } if (vesselProto.vesselID == Guid.Empty) { LunaLog.LogError("[LMP]: protoVessel id is null!"); return(false); } if (vesselProto.situation == Vessel.Situations.FLYING) { if (vesselProto.orbitSnapShot == null) { LunaLog.LogWarning("[LMP]: Skipping flying vessel load - Protovessel does not have an orbit snapshot"); return(false); } if (FlightGlobals.Bodies == null || FlightGlobals.Bodies.Count < vesselProto.orbitSnapShot.ReferenceBodyIndex) { LunaLog.LogWarning($"[LMP]: Skipping flying vessel load - Could not find celestial body index {vesselProto.orbitSnapShot.ReferenceBodyIndex}"); return(false); } } return(true); }
public void SendVesselMessage(Vessel vessel, bool forceReload = false) { if (vessel == null || vessel.state == Vessel.State.DEAD || VesselRemoveSystem.Singleton.VesselWillBeKilled(vessel.id)) { return; } if (!vessel.orbitDriver) { LunaLog.LogWarning($"Cannot send vessel {vessel.vesselName} - {vessel.id}. It's orbit driver is null!"); return; } if (vessel.orbitDriver.Ready()) { vessel.protoVessel = vessel.BackupVessel(); SendVesselMessage(vessel.protoVessel, forceReload); } else { //Orbit driver is not ready so wait max 10 frames until it's ready CoroutineUtil.StartConditionRoutine("SendVesselMessage", () => SendVesselMessage(vessel), () => vessel.orbitDriver.Ready(), 10); } }
private static bool ConnectionIsStuck(int maxIdleMiliseconds = 2000) { if ((LunaComputerTime.UtcNow - _lastStateTime).TotalMilliseconds > maxIdleMiliseconds) { LunaLog.LogWarning($"Connection got stuck while connecting after waiting {maxIdleMiliseconds} ms, resending last request!"); return(true); } return(false); }
/// <summary> /// Checks if the protovessel has resources, parts that you don't have or that they are banned /// </summary> public static bool HasInvalidParts(this ProtoVessel pv, bool verboseErrors) { foreach (var pps in pv.protoPartSnapshots) { if (ModSystem.Singleton.ModControl && !ModSystem.Singleton.AllowedParts.Contains(pps.partName)) { if (verboseErrors) { var msg = $"Protovessel {pv.vesselID} ({pv.vesselName}) contains the BANNED PART '{pps.partName}'. Skipping load."; LunaLog.LogWarning(msg); ChatSystem.Singleton.PmMessageServer(msg); } return(true); } var invalidResources = pps.resources.Select(r => r.resourceName).Except(ModSystem.Singleton.AllowedResources).ToArray(); if (ModSystem.Singleton.ModControl && invalidResources.Any()) { if (verboseErrors) { var msg = $"Protovessel {pv.vesselID} ({pv.vesselName}) contains the BANNED RESOURCE/S '{string.Join(", ", invalidResources)}'. Skipping load."; LunaLog.LogWarning(msg); ChatSystem.Singleton.PmMessageServer(msg); } return(true); } if (pps.partInfo == null) { if (verboseErrors) { LunaLog.LogWarning($"Protovessel {pv.vesselID} ({pv.vesselName}) contains the MISSING PART '{pps.partName}'. Skipping load."); LunaScreenMsg.PostScreenMessage($"Cannot load '{pv.vesselName}' - missing part: {pps.partName}", 10f, ScreenMessageStyle.UPPER_CENTER); } return(true); } var missingResource = pps.resources.FirstOrDefault(r => !PartResourceLibrary.Instance.resourceDefinitions.Contains(r.resourceName)); if (missingResource != null && verboseErrors) { var msg = $"Protovessel {pv.vesselID} ({pv.vesselName}) contains the MISSING RESOURCE '{missingResource.resourceName}'."; LunaLog.LogWarning(msg); ChatSystem.Singleton.PmMessageServer(msg); LunaScreenMsg.PostScreenMessage($"Vessel '{pv.vesselName}' contains the modded RESOURCE: {pps.partName}", 10f, ScreenMessageStyle.UPPER_CENTER); //We allow loading of vessels that have missing resources. They will be removed by the player with the lock tough... } } return(false); }
public void PartModuleObjectFieldChanged(PartModule module, string fieldName, object newValue) { if (!CallIsValid(module, fieldName)) { return; } LunaLog.Log($"Field {fieldName} in module {module.moduleName} from part {module.part.flightID} has a new OBJECT value of {newValue}."); LunaLog.LogWarning($"Field {fieldName} in module {module.moduleName} from part {module.part.flightID} has a field type that is not supported!"); System.MessageSender.SendVesselPartSyncFieldObjectMsg(module.vessel, module.part, module.moduleName, fieldName, newValue); }
/// <summary> /// Forces a time sync against the server time /// </summary> public void ForceTimeSync() { if (Enabled && !CurrentlyWarping && CanSyncTime && !WarpSystem.Singleton.WaitingSubspaceIdFromServer) { var targetTime = WarpSystem.Singleton.CurrentSubspaceTime; var currentError = TimeUtil.SecondsToMilliseconds(CurrentErrorSec); LunaLog.LogWarning($"FORCING a time sync from: {Planetarium.GetUniversalTime()} to: {targetTime}. Error:{currentError}"); ClockHandler.StepClock(targetTime); } }
/// <summary> /// Routine that checks our time against the server time and adjust it if needed. /// If the error is too big this routine will adjust the clock with the planetarium instead of accelerating / decreasing the game time /// </summary> /// <returns></returns> private void SyncTime() { if (Enabled && !CurrentlyWarping && CanSyncTime && !SystemsContainer.Get <WarpSystem>().WaitingSubspaceIdFromServer) { var targetTime = (int)SystemsContainer.Get <WarpSystem>().CurrentSubspaceTime; var currentError = TimeSpan.FromSeconds(CurrentErrorSec).TotalMilliseconds; if (targetTime != 0 && Math.Abs(currentError) > MaxClockErrorMs) { LunaLog.LogWarning($"[LMP] Adjusted time from: {Planetarium.GetUniversalTime()} to: {targetTime} due to error:{currentError}"); ClockHandler.StepClock(targetTime); } } }
/// <summary> /// Routine that checks our time against the server time and adjust it if needed. /// If the error is too big this routine will adjust the clock with the planetarium instead of accelerating / decreasing the game time /// </summary> /// <returns></returns> private void SyncTime() { if (Enabled && !CurrentlyWarping && CanSyncTime && !WarpSystem.Singleton.WaitingSubspaceIdFromServer) { var targetTime = WarpSystem.Singleton.CurrentSubspaceTime; var currentError = TimeUtil.SecondsToMilliseconds(CurrentErrorSec); if (targetTime != 0 && Math.Abs(currentError) > MaxClockErrorMs) { LunaLog.LogWarning($"[LMP] Adjusted time from: {Planetarium.GetUniversalTime()} to: {targetTime} due to error:{currentError}"); ClockHandler.StepClock(targetTime); } } }
/// <summary> /// Creates a protovessel from a ConfigNode /// </summary> public static ProtoVessel CreateSafeProtoVesselFromConfigNode(ConfigNode inputNode, Guid protoVesselId) { try { //Cannot create a protovessel if HighLogic.CurrentGame is null as we don't have a CrewRoster //and the protopartsnapshot constructor needs it if (HighLogic.CurrentGame == null) { return(null); } //Cannot reuse the Protovessel to save memory garbage as it does not have any clear method :( var pv = new ProtoVessel(inputNode, HighLogic.CurrentGame); foreach (var pps in pv.protoPartSnapshots) { if (ModSystem.Singleton.ModControl && !ModSystem.Singleton.AllowedParts.Contains(pps.partName)) { var msg = $"Protovessel {protoVesselId} ({pv.vesselName}) contains the BANNED PART '{pps.partName}'. Skipping load."; LunaLog.LogWarning(msg); ChatSystem.Singleton.PmMessageServer(msg); return(null); } if (pps.partInfo == null) { LunaLog.LogWarning($"WARNING: Protovessel {protoVesselId} ({pv.vesselName}) contains the MISSING PART '{pps.partName}'. Skipping load."); LunaScreenMsg.PostScreenMessage($"Cannot load '{pv.vesselName}' - missing {pps.partName}", 10f, ScreenMessageStyle.UPPER_CENTER); return(null); } var missingeResource = pps.resources.FirstOrDefault(r => !PartResourceLibrary.Instance.resourceDefinitions.Contains(r.resourceName)); if (missingeResource != null) { var msg = $"WARNING: Protovessel {protoVesselId} ({pv.vesselName}) contains the MISSING RESOURCE '{missingeResource.resourceName}'. Skipping load."; LunaLog.LogWarning(msg); ChatSystem.Singleton.PmMessageServer(msg); LunaScreenMsg.PostScreenMessage($"Cannot load '{pv.vesselName}' - missing resource {missingeResource.resourceName}", 10f, ScreenMessageStyle.UPPER_CENTER); return(null); } } return(pv); } catch (Exception e) { LunaLog.LogError($"[LMP]: Damaged vessel {protoVesselId}, exception: {e}"); return(null); } }
/// <summary> /// Check vessels that must be reloaded /// </summary> private void CheckVesselsToRefresh() { try { if (TimeUtil.IsInInterval(ref _lastReloadCheck, 1500) && ProtoSystemBasicReady) { VesselsToRefresh.Clear(); //We get the vessels that already exist VesselsToRefresh.AddRange(VesselsProtoStore.AllPlayerVessels .Where(pv => pv.Value.VesselExist && pv.Value.VesselHasUpdate && !pv.Value.HasInvalidParts) .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; } if (FlightGlobals.ActiveVessel?.id == vesselIdToReload) { LunaLog.LogWarning("Reloading our OWN active vessel!"); } if (VesselsProtoStore.AllPlayerVessels.TryGetValue(vesselIdToReload, out var vesselProtoUpd)) { CurrentlyUpdatingVesselId = vesselIdToReload; ProtoToVesselRefresh.UpdateVesselPartsFromProtoVessel(vesselProtoUpd.Vessel, vesselProtoUpd.ProtoVessel, vesselProtoUpd.ForceReload, vesselProtoUpd.VesselParts.Keys); vesselProtoUpd.VesselHasUpdate = false; VesselReloadEvent.onLmpVesselReloaded.Fire(vesselProtoUpd.Vessel); CurrentlyUpdatingVesselId = Guid.Empty; } } } } catch (Exception e) { LunaLog.LogError($"[LMP]: Error in CheckVesselsToReload {e}"); } }
/// <summary> /// Checks the protovessel for errors /// </summary> public static bool Validate(this ProtoVessel protoVessel) { if (protoVessel == null) { LunaLog.LogError("[LMP]: protoVessel is null!"); return(false); } if (protoVessel.vesselID == Guid.Empty) { LunaLog.LogError("[LMP]: protoVessel id is null!"); return(false); } if (protoVessel.situation == Vessel.Situations.FLYING) { if (protoVessel.orbitSnapShot == null) { LunaLog.LogWarning("[LMP]: Skipping flying vessel load - Protovessel does not have an orbit snapshot"); return(false); } if (FlightGlobals.Bodies == null || FlightGlobals.Bodies.Count < protoVessel.orbitSnapShot.ReferenceBodyIndex) { LunaLog.LogWarning($"[LMP]: Skipping flying vessel load - Could not find celestial body index {protoVessel.orbitSnapShot.ReferenceBodyIndex}"); return(false); } } //Fix the flags urls in the vessel. The flag have the value as: "Squad/Flags/default" foreach (var part in protoVessel.protoPartSnapshots.Where(p => !string.IsNullOrEmpty(p.flagURL))) { if (!FlagSystem.Singleton.FlagExists(part.flagURL)) { LunaLog.Log($"[LMP]: Flag '{part.flagURL}' doesn't exist, setting to default!"); part.flagURL = "Squad/Flags/default"; } } return(true); }
/// <summary> /// Routine that checks our time against the server time and adjust it if needed. /// </summary> /// <returns></returns> private void SyncTime() { if (Enabled && Synced && !CurrentlyWarping && CanSyncTime() && !SystemsContainer.Get <WarpSystem>().WaitingSubspaceIdFromServer) { var targetTime = SystemsContainer.Get <WarpSystem>().GetCurrentSubspaceTime(); var currentError = TimeSpan.FromSeconds(GetCurrentError()).TotalMilliseconds; if (targetTime != 0 && Math.Abs(currentError) > MaxClockMsError) { if (Math.Abs(currentError) > MaxClockSkew) { LunaLog.LogWarning($"[LMP] Adjusted time from: {Planetarium.GetUniversalTime()} to: {targetTime} due to error:{currentError}"); //TODO: This causes the throttle to reset when called. This happens due to vessel unpacking resetting the throttle controls. //TODO: Try to get Squad to change their code. ClockHandler.StepClock(targetTime); } else { SkewClock(currentError); } } } }
/// <summary> /// Routine that checks our time against the server time and adjust it if needed. /// We only adjust the GAME time. And we will only do if the time error is between <see cref="MinPhisicsClockMsError"/> and <see cref="MaxPhisicsClockMsError"/> /// We cannot do it with more than <see cref="MaxPhisicsClockMsError"/> as then we would need a lot of time to catch up with the time error. /// For greater errors we just fix the time with the StepClock /// </summary> private void SyncTimeScale() { if (Enabled && !CurrentlyWarping && CanSyncTime && !WarpSystem.Singleton.WaitingSubspaceIdFromServer) { var targetTime = WarpSystem.Singleton.CurrentSubspaceTime; var currentError = TimeUtil.SecondsToMilliseconds(CurrentErrorSec); if (Math.Abs(currentError) < MinPhisicsClockMsError) { Time.timeScale = 1; } if (Math.Abs(currentError) > MinPhisicsClockMsError && Math.Abs(currentError) < MaxPhisicsClockMsError) { //Time error is not so big so we can fix it adjusting the physics time SkewClock(); } else if (Math.Abs(currentError) > MaxPhisicsClockMsError) { LunaLog.LogWarning($"[LMP] Adjusted time from: {UniversalTime} to: {targetTime} due to error: {currentError}"); ClockHandler.StepClock(targetTime); } } }
/// <summary> /// Load all the received vessels from the server into the game /// </summary> public void LoadVesselsIntoGame() { LunaLog.Log("[LMP]: Loading vessels in subspace 0 into game"); var numberOfLoads = 0; foreach (var vessel in System.AllPlayerVessels) { if (vessel.Value.ProtoVessel != null && vessel.Value.ProtoVessel.vesselID == vessel.Key) { RegisterServerAsteriodIfVesselIsAsteroid(vessel.Value.ProtoVessel); HighLogic.CurrentGame.flightState.protoVessels.Add(vessel.Value.ProtoVessel); numberOfLoads++; } else { LunaLog.LogWarning($"[LMP]: Protovessel {vessel.Key} is DAMAGED!. Skipping load."); SystemsContainer.Get <ChatSystem>().PmMessageServer($"WARNING: Protovessel {vessel.Key} is DAMAGED!. Skipping load."); } vessel.Value.Loaded = true; } LunaLog.Log($"[LMP]: {numberOfLoads} Vessels loaded into game"); }
public void OnVesselCreate(Vessel vessel) { //Kerbals are put in the vessel *after* OnVesselCreate. Thanks squad!. if (System.VesselToKerbal.ContainsKey(vessel.id)) { OnVesselDestroyed(vessel); } if (vessel.GetCrewCount() > 0) { System.VesselToKerbal.Add(vessel.id, new List <string>()); foreach (var protoCrewMember in vessel.GetVesselCrew()) { System.VesselToKerbal[vessel.id].Add(protoCrewMember.name); if (System.KerbalToVessel.ContainsKey(protoCrewMember.name) && System.KerbalToVessel[protoCrewMember.name] != vessel.id) { LunaLog.LogWarning($"[LMP]: Warning, kerbal double take on {vessel.id} ({vessel.name})"); } System.KerbalToVessel[protoCrewMember.name] = vessel.id; LunaLog.Log($"[LMP]: OVC {protoCrewMember.name} belongs to {vessel.id}"); } } }
private static bool CheckDetouring(MethodInfo source, MethodInfo destination) { var sourceStr = source.DeclaringType?.FullName + "." + source.Name + " @ 0x" + source.MethodHandle.GetFunctionPointer().ToString("X" + IntPtr.Size * 2); var destStr = destination.DeclaringType?.FullName + "." + destination.Name + " @ 0x" + destination.MethodHandle.GetFunctionPointer().ToString("X" + IntPtr.Size * 2); if (Detours.ContainsKey(sourceStr)) { //Othwerise we are just detouring to the same method... if (destStr != Detours[sourceStr]) { LunaLog.LogWarning($"[Detour] Source method('{sourceStr}') was previously detoured to '{Detours[sourceStr]}'"); } return(false); } Detours.Add(sourceStr, destStr); LunaLog.Log($"[Detour] Detouring '{sourceStr}' to '{destStr}'"); return(true); }
public void HandleMessage(IMessageData messageData) { var msgData = messageData as GroupBaseMsgData; if (msgData == null) { return; } switch (msgData.GroupMessageType) { case GroupMessageType.Add: { var data = (GroupAddMsgData)messageData; System.RegisterGroup(data.GroupName, data.Owner); } break; case GroupMessageType.Remove: { var data = (GroupRemoveMsgData)messageData; System.DeregisterGroup(data.GroupName); } break; case GroupMessageType.Invite: { var data = (GroupInviteMsgData)messageData; if (data.AddressedTo == SettingsSystem.CurrentSettings.PlayerName) { System.Invite(data.GroupName); } } break; case GroupMessageType.Accept: { var data = (GroupAcceptMsgData)messageData; System.AddPlayerToGroup(data.GroupName, data.AddressedTo); } break; case GroupMessageType.Kick: { var data = (GroupKickMsgData)messageData; System.KickPlayerFromGroup(data.GroupName, data.Player); } break; case GroupMessageType.ListResponse: { var data = (GroupListResponseMsgData)messageData; if (data.Groups.Length != data.Owners.Length) { LunaLog.LogWarning("Malformed message of type GroupSystem.ListResponse"); } else { for (var i = 0; i < data.Groups.Length; i++) { if (!System.GroupExists(data.Groups[i])) { System.RegisterGroup(data.Groups[i], data.Owners[i]); } } } if (!System.IsSynced) { if (data.Groups.Length == 0) { System.IsSynced = true; MainSystem.NetworkState = ClientState.GroupsSynced; } System.NumGroups = data.Groups.Length; System.NumGroupsSynced = 0; foreach (var groupName in data.Groups) { System.MessageSender.SendMessage(new GroupUpdateRequestMsgData { GroupName = groupName }); } } } break; case GroupMessageType.UpdateResponse: { var data = (GroupUpdateResponseMsgData)messageData; if (System.NumGroupsSynced < System.NumGroups) { System.NumGroupsSynced += 1; } if (System.NumGroupsSynced == System.NumGroups) { MainSystem.NetworkState = ClientState.GroupsSynced; System.IsSynced = true; } if (!System.GroupExists(data.Name)) { System.RegisterGroup(data.Name, data.Owner); } foreach (string member in data.Members) { System.AddPlayerToGroup(data.Name, member); } } break; } }
public void Start() { // Checkers are identified by the type Name and version field Name. var fields = GetAllTypes() .Where(t => t.Name == "CompatibilityChecker") .Select(t => t.GetField("_version", BindingFlags.Static | BindingFlags.NonPublic)) .Where(f => f != null) .Where(f => f.FieldType == typeof(int)) .ToArray(); // Let the latest version of the checker execute. if (_version != fields.Max(f => (int)f.GetValue(null))) { return; } LunaLog.Log($"[CompatibilityChecker] Running checker version {_version} from '{Assembly.GetExecutingAssembly().GetName().Name}'"); // Other checkers will see this version and not run. // This accomplishes the same as an explicit "ran" flag with fewer moving parts. _version = int.MaxValue; // A mod is incompatible if its compatibility checker has an IsCompatible method which returns false. var incompatible = fields .Select(f => f.DeclaringType.GetMethod("IsCompatible", Type.EmptyTypes)) .Where(m => m.IsStatic) .Where(m => m.ReturnType == typeof(bool)) .Where(m => { try { return(!(bool)m.Invoke(null, new object[0])); } catch (Exception e) { // If a mod throws an exception from IsCompatible, it's not compatible. LunaLog.LogWarning($"[CompatibilityChecker] Exception while invoking IsCompatible() from '{m.DeclaringType?.Assembly.GetName().Name}':\n\n{e}"); return(true); } }) .Select(m => m.DeclaringType?.Assembly.GetName().Name) .ToArray(); // A mod is incompatible with Unity if its compatibility checker has an IsUnityCompatible method which returns false. var incompatibleUnity = fields .Select(f => f.DeclaringType.GetMethod("IsUnityCompatible", Type.EmptyTypes)) .Where(m => m != null) // Mods without IsUnityCompatible() are assumed to be compatible. .Where(m => m.IsStatic) .Where(m => m.ReturnType == typeof(bool)) .Where(m => { try { return(!(bool)m.Invoke(null, new object[0])); } catch (Exception e) { // If a mod throws an exception from IsUnityCompatible, it's not compatible. LunaLog.LogWarning($"[CompatibilityChecker] Exception while invoking IsUnityCompatible() from '{m.DeclaringType?.Assembly.GetName().Name}':\n\n{e}"); return(true); } }) .Select(m => m.DeclaringType?.Assembly.GetName().Name) .ToArray(); Array.Sort(incompatible); Array.Sort(incompatibleUnity); var message = "Some installed mods may be incompatible with this version of Kerbal Space Program. Features may be broken or disabled. Please check for updates to the listed mods."; if (incompatible.Length > 0) { LunaLog.LogWarning($"[CompatibilityChecker] Incompatible mods detected: {string.Join(", ", incompatible)}"); message += $"\n\nThese mods are incompatible with KSP {Versioning.version_major}.{Versioning.version_minor}.{Versioning.Revision}:\n\n"; message += string.Join("\n", incompatible); } if (incompatibleUnity.Length > 0) { LunaLog.LogWarning("[CompatibilityChecker] Incompatible mods (Unity) detected: " + string.Join(", ", incompatibleUnity)); message += $"\n\nThese mods are incompatible with Unity {Application.unityVersion}:\n\n"; message += string.Join("\n", incompatibleUnity); } if ((incompatible.Length > 0) || (incompatibleUnity.Length > 0)) { PopupDialog.SpawnPopupDialog(new MultiOptionDialog("CompatibilityChecker", message, "Incompatible Mods Detected", HighLogic.UISkin), true, HighLogic.UISkin); } }