# Forum > World of Warcraft > World of Warcraft Bots and Programs > WoW Memory Editing > [Release] Writing Bots with Robot-js

## Torpedoes

*ABOUT*
To celebrate the release of Robot, I have created some quick demos to show off its capabilities. Each day for the next eight days, I'll be showing practical examples of code which implements a part of a WoW bot. Obviously, this is nothing we haven't seen before, but it'll still be a good learning experience for many wishing to write bots. All code here will be written in JavaScript (nothing to do with Java) and is compatible with WoW 6.2.4.21463 for Windows (not being kept up to date). Hopefully by the end, you will see the benefits of writing bots in JavaScript and maybe even develop some of your own to showcase here! I look forward to your feedback and suggestions.
*WHY JAVASCRIPT?*
Traditionally, bots have been written in C++, C#, VB.net and AutoIt. And that's been fine for many years. The problem is that some of these languages are really complicated for beginners (C++). Some are not entirely platform-independent (C#, VB.net, and AutoIt). Some are antiquated (VB.net). And some lack the power of other programming languages (AutoIt). Furthermore, all of these languages suffer from the fact that you have to write a lot of things yourself, things like libraries to write a bot.

All these problems have been plaguing the botting community for years. But how can we fix them? Well, since the introduction of Node.js in 2009, we've seen a massive explosion of interest in JavaScript. Thousands of libraries doing all sorts of neat things have been written, and with the new language specifications, JavaScript has never looked more attractive. Some advantages of JavaScript include that it's very easy to pick up, a lot of developers already know how to use it, it has a thriving community and it's completely cross-platform. Furthermore, because there is no compile step you can just write your program and run it!

Sounds great right? But how can you use it to write bots? The answer is to use robot-js. This library is the final missing link enabling everyone to write system automating bots for any purpose. And in keeping with the JavaScript tradition, no compiler is needed. Just write and run!
*SAFETY*
Exactly how safe are these bots? Well, since this is the first time we've seen something like this, I have no idea. But I suspect writing bots in JavaScript is arguably the safest method to use. All code is run by the V8 interpreter running within "Node.exe" and if ran in admin mode, regular applications will have no idea what code is actually being ran. The only way to detect it is if you were to directly modify some code in the target application.
*GET STARTED*
Start by downloading Node.js, as of the time of this writing I recommend getting 6.0. Then create a folder to contain the project. With the command line, cd into your folder and type "npm install robot-js" (no quotes). You're now ready to write bots. You can download any other library you wish but for the purposes of these demo's, only robot-js is required.

For these demo's, there are three files which are used by all eight demos, think of it as a primitive library for implementing common functionality. The code for these files is below. Each demo consists of a single file which is ran with "node <name_of_demo>.js" (no quotes). All demo's are console applications and have no UI associated with them.
*DISCLAIMER*
None of the code in the demos infringe on anybody's copyright. The demos should also only be used for educational purposes, specifically for the purposes of learning robot-js. You should never use these demos to break the terms of service of any software. The author(s) of these demos cannot be held liable for any damages caused by the code. By downloading and using the code, you agree to do so at your own risk. All these demos are licensed under the ZLib license.
*DEMOS*
Part 1: BMAH MonitorPart 2: Chat Monitor (Writing Bots with Robot-js)Part 3: Entity DumperPart 4: Aura MonitorPart 5: Cooldown MonitorPart 6: Target MonitorPart 7: Player NamesPart 8: Fishing BotPart X: Conclusion
*COMMON CODE*
Includes three files implementing common functionality used by all eight demos. app_controller.js implements the main event loop. game_instance.js represents an instance of WoW, along with helpers to select it. offsets.js represents common memory offsets used by all eight demos.
*app_controller.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Constants                                                                  //
//----------------------------------------------------------------------------//

const GameInstance = require ("./game_instance");



//----------------------------------------------------------------------------//
// Export                                                                     //
//----------------------------------------------------------------------------//

module.exports = class
{
    ////////////////////////////////////////////////////////////////////////////////
    /// Creates a new application controller

    constructor (updateRate, updateFunc)
    {
        this._updateRate = updateRate;
        this._updateFunc = updateFunc;

        this._gameInstance = new GameInstance();
        console.log ("Select a WoW Window...");

        this._heartbeat();
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Main event loop executed by the heartbeat

    _eventLoop()
    {
        // Waits for a game to get selected
        if (!this._gameInstance.isSelected())
        {
            this._gameInstance.selectByActiveWindow();
            return;
        }

        // Ensures the game is still running
        if (!this._gameInstance.isRunning())
        {
            console.log ("Select a WoW Window...");
            this._gameInstance.deselect(); return;
        }

        // Checks whether the player is in-game
        if (!this._gameInstance.memory.readBool
            (this._gameInstance.offsets.GameState +
             this._gameInstance.module)) return;

        // Call the specified update function
        this._updateFunc (this._gameInstance);

        // Don't forget to reset memory cache
        this._gameInstance.memory.clearCache();
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Performs heartbeat and enqueues the next one

    _heartbeat()
    {
        this._eventLoop();
        setTimeout (() =>
            this._heartbeat(),
            this._updateRate);
    }
};
```

*game_instance.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Constants                                                                  //
//----------------------------------------------------------------------------//

const Robot   = require ("robot-js" );
const Offsets = require ("./offsets");

// Shortcuts to Robot classes
const Process = Robot.Process;
const Module  = Robot.Module;
const Memory  = Robot.Memory;
const Window  = Robot.Window;



//----------------------------------------------------------------------------//
// Export                                                                     //
//----------------------------------------------------------------------------//

module.exports = class
{
    ////////////////////////////////////////////////////////////////////////////////
    /// Creates a new unselected game instance object

    constructor() { this.deselect(); }

    ////////////////////////////////////////////////////////////////////////////////
    /// Deselects any currently selected game instance

    deselect()
    {
        this._window  = null;       // The game window

        this._process = null;       // The game process
        this._is64Bit = false;      // If game is 64Bit

        this._memory  = null;       // The game memory
        this._module  = null;       // Main module addr

        this._offsets = null;       // Correct offsets
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Selects a game instance using the specified process

    selectByProcess (process)
    {
        // Check if arguments are correct
        if (!(process instanceof Process))
            throw new TypeError ("Invalid arguments");

        // Attempt to select the main window
        let window = process.getWindows()[0];

        return window &&
            // Perform window selection
            this.selectByWindow (window);
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Selects a game instance using the specified window

    selectByWindow (window)
    {
        // Check if arguments are correct
        if (!(window instanceof Window))
            throw new TypeError ("Invalid arguments");

        // Check if the window title correctly matches
        if (window.getTitle() !== "World of Warcraft")
            return false;

        let process = window.getProcess();
        // Ensure that the process was opened
        if (!process.isValid()) return false;

        let module =
            // Get the main executable module
            process.getModules (".*\.exe")[0];
        if (!module) return false;
        module = module.getBase();

        // Determine if game is 64Bit
        let is64Bit = process.is64Bit();
        let offsets = is64Bit ?
            // Make sure to select correct offsets
            Offsets.Offsets64 : Offsets.Offsets32;

        // Create a new memory object
        let memory = Memory (process);
        if (memory.readString
            // Ensure game build is supported
            (module + offsets.GameBuild, 6) !==
            Offsets.GameBuild) return false;

        this._window  = window;
        this._process = process;
        this._is64Bit = is64Bit;
        this._memory  = memory;
        this._module  = module;
        this._offsets = offsets;

        // Create the memory cache
        this._memory.createCache
            (16384, 4096, 10485760);

        return true;
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Selects a game instance by scanning all open processes

    selectByFindProcess()
    {
        for (let p of Process.getList ("Wow.*\.exe"))
        {
            // Select the first suitable process value
            if (this.selectByProcess (p)) return true;
        }

        return false;
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Selects a game instance by scanning all open windows

    selectByFindWindow()
    {
        for (let w of Window.getList ("World of Warcraft"))
        {
            // Select the first suitable window value
            if (this.selectByWindow (w)) return true;
        }

        return false;
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Selects a game instance using the current active window

    selectByActiveWindow()
    {
        // Attempt to select the current active window
        return this.selectByWindow (Window.getActive());
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Returns true if a game instance is currently selected

    isSelected()
    {
        return this._window !== null;
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Returns true if the selected game instance is running

    isRunning()
    {
        // Ensure a game window is selected
        if (!this.isSelected()) return false;

        return !this._process.hasExited() &&
                this._window .isValid  ();
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// Various properties to extract game instance information

    get window () { return this._window;  }

    get process() { return this._process; }
    get is64Bit() { return this._is64Bit; }

    get memory () { return this._memory;  }
    get module () { return this._module;  }

    get offsets() { return this._offsets; }
};
```

*offsets.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Offsets                                                                    //
//----------------------------------------------------------------------------//

module.exports =
{
    GameBuild                       : "21463",

    Offsets32:
    {
        GameHash                    : 0xC7FEDA45,
        IconHash                    : 0xA118EC28,
        GameBuild                   : 0x9B24E4,
        GameState                   : 0xDA55B2,

        LocalPlayer                 : 0xD2E4C0,
        LocalCont                   : 0xBB3F7C,
        LocalZone                   : 0xBC0A64,
        IsLooting                   : 0xE165C8,
        IsTexting                   : 0xC1EB08,
         MouseGuid                  : 0xDA5988,
        TargetGuid                  : 0xDA59B8,

        Camera:
        {
            Struct                  : 0xDA5D58,
            Offset                  : 0x7610,
            Origin                  : 0x08,
            Matrix                  : 0x14,
            Fov                     : 0x38
        },

        Entity:
        {
            TableBase               : 0xC9D530,
            EntryFirst              : 0x0C,
            EntryNext               : 0x3C,

            Entry:
            {
                Type                : 0x0C,
                Descriptors         : 0x04,

                Desc:
                {
                    GlobalID        : 0x00,
                    EntityID        : 0x24,
                    DynFlags        : 0x28
                }
            },

            Unit:
            {
                Transport           : 0xAB0,
                Origin              : 0xAC0,
                Angle               : 0xAD0,
                Casting             : 0xF98,
                CastingStarted      : 0xFB0,
                CastingWillEnd      : 0xFB4,
                Channel             : 0xFB8,
                ChannelStarted      : 0xFBC,
                ChannelWillEnd      : 0xFC0,

                Aura:
                {
                    Count1          : 0x1588,
                    Count2          : 0x1108,
                    TableBase1      : 0x1108,
                    TableBase2      : 0x110C,
                    EntrySize       : 0x48,

                    Entry:
                    {
                        Owner       : 0x20,
                        SpellID     : 0x30,
                        Flags       : 0x38,
                        Stacks      : 0x39,
                        Level       : 0x3A,
                        EndTime     : 0x40
                    }
                },

                Desc:
                {
                    Creator         : 0x080,
                    Health          : 0x0F0,
                    Power           : 0x0F4,
                    HealthMax       : 0x10C,
                     PowerMax       : 0x110,
                    Level           : 0x158,
                    Flags           : 0x17C
                }
            },

            NPC:
            {
                Name1               : 0xC38,
                Name2               : 0x07C
            },

            Player:
            {
                Money1              : 0x190C,
                Money2              : 0x1890,
                Arch                : 0x1910,
                ArchCount           : 0x08,
                ArchSites           : 0x10,
                Target              : 0x41E8
            },

            Object:
            {
                Bobbing             : 0x104,
                Transport           : 0x130,
                Origin              : 0x140,
                Rotation            : 0x150,
                Transform           : 0x278,
                Name1               : 0x274,
                Name2               : 0x0B4,

                Desc:
                {
                    Creator         : 0x030,
                    Display         : 0x040
                }
            }
        },

        NameCache:
        {
            TableBase               : 0xC6043C,
            EntryNext               : 0x00,

            Entry:
            {
                Guid                : 0x10,
                Name                : 0x21,
                Race                : 0x70,
                Class               : 0x78
            }
        },

        Cooldown:
        {
            TableBase               : 0xC8AC88,
            EntryNext               : 0x04,

            Entry:
            {
                SpellID             : 0x08,
                 ItemID             : 0x0C,
                SpellStartTime      : 0x10,
                SpellDuration       : 0x14,
                GroupID             : 0x18,
                GroupStartTime      : 0x1C,
                GroupDuration       : 0x20,
                IsActive            : 0x24,
                GcdStartTime        : 0x28,
                GcdDuration         : 0x30
            }
        },

        BMAH:
        {
            Count                   : 0xE536D0,
            TableBase               : 0xE536D4,
            EntrySize               : 0x70,

            Entry:
            {
                MarketID            : 0x00,
                  ItemID            : 0x08,
                MinimumBid          : 0x48,
                MaximumInc          : 0x50,
                CurrentBid          : 0x58,
                TimeLeft            : 0x60,
                BidCount            : 0x68
            }
        },

        Chat:
        {
            Position                : 0xE01894,
            TableBase               : 0xDA7518,
            EntrySize               : 0x17E8,

            Entry:
            {
                SenderGuid          : 0x0000,
                SenderName          : 0x0034,
                FullMessage         : 0x0065,
                OnlyMessage         : 0x0C1D,
                ChannelNum          : 0x17D8,
                TimeStamp           : 0x17E4
            }
        }
    },

    Offsets64:
    {
        GameHash                    : 0x64C90819,
        IconHash                    : 0xA118EC28,
        GameBuild                   : 0x0F415FC,
        GameState                   : 0x1519A7E,

        LocalPlayer                 : 0x147E680,
        LocalCont                   : 0x124B40C,
        LocalZone                   : 0x125F694,
        IsLooting                   : 0x158D1A4,
        IsTexting                   : 0x12CD4B0,
         MouseGuid                  : 0x151A0B8,
        TargetGuid                  : 0x151A0E8,

        Camera:
        {
            Struct                  : 0x151A520,
            Offset                  : 0x7768,
            Origin                  : 0x10,
            Matrix                  : 0x1C,
            Fov                     : 0x40
        },

        Entity:
        {
            TableBase               : 0x135D120,
            EntryFirst              : 0x18,
            EntryNext               : 0x68,

            Entry:
            {
                Type                : 0x18,
                Descriptors         : 0x08,

                Desc:
                {
                    GlobalID        : 0x00,
                    EntityID        : 0x24,
                    DynFlags        : 0x28
                }
            },

            Unit:
            {
                Transport           : 0x1538,
                Origin              : 0x1548,
                Angle               : 0x1558,
                Casting             : 0x1B98,
                CastingStarted      : 0x1BB0,
                CastingWillEnd      : 0x1BB4,
                Channel             : 0x1BB8,
                ChannelStarted      : 0x1BBC,
                ChannelWillEnd      : 0x1BC0,

                Aura:
                {
                    Count1          : 0x2390,
                    Count2          : 0x1D10,
                    TableBase1      : 0x1D10,
                    TableBase2      : 0x1D18,
                    EntrySize       : 0x68,

                    Entry:
                    {
                        Owner       : 0x40,
                        SpellID     : 0x50,
                        Flags       : 0x58,
                        Stacks      : 0x59,
                        Level       : 0x5A,
                        EndTime     : 0x60
                    }
                },

                Desc:
                {
                    Creator         : 0x080,
                    Health          : 0x0F0,
                    Power           : 0x0F4,
                    HealthMax       : 0x10C,
                     PowerMax       : 0x110,
                    Level           : 0x158,
                    Flags           : 0x17C
                }
            },

            NPC:
            {
                Name1               : 0x16F0,
                Name2               : 0x00A0
            },

            Player:
            {
                Money1              : 0x2790,
                Money2              : 0x1890,
                Arch                : 0x2798,
                ArchCount           : 0x08,
                ArchSites           : 0x18,
                Target              : 0x77E8,
            },

            Object:
            {
                Bobbing             : 0x1E0,
                Transport           : 0x238,
                Origin              : 0x248,
                Rotation            : 0x258,
                Transform           : 0x4A0,
                Name1               : 0x498,
                Name2               : 0x0D8,

                Desc:
                {
                    Creator         : 0x030,
                    Display         : 0x040
                }
            }
        },

        NameCache:
        {
            TableBase               : 0x1316E98,
            EntryNext               : 0x00,

            Entry:
            {
                Guid                : 0x20,
                Name                : 0x31,
                Race                : 0x88,
                Class               : 0x90
            }
        },

        Cooldown:
        {
            TableBase               : 0x1354D50,
            EntryNext               : 0x08,

            Entry:
            {
                SpellID             : 0x10,
                 ItemID             : 0x14,
                SpellStartTime      : 0x18,
                SpellDuration       : 0x1C,
                GroupID             : 0x20,
                GroupStartTime      : 0x24,
                GroupDuration       : 0x28,
                IsActive            : 0x2C,
                GcdStartTime        : 0x30,
                GcdDuration         : 0x38
            }
        },

        BMAH:
        {
            Count                   : 0x15CE6E8,
            TableBase               : 0x15CE6F0,
            EntrySize               : 0xA8,

            Entry:
            {
                MarketID            : 0x00,
                  ItemID            : 0x08,
                MinimumBid          : 0x80,
                MaximumInc          : 0x88,
                CurrentBid          : 0x90,
                TimeLeft            : 0x98,
                BidCount            : 0xA0
            }
        },

        Chat:
        {
            Position                : 0x157627C,
            TableBase               : 0x151BD20,
            EntrySize               : 0x17F0,

            Entry:
            {
                SenderGuid          : 0x0000,
                SenderName          : 0x0034,
                FullMessage         : 0x0065,
                OnlyMessage         : 0x0C1D,
                ChannelNum          : 0x17D8,
                TimeStamp           : 0x17E8
            }
        }
    }
};
```

----------


## Torpedoes

*BMAH Monitor*
The first demo features a simple Black Market Auction House monitor. Assuming you set everything up correctly, just run the script "node bmah_monitor.js" (no quotes) and select your WoW window. Speaking with *Madam Goya* will update the game client, which in turn, prints the BMAH items to the console. This script has been tested on Windows 7 running Node 6.0.0 x64
*bmah_monitor.js*


```
"use strict";

//----------------------------------------------------------------------------//
// BMAH Monitor                                                               //
//----------------------------------------------------------------------------//

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    // Read BMAH count and table
    const count = memory.readInt32 (module + offsets.BMAH.Count    );
    const table = memory.readPtr   (module + offsets.BMAH.TableBase);

    // Make sure count and table are valid
    if (count <= 0 || table <= 0) return;

    let items = [ ];
    // Loop through all BMAH items
    for (let i = 0; i < count; ++i)
    {
        // Retrieve the current BMAH item offset in memory
        const slide = table + (offsets.BMAH.EntrySize * i);

        items.push
        ({
            marketID  : memory.readInt32 (slide + offsets.BMAH.Entry.MarketID  ),
              itemID  : memory.readInt32 (slide + offsets.BMAH.Entry.  ItemID  ),
            minimumBid: memory.readInt64 (slide + offsets.BMAH.Entry.MinimumBid),
            maximumInc: memory.readInt64 (slide + offsets.BMAH.Entry.MaximumInc),
            currentBid: memory.readInt64 (slide + offsets.BMAH.Entry.CurrentBid),
            timeLeft  : memory.readInt32 (slide + offsets.BMAH.Entry.TimeLeft  ),
            bidCount  : memory.readInt32 (slide + offsets.BMAH.Entry.BidCount  )
        });
    }

    // Cheap way to clear the screen
    process.stdout.write ("\x1Bc");

    // Print each item
    items.map (item =>
    {
        // TIP: ItemID is an item so you can use the WowAPI
        // or WowHead to retrieve more information about it

        console.log (`Item=${item.itemID} Bid=${item.currentBid /
            10000} Bids=${item.bidCount} Time=${item.timeLeft}`);
    });
});
```

----------


## kyperbelt

damn this is cool stuff. Been working on a project (in java) with the goal of making it easy to run scripts for automation purposes. Mind if i port some of your stuff on to my project @ a later date?

man i just looked up the robot api its so good lol , my project seems a bit pointless now.. I guess ill still post it.

----------


## Torpedoes

> Mind if i port some of your stuff on to my project?


Feel free to port Robot to any platform you wish, and help spread the word :-D

----------


## kyperbelt

> Feel free to port Robot to any platform you wish, and help spread the word :-D


i just meant porting your specific scripts in particular, But might take a deeper look into robotjs. Thanks for the great post  :Smile:

----------


## CrimeTime

looks pretty neat, will defently look at this!
Thanks  :Wink:

----------


## Torpedoes

*Chat Monitor*
The second demo features a simple chat monitor. Assuming you set everything up correctly, just run the script "node chat_monitor.js" (no quotes) and select your WoW window. If you're in a particularly chatty area of the game, everything said will be printed to the console. The script compensates for missed texts as well. This script has been tested on Windows 7 running Node 6.0.0 x64
*chat_monitor.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Chat Monitor                                                               //
//----------------------------------------------------------------------------//

// Old chat position
let position = null;
// Used to retrieve player guid
let guidBuff = new Buffer (16);

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    // Read the new position of the chat
    const newPosition = memory.readInt32
        (module + offsets.Chat.Position);

    // Make sure the position is reasonable
    if (newPosition < 0 || newPosition > 60)
        return;

    if (position === null)
    {
        // First time this is being run
        position = newPosition; return;
    }

    let newMessages = [ ];
    // Read any new incoming messages
    while (position !== newPosition)
    {
        if (++position === 60) position = 0;
        // Get the current message offset in memory
        const slide = module + offsets.Chat.TableBase +
            (offsets.Chat.EntrySize * (position - 1));

        newMessages.push
        ({
            senderGuid : memory.readData (slide + offsets.Chat.Entry.SenderGuid, guidBuff, 16) ?
                                          guidBuff.toString ("hex").toUpperCase() : "",

            senderName : memory.readString (slide + offsets.Chat.Entry.SenderName,  40  ),
            fullMessage: memory.readString (slide + offsets.Chat.Entry.FullMessage, 3000),
            onlyMessage: memory.readString (slide + offsets.Chat.Entry.OnlyMessage, 3000),

            channelNum : memory.readInt32  (slide + offsets.Chat.Entry.ChannelNum),
            timeStamp  : memory.readInt32  (slide + offsets.Chat.Entry.TimeStamp )
        });
    }

    // Process each new message
    newMessages.map (message =>
    {
        // Convert the Unix timestamp into a normal Date
        const date = new Date (message.timeStamp * 1000);
        const hrs  = `0${date.getHours  ()}`.substr (-2);
        const min  = `0${date.getMinutes()}`.substr (-2);
        const sec  = `0${date.getSeconds()}`.substr (-2);

        console.log (`[${hrs}:${min}:${sec}] [${message.senderName}]: ${message.onlyMessage}`);
    });
});
```

----------


## karliky

Hey Torpedoes,

Thank you for putting all the effort of building a library like this, it's really impressive. I've tried to do it by myself for over the years but my lack of knowledge on macosx and linux made it impossible to achieve.

It's great to see finally al real multiplatform library for manage processes. Back in the past I started a simple library with c++ <--> js bindings to the windows kernel using Node.js in order to manage processes trough node js, you can see it here:

https://github.com/karliky/CrazinessJS

And this is my intent of improve CrazinessJS with Blackbone (another excelent memory managment library but not crossplatform)
https://github.com/karliky/CrazinessJS/tree/dev

Here you can see some of the tools that I've made with CrazinessJS and node.js:

https://www.youtube.com/watch?v=0ZTYTyp3Tuw
https://www.youtube.com/watch?v=2fklKtVjfPw
https://www.youtube.com/watch?v=AVkWeHqhPwo
https://www.youtube.com/watch?v=fjrlAJumH1s
https://www.youtube.com/watch?v=r8BAlc5fS0g

I'll definitely use your library and I'll try to contribute to robot-js.

Once again, amazing work!

Cheers

----------


## Torpedoes

> In the past I started a simple library with c++ <--> js bindings to the windows kernel using Node.js in order to manage processes trough node js, you can see it here:
> 
> https://github.com/karliky/CrazinessJS


Nice! I'm glad I'm not the only one exploring this technology. But I'm guessing from the source that you only support Node 0.10 since the API changed so drastically in 0.12.




> And this is my intent of improve CrazinessJS with Blackbone (another excelent memory managment library but not crossplatform)
> https://github.com/karliky/CrazinessJS/tree/dev


Yes, I actually used BlackBone, among other libraries, for inspiration.




> Here you can see some of the tools that I've made with CrazinessJS and node.js:
> 
> https://www.youtube.com/watch?v=0ZTYTyp3Tuw
> https://www.youtube.com/watch?v=2fklKtVjfPw
> https://www.youtube.com/watch?v=AVkWeHqhPwo
> https://www.youtube.com/watch?v=fjrlAJumH1s
> https://www.youtube.com/watch?v=r8BAlc5fS0g


Those are really cool videos man. Keep up the great work!




> I'll definitely use your library and I'll try to contribute to robot-js.
> 
> Once again, amazing work!


Thanks for your support!

I would love nothing more than to see people using my library. I don't know if people realize this but Robot is the library that's been powering Yeti-Bots for three years now.

For contributing, keep in mind that robot-js is just a port of robot. So for new features, be sure to implement it there. The robot-js version will naturally be ported over. For more information about contributing, be sure to check out this guide!

----------


## IChangedMyUsername

Very cool. Thanks for the posts

----------


## ev0

Whelp, I know what i'm doing this weekend. This is an amazing release. I can't wait to fiddle

----------


## Torpedoes

*Entity Dumper*
The third demo features a small entity dumper. Assuming you set everything up correctly, just run the script "node entity_dumper.js" (no quotes) and select your WoW window. Standing in a moderately populated area will print the locations of all NPCs, Players and Objects. This script has been tested on Windows 7 running Node 6.0.0 x64
*entity_dumper.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Entity Dumper                                                              //
//----------------------------------------------------------------------------//

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    let entry = memory.readPtr
        // Retrieve the entity list manager
        (module + offsets.Entity.TableBase);

    // Validate the pointer
    if (entry <= 0) return;

    entry = memory.readPtr
        // Retrieve the first entity entry
        (entry + offsets.Entity.EntryFirst);

    let entities = {
        npcs   : [ ],
        players: [ ],
        objects: [ ]
    };
    let repeater = { };
    let infinite = 0;

    // Read all entries from first to last
    while (entry > 0 && (entry & 1) === 0)
    {
        // Avoid repetition of entries
         if (repeater[entry]) break;
        else repeater[entry] = true;

        // Avoid possible infinite loop
        if (++infinite >= 20000) break;

        // Retrieve type and descriptor
        const type = memory.readInt32 (entry + offsets.Entity.Entry.Type       );
        const desc = memory.readPtr   (entry + offsets.Entity.Entry.Descriptors);

        if (desc > 0)
        {
            type === 3 && entities.npcs.push
            ({
                entry,
                x: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 0),
                y: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 4),
                z: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 8)
            });

            type === 4 && entities.players.push
            ({
                entry,
                x: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 0),
                y: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 4),
                z: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 8)
            });

            type === 5 && entities.objects.push
            ({
                entry,
                x: memory.readReal32 (entry + offsets.Entity.Object.Origin + 0),
                y: memory.readReal32 (entry + offsets.Entity.Object.Origin + 4),
                z: memory.readReal32 (entry + offsets.Entity.Object.Origin + 8)
            });
        }

        // Read the next entry in table
        entry = memory.readPtr (entry +
            offsets.Entity.EntryNext);
    }

    for (let e in entities)
    {
        console.log (`\n${e}`);
        // Print grouped entities
        entities[e].map (entity =>
        {
            console.log (`entry=${entity.entry.toString (16).toUpperCase()} x=${entity.x.toFixed (2)} y=${entity.y.toFixed (2)} z=${entity.z.toFixed (2)}`);
        });
    }

    // All finished
    process.exit();
});
```

----------


## karliky

Hey Torpedoes,

I've made an HTML5 desktop app, very simple, in order to show to the users of robot-js how to render information in the desktop instead of just the terminal using console.log.

Here is an example of the app, it shows the chat messages using your script of above, I had to modify it to emit websockets everytime it processes a message:



The repo is here:
https://github.com/karliky/robotjs-desktop-wow

How to use it:

- clone de repo in local or download it as a zip https://github.com/karliky/robotjs-d...ive/master.zip
- cd into the directory and then execute: "$ npm install"
- open a terminal, execute "$ node examples\chat_monitor.js"
- open a terminal, execute "$ node ./node_modules/.bin/electron ."

You should see the chat messages in the desktop app.

Will work on improving this soon.

Great work with robot-js mate!

----------


## Torpedoes

> I've made an HTML5 desktop app, very simple, in order to show to the users of robot-js how to render information in the desktop instead of just the terminal using console.log!


Super cool!! I'll have to check it out but I look forward to seeing more projects like this :-D

----------


## Torpedoes

*Aura Monitor*
The fourth demo features an aura monitor. Assuming you set everything up correctly, just run the script "node aura_monitor.js" (no quotes) and select your WoW window. Any buffs and debuffs your character has will be displayed in the console. As a challenge, try to make this work with other players and NPCs. It should be easy if you combine this code with the code from the third demo. This script has been tested on Windows 7 running Node 6.0.0 x64
*aura_monitor.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Aura Monitor                                                               //
//----------------------------------------------------------------------------//

// Used to retrieve player guid
let guidBuff = new Buffer (16);
// We need timer to calculate aura times
const Timer = require ("robot-js").Timer;

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;
    const auraOff = offsets.Entity.Unit.Aura;

    const player = memory.readPtr
        // Retrieve the player pointer
        (module + offsets.LocalPlayer);

    // Validate the pointer
    if (player <= 0) return;

    // When less than 16 Auras, they are stored in an array
    let count = memory.readInt32 (player + auraOff.Count1);
    let table =                   player + auraOff.TableBase1;

    // Using the heap
    if (count === -1)
    {
        // The auras were dynamically allocated on the heap
        count = memory.readInt32 (player + auraOff.Count2);
        table = memory.readPtr   (player + auraOff.TableBase2);
    }

    // Make sure count and table are valid
    if (count <= 0 || table <= 0) return;

    let auras = [ ];
    // Loop through all aura entries
    for (let i = 0; i < count; ++i)
    {
        // Get the current aura entry offset in memory
        const slide = table + (auraOff.EntrySize * i);

        // Skip the current entry if the aura spell ID is zero
        if (!memory.readInt32 (slide + auraOff.Entry.SpellID))
            continue;

        auras.push
        ({
            owner : memory.readData (slide + auraOff.Entry.Owner, guidBuff, 16) ?
                                     guidBuff.toString ("hex").toUpperCase() : "",

            spellID: memory.readInt32 (slide + auraOff.Entry.SpellID),
            flags  : memory.readInt8  (slide + auraOff.Entry.Flags  ),
            stacks : memory.readInt8  (slide + auraOff.Entry.Stacks ),
            level  : memory.readInt8  (slide + auraOff.Entry.Level  ),
            endTime: memory.readInt32 (slide + auraOff.Entry.EndTime)
        });
    }

    // Cheap way to clear the screen
    process.stdout.write ("\x1Bc");

    // Retrieve the current Cpu Time
    const now = Timer.getCpuTime();

    // Print each aura
    auras.map (aura =>
    {
        // TIP: SpellID is a spell so you can use the WowAPI
        // or WowHead to retrieve more information about it

        // Convert WoW's time using the current time
        const timeLeft = (aura.endTime - now) / 1000;
        console.log (`Spell=${aura.spellID} Flags=${aura.flags} Stack=${
                aura.stacks} TimeLeft=${timeLeft > 0 ? timeLeft : 0}s`);
    });
});
```

----------


## lolp1

Very neat read for some one who has never used JavaScript. I'd like to point out that C# not entirely tied down to any one platform. There are real strives to make it easy to do cross-platform. In both C# and F# exist some interesting well implemented choices for native cross-platform code.

----------


## Torpedoes

*Cooldown Monitor*
The fifth demo features a cooldown monitor. Assuming you set everything up correctly, just run the script "node cooldown_monitor.js" (no quotes) and select your WoW window. Any spells currently on cooldown will be displayed in the console. Spells in the same spell category will not show up twice but rather only spells you've casted will show up. This means that if you used a PvP trinket, it will be put on cooldown but keep in mind that Every Man for Himself will also on cooldown. This script has been tested on Windows 7 running Node 6.0.0 x64
*cooldown_monitor.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Cooldown Monitor                                                           //
//----------------------------------------------------------------------------//

// We need timer to calculate aura times
const Timer = require ("robot-js").Timer;

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    let entry = memory.readPtr
        // Retrieve the first cooldown entry
        (module + offsets.Cooldown.TableBase);

    let cooldowns = [ ];
    let repeater  = { };
    let infinite  = 0;

    // Read all entries from first to last
    while (entry > 0 && (entry & 1) === 0)
    {
        // Avoid repetition of entries
         if (repeater[entry]) break;
        else repeater[entry] = true;

        // Avoid possible infinite loop
        if (++infinite >= 20000) break;

        cooldowns.push
        ({
            spellID       : memory.readInt32 (entry + offsets.Cooldown.Entry.SpellID       ),
             itemID       : memory.readInt32 (entry + offsets.Cooldown.Entry. ItemID       ),
            spellStartTime: memory.readInt32 (entry + offsets.Cooldown.Entry.SpellStartTime),
            spellDuration : memory.readInt32 (entry + offsets.Cooldown.Entry.SpellDuration ),
            groupID       : memory.readInt32 (entry + offsets.Cooldown.Entry.GroupID       ),
            groupStartTime: memory.readInt32 (entry + offsets.Cooldown.Entry.GroupStartTime),
            groupDuration : memory.readInt32 (entry + offsets.Cooldown.Entry.GroupDuration ),
            isActive      : memory.readBool  (entry + offsets.Cooldown.Entry.IsActive      ),
            gcdStartTime  : memory.readInt32 (entry + offsets.Cooldown.Entry.GcdStartTime  ),
            gcdDuration   : memory.readInt32 (entry + offsets.Cooldown.Entry.GcdDuration   )
        });

        // Read the next entry in table
        entry = memory.readPtr (entry +
            offsets.Cooldown.EntryNext);
    }

    // Cheap way to clear the screen
    process.stdout.write ("\x1Bc");

    // Retrieve the current Cpu Time
    const now = Timer.getCpuTime();

    // Print each cooldown
    cooldowns.map (cd =>
    {
        // TIP: More information about SpellID and ItemID
        // can be retrieved through the WowAPI or WowHead

        const endGCD  = cd.gcdStartTime + cd.gcdDuration;
        const endTime = (cd.spellStartTime || cd.groupStartTime) +
                        (cd.spellDuration  || cd.groupDuration );

        const remGCD  = endGCD  - now;
        const remTime = endTime - now;

        console.log (`Spell=${cd.spellID} Item=${cd.itemID} RemGCD=${remGCD > 0 ? remGCD /
        1000 : 0}s RemTime=${remTime > 0 ? remTime / 1000 : 0}s IsActive=${cd.isActive}`);

        // WARNING: This algorithm is not completely accurate and
        // may return incorrect results during certain conditions
    });
});
```

----------


## Torpedoes

> I'd like to point out that C# not entirely tied down to any one platform. There are real strives to make it easy to do cross-platform. In both C# and F# exist some interesting well implemented choices for native cross-platform code.


It's not tied down you're right but it's also not something officially supported by Microsoft. It's something the community is supporting, similarly to how Wine can run Windows applications on Linux. I also think that C# is only supported as a language, the WIN32 APIs are not ported over but rather rewritten to use whatever system calls are required on the target platform. If Robot ever get's ported to C# I don't think we'll see cross-platform support out of the box, but I could be wrong.

----------


## Torpedoes

*Target Monitor*
The sixth demo features a target monitor. Assuming you set everything up correctly, just run the script "node target_monitor.js" (no quotes) and select your WoW window. The script will print the GUID's of all players and their targets. It will also print the GUID's of all players targeting you. As far as I know, this does not support NPC's. This script has been tested on Windows 7 running Node 6.0.0 x64
*target_monitor.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Target Monitor                                                             //
//----------------------------------------------------------------------------//

// Used to retrieve player guid
let guidBuff = new Buffer (16);
// We need timer to calculate aura times
const Timer = require ("robot-js").Timer;

// Calculate time left and percentage info
function calculateCastTimes (player, now)
{
    const casting = player.casting || player.channel;
    const castingStarted = player.castingStarted || player.channelStarted;
    const castingWillEnd = player.castingWillEnd || player.channelWillEnd;

    let timeleft = 0;
    let percent  = 0;

    if (casting > 0 &&
        castingStarted > 0 && castingWillEnd > 0)
    {
        timeleft = (castingWillEnd - now) / 1000;
        percent  = (now - castingStarted) /
            (castingWillEnd - castingStarted) * 100;

        if (timeleft <   0) timeleft = 0;
        if (percent  <   0) percent  = 0;
        if (percent  > 100) percent  = 100;
    }

    return { casting, timeleft, percent };
}

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    const player = memory.readPtr
        // Retrieve the player pointer
        (module + offsets.LocalPlayer);

    let entry = memory.readPtr
        // Retrieve the entity list manager
        (module + offsets.Entity.TableBase);

    // Make sure player and entry are valid
    if (player <= 0 || entry <= 0) return;

    entry = memory.readPtr
        // Retrieve the first entity entry
        (entry + offsets.Entity.EntryFirst);

    let players   = [ ];
    let localGuid = "";
    let repeater  = { };
    let infinite  = 0;

    // Read all entries from first to last
    while (entry > 0 && (entry & 1) === 0)
    {
        // Avoid repetition of entries
         if (repeater[entry]) break;
        else repeater[entry] = true;

        // Avoid possible infinite loop
        if (++infinite >= 20000) break;

        // Retrieve type and descriptor
        const type = memory.readInt32 (entry + offsets.Entity.Entry.Type       );
        const desc = memory.readPtr   (entry + offsets.Entity.Entry.Descriptors);

        // Scan only player entities
        if (desc > 0 && type === 4)
        {
            const guid   = memory.readData (desc +
                offsets.Entity.Entry.Desc.GlobalID, guidBuff, 16) ?
                guidBuff.toString ("hex").toUpperCase() : "";

            const target = memory.readData (entry +
                offsets.Entity.Player.Target, guidBuff, 16) ?
                guidBuff.toString ("hex").toUpperCase() : "";

            // Store local GUID
            if (entry === player)
                localGuid = guid;

            players.push
            ({
                guid, target,
                casting       : memory.readInt32 (entry + offsets.Entity.Unit.Casting       ),
                castingStarted: memory.readInt32 (entry + offsets.Entity.Unit.CastingStarted),
                castingWillEnd: memory.readInt32 (entry + offsets.Entity.Unit.CastingWillEnd),
                channel       : memory.readInt32 (entry + offsets.Entity.Unit.Channel       ),
                channelStarted: memory.readInt32 (entry + offsets.Entity.Unit.ChannelStarted),
                channelWillEnd: memory.readInt32 (entry + offsets.Entity.Unit.ChannelWillEnd)
            });
        }

        // Read the next entry in table
        entry = memory.readPtr (entry +
            offsets.Entity.EntryNext);
    }

    // Cheap way to clear the screen
    process.stdout.write ("\x1Bc");

    // Retrieve the current Cpu Time
    const now = Timer.getCpuTime();

    console.log ("Targeting Me");
    // Print each player
    players.map (player =>
    {
        if (player.target === localGuid)
        {
            let times = calculateCastTimes (player, now);
            console.log (`${player.guid} Casting=${times.casting} TimeLeft=${
                        times.timeleft} Percent=${times.percent.toFixed (2)}`);
        }
    });

    console.log ("\nTargeting");
    // Print each player
    players.map (player =>
    {
        let times = calculateCastTimes (player, now);
        console.log (`${player.guid} -> ${player.target} Casting=${times.casting
            } TimeLeft=${times.timeleft} Percent=${times.percent.toFixed (2)}`);
    });
});
```

----------


## Torpedoes

*Player Names*
The seventh demo features a player name extractor. Assuming you set everything up correctly, just run the script "node player_names.js" (no quotes) and select your WoW window. The script will print out the name, race and class of all the players currently cached by the game. This information can be combined with the entity reader to better identify players. This script has been tested on Windows 7 running Node 6.0.0 x64
*player_names.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Constants                                                                  //
//----------------------------------------------------------------------------//

////////////////////////////////////////////////////////////////////////////////
/// Player race enumeration constants

const PlayerRace =
{
    0   : "None",
    1   : "Human",
    2   : "Orc",
    3   : "Dwarf",
    4   : "NightElf",
    5   : "Undead",
    6   : "Tauren",
    7   : "Gnome",
    8   : "Troll",
    9   : "Goblin",
    10  : "BloodElf",
    11  : "Draenei",
    22  : "Worgen",
    24  : "PandarenN",
    25  : "PandarenA",
    26  : "PandarenH"
};

////////////////////////////////////////////////////////////////////////////////
/// Player class enumeration constants

const PlayerClass =
{
    0   : "None",
    1   : "Warrior",
    2   : "Paladin",
    3   : "Hunter",
    4   : "Rogue",
    5   : "Priest",
    6   : "DeathKnight",
    7   : "Shaman",
    8   : "Mage",
    9   : "Warlock",
    10  : "Monk",
    11  : "Druid"
};



//----------------------------------------------------------------------------//
// Player Names                                                               //
//----------------------------------------------------------------------------//

// Used to retrieve player guid
let guidBuff = new Buffer (16);

// Create application controller
new (require ("./app_controller"))
    (250, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    let entry = memory.readPtr
        // Retrieve the first name cache entry
        (module + offsets.NameCache.TableBase);

    let names    = [ ];
    let repeater = { };
    let infinite = 0;

    // Read all entries from first to last
    while (entry > 0 && (entry & 1) === 0)
    {
        // Avoid repetition of entries
         if (repeater[entry]) break;
        else repeater[entry] = true;

        // Avoid possible infinite loop
        if (++infinite >= 20000) break;

        names.push
        ({
            guid : memory.readData (entry + offsets.NameCache.Entry.Guid, guidBuff, 16) ?
                                    guidBuff.toString ("hex").toUpperCase() : "",

            name : memory.readString (entry + offsets.NameCache.Entry.Name, 80),
            race : memory.readInt32  (entry + offsets.NameCache.Entry.Race    ),
            clazz: memory.readInt32  (entry + offsets.NameCache.Entry.Class   )
        });

        // Read the next entry in table
        entry = memory.readPtr (entry +
            offsets.NameCache.EntryNext);
    }

    // Print each name
    names.map (name =>
    {
        const race  = PlayerRace [name.race ] || "";
        const clazz = PlayerClass[name.clazz] || "";
        console.log (`Name=${name.name} Race=${race} Class=${clazz}`);
    });

    // All finished
    process.exit();
});
```

----------


## lolp1

> It's not tied down you're right but it's also not something officially supported by Microsoft. It's something the community is supporting, similarly to how Wine can run Windows applications on Linux. I also think that C# is only supported as a language, the WIN32 APIs are not ported over but rather rewritten to use whatever system calls are required on the target platform. If Robot ever get's ported to C# I don't think we'll see cross-platform support out of the box, but I could be wrong.


Microsoft is starting to support it themselves, check out xamarin which is partly open source and included as standard Microsoft visual studio project templates.

----------


## Torpedoes

> Microsoft is starting to support it themselves, check out xamarin which is partly open source and included as standard Microsoft visual studio project templates.


It's been a while since I touched C# and perhaps you are right and the cross-platform capabilities of the framework have improved. But it still doesn't solve the fact that it requires a runtime in order to operate. Similar to Java. Though what am I saying, so does robot-js :-P But anyways, despite all this, I'll probably investigate this further when I port robot to C#.

----------


## Torpedoes

*Fishing Bot*
The eighth and final demo features a very simple fishing bot. Assuming you set everything up correctly, just run the script "node fishing_bot.js" (no quotes) and select your WoW window. Standing next to a water source will make your character fish, just make sure to configure your cast key to "0". Catching the fish is handled by writing to the MouseOver GUID in memory and pressing the interact with mouse over key, configured to "~". Full background mode fishing is not yet supported by robot-js. Please remember that this is just a demo, use a more complete bot like Wild-Catch for more advanced functionality. This script has been tested on Windows 7 running Node 6.0.0 x64
*fishing_bot.js*


```
"use strict";

//----------------------------------------------------------------------------//
// Fishing Bot                                                                //
//----------------------------------------------------------------------------//

// Used to retrieve player guid
let guidBuff = new Buffer (16);
// We need a keyboard to cast the fishing pole
const Keyboard = require ("robot-js").Keyboard();

// Create application controller
new (require ("./app_controller"))
    (500, function (gameInstance)
{
    // Some shortcuts to update the game
    const memory  = gameInstance.memory;
    const module  = gameInstance.module;
    const offsets = gameInstance.offsets;

    const player = memory.readPtr
        // Retrieve the player pointer
        (module + offsets.LocalPlayer);

    let entry = memory.readPtr
        // Retrieve the entity list manager
        (module + offsets.Entity.TableBase);

    // Make sure player and entry are valid
    if (player <= 0 || entry <= 0) return;

    entry = memory.readPtr
        // Retrieve the first entity entry
        (entry + offsets.Entity.EntryFirst);

    let lPlayer  = { };
    let bobbers  = [ ];
    let repeater = { };
    let infinite = 0;

    // Read all entries from first to last
    while (entry > 0 && (entry & 1) === 0)
    {
        // Avoid repetition of entries
         if (repeater[entry]) break;
        else repeater[entry] = true;

        // Avoid possible infinite loop
        if (++infinite >= 20000) break;

        // Retrieve type and descriptor
        const type = memory.readInt32 (entry + offsets.Entity.Entry.Type       );
        const desc = memory.readPtr   (entry + offsets.Entity.Entry.Descriptors);

        if (desc > 0)
        {
            // Check local player
            if (entry === player)
            {
                const guid = memory.readData (desc + offsets.Entity.Entry.Desc.GlobalID,
                             guidBuff, 16) ? guidBuff.toString ("hex").toUpperCase():"";

                const isLooting = memory.readBool  (module + offsets.IsLooting          );
                const isChannel = memory.readInt32 (entry  + offsets.Entity.Unit.Channel);

                lPlayer = { guid, isLooting, isChannel };
            }

            // Maybe bobber
            if (type === 5)
            {
                const guid = memory.readData (desc + offsets.Entity.Entry.Desc.GlobalID,
                             guidBuff, 16) ? guidBuff.toString ("hex").toUpperCase():"";

                const bobbing = memory.readBool (entry + offsets.Entity.Object.Bobbing);
                const creator = memory.readData (desc  + offsets.Entity.Object.Desc.Creator,
                                guidBuff, 16) ? guidBuff.toString ("hex").toUpperCase():"";

                const display = memory.readInt32 (desc + offsets.Entity.Object.Desc.Display);

                if (guid && display === 668) bobbers.push ({ guid, bobbing, creator });
            }
        }

        // Read the next entry in table
        entry = memory.readPtr (entry +
            offsets.Entity.EntryNext);
    }

    // Player is still looting fish
    if (lPlayer.isLooting) return;

    // Cast the fishing pole
    if (!lPlayer.isChannel)
        Keyboard.click ("0");

    // Check the bobbers
    bobbers.map (bobber =>
    {
        // Check if bobber belongs to the player and is bobbing
        if (bobber.creator === lPlayer.guid && bobber.bobbing)
        {
            // Pretend to mouse over the bobber
            guidBuff.fill (bobber.guid, "hex");
            memory.writeData (module + offsets
                    .MouseGuid, guidBuff, 16);

            // Interact with it
            Keyboard.click ("`");
        }
    });
});
```

----------


## Filint

> Microsoft is starting to support it themselves, check out xamarin which is partly open source and included as standard Microsoft visual studio project templates.


I do a lot of work with Xamarin, mainly on Linux. While not everything is supported, and some things don't work quite as they should, the progress is very encouraging and I look forward to seeing where things are a year or two from now. We've been able to really drive down costs by moving from Windows to Linux for a good deal of our .NET internal infrastructure.

@Torpedoes I'd love to see a C# port of robot. One of my favourite languages plus what seems to be a very well built and documented library would be a great combination, I think. The current JS implementation is interesting, too, and I'm playing around with it at the moment.

----------


## Torpedoes

*CONCLUSION*
And with that comes the end of our little journey. I hope that you found the robot-js library interesting enough to use in your own projects and I look forward to seeing what each and every one of you will be able to come up with. If you're interested in contributing to the project itself, be sure to check out the contributing guide on the project website. Please remember that this code will *not* be kept up to date but you are free to use and update it as time goes by.

----------


## ~Jagris

Good code, and great post. Thanks for the contribution.

----------


## karliky

I had to read a multi level ptr and I did it this way:



```
  /**
   * Resolves the given multi level pointer to
   * the correct offset of the memory
   */
  const readMultiLevelPtr = function(offsets) {
    var address = module + offsets[0];
    var offset = 0;
    for (var i = 1; i < offsets.length; i++) {
      address = memory.readPtr(address);
      address += offsets[i];
    }
    return address;
  };
```

I use it like this:



```
const offsets = {
  camera: {
    ptr: [0x0175A124, 0x1d4, 0x114, 0x4, 0x2b0, 0x0]
  }
};
memory.readMultiLevelPtr(offsets.camera.ptr);
```

Is there a better way to do it?

thanks!

----------


## karliky

Just made this with Robot-js  :Big Grin:

----------


## carloxx

great work dude, im already playing around with it :-) i wanna make some kind of arena-rating tank bot (join arena, leave once player in combat)

btw: do you think it is possible to call lua functions / write to chat with robot (or do i need to inject a dll for that)?
i need:
*queue arena (lua function?)
*accept ready check (lua function?)
*accept qpop (lua function?)
*player.inCombat (read memory)
*leave arena (lua function?)

----------


## Corthezz

> great work dude, im already playing around with it :-) i wanna make some kind of arena-rating tank bot (join arena, leave once player in combat)
> 
> btw: do you think it is possible to call lua functions / write to chat with robot (or do i need to inject a dll for that)?
> i need:
> *queue arena (lua function?)
> *accept ready check (lua function?)
> *accept qpop (lua function?)
> *player.inCombat (read memory)
> *leave arena (lua function?)


You can always write the bytes for the asm instructions to memory which should work out. Example:
Allocate memory
Write bytes for your instructions to the allocated memory
Find EndScene in memory
Place a jmp at the beginning of it which leads to your code

Result: Your injected instructions will be executed without being injected.

----------


## WiNiFiX

@Torpedoes - what IDE do you recommend for Node.js, because I am testing with Visual Studio Code [code.visualstudio.com] and it works well (for 1 file) but as soon as you referencing another file and want to right click on a variable and say "Go To Definition" it fails and doesn't go there.
Coding without being able to quickly jumping with "go to definition" is painfully slow and annoying.

----------


## Torpedoes

> @Torpedoes - what IDE do you recommend for Node.js, because I am testing with Visual Studio Code [code.visualstudio.com] and it works well (for 1 file) but as soon as you referencing another file and want to right click on a variable and say "Go To Definition" it fails and doesn't go there.
> Coding without being able to quickly jumping with "go to definition" is painfully slow and annoying.


I personally use Visual Studio and Sublime Text. I don't really use code jumping or "go to definition" but maybe you'll find the IDE's useful, I know the community edition of Visual Studio is free and supports a lot of cool Node.js development semantics.

----------


## WiNiFiX

> I personally use Visual Studio and Sublime Text. I don't really use code jumping or "go to definition" but maybe you'll find the IDE's useful, I know the community edition of Visual Studio is free and supports a lot of cool Node.js development semantics.


Played around more with node its quite interesting, but hell of annoying initially, i couldn't find a bug in my code for 1 hour till I noticed i was missing '' around one of my requires 
I had var fs = require(fs); and it should have been var fs = require('fs');
Boy oh boy do they need to fix their error's to be friendly like VS :P

----------


## Torpedoes

> Played around more with node its quite interesting, but hell of annoying initially, i couldn't find a bug in my code for 1 hour till I noticed i was missing '' around one of my requires 
> I had var fs = require(fs); and it should have been var fs = require('fs');
> Boy oh boy do they need to fix their error's to be friendly like VS :P


Yes, I do agree that it's a bit tough right now to start out and even debug code. But once you get into it, you'll find that it's actually quite nice. I suggest checking out node-inspector for debugging code, though not usually fast, it'll help you figure out what's going on. Furthermore, nothing beats console.log ha ha. For me I'm just treating node as though I'm writing a GPU shader :-P

----------


## WiNiFiX

> Yes, I do agree that it's a bit tough right now to start out and even debug code. But once you get into it, you'll find that it's actually quite nice. I suggest checking out node-inspector for debugging code, though not usually fast, it'll help you figure out what's going on. Furthermore, nothing beats console.log ha ha. For me I'm just treating node as though I'm writing a GPU shader :-P


Where did you find your sources for memory reading / writing from node.js, because I can't find anything relating to it on the net, and your Memory.js doesn't explain much, like for starters what DLL (or whatever node calls it) is it using for memory reading.
I did notice you have a C++ app that seems to do the reading from the memory, however can't find any obvious link in the *.js code to the c++ code.

In c# its clear that we using Kernel32.dll



```

        [DllImport("kernel32.dll")]        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);        [DllImport("kernel32.dll")]        public static extern bool ReadProcessMemory(int hProcess, long lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);        [DllImport("kernel32.dll", SetLastError = true)]        [return: MarshalAs(UnmanagedType.Bool)]        public static extern bool WriteProcessMemory(int hProcess, long lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten); 


```

----------


## Torpedoes

> Where did you find your sources for memory reading / writing from node.js, because I can't find anything relating to it on the net, and your Memory.js doesn't explain much, like for starters what DLL (or whatever node calls it) is it using for memory reading.
> I did notice you have a C++ app that seems to do the reading from the memory, however can't find any obvious link in the *.js code to the c++ code


Hey, robot-js is just a wrapper around robot. In Node.js, native libraries are linked through external ".node" files which are imported via "require". On windows, these ".node" files are just DLL plugins. In this case, I provide precompiled versions for people to use. The appropriate version is automatically downloaded during installation. The "link" to the C++ code you're talking about is here.

For the native implementation of the memory class, see Memory.h and Memory.cc. Memory manipulation goes through several steps but ultimately ends up here, with the ReadProcessMemory system call. I hope this answers your question.

----------


## getrektucker

Hey, this looks pretty neat. I'm new to game hacking (not programming in general though) so forgive me if this is a dumb question. Regarding fishing_bot.js: why do you go through the process of setting up a mouse over macro with a keyboard press "~" instead of simply clicking the mouse (as a mouse click is available via your API anyways)?

----------


## Torpedoes

> Hey, this looks pretty neat. I'm new to game hacking (not programming in general though) so forgive me if this is a dumb question. Regarding fishing_bot.js: why do you go through the process of setting up a mouse over macro with a keyboard press "~" instead of simply clicking the mouse (as a mouse click is available via your API anyways)?


Does clicking the mouse work?

----------


## getrektucker

I don't know, I havn't run the code. I was just looking at the code examples and reading some stuff on the API. Just seemed weird to simulate a mouse click using the keyboard when there is a mouse click available in the API. Wasn't sure if there was a specific reason for that or not.

----------


## Torpedoes

> I don't know, I havn't run the code. I was just looking at the code examples and reading some stuff on the API. Just seemed weird to simulate a mouse click using the keyboard when there is a mouse click available in the API. Wasn't sure if there was a specific reason for that or not.


Honestly, it's because I didn't think of doing that :-P

These were mostly just for demo purposes only, the interact with mouse-over thing came from Wild-Catch, where that particular method is required to make background mode work. But in this instance, it may very well be unnecessary.

----------


## WiNiFiX

> Honestly, it's because I didn't think of doing that :-P
> 
> These were mostly just for demo purposes only, the interact with mouse-over thing came from Wild-Catch, where that particular method is required to make background mode work. But in this instance, it may very well be unnecessary.


Nope mouse clicking would not work because the code has no logic for world to screen and hence does not know where to click the mouse. The current code doesn't need this because he is setting the current target guid that the mouse is over to the fishing bobber and then using interact with target. If instead you clicked the mouse it would have to be over the bobber else there would be a new current target guid that overwrites the one you just set. Hopefully that makes sense I explained as well as I could .

----------


## Torpedoes

> Nope mouse clicking would not work because the code has no logic for world to screen and hence does not know where to click the mouse. The current code doesn't need this because he is setting the current target guid that the mouse is over to the fishing bobber and then using interact with target. If instead you clicked the mouse it would have to be over the bobber else there would be a new current target guid that overwrites the one you just set. Hopefully that makes sense I explained as well as I could .


Except I am setting the mouse over guid manually by writing to memory. Wouldn't simply right clicking the mouse work?

----------


## WiNiFiX

> Except I am setting the mouse over guid manually by writing to memory. Wouldn't simply right clicking the mouse work?


It will be nice if it works like that, but i doubt it, if i coded the system i would make it
1. Read the xy cords of a click
2. Find the mouse-over guid of the object im on
3. call interact with target.

Maybe blizz does it another way, just test it :P let us know, trial accounts are free and you know how to make your bot right click, my c# code doesnt work so well in it :P

----------


## Frosttall

> Except I am setting the mouse over guid manually by writing to memory. Wouldn't simply right clicking the mouse work?


Well working with mouse clicking to interact with objects makes you rely on the viewport.

You would have to take into account whether the object is hidden by another object in front (not speaking about the fishing bobber but in general) and if the camera is facing the object (outside of viewport). Working with mouse actions gets quiet nasty the more you are trying to use it.

----------


## WiNiFiX

But you a bad fish-botter if you position yourself incorrectly, you deserve to catch no fish.

Out of curiosity why doesn't blizz care if your write to the currenttargetGUID, surely that is something that should be picked up by warden, yet my bots have been doing it for years un-detected?

----------


## Frosttall

> But you a bad fish-botter if you position yourself incorrectly, you deserve to catch no fish.


Well my thoughts were rather long-term oriented and taking all use-cases into account instead of only fishing. Imagine you have to pick up objects on the floor - it would also require an interact with either TargetGUID and hotkey or mouse. So this problem would not only apply to fishing but to interaction with non-targetable objects in general.

----------


## WiNiFiX

Ye but then you have to do maths and geometry and trigonometry, thats confusing :P
But ye i know what you mean, i use lazy-raiders world2screen methods for all that LOS checking before clicks etc...

And kinda offtopic, but does Ownedcore have a discord channel?

----------


## Torpedoes

> Was confused for the longest time because there's a robotjs and a robot-js.


Yeah, sorry about that. I had initially reserved robot-js on NPM before I even knew about robotjs' existence. That and Robot itself had been in development for quite a while before then. Although Robot has only been recently released, it has existed for a quite while, in fact, version 1 is still being used to power the Yeti-Bots applications. As for robotjs, it has a lot of the same functionality Keyboard and Mouse has but it's not really geared towards bot development since it's missing a lot of features that robot-js has.




> A few recommendations, ARGB is too... difficult, to use. In the world of automation, a hex value of the color like 0xFFFFFF white would be much more user friendly. If you plan to stick with ARGB, perhaps provide a tool like AutoIt (AutoIt Window Info) that let's you extract PixelColor at mouse location and provide ARGB of it. Though many would prefer to provide a parameter value to get the hex value. As of right now I'm going to have to take the extra step to write a quick pixelcolor at mouse program, I think the website links to one provided as well, but gives the hex value instead.


Wait, what's the problem, I don't think I understand. Why is ARGB too difficult? And why are you using the color class, you can just manipulate the image data directly, as unsigned 32-bit integers. Here's an example:



```
const robot = require ("robot-js");

// Function to print numbers as hex
const printHex = v => console.log
	(v.toString (16).toUpperCase());

// If you need full-screen
// grabbing without params
robot.Screen.synchronize();

let image = robot.Image();
// Take screenshot of the screen
robot.Screen.grabScreen (image);

// Get the image pixel data
let data = image.getData();

// Get the last pixel in image
let n = image.getLength() - 1;

printHex (data[0]); // 0xAARRGGBB
printHex (data[1]); // 0xAARRGGBB
printHex (data[n]); // 0xAARRGGBB

printHex (data[0] & 0x00FFFFFF); // 0xRRGGBB
printHex (data[1] & 0x00FFFFFF); // 0xRRGGBB
printHex (data[n] & 0x00FFFFFF); // 0xRRGGBB
```

If you need to get the color at the position of the mouse, you could use a function like this:



```
const robot = require ("robot-js");

// Function to print numbers as hex
const printHex = v => console.log
	(v.toString (16).toUpperCase());

// Reuse same image instance
let image = robot.Image (1);
let data  = image.getData();

// Returns result as 0xRRGGBB
const getColorUnderMouse = () =>
{
	// Get the current mouse position
	const pos = robot.Mouse.getPos();

	// Get pixel color under mouse
	return robot.Screen.grabScreen
		(image, pos.x, pos.y, 1, 1) ?
		data[0] & 0x00FFFFFF : 0x0;
}

// Using GetPixel is slow because it has
// to make a native function call. Here,
// we're leveraging the fact that the
// image data buffers are reused so the
// data pointer will always be valid.

while (true) printHex (getColorUnderMouse());
```

I hope this has alleviated some of your concerns.

----------


## StinkyTwitch

How would I read NPC names out of the entry list? I keep trying:


```
name: memory.readString(entry + offsets.Entity.NPC.Name1, 80),
```

But getting strange results. Weird characters or no characters.

----------


## Torpedoes

> How would I read NPC names out of the entry list? I keep trying:
> 
> 
> ```
> name: memory.readString(entry + offsets.Entity.NPC.Name1, 80),
> ```
> 
> But getting strange results. Weird characters or no characters.


Hard to say but I'm pretty sure those examples still work without any changes or if there are changes, very minor ones to the memory offsets. You'd have to step through with a debugger and try and make sense of it. Use cheat engine to help you map out the data structures and make sure you're reading the correct values.

----------


## murmir

> Hard to say but I'm pretty sure those examples still work without any changes or if there are changes, very minor ones to the memory offsets. You'd have to step through with a debugger and try and make sense of it. Use cheat engine to help you map out the data structures and make sure you're reading the correct values.


Having the same issue, it shows a load of weird characters - on OSX this is, also doesn't find the window / process, without going the long way around, i.e get active window etc.

Could you provide a working OSX/Mac example please? Would be super helpful.  :Smile:

----------


## Torpedoes

> Having the same issue, it shows a load of weird characters - on OSX this is, also doesn't find the window / process, without going the long way around, i.e get active window etc.


Yeah OSX is a bit... different. I'm not sure how different though since I've never developed bots on that platform. But in theory, you should still be able to use the same principles from these examples to write a bot there. I'll try to write some examples when I have time but it might be a while.

----------


## drooky

+1 on a simple example on how to get it working on macOS / OSX.
That would be pretty awesome.

I was also wondering if this works for older versions of classic vanilla wow or only on current version?

Thanks for sharing!

----------


## Torpedoes

> I was also wondering if this works for older versions of classic vanilla wow or only on current version?


Some data structures are quite different (or non-existent) in vanilla. Luckily the vanilla client has been reversed extensively so I'm sure you could adapt portions of the code to run on that version.

----------


## tutrakan

> Some data structures are quite different (or non-existent) in vanilla. Luckily the vanilla client has been reversed extensively so I'm sure you could adapt portions of the code to run on that version.


I would like to take a look at this "extensively reversed vanilla client". Can I?

Edit:
I meaned .idb or .dbg. Sorry.

----------


## murmir

> Yeah OSX is a bit... different. I'm not sure how different though since I've never developed bots on that platform. But in theory, you should still be able to use the same principles from these examples to write a bot there. I'll try to write some examples when I have time but it might be a while.


Ok cool, thanks, i'm a well seasoned JS dev (can link my github profile via PM ;p), not being able to connect to the process is the only thing holding me back really at the moment. The selectByFindProcess & Window functions in the example weren't selecting as getList for window or proc was not finding WoW at all. When I mashed it in to select by active window, the readStrings were returning unreadable characters - the offsets were correct.

I'd be happy to contribute to the JS side and help others, I just need to get started reading from the process ha. PM if you wana screenshare my mac etc if you don't have mac/osx

EDIT: its all coming back to me now! The find by process wouldn't work if wow was launched before running node script, would show only if you run node script then wow - but only occasionally worked, was weird.

----------


## Torpedoes

> I would like to take a look at this "extensively reversed vanilla client". Can I?


I'm sure if you do a search online for 1.12.1 you'll find countless offsets and source code to reverse just about anything. For instance,  ([WoW] 1.12.1.5875 Info Dump Thread)here's one I found on OwnedCore.




> The selectByFindProcess & Window functions in the example weren't selecting as getList for window or proc was not finding WoW at all. When I mashed it in to select by active window, the readStrings were returning unreadable characters - the offsets were correct.


So the first thing that you must do is ensure you're running node with admin privileges. Next, start with the robot documentation and, more importantly, the node page. Play around with things like "robot.process.getList()" and then build a function similar to selectByFindProcess & window functions because those will be different on Mac due to the subtle differences. You will want to run with a debugger (or console.log) to find places where the code is failing.

----------


## murmir

> I'm sure if you do a search online for 1.12.1 you'll find countless offsets and source code to reverse just about anything. For instance,  ([WoW] 1.12.1.5875 Info Dump Thread)here's one I found on OwnedCore.
> 
> 
> 
> So the first thing that you must do is ensure you're running node with admin privileges. Next, start with the robot documentation and, more importantly, the node page. Play around with things like "robot.process.getList()" and then build a function similar to selectByFindProcess & window functions because those will be different on Mac due to the subtle differences. You will want to run with a debugger (or console.log) to find places where the code is failing.


Aye have done all that, am not a novice (am a node contributor), the problem is it can find the process fine,it just finds no windows when doing process.getWindows(), returns an empty array. findByProcess stops after trying to getWindows (because it doesn't find any windows) - and findByWindow fails because it also returns an empty array - the issue is with window finding always returning empty. AFAIK macs are limited to listing their own windows, or it only works using something like: Son of Grab

Would me spawning a process in node then using find by process window work there - wouldn't that be more risky as it's now a child of node? :confused:

----------


## Torpedoes

> Aye have done all that, am not a novice (am a node contributor), the problem is it can find the process fine,it just finds no windows when doing process.getWindows(), returns an empty array. findByProcess stops after trying to getWindows (because it doesn't find any windows) - and findByWindow fails because it also returns an empty array - the issue is with window finding always returning empty. AFAIK macs are limited to listing their own windows, or it only works using something like: Son of Grab
> 
> Would me spawning a process in node then using find by process window work there - wouldn't that be more risky as it's now a child of node? :confused:


Robot doesn't let you spawn processes yet (and doing it through node isn't exactly what you're looking for). I'm a bit surprised that it doesn't work for you. I haven't tested this on Sierra but El Capitan works fine. Perhaps I'll have to do further investigation.

----------


## murmir

> Robot doesn't let you spawn processes yet (and doing it through node isn't exactly what you're looking for). I'm a bit surprised that it doesn't work for you. I haven't tested this on Sierra but El Capitan works fine. Perhaps I'll have to do further investigation.


I was having the same issue before upgrading to Sierra - only upgraded to Sierra last night due to blizzard screwing up WoW on El Capitan (5 min load screens etc)

I can get around no window, spawning as a child_process in node kinda works - find by process now works, then I get modules, there's a ton of modules though not sure which one to use, tried the one named 'World of Warcraft' - then tried reading the game build but it returns an empty string, am using the latest 64bit offsets in your 7.1.5 thread, output below:



```
------------ MODULE -------------
modName World of Warcraft
modPath /Applications/World of Warcraft/World of Warcraft.app/Contents/MacOS/World of Warcraft
modBase 4294967296
modSize 0


------------ SEGMENTS -------------
name __PAGEZERO valid true segBase 0 segSize 4294967296
name __TEXT valid true segBase 4294967296 segSize 25460736
name __DATA valid true segBase 4320428032 segSize 8134656
name __LINKEDIT valid true segBase 4328562688 segSize 319488
containsAddress? 4311581900 false
GameBuild readString result:
```

Code for the above:


```
    console.log('');
    console.log('------------ MODULE -------------')
    console.log('modName', module.getName());
    console.log('modPath', module.getPath());
    console.log('modBase', module.getBase());
    console.log('modSize', module.getSize());
    console.log('');

    // Create a new memory object
    const memory = Memory(process);
    const offsets = Offsets.Offsets64;
    const segs = module.getSegments();

    console.log('');
    console.log('------------ SEGMENTS -------------')
    // Segs is an array
    segs.forEach(function (s) {
      console.log('name', s.name, 'valid', s.valid, 'segBase', s.base, 'segSize', s.size); // Load address
    });

    console.log('containsAddress?', module.getBase() + offsets.GameBuild, module.contains(module.getBase() + offsets.GameBuild));

    const build = memory.readString(module.getBase() + offsets.GameBuild, 6);

    console.log('GameBuild readString result:', build);
```

I have a feeling this is the wrong module, not sure which one to look at, there's several hundred:


```
World of Warcraft
AGL
AppleIntelHD5000GraphicsMTLDriver
GeForceMTLDriverWeb
AVFoundation
AVFAudio
Accelerate
vImage
libBLAS.dylib
libBNNS.dylib
libLAPACK.dylib
libLinearAlgebra.dylib
libQuadrature.dylib
libSparseBLAS.dylib
libvDSP.dylib
libvMisc.dylib
vecLib
AppKit
ApplicationServices
ATS
libFontParser.dylib
libFontRegistry.dylib
ColorSync
HIServices
LangAnalysis
PrintCore
QD
SpeechSynthesis
AudioToolbox
AudioUnit
CFNetwork
Carbon
CommonPanels
HIToolbox
Help
ImageCapture
Ink
OpenScripting
Print
SecurityHI
SpeechRecognition
Cocoa
CoreAudio
CoreBluetooth
CoreData
CoreDisplay
CoreFoundation
CoreGraphics
CoreImage
CoreMedia
CoreMediaIO
CoreServices
AE
CarbonCore
DictionaryServices
FSEvents
LaunchServices
Metadata
OSServices
SearchKit
SharedFileList
CoreText
CoreVideo
CoreWLAN
DiskArbitration
Foundation
GSS
IOBluetooth
IOKit
IOSurface
ImageIO
libGIF.dylib
libJP2.dylib
libJPEG.dylib
libPng.dylib
libRadiance.dylib
libTIFF.dylib
Kerberos
MediaAccessibility
MediaToolbox
Metal
NetFS
OpenCL
CFOpenDirectory
OpenDirectory
libCVMSPluginSupport.dylib
libCoreFSCache.dylib
libCoreVMClient.dylib
libGFXShared.dylib
libGL.dylib
libGLImage.dylib
libGLU.dylib
OpenGL
QuartzCore
ScriptingBridge
Security
SecurityFoundation
ServiceManagement
SystemConfiguration
VideoToolbox
AppContainer
AppSandbox
Apple80211
AppleFSCompression
AppleJPEG
AppleVA
Backup
ChunkingLibrary
CommonAuth
CoreAUC
CoreAVCHD
CoreEmoji
CoreServicesInternal
CoreSymbolication
CoreUI
CoreUtils
CoreWiFi
CrashReporterSupport
DataDetectorsCore
DebugSymbols
DesktopServicesPriv
FaceCore
libmetal_timestamp.dylib
GenerationalStorage
Heimdal
IOAccelerator
IOPresentment
IconServices
IntlPreferences
LanguageModeling
Mangrove
MetalPerformanceShaders
MultitouchSupport
NetAuth
PerformanceAnalysis
ProtocolBuffer
RemoteViewServices
SecCodeWrapper
Sharing
SkyLight
SpeechRecognitionCore
Symbolication
TCC
TextureIO
UIFoundation
XPCService
loginsupport
libCRFSuite.dylib
libChineseTokenizer.dylib
libDiagnosticMessagesClient.dylib
libFosl_dynamic.dylib
libMatch.1.dylib
libOpenScriptingUtil.dylib
libScreenReader.dylib
libSystem.B.dylib
libarchive.2.dylib
libate.dylib
libauto.dylib
libbsm.0.dylib
libbz2.1.0.dylib
libc++.1.dylib
libc++abi.dylib
libcmph.dylib
libcompression.dylib
libcoretls.dylib
libcoretls_cfhelpers.dylib
libcups.2.dylib
libdscsym.dylib
libenergytrace.dylib
libffi.dylib
libheimdal-asn1.dylib
libiconv.2.dylib
libicucore.A.dylib
liblangid.dylib
liblzma.5.dylib
libmarisa.dylib
libmecabra.dylib
libnetwork.dylib
libobjc.A.dylib
libpam.2.dylib
libpcap.A.dylib
libresolv.9.dylib
libsandbox.1.dylib
libspindump.dylib
libsqlite3.dylib
libxar.1.dylib
libxml2.2.dylib
libxslt.1.dylib
libz.1.dylib
libcache.dylib
libcommonCrypto.dylib
libcompiler_rt.dylib
libcopyfile.dylib
libcorecrypto.dylib
libdispatch.dylib
libdyld.dylib
libkeymgr.dylib
libkxld.dylib
liblaunch.dylib
libmacho.dylib
libquarantine.dylib
libremovefile.dylib
libsystem_asl.dylib
libsystem_blocks.dylib
libsystem_c.dylib
libsystem_configuration.dylib
libsystem_coreservices.dylib
libsystem_coretls.dylib
libsystem_dnssd.dylib
libsystem_info.dylib
libsystem_kernel.dylib
libsystem_m.dylib
libsystem_malloc.dylib
libsystem_network.dylib
libsystem_networkextension.dylib
libsystem_notify.dylib
libsystem_platform.dylib
libsystem_pthread.dylib
libsystem_sandbox.dylib
libsystem_secinit.dylib
libsystem_symptoms.dylib
libsystem_trace.dylib
libunwind.dylib
libxpc.dylib
```

----------


## Torpedoes

> I'm using the latest 64bit offsets in your 7.1.5 thread.


I hope you realize that those offsets are for Windows only. You'll have to reverse the client yourself to find out the OSX offsets.

----------


## murmir

> I hope you realize that those offsets are for Windows only. You'll have to reverse the client yourself to find out the OSX offsets.


I'm an idiot, sorry for wasting your time.

What would be the best way to get these, sorry for the questions ;p I'm trying Bit Slicer atm, and have searched for the build number and found several addresses with it etc, I feel like a noob here, welp

----------


## Torpedoes

> I'm an idiot, sorry for wasting your time.


You're not an idiot. We're all here to learn.

I personally use IDA to reverse engineer the client. But I know OSX has Hopper as well which I think is good also. Start by reverse engineering the Lua functions, I'm thinking the data structures should all be similar so the majority of your work will be finding the global offsets such as the object manager, camera, etc. You can use the windows client to see how different the code is compared to the OSX one. Similar applications to Cheat Engine are helpful in figuring out these offsets as the game is running.

----------


## para_

Can't figure out for the life of me why it won't attach to a vanilla client. I've tried selectByFindProcess() and selectByWindow(). selectByWindow() suggests that the process isn't valid. I'm still looking around but I thought maybe someone else had this issue and figure it out.

----------


## DrMight

> Can't figure out for the life of me why it won't attach to a vanilla client. I've tried selectByFindProcess() and selectByWindow(). selectByWindow() suggests that the process isn't valid. I'm still looking around but I thought maybe someone else had this issue and figure it out.


Trying to get it working with Vanilla client aswell, attempting to run chat_monitor.js.
It seems to stop at line 77 in game_instance.js, at 

if (!process.isValid()) return false;

Did some testing


```
for (let window of Window.getList("World of Warcraft")){
    console.log(window.isValid());
    let pid = window.getPID();
    let process = window.getProcess();
    console.log(pid);
    console.log(process.getPID());
    console.log(process.open(pid));
    console.log(process.isValid());
    console.log(process.getName());
    console.log(process.getPath());
}
```

> true
> 187864 
> 0 // Should be the same as above
> false // From documentation: True if valid and selectable
> false // From documentation: Returns true if a process has been selected and is still currently accessible. 
>
>

So, for some reason the process doesn't get selected it seems, or it's not accessible.



```
for (let window of Window.getList("Calculator")){
    console.log(window.isValid());
    let pid = window.getPID();
    let process = window.getProcess();
    console.log(pid);
    console.log(process.getPID());
    console.log(process.open(pid));
    console.log(process.isValid());
    console.log(process.getName());
    console.log(process.getPath());
}
```

> true
> 52208 
> 52208
> true
> true
> ApplicationFrameHost.exe
> C:/Windows/System32/ApplicationFrameHost.exe

Seems to work with calculator atleast.
Tried running both cmd & wow as admin, no change though.

That said, Im totally inexperienced with Node.js, but I've used javascript quite a bit.

Downloaded Sonar, and it gives me the error "Insufficient Privileges", so I guess there's something related to the client maybe.

----------


## para_

DrMight, I was doing the same thing. I was printing properties of the process object and seeing similar results. I still haven't figured it out, but I thought this was interesting. I attempted to use hacksniff by namreeb to learn possibly where or how offsets were determined to be cheat triggers (still very new to everything). Anyways, hacksniff uses hadesmem by jadd, and in my attempt to attach to the process I get a Windows error code of 5 or "Access Denied" according to documentation. I was running both the node.js script and hacksniff from an elevated command prompt, so I'm not sure why I'm still getting this issue. Just wanted to throw this out there in case it helps.

----------


## Torpedoes

> Trying to get it working with Vanilla client aswell, attempting to run chat_monitor.js.


Hey there, So window.getProcess already returns a process. You can check if it's valid by using process.isValid. If this function returns false then I suspect that you have insufficient privileges to open the process. Try running the command line in admin mode. Another issue is that you are trying to select a 64-Bit version of the game from a 32-Bit version of Node. I don't think this would be the case though since I don't think Vanilla has a 64-Bit client.

That being said, maybe whatever WoW client you're using has some weird protections? Or maybe it's something with your system. See if you can select the client with something like Cheat Engine. If that works but robot doesn't, I'm hoping it's not related to this issue. If you get it working, keep in mind that you'll have to reverse the client yourself to find the new memory offsets. I have no idea how chat differs in vanilla but I'm sure there's plenty of information online you can find.




> Anyways, hacksniff uses hadesmem by jadd.


I thought HadesMem was written by Crypher. Regardless, robot-js is also available in C++ form in case you needed it. Blackbone is another good one, along with all the other white/gray/black magic memory libraries.

----------


## para_

> I thought HadesMem was written by Crypher. Regardless, robot-js is also available in C++ form in case you needed it. Blackbone is another good one, along with all the other white/gray/black magic memory libraries.


You're probably right. I thought I saw Jadd with a picture of a raptor as his icon and just assumed that github was his, but I'm probably misremembering things.




> That being said, maybe whatever WoW client you're using has some weird protections? Or maybe it's something with your system. See if you can select the client with something like Cheat Engine. If that works but robot doesn't, I'm hoping it's not related to this issue. If you get it working, keep in mind that you'll have to reverse the client yourself to find the new memory offsets. I have no idea how chat differs in vanilla but I'm sure there's plenty of information online you can find.


I was able to attach Cheat Engine to WoW, but still got an invalid process using robot-js. It certainly finds the window. I'm printing a bunch of nonsense until the correct one is selected. I'll take a look at the C++, and also try to use one of my old tools to do a quick attach and see if I can't do it using my own codez.

----------


## Torpedoes

> I was able to attach Cheat Engine to WoW, but still got an invalid process using robot-js. It certainly finds the window. I'm printing a bunch of nonsense until the correct one is selected. I'll take a look at the C++, and also try to use one of my old tools to do a quick attach and see if I can't do it using my own codez.


Here's the code that does the actual opening of the process. You can try copying that and see why it's failing. The problem is probably going to be with OpenProcess in which case refer to Microsoft's documentation. That being said, I still think you should be able to open the process by passing in the PID directly into the open function. If it doesn't work because of an ERROR_ACCESS_DENIED (0x5) error and you're running as admin then that requires some further investigating.

----------


## kita12

Thanks for writing all this up. I'm relatively new to reading memory but I was working with your code to extract the NPC name and I haven't quite gotten it to work yet. I've no idea if I'm doing this correctly but in order for me to invoke memory.readString how can I find the unit32_length (per Robot for Node.js

Below is the snippet I'm trying to modify



```
        if (desc > 0)
        {
            type === 3 && entities.npcs.push
            ({
                entry,
                x: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 0),
                y: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 4),
                z: memory.readReal32 (entry + offsets.Entity.Unit.Origin + 8),
                name1: memory.readString(entry + offsets.Entity.NPC.Name1, 6), // <----This is where I'm struggling
                name2: memory.readString(entry + offsets.Entity.NPC.Name2, 6),

            });
```

----------


## Torpedoes

> Thanks for writing all this up. I'm relatively new to reading memory but I was working with your code to extract the NPC name and I haven't quite gotten it to work yet


Name 1 is a pointer to Name 2 which is a pointer to a string. You should use readPtr for Name1 and Name2, then use readString. Use Cheat engine to help you out.

----------


## kita12

Thanks for the quick reply. Still banging my head to get this to work. Say that I had an entity entry (given to me by entitydump.js) at 29DDF7B7A18. This is from offsets.Entity.EntryFirst and verified that its an NPC entry where type == 3. I would go enter in that address manually to cheat engine, hit pointer, add in the first offset name1 (0x16F0), then add in the name2 offset (0x00A0), it leads to an address where I would display as text and it just looks gibberish. Why?

----------


## Torpedoes

> Thanks for the quick reply. Still banging my head to get this to work. Say that I had an entity entry (given to me by entitydump.js) at 29DDF7B7A18. This is from offsets.Entity.EntryFirst and verified that its an NPC entry where type == 3. I would go enter in that address manually to cheat engine, hit pointer, add in the first offset name1 (0x16F0), then add in the name2 offset (0x00A0), it leads to an address where I would display as text and it just looks gibberish. Why?


Try playing around with the CheatEngine table I provide  ([WoW] [7.1.5.23360])here.

----------


## pyre

> You're probably right. I thought I saw Jadd with a picture of a raptor as his icon and just assumed that github was his, but I'm probably misremembering things.
> 
> 
> 
> I was able to attach Cheat Engine to WoW, but still got an invalid process using robot-js. It certainly finds the window. I'm printing a bunch of nonsense until the correct one is selected. I'll take a look at the C++, and also try to use one of my old tools to do a quick attach and see if I can't do it using my own codez.


I have the same issues, thus I gave up on trying to use robotJS for vanilla WoW. Still yet, it is a great node library and I appreciate this thread.

Ended up making a few cheats for Assault Cube just to see if I could. Great library!

----------


## antiblizzard

Could you give offsets.js for WoW 6.2.3 Build: 20779

----------


## Torpedoes

> Could you give offsets.js for WoW 6.2.3 Build: 20779


Some of them are available  ([WoW] [6.2.3.20779] Release Info Dump Thread)here.

----------


## antiblizzard

I already saw this link earlier. There are no offsets that I need. I need offsets aura and cooldowns for WoW 6.2.3: 20779 x32

----------


## ircdirk

@ Torpedoes can u give us example how to use Click To Move with your excellent Robot-js library?

----------


## furyous26

Hello, i am not new to nodejs but i installed the library robot-js, and for example for robot.Procces, i have only 3 function (getList(), currentProcess() and is64bitsys) and getList returning me an array with empty process
Pasteboard — Uploaded Image
Pasteboard — Uploaded Image

PS : i compiled the robot project correctly with vs2017
PS2 : also not read or write function for memory module

----------


## Subi

This is actually really neat, I'm glad there's other people out there who make things like this that lower the barrier of entry into game hacking.

----------


## SeionTop

Very nice, thanks for this.

----------


## fearlesselite

I am running High Sierra on OSX (10.13.4) and I can't get it to notice the WoW window. I am stuck on the "Select a WoW Window..." stage. 

Is there anything I can do?

----------


## Torpedoes

> I am running High Sierra on OSX (10.13.4) and I can't get it to notice the WoW window. I am stuck on the "Select a WoW Window..." stage. 
> 
> Is there anything I can do?


It's been a while since I looked at all this stuff but make sure your application (node.js - or the one running robot-js) has accessibility rights and you have disabled SIP. It would also help to ensure it runs with root access. If that doesn't work then you'll have to debug your JavaScript application line by line. If you don't get anywhere with JavaScript then you'll have to do it on the C++ side. Hopefully this helps, OSX support has always been a bit spotty but I try to get it similar to Windows, it's just a bit more restrictive with its security policies.

----------


## fearlesselite

Yea, I figured it out. Because I downloaded the vanilla client 1.12 for Mac, it was a different name. In the code it scans for World Of Warcraft.exe and I had to change it to WoW*.*. 

However this seems to be for a different build? I'm on 1.12.1 (5875) so it looks like the offsets are off.

I haven't meet able to find any offsets for Mac for 1.12.1 (5875) seems to be all windows :-(

----------


## Torpedoes

> Yea, I figured it out. Because I downloaded the vanilla client 1.12 for Mac, it was a different name. In the code it scans for World Of Warcraft.exe and I had to change it to WoW*.*. 
> 
> However this seems to be for a different build? I'm on 1.12.1 (5875) so it looks like the offsets are off.
> 
> I haven't meet able to find any offsets for Mac for 1.12.1 (5875) seems to be all windows :-(


The offsets are different every build, every architecture and every operating system. For such an old client, you might have to rewrite parts of the memory reading logic because some offsets might simply not exist.

----------


## fearlesselite

Yea, to make life a little bit easier I am running dual boot of Windows 10 and installed cheat engine. I'm still new to learning about memory mapping, offsets, etc.

Do you know any guides that help break it down?

----------


## WiNiFiX

> Yea, to make life a little bit easier I am running dual boot of Windows 10 and installed cheat engine. I'm still new to learning about memory mapping, offsets, etc.
> Do you know any guides that help break it down?


https://www.ownedcore.com/forums/wor...ookthread.html (Bookthread)
GH Hack Video Tutorials | Guided Hacking

----------


## xalcon

> Yea, to make life a little bit easier I am running dual boot of Windows 10 and installed cheat engine. I'm still new to learning about memory mapping, offsets, etc.
> 
> Do you know any guides that help break it down?


There are tons of threads in this memory editing section. I suggest just using google with the site filter to search quickly through this section. i.e.



> site:https://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/ guide


Just type that into google. This also allows you to search for terms that are shorter than 4 letters and its also much faster.

In addition to all the threads on ownedcore, you can also look for tutorials on youtube (yes, there are tons of reverse engineering guides on youtube). Just keep in mind, most guides wont allow you to copy'n'paste things. 

As a baseline, i suggest some advanced knowledge in the following fields:
C/C++: WoW is written in c++. Writing your own c++ application and then reverse and hacking it is probably the best way to learn. Also Reversing C++ was really helpful in getting a better understanding of it.x86/x86_64 Assembler: The Hexrays IDA plugin might be helpful, but its still just pseduo-c code. Being able to understand assembler is extremely helpful, if not mandatory. You dont need to know every single opcode, just the basics should get you going.Embedding Lua: this more wow related, but it would apply to most other games that use lua as well. A lot of functionality is available via lua c functions and its rather easy to find them in memory. Knowing how a lua c function is structured is extremely helpful in reversing wow code.DirectX/OpenGL: WoW is game, this should be obvious. Its not mandatory, but it was really helpful for me in the past.Other Projects: Github is full with hacking projects and bots. You can either use the github search or use the google site-search (i.e. site:ownedcore.com github) and you should find plenty of resources. Most of them are outdated but all this stuff is about learning concepts, not getting spoonfed with working code.

----------


## WiNiFiX

Was bored so made anti-boredom application :P
Random question, why cant you call mouse.setPos(x, y); it insists on robot.Mouse.setPos(x, y); 

I would paste code, but PHP tags are rather broken - so here is image

----------


## Saridormi

> Was bored so made anti-boredom application :P
> Random question, why cant you call mouse.setPos(x, y); it insists on robot.Mouse.setPos(x, y); 
> 
> I would paste code, but PHP tags are rather broken - so here is image


You're setting mouse to the result of calling robot.Mouse(). Did you mean to set it to robot.Mouse instead?

----------


## WiNiFiX

> You're setting mouse to the result of calling robot.Mouse(). Did you mean to set it to robot.Mouse instead?


I will try that, i was not aware java supported that, everything ends in () with it.

----------


## maltikism

I don't think robot-js supports any of the remotely recent versions of Node? i.e. ones with the cool stuff like async await. I was using a custom fork of memoryjs instead - GitHub - idiidk/memoryjs: Read and write memory in NodeJS (finally!) - this lib supports memory pattern matching too.

----------


## Torpedoes

> I will try that, i was not aware java supported that, everything ends in () with it.


In your case, you were creating an instance of the mouse object but setPos is a static variable. What you were doing is identical to "new Mouse()". This is not normal for JavaScript and it's something special I did to save on having to write "new". If you do "let mouse = robot.Mouse". then setPos will work fine.




> I don't think robot-js supports any of the remotely recent versions of Node?


While I haven't tried node 10, robot-js works with Node 0.12 up to and including 9. If you have Node 10 you can try installing robot-js and see if it compiles automatically.




> i.e. ones with the cool stuff like async await. I was using a custom fork of memoryjs instead - GitHub - idiidk/memoryjs: Read and write memory in NodeJS (finally!) - this lib supports memory pattern matching too.


So that's one of the limitations of robot-js, it's all blocking so it doesn't technically support Promises and async/await. But thanks for linking me this project. It'll help me improve robot-js.

----------


## Thornsworth

First off, thank you Torpedoes for all your hard work. I have a few questions and I'm very much willing to do the research but I am unsure where to start. I've sifted through a lot of Google Searches as well as OwnedCore forum threads and I've come to a bit of a road block.

I'm attempting to write a super basic bot (eventually just a rotation bot). For right now I just want to read memory and get used to the ins and out of robot-js as well as the WoW client itself.

I've already written (with the help of the many robot-js tutorials) a node app that can attach to the client and runs through a loop function (already done in the tutorials but I wanted Promises and ES6/7 functionality/syntax). All that works great.

I've been using CE to find what the currently selected character is (unable to remember yet how to use IDA as the last time I used it was maybe 6 years ago). It seems obvious now that the client is dynamically referencing offsets as I am unable to lock anything down. I am able to get pointer for the module.



```
let entry = memory.readPtr(module); //12894362189 <-- Looks to be a decimal
```

And once I find the address on CE of the selected target I am able to console log the result:



```
let target = 0x238A07A350A; // From CE
console.log(memory.readString(target, 80).split("|")[0]);
```

But the target address changes every time the client is loaded. Is there a static pointer/offset address from the entry that I am not finding information about in my searches? Am I even on the right track? Is the fact that its all dynamic making this all a moot project? I want to learn this but I am on the hard cord struggle bus. Any help with my endeavors would be appreciated.

----------


## szuecsg

Hello All,

This is a very nice tutorial for a beginner, altough i managed to get stuck, or maybe the client changed somehow.
I was trying out your cooldown iterator demo, but i have some problems.
I am not a javascript expert, i worked with C++ before, so the concept of pointers are clear to me.
But here what i am getting on the latest clients:

Cooldown table address: 7ff719b5f9e8 (This is the base address + the offset for the cooldown table)
After reading this, i get the following address:132965ee000
This address does contain valid data as i can see that spellID = 28730 is valid.
After trying to read the next entry, which should be located at currentAddress + 0x08, io get the following address: 7ff719b5f9e9 (This is the cooldown table + 0x01)
This address does not contain any valid address or valid data.

So the structure should look something like this if the entry holds the next pointer:
class CooldownEntry {
Int64 unused; // Or 2 Int32, etc
Pointer nextEntry; // located at 0x08 - 64bit pointer size
Int32 SpellID; // located at 0x10
Int32 itemID; //located at 0x14
}

Update: I could find the second entry at the cooldown table base(7ff719b5f9e8 ) + 0x08
The third one is not at cooldown table base + 0x10  :Big Grin: 

*Did anyone manage to find these cooldowns?*

And also a little question about the timestamps for example spellStartTime, it is not epoch, what is it then?

Thank you all for your answers in advance.

Best Regards,
szuecsg

----------


## ejt

> Hello All,
> 
> This is a very nice tutorial for a beginner, altough i managed to get stuck, or maybe the client changed somehow.
> I was trying out your cooldown iterator demo, but i have some problems.
> I am not a javascript expert, i worked with C++ before, so the concept of pointers are clear to me.
> But here what i am getting on the latest clients:
> 
> Cooldown table address: 7ff719b5f9e8 (This is the base address + the offset for the cooldown table)
> After reading this, i get the following address:132965ee000
> ...


All times are calculated using the timing functions that wow uses. They have 2 methods AFAIK, one is located at 0x285CD0 (8.0.1.28153) the other one is high performance counter, cba to find it as the first one works fine for me. If you are out-of-process you can look at the function in IDA, the function reads the time from a global variable.

As for the cooldown structure, you have to RE it in IDA (or CE if you're brave enough).

----------


## 3588

Hi Torpedoes 
test more than 3days, but still didn't work on 8.0.1.28153. Could you please update the new offsets.js? so for some noobs like me can start use it.
I know made some Cheat Engine Table before, is possible to have some CE table for 8.0.1.28153? don't know how to use OD xD

Thanks

----------

