The Webclient

KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭

I've been working with the webclient more often now doing a basic razing package and trying to help some less-skilled coders get some basic attacks going on it, and I've come to the conclusion that the webclient is the right tool for the wrong job. You've created a decent webclient for people who can program while ignoring the fact that nobody who can program actually uses the webclient. The issue here is that people who can script in the webclient are more than capable of scripting in Mudlet, and so they're all doing that because Mudlet has a better UI and better features. The only people left in the webclient are the people who can't really program well, and those people are in over their head with it.

Honestly, trying to walk people without experience through this client is a goddamn nightmare, because they don't have enough of a programming foundation to understand things like "the target variable is out of the scope of this script, so you need to call client.get_variable() to bring the value of the variable into the script and then you have to assign it to a local variable, and you have to do this in every script of every alias", and javascript as a whole is a very intimidating looking language. 

I mean, here's a simple script to check and see if the person who shielded is my target and set a variable if they are, while accounting for the fact that people like to target partials names:

if (args[1].toLowerCase().indexof( String(client.get_variable('target') ).toLowerCase()) == 0 ){
   client.set_variable('targetShielded','1')
   }

That's incredibly ugly, as well as being a confusing mess to people who don't understand programming. I'm testing if the trigger subject is my target and the only equals is against zero? What's this "index of" crap?

I really think that the best possible change you could make to the webclient would be working less on features and more on ease of use.

  • Have a preset target variable with built-in target checking functions, so that when I want to see if somebody is my target I can just do "if (isTarget(args[1])) {..." to account for partial matching. 
  • Built in GCMP support in a way that makes it simple to check things like isWielded(longsword) 
  • Documentation that doesn't completely suck. I actually gave up on trying to call functions via the "call function" trigger action because I couldn't figure out how it was passing parameters and just resorted to calling run_function() from the "execute script". Specifically, a function that works when called via run_function() would not work when called via 'call function' and I could not figure out why.
  • Get Garryn or somebody to do a youtube video or a detailed tutorial explaining how you make a rebounding tracker, maybe some other basic scripts. 
As it is, the webclient falls into an awkward sort of gap. It's too basic for the people who can program well while being too complicated for the people who cannot.

"On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

OystirGarrynClaudius
«1

Comments

  • GarrynGarryn Member, Administrator Posts: 527 admin
    Excellent feedback, exactly the kind of real-world usage report we need, thank you.

    Ideally we'd like to have basic scripting features available without having to use javascript at all (a few things, like command delaying, are already in), more should hopefully follow. We do also need better API/documentation, yes.
  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    One thing I find is that a lot of the stuff in the webclient(client.get/set_variable, javascript string methods) is involved enough that I don't really feel like going into detail about it with non-coders. Basics like "this is the idea behind an if/then/else statement" are fine, but I really don't want to teach Intro to Programming here as a hobby, and I especially don't want to teach it in javascript. 

    The problem with that is that talking to some of the people I've helped gives me the impression that the scripts I give them are basically just magic words to them; they know that saying them makes the system work but they don't understand why, and that means that the odds of them successfully branching out from that script seems pretty low. They'd really be better off with a PlaySkool "My First MudClient" than the current implementation, imo.

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    edited October 2015
    THERE IS A TLDR AT THE BOTTOM

    Khizan said:
    • Documentation that doesn't completely suck. I actually gave up on trying to call functions via the "call function" trigger action because I couldn't figure out how it was passing parameters and just resorted to calling run_function() from the "execute script". Specifically, a function that works when called via run_function() would not work when called via 'call function' and I could not figure out why.

    It's actually better to do it with execute script. Allows you to call more than one function, much more flexible.


    Honestly, trying to walk people without experience through this client is a goddamn nightmare, because they don't have enough of a programming foundation to understand things like "the target variable is out of the scope of this script, so you need to call client.get_variable() to bring the value of the variable into the script and then you have to assign it to a local variable, and you have to do this in every script of every alias"

    One of the ways I bypassed this was to use localStorage and create a global variable that is incompatible with the internal system; this made scripting a lot easier for me - the idea would be to distribute it to whomever, and afaik, Garryn said retrieving the 'internal' variable was slower than localStorage:

    // initialized
    e.target = typeof(e.target) == 'undefined' ? "Iniar" : e.target;

    // my Moradeim bypass alias looked like this:
    var str = "rt bypassing to target" + e.sp + "focus moradeim target bypass"

    if (args[1]) {
        str = replaceAll(str, "target", args[1]);
    } else {
        str = replaceAll(str, "target", e.target);
    };
    run_function("ac.off");
    client.send_direct(str);


    • Built in GCMP support in a way that makes it simple to check things like isWielded(longsword) 
    This can be done and packaged up for other players:

    function() onGMCP
    // Place any code here you'd like to run when a GMCP message is received
    // - The GMCP message is received as "args.gmcp_method" and "args.gmcp_args"
    gm = typeof(gm) == 'undefined' ? {} : gm;
    mclass = typeof(mclass) == 'undefined' ? "Deathknight" : mclass;

    // handle room players
    if (args.gmcp_method == "Room.Players") {
        for (var k in args.gmcp_args) {
            var v = args.gmcp_args[k]
            if (!player(v.name)){
                dbp.make(v.name);
            };
        };
    };

    // handle wielding
    if (args.gmcp_method == "Char.Vitals") {
        e.left = args.gmcp_args.leftwield;
        e.right = args.gmcp_args.rightwield;
    };

    Something for wielding would be easy to create:

    function isWielded(item) {
    if (e.left == item || e.right == item) { 
    return true;
    } else { 
    return false;
     };
    };

    (which brings us to a real coding problem, exposing a variable for viewing in the client is extremely difficult! - here is my solution from earlier:)

    Alias
    ^-js (.+)$
    Code
    tprint(args[1]);

    Underlying function
    function tprint(arg) {
        if (arg.match("\\.")) {
            tablen = arg.split("\.");
            for (i = 0; i < tablen.length; i++) {
                client.print(tablen[i]);
            };
            for (i = 0; i < tablen.length; i++) {
                x = x[tablen[i]]
                if (window[x]) {
                    client.print("Tick");
                } else {
                    client.print("Error")
                };
            }
        } else {
            if (window[args[1]]) {
                client.print("Global Variable: ");
                client.print(window[args[1]]);
            } else if (e[args[1]]) {
                client.print("(e) Variable: ");
                client.print(e[args[1]]);
            } else if (client.get_variable(args[1])) {
                client.print("Client Variable: ");
                client.print(client.get_variable(args[1])); 
            } else {
                client.print("Cannot locate variable " + args[1]);
            };
        };
    };

    // which doesn't actually work well, but I couldn't be arsed finishing it given the inability to make custom displays for variables at the time...

    This is what my custom DK bleed code looked like:

    deathknight.bleed = function(name) {
        var str = "";
        name = toTitleCase(name);
        if (!player(name)){ dbp.make(name); };
        var toxins = deathknight.choose_toxin(name);
        var toxin = toxins[0];
        var toxin2 = toxins[1];
        
        if (!player(name)){
            dbp.make(name);
        } else {
            var sword = "59299";
            if (!e.has(name,"haemophilia")) {
                sword = "151790";
            };
            if (e.has(name,"rebounding") && e.has(name,"shield")) {
                str = str + "quickdraw sabre shield" + e.sp;
                str = str + "wm raze raze " + name + e.sp;
            } else if (e.has(name,"rebounding") || e.has(name,"shield")) {
                str = str + "quickdraw sabre shield" + e.sp;
                str = str + "rsl " + name;
                str = str + " " + toxin + e.sp;
            } else {
                str = str + "quickdraw " + sword + " shield" + e.sp;
                var a = "lacerate";
                var b = "lacerate";
                if (!e.has(name,"haemophilia")) { a = "slash"; b = "shred"; };
                str = str + "wm " + a + " " + b + " " + name + " " + toxin + " " + toxin2 + e.sp;
                if (name != e.me) { str = str + "engage " + name + e.sp; };
            };
        };
        
        str = "ifeqbal " + str
        str = str + "trueassess " + name + e.sp;
        msend(str);
    };
    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    edited October 2015
    I guess the take away is that, if you design your stuff well, people can essentially template off what you write and then pick up things along the way..

    Instead of going:

    client.get_variable(arg[1]) bla bla bla bla bla 

    If you write it correctly, they can do:

    if (has(target,"shield")) { 
    // do something
    } else {
    // do something else
    };

    Just wrapping up your code into a neat function would be useful:

    if (args[1].toLowerCase().indexof( String(client.get_variable('target') ).toLowerCase()) == 0 ){
       client.set_variable('targetShielded','1')
       }

    function isLegit( ji ) {
    if (ji.toLowerCase().indexof(String(client.get_variable('target')).toLowerCase()) == 0) {
    return true;
    } else {
    return false;
    };
    };

    // into this
    if isLegit(args[1]) {
    client.set_variable('targetShielded','1');
    }


    TLDR:

    packaging up a basic system would be immensely helpful so people can slowly dissect your pseudo-code, without exposing them (too much) to the nightmare that is Javascript
    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    function tprint(arg) {
        if (arg.match("\\.")) {
            var tablen = {};
            tablen = arg.split("\.");
            for (i = 0; i < tablen.length; i++) {
               // client.print(tablen[i]);
            };
            var x = null;
            var str = "";
            for (i = 0; i < tablen.length; i++) {
                if (i == 0) {
                   x = window;
                };
                x = x[tablen[i]];
                if (i < tablen.length - 1) {
                    str = str + ".";
                } else {
                    client.print(str + " " + x);
                    // client.print(x);
                };
            }
        } else {
            if (window[args[1]]) {
                client.print("Global Variable: ");
                client.print(window[args[1]]);
            } else if (e && e[args[1]]) {
                client.print("(e) Variable: ");
                client.print(e[args[1]]);
            } else if (client.get_variable(args[1])) {
                client.print("Client Variable: ");
                client.print(client.get_variable(args[1])); 
            } else {
                client.print("Cannot locate variable " + args[1]);
            };
        };
    };

    Probably will have to write a recursive function to display nested values in an array :/
    wit beyond measure is a Sidhe's greatest treasure
  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    That's all good and well, but I don't want to write a client inside of their client, especially not in javascript. That's the kind of stuff that should be handled by Garryn and then packaged up where they can't easily get their grubby fingers on it.

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

    Chronus
  • JulesJules Member Posts: 1,080 ✭✭✭
    I thought I had learned a little bit about coding until I looked at this thread :( </3
  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    edited November 2015
    Here's a weird problem I am having with the webclient today. I want to run the following script in an alias called "testal".

    var i;
    i = 0;
    client.send_direct("Think  " + i);
    i = i + 1;
    client.send_direct("Think  " + i);
    i++;
    client.send_direct("Think  " + i);
    i = i + 1;
    client.send_direct("Think  " + i);
    i++;
    client.send_direct("Think  " + i);

    I'd expect to get  output along the lines of:

    You think: 0.
    You think: 1.
    You think: 2.
    You think: 3.
    You think: 4.

    But this is what I actually get:

    You think: 0.
    You think: 11.
    You think: 12.
    You think: 12.
    You think: 12.

    I have no idea why, because THIS script:

    var x;
    x = 0;
    client.send_direct("Think  " + x);
    x = x + 1;
    client.send_direct("Think  " + x);
    x++;
    client.send_direct("Think  " + x);
    x = x + 1;
    client.send_direct("Think  " + x);
    x++;
    client.send_direct("Think  " + x);

    produces this output:

    You think: 0.
    You think: 1.
    You think: 2.
    You think: 3.
    You think: 4.

    with nothing but a changed variable name.

    I ran into this problem attempting to iterate through an array because I was trying to use 'i' as the index in a for loop and it's hard to do "for (i = 0;i < 5;i++)" when it thinks that "i=0;i++" results in "i = 11"

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

  • GarrynGarryn Member, Administrator Posts: 527 admin
    ... huh. I'll investigate.
  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    It gives me the same output for both the i script and the x script every time I run them, as a note.

    I run the first script a dozen times, it'll throw out the 0-11-12-12-12 sequence every time. Second script will give me the 0-1-2-3-4 sequence every time.

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

  • ClaudiusClaudius Member Posts: 90 ✭✭✭
    edited November 2015
    Use send, not send direct. Send direct does not fully expand script (meaning it sends without executing most operations including variable re-assigning) it's just a bad function in the code. Does it concatenate before sending? Yes. Does it perform operations before sending? Rarely. Which is worse than never. It is a problem in what is being returned at the end of the function.

    This is an error in the docs that I believe they are fixing. Send_direct is a bad method for most things until the source is corrected.

    Using send over send direct is going to eliminate A LOT OF confusion for you.
  • GarrynGarryn Member, Administrator Posts: 527 admin
    Do not use send(), that sends the contents of the input line, which usually isn't what you want.
  • ClaudiusClaudius Member Posts: 90 ✭✭✭
    Garryn said:

    Do not use send(), that sends the contents of the input line, which usually isn't what you want.

    After a great deal of testing, I found that send when in execute script is the way to go. Maybe some clarification on how this works? When I get home I will post my testing here for others to look at
  • ClaudiusClaudius Member Posts: 90 ✭✭✭
    edited November 2015
    So, quick look at my laptop and yes - I'm wrong in send(), which just grabs from #user_input (clients actual input line). I was running into a serious problem with send_direct not performing operations which I couldn't figure out the why of. Tried testing why this was happening, by checking typeof thinking maybe it was concatenating and not performing math, tried a few other things. What I found last night was my client was acting correctly when using just send(). I've just looked at it again and realized that what I was testing involved sending a matched pattern from the regex, which when using just send() looked like it was working perfectly!! But alas, I was fooled. The outcome I was expecting from the script I had written to test this, was the same outcome as the straight #input line. So, at the end of the day, my solution does NOT work and the problem persists. No idea what is causing it, so I'm now in the same boat as Khizan.

    edit: I was testing ^em(?: (\w+))?$ and the script was using "emote " (the matches). didn't realize em is already a shortened way to emote. Now I feel dumb!
    OystirKhizan
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    Garryn said:
    ... huh. I'll investigate.
    var i;
    i = 0;
    var n = i;
    client.print(i);
    i = -1;
    client.send_direct("Think  " + i);
    client.print(i);
    n = n + 1;
    i++;
    client.print(n);
    client.send_direct("Think  " + n);
    client.print(i);
    client.send_direct("Think " + i);
    i = -1;
    client.print(i);
    i++;
    client.print(i);
    client.send_direct("Think " + i);
    client.print(i);

    0
    Think  -1
    11
    1
    Think  1
    10
    Think 10
    -1
    0
    Think 0
    9

    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    var i = 0;
    i++;
    client.print(i);
    client.send_direct(i);
    client.print(i);

    var str = "Print Me, ";
    str = str + i;

    client.send_direct(str);
    client.print(str);

    client.print(i);

    var s = "";
    i = 0;
    s = s + i;
    client.print(i);
    client.send_direct(s);
    client.print(s);

    1
    1
    3
    Print Me, 3
    Print Me, 3
    13
    0
    0
    0
    wit beyond measure is a Sidhe's greatest treasure
  • ClaudiusClaudius Member Posts: 90 ✭✭✭
    :|
  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    var i;
    i = 0;
    client.send_direct("Think i " + i);
    i = i + 1;
    client.send_direct("Think i " + i);
    i++;
    client.send_direct("Think i " + i);
    i = 0;
    client.print("i" + i);
    i = i + 1;
    client.print("i" + i);
    i++;
    client.print("i" + i);
    var j;
    j = 0;
    client.send_direct("Think j " + j);
    j = j + 1;
    client.send_direct("Think j " + j);
    j++;
    client.send_direct("Think j " + j);
    j = 0;
    client.print("j" + j);
    j = j + 1;
    client.print("j" + j);
    j++;
    client.print("j" + j);
    var potato;
    potato = 0;
    client.send_direct("Think potato " + potato);
    potato = potato + 1;
    client.send_direct("Think potato " + potato);
    potato++;
    client.send_direct("Think potato " + potato);
    potato = 0;
    client.print("potato" + potato);
    potato = potato + 1;
    client.print("potato" + potato);
    potato++;
    client.print("potato" + potato);

    -----------------------------------------------------------------------

    i0
    i1
    i2
    j0
    j1
    j2
    potato0
    potato1
    potato2
    You think: i 0.
    You think: i 12.
    You think: i 13.
    You think: j 0.
    You think: j 1.
    You think: j 2.
    You think: potato 0.
    You think: potato 1.
    You think: potato 2.

    After going over it with Claudius, I did some tests and I'm pretty sure the problem is that client.send_direct() is using 'i' as an index and that my script ends up running using send_direct's i values.

    var i;
    for (i = 0; i < 10;i++){
    client.print(i)
    }
    for (i = 0; i < 10;i++){
    client.send_direct("think " + i)
    }

    ---------------------------------

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    You think: 0.

    And this:

    var i;
    for (i = 0; i < 10;i++){
    client.print(i)
    }
    for (i = 0; i < 12;i++){
    client.send_direct("think " + i)
    }



    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    You seem to have send more than 200 commands within a second. You probably have some runaway trigger - disabling commands for a while.
    You think: 0.
    You think: 10.
    You think: 11.
    You think: 11.
    You think: 11.
    You think: 11.
    You think: 11.
    You think: 11.
    ...
    You think: 11.

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

  • KhizanKhizan Member Posts: 2,648 ✭✭✭✭✭
    edited November 2015

    More tests:

    var potato;
    for (potato = 0; potato < 10;potato++){
    client.print(potato)
    }
    for (potato = 0; potato < 12;potato++){
    client.send_direct("think " + potato)
    }

    ---------------------------------------

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    You think: 0.
    You think: 1.
    You think: 2.
    You think: 3.
    You think: 4.
    You think: 5.
    You think: 6.
    You think: 7.
    You think: 8.
    You think: 9.
    You think: 10.
    You think: 11.

    =======================================================

    var potato;
    var i = 0;
    for (potato = 0; potato < 12;potato++){
    client.send_direct("think " + i)
    }
    ------------------------------------------------
    You think: 0.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.
    You think: 9.

    "On the battlefield I am a god. I love war. The steel, the smell, the corpses. I wish there were more. On the first day I drove the Northmen back alone at the ford. Alone! On the second I carried the bridge! Me! Yesterday I climbed the Heroes! I love war! I… I wish it wasn’t over."

  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    @Garryn, can you make a color-able version of client.print? thanks.
    wit beyond measure is a Sidhe's greatest treasure
  • EoghanEoghan Member, Immortal Posts: 779 mod
    You can use the String fontcolor() method: http://www.w3schools.com/jsref/jsref_fontcolor.asp
    Like what we're doing? Why not take a second to vote? Vote for Imperian at http://www.imperian.com/vote
    Iniar
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    :(

    image

    image
    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    edited November 2015
    @Garryn

    would you mind looking at what I have for the webclient and tell me why my ping jumps to 1+ s when trying to aff with Bard? My baseline ping is about 330+ ms

    Also, can you give us a shortcut to collapse the scrollback? I hate having to move my cursor to hit the 'Hide' button.

    image
    Post edited by Iniar on
    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    I've probably missed it in the documents somewhere, but how do I send a GMCP message to Imperian's server via the webclient?
    wit beyond measure is a Sidhe's greatest treasure
  • EoghanEoghan Member, Immortal Posts: 779 mod
    edited November 2015
    send_GMCP(message_type, message), e.g. client.send_GMCP("IRE.Target.Set", "12345")
    Like what we're doing? Why not take a second to vote? Vote for Imperian at http://www.imperian.com/vote
    Iniar
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    Thanks!
    wit beyond measure is a Sidhe's greatest treasure
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    wit beyond measure is a Sidhe's greatest treasure
    Dicene
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    edited November 2015
    @Garryn;

    image

    With MXP turned on, the second is achieved with 

                        client.current_line.parsed_line.replace(matches.index, matches.index + n, k, v, null); 

    while the first is a failed attempt at highlights with

                        client.current_line.parsed_line.colorize(matches.index, matches.index + n, v, null); 

    Is there away to allow colorize to override MXP colors (but not links?); thanks.

    Addit: .colorize() seems to work over the QW mxp-names, but not the CW and GW mxp-names. :(
    wit beyond measure is a Sidhe's greatest treasure
    Dicene
  • IniarIniar AustraliaMember Posts: 3,213 ✭✭✭✭✭
    edited November 2015
    @Garryn

    I'd really like a separate window that I can push messages to... the biggest use for me would be just three lines above the input bar so I can place in-room names along with auto-assigned numbers, that's all :(

    also:

    when hitting the up arrow, I retrieve the value at position 1 of a 0-based index of an array of last commands entered; can we make it retrieve position 0?
    wit beyond measure is a Sidhe's greatest treasure
    Dicene
«1
Sign In or Register to comment.