Jelajahi Sumber

BETA: Fixing auto skill learn issue causing server to execute ~600 mysql queries and broadcast 3 * 600 packets because of not optimized for batch methods.
* Implementing Interface ISkillsHolder on L2Character and creating new !PlayerSkillHolder that would replace L2PcInstance while skill learn batch is procede, in order to receive final skills that have to be added, stored and broadcasted.
* Reported by: Raspoutine, Arantir, Maylorian, Tryskell, laikeriz, MELERIX, CorriGaN, djmouse, CorriGaN, djmouse

Rumen Nikiforov 12 tahun lalu
induk
melakukan
796180a180

+ 43 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SkillTreesData.java

@@ -44,7 +44,9 @@ import com.l2jserver.gameserver.model.base.Race;
 import com.l2jserver.gameserver.model.base.SocialClass;
 import com.l2jserver.gameserver.model.base.SubClass;
 import com.l2jserver.gameserver.model.holders.ItemHolder;
+import com.l2jserver.gameserver.model.holders.PlayerSkillHolder;
 import com.l2jserver.gameserver.model.holders.SkillHolder;
+import com.l2jserver.gameserver.model.interfaces.ISkillsHolder;
 import com.l2jserver.gameserver.model.skills.L2Skill;
 
 import gnu.trove.map.hash.TIntObjectHashMap;
@@ -488,6 +490,20 @@ public final class SkillTreesData extends DocumentParser
 	 * @return all available skills for a given {@code player}, {@code classId}, {@code includeByFs} and {@code includeAutoGet}.
 	 */
 	public List<L2SkillLearn> getAvailableSkills(L2PcInstance player, ClassId classId, boolean includeByFs, boolean includeAutoGet)
+	{
+		return getAvailableSkills(player, classId, includeByFs, includeAutoGet, player);
+	}
+	
+	/**
+	 * Gets the available skills.
+	 * @param player the learning skill player.
+	 * @param classId the learning skill class ID.
+	 * @param includeByFs if {@code true} skills from Forgotten Scroll will be included.
+	 * @param includeAutoGet if {@code true} Auto-Get skills will be included.
+	 * @param holder
+	 * @return all available skills for a given {@code player}, {@code classId}, {@code includeByFs} and {@code includeAutoGet}.
+	 */
+	private List<L2SkillLearn> getAvailableSkills(L2PcInstance player, ClassId classId, boolean includeByFs, boolean includeAutoGet, ISkillsHolder holder)
 	{
 		final List<L2SkillLearn> result = new ArrayList<>();
 		final Map<Integer, L2SkillLearn> skills = getCompleteClassSkillTree(classId);
@@ -502,7 +518,7 @@ public final class SkillTreesData extends DocumentParser
 		{
 			if (((includeAutoGet && skill.isAutoGet()) || skill.isLearnedByNpc() || (includeByFs && skill.isLearnedByFS())) && (player.getLevel() >= skill.getGetLevel()))
 			{
-				final L2Skill oldSkill = player.getSkills().get(skill.getSkillId());
+				final L2Skill oldSkill = holder.getKnownSkill(skill.getSkillId());
 				if (oldSkill != null)
 				{
 					if (oldSkill.getLevel() == (skill.getSkillLevel() - 1))
@@ -519,6 +535,32 @@ public final class SkillTreesData extends DocumentParser
 		return result;
 	}
 	
+	public Collection<L2Skill> getAllAvailableSkills(L2PcInstance player, ClassId classId, boolean includeByFs, boolean includeAutoGet)
+	{
+		// Get available skills
+		int unLearnable = 0;
+		PlayerSkillHolder holder = new PlayerSkillHolder();
+		List<L2SkillLearn> learnable = getAvailableSkills(player, classId, includeByFs, includeAutoGet, holder);
+		while (learnable.size() > unLearnable)
+		{
+			for (L2SkillLearn s : learnable)
+			{
+				L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
+				if ((sk == null) || ((sk.getId() == L2Skill.SKILL_DIVINE_INSPIRATION) && !Config.AUTO_LEARN_DIVINE_INSPIRATION && !player.isGM()))
+				{
+					unLearnable++;
+					continue;
+				}
+				
+				holder.addSkill(sk);
+			}
+			
+			// Get new available skills, some skills depend of previous skills to be available.
+			learnable = getAvailableSkills(player, classId, includeByFs, includeAutoGet, holder);
+		}
+		return holder.getSkills().values();
+	}
+	
 	/**
 	 * Gets the available auto get skills.
 	 * @param player the player requesting the Auto-Get skills.

+ 6 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Character.java

@@ -85,6 +85,7 @@ import com.l2jserver.gameserver.model.effects.L2Effect;
 import com.l2jserver.gameserver.model.effects.L2EffectType;
 import com.l2jserver.gameserver.model.entity.Instance;
 import com.l2jserver.gameserver.model.holders.SkillHolder;
+import com.l2jserver.gameserver.model.interfaces.ISkillsHolder;
 import com.l2jserver.gameserver.model.itemcontainer.Inventory;
 import com.l2jserver.gameserver.model.items.L2Item;
 import com.l2jserver.gameserver.model.items.L2Weapon;
@@ -157,7 +158,7 @@ import com.l2jserver.util.Rnd;
  * This link is stored in {@link #_template}
  * @version $Revision: 1.53.2.45.2.34 $ $Date: 2005/04/11 10:06:08 $
  */
-public abstract class L2Character extends L2Object
+public abstract class L2Character extends L2Object implements ISkillsHolder
 {
 	public static final Logger _log = Logger.getLogger(L2Character.class.getName());
 	
@@ -6034,6 +6035,7 @@ public abstract class L2Character extends L2Object
 	 * @param newSkill The L2Skill to add to the L2Character
 	 * @return The L2Skill replaced or null if just added a new L2Skill
 	 */
+	@Override
 	public L2Skill addSkill(L2Skill newSkill)
 	{
 		L2Skill oldSkill = null;
@@ -6281,6 +6283,7 @@ public abstract class L2Character extends L2Object
 	/**
 	 * @return the map containing this character skills.
 	 */
+	@Override
 	public Map<Integer, L2Skill> getSkills()
 	{
 		return _skills;
@@ -6304,6 +6307,7 @@ public abstract class L2Character extends L2Object
 	 * @param skillId The identifier of the L2Skill whose level must be returned
 	 * @return The level of the L2Skill identified by skillId
 	 */
+	@Override
 	public int getSkillLevel(int skillId)
 	{
 		final L2Skill skill = getKnownSkill(skillId);
@@ -6314,6 +6318,7 @@ public abstract class L2Character extends L2Object
 	 * @param skillId The identifier of the L2Skill to check the knowledge
 	 * @return the skill from the known skill.
 	 */
+	@Override
 	public final L2Skill getKnownSkill(int skillId)
 	{
 		return _skills.get(skillId);

+ 17 - 30
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -3102,48 +3102,35 @@ public final class L2PcInstance extends L2Playable
 	 */
 	public int giveAvailableSkills(boolean includedByFs, boolean includeAutoGet)
 	{
-		int unLearnable = 0;
+		long exec_time = System.currentTimeMillis();
 		int skillCounter = 0;
 		
 		// Get available skills
-		List<L2SkillLearn> skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet);
-		while (skills.size() > unLearnable)
+		Collection<L2Skill> skills = SkillTreesData.getInstance().getAllAvailableSkills(this, getClassId(), includedByFs, includeAutoGet);
+		for (L2Skill sk : skills)
 		{
-			for (L2SkillLearn s : skills)
+			if (getSkillLevel(sk.getId()) == -1)
 			{
-				L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel());
-				if ((sk == null) || ((sk.getId() == L2Skill.SKILL_DIVINE_INSPIRATION) && !Config.AUTO_LEARN_DIVINE_INSPIRATION && !canOverrideCond(PcCondOverride.SKILL_CONDITIONS)))
-				{
-					unLearnable++;
-					continue;
-				}
-				
-				if (getSkillLevel(sk.getId()) == -1)
-				{
-					skillCounter++;
-				}
-				
-				// fix when learning toggle skills
-				if (sk.isToggle())
+				skillCounter++;
+			}
+			
+			// fix when learning toggle skills
+			if (sk.isToggle())
+			{
+				final L2Effect toggleEffect = getFirstEffect(sk.getId());
+				if (toggleEffect != null)
 				{
-					L2Effect toggleEffect = getFirstEffect(sk.getId());
-					if (toggleEffect != null)
-					{
-						// stop old toggle skill effect, and give new toggle skill effect back
-						toggleEffect.exit();
-						sk.getEffects(this, this);
-					}
+					// stop old toggle skill effect, and give new toggle skill effect back
+					toggleEffect.exit();
+					sk.getEffects(this, this);
 				}
-				addSkill(sk, true);
 			}
-			
-			// Get new available skills, some skills depend of previous skills to be available.
-			skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet);
+			addSkill(sk, true);
 		}
 		
 		if (Config.AUTO_LEARN_SKILLS && (skillCounter > 0))
 		{
-			sendMessage("You have earned " + skillCounter + " new skills.");
+			sendMessage("You have learned " + skillCounter + " new skills, Execution time: " + ((System.currentTimeMillis() - exec_time)) + " milli-seconds");
 		}
 		return skillCounter;
 	}

+ 74 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/holders/PlayerSkillHolder.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.holders;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.l2jserver.gameserver.model.interfaces.ISkillsHolder;
+import com.l2jserver.gameserver.model.skills.L2Skill;
+
+/**
+ * @author UnAfraid
+ */
+public class PlayerSkillHolder implements ISkillsHolder
+{
+	private final Map<Integer, L2Skill> _skills = new HashMap<>();
+	
+	/**
+	 * @return the map containing this character skills.
+	 */
+	@Override
+	public Map<Integer, L2Skill> getSkills()
+	{
+		return _skills;
+	}
+	
+	/**
+	 * Add a skill to the skills map.<br>
+	 * @param skill
+	 */
+	@Override
+	public L2Skill addSkill(L2Skill skill)
+	{
+		return _skills.put(skill.getId(), skill);
+	}
+	
+	/**
+	 * Return the level of a skill owned by the L2Character.
+	 * @param skillId The identifier of the L2Skill whose level must be returned
+	 * @return The level of the L2Skill identified by skillId
+	 */
+	@Override
+	public int getSkillLevel(int skillId)
+	{
+		final L2Skill skill = getKnownSkill(skillId);
+		return (skill == null) ? -1 : skill.getLevel();
+	}
+	
+	/**
+	 * @param skillId The identifier of the L2Skill to check the knowledge
+	 * @return the skill from the known skill.
+	 */
+	@Override
+	public L2Skill getKnownSkill(int skillId)
+	{
+		return _skills.get(skillId);
+	}
+}

+ 37 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/interfaces/ISkillsHolder.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.interfaces;
+
+import java.util.Map;
+
+import com.l2jserver.gameserver.model.skills.L2Skill;
+
+/**
+ * @author UnAfraid
+ */
+public interface ISkillsHolder
+{
+	public Map<Integer, L2Skill> getSkills();
+	
+	public L2Skill addSkill(L2Skill skill);
+	
+	public L2Skill getKnownSkill(int skillId);
+	
+	public int getSkillLevel(int skillId);
+}