Dismiss Notice
Wynncraft, the Minecraft MMORPG. Play it now on your Minecraft client at (IP): play.wynncraft.com. No mods required! Click here for more info...

SPOILER Wynnscript's Workings [UPDATED!]

Discussion in 'Wynncraft' started by Endistic, Mar 12, 2023.

Thread Status:
Not open for further replies.
  1. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    What is Wynnscript?

    Wynnscript is the programming language behind Wynncraft. It’s used by the CT to create things like quests, raids, etc. However, not too much is known about it. All we know is that it’s like JavaScript. Or do we?


    VSCode Extensions

    Visual Studio and Visual Studio Code are 2 code editors developed by Microsoft. They’re extremely widely used by developers today. However, they have these things called “Extensions”, where you can extend the functionality of them.
    Two people, sockmower and Thomka posted WynnScript extensions onto the Extensions section for other WynnScript developers to use. However, they probably didn’t know you can decompile extensions.


    Extension Decompilation

    Each extension comes as a .vsix file. However, if you install the extension and then look into your extensions folder, you will see them all there with their source code. This also includes the WynnScript extensions.
    Inside these extensions were so called “grammar files”. These files define the grammar of a programming language, specifically how you’re supposed to write it. However, these grammars can easily be read and understood if you know what you’re looking for.

    What now?
    Now we have 2 grammars for WynnScript, one from 2021, and one from 2023. We can compare and contrast them to figure out a lot about WynnScript. I’ll save you the boring details of comparing and contrasting and skip straight to how it is.

    What does Wynnscript look like?

    We already know it looks a lot like JavaScript.

    Here’s a comparison between the two.


    JavaScript:

    Code:
    
    let count = 10; //var is valid but less modern
    while(count > 0) {
        count -= 1;
        console.log(count);
    }
    console.log(“Countdown complete!”);
    
    WynnScript:

    Code:
    var count = 10;
    while(count > 0) {
        count -= 1;
        player.sendMessage(@“{count}”); // sendMessage is not confirmed but must exist in some form
    }
    
    player.sendMessage(“Countdown complete!”);
    

    As you can see there’s not many differences. We’ll start off with the similarities. So non-programmers aren’t left in the dark, we’ll glance over what each thing means.


    Strings


    Strings are essentially a chunk of text, as small or as large as you want.
    Strings are outlined with either “ or ‘, e.g “Hello world!” and ‘Hello world!’, but not “Hello world!’.
    For whatever reason, you can optionally prefix a string with @. I’m not sure why. Here, we will assume it is for putting values in strings.
    You can put values in strings by wrapping it in curly braces, e.g {world} to insert the value of the variable world.
    You can escape special characters such as curly braces using a backslash (\).

    An example would be: @“Hello! This string has a value of {myVariable} inside.”


    Variables

    Variables are essentially boxes. The box has a name (the name of the variable) and a value inside it. To create a variable you would use this syntax.


    Code:
    var variableName: int = 10;
    
    And to refer to it in a string you could:

    Code:
    “The value is {variableName}.”
    
    Do note the types are unconfirmed. We don't know if it's "int", "integer", etc. or anything like that. Type specifications are optional.

    Booleans

    Booleans are either true or false. In Wynnscript, they are written as “true” and “false”.
    e.g.
    Code:
    var myBoolean = true;
    

    Control Flow

    Control flow is essentially how a program flows. It contains things such as if statements (if this is true, do this), while statements (while something is true, do this) and for statements (for each item in a list, do this).

    In WynnScript, it’s written like so.

    If statements:

    Code:
    if([condition]) {
        // what to do if [condition] is true…
    } else {
        // what to do if [condition] is not true..
    }
    
    While statements:

    Code:
    
    while([condition]) {
        // what to do while [condition] is true
    }
    
    

    Comments


    Comments are essentially text you can put in your program that has no effect on the output. It’s usually used for documenting what things do. They look like this:

    Code:
    // Simple single line comment.
    
    /*
    Multiline comment!
    */
    
    

    There are also deprecated variants using #.
    Code:
    
    # Simple single line comment.
    
    #*
    Multline comment!
    *#
    
    
    Functions

    This is the part where we go from practically confirmed from the grammar territory to partial speculation.
    Functions are called like so:

    Code:
    functionName(parameter1, parameter2, ...);
    

    Here are some examples:

    Code:
    
    // So, if you wanted to teleport a player to the coordinates 100, 200, 300, you could:
    player.teleport(100, 200, 300, 0); // fourth parameter unknown
    
    // Or give them Blindness 5 for 5 seconds.
    player.addPotion(“BLINDNESS”, 5, 5);
    
    These functions were confirmed in this screenshot of code from the Nest of the Grootslangs raid:

    [​IMG]

    In Wynnscript, functions can be defined like so:

    Code:
    fn my_function_name(type var_name) {
       // ..code here
    }
    
    It's also possible functions with parameters look like this:
    Code:
    fn my_other_function(val pass_by_value: int, ref pass_by_reference: int) {
      // ..code here
    }
    
    For more information, look up Pass by Value and Pass by Reference.

    For some reason, in function names, variables are declared type-first, but in variable definitions, variables are declared type-last.

    You can include functions from other files with the "using" syntax.
    Code:
    #using /my_script.ws
    
    Atomics & Extra Features
    WynnScript also has unique features that are in other languages but aren't applied like in other languages.
    Now we’re almost 100% speculating based off the little hints in the grammar files.

    This is gonna get a bit complicated.

    In Wynnscript, an atomic code block is a code block that is assumed to not be possible to interrupt. So you can not have any delay functions (like sleep, common in lower-level programming languages) inside of the code block.

    Atomics are intended to be used alongside the async keyword, which we will go over next.

    Code:
    atomic {
        player.sendMessage(“I’m an atomic code block!”);
    }
    
    Code can also be ran asynchronously using the async keyword.
    Chances are, it's ran like a block of code. The only other option would be asynchronous functions, but we don't have functions in Wynnscript.

    Code:
    async {
      // asynchronous operations
    }
    
    Ok, but how do we combine them? Unfortunately, I haven't worked with either of those, so I'll just paste in an explanation from StinkEyeCookie.

    For more information on atomics in other languages, refer to https://doc.rust-lang.org/std/sync/atomic/

    Integers also seem to accept time units like milliseconds, seconds, minutes, and hours, allowing you to do things like this:

    Code:
    while([some condition]){
        player.addPotion(“BLINDNESS”, 5, 5);
        sleep(5s);
    }
    

    Example Scripts

    Now that we have the basics down, let’s write some example programs in Wynnscript.


    Code:
    // This program assumes:
    // script.getPlayer(); returns the player who rolled
    // the dice.
    // random(x, y); returns a random number from x to y, inclusive.
    // player.playSound(sound); plays a sound to the player.
    //
    // This item simulates rolling a dice.
    // Activated via Script Item (Cursed Dice)
    // Item chest located @ 120.5, 20, 189.5 (Fake coordinates)
    // Safe teleportation area located @ 120.5, 21, 189.5 (Fake coordinates)
    
    // Initialize dice rolling.
    var player = script.getPlayer();
    player.sendMessage(“&7Rolling the dice. Let fate be in your hands.”);
    
    // Play the sound effect of rolling the dice.
    var counter = 0;
    var timeBetween = 1000;
    while(counter < 20) {
        player.playSound(“block.ancient_debris.hit”);
        timeBetween /= 2;
        counter += 1;
        sleep(timeBetween);
    }
    
    
    // Generate output of dice.
    var result = random(1, 6);
    
    // Based on output, make the number a certain color.
    var color = “&a”;
    if(result == 3 || result == 4) {
        color = “&e”;
    } else {
        color = “&c”;
    }
    
    
    // Send the result to the player.
    player.sendMessage(“&7You rolled a {color}{result}&7.”);
    

    How is it executed?


    Well, there’s 2 possible ways.

    Interpreter
    The most likely way is that every once in a while, a plugin grabs from the GitHub repo containing all of the scripts. It uses an interpreter (https://en.wikipedia.org/wiki/Interpreter_(computing)) to run through the scripts and execute them as they need to be.

    Command Blocks
    There’s a chance that it’s also converted into command blocks. We know CMD does a fair amount in WynnScript and also codes in command blocks.

    However, I’m not really sure about which way it’s handled. It’s most likely the first one.


    Disclaimer & Notes

    Do note that a lot of this is speculation. I can’t confirm how much of this is accurate or not.
    If we find new discoveries we will probably update the thread as we find more or make more example scripts.
    Also, thanks to the CT for being chill with this. I worked it out with Hams and I’m 95% confident that I’m allowed to post this. We did a bit of extra elaboration since our chat but I believe it's still in the realm of being legal.


    Errata & Update Log
    - A reply by @StinkEyeCookie. I attempted to edit the post to be more accurate.
    - WE GOT SYNTAX HIGHLIGHTING WORKING! Expect code blocks to be updated with much better highlighting in the next few days.
    - [3/29/23] A new update was released to Thomka's extension. The thread has been updated accordingly. We're currently working through the final thing that we can do: bundle.js, a 14k line file with an obfuscated javascript wynnscript parser. However, if we are allowed to disclose the file here (we're not sure since a mod took down a link), I would like to request help. If anyone here is willing to help out, please reach out to me.

    Credits & Attribution
    @Endistic - Starting out and getting the initial extension source decompiled.
    @SSurvivor64 - Proof reader & regex translator.
    @StinkEyeCookie - Analyzed Thomka's update to the extension - deconfirmed 1-function-per-script and other things. Thread has yet to be updated to account for this.
    Thomka & sockmower - Providing extension code that can be decompiled.



    That’s about it.
    Have a good day everyone!
     
    Last edited: Mar 29, 2023
  2. DungeonBee

    DungeonBee Hunter of the Realm

    Messages:
    530
    Likes Received:
    670
    Trophy Points:
    91
    Guild:
    Minecraft:
  3. Hams

    Hams Content Team Manager CT Manager Support Team Community Manager Builder

    Messages:
    318
    Likes Received:
    2,563
    Trophy Points:
    111
    Creator Karma:
    Minecraft:
    It's kinda funny to see this from the inside to me

    I'm not going to comment beyond that tho ^^
     
    Wow, ThedumbOX, luckeyLuuk and 2 others like this.
  4. Thomka

    Thomka QA CMD CHAMPION

    Messages:
    166
    Likes Received:
    921
    Trophy Points:
    75
    Minecraft:
    I did know that, which is why there are checks that enable the extension's code only if you're in a CT. That's why there's this text in a README:
    [​IMG]
    I assume you got all the info from 'grammar.json' file (which is required for the extension highlighting and cannot be hidden:sob:). Congratz on figuring out so many stuff from a single file. Didn't expect for someone to do this.
     
    Stag2001, luckeyLuuk and Endistic like this.
  5. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    i did get it from grammar.json!

    and for function name correctness
    i'm honestly not sure either
    i took wild guesses based on what i've seen in javascript and spigot
    i meshed it all together (for js, it's __.__() format, for spigot it's the camelCase and the methods on players/entities) and got these predictions
    for functions i did have to come up with, i used existing wynnscript leaks to figure out the wynnscript styling
    and i just kept trying things until it sounded like it made sense and fit in with wynnscript

    to be honest i could probably keep going and fully reverse engineer wynnscript
    but
    a. i really just don't feel like it
    b. i don't want to leak things that shouldn't be leaked in the instance i am correct about this

    your extension was a massive help though lol
    and it seems really cool
    i couldn't set it up myself
    but keep up the good work
    ________________________________
    i forgot to mention @SSurvivor64
    he was the glue that made this project go from probably 50% guesswork to like 90% confidence
    he knows what regex is and how to read it
    i don't

    and seemed to know a lot more about js than me
    which is fair
    i mostly code in rust
     
    luckeyLuuk likes this.
  6. thatswhatido

    thatswhatido Supreme Wynncraft Player thatswhatido CHAMPION

    Messages:
    771
    Likes Received:
    2,131
    Trophy Points:
    148
    Guild:
    Minecraft:
    Wow a vscode extension, how fancy. Seems like less fancy version of JavaScript to me lol.
     
    Endistic likes this.
  7. shtnck eyh ckhhe

    shtnck eyh ckhhe Jesus of Nether-eth

    Messages:
    879
    Likes Received:
    1,888
    Trophy Points:
    148
    Minecraft:
    My guess? That they're not unique to WynnScript, but that they're just built-in language features unlike as done in other languages.
    By the way, I think the * in `atomic *{` is repetition for spaces, not like an actual symbol in the language.

    Code:
    // its supposed to look like this
    atomic { ... }
    atomic    { ... }
    
    // not this
    atomic *{ ... }
    So, below is what I assume might be true:

    Async is not for functions (after all, WynnScript doesn't have functions, so how could it have async functions?), but instead spawns threads that execute code blocks independently of one another. They can execute at the same time, and are not bound by a specific order of execution.

    Atomics, like you said, are not possible to interrupt, but to explain that a little more: they act as one operation, and no other async/threads cause state changes during it. Take the below code (I call it WynnScript++, or "WynnScript With Lists") for example:

    Code:
    var queue = ["Joe", "Bob"];
    
    let i = 0;
    while (i < 2) {
        i++;
        async {
            var name = queue[0]; // get the first person in the list
            queue.remove(0); // remove that first person from the list
            player.sendMessage("say hello to {name}!");
        }
    }
    
    If you were just looking at the code, you might expect two possible outcomes: first "say hello to Joe!" and then "say hello to Bob!", or first "say hello to Bob!" and then "say hello to Joe!".
    But there's another possible outcome: "say hello to Joe!" printed twice, and "say hello to Bob!" printed zero times. How is that possible? Well consider:

    Code:
    expected execution:
    
    1st ASYNC BLOCK                         2nd ASYNC BLOCK
    var name = queue[0]; // name = "Joe"
    queue.remove(0);
    print("{name}"); // prints "Joe"
                                            var name = queue[0]; // name = "Bob"
                                            queue.remove(0);
                                            print("{name}"); // prints "Bob"
    
    
    
    or:
    
    1st ASYNC BLOCK                         2nd ASYNC BLOCK
                                            var name = queue[0]; // name = "Joe"
                                            queue.remove(0);
                                            print("{name}"); // prints "Joe"
    var name = queue[0]; // name = "Bob"
    queue.remove(0);
    print("{name}"); // prints "Bob"
    
    
    
    but weird things can happen:
    
    1st ASYNC BLOCK                         2nd ASYNC BLOCK
    var name = queue[0]; // name = "Joe"
                                            var name = queue[0]; // name = "Joe", because "Joe" hasnt been removed from `queue` yet
    queue.remove(0);
    print("{name}"); // prints "Joe"
                                            queue.remove(0);
                                            print("{name}"); // prints "Joe"
    However, with an atomic block around the read and removal of the first element from `queue`, it would be guaranteed that no other code could affect `queue` while the atomic block is running.

    Code:
    atomic {
        var name = queue[0];
        queue.remove(0);
    }
    print("{name}");
    By the way: https://doc.rust-lang.org/std/sync/atomic/
     
    Last edited: Mar 12, 2023
  8. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    ty!
    i looked at it and yeha, i agree with your theory
    it makes a lot mor sense than mine

    i have reflected the thread to be more accurate to this and have a direct quotation and attribution
     
    luckeyLuuk likes this.
  9. TrapinchO

    TrapinchO retired observer of the wiki VIP+ Featured Wynncraftian

    Messages:
    4,662
    Likes Received:
    6,602
    Trophy Points:
    217
    Minecraft:
    Simple but nice. Good job on discovering it!

    I have three ideas for that, the first two coming from python.
    1) it means "literal" string, i.e. "special" characters (e.g. backslashes) are not treated as special but regular ones
    2) it marks "interpreted" strings, where expressions in curly braces are evaluated, just like you described
    3) it allows it to go multiline, e.g.
    "bla
    baal
    lab"
    (this would usually throw an error, because it is not on one line)

    If you are allowed to, could you please send the part with it? Maybe there is something.


    The correct term is "interpreting".

    Technically "parsing" is the process of changing the source code into a "middle thing" before it is translated into machine code or whatever. Wynnscript is parsed either way, whether the end result is an interpreter or CMDs.
     
  10. Crouton_18

    Crouton_18 Either an Airship Captain or a Masochist CHAMPION

    Messages:
    484
    Likes Received:
    1,610
    Trophy Points:
    91
    Guild:
    Minecraft:
    I have no clue what this means but good job!
     
    TrapinchO likes this.
  11. TrapinchO

    TrapinchO retired observer of the wiki VIP+ Featured Wynncraftian

    Messages:
    4,662
    Likes Received:
    6,602
    Trophy Points:
    217
    Minecraft:
    TLDR: CT had their own secret mini programming language and this guy stole it

    also I definitely did not think this is hmtn's orc script thread when I first replied
     
    Earthbrine and Endistic like this.
  12. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    reviving this thread
    i took too long on this lmao
    fixed the error
    for now i won't add anything to @ as i'm not confident on what it even means
    maybe it has other meaning, who knows
     
    TrapinchO likes this.
  13. shtnck eyh ckhhe

    shtnck eyh ckhhe Jesus of Nether-eth

    Messages:
    879
    Likes Received:
    1,888
    Trophy Points:
    148
    Minecraft:
    Was looking through the extensions again, and I noticed a few things:


    Code:
    {
        "name": "keyword.type.wynnscript",
        "match": "\\b(var|fn)\\b"
    },
    There does, actually, seem to be a way to define functions, via the `fn` keyword. So I revise my previous comment- maybe there are async functions, and maybe not async blocks if that's the case (or maybe WynnScript is spicy and does async like Zig does it...).


    There also seems to be typing, or at least that's my best guess (the syntax is reminiscent of types in other langs):
    Code:
    {
        "name": "entity.name.class.wynnscript",
        "match": "\\b(?<=: *)[_a-zA-Z0-9]+\\b"
    },
    That might look something like this:
    Code:
    var foo: int = 123;
    It does make sense that WynnScript would have types. Dynamic typing is indeed kind of a pain, and plus, if they're gonna compile it, they may as well just do a type checking pass anyways.


    And there's also this odd syntax:
    Code:
    {
        "name": "entity.name.class.wynnscript",
        "match": "\\b(?<=\\(|\\, ) *[_a-zA-Z0-9]+(?= +[_a-zA-Z0-9]+)\\b"
    },
    Code:
    // examples
    (foo bar
    ,foo bar
    
    // i assume its supposed to look like this
    (foo bar, baz qux, etc etc)
    
    // fn decls, maybe?!
    fn myFunction(int a, int b, int c) { ... }
    This, I don't know. Might be function parameter typing, but that's barely a guess. It is under the variable category though, so it's probably variable related.


    Code:
    {
        "name": "keyword.control.wynnscript",
        "match": "\\b(if|else|while|fwhile|break|atomic)\\b"
    },
    `fwhile` also seems to be a keyword, though you didn't mention it. It might be a for-while loop or something.


    Code:
    {
        "name": "keyword.include.wynnscript",
        "match": "include"
    }
    Maybe import declarations. Dunno if this is just for the built-in namespaces (like player.whatever or script.doSomething) or if people can actually define their own namespaces. If this is the case, though, I dunno what the below is for:
    Code:
    {
        "name": "keyword.control.wynnscript",
        "match": "^#using"
    },
    Given that it uses `#`, which is generally used for comments, this could be an older, maybe deprecated version of `include` (maybe it's like JavaScript's `"use strict"`, idk). Also, while Sockmower's extension has operators and a bunch of deprecated things, Thomka's doesn't, and the fact that someone once mentioned that Wynnscript some time recently (or maybe not recently idk) got updated so that it support arithmetic operations, Sockmower's might moreso reflect the newer WynnScript, while Thomka's is the older one. Or not, since Thomka just updated their extension but a week ago. I dunno.
    It could also however have a different function, perhaps one similar to that of C++'s `using`.


    Code:
    {
        "name": "keyword.includeAsync.wynnscript",
        "match": "includeAsync"
    },
    I dunno what this is, but it's probably related to `include` and `async` (it's a difficult conclusion, I know). Maybe some CT were using `async` as variable names for some reason and to make sure that newer compiler versions don't mistake those `async`s for keywords `includeAsync` is necessary. But that'd be kinda weird.


    Code:
    {
        "name": "keyword.terminate.wynnscript",
        "match": "terminate"
    },
    Maybe terminates subscripts started by `script.startSubscript`.





    Just another little bit of information:
    upload_2023-3-25_18-0-48.png
    Thomka updated their extension just a few days after you posted this thread. Interesting.

    upload_2023-3-25_18-2-36.png
    Or not so interesting. Thomka seems to update their extension every week. Maybe there's new features to WynnScript every week, maybe Thomka's just pulling something. I dunno.





    Note: bundle.js from Thomka's extension seems to be code autogenerated by a parser generator (I mean it literally says ANTLR at the bottom so...)
     
    Last edited: Mar 25, 2023
    luckeyLuuk, TrapinchO and Endistic like this.
  14. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    I will update the rest later but this...
    This has to mean something.
    Thomka either somehow got a grammar from the higher-ups or made on themself.
    This comment slightly stuck out to me because I know quite a bit about parser generators.
    What if I try to reconstruct the grammars from the JSON back to the hypothetical grammar to get more information?
     
  15. shtnck eyh ckhhe

    shtnck eyh ckhhe Jesus of Nether-eth

    Messages:
    879
    Likes Received:
    1,888
    Trophy Points:
    148
    Minecraft:
    syntax highlighting example
    upload_2023-3-25_18-38-30.png
     
    luckeyLuuk, TrapinchO and Endistic like this.
  16. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    I see! How did you get it working?
     
  17. shtnck eyh ckhhe

    shtnck eyh ckhhe Jesus of Nether-eth

    Messages:
    879
    Likes Received:
    1,888
    Trophy Points:
    148
    Minecraft:
    i installed the extension, created a file called source.ws, and started typing; doesnt seem to have anything other than syntax highlighting though
     
    luckeyLuuk and Endistic like this.
  18. Endistic

    Endistic Acolyte Enjoyer HERO

    Messages:
    724
    Likes Received:
    1,300
    Trophy Points:
    148
    Guild:
    Minecraft:
    Yep, it worked. Thanks!

    Here's my game plan for this thread:
    - Figure out the async/await shenaniganery (I do agree that it could be zig-like - I really hope it is. Zig is one of my languages that I want to learn this year.)
    - Try to reconstruct the original grammar file to have a more "regular" way of viewing it.
    - Update functions to allow for 'fn' keyword.
    - Improve Attributions & Credits by making a dedicated section for it.
    - Over the next few days, thread should be semi-consistently updated.
     
    luckeyLuuk likes this.
  19. TrapinchO

    TrapinchO retired observer of the wiki VIP+ Featured Wynncraftian

    Messages:
    4,662
    Likes Received:
    6,602
    Trophy Points:
    217
    Minecraft:
    maybe deconstruction? "(x, y) = foo(20) // foo returns list [2, 0]"
    otherwise I would assume tuples

    make a working version of WynnScript
     
  20. SSurvivor64

    SSurvivor64 Watcher of the Realm

    Messages:
    30
    Likes Received:
    59
    Trophy Points:
    51
    Guild:
    Minecraft:
    I’m here

    Modern JavaScript does have this, but it’s labeled the same as variable typing, so it’s likely a parameter type check. Kinda weird that it’s inconsistent format:
    Code:
    var x: int = 7;
    fn thing(int x){
        return x+1;
    }
    Wait… is there even a return keyword?
     
Thread Status:
Not open for further replies.