You can add compatibility for it to send error reports to the server. If I added this as an in-built function, would people use it?
- Yes
- No
0 voters
You can add compatibility for it to send error reports to the server. If I added this as an in-built function, would people use it?
0 voters
wow thanks for making this! I’ll use it and I think several of my friends will be interested too!
This is how my current one works and it is very helpful for finding bugs. Please do!
Its also important to mention that there can only be 100,000 unique event_ids per day, so nesting all the information into the event_id may not be a good idea.
Just to clarify what he means by this is not that there is a limit of 100k events a day.
For resource events (the only type this tutorial shows how to use which you can affect in terms of event ids) these are two events with separate ids and count as two to your 100k limit
Analytics.RecordTransaction(Player, 500, “Pet:Dragon”)
Analytics.RecordTransaction(Player, 1000000, “Pet:UhhhNotADragon”)
However, if I was to fire the first one twice, they’d have the same id, and count as one towards your 100k limit.
There is no limit to the amount of data you can send to game analytics, and it’s all free!
no clue how they make money plz don’t go out of business
Oh yay game analytics, I love using it. All the custom events are super useful, for example I keep track of average times for each level in my game (ie I know that it takes a user on average 110 min to reach level 2 etc) and then you can graph those, and like do cool things like compare different weeks worth of data to eachother etc. Although I use @ByDefault’s Module as I found it works well enough but digging through yours seems pretty well written as well. One thing I would like to note about anyone putting this in their place, while encoding strings to be sent to the game analytics server, server CPU usage spikes with lots of pending requests. To combat this I modified by sha2_256 file to include some heartbeat waits, this reduces lag spikes significantly to like 5% CPU usage, if you want to see how bad it is watch F9 Server Script Usage, spikes to 50% sometimes causing replication issues depending on whatever your game analytics refresh is set to:
local Bit = require(lockbox.util.bit);
local String = string;
local Math = math;
local Queue = require(lockbox.util.queue);
local CONSTANTS = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
local AND = Bit.band;
local OR = Bit.bor;
local NOT = Bit.bnot;
local XOR = Bit.bxor;
local LROT = Bit.lrotate;
local RROT = Bit.rrotate;
local LSHIFT = Bit.lshift;
local RSHIFT = Bit.rshift;
--SHA2 is big-endian
local bytes2word = function(b0,b1,b2,b3)
local i = b0; i = LSHIFT(i,8);
i = OR(i,b1); i = LSHIFT(i,8);
i = OR(i,b2); i = LSHIFT(i,8);
i = OR(i,b3);
return i;
end
local word2bytes = function(word)
local b0,b1,b2,b3;
b3 = AND(word,0xFF); word = RSHIFT(word,8);
b2 = AND(word,0xFF); word = RSHIFT(word,8);
b1 = AND(word,0xFF); word = RSHIFT(word,8);
b0 = AND(word,0xFF);
return b0,b1,b2,b3;
end
local bytes2dword = function(b0,b1,b2,b3,b4,b5,b6,b7)
local i = bytes2word(b0,b1,b2,b3);
local j = bytes2word(b4,b5,b6,b7);
return (i*0x100000000)+j;
end
local dword2bytes = function(i)
local b4,b5,b6,b7 = word2bytes(i);
local b0,b1,b2,b3 = word2bytes(Math.floor(i/0x100000000));
return b0,b1,b2,b3,b4,b5,b6,b7;
end
local SHA2_256 = function()
local queue = Queue();
local h0 = 0x6a09e667;
local h1 = 0xbb67ae85;
local h2 = 0x3c6ef372;
local h3 = 0xa54ff53a;
local h4 = 0x510e527f;
local h5 = 0x9b05688c;
local h6 = 0x1f83d9ab;
local h7 = 0x5be0cd19;
local public = {};
local processBlock = function()
local a = h0;
local b = h1;
local c = h2;
local d = h3;
local e = h4;
local f = h5;
local g = h6;
local h = h7;
local w = {};
for i=0,15 do
w[i] = bytes2word(queue.pop(),queue.pop(),queue.pop(),queue.pop());
end
game:GetService("RunService").Heartbeat:Wait()
for i=16,63 do
if i%20 == 0 then
game:GetService("RunService").Heartbeat:Wait()
end
local s0 = XOR(RROT(w[i-15],7), XOR(RROT(w[i-15],18), RSHIFT(w[i-15],3)));
local s1 = XOR(RROT(w[i-2],17), XOR(RROT(w[i-2], 19), RSHIFT(w[i-2],10)));
w[i] = AND(w[i-16] + s0 + w[i-7] + s1, 0xFFFFFFFF);
end
for i=0,63 do
if i%12 == 0 then
game:GetService("RunService").Heartbeat:Wait()
end
local s1 = XOR(RROT(e,6), XOR(RROT(e,11),RROT(e,25)));
local ch = XOR(AND(e,f), AND(NOT(e),g));
local temp1 = h + s1 + ch + CONSTANTS[i+1] + w[i];
local s0 = XOR(RROT(a,2), XOR(RROT(a,13), RROT(a,22)));
local maj = XOR(AND(a,b), XOR(AND(a,c), AND(b,c)));
local temp2 = s0 + maj;
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
end
h0 = AND(h0 + a, 0xFFFFFFFF);
h1 = AND(h1 + b, 0xFFFFFFFF);
h2 = AND(h2 + c, 0xFFFFFFFF);
h3 = AND(h3 + d, 0xFFFFFFFF);
h4 = AND(h4 + e, 0xFFFFFFFF);
h5 = AND(h5 + f, 0xFFFFFFFF);
h6 = AND(h6 + g, 0xFFFFFFFF);
h7 = AND(h7 + h, 0xFFFFFFFF);
game:GetService("RunService").Heartbeat:Wait()
end
public.init = function()
queue.reset();
h0 = 0x6a09e667;
h1 = 0xbb67ae85;
h2 = 0x3c6ef372;
h3 = 0xa54ff53a;
h4 = 0x510e527f;
h5 = 0x9b05688c;
h6 = 0x1f83d9ab;
h7 = 0x5be0cd19;
return public;
end
public.update = function(bytes)
for b in bytes do
queue.push(b);
if queue.size() >= 64 then processBlock(); end
end
return public;
end
public.finish = function()
local bits = queue.getHead() * 8;
queue.push(0x80);
while ((queue.size()+7) % 64) < 63 do
queue.push(0x00);
end
local b0,b1,b2,b3,b4,b5,b6,b7 = dword2bytes(bits);
queue.push(b0);
queue.push(b1);
queue.push(b2);
queue.push(b3);
queue.push(b4);
queue.push(b5);
queue.push(b6);
queue.push(b7);
while queue.size() > 0 do
processBlock();
end
return public;
end
public.asBytes = function()
local b0, b1, b2, b3 = word2bytes(h0);
local b4, b5, b6, b7 = word2bytes(h1);
local b8, b9,b10,b11 = word2bytes(h2);
local b12,b13,b14,b15 = word2bytes(h3);
local b16,b17,b18,b19 = word2bytes(h4);
local b20,b21,b22,b23 = word2bytes(h5);
local b24,b25,b26,b27 = word2bytes(h6);
local b28,b29,b30,b31 = word2bytes(h7);
return { b0, b1, b2, b3, b4, b5, b6, b7, b8, b9,b10,b11,b12,b13,b14,b15
,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25,b26,b27,b28,b29,b30,b31};
end
public.asHex = function()
local b0, b1, b2, b3 = word2bytes(h0);
local b4, b5, b6, b7 = word2bytes(h1);
local b8, b9,b10,b11 = word2bytes(h2);
local b12,b13,b14,b15 = word2bytes(h3);
local b16,b17,b18,b19 = word2bytes(h4);
local b20,b21,b22,b23 = word2bytes(h5);
local b24,b25,b26,b27 = word2bytes(h6);
local b28,b29,b30,b31 = word2bytes(h7);
local fmt = "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
return String.format(fmt, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9,b10,b11,b12,b13,b14,b15
,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25,b26,b27,b28,b29,b30,b31);
end
return public;
end
return SHA2_256;
One thing I do also is send any local errors to the server, then the server sends those local errors to game analytics, so I can keep track of local player errors without asking people to open up their F9.
Thanks man.
I’ll add that + bug catching support to my module tomorrow
I really don’t like how the credentials are stored in the same ModuleScript that is given to clients. Why not have those in a separate “Credentials” script that can be placed in ServerStorage, since only the server needs access to them?
I updated my version to do Analytics.ServerInit(GameKey, SecretKey).
Been rushed for time so didn’t publish the changes (my version is different to the public ones). Will do it today however, thanks for reminding me
And I did it. Highly recommend you use the new script!
Figured it out. Had to hard-code the keys into your script. Just putting them into the initialization function doesn’t work.
Right now it’s
PlayerData[Player.Name] = Functions.AnnotateEvent:InvokeClient(Player)
PlayerJoinTimes[Player.Name] = os.time()
And your diagram infers that’s right, but I assume that’s not what you mean?
What’s the issue too, please?
I kept getting errors when the player left saying that the player never had a PlayerJoinTime.
Also I added a :WaitForChild() between Functions and AnnotateEvent because I was getting race conditions.
Thanks, I implemented both these changes.
Still having trouble. I can’t get it to work. I have HTTP enabled and the correct keys where they should be. The site doesn’t show anything an no errors.
There is a delay on the real time display of 15-45 minutes. Is it printing out the right things?
It’s been about 40 minutes and there’s 0 data. Even the ‘Live feed’ for Latest 10 processed events is blank. The script appears to be running without issue.