L2AttackableAIScript の問題点
					2012/07/12 L2J-SFJP JOJO


L2AttackableAIScript#main にて L2Attackable なモンスターすべて(5282件)の
ON_ATTACK,ON_KILL,...ON_AGGRO_RANGE_ENTER などのイベント処理を登録している.

◆問題点１

たとえばON_ATTACKイベントについて、プレイヤーがモンスターを攻撃したとき
L2AttackableAIScript#onAttack がコールされる仕組みになっている.

L2AttackableAIScript クラスを継承した派生クラスにおいて、下記のようなコードが書かれており
(例:StakatoNestから抜粋)
|public class StakatoNest extends L2AttackableAIScript {
|	@Override public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet) {
|		・・・中略・・・
|		return super.onAttack(npc, attacker, damage, isPet);	…①
|	}
|	public StakatoNest(int questId, String name, String descr) {
|		super(questId, name, descr);
|		registerMobs(_stakato_mobs);
|	}
|}

① の super.onAttack() は L2AttackableAIScript#onAttack がコールされるから、
全体では、L2AttackableAIScript#onAttack が重複してコールされている.(②と④)

    L2AttackableAIScript#onAttack	…② L2AttackableAIScript.java
                  ↓
         StakatoNest#onAttack		…③ StakatoNest.java
                  ↓
    L2AttackableAIScript#onAttack	…④ StakatoNest.java から ① の super.onAttack() で間接的にコールされる.

① の部分は return null; に改めるべきである.その結果 ④ がコールされなくなり、重複が避けられる.
全体では、下記の順にコールされるよう順番を調整すべきである.

         StakatoNest#onAttack		…③
                  ↓
    L2AttackableAIScript#onAttack	…②

◆問題点２

ON_KILL のみ、他のイベントとは異なり、ThreadPoolManager#scheduleEffect を用いて複数スレッドで平行処理が行われている.

L2Attackable#doDie において、
|	@Override public boolean doDie(L2Character killer) {
|		・・・中略・・・
|				if (getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL) != null) {
|					for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL)) {
|						ThreadPoolManager.getInstance().scheduleEffect(new OnKillNotifyTask(this, quest, player, killer instanceof L2Summon), _onKillDelay);
|					}
|				}
|
|		・・・中略・・・
|		return true;
|	}
|	protected static class OnKillNotifyTask implements Runnable {
|		・・・中略・・・
|		
|		public OnKillNotifyTask(L2Attackable attackable, Quest quest, L2PcInstance killer, boolean isPet) {
|		・・・中略・・・
|		}
|		
|		@Override public void run() {
|			_quest.notifyKill(_attackable, _killer, _isPet);
|		}
|	}

おそらく、モンスターが死亡してから一定時間(_onKillDelay=5秒)の"間"をとってから
ON_KILL イベントを起動したいという意図であろうが、実際には下記のように動作している.

                       モンスター死亡
                 ┌─────┴──────┐
                 5秒                       5秒
             スレッド１                 スレッド２
    L2AttackableAIScript#onKill     StakatoNest#onKill
                                             ↓
                                L2AttackableAIScript#onKill

    ※問題点１も絡んでいることに注意.
    ※実際にはスレッドプールの性質上、2つのスレッドで平行に処理される場合もあれば、
      1つのスレッドで順番に処理される場合もある.
      また、2つのうちのどちらが先か、あとか、は不確定である.

ThreadPoolManager#scheduleEffect は一定時間の"間"をおくだけに使用すべきであり、
複数の onKill を単一スレッドで順次コールすべきである.

                       モンスター死亡
                             │
                            5秒
                     StakatoNest#onKill
                             ↓
                L2AttackableAIScript#onKill
