diff --git a/README.md b/README.md index e05d678..149e2f1 100644 --- a/README.md +++ b/README.md @@ -8,24 +8,36 @@ There are three stages of integration. For a fast integration, please follow the ### Stage 1 - Before you begin 1. Grab a copy of the library via our public repo [GetDotaStats/stat-collection](https://github.com/GetDotaStats/stat-collection). -2. Login to http://getdotastats.com, by clicking the big green button at the top of this page. -3. Register your mod on the site by navigating to `Custom Games -> Mods (My Section) -> Add a new mod`, or by going straight to the registration form. -4. Go back to your list of mods by navigating to `Custom Games -> Mods (My Section)`, or by going straight to the My Mods page. You should now see a new entry there, that matches the mod your just registered. +2. Login to [http://getdotastats.com](http://getdotastats.com/), by clicking the big green button at the top of this page. +3. Register your mod on the site by navigating to `Custom Games -> Mods (My Section) -> Add a new mod`, or by going straight to the [registration form](http://getdotastats.com/#s2__my__mod_request). +4. Go back to your list of mods by navigating to `Custom Games -> Mods (My Section)`, or by going straight to the [My Mods](http://getdotastats.com/#s2__my__mods) page. You should now see a new entry there, that matches the mod your just registered. 5. Take note of your *modID* key of 32characters. If you lose this string, refer back to this page. -6. Make sure not to share this key, as it is unique to your mod and is used when recording stats!
If you use Github, add a .gitignore file to the root of your project. Adding **`settings.kv`** to it will prevent from accidentally leaking your *modID*. -7. An Admin will review your mod registration and approve it if it meets the submission guidelines outlined on the registration page. While your mod is reviewed, you can continue following this guide. +6. Make sure not to share this key, as it is unique to your mod and is used when recording stats!
If you use Github, add a `.gitignore` file to the root of your project. Adding **`settings.kv`** to it will prevent from accidentally leaking your *modID*. +7. An Admin will review your mod registration and approve it if it meets the submission guidelines outlined on the registration page and has a few completed games recorded. While your mod is reviewed, you can continue following this guide. ### Stage 2 - Basic Integration Now that you have the library and have completed the sign-up process, we can start the actual integration. -1. Merge the files downloaded in (Stage 1 - Step 1). If done successfully, you will see a statcollection folder in your **`game/YOUR_ADDON/scripts/vscripts`** folder. -2. In your addon_game_mode.lua file, add a require statement at the top of your code that points at our library initialiser file. **`require("statcollection/init")`** -3. Go into the scripts/vscripts/statcollection folder and inside the settings.kv file, change the modID XXXXXXX value to the modID key you noted above (Stage 1 - Step 4). -4. Check your game logic to ensure you set player win conditions nicely. This library hooks the SetGameWinner() function, so make sure to convert all of your `MakeTeamLose()` calls into `SetGameWinner(`) calls. Also make sure to check every win and lose condition, as this library will only send stats at POST_GAME after a winner has been declared. +1. Merge the files downloaded in (Stage 1 - Step 1). If done successfully, you will see a statcollection folder in your **`game/YOUR_ADDON/scripts/vscripts`** folder. Pay attention to the included panorama files. They should be merged into content/YOUR_ADDON/panorama folder. +2. In your **`addon_game_mode.lua`** file, add a require statement at the top of your code that points at our library initialiser file. **`require("statcollection/init")`** +3. Go into the **`scripts/vscripts/`** folder and inside the settings.kv file, change the modID XXXXXXX value to the modID key you noted above (Stage 1 - Step 4). If your mod requires rounds, skip to Stage 2.5 in these instructions. If you can possibly help it, we advise modders to avoid using rounds. +4. Check your game logic to ensure you set player win conditions nicely. This library hooks the SetGameWinner() function, so make sure to convert all of your MakeTeamLose() calls into SetGameWinner() calls. Also make sure to check every win and lose condition, as this library will only send stats at POST_GAME after a winner has been declared. 5. Test your custom game (via workshop tools is fine), and see if stats were recorded. You can find games recently recorded against your steamID by navigating to `Custom Games -> Public Profile (My Section)`, or by going straight to your Public Profile. -8. You have completed the basic integration successfully if your games are recorded with a Phase value of 3 or higher (a column in the tables on both pages). If you don't see any recorded games, or they are not reaching Phase 3, refer to the troubleshooting section below. -7. Update your `settings.kv` by setting **"TESTING"** to *false*, and the **"MIN_PLAYERS"** to the minimum number of players required to have a proper game. +6. You have completed the basic integration successfully if the games recorded under your mod on the [RECENT GAMES](http://getdotastats.com/#s2__recent_games) page (or in your public profile) have a green phase value. If you don't see any recorded games, or they are not reaching the green phase, refer to the troubleshooting section below. +7. Update your settings.kv by setting TESTING to false, and the "MIN_PLAYERS" to the minimum number of players you believe are required to have an interesting (playable) game. Only set TESTING to true, when troubleshooting stats in your workshop tools. + +### Stage 2.5 - Basic Integration for Round Based Games + +Skip this section if your game is not round based. Implementing round based stats is not for the faint of heart. You will need the ability to think critically, and hopefully understand how the logic in your game works. + +1. Your mod should already have our library files merged from Stage 2 - Step 1. If not, go back and do that now. +2. Go into the **`scripts/vscripts/statcollection`** folder and inside the settings.kv file. Set HAS_ROUNDS to true and both of the win conditions (GAME_WINNER and ANCIENT_EXPLOSION) to false. +3. In your game logic, call statCollection:submitRound(false) at the end of every round. At the end of the final round, call statCollection:submitRound(true). Make sure to update line 108 in the schema.lua (for local current_winner_team), as we have no generic way of determining who won your arbitrary round. +4. Double check your game logic to ensure you properly indicate which teams won each round, and that it is recorded in line 108 of **`schema.lua`** +5. Test your custom game (via workshop tools is fine), and see if stats were recorded. You will need to play your game all the way through (up to your win or lose condition), unless you created a way to skip to the end of the game. You can find games recently recorded against your steamID by navigating to `Custom Games -> Public Profile (My Section)`, or by going straight to your Public Profile. +6. You have completed the basic integration (for rounds) successfully if the games recorded under your mod on the [RECENT GAMES](http://getdotastats.com/#s2__recent_games) page (or in your public profile) have a green phase value. If you don't see any recorded games, or they are not reaching the green phase, refer to the troubleshooting section below. +7. Update your **`settings.kv`** by setting TESTING to false, and the "MIN_PLAYERS" to the minimum number of players you believe are required to have an interesting (playable) game. For most games, this will be a value of 2 or 4. Only set TESTING to true, when troubleshooting stats in your workshop tools, as this setting overrides MIN_PLAYERS to 0 and prints your schema stats (Stage 3) to console. ### Stage 3 - Advanced Integration **OPTIONAL** @@ -99,11 +111,12 @@ Now that you have the library and have completed the sign-up process, we can sta **My custom game never reaches Phase 3!** * Have a look in your console log for an error. * Check your win conditions. We hook SetGameWinner(), so make sure you don't use MakeTeamLose(). +**I am in despair! Help me!** +* Contact us via one of our numerous channels of contact. You can find the official list on [our site](http://getdotastats.com/#site__contact) ### Implementations - Noya [PMP] - https://github.com/MNoya/PMP - Noya [Warchasers] - https://github.com/MNoya/Warchasers - - Azarak [The Predator] - http://steamcommunity.com/sharedfiles/filedetails/?id=494708836 ### Recent Games - Reported on the site: http://getdotastats.com/#s2__recent_games @@ -112,7 +125,7 @@ Now that you have the library and have completed the sign-up process, we can sta ### Credits - Big thanks to [SinZ163](https://github.com/SinZ163), [Noya](https://github.com/MNoya/), [Ash47](https://github.com/Ash47), [BMD](https://github.com/bmddota), and [Tet](https://github.com/tetl) for their contributions.is - ### Contact +### Contact - You can get in contact with us via Github issues, or via other methods http://getdotastats.com/#site__contact - This repo should contain all of the code required to get stats working. It should work out of the box, but will require modification if you want record additional stats. diff --git a/panorama/layout/custom_game/statcollection.xml b/content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/statcollection.xml similarity index 100% rename from panorama/layout/custom_game/statcollection.xml rename to content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/statcollection.xml diff --git a/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js b/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js new file mode 100644 index 0000000..c129f6b --- /dev/null +++ b/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js @@ -0,0 +1,39 @@ +"use strict"; + +function OnClientCheckIn(args) { + + var payload = { + modIdentifier: args.modID, + steamID32: GetSteamID32(), + matchID: args.matchID, + schemaVersion: args.schemaVersion + }; + + $.Msg('Sending: ', payload); + + $.AsyncWebRequest('http://getdotastats.com/s2/api/s2_check_in.php', + { + type: 'POST', + data: {payload: JSON.stringify(payload)}, + success: function (data) { + $.Msg('GDS Reply: ', data) + } + }); +} + +function GetSteamID32() { + var playerInfo = Game.GetPlayerInfo(Game.GetLocalPlayerID()); + + var steamID64 = playerInfo.player_steamid, + steamIDPart = Number(steamID64.substring(3)), + steamID32 = String(steamIDPart - 61197960265728); + + return steamID32; +} + +(function () { + $.Msg("StatCollection Client Loaded"); + + GameEvents.Subscribe("statcollection_client", OnClientCheckIn); + +})(); \ No newline at end of file diff --git a/scripts/vscripts/statcollection/init.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua similarity index 100% rename from scripts/vscripts/statcollection/init.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/md5.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/md5.lua new file mode 100644 index 0000000..f6a0f13 --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/md5.lua @@ -0,0 +1,382 @@ +local md5 = { + _VERSION = "md5.lua 0.5.0", + _DESCRIPTION = "MD5 computation in Lua (5.1)", + _URL = "https://github.com/kikito/md5.lua", + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +-- bit lib implementions + +local floor, abs, max = math.floor, math.abs, math.max +local char, byte, format, rep, sub = +string.char, string.byte, string.format, string.rep, string.sub + +local function check_int(n) + -- checking not float + if (n - floor(n) > 0) then + error("trying to use bitwise operation on non-integer!") + end +end + +local function tbl2number(tbl) + local n = #tbl + + local rslt = 0 + local power = 1 + for i = 1, n do + rslt = rslt + tbl[i] * power + power = power * 2 + end + + return rslt +end + +local function expand(tbl_m, tbl_n) + local big = {} + local small = {} + if (#tbl_m > #tbl_n) then + big = tbl_m + small = tbl_n + else + big = tbl_n + small = tbl_m + end + -- expand small + for i = #small + 1, #big do + small[i] = 0 + end +end + +local to_bits -- needs to be declared before bit_not + +local function bit_not(n) + local tbl = to_bits(n) + local size = max(#tbl, 32) + for i = 1, size do + if (tbl[i] == 1) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + return tbl2number(tbl) +end + +-- defined as local above +to_bits = function(n) + check_int(n) + if (n < 0) then + -- negative + return to_bits(bit_not(abs(n)) + 1) + end + -- to bits table + local tbl = {} + local cnt = 1 + while (n > 0) do + local last = math.mod(n, 2) + if (last == 1) then + tbl[cnt] = 1 + else + tbl[cnt] = 0 + end + n = (n - last) / 2 + cnt = cnt + 1 + end + + return tbl +end + +local function bit_or(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if (tbl_m[i] == 0 and tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) +end + +local function bit_and(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if (tbl_m[i] == 0 or tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) +end + +local function bit_xor(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if (tbl_m[i] ~= tbl_n[i]) then + tbl[i] = 1 + else + tbl[i] = 0 + end + end + + return tbl2number(tbl) +end + +local function bit_rshift(n, bits) + check_int(n) + + local high_bit = 0 + if (n < 0) then + -- negative + n = bit_not(abs(n)) + 1 + high_bit = 2147483648 -- 0x80000000 + end + + for i = 1, bits do + n = n / 2 + n = bit_or(floor(n), high_bit) + end + return floor(n) +end + +local function bit_lshift(n, bits) + check_int(n) + + if (n < 0) then + -- negative + n = bit_not(abs(n)) + 1 + end + + for i = 1, bits do + n = n * 2 + end + return bit_and(n, 4294967295) -- 0xFFFFFFFF +end + +-- convert little-endian 32-bit int to a 4-char string +local function lei2str(i) + local f = function(s) return char(bit_and(bit_rshift(i, s), 255)) end + return f(0) .. f(8) .. f(16) .. f(24) +end + +-- convert raw string to big-endian int +local function str2bei(s) + local v = 0 + for i = 1, #s do + v = v * 256 + byte(s, i) + end + return v +end + +-- convert raw string to little-endian int +local function str2lei(s) + local v = 0 + for i = #s, 1, -1 do + v = v * 256 + byte(s, i) + end + return v +end + +-- cut up a string in little-endian ints of given size +local function cut_le_str(s, ...) + local o, r = 1, {} + local args = { ... } + for i = 1, #args do + table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) + o = o + args[i] + end + return r +end + +local swap = function(w) return str2bei(lei2str(w)) end + +local function hex2binaryaux(hexval) + return char(tonumber(hexval, 16)) +end + +local function hex2binary(hex) + local result, _ = hex:gsub('..', hex2binaryaux) + return result +end + +-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh) +-- 10/02/2001 jcw@equi4.com + +local FF = 0xffffffff +local CONSTS = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 +} + +local f = function(x, y, z) return bit_or(bit_and(x, y), bit_and(-x - 1, z)) end +local g = function(x, y, z) return bit_or(bit_and(x, z), bit_and(y, -z - 1)) end +local h = function(x, y, z) return bit_xor(x, bit_xor(y, z)) end +local i = function(x, y, z) return bit_xor(y, bit_or(x, -z - 1)) end +local z = function(f, a, b, c, d, x, s, ac) + a = bit_and(a + f(b, c, d) + x + ac, FF) + -- be *very* careful that left shift does not cause rounding! + return bit_or(bit_lshift(bit_and(a, bit_rshift(FF, s)), s), bit_rshift(a, 32 - s)) + b +end + +local function transform(A, B, C, D, X) + local a, b, c, d = A, B, C, D + local t = CONSTS + + a = z(f, a, b, c, d, X[0], 7, t[1]) + d = z(f, d, a, b, c, X[1], 12, t[2]) + c = z(f, c, d, a, b, X[2], 17, t[3]) + b = z(f, b, c, d, a, X[3], 22, t[4]) + a = z(f, a, b, c, d, X[4], 7, t[5]) + d = z(f, d, a, b, c, X[5], 12, t[6]) + c = z(f, c, d, a, b, X[6], 17, t[7]) + b = z(f, b, c, d, a, X[7], 22, t[8]) + a = z(f, a, b, c, d, X[8], 7, t[9]) + d = z(f, d, a, b, c, X[9], 12, t[10]) + c = z(f, c, d, a, b, X[10], 17, t[11]) + b = z(f, b, c, d, a, X[11], 22, t[12]) + a = z(f, a, b, c, d, X[12], 7, t[13]) + d = z(f, d, a, b, c, X[13], 12, t[14]) + c = z(f, c, d, a, b, X[14], 17, t[15]) + b = z(f, b, c, d, a, X[15], 22, t[16]) + + a = z(g, a, b, c, d, X[1], 5, t[17]) + d = z(g, d, a, b, c, X[6], 9, t[18]) + c = z(g, c, d, a, b, X[11], 14, t[19]) + b = z(g, b, c, d, a, X[0], 20, t[20]) + a = z(g, a, b, c, d, X[5], 5, t[21]) + d = z(g, d, a, b, c, X[10], 9, t[22]) + c = z(g, c, d, a, b, X[15], 14, t[23]) + b = z(g, b, c, d, a, X[4], 20, t[24]) + a = z(g, a, b, c, d, X[9], 5, t[25]) + d = z(g, d, a, b, c, X[14], 9, t[26]) + c = z(g, c, d, a, b, X[3], 14, t[27]) + b = z(g, b, c, d, a, X[8], 20, t[28]) + a = z(g, a, b, c, d, X[13], 5, t[29]) + d = z(g, d, a, b, c, X[2], 9, t[30]) + c = z(g, c, d, a, b, X[7], 14, t[31]) + b = z(g, b, c, d, a, X[12], 20, t[32]) + + a = z(h, a, b, c, d, X[5], 4, t[33]) + d = z(h, d, a, b, c, X[8], 11, t[34]) + c = z(h, c, d, a, b, X[11], 16, t[35]) + b = z(h, b, c, d, a, X[14], 23, t[36]) + a = z(h, a, b, c, d, X[1], 4, t[37]) + d = z(h, d, a, b, c, X[4], 11, t[38]) + c = z(h, c, d, a, b, X[7], 16, t[39]) + b = z(h, b, c, d, a, X[10], 23, t[40]) + a = z(h, a, b, c, d, X[13], 4, t[41]) + d = z(h, d, a, b, c, X[0], 11, t[42]) + c = z(h, c, d, a, b, X[3], 16, t[43]) + b = z(h, b, c, d, a, X[6], 23, t[44]) + a = z(h, a, b, c, d, X[9], 4, t[45]) + d = z(h, d, a, b, c, X[12], 11, t[46]) + c = z(h, c, d, a, b, X[15], 16, t[47]) + b = z(h, b, c, d, a, X[2], 23, t[48]) + + a = z(i, a, b, c, d, X[0], 6, t[49]) + d = z(i, d, a, b, c, X[7], 10, t[50]) + c = z(i, c, d, a, b, X[14], 15, t[51]) + b = z(i, b, c, d, a, X[5], 21, t[52]) + a = z(i, a, b, c, d, X[12], 6, t[53]) + d = z(i, d, a, b, c, X[3], 10, t[54]) + c = z(i, c, d, a, b, X[10], 15, t[55]) + b = z(i, b, c, d, a, X[1], 21, t[56]) + a = z(i, a, b, c, d, X[8], 6, t[57]) + d = z(i, d, a, b, c, X[15], 10, t[58]) + c = z(i, c, d, a, b, X[6], 15, t[59]) + b = z(i, b, c, d, a, X[13], 21, t[60]) + a = z(i, a, b, c, d, X[4], 6, t[61]) + d = z(i, d, a, b, c, X[11], 10, t[62]) + c = z(i, c, d, a, b, X[2], 15, t[63]) + b = z(i, b, c, d, a, X[9], 21, t[64]) + + return A + a, B + b, C + c, D + d +end + +---------------------------------------------------------------- +function md5.sumhexa(s) + local msgLen = #s + local padLen = 56 - msgLen % 64 + + if msgLen % 64 > 56 then padLen = padLen + 64 end + + if padLen == 0 then padLen = 64 end + + s = s .. char(128) .. rep(char(0), padLen - 1) .. lei2str(8 * msgLen) .. lei2str(0) + + assert(#s % 64 == 0) + + local t = CONSTS + local a, b, c, d = t[65], t[66], t[67], t[68] + + for i = 1, #s, 64 do + local X = cut_le_str(sub(s, i, i + 63), 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4) + assert(#X == 16) + X[0] = table.remove(X, 1) -- zero based! + a, b, c, d = transform(a, b, c, d, X) + end + + return format("%08x%08x%08x%08x", swap(a), swap(b), swap(c), swap(d)) +end + +function md5.sum(s) + return hex2binary(md5.sumhexa(s)) +end + +return md5 \ No newline at end of file diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua similarity index 86% rename from scripts/vscripts/statcollection/lib/statcollection.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index de1a788..6f2dc03 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -28,7 +28,7 @@ local statInfo = LoadKeyValues('scripts/vscripts/statcollection/settings.kv') local postLocation = 'http://getdotastats.com/s2/api/' -- The schema version we are currently using -local schemaVersion = 3 +local schemaVersion = 4 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' @@ -57,7 +57,7 @@ local messagePhase1Complete = 'Match was successfully registered with GetDotaSta local messagePhase2Complete = 'Match pregame settings have been recorded!' local messagePhase3Complete = 'Match stats were successfully recorded!' local messageCustomComplete = 'Match custom stats were successfully recorded!' -local messageFlagsSet = 'Flag was successfully set!' +local messageFlagsSet = 'Flag was successfully set!' -- Create the stat collection class if not statCollection then @@ -82,7 +82,7 @@ function statCollection:init() print(printPrefix .. errorMissingModIdentifier) elseif modIdentifier == 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' then - print(printPrefix.. errorDefaultModIdentifier) + print(printPrefix .. errorDefaultModIdentifier) self.doneInit = false return @@ -113,23 +113,20 @@ function statCollection:init() -- Set the default winner to -1 (no winner) self.winner = -1 - + --Store roundID globally self.roundID = 0 - + -- Hook requred functions to operate correctly self:hookFunctions() - - -- Send stage1 stuff - self:sendStage1() end --Build the winners array function statCollection:calcWinnersByTeam() - output = {} + local output = {} local winningTeam = self.winner - for playerID = 0, DOTA_MAX_PLAYERS do + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then output[PlayerResource:GetSteamAccountID(playerID)] = PlayerResource:GetTeam(playerID) == winningTeam and '1' or '0' end @@ -158,17 +155,37 @@ function statCollection:hookFunctions() end end + --Wait for host before sending Phase 1 + ListenToGameEvent('player_connect_full', function(keys) + -- Ensure we can only send it once, and everything is good to go + if self.playerCheckStage1 then return end + + -- Check each connected player to see if they are host + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + local player = PlayerResource:GetPlayer(playerID) + + if GameRules:PlayerHasCustomGameHostPrivileges(player) then + self.playerCheckStage1 = true + -- Send stage1 stuff + self:sendStage1() + break + end + end + end + end, nil) + -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) - -- Grab the current state + -- Grab the current state local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then -- Load time flag - statCollection:setFlags({loadTime = math.floor(GameRules:GetGameTime())}) + statCollection:setFlags({ loadTime = math.floor(GameRules:GetGameTime()) }) -- Start the client checking recording - CustomUI:DynamicHud_Create(-1,"statcollection","file://{resources}/layout/custom_game/statcollection.xml",nil) + CustomUI:DynamicHud_Create(-1, "statcollection", "file://{resources}/layout/custom_game/statcollection.xml", nil) elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then -- Send pregame stats @@ -181,7 +198,6 @@ function statCollection:hookFunctions() this:sendStage3(this:calcWinnersByTeam(), true) end end - end, nil) end @@ -220,11 +236,11 @@ function statCollection:setFlags(flags) if type(flags) == "table" then -- Store the new flags - for flagKey,flagValue in pairs(flags) do + for flagKey, flagValue in pairs(flags) do self.flags[flagKey] = flagValue - print(printPrefix .. messageFlagsSet .. " {"..flagKey..":"..tostring(flagValue).."}") + print(printPrefix .. messageFlagsSet .. " {" .. flagKey .. ":" .. tostring(flagValue) .. "}") end - + else -- Yell at the developer print(printPrefix .. errorFlags) @@ -252,14 +268,14 @@ function statCollection:sendStage1() -- Workout the player count local playerCount = PlayerResource:GetPlayerCount() if playerCount <= 0 then playerCount = 1 end - statCollection:setFlags({numPlayers = playerCount}) + statCollection:setFlags({ numPlayers = playerCount }) -- Workout who is hosting - local hostID - for playerID = 0, DOTA_MAX_PLAYERS do + local hostID = 0 + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) - if GameRules:PlayerHasCustomGameHostPrivileges(player) then + if GameRules:PlayerHasCustomGameHostPrivileges(player) then hostID = playerID break end @@ -269,11 +285,11 @@ function statCollection:sendStage1() -- Workout if the server is dedicated or not local isDedicated = (IsDedicatedServer() and 1) or 0 - statCollection:setFlags({dedi = isDedicated}) + statCollection:setFlags({ dedi = isDedicated }) -- Grab the mapname local mapName = GetMapName() - statCollection:setFlags({map = mapName}) + statCollection:setFlags({ map = mapName }) -- Build the payload local payload = { @@ -284,7 +300,7 @@ function statCollection:sendStage1() -- Begin the initial request self:sendStage('s2_phase_1.php', payload, function(err, res) - -- Check if we got an error + -- Check if we got an error if err then print(printPrefix .. errorJsonDecode) print(printPrefix .. err) @@ -323,11 +339,11 @@ function statCollection:sendStage2() print(printPrefix .. messagePhase2Starting) -- Client check in - CustomGameEventManager:Send_ServerToAllClients("statcollection_client", { modID = self.modIdentifier, matchID = self.matchID, schemaVersion = schemaVersion}) + CustomGameEventManager:Send_ServerToAllClients("statcollection_client", { modID = self.modIdentifier, matchID = self.matchID, schemaVersion = schemaVersion }) -- Build players array local players = {} - for playerID = 0, DOTA_MAX_PLAYERS do + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then table.insert(players, { playerName = PlayerResource:GetPlayerName(playerID), @@ -348,7 +364,7 @@ function statCollection:sendStage2() -- Send stage2 self:sendStage('s2_phase_2.php', payload, function(err, res) - -- Check if we got an error + -- Check if we got an error if err then print(printPrefix .. errorJsonDecode) print(printPrefix .. err) @@ -389,20 +405,22 @@ function statCollection:sendStage3(winners, lastRound) -- Build players array local players = {} - for i = 1, (PlayerResource:GetPlayerCount() or 1) do - local steamID = PlayerResource:GetSteamAccountID(i - 1) - - table.insert(players, { - steamID32 = steamID, - connectionState = PlayerResource:GetConnectionState(i - 1), - isWinner = winners[PlayerResource:GetSteamAccountID(i - 1)] - }) + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + local steamID = PlayerResource:GetSteamAccountID(playerID) + + table.insert(players, { + steamID32 = steamID, + connectionState = PlayerResource:GetConnectionState(playerID), + isWinner = winners[steamID] + }) + end end -- Build rounds table - rounds = {} + local rounds = {} rounds[tostring(self.roundID)] = { - players=players + players = players } local payload = { authKey = self.authKey, @@ -418,7 +436,7 @@ function statCollection:sendStage3(winners, lastRound) -- Send stage3 self:sendStage('s2_phase_3.php', payload, function(err, res) - -- Check if we got an error + -- Check if we got an error if err then print(printPrefix .. errorJsonDecode) print(printPrefix .. err) @@ -436,10 +454,11 @@ function statCollection:sendStage3(winners, lastRound) print(printPrefix .. messagePhase3Complete) end) end + function statCollection:submitRound(args) --We receive the winners from the custom schema, lets tell phase 3 about it! - returnArgs = customSchema:submitRound(args) - self:sendStage3(returnArgs.winners, returnArgs.lastRound) + local returnArgs = customSchema:submitRound(args) + self:sendStage3(returnArgs.winners, returnArgs.lastRound) end -- Sends custom @@ -465,19 +484,19 @@ function statCollection:sendCustom(args) end -- Ensure we can only send it once, and everything is good to go - if self.HAS_ROUNDS == false then + if self.HAS_ROUNDS == false then if self.sentCustom then return end self.sentCustom = true end - + -- Print the intro message print(printPrefix .. messageCustomStarting) -- Build rounds table - rounds = {} + local rounds = {} rounds[tostring(self.roundID)] = { game = game, - players=players + players = players } local payload = { @@ -491,7 +510,7 @@ function statCollection:sendCustom(args) -- Send custom self:sendStage('s2_custom.php', payload, function(err, res) - -- Check if we got an error + -- Check if we got an error if err then print(printPrefix .. errorJsonDecode) print(printPrefix .. err) @@ -534,9 +553,9 @@ function statCollection:sendStage(stageName, payload, callback) end function tobool(s) - if s=="true" or s=="1" or s==1 then + if s == "true" or s == "1" or s == 1 then return true else --nil "false" "0" - return false + return false end -end \ No newline at end of file +end diff --git a/scripts/vscripts/statcollection/lib/utilities.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/utilities.lua similarity index 74% rename from scripts/vscripts/statcollection/lib/utilities.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/utilities.lua index 2b3ce2d..179bb3a 100644 --- a/scripts/vscripts/statcollection/lib/utilities.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/utilities.lua @@ -7,7 +7,7 @@ STAT_UTILITIES_VERSION = "0.2" ]] ------------------------------ --- Game Functions -- +-- Game Functions -- ------------------------------ -- Number of times roshan was killed @@ -15,7 +15,7 @@ function GetRoshanKills() local total_rosh_kills = 0 for playerID = 0, DOTA_MAX_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then - local roshan_kills_player = PlayerResource:GetRoshanKills(playerID) + local roshan_kills_player = PlayerResource:GetRoshanKills(playerID) total_rosh_kills = total_rosh_kills + roshan_kills_player end end @@ -24,24 +24,24 @@ function GetRoshanKills() end ------------------------------ --- Player Functions -- +-- Player Functions -- ------------------------------ -- Hero name without its npc_dota_hero prefix. -- If you would like to send custom hero names you should use a different function instead -function GetHeroName( playerID ) - local heroName = PlayerResource:GetSelectedHeroName( playerID ) - heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix - +function GetHeroName(playerID) + local heroName = PlayerResource:GetSelectedHeroName(playerID) + heroName = string.gsub(heroName, "npc_dota_hero_", "") --Cuts the npc_dota_hero_ prefix + return heroName end -- Current gold and item net worth -function GetNetworth( hero ) +function GetNetworth(hero) local networth = hero:GetGold() -- Iterate over item slots adding up its gold cost - for i=0,15 do + for i = 0, 15 do local item = hero:GetItemInSlot(i) if item then networth = networth + item:GetCost() @@ -52,14 +52,14 @@ function GetNetworth( hero ) end -- String of item name, without the item_ prefix -function GetItemSlot(hero,slot) +function GetItemSlot(hero, slot) local item = hero:GetItemInSlot(slot) local itemName = "" - + if item then - itemName = string.gsub(item:GetAbilityName(),"item_","") + itemName = string.gsub(item:GetAbilityName(), "item_", "") end - + return itemName end @@ -67,11 +67,11 @@ end function GetItemList(hero) local itemTable = {} - for i=0,5 do + for i = 0, 5 do local item = hero:GetItemInSlot(i) if item then - local itemName = string.gsub(item:GetAbilityName(),"item_","") --Cuts the item_ prefix - table.insert(itemTable,itemName) + local itemName = string.gsub(item:GetAbilityName(), "item_", "") --Cuts the item_ prefix + table.insert(itemTable, itemName) end end diff --git a/scripts/vscripts/statcollection/schema.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua similarity index 91% rename from scripts/vscripts/statcollection/schema.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua index ed5b48f..509633c 100644 --- a/scripts/vscripts/statcollection/schema.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua @@ -22,12 +22,12 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) @@ -62,7 +62,6 @@ function BuildPlayersArray() -- Example functions for generic stats are defined in statcollection/lib/utilities.lua -- Add player values here as someValue = GetSomePlayerValue(), - }) end end @@ -72,7 +71,7 @@ function BuildPlayersArray() end -- Prints the custom schema, required to get an schemaID -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -82,7 +81,7 @@ end -- Write 'test_schema' on the console to test your current functions instead of having to end the game if Convars:GetBool('developer') then - Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) end ------------------------------------- @@ -96,10 +95,10 @@ function customSchema:submitRound(isLastRound) local game = BuildGameArray() local players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. - return {winners = winners, lastRound = isLastRound} + return { winners = winners, lastRound = isLastRound } end -- A list of players marking who won this round diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battle_of_mirkwood.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battle_of_mirkwood.lua new file mode 100644 index 0000000..0af215e --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battle_of_mirkwood.lua @@ -0,0 +1,163 @@ +customSchema = class({}) + +function customSchema:init() + + -- Check the schema_examples folder for different implementations + + -- Flag Example + -- statCollection:setFlags({version = GetVersion()}) + + -- Listen for changes in the current state + ListenToGameEvent('game_rules_state_change', function(keys) + local state = GameRules:State_Get() + + -- Send custom stats when the game ends + if state == DOTA_GAMERULES_STATE_POST_GAME then + + -- Build game array + local game = BuildGameArray() + + -- Build players array + local players = BuildPlayersArray() + + -- Print the schema data to the console + if statCollection.TESTING then + PrintSchema(game, players) + end + + -- Send custom stats + if statCollection.HAS_SCHEMA then + statCollection:sendCustom({ game = game, players = players }) + end + end + end, nil) +end + +------------------------------------- + +-- In the statcollection/lib/utilities.lua, you'll find many useful functions to build your schema. +-- You are also encouraged to call your custom mod-specific functions + +-- Returns a table with our custom game tracking. +function BuildGameArray() + local game = {} + + -- Add game values here as game.someValue = GetSomeGameValue() + + -- team 1 score + game.s1 = GameRules.WAGameMode.vDeaths[DOTA_TEAM_BADGUYS] or 0 + -- team 2 score + game.s2 = GameRules.WAGameMode.vDeaths[DOTA_TEAM_GOODGUYS] or 0 + + return game +end + +-- Returns a table containing data for every player in the game +function BuildPlayersArray() + local players = {} + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + + local hero = PlayerResource:GetSelectedHeroEntity(playerID) + + local __stats__ = { + -- steamID32 required in here + steamID32 = PlayerResource:GetSteamAccountID(playerID), + + -- Example functions for generic stats are defined in statcollection/lib/utilities.lua + -- Add player values here as someValue = GetSomePlayerValue(), + + -- player hero name + hn = GetHeroName(playerID), + -- kills + hk = hero:GetKills(), + -- deaths + hd = hero:GetDeaths(), + -- last hits/ creep kills + lh = PlayerResource:GetLastHits(playerID), + -- total gold earned + xg = hero.xGoldEarned, + -- abadon time + at = GameRules.vDisconnectedHeroes[hero] or -1, + } + + + -- record abilities and ability level + local abilities = GetAbilityList(hero) + for i = 1, BOM_ABILITY_LIMIT do + --[[ + = + a1 = "evasion,1" + a2 = "life_steal,30" + ... + a7 = "xxx,15" + ]] + __stats__["a" .. i] = abilities[i] or "empty,-1" + end + + table.insert(players, __stats__) + end + end + end + + return players +end + +-- Prints the custom schema, required to get an schemaID +function PrintSchema(gameArray, playerArray) + print("-------- GAME DATA --------") + DeepPrintTable(gameArray) + print("\n-------- PLAYER DATA --------") + DeepPrintTable(playerArray) + print("-------------------------------------") +end + +-- Write 'test_schema' on the console to test your current functions instead of having to end the game +if Convars:GetBool('developer') then + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) +end + +------------------------------------- + +-- If your gamemode is round-based, you can use statCollection:submitRound(bLastRound) at any point of your main game logic code to send a round +-- If you intend to send rounds, make sure your settings.kv has the 'HAS_ROUNDS' set to true. Each round will send the game and player arrays defined earlier +-- The round number is incremented internally, lastRound can be marked to notify that the game ended properly +function customSchema:submitRound(isLastRound) + + local winners = BuildRoundWinnerArray() + local game = BuildGameArray() + local players = BuildPlayersArray() + + statCollection:sendCustom({ game = game, players = players }) + + isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. + return { winners = winners, lastRound = isLastRound } +end + +-- A list of players marking who won this round +function BuildRoundWinnerArray() + local winners = {} + local current_winner_team = GameRules.Winner or 0 --You'll need to provide your own way of determining which team won the round + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 + end + end + end + return winners +end + +---------------------------- Custom Stats------------------------ +function GetAbilityList(hero) + local abilities = {} + if hero.vAbilityLevel and type(hero.vAbilityLevel) == "table" then + for ability_name, ability_level in pairs(hero.vAbilityLevel) do + table.insert(abilities, ability_name .. "," .. ability_level) + end + end + return abilities +end + +----------------------------End Custom Stats Functions---------- \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/hivemind.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battleship.lua similarity index 51% rename from scripts/vscripts/statcollection/schema_examples/hivemind.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battleship.lua index 653f7d3..08557c4 100644 --- a/scripts/vscripts/statcollection/schema_examples/hivemind.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battleship.lua @@ -5,7 +5,7 @@ function customSchema:init() -- Check the schema_examples folder for different implementations -- Flag Example - -- statCollection:setFlags({version = GetVersion()}) + statCollection:setFlags({ version = storage:GetVersion() }) -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) @@ -22,12 +22,12 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) @@ -35,46 +35,18 @@ end ------------------------------------- -function customSchema:submitRound(args) - winners = BuildRoundWinnerArray() - game = BuildGameArray() - players = BuildPlayersArray() - - statCollection:sendCustom({game=game, players=players}) - - return {winners = winners, lastRound = false} -end - -------------------------------------- - -function BuildRoundWinnerArray() - local winners = {} - local current_winner_team = tonumber(CustomNetTables:GetTableValue("gamestate", "winning_team")["1"]) - for playerID = 0, DOTA_MAX_PLAYERS do - if PlayerResource:IsValidPlayerID(playerID) then - if not PlayerResource:IsBroadcaster(playerID) then - winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 - end - end - end - return winners -end - -------------------------------------- - -- In the statcollection/lib/utilities.lua, you'll find many useful functions to build your schema. -- You are also encouraged to call your custom mod-specific functions -- Returns a table with our custom game tracking. function BuildGameArray() local game = {} - game.m = match_count -- Match # - game.l = 0 -- Round length - for k,v in pairs(round_times) do - if v.length then - game.l = game.l + v.length - end - end + + -- Add game values here as game.someValue = GetSomeGameValue() + game.eh = storage:GetEmpGoldHist() -- Team advantage history + game.wn = storage:getWinner() -- Team winner + --game.th=storage:GetTideKillers() + return game end @@ -87,16 +59,31 @@ function BuildPlayersArray() local hero = PlayerResource:GetSelectedHeroEntity(playerID) + local teamname = "North" + if hero:GetTeamNumber() == DOTA_TEAM_GOODGUYS then + teamname = "South" + end + + local kickStatus = "Active" + if storage:GetDisconnectState(playerID) ~= 0 then + kickStatus = "Kicked" + end + table.insert(players, { -- steamID32 required in here steamID32 = PlayerResource:GetSteamAccountID(playerID), - -- Example functions of generic stats defined in statcollection/lib/utilities.lua - -- Keep, delete or change any as needed - ph = GetHeroName(playerID), -- Hero by its short name - ps = GameMode:GetScoreForTeam(PlayerResource:GetPlayer(playerID):GetTeam()), -- Score - st = split_time[PlayerResource:GetPlayer(playerID)] or 0, -- The amount of time this player spent in split form - ht = hero_time[PlayerResource:GetPlayer(playerID)] or 0, -- The amount of time this player spent in hero form + -- Example functions for generic stats are defined in statcollection/lib/utilities.lua + -- Add player values here as someValue = GetSomePlayerValue(), + tm = teamname, + shp = storage:GetHeroName(playerID), --Hero by its short name + kls = hero:GetKills(), --Player Kills + dth = hero:GetDeaths(), --Player Deaths + lvl = hero:GetLevel(), --Player Levels + afk = kickStatus, --Custom Player status + + -- Item List + bo = storage:GetPlayerHist(playerID) }) end end @@ -106,7 +93,7 @@ function BuildPlayersArray() end -- Prints the custom schema, required to get an schemaID -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -116,5 +103,38 @@ end -- Write 'test_schema' on the console to test your current functions instead of having to end the game if Convars:GetBool('developer') then - Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) +end + +------------------------------------- + +-- If your gamemode is round-based, you can use statCollection:submitRound(bLastRound) at any point of your main game logic code to send a round +-- If you intend to send rounds, make sure your settings.kv has the 'HAS_ROUNDS' set to true. Each round will send the game and player arrays defined earlier +-- The round number is incremented internally, lastRound can be marked to notify that the game ended properly +function customSchema:submitRound(isLastRound) + + local winners = BuildRoundWinnerArray() + local game = BuildGameArray() + local players = BuildPlayersArray() + + statCollection:sendCustom({ game = game, players = players }) + + isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. + return { winners = winners, lastRound = isLastRound } end + +-- A list of players marking who won this round +function BuildRoundWinnerArray() + local winners = {} + local current_winner_team = GameRules.Winner or 0 --You'll need to provide your own way of determining which team won the round + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 + end + end + end + return winners +end + +------------------------------------- \ No newline at end of file diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/dota_imba.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/dota_imba.lua new file mode 100644 index 0000000..0abf70a --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/dota_imba.lua @@ -0,0 +1,180 @@ +customSchema = class({}) + +function customSchema:init() + + -- Check the schema_examples folder for different implementations + + -- Flag Example + statCollection:setFlags({ version = IMBA_VERSION }) + + -- Listen for changes in the current state + ListenToGameEvent('game_rules_state_change', function(keys) + local state = GameRules:State_Get() + + -- Send custom stats when the game ends + if state == DOTA_GAMERULES_STATE_POST_GAME then + + -- Build game array + local game = BuildGameArray() + + -- Build players array + local players = BuildPlayersArray() + + -- Print the schema data to the console + if statCollection.TESTING then + PrintSchema(game, players) + end + + -- Send custom stats + if statCollection.HAS_SCHEMA then + statCollection:sendCustom({ game = game, players = players }) + end + end + end, nil) +end + +------------------------------------- + +-- In the statcollection/lib/utilities.lua, you'll find many useful functions to build your schema. +-- You are also encouraged to call your custom mod-specific functions + +-- Returns a table with our custom game tracking. +function BuildGameArray() + local game = {} + + -- Add game values here as game.someValue = GetSomeGameValue() + game.gl = GAME_TIME_ELAPSED -- Game length, from the horn sound, in seconds + game.wt = GAME_WINNER_TEAM -- Winning team + + return game +end + +-- Returns a table containing data for every player in the game +function BuildPlayersArray() + local players = {} + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + + local hero = PlayerResource:GetSelectedHeroEntity(playerID) + + -- Team string logic + local player_team = "" + if hero:GetTeam() == DOTA_TEAM_GOODGUYS then + player_team = "Radiant" + else + player_team = "Dire" + end + + table.insert(players, { + -- steamID32 required in here + steamID32 = PlayerResource:GetSteamAccountID(playerID), + + -- Example functions for generic stats are defined in statcollection/lib/utilities.lua + -- Add player values here as someValue = GetSomePlayerValue(), + + ph = GetHeroName(playerID), -- Hero by its short name + pl = hero:GetLevel(), -- Hero level at the end of the game + pnw = GetNetworth(hero), -- Sum of hero gold and item worth + pbb = hero.buyback_count, -- Amount of buybacks performed during the game + pt = player_team, -- Team this hero belongs to + pk = hero:GetKills(), -- Number of kills of this players hero + pa = hero:GetAssists(), -- Number of deaths of this players hero + pd = hero:GetDeaths(), -- Number of deaths of this players hero + i1 = GetItemSlotIMBA(hero, 0), -- Item Slot #1 + i2 = GetItemSlotIMBA(hero, 1), -- Item Slot #2 + i3 = GetItemSlotIMBA(hero, 2), -- Item Slot #3 + i4 = GetItemSlotIMBA(hero, 3), -- Item Slot #4 + i5 = GetItemSlotIMBA(hero, 4), -- Item Slot #5 + i6 = GetItemSlotIMBA(hero, 5), -- Item Slot #6 + }) + end + end + end + + return players +end + +-- Prints the custom schema, required to get an schemaID +function PrintSchema(gameArray, playerArray) + print("-------- GAME DATA --------") + DeepPrintTable(gameArray) + print("\n-------- PLAYER DATA --------") + DeepPrintTable(playerArray) + print("-------------------------------------") +end + +-- Write 'test_schema' on the console to test your current functions instead of having to end the game +if Convars:GetBool('developer') then + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) +end + +------------------------------------- + +-- If your gamemode is round-based, you can use statCollection:submitRound(bLastRound) at any point of your main game logic code to send a round +-- If you intend to send rounds, make sure your settings.kv has the 'HAS_ROUNDS' set to true. Each round will send the game and player arrays defined earlier +-- The round number is incremented internally, lastRound can be marked to notify that the game ended properly +function customSchema:submitRound(isLastRound) + + local winners = BuildRoundWinnerArray() + local game = BuildGameArray() + local players = BuildPlayersArray() + + statCollection:sendCustom({ game = game, players = players }) + + isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. + return { winners = winners, lastRound = isLastRound } +end + +-- A list of players marking who won this round +function BuildRoundWinnerArray() + local winners = {} + local current_winner_team = GameRules.Winner or 0 --You'll need to provide your own way of determining which team won the round + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 + end + end + end + return winners +end + +------------------------------------- +-- MY CUSTOM FUNCTIONS +------------------------------------- +function GetItemListImba(hero) + local itemTable = {} + + for i = 0, 5 do + local item = hero:GetItemInSlot(i) + if item then + if string.find(item:GetAbilityName(), "imba") then + local itemName = string.gsub(item:GetAbilityName(), "item_imba_", "") + table.insert(itemTable, itemName) + else + local itemName = string.gsub(item:GetAbilityName(), "item_", "") + table.insert(itemTable, itemName) + end + end + end + + table.sort(itemTable) + local itemList = table.concat(itemTable, ",") + + return itemList +end + +function GetItemSlotIMBA(hero, slot) + local item = hero:GetItemInSlot(slot) + + if item then + if string.find(item:GetAbilityName(), "imba") then + local itemName = string.gsub(item:GetAbilityName(), "item_imba_", "") + else + local itemName = string.gsub(item:GetAbilityName(), "item_", "") + end + end + + return itemName +end \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/enfos.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/enfos.lua similarity index 74% rename from scripts/vscripts/statcollection/schema_examples/enfos.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/enfos.lua index 7a3ac01..5287455 100644 --- a/scripts/vscripts/statcollection/schema_examples/enfos.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/enfos.lua @@ -4,8 +4,8 @@ function customSchema:init() -- Check the schema_examples folder for different implementations - -- Flag Example - -- statCollection:setFlags({version = GetVersion()}) + -- Flags + statCollection:setFlags({ version = CEnfosGameMode:GetVersion() }) -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) @@ -22,12 +22,12 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) @@ -63,18 +63,17 @@ function BuildPlayersArray() -- Example functions for generic stats are defined in statcollection/lib/utilities.lua -- Add player values here as someValue = GetSomePlayerValue(), - hn = GetHeroName(playerID), -- name - hl = hero:GetLevel(), -- level - hnw = GetNetworth(PlayerResource:GetSelectedHeroEntity(playerID)), -- Networth - pt = hero:GetTeam(), -- Hero's team - pcs = PlayerResource:GetConnectionState(playerID), -- Player connection state - pk = hero:GetKills(), -- Kills - pa = hero:GetAssists(), -- Assists - pd = hero:GetDeaths(), -- Deaths - plh = PlayerResource:GetLastHits(hero:GetPlayerOwnerID()), -- Last hits - ph = PlayerResource:GetHealing(hero:GetPlayerOwnerID()), -- Healing - pgpm = math.floor(PlayerResource:GetGoldPerMin(hero:GetPlayerOwnerID())), -- GPM - il = GetItemList(hero) -- Item list + hn = GetHeroName(playerID), -- name + hl = hero:GetLevel(), -- level + hnw = GetNetworth(PlayerResource:GetSelectedHeroEntity(playerID)), -- Networth + pt = GetPlayerTeam(PlayerResource:GetSelectedHeroEntity(playerID)), -- Hero's team + pk = hero:GetKills(), -- Kills + pa = hero:GetAssists(), -- Assists + pd = hero:GetDeaths(), -- Deaths + plh = PlayerResource:GetLastHits(hero:GetPlayerOwnerID()), -- Last hits + ph = PlayerResource:GetHealing(hero:GetPlayerOwnerID()), -- Healing + pgpm = math.floor(PlayerResource:GetGoldPerMin(hero:GetPlayerOwnerID())), -- GPM + il = GetItemList(hero) -- Item list }) end end @@ -84,7 +83,7 @@ function BuildPlayersArray() end -- Prints the custom schema, required to get an schemaID -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -94,7 +93,7 @@ end -- Write 'test_schema' on the console to test your current functions instead of having to end the game if Convars:GetBool('developer') then - Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) end ------------------------------------- @@ -108,10 +107,10 @@ function customSchema:submitRound(isLastRound) local game = BuildGameArray() local players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. - return {winners = winners, lastRound = isLastRound} + return { winners = winners, lastRound = isLastRound } end -- A list of players marking who won this round @@ -128,4 +127,4 @@ function BuildRoundWinnerArray() return winners end -------------------------------------- +------------------------------------- \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/battleship.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/epic_boss_fight.lua similarity index 60% rename from scripts/vscripts/statcollection/schema_examples/battleship.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/epic_boss_fight.lua index 396da2e..7c95e99 100644 --- a/scripts/vscripts/statcollection/schema_examples/battleship.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/epic_boss_fight.lua @@ -22,12 +22,12 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) @@ -43,10 +43,12 @@ function BuildGameArray() local game = {} -- Add game values here as game.someValue = GetSomeGameValue() - game.eh=storage:GetEmpGoldHist() - game.wn=storage:getWinner() - --game.th=storage:GetTideKillers() - + game.M_R = GameRules._roundnumber -- max round achieved + game.F_G = GameRules._finish -- has the game finished (win) + game.life = GameRules._live -- how many lives left + game.U_L = GameRules._used_live -- Used Life + game.T_L = GameRules._used_live + GameRules._live -- Total Life + return game end @@ -58,16 +60,6 @@ function BuildPlayersArray() if not PlayerResource:IsBroadcaster(playerID) then local hero = PlayerResource:GetSelectedHeroEntity(playerID) - - local teamname="North" - if hero:GetTeamNumber()==DOTA_TEAM_GOODGUYS then - teamname="South" - end - - local kickStatus="Active" - if storage:GetDisconnectState(playerID)~=0 then - kickStatus="Kicked" - end table.insert(players, { -- steamID32 required in here @@ -75,15 +67,23 @@ function BuildPlayersArray() -- Example functions for generic stats are defined in statcollection/lib/utilities.lua -- Add player values here as someValue = GetSomePlayerValue(), - tm = teamname, --This hero's custom team name - shp = storage:GetHeroName(playerID), --This hero's custom name - kls = hero:GetKills(), --This hero's kills - dth = hero:GetDeaths(), --This hero's deaths - lvl = hero:GetLevel(), --This hero's levels - afk = kickStatus, --This hero's custom connection status - - -- Item List - bo=storage:GetPlayerHist(playerID), + HN = GetHeroName(playerID), -- Hero + P_L = hero:GetLevel(), -- Level + P_NW = GetNetworth(PlayerResource:GetSelectedHeroEntity(playerID)), -- Networth + P_T = team, -- Team + P_K = hero:GetKills(), -- Kills + P_D = hero:GetDeaths(), -- Deaths + P_H = PlayerResource:GetHealing(hero:GetPlayerOwnerID()), -- Healing + P_R = hero.Ressurect, -- Ressurections + P_GPM = math.floor(PlayerResource:GetGoldPerMin(hero:GetPlayerOwnerID())), -- GPM + + --inventory : + i1 = GetItemSlot(hero, 0), + i2 = GetItemSlot(hero, 1), + i3 = GetItemSlot(hero, 2), + i4 = GetItemSlot(hero, 3), + i5 = GetItemSlot(hero, 4), + i6 = GetItemSlot(hero, 5) }) end end @@ -93,7 +93,7 @@ function BuildPlayersArray() end -- Prints the custom schema, required to get an schemaID -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -103,7 +103,7 @@ end -- Write 'test_schema' on the console to test your current functions instead of having to end the game if Convars:GetBool('developer') then - Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) end ------------------------------------- @@ -117,10 +117,10 @@ function customSchema:submitRound(isLastRound) local game = BuildGameArray() local players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. - return {winners = winners, lastRound = isLastRound} + return { winners = winners, lastRound = isLastRound } end -- A list of players marking who won this round @@ -137,58 +137,4 @@ function BuildRoundWinnerArray() return winners end -------------------------------------- --- My Custom Functions -- -------------------------------------- - -playerItemHist={} - -function storage:AddToPlayerItemHist(pid, ic) - - if playerItemHist[pid]==nil then - playerItemHist[pid]="" - print("Created Array") - end - if ic~=nil then - if string.len(tostring(playerItemHist[pid]))<96 then - playerItemHist[pid]=playerItemHist[pid] .. ic .. "," - end - end -end - -function storage:GetPlayerHist(playerID) - if playerItemHist[playerID] ~= nil then - return playerItemHist[playerID] - end - return "none" -end - -DisconnectKicked={} - -function storage:GetDisconnectState(playerID) - print("getDisconnect") - for _,hero in pairs( Entities:FindAllByClassname( "npc_dota_hero*")) do - if hero ~= nil and hero:IsOwnedByAnyPlayer() then - if hero:GetPlayerID() == playerID then - if DisconnectKicked[hero]~= nil then - return DisconnectKicked[hero] - else - return 0 - end - - end - end - end - return 0 - end - - function storage:GetHeroName(playerID) - for _,hero in pairs( Entities:FindAllByClassname( "npc_dota_hero*")) do - if hero ~= nil and hero:IsOwnedByAnyPlayer() then - if hero:GetPlayerID() == playerID then - return name_lookup[hero:GetName()] - end - end - end - return "failed" -end +------------------------------------- \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/example.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/example.lua similarity index 84% rename from scripts/vscripts/statcollection/schema_examples/example.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/example.lua index 3b10399..fe97e1e 100644 --- a/scripts/vscripts/statcollection/schema_examples/example.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/example.lua @@ -21,25 +21,23 @@ function customSchema:init() local players = BuildPlayersArray() -- Send custom stats - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end, nil) end ------------------------------------- - function customSchema:submitRound(args) winners = BuildRoundWinnerArray() game = BuildGameArray() players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) - return {winners = winners, lastRound = false} + return { winners = winners, lastRound = false } end ------------------------------------- - function BuildRoundWinnerArray() local winners = {} local current_winner_team = GameRules.Winner or 0 @@ -75,8 +73,8 @@ function BuildPlayersArray() -- Example functions of generic stats (keep, delete or change any that you don't need) ph = GetHeroName(playerID), --Hero by its short name - pk = hero:GetKills(), --Number of kills of this players hero - pd = hero:GetDeaths(), --Number of deaths of this players hero + pk = hero:GetKills(), --Number of kills of this players hero + pd = hero:GetDeaths(), --Number of deaths of this players hero nt = GetNetworth(hero), --Sum of hero gold and item worth -- Item List @@ -90,30 +88,29 @@ function BuildPlayersArray() end ------------------------------------- --- Stat Functions -- +-- Stat Functions -- ------------------------------------- - function GetRoshanKills() local total_rosh_kills = 0 for playerID = 0, DOTA_MAX_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then - local roshan_kills_player = PlayerResource:GetRoshanKills(playerID) + local roshan_kills_player = PlayerResource:GetRoshanKills(playerID) total_rosh_kills = total_rosh_kills + roshan_kills_player end end end -function GetHeroName( hero ) +function GetHeroName(hero) local heroName = hero:GetUnitName() - heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix + heroName = string.gsub(heroName, "npc_dota_hero_", "") --Cuts the npc_dota_hero_ prefix return heroName end -function GetNetworth( hero ) +function GetNetworth(hero) local gold = hero:GetGold() -- Iterate over item slots adding up its gold cost - for i=0,15 do + for i = 0, 15 do local item = hero:GetItemInSlot(i) if item then gold = gold + item:GetCost() @@ -125,7 +122,7 @@ function GetItemName(hero, slot) local item = hero:GetItemInSlot(slot) if item then local itemName = item:GetAbilityName() - itemName = string.gsub(itemName,"item_","") --Cuts the item_ prefix + itemName = string.gsub(itemName, "item_", "") --Cuts the item_ prefix return itemName else return "" @@ -143,12 +140,12 @@ function GetItemList(hero) local itemTable = {} local itemList - for i=0,5 do + for i = 0, 5 do item = hero:GetItemInSlot(i) if item then itemID = item:GetAbilityIndex() if itemID then - table.insert(itemTable,itemID) + table.insert(itemTable, itemID) end end end diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua new file mode 100644 index 0000000..ae7bff2 --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua @@ -0,0 +1,142 @@ +customSchema = class({}) + +function customSchema:init() + + -- Check the schema_examples folder for different implementations + + -- Flag Example + statCollection:setFlags({ version = _G.AGS_VERSION }) + + -- Listen for changes in the current state + ListenToGameEvent('game_rules_state_change', function(keys) + local state = GameRules:State_Get() + + -- Send custom stats when the game ends + if state == DOTA_GAMERULES_STATE_POST_GAME then + + -- Build game array + local game = BuildGameArray() + + -- Build players array + local players = BuildPlayersArray() + + -- Print the schema data to the console + if statCollection.TESTING then + PrintSchema(game, players) + end + + -- Send custom stats + if statCollection.HAS_SCHEMA then + statCollection:sendCustom({ game = game, players = players }) + end + end + end, nil) +end + +------------------------------------- + +-- In the statcollection/lib/utilities.lua, you'll find many useful functions to build your schema. +-- You are also encouraged to call your custom mod-specific functions + +-- Returns a table with our custom game tracking. +function BuildGameArray() + local game = {} + + -- Add game values here as game.someValue = GetSomeGameValue() + game.gw = _G.GAME_WINNER_TEAM -- winning team + + return game +end + +-- Returns a table containing data for every player in the game +function BuildPlayersArray() + local players = {} + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + + local hero = PlayerResource:GetSelectedHeroEntity(playerID) + + local heroTeam = PlayerResource:GetTeam(playerID) + local heroTeamStr = "" + if heroTeam == 2 then + heroTeamStr = "Radiant" + elseif heroTeam == 3 then + heroTeamStr = "Dire" + end + + table.insert(players, { + -- steamID32 required in here + steamID32 = PlayerResource:GetSteamAccountID(playerID) or 0, + + -- Example functions for generic stats are defined in statcollection/lib/utilities.lua + -- Add player values here as someValue = GetSomePlayerValue(), + ph = GetHeroName(playerID) or "", -- Hero by its short name + pt = heroTeamStr or "", -- Team this hero belongs to + pl = hero:GetLevel() or 1, -- Hero level at the end of the game + pnw = GetNetworth(hero) or 0, -- Sum of hero gold and item worth + pk = hero:GetKills() or 0, -- Number of kills of this players hero + pa = hero:GetAssists() or 0, -- Number of deaths of this players hero + pd = hero:GetDeaths() or 0, -- Number of deaths of this players hero + + -- Item list + is1 = GetItemSlot(hero, 0), + is2 = GetItemSlot(hero, 1), + is3 = GetItemSlot(hero, 2), + is4 = GetItemSlot(hero, 3), + is5 = GetItemSlot(hero, 4), + is6 = GetItemSlot(hero, 5) + }) + end + end + end + + return players +end + +-- Prints the custom schema, required to get an schemaID +function PrintSchema(gameArray, playerArray) + print("-------- GAME DATA --------") + DeepPrintTable(gameArray) + print("\n-------- PLAYER DATA --------") + DeepPrintTable(playerArray) + print("-------------------------------------") +end + +-- Write 'test_schema' on the console to test your current functions instead of having to end the game +if Convars:GetBool('developer') then + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) +end + +------------------------------------- + +-- If your gamemode is round-based, you can use statCollection:submitRound(bLastRound) at any point of your main game logic code to send a round +-- If you intend to send rounds, make sure your settings.kv has the 'HAS_ROUNDS' set to true. Each round will send the game and player arrays defined earlier +-- The round number is incremented internally, lastRound can be marked to notify that the game ended properly +function customSchema:submitRound(isLastRound) + + local winners = BuildRoundWinnerArray() + local game = BuildGameArray() + local players = BuildPlayersArray() + + statCollection:sendCustom({ game = game, players = players }) + + isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. + return { winners = winners, lastRound = isLastRound } +end + +-- A list of players marking who won this round +function BuildRoundWinnerArray() + local winners = {} + local current_winner_team = GameRules.Winner or 0 --You'll need to provide your own way of determining which team won the round + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 + end + end + end + return winners +end + +------------------------------------- \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/hivemind.lua similarity index 74% rename from scripts/vscripts/statcollection/schema_examples/global_skillshots.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/hivemind.lua index f80de39..10111e7 100644 --- a/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/hivemind.lua @@ -5,7 +5,7 @@ function customSchema:init() -- Check the schema_examples folder for different implementations -- Flag Example - statCollection:setFlags({version = _G.AGS_VERSION}) + statCollection:setFlags({ version = HIVEMIND_VERSION, kills_to_win = KILLS_TO_WIN }) -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) @@ -22,12 +22,12 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) @@ -41,10 +41,14 @@ end -- Returns a table with our custom game tracking. function BuildGameArray() local game = {} - game.len = GameRules:GetGameTime() - game.win = _G.GAME_WINNER_TEAM - -- Add game values here as game.someValue = GetSomeGameValue() + -- Add game values here as game.someValue = GetSomeGameValue() + game.l = 0 -- Round length + for k, v in pairs(round_times) do + if v.length then + game.l = math.floor(game.l + v.length) + end + end return game end @@ -56,29 +60,17 @@ function BuildPlayersArray() if not PlayerResource:IsBroadcaster(playerID) then local hero = PlayerResource:GetSelectedHeroEntity(playerID) - local heroTeam = PlayerResource:GetTeam(playerID) - local heroTeamStr = "" - if heroTeam == 2 then - heroTeamStr = "Radiant" - elseif heroTeam == 3 then - heroTeamStr = "Dire" - end - + table.insert(players, { -- steamID32 required in here steamID32 = PlayerResource:GetSteamAccountID(playerID), -- Example functions for generic stats are defined in statcollection/lib/utilities.lua -- Add player values here as someValue = GetSomePlayerValue(), - - nam= GetHeroName(playerID), -- Hero by its short name - lvl = hero:GetLevel(), -- Hero level at the end of the game - pnw = GetNetworth(hero), -- Sum of hero gold and item worth - pt = heroTeamStr, -- Team this hero belongs to - pk = hero:GetKills(), -- Number of kills of this players hero - pa = hero:GetAssists(), -- Number of deaths of this players hero - pd = hero:GetDeaths(), -- Number of deaths of this players hero - pil = GetItemList(hero) -- Item list + ph = GetHeroName(playerID), -- Hero by its short name + ps = GameMode:GetScoreForTeam(PlayerResource:GetPlayer(playerID):GetTeam()), -- Score + st = math.floor(split_time[PlayerResource:GetPlayer(playerID)] or 0), -- The amount of time this player spent in split form + ht = math.floor(hero_time[PlayerResource:GetPlayer(playerID)] or 0), -- The amount of time this player spent in hero form }) end end @@ -88,7 +80,7 @@ function BuildPlayersArray() end -- Prints the custom schema, required to get an schemaID -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -98,7 +90,7 @@ end -- Write 'test_schema' on the console to test your current functions instead of having to end the game if Convars:GetBool('developer') then - Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(), BuildPlayersArray()) end, "Test the custom schema arrays", 0) end ------------------------------------- @@ -112,10 +104,10 @@ function customSchema:submitRound(isLastRound) local game = BuildGameArray() local players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) isLastRound = isLastRound or false --If the function is passed with no parameter, default to false. - return {winners = winners, lastRound = isLastRound} + return { winners = winners, lastRound = isLastRound } end -- A list of players marking who won this round @@ -132,4 +124,4 @@ function BuildRoundWinnerArray() return winners end -------------------------------------- +------------------------------------- \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/pmp.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/pmp.lua similarity index 93% rename from scripts/vscripts/statcollection/schema_examples/pmp.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/pmp.lua index 8149d73..29e6a05 100644 --- a/scripts/vscripts/statcollection/schema_examples/pmp.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/pmp.lua @@ -3,11 +3,11 @@ customSchema = class({}) function customSchema:init(options) -- Flags - statCollection:setFlags({version = GetVersion()}) + statCollection:setFlags({ version = GetVersion() }) -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) - -- Grab the current state + -- Grab the current state local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_POST_GAME then @@ -19,7 +19,7 @@ function customSchema:init(options) local players = BuildPlayersArray() -- Send custom stats - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end, nil) end @@ -29,13 +29,12 @@ function customSchema:submitRound(args) game = BuildGameArray() players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) - return {winners = winners, lastRound = false} + return { winners = winners, lastRound = false } end ------------------------------------- - function BuildRoundWinnerArray() local winners = {} local current_winner_team = GameRules.Winner or 0 @@ -110,7 +109,7 @@ function BuildPlayersArray() return players end -function GetPlayerWeaponLevel( playerID ) +function GetPlayerWeaponLevel(playerID) local player_upgrades = PMP:GetUpgradeList(playerID) local race = GetPlayerRace(playerID) local weapon_level = 0 diff --git a/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua similarity index 83% rename from scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua index d47d88b..b9968b6 100644 --- a/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua @@ -5,7 +5,7 @@ function customSchema:init() -- Check the schema_examples folder for different implementations -- Flag Example - statCollection:setFlags({version = GetVersion()}) + statCollection:setFlags({ version = GetVersion() }) -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) @@ -22,31 +22,29 @@ function customSchema:init() -- Print the schema data to the console if statCollection.TESTING then - PrintSchema(game,players) + PrintSchema(game, players) end -- Send custom stats if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) end end end, nil) end ------------------------------------- - function customSchema:submitRound(args) winners = BuildRoundWinnerArray() game = BuildGameArray() players = BuildPlayersArray() - statCollection:sendCustom({game=game, players=players}) + statCollection:sendCustom({ game = game, players = players }) - return {winners = winners, lastRound = false} + return { winners = winners, lastRound = false } end ------------------------------------- - function BuildRoundWinnerArray() local winners = {} local current_winner_team = GameRules.Winner or 0 @@ -85,8 +83,8 @@ function BuildPlayersArray() -- Example functions of generic stats (keep, delete or change any that you don't need) ph = GetHeroName(playerID), --Hero by its short name - lvl = hero:GetLevel(), -- Return the level of the hero - pd = hero:GetDeaths(), --Number of deaths of this players hero + lvl = hero:GetLevel(), --Return the level of the hero + pd = hero:GetDeaths(), --Number of deaths of this players hero nt = GetNetworth(hero), --Sum of hero gold and item worth -- Item List @@ -95,19 +93,18 @@ function BuildPlayersArray() -- Ability List an1 = GetAbilityName(hero, 0), --ability 1 (name) -- shows us the total selection and winrate of each skill, broken into SB and Normal theme al1 = GetAbilityNameLevel(hero, 0), --ability 1 (name + level) -- shows us the final level and its winrate of each skill, broken into SB and Normal theme - + an2 = GetAbilityName(hero, 1), --ability 2 (name) al2 = GetAbilityNameLevel(hero, 1), --ability 2 (name + level) - + an3 = GetAbilityName(hero, 2), --ability 3 (name) al3 = GetAbilityNameLevel(hero, 2), --ability 3 (name + level) - + an4 = GetAbilityName(hero, 3), --ability 4 (name) al4 = GetAbilityNameLevel(hero, 3), --ability 4 (name + level) -- SNS Specific - scr = hero.score, -- Save-to-death ratio - + scr = hero.score, --Save-to-death ratio }) end end @@ -117,10 +114,9 @@ function BuildPlayersArray() end ------------------------------------- --- Stat Functions -- +-- Stat Functions -- ------------------------------------- - -function PrintSchema( gameArray, playerArray ) +function PrintSchema(gameArray, playerArray) print("-------- GAME DATA --------") DeepPrintTable(gameArray) print("\n-------- PLAYER DATA --------") @@ -128,17 +124,17 @@ function PrintSchema( gameArray, playerArray ) print("-------------------------------------") end -function GetHeroName( playerID ) - local heroName = PlayerResource:GetSelectedHeroName( playerID ) - heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix +function GetHeroName(playerID) + local heroName = PlayerResource:GetSelectedHeroName(playerID) + heroName = string.gsub(heroName, "npc_dota_hero_", "") --Cuts the npc_dota_hero_ prefix return heroName end -function GetNetworth( hero ) +function GetNetworth(hero) local gold = hero:GetGold() -- Iterate over item slots adding up its gold cost - for i=0,15 do + for i = 0, 15 do local item = hero:GetItemInSlot(i) if item then gold = gold + item:GetCost() @@ -160,7 +156,7 @@ end function GetItemList(hero) local itemTable = {} - for i=0,5 do + for i = 0, 5 do local item = hero:GetItemInSlot(i) if item then local itemName = string.gsub(item:GetAbilityName(), "item_", "") @@ -172,7 +168,7 @@ function GetItemList(hero) return itemList end -function GetAbilityName( hero, id ) +function GetAbilityName(hero, id) local ability = hero:GetAbilityByIndex(id) if ability then local abilityName = string.gsub(ability:GetAbilityName(), "antimage_", "") --remove unnecessary parts of string @@ -182,15 +178,15 @@ function GetAbilityName( hero, id ) return "" end -function GetAbilityNameList( hero ) +function GetAbilityNameList(hero) local nameTable = {} - for i=0,3 do + for i = 0, 3 do table.insert(nameTable, GetAbilityName(hero, i)) end return table.concat(nameTable, ",") end -function GetAbilityLevel( hero, id ) +function GetAbilityLevel(hero, id) ability = hero:GetAbilityByIndex(id) if ability then return ability:GetLevel() @@ -198,19 +194,19 @@ function GetAbilityLevel( hero, id ) return 0 end -function GetAbilityLevelList( hero ) +function GetAbilityLevelList(hero) local nameTable = {} - for i=0,3 do + for i = 0, 3 do table.insert(nameTable, GetAbilityLevel(hero, i)) end return table.concat(nameTable, ",") end -function GetAbilityNameLevel( hero, id ) +function GetAbilityNameLevel(hero, id) return GetAbilityName(hero, id) .. "__" .. GetAbilityLevel(hero, id) end -function GetTheme( ) +function GetTheme() local theme = GameMode.gameTheme if theme == 1 then return "Normal" @@ -218,4 +214,4 @@ function GetTheme( ) return "SpongeBob" end return "Unknown" -end +end \ No newline at end of file diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/thd2.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/thd2.lua new file mode 100644 index 0000000..cb211f7 --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/thd2.lua @@ -0,0 +1,272 @@ +customSchema = class({}) + +function customSchema:init() + + -- Check the schema_examples folder for different implementations + + -- Flag Example + statCollection:setFlags({ version = "1.0" }) + + -- Listen for changes in the current state + ListenToGameEvent('game_rules_state_change', function(keys) + local state = GameRules:State_Get() + + -- Send custom stats when the game ends + if state == DOTA_GAMERULES_STATE_POST_GAME then + + -- Build game array + local game = BuildGameArray() + + -- Build players array + local players = BuildPlayersArray() + + -- Print the schema data to the console + if statCollection.TESTING then + PrintSchema(game, players) + end + + -- Send custom stats + if statCollection.HAS_SCHEMA then + statCollection:sendCustom({ game = game, players = players }) + end + end + end, nil) +end + +------------------------------------- +function customSchema:submitRound(args) + winners = BuildRoundWinnerArray() + game = BuildGameArray() + players = BuildPlayersArray() + + statCollection:sendCustom({ game = game, players = players }) + + return { winners = winners, lastRound = false } +end + +------------------------------------- +function BuildRoundWinnerArray() + local winners = {} + local current_winner_team = GameRules.Winner or 0 + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + winners[PlayerResource:GetSteamAccountID(playerID)] = (PlayerResource:GetTeam(playerID) == current_winner_team) and 1 or 0 + end + end + end + return winners +end + +-- Returns a table with our custom game tracking. +function BuildGameArray() + local game = {} + --game.rs = GetRoshanKills() -- This is an example of a function that returns how many times roshan was killed + return game +end + +-- Returns a table containing data for every player in the game +function BuildPlayersArray() + local players = {} + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + if not PlayerResource:IsBroadcaster(playerID) then + + local hero = PlayerResource:GetSelectedHeroEntity(playerID) + + local teamname = "Moriya Shrine" + if hero:GetTeamNumber() == DOTA_TEAM_GOODGUYS then + teamname = "Hakurei Shrine" + end + + local item0 = hero:GetItemInSlot(0) + local item1 = hero:GetItemInSlot(1) + local item2 = hero:GetItemInSlot(2) + local item3 = hero:GetItemInSlot(3) + local item4 = hero:GetItemInSlot(4) + local item5 = hero:GetItemInSlot(5) + + local itemName0 = "" + local itemName1 = "" + local itemName2 = "" + local itemName3 = "" + local itemName4 = "" + local itemName5 = "" + + if item0 then + itemName0 = item0:GetAbilityName() + end + if item1 then + itemName1 = item1:GetAbilityName() + end + if item2 then + itemName2 = item2:GetAbilityName() + end + if item3 then + itemName3 = item3:GetAbilityName() + end + if item4 then + itemName4 = item4:GetAbilityName() + end + if item5 then + itemName5 = item5:GetAbilityName() + end + + local ability1 = hero:GetAbilityByIndex(0) + local ability2 = hero:GetAbilityByIndex(1) + local ability3 = hero:GetAbilityByIndex(2) + local ability4 = hero:GetAbilityByIndex(3) + local ability5 = hero:GetAbilityByIndex(4) + local ability6 = hero:GetAbilityByIndex(5) + local ability7 = hero:GetAbilityByIndex(6) + local ability8 = hero:GetAbilityByIndex(7) + local ability9 = hero:GetAbilityByIndex(8) + local ability10 = hero:GetAbilityByIndex(9) + + local abilityLevel1 = 0 + local abilityLevel2 = 0 + local abilityLevel3 = 0 + local abilityLevel4 = 0 + local abilityLevel5 = 0 + local abilityLevel6 = 0 + local abilityLevel7 = 0 + local abilityLevel8 = 0 + local abilityLevel9 = 0 + local abilityLevel10 = 0 + + if ability1 then + abilityLevel1 = ability1:GetLevel() + end + if ability2 then + abilityLevel2 = ability2:GetLevel() + end + if ability3 then + abilityLevel3 = ability3:GetLevel() + end + if ability4 then + abilityLevel4 = ability4:GetLevel() + end + if ability5 then + abilityLevel5 = ability5:GetLevel() + end + if ability6 then + abilityLevel6 = ability6:GetLevel() + end + if ability7 then + abilityLevel7 = ability7:GetLevel() + end + if ability8 then + abilityLevel8 = ability8:GetLevel() + end + if ability9 then + abilityLevel9 = ability9:GetLevel() + end + if ability10 then + abilityLevel10 = ability10:GetLevel() + end + + + table.insert(players, { + --steamID32 required in here + steamID32 = PlayerResource:GetSteamAccountID(playerID), + ph = GetHeroName(playerID), -- Hero name + pl = hero:GetLevel(), -- Levels + pk = hero:GetKills(), -- Kills + pa = hero:GetAssists(), -- Assists + pd = hero:GetDeaths(), -- Deaths + pg = hero:GetGold(), -- Now gold + dn = hero:GetDenies(), -- Denies + lh = hero:GetLastHits(), -- Lasthit + sa = math.floor(PlayerResource:GetStuns(playerID)), -- StunAmount + sb = PlayerResource:GetGoldSpentOnBuybacks(playerID), -- Gold spent buy backs + sc = PlayerResource:GetGoldSpentOnConsumables(playerID), -- Gold spent on consumables + si = PlayerResource:GetGoldSpentOnItems(playerID), -- Gold spent on items + ss = PlayerResource:GetGoldSpentOnSupport(playerID), -- Gold spent on support + pc = PlayerResource:GetNumConsumablesPurchased(playerID), -- Num consumables purchased + pi = PlayerResource:GetNumItemsPurchased(playerID), -- Num items purchased + eg = PlayerResource:GetTotalEarnedGold(playerID), -- Total earned gold + ex = hero:GetCurrentXP(), -- Total earned xp + nw = GetNetworth(hero), -- Networth + i1 = itemName0, + i2 = itemName1, + i3 = itemName2, + i4 = itemName3, + i5 = itemName4, + i6 = itemName5, + al1 = abilityLevel1, + al2 = abilityLevel2, + al3 = abilityLevel3, + al4 = abilityLevel4, + al5 = abilityLevel5, + al6 = abilityLevel6, + al7 = abilityLevel7, + al8 = abilityLevel8, + al9 = abilityLevel9, + al10 = abilityLevel10, + pt = teamname, -- Team name + }) + end + end + end + + return players +end + +------------------------------------- +-- Stat Functions -- +------------------------------------- +function PrintSchema(gameArray, playerArray) + print("-------- GAME DATA --------") + DeepPrintTable(gameArray) + print("\n-------- PLAYER DATA --------") + DeepPrintTable(playerArray) + print("-------------------------------------") +end + +--NOTE THAT THIS FUNCTION RELIES ON YOUR npc_items_custom.txt +--having "ID" properly set to unique values (within your mod) +function GetItemNameList(hero) + --Create a table of items for the hero + --Order that table to remove the impact of slot order + --Concatonate the table into a single string + local item + local itemID + local itemTable = {} + local itemList + + for i = 0, 5 do + item = hero:GetItemInSlot(i) + if item then + itemID = item:GetAbilityName() + itemID = string.gsub(itemID, "item_", "") + if itemID then + table.insert(itemTable, itemID) + end + end + end + + table.sort(itemTable) + itemList = table.concat(itemTable, ",") + + return itemList +end + +function GetAbilityNameList(hero) + local abilityName + local abilityData = {} + local abilityCount = 0 + while abilityCount < 16 do + local ab = hero:GetAbilityByIndex(abilityCount) + + if IsValidEntity(ab) then + abilityName = ab:GetAbilityName() + table.insert(abilityData, abilityName) + end + + abilityCount = abilityCount + 1 + end + + table.sort(abilityData) + local abilityNameList = table.concat(abilityData, ",") + return abilityNameList +end \ No newline at end of file diff --git a/scripts/vscripts/statcollection/settings.kv b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv similarity index 100% rename from scripts/vscripts/statcollection/settings.kv rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv diff --git a/panorama/scripts/custom_game/statcollection.js b/panorama/scripts/custom_game/statcollection.js deleted file mode 100644 index 358bcc5..0000000 --- a/panorama/scripts/custom_game/statcollection.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; - -function OnClientCheckIn (args) { - - var payload = { - modIdentifier: args.modID, - steamID32: GetSteamID32(), - matchID: args.matchID, - schemaVersion: args.schemaVersion - } - - $.Msg('Sending: ',payload) - - $.AsyncWebRequest( 'http://getdotastats.com/s2/api/s2_check_in.php', - { - type: 'POST', - data: {payload: JSON.stringify(payload)}, - success: function( data ) - { - $.Msg('GDS Reply: ', data) - } - }); -} - -function GetSteamID32() { - var playerInfo = Game.GetPlayerInfo(Game.GetLocalPlayerID()) - - var steamID64 = playerInfo.player_steamid - var steamIDPart = Number(steamID64.substring(3)) - var steamID32 = String(steamIDPart - 61197960265728) - - return steamID32 -} - -(function () { - $.Msg("StatCollection Client Loaded") - - GameEvents.Subscribe( "statcollection_client", OnClientCheckIn ); - -})(); \ No newline at end of file diff --git a/scripts/vscripts/statcollection/lib/md5.lua b/scripts/vscripts/statcollection/lib/md5.lua deleted file mode 100644 index 199b6ba..0000000 --- a/scripts/vscripts/statcollection/lib/md5.lua +++ /dev/null @@ -1,384 +0,0 @@ -local md5 = { - _VERSION = "md5.lua 0.5.0", - _DESCRIPTION = "MD5 computation in Lua (5.1)", - _URL = "https://github.com/kikito/md5.lua", - _LICENSE = [[ - MIT LICENSE - - Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ]] -} - --- bit lib implementions - -local floor, abs, max = math.floor, math.abs, math.max -local char, byte, format, rep, sub = - string.char, string.byte, string.format, string.rep, string.sub - -local function check_int(n) - -- checking not float - if(n - floor(n) > 0) then - error("trying to use bitwise operation on non-integer!") - end -end - -local function tbl2number(tbl) - local n = #tbl - - local rslt = 0 - local power = 1 - for i = 1, n do - rslt = rslt + tbl[i]*power - power = power*2 - end - - return rslt -end - -local function expand(tbl_m, tbl_n) - local big = {} - local small = {} - if(#tbl_m > #tbl_n) then - big = tbl_m - small = tbl_n - else - big = tbl_n - small = tbl_m - end - -- expand small - for i = #small + 1, #big do - small[i] = 0 - end - -end - -local to_bits -- needs to be declared before bit_not - -local function bit_not(n) - local tbl = to_bits(n) - local size = max(#tbl, 32) - for i = 1, size do - if(tbl[i] == 1) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - return tbl2number(tbl) -end - --- defined as local above -to_bits = function (n) - check_int(n) - if(n < 0) then - -- negative - return to_bits(bit_not(abs(n)) + 1) - end - -- to bits table - local tbl = {} - local cnt = 1 - while (n > 0) do - local last = math.mod(n,2) - if(last == 1) then - tbl[cnt] = 1 - else - tbl[cnt] = 0 - end - n = (n-last)/2 - cnt = cnt + 1 - end - - return tbl -end - -local function bit_or(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = max(#tbl_m, #tbl_n) - for i = 1, rslt do - if(tbl_m[i]== 0 and tbl_n[i] == 0) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - - return tbl2number(tbl) -end - -local function bit_and(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = max(#tbl_m, #tbl_n) - for i = 1, rslt do - if(tbl_m[i]== 0 or tbl_n[i] == 0) then - tbl[i] = 0 - else - tbl[i] = 1 - end - end - - return tbl2number(tbl) -end - -local function bit_xor(m, n) - local tbl_m = to_bits(m) - local tbl_n = to_bits(n) - expand(tbl_m, tbl_n) - - local tbl = {} - local rslt = max(#tbl_m, #tbl_n) - for i = 1, rslt do - if(tbl_m[i] ~= tbl_n[i]) then - tbl[i] = 1 - else - tbl[i] = 0 - end - end - - return tbl2number(tbl) -end - -local function bit_rshift(n, bits) - check_int(n) - - local high_bit = 0 - if(n < 0) then - -- negative - n = bit_not(abs(n)) + 1 - high_bit = 2147483648 -- 0x80000000 - end - - for i=1, bits do - n = n/2 - n = bit_or(floor(n), high_bit) - end - return floor(n) -end - -local function bit_lshift(n, bits) - check_int(n) - - if(n < 0) then - -- negative - n = bit_not(abs(n)) + 1 - end - - for i=1, bits do - n = n*2 - end - return bit_and(n, 4294967295) -- 0xFFFFFFFF -end - --- convert little-endian 32-bit int to a 4-char string -local function lei2str(i) - local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end - return f(0)..f(8)..f(16)..f(24) -end - --- convert raw string to big-endian int -local function str2bei(s) - local v=0 - for i=1, #s do - v = v * 256 + byte(s, i) - end - return v -end - --- convert raw string to little-endian int -local function str2lei(s) - local v=0 - for i = #s,1,-1 do - v = v*256 + byte(s, i) - end - return v -end - --- cut up a string in little-endian ints of given size -local function cut_le_str(s,...) - local o, r = 1, {} - local args = {...} - for i=1, #args do - table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) - o = o + args[i] - end - return r -end - -local swap = function (w) return str2bei(lei2str(w)) end - -local function hex2binaryaux(hexval) - return char(tonumber(hexval, 16)) -end - -local function hex2binary(hex) - local result, _ = hex:gsub('..', hex2binaryaux) - return result -end - --- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh) --- 10/02/2001 jcw@equi4.com - -local FF = 0xffffffff -local CONSTS = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 -} - -local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end -local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end -local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end -local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end -local z=function (f,a,b,c,d,x,s,ac) - a=bit_and(a+f(b,c,d)+x+ac,FF) - -- be *very* careful that left shift does not cause rounding! - return bit_or(bit_lshift(bit_and(a,bit_rshift(FF,s)),s),bit_rshift(a,32-s))+b -end - -local function transform(A,B,C,D,X) - local a,b,c,d=A,B,C,D - local t=CONSTS - - a=z(f,a,b,c,d,X[ 0], 7,t[ 1]) - d=z(f,d,a,b,c,X[ 1],12,t[ 2]) - c=z(f,c,d,a,b,X[ 2],17,t[ 3]) - b=z(f,b,c,d,a,X[ 3],22,t[ 4]) - a=z(f,a,b,c,d,X[ 4], 7,t[ 5]) - d=z(f,d,a,b,c,X[ 5],12,t[ 6]) - c=z(f,c,d,a,b,X[ 6],17,t[ 7]) - b=z(f,b,c,d,a,X[ 7],22,t[ 8]) - a=z(f,a,b,c,d,X[ 8], 7,t[ 9]) - d=z(f,d,a,b,c,X[ 9],12,t[10]) - c=z(f,c,d,a,b,X[10],17,t[11]) - b=z(f,b,c,d,a,X[11],22,t[12]) - a=z(f,a,b,c,d,X[12], 7,t[13]) - d=z(f,d,a,b,c,X[13],12,t[14]) - c=z(f,c,d,a,b,X[14],17,t[15]) - b=z(f,b,c,d,a,X[15],22,t[16]) - - a=z(g,a,b,c,d,X[ 1], 5,t[17]) - d=z(g,d,a,b,c,X[ 6], 9,t[18]) - c=z(g,c,d,a,b,X[11],14,t[19]) - b=z(g,b,c,d,a,X[ 0],20,t[20]) - a=z(g,a,b,c,d,X[ 5], 5,t[21]) - d=z(g,d,a,b,c,X[10], 9,t[22]) - c=z(g,c,d,a,b,X[15],14,t[23]) - b=z(g,b,c,d,a,X[ 4],20,t[24]) - a=z(g,a,b,c,d,X[ 9], 5,t[25]) - d=z(g,d,a,b,c,X[14], 9,t[26]) - c=z(g,c,d,a,b,X[ 3],14,t[27]) - b=z(g,b,c,d,a,X[ 8],20,t[28]) - a=z(g,a,b,c,d,X[13], 5,t[29]) - d=z(g,d,a,b,c,X[ 2], 9,t[30]) - c=z(g,c,d,a,b,X[ 7],14,t[31]) - b=z(g,b,c,d,a,X[12],20,t[32]) - - a=z(h,a,b,c,d,X[ 5], 4,t[33]) - d=z(h,d,a,b,c,X[ 8],11,t[34]) - c=z(h,c,d,a,b,X[11],16,t[35]) - b=z(h,b,c,d,a,X[14],23,t[36]) - a=z(h,a,b,c,d,X[ 1], 4,t[37]) - d=z(h,d,a,b,c,X[ 4],11,t[38]) - c=z(h,c,d,a,b,X[ 7],16,t[39]) - b=z(h,b,c,d,a,X[10],23,t[40]) - a=z(h,a,b,c,d,X[13], 4,t[41]) - d=z(h,d,a,b,c,X[ 0],11,t[42]) - c=z(h,c,d,a,b,X[ 3],16,t[43]) - b=z(h,b,c,d,a,X[ 6],23,t[44]) - a=z(h,a,b,c,d,X[ 9], 4,t[45]) - d=z(h,d,a,b,c,X[12],11,t[46]) - c=z(h,c,d,a,b,X[15],16,t[47]) - b=z(h,b,c,d,a,X[ 2],23,t[48]) - - a=z(i,a,b,c,d,X[ 0], 6,t[49]) - d=z(i,d,a,b,c,X[ 7],10,t[50]) - c=z(i,c,d,a,b,X[14],15,t[51]) - b=z(i,b,c,d,a,X[ 5],21,t[52]) - a=z(i,a,b,c,d,X[12], 6,t[53]) - d=z(i,d,a,b,c,X[ 3],10,t[54]) - c=z(i,c,d,a,b,X[10],15,t[55]) - b=z(i,b,c,d,a,X[ 1],21,t[56]) - a=z(i,a,b,c,d,X[ 8], 6,t[57]) - d=z(i,d,a,b,c,X[15],10,t[58]) - c=z(i,c,d,a,b,X[ 6],15,t[59]) - b=z(i,b,c,d,a,X[13],21,t[60]) - a=z(i,a,b,c,d,X[ 4], 6,t[61]) - d=z(i,d,a,b,c,X[11],10,t[62]) - c=z(i,c,d,a,b,X[ 2],15,t[63]) - b=z(i,b,c,d,a,X[ 9],21,t[64]) - - return A+a,B+b,C+c,D+d -end - ----------------------------------------------------------------- - -function md5.sumhexa(s) - local msgLen = #s - local padLen = 56 - msgLen % 64 - - if msgLen % 64 > 56 then padLen = padLen + 64 end - - if padLen == 0 then padLen = 64 end - - s = s .. char(128) .. rep(char(0),padLen-1) .. lei2str(8*msgLen) .. lei2str(0) - - assert(#s % 64 == 0) - - local t = CONSTS - local a,b,c,d = t[65],t[66],t[67],t[68] - - for i=1,#s,64 do - local X = cut_le_str(sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) - assert(#X == 16) - X[0] = table.remove(X,1) -- zero based! - a,b,c,d = transform(a,b,c,d,X) - end - - return format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d)) -end - -function md5.sum(s) - return hex2binary(md5.sumhexa(s)) -end - -return md5 \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema_examples/dota_imba.lua b/scripts/vscripts/statcollection/schema_examples/dota_imba.lua deleted file mode 100644 index 0103ace..0000000 --- a/scripts/vscripts/statcollection/schema_examples/dota_imba.lua +++ /dev/null @@ -1,177 +0,0 @@ -customSchema = class({}) - -function customSchema:init() - - -- Check the schema_examples folder for different implementations - - -- Tracks game version - statCollection:setFlags({version = IMBA_VERSION}) - print("sent version flag") - - -- Listen for changes in the current state - ListenToGameEvent('game_rules_state_change', function(keys) - local state = GameRules:State_Get() - - -- Send custom stats when the game ends - if state == DOTA_GAMERULES_STATE_POST_GAME then - print("got to post game stage") - - -- Build game array - local game = BuildGameArray() - print("built game array") - - -- Build players array - local players = BuildPlayersArray() - print("built players array") - - -- Print the schema data to the console - if statCollection.TESTING then - PrintSchema(game,players) - print("printed schema") - end - - -- Send custom stats - if statCollection.HAS_SCHEMA then - statCollection:sendCustom({game=game, players=players}) - print("sent stats") - end - end - end, nil) -end - -------------------------------------- - -function customSchema:submitRound(args) - winners = BuildRoundWinnerArray() - game = BuildGameArray() - players = BuildPlayersArray() - - statCollection:sendCustom({game=game, players=players}) - - return {winners = winners, lastRound = false} -end - -------------------------------------- - -function BuildRoundWinnerArray() - local winners = {} - return winners -end - --- Returns a table with our custom game tracking. -function BuildGameArray() - - local game = {} - - -- Tracks total game length, from the horn sound, in seconds - game.len = GAME_TIME_ELAPSED - - -- Tracks which team won the game - game.win = GAME_WINNER_TEAM - - return game -end - --- Returns a table containing data for every player in the game -function BuildPlayersArray() - local players = {} - for playerID = 0, DOTA_MAX_PLAYERS do - if PlayerResource:IsValidPlayerID(playerID) then - if not PlayerResource:IsBroadcaster(playerID) then - - local hero = PlayerResource:GetSelectedHeroEntity(playerID) - - -- Team string logic - local player_team = "" - if hero:GetTeam() == DOTA_TEAM_GOODGUYS then - player_team = "Radiant" - else - player_team = "Dire" - end - - table.insert(players, { - - -- SteamID32 required in here - steamID32 = PlayerResource:GetSteamAccountID(playerID), - - nam = GetHeroName(playerID), -- Hero by its short name - lvl = hero:GetLevel(), -- Hero level at the end of the game - pnw = GetNetworth(hero), -- Sum of hero gold and item worth - pbb = hero.buyback_count, -- Amount of buybacks performed during the game - pt = player_team, -- Team this hero belongs to - pk = hero:GetKills(), -- Number of kills of this players hero - pa = hero:GetAssists(), -- Number of deaths of this players hero - pd = hero:GetDeaths(), -- Number of deaths of this players hero - pil = GetItemList(hero) -- Item list - }) - end - end - end - - return players -end - -------------------------------------- --- Stat Functions -- -------------------------------------- - -function PrintSchema( gameArray, playerArray ) - print("--------- GAME DATA ---------") - DeepPrintTable(gameArray) - print("\n-------- PLAYER DATA --------") - DeepPrintTable(playerArray) - print("-----------------------------") -end - -function GetHeroName( playerID ) - local heroName = PlayerResource:GetSelectedHeroName( playerID ) - heroName = string.gsub(heroName,"npc_dota_hero_","") - return heroName -end - -function GetNetworth( hero ) - local gold = hero:GetGold() - - -- Iterate over item slots adding up its gold cost - for i = 0,15 do - local item = hero:GetItemInSlot(i) - if item then - gold = gold + item:GetCost() - end - end - - return gold -end - -function GetItemName(hero, slot) - local item = hero:GetItemInSlot(slot) - if item then - local itemName = item:GetAbilityName() - itemName = string.gsub(itemName,"item_","") --Cuts the item_ prefix - return itemName - else - return "" - end -end - -function GetItemList(hero) - local itemTable = {} - - for i=0,5 do - local item = hero:GetItemInSlot(i) - if item then - if string.find(item:GetAbilityName(), "imba") then - local itemName = string.gsub(item:GetAbilityName(),"item_imba_","") - table.insert(itemTable,itemName) - else - local itemName = string.gsub(item:GetAbilityName(),"item_","") - table.insert(itemTable,itemName) - end - end - end - - table.sort(itemTable) - local itemList = table.concat(itemTable, ",") - - return itemList -end