Skip to content

Instantly share code, notes, and snippets.

@YetiBots
Last active April 6, 2024 05:54
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save YetiBots/6c933f34f22e62ecd18eec7a88011388 to your computer and use it in GitHub Desktop.
Save YetiBots/6c933f34f22e62ecd18eec7a88011388 to your computer and use it in GitHub Desktop.
"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);
}
};
"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`);
});
});
"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}`);
});
});
"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}`);
});
});
"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
});
});
"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();
});
"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 ("`");
}
});
});
"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; }
};
"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
}
}
}
};
"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();
});
"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)}`);
});
});
@Yousha
Copy link

Yousha commented Aug 24, 2017

Error: Cannot find module './win32-x64-57'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment