From 723806faecf5a8d27e4a25a9529e6b02ad724b8a Mon Sep 17 00:00:00 2001 From: o0oradaro0o Date: Mon, 12 Oct 2015 19:18:02 -0700 Subject: [PATCH 01/62] Battleship.lua with testing mode and such --- .../schema_examples/Battleship.lua | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/Battleship.lua diff --git a/scripts/vscripts/statcollection/schema_examples/Battleship.lua b/scripts/vscripts/statcollection/schema_examples/Battleship.lua new file mode 100644 index 0000000..a4d8cf2 --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/Battleship.lua @@ -0,0 +1,187 @@ +customSchema = class({}) + +function customSchema:init() + + -- Check the schema_examples folder for different implementations + + -- Flag Example + statCollection:setFlags({version = storage: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 + +------------------------------------- + +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.eh=storage:GetEmpGoldHist() + game.wn=storage:getWinner() + --game.th=storage:GetTideKillers() + 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="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), + tm=teamname, + -- Example functions of generic stats (keep, delete or change any that you don't need) + shp = storage:GetHeroName(playerID), --Hero by its short name + kls = hero:GetKills(), --Number of kills of this players hero + dth = hero:GetDeaths(), --Number of deaths of this players hero + lvl = hero:GetLevel(), + afk = kickStatus, + -- Item List + bo=storage:GetPlayerHist(playerID), + }) + end + end + end + DeepPrintTable(players) + return players +end + +------------------------------------- +-- Stat Functions -- +------------------------------------- + +function PrintSchema( gameArray, playerArray ) + print("-------- GAME DATA --------") + DeepPrintTable(gameArray) + print("\n-------- PLAYER DATA --------") + DeepPrintTable(playerArray) + print("-------------------------------------") +end + +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) + total_rosh_kills = total_rosh_kills + roshan_kills_player + end + end +end + +function GetHeroName( playerID ) + local heroName = GetSelectedHeroName( playerID ) + heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix + 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 +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 + +--NOTE THAT THIS FUNCTION RELIES ON YOUR npc_items_custom.txt +--having "ID" properly set to unique values (within your mod) +function GetItemList(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:GetAbilityIndex() + if itemID then + table.insert(itemTable,itemID) + end + end + end + + table.sort(itemTable) + itemList = table.concat(itemTable, "_") + + return itemList +end From 124adacdf8235e5835b277d62c159627cc31da1f Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 13 Oct 2015 14:04:23 +1000 Subject: [PATCH 02/62] Set new version checkpoint. Major library update. Set new version checkpoint. --- scripts/vscripts/statcollection/lib/statcollection.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index d62d7c2..4dd324c 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/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 = 1 +local schemaVersion = 2 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' @@ -538,4 +538,4 @@ function tobool(s) else --nil "false" "0" return false end -end \ No newline at end of file +end From ff23c35832c80ff23d7f08897963abab271acc4e Mon Sep 17 00:00:00 2001 From: Martin Noya Date: Tue, 13 Oct 2015 13:17:05 -0200 Subject: [PATCH 03/62] Update README.md --- README.md | 131 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 798e7eb..e05d678 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,118 @@ GetDotaStats Stat-Collection ===== +## Integrating Stat Collection -###About### - - The code and concept is very much a work in progress. Please refer to http://getdotastats.com/#s2__schema_matches for a protocol overview. - - 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. - -###Credits### - - Big thanks to SinZ163, mnoya, Ash47, BMD, and Tet for their contributions. +### Quick Start +There are three stages of integration. For a fast integration, please follow these instructions in the correct order. + +### 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. +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. + +### 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. +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. + +### Stage 3 - Advanced Integration **OPTIONAL** + +* Now that you have basic stats, you are encouraged to create game-specific stats. Having a schema is the best way to acquire relevant stats about your custom game, such as pick and winrates of different heroes, keeping track of special game events, many other things that you might find appropriate to register and track. This information can help you decide what changes or additions to make. +* Keep in mind that all stats that you send need to form a snapshot of the end game results. Time Series data that attempts to match player actions to timings (like an array of item purchase times) do not belong in this library (we plan to release a solution for this soon). Data that you send us must not be too unique either (like an item build order that is slot sensitive). The data must be aggregatable given a large enough sample. The last thing to keep in mind is that values can not be longer than 100characters. We are working towards improving this in the near future. +* Making a custom schema requires that you build your own custom array of stats and write your own Lua functions to put data into them. In the `scripts/vscripts/statcollection/schema_examples` folder we provide examples of how various mods implemented their tracking. +* If your game uses a Round system (where progress is reset between rounds) and you would like to treat each round as a separate match, the library can handle it! You will need to get in contact with us for implementation concerns, but you would need to manually invoke the stat sending function and update your settings.kv to enable rounds. +* Sending custom data is done inside `schema.lua`. The data to send is split into 3 parts: **Flags**, **Game**, and **Players**. + +#### Flags +* The Flags array contains general information about the game, determined before the PRE_GAME phase. +* Flags are recorded by calling the `setFlags()` function any where you can access the library class from. +* The recommended place to set flags is near the top of your schema file in the init() function. +* You can set the same flag multiple times. If the flag is already defined, it will be overwritten. +* You can set a flag at any point of time, up until PRE_GAME. +* A code example of setting a flag: `statCollection:setFlags({version = '4.20'})` +* Some examples of potential values are: + * Mod version (manually incremented by the mod developer) + * Map name (tracked by default) + * Victory condition (e.g. 50kills, 10mins, etc.) + * Lobby options + * Hero selection options + +#### Game +* The Game array should contain general info about the game session, determined after the PRE_GAME phase. +* Refer to the default or example schemas (inside the [schema_examples folder](https://github.com/GetDotaStats/stat-collection/tree/master/scripts/vscripts/statcollection/schema_examples)) for implementation, specifically the lines in the BuildGameArray(). +* Some examples of potential values are: + * The number of Roshan kills + * The number of remaining towers for Team #1 + * Any settings decided after the pre-game phase -###Implementations### +#### Players +* The Players array should contain information specific to each player. +* Refer to the default or example schemas (inside the [schema_examples folder](https://github.com/GetDotaStats/stat-collection/tree/master/scripts/vscripts/statcollection/schema_examples)) for implementation, specifically the lines in the BuildPlayersArray(). +* Some examples of potential values are: + * Hero name (you could use custom names) + * Kills + * Level + * Item list (a comma delimited string of the item held at the end of the game) + * Ability name + * Ability level + * Wood farmed + * Buildings created + * Trees planted + +#### Schema Implementation steps +1. Create your schema after reading the above. +2 Ensure that your `settings.kv` has **"TESTING"** set to true. +3. Clear your console log and play a single match of your custom game. +4. Save the console log to a pastebin, hastebin, or other text hosting service. +5. Create a new issue in [our issue tracker](https://github.com/GetDotaStats/stat-collection/issues), with the following in it: + * Issue Title: [SCHEMA] Mod name + * Issue Body: + * Link to your console log + * Link to your `settings.kv` (censor the modID) + * Link to your `schema.lua` + * Link to your `addon_game_mode.lua` and any other Lua file that defines the functions you pull stats from +6. When an admin has accepted or looked at your schema, they will post back to that issue. There will likely be multiple iterations of your schema, as the admin will likely have suggestions for improvement. +7. When your schema is accepted, go back to your mod list by navigating to `Custom Games -> Mods (My Section)`, or by going straight to the My Mods page. Note your new *schemaID*, and update your `settings.kv` accordingly. + +## Troubleshooting FAQ + +**It's not working!** +* Look in your console log, and do a search for lines starting with "Stat Collection:" +**My Mod Stats (Stage 2) stopped working!** +* Have a look in your console log for an error. +* Check that your modID matches the one in your Mod page. +**My Schema Stats (Stage 3) stopped working!** +* Have a look in your console log for an error. +* Check that your schemaID matches the one in your Mod page. +**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(). + +### 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### +### Recent Games - Reported on the site: http://getdotastats.com/#s2__recent_games - Reported in the IRC channel: https://kiwiirc.com/client/irc.gamesurge.net/?#getdotastats-announce + +### 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 -###Installation of Library### - -Integrating the library into your scripts - -1. Download the statcollection from github and merge the scripts folder into your game/YOUR_ADDON/ folder. -2. In your addon_game_mode.lua file, copy this line at the top: require('statcollection/init') -3. Go into the scripts/vscripts/statcollection folder and inside the `settings.kv` file, change the modID XXXXX value with the modID key from the site (when an admin has approved the mod). You can see the stats of your mod and the key here: http://getdotastats.com/#s2__my__mods -4. Test the game by playing through once. You can use the workshop tools, but don't panic when your playerName becomes ???? on the site (workshop tools don't send steamNames). -5. If the test is successful, you will have sent the default basic stats at the start and conclusion of the match. You can look for this game in the `Recent Games` section of the site. http://getdotastats.com/#s2__recent_games -6. You are encouraged to add your own gamemode-specific stats (such as particular game settings, player values such as end game items). More about this soon. - -###Note### - -We rely on GameRules:SetGameWinner(). Make sure to use this, instead of GameRules:MakeTeamLose() + ### 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. -If you'd like to store flags, for example, the amount of kills to win, it can be done like so: - - statCollection:setFlags({FlagName = 'FlagValue'}) \ No newline at end of file From cf7a18b063133fee17abe5c7101d0a0bfe224a44 Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 14 Oct 2015 00:13:19 -0200 Subject: [PATCH 04/62] Added test_schema and fixed GetItemList to use item string names --- scripts/vscripts/statcollection/schema.lua | 33 ++++++---------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/scripts/vscripts/statcollection/schema.lua b/scripts/vscripts/statcollection/schema.lua index 82fd229..4658533 100644 --- a/scripts/vscripts/statcollection/schema.lua +++ b/scripts/vscripts/statcollection/schema.lua @@ -136,40 +136,23 @@ function GetNetworth( hero ) end 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 - ---NOTE THAT THIS FUNCTION RELIES ON YOUR npc_items_custom.txt ---having "ID" properly set to unique values (within your mod) function GetItemList(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) + local item = hero:GetItemInSlot(i) if item then - itemID = item:GetAbilityIndex() - if itemID then - table.insert(itemTable,itemID) - end + local itemName = string.gsub(item:GetAbilityName(),"item_","") + table.insert(itemTable,itemName) end end table.sort(itemTable) - itemList = table.concat(itemTable, "_") + local itemList = table.concat(itemTable, "_") return itemList +end + +if Convars:GetBool('developer') then + Convars:RegisterCommand("test_schema", function() PrintSchema(BuildGameArray(),BuildPlayersArray()) end, "Test the custom schema arrays", 0) end \ No newline at end of file From 98e840f1562dd588c0c0f72a806914981cb11446 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 18 Oct 2015 13:15:05 -0200 Subject: [PATCH 05/62] Separate the commonly used schema functions into separate file #32 Fixed a couple of faulty functions on the way. Need ideas for more utilities! --- scripts/vscripts/statcollection/init.lua | 1 + .../vscripts/statcollection/lib/utilities.lua | 70 +++++++++++++++++++ scripts/vscripts/statcollection/schema.lua | 63 +++-------------- 3 files changed, 82 insertions(+), 52 deletions(-) create mode 100644 scripts/vscripts/statcollection/lib/utilities.lua diff --git a/scripts/vscripts/statcollection/init.lua b/scripts/vscripts/statcollection/init.lua index 7af9cf4..4ae828a 100644 --- a/scripts/vscripts/statcollection/init.lua +++ b/scripts/vscripts/statcollection/init.lua @@ -1,5 +1,6 @@ require("statcollection/schema") require('statcollection/lib/statcollection') +require('statcollection/lib/utilities') local statInfo = LoadKeyValues('scripts/vscripts/statcollection/settings.kv') local COLLECT_STATS = not Convars:GetBool('developer') diff --git a/scripts/vscripts/statcollection/lib/utilities.lua b/scripts/vscripts/statcollection/lib/utilities.lua new file mode 100644 index 0000000..af3dc32 --- /dev/null +++ b/scripts/vscripts/statcollection/lib/utilities.lua @@ -0,0 +1,70 @@ +STAT_UTILITIES_VERSION = "0.1" + +--[[ + This file contains a general use API for collecting stats, to be used in your schema.lua BuildGameArray or BuildPlayersArray + It will be extended with more functionalities as the library gets more usage and example cases + All the functions should only use methods from the Dota API, and not contain calls to a value specific to a certain game mode (use a different file for this!) +]] + +------------------------------ +-- Game Functions -- +------------------------------ + +-- Number of times roshan was killed +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) + total_rosh_kills = total_rosh_kills + roshan_kills_player + end + end + + return total_rosh_kills +end + +------------------------------ +-- 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 + + return heroName +end + +-- Current gold and item net worth +function GetNetworth( hero ) + local networth = hero:GetGold() + + -- Iterate over item slots adding up its gold cost + for i=0,15 do + local item = hero:GetItemInSlot(i) + if item then + networth = networth + item:GetCost() + end + end + + return networth +end + +-- Long string of item names ordered alphabetically, without the item_ prefix and separated by commas +function GetItemList(hero) + local itemTable = {} + + 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) + end + end + + table.sort(itemTable) + local itemList = table.concat(itemTable, ",") --Concatenates with a comma + + return itemList +end \ No newline at end of file diff --git a/scripts/vscripts/statcollection/schema.lua b/scripts/vscripts/statcollection/schema.lua index 4658533..25fbb98 100644 --- a/scripts/vscripts/statcollection/schema.lua +++ b/scripts/vscripts/statcollection/schema.lua @@ -49,7 +49,7 @@ end function BuildRoundWinnerArray() local winners = {} - local current_winner_team = GameRules.Winner or 0 + 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 @@ -60,6 +60,11 @@ function BuildRoundWinnerArray() 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 = {} @@ -77,10 +82,11 @@ function BuildPlayersArray() local hero = PlayerResource:GetSelectedHeroEntity(playerID) table.insert(players, { - --steamID32 required in here + -- steamID32 required in here steamID32 = PlayerResource:GetSteamAccountID(playerID), - -- Example functions of generic stats (keep, delete or change any that you don't need) + -- 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 pk = hero:GetKills(), --Number of kills of this players hero pd = hero:GetDeaths(), --Number of deaths of this players hero @@ -96,10 +102,7 @@ function BuildPlayersArray() return players end -------------------------------------- --- Stat Functions -- -------------------------------------- - +-- Prints the custom schema, required to get an schemaID function PrintSchema( gameArray, playerArray ) print("-------- GAME DATA --------") DeepPrintTable(gameArray) @@ -108,51 +111,7 @@ function PrintSchema( gameArray, playerArray ) print("-------------------------------------") end -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) - total_rosh_kills = total_rosh_kills + roshan_kills_player - end - end -end - -function GetHeroName( playerID ) - local heroName = GetSelectedHeroName( playerID ) - heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix - 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 -end - -function GetItemList(hero) - local itemTable = {} - - for i=0,5 do - local item = hero:GetItemInSlot(i) - if item then - local itemName = string.gsub(item:GetAbilityName(),"item_","") - table.insert(itemTable,itemName) - end - end - - table.sort(itemTable) - local itemList = table.concat(itemTable, "_") - - return itemList -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 \ No newline at end of file From 6eec1db4e3b433f7ef9c60527b037d4d061af2b8 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 18 Oct 2015 18:35:48 -0200 Subject: [PATCH 06/62] Started clientside stat collection It might be possible to just use the 'address' field on the player_connect event --- .../statcollection/lib/statcollection.lua | 11 ++++ .../lib/statcollection_client.lua | 55 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 scripts/vscripts/statcollection/lib/statcollection_client.lua diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index d62d7c2..f562e77 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -72,11 +72,22 @@ ListenToGameEvent('player_connect', function(keys) firstConnectedSteamID = steamID end, nil) +-- Lua Modifier used for collecting client data +LinkLuaModifier( "modifier_statcollection_network", "statcollection/lib/statcollection_client.lua", LUA_MODIFIER_MOTION_NONE ) + -- Create the stat collection class if not statCollection then statCollection = class({}) end +function statCollection:OnPlayerPickHero(keys) + local hero = EntIndexToHScript(keys.heroindex) + + if hero then + hero:AddNewModifier(hero, nil, "modifier_statcollection_network", {}) + end +end + -- Function that will setup stat collection function statCollection:init() -- Only allow init to be run once diff --git a/scripts/vscripts/statcollection/lib/statcollection_client.lua b/scripts/vscripts/statcollection/lib/statcollection_client.lua new file mode 100644 index 0000000..ae749ed --- /dev/null +++ b/scripts/vscripts/statcollection/lib/statcollection_client.lua @@ -0,0 +1,55 @@ +modifier_statcollection_network = class({}) + +function modifier_statcollection_network:OnCreated(keys) + if not IsServer() then + local playerID = self:GetCaster():GetPlayerOwnerID() + local printPrefix = 'Stat Collection: ' + print(printPrefix .. " Client Network Created for Player "..playerID) + + -- Build the payload + local payload = { + modIdentifier = '0a545b9a80b2c06512e7bf3d8a9bbae6', + steamID32 = tostring('322'), + matchID = '420', + schemaVersion = 2 + } + + -- Create the request + local req = CreateHTTPRequest('POST', 'http://getdotastats.com/s2/api/s2_check_in.php') + print(json.encode(payload)) + + -- Add the data + req:SetHTTPRequestGetOrPostParameter('payload', json.encode(payload)) + + -- Send the request + req:Send(function(res) + if res.StatusCode ~= 200 or not res.Body then + print(printPrefix .. 'Failed to contact the master server! Bad status code, or no body!') + return + end + + print(res.Body) + + -- Try to decode the result + local obj, pos, err = json.decode(res.Body, 1, nil) + + -- Check if we got an error + if err then + print(printPrefix .. 'There was an issue decoding the JSON returned from the server, see below:') + print(printPrefix .. err) + end + + -- Check for an error + if res.error then + print(printPrefix .. 'The server said something went wrong, see below:') + print(res.error) + end + + self:Destroy() + end) + end +end + +function modifier_statcollection_network:IsHidden() + return true +end \ No newline at end of file From c1c1c461b3d6b7136f51eaf23a7ee906fb47c4c2 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 18 Oct 2015 18:42:48 -0200 Subject: [PATCH 07/62] Net Tables Also adjusted the schema on the test branch --- scripts/custom_net_tables.txt | 7 +++++++ scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 scripts/custom_net_tables.txt diff --git a/scripts/custom_net_tables.txt b/scripts/custom_net_tables.txt new file mode 100644 index 0000000..144b921 --- /dev/null +++ b/scripts/custom_net_tables.txt @@ -0,0 +1,7 @@ + +{ + custom_net_tables = + [ + "statcollection", + ] +} \ No newline at end of file diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index f562e77..c286e64 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/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 = 1 +local schemaVersion = 2 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' From 6b09b4944c7c8eb95b3de75d422d5bb82ff50041 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 18 Oct 2015 18:58:46 -0200 Subject: [PATCH 08/62] updated client modifier --- .../lib/statcollection_client.lua | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection_client.lua b/scripts/vscripts/statcollection/lib/statcollection_client.lua index ae749ed..ddcc254 100644 --- a/scripts/vscripts/statcollection/lib/statcollection_client.lua +++ b/scripts/vscripts/statcollection/lib/statcollection_client.lua @@ -6,11 +6,26 @@ function modifier_statcollection_network:OnCreated(keys) local printPrefix = 'Stat Collection: ' print(printPrefix .. " Client Network Created for Player "..playerID) + local modID = CustomNetTables:GetTableValue("statcollection", "modID").modID + local steamID = CustomNetTables:GetTableValue("statcollection", tostring(playerID)).steamID + local matchID = CustomNetTables:GetTableValue("statcollection", "matchID").matchID + + if not modID then + print("Client doesn't know the modID, abort!") + return + elseif not steamID then + print("Client doesn't know the steamID, abort!") + return + elseif not matchID then + print("Client doesn't know the matchID, abort!") + return + end + -- Build the payload local payload = { - modIdentifier = '0a545b9a80b2c06512e7bf3d8a9bbae6', - steamID32 = tostring('322'), - matchID = '420', + modIdentifier = modID, + steamID32 = steamID, + matchID = matchID, schemaVersion = 2 } @@ -45,11 +60,8 @@ function modifier_statcollection_network:OnCreated(keys) print(res.error) end + -- Remove the modifier self:Destroy() end) end -end - -function modifier_statcollection_network:IsHidden() - return true end \ No newline at end of file From 43deed404c315bce99c4c4361922f3dc6871ba7a Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 21 Oct 2015 00:10:33 -0200 Subject: [PATCH 09/62] Emptied base schema, added more comments Moved rounds to the bottom of the default schema and changed the args to a 'isLastRound' parameter that defaults to false in case of customSchema:submitRound() calls Needs testing! --- scripts/vscripts/statcollection/schema.lua | 76 +++++++++++----------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/scripts/vscripts/statcollection/schema.lua b/scripts/vscripts/statcollection/schema.lua index 25fbb98..e99c956 100644 --- a/scripts/vscripts/statcollection/schema.lua +++ b/scripts/vscripts/statcollection/schema.lua @@ -35,40 +35,15 @@ 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 --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 - -------------------------------------- - -- 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.rs = GetRoshanKills() -- This is an example of a function that returns how many times roshan was killed + + -- Add game values here as game.someValue = GetSomeGameValue() + return game end @@ -85,15 +60,9 @@ function BuildPlayersArray() -- 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 - 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 + -- Example functions for generic stats are defined in statcollection/lib/utilities.lua + -- Add player values here as someValue = GetSomePlayerValue(), - -- Item List - il = GetItemList(hero), }) end end @@ -114,4 +83,37 @@ 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 \ No newline at end of file +end + +------------------------------------- + +-- If your gamemode is round-based, you can use customSchema:submitRound() 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 From 08ce678bd22ba8c57f70009d775bdad6422a67cb Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 21 Oct 2015 00:21:08 -0200 Subject: [PATCH 10/62] Corrected bad comment line --- scripts/vscripts/statcollection/schema.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vscripts/statcollection/schema.lua b/scripts/vscripts/statcollection/schema.lua index e99c956..ed5b48f 100644 --- a/scripts/vscripts/statcollection/schema.lua +++ b/scripts/vscripts/statcollection/schema.lua @@ -87,7 +87,7 @@ end ------------------------------------- --- If your gamemode is round-based, you can use customSchema:submitRound() at any point of your main game logic code to send a round +-- 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) From e6bf300f8fad9bc00ada22d52d5c8102e7f07991 Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 21 Oct 2015 23:37:56 -0200 Subject: [PATCH 11/62] Reverted the lua client modifier Address method also failed --- scripts/custom_net_tables.txt | 7 -- .../statcollection/lib/statcollection.lua | 26 +++---- .../lib/statcollection_client.lua | 67 ------------------- 3 files changed, 8 insertions(+), 92 deletions(-) delete mode 100644 scripts/custom_net_tables.txt delete mode 100644 scripts/vscripts/statcollection/lib/statcollection_client.lua diff --git a/scripts/custom_net_tables.txt b/scripts/custom_net_tables.txt deleted file mode 100644 index 144b921..0000000 --- a/scripts/custom_net_tables.txt +++ /dev/null @@ -1,7 +0,0 @@ - -{ - custom_net_tables = - [ - "statcollection", - ] -} \ No newline at end of file diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index c286e64..202da69 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -72,22 +72,11 @@ ListenToGameEvent('player_connect', function(keys) firstConnectedSteamID = steamID end, nil) --- Lua Modifier used for collecting client data -LinkLuaModifier( "modifier_statcollection_network", "statcollection/lib/statcollection_client.lua", LUA_MODIFIER_MOTION_NONE ) - -- Create the stat collection class if not statCollection then statCollection = class({}) end -function statCollection:OnPlayerPickHero(keys) - local hero = EntIndexToHScript(keys.heroindex) - - if hero then - hero:AddNewModifier(hero, nil, "modifier_statcollection_network", {}) - end -end - -- Function that will setup stat collection function statCollection:init() -- Only allow init to be run once @@ -338,12 +327,14 @@ function statCollection:sendStage2() -- Build players array local players = {} - for i = 1, (PlayerResource:GetPlayerCount() or 1) do - table.insert(players, { - playerName = PlayerResource:GetPlayerName(i - 1), - steamID32 = PlayerResource:GetSteamAccountID(i - 1), - connectionState = PlayerResource:GetConnectionState(i - 1) - }) + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + table.insert(players, { + playerName = PlayerResource:GetPlayerName(playerID), + steamID32 = PlayerResource:GetSteamAccountID(playerID), + connectionState = PlayerResource:GetConnectionState(playerID) + }) + end end local payload = { @@ -482,7 +473,6 @@ function statCollection:sendCustom(args) -- Print the intro message print(printPrefix .. messageCustomStarting) - -- Build rounds table -- Build rounds table rounds = {} rounds[tostring(self.roundID)] = { diff --git a/scripts/vscripts/statcollection/lib/statcollection_client.lua b/scripts/vscripts/statcollection/lib/statcollection_client.lua deleted file mode 100644 index ddcc254..0000000 --- a/scripts/vscripts/statcollection/lib/statcollection_client.lua +++ /dev/null @@ -1,67 +0,0 @@ -modifier_statcollection_network = class({}) - -function modifier_statcollection_network:OnCreated(keys) - if not IsServer() then - local playerID = self:GetCaster():GetPlayerOwnerID() - local printPrefix = 'Stat Collection: ' - print(printPrefix .. " Client Network Created for Player "..playerID) - - local modID = CustomNetTables:GetTableValue("statcollection", "modID").modID - local steamID = CustomNetTables:GetTableValue("statcollection", tostring(playerID)).steamID - local matchID = CustomNetTables:GetTableValue("statcollection", "matchID").matchID - - if not modID then - print("Client doesn't know the modID, abort!") - return - elseif not steamID then - print("Client doesn't know the steamID, abort!") - return - elseif not matchID then - print("Client doesn't know the matchID, abort!") - return - end - - -- Build the payload - local payload = { - modIdentifier = modID, - steamID32 = steamID, - matchID = matchID, - schemaVersion = 2 - } - - -- Create the request - local req = CreateHTTPRequest('POST', 'http://getdotastats.com/s2/api/s2_check_in.php') - print(json.encode(payload)) - - -- Add the data - req:SetHTTPRequestGetOrPostParameter('payload', json.encode(payload)) - - -- Send the request - req:Send(function(res) - if res.StatusCode ~= 200 or not res.Body then - print(printPrefix .. 'Failed to contact the master server! Bad status code, or no body!') - return - end - - print(res.Body) - - -- Try to decode the result - local obj, pos, err = json.decode(res.Body, 1, nil) - - -- Check if we got an error - if err then - print(printPrefix .. 'There was an issue decoding the JSON returned from the server, see below:') - print(printPrefix .. err) - end - - -- Check for an error - if res.error then - print(printPrefix .. 'The server said something went wrong, see below:') - print(res.error) - end - - -- Remove the modifier - self:Destroy() - end) - end -end \ No newline at end of file From 2d6ab930951365c66d31f1eccc3cabe010d9c04a Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 00:49:59 +1000 Subject: [PATCH 12/62] Create dota_imba.lua --- .../schema_examples/dota_imba.lua | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/dota_imba.lua diff --git a/scripts/vscripts/statcollection/schema_examples/dota_imba.lua b/scripts/vscripts/statcollection/schema_examples/dota_imba.lua new file mode 100644 index 0000000..0103ace --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/dota_imba.lua @@ -0,0 +1,177 @@ +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 From 0624a485a3c5930edf2ba301b4a5a9eeeb2c6d15 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 00:50:42 +1000 Subject: [PATCH 13/62] Create slideninjaslide --- .../schema_examples/slideninjaslide | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/slideninjaslide diff --git a/scripts/vscripts/statcollection/schema_examples/slideninjaslide b/scripts/vscripts/statcollection/schema_examples/slideninjaslide new file mode 100644 index 0000000..d47d88b --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/slideninjaslide @@ -0,0 +1,221 @@ +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 + +------------------------------------- + +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.rnd = GameMode.nCurrentRound -- Current Round + game.ch = GameMode.livesUsed -- Chances Used + game.dth = GameMode.nDeaths -- Total ninja deaths + game.the = GetTheme() -- Game Theme (can be 0,1 or "yes", "no") + 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) + + table.insert(players, { + --steamID32 required in here + steamID32 = PlayerResource:GetSteamAccountID(playerID), + + -- 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 + nt = GetNetworth(hero), --Sum of hero gold and item worth + + -- Item List + il = GetItemList(hero), + + -- 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 + + }) + 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_","") --Cuts the npc_dota_hero_ prefix + 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() + print(itemName) + return itemName + else + return "" + end +end + +function GetItemList(hero) + local itemTable = {} + for i=0,5 do + local item = hero:GetItemInSlot(i) + if item then + local itemName = string.gsub(item:GetAbilityName(), "item_", "") + table.insert(itemTable, itemName) + end + end + table.sort(itemTable) + local itemList = table.concat(itemTable, ",") + return itemList +end + +function GetAbilityName( hero, id ) + local ability = hero:GetAbilityByIndex(id) + if ability then + local abilityName = string.gsub(ability:GetAbilityName(), "antimage_", "") --remove unnecessary parts of string + abilityName = string.gsub(ability:GetAbilityName(), "spongebob_", "sb_") --remove unnecessary parts of string + return abilityName + end + return "" +end + +function GetAbilityNameList( hero ) + local nameTable = {} + for i=0,3 do + table.insert(nameTable, GetAbilityName(hero, i)) + end + return table.concat(nameTable, ",") +end + +function GetAbilityLevel( hero, id ) + ability = hero:GetAbilityByIndex(id) + if ability then + return ability:GetLevel() + end + return 0 +end + +function GetAbilityLevelList( hero ) + local nameTable = {} + for i=0,3 do + table.insert(nameTable, GetAbilityLevel(hero, i)) + end + return table.concat(nameTable, ",") +end + +function GetAbilityNameLevel( hero, id ) + return GetAbilityName(hero, id) .. "__" .. GetAbilityLevel(hero, id) +end + +function GetTheme( ) + local theme = GameMode.gameTheme + if theme == 1 then + return "Normal" + elseif theme == 2 then + return "SpongeBob" + end + return "Unknown" +end From 139bbd0e1769453137c32fd267acaee8559f18aa Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 00:51:14 +1000 Subject: [PATCH 14/62] Forgot the file extension --- .../schema_examples/{slideninjaslide => slideninjaslide.lua} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/vscripts/statcollection/schema_examples/{slideninjaslide => slideninjaslide.lua} (100%) diff --git a/scripts/vscripts/statcollection/schema_examples/slideninjaslide b/scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua similarity index 100% rename from scripts/vscripts/statcollection/schema_examples/slideninjaslide rename to scripts/vscripts/statcollection/schema_examples/slideninjaslide.lua From aeefbb93878aa438c5749a7df9a518b011aeccdc Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 25 Oct 2015 13:46:04 -0200 Subject: [PATCH 15/62] Player count flag 'numPlayers' #37 --- scripts/vscripts/statcollection/lib/statcollection.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 8bb39fe..8efc0e9 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -258,6 +258,7 @@ function statCollection:sendStage1() -- Workout the player count local playerCount = PlayerResource:GetPlayerCount() if playerCount <= 0 then playerCount = 1 end + statCollection:setFlags({numPlayers = playerCount}) -- Workout who is hosting local hostSteamID = PlayerResource:GetSteamAccountID(0) From 38cc20b599be8203ccdf41bc625c5dfceaed05ca Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 25 Oct 2015 14:02:45 -0200 Subject: [PATCH 16/62] Fixed reporting wrong hostSteamID32 #25 --- .../statcollection/lib/statcollection.lua | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 8efc0e9..1a716c1 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -59,19 +59,6 @@ local messagePhase3Complete = 'Match stats were successfully recorded!' local messageCustomComplete = 'Match custom stats were successfully recorded!' local messageFlagsSet = 'Flag was successfully set!' --- Store the first detected steamID -local firstConnectedSteamID = -1 -ListenToGameEvent('player_connect', function(keys) --- Grab their steamID - local steamID64 = tostring(keys.xuid) - local steamIDPart = tonumber(steamID64:sub(4)) - if not steamIDPart then return end - local steamID = tostring(steamIDPart - 61197960265728) - - -- Store it - firstConnectedSteamID = steamID -end, nil) - -- Create the stat collection class if not statCollection then statCollection = class({}) @@ -261,14 +248,17 @@ function statCollection:sendStage1() statCollection:setFlags({numPlayers = playerCount}) -- Workout who is hosting - local hostSteamID = PlayerResource:GetSteamAccountID(0) - if hostSteamID == 0 then - if firstConnectedSteamID ~= -1 then - hostSteamID = firstConnectedSteamID - else - hostSteamID = -1 + local hostID + for playerID = 0, DOTA_MAX_PLAYERS do + if PlayerResource:IsValidPlayerID(playerID) then + local player = PlayerResource:GetPlayer(playerID) + if GameRules:PlayerHasCustomGameHostPrivileges(player) then + hostID = playerID + break + end end end + local hostSteamID = PlayerResource:GetSteamAccountID(hostID) -- Workout if the server is dedicated or not local isDedicated = (IsDedicatedServer() and 1) or 0 From 07f4de29e520b27eefd0eebf36e771ed03f64a2c Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 25 Oct 2015 17:19:15 -0200 Subject: [PATCH 17/62] Started the panorama check-in, erroring out on the payload Debug: Sending: {"modIdentifier":"0a545b9a80b2c06512e7bf3d8a9bbae6","steamID32":"86718505","matchID":1297354,"schemaVersion":2} GDS Reply: {"result":0,"error":"Caught Exception: Missing payload!","schemaVersion":1} --- panorama/manifest.xml | 3 ++ panorama/statcollection.js | 40 +++++++++++++++++++ .../statcollection/lib/statcollection.lua | 5 ++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 panorama/manifest.xml create mode 100644 panorama/statcollection.js diff --git a/panorama/manifest.xml b/panorama/manifest.xml new file mode 100644 index 0000000..4977c1a --- /dev/null +++ b/panorama/manifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/panorama/statcollection.js b/panorama/statcollection.js new file mode 100644 index 0000000..ff97fbf --- /dev/null +++ b/panorama/statcollection.js @@ -0,0 +1,40 @@ +"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, + 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/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 1a716c1..fc84c4e 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -316,6 +316,9 @@ function statCollection:sendStage2() -- Print the intro message print(printPrefix .. messagePhase2Starting) + -- Client check in + 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 @@ -530,4 +533,4 @@ function tobool(s) else --nil "false" "0" return false end -end +end \ No newline at end of file From d04d01a05fccf91bfa5f685667bf76343929bbb9 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 17:22:00 +1000 Subject: [PATCH 18/62] Add global_skillshots schema --- .../schema_examples/global_skillshots.lua | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/global_skillshots.lua diff --git a/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua b/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua new file mode 100644 index 0000000..f80de39 --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua @@ -0,0 +1,135 @@ +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 = {} + game.len = GameRules:GetGameTime() + game.win = _G.GAME_WINNER_TEAM + -- Add game values here as game.someValue = GetSomeGameValue() + + 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), + + -- 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 + }) + 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 + +------------------------------------- From e6c2bebaf73a07df2e100ab0814bf6d45921caa0 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 17:32:48 +1000 Subject: [PATCH 19/62] update bships schema --- .../schema_examples/Battleship.lua | 187 ----------------- .../schema_examples/battleship.lua | 194 ++++++++++++++++++ 2 files changed, 194 insertions(+), 187 deletions(-) delete mode 100644 scripts/vscripts/statcollection/schema_examples/Battleship.lua create mode 100644 scripts/vscripts/statcollection/schema_examples/battleship.lua diff --git a/scripts/vscripts/statcollection/schema_examples/Battleship.lua b/scripts/vscripts/statcollection/schema_examples/Battleship.lua deleted file mode 100644 index a4d8cf2..0000000 --- a/scripts/vscripts/statcollection/schema_examples/Battleship.lua +++ /dev/null @@ -1,187 +0,0 @@ -customSchema = class({}) - -function customSchema:init() - - -- Check the schema_examples folder for different implementations - - -- Flag Example - statCollection:setFlags({version = storage: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 - -------------------------------------- - -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.eh=storage:GetEmpGoldHist() - game.wn=storage:getWinner() - --game.th=storage:GetTideKillers() - 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="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), - tm=teamname, - -- Example functions of generic stats (keep, delete or change any that you don't need) - shp = storage:GetHeroName(playerID), --Hero by its short name - kls = hero:GetKills(), --Number of kills of this players hero - dth = hero:GetDeaths(), --Number of deaths of this players hero - lvl = hero:GetLevel(), - afk = kickStatus, - -- Item List - bo=storage:GetPlayerHist(playerID), - }) - end - end - end - DeepPrintTable(players) - return players -end - -------------------------------------- --- Stat Functions -- -------------------------------------- - -function PrintSchema( gameArray, playerArray ) - print("-------- GAME DATA --------") - DeepPrintTable(gameArray) - print("\n-------- PLAYER DATA --------") - DeepPrintTable(playerArray) - print("-------------------------------------") -end - -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) - total_rosh_kills = total_rosh_kills + roshan_kills_player - end - end -end - -function GetHeroName( playerID ) - local heroName = GetSelectedHeroName( playerID ) - heroName = string.gsub(heroName,"npc_dota_hero_","") --Cuts the npc_dota_hero_ prefix - 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 -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 - ---NOTE THAT THIS FUNCTION RELIES ON YOUR npc_items_custom.txt ---having "ID" properly set to unique values (within your mod) -function GetItemList(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:GetAbilityIndex() - if itemID then - table.insert(itemTable,itemID) - end - end - end - - table.sort(itemTable) - itemList = table.concat(itemTable, "_") - - return itemList -end diff --git a/scripts/vscripts/statcollection/schema_examples/battleship.lua b/scripts/vscripts/statcollection/schema_examples/battleship.lua new file mode 100644 index 0000000..396da2e --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/battleship.lua @@ -0,0 +1,194 @@ +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() + game.eh=storage:GetEmpGoldHist() + game.wn=storage:getWinner() + --game.th=storage:GetTideKillers() + + 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="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 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), + }) + 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 -- +------------------------------------- + +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 From d95e924016512c352380153e42658b0b842bd58a Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 17:34:33 +1000 Subject: [PATCH 20/62] hivemind schema --- .../schema_examples/hivemind.lua | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/hivemind.lua diff --git a/scripts/vscripts/statcollection/schema_examples/hivemind.lua b/scripts/vscripts/statcollection/schema_examples/hivemind.lua new file mode 100644 index 0000000..653f7d3 --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/hivemind.lua @@ -0,0 +1,120 @@ +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 + +------------------------------------- + +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 + 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) + + 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 + }) + 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 From f2fe9c8284743e4e39dd28963d830cde2d856ef7 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Mon, 26 Oct 2015 17:35:14 +1000 Subject: [PATCH 21/62] enfos schema --- .../statcollection/schema_examples/enfos.lua | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 scripts/vscripts/statcollection/schema_examples/enfos.lua diff --git a/scripts/vscripts/statcollection/schema_examples/enfos.lua b/scripts/vscripts/statcollection/schema_examples/enfos.lua new file mode 100644 index 0000000..7a3ac01 --- /dev/null +++ b/scripts/vscripts/statcollection/schema_examples/enfos.lua @@ -0,0 +1,131 @@ +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() + game.r = Enfos.curRound + + 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) + + 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(), + 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 + }) + 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 + +------------------------------------- From 7e5155becaaaa4c6ecf13c18195986dd36452421 Mon Sep 17 00:00:00 2001 From: MNoya Date: Mon, 2 Nov 2015 00:22:29 -0300 Subject: [PATCH 22/62] Faster init to track the load time --- scripts/vscripts/statcollection/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/vscripts/statcollection/init.lua b/scripts/vscripts/statcollection/init.lua index 4ae828a..87f9e2d 100644 --- a/scripts/vscripts/statcollection/init.lua +++ b/scripts/vscripts/statcollection/init.lua @@ -10,8 +10,8 @@ local MIN_PLAYERS = tonumber(statInfo.MIN_PLAYERS) if COLLECT_STATS or TESTING then ListenToGameEvent('game_rules_state_change', function(keys) local state = GameRules:State_Get() - - if state == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then + + if state >= DOTA_GAMERULES_STATE_INIT and not statCollection.doneInit then if PlayerResource:GetPlayerCount() >= MIN_PLAYERS or TESTING then From 35d42f6b7582a4c78c164e07d7fe545da5c55ad0 Mon Sep 17 00:00:00 2001 From: MNoya Date: Mon, 2 Nov 2015 00:22:58 -0300 Subject: [PATCH 23/62] v3, removed numPlayers from stage1 payload --- scripts/vscripts/statcollection/lib/statcollection.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index fc84c4e..8fc3753 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/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 = 2 +local schemaVersion = 3 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' @@ -272,7 +272,6 @@ function statCollection:sendStage1() local payload = { modIdentifier = self.modIdentifier, hostSteamID32 = tostring(hostSteamID), - numPlayers = playerCount, schemaVersion = schemaVersion } From 7b4fe0b0d96d1a9998dff339e7c9cdb10ecbe9a3 Mon Sep 17 00:00:00 2001 From: MNoya Date: Mon, 2 Nov 2015 00:52:16 -0300 Subject: [PATCH 24/62] Set loadTime flag when CUSTOM_GAME_SETUP hits --- scripts/vscripts/statcollection/lib/statcollection.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 8fc3753..ee28117 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -163,7 +163,11 @@ function statCollection:hookFunctions() -- Grab the current state local state = GameRules:State_Get() - if state >= DOTA_GAMERULES_STATE_PRE_GAME then + if state == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then + -- Load time flag + statCollection:setFlags({loadTime = math.floor(GameRules:GetGameTime())}) + + elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then -- Send pregame stats this:sendStage2() end From a6a078edd8cfd117440dc8e97de22c6b0a90bea8 Mon Sep 17 00:00:00 2001 From: MNoya Date: Mon, 2 Nov 2015 00:58:49 -0300 Subject: [PATCH 25/62] Removed print on sendStage This broadcasts the super sekret keys --- scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index ee28117..437813c 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -511,7 +511,7 @@ end function statCollection:sendStage(stageName, payload, callback) -- Create the request local req = CreateHTTPRequest('POST', postLocation .. stageName) - print(json.encode(payload)) + --print(json.encode(payload)) -- Add the data req:SetHTTPRequestGetOrPostParameter('payload', json.encode(payload)) From 434939942b24c974faf96bd1acbdb2cb51944acb Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 4 Nov 2015 01:16:32 -0300 Subject: [PATCH 26/62] Change panorama folder structure Removed .xml, must use lua Dynamic_HUD --- panorama/manifest.xml | 3 --- panorama/{ => scripts}/statcollection.js | 0 2 files changed, 3 deletions(-) delete mode 100644 panorama/manifest.xml rename panorama/{ => scripts}/statcollection.js (100%) diff --git a/panorama/manifest.xml b/panorama/manifest.xml deleted file mode 100644 index 4977c1a..0000000 --- a/panorama/manifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/panorama/statcollection.js b/panorama/scripts/statcollection.js similarity index 100% rename from panorama/statcollection.js rename to panorama/scripts/statcollection.js From 70a6b7c5ca8e5c9ce64b4f0e5ded3e78b27bd094 Mon Sep 17 00:00:00 2001 From: MNoya Date: Wed, 4 Nov 2015 13:35:19 -0300 Subject: [PATCH 27/62] v3 ready Resolved #52 --- panorama/layout/custom_game/statcollection.xml | 6 ++++++ panorama/scripts/{ => custom_game}/statcollection.js | 12 ++++++------ .../vscripts/statcollection/lib/statcollection.lua | 3 +++ 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 panorama/layout/custom_game/statcollection.xml rename panorama/scripts/{ => custom_game}/statcollection.js (84%) diff --git a/panorama/layout/custom_game/statcollection.xml b/panorama/layout/custom_game/statcollection.xml new file mode 100644 index 0000000..47495d1 --- /dev/null +++ b/panorama/layout/custom_game/statcollection.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/panorama/scripts/statcollection.js b/panorama/scripts/custom_game/statcollection.js similarity index 84% rename from panorama/scripts/statcollection.js rename to panorama/scripts/custom_game/statcollection.js index ff97fbf..358bcc5 100644 --- a/panorama/scripts/statcollection.js +++ b/panorama/scripts/custom_game/statcollection.js @@ -2,7 +2,7 @@ function OnClientCheckIn (args) { - var payload = { + var payload = { modIdentifier: args.modID, steamID32: GetSteamID32(), matchID: args.matchID, @@ -13,12 +13,12 @@ function OnClientCheckIn (args) { $.AsyncWebRequest( 'http://getdotastats.com/s2/api/s2_check_in.php', { - type: 'POST', - data: payload, - success: function( data ) - { + type: 'POST', + data: {payload: JSON.stringify(payload)}, + success: function( data ) + { $.Msg('GDS Reply: ', data) - } + } }); } diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 437813c..de1a788 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -167,6 +167,9 @@ function statCollection:hookFunctions() -- Load time flag 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) + elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then -- Send pregame stats this:sendStage2() From d25ea5ae690b4ab420f645a4bbed1667e3f19164 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Thu, 12 Nov 2015 10:42:48 +1000 Subject: [PATCH 28/62] Add GetItemSlot() --- scripts/vscripts/statcollection/lib/utilities.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/utilities.lua b/scripts/vscripts/statcollection/lib/utilities.lua index af3dc32..2b8f26b 100644 --- a/scripts/vscripts/statcollection/lib/utilities.lua +++ b/scripts/vscripts/statcollection/lib/utilities.lua @@ -1,4 +1,4 @@ -STAT_UTILITIES_VERSION = "0.1" +STAT_UTILITIES_VERSION = "0.2" --[[ This file contains a general use API for collecting stats, to be used in your schema.lua BuildGameArray or BuildPlayersArray @@ -51,6 +51,17 @@ function GetNetworth( hero ) return networth end +-- String of item name, without the item_ prefix +function GetItemSlot(hero,slot) + local item = hero:GetItemInSlot(slot) + + if item then + local itemName = string.gsub(item:GetAbilityName(),"item_","") or nil + end + + return itemName +end + -- Long string of item names ordered alphabetically, without the item_ prefix and separated by commas function GetItemList(hero) local itemTable = {} @@ -67,4 +78,4 @@ function GetItemList(hero) local itemList = table.concat(itemTable, ",") --Concatenates with a comma return itemList -end \ No newline at end of file +end From 5fd64ae96df6ee57833e6b83401ed7ad9a4b4291 Mon Sep 17 00:00:00 2001 From: litianren001 <406161668@qq.com> Date: Thu, 12 Nov 2015 09:15:21 -0600 Subject: [PATCH 29/62] GetItemSlot() bug fixed --- scripts/vscripts/statcollection/lib/utilities.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/vscripts/statcollection/lib/utilities.lua b/scripts/vscripts/statcollection/lib/utilities.lua index 2b8f26b..2b3ce2d 100644 --- a/scripts/vscripts/statcollection/lib/utilities.lua +++ b/scripts/vscripts/statcollection/lib/utilities.lua @@ -54,9 +54,10 @@ end -- String of item name, without the item_ prefix function GetItemSlot(hero,slot) local item = hero:GetItemInSlot(slot) + local itemName = "" if item then - local itemName = string.gsub(item:GetAbilityName(),"item_","") or nil + itemName = string.gsub(item:GetAbilityName(),"item_","") end return itemName From 7e485c0ba7cb307d1c28d1fe3a104371fc8e5092 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 16:35:48 +1000 Subject: [PATCH 30/62] Send Phase1 if host connected --- .../statcollection/lib/statcollection.lua | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index de1a788..a2355b4 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -119,9 +119,6 @@ function statCollection:init() -- Hook requred functions to operate correctly self:hookFunctions() - - -- Send stage1 stuff - self:sendStage1() end --Build the winners array @@ -157,6 +154,27 @@ function statCollection:hookFunctions() this:sendStage3(this:calcWinnersByTeam(), true) end end + + --Wait for host before sending Phase 1 + ListenToGameEvent('player_connect', 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_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) @@ -539,4 +557,4 @@ function tobool(s) else --nil "false" "0" return false end -end \ No newline at end of file +end From 7176aef6da32773d4c9159dd6e3d62dddabd6b79 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 16:51:47 +1000 Subject: [PATCH 31/62] Standardise building of phase3 players array --- .../statcollection/lib/statcollection.lua | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index a2355b4..3180c6b 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -404,17 +404,19 @@ function statCollection:sendStage3(winners, lastRound) -- Print the intro message print(printPrefix .. messagePhase3Starting) - + -- 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_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 From 2bc8f6b8a59b10b3445125fd72a4f915ae1e6606 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 17:22:22 +1000 Subject: [PATCH 32/62] Listen to `player_connect_full` instead of `player_connect` --- scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vscripts/statcollection/lib/statcollection.lua b/scripts/vscripts/statcollection/lib/statcollection.lua index 3180c6b..64b0089 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/scripts/vscripts/statcollection/lib/statcollection.lua @@ -156,7 +156,7 @@ function statCollection:hookFunctions() end --Wait for host before sending Phase 1 - ListenToGameEvent('player_connect', function(keys) + 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 From d57b5d24da374be144e640f3549a29cc4fbd3411 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 20:27:07 +1000 Subject: [PATCH 33/62] Update readme --- README.md | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) 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. From 8317d5bcc0917e911c8011c9dfcd88cbc7132620 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 20:49:19 +1000 Subject: [PATCH 34/62] Update example schemas --- .../schema_examples/battle_of_mirkwood.lua | 163 +++++++++++ .../schema_examples/battleship.lua | 110 ++++--- .../schema_examples/dota_imba.lua | 180 ++++++++++++ .../statcollection/schema_examples/enfos.lua | 41 ++- .../schema_examples/epic_boss_fight.lua | 114 ++------ .../schema_examples/example.lua | 31 +- .../schema_examples/global_skillshots.lua | 142 +++++++++ .../schema_examples/hivemind.lua | 48 ++-- .../statcollection/schema_examples/pmp.lua | 13 +- .../schema_examples/slideninjaslide.lua | 60 ++-- .../statcollection/schema_examples/thd2.lua | 272 ++++++++++++++++++ 11 files changed, 940 insertions(+), 234 deletions(-) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battle_of_mirkwood.lua rename scripts/vscripts/statcollection/schema_examples/hivemind.lua => game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/battleship.lua (51%) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/dota_imba.lua rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/schema_examples/enfos.lua (74%) rename scripts/vscripts/statcollection/schema_examples/battleship.lua => game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/epic_boss_fight.lua (60%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/schema_examples/example.lua (84%) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/global_skillshots.lua rename scripts/vscripts/statcollection/schema_examples/global_skillshots.lua => game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/hivemind.lua (74%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/schema_examples/pmp.lua (93%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/schema_examples/slideninjaslide.lua (83%) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema_examples/thd2.lua 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 From 6d211ec11525a26d32c8be447b4041d44565921f Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 20:52:41 +1000 Subject: [PATCH 35/62] Update JS. It's using strict, yet is missing almost all of the line terminators --- .../scripts/custom_game/statcollection.js | 39 ++++++++++++++++++ .../scripts/custom_game/statcollection.js | 40 ------------------- 2 files changed, 39 insertions(+), 40 deletions(-) create mode 100644 content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js delete mode 100644 panorama/scripts/custom_game/statcollection.js 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/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 From 2a1cb6585df2c5d210f60a55875c27b3fbdacec0 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 20:53:58 +1000 Subject: [PATCH 36/62] Formatting --- .../layout/custom_game/statcollection.xml | 0 .../scripts}/vscripts/statcollection/init.lua | 0 .../vscripts/statcollection/lib/md5.lua | 382 +++++++++++++++++ .../statcollection/lib/statcollection.lua | 98 ++--- .../vscripts/statcollection/lib/utilities.lua | 32 +- .../vscripts/statcollection/schema.lua | 13 +- .../vscripts/statcollection/settings.kv | 0 scripts/vscripts/statcollection/lib/md5.lua | 384 ------------------ .../schema_examples/dota_imba.lua | 177 -------- 9 files changed, 453 insertions(+), 633 deletions(-) rename {panorama => content/dota_addons/YOUR_ADDON/panorama}/layout/custom_game/statcollection.xml (100%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/init.lua (100%) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/md5.lua rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/lib/statcollection.lua (90%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/lib/utilities.lua (74%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/schema.lua (91%) rename {scripts => game/dota_addons/YOUR_ADDON/scripts}/vscripts/statcollection/settings.kv (100%) delete mode 100644 scripts/vscripts/statcollection/lib/md5.lua delete mode 100644 scripts/vscripts/statcollection/schema_examples/dota_imba.lua 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/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 90% rename from scripts/vscripts/statcollection/lib/statcollection.lua rename to game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 64b0089..1e66d9a 100644 --- a/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -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,10 +113,10 @@ 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() end @@ -154,39 +154,39 @@ function statCollection:hookFunctions() this:sendStage3(this:calcWinnersByTeam(), true) 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_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 + + -- 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_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 @@ -199,7 +199,6 @@ function statCollection:hookFunctions() this:sendStage3(this:calcWinnersByTeam(), true) end end - end, nil) end @@ -238,11 +237,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) @@ -270,14 +269,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 if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) - if GameRules:PlayerHasCustomGameHostPrivileges(player) then + if GameRules:PlayerHasCustomGameHostPrivileges(player) then hostID = playerID break end @@ -287,11 +286,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 = { @@ -302,7 +301,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) @@ -341,7 +340,7 @@ 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 = {} @@ -366,7 +365,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) @@ -404,14 +403,14 @@ function statCollection:sendStage3(winners, lastRound) -- Print the intro message print(printPrefix .. messagePhase3Starting) - + -- Build players array local players = {} for playerID = 0, DOTA_MAX_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then - local steamID = PlayerResource:GetSteamAccountID(playerID) - - table.insert(players, { + local steamID = PlayerResource:GetSteamAccountID(playerID) + + table.insert(players, { steamID32 = steamID, connectionState = PlayerResource:GetConnectionState(playerID), isWinner = winners[steamID] @@ -422,7 +421,7 @@ function statCollection:sendStage3(winners, lastRound) -- Build rounds table rounds = {} rounds[tostring(self.roundID)] = { - players=players + players = players } local payload = { authKey = self.authKey, @@ -438,7 +437,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) @@ -456,10 +455,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) + self:sendStage3(returnArgs.winners, returnArgs.lastRound) end -- Sends custom @@ -485,11 +485,11 @@ 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) @@ -497,7 +497,7 @@ function statCollection:sendCustom(args) rounds = {} rounds[tostring(self.roundID)] = { game = game, - players=players + players = players } local payload = { @@ -511,7 +511,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) @@ -554,9 +554,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 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/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/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 From 87153d05927420ef1a87a9a7f72b653b59dc6ef8 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 17 Nov 2015 20:54:55 +1000 Subject: [PATCH 37/62] Formatting --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 1e66d9a..5dbb341 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -157,7 +157,6 @@ function statCollection:hookFunctions() --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 From f09aba787b62ce3d60172d5976ac21ac3366daa7 Mon Sep 17 00:00:00 2001 From: Martin Noya Date: Tue, 17 Nov 2015 11:44:27 -0300 Subject: [PATCH 38/62] safeguard for hostID in dedis --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 5dbb341..be3d2e4 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -271,7 +271,7 @@ function statCollection:sendStage1() statCollection:setFlags({ numPlayers = playerCount }) -- Workout who is hosting - local hostID + local hostID = 0 for playerID = 0, DOTA_MAX_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) From 4dce918cfd98296c780fe878af4a763b3941ef5d Mon Sep 17 00:00:00 2001 From: Martin Noya Date: Tue, 17 Nov 2015 11:50:48 -0300 Subject: [PATCH 39/62] Changed MAX_PLAYERS for MAX_TEAM_PLAYERS Fixed globals --- .../statcollection/lib/statcollection.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index be3d2e4..fe49e69 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -123,10 +123,10 @@ 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 @@ -161,7 +161,7 @@ function statCollection:hookFunctions() if self.playerCheckStage1 then return end -- Check each connected player to see if they are host - for playerID = 0, DOTA_MAX_PLAYERS do + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) @@ -272,7 +272,7 @@ function statCollection:sendStage1() -- Workout who is hosting local hostID = 0 - for playerID = 0, DOTA_MAX_PLAYERS do + for playerID = 0, DOTA_MAX_TEAM_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) if GameRules:PlayerHasCustomGameHostPrivileges(player) then @@ -343,7 +343,7 @@ function statCollection:sendStage2() -- 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), @@ -405,7 +405,7 @@ function statCollection:sendStage3(winners, lastRound) -- 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 local steamID = PlayerResource:GetSteamAccountID(playerID) @@ -418,7 +418,7 @@ function statCollection:sendStage3(winners, lastRound) end -- Build rounds table - rounds = {} + local DOrounds = {} rounds[tostring(self.roundID)] = { players = players } @@ -457,7 +457,7 @@ end function statCollection:submitRound(args) --We receive the winners from the custom schema, lets tell phase 3 about it! - returnArgs = customSchema:submitRound(args) + local returnArgs = customSchema:submitRound(args) self:sendStage3(returnArgs.winners, returnArgs.lastRound) end @@ -493,7 +493,7 @@ function statCollection:sendCustom(args) print(printPrefix .. messageCustomStarting) -- Build rounds table - rounds = {} + local rounds = {} rounds[tostring(self.roundID)] = { game = game, players = players From 2f8923f42dd5d6934e5dc65765236810a7406949 Mon Sep 17 00:00:00 2001 From: Martin Noya Date: Tue, 17 Nov 2015 11:55:20 -0300 Subject: [PATCH 40/62] typo --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index fe49e69..b957194 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -418,7 +418,7 @@ function statCollection:sendStage3(winners, lastRound) end -- Build rounds table - local DOrounds = {} + local rounds = {} rounds[tostring(self.roundID)] = { players = players } From 1090d17a6a953c2fc6f5652eddb188d3a38af3ba Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Thu, 19 Nov 2015 09:15:08 +1000 Subject: [PATCH 41/62] Bump version to 4 Let's roll with these changes. --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index b957194..6f2dc03 100644 --- a/game/dota_addons/YOUR_ADDON/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: ' From 5a2fc241968463bf5dea4af2d5f5d563d382f2e2 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 24 Nov 2015 19:17:45 +1000 Subject: [PATCH 42/62] Add new error message, add a testing override when waiting for host --- .../statcollection/lib/statcollection.lua | 61 +++++++++++++------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 6f2dc03..c75fa38 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -42,6 +42,7 @@ local errorInitCalledTwice = 'Please ensure you only make a single call to statC local errorJsonDecode = 'There was an issue decoding the JSON returned from the server, see below:' local errorSomethingWentWrong = 'The server said something went wrong, see below:' local errorRunInit = 'You need to call the init function before you can send stats!' +local errorMissedStage1 = 'You need to call the sendStage1 function before you can continue!' local errorFlags = 'Flags needs to be a table!' local errorSchemaNotEnabled = 'Schema has not been enabled!!' local errorBadSchema = 'This schema doesn\'t exist!!' @@ -155,25 +156,31 @@ 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 + -- If we are testing (i.e. in workshop tools, don't wait for player connects to check) + if self.TESTING then + -- Send stage1 stuff + this:sendStage1() + else + --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 + this:sendStage1() + break + end end end - end - end, nil) + end, nil) + end -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) @@ -251,6 +258,7 @@ end function statCollection:sendStage1() -- If we are missing required parameters, then don't send if not self.doneInit then + print("sendStage1 ERROR") print(printPrefix .. errorRunInit) return end @@ -326,11 +334,19 @@ end -- Sends stage2 function statCollection:sendStage2() -- If we are missing required parameters, then don't send - if not self.doneInit or not self.authKey or not self.matchID then + if not self.doneInit then + print("sendStage2 ERROR") print(printPrefix .. errorRunInit) return end + -- If we are missing stage1 stuff, don't continue + if not self.authKey or not self.matchID then + print("sendStage2 ERROR") + print(printPrefix .. errorMissedStage1) + return + end + -- Ensure we can only send it once, and everything is good to go if self.sentStage2 then return end self.sentStage2 = true @@ -386,12 +402,19 @@ end -- Sends stage3 function statCollection:sendStage3(winners, lastRound) -- If we are missing required parameters, then don't send - if not self.doneInit or not self.authKey or not self.matchID then + if not self.doneInit then print("sendStage3 ERROR") print(printPrefix .. errorRunInit) return end + -- If we are missing stage1 stuff, don't continue + if not self.authKey or not self.matchID then + print("sendStage3 ERROR") + print(printPrefix .. errorMissedStage1) + return + end + -- Ensure we can only send it once, and everything is good to go if not self.HAS_ROUNDS then if self.sentStage3 then return end From da96478e2e52c103f2a8a6a43d1e3011c3eef3a2 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sat, 16 Jan 2016 17:09:06 -0300 Subject: [PATCH 43/62] Fix #68 - Update http://getdotastats.com/#s2__guide_stat_collection requesting to merge the custom UI manifest line --- .../layout/custom_game/MERGE_custom_ui_manifest.xml | 6 ++++++ .../scripts/vscripts/statcollection/lib/statcollection.lua | 5 +---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/MERGE_custom_ui_manifest.xml diff --git a/content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/MERGE_custom_ui_manifest.xml b/content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/MERGE_custom_ui_manifest.xml new file mode 100644 index 0000000..d37112e --- /dev/null +++ b/content/dota_addons/YOUR_ADDON/panorama/layout/custom_game/MERGE_custom_ui_manifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index c75fa38..bdf9748 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -1,4 +1,4 @@ ---[[ +f--[[ Integrating the library into your scripts 1. Download the statcollection from github and merge the scripts folder into your game/YOUR_ADDON/ folder. @@ -191,9 +191,6 @@ function statCollection:hookFunctions() -- Load time flag 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) - elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then -- Send pregame stats this:sendStage2() From 9ed29e55197340c0d738b59dde382cdf8ec014bb Mon Sep 17 00:00:00 2001 From: Martin Noya Date: Thu, 21 Jan 2016 16:19:57 -0300 Subject: [PATCH 44/62] F --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index bdf9748..8611ed2 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -1,4 +1,4 @@ -f--[[ +--[[ Integrating the library into your scripts 1. Download the statcollection from github and merge the scripts folder into your game/YOUR_ADDON/ folder. From 2f532ebd63c3924091c5b6ec69a9118fc492b17b Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 7 Feb 2016 22:33:29 -0300 Subject: [PATCH 45/62] Added a setting to override sendStage2, allowing modders to delay the pre-game send until the game is officially ready --- .../statcollection/lib/statcollection.lua | 15 +++++++++++++-- .../scripts/vscripts/statcollection/settings.kv | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 8611ed2..2d3e1b0 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -43,6 +43,7 @@ local errorJsonDecode = 'There was an issue decoding the JSON returned from the local errorSomethingWentWrong = 'The server said something went wrong, see below:' local errorRunInit = 'You need to call the init function before you can send stats!' local errorMissedStage1 = 'You need to call the sendStage1 function before you can continue!' +local errorMissedStage2 = 'You need to call the sendStage2 function before you can continue!' local errorFlags = 'Flags needs to be a table!' local errorSchemaNotEnabled = 'Schema has not been enabled!!' local errorBadSchema = 'This schema doesn\'t exist!!' @@ -104,6 +105,7 @@ function statCollection:init() self.HAS_ROUNDS = tobool(statInfo.HAS_ROUNDS) self.GAME_WINNER = tobool(statInfo.GAME_WINNER) self.ANCIENT_EXPLOSION = tobool(statInfo.ANCIENT_EXPLOSION) + self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 = tobool(statInfo.OVERRIDE_AUTOMATIC_SEND_STAGE_2) self.TESTING = tobool(statInfo.TESTING) -- Store the modIdentifier @@ -192,8 +194,10 @@ function statCollection:hookFunctions() statCollection:setFlags({ loadTime = math.floor(GameRules:GetGameTime()) }) elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then - -- Send pregame stats - this:sendStage2() + if self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 then + -- Send pregame stats + this:sendStage2() + end end if self.ANCIENT_EXPLOSION then if state >= DOTA_GAMERULES_STATE_POST_GAME then @@ -412,6 +416,13 @@ function statCollection:sendStage3(winners, lastRound) return end + -- If we are missing stage2 stuff, don't continue + if not self.sentStage2 then + print("sendStage3 ERROR") + print(printPrefix .. errorMissedStage2) + return + end + -- Ensure we can only send it once, and everything is good to go if not self.HAS_ROUNDS then if self.sentStage3 then return end diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv index 02329db..3aebf73 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/settings.kv @@ -16,6 +16,10 @@ // Do we want statCollection to use ancient explosions for game victory? "ANCIENT_EXPLOSION" "true" + // Lets you specify the point where the game has really started. Useful for mods that set options after pre-game + // If this enabled, you must manually call `statCollection:sendStage2()` when your game is officially ready + "OVERRIDE_AUTOMATIC_SEND_STAGE_2" "false" + // Turn this off to prevent from sending stats of workshop tools developer lobbies "TESTING" "true" From d2faca28eadb86fa3783ecdf746d9761b94cfde6 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 7 Feb 2016 22:35:05 -0300 Subject: [PATCH 46/62] not --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 2d3e1b0..5eb5ba8 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -194,7 +194,7 @@ function statCollection:hookFunctions() statCollection:setFlags({ loadTime = math.floor(GameRules:GetGameTime()) }) elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then - if self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 then + if not self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 then -- Send pregame stats this:sendStage2() end From e21af3eae8b1c4fe0bf1ad391062c51b5fc06425 Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 20:53:14 -0300 Subject: [PATCH 47/62] Updated print system - In dedicated servers, print to panorama console if TESTING is enabled - In developer tools or non-dedi servers with TESTING Enabled, print to vscript console --- .../statcollection/lib/statcollection.lua | 137 ++++++++++-------- 1 file changed, 75 insertions(+), 62 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 5eb5ba8..c0a7691 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -70,36 +70,26 @@ end function statCollection:init() -- Only allow init to be run once if self.doneInit then - print(printPrefix .. errorInitCalledTwice) + statCollection:print(errorInitCalledTwice) return end self.doneInit = true -- Print the intro message - print(printPrefix .. messageStarting) + statCollection:print(messageStarting) -- Check for a modIdentifier local modIdentifier = statInfo.modID if not modIdentifier then - print(printPrefix .. errorMissingModIdentifier) + statCollection:print(errorMissingModIdentifier) elseif modIdentifier == 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' then - print(printPrefix .. errorDefaultModIdentifier) + statCollection:print(errorDefaultModIdentifier) self.doneInit = false return end - --[[ Check for a schemaIdentifier - if not schemaID then - print(printPrefix .. errorMissingSchemaIdentifier) - elseif schemaID == 'XXXXXXXXXXXXXXXX' and self.HAS_SCHEMA then - print(printPrefix.. errorDefaultSchemaIdentifier) - - self.doneInit = false - return - end]] - -- Load and set settings self.HAS_SCHEMA = statInfo.schemaID ~= 'XXXXXXXXXXXXXXXX' self.HAS_ROUNDS = tobool(statInfo.HAS_ROUNDS) @@ -246,12 +236,12 @@ function statCollection:setFlags(flags) -- Store the new flags for flagKey, flagValue in pairs(flags) do self.flags[flagKey] = flagValue - print(printPrefix .. messageFlagsSet .. " {" .. flagKey .. ":" .. tostring(flagValue) .. "}") + statCollection:print(messageFlagsSet .. " {" .. flagKey .. ":" .. tostring(flagValue) .. "}") end else -- Yell at the developer - print(printPrefix .. errorFlags) + statCollection:print(errorFlags) end end @@ -259,8 +249,8 @@ end function statCollection:sendStage1() -- If we are missing required parameters, then don't send if not self.doneInit then - print("sendStage1 ERROR") - print(printPrefix .. errorRunInit) + statCollection:print("sendStage1 ERROR") + statCollection:print(errorRunInit) return end @@ -269,7 +259,7 @@ function statCollection:sendStage1() self.sentStage1 = true -- Print the intro message - print(printPrefix .. messagePhase1Starting) + statCollection:print(messagePhase1Starting) -- Grab a reference to self local this = self @@ -311,15 +301,15 @@ function statCollection:sendStage1() self:sendStage('s2_phase_1.php', payload, function(err, res) -- Check if we got an error if err then - print(printPrefix .. errorJsonDecode) - print(printPrefix .. err) + statCollection:print(errorJsonDecode) + statCollection:print(err) return end -- Check for an error if res.error then - print(printPrefix .. errorSomethingWentWrong) - print(res.error) + statCollection:print(errorSomethingWentWrong) + statCollection:print(res.error) return end @@ -328,7 +318,9 @@ function statCollection:sendStage1() this.matchID = res.matchID -- Tell the user - print(printPrefix .. messagePhase1Complete) + statCollection:print(messagePhase1Complete) + statCollection:print("Auth Key: ", self.authKey) + statCollection:print("MatchID: ", self.matchID) end) end @@ -336,15 +328,17 @@ end function statCollection:sendStage2() -- If we are missing required parameters, then don't send if not self.doneInit then - print("sendStage2 ERROR") - print(printPrefix .. errorRunInit) + statCollection:print("sendStage2 ERROR") + statCollection:print(errorRunInit) return end -- If we are missing stage1 stuff, don't continue if not self.authKey or not self.matchID then - print("sendStage2 ERROR") - print(printPrefix .. errorMissedStage1) + statCollection:print("sendStage2 ERROR") + statCollection:print(errorMissedStage1) + statCollection:print("Auth Key: ", self.authKey) + statCollection:print("MatchID: ", self.matchID) return end @@ -353,7 +347,7 @@ function statCollection:sendStage2() self.sentStage2 = true -- Print the intro message - print(printPrefix .. messagePhase2Starting) + statCollection:print(messagePhase2Starting) -- Client check in CustomGameEventManager:Send_ServerToAllClients("statcollection_client", { modID = self.modIdentifier, matchID = self.matchID, schemaVersion = schemaVersion }) @@ -383,20 +377,20 @@ function statCollection:sendStage2() self:sendStage('s2_phase_2.php', payload, function(err, res) -- Check if we got an error if err then - print(printPrefix .. errorJsonDecode) - print(printPrefix .. err) + statCollection:print(errorJsonDecode) + statCollection:print(err) return end -- Check for an error if res.error then - print(printPrefix .. errorSomethingWentWrong) - print(res.error) + statCollection:print(errorSomethingWentWrong) + statCollection:print(res.error) return end -- Tell the user - print(printPrefix .. messagePhase2Complete) + statCollection:print(messagePhase2Complete) end) end @@ -404,22 +398,24 @@ end function statCollection:sendStage3(winners, lastRound) -- If we are missing required parameters, then don't send if not self.doneInit then - print("sendStage3 ERROR") - print(printPrefix .. errorRunInit) + statCollection:print("sendStage3 ERROR") + statCollection:print(errorRunInit) return end -- If we are missing stage1 stuff, don't continue if not self.authKey or not self.matchID then - print("sendStage3 ERROR") - print(printPrefix .. errorMissedStage1) + statCollection:print("sendStage3 ERROR") + statCollection:print(errorMissedStage1) return end -- If we are missing stage2 stuff, don't continue if not self.sentStage2 then - print("sendStage3 ERROR") - print(printPrefix .. errorMissedStage2) + statCollection:print("sendStage3 ERROR") + statCollection:print(errorMissedStage2) + statCollection:print("Auth Key: ", self.authKey) + statCollection:print("MatchID: ", self.matchID) return end @@ -432,7 +428,7 @@ function statCollection:sendStage3(winners, lastRound) end -- Print the intro message - print(printPrefix .. messagePhase3Starting) + statCollection:print(messagePhase3Starting) -- Build players array local players = {} @@ -469,20 +465,20 @@ function statCollection:sendStage3(winners, lastRound) self:sendStage('s2_phase_3.php', payload, function(err, res) -- Check if we got an error if err then - print(printPrefix .. errorJsonDecode) - print(printPrefix .. err) + statCollection:print(errorJsonDecode) + statCollection:print(err) return end -- Check for an error if res.error then - print(printPrefix .. errorSomethingWentWrong) - print(res.error) + statCollection:print(errorSomethingWentWrong) + statCollection:print(res.error) return end -- Tell the user - print(printPrefix .. messagePhase3Complete) + statCollection:print(messagePhase3Complete) end) end @@ -495,8 +491,8 @@ end -- Sends custom function statCollection:sendCustom(args) if not self.HAS_SCHEMA then - print("sendCustom ERROR") - print(printPrefix .. errorDefaultSchemaIdentifier) + statCollection:print("sendCustom ERROR") + statCollection:print(errorDefaultSchemaIdentifier) return end @@ -507,9 +503,9 @@ function statCollection:sendCustom(args) end -- If we are missing required parameters, then don't send if not self.doneInit or not self.authKey or not self.matchID or not self.SCHEMA_KEY then - print(printPrefix .. errorRunInit) + statCollection:print(errorRunInit) if not self.SCHEMA_KEY then - print(printPrefix .. errorRunInit) + statCollection:print(errorRunInit) end return end @@ -521,7 +517,7 @@ function statCollection:sendCustom(args) end -- Print the intro message - print(printPrefix .. messageCustomStarting) + statCollection:print(messageCustomStarting) -- Build rounds table local rounds = {} @@ -543,20 +539,20 @@ function statCollection:sendCustom(args) self:sendStage('s2_custom.php', payload, function(err, res) -- Check if we got an error if err then - print(printPrefix .. errorJsonDecode) - print(printPrefix .. err) + statCollection:print(errorJsonDecode) + statCollection:print(err) return end -- Check for an error if res.error then - print(printPrefix .. errorSomethingWentWrong) - print(res.error) + statCollection:print(errorSomethingWentWrong) + statCollection:print(res.error) return end -- Tell the user - print(printPrefix .. messageCustomComplete) + statCollection:print(messageCustomComplete) end) end @@ -571,7 +567,7 @@ function statCollection:sendStage(stageName, payload, callback) -- Send the request req:Send(function(res) if res.StatusCode ~= 200 or not res.Body then - print(printPrefix .. errorFailedToContactServer) + statCollection:print(errorFailedToContactServer) return end @@ -583,10 +579,27 @@ function statCollection:sendStage(stageName, payload, callback) end) end -function tobool(s) - if s == "true" or s == "1" or s == 1 then - return true - else --nil "false" "0" - return false +function statCollection:print(s1, s2) + local str = s1 + if s1 then + str = printPrefix .. tostring(s1) + end + + if s2 then + str = str .. " " .. tostring(s2) + end + + -- print to panorama console in dedicated servers Testing mode + if IsDedicatedServer() and self.TESTING then + CustomGameEventManager:Send_ServerToAllClients("statcollection_print", { content = str }) + else + -- print to vscript developer console, or non-dedi server + if self.TESTING or Convars:GetBool("developer") then + print(str) + end end end + +function tobool(s) + return s == true or s == "true" or s == "1" or s == 1 then +end \ No newline at end of file From 1f389e8993f5699c98ea2ed6cc0e056ace017776 Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 20:53:45 -0300 Subject: [PATCH 48/62] round the gameDuration on stage3 --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index c0a7691..67565c9 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -455,7 +455,7 @@ function statCollection:sendStage3(winners, lastRound) modIdentifier = self.modIdentifier, schemaVersion = schemaVersion, rounds = rounds, - gameDuration = GameRules:GetGameTime() + gameDuration = math.floor(GameRules:GetGameTime()+0.5) } if lastRound == false then payload.gameFinished = 0 From dbedc367ad9b079335e148521f0d197dd48ef721 Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 21:23:04 -0300 Subject: [PATCH 49/62] Changed the way sentStage flags are confirmed - Only turn on the flag when the callback received was successful --- .../statcollection/lib/statcollection.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 67565c9..40696a7 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -256,7 +256,6 @@ function statCollection:sendStage1() -- Ensure we can only send it once, and everything is good to go if self.sentStage1 then return end - self.sentStage1 = true -- Print the intro message statCollection:print(messagePhase1Starting) @@ -313,6 +312,8 @@ function statCollection:sendStage1() return end + self.sentStage1 = true + -- Woot, store our vars this.authKey = res.authKey this.matchID = res.matchID @@ -344,7 +345,6 @@ function statCollection:sendStage2() -- Ensure we can only send it once, and everything is good to go if self.sentStage2 then return end - self.sentStage2 = true -- Print the intro message statCollection:print(messagePhase2Starting) @@ -389,6 +389,8 @@ function statCollection:sendStage2() return end + self.sentStage2 = true + -- Tell the user statCollection:print(messagePhase2Complete) end) @@ -422,7 +424,6 @@ function statCollection:sendStage3(winners, lastRound) -- Ensure we can only send it once, and everything is good to go if not self.HAS_ROUNDS then if self.sentStage3 then return end - self.sentStage3 = true else self.roundID = self.roundID + 1 end @@ -477,6 +478,10 @@ function statCollection:sendStage3(winners, lastRound) return end + if not self.HAS_ROUNDS then + self.sentStage3 = true + end + -- Tell the user statCollection:print(messagePhase3Complete) end) @@ -513,7 +518,6 @@ function statCollection:sendCustom(args) -- Ensure we can only send it once, and everything is good to go if self.HAS_ROUNDS == false then if self.sentCustom then return end - self.sentCustom = true end -- Print the intro message @@ -551,6 +555,10 @@ function statCollection:sendCustom(args) return end + if self.HAS_ROUNDS == false then + self.sentCustom = true + end + -- Tell the user statCollection:print(messageCustomComplete) end) From 9f10ed4ca9b788ed9ba7e3bb9e194f99f30e976a Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 21:39:12 -0300 Subject: [PATCH 50/62] Added `dotaMatchID` to stage2 --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 40696a7..997e8da 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -370,6 +370,7 @@ function statCollection:sendStage2() modIdentifier = self.modIdentifier, flags = self.flags, schemaVersion = schemaVersion, + dotaMatchID = GameRules:GetMatchID(), players = players } @@ -409,6 +410,8 @@ function statCollection:sendStage3(winners, lastRound) if not self.authKey or not self.matchID then statCollection:print("sendStage3 ERROR") statCollection:print(errorMissedStage1) + statCollection:print("Auth Key: ", self.authKey) + statCollection:print("MatchID: ", self.matchID) return end From b23c7f965a97c82c11e0bd42f113c6b0434e1c04 Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 21:50:28 -0300 Subject: [PATCH 51/62] Fixest Convars registering more than once, added test_end_game --- .../scripts/vscripts/statcollection/schema.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua index 509633c..0c5e192 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua @@ -31,8 +31,16 @@ function customSchema:init() end end end, nil) + + -- 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_end_game", function() GameRules:SetGameWinner(DOTA_TEAM_GOODGUYS) end, "Test the end game", 0) + end end + + ------------------------------------- -- In the statcollection/lib/utilities.lua, you'll find many useful functions to build your schema. @@ -79,11 +87,6 @@ function PrintSchema(gameArray, 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 From fa30c48f1aedf8bbfb4bcd8278f1a2c5f1555d4c Mon Sep 17 00:00:00 2001 From: MNoya Date: Fri, 18 Mar 2016 22:51:33 -0300 Subject: [PATCH 52/62] New postLocation - Convert matchID to string - Round the loading time properly - Print the encoded payload if TESTING --- .../vscripts/statcollection/lib/statcollection.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 997e8da..7861384 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -25,7 +25,7 @@ require('statcollection/schema') local statInfo = LoadKeyValues('scripts/vscripts/statcollection/settings.kv') -- Where stuff is posted to -local postLocation = 'http://getdotastats.com/s2/api/' +local postLocation = 'https://api.getdotastats.com/' -- The schema version we are currently using local schemaVersion = 4 @@ -181,7 +181,7 @@ function statCollection:hookFunctions() 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()+0.5) }) elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then if not self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 then @@ -370,7 +370,7 @@ function statCollection:sendStage2() modIdentifier = self.modIdentifier, flags = self.flags, schemaVersion = schemaVersion, - dotaMatchID = GameRules:GetMatchID(), + dotaMatchID = tostring(GameRules:GetMatchID()), players = players } @@ -571,9 +571,11 @@ end function statCollection:sendStage(stageName, payload, callback) -- Create the request local req = CreateHTTPRequest('POST', postLocation .. stageName) - --print(json.encode(payload)) + local encoded = json.encode(payload) + --print(encoded) + -- Add the data - req:SetHTTPRequestGetOrPostParameter('payload', json.encode(payload)) + req:SetHTTPRequestGetOrPostParameter('payload', encoded) -- Send the request req:Send(function(res) @@ -612,5 +614,5 @@ function statCollection:print(s1, s2) end function tobool(s) - return s == true or s == "true" or s == "1" or s == 1 then + return s == true or s == "true" or s == "1" or s == 1 end \ No newline at end of file From 9c3c5e2af5b66c0afdf2f42ff892af4356a99ba9 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sat, 19 Mar 2016 01:32:39 -0300 Subject: [PATCH 53/62] Restrict automatic stage2 send to pregame only Added panorama print js which was missing earlier --- .../panorama/scripts/custom_game/statcollection.js | 5 +++++ .../scripts/vscripts/statcollection/lib/statcollection.lua | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) 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 index c129f6b..2f3bd3b 100644 --- a/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js +++ b/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js @@ -31,9 +31,14 @@ function GetSteamID32() { return steamID32; } +function Print(msg) { + $.Msg(msg.content) +} + (function () { $.Msg("StatCollection Client Loaded"); GameEvents.Subscribe("statcollection_client", OnClientCheckIn); + GameEvents.Subscribe("statcollection_print", Print); })(); \ No newline at end of file diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 7861384..f87c888 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -183,7 +183,7 @@ function statCollection:hookFunctions() -- Load time flag statCollection:setFlags({ loadTime = math.floor(GameRules:GetGameTime()+0.5) }) - elseif state >= DOTA_GAMERULES_STATE_PRE_GAME then + elseif state == DOTA_GAMERULES_STATE_PRE_GAME then if not self.OVERRIDE_AUTOMATIC_SEND_STAGE_2 then -- Send pregame stats this:sendStage2() @@ -572,7 +572,9 @@ function statCollection:sendStage(stageName, payload, callback) -- Create the request local req = CreateHTTPRequest('POST', postLocation .. stageName) local encoded = json.encode(payload) - --print(encoded) + if self.TESTING then + statCollection:print(encoded) + end -- Add the data req:SetHTTPRequestGetOrPostParameter('payload', encoded) From 0e4222d25fcdc67f40fde9ac7721101ce903c425 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sat, 19 Mar 2016 17:21:11 -0300 Subject: [PATCH 54/62] Custom staging support and other minor changes - init will no longer vscript crash the game if the settings.kv file can't be found - updated commented readme (chatbox is gone) - Print improvements: 1. only print setflags when testing 2. The four stage callback error prints now all go to a ReturnedErrors function check 3. added printError, always prints where, msg and the 2 keys --- .../scripts/vscripts/statcollection/init.lua | 9 +- .../statcollection/lib/statcollection.lua | 132 ++++++++---------- .../vscripts/statcollection/staging.lua | 17 +++ 3 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/staging.lua diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua index 87f9e2d..79068d0 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/init.lua @@ -1,8 +1,14 @@ +local statInfo = LoadKeyValues('scripts/vscripts/statcollection/settings.kv') +if not statInfo then + print("Stat Collection: Critical Error, no settings.kv file found") + return +end + require("statcollection/schema") require('statcollection/lib/statcollection') +require('statcollection/staging') require('statcollection/lib/utilities') -local statInfo = LoadKeyValues('scripts/vscripts/statcollection/settings.kv') local COLLECT_STATS = not Convars:GetBool('developer') local TESTING = tobool(statInfo.TESTING) local MIN_PLAYERS = tonumber(statInfo.MIN_PLAYERS) @@ -14,7 +20,6 @@ if COLLECT_STATS or TESTING then if state >= DOTA_GAMERULES_STATE_INIT and not statCollection.doneInit then if PlayerResource:GetPlayerCount() >= MIN_PLAYERS or TESTING then - -- Init stat collection statCollection:init() customSchema:init() diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index f87c888..f414218 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -14,7 +14,7 @@ statCollection:setFlags({FlagName = 'FlagValue'}) Customising the stats beyond this will require talking to the GetDotaStats staff so a custom schema can be built for you. Extended functionality will be added as it is needed. -Come bug us in our IRC channel or get in contact via the site chatbox. http://getdotastats.com/#contact +Come bug us in our IRC channel #getdotastats at GameSurge.net ]] -- Require libs @@ -236,7 +236,9 @@ function statCollection:setFlags(flags) -- Store the new flags for flagKey, flagValue in pairs(flags) do self.flags[flagKey] = flagValue - statCollection:print(messageFlagsSet .. " {" .. flagKey .. ":" .. tostring(flagValue) .. "}") + if self.TESTING then + statCollection:print(messageFlagsSet .. " {" .. flagKey .. ":" .. tostring(flagValue) .. "}") + end end else @@ -299,47 +301,37 @@ function statCollection:sendStage1() -- Begin the initial request self:sendStage('s2_phase_1.php', payload, function(err, res) -- Check if we got an error - if err then - statCollection:print(errorJsonDecode) - statCollection:print(err) - return - end - - -- Check for an error - if res.error then - statCollection:print(errorSomethingWentWrong) - statCollection:print(res.error) + if self:ReturnedErrors(err, res) then return end - self.sentStage1 = true - -- Woot, store our vars this.authKey = res.authKey this.matchID = res.matchID + self.sentStage1 = true + -- Tell the user statCollection:print(messagePhase1Complete) statCollection:print("Auth Key: ", self.authKey) statCollection:print("MatchID: ", self.matchID) end) + + -- Custom staging + self:Stage1(payload) end -- Sends stage2 function statCollection:sendStage2() -- If we are missing required parameters, then don't send if not self.doneInit then - statCollection:print("sendStage2 ERROR") - statCollection:print(errorRunInit) + statCollection:printError("sendStage2", errorRunInit) return end -- If we are missing stage1 stuff, don't continue if not self.authKey or not self.matchID then - statCollection:print("sendStage2 ERROR") - statCollection:print(errorMissedStage1) - statCollection:print("Auth Key: ", self.authKey) - statCollection:print("MatchID: ", self.matchID) + statCollection:printError("sendStage2", errorMissedStage1) return end @@ -377,16 +369,7 @@ function statCollection:sendStage2() -- Send stage2 self:sendStage('s2_phase_2.php', payload, function(err, res) -- Check if we got an error - if err then - statCollection:print(errorJsonDecode) - statCollection:print(err) - return - end - - -- Check for an error - if res.error then - statCollection:print(errorSomethingWentWrong) - statCollection:print(res.error) + if self:ReturnedErrors(err, res) then return end @@ -395,38 +378,35 @@ function statCollection:sendStage2() -- Tell the user statCollection:print(messagePhase2Complete) end) + + -- Custom staging + self:Stage2(payload) end -- Sends stage3 function statCollection:sendStage3(winners, lastRound) -- If we are missing required parameters, then don't send if not self.doneInit then - statCollection:print("sendStage3 ERROR") - statCollection:print(errorRunInit) + statCollection:printError("sendStage3", errorRunInit) return end -- If we are missing stage1 stuff, don't continue if not self.authKey or not self.matchID then - statCollection:print("sendStage3 ERROR") - statCollection:print(errorMissedStage1) - statCollection:print("Auth Key: ", self.authKey) - statCollection:print("MatchID: ", self.matchID) + statCollection:printError("sendStage3", errorMissedStage1) return end -- If we are missing stage2 stuff, don't continue if not self.sentStage2 then - statCollection:print("sendStage3 ERROR") - statCollection:print(errorMissedStage2) - statCollection:print("Auth Key: ", self.authKey) - statCollection:print("MatchID: ", self.matchID) + statCollection:printError("sendStage3", errorMissedStage2) return end -- Ensure we can only send it once, and everything is good to go if not self.HAS_ROUNDS then if self.sentStage3 then return end + self.sentStage3 = true else self.roundID = self.roundID + 1 end @@ -468,26 +448,16 @@ function statCollection:sendStage3(winners, lastRound) -- Send stage3 self:sendStage('s2_phase_3.php', payload, function(err, res) -- Check if we got an error - if err then - statCollection:print(errorJsonDecode) - statCollection:print(err) + if self:ReturnedErrors(err, res) then return end - -- Check for an error - if res.error then - statCollection:print(errorSomethingWentWrong) - statCollection:print(res.error) - return - end - - if not self.HAS_ROUNDS then - self.sentStage3 = true - end - -- Tell the user statCollection:print(messagePhase3Complete) end) + + -- Custom staging + self:Stage3(payload) end function statCollection:submitRound(args) @@ -499,8 +469,7 @@ end -- Sends custom function statCollection:sendCustom(args) if not self.HAS_SCHEMA then - statCollection:print("sendCustom ERROR") - statCollection:print(errorDefaultSchemaIdentifier) + statCollection:print("sendCustom", errorDefaultSchemaIdentifier) return end @@ -521,6 +490,7 @@ function statCollection:sendCustom(args) -- Ensure we can only send it once, and everything is good to go if self.HAS_ROUNDS == false then if self.sentCustom then return end + self.sentCustom = true end -- Print the intro message @@ -545,32 +515,25 @@ function statCollection:sendCustom(args) -- Send custom self:sendStage('s2_custom.php', payload, function(err, res) -- Check if we got an error - if err then - statCollection:print(errorJsonDecode) - statCollection:print(err) + if self:ReturnedErrors(err, res) then return end - -- Check for an error - if res.error then - statCollection:print(errorSomethingWentWrong) - statCollection:print(res.error) - return - end - - if self.HAS_ROUNDS == false then - self.sentCustom = true - end - -- Tell the user statCollection:print(messageCustomComplete) end) + + -- Custom staging + self:StageCustom(payload) end -- Sends the payload data for the given stage, and return the result -function statCollection:sendStage(stageName, payload, callback) +-- Optional override_host can be added to reutilize this function for other sites +function statCollection:sendStage(stageName, payload, callback, override_host) + local host = override_host or postLocation + -- Create the request - local req = CreateHTTPRequest('POST', postLocation .. stageName) + local req = CreateHTTPRequest('POST', host .. stageName) local encoded = json.encode(payload) if self.TESTING then statCollection:print(encoded) @@ -594,6 +557,31 @@ function statCollection:sendStage(stageName, payload, callback) end) end +-- Checks the error and result objects and returns whether its invalid or not +function statCollection:ReturnedErrors(err, res) + if err then + statCollection:print(errorJsonDecode) + statCollection:print(err) + return true + end + + if res.error then + statCollection:print(errorSomethingWentWrong) + statCollection:print(res.error) + return true + end + + -- no errors + return false +end + +function statCollection:printError(where, msg) + statCollection:print("ERROR at "..where) + statCollection:print(msg) + statCollection:print("Auth Key: ", self.authKey) + statCollection:print("MatchID: ", self.matchID) +end + function statCollection:print(s1, s2) local str = s1 if s1 then diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/staging.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/staging.lua new file mode 100644 index 0000000..913e421 --- /dev/null +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/staging.lua @@ -0,0 +1,17 @@ +-- This exposes the stages and can be used to send custom requests to secondary locations + +function statCollection:Stage1(payload) + +end + +function statCollection:Stage2(payload) + +end + +function statCollection:Stage3(payload) + +end + +function statCollection:StageCustom(payload) + +end \ No newline at end of file From 025bfd0ad6fce83131c04504b1325c2a1ef8338b Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 20 Mar 2016 22:16:34 -0300 Subject: [PATCH 55/62] Removing the checks preventing stage1 send (TESTING & Wait for host) --- .../statcollection/lib/statcollection.lua | 29 ++----------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index f414218..e14fcad 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -112,6 +112,9 @@ function statCollection:init() -- Hook requred functions to operate correctly self:hookFunctions() + + -- Send stage1 stuff + self:sendStage1() end --Build the winners array @@ -148,32 +151,6 @@ function statCollection:hookFunctions() end end - -- If we are testing (i.e. in workshop tools, don't wait for player connects to check) - if self.TESTING then - -- Send stage1 stuff - this:sendStage1() - else - --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 - this:sendStage1() - break - end - end - end - end, nil) - end - -- Listen for changes in the current state ListenToGameEvent('game_rules_state_change', function(keys) -- Grab the current state From 90bf393a0cfe02357df42d8619e2e4981d711fe9 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 20 Mar 2016 22:28:16 -0300 Subject: [PATCH 56/62] Added `isHost` on client check-in --- .../panorama/scripts/custom_game/statcollection.js | 7 +++++++ 1 file changed, 7 insertions(+) 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 index 2f3bd3b..78d7866 100644 --- a/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js +++ b/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js @@ -2,11 +2,18 @@ function OnClientCheckIn(args) { + var playerInfo = Game.GetLocalPlayerInfo(); + var hostInfo = 0 + if ( playerInfo ) + hostInfo = playerInfo.player_has_host_privileges + var payload = { modIdentifier: args.modID, steamID32: GetSteamID32(), + isHost: hostInfo, matchID: args.matchID, schemaVersion: args.schemaVersion + }; $.Msg('Sending: ', payload); From 41eef9afa03d6c46b8450740ad50c9a654543f8d Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 20 Mar 2016 22:32:15 -0300 Subject: [PATCH 57/62] Updated js post location --- .../YOUR_ADDON/panorama/scripts/custom_game/statcollection.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 index 78d7866..11d4920 100644 --- a/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js +++ b/content/dota_addons/YOUR_ADDON/panorama/scripts/custom_game/statcollection.js @@ -13,12 +13,11 @@ function OnClientCheckIn(args) { isHost: hostInfo, matchID: args.matchID, schemaVersion: args.schemaVersion - }; $.Msg('Sending: ', payload); - $.AsyncWebRequest('http://getdotastats.com/s2/api/s2_check_in.php', + $.AsyncWebRequest('https://api.getdotastats.com/s2_check_in.php', { type: 'POST', data: {payload: JSON.stringify(payload)}, From f4ff7e75be0f84ffc383ea12da073abe79d3ae57 Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 20 Mar 2016 22:39:04 -0300 Subject: [PATCH 58/62] Dedicated server check in --- .../statcollection/lib/statcollection.lua | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index e14fcad..150ef4f 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -321,6 +321,11 @@ function statCollection:sendStage2() -- Client check in CustomGameEventManager:Send_ServerToAllClients("statcollection_client", { modID = self.modIdentifier, matchID = self.matchID, schemaVersion = schemaVersion }) + -- Dedicated server check in + if IsDedicatedServer() then + self:sendHostCheckIn() + end + -- Build players array local players = {} for playerID = 0, DOTA_MAX_TEAM_PLAYERS do @@ -504,6 +509,25 @@ function statCollection:sendCustom(args) self:StageCustom(payload) end +function statCollection:sendHostCheckIn() + local payload = { + modIdentifier = self.modIdentifier, + steamID32 = "-1", + isHost = "1", + matchID = self.matchID, + schemaVersion: schemaVersion, + } + + -- Send check in + self:sendStage('s2_check_in.php', payload, function(err, res) + -- Check if we got an error + if self:ReturnedErrors(err, res) then + statCollection:printError("sendHostCheckIn", "Dedicated server check-in failed!") + return + end + end) +end + -- Sends the payload data for the given stage, and return the result -- Optional override_host can be added to reutilize this function for other sites function statCollection:sendStage(stageName, payload, callback, override_host) From 485b1578a4a56b4fd2570d818df7e3948f46de2e Mon Sep 17 00:00:00 2001 From: MNoya Date: Sun, 20 Mar 2016 22:48:51 -0300 Subject: [PATCH 59/62] schemaVersion 5 Player Count is now set during stage2 --- .../vscripts/statcollection/lib/statcollection.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 150ef4f..0bc7582 100644 --- a/game/dota_addons/YOUR_ADDON/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 = 'https://api.getdotastats.com/' -- The schema version we are currently using -local schemaVersion = 4 +local schemaVersion = 5 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' @@ -242,11 +242,6 @@ function statCollection:sendStage1() -- Grab a reference to self local this = self - -- Workout the player count - local playerCount = PlayerResource:GetPlayerCount() - if playerCount <= 0 then playerCount = 1 end - statCollection:setFlags({ numPlayers = playerCount }) - -- Workout who is hosting local hostID = 0 for playerID = 0, DOTA_MAX_TEAM_PLAYERS do @@ -326,6 +321,11 @@ function statCollection:sendStage2() self:sendHostCheckIn() end + -- Save the player count + local playerCount = PlayerResource:GetPlayerCount() + if playerCount <= 0 then playerCount = 1 end + statCollection:setFlags({ numPlayers = playerCount }) + -- Build players array local players = {} for playerID = 0, DOTA_MAX_TEAM_PLAYERS do @@ -515,7 +515,7 @@ function statCollection:sendHostCheckIn() steamID32 = "-1", isHost = "1", matchID = self.matchID, - schemaVersion: schemaVersion, + schemaVersion = schemaVersion, } -- Send check in From e6d45e9b806f8c99866a8911bf493e91bb54fa2d Mon Sep 17 00:00:00 2001 From: MNoya Date: Sat, 16 Apr 2016 20:04:31 -0300 Subject: [PATCH 60/62] Fix round bugs --- .../statcollection/lib/statcollection.lua | 16 +++++++++------- .../scripts/vscripts/statcollection/schema.lua | 5 +---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index 0bc7582..e7fc304 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -107,8 +107,8 @@ function statCollection:init() -- Set the default winner to -1 (no winner) self.winner = -1 - --Store roundID globally - self.roundID = 0 + -- Set round to -1 (not finished) + self.roundID = -1 -- Hook requred functions to operate correctly self:hookFunctions() @@ -389,10 +389,10 @@ function statCollection:sendStage3(winners, lastRound) if not self.HAS_ROUNDS then if self.sentStage3 then return end self.sentStage3 = true - else - self.roundID = self.roundID + 1 end + self.roundID = self.roundID + 1 + -- Print the intro message statCollection:print(messagePhase3Starting) @@ -442,10 +442,10 @@ function statCollection:sendStage3(winners, lastRound) self:Stage3(payload) end -function statCollection:submitRound(args) +function statCollection:submitRound(lastRound) --We receive the winners from the custom schema, lets tell phase 3 about it! - local returnArgs = customSchema:submitRound(args) - self:sendStage3(returnArgs.winners, returnArgs.lastRound) + self:sendStage3(BuildRoundWinnerArray(), lastRound) + customSchema:submitRound() end -- Sends custom @@ -547,6 +547,8 @@ function statCollection:sendStage(stageName, payload, callback, override_host) req:Send(function(res) if res.StatusCode ~= 200 or not res.Body then statCollection:print(errorFailedToContactServer) + statCollection:print("Status Code", res.StatusCode or "nil") + statCollection:print("Body", res.StatusCode or "nil") return end diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua index 0c5e192..972c9cc 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/schema.lua @@ -92,16 +92,13 @@ 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) +function customSchema:submitRound() 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 From 494ef62d68761c4a8793e8cb3f4cfee6d7c29418 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Tue, 14 Feb 2017 15:26:43 +1000 Subject: [PATCH 61/62] Fixes #92 Fixes typo for HTTPRequest error message. Separate empty server response from bad status code in error log. --- .../statcollection/lib/statcollection.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index e7fc304..e20ad35 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -33,7 +33,8 @@ local schemaVersion = 5 -- Constants used for pretty formatting, as well as strings local printPrefix = 'Stat Collection: ' -local errorFailedToContactServer = 'Failed to contact the master server! Bad status code, or no body!' +local errorFailedToContactServer = 'Failed to contact the master server! Bad status code!' +local errorEmptyServerResponse = 'Master server returned empty response!' local errorMissingModIdentifier = 'Please ensure you have a settings.kv in your statcollection folder! Missing modID!' local errorDefaultModIdentifier = 'Please change your settings.kv with a valid modID, acquired after registration of your mod on the site!' local errorMissingSchemaIdentifier = 'Please ensure you have a settings.kv in your statcollection folder! Missing schemaID!' @@ -545,10 +546,16 @@ function statCollection:sendStage(stageName, payload, callback, override_host) -- Send the request req:Send(function(res) - if res.StatusCode ~= 200 or not res.Body then + if res.StatusCode ~= 200 then statCollection:print(errorFailedToContactServer) statCollection:print("Status Code", res.StatusCode or "nil") - statCollection:print("Body", res.StatusCode or "nil") + statCollection:print("Body", res.Body or "nil") + return + end + + if not res.Body then + statCollection:print(errorEmptyServerResponse) + statCollection:print("Status Code", res.StatusCode or "nil") return end @@ -608,4 +615,4 @@ end function tobool(s) return s == true or s == "true" or s == "1" or s == 1 -end \ No newline at end of file +end From b41c2cc8f7ccb466b6f867d69f12630fad092a08 Mon Sep 17 00:00:00 2001 From: jimmydorry Date: Wed, 29 Mar 2017 18:52:10 +1000 Subject: [PATCH 62/62] Replace CreateHTTPRequest due to breaking update Valve replaced `CreateHTTPRequest` with `CreateHTTPRequestScriptVM` --- .../scripts/vscripts/statcollection/lib/statcollection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua index e20ad35..7e95673 100644 --- a/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua +++ b/game/dota_addons/YOUR_ADDON/scripts/vscripts/statcollection/lib/statcollection.lua @@ -535,7 +535,7 @@ function statCollection:sendStage(stageName, payload, callback, override_host) local host = override_host or postLocation -- Create the request - local req = CreateHTTPRequest('POST', host .. stageName) + local req = CreateHTTPRequestScriptVM('POST', host .. stageName) local encoded = json.encode(payload) if self.TESTING then statCollection:print(encoded)