How do you use functions in Nexus?

QuenoiQuenoi Member Posts: 11 Inept
I am confused about how to define a JS function in the Nexus web client.

I have a code block that works perfectly when pasted into an "Execute Script" action, however the same block of code pasted into a "Function" reflex fails to take affect when called from the "call function" action in an alias or trigger.

I have tried multiple ways of decorating the JS code in the function declaration but I can not find what I am doing wrong.
Can anyone offer some advice?

Comments

  • SaranSaran Member Posts: 1,696 Mythical
    AFAIK Nexus isn't actually working exactly as documented in all cases.

    It happened for me when I was trying to do what you describe in a package. The resolution is to write all of your code in the onLoad function


    You start it with

    client.packagename = (function () {
        "use strict";

      //all of your code stuff here for example

            const exampleFunction1 = function() {
                //stuffff
            };

            const exampleFunction2 = function(avariable) {
                //thingys
            };


        return Object.freeze({
            exampleFunction1,
            exampleFunction2
        });
    }());

    Then you can call the functions using Execute Script, for example packagename.exampleFunction1. Someone over on Achaea figured it out a while back and there's a tutorial somewhere on their forums that covers parts of it.
    image
    image
  • TambadorTambador Member Posts: 48 Apprentice
    edited August 2016
    How are you calling the function and how are you passing values to it? 

    Have you looked at this http://nexus.ironrealms.com/Functions

    I've written and called lots of functions in Nexus and not had any issues. 
  • SaranSaran Member Posts: 1,696 Mythical
    Oh, also because it's the onLoad function that does it you need to trigger that to get the code going.

    It happens automatically around the same time as commune/city messages pop up when you're logging in, though that does mean that if you've got a bug and you just quit/login, it will still be buggy for those first few seconds, it's generally noticable when it's something called by onGMCP
    image
    image
  • TambadorTambador Member Posts: 48 Apprentice
    If it is onLoad onBlock or onGMCP those all get called automatically but looks like they are saying calling it from an alias or trigger it is failing so that is something different
  • SaranSaran Member Posts: 1,696 Mythical
    edited August 2016
    @Tambador, I know. But calling functions other than onLoad and onGMCP don't really work right apparently when you're using packages. You likely won't notice it if you aren't using packages, because it doesn't affect the "main package" but the behaviour that @Quenoi is describing matches up with the testing I've done on using different packages. (If they aren't then it's a separate and potentially new issue)

    There won't be any issues with the script, you can even directly copy everything from the other package and into the main package to get the script to work fine. But yeah, if the function is not in the main package then it just won't run, someone figured out why it happens and it's been sent through as feedback, not sure if it's meant to be fixed or not but if it's still happening then probably not.
    image
    image
  • TambadorTambador Member Posts: 48 Apprentice
    Yeah I had around 20 packages. I'll look at my notes I know I had to bug the development team to tweak the call to work if they are outside of main. I didn't see the OP say he was calling them from outside the main package. 
  • TambadorTambador Member Posts: 48 Apprentice
    One way around it is to make it a JavaScript function rather than a Nexus client function just declare with the global client.function_name approach 
  • TambadorTambador Member Posts: 48 Apprentice
    I just doubled checked and on MKO and Achaea

    and using this format works just fine

    run_function("combatLoad","", "Offense")

    This is being called out of onLoad in the web client on both those IRE(s) and working properly. When I get a chance to start dropping packages into the system here I'll double check. 
  • QuenoiQuenoi Member Posts: 11 Inept
    edited August 2016
    Here are the exact details of what I did - and what I would like to achieve:

    I have the main package of reflexes with generic utility reflexes and then separate packages of reflexes for influence, hunting, there will eventually be one for pvp also.

    In the influence package I created a background reflex to run continuously on a timer randomly rotating mindsets.
    it works something like this:

    CMD1:
    set variable loopmindset =1
    Repeat while loopmindset = 1 
    {
       set variable randomMinimum=1
       set varable randomMaximum=3
       execute script: JS using Math.random and some manipulation to give a random number within the specified min/max
       set a mindset according to the random value determined by JS
       wait for a few seconds
    }

    CMD2:
    set variable loopmindset=0

    This command pair works well and does exactly what I want.

    Now to the problem!
    Having done this and considered that there are many other users for random numbers, either for debating, RP or possibly to add a wild factor into hunting/combat, I decided that my implementation was not very elegant, and that it would be much better to have a "getRandomNumber(min,max)" function defined that I could call from multiple packages where I wanted to use it.

    As an initial step I tried porting the trivial random number JS from the "execute script" action of CMD1 in the influence package to a Function in the influence package and replacing "execute script" with "call function".

    This is where I have drawn a total blank.
    I select the "call function" sub action and get no errors, but no results either.
    I also noted that the "call function" action does not allow any arguments.
    How do you recommend I fix this?


    Post edited by Quenoi on
  • TambadorTambador Member Posts: 48 Apprentice
    edited August 2016
    So two things

    1. instead of using the call function option just execute a script and call the function either as a JavaScript function or call it like I do with the run_function call that I showed in the last post

    2. While and for loops are a bad thing in JavaScript, why you ask because JavaScript is single threaded and they are blocking functions, which means while either are in effect nothing else is processing. So if you have an array you are looping through or a counter you are stepping through,nothing else is going on inside the client. Affliction notices are not coming in, attack notices, GMCP, etc... this is one of the biggest issues with Nexus vs Mudlet, MUSH etc... They can all have these nice big arrays and for loops and while loops and not impact the client. Now you might think oh hey it only takes a few milliseconds to process them so no biggie, I will tell you it gets to be a major issue in PvP when your for/while loops are firing over and over. 

    @Saran I just checked and I am able to directly call functions on Lusty in packages just like I do on the other IRE games, so perhaps the drop down box run function does not work but you can still do it. 

  • QuenoiQuenoi Member Posts: 11 Inept
    Interesting point on the loops - is it much better to use the nexus action steps to chain together a loop than to just have single-action-do-it-all-in-JS aliases?
  • TambadorTambador Member Posts: 48 Apprentice
    I haven't dug into all the client calls, I use very little of the over arching client, I work underneath the engine, like I do with the run_function, I make lots of my calls directly to underlying systems or using native JavaScript as functions, not Nexus functions. 
  • SaranSaran Member Posts: 1,696 Mythical
    @Tambador the tutorial is here http://forums.achaea.com/discussion/4608/javascript-for-nexus-newbies-part-3-the-matrix-refactored if the run_function function is working, then woo?

    It's actually a bit easier in the long run, so I have no plans to stop using it unless they actually do something to prevent its use. Which would probably shut down the various examples I've found in achaea that use it.
    image
    image
  • TambadorTambador Member Posts: 48 Apprentice
    @Saran thanks for the link :) I prefer to use the code underneath the engine rather than relying on what they do with the client, I've seen the code and they do somethings that are a bit messy. I've written 2 80K+ line full systems for the nexus client on two other IRE(s) so I'm pretty comfy with how to interact with. 
  • WeiwaeWeiwae Member, Gods Posts: 443 Divine
    I wonder if you can use a polyfill tool to give yourself async loops.
  • WeiwaeWeiwae Member, Gods Posts: 443 Divine
    Quenoi said:
    Interesting point on the loops - is it much better to use the nexus action steps to chain together a loop than to just have single-action-do-it-all-in-JS aliases?
    Just got it confirmed. The simple scripting will make sure your loops are not blocking the UI.
  • TambadorTambador Member Posts: 48 Apprentice
    edited August 2016
    It's not blocking the UI that is an issue it is blocking the processing of other events, I asked in the past about adding support for polyfill and similar libs but was told it is not something they planned. 

    eta*

    now I've not done any testing with things like worker queues or node.js to try and address this since the client update but they did not work on the past builds of the client
    Post edited by Tambador on
  • QuenoiQuenoi Member Posts: 11 Inept
    Thanks for the responses, I'll read that tutorial and look at the other suggestions.
    Worst case I can write perfect code, copy past it as needed and pray to never make a change ever......  o:)
  • AngharenAngharen Member Posts: 4 Inept
    It's also possible that you may not need to loop at all. Just set the function trigger to fire on something that happens with about the right duration. If you want it to change often, set it on balance recovery. Assuming you are doing anything, it'll pop frequently. For a longer duration, you could grab an environmental message of some sort, or tie it to another command that you use frequently- for instance use lk to poke the script before sending look.

    (It used to be that you could overload the default commands by tagging them ,false in the javascriptsend_command function  so that you could use "look" to trigger something and not have it drop into an infinite loop. Alas, no more).
  • SaranSaran Member Posts: 1,696 Mythical
    Angharen said:
    It's also possible that you may not need to loop at all. Just set the function trigger to fire on something that happens with about the right duration. If you want it to change often, set it on balance recovery. Assuming you are doing anything, it'll pop frequently. For a longer duration, you could grab an environmental message of some sort, or tie it to another command that you use frequently- for instance use lk to poke the script before sending look.

    (It used to be that you could overload the default commands by tagging them ,false in the javascriptsend_command function  so that you could use "look" to trigger something and not have it drop into an infinite loop. Alas, no more).
    @Quenoi you could probably use onGMCP for this actually, Char.Vitals should be sent every time your prompt shows up.

    If you store the time of the last time you changed mindsets, you can then compare it to the current time and if the difference is more than however long you want you can then fire the changing function.


    Also, if you like I can send you Jairani, it's an influencer that I've written for nexus, not as complete as i'd like and at the moment it requires stratagems and trans influencing. (It'll also set mindsets for you, try to apply oils, and wear scarecrow hats).
    image
    image
  • QuenoiQuenoi Member Posts: 11 Inept
    Saran said:
    Angharen said:
    It's also possible that you may not need to loop at all. Just set the function trigger to fire on something that happens with about the right duration. If you want it to change often, set it on balance recovery. Assuming you are doing anything, it'll pop frequently. For a longer duration, you could grab an environmental message of some sort, or tie it to another command that you use frequently- for instance use lk to poke the script before sending look.

    (It used to be that you could overload the default commands by tagging them ,false in the javascriptsend_command function  so that you could use "look" to trigger something and not have it drop into an infinite loop. Alas, no more).
    @Quenoi you could probably use onGMCP for this actually, Char.Vitals should be sent every time your prompt shows up.

    If you store the time of the last time you changed mindsets, you can then compare it to the current time and if the difference is more than however long you want you can then fire the changing function.


    Also, if you like I can send you Jairani, it's an influencer that I've written for nexus, not as complete as i'd like and at the moment it requires stratagems and trans influencing. (It'll also set mindsets for you, try to apply oils, and wear scarecrow hats).

    this is more than mindsets, I want to use random numbers to generate random reactions for several things.
    examples:
    Random RP action from a set of similar emotes
    Random mindset for debates
    Random direction chooser when exploring new areas

    I would prefer to have a good way of handling random numbers since this seems to be lacking in the client as a general "quality of life" improvement.

    On the other topic - please send me Jairani - I would prefer to build my own system based on my capabilities, but it would be interesting to see another implementation and learn from the approach taken and areas focused on.

    I have never used strategems yet so that could be an interesting side learning opportunity!

  • AngharenAngharen Member Posts: 4 Inept
    Ok, I was wrong. You can still overload commands, just the syntax has changed.

    So if you set an alias for "look" you can attach the following as a script:

         //Overloaded look script
         send_command("look", 1) //the 1 tells the system "not to expand" the command that's sent, which basically means, don't repeat it if it matched the alias.
         display_notice("This is an example of a basic command that's been overloaded to do something else.") 

    One oddity you may notice is that the display notice actually processes before the look command. That was something we noticed a lot of in MKO too, so many of the notices for things actually had a delay attached. Also only "look" is overloaded "l" will send look as normal.

    As for the random emote thing. How about this. Set an alias for "crazy" and attach this as a script:

         //Act cray cray random emoter

         var actcray = Math.floor(Math.random()*5);

             switch(actcray) {
                  case 0:
                     send_command("emote mutters softly to herself about her father, and something about flaying the skin off of...something. The rest is unintelligible.")
                     break;
                 case 1:
                     send_command("emote suddenly puts her hands to her face, shrieking.")
                     break;
                case 2:
                      send_command("emote snaps her head around clearly watching something you can't see.")
                      break;
         default:
            send_command("emote stares at her hand, clenching and unclenching her fist as if it is the most fascinating thing in the world.")
    }

    That will send a random emote anytime you type "crazy". If you want to type it once and have it on a timer you could use setInterval().  If you further want it to sometimes do nothing, just increase the random number and leave some of the cases, and/or the default blank.
  • AngharenAngharen Member Posts: 4 Inept
    And after a bit of mucking about, the answer to your original question about how to use functions.  Be nice if there was just a bit more documentation (like any at all really). It was ok with me using the client. namespace at first, and then it started complaining. I got it to work without, even with the functions in a separate package.  So, two functions- one is randomnumber and the other is getnum, both are in a package called Functions:

    first in onLoad put:
    var randnum //this lets us call this variable across our functions. There is probably a way to do the same thing with namespacing, but it was (redacted) me off.

    randomnumber=
         //Enter the function here
         randnum = Math.floor(Math.random()*6);

    getnum=
         //Enter the function here
         run_function("randomnumber",args,"Functions") //if you put this all in the main package, you can just use run_function("randomnumber")
         display_notice("return is:"+randnum)

    That works just fine and returns a random number when you type getnum.  

    Say you want to pass the function a min and max number, and have it calculate from that. You can grab the arguments and pass them along, though I couldn't get it to send them separately ie (min,max) only passes the second argument, and min is lost. (min+max) passes them along as one thing, which you can separate on arrival. Here are the test functions I was using for this, again both are in a package called Functions:

    argtest
         //Enter the function here
         var min = args[0]
         var max = args[1]
         run_function("otherfunction",(min+max),"Functions")
         display_notice("min: "+min)
         display_notice("max: "+max)

    otherfunction
         //Enter the function here
        display_notice("all args: "+args)
        display_notice("args sorted: "+args[0] +" " +args[1])

    so typing "argtest 4 5" returns:

         min: 4

         max: 5

         all args: 45

         args sorted: 4 5

    I'll leave it up to you to figure out the code to turn the min max numbers into an appropriate range for your random variable. 

  • QuenoiQuenoi Member Posts: 11 Inept
    Big thankyou Angharen , this is exactly what I wanted - some example code for functions that showed how it worked.

    FWIW here is the maths to return a random number between min and max:

        Math.floor( Math.random()*(max-min+1) )+min
  • QuenoiQuenoi Member Posts: 11 Inept
    Your examples work perfectly and can be adapted for everything I wanted to do - tagged this post for the admins with a request they use your examples to improve the help documentation!
  • QuenoiQuenoi Member Posts: 11 Inept
    edited August 2016
    Actually I found a few bugs in your usage of arguments - it is only valid for single digit integers.

    Here are my updated functions for random numbers with an example that works for any number of digits in min and max with a little default error handling thrown in.


    Package:-- main package --
    Function:onLoad

    // Place any code here you'd like to autorun when the system is loaded
    var randnum;


    Package:Functions
    Function:randomnumber

    //Enter the function here
         var min = args[0];
         var max = args[1];

         min = (typeof min === 'undefined') ? 1 : min;
         max = (typeof max === 'undefined') ? 3 : max;
          
         randnum = Math.floor( Math.random()*(max-min+1) )+to_number(min);


    Package:Functions
    Function:rmind

    //Enter the function here
        var min=1;
        var max=3;
        run_function("randomnumber",[min,max],"Functions");

        switch(randnum) {
            case 3:
                send_command("mindset pedantic", 1);
                break;
            case 2:
                send_command("mindset analytical", 1);
                break;
            default:
                send_command("mindset cautious", 1);
        }
     
  • AngharenAngharen Member Posts: 4 Inept
    Yeah, I knew it was only handling single digits, glad you figured out that it's [x,y] instead of (x,y)... that'll be useful.
Sign In or Register to comment.