迦南之骚 发表于 2015-12-29 10:05:36

HonorBuddy相关技术介绍(抛砖引玉)

本帖最后由 canaansand 于 2015-12-29 10:47 编辑

随着今年暴雪连续两次针对HonorBuddy的大规模Ban,数十万账号被封,导致很多圈里人认为HB被封是因为技术落后。
这是一个认识误区,HB被封,主要还是太高调,一个HKJ坐拥有上百万用户,严重影响了wow的生存,暴雪不针对你针对谁?

当然如果只谈防封技术,就现在的情况来看,HB有很大的漏洞。
但是,就程序本身的安全性和技术含量来看,HB还是领先的,值得其它研发团队借鉴。
况且即便是今年5月份第一次BanWave后,HB迅速开发了在线防封技术。这一次十二月份引发的大规模Ban,按照HB官方的解释,是因为防封服务器在一个时间段内工作不正常,可能被黑。
如果真如HB官方的说法所言,HB的防封技术已经开始成熟了。

现在楼主就HB中一些特别的技术进行介绍,限于个人知识层面,肯定有理解不对的地方,欢迎大家批评指正。楼主的本意就是抛砖引玉。

开始正题:
       对HB研究的越深入,有一个想法就越来越强烈:开发HB的BossLand这货,完全有实力独自去开发一款MMOrpg了,为什么还一直纠结于做**,难道这货对暴雪就是不爽?当然,只是揣测,毕竟MMOrpg的核心技术是服务端技术,不仅是客户端研究到位就能上。
好了,废话已经很久了,开始上干活。

一、独有的API系统。
      我们先看HB几乎所有代码的命名空间引入语句

using System;
using System.Linq;
using Singular.Dynamics;
using Singular.Helpers;
using Singular.Managers;
using Singular.Settings;
using Styx
using Styx.CommonBot;
using Styx.TreeSharp;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;
using Rest = Singular.Helpers.Rest;
using System.Collections.Generic;
using CommonBehaviors.Actions;
using Styx.WoWInternals;
using System.Drawing;
using Styx.CommonBot.POI;
using Styx.Helpers;
using Styx以及相关的系列语句所表示的命名空间是什么?
HB官方解释:Contains Honorbuddy's WoW API and also bot related classes.
这说明了什么?
HB并没有像其它HKJ那样直接解锁wow,并使用wow内部API,而是使用自己的API函数(尽管这些接口本质上还调用是wow内部函数)。
这就是我为什么说就程序本身来说HB安全性高于**所有直接解锁的玩具,毕竟直接使用wow的API太容易检测了。

二、高效的行为树开发与运行机制。
早期的HB语句是标准的c#语句,比如一段德鲁伊战斗代码:
      public override void Combat()
      {
            if (Battlegrounds.IsInsideBattleground)
            {
                _lastGuid = 0;
            }
            else if (!FightTimer.IsRunning || MyTarget.Guid != _lastGuid)
            {
                FightTimer.Reset();
                FightTimer.Start();
                _lastGuid = MyTarget.Guid;
            }

            if (Me.HealthPercent < 25)
            {
                Healing.UseHealthPotion();
                GiftOfTheNaaru();
            }

            if (Me.ManaPercent < 50 && MyTarget.HealthPercent > Me.HealthPercent)
            {
                Healing.UseManaPotion();
            }

            if (Targeting.Instance.FirstUnit != null && Targeting.Instance.FirstUnit.CurrentTargetGuid == Me.Guid &&
                (!GotTarget || !MyTarget.Combat))
            {
                Targeting.Instance.FirstUnit.Target();
                Thread.Sleep(200);
            }

            if (Me.IsMoving)
            {
                WoWMovement.MoveStop();
                Thread.Sleep(100);
            }

            if (Battlegrounds.IsInsideBattleground)
            {
                WoWPlayer healYou = BestHealTarget();
                if (!Equals(null, healYou) && Me.ManaPercent > 50)
                {
                  healYou.Target();
                  Regrowth();
                  return;
                }
            }

            WoWMovement.Face();

            if (!Me.IsAutoAttacking && GotTarget)
            {
                Lua.DoString("StartAttack()");
            }

            if (CombatChecks && Me.Shapeshift == ShapeshiftForm.Normal && Me.ManaPercent > 60 && Me.HealthPercent < 55)
            {
                Regrowth();
            }

            if (AddsList.Count < 2 && SpellManagerEx.HasSpell("Cat Form") && !HasBearForm)
            {
                TakeForm(ShapeshiftForm.Cat);
                Fluffycat();
            }
            else if (AddsList.Count < 2 && SpellManagerEx.HasSpell("Cat Form") && HasBearForm && Me.ManaPercent > 50 &&
                     Me.HealthPercent > 50)
            {
                Logging.Write("Leaving {0} form with {1}% mana and {2}% health.", Me.Shapeshift,
                              (int)Me.ManaPercent,
                              (int)Me.HealthPercent);
                TakeForm(ShapeshiftForm.Cat);
                Fluffycat();
            }
            else if (SpellManagerEx.HasSpell("Bear Form") || SpellManagerEx.HasSpell("Dire Bear Form"))
            {
                if (!HasBearForm)
                {
                  if (AddsList.Count > 1 && SpellManagerEx.HasSpell("Cat Form"))
                  {
                        Logging.Write("Using Bear Form as there is more than one attacker.");
                  }

                  TakeForm(ShapeshiftForm.Bear);
                }

                if (AddsList.Count > 1 && HasBearForm)
                {
                  Barkskin();
                  Berserk();
                }

                HuggyBear();
            }
            else
            {
                SquishyNoob();
            }

            if (!MyTarget.IsPlayer && FightTimer.ElapsedMilliseconds > 40 * 1000 && MyTarget.HealthPercent > 95)
            {
                Slog(" This " + MyTarget.Name + " is a bugged mob.blacklisting for 1 hour.");

                Blacklist.Add(MyTarget.Guid, TimeSpan.FromHours(1.00));

                KeyboardManager.PressKey('S');
                Thread.Sleep(5 * 1000);
                KeyboardManager.ReleaseKey('S');
                Me.ClearTarget();
                _lastGuid = 0;
            }
      }

大家看到大量的if语句,事实上玩具脚本中存在最多的就是这些if语句,他们充当bot的拟人操作机制的实现。

但是HB的开发者意识到,既然自己开发的是bot,为什么不能用相关的AI技术?而现今游戏开发最常用的AI技术无疑就是行为树
于是下面这样的使用了行为树技术的代码便诞生了。
public static Composite CreateFeralNormalCombat()
      {
            return new PrioritySelector(
                Helpers.Common.EnsureReadyToAttackFromMelee(req => !Me.HasAura("Prowl")),

                Spell.WaitForCast(),

                new Decorator(
                  ret => !Spell.IsGlobalCooldown(),
                  new PrioritySelector(

                        SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.Heal),
                        SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.CombatBuffs),

                        // updated time to death tracking values before we need them
                        new Action( ret => { Me.CurrentTarget.TimeToDeath(); return RunStatus.Failure; } ),

                        Helpers.Common.CreateInterruptBehavior(),

                        Movement.WaitForFacing(),
                        Movement.WaitForLineOfSpellSight(),

                        CreateFeralAoeCombat(),

                        //Single target
                        CreateFeralFaerieFireBehavior(),

                        ///
                        /// Savage Roar - original spell id = 52610, override is 127538.both spells valid but there is not an obvious need for the
                        /// override.Additionally, CanCast AWLAYS fails for 127538 meaning CanCast("Spell Manager") always fails.workaround
                        /// is to cast by id
                        ///
                        CastSavageRoar( on => Me.CurrentTarget, req => !Me.HasAura("Savage Roar") && (Me.ComboPoints > 1 || TalentManager.HasGlyph("Savagery"))),

                        new Throttle( Spell.BuffSelf("Tiger's Fury",
                                 ret => Me.EnergyPercent <= 35
                                       && !Me.ActiveAuras.ContainsKey("Clearcasting")
                                       && !Me.HasAura("Berserk")
                                       )),

                        new Throttle(
                            Spell.BuffSelf("Berserk",
                              ret => Me.HasAura("Tiger's Fury")
                                    && Me.GotTarget() && (Me.CurrentTarget.IsBoss() || Me.CurrentTarget.IsPlayer || (SingularRoutine.CurrentWoWContext != WoWContext.Instances && Me.CurrentTarget.TimeToDeath() >= 20 ))
                              )
                            ),

                        new Throttle( Spell.Cast("Nature's Vigil", ret => Me.HasAura("Berserk"))),
                        Spell.Cast("Incarnation", ret => Me.HasAura("Berserk")),

                        // bite if rip on for awhile or target dying soon
                        Spell.Cast("Ferocious Bite",
                            ret => (Me.ComboPoints >= 5 && Me.CurrentTarget.HealthPercent <= 25 && !Me.CurrentTarget.HasAuraExpired("Rip", 6))
                              || Me.ComboPoints >= Me.CurrentTarget.TimeToDeath(99)
                              ),

                        Spell.Cast("Rip",
                            ret => Me.ComboPoints >= 5
                              && Me.CurrentTarget.HealthPercent > 25
                              && Me.CurrentTarget.TimeToDeath() >= 7
                              && Me.CurrentTarget.GetAuraTimeLeft("Rip", true).TotalSeconds < 3),

                        Spell.Buff("Rake", req => Me.GotTarget() && Me.CurrentTarget.IsWithinMeleeRange),

#if MULTI_DOT_MOONFIRE
                        // multi-dot with if we can (in range and enough energy)
                        new Decorator(
                            req => Common.HasTalent(DruidTalents.LunarInspiration) && Spell.CanCastHack("Moonfire", Me.CurrentTarget),
                            Spell.Cast(
                              "Moonfire",
                              on => Unit.UnitsInCombatWithUsOrOurStuff(40)
                                    .Where(u => !u.IsCrowdControlled() && Me.IsSafelyFacing(u) && !Me.CurrentTarget.HasMyAura("Moonfire") && Spell.CanCastHack("Moonfire", u))
                                    .FirstOrDefault()
                              )
                            ),
#else
                        Spell.Buff( "Moonfire", req => Common.HasTalent(DruidTalents.LunarInspiration)),
#endif

                        Spell.Cast("Shred"),

                        Spell.HandleOffGCD(Spell.Cast("Force of Nature", req => TalentManager.CurrentSpec != WoWSpec.DruidRestoration && Me.CurrentTarget.TimeToDeath() > 8)),

                        new Decorator(
                            ret => MovementManager.IsClassMovementAllowed && Me.IsMoving && Me.CurrentTarget.Distance > (Me.CurrentTarget.IsPlayer ? 10 : 15),
                            new PrioritySelector(
                              CreateFeralWildChargeBehavior(),
                              Spell.BuffSelf("Dash", ret => MovementManager.IsClassMovementAllowed && Spell.GetSpellCooldown("Wild Charge", 0).TotalSeconds < 13 )
                              )
                            )

                        )
                  ),

                Movement.CreateMoveToMeleeBehavior(true)
                );
      }代码的清晰度立即上升了一个档次,少去了很多if语句造成的大量分支带来的开发和运行效率低下。因此后一段代码实现的功能是前一段远远不能比拟的。
这也是我为什么一直坚持在HB原有cc上修改代码的原由,就是因为这个强大的TreeSharp!








[email protected] 发表于 2015-12-29 10:28:52

高端。。。貌似很有道理的样子艾,{:5_247:}

a75507672 发表于 2015-12-29 10:36:04

后面还继续写么,我只是觉得HB功能真是强大

l0stazure 发表于 2015-12-29 11:17:18

这个可以写成历史了!好厉害!

丁丁 发表于 2015-12-29 13:10:47

有什么用啊?年年被封号

大象无形 发表于 2015-12-29 13:37:20

可惜被暴雪重点照顾,想不死都难

china628 发表于 2015-12-29 14:45:28

这个太专业 想点评两句 发现自己根本看不懂 只能胡说一下

astyie 发表于 2015-12-29 15:10:01

个人感觉暴雪之所以封号主要还是根据人物数据来的吧?这样检测出人物数据都是大同小异才封号的..如果楼主所说的用自己的函数来控制角色的行为..理论上应该检测不出来``

迦南之骚 发表于 2015-12-29 15:22:44

astyie 发表于 2015-12-29 15:10
个人感觉暴雪之所以封号主要还是根据人物数据来的吧?这样检测出人物数据都是大同小异才封号的..如果楼主所 ...

在今年之前HB被封号,基本上都是因为用采集挂,因为行为路线重复,且容易被**玩家发现举报。
今年的两次Ban是因为暴雪针对性的检测技术。
说实话,不存在检测不出来的挂,只是开发检测技术需要研发经费,商业运行自然成本为重
HB太高调,已经影响到了wow的生态环境,自然被针对

qwe1231 发表于 2015-12-30 22:51:01

页: [1] 2
查看完整版本: HonorBuddy相关技术介绍(抛砖引玉)