-
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.
-
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
Code:
"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
});
});
-
Originally Posted by
lolp1
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.
-
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
Code:
"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)}`);
});
});
-
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
Code:
"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();
});
-
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.
-
Originally Posted by
lolp1
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#.
-
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
Code:
"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 ("`");
}
});
});
-
Originally Posted by
lolp1
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.
-
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.
Last edited by Torpedoes; 05-03-2016 at 06:18 PM.
-
Contributor
Good code, and great post. Thanks for the contribution.
-
I had to read a multi level ptr and I did it this way:
Code:
/**
* 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:
Code:
const offsets = {
camera: {
ptr: [0x0175A124, 0x1d4, 0x114, 0x4, 0x2b0, 0x0]
}
};
memory.readMultiLevelPtr(offsets.camera.ptr);
Is there a better way to do it?
thanks!
-
Post Thanks / Like - 1 Thanks
Torpedoes (1 members gave Thanks to karliky for this useful post)
-
Just made this with Robot-js
-
Post Thanks / Like - 1 Thanks
Torpedoes (1 members gave Thanks to karliky for this useful post)
-
Member
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?)
Last edited by carloxx; 05-10-2016 at 06:20 AM.
-
Originally Posted by
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?)
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.
Check my blog: https://zzuks.blogspot.com
-
Post Thanks / Like - 1 Thanks
carloxx (1 members gave Thanks to Corthezz for this useful post)