mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-04-26 12:41:40 +00:00
non-user identifying analytics
This commit is contained in:
parent
fa5d1f1688
commit
cdc05e130f
5 changed files with 221 additions and 3 deletions
147
src/Analytics.hx
Normal file
147
src/Analytics.hx
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
package src;
|
||||||
|
|
||||||
|
import haxe.Json;
|
||||||
|
import src.Util;
|
||||||
|
import src.Settings;
|
||||||
|
import src.Http;
|
||||||
|
import src.Console;
|
||||||
|
|
||||||
|
typedef PayloadData = {
|
||||||
|
type:String,
|
||||||
|
payload:{
|
||||||
|
hostname:String, language:String, referrer:String, screen:String, title:String, url:String, website:String, name:String, ?data:Dynamic
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// I'm sorry to add this
|
||||||
|
// Your data is private and anonymous and we don't track you at all, I promise!
|
||||||
|
// The analytics are stored in a self hosted Umami instance inside EU.
|
||||||
|
class Analytics {
|
||||||
|
static var umami = "https://analytics.randomityguy.me/api/send";
|
||||||
|
|
||||||
|
public static function trackSingle(eventName:String) {
|
||||||
|
var p = payload(eventName, null);
|
||||||
|
var json = Json.stringify(p);
|
||||||
|
Http.post(umami, json, (b) -> {
|
||||||
|
// Console.log("Analytics suceeded: " + b.toString());
|
||||||
|
}, (e) -> {
|
||||||
|
// Console.log("Analytics failed: " + e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trackLevelPlay(levelName:String, levelFile:String) {
|
||||||
|
var p = payload("level-play", {
|
||||||
|
name: levelName,
|
||||||
|
file: levelFile
|
||||||
|
});
|
||||||
|
var json = Json.stringify(p);
|
||||||
|
Http.post(umami, json, (b) -> {
|
||||||
|
// Console.log("Analytics suceeded: " + b.toString());
|
||||||
|
}, (e) -> {
|
||||||
|
// Console.log("Analytics failed: " + e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trackLevelScore(levelName:String, levelFile:String, time:Int, oobs:Int, respawns:Int, rewind:Bool) {
|
||||||
|
var p = payload("level-score", {
|
||||||
|
name: levelName,
|
||||||
|
file: levelFile,
|
||||||
|
time: time,
|
||||||
|
oobs: oobs,
|
||||||
|
respawns: respawns,
|
||||||
|
rewind: rewind
|
||||||
|
});
|
||||||
|
var json = Json.stringify(p);
|
||||||
|
Http.post(umami, json, (b) -> {
|
||||||
|
// Console.log("Analytics suceeded: " + b.toString());
|
||||||
|
}, (e) -> {
|
||||||
|
// Console.log("Analytics failed: " + e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trackLevelQuit(levelName:String, levelFile:String, time:Int, oobs:Int, respawns:Int, rewind:Bool) {
|
||||||
|
var p = payload("level-quit", {
|
||||||
|
name: levelName,
|
||||||
|
file: levelFile,
|
||||||
|
time: time,
|
||||||
|
oobs: oobs,
|
||||||
|
respawns: respawns,
|
||||||
|
rewind: rewind
|
||||||
|
});
|
||||||
|
var json = Json.stringify(p);
|
||||||
|
Http.post(umami, json, (b) -> {
|
||||||
|
// Console.log("Analytics suceeded: " + b.toString());
|
||||||
|
}, (e) -> {
|
||||||
|
// Console.log("Analytics failed: " + e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function trackPlatformInfo() {
|
||||||
|
var p = payload("device-telemetry", {
|
||||||
|
platform: Util.getPlatform(),
|
||||||
|
screen: screen(),
|
||||||
|
});
|
||||||
|
var json = Json.stringify(p);
|
||||||
|
Http.post(umami, json, (b) -> {
|
||||||
|
// Console.log("Analytics suceeded: " + b.toString());
|
||||||
|
}, (e) -> {
|
||||||
|
// Console.log("Analytics failed: " + e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static function payload(eventName:String, eventData:Dynamic):PayloadData {
|
||||||
|
var p:PayloadData = {
|
||||||
|
type: "event",
|
||||||
|
payload: {
|
||||||
|
hostname: hostname(),
|
||||||
|
language: language(),
|
||||||
|
referrer: referrer(),
|
||||||
|
screen: screen(),
|
||||||
|
title: "MBHaxe Platinum",
|
||||||
|
url: "/",
|
||||||
|
website: "e6da43f0-fc6a-49cb-a4a9-4b7e7745e538",
|
||||||
|
name: eventName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (eventData == null)
|
||||||
|
return p;
|
||||||
|
p.payload.data = eventData;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function hostname() {
|
||||||
|
#if js
|
||||||
|
return js.Browser.window.location.hostname;
|
||||||
|
#end
|
||||||
|
#if hl
|
||||||
|
return "marbleblast.randomityguy.me";
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
static function language() {
|
||||||
|
#if js
|
||||||
|
return js.Browser.window.navigator.language;
|
||||||
|
#end
|
||||||
|
#if hl
|
||||||
|
return "en-us";
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
static function referrer() {
|
||||||
|
#if js
|
||||||
|
return js.Browser.window.document.referrer;
|
||||||
|
#end
|
||||||
|
#if hl
|
||||||
|
return "";
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
static function screen() {
|
||||||
|
#if js
|
||||||
|
return '${js.Browser.window.screen.width}x${js.Browser.window.screen.height}';
|
||||||
|
#end
|
||||||
|
#if hl
|
||||||
|
return '${Settings.optionsSettings.screenWidth}x${Settings.optionsSettings.screenHeight}';
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Http.hx
45
src/Http.hx
|
|
@ -1,6 +1,7 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
import src.Console;
|
import src.Console;
|
||||||
|
import src.Util;
|
||||||
|
|
||||||
typedef HttpRequest = {
|
typedef HttpRequest = {
|
||||||
var url:String;
|
var url:String;
|
||||||
|
|
@ -8,6 +9,8 @@ typedef HttpRequest = {
|
||||||
var errCallback:String->Void;
|
var errCallback:String->Void;
|
||||||
var cancelled:Bool;
|
var cancelled:Bool;
|
||||||
var fulfilled:Bool;
|
var fulfilled:Bool;
|
||||||
|
var post:Bool;
|
||||||
|
var postData:String;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Http {
|
class Http {
|
||||||
|
|
@ -41,7 +44,7 @@ class Http {
|
||||||
var http = new sys.Http(req.url);
|
var http = new sys.Http(req.url);
|
||||||
http.onError = (e) -> {
|
http.onError = (e) -> {
|
||||||
trace('HTTP Request Failed: ' + req.url);
|
trace('HTTP Request Failed: ' + req.url);
|
||||||
responses.add(() -> req.errCallback(e));
|
responses.add(() -> req.errCallback(e + ":" + http.responseBytes.toString()));
|
||||||
req.fulfilled = true;
|
req.fulfilled = true;
|
||||||
};
|
};
|
||||||
http.onBytes = (b) -> {
|
http.onBytes = (b) -> {
|
||||||
|
|
@ -51,9 +54,14 @@ class Http {
|
||||||
};
|
};
|
||||||
#if !MACOS_BUNDLE
|
#if !MACOS_BUNDLE
|
||||||
hl.Gc.blocking(true); // Wtf is this shit
|
hl.Gc.blocking(true); // Wtf is this shit
|
||||||
|
if (req.post) {
|
||||||
|
http.setHeader('User-Agent', 'MBHaxe/1.0 ${Util.getPlatform()}');
|
||||||
|
http.setHeader('Content-Type', "application/json"); // support json data only (for now)
|
||||||
|
http.setPostData(req.postData);
|
||||||
|
}
|
||||||
trace('HTTP Request: ' + req.url);
|
trace('HTTP Request: ' + req.url);
|
||||||
#end
|
#end
|
||||||
http.request(false);
|
http.request(req.post);
|
||||||
#if !MACOS_BUNDLE
|
#if !MACOS_BUNDLE
|
||||||
hl.Gc.blocking(false);
|
hl.Gc.blocking(false);
|
||||||
#end
|
#end
|
||||||
|
|
@ -68,7 +76,9 @@ class Http {
|
||||||
callback: callback,
|
callback: callback,
|
||||||
errCallback: errCallback,
|
errCallback: errCallback,
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
fulfilled: false
|
fulfilled: false,
|
||||||
|
post: false,
|
||||||
|
postData: null,
|
||||||
};
|
};
|
||||||
#if sys
|
#if sys
|
||||||
requests.add(req);
|
requests.add(req);
|
||||||
|
|
@ -80,6 +90,35 @@ class Http {
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns HTTPRequest on sys, Int on js
|
||||||
|
public static function post(url:String, postData:String, callback:haxe.io.Bytes->Void, errCallback:String->Void) {
|
||||||
|
var req = {
|
||||||
|
url: url,
|
||||||
|
callback: callback,
|
||||||
|
errCallback: errCallback,
|
||||||
|
cancelled: false,
|
||||||
|
fulfilled: false,
|
||||||
|
post: true,
|
||||||
|
postData: postData
|
||||||
|
};
|
||||||
|
#if sys
|
||||||
|
requests.add(req);
|
||||||
|
return req;
|
||||||
|
#else
|
||||||
|
return js.Browser.window.setTimeout(() -> {
|
||||||
|
js.Browser.window.fetch(url,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"User-Agent": js.Browser.window.navigator.userAgent,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: postData
|
||||||
|
}).then(r -> r.arrayBuffer().then(b -> callback(haxe.io.Bytes.ofData(b))), e -> errCallback(e.toString()));
|
||||||
|
}, 75);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
public static function loop() {
|
public static function loop() {
|
||||||
#if sys
|
#if sys
|
||||||
var resp = responses.pop(false);
|
var resp = responses.pop(false);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import src.Settings;
|
||||||
import src.Console;
|
import src.Console;
|
||||||
import src.Debug;
|
import src.Debug;
|
||||||
import src.Gamepad;
|
import src.Gamepad;
|
||||||
|
import src.Analytics;
|
||||||
|
|
||||||
@:publicFields
|
@:publicFields
|
||||||
class MarbleGame {
|
class MarbleGame {
|
||||||
|
|
@ -170,6 +171,9 @@ class MarbleGame {
|
||||||
|
|
||||||
Window.getInstance().removeEventTarget(@:privateAccess Key.onEvent);
|
Window.getInstance().removeEventTarget(@:privateAccess Key.onEvent);
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
Analytics.trackSingle("game-start");
|
||||||
|
Analytics.trackPlatformInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(dt:Float) {
|
public function update(dt:Float) {
|
||||||
|
|
@ -257,6 +261,9 @@ class MarbleGame {
|
||||||
public function quitMission() {
|
public function quitMission() {
|
||||||
Console.log("Quitting mission");
|
Console.log("Quitting mission");
|
||||||
world.setCursorLock(false);
|
world.setCursorLock(false);
|
||||||
|
var stats = Settings.levelStatistics[world.mission.path];
|
||||||
|
Analytics.trackLevelQuit(world.mission.title, world.mission.path, Std.int(world.timeState.timeSinceLoad * 1000), stats.oobs, stats.respawns,
|
||||||
|
Settings.optionsSettings.rewindEnabled);
|
||||||
paused = false;
|
paused = false;
|
||||||
if (world.isWatching) {
|
if (world.isWatching) {
|
||||||
#if !js
|
#if !js
|
||||||
|
|
@ -284,12 +291,14 @@ class MarbleGame {
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
world.dispose();
|
world.dispose();
|
||||||
}
|
}
|
||||||
|
Analytics.trackLevelPlay(mission.title, mission.path);
|
||||||
world = new MarbleWorld(scene, scene2d, mission, toRecord);
|
world = new MarbleWorld(scene, scene2d, mission, toRecord);
|
||||||
world.init();
|
world.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function watchMissionReplay(mission:Mission, replay:Replay) {
|
public function watchMissionReplay(mission:Mission, replay:Replay) {
|
||||||
canvas.clearContent();
|
canvas.clearContent();
|
||||||
|
Analytics.trackSingle("replay-watch");
|
||||||
world = new MarbleWorld(scene, scene2d, mission);
|
world = new MarbleWorld(scene, scene2d, mission);
|
||||||
world.replay = replay;
|
world.replay = replay;
|
||||||
world.isWatching = true;
|
world.isWatching = true;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ import src.ResourceLoaderWorker;
|
||||||
import haxe.io.Path;
|
import haxe.io.Path;
|
||||||
import src.Console;
|
import src.Console;
|
||||||
import src.Gamepad;
|
import src.Gamepad;
|
||||||
|
import src.Analytics;
|
||||||
|
|
||||||
class MarbleWorld extends Scheduler {
|
class MarbleWorld extends Scheduler {
|
||||||
public var collisionWorld:CollisionWorld;
|
public var collisionWorld:CollisionWorld;
|
||||||
|
|
@ -1533,6 +1534,15 @@ class MarbleWorld extends Scheduler {
|
||||||
this.finishYaw = this.marble.camera.CameraYaw;
|
this.finishYaw = this.marble.camera.CameraYaw;
|
||||||
this.finishPitch = this.marble.camera.CameraPitch;
|
this.finishPitch = this.marble.camera.CameraPitch;
|
||||||
displayAlert("Congratulations! You've finished!");
|
displayAlert("Congratulations! You've finished!");
|
||||||
|
if (!Settings.levelStatistics.exists(mission.path)) {
|
||||||
|
Settings.levelStatistics.set(mission.path, {
|
||||||
|
oobs: 0,
|
||||||
|
respawns: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Analytics.trackLevelScore(mission.title, mission.path, Std.int(finishTime.gameplayClock * 1000), Settings.levelStatistics[mission.path].oobs,
|
||||||
|
Settings.levelStatistics[mission.path].respawns, Settings.optionsSettings.rewindEnabled);
|
||||||
if (!this.isWatching)
|
if (!this.isWatching)
|
||||||
this.schedule(this.timeState.currentAttemptTime + 2, () -> cast showFinishScreen());
|
this.schedule(this.timeState.currentAttemptTime + 2, () -> cast showFinishScreen());
|
||||||
// Stop the ongoing sounds
|
// Stop the ongoing sounds
|
||||||
|
|
|
||||||
13
src/Util.hx
13
src/Util.hx
|
|
@ -440,4 +440,17 @@ class Util {
|
||||||
|
|
||||||
return totBytes.getBytes().toString();
|
return totBytes.getBytes().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getPlatform() {
|
||||||
|
#if js
|
||||||
|
return js.Browser.navigator.platform;
|
||||||
|
#end
|
||||||
|
#if hl
|
||||||
|
#if MACOS_BUNDLE
|
||||||
|
return "MacOS";
|
||||||
|
#else
|
||||||
|
return "Windows";
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue