Jump to content

The official unoffical "help a fellow plugin developer" thread


Recommended Posts

I did notice :P

Anyway, I wanted to read in the full list of hired and applicant kerbals, but that was before I found out the proper way to get them using HighLogic :P

System.IO and System.XML have been allowed again a few versions ago indeed. AFAIK, the only forbidden namespace right now is System.Diagnostics.Process

Edited by stupid_chris
Link to comment
Share on other sites

I'm having some problems with reflection. I'm trying to find all the types that have an attribute that I define. I am using this code:


private void FindAllCrewDataGenerators()
{
var generators = from type in AssemblyLoader.loadedTypes
where Attribute.IsDefined(type, typeof(CrewDataGenerator))
select type;


this.Generators = generators.ToList();
}

where CrewDataGenerator is my custom attribute.

However, with this method defined my assembly won't load. In the output log I find this:

I asked in the IRC channel: can't use linq, need to target .NET 3.5. Thank you! :)

Edited by Ippo
Link to comment
Share on other sites

I'm having some problems with reflection. I'm trying to find all the types that have an attribute that I define. I am using this code:


private void FindAllCrewDataGenerators()
{
var generators = from type in AssemblyLoader.loadedTypes
where Attribute.IsDefined(type, typeof(CrewDataGenerator))
select type;


this.Generators = generators.ToList();
}

where CrewDataGenerator is my custom attribute.

However, with this method defined my assembly won't load. In the output log I find this:

I asked in the IRC channel: can't use linq, need to target .NET 3.5. Thank you! :)

Erm, LINQ was introduced with .Net 3.5.

Link to comment
Share on other sites

Good luck convincing the Assembly Loader of that. ^^

Why can't you use LINQ AND target .Net 3.5 at the same time? I don't have a handy C# project to test with, but I compiled and ran the following C++/CLI code just fine:


using namespace System::Linq;

bool attfilter(System::Type ^t);

bool attfilter(System::Type ^t) {
return System::Attribute::IsDefined(t, KSPAddon::typeid);
}

void Events::Start(void) {

PDebug::Log("Events::Start()");
auto generators = Enumerable::Where(AssemblyLoader::loadedTypes, gcnew System::Func<System::Type ^, bool>(&attfilter));
auto Generators = Enumerable::ToList(generators);
for each (auto g in Generators) {
PDebug::Log(g->FullName);
}

}

Link to comment
Share on other sites

Why can't you use LINQ AND target .Net 3.5 at the same time? I don't have a handy C# project to test with, but I compiled and ran the following C++/CLI code just fine:


using namespace System::Linq;

bool attfilter(System::Type ^t);

bool attfilter(System::Type ^t) {
return System::Attribute::IsDefined(t, KSPAddon::typeid);
}

void Events::Start(void) {

PDebug::Log("Events::Start()");
auto generators = Enumerable::Where(AssemblyLoader::loadedTypes, gcnew System::Func<System::Type ^, bool>(&attfilter));
auto Generators = Enumerable::ToList(generators);
for each (auto g in Generators) {
PDebug::Log(g->FullName);
}

}

You should be targetting .NET 3.5 anyway because KSP is built on that. Anyway, AssemblyLoader will often fail to load plugins using LINQ and that target .NET any higher than 3.5. It's more of a simple fact than anything else.

Link to comment
Share on other sites

You should be targetting .NET 3.5 anyway because KSP is built on that. Anyway, AssemblyLoader will often fail to load plugins using LINQ and that target .NET any higher than 3.5. It's more of a simple fact than anything else.

I'm going to have to investigate this at some point. LINQ in KSP plugins, that is. While I haven't used LINQ itself, I have used the same classes and methods that LINQ is built on with no problems.

EDIT:

Ippo, would you mind posting a zip containing your project which crashes KSP? My quick and dirty test plugin loads fine, so I don't know if it's because the code is too trivial to trigger any problems or if I set project settings out of habit which allow me to use LINQ without any problems.

Edited by Echo 8 ÉRÀ
Link to comment
Share on other sites

I'm afraid I don't have it anymore, I didn't commit it until I got it to load.

Anyway, it doesn't crash KSP: it just won't be loaded. So KSP works just fine, but your plugin is ignored.

Using .NET 4.5 the assembly won't load it at all with the code in my post above;

Using .NET 3.5 it gets loaded: but for some reason, it doesn't work (as in, it finds no types with my custom attributes even when I'm sure there are);

Using .NET 3.5 and refactoring that neat linq to two hideous foreach loops makes it work as expected:


private void FindAllCrewDataGenerators()
{
this.Generators = new List<Type>();
foreach (AssemblyLoader.LoadedAssembly asm in AssemblyLoader.loadedAssemblies)
foreach (Type t in asm.assembly.GetTypes())
if (Attribute.IsDefined(t, typeof(CrewDataGenerator)))
this.Generators.Add(t);
}

Link to comment
Share on other sites

I'm afraid I don't have it anymore, I didn't commit it until I got it to load.

Anyway, it doesn't crash KSP: it just won't be loaded. So KSP works just fine, but your plugin is ignored.

Using .NET 4.5 the assembly won't load it at all with the code in my post above;

Using .NET 3.5 it gets loaded: but for some reason, it doesn't work (as in, it finds no types with my custom attributes even when I'm sure there are);

Using .NET 3.5 and refactoring that neat linq to two hideous foreach loops makes it work as expected:


private void FindAllCrewDataGenerators()
{
this.Generators = new List<Type>();
foreach (AssemblyLoader.LoadedAssembly asm in AssemblyLoader.loadedAssemblies)
foreach (Type t in asm.assembly.GetTypes())
if (Attribute.IsDefined(t, typeof(CrewDataGenerator)))
this.Generators.Add(t);
}

As for your case of using .Net 3.5 but finding no types, it might be a case of AssemblyLoader.loadedTypes not being populated with what you think it is. At least in the flight scene for me, AssemblyLoader.loadedTypes just contains Part, Partmodule, InternalModule and ScenarioModule.

Link to comment
Share on other sites

As for your case of using .Net 3.5 but finding no types, it might be a case of AssemblyLoader.loadedTypes not being populated with what you think it is. At least in the flight scene for me, AssemblyLoader.loadedTypes just contains Part, Partmodule, InternalModule and ScenarioModule.

You are probably right. I didn't give it too much though: "is it working? Ok I'm switching to the foreach" :)

Link to comment
Share on other sites

You are probably right. I didn't give it too much though: "is it working? Ok I'm switching to the foreach" :)

Just for kicks, I tried to write your nested foreach loops (with KSPAddon instead of your attribute) with LINQ instead:


private void FindAllCrewDataGenerators()
{

var generators = from type in (
(from asm in AssemblyLoader.loadedAssemblies
select asm.assembly.GetTypes().AsEnumerable())
.Aggregate((first, second) => first.Concat(second)))
where Attribute.IsDefined(type, typeof(KSPAddon))
select type;

this.Generators = generators.ToList();

}

The nested loops is more efficient and easier to understand, IMO.

Edited by Echo 8 ÉRÀ
Link to comment
Share on other sites

Alright.

To interface with my action groups mod, I'm trying to work with the Action Group Panels that KSP shows down the left side of the screen when you go to Action Groups mode in the editor.

However, I'm hitting a brick wall.

Has anyone done anything like this before?

I'm currently attempting 2 things:

1) Detect when an action is added or removed (mouse click detection maybe?)

2) Show/hide the panel while staying in Action groups mode.

I did manage to find the EditorActionGroups class which has a groupActionList field of type UIScrollList. However, it looks like that is a private property and I can't do anything with it, not even read its contents.

To hide the panels, I found EditorPanls with a float of actionsPanelWidth that I can set (according to Visual Studio), but this seems to do nothing.

Does anyone have any ideas for how to do this, or where I should start investigating?

D.

Link to comment
Share on other sites

So I got a part working in game... everything looks and functions as intended

but in debug window this error comes up every update.

[Exception]: MissingFieldException: Field '.Part.attachJoint' not found.

what is it? can I fix it?

Link to comment
Share on other sites

Quick question: How do I make my craft uncontrollable when a specific resource runs out?

I've made new resources based on the layout of ElectricCharge in ResourcesGeneric, and added them to the stock command pods. The resources show up on right-click and deplete slowly as expected (I added a drain rate to the parts using the new resources). But when they run out, it doesn't kill the ships control. It's based on ElectricCharge, and it uses ModuleGenerator to deplete. Would I need a custom plugin to make the ship uncontrollable? Or am I just missing something simple?

If you would like the code then I'll put it up.

Any help would be appreciated (i feel foolish i dont know this =P)

Link to comment
Share on other sites

...

Look for the InputLockManager class: you can use it to disable the controls. I haven't used it myself though, so you'll have to experiment a bit to find out what you want to lock exactly.

Link to comment
Share on other sites

To interface with my action groups mod, I'm trying to work with the Action Group Panels that KSP shows down the left side of the screen when you go to Action Groups mode in the editor.

...

I'm currently attempting 2 things:

1) Detect when an action is added or removed (mouse click detection maybe?)

2) Show/hide the panel while staying in Action groups mode.

I did manage to find the EditorActionGroups class which has a groupActionList field of type UIScrollList. However, it looks like that is a private property and I can't do anything with it, not even read its contents.

Looks like the EzGUI stuff is all in Assembly-CSharp-firstpass DLL. Here's something I put together that should help with both your goals:

[KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class ActionGroupEditorTest : MonoBehaviour
{

void Start()
{
var panel = EditorPanels.Instance.actions;

// fired when the user selects a different part and selection list changes
// This list appears to be the rightmost list, containing possible actions
// note: the "reset" button is included in this list once an item has been moved
// out of this list
EditorActionGroups.Instance.partActionList.AddValueChangedDelegate(OnPartActionListChange);

// This is the left-most list, listing possible action groups
EditorActionGroups.Instance.actionGroupList.AddValueChangedDelegate(OnActionGroupListChange);

// This is the middle list. Note that the name of the part
// is included as an item.
EditorActionGroups.Instance.groupActionsList.AddValueChangedDelegate(OnGroupActionsListChange);

// Seems to be fired when the panel state changes (opens, closes)
// also triggered by calls in ShowPanel
panel.AddValueChangedDelegate(OnChanged);
}



public void ShowPanel(bool visible = true)
{
if (EditorLogic.fetch.editorScreen == EditorLogic.EditorScreen.Actions)
if (!visible)
{
// Dismiss causes transition; this hides panel immediately
EditorPanels.Instance.panelManager.DismissImmediate();

// note: stays in action group edit mode
}
else
{
//EditorPanels.Instance.ShowActionGroups(); doesn't work
EditorPanels.Instance.panelManager.BringInImmediate(EditorPanels.Instance.actions); // works
}

}


// Invoked when actions panel changes (made visible or invisible);
// called AFTER transition is complete
public void OnChanged(IUIObject obj)
{
Log.Error("OnChanged delegate! {0}", obj.name);

Log.Write("Action editor {0}", EditorLogic.fetch.editorScreen == EditorLogic.EditorScreen.Actions ? "activated" : "deactivated");
}


/// <summary>
/// Invoked when an action is added to the middle list.
/// </summary>
/// <param name="obj"></param>
public void OnGroupActionsListChange(IUIObject obj)
{
Log.Error("OnGroupActionsListChange: {0}", obj.name);

for (int i = 0; i < EditorActionGroups.Instance.groupActionsList.Count; ++i)
{
var item = EditorActionGroups.Instance.groupActionsList.GetItem(i);

Log.Write("groupActionsList item {0} = {1}", i, item);

//EditorActionGroups.Instance.groupActionsList.GetItem(i).gameObject.PrintComponents();

/*
* [LOG 06:27:30.993] DebugTools, ActionGroupItem(Clone) has components:
[LOG 06:27:30.994] DebugTools, ...c: UnityEngine.Transform
[LOG 06:27:30.994] DebugTools, ...c: UIListItemContainer
[LOG 06:27:30.995] DebugTools, ...c: EditorActionPartItem
[LOG 06:27:30.995] DebugTools, --->ActionGroupTitle has components:
[LOG 06:27:30.996] DebugTools, ......c: UnityEngine.Transform
[LOG 06:27:30.997] DebugTools, ......c: UnityEngine.MeshRenderer
[LOG 06:27:30.997] DebugTools, ......c: UnityEngine.MeshFilter
[LOG 06:27:30.998] DebugTools, ......c: SpriteText
[LOG 06:27:30.999] DebugTools, --->Background has components:
[LOG 06:27:30.999] DebugTools, ......c: UnityEngine.Transform
[LOG 06:27:31.000] DebugTools, ......c: UnityEngine.MeshFilter
[LOG 06:27:31.000] DebugTools, ......c: UnityEngine.MeshRenderer
[LOG 06:27:31.001] DebugTools, ......c: UIButton
[LOG 06:27:31.001] DebugTools, ......c: UnityEngine.BoxCollider
* */
var pa = item.gameObject.GetComponent<EditorActionPartItem>();


if (pa != null && pa.evt != null)
Log.Write("guiName: {0}", pa.evt.guiName);
}
}

/// <summary>
/// right-most list
/// </summary>
/// <param name="obj"></param>
public void OnPartActionListChange(IUIObject obj)
{
Log.Error("OnPartActionListChange: {0}", obj.name);

for (int i = 0; i < EditorActionGroups.Instance.partActionList.Count; ++i)
Log.Write("partActionList item {0} = {1}", i, EditorActionGroups.Instance.partActionList.GetItem(i));

}


/// <summary>
/// left-most list
/// </summary>
/// <param name="obj"></param>
public void OnActionGroupListChange(IUIObject obj)
{
Log.Error("OnActionGroupListChange: {0}", obj.name);

for (int i = 0; i < EditorActionGroups.Instance.actionGroupList.Count; ++i)
Log.Write("ActionGroupList item {0} = {1}", i, EditorActionGroups.Instance.actionGroupList.GetItem(i));
}


void OnGUI()
{
if (EditorLogic.fetch.editorScreen == EditorLogic.EditorScreen.Actions)
{
if (GUI.Button(new Rect(400f, 400f, 128f, 20f), "Show")) ShowPanel(true);
if (GUI.Button(new Rect(400f, 424f, 128f, 20f), "Hide")) ShowPanel(false);
}
}
}

I wonder if we can get our own UIButtons running. It might solve the clickthrough problem with Unity's GUI. No time to look into it today though :(

Link to comment
Share on other sites

@EvilReeper: Wow, that is a lot more detailed then I was expecting.

However, I seem to have something wrong in my development environment.

When I first tried what you posted for me, I got a "Type or Namespace IUIObject could not be found". Digging into that it looks like it means I don't have Assembly-CSharp-firstpass.dll referenced correctly.

However, it certainly appears in my references list in Visual Studio so I then went and added it to the top of my .cs page with a Using statement as per the name on the assembly from the reference properties which is Assembly-CSharp.

But that does not work either as it seems to be confused by the - in the middle. If I try to compile with

using Assembly-CSharp;

it underlines the dash and throws a "; expected" error. If I remove that line so I only have the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KSP.IO;
using System.Timers;
using UnityEngine;

I'm back to the "Type or Namespace IUIObject could not be found" error.

Either way, I can't get your test code to actually compile to test it, are you able to point me towards what I have configured wrong?

Thanks a lot for the help here.

D.

Link to comment
Share on other sites

When I first tried what you posted for me, I got a "Type or Namespace IUIObject could not be found". Digging into that it looks like it means I don't have Assembly-CSharp-firstpass.dll referenced correctly.

I didn't do anything special. Right-click project name in the solution explorer, "Add reference", locate the DLL in appropriate folder, made sure there's a checkmark next to it and close the dialog. No using statements necessary

Link to comment
Share on other sites

Odd, I double checked I have all that setup correctly.

You are referring to using the Assembly-CSharp-firstpass.dll that comes with KSP and is in the KSP_Install\KSP_Data\Managed folder correct?

Searching through the Object Browser in Visual Studio, I can see that the BaseAction class is also part of Assembly-CSharp-firstpass and I use that all over the place so I do have the reference setup.

However there is no IUIObject anywhere in my references when I do a search, indeed neither does "IUI" or "IObject" return any references. (Technically IObject returns two, but they are both in the System assembly.)

Do you have a different .dll file somehow?

D.

edit: Not a huge deal in the end as I've found workable methods for both issues.

If I can get what you are showing me working to show/hide the panel, that is a lot nicer method then the hack job I've done for that.

However, I've come up with a way to monitor action groups directly and will be using that regardless as that means if there is another mod installed that modified action group assignments, my AGX mod will recognize that.

Edited by Diazo
Link to comment
Share on other sites

Yes, that's the DLL. Odd. What method signature do you see when you use the object browser in visual studio to look up EZValueChangedDelegate? Or is that also hidden?

Searching through the Object Browser in Visual Studio, I can see that the BaseAction class is also part of Assembly-CSharp-firstpass and I use that all over the place so I do have the reference setup.

Are you sure about that? I see it in Assembly-CSharp

08ce3228a9.png7b654f59ef.png
Link to comment
Share on other sites

Really?

/checks

/wanders off to bang his head into a wall

Okay, I have my Solution Explorer shoved off to the right side of the screen so it cuts the file name off. I assumed my reference displaying "Assembly-CSHar..." was the one we were talking about and was wrong.

Off to investigate, thank you again for all the help.

D.

Link to comment
Share on other sites

Okay, I have the UIpanels working.

One important caveat however is that you have to resummon the panels yourself, switching to another mode (actions -> parts) does not do it for you.

All I cared about was the actions panel, so I ran

EditorPanels.Instance.panelManager.DismissImmediate();

when the player switched to the Actions Editor. However, when the player switched back to the parts or the crew editor, the UIpanel stayed hidden.

I had to hook into the Parts and Crew buttons at the top of the screen and do a check that if the EditorPanel was hidden when the player clicked the Parts or Crew button, run

EditorPanels.Instance.panelManager.BringInImmediate(EditorPanels.Instance.actions);

so that KSP could find the EditorPanel to hide it before it would show the Parts UIPanel.

Oddly enough, EZGui queues these transitions up. In one test I hid the editor UIPanel, clicked the crew/parts buttons a bunch of times and nothing happened. I then switched back to actions and resummoned the editor UIPanel and the UIPanel went nuts doing all the switching of buttons I had clicked while it was hidden.

So, those looking into this also make sure you include code to resummon the UIPanel if you hide it.

D.

Link to comment
Share on other sites

Hi, how can I make my events show up in the action group selection?

You just want some of your events ([KSPEvents]) to also be available through action groups?

Create a [KSPAction("name")] and have it call your KSPEvent method, or make separate code for it.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...