Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Hardcore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ local speedrun_levels = {
[45] = 1,
[50] = 1,
[60] = 1,
[80] = 1,
}
local last_received_xguild_chat = ""
local debug = false
Expand Down
2 changes: 2 additions & 0 deletions Hardcore_Classic.toc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ embeds.xml
CustomLayouts.lua
utils.lua
Security.lua
JsonConverter.lua
Base64.lua
Hardcore.xml

#Tokens
Expand Down
2 changes: 2 additions & 0 deletions Hardcore_Wrath.toc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ embeds.xml
CustomLayouts.lua
utils.lua
Security.lua
JsonConverter.lua
Base64.lua
Hardcore.xml

#Tokens
Expand Down
184 changes: 184 additions & 0 deletions JsonConverter.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
--[[ json.lua
A compact pure-Lua JSON library.
The main functions are: json.stringify, json.parse.
## json.stringify:
This expects the following to be true of any tables being encoded:
* They only have string or number keys. Number keys must be represented as
strings in json; this is part of the json spec.
* They are not recursive. Such a structure cannot be specified in json.
A Lua table is considered to be an array if and only if its set of keys is a
consecutive sequence of positive integers starting at 1. Arrays are encoded like
so: `[2, 3, false, "hi"]`. Any other type of Lua table is encoded as a json
object, encoded like so: `{"key1": 2, "key2": false}`.
Because the Lua nil value cannot be a key, and as a table value is considerd
equivalent to a missing key, there is no way to express the json "null" value in
a Lua table. The only way this will output "null" is if your entire input obj is
nil itself.
An empty Lua table, {}, could be considered either a json object or array -
it's an ambiguous edge case. We choose to treat this as an object as it is the
more general type.
To be clear, none of the above considerations is a limitation of this code.
Rather, it is what we get when we completely observe the json specification for
as arbitrary a Lua object as json is capable of expressing.
## json.parse:
This function parses json, with the exception that it does not pay attention to
\u-escaped unicode code points in strings.
It is difficult for Lua to return null as a value. In order to prevent the loss
of keys with a null value in a json string, this function uses the one-off
table value json.null (which is just an empty table) to indicate null values.
This way you can check if a value is null with the conditional
`val == json.null`.
If you have control over the data and are using Lua, I would recommend just
avoiding null values in your data to begin with.
--]]


function Hardcore_GetJSONConverter()
local json = {}


-- Internal functions.

local function kind_of(obj)
if type(obj) ~= 'table' then return type(obj) end
local i = 1
for _ in pairs(obj) do
if obj[i] ~= nil then i = i + 1 else return 'table' end
end
if i == 1 then return 'table' else return 'array' end
end

local function escape_str(s)
local in_char = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
local out_char = {'\\', '"', '/', 'b', 'f', 'n', 'r', 't'}
for i, c in ipairs(in_char) do
s = s:gsub(c, '\\' .. out_char[i])
end
return s
end

-- Returns pos, did_find; there are two cases:
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
-- 2. Delimiter not found: pos = pos after leading space; did_find = false.
-- This throws an error if err_if_missing is true and the delim is not found.
local function skip_delim(str, pos, delim, err_if_missing)
pos = pos + #str:match('^%s*', pos)
if str:sub(pos, pos) ~= delim then
if err_if_missing then
error('Expected ' .. delim .. ' near position ' .. pos)
end
return pos, false
end
return pos + 1, true
end

-- Expects the given pos to be the first character after the opening quote.
-- Returns val, pos; the returned pos is after the closing quote character.
local function parse_str_val(str, pos, val)
val = val or ''
local early_end_error = 'End of input found while parsing string.'
if pos > #str then error(early_end_error) end
local c = str:sub(pos, pos)
if c == '"' then return val, pos + 1 end
if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
-- We must have a \ character.
local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
local nextc = str:sub(pos + 1, pos + 1)
if not nextc then error(early_end_error) end
return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
end

-- Returns val, pos; the returned pos is after the number's final character.
local function parse_num_val(str, pos)
local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
local val = tonumber(num_str)
if not val then error('Error parsing number at position ' .. pos .. '.') end
return val, pos + #num_str
end


-- Public values and functions.

function json.stringify(obj, as_key)
local s = {} -- We'll build the string as an array of strings to be concatenated.
local kind = kind_of(obj) -- This is 'array' if it's an array or type(obj) otherwise.
if kind == 'array' then
if as_key then error('Can\'t encode array as key.') end
s[#s + 1] = '['
for i, val in ipairs(obj) do
if i > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(val)
end
s[#s + 1] = ']'
elseif kind == 'table' then
if as_key then error('Can\'t encode table as key.') end
s[#s + 1] = '{'
for k, v in pairs(obj) do
if #s > 1 then s[#s + 1] = ', ' end
s[#s + 1] = json.stringify(k, true)
s[#s + 1] = ':'
s[#s + 1] = json.stringify(v)
end
s[#s + 1] = '}'
elseif kind == 'string' then
return '"' .. escape_str(obj) .. '"'
elseif kind == 'number' then
if as_key then return '"' .. tostring(obj) .. '"' end
return tostring(obj)
elseif kind == 'boolean' then
return tostring(obj)
elseif kind == 'nil' then
return 'null'
else
error('Unjsonifiable type: ' .. kind .. '.')
end
return table.concat(s)
end

json.null = {} -- This is a one-off table to represent the null value.

function json.parse(str, pos, end_delim)
pos = pos or 1
if pos > #str then error('Reached unexpected end of input.') end
local pos = pos + #str:match('^%s*', pos) -- Skip whitespace.
local first = str:sub(pos, pos)
if first == '{' then -- Parse an object.
local obj, key, delim_found = {}, true, true
pos = pos + 1
while true do
key, pos = json.parse(str, pos, '}')
if key == nil then return obj, pos end
if not delim_found then error('Comma missing between object items.') end
pos = skip_delim(str, pos, ':', true) -- true -> error if missing.
obj[key], pos = json.parse(str, pos)
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '[' then -- Parse an array.
local arr, val, delim_found = {}, true, true
pos = pos + 1
while true do
val, pos = json.parse(str, pos, ']')
if val == nil then return arr, pos end
if not delim_found then error('Comma missing between array items.') end
arr[#arr + 1] = val
pos, delim_found = skip_delim(str, pos, ',')
end
elseif first == '"' then -- Parse a string.
return parse_str_val(str, pos + 1)
elseif first == '-' or first:match('%d') then -- Parse a number.
return parse_num_val(str, pos)
elseif first == end_delim then -- End of an object or array.
return nil, pos + 1
else -- Parse true, false, or null.
local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
for lit_str, lit_val in pairs(literals) do
local lit_end = pos + #lit_str - 1
if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
end
local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
error('Invalid json syntax starting at ' .. pos_info_str)
end
end

return json
end
77 changes: 48 additions & 29 deletions MainMenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -609,44 +609,63 @@ end
local function DrawVerifyTab(container, _hardcore_character)
local ATTRIBUTE_SEPARATOR = "_"
local function GenerateVerificationString()
local version = GetAddOnMetadata("Hardcore", "Version")
local _, class, _, race, _, name = GetPlayerInfoByGUID(UnitGUID("player"))
local version = GetAddOnMetadata("Hardcore", "Version")
local realm = GetRealmName()
local level = UnitLevel("player")

local tradePartners = Hardcore_join(_hardcore_character.trade_partners, ",")
local converted_successfully = "FALSE"
if _hardcore_character.converted_successfully then
converted_successfully = "TRUE"
end

local game_version_checker = _hardcore_character.game_version or { _G["HardcoreBuildLabel"] }

local baseVerificationData = {
version,
_hardcore_character.guid,
realm,
race,
class,
name,
level,
_hardcore_character.time_played,
_hardcore_character.time_tracked,
#_hardcore_character.deaths,
tradePartners,
_hardcore_character.sacrificed_at,
converted_successfully,
game_version_checker,
}
local baseVerificationString =
Hardcore_join(Hardcore_map(baseVerificationData, Hardcore_stringOrNumberToUnicode), ATTRIBUTE_SEPARATOR)
local bubbleHearthIncidentsVerificationString =
Hardcore_tableToUnicode(_hardcore_character.bubble_hearth_incidents)
local playedtimeGapsVerificationString = Hardcore_tableToUnicode(_hardcore_character.played_time_gap_warnings)
return Hardcore_join({
baseVerificationString,
bubbleHearthIncidentsVerificationString,
playedtimeGapsVerificationString,
}, ATTRIBUTE_SEPARATOR)
local data_to_encode = {}
data_to_encode["race"] = race
data_to_encode["version"] = version
data_to_encode["class"] = class
data_to_encode["realm"] = realm
data_to_encode["level"] = level
data_to_encode["trade_parters"] = _hardcore_character.trade_partners

local achievement_nums = {}
for _,v in ipairs(_hardcore_character.achievements) do
if _G.a_id[v] ~= nil then
achievement_nums[#achievement_nums+1] = _G.a_id[v]
end
end
data_to_encode["achievements"] = achievement_nums

local passive_achievement_nums = {}
for _,v in ipairs(_hardcore_character.passive_achievements) do
if _G.pa_id[v] ~= nil then
passive_achievement_nums[#passive_achievement_nums+1] = _G.pa_id[v]
end
end
data_to_encode["passive_achievements"] = passive_achievement_nums
data_to_encode["converted_successfully"] = converted_successfully
data_to_encode["game_version_checker"] = game_version_checker
data_to_encode["guid"] = _hardcore_character.guid
data_to_encode["name"] = name
data_to_encode["time_played"] = _hardcore_character.time_played
data_to_encode["time_tracked"] = _hardcore_character.time_tracked
data_to_encode["deaths"] = #_hardcore_character.deaths
data_to_encode["sacrificed_at"] = _hardcore_character.sacrificed_at
data_to_encode["bubble_hearth_incidents"] = _hardcore_character.bubble_hearth_incidents
data_to_encode["played_time_gap_warnings"] = _hardcore_character.played_time_gap_warnings
data_to_encode["adjusted_time10"] = _hardcore_character.adjusted_time10
data_to_encode["adjusted_time15"] = _hardcore_character.adjusted_time15
data_to_encode["adjusted_time20"] = _hardcore_character.adjusted_time20
data_to_encode["adjusted_time30"] = _hardcore_character.adjusted_time30
data_to_encode["adjusted_time40"] = _hardcore_character.adjusted_time40
data_to_encode["adjusted_time50"] = _hardcore_character.adjusted_time50
data_to_encode["adjusted_time60"] = _hardcore_character.adjusted_time60
data_to_encode["adjusted_time80"] = _hardcore_character.adjusted_time80

local json = Hardcore_GetJSONConverter()
local base64 = Hardcore_Base64()
return base64.encode(json.stringify(data_to_encode))
end

local version = GetAddOnMetadata("Hardcore", "Version")
Expand Down Expand Up @@ -701,7 +720,7 @@ local function DrawVerifyTab(container, _hardcore_character)


local extra_lines = ""
if UnitLevel("player") < max_level then
if UnitLevel("player") < max_level and false then
-- local general_rules_description = AceGUI:Create("Label")
-- general_rules_description:SetWidth(_menu_width)
-- general_rules_description:SetText("\n\nYou must be max level for your chosen expansion (60 or 80) to get a verification string your character.")
Expand Down
Loading