1. Hello and nice to meet you all, since this is my first post, and, surprisingly, i need your help :)

    FULL DISCLOSURE(scroll down if too long): i've really tried to find a solution for all this myself. that includes google, oxide docs, forums, and other plugin sources. i hope this thread will help other beginners . ALL PROBLEMS SUMMARIZED IN THE LAST QUOTED CODE. also i have some experience in programming and in debugging, mostly small tools for solving unusual problems i've encountered over the years of using personal computers. that said, i tried really hard to stay way from things like Java and C#, for many reasons, but mainly i found them too heavy to work with for my needs and disliked the lack of control i had over the execution of my apps. this is the first time i regretted it, since if i understand correctly many things can only be scripted in C#. we have a private server with a bunch of friends, and we were amazed by the possibilities oxide gives. we came up with few ideas that would (we believe) make for a fun play while completely twisting core rust concepts. i know it's doable because i've seen needed pieces of functionality in other plugins. i'm also confident i can script most of them, in fact i already pseudocoded all the basics. the problem is, i'm stuck at the very beginning because i can get (and wrap my mind) around some C# behaviours. my friends already came up with few amazing structures that are just begging to be scripted around so they can come to live, yet here i am banging my head:/
    now, here lies my problem:
    as far as i understand for the C# plugin to interact with the user input part of Unity engine, in its RustPlugin derived class one has to nest a MonoBehaviour, like this (and those classes cannot be just "merged" into one, either because it's impossible or simply because it doesn't make sense):

    Code:
    // Reference: Oxide.Ext.Rustusing System;
    using System.Reflection;
    using System.Collections.Generic;
    using UnityEngine; 
    using Rust;using Oxide.Core;
    namespace Oxide.Plugins
    {
        [Info("Testcase", "seant", 0.01)]
        class Testcase : RustPlugin
        {
            private static FieldInfo serverinput;
            void Unload()
            {  
                var objects = GameObject.FindObjectsOfType(typeof(Testcase));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }
           
            class engine_interact : MonoBehaviour
            {
                private InputState input;
                private BasePlayer player;
               
                void Awake()
                {
                    input = serverinput.GetValue(GetComponent<BasePlayer>()) as InputState;
                    player = GetComponent<BasePlayer>();
                    enabled = true;
                }
               
                void Update()
                {            }        }
           
            [ChatCommand("testcase")]
            void test_msg1(BasePlayer player, string command, string[] args)
            {
                PrintToChat(player, "working.");
            }
           
           
        }
    }

    first time i've run into this problem was when i was customizing the wonderful Telekinesis plugin (big kudos Bombardir!). for debug purposes i wanted to output some info from inside the MonoBehaviour class to chat - i found out that in C# i have to use PrintToChat() instead of Rust.SendToChat and others (which is strange in itself, what is the "Using Rust;" clause for then?). but when i tried to use it i got the dreaded error:

    after some headbanging i've decided to initialize new RustPugin() object, and use it to send output to the chat:

    Code:
    // Reference: Oxide.Ext.Rustusing System;
    using System.Reflection;
    using System.Collections.Generic;
    using UnityEngine; 
    using Rust;using Oxide.Core;
    namespace Oxide.Plugins
    {
        [Info("Testcase", "seant", 0.02)]
        class Testcase : RustPlugin
        {
            private static FieldInfo serverinput;
            void Unload()
            {  
                var objects = GameObject.FindObjectsOfType(typeof(Testcase));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }
           
            class engine_interact : MonoBehaviour
            {
                private InputState input;
                private BasePlayer player;
               
                void Awake()
                {
                    input = serverinput.GetValue(GetComponent<BasePlayer>()) as InputState;
                    player = GetComponent<BasePlayer>();
                    enabled = true;
                    Testcase chat_gateway = new Testcase(); // how can i print to chat without doing this? while plugin grows over time wouldn't it mean performance hit?
                   
                    //PrintToChat(player, "working 2.");
                    //this gives CS0038: Cannot access a nonstatic member of outer type `Oxide.Plugins.Testcase' via nested type `Oxide.Plugins.Testcase.engine_interact'
                   
                    //Testcase.PrintToChat(player, "working 2.");
                    //this will make it fail to compile with [Warning] C:\(...)\Testcase.cs(35,14): error CS0120: An object reference is required to access non-static member `Oxide.Plugins.RustPlugin.PrintToChat(BasePlayer, string, params object[])'
                   
                    chat_gateway.PrintToChat(player, "working2.");
                    //this works but it doesn't seem like a "proper" solution, PrintToChat() should be accessible somehow already i imagine, but how?
                }
               
                void Update()
                {            }        }
           
            [ChatCommand("testcase")]
            void test_msg1(BasePlayer player, string command, string[] args)
            {
                PrintToChat(player, "working.");
            }
           
           
        }
    }
    this worked for stricte debugging purposes, and i left it like that. after adding some more codes to some other plugins, i encountered another instance of the same problem: from the nested class i couldn't access its "parent" class variable. no matter if i made the variable private or public, i would get the same errors about the method not being able to call/access the desired method/variable. when i made it static i was informed i need a new instance of an object to access it. again couple of hours of my life lost, again gave up with a dirty hack - since making new RustPlugin() wouldn't work here (the variable would return default value that it initialized with), i just moved the variable itself and the dependant parts of code inside the nested : MonoBehaviour class. that was it for a while.

    now that our project got more ambitious, i decided we can't rely on other plugin authors' vision and implementations to manage a heavily customized server like ours. i started to code an all-in-one toolkit, as i mentioned before i have all the parts needed, parts written in code, parts in my head, but yet again i encountered the same problem at the very beginning, and decided i can't just hack around this time, i need to understand WTF is going on. basically, i have a piece of code like this:

    Code:
    // Reference: Oxide.Ext.Rustusing System;
    using System.Reflection;
    using System.Collections.Generic;
    using UnityEngine; 
    using Rust;using Oxide.Core;
    namespace Oxide.Plugins
    {
        [Info("Testcase", "seant", 0.02)]
        class Testcase : RustPlugin
        {
            private static FieldInfo serverinput;
           
            private bool logic_toggle = false;
           
            void Unload()
            {  
                var objects = GameObject.FindObjectsOfType(typeof(Testcase));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }
           
            class engine_interact : MonoBehaviour
            {
                private InputState input;
                private BasePlayer player;
               
                void Awake()
                {
                    input = serverinput.GetValue(GetComponent<BasePlayer>()) as InputState;
                    player = GetComponent<BasePlayer>();
                    enabled = true;            }
               
                void Update()
                {
                //    int x;
                //    if(logic_toggle) x = 1;
                //    else x = 0;
                //above code gives error CS0038: Cannot access a nonstatic member of outer type...
                //i noticed it will morke if i make it "private static bool logic_toggle", but i'm not sure if it's the proper way to do it? seen couple of plugins where every line starts with "public static" :) that doesn't seem like a way to go.
                }        }
           
            [ChatCommand("testtoggle")]
            void toggle(BasePlayer player, string command, string[] args)
            {
                logic_toggle = !logic_toggle;
            }
           
            [ChatCommand("testcase")]
            void test_msg1(BasePlayer player, string command, string[] args)
            {
                PrintToChat(player, "working.");
            }
           
           
        }
    }
     
  2. SPLITTED IN HALF - LONG POST :)

    to be honest i was surprised by myself being surprised that this doesn't work, given all the similar problems i had before. we can only handle chat commands in 'main' RustPlugin class, and we can only handle input in the nested MonoBehaviour class. how in that case can i set a logical state (and other types of var, methods access if possible), that is accessible and changeable by both chat commands and methods handling user input? for example, for the command "/test" two return two different results depending on if i look straight up or straight down that very moment?

    finally, when printing to chat i have to spawn new RustPlugin() when in nested MonoBehaviour class. but, to my thinking, there should be an RustPlugin class type object already somewhere in the memory, created by the oxide when loading the plugin, correct? if so, is there a way to get a "pointer", or some kind of reference to it AND access it's public variables and methods? scarce oxide documentation (pretty understable at this stage of the project, i hope to contribute soon) doesn't mention anything like that, i tried to read oxide source but i'm getting lost in there, what's worse grepping for keywords bring poor to none results surprisingly, so i fear some of the interesting stuff might sit in the patched assembly dll, if any of the devs is reading and would be so kind to clarify or at least point to the relevant point in the source code i would be very grateful.

    TO SUMMARIZE:

    Code:
    // Reference: Oxide.Ext.Rustusing System;
    using System.Reflection;
    using System.Collections.Generic;
    using Rust; // what does this do if we can't use stuff like Rust.BrodacastChat() and Rust.SendToChat()  etc with C#?
    using UnityEngine; using Oxide.Core;
    namespace Oxide.Plugins
    {
        [Info("Testcase", "seant", 0.02)]
        class Testcase : RustPlugin
        {
            private static FieldInfo serverinput;
           
            private bool logic_toggle; // i want this to be accessible from nested classes!
           
            void Unload()
            {  
                var objects = GameObject.FindObjectsOfType(typeof(Testcase));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }
           
            class engine_interact : MonoBehaviour
            {
                private InputState input;
                private BasePlayer player;
               
                void Awake()
                {
                    input = serverinput.GetValue(GetComponent<BasePlayer>()) as InputState;
                    player = GetComponent<BasePlayer>();
                    enabled = true;
                    Testcase chat_gateway = new Testcase(); // how can i print to chat without doing this? while plugin grows over time wouldn't it mean performance hit?
                   
                    //PrintToChat(player, "working 2.");
                    //this gives CS0038: Cannot access a nonstatic member of outer type `Oxide.Plugins.Testcase' via nested type `Oxide.Plugins.Testcase.engine_interact'
                   
                    //Testcase.PrintToChat(player, "working 2.");
                    //this will make it fail to compile with [Warning] C:\(...)\Testcase.cs(35,14): error CS0120: An object reference is required to access non-static member `Oxide.Plugins.RustPlugin.PrintToChat(BasePlayer, string, params object[])'
                   
                    chat_gateway.PrintToChat(player, "working2.");
                    //this works but it doesn't seem like a "proper" solution, PrintToChat() should be accessible somehow already i imagine, but how?
                }
               
                void Update()
                {
                //    int x;
                //    if(logic_toggle) x = 1;
                //    else x = 0;
                //above code gives error CS0038: Cannot access a nonstatic member of outer type...
                //i noticed it will morke if i make it "private static bool logic_toggle", but i'm not sure if it's the proper way to do it? seen couple of plugins where every line starts with "public static" :) that doesn't seem like a way to go.
                }        }
           
            [ChatCommand("testtoggle")]
            void toggle(BasePlayer player, string command, string[] args)
            {
                logic_toggle = !logic_toggle;
            }
           
            [ChatCommand("testcase")]
            void test_msg1(BasePlayer player, string command, string[] args)
            {
                PrintToChat(player, "working.");
            }
           
           
        }
    }
    obviously i am missing something very obvious here. my best bet is i'm trying to do the whole thing upside down, coming from the more traditional languages background (and being a crap coder, too ;p). i realize i will have to cry myself to sleep after realizing how dumb i have been, after one of you good souls (hopefully) explains the whole business to me. till then it's flying around, hunting bears with a rocket launcher for me...
     
  3. This should work and get you started down the correct path as I don't have the time tonight to explain everything you have asked.
    Code:
    using System.Reflection;
    using UnityEngine;namespace Oxide.Plugins
    {
        [Info("Testcase", "seant", 0.02)]
        class Testcase : RustPlugin
        {
            private static FieldInfo serverinput;        void Init()
            {
                serverinput = typeof(BasePlayer).GetField("serverInput", (BindingFlags.Instance | BindingFlags.NonPublic));
            }        void Unload()
            {
                var objects = GameObject.FindObjectsOfType(typeof(engine_interact));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }        class engine_interact : MonoBehaviour
            {
                private InputState input;
                private BasePlayer player;            void Awake()
                {
                    player = GetComponent<BasePlayer>();
                    input = serverinput.GetValue(player) as InputState;
                    enabled = true;
                }            void Update()
                {
                    if (enabled && input.IsDown(BUTTON.JUMP))
                    {
                        player.SendConsoleCommand("chat.add", 0, "Hello I Clicked Jump", 1f);
                    }
                }        }        [ChatCommand("testtoggle")]
            void toggle(BasePlayer player, string command, string[] args)
            {
                engine_interact ei = player.GetComponent<engine_interact>();
                if (!ei)
                {
                    ei = player.gameObject.AddComponent<engine_interact>();
                    ei.enabled = true;
                    SendReply(player, "Engine Interact Enabled");
                } else
                {
                    ei.enabled = false;
                    SendReply(player, "Engine Interact Disabled");            }
            }
        }
    }
     
  4. ...
    ...
    ... thank you.

    EDIT:
    Code:
            void Unload()
            {
                var objects = GameObject.FindObjectsOfType(typeof(engine_interact));
                if (objects != null)
                    foreach (var gameObj in objects)
                        GameObject.Destroy(gameObj);
            }
    did you mean var objects = GameObject.FindObjectsOfType(typeof(Testcase)); ? or do we destroy engine_interact objects that we might've created in our code because oxide has no control (or knowledge?) of them, but it will handle destroying Testace() objects just fine?

    EDIT2:
    also i don't see "enabled" variable declaration anywhere, did you missed it in engine_interact class or is it some special variable that is set elsewhere?
     
    Last edited by a moderator: Apr 30, 2015
  5. The enabled = true was in the Awake() method which is executed as soon as the MonoBehaviour is attached to a gameobject, because of that it is redundant to set an enabled variable to true. If it would be the case that it didn't need to be enabled from the start you could set enabled = false there.

    The unload code is required to remove all the gameobjects that you created with your plugin when it's unloaded/reloaded as that is something you need to do yourself.

    Another thing, I read you were adding the MonoBehaviour class to capture player input, are you planning to capture the input for a few specific players after running a command or is it supposed to check all players at any given time? If it would be the latter you could have a look at the OnPlayerInput(BasePlayer player, InputState input) hook.
     
  6. not in this plugin. i would like to monitor all players movement in specific conditions for some other plugin, but i'm worried about the performance.
     
  7. well you should use FixedUpdate() instead of Update()
    you'll gain 2-3 times performance ^^

    and yes monitor all player movements, if you have a lot of players it might be a big problem at some point.
     
  8. interesting. unity docs recommend using it when working with rigid bodies. aren't all <BaseNetworkable>s in rust rigid bodies? does that mean i should use it for every plugin that handles userinput or manipulates objects positions in game? are there any disadvantages for using FixedUpdate() by default over Update()? (apart from the obvious one of being executed only on fixed intervals). finally, wouldn't having many plugins using FixedUpdate() result in a performance hit, since they all execute their code at the same time (and it'd be the time when the server has already a lot of work i imagine, trying to push out the frame on time?)

    edit: typos