Permalink
Browse files

[11964] Implement new conditions system

Tree-like design idea by Faramir118, thanks for that!

* Add `conditions` table to store conditions.
* REPLACE current handling of conditions for the *_loot_template tables
  Convert the old conditions in *_loot_template to the new system by SQL-Queries
* ADD support for new conditions to gossip_menu and gossip_menu_option.
  If for these tables no condition_id (new system) is provided, the old conditions will still be used
* Add a small helper python script to contrib/convertConditions, see README there for details
* Add new command to reload the `conditions` table (.reload conditions)
* Add two Meta-Condition types CONDITION_AND (-1) and CONDITION_OR (-2) which are used as:
  value1 (as condition_entry) AND / OR value2 (as condition_entry)

  With these meta-conditions it is possible to create tree like and very complicated combined conditions (like HasAura && (HasItem || HasQuest))

NOTE about conversion:
For easier convertion all the old table data is still preserved, but will be removed eventually (within a circle of the moon approximately)
The python script will not create an optimal initial fill of the `conditions` table. You might want to tweak it manually or suggest some optimized algorithm :)

Signed-off-by: Schmoozerd <schmoozerd@scriptdev2.com>
  • Loading branch information...
Schmoozerd committed Apr 15, 2012
1 parent 641e7ce commit ccfeefbc01734bd25a6499abc46733fd0963a8e0
@@ -0,0 +1,332 @@
+#
+# Copyright (C) 2005-2012 MaNGOS <http://getmangos.com/>
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+import MySQLdb as mdb
+import sys
+
+#global Variables (change as required)
+host = "localhost"
+user = "mangos"
+passw = "mangos"
+# databases format: list of [name, expansion]
+databases = [ ["mangos", 2] ]
+#databases = [ ["zero_db", 0], ["tbcdb", 1], ["udb_clean", 2], ["ytdb", 2] ]
+
+# Should the current conditions table be loaded? (usefull for appending custom content)
+loadOldConditions = 0
+# database, from which the conditions table will be loaded
+database = databases[0][0]
+#database = "mangos_custom"
+
+# be very chatty with debug output
+debug = 0
+
+# global variables for internal use
+false = 0
+true = 1
+processNumConditions = 0
+fUpdates = 0
+
+# Some Helper functions, main code at the bottom
+def isSameCondition(c1, v11, v12, c2, v21, v22):
+ return (c1 == c2) and (v11 == v21) and (v12 == v22)
+#
+
+def compareCondition(c1, v11, v12, c2, v21, v22):
+ if (c1 > c2):
+ return true
+ if (c1 == c2):
+ if (v11 > v21):
+ return true
+ if (v11 == v21):
+ if (v12 > v22):
+ return true
+
+ return false
+#
+
+def insertCondition(c, v1, v2):
+ global old_max
+ old_max = old_max + 1
+ linkedList.append( [old_max, c, v1, v2, database] )
+ if (debug):
+ print "Inserted: [%d, %d, %d, %d], (%s)" % (old_max, c, v1, v2, database)
+#
+
+def findCondition(c, v1, v2):
+ for entry in linkedList:
+ if (isSameCondition(c, v1, v2, entry[1], entry[2], entry[3])):
+ return entry[0]
+ return 0
+#
+
+# Function that processes table tableName for keys keyName1, keyName2, parses the conditions of conditionString, which must select numberOfConditions conditions
+def progressTable(tableName, keyName1, keyName2, conditionString, numberOfConditions):
+ global old_max
+ global processNumConditions
+ global fUpdates
+
+ try:
+ con = mdb.connect(host, user, passw, database);
+ cur = con.cursor()
+ cur.execute('SELECT %s, %s, %s FROM %s; ' % (keyName1, keyName2, conditionString, tableName))
+
+ result = cur.fetchall()
+
+ if (debug):
+ print 'ProgressTable %s in database %s' % (tableName, database)
+
+ for row in result:
+ key1 = row[0]
+ key2 = row[1]
+
+ c1 = v11 = v12 = c2 = v21 = v22 = c3= v31 =v32 = 0
+
+ c1 = row[2]
+ v11 = row[3]
+ v12 = row[4]
+ if (numberOfConditions >= 2):
+ c2 = row[5]
+ v21 = row[6]
+ v22 = row[7]
+ if (numberOfConditions >= 3):
+ c3 = row[8]
+ v31 = row[9]
+ v32 = row[10]
+
+ # Order the conditions of one row from big to slow
+ if (numberOfConditions >= 2) and (compareCondition(c2, v21, v22, c1, v11, v12)):
+ c1, v11, v12, c2, v21, v22 = c2, v21, v22, c1, v11, v12
+ if (numberOfConditions >= 3):
+ if (compareCondition(c3, v31, v32, c2, v21, v22)):
+ c2, v21, v22, c3, v31, v32 = c3, v31, v32, c2, v21, v22
+ if (compareCondition(c2, v21, v22, c1, v11, v12)):
+ c1, v11, v12, c2, v21, v22 = c2, v21, v22, c1, v11, v12
+
+ # How many conditions do we have?
+ rowConditionNumber = 0
+ if (c1 > 0):
+ rowConditionNumber = rowConditionNumber + 1
+ if (c2 > 0):
+ rowConditionNumber = rowConditionNumber + 1
+ if (c3 > 0):
+ rowConditionNumber = rowConditionNumber + 1
+
+ if (rowConditionNumber == 0): #nothing to do
+ continue;
+
+ if (debug):
+ print "Condition(s) for Key (%d, %d): %d, %d, %d -- %d, %d, %d -- %d, %d, %d" % (key1, key2, c1, v11, v12, c2, v21, v22, c3, v31, v32)
+
+ # Just insert
+ if (processNumConditions == 0):
+ if (rowConditionNumber >= 1 and findCondition(c1, v11, v12) == 0):
+ insertCondition(c1, v11, v12)
+ if (rowConditionNumber >= 2 and findCondition(c2, v21, v22) == 0):
+ insertCondition(c2, v21, v22)
+ if (rowConditionNumber >= 3 and findCondition(c3, v31, v32) == 0):
+ insertCondition(c3, v31, v32)
+ continue
+ #
+
+ # Currently processing?
+ if (processNumConditions != rowConditionNumber):
+ continue
+
+ founds = [0, 0, 0]
+ countFound = 0 # helper for error
+ if (rowConditionNumber >= 1):
+ founds[0] = findCondition(c1, v11, v12)
+ if (founds[0] > 0):
+ countFound = countFound + 1
+ if (rowConditionNumber >= 2):
+ founds[1] = findCondition(c2, v21, v22)
+ if (founds[1] > 0):
+ countFound = countFound + 1
+ if (rowConditionNumber >= 3):
+ founds[2] = findCondition(c3, v31, v32)
+ if (founds[2] > 0):
+ countFound = countFound + 1
+
+ if (countFound != rowConditionNumber):
+ print 'An error happened for: Condition(s) for Key (%d, %d): %d, %d, %d -- %d, %d, %d -- %d, %d, %d' % (key1, key2, c1, v11, v12, c2, v21, v22, c3, v31, v32)
+ continue
+
+ last_point = 0
+ #3-vector condition
+ if (rowConditionNumber == 3):
+ # search for 2 match
+ notSearched = [0, 0, 0]
+ notSearched[2] = findCondition(-1, founds[0], founds[1])
+ if (notSearched[2] == 0):
+ notSearched[2] = findCondition(-1, founds[1], founds[0])
+ notSearched[1] = findCondition(-1, founds[0], founds[2])
+ if (notSearched[1] == 0):
+ notSearched[1] = findCondition(-1, founds[2], founds[0])
+ notSearched[0] = findCondition(-1, founds[1], founds[2])
+ if (notSearched[0] == 0):
+ notSearched[0] = findCondition(-1, founds[2], founds[1])
+
+ if (notSearched == [0, 0, 0]): # nothing found
+ insertCondition(-1, founds[1], founds[2])
+ notSearched[0] = old_max
+ for i in range(0, 3):
+ if (notSearched[i] > 0):
+ last_point = findCondition(-1, notSearched[i], founds[i])
+ if (last_point == 0):
+ last_point = findCondition(-1, founds[i], notSearched[i])
+ if (last_point > 0):
+ break
+ if (last_point == 0):
+ for i in range(0, 3):
+ if (notSearched[i] > 0):
+ insertCondition(-1, founds[i], notSearched[i])
+ last_point = old_max
+ break
+
+ #2-vector condition
+ if (rowConditionNumber == 2):
+ # search for 2 match
+ last_point = findCondition(-1, founds[1], founds[0])
+ if (last_point == 0):
+ last_point = findCondition(-1, founds[0], founds[1])
+ if (last_point == 0):
+ #Not found, insert list
+ insertCondition(-1, founds[1], founds[0])
+ last_point = old_max
+
+ #1-vector condition
+ if (rowConditionNumber == 1):
+ last_point = founds[0]
+
+ # Now we must have last_point > 0 (for a condition), and linking to proper place
+ if (last_point > 0 and processNumConditions > 0):
+ #cur.execute('UPDATE %s SET condition_id=%d WHERE %s=%d AND %s=%d; ' % (tableName, last_point, keyName1, key1, keyName2, key2))
+ print >> fUpdates, 'UPDATE %s SET condition_id=%d WHERE %s=%d AND %s=%d;' % (tableName, last_point, keyName1, key1, keyName2, key2)
+
+ except mdb.Error, e:
+ print 'Error %d, %s' % (e.args[0], e.args[1])
+ sys.exit(1)
+
+ finally:
+ if con:
+ con.close()
+## End of Helper function
+
+linkedList = []
+old_max = 0
+linkedList.append( [0, 0, 0, 0, 'initial fill'] )
+
+# Extract old conditions
+if (loadOldConditions):
+ try:
+ con = mdb.connect(host, user, passw, database);
+ cur = con.cursor()
+ cur.execute('SELECT condition_entry, type, value1, value2 FROM conditions')
+
+ for row in cur:
+ linkedList.append( [row[0], row[1], row[2], row[3], 'reloaded from %s' % database ] )
+ old_max = old_max + 1
+ if (row[0] != old_max):
+ print 'An error happened at old_max=%d, entry=%d' % (old_max, row[0])
+
+ print 'Loaded %d values from %s conditions table' % (old_max, database)
+
+ except mdb.Error, e:
+ print 'Error %d, %s' % (e.args[0], e.args[1])
+ sys.exit(1)
+
+ finally:
+ if con:
+ con.close()
+#
+start_entry=old_max
+
+def doTables(db):
+ global processNumConditions
+ global fUpdates
+ global database
+
+ database = db[0]
+ print 'Processing database %s (%d vector conditions)' % (database, processNumConditions)
+ try:
+ if (processNumConditions == 0):
+ fUpdates = open("%s_updates.sql" % database, "w")
+ else:
+ fUpdates = open("%s_updates.sql" % database, "a")
+
+ if (processNumConditions <= 1):
+ progressTable("reference_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("creature_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("gameobject_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("pickpocketing_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("item_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("fishing_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("skinning_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("disenchant_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("mail_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ # Not all expansions have all tables
+ if (db[1] >= 1):
+ progressTable("prospecting_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ if (db[1] >= 2):
+ progressTable("spell_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+ progressTable("milling_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1)
+
+ if (processNumConditions < 3):
+ progressTable("gossip_menu", "entry", "text_id", "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2", 2)
+ progressTable("gossip_menu_option", "menu_id", "id", "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2, cond_3, cond_3_val_1, cond_3_val_2", 3)
+
+ except:
+ print "An error happened here"
+ sys.exit(1)
+
+ finally:
+ fUpdates.close()
+
+# end of helper function doTables
+
+try:
+ fConditions = open("conditions_dump.sql", "w")
+ if (debug):
+ print 'Opened conditions_dump.sql successfully'
+
+ for i in range (0, 4):
+ processNumConditions = i
+ for db in databases:
+ doTables(db)
+ print 'Inserted %d rows for database %s' % (old_max - start_entry, database)
+ start_entry = old_max
+
+ print 'Processed database(s): %s' % databases
+ #create dump
+ print >> fConditions, 'TRUNCATE conditions;'
+ print >> fConditions, 'INSERT INTO conditions VALUES'
+ for i in range(1, old_max):
+ if (linkedList[i][0] != i):
+ print 'AN ERROR HAPPENED for i=%d, liLi[i].entry=%d' % (i, linkedList[i][0])
+ print >> fConditions, '(%d, %d, %d, %d), -- %s' % (linkedList[i][0], linkedList[i][1], linkedList[i][2], linkedList[i][3], linkedList[i][4])
+
+ i = old_max
+ print >> fConditions, '(%d, %d, %d, %d); -- %s' % (linkedList[i][0], linkedList[i][1], linkedList[i][2], linkedList[i][3], linkedList[i][4])
+
+except:
+ print "An error happened"
+ sys.exit(1)
+
+finally:
+ fConditions.close()
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2005-2012 MaNGOS <http://getmangos.com/>
+#
+
+This small Python script is intended to help with the convertion
+of the old condition-system to the new conditions system.
+
+Requirements:
+* Python 2.5 (at least tested with this version)
+* The MySQLdb module (found on http://sourceforge.net/projects/mysql-python/)
+
+You need to edit the ConvertConditions.py file for configure.
+
+First variables that must be set are related to the database connection (host, user, passw).
+
+Then you must specify which databases you want to convert (databases)
+For portability, you must provide the name of the database, and the client expansion for which it is.
+(2 == WotLK, 1 == TBC, 0 == classic)
+
+You can also provide a list of databases to process multiple in one run.
+
+With the next option, you can specify if you want to load a current `conditions` table (loadOldConditions)
+Setting = 1 will load old conditions (default = 0).
+In case you want to load from a database, which is not the first database you want the script to work on,
+you can provide its name with the database variable.
+
+The script will create (and overwrite existing!) sql-files:
+conditions_dump.sql <-- this file stores a possible dump for the conditions table for the old conditions of your databases. (including the loaded)
+
+<DB_NAME>_updates.sql <-- these files store the UPDATE statements to fill the condition_id fields for the tables.
+ One file is created for each provided database.
+
+Backup your data, before you change anything!
Oops, something went wrong.

0 comments on commit ccfeefb

Please sign in to comment.