Merge branch 'master' of zorba@192.168.100.11:questhelper
[QuestHelper.git] / upgrade.lua
blob1a01598b450d15e084c59bad7108a9f623691556
1 QuestHelper_File["upgrade.lua"] = "Development Version"
2 QuestHelper_Loadtime["upgrade.lua"] = GetTime()
4 QuestHelper_Zones =
5 {{[0]="Kalimdor",
6 [1]="Ashenvale",
7 [2]="Azshara",
8 [3]="Azuremyst Isle",
9 [4]="Bloodmyst Isle",
10 [5]="Darkshore",
11 [6]="Darnassus",
12 [7]="Desolace",
13 [8]="Durotar",
14 [9]="Dustwallow Marsh",
15 [10]="Felwood",
16 [11]="Feralas",
17 [12]="Moonglade",
18 [13]="Mulgore",
19 [14]="Orgrimmar",
20 [15]="Silithus",
21 [16]="Stonetalon Mountains",
22 [17]="Tanaris",
23 [18]="Teldrassil",
24 [19]="The Barrens",
25 [20]="The Exodar",
26 [21]="Thousand Needles",
27 [22]="Thunder Bluff",
28 [23]="Un'Goro Crater",
29 [24]="Winterspring"},
30 {[0]="Eastern Kingdoms",
31 [1]="Alterac Mountains",
32 [2]="Arathi Highlands",
33 [3]="Badlands",
34 [4]="Blasted Lands",
35 [5]="Burning Steppes",
36 [6]="Deadwind Pass",
37 [7]="Dun Morogh",
38 [8]="Duskwood",
39 [9]="Eastern Plaguelands",
40 [10]="Elwynn Forest",
41 [11]="Eversong Woods",
42 [12]="Ghostlands",
43 [13]="Hillsbrad Foothills",
44 [14]="Ironforge",
45 [15]="Isle of Quel'Danas",
46 [16]="Loch Modan",
47 [17]="Redridge Mountains",
48 [18]="Searing Gorge",
49 [19]="Silvermoon City",
50 [20]="Silverpine Forest",
51 [21]="Stormwind City",
52 [22]="Stranglethorn Vale",
53 [23]="Swamp of Sorrows",
54 [24]="The Hinterlands",
55 [25]="Tirisfal Glades",
56 [26]="Undercity",
57 [27]="Western Plaguelands",
58 [28]="Westfall",
59 [29]="Wetlands"},
60 {[0]="Outland",
61 [1]="Blade's Edge Mountains",
62 [2]="Hellfire Peninsula",
63 [3]="Nagrand",
64 [4]="Netherstorm",
65 [5]="Shadowmoon Valley",
66 [6]="Shattrath City",
67 [7]="Terokkar Forest",
68 [8]="Zangarmarsh"},
69 {[0]="Northrend",
70 [1]="Borean Tundra",
71 [2]="Crystalsong Forest",
72 [3]="Dalaran",
73 [4]="Dragonblight",
74 [5]="Grizzly Hills",
75 [6]="Howling Fjord",
76 [7]="Icecrown",
77 [8]="Sholazar Basin",
78 [9]="The Storm Peaks",
79 [10]="Wintergrasp",
80 [11]="Zul'Drak"},
82 [-77]={[0]="ScarletEnclave_Continent", [1]="ScarletEnclave"},
86 -- This will be translated to [LOCALE_NAME] = INDEX by QuestHelper_BuildZoneLookup.
87 -- Additionally, [CONT_INDEX][ZONE_INDEX] = INDEX will also be added.
88 QuestHelper_IndexLookup =
89 {["Hinterlands"] = {42, 2, 24},
90 ["Moonglade"] = {20, 1, 12},
91 ["ThousandNeedles"] = {14, 1, 21},
92 ["Winterspring"] = {19, 1, 24},
93 ["BloodmystIsle"] = {9, 1, 4},
94 ["TerokkarForest"] = {55, 3, 7},
95 ["Arathi"] = {39, 2, 2},
96 ["EversongWoods"] = {41, 2, 11},
97 ["Dustwallow"] = {10, 1, 9},
98 ["Badlands"] = {27, 2, 3},
99 ["Darkshore"] = {16, 1, 5},
100 ["Ogrimmar"] = {1, 1, 14},
101 ["BladesEdgeMountains"] = {54, 3, 1},
102 ["Undercity"] = {45, 2, 26},
103 ["Desolace"] = {4, 1, 7},
104 ["Netherstorm"] = {59, 3, 4},
105 ["Barrens"] = {11, 1, 19},
106 ["Tanaris"] = {8, 1, 17},
107 ["Stormwind"] = {36, 2, 21},
108 ["Zangarmarsh"] = {57, 3, 8},
109 ["Durotar"] = {7, 1, 8},
110 ["Hellfire"] = {56, 3, 2},
111 ["Silithus"] = {5, 1, 15},
112 ["ShattrathCity"] = {60, 3, 6},
113 ["ShadowmoonValley"] = {53, 3, 5},
114 ["SwampOfSorrows"] = {46, 2, 23},
115 ["SilvermoonCity"] = {52, 2, 19},
116 ["Darnassis"] = {21, 1, 6},
117 ["AzuremystIsle"] = {3, 1, 3},
118 ["Elwynn"] = {37, 2, 10},
119 ["Stranglethorn"] = {38, 2, 22},
120 ["EasternPlaguelands"] = {34, 2, 9},
121 ["Duskwood"] = {31, 2, 8},
122 ["WesternPlaguelands"] = {50, 2, 27},
123 ["Westfall"] = {49, 2, 28},
124 ["Ashenvale"] = {2, 1, 1},
125 ["Teldrassil"] = {24, 1, 18},
126 ["Redridge"] = {30, 2, 17},
127 ["UngoroCrater"] = {18, 1, 23},
128 ["Mulgore"] = {22, 1, 13},
129 ["Ironforge"] = {25, 2, 14},
130 ["Felwood"] = {13, 1, 10},
131 ["Hilsbrad"] = {48, 2, 13},
132 ["DeadwindPass"] = {47, 2, 6},
133 ["BurningSteppes"] = {40, 2, 5},
134 ["Ghostlands"] = {44, 2, 12},
135 ["Tirisfal"] = {43, 2, 25},
136 ["TheExodar"] = {12, 1, 20},
137 ["Wetlands"] = {51, 2, 29},
138 ["SearingGorge"] = {32, 2, 18},
139 ["BlastedLands"] = {33, 2, 4},
140 ["Silverpine"] = {35, 2, 20},
141 ["LochModan"] = {29, 2, 16},
142 ["Feralas"] = {17, 1, 11},
143 ["DunMorogh"] = {28, 2, 7},
144 ["Alterac"] = {26, 2, 1},
145 ["ThunderBluff"] = {23, 1, 22},
146 ["Aszhara"] = {15, 1, 2},
147 ["StonetalonMountains"] = {6, 1, 16},
148 ["Nagrand"] = {58, 3, 3},
149 ["Kalimdor"] = {61, 1, 0},
150 ["Azeroth"] = {62, 2, 0},
151 ["Expansion01"] = {63, 3, 0},
152 ["Sunwell"] = {64, 2, 15},
154 ["Northrend"] = {76, 4, 0},
155 ["BoreanTundra"] = {65, 4, 1},
156 ["CrystalsongForest"] = {66, 4, 2},
157 ["Dalaran"] = {67, 4, 3},
158 ["Dragonblight"] = {68, 4, 4},
159 ["GrizzlyHills"] = {69, 4, 5},
160 ["HowlingFjord"] = {70, 4, 6},
161 ["IcecrownGlacier"] = {71, 4, 7},
162 ["SholazarBasin"] = {72, 4, 8},
163 ["TheStormPeaks"] = {73, 4, 9},
164 ["LakeWintergrasp"] = {74, 4, 10},
165 ["ZulDrak"] = {75, 4, 11},
167 ["ScarletEnclave_Continent"] = {77, -77, 0},
168 ["ScarletEnclave"] = {78, -77, 1},
171 QuestHelper_RestrictedZones = { -- Everything defaults to "nil"
172 [78] = 1,
175 local next_index = 1
176 for i, j in pairs(QuestHelper_IndexLookup) do next_index = math.max(next_index, j[1]+1) end
178 -- Maps zone names and indexes to a two element array, containing zone index a continent/zone
179 QuestHelper_ZoneLookup = {}
181 -- Maps indexes to zone names.
182 QuestHelper_NameLookup = {}
184 local built = false
186 function QuestHelper_BuildZoneLookup()
187 if built then return end
188 built = true
190 if GetMapContinents and GetMapZones then
191 -- Called from inside the WoW client.
193 local original_lookup, original_zones = QuestHelper_IndexLookup, QuestHelper_Zones
194 QuestHelper_IndexLookup = {}
195 QuestHelper_Zones = {}
197 for c, cname in pairs(QuestHelper.Astrolabe:GetMapVirtualContinents()) do
198 QuestHelper_Zones[c] = {}
199 local tpx = QuestHelper.Astrolabe:GetMapVirtualZones(c)
200 tpx[0] = cname
201 for z, zname in pairs(tpx) do
203 local base_name = QuestHelper.Astrolabe:GetMapTexture(c, z)
205 local index = original_lookup[base_name] and original_lookup[base_name][1]
207 local altered_index = "!!! QuestHelper_IndexLookup entry needs update: [%q] = {%s, %s, %s}"
208 local altered_zone = "!!! QuestHelper_Zones entry needs update: [%s][%s] = %q -- was %s"
210 if not index then
211 QuestHelper:TextOut(altered_index:format(base_name, next_index, c, z))
212 next_index = next_index + 1
213 else
214 if QuestHelper_Locale == "enUS" then
215 if original_lookup[base_name][2] ~= c or original_lookup[base_name][3] ~= z then
216 QuestHelper:TextOut(altered_index:format(base_name, index, c, z))
219 if not original_zones[c] or original_zones[c][z] ~= zname then
220 QuestHelper:TextOut(altered_zone:format(c, z, zname, original_zones[c] and original_zones[c][z] or "missing"))
224 local pair = {c, z}
225 if not QuestHelper_IndexLookup[c] then QuestHelper_IndexLookup[c] = {} end
226 QuestHelper_IndexLookup[c][z] = index
227 QuestHelper_IndexLookup[zname] = index
229 QuestHelper_NameLookup[index] = zname
231 QuestHelper_ZoneLookup[zname] = pair
232 QuestHelper_ZoneLookup[index] = pair
234 QuestHelper_Zones[c][z] = zname
239 for name, index in pairs(original_lookup) do
240 if index[2] == -1 then
241 assert(not QuestHelper_IndexLookup[name])
242 QuestHelper_IndexLookup[name] = index[1]
245 else
246 -- Called from some lua script.
247 local original_lookup = QuestHelper_IndexLookup
248 QuestHelper_IndexLookup = {}
250 for base_name, i in pairs(original_lookup) do
251 local index = i[1]
252 local pair = {i[2], i[3]}
253 local name = QuestHelper_Zones[pair[1]][pair[2]]
255 assert(index and name)
257 if not QuestHelper_IndexLookup[pair[1]] then QuestHelper_IndexLookup[pair[1]] = {} end
258 QuestHelper_IndexLookup[pair[1]][pair[2]] = index
259 QuestHelper_IndexLookup[name] = index
261 QuestHelper_NameLookup[index] = name
263 QuestHelper_ZoneLookup[name] = pair
264 QuestHelper_ZoneLookup[index] = pair
269 local convert_lookup =
270 {{2, 15, 3, 9, 16, 21, 4, 7, 10, 13, 17, 20, 22, 1, 5, 6, 8, 24, 11, 12, 14, 23, 18, 19},
271 {26, 39, 27, 33, 40, 47, 28, 31, 34, 37, 41, 44, 48, 25, 29, 30, 32, 52, 35, 36, 38, 46, 42, 43, 45, 50, 49, 51},
272 {54, 56, 58, 59, 53, 60, 55, 57}}
274 function QuestHelper_ValidPosition(c, z, x, y)
275 return type(x) == "number" and type(y) == "number" and x > -0.1 and y > -0.1 and x < 1.1 and y < 1.1 and c and convert_lookup[c] and z and convert_lookup[c][z] and true
278 function QuestHelper_PrunePositionList(list)
279 if type(list) ~= "table" then
280 return nil
283 local i = 1
284 while i <= #list do
285 local pos = list[i]
286 if QuestHelper_ValidPosition(unpack(list[i])) and type(pos[5]) == "number" and pos[5] >= 1 then
287 i = i + 1
288 else
289 local rem = table.remove(list, i)
293 return #list > 0 and list or nil
296 local function QuestHelper_ConvertPosition(pos)
297 pos[2] = convert_lookup[pos[1]][pos[2]]
298 table.remove(pos, 1)
301 local function QuestHelper_ConvertPositionList(list)
302 if list then
303 for i, pos in pairs(list) do
304 QuestHelper_ConvertPosition(pos)
309 local function QuestHelper_ConvertFaction(faction)
310 if faction == 1 or faction == "Alliance" or faction == FACTION_ALLIANCE then return 1
311 elseif faction == 2 or faction == "Horde" or faction == FACTION_HORDE then return 2
312 else
313 assert(false, "Unknown faction: "..faction.."'")
317 function QuestHelper_UpgradeDatabase(data)
318 if data.QuestHelper_SaveVersion == 1 then
320 -- Reputation objectives weren't parsed correctly before.
321 if data.QuestHelper_Objectives["reputation"] then
322 for faction, objective in pairs(data.QuestHelper_Objectives["reputation"]) do
323 local real_faction = string.find(faction, "%s*(.+)%s*:%s*") or faction
324 if faction ~= real_faction then
325 data.QuestHelper_Objectives["reputation"][real_faction] = data.QuestHelper_Objectives["reputation"][faction]
326 data.QuestHelper_Objectives["reputation"][faction] = nil
331 -- Items that wern't in the local cache when I read the quest log ended up with empty names.
332 if data.QuestHelper_Objectives["item"] then
333 data.QuestHelper_Objectives["item"][" "] = nil
336 data.QuestHelper_SaveVersion = 2
339 if data.QuestHelper_SaveVersion == 2 then
341 -- The hashes for the quests were wrong. Damnit!
342 for faction, level_list in pairs(data.QuestHelper_Quests) do
343 for level, quest_list in pairs(level_list) do
344 for quest_name, quest_data in pairs(quest_list) do
345 quest_data.hash = nil
346 quest_data.alt = nil
351 -- None of the information I collected about quest items previously can be trusted.
352 -- I also didn't properly mark quest items as such, so I'll have to remove the information for non
353 -- quest items also.
355 if data.QuestHelper_Objectives["item"] then
356 for item, item_data in pairs(data.QuestHelper_Objectives["item"]) do
357 -- I'll remerge the bad data later if I find out its not used solely for quests.
358 item_data.bad_pos = item_data.bad_pos or item_data.pos
359 item_data.bad_drop = item_data.bad_drop or item_data.drop
360 item_data.pos = nil
361 item_data.drop = nil
363 -- In the future i'll delete the bad_x data.
364 -- When I do, either just delete it, or of all the monsters or positions match the monsters and positions of the
365 -- quest, merge them into that.
369 data.QuestHelper_SaveVersion = 3
372 if data.QuestHelper_SaveVersion == 3 then
373 -- We'll go through this and make sure all the position lists are correct.
374 for faction, level_list in pairs(data.QuestHelper_Quests) do
375 for level, quest_list in pairs(level_list) do
376 for quest_name, quest_data in pairs(quest_list) do
377 quest_data.pos = QuestHelper_PrunePositionList(quest_data.pos)
378 if quest_data.item then for name, data in pairs(quest_data.item) do
379 data.pos = QuestHelper_PrunePositionList(data.pos)
380 end end
381 if quest_data.alt then for hash, data in pairs(quest_data.alt) do
382 data.pos = QuestHelper_PrunePositionList(data.pos)
383 if data.item then for name, data in pairs(data.item) do
384 data.pos = QuestHelper_PrunePositionList(data.pos)
385 end end
386 end end
391 for cat, list in pairs(data.QuestHelper_Objectives) do
392 for name, data in pairs(list) do
393 data.pos = QuestHelper_PrunePositionList(data.pos)
397 if data.QuestHelper_ZoneTransition then
398 for c, z1list in pairs(data.QuestHelper_ZoneTransition) do
399 for z1, z2list in pairs(z1list) do
400 for z2, poslist in pairs(z2list) do
401 z2list[z2] = QuestHelper_PrunePositionList(poslist)
407 data.QuestHelper_SaveVersion = 4
410 if data.QuestHelper_SaveVersion == 4 then
411 -- Zone transitions have been obsoleted by a bug.
412 data.QuestHelper_ZoneTransition = nil
413 data.QuestHelper_SaveVersion = 5
416 if data.QuestHelper_SaveVersion == 5 then
417 -- For version 6, I'm converting area positions from a continent/zone index pair to a single index.
419 if data.QuestHelper_FlightRoutes then
420 local old_routes = data.QuestHelper_FlightRoutes
421 data.QuestHelper_FlightRoutes = {}
422 for c, value in pairs(old_routes) do
423 data.QuestHelper_FlightRoutes[QuestHelper_IndexLookup[c][0]] = value
427 for faction, level_list in pairs(data.QuestHelper_Quests) do
428 for level, quest_list in pairs(level_list) do
429 for quest_name, quest_data in pairs(quest_list) do
430 QuestHelper_ConvertPositionList(quest_data.pos)
431 if quest_data.item then for name, data in pairs(quest_data.item) do
432 QuestHelper_ConvertPositionList(data.pos)
433 end end
434 if quest_data.alt then for hash, data in pairs(quest_data.alt) do
435 QuestHelper_ConvertPositionList(data.pos)
436 if data.item then for name, data in pairs(data.item) do
437 QuestHelper_ConvertPositionList(data.pos)
438 end end
439 end end
444 for cat, list in pairs(data.QuestHelper_Objectives) do
445 for name, data in pairs(list) do
446 QuestHelper_ConvertPositionList(data.pos)
450 data.QuestHelper_SaveVersion = 6
453 if data.QuestHelper_SaveVersion == 6 then
454 -- Redoing how flightpaths work, previously collected flightpath data is now obsolete.
455 data.QuestHelper_FlightRoutes = {}
457 -- FlightInstructors table should be fine, will leave it.
458 -- Upgrading per-character data is handled in main.lua.
460 -- Also converting factions to numbers, 1 for Alliance, 2 for Horde.
461 local replacement = {}
462 for faction, dat in pairs(data.QuestHelper_Quests) do
463 replacement[QuestHelper_ConvertFaction(faction)] = dat
465 data.QuestHelper_Quests = replacement
467 replacement = {}
468 if data.QuestHelper_FlightInstructors then for faction, dat in pairs(data.QuestHelper_FlightInstructors) do
469 replacement[QuestHelper_ConvertFaction(faction)] = dat
470 end end
471 data.QuestHelper_FlightInstructors = replacement
473 for cat, list in pairs(data.QuestHelper_Objectives) do
474 for name, obj in pairs(list) do
475 if obj.faction then
476 obj.faction = QuestHelper_ConvertFaction(obj.faction)
481 data.QuestHelper_SaveVersion = 7
484 if data.QuestHelper_SaveVersion == 7 then
485 -- It sure took me long enough to discover that I broke vendor objectives.
486 -- their factions were strings and didn't match the number value of QuestHelper.faction
488 for cat, list in pairs(data.QuestHelper_Objectives) do
489 for name, obj in pairs(list) do
490 if type(obj.faction) == "string" then
491 obj.faction = (obj.faction == "Alliance" and 1) or (obj.faction == "Horde" and 2) or nil
496 data.QuestHelper_SaveVersion = 8
499 if data.QuestHelper_SaveVersion == 8 then
500 -- Two things we're doing here
501 -- First, wrath-ize Stormwind coordinates
503 --[[
504 for cat, list in pairs(QuestHelper_Objectives) do
505 for name, obj in pairs(list) do
506 if obj.pos then
507 for i, cpos in pairs(obj.pos) do
508 QuestHelper_ConvertCoordsToWrath(cpos, true)
512 end]] -- okay we're not actually doing this, coordinates are staying native
514 -- Second, split up the entire thing into versions
515 local function versionize(item)
516 --if not item or type(item) ~= "table" then return end -- blue magician doesn't know what the fuck
518 local temp = {}
519 local foundthings = false
520 for k, v in pairs(item) do
521 temp[k] = v
522 foundthings = true
524 if not foundthings then return end -- just to avoid extra keys hanging around in people's tables
526 for key in pairs(item) do
527 item[key] = nil
530 item["unknown on unknown"] = temp
533 versionize(data.QuestHelper_Quests)
534 versionize(data.QuestHelper_Objectives)
535 versionize(data.QuestHelper_FlightInstructors)
536 versionize(data.QuestHelper_FlightRoutes)
538 data.QuestHelper_SaveVersion = 9
541 if data.QuestHelper_SaveVersion == 9 then
542 -- The only thing we're doing here is moving the QuestHelper_ErrorList into QuestHelper_Errors
543 data.QuestHelper_Errors = {}
544 data.QuestHelper_Errors.crashes = {}
546 if data.QuestHelper_ErrorList then
547 for k, v in pairs(data.QuestHelper_ErrorList) do
548 data.QuestHelper_Errors.crashes[k] = v
552 data.QuestHelper_ErrorList = nil
554 data.QuestHelper_SaveVersion = 10
558 function QuestHelper_UpgradeComplete()
559 -- This function deletes everything related to upgrading, as it isn't going to be needed again.
560 built = nil
561 next_index = nil
562 convert_lookup = nil
563 QuestHelper_BuildZoneLookup = nil
564 QuestHelper_ValidPosition = nil
565 QuestHelper_PrunePositionList = nil
566 QuestHelper_ConvertPosition = nil
567 QuestHelper_ConvertPositionList = nil
568 QuestHelper_ConvertFaction = nil
569 QuestHelper_UpgradeDatabase = nil
570 QuestHelper_UpgradeComplete = nil
573 -- These are used to convert coordinates back and forth from "Wrath" to "Native". "Force" is used to convert back and forth from "Wrath" to "BC".
574 -- Both changes the data in-place and returns the data.
575 function QuestHelper_ConvertCoordsToWrath(data, force)
576 if (force or not QuestHelper:IsWrath()) then
577 if data[1] == 36 then -- Stormwind
578 data[2] = data[2] * 0.77324 + 0.197
579 data[3] = data[3] * 0.77324 + 0.245
580 elseif data[1] == 34 then -- EPL
581 data[2] = data[2] * 0.960 - 0.0254
582 data[3] = data[3] * 0.960 - 0.03532
585 return data
588 function QuestHelper_ConvertCoordsFromWrath(data, force)
589 if (force or not QuestHelper:IsWrath()) then
590 if data[1] == 36 then -- Stormwind
591 data[2] = (data[2] - 0.197) / 0.77324
592 data[3] = (data[3] - 0.245) / 0.77324
593 elseif data[1] == 34 then -- EPL
594 data[2] = (data[2] + 0.0254) / 0.960
595 data[3] = (data[3] + 0.03532) / 0.960
598 return data
601 local QuestHelper_PrivateServerBlacklist_Find = {
602 "WoWFusion",
603 "WoWgasm",
604 "Egyéb",
605 "Reagens/",
608 local QuestHelper_PrivateServerBlacklist_Exact = {
609 "WarcraftMMO",
610 "TAXI",
611 "GeNiuS",
612 "Columbian Drug Dealer",
613 "PlayBoy Fun Vendor",
614 "Gm Vendor",
615 "Accessories Vendor",
616 "General Goods Vendor",
617 "Party Vendor",
618 "Potion Vendor",
619 "Totem Vendor",
620 "Gm Vendor",
621 "Misc",
622 "Off-Hands Vendor",
623 "Ore Vendor",
624 "Enchanting Vendor",
625 "Gem Vendor",
626 "Fooooood and Drinks!",
627 "I Sell Consumables",
628 "Armor Raid Tier V",
629 "world translate",
630 "Bobby", -- I have no idea if this is an actual private server NPC
631 "Nejeib", -- same
632 "Shaman Set Vendor",
633 "Warrior Set Vendor",
634 "Priest Set Vendor",
635 "Warlock Set Vendor",
636 "Paladin Set Vendor",
637 "Hunter Set Vendor",
638 "Mage Set Vendor", -- yeah yeah this isn't everyone whatever
641 local matchstring = nil
643 function QuestHelper_IsPolluted(input)
644 if not input then input = _G end
646 for version, data in pairs(input.QuestHelper_Objectives) do
647 for cat, name_list in pairs(data) do
648 for name, obj in pairs(name_list) do
649 for k, v in pairs(QuestHelper_PrivateServerBlacklist_Find) do
650 if string.find(name, v) then
651 for _, __ in pairs(obj) do
652 return true -- if there's nothing actually in the object, the player may not have contributed data, he may have just gotten smacked by old corrupted data.
656 for k, v in pairs(QuestHelper_PrivateServerBlacklist_Exact) do
657 if name == v then
658 for _, __ in pairs(obj) do
659 return true -- if there's nothing actually in the object, the player may not have contributed data, he may have just gotten smacked by old corrupted data.