GithubHelp home page GithubHelp logo

ga-sdk-roblox's People

Contributors

ambergracerblx avatar baileyeatspizza avatar beiims avatar brinkokevin avatar buildthomas avatar dekkonot avatar dorin-ga avatar fablerbx avatar howmanysmall avatar lextoumbourou avatar mkargus avatar nimblz avatar ninjoonline avatar outofbears 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

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.

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.

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

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

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.

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.

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.

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

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?

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.

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 = {};
})

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.

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.

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.")

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

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")

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.

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.