FYI this is being posted on other forums, too. Though I go by different handles for anonymity's sake I am the same person and original author.
Original post:
I know some of the code is ugly, references functions I haven't posted, is full of debug related statements, and obviously some pieces are missing. This is (believe it or not) not copy-pasta but a reference!
Note the comment on VisibilityFlags. You can track these flags to help track invisible units. A mostly functional POC is included as StealthTracker.cs.
One thing I'm working the kinks out on right now is a module to automate the Legionnaire jungling process. I've implemented a fairly rudimentary GOAP system to decide which camps to go to and whether to dual/triple spawn etc.. The results are encouraging. As I've posted before I also have a last hit module which performs well, but it has a few glitches I don't think I'm going to bother with. As neat of an idea as it sounds, I personally prefer harassing my opponents than farming gold on creep kills.
IGameEntity.cs:
Code:
using System;
using System.Runtime.InteropServices;
using Hon.Classes.Game;
namespace Hon.Classes.Entities
{
public enum Faction : byte
{
Legion = 1,
Hellbourne = 2
}
public class IGameEntity
{
#region Properties
public readonly IntPtr UnmanagedPtr;
public int ResourceType
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0xC); }
set { Marshal.WriteInt32(UnmanagedPtr, 0xC, value); }
}
public uint Id
{
get { return (uint)Marshal.ReadInt32(UnmanagedPtr, 0x14); }
set { Marshal.WriteInt32(UnmanagedPtr, 0x14, (int)value); }
}
public string Name
{
get { return Marshal.PtrToStringUni(Marshal.ReadIntPtr(UnmanagedPtr, 0x1C)); }
set { throw new NotImplementedException("IGameEntity.Name cannot be changed"); }
}
public uint EntityType
{
get { return (uint)Marshal.ReadInt32(UnmanagedPtr, 0x2C); }
set { Marshal.WriteInt32(UnmanagedPtr, 0x2C, (int)value); }
}
public ushort EntityType2
{
get { return (ushort)Marshal.ReadInt16(UnmanagedPtr, 0x3C); }
set { Marshal.WriteInt16(UnmanagedPtr, 0x3C, (short)value); }
}
#endregion
#region Delegates
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
protected delegate float NoParamReturnFloatDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
protected delegate uint NoParamReturnUIntDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
protected delegate int NoParamReturnIntDelegate(IntPtr thisPtr);
#endregion
public IGameEntity(IntPtr unmanagedPtr)
{
UnmanagedPtr = unmanagedPtr;
}
public IGameEntity(uint entityId)
{
UnmanagedPtr = IGame.Current.EntityDirectory.GetGameEntityFromId(entityId).UnmanagedPtr;
if (UnmanagedPtr == IntPtr.Zero)
throw new NullReferenceException("Entity not found");
}
#region Actions
public void Control()
{
IBuffer controlPacket = new IBuffer(3);
controlPacket.Write((byte)ClientOpCode.TargetControllableUnit);
controlPacket.Write((ushort)Id);
controlPacket.Write((ushort)0xFFFF);
IGame.Current.HostClient.SendGameData(controlPacket);
}
public void Cast(Spell spell, IGameEntity target, bool enqueue = false)
{
IBuffer castPacket = new IBuffer(12);
castPacket.Write((byte)ClientOpCode.CastSpellUnitTarget);
castPacket.Write(Id);
castPacket.Write((byte)spell);
castPacket.Write(target.Id);
castPacket.Write((byte)(enqueue ? 1 : 0));
castPacket.Write((byte)0);
IGame.Current.HostClient.SendGameData(castPacket);
}
public void Cast(Spell spell, bool enqueue = false)
{
IBuffer castPacket = new IBuffer(7);
castPacket.Write((byte)ClientOpCode.CastSpellNoTarget);
castPacket.Write(Id);
castPacket.Write((byte)spell);
castPacket.Write((byte)(enqueue ? 1 : 0));
IGame.Current.HostClient.SendGameData(castPacket);
}
public void Attack(uint entityId, bool enqueue = false)
{
IBuffer attackPacket = new IBuffer(0x10);
attackPacket.Write((byte)0x1F);
attackPacket.Write((byte)0x08);
attackPacket.Write(entityId);
attackPacket.Write(0x00000000);
attackPacket.Write((byte)0x00);
attackPacket.Write(0xFFFFFFFF);
attackPacket.Write((byte)(enqueue ? 1 : 0));
IGame.Current.HostClient.SendGameData(attackPacket);
}
public void Attack(IGameEntity entity)
{
Attack(entity.Id);
}
public void Move(float x, float y, bool enqueue = false)
{
IBuffer movePacket = new IBuffer(20);
movePacket.Write((byte)ClientOpCode.SetCurrentTargetDestination);
movePacket.Write((byte)MovementType.Move);
movePacket.Write(x);
movePacket.Write(y);
movePacket.Write((uint)0);
movePacket.Write((byte)(enqueue ? 1 : 0));
movePacket.Write(0xFFFFFFFF);
movePacket.Write((byte)0);
IGame.Current.HostClient.SendGameData(movePacket);
}
public void Move(IGameEntity entity, bool enqueue = false)
{
IBuffer movePacket = new IBuffer(13);
movePacket.Write((byte)ClientOpCode.SetDestinationEntity);
movePacket.Write((byte)MovementType.Interact);
movePacket.Write(entity.Id);
movePacket.Write((byte)0);
movePacket.Write((uint)0);
movePacket.Write(0xFFFFFFFF);
movePacket.Write((byte)(enqueue ? 1 : 0));
IGame.Current.HostClient.SendGameData(movePacket);
}
public void Halt()
{
IBuffer haltPacket = new IBuffer(6);
haltPacket.Write((byte)0x21);
haltPacket.Write((byte)0x00);
haltPacket.Write(0xFFFFFFFF);
IGame.Current.HostClient.SendGameData(haltPacket);
}
public void SkillUp(byte slot)
{
IBuffer skillUpPacket = new IBuffer(6);
skillUpPacket.Write((byte)ClientOpCode.SkillUp);
skillUpPacket.Write(Id);
skillUpPacket.Write(slot);
IGame.Current.HostClient.SendGameData(skillUpPacket);
}
#endregion
}
}
IUnitEntity.cs:
Code:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Hon.Classes.Game;
namespace Hon.Classes.Entities
{
public class IUnitEntity : IGameEntity
{
#region Delegates
//[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
//private delegate float NoParamReturnFloatDelegate(IntPtr thisPtr);
//[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
//private delegate uint NoParamReturnUIntDelegate(IntPtr thisPtr);
//[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
//private delegate int NoParamReturnIntDelegate(IntPtr thisPtr);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate byte IsEnemyDelegate(IntPtr thisPtr, IntPtr otherPtr);
#endregion
//
// VMT Information obtained mostly from cgame.dll!sub_190B4230 in 1.0.20.0
//
#region Properties
public int OwnerClientNumber
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0x394); }
}
public int OwnerEntityIndex
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0x398); }
}
public int SpawnTime
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0x40C); }
}
public int LifeTime
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0x410); }
}
/// <summary>
/// Counter clockwise orientation (North being 0.00)
/// </summary>
public float Orientation
{
get
{
var ret = new float[1];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x380), ret, 0, 1);
return ret[0];
}
}
/// <summary>
/// 8 = stealthed
/// </summary>
public ushort Flags
{
get { return (ushort) Marshal.ReadInt16(UnmanagedPtr, 0x250); }
}
public bool IsStealthed
{
get { return (Flags & 0x8) == 0 ? false : true; }
}
public float StealthPercentage
{
get
{
var ret = new float[1];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x3DC), ret, 0, 1);
return ret[0];
}
}
public bool IsAlive
{
get { return Health > 0; }
}
public Faction Faction
{
get { return (Faction)Marshal.ReadInt32(UnmanagedPtr, 0x90); }
set { Marshal.WriteInt32(UnmanagedPtr, 0x90, (int)value); }
}
public byte Status
{
get { return Marshal.ReadByte(UnmanagedPtr, 0x94); }
set { Marshal.WriteByte(UnmanagedPtr, 0x94, value); }
}
/// <summary>
/// Flags: 0x001 = visible to legion, 0x002 = visible to legion hero, 0x100 = visible to hellbourne, 0x200 = visible to hellbourne hero
/// </summary>
public ushort VisibilityFlags
{
get { return (ushort)Marshal.ReadInt16(UnmanagedPtr, 0x96); }
set { Marshal.WriteInt16(UnmanagedPtr, 0x96, (short)value); }
}
public float X
{
get
{
var ret = new float[1];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x98), ret, 0, 1);
return ret[0];
}
set
{
var val = new[] { value };
Marshal.Copy(val, 0, new IntPtr(UnmanagedPtr.ToInt32() + 0x98), 1);
}
}
public float Y
{
get
{
var ret = new float[1];
var yPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0x9C);
Marshal.Copy(yPtr, ret, 0, 1);
return ret[0];
}
set
{
var val = new[] { value };
var yPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0x9C);
Marshal.Copy(val, 0, yPtr, 1);
}
}
public float Z
{
get
{
var ret = new float[1];
var zPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0xA0);
Marshal.Copy(zPtr, ret, 0, 1);
return ret[0];
}
set
{
var val = new[] { value };
var zPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0xA0);
Marshal.Copy(val, 0, zPtr, 1);
}
}
public float Health
{
get
{
var ret = new float[1];
var hPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0x258);
Marshal.Copy(hPtr, ret, 0, 1);
return ret[0];
}
set
{
var val = new[] { value };
var hPtr = new IntPtr(UnmanagedPtr.ToInt32() + 0x258);
Marshal.Copy(val, 0, hPtr, 1);
}
}
public float Mana
{
get
{
var ret = new float[1];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x25C), ret, 0, 1);
return ret[0];
}
set
{
Marshal.Copy(new[] { value }, 0, new IntPtr(UnmanagedPtr.ToInt32() + 0x25C), 1);
}
}
public int Level
{
get { return Marshal.ReadInt32(UnmanagedPtr, 0x257); }
set { Marshal.WriteInt32(UnmanagedPtr, 0x257, value); }
}
public float MaxHealth
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x2E8);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set max health"); }
}
public float HealthRegen
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x2F8);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set health regen"); }
}
public float MaxMana
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x308);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set max mana"); }
}
public float ManaRegen
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x318);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set mana regen"); }
}
private uint PhysicalRes
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x31C);
var func =
(NoParamReturnUIntDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnUIntDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set physical res"); }
}
public float Armor
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x324);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set armor"); }
}
private uint MagicRes
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x328);
var func =
(NoParamReturnUIntDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnUIntDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set magic res"); }
}
public float MagicArmor
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x330);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set magic armor"); }
}
public float MoveSpeed
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x34C);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set move speed"); }
}
public float AttackRange
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x354);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set attack range"); }
}
public float AttackSpeed
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x370);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set attack speed"); }
}
public float LifeSteal
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x3B0);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set life steal"); }
}
// taken from AttackDamage
public float MinDamage
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x5A4);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr) + DamageModifier;
}
set { throw new NotImplementedException("Cannot set attack damage"); }
}
// taken from AttackDamage
public float MaxDamage
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x5A8);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr) + DamageModifier;
}
set { throw new NotImplementedException("Cannot set attack damage"); }
}
public float DamageModifier
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x3A8);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set attack damage"); }
}
public float AttackDamage
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x49C);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set attack damage"); }
}
public float Stealth
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x4AC);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set stealth"); }
}
public float SightRange
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x634);
var func =
(NoParamReturnFloatDelegate)Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(NoParamReturnFloatDelegate));
return func(UnmanagedPtr);
}
set { throw new NotImplementedException("Cannot set sight range"); }
}
public float AttackDamageDelay
{
get
{
var vmtPtr = Marshal.ReadIntPtr(UnmanagedPtr);
var funcPtr = Marshal.ReadIntPtr(vmtPtr, 0x718);
var func =
(NoParamReturnUIntDelegate)
Marshal.GetDelegateForFunctionPointer(funcPtr, typeof (NoParamReturnUIntDelegate));
return (func(UnmanagedPtr) / 1000f);
}
set { throw new NotImplementedException("Cannot set attack damage delay"); }
}
public float DamageReduction
{
get
{
return (float)(1.0 - IGame.Current.GameMechanics.GetArmorDamageAdjustment(PhysicalRes, Armor));
}
}
public IntPtr[] Abilities
{
get
{
var ret = new IntPtr[12];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x2C8), ret, 0, 12);
return ret;
}
}
public IEntityItem[] Inventory
{
get
{
var ptrs = new IntPtr[13];
var ret = new IEntityItem[13];
Marshal.Copy(new IntPtr(UnmanagedPtr.ToInt32() + 0x338), ptrs, 0, 13);
for (int i = 0; i < 13; i++)
ret[i] = (ptrs[i] == IntPtr.Zero ? null : new IEntityItem(ptrs[i]));
return ret;
}
}
#endregion
public IUnitEntity(IntPtr unmanagedPtr) : base(unmanagedPtr) {}
public IUnitEntity(uint entityId) : base(entityId) {}
#region Actions
/// <summary>
/// Use an item in the unit's inventory.
/// </summary>
/// <param name="name">Item name</param>
/// <returns>True if successfully used, else returns false</returns>
public bool UseItem(string name)
{
var inventory = Inventory;
for (int i = 0; i < Math.Min(6, inventory.Length); i++)
{
if (inventory[i] == null || inventory[i].Name != name || inventory[i].OwnerIndex != Id)
continue;
Spell spell = 0;
switch (i)
{
case 0:
spell = Spell.Inventory1;
break;
case 1:
spell = Spell.Inventory2;
break;
case 2:
spell = Spell.Inventory3;
break;
case 3:
spell = Spell.Inventory4;
break;
}
Cast(spell);
return true;
}
return false;
}
public bool UseItem(string name, IGameEntity target, bool enqueue = false)
{
var inventory = Inventory;
for (int i = 0; i < Math.Min(6, inventory.Length); i++)
{
if (inventory[i] == null || inventory[i].Name != name || inventory[i].OwnerIndex != Id)
continue;
Spell spell = 0;
switch (i)
{
case 0:
spell = Spell.Inventory1;
break;
case 1:
spell = Spell.Inventory2;
break;
case 2:
spell = Spell.Inventory3;
break;
case 3:
spell = Spell.Inventory4;
break;
}
Cast(spell, target, enqueue);
return true;
}
return false;
}
public void UseItem(IEntityItem item)
{
UseItem(item.Name);
}
#endregion
#region Queries
/// <summary>
/// Probability of killing this unit, given min and max damage
/// </summary>
/// <param name="minDamage">Minimum damage per attack</param>
/// <param name="maxDamage">Maximum damage per attack</param>
/// <returns>Probablity (ranging from 0 to 1) of killing the unit in one attack</returns>
public float OneShotChance(float minDamage, float maxDamage)
{
return OneShotChance(Health, minDamage, maxDamage);
}
/// <summary>
/// Probability of killing this unit, given min and max damage
/// </summary>
/// <param name="health">Estimated health at time attack lands</param>
/// <param name="minDamage">Minimum damage per attack</param>
/// <param name="maxDamage">Maximum damage per attack</param>
/// <returns>Probablity (ranging from 0 to 1) of killing the unit in one attack</returns>
public float OneShotChance(float health, float minDamage, float maxDamage)
{
return (health > maxDamage || health < 0) ? 0f : (maxDamage - health)/(maxDamage - minDamage);
}
public List<IUnitEntity> NearbyUnits(float radius)
{
return IGame.Current.World.WorldTree.GetEntitiesInRadius(X, Y, radius);
}
public float Distance(IUnitEntity other)
{
double dX = X - other.X;
double dY = Y - other.Y;
double dZ = Z - other.Z;
double ret = Math.Sqrt(dX * dX + dY * dY + dZ * dZ);
return (float)ret;
}
public float Distance(float x, float y)
{
double dX = X - x;
double dY = Y - y;
double ret = Math.Sqrt(dX * dX + dY * dY);
return (float)ret;
}
public bool IsEnemy(IGameEntity other)
{
var isEnemy =
(IsEnemyDelegate)
Marshal.GetDelegateForFunctionPointer(
Utilities.GetProcAddress("game_shared.dll", @"?IsEnemy@IUnitEntity@@QBE_NPAV1@@Z"),
typeof (IsEnemyDelegate));
return isEnemy(UnmanagedPtr, other.UnmanagedPtr) == 1;
}
/// <summary>
/// Whether this unit is within the LOS of the opposing team
/// </summary>
public bool WithinOpposingTeamLos
{
get
{
return (Faction == Faction.Legion && (VisibilityFlags & 0x100) != 0) ||
(Faction == Faction.Hellbourne && (VisibilityFlags & 0x001) != 0);
}
}
/// <summary>
/// Whether this unit is within the LOS of a hero on the opposing team
/// </summary>
public bool WithinOpposingTeamHeroLos
{
get
{
return (Faction == Faction.Legion && (VisibilityFlags & 0x200) != 0) ||
(Faction == Faction.Hellbourne && (VisibilityFlags & 0x002) != 0);
}
}
public bool HasItem(string name, bool includeStash = false, uint ownerId = 0xFFFFFFFF)
{
var inventory = Inventory;
for (int i = 0; i < (includeStash ? inventory.Length : Math.Min(6, inventory.Length)); i++)
{
if (inventory[i] == null)
continue;
if (inventory[i].Name == name && (ownerId == 0xFFFFFFFF || ownerId == inventory[i].OwnerIndex))
return true;
}
return false;
}
#endregion
}
public enum MovementType : byte
{
Move = 2,
Interact = 7,
Attack = 8,
Follow = 9
}
public enum Spell : byte
{
Ability1 = 0,
Ability2 = 1,
Ability3 = 2,
Ability4 = 3,
Inventory1 = 0x1C,
Inventory2 = 0x1D,
Inventory3 = 0x1E,
Inventory4 = 0x1F
}
}
NetSniff.cs:
Code:
using System;
using System.Runtime.InteropServices;
using System.Linq;
//using Hon.Automation;
using Hon.Classes;
using Hon.Classes.Entities;
using Hon.Classes.Game;
using Hon.Native;
namespace Hon
{
static class NetSniff
{
#region Hook Delegates
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int SendToDelegate(IntPtr socket, IntPtr buff, int len, int flags, IntPtr to, int toLen);
private static readonly SendToDelegate SendToDetour = SendToHandler;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr SendGameDataDelegate(IntPtr thisPtr, IntPtr bufferPtr, bool boolean);
private static readonly SendGameDataDelegate SendGameDataDetour = SendGameDataHandler;
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate byte ClientParsePacketDelegate(IntPtr hostClientPtr, IntPtr packetPtr);
private static readonly ClientParsePacketDelegate ReadServerPacketsDetour = ReadServerPacketsHandler;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate byte SubmitChatMessageDelegate(IntPtr chatManagerPtr, IntPtr strPtr, uint unk);
private static readonly SubmitChatMessageDelegate SubmitChatMessageDetour = SubmitChatMessageHandler;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate byte EntityReadSnapshotDelegate(IntPtr thisPtr, IntPtr snapshotPtr, uint a3);
private static readonly EntityReadSnapshotDelegate PlayerReadSnapshotDetour = PlayerReadSnapshotHandler;
private static readonly EntityReadSnapshotDelegate HeroReadSnapshotDetour = HeroReadSnapshotHandler;
private static readonly EntityReadSnapshotDelegate UnitReadSnapshotDetour = UnitReadSnapshotHandler;
private static readonly EntityReadSnapshotDelegate ItemReadSnapshotDetour = ItemReadSnapshotHandler;
#endregion
#region Hooks
private static int SendToHandler(IntPtr socket, IntPtr buff, int len, int flags, IntPtr to, int toLen)
{
return GuiControl.DropClientToServer
? toLen
: (int) Detours.Get("SendTo_Hook").CallOriginal(socket, buff, len, flags, to, toLen);
//var ret = (int) Detours.Get("SendTo_Hook").CallOriginal(socket, buff, len, flags, to, toLen);
//byte[] data = new byte[ret];
//Marshal.Copy(buff, data, 0, ret);
//Logging.Write(data.Aggregate(string.Format("sendto(0x{0}) Data:", socket.ToString("X")),
// (current, b) => current + (" 0x" + b.ToString("X"))));
//return ret;
}
private static IntPtr SendGameDataHandler(IntPtr thisPtr, IntPtr bufferPtr, bool boolean)
{
ParseClientGameData(new IBuffer(bufferPtr, true));
return (IntPtr)Detours.Get("SendGameData_Hook").CallOriginal(thisPtr, bufferPtr, boolean);
}
private static byte ReadServerPacketsHandler(IntPtr hostClientPtr, IntPtr packetPtr)
{
try
{
ParseServerPacket(new CPacket(packetPtr, true).Buffer);
return GuiControl.DropServerToClient
? (byte)1
: (byte) Detours.Get("ReadServerPackets_Hook").CallOriginal(hostClientPtr, packetPtr);
}
catch (System.Reflection.TargetInvocationException e)
{
Logging.Write("Internal exception during ReadServerPacketsHandler: " + e.InnerException);
}
catch (Exception e)
{
Logging.Write("Exception during ReadServerPacketsHandler: " + e);
}
return 0;
}
private static byte SubmitChatMessageHandler(IntPtr chatManagerPtr, IntPtr strPtr, uint unk)
{
string str = new StlWrapper.StlWString(strPtr).String;
Logging.Write("[CHAT] 0x" + chatManagerPtr.ToString("X") + " str: " + str + " unk: 0x" + unk.ToString("X"));
return (byte) Detours.Get("SubmitChatMessage_Hook").CallOriginal(chatManagerPtr, strPtr, unk);
}
private static byte PlayerReadSnapshotHandler(IntPtr thisPtr, IntPtr snapshotPtr, uint a3)
{
var ret = (byte) Detours.Get("PlayerReadSnapshot_Hook").CallOriginal(thisPtr, snapshotPtr, a3);
new CEntitySnapshot(snapshotPtr, true);
Detours.Get("PlayerReadSnapshot_Hook").Remove();
return ret;
}
private static byte HeroReadSnapshotHandler(IntPtr thisPtr, IntPtr snapshotPtr, uint a3)
{
return (byte)Detours.Get("HeroReadSnapshot_Hook").CallOriginal(thisPtr, snapshotPtr, a3);
}
private static byte UnitReadSnapshotHandler(IntPtr thisPtr, IntPtr snapshotPtr, uint a3)
{
var ret = (byte) Detours.Get("UnitReadSnapshot_Hook").CallOriginal(thisPtr, snapshotPtr, a3);
var unit = new IUnitEntity(thisPtr);
if (GuiControl.EntityTrackingActive && GuiControl.EntityId == unit.Id)
GuiControl.LastEntityUpdateReceived = DateTime.Now;
return ret;
}
private static byte ItemReadSnapshotHandler(IntPtr thisPtr, IntPtr snapshotPtr, uint a3)
{
var ret = (byte) Detours.Get("ItemReadSnapshot_Hook").CallOriginal(thisPtr, snapshotPtr, a3);
new CEntitySnapshot(snapshotPtr, true);
//Detours.Get("ToolReadSnapshot_Hook").Remove();
return ret;
}
#endregion
static NetSniff()
{
Events.OnApplicationLoaded += Init;
}
public static void Init()
{
try
{
Detours.Add(Utilities.RegisterDelegate<SendGameDataDelegate>(Locator.CHostClient__SendGameData),
SendGameDataDetour, "SendGameData_Hook").Apply();
Detours.Add(Utilities.RegisterDelegate<ClientParsePacketDelegate>(Locator.CHostClient__ReadServerPackets),
ReadServerPacketsDetour, "ReadServerPackets_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<EntityReadSnapshotDelegate>(Locator.IHeroEntity__ReadSnapshot),
// HeroReadSnapshotDetour, "HeroReadSnapshot_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<EntityReadSnapshotDelegate>(Locator.IUnitEntity__ReadSnapshot),
// UnitReadSnapshotDetour, "UnitReadSnapshot_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<EntityReadSnapshotDelegate>(Locator.CPlayer__ReadSnapshot),
// PlayerReadSnapshotDetour, "PlayerReadSnapshot_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<EntityReadSnapshotDelegate>(Locator.IEntityItem__ReadSnapshot),
// ItemReadSnapshotDetour, "ItemReadSnapshot_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<SubmitChatMessageDelegate>(Locator.CChatManager__SubmitChatMessage),
// SubmitChatMessageDetour, "SubmitChatMessage_Hook").Apply();
//Detours.Add(Utilities.RegisterDelegate<SendToDelegate>(Locator.SendTo), SendToDetour, "SendTo_Hook").Apply();
}
catch (Exception e)
{
Logging.Write("Exception during NetSnif.Init(): " + e);
}
}
private static void ParseClientGameData(IBuffer buffer)
{
try
{
var origBytesRead = buffer.BytesRead;
ClientOpCode opCode = (ClientOpCode) buffer.Read<byte>();
string log = "[C->S] " + (GuiControl.DropClientToServer ? "DROPPED " : "") + opCode + ": ";
#region switch(opCode)
switch (opCode)
{
case ClientOpCode.SetCurrentTargetDestination:
{
var type = buffer.Read<MovementType>();
float x = buffer.Read<float>();
float y = buffer.Read<float>();
buffer.BytesRead += 4;
var enqueued = buffer.Read<byte>() == 1;
buffer.BytesRead += 5;
//log += "New destination: (" + x + ", " + y + ") Movement Type: " + type +
// (enqueued ? " (Enqueued)" : "");
log = string.Empty;
break;
}
case ClientOpCode.SetDestinationEntity:
{
var a = buffer.Read<MovementType>();
var entityId = buffer.Read<short>();
log += "a = " + a + " entityId = " + entityId;
break;
}
case ClientOpCode.TeamSelection:
{
int team = buffer.Read<int>();
int slot = buffer.Read<int>();
log += "Team: " + team + " Slot: " + slot;
break;
}
case ClientOpCode.HeroSelect:
{
short heroId = buffer.Read<short>();
log += "heroId = " + heroId;
break;
}
case ClientOpCode.GameStart:
case ClientOpCode.Ready:
case ClientOpCode.Halt:
case ClientOpCode.CreateGame:
case ClientOpCode.EnteringWorld:
break;
case ClientOpCode.Stop:
{
buffer.BytesRead += 4;
break;
}
case ClientOpCode.PurchaseRecipe:
{
int heroEntityId = buffer.Read<int>();
short itemId = buffer.Read<short>();
log += "heroEntityId = " + heroEntityId + " itemId = " + itemId;
break;
}
case ClientOpCode.HeroRandomSelect:
{
log += "BytesWritten: " + buffer.BytesWritten + " Capacity: " + buffer.Capacity +
" Full buffer: " + buffer.FullHexString;
break;
}
case ClientOpCode.ChatTeam:
case ClientOpCode.ChatRoll:
case ClientOpCode.ChatAll:
{
buffer.ReadAnsiString();
log = string.Empty;
break;
}
case ClientOpCode.LockSlot:
{
log += "Team: " + buffer.Read<int>() + " Slot: " + buffer.Read<int>();
break;
}
case ClientOpCode.SkillUp:
{
var entity = new IGameEntity(buffer.Read<uint>());
byte skill = buffer.Read<byte>();
log += "Entity: " + entity.Name + " Skill: " + skill;
break;
}
case ClientOpCode.SetGameplayOption:
{
log += buffer.ReadAnsiString();
break;
}
case ClientOpCode.TargetControllableUnit:
{
var entity = new IGameEntity((uint)buffer.Read<short>());
log += "New target: " + entity.Name;
buffer.BytesRead += 2;
break;
}
case ClientOpCode.CastSpellUnitTarget:
{
var caster = new IUnitEntity(buffer.Read<uint>());
var spell = buffer.Read<Spell>();
var target = new IUnitEntity(buffer.Read<uint>());
var enqueued = buffer.Read<byte>() == 1;
buffer.BytesRead++;
log += "Caster: " + caster.Name + " Spell: " + spell + " Target: " + target.Name +
" Target position: (" + target.X + ", " + target.Y + ")" +
(enqueued ? " (Enqueued)" : "");
break;
}
case ClientOpCode.CastSpellAreaTarget:
{
var casterId = buffer.Read<uint>();
var spellId = buffer.Read<Spell>();
var targetX = buffer.Read<float>();
var targetY = buffer.Read<float>();
log += "Caster ID: " + casterId + " Spell ID: " + spellId + " Target Location: (" + targetX +
", " + targetY + ")";
break;
}
case ClientOpCode.CastSpellNoTarget:
{
var casterId = buffer.Read<uint>();
var spellId = buffer.Read<Spell>();
var enqueued = buffer.Read<byte>() == 1;
log += "Caster ID: " + casterId + " Spell ID: " + spellId +
(enqueued ? " (Enqueued)" : "") + " Full hex: " + buffer.FullHexString;
break;
}
case ClientOpCode.SellItem:
{
var sellerId = buffer.Read<uint>();
var slotId = buffer.Read<uint>();
log += "Seller ID: " + sellerId + " Slot ID: " + slotId;
break;
}
case ClientOpCode.MoveItem:
{
uint entityId = buffer.Read<uint>();
uint oldSlotId = buffer.Read<uint>();
uint newSlotId = buffer.Read<uint>();
log += "Entity ID: " + entityId + " Old slot: " + oldSlotId + " New slot: " + newSlotId;
break;
}
case ClientOpCode.ReadinessUpdate:
{
float percentage = buffer.Read<float>();
if (percentage == 1)
log = string.Empty;
else
log += "Percentage: " + ((int)(100 * percentage)) + "%";
break;
}
case ClientOpCode.PurchaseItem:
{
var entity = new IGameEntity(buffer.Read<uint>());
var category = buffer.Read<short>();
var item = buffer.Read<byte>();
log += "entity: " + entity.Name + " category: " + category + " item: " + item;
break;
}
default:
log += "Unhandled. Full buffer: " + buffer.FullHexString;
break;
}
#endregion
buffer.BytesRead = origBytesRead;
if (log.EndsWith(": "))
log.TrimEnd(':', ' ');
if (log != string.Empty)
Logging.Write(log);
}
catch (Exception e)
{
Logging.Write("[NetSniff] Exception during ParseClientGameData: " + e);
}
}
private static void ParseGameData(IBuffer buffer)
{
try
{
GameDataOpCode opCode = (GameDataOpCode)buffer.Read<byte>();
string log = "[S->C] " + (GuiControl.DropServerToClient ? "DROPPED " : "") + "(GameData) " + opCode + ": ";
#region switch (opCode)
switch (opCode)
{
case GameDataOpCode.ClientConnected:
{
int slot = buffer.Read<int>();
log += "Slot: " + slot;
break;
}
case GameDataOpCode.ChatAll:
case GameDataOpCode.ChatRoll:
case GameDataOpCode.ChatEmote:
case GameDataOpCode.ChatTeam:
{
int a = buffer.Read<int>();
string b = buffer.ReadAnsiString();
log += "a = " + a + " data = " + b;
break;
}
case GameDataOpCode.ChatServerAlso:
{
string a = buffer.ReadUnicodeString();
log += "data = \"" + a + "\"";
break;
}
case GameDataOpCode.ChatServer:
{
string a = buffer.ReadAnsiString();
log += a;
break;
}
case GameDataOpCode.Unk10:
{
var a = buffer.Read<int>();
var b = buffer.Read<short>();
var c = buffer.Read<int>();
log += "a: " + a + " b: " + b + " c: " + c;
break;
}
case GameDataOpCode.Unk1:
case GameDataOpCode.Unk2:
{
var a = buffer.Read<byte>();
var b = buffer.Read<byte>();
var entityId = buffer.Read<short>();
var lifetime = buffer.Read<int>();
var entity = IGame.Current.EntityDirectory.GetGameEntityFromId((uint)entityId);
//log += string.Format("a: {0} b: {1} entityId: {2} Name: {3} lifetime: {4} Bytes Read: {5} Bytes Written: {6}", a, b,
// entityId, entity.Name, lifetime, buffer.BytesRead, buffer.BytesWritten);
//if (opCode == GameDataOpCode.Unk2)
// log += "e: " + buffer.Read<short>();
log = string.Empty;
break;
}
case GameDataOpCode.SetGamePhase:
{
var newPhase = buffer.Read<GamePhase>();
buffer.BytesRead += 8;
log += "New phase: " + newPhase;
Events.TriggerOnSetGamePhase(newPhase);
break;
}
case GameDataOpCode.UpdateSkill:
{
short entityId = buffer.Read<short>();
byte skillId = buffer.Read<byte>();
log += "Entity: " + entityId + " Skill ID: " + skillId;
break;
}
case GameDataOpCode.Unk4:
{
int a = buffer.Read<int>();
int b = buffer.Read<int>();
log += "a = " + a + " b = " + b;
break;
}
case GameDataOpCode.UpdateDestinationEntity:
case GameDataOpCode.Unk5:
{
short entityId = buffer.Read<short>();
log += "entityId = " + entityId + " remaining bytes = " + buffer.BytesRemaining;
break;
}
case GameDataOpCode.Unk7:
case GameDataOpCode.Unk8:
{
short a = buffer.Read<short>();
log += "a = " + a;
break;
}
case GameDataOpCode.Unk9:
{
short a = buffer.Read<short>();
byte b = buffer.Read<byte>();
log += "a: " + a + " b: " + b;
break;
}
case GameDataOpCode.UpdateDestination:
{
var unit = new IUnitEntity((uint)buffer.Read<short>());
//if (LegionnaireJungling.Enabled)
// log = string.Empty;
//else
log += "Unit: " + unit.Name;
break;
}
case GameDataOpCode.Play2DSound:
{
var target = new IGameEntity((uint) buffer.Read<short>());
short b = buffer.Read<short>();
log += "target: " + target.Name + " b = " + b;
break;
}
case GameDataOpCode.ServerFrame:
case GameDataOpCode.KillStreak:
case GameDataOpCode.MultiKill:
case GameDataOpCode.TeamGenocide:
{
int a = buffer.Read<int>();
log += "a = " + a;
break;
}
case GameDataOpCode.HeroKill:
case GameDataOpCode.KillStreakEnd:
{
int a = buffer.Read<int>();
int b = buffer.Read<int>();
short c = buffer.Read<short>();
log += "a = " + a + " b = " + b + " c = " + c;
break;
}
case GameDataOpCode.HeroKill2:
case GameDataOpCode.DenyBuilding:
{
int a = buffer.Read<int>();
int b = buffer.Read<int>();
log += "a = " + a + " b = " + b;
break;
}
case GameDataOpCode.LobbyForceSwap:
{
int a = buffer.Read<int>();
int b = buffer.Read<int>();
int c = buffer.Read<int>();
int d = buffer.Read<int>();
log += "a = " + a + " b = " + b + " c = " + c + " d = " + d;
break;
}
case GameDataOpCode.HeroBan:
case GameDataOpCode.HeroKillFirst:
{
int a = buffer.Read<int>();
short b = buffer.Read<short>();
log += " a = " + a + " b = " + b;
break;
}
case GameDataOpCode.KillBuilding:
{
int slot = buffer.Read<int>();
int team = buffer.Read<int>();
short c = buffer.Read<short>();
int entityId = buffer.Read<int>();
log += "slot = " + slot + " team = " + team + " c = " + c + " entityId = " + entityId;
break;
}
case GameDataOpCode.KillCourier:
{
int a = buffer.Read<int>();
int b = buffer.Read<int>();
int c = buffer.Read<int>();
log += "a = " + a + " b = " + b + " c = " + c;
break;
}
case GameDataOpCode.TeamKillKongor:
{
int a = buffer.Read<int>();
short b = buffer.Read<short>();
log += (a == 1 ? "The Legtion" : "The Hellbourne") + " b = " + b;
break;
}
case GameDataOpCode.HeroPick:
{
var player = buffer.Read<int>();
var hero = buffer.Read<short>();
log += "Player: " + player + " Hero: " + hero;
break;
}
case GameDataOpCode.PlayerReady:
{
var player = buffer.Read<int>();
log += "Player: " + player;
break;
}
case GameDataOpCode.StructureUnderAttack:
{
var structure = new IGameEntity((uint)buffer.Read<int>());
log += "Structure: " + structure.Name;
break;
}
case GameDataOpCode.PrepareForRunningPhase:
{
log += string.Format("GameInfo.PhaseStartTime: {0} PhaseDuration: {1}", IGame.Current.GameInfo.PhaseStartTime,
IGame.Current.GameInfo.PhaseDuration);
break;
}
case GameDataOpCode.StructureDestroyed:
{
var faction = buffer.Read<int>();
var gold = buffer.Read<short>();
var structure = new IGameEntity(buffer.Read<uint>());
log += "Faction: " + faction + " Gold: " + gold + " Structure: " + structure.Name;
break;
}
case GameDataOpCode.UpdateExperience:
{
short entity = buffer.Read<short>();
//if (LegionnaireJungling.Enabled)
// log = string.Empty;
//else
//log += "Entity: " + entity;
log = string.Empty;
break;
}
case GameDataOpCode.UpdateLevel:
{
short entity = buffer.Read<short>();
//if (LegionnaireJungling.Enabled)
// log = string.Empty;
//else
log += "Entity: " + entity;
break;
}
case GameDataOpCode.UiTrigger:
{
var entityId = buffer.Read<short>();
var b = buffer.Read<byte>();
//if (LegionnaireJungling.Enabled)
// log = string.Empty;
//else
log += "entityId: " + entityId + " b: " + b;
break;
}
case GameDataOpCode.OpenShop:
{
var shop = new IGameEntity((uint) buffer.Read<short>());
var unit = new IUnitEntity((uint) buffer.Read<short>());
log += "shop: " + shop.Name + " unit: " + unit.Name;
Events.TriggerOnOpenShop(unit, shop);
break;
}
case GameDataOpCode.BoughtWalkingCourier:
case GameDataOpCode.BoughtFlyingCourier:
{
var entityId = buffer.Read<int>();
log += "entityId: " + entityId;
break;
}
case GameDataOpCode.IntroduceCourier:
{
var a = buffer.Read<byte>();
log += "a: " + a;
if (a == 1)
{
try
{
var courier = new IUnitEntity(buffer.Read<uint>());
log += " entityId: " + courier.Id;
Events.TriggerOnIntroduceCourier(courier);
}
catch (NullReferenceException)
{
log += " (Entity not found. Full buffer: \"" + buffer.FullHexString + "\")";
Logging.Write(log);
}
}
break;
}
}
#endregion
if (log != string.Empty)
Logging.Write(log);
}
catch (Exception e)
{
Logging.Write("[NetSniff] Exception during ParseGameData: " + e);
}
}
private static void ParseServerPacket(IBuffer buffer)
{
try
{
var oldBytesRead = buffer.BytesRead;
while (buffer.BytesWritten > buffer.BytesRead)
{
ServerOpCode opCode = buffer.Read<ServerOpCode>();
string log = "[S->C] " + (GuiControl.DropServerToClient ? "DROPPED " : "") + opCode + ": ";
#region switch (opCode)
switch (opCode)
{
case ServerOpCode.Unk1:
log += "CHostClient::byte452B = 0";
break;
case ServerOpCode.Unk2:
log += buffer.ReadAnsiString();
break;
case ServerOpCode.ConnectedToGame:
short creationTime = buffer.Read<short>();
int clientNumber = buffer.Read<int>();
byte byte45A8 = buffer.Read<byte>();
string clientVersion = buffer.ReadAnsiString();
log +=
string.Format("CreationTime = {0} Client Number = {1} Byte45A8 = {2} Client version: {3}", creationTime,
clientNumber, byte45A8, clientVersion);
break;
case ServerOpCode.Unk4:
log += string.Format("byte452C = {0}", (buffer.Read<byte>() != 0 ? "true" : "false"));
break;
case ServerOpCode.Unk5:
log += "CHostClient::byte452A = 1";
break;
case ServerOpCode.ClearStateData:
log += "Full buffer: " + buffer.FullHexString;
break;
case ServerOpCode.StateString:
case ServerOpCode.StateStringZipped:
{
short a = buffer.Read<short>();
int size = buffer.Read<short>();
if (size > buffer.BytesRemaining)
throw new OverflowException("CPacket decompression read overflow");
//log += string.Format("Size = {0}{1}", size,
// (opCode == ServerOpCode.StateStringZipped
// ? " Decompressed Size: " + packet.Read<int>()
// : ""));
buffer.BytesRead += size;
log = string.Empty;
break;
}
case ServerOpCode.StateStringFragment:
{
short stateStringId = buffer.Read<short>();
short stateStringLength = buffer.Read<short>();
//log += "stateStringId = " + stateStringId + " Length: " + stateStringLength;
buffer.BytesRead += stateStringLength;
log = string.Empty;
break;
}
case ServerOpCode.StateStringTermination:
case ServerOpCode.StateStringTerminationZipped:
{
short stateStringId = buffer.Read<short>();
int size = buffer.Read<short>();
//log += "stateStringId = " + stateStringId + " Size = " + size +
// (opCode == ServerOpCode.StateStringTerminationZipped
// ? " Decompressed Size: " + packet.Read<int>()
// : "");
if (opCode == ServerOpCode.StateStringTerminationZipped)
size = buffer.Read<int>();
buffer.BytesRead += size;
log = string.Empty;
break;
}
case ServerOpCode.StateBlockUpdate:
case ServerOpCode.StateBlockUpdateZipped:
{
buffer.Read<short>();
int stateBlockUpdateStringLength = buffer.Read<short>();
//log += "Length = " + stateBlockUpdateStringLength +
// (opCode == ServerOpCode.StateBlockUpdateZipped
// ? " Decompressed Size: " + packet.Read<int>()
// : "");
buffer.BytesRead += stateBlockUpdateStringLength;
log = string.Empty;
break;
}
case ServerOpCode.StateBlockFragment:
{
short stateBlockId = buffer.Read<short>();
short stateBlockLength = buffer.Read<short>();
//log += "stateBlockId = " + stateBlockId + " Length = " + stateBlockLength;
buffer.BytesRead += stateBlockLength;
log = string.Empty;
break;
}
case ServerOpCode.StateBlockTermination:
case ServerOpCode.StateBlockTerminationZipped:
{
short stateBlockId = buffer.Read<short>();
int size = buffer.Read<short>();
//log += "stateBlockId = " + stateBlockId + " Length = " + stateBlockLen +
// (opCode == ServerOpCode.StateBlockTerminationZipped
// ? " Decompressed Size: " + packet.Read<int>()
// : "");
if (opCode == ServerOpCode.StateBlockTerminationZipped)
size = buffer.Read<int>();
buffer.BytesRead += size;
log = string.Empty;
break;
}
case ServerOpCode.ServerDataFinished:
log += "ServerDataFinished()" +
(buffer.BytesRemaining > 0 ? ". Bytes remaining = " + buffer.BytesRemaining : "");
break;
case ServerOpCode.LoadWorld:
string world = buffer.ReadUnicodeString();
//log += "LoadWorld(" +
// world.ToCharArray().Aggregate("World:",
// (current, b) =>
// current + (" 0x" + ((byte) b).ToString("X"))) + ")";
break;
case ServerOpCode.Snapshot:
case ServerOpCode.SnapshotZipped:
{
int snapshotLen = buffer.Read<int>();
if (opCode == ServerOpCode.SnapshotZipped)
buffer.BytesRead += 4;
buffer.BytesRead += snapshotLen;
log = string.Empty;
break;
}
case ServerOpCode.SnapshotFragment:
{
int snapshotFrameNumber = buffer.Read<int>();
int snapshotFragmentNumber = buffer.Read<byte>();
//log += "Frame #: " + snapshotFrameNumber + " Fragment #: " + snapshotFragmentNumber;
log = string.Empty;
buffer.BytesRead += buffer.BytesRemaining;
break;
}
case ServerOpCode.SnapshotTermination:
case ServerOpCode.SnapshotTerminationZipped:
{
int snapshotFrameNumber = buffer.Read<int>();
byte snapshotTerminatorNumber = buffer.Read<byte>();
int compressedLength = opCode == ServerOpCode.SnapshotTerminationZipped
? buffer.Read<int>()
: 0;
short snapshotLength = buffer.Read<short>();
//log += "Length = " + snapshotLength +
// (opCode == ServerOpCode.SnapshotTerminationZipped
// ? " Decompressed Length = " + compressedLength
// : "");
log = string.Empty;
buffer.BytesRead += snapshotLength;
break;
}
// ClientParsePacket + 0x66F is the call to the parsing function ...
// CHostClient->Library->ReadGameData = cgame.dll sub_1901AFB0 which calls sub_190B41C0
// Or find references to "Game client failed to handle a NETCMD_SERVER_GAME_DATA message"
case ServerOpCode.GameData:
ParseGameData(buffer);
return;
case ServerOpCode.Disconnect:
log += "Disconnect";
break;
case ServerOpCode.InitVoiceManager:
log += "InitVoiceManager";
buffer.BytesRead += 3;
break;
// this happens when we enter the game world.
case ServerOpCode.InitVoiceManagers:
buffer.BytesRead += 3;
byte voiceManagers = buffer.Read<byte>();
buffer.BytesRead += 3*voiceManagers;
log += "InitVoiceManagers. Count = " + (int) voiceManagers + " Phase duration: " +
Status.PhaseDuration;
break;
case ServerOpCode.VoiceDisconnect:
byte id = buffer.Read<byte>();
log += "VoiceDisconnect(0x" + id.ToString("x") + ")";
break;
case ServerOpCode.UITrigger:
log += "UITrigger";
break;
case ServerOpCode.RelayInvite:
string inv = buffer.ReadUnicodeString();
log += "RelayInvite(\"" + inv + "\")";
break;
default:
Logging.Write(
string.Format(
"[S->C] opCode unknown: 0x{0}. This is not good. It means we don't know where the next opcode is. This usually means the opcode before it was parsed incorrectly.",
((byte) opCode).ToString("X")));
return;
}
#endregion
if (log != string.Empty)
Logging.Write(log);
}
buffer.BytesRead = oldBytesRead;
}
catch (Exception e)
{
Logging.Write("[NetSniff] Exception during ParseServerPacket: " + e);
}
}
public static void SendFakeRoll(string playerName, int low, int high, int result)
{
SendFakeRoll(string.Format("{0} rolled {1} {2} and got {3}.", playerName, low, high, result));
}
public static void SendFakeRoll(string message)
{
try
{
var buff = new IBuffer(message.Length + 2);
buff.Write((byte) ClientOpCode.ChatRoll);
buff.Write(message);
Logging.Write("Sending fake roll: " + buff.FullHexString + " Bytes written: " + buff.BytesWritten +
" Capacity: " + buff.Capacity);
IGame.Current.HostClient.SendGameData(buff);
}
catch (Exception e)
{
Logging.Write("Exception during SendFakeRoll: " + e);
}
}
}
#region Op codes
public enum ServerOpCode : byte
{
Something = 0x23,
ConnectedToGame = 0x50,
Disconnect = 0x51,
ClearStateData = 0x52,
ServerDataFinished = 0x53,
StateString = 0x54,
StateStringZipped = 0x55,
StateStringFragment = 0x56,
StateStringTermination = 0x57,
StateStringTerminationZipped = 0x58,
LoadWorld = 0x59,
Snapshot = 0x5A,
SnapshotZipped = 0x5B,
SnapshotFragment = 0x5C,
SnapshotTermination = 0x5D,
SnapshotTerminationZipped = 0x5E,
GameData = 0x5F,
StateBlockUpdate = 0x60,
StateBlockUpdateZipped = 0x61,
StateBlockFragment = 0x62,
StateBlockTermination = 0x63,
StateBlockTerminationZipped = 0x64,
Unk2 = 0x65,
Unk1 = 0x67,
Unk5 = 0x68,
Unk4 = 0x69,
InitVoiceManager = 0x6A,
InitVoiceManagers = 0x6B,
VoiceDisconnect = 0x6C,
UITrigger = 0x6D,
RelayInvite = 0x6E,
}
public enum ClientOpCode : byte
{
TeamSelection = 0x1,
ChatAll = 0x2,
ChatTeam = 0x3,
HeroSelect = 0x6,
HeroRandomSelect = 0x7,
PurchaseItem = 0x9, // 0xF5
SellItem = 0xA,
SkillUp = 0x15,
CreateGame = 0x1A,
CastSpellNoTarget = 0x1B,
CastSpellAreaTarget = 0x1C,
CastSpellUnitTarget = 0x1D, // 0xF5
SetCurrentTargetDestination = 0x1E,
SetDestinationEntity = 0x1F,
Stop = 0x20,
Halt = 0x21,
MoveItem = 0x2C,
PurchaseRecipe = 0x2E, // 0xF5
Ready = 0x2F,
GameStart = 0x30,
HeroRepick = 0x32,
TargetControllableUnit = 0x40,
EnteringWorld = 0x49,
ReadinessUpdate = 0x4A,
LockSlot = 0x4D,
AutoBalance = 0x4E,
ChatRoll = 0x52,
SetGameplayOption = 0xBA
}
enum GameDataOpCode : byte
{
ChatAll = 0x2,
ChatTeam = 0x3,
ChatServerAlso = 0x5,
Unk4 = 0xC,
ChatServer = 0xD,
Play2DSound = 0x22,
ServerFrame = 0x24,
Unk7 = 0x29,
Unk8 = 0x2A,
UpdateDestinationEntity = 0x2D,
Unk3 = 0x34,
UpdateDestination = 0x35,
Unk5 = 0x36,
Unk1 = 0x3E,
Unk2 = 0x3F,
OpenShop = 0x41,
UpdateLevel = 0x42,
UpdateExperience = 0x43,
Unk9 = 0x44,
UiTrigger = 0x45,
UpdateSkill = 0x46,
ChatRoll = 0x52,
ChatEmote = 0x53,
HeroKill = 0x60,
HeroKillFirst = 0x61,
KillStreak = 0x62,
KillStreakEnd = 0x63,
PrepareForRunningPhase = 0x64,
MultiKill = 0x65,
KillBuilding = 0x66,
DenyBuilding = 0x67,
KillCourier = 0x68,
Unk10 = 0x69,
TeamKillHero = 0x6A,
StructureDestroyed = 0x6B,
TeamKillKongor = 0x72,
CreepUpgrade = 0x75,
TeamGenocide = 0x79,
ClientConnected = 0x7E,
HeroPick = 0x81,
PlayerReady = 0x85,
AfkWarning = 0x8D,
StructureUnderAttack = 0x95,
HeroKill2 = 0xA0,
LobbyForceSwap = 0xA1,
HeroBan = 0xA2,
BoughtWalkingCourier = 0xA4,
BoughtFlyingCourier = 0xA5,
SetGamePhase = 0xB7,
IntroduceCourier = 0xC3,
AfkMessage = 0xC7
}
#endregion
}
CPlayer.cs:
Code:
using System;
using System.Runtime.InteropServices;
namespace Hon.Classes.Entities
{
class CPlayer : IGameEntity
{
#region Properties
public short Gold
{
get { return Marshal.ReadInt16(UnmanagedPtr, 0x288); }
set { Marshal.WriteInt16(UnmanagedPtr, 0x288, value); }
}
#endregion
public CPlayer(IntPtr unmanagedPtr) : base(unmanagedPtr) {}
public CPlayer(uint entityId) : base(entityId) {}
}
}
StealthTracker.cs:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using Hon.Automation;
using Hon.Classes;
using Hon.Classes.Entities;
using Hon.Classes.Game;
using Hon.Classes.Utility;
namespace Hon.Hacks
{
static class StealthTracker
{
#region Properties
public static IUnitEntity[] EnemyHeroes { get; private set; }
public static bool Active { get; private set; }
public static bool Enabled { get; private set; }
private static readonly Dictionary<IUnitEntity, bool> StealthedUnits;
private static readonly Dictionary<IUnitEntity, bool> TeamLosMap;
private static readonly Dictionary<IUnitEntity, bool> HeroLosMap;
private static bool _initialTickLogged;
#endregion
private static void GamePhaseChange(GamePhase gamePhase)
{
var hero = EasierDeny.Hero;
if (!(gamePhase == GamePhase.Initial || (!Active && gamePhase == GamePhase.Running)) || hero == null)
return;
var gameEntities = IGame.Current.World.WorldTree.All;
EnemyHeroes = gameEntities.Where(entity => entity.Name.StartsWith("Hero_") && entity.IsEnemy(hero)).ToArray();
foreach (var enemy in EnemyHeroes)
{
StealthedUnits.Add(enemy, enemy.IsStealthed);
TeamLosMap.Add(enemy, enemy.WithinOpposingTeamLos);
HeroLosMap.Add(enemy, enemy.WithinOpposingTeamHeroLos);
}
Active = true;
}
static StealthTracker()
{
StealthedUnits = new Dictionary<IUnitEntity, bool>();
TeamLosMap = new Dictionary<IUnitEntity, bool>();
HeroLosMap = new Dictionary<IUnitEntity, bool>();
}
public static void Init()
{
if (Enabled)
Deinit();
Events.OnSetGamePhase += GamePhaseChange;
Active = false;
_initialTickLogged = false;
Enabled = true;
if (Status.InGame)
GamePhaseChange(IGame.Current.GameInfo.Phase);
}
public static void Deinit()
{
Events.OnSetGamePhase -= GamePhaseChange;
Active = false;
_initialTickLogged = false;
Enabled = false;
StealthedUnits.Clear();
TeamLosMap.Clear();
HeroLosMap.Clear();
}
public static void Tick()
{
if (EnemyHeroes == null || !Status.InGame || !Active || !Enabled)
return;
try
{
foreach (var enemy in EnemyHeroes)
{
var withinOurTeamHeroLos = enemy.WithinOpposingTeamHeroLos;
var withinOurTeamLos = enemy.WithinOpposingTeamLos;
// if the enemy has become stealthed
if (enemy.IsStealthed && !StealthedUnits[enemy])
{
// if the enemy is within a hero LOS
if (withinOurTeamHeroLos)
{
// XXX: Should be able to ping map in this case
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has become stealthed near a friendly hero",
enemy.Name));
}
else if (withinOurTeamLos)
{
// XXX: Should be able to ping map in this case
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has become stealthed near an ally (creep/building)",
enemy.Name));
}
else
{
// XXX: CANNOT ping map in this case, possibly track last known position?
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has become stealthed outside team vision", enemy.Name));
}
}
// if the enemy has become unstealthed
else if (!enemy.IsStealthed && StealthedUnits[enemy])
{
// if the enemy is within a hero LOS
if (withinOurTeamHeroLos)
{
// XXX: Should be able to ping map in this case
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has become unstealthed near a friendly hero",
enemy.Name));
}
else if (withinOurTeamLos)
{
// XXX: Should be able to ping map in this case
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has become unstealthed near an ally (creep/building)",
enemy.Name));
}
else
{
// XXX: CANNOT ping map in this case, possibly track last known position?
CChatManager.AddGameChatMessage(
string.Format("^y{0}^* has become unstealthed outside team vision range", enemy.Name));
}
}
// if the enemy, while stealthed, has entered hero los
else if (enemy.IsStealthed && withinOurTeamHeroLos && !HeroLosMap[enemy])
{
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* is near a hero while stealthed", enemy.Name));
}
// if the enemy, while stealthed, has entered team los
else if (enemy.IsStealthed && withinOurTeamLos && !TeamLosMap[enemy])
{
CChatManager.AddGameChatMessage(
string.Format("^r[WARNING] ^y{0}^* has entered team vision range while stealthed", enemy.Name));
}
StealthedUnits[enemy] = enemy.IsStealthed;
HeroLosMap[enemy] = withinOurTeamHeroLos;
TeamLosMap[enemy] = withinOurTeamLos;
}
}
catch (Exception e)
{
Logging.Write("Exception during StealthTracker.Tick: " + e);
}
}
}
}
ZoomHack.cs:
Code:
using System.Runtime.InteropServices;
namespace Hon.Hacks
{
static class ZoomHack
{
static ZoomHack()
{
Events.OnApplicationLoaded += Init;
}
public static void Init()
{
OldMaxCameraHeight = MaxCameraHeight;
OldDefaultMaxCameraHeight = DefaultMaxCameraHeight;
OldFarClipDistance = FarClipDistance;
Enabled = true;
}
#region Properties
private const float NewValue = 10000f;
public static bool Enabled
{
set
{
if (value)
MaxCameraHeight = DefaultMaxCameraHeight = FarClipDistance = NewValue;
else
{
MaxCameraHeight = OldMaxCameraHeight;
DefaultMaxCameraHeight = OldDefaultMaxCameraHeight;
FarClipDistance = OldFarClipDistance;
}
}
}
public static float OldMaxCameraHeight { get; private set; }
public static float OldDefaultMaxCameraHeight { get; private set; }
public static float OldFarClipDistance { get; private set; }
public static float MaxCameraHeight
{
get
{
float[] newValue = new float[1];
Marshal.Copy(Locator.MaxCameraHeight, newValue, 0, 1);
return newValue[0];
}
set
{
float oldMaxCameraHeight = MaxCameraHeight;
if (oldMaxCameraHeight == value)
return;
float[] newValue = new[] {value};
Marshal.Copy(newValue, 0, Locator.MaxCameraHeight, 1);
Logging.Write(string.Format("[ZoomHack] MaxCameraHeight was {0} and is now {1}", oldMaxCameraHeight, MaxCameraHeight));
}
}
public static float DefaultMaxCameraHeight
{
get
{
float[] newValue = new float[1];
Marshal.Copy(Locator.DefaultMaxCameraHeight, newValue, 0, 1);
return newValue[0];
}
set
{
float oldOtherMaxCameraHeight = DefaultMaxCameraHeight;
if (oldOtherMaxCameraHeight == value)
return;
float[] newValue = new[] {value};
Marshal.Copy(newValue, 0, Locator.DefaultMaxCameraHeight, 1);
Logging.Write(string.Format("[ZoomHack] DefaultMaxCameraHeight was {0} and is now {1}", oldOtherMaxCameraHeight, DefaultMaxCameraHeight));
}
}
public static float FarClipDistance
{
get
{
float[] newValue = new float[1];
Marshal.Copy(Locator.FarClipDistance, newValue, 0, 1);
return newValue[0];
}
set
{
float oldFarClipDistance = FarClipDistance;
if (oldFarClipDistance == value)
return;
float[] newValue = new[] {value};
Marshal.Copy(newValue, 0, Locator.FarClipDistance, 1);
Logging.Write(string.Format("[ZoomHack] FarClipDistance was {0} and is now {1}", oldFarClipDistance, FarClipDistance));
}
}
#endregion
}
}
ZoomHack relevant snippets from Locator.cs:
Code:
#region FarClipDistance
public static IntPtr FarClipDistance
{
get
{
IntPtr k2Handle = Utilities.GetModuleHandle("k2.dll");
if (k2Handle == IntPtr.Zero)
return IntPtr.Zero;
IntPtr pPrepCamera = Utilities.GetProcAddress(k2Handle, @"?PrepCamera@CSceneManager@@QAEXABVCCamera@@@Z");
return Marshal.ReadIntPtr(new IntPtr(pPrepCamera.ToInt32() + 0x391));
}
}
#endregion
#region MaxCameraHeight
public static IntPtr MaxCameraHeight
{
get
{
IntPtr pZoomOut = CPlayer__ZoomOut;
if (pZoomOut == IntPtr.Zero)
throw new Exception("CPlayer::ZoomOut not found");
IntPtr pMaxCameraHeight = new IntPtr(pZoomOut.ToInt32() + 0x35);
return Marshal.ReadIntPtr(pMaxCameraHeight);
}
}
#endregion
#region DefaultMaxCameraHeight
public static IntPtr DefaultMaxCameraHeight
{
get
{
IntPtr pZoomOut = CPlayer__ZoomOut;
if (pZoomOut == IntPtr.Zero)
throw new Exception("CPlayer::ZoomOut not found");
IntPtr pMaxZoom = new IntPtr(pZoomOut.ToInt32() + 0x35);
return new IntPtr(Marshal.ReadInt32(pMaxZoom) - 4);
}
}
#endregion
#region MaxCameraHeightICVar
public static IntPtr MaxCameraHeightICVar
{
get
{
IntPtr pVarLocation = new IntPtr(CPlayer__PrepareClientSnapshot.ToInt32() + 0x238);
return Marshal.ReadIntPtr(pVarLocation);
}
}
#endregion