How to use Methods from Another Plugin without using [PluginReference]

FYI: I have googled for several hours prior to posting to try and find a solution but was unable to. Perhaps I could not because i'm not familiar with the correct terminology to search for? I've a programming background however this is my first time dabling with C#.

Problem: I have created several Rust plugins for myself to use all of which obviously use a very similar structure and some of them even use similar methods. To eliminate code repitition I decided it would be a good idea to make a SDK plugin for myself to put my common helper methods into it that I could use from my various plugins. However, when trying to call the functions from my SDK plugin (using Method 2 from below) in my other plugins i get "Unable to load Test. Test.cs(21,24): error CS0246: The type or namespace name `LethalsSDK' could not be found. Are you missing an assembly reference?".

Now from what i've searched and come up with is there are two ways to call another class:

Method 1) 

[PluginReference]
Plugin LethalsSDK;
void SomeMethodInMainPlugin() {
    LethalsSDK.Call<object>("SomeSDKMethod", arg1);
}

- This method also needs the called SDK methods to be private.


Method 2)

// Requires: OtherClassnamespace Oxide.Plugins
namespace Oxide.Plugins
{
  class Test : RustPlugin
  {
    private static LethalsSDK lSDK;
    void SomeMethod(){
      lSDK.SomeSDKMethod(arg1);
    }
  }
}

- This method needs the called SDK methods to be public.

I've tried Method 1 and it works, however, I don't like it. I'd prefer to be able to have my autocomplete feature with my method calls instead of having to manually enter them as an argument to the Call function.

I've read that Method 2 allows for calling the methods directly as I would like to be able to do so but I cannot figure out the proper semantics obviously.  What am I doing wrong and how can I fix it?

Structure of my SDK plugin:

namespace Oxide.Plugins
{
  [Info("Lethals SDK", "LethalBunny", "0.0.1")]
  [Description("Development Tool Required by most of Lethals Mods")]
  public class LethalsSDK : RustPlugin
  {
    public static void SomeSDKMethod(string arg1)
    {
      Puts("Just testing " + arg1);
    }
  }
}

 

AFAIK methods are not meant to be public. There is no actual way to hide method from call inside of the Interface. You can use 
Interface.CallHook("HookName", arg1);​

To call method in ALL the loaded plugins without referencing them.
Or, if you want to call specific plugin without referencing it - use plugin manager

Manager.GetPlugin("PluginName")?.Call<Type>("HookName", someArgs);
You can use // Requires and call any public method in the other plugin without a reference.
yes but that requires me to manually input "HookName"... i'm hoping for a solution where i can type LethalsSDK. and at the <dot> intellisense will list all available methods n fields such that it ends up being
LethalsSDK.SomeSDKMethod();​

Maybe it's my java background wanted to treat C# as Java but I rather dislike having to call a hook/method by manually writing it in as a string



Merged post

Thanks for the reply Wulf, according to what you've suggested if I understand you correctly i'd do this:

// Requires: OtherClassnamespace Oxide.Plugins
namespace Oxide.Plugins
{
  class Test : RustPlugin
  {
    void SomeMethod(){
      SomeSDKMethod(arg1);
    }
  }
}

However, Visual Studio tells me "SomeSDKMethod" cannot be found and recommends implimenting it in my Test plugin.

If those methods and your class are public and the file is included in the same project, then I don’t see why you couldn’t. 
yes, my LethalsSDK is in the same project and it's structure is pasted in the original message. I'm confused as to why it isnt working... could be that i'm using VS 2017 lol... I'll try updating 

Merged post

Also @Wulf, I'm a little unclear how adding a comment
// Requires: OtherClassnamespace Oxide.Plugins​

 before the class allows the class to use methods from other classes within the same project? :/



Merged post

I've upgraded to 2019 and my Test class still does not recognize the methods from my SDK class within the same project even using the "// Requires" comment thing. If I use the reference before the method: LethalsSDK.SomeSDKMethod() then Visual Studio doesn't give me any errors but Oxide compiler still gives me the same error stated in the original message.

Could someone perhaps give me a working example of a ClassA and ClassB where ClassB can call ClassA methods please.

In response to LethalBunny ():
yes, my LethalsSDK is in the same project and it's structure is pasted in the original message. I'm...
// Requires: <Plugin or extension>

Is an uMod/Oxide specific way to handle dependencies. It won't let you load the plugin until his dependency will be loaded (To prevent NREs and call fails), it also will automatically reload all the dependant plugins when dependency is being reloaded and so on. You can explore Oxide.Plugins.Compilation.PreparseScript() to see, what I'm talking about.

Could someone perhaps give me a working example of a ClassA and ClassB where ClassB can call ClassA methods please.
I gave you a pretty clear example.

In response to LethalBunny ():
yes but that requires me to manually input "HookName"... i'm hoping for a solution where i can type...
yes but that requires me to manually input "HookName"... i'm hoping for a solution where i can type LethalsSDK. and at the <dot> intellisense will list all available methods n fields such that it ends up being
- You should never go into Reflection forest then lol =)
However, Visual Studio tells me "SomeSDKMethod" cannot be found and recommends implimenting it in my Test plugin.
- At first - you can't call methods in C# like that. You either have to call them from the instance of the class they belong to, or call static methods typing the class name, or use
using static <namespace>.<class>;​

To make all the static methods of that class available without typing the class name.



Merged post

If you still want example:
using System.Linq;

namespace Oxide.Plugins
{
    [Info("ClassA", "2CHEVSKII", "1.0.0")]
    [Description("Responces on class B calls")]
    class ClassA : RustPlugin
    {
        string ReferenceResponce(object arg)
        {
            if(arg is string)
            {
                var chararr = (arg as string).Reverse();
                var reply = string.Empty;
                foreach(var item in chararr)
                {
                    reply += item;
                }
                return reply;
            }
            else return null;
        }
        void SendSomeThingInChat()
        {
            Server.Broadcast("Something or some thing?");
        }
    }
}
​
// Requires: ClassA
using Oxide.Core;
using Oxide.Core.Plugins;

namespace Oxide.Plugins
{
    [Info("ClassB", "2CHEVSKII", "1.0.0")]
    [Description("Calls class A")]
    class ClassB : RustPlugin
    {
        void Loaded()
        {
            timer.Once(3f, () => CallThroughReference());

            timer.Once(5f, () => CallThroughInterface());

        }
        [PluginReference] Plugin ClassA; //Plugin reference way
        void CallThroughReference()
        {
            var callResponce = ClassA.Call<string>("ReferenceResponce", "String as arg");

            if(callResponce != null && callResponce is string)
            {
                Server.Broadcast(callResponce);
            }
            else Server.Broadcast("No string returned");
        }
        void CallThroughInterface()//Interface way (call all the plugins with that method)
        {
            Interface.CallHook("SendSomeThingInChat");
        }
    }
}
In response to 2CHEVSKII ():
yes but that requires me to manually input "HookName"... i'm hoping for a solution where i can type...
You can call methods directly as I mentioned previously, doing a hook call is not required for the method I mentioned when doing it as such. In your examples, the // Requires is only being partially used for the load order, nothing else; it can be used for directly referencing as well.

Thanks alot for the example @2CHEVSKII!! That unfortunately still uses the Call<T> method which requires manually inputting the method names. I have however, after much experimentation achieved what I was hoping to accomplish. 

I discovered I was making several mistakes with the symantics as I took examples too literally. For anyone else in the future looking to do what I sought after, here is how I did it with some key notes:

LethalsSDK.cs:

using System;     // Or whatever other imports you need

namespace Oxide.Plugins
{
    [Info("Lethals SDK", "Author", "1.0.0")]
    [Description("A helper class for my other plugins")]
    class LethalsSDK : Rust Plugin
    {
        [HookMethod("SameMethodA")]
        public void SameMethodA(string msg)
        {
            Puts(msg);
        }

        [HookMethod("SameMethodB")]
        public bool SameMethodB()
        {
            return true;
        }
    }
}



TestPlugin.cs
:

// Requires: LethalsSDK
// ^ THIS NEEDS TO BE AT THE VERY TOP and was the part I was messing up

using System;     // Or whatever other imports you need

namespace Oxide.Plugins
{
    [Info("A Test Plugin", "Author", "1.0.0")]
    [Description("Testing to see if I can use another Class")]
    class TestPlugin : Rust Plugin
    {
        [PluginReference] LethalsSDK LethalsSDK
        // ^ This line needs to be [PluginReference] <nameOfOtherClass> <nameOfOtherClass>

        void MyOwnMethod()
        {
            LethalsSDK.SomeMethodA("Hey I can use Intellisense or some other Autocomplete now!");
            Puts("Isn't this cool? " + LethalsSDK.SomeMethodB());
        }
    }
}



This is how I got it working, may not be the absolute correct way to do it but it's accomplished what I was after.

Key Notes:

- The "other class" needs to have it's functions have the
  [HookMethod] (designation?) on each of them to make the
  methods act as hooks.


- In your "main class", where <OCN> is the name of your "other class":

    - // Requires: <OCN>                - MUST be at the very top

    - [PluginReference] <OCN> <OCN>     - must be exactly as shown in the
                                          example, no custom variable name
                                        - Also only needs to be placed ONCE
                                          within your plugin class.



In response to LethalBunny ():
Thanks alot for the example @2CHEVSKII!! That unfortunately still uses the Call<T> method whic...
The PluginReference attribute is not necessary when using // Requires that I am aware of, but you can set a plugin reference to anything if you are doing the manual .Call methods.

[PluginReference] Plugin LethalSDK is also acceptable for your example, though shouldn’t be necessary as that is a separate handling than what // Requires is for.
yeah i tried [PluginReference] Plugin LethalSDK but then Visual Studio failed to recognize it's methods it kept underlining the methods as "unimplemented" errors. :/

Merged post

and to completely not use [PluginReference] resulted in "an object reference is required" errors on LethalsSDK. :/
Ah, never really tried with that in Visual Studio.
Anyways, im supler glad it's working now and thank you both very very much for helping me figure it out!

Lastly, having a Java background I'm use to having JavaDocs to reference to and find calls n such in the API. Is there any sort of similar structure or library for the Oxide C# project or is decompiling the dll's on my own and scrounging through them my best option?
In response to LethalBunny ():
Anyways, im supler glad it's working now and thank you both very very much for helping me figure it...
The documentation that we provide is what we offer mostly. You can find some additional info on our GitHub, but other than that the remainder is from each game.