Преглед на файлове

BETA: Rework of MultisellData table:
* Using DocumentParser
* Implemented ability to use different FileFilter then XMLFilter.
* Using strict filename filter for multisells.
* Implemented strict checks to bind specific multisells to specific npcs to prevent from ability to open every multisell from every merchant npc.
* Reviewed by: Zoey76, MELERIX

Rumen Nikiforov преди 12 години
родител
ревизия
d5b34fddf3

+ 2 - 2
L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java

@@ -63,7 +63,7 @@ import com.l2jserver.gameserver.datatables.InitialEquipmentData;
 import com.l2jserver.gameserver.datatables.ItemTable;
 import com.l2jserver.gameserver.datatables.ManorData;
 import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable;
-import com.l2jserver.gameserver.datatables.MultiSell;
+import com.l2jserver.gameserver.datatables.MultisellData;
 import com.l2jserver.gameserver.datatables.NpcBufferTable;
 import com.l2jserver.gameserver.datatables.NpcTable;
 import com.l2jserver.gameserver.datatables.OfflineTradersTable;
@@ -224,7 +224,7 @@ public class GameServer
 		EnchantHPBonusData.getInstance();
 		MerchantPriceConfigTable.getInstance().loadInstances();
 		TradeController.getInstance();
-		MultiSell.getInstance();
+		MultisellData.getInstance();
 		RecipeData.getInstance();
 		ArmorSetsData.getInstance();
 		FishData.getInstance();

+ 148 - 210
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/MultiSell.java → L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/MultisellData.java

@@ -18,23 +18,18 @@
  */
 package com.l2jserver.gameserver.datatables;
 
-import java.io.File;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import javolution.util.FastList;
 
 import org.w3c.dom.DOMException;
-import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 
 import com.l2jserver.Config;
+import com.l2jserver.gameserver.engines.DocumentParser;
+import com.l2jserver.gameserver.model.StatsSet;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.multisell.Entry;
@@ -46,12 +41,11 @@ import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo;
 import com.l2jserver.gameserver.network.serverpackets.MultiSellList;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.network.serverpackets.UserInfo;
-import com.l2jserver.util.file.filter.XMLFilter;
+import com.l2jserver.gameserver.util.Util;
+import com.l2jserver.util.file.filter.MultisellFilter;
 
-public class MultiSell
+public class MultisellData extends DocumentParser
 {
-	private static final Logger _log = Logger.getLogger(MultiSell.class.getName());
-	
 	public static final int PAGE_SIZE = 40;
 	
 	public static final int PC_BANG_POINTS = -100;
@@ -60,15 +54,145 @@ public class MultiSell
 	
 	private final Map<Integer, ListContainer> _entries = new HashMap<>();
 	
-	protected MultiSell()
+	protected MultisellData()
 	{
+		setCurrentFileFilter(new MultisellFilter());
 		load();
 	}
 	
-	public final void reload()
+	@Override
+	public final void load()
 	{
 		_entries.clear();
-		load();
+		parseDirectory("data/multisell");
+		if (Config.CUSTOM_MULTISELL_LOAD)
+		{
+			parseDirectory("data/multisell/custom");
+		}
+		
+		verify();
+		_log.log(Level.INFO, getClass().getSimpleName() + ": Loaded " + _entries.size() + " lists.");
+	}
+	
+	@Override
+	protected final void parseDocument()
+	{
+		try
+		{
+			int id = Integer.parseInt(getCurrentFile().getName().replaceAll(".xml", ""));
+			int entryId = 1;
+			Node att;
+			final ListContainer list = new ListContainer(id);
+			
+			for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
+			{
+				if ("list".equalsIgnoreCase(n.getNodeName()))
+				{
+					att = n.getAttributes().getNamedItem("applyTaxes");
+					list.setApplyTaxes((att != null) && Boolean.parseBoolean(att.getNodeValue()));
+					
+					att = n.getAttributes().getNamedItem("useRate");
+					if (att != null)
+					{
+						try
+						{
+							
+							list.setUseRate(Double.valueOf(att.getNodeValue()));
+							if (list.getUseRate() <= 1e-6)
+							{
+								throw new NumberFormatException("The value cannot be 0"); // threat 0 as invalid value
+							}
+						}
+						catch (NumberFormatException e)
+						{
+							
+							try
+							{
+								list.setUseRate(Config.class.getField(att.getNodeValue()).getDouble(Config.class));
+							}
+							catch (Exception e1)
+							{
+								_log.warning(e1.getMessage() + getCurrentDocument().getLocalName());
+								list.setUseRate(1.0);
+							}
+							
+						}
+						catch (DOMException e)
+						{
+							_log.warning(e.getMessage() + getCurrentDocument().getLocalName());
+						}
+					}
+					
+					att = n.getAttributes().getNamedItem("maintainEnchantment");
+					list.setMaintainEnchantment((att != null) && Boolean.parseBoolean(att.getNodeValue()));
+					
+					for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
+					{
+						if ("item".equalsIgnoreCase(d.getNodeName()))
+						{
+							Entry e = parseEntry(d, entryId++, list);
+							list.getEntries().add(e);
+						}
+						else if ("npcs".equalsIgnoreCase(d.getNodeName()))
+						{
+							for (Node b = d.getFirstChild(); b != null; b = b.getNextSibling())
+							{
+								if ("npc".equalsIgnoreCase(b.getNodeName()))
+								{
+									if (Util.isDigit(b.getTextContent()))
+									{
+										list.allowNpc(Integer.parseInt(b.getTextContent()));
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			_entries.put(id, list);
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error in file " + getCurrentFile(), e);
+		}
+	}
+	
+	private final Entry parseEntry(Node n, int entryId, ListContainer list)
+	{
+		Node first = n.getFirstChild();
+		final Entry entry = new Entry(entryId);
+		
+		NamedNodeMap attrs;
+		Node att;
+		StatsSet set;
+		
+		for (n = first; n != null; n = n.getNextSibling())
+		{
+			if ("ingredient".equalsIgnoreCase(n.getNodeName()))
+			{
+				attrs = n.getAttributes();
+				set = new StatsSet();
+				for (int i = 0; i < attrs.getLength(); i++)
+				{
+					att = attrs.item(i);
+					set.set(att.getNodeName(), att.getNodeValue());
+				}
+				entry.addIngredient(new Ingredient(set));
+			}
+			else if ("production".equalsIgnoreCase(n.getNodeName()))
+			{
+				attrs = n.getAttributes();
+				set = new StatsSet();
+				for (int i = 0; i < attrs.getLength(); i++)
+				{
+					att = attrs.item(i);
+					set.set(att.getNodeName(), att.getNodeValue());
+				}
+				entry.addProduct(new Ingredient(set));
+			}
+		}
+		
+		return entry;
 	}
 	
 	/**
@@ -109,6 +233,12 @@ public class MultiSell
 			return;
 		}
 		
+		if (((npc != null) && !template.isNpcAllowed(npc.getNpcId())) || ((npc == null) && template.isNpcOnly()))
+		{
+			_log.warning(getClass().getSimpleName() + ": player " + player + " attempted to open multisell " + listId + " from npc " + npc + " which is not allowed!");
+			return;
+		}
+		
 		final PreparedListContainer list = new PreparedListContainer(template, inventoryOnly, player, npc);
 		
 		// Pass through this only when multipliers are different from 1
@@ -211,193 +341,6 @@ public class MultiSell
 		}
 	}
 	
-	private final void load()
-	{
-		Document doc = null;
-		int id = 0;
-		List<File> files = new FastList<>();
-		hashFiles("data/multisell", files);
-		if (Config.CUSTOM_MULTISELL_LOAD)
-		{
-			hashFiles("data/multisell/custom", files);
-		}
-		
-		for (File f : files)
-		{
-			try
-			{
-				id = Integer.parseInt(f.getName().replaceAll(".xml", ""));
-				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-				factory.setValidating(false);
-				factory.setIgnoringComments(true);
-				doc = factory.newDocumentBuilder().parse(f);
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error loading file " + f, e);
-				continue;
-			}
-			
-			try
-			{
-				ListContainer list = parseDocument(doc);
-				list.setListId(id);
-				_entries.put(id, list);
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, getClass().getSimpleName() + ": Error in file " + f, e);
-			}
-		}
-		verify();
-		_log.log(Level.INFO, getClass().getSimpleName() + ": Loaded " + _entries.size() + " lists.");
-	}
-	
-	private final ListContainer parseDocument(Document doc)
-	{
-		int entryId = 1;
-		Node attribute;
-		ListContainer list = new ListContainer();
-		
-		for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
-		{
-			if ("list".equalsIgnoreCase(n.getNodeName()))
-			{
-				attribute = n.getAttributes().getNamedItem("applyTaxes");
-				if (attribute == null)
-				{
-					list.setApplyTaxes(false);
-				}
-				else
-				{
-					list.setApplyTaxes(Boolean.parseBoolean(attribute.getNodeValue()));
-				}
-				
-				attribute = n.getAttributes().getNamedItem("useRate");
-				if (attribute != null)
-				{
-					try
-					{
-						
-						list.setUseRate(Double.valueOf(attribute.getNodeValue()));
-						if (list.getUseRate() <= 1e-6)
-						{
-							throw new NumberFormatException("The value cannot be 0"); // threat 0 as invalid value
-						}
-					}
-					catch (NumberFormatException e)
-					{
-						
-						try
-						{
-							list.setUseRate(Config.class.getField(attribute.getNodeValue()).getDouble(Config.class));
-						}
-						catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException | DOMException e1)
-						{
-							_log.warning(e.getMessage() + doc.getLocalName());
-							list.setUseRate(1.0);
-						}
-						
-					}
-					catch (DOMException e)
-					{
-						_log.warning(e.getMessage() + doc.getLocalName());
-					}
-				}
-				
-				attribute = n.getAttributes().getNamedItem("maintainEnchantment");
-				if (attribute == null)
-				{
-					list.setMaintainEnchantment(false);
-				}
-				else
-				{
-					list.setMaintainEnchantment(Boolean.parseBoolean(attribute.getNodeValue()));
-				}
-				
-				for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
-				{
-					if ("item".equalsIgnoreCase(d.getNodeName()))
-					{
-						Entry e = parseEntry(d, entryId++, list);
-						list.getEntries().add(e);
-					}
-				}
-			}
-			else if ("item".equalsIgnoreCase(n.getNodeName()))
-			{
-				Entry e = parseEntry(n, entryId++, list);
-				list.getEntries().add(e);
-			}
-		}
-		
-		return list;
-	}
-	
-	private final Entry parseEntry(Node n, int entryId, ListContainer list)
-	{
-		Node attribute;
-		Node first = n.getFirstChild();
-		final Entry entry = new Entry(entryId);
-		
-		for (n = first; n != null; n = n.getNextSibling())
-		{
-			if ("ingredient".equalsIgnoreCase(n.getNodeName()))
-			{
-				int id = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
-				long count = Long.parseLong(n.getAttributes().getNamedItem("count").getNodeValue());
-				boolean isTaxIngredient, mantainIngredient;
-				
-				attribute = n.getAttributes().getNamedItem("isTaxIngredient");
-				if (attribute != null)
-				{
-					isTaxIngredient = Boolean.parseBoolean(attribute.getNodeValue());
-				}
-				else
-				{
-					isTaxIngredient = false;
-				}
-				
-				attribute = n.getAttributes().getNamedItem("maintainIngredient");
-				if (attribute != null)
-				{
-					mantainIngredient = Boolean.parseBoolean(attribute.getNodeValue());
-				}
-				else
-				{
-					mantainIngredient = false;
-				}
-				
-				entry.addIngredient(new Ingredient(id, count, isTaxIngredient, mantainIngredient));
-			}
-			else if ("production".equalsIgnoreCase(n.getNodeName()))
-			{
-				int id = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
-				long count = (long) (Long.parseLong(n.getAttributes().getNamedItem("count").getNodeValue()) * list.getUseRate());
-				
-				entry.addProduct(new Ingredient(id, count, false, false));
-			}
-		}
-		
-		return entry;
-	}
-	
-	private final void hashFiles(String dirname, List<File> hash)
-	{
-		File dir = new File(Config.DATAPACK_ROOT, dirname);
-		if (!dir.exists())
-		{
-			_log.log(Level.WARNING, getClass().getSimpleName() + ": Dir " + dir.getAbsolutePath() + " not exists");
-			return;
-		}
-		
-		File[] files = dir.listFiles(new XMLFilter());
-		for (File f : files)
-		{
-			hash.add(f);
-		}
-	}
-	
 	private final void verify()
 	{
 		ListContainer list;
@@ -434,22 +377,17 @@ public class MultiSell
 			case FAME:
 				return true;
 			default:
-				if (ing.getTemplate() != null)
-				{
-					return true;
-				}
+				return ing.getTemplate() != null;
 		}
-		
-		return false;
 	}
 	
-	public static MultiSell getInstance()
+	public static MultisellData getInstance()
 	{
 		return SingletonHolder._instance;
 	}
 	
 	private static class SingletonHolder
 	{
-		protected static final MultiSell _instance = new MultiSell();
+		protected static final MultisellData _instance = new MultisellData();
 	}
 }

+ 15 - 2
L2J_Server_BETA/java/com/l2jserver/gameserver/engines/DocumentParser.java

@@ -19,6 +19,7 @@
 package com.l2jserver.gameserver.engines;
 
 import java.io.File;
+import java.io.FileFilter;
 import java.util.logging.Logger;
 
 import javax.xml.parsers.DocumentBuilder;
@@ -51,6 +52,8 @@ public abstract class DocumentParser
 	
 	private Document _currentDocument;
 	
+	private FileFilter _currentFilter = null;
+	
 	/**
 	 * This method can be used to load/reload the data.<br>
 	 * It's highly recommended to clear the data storage, either the list or map.
@@ -74,7 +77,7 @@ public abstract class DocumentParser
 	 */
 	protected void parseFile(File f)
 	{
-		if (!xmlFilter.accept(f))
+		if (!getCurrentFileFilter().accept(f))
 		{
 			_log.warning(getClass().getSimpleName() + ": Could not parse " + f.getName() + " is not a file or it doesn't exist!");
 			return;
@@ -171,7 +174,7 @@ public abstract class DocumentParser
 			{
 				parseDirectory(f, recursive);
 			}
-			else if (xmlFilter.accept(f))
+			else if (getCurrentFileFilter().accept(f))
 			{
 				parseFile(f);
 			}
@@ -281,6 +284,16 @@ public abstract class DocumentParser
 		return (b == null) ? "" : b.getNodeValue();
 	}
 	
+	public void setCurrentFileFilter(FileFilter filter)
+	{
+		_currentFilter = filter;
+	}
+	
+	public FileFilter getCurrentFileFilter()
+	{
+		return _currentFilter != null ? _currentFilter : xmlFilter;
+	}
+	
 	/**
 	 * Simple XML error handler.
 	 * @author Zoey76

+ 6 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/multisell/Ingredient.java

@@ -19,6 +19,7 @@
 package com.l2jserver.gameserver.model.multisell;
 
 import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.model.StatsSet;
 import com.l2jserver.gameserver.model.items.L2Armor;
 import com.l2jserver.gameserver.model.items.L2Item;
 import com.l2jserver.gameserver.model.items.L2Weapon;
@@ -36,6 +37,11 @@ public class Ingredient
 	private L2Item _template = null;
 	private ItemInfo _itemInfo = null;
 	
+	public Ingredient(StatsSet set)
+	{
+		this(set.getInteger("id"), set.getLong("count"), set.getBool("isTaxIngredient", false), set.getBool("maintainIngredient", false));
+	}
+	
 	public Ingredient(int itemId, long itemCount, boolean isTaxIngredient, boolean maintainIngredient)
 	{
 		_itemId = itemId;

+ 24 - 16
L2J_Server_BETA/java/com/l2jserver/gameserver/model/multisell/ListContainer.java

@@ -19,7 +19,9 @@
 package com.l2jserver.gameserver.model.multisell;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * @author DS
@@ -31,18 +33,10 @@ public class ListContainer
 	protected boolean _maintainEnchantment = false;
 	protected double _useRate = 1.0;
 	
-	protected List<Entry> _entries;
+	protected List<Entry> _entries = new ArrayList<>();
+	protected Set<Integer> _npcsAllowed = null;
 	
-	public ListContainer()
-	{
-		_entries = new ArrayList<>();
-	}
-	
-	/**
-	 * This constructor used in PreparedListContainer only ArrayList not created
-	 * @param listId
-	 */
-	protected ListContainer(int listId)
+	public ListContainer(int listId)
 	{
 		_listId = listId;
 	}
@@ -52,11 +46,6 @@ public class ListContainer
 		return _entries;
 	}
 	
-	public final void setListId(int listId)
-	{
-		_listId = listId;
-	}
-	
 	public final int getListId()
 	{
 		return _listId;
@@ -96,4 +85,23 @@ public class ListContainer
 	{
 		return _maintainEnchantment;
 	}
+	
+	public void allowNpc(int npcId)
+	{
+		if (_npcsAllowed == null)
+		{
+			_npcsAllowed = new HashSet<>();
+		}
+		_npcsAllowed.add(npcId);
+	}
+	
+	public boolean isNpcAllowed(int npcId)
+	{
+		return (_npcsAllowed == null) || _npcsAllowed.contains(npcId);
+	}
+	
+	public boolean isNpcOnly()
+	{
+		return _npcsAllowed != null;
+	}
 }

+ 4 - 4
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/MultiSellChoose.java

@@ -23,7 +23,7 @@ import java.util.ArrayList;
 import javolution.util.FastList;
 
 import com.l2jserver.Config;
-import com.l2jserver.gameserver.datatables.MultiSell;
+import com.l2jserver.gameserver.datatables.MultisellData;
 import com.l2jserver.gameserver.model.Elementals;
 import com.l2jserver.gameserver.model.L2Augmentation;
 import com.l2jserver.gameserver.model.actor.L2Npc;
@@ -216,7 +216,7 @@ public class MultiSellChoose extends L2GameClientPacket
 					}
 					if (e.getItemId() < 0)
 					{
-						if (!MultiSell.checkSpecialIngredient(e.getItemId(), e.getItemCount() * _amount, player))
+						if (!MultisellData.checkSpecialIngredient(e.getItemId(), e.getItemCount() * _amount, player))
 						{
 							return;
 						}
@@ -247,7 +247,7 @@ public class MultiSellChoose extends L2GameClientPacket
 					{
 						if (e.getItemId() < 0)
 						{
-							if (!MultiSell.getSpecialIngredient(e.getItemId(), e.getItemCount() * _amount, player))
+							if (!MultisellData.getSpecialIngredient(e.getItemId(), e.getItemCount() * _amount, player))
 							{
 								return;
 							}
@@ -387,7 +387,7 @@ public class MultiSellChoose extends L2GameClientPacket
 					{
 						if (e.getItemId() < 0)
 						{
-							MultiSell.addSpecialProduct(e.getItemId(), e.getItemCount() * _amount, player);
+							MultisellData.addSpecialProduct(e.getItemId(), e.getItemCount() * _amount, player);
 						}
 						else
 						{

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/MultiSellList.java

@@ -18,7 +18,7 @@
  */
 package com.l2jserver.gameserver.network.serverpackets;
 
-import static com.l2jserver.gameserver.datatables.MultiSell.PAGE_SIZE;
+import static com.l2jserver.gameserver.datatables.MultisellData.PAGE_SIZE;
 
 import com.l2jserver.gameserver.model.multisell.Entry;
 import com.l2jserver.gameserver.model.multisell.Ingredient;

+ 40 - 0
L2J_Server_BETA/java/com/l2jserver/util/file/filter/MultisellFilter.java

@@ -0,0 +1,40 @@
+/*
+ * 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.util.file.filter;
+
+import java.io.File;
+import java.io.FileFilter;
+
+/**
+ * Specialized {@link FileFilter} class.<br>
+ * Accepts <b>files</b> matching "numbers".xml only.
+ * @author UnAfraid
+ */
+public class MultisellFilter extends XMLFilter
+{
+	@Override
+	public boolean accept(File f)
+	{
+		if (!super.accept(f))
+		{
+			return false;
+		}
+		return f.getName().matches("\\d+\\.xml");
+	}
+}