Code Monkey home page Code Monkey logo

ga-sdk-roblox's Introduction

GA-SDK-ROBLOX

GameAnalytics Roblox SDK.

Documentation can be found here.

If you have any issues or feedback regarding the SDK, please contact our friendly support team here.

Requirements

  • rojo (optional, but needed if you want to automatically sync the source files inside the GameAnalyticsSDK folder into your Roblox project)

Changelog

2.2.4

  • removed LocalScript that was added in 2.2.3, causing errors on requiring GameAnalyticsClient module

2.2.3

  • fixed bug where PlayerData would reference BasePlayerData tables instead of making copies
  • added datastore queue

2.2.2

  • fix to wally support

2.2.1

  • fixed bug related to game passes purchases

2.2.0

  • added support for wally (OBS: breaking changes)

2.1.35

  • fixed argument order

2.1.34

  • corrected to use new postie api where referenced

2.1.33

  • postie script updated

2.1.32

  • replace global spawn and wait with task library

2.1.31

  • fixed postie bug after moving the script under gameanalytics script

2.1.30

  • replaced rbxmx generator script with rojo build command (requires min. rojo 6)

2.1.29

  • moved postie script inside GameAnalytics scripts

2.1.28

  • fixed error message in errorhandler

2.1.27

  • country code field always sent with events now (sent as 'null' if country code couldn't be fetched)

2.1.26

  • fixed undefined variable errors

2.1.25

  • fixed potential error in session end event code

2.1.24

  • added error tracking if country code fails to get retrieved

2.1.23

  • fixed ab testing

2.1.22

  • fixed bug with remote configs and ab testing ids not being added to events

2.1.21

  • fixed GetPlayerDataFromCache function

2.1.20

  • fixes to gamepass in business events

2.1.19

  • fixed detection of website gamepasses throttling datastores

2.1.18

  • fixed sesion start and end issues which caused problems with metrics

2.1.17

  • corrected variable name inside GetPlayerDataFromCache function

2.1.16

  • player data cache now accepts both userId of string or number type

2.1.15

  • fixed not clearing session start ts because teleport flag was not cleared

2.1.14

  • fixed logic for error handler

2.1.13

  • added player id to error events sent from error reporting

2.1.12

  • fixed ScriptContext.Error error reporting

2.1.11

  • switched from using LogService to ScriptContext.Error for error reporting

2.1.10

  • fixed setAvailableGamepasses function

2.1.9

  • correct install instructions and fixed GameAnalyticsServerInit script

2.1.8

  • correct business event for 'Gamepass' itemType

2.1.7

  • added country code to events to get correct country of users

2.1.6

  • corrected install instructions

2.1.5

  • Moved everything from GameAnalyticsServer to GameAnalytics module and created a template server script for calling the initialize function.
  • Added queue for functions like addDesignEvent etc. that are called before player or GA is initialized
  • renamed server init with settings script and restructured it (new usage)

2.1.4

  • added session_num to init request

2.1.3

  • Replaced previous HMAC + SHA256 + Base64 implementation with HashLib. This version is around 23 - 25% faster.
  • Changed indenting from spaces to tabs (Roblox default).
  • Worked on reformatting so it followed the Roblox Lua style guide a little better..
  • Updated the luacheck files more.

2.1.2

  • fixed rojo file

2.1.1

  • updated postie script

2.1.0

  • added website game pass purchase tracking support

2.0.1

  • remote configs fixes

2.0.0

  • Remote Config calls have been updated and the old calls have deprecated. Please see GA documentation for the new SDK calls and migration guide
  • A/B testing support added

1.4.2

  • improvements for business event

1.4.1

  • fix to playerRemoved function

1.4.0

  • added bindable event to listen to when player is ready (has gotten its player data loaded)

1.3.9

  • started using new bit module instead of old one

1.3.8

  • fixes for progression events

1.3.7

  • bug fix for platform name fallback option

1.3.6

  • fix for command center populated events

1.3.5

  • fixes to some types of events not being sent

1.3.4

  • fixed bug with automatic error events

1.3.3

  • fixed bug with error events not sending (another one)

1.3.2

  • fixed bug with error events not sending

1.3.1

  • fixed multi-place game bugs

1.3.0

  • added support for multi-place game sessions

1.2.13

  • changed Postie from being a script to a modulescript

1.2.12

  • added Postie module to replace invokeclient call in playerjoined

1.2.11

  • fixed playerjoined method to not wait indefinitely in some cases

1.2.10

  • fixed playerjoined method to not wait indefinitely in some cases

1.2.9

  • fixed load table bug

1.2.8

  • added missing files to rbxmx

1.2.7

  • performance to enum lookups

1.2.6

  • added limit to how many events there can max be in the events queue

1.2.5

  • added better error handling for thread task execution

1.2.4

  • added toggle function for debug logging in studio mode
  • threading performance fix

1.2.3

  • various bug fixes

1.2.2

  • bug fixes to manual configuration and initialization of sdk

1.2.1

  • updated server scripts to just be descendants of ServerScriptService and not just direct child of ServerScriptService

1.2.0

  • added enable/disable event submission function

1.1.0

  • moved settings related code in GameAnalyticsServer script into a new script called GameAnalyticsServerInitUsingSettings to allow manual initialization from own script (OPS look at new INSTALL instructions for new script)

1.0.5

  • renamed GameAnalyticsScript to GameAnalyticsServer
  • removed script location restriction on GameAnalyticsClient

1.0.4

  • small corrections

1.0.3

  • fixed automatic sending of error events
  • added script for generating rbxmx file

1.0.2

  • fixed sha256 performance issues
  • added processReceiptCallback function to use within your own processReceipt method
  • replaced all string.len and table.getn with # operator instead
  • using game:GetService() to access services instead of using game.[some_service]
  • fixed device recognition method
  • fixed automatic sending of error events

1.0.1

  • small bugs fixes

1.0.0

  • initial release

ga-sdk-roblox's People

Contributors

ambergracerblx avatar baileyeatspizza avatar beiims avatar buildthomas avatar dekkonot avatar dorin-ga avatar fablerbx avatar howmanysmall avatar lextoumbourou avatar mkargus avatar nexure avatar nimblz avatar ninjoonline avatar quenty avatar rafaskb avatar steadyon avatar tacheometry avatar the1schwartz avatar thepotato97 avatar travddm avatar xarshy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ga-sdk-roblox's Issues

SDK doesn't load: require("Table")

I'm trying to add the SDK to my game and have followed the steps to add the module, move scripts to the right place, and add my keys, but I'm getting errors related to scripts trying to require a script named Table:

10:06:53.743 - Attempted to call require with invalid argument(s).
10:06:53.743 - Stack Begin
10:06:53.744 - Script 'ServerStorage.GameAnalytics.GAResourceFlowType', Line 1
10:06:53.744 - Stack End

Contents of GAResourceFlowType:

local Table = require("Table")

return Table.ReadOnly({
    Source = "Source";
    Sink = "Sink";
})

I can't find anything called Table.

Configuring resource currencies using `setAvailableResourceCurrencies` does not work

Repo steps

  1. Configure GameAnalytics with a script instead of Settings.lua
  2. Call
GameAnalytics:setAvailableResourceCurrencies({"test"})
  1. Try to call addResourceEvent with the currency "test".
  2. Note how test is an invalid currency

Why this happens

This line of code reads from settings.AvailableResourceCurrencies, which is never written when you call GameAnalytics:setAvailableResourceCurrencies. It should be checking the internal store version instead.

if not validation:validateResourceEvent(flowType, currency, amount, itemType, itemId, settings.AvailableResourceCurrencies, settings.AvailableResourceItemTypes) then

README Documentation link is incorrect

Documentation link on the README file links to https://gameanalytics.com/docs/roblox-sdk, but should link to https://gameanalytics.com/docs/item/roblox-sdk instead. The current link leads to a 404.

Add Premium Tracking

Roblox recently released Premium Payouts. Because of this, it's now much more important to track premium players and stuff like their session length, play time, and how many active premium players are playing your game.

Use of deprecated table.getn

table.getn is deprecated. To get the length of a table, use # e.g. local t = {1,2,3} print(#t)

Offending Code

if table.getn(Settings.AvailableCustomDimensions01) > 0 then

if table.getn(Settings.AvailableCustomDimensions02) > 0 then

if table.getn(Settings.AvailableCustomDimensions03) > 0 then

if table.getn(Settings.AvailableResourceCurrencies) > 0 then

if table.getn(Settings.AvailableResourceItemTypes) > 0 then

if not allowNoValues and table.getn(arrayOfStrings) == 0 then

if maxCount > 0 and table.getn(arrayOfStrings) > maxCount then

logger:w(arrayTag .. " validation failed: array cannot exceed " .. tostring(maxCount) .. " values. It has " .. table.getn(arrayOfStrings) .. " values.")

if table.getn(array) == 0 then

if not eventArray or table.getn(eventArray) == 0 then

if table.getn(queue) == 0 then

logger:i("Event queue: Sending " .. tostring(table.getn(queue)) .. " events.")

logger:i("Event queue: " .. tostring(table.getn(queue)) .. " events sent.")

if table.getn(store.EventsQueue) < 500 then

logger:w("Event queue: " .. tostring(table.getn(queue)) .. " events sent. " .. tostring(table.getn(responseBody)) .. " events failed GA server validation.")

ProcessReceipt does not return ProductPurchaseDecision

MKT.ProcessReceipt = function(Info)

It is necessary for ProcessReceipt callbacks to explicitly return a ProductPurchaseDecision enum value or else the purchase will fail and the buyer will be refunded in a few days. This function should return Enum.ProductPurchaseDecision.PurchaseGranted when the purchase is successful. See this page for more information:

https://developer.roblox.com/api-reference/callback/MarketplaceService/ProcessReceipt

Useless init.lua

The current init.lua is just an empty module. It should be an API with a method to call the code of GameAnalyticsClient and the code of GameAnalyticsScript (nit pick, should be Server not Script) so that it's easier to integrate as a submodule.

ga:PlayerRemoved() can error if player data not loaded

If a player leaves before their data is loaded from the datastore then this line in ga:PlayerRemoved() will cause an error:

if not PlayerData.PlayerTeleporting then

There should be a check to determine if the data exists before checking the PlayerTeleporting variable.

Automatic error report sending from studio seems to be broken

Repo steps

  1. Turn on ReportErrors = true;
  2. Create an error error("Test")
  3. Watch as error does not report
  Info/GameAnalytics: Add ERROR event: {severity:error, message:error("Test"):1: Test}
  Info/GameAnalytics: Event queue: Sending 1 events.
  16:09:13.818 - Warning/GameAnalytics: Event queue: Failed to send events.
  Debug/GameAnalytics: Failed Events Call. Bad request. Response: [{"event":{"session_num":1,"severity":"error","category":"error","manufacturer":"unknown","os_version":"uwp_desktop 0.0.0","message":"error(\"Test\"):1: Test","v":2,"device":"unknown","client_ts":1551910380,"platform":"uwp_desktop","build":"alpha-1.26.2","session_id":1,"user_id":"DummyId","sdk_version":"roblox 1.2.2"},"errors":[{"error_type":"wrong_type","path":"/session_id"}]}]

I checked the API error received, and it's a 6: bad request. Not sure if this is an issue with the sandbox API, or something internal with the formatting. Will investigate further.

Use of deprecated string.len

string.len is deprecated. To get the length of a string, use # e.g. local s = "string" print(#s)

Offending Code

if string.len(m) > 8192 then

if string.len(key) > 50 then

stringLength = string.len(arrayString)

if utilities:isStringNullOrEmpty(shortString) or string.len(shortString) > 32 then

if string.find(gameKey, "^[A-Za-z0-9]+$") and string.len(gameKey) == 32 then

if string.find(secretKey, "^[A-Za-z0-9]+$") and string.len(secretKey) == 40 then

if string.find(currency, "^[A-Z]+$") and string.len(currency) == 3 then

if string.len(eventPart) == 0 or string.len(eventPart) > 64 then

if string.len(s) > 64 then

if utilities:isStringNullOrEmpty(longString) or string.len(longString) > 8192 then

if string.len(key) > 0 and configuration["value"] and client_ts_adjusted > start_ts and client_ts_adjusted < end_ts then

return (not s) or string.len(s) == 0

if not body or string.len(body) == 0 then

for i=1,String.len(hex)/2 do

if(i <= String.len(string)) then

for i=1,String.len(hex)/2 do

if PlayerData.CurrentCustomDimension01 and string.len(PlayerData.CurrentCustomDimension01) > 0 then

if PlayerData.CurrentCustomDimension02 and string.len(PlayerData.CurrentCustomDimension02) > 0 then

if PlayerData.CurrentCustomDimension03 and string.len(PlayerData.CurrentCustomDimension03) > 0 then

Add support for RoStrap

I love using this module and this tool entirely, and I do want to ensure that I'm always running the latest version with all the new additions, however going over here to Github for every new update, however small it may be, can definitely seem like a bit of a waste of time.

They have released instructions on how to actually add a module into RoStrap which can be seen here.

What this would allow is for users to just simply press a button in the RoStrap plugin and easily update to the latest version, without even needing to leave studio.

getPlatform mistakenly returns touchscreen laptops as mobile

function getPlatform()

UIS.TouchEnabled will be true for laptops with touchscreens, so the existing function will flag these devices as mobile when they're not. This can be fixed by checking if a mouse exists as well:

function getPlatform()

    if (GS:IsTenFootInterface()) then
        return "Console"
    elseif (UIS.TouchEnabled and not UIS.MouseEnabled) then
        return "Mobile"
    else
        return "Desktop"
    end
end

Add a way to determine if a player is ready

I am running into an issue where I am trying to submit events for players who have not yet loaded - I think the library is waiting for the player data to load from the datastore. Is there a way to determine if a player is ready for events, or could one be added?

I also want to add custom dimensions to each player, and this is tricky to time. Maybe a callback could be added that is triggered after the player is loaded?

Implement Game Pass Purchase Tracking for Website Purchases

I have plans of opening a pull request adding this feature sometime this week (if I remember).

My idea:

  1. On player join, check for which game passes a player owns using MarketplaceService:UserOwnsGamePassAsync() (Unfortunately developers will need to insert a list of game pass IDs into settings manually because Roblox doesn't provide an API to retreive the game's game passes)
  2. Compare owned game passes from step 1 with a list of owned game passes saved in the PlayerData data store.
  3. If there's any new game passes that weren't in the data store, send GameAnalytics the business events using MarketplaceService:GetProductInfo() to get info about the game passes
  4. Update player data (if needed)

This should be pretty easy to implement, maybe an hour at most. The biggest down side to this is that if the price changes between a player purchasing it and opening the game the event sent to GameAnalytics will be inaccurate, but this probably wouldn't happen very often.

SDK prevents developers from processing purchases

MKT.ProcessReceipt = function(Info)

MarketplaceService.ProcessReceipt is a callback that can only be registered to one function. When the GameAnalytics SDK registers a callback, any developer callbacks will be overwritten and no longer invoked. When a developer registers a callback, the GameAnalytics callback will be overwritten and no longer invoked.

The GameAnalytics SDK should require developers to manually give it purchase information -- not hook into ProcessReceipt directly -- so that it does not create conflicts like this.

Progression events are not sent if progression args are nil

The docs state that the progression02 and progression03 arguments can be nil for progression events, but if this is the case the event will silently fail and not be sent to the server. This is because line Events:427 attempts to concatenate the nil arguments into a string.

SDK accesses services as children of game instead of through GetService

In a number of places (linked below), the SDK accesses services via game.SERVICENAME instead of game:GetService("SERVICENAME"). This will break if the service names are ever changed (developer of game changed them, or the default name for the service changes). The SDK should be updated to use GetService whenever it accesses services. If a service is used multiple times in a script, it is good practice to save it in a variable at the top of the script i.e.:

local replicatedStorage = game:GetService("ReplicatedStorage")

rather than using GetService every time it is accessed.

Problem code:

if not game.ReplicatedStorage:FindFirstChild("GameAnalyticsFiltering") then

f.Parent = game.ReplicatedStorage

if not game.ReplicatedStorage:FindFirstChild("GameAnalyticsSendMessage") then

f.Parent = game.ReplicatedStorage

if not game.ReplicatedStorage:FindFirstChild("GameAnalyticsCommandCenter") then

f.Parent = game.ReplicatedStorage

local GameAnalytics = require(game.ServerStorage.GameAnalytics)

local Settings = require(game.ServerStorage.GameAnalytics.Settings)

local logger = require(game.ServerStorage.GameAnalytics.Logger)

local store = require(game.ServerStorage.GameAnalytics.Store)

local Player = game.Players:GetPlayerByUserId(Info.PlayerId)

for _, Player in pairs(game.Players:GetPlayers()) do

game.Players.PlayerAdded:Connect(function(Player)

game.Players.PlayerRemoving:Connect(function(Player)

local GameAnalyticsFiltering = game.ReplicatedStorage:WaitForChild("GameAnalyticsFiltering")

local GameAnalyticsSendMessage = game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

GameAnalyticsFiltering = GameAnalyticsFiltering or game.ReplicatedStorage:WaitForChild("GameAnalyticsFiltering")

if (not Player) or (Player.Parent ~= game.Players) then return end

-- GameAnalyticsSendMessage = GameAnalyticsSendMessage or game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

-- GameAnalyticsSendMessage = GameAnalyticsSendMessage or game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

-- GameAnalyticsSendMessage = GameAnalyticsSendMessage or game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

-- GameAnalyticsSendMessage = GameAnalyticsSendMessage or game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

-- GameAnalyticsSendMessage = GameAnalyticsSendMessage or game.ReplicatedStorage:WaitForChild("GameAnalyticsSendMessage")

GameAnalyticsCommandCenter = GameAnalyticsCommandCenter or game.ReplicatedStorage:WaitForChild("GameAnalyticsCommandCenter")

Simplify the Initialization process

It would be great if you could move code from GameAnalyticsServer to the main module. I don't see why we would have to use two server scripts to setup game analytics. The Settings table should also be moved to GameAnalyticsServerInitUsingSettings instead of being a table inside of the GameAnalytics module. At the moment every time I want to update GameAnalytics to the latest version I have to save my Settings module, replace all files with the new ones and add my settings back to the module. Ideally, there should be only 1 server script where we modify the settings which we can send to the "initialize" method. Initialization should accept all arguments which the Settings module has at the moment.

My server script should look something like this

local ServerStorage = game:GetService("ServerStorage")
local GameAnalytics = require(ServerStorage.GameAnalytics)

GameAnalytics:Initialize({
	Build = "0.1";

	GameKey = "";
	SecretKey = "";

	EnableInfoLog = true;
	EnableVerboseLog = false;
	AutomaticSendBusinessEvents = true;
	ReportErrors = true;
	
	AvailableCustomDimensions01 = {};
	AvailableCustomDimensions02 = {};
	AvailableCustomDimensions03 = {};
	AvailableResourceCurrencies = {};
	AvailableResourceItemTypes = {};
	AvailableGamepasses = {};
})

SecretKey is exposed to the client

I don't know how if this is required for GA, but this seems like a huge oversight.

I know you can use :initialize, but it still seems too easy to mess that up.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.