Jump to content

xEvilReeperx

Members
  • Posts

    894
  • Joined

  • Last visited

Everything posted by xEvilReeperx

  1. Well, I meant fixing them ourselves, not just passing the info on. I haven't had any numbers nearly as high as yours, but for example, the Learstar A1 sitting on the pad with engines running (no other vessels) generates about 7kB of garbage per update and ~15kB per fixed update on my testbed install so wasting 1.5kB in a LateUpdate is significant
  2. @Padishar Have you also considered looking for low hanging stock fruit to fix up? The main editor GC problem seems fixable to me and there are some small bits we can fix here and there (METDisplay wastes 1.5 kB every update frame for example) which could add up to some significant GC reduction
  3. @JPLRepo The order hasn't changed, but now the event list gets copied into a local value so it can't be affected while the event is firing (meaning all the previous code is useless now). You could either rebuild the ConfigNode as was suggested earlier or else devise a way to control GameEvents.onScienceReceived somehow. Since that field is actually writable and you can force ProgressNode to add/remove callbacks at will, it's possible to mickey mouse a solution by tricking ProgressNode into registering for a proxy GameEvent which you can then control as you want. Here's my proof of concept: [KSPAddon(KSPAddon.Startup.Instantly, true)] class ScienceProgressionBlocker : MonoBehaviour { public static readonly EventData<float, ScienceSubject, ProtoVessel, bool> ProxyOnScienceReceived = new EventData<float, ScienceSubject, ProtoVessel, bool>("Proxy.OnScienceReceived"); private static ScienceProgressionBlocker Instance { get; set; } private static bool _block = false; public static void BlockSingleEvent() { _block = true; } private void Awake() { if (Instance != null) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(this); // GameEvents throws an exception on static methods, so we need a reference ;\ // We want this event to be the very first one, if possible. That will ensure it runs last GameEvents.OnScienceRecieved.Add(OnScienceReceived); } private void OnScienceReceived(float data0, ScienceSubject data1, ProtoVessel data2, bool data3) { if (!_block) ProxyOnScienceReceived.Fire(data0, data1, data2, data3); _block = false; } } [KSPScenario(ScenarioCreationOptions.AddToAllGames, GameScenes.FLIGHT, GameScenes.TRACKSTATION, GameScenes.SPACECENTER)] public class ProgressTracker_TrapTest : ScenarioModule { private IEnumerator Start() { yield return new WaitForEndOfFrame(); // ProgressTracker might not have started, wait and make sure if (ProgressTracking.Instance == null) { Debug.LogError("ProgressTracking instance not found!"); yield break; } foreach (var cb in FlightGlobals.Bodies) { var tree = ProgressTracking.Instance.GetBodyTree(cb); var scienceAchievement = tree.science; // remove science callbacks to onScienceReceived and onScienceDataTransmitted scienceAchievement.OnStow(); // wrap the add/remove callbacks inside another little method that tricks them into // registering with the proxy GameEvents var originalStow = scienceAchievement.OnStow; var originalDeploy = scienceAchievement.OnDeploy; scienceAchievement.OnStow = () => { SwapInProxyEvents(originalStow); }; scienceAchievement.OnDeploy = () => { SwapInProxyEvents(originalDeploy); }; // restore science callbacks (although now they'll register with ScienceProgressionBlocker instead of the real ones) scienceAchievement.OnDeploy(); } } private static void SwapInProxyEvents(Callback call) { var original = GameEvents.OnScienceRecieved; try { GameEvents.OnScienceRecieved = ScienceProgressionBlocker.ProxyOnScienceReceived; call(); } finally { GameEvents.OnScienceRecieved = original; } } } And then your contract award method becomes this: protected override void AwardCompletion() { ScienceProgressionBlocker.BlockSingleEvent(); base.AwardCompletion(); } As is this will only prevent science progression if the related contract is active and submitting science (whether recovery or transmission) completes it. You might not want the telescope to trip any progress nodes at all ever in which case you could filter out any events involving a particular experiment or however you want to do it
  4. Dark Matter, Killjoys, and Continuum if you haven't tried them out yet
  5. You essentially have the right idea on the first one. The idea is to add a script onto prefab the game will use for each gizmo so that when the game clones it, your script will get cloned too. Then the usual MonoBehaviour methods will start running which is your chance to fire an event, add the gizmo to a list of opened gizmos, or whatever you want to do with it. Here's a self-contained example in the style of GameEvents: [KSPAddon(KSPAddon.Startup.EditorAny, true)] class GizmoEvents : MonoBehaviour { public static readonly EventData<GizmoRotate> onRotateGizmoSpawned = new EventData<GizmoRotate>("onRotateGizmoSpawned"); public static readonly EventData<GizmoOffset> onOffsetGizmoSpawned = new EventData<GizmoOffset>("onOffsetGizmoSpawned"); class GizmoCreationListener : MonoBehaviour { private void Start() // I use Start instead of Awake because whatever setup the editor does to the gizmo won't be done yet { GizmoRotate rotate = null; GizmoOffset offset = null; if (gameObject.GetComponentCached(ref rotate) != null) { onRotateGizmoSpawned.Fire(rotate); } else if (gameObject.GetComponentCached(ref offset) != null) { onOffsetGizmoSpawned.Fire(offset); } else Debug.LogError("Didn't find a gizmo on this GameObject -- something has broken"); // could destroy this MB now, unless you wanted to use OnDestroy to sent an event } private void OnDestroy() { // could also send an event on despawn here } } private void Awake() { AddListenerToGizmo("RotateGizmo"); AddListenerToGizmo("OffsetGizmo"); Destroy(gameObject); } private static void AddListenerToGizmo(string prefabName) { var prefab = AssetBase.GetPrefab(prefabName); if (prefab == null) { Debug.LogError("Couldn't find gizmo '" + prefabName + "'"); return; } prefab.AddOrGetComponent<GizmoCreationListener>(); #if DEBUG Debug.Log("Added listener to " + prefabName); #endif } } [KSPAddon(KSPAddon.Startup.EditorAny, false)] class TestGizmoEvents : MonoBehaviour { private void Start() { GizmoEvents.onRotateGizmoSpawned.Add(RotateGizmoSpawned); GizmoEvents.onOffsetGizmoSpawned.Add(OffsetGizmoSpawned); } private void OnDestroy() { GizmoEvents.onRotateGizmoSpawned.Remove(RotateGizmoSpawned); GizmoEvents.onOffsetGizmoSpawned.Remove(OffsetGizmoSpawned); } private void RotateGizmoSpawned(GizmoRotate data) { print("Rotate gizmo was spawned"); } private void OffsetGizmoSpawned(GizmoOffset data) { print("Offset gizmo was spawned"); } }
  6. You could add a little script to the gizmo prefabs that fires an event when they're created. You can avoid any FindObjectsOfType calls entirely that way
  7. I've been using it. I updated the download link. There might be something wrong with the symbol loading so if the game crashes on startup, try removing the MDBs. I haven't had time to track down the cause yet
  8. Recompiled for 1.1, let me know if there are any problems
  9. Some references got broken in 1.1, that seems to be the only reason it's not working. Try the recompiled version I just uploaded
  10. This sounds like the usual problem with duplicate experimentIDs being defined. @sp1989 It would be more helpful if you included logs where you tried to run the experiment. Your logs show you going into an editor and then backing out to the main menu before quitting without having done anything involving experiments...
  11. Sure, happy to answer Unity will call those magic name methods if they exist on MonoBehaviour-derived objects, regardless of their access level. The example actually shows a private OnDestroy (the default access modifier for a struct/class member is private, if not specified) Yes. There are some specifics to be aware of (see Unity docs). Some of the magic methods can be coroutines (Start is the only one I've ever used personally) which Unity will automatically start for you if they have the correct return type. I could have sworn that was in the unity documentation at one point but I can't find it now. Moving the code from Start into a new IEnumerator MyMethod() and having Start call StartCoroutine(MyMethod()) would be equivalent. It might not even be necessary in this case, if UIPartActionController.Instance always exists by the time KSPAddons are created. I didn't check I suppose you could call it a bugfix. I wouldn't say it's brain surgery though. In a nutshell, the code takes advantage of the way Instantiate works by inserting a script into the resource slider prefab. Whenever that prefab gets cloned, the custom script gets cloned too.
  12. I'd look to editing the resource item prefab and adding a component that fires GameEvents.onEditorPartEvent, or your own event if you want more information. Here's how it might look in 1.1, 1.0.5 would be similar but with EzGUI instead of uGUI [KSPAddon(KSPAddon.Startup.EditorAny, false)] class TestResourceTweakEvents : MonoBehaviour { public void Start() { GameEvents.onEditorPartEvent.Add(EditorPartEvent); } private void OnDestroy() { GameEvents.onEditorPartEvent.Remove(EditorPartEvent); } private void EditorPartEvent(ConstructionEventType constructionEvent, Part part) { if (constructionEvent != ConstructionEventType.PartTweaked) return; print("Part tweaked: " + part + ", " + EditorLogic.fetch.ship.parts.IndexOf(part)); } } [KSPAddon(KSPAddon.Startup.EditorAny, false)] class InstallResourceTweakListener : MonoBehaviour { class ResourceTweakListener : MonoBehaviour { // the UIPartActionResourceEditor contains everything we might want to know, right down // to which exact PartModule and resource has been edited private UIPartActionResourceEditor _host; private Slider _resourceSlider; private UIButtonToggle _resourceToggle; private void Start() { _host = GetComponentInParent<UIPartActionResourceEditor>(); // use the version of GetComponentsInChildren that returns inactive objects as well, since resources with // no flow mode will have the toggle button disabled _resourceToggle = GetComponentsInChildren<UIButtonToggle>(true).FirstOrDefault(); _resourceSlider = GetComponentInChildren<Slider>(); if (!_host || !_resourceSlider || !_resourceToggle) { Debug.LogError("Couldn't find something ResourceTweakListener needed"); _resourceSlider = null; _resourceToggle = null; Destroy(this); } else { _resourceSlider.onValueChanged.AddListener(ResourceValueChanged); _resourceToggle.onToggle.AddListener(ResourceToggled); } } private void OnDestroy() { if (_resourceSlider != null) _resourceSlider.onValueChanged.RemoveListener(ResourceValueChanged); if (_resourceToggle != null) _resourceToggle.onToggle.RemoveListener(ResourceToggled); } // note: slider value is in range of [0..1] private void ResourceValueChanged(float newValue) { print("Resource " + _host.Resource.resourceName + " set to " + _host.Resource.amount); GameEvents.onEditorPartEvent.Fire(ConstructionEventType.PartTweaked, _host.Part); } private void ResourceToggled() { print("Resource " + _host.Resource.resourceName + " toggled: " + (_resourceToggle.state ? "ON" : "OFF")); GameEvents.onEditorPartEvent.Fire(ConstructionEventType.PartTweaked, _host.Part); } } private IEnumerator Start() { while (UIPartActionController.Instance == null) yield return 0; UIPartActionController.Instance.resourceItemEditorPrefab.gameObject .AddOrGetComponent<ResourceTweakListener>(); Destroy(gameObject); } }
  13. A lot of the interesting GUI stuff can be private (and therefore not directly accessible without breaking rules) so you might have to jump through some hoops to make things work. In 1.0.5, I'd say your best bet would be registering for GameEvents.onInputLocksModified and checking for the quicksave dialog control lock. You might be forced to use FindObjectOfType<PopupDialog> afterwards to get the dialog itself since I don't see any convenient references anywhere. The same tactic should work fine for 1.1. The advantage in 1.1 is that the dialog uses the new GUI system and exposes the dialog prefab (PopupDialogController.PopupDialogBase) which allows you to do almost anything you want to it, although I'd still go down the "replace it with own version" route than trying to tweak it to do what I want
  14. ModuleDeployableSolarPanel.animationName isn't among the AnimationStates the part model's first Animation component has. Make sure that the animationName defined in your part's ConfigNode for the solar panel module actually exists in the Animation
  15. Yes unfortunately, got quite busy (and frankly a little burned out; I probably have at least 10 hours modding for every actual hour of playtime ;)) but I'm working on it now so hopefully a day or two at most
  16. What's really cool is that we now have access to Unity's profiler. It's somewhat fiddly (I've managed to crash KSP a number of times so far) but a lot better than the nothing we were using before
  17. The new PartTools should still support .mu files so I don't think it will be much work to get parts working in the update
  18. I think the release version of the engine might suppress warnings like this KER does appear to access a Unity object from the background thread (assuming the code on git is correct). It's easy to identify if you have the whole stack trace of the error to go by: [LOG 18:37:17.542] KerbalEngineer -> SimManager.RunSimulation() // CompareBaseObjectsInternal can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function. [LOG 18:37:17.544] KerbalEngineer -> at (wrapper managed-to-native) UnityEngine.Object:CompareBaseObjectsInternal (UnityEngine.Object,UnityEngine.Object) at UnityEngine.Object.CompareBaseObjects (UnityEngine.Object lhs, UnityEngine.Object rhs) [0x00000] in <filename unknown>:0 at UnityEngine.Object.op_Equality (UnityEngine.Object x, UnityEngine.Object y) [0x00000] in <filename unknown>:0 at PhysicsGlobals.get_Instance () [0x00000] in <filename unknown>:0 at PhysicsGlobals.get_Stack_PriUsesSurf () [0x00000] in <filename unknown>:0 at KerbalEngineer.VesselSimulator.EngineSim.SetResourceDrains (System.Collections.Generic.List`1 allParts, System.Collections.Generic.List`1 allFuelLines, System.Collections.Generic.HashSet`1 drainingParts) [0x00000] in <filename unknown>:0 at KerbalEngineer.VesselSimulator.Simulation.UpdateResourceDrains () [0x00000] in <filename unknown>:0 at KerbalEngineer.VesselSimulator.Simulation.RunSimulation () [0x00000] in <filename unknown>:0 at KerbalEngineer.VesselSimulator.SimManager.RunSimulation (System.Object simObject) [0x00000] in <filename unknown>:0
  19. 1.0.5 introduced Vessel.totalMass so the mass calculations are redundant anyway. This is probably just some legacy code from before that's gone unnoticed
  20. It doesn't look like your log is complete. Search it for the exception message and it'll show a call stack that 97% of the time makes it pretty clear which mod is causing the issue
  21. I think the logs are telling you the truth here. I checked out the version from SpaceDock and every KFModuleWheel does some really inefficient stuff if the total vehicle's mass has changed by more than a tenth of a unit since the last Update. If the rate of change is fast enough to trigger this very often, your frame rate will get heavily penalized (mainly by the debug statements themselves) by however many KFModuleWheels you have on that vessel
×
×
  • Create New...