mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
non-user identifying analytics
This commit is contained in:
parent
3dbee00b0b
commit
177e7903b2
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;
|
||||
|
||||
import src.Console;
|
||||
import src.Util;
|
||||
|
||||
typedef HttpRequest = {
|
||||
var url:String;
|
||||
|
|
@ -8,6 +9,8 @@ typedef HttpRequest = {
|
|||
var errCallback:String->Void;
|
||||
var cancelled:Bool;
|
||||
var fulfilled:Bool;
|
||||
var post:Bool;
|
||||
var postData:String;
|
||||
};
|
||||
|
||||
class Http {
|
||||
|
|
@ -41,7 +44,7 @@ class Http {
|
|||
var http = new sys.Http(req.url);
|
||||
http.onError = (e) -> {
|
||||
trace('HTTP Request Failed: ' + req.url);
|
||||
responses.add(() -> req.errCallback(e));
|
||||
responses.add(() -> req.errCallback(e + ":" + http.responseBytes.toString()));
|
||||
req.fulfilled = true;
|
||||
};
|
||||
http.onBytes = (b) -> {
|
||||
|
|
@ -53,7 +56,12 @@ class Http {
|
|||
hl.Gc.blocking(true); // Wtf is this shit
|
||||
trace('HTTP Request: ' + req.url);
|
||||
#end
|
||||
http.request(false);
|
||||
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);
|
||||
}
|
||||
http.request(req.post);
|
||||
#if !MACOS_BUNDLE
|
||||
hl.Gc.blocking(false);
|
||||
#end
|
||||
|
|
@ -68,7 +76,9 @@ class Http {
|
|||
callback: callback,
|
||||
errCallback: errCallback,
|
||||
cancelled: false,
|
||||
fulfilled: false
|
||||
fulfilled: false,
|
||||
post: false,
|
||||
postData: null,
|
||||
};
|
||||
#if sys
|
||||
requests.add(req);
|
||||
|
|
@ -80,6 +90,35 @@ class Http {
|
|||
#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() {
|
||||
#if sys
|
||||
var resp = responses.pop(false);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import src.Settings;
|
|||
import src.Console;
|
||||
import src.Debug;
|
||||
import src.Gamepad;
|
||||
import src.Analytics;
|
||||
import src.PreviewWorld;
|
||||
|
||||
@:publicFields
|
||||
|
|
@ -173,6 +174,9 @@ class MarbleGame {
|
|||
|
||||
Window.getInstance().removeEventTarget(@:privateAccess Key.onEvent);
|
||||
#end
|
||||
|
||||
Analytics.trackSingle("game-start");
|
||||
Analytics.trackPlatformInfo();
|
||||
}
|
||||
|
||||
public function update(dt:Float) {
|
||||
|
|
@ -282,6 +286,9 @@ class MarbleGame {
|
|||
var missionType = world.mission.type;
|
||||
var isNotCustom = !world.mission.isClaMission && !world.mission.isCustom;
|
||||
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);
|
||||
world.dispose();
|
||||
world = null;
|
||||
paused = false;
|
||||
|
|
@ -310,6 +317,7 @@ class MarbleGame {
|
|||
if (world != null) {
|
||||
world.dispose();
|
||||
}
|
||||
Analytics.trackLevelPlay(mission.title, mission.path);
|
||||
world = new MarbleWorld(scene, scene2d, mission, toRecord);
|
||||
world.init();
|
||||
}
|
||||
|
|
@ -317,6 +325,7 @@ class MarbleGame {
|
|||
public function watchMissionReplay(mission:Mission, replay:Replay) {
|
||||
canvas.clearContent();
|
||||
destroyPreviewWorld();
|
||||
Analytics.trackSingle("replay-watch");
|
||||
world = new MarbleWorld(scene, scene2d, mission);
|
||||
world.replay = replay;
|
||||
world.isWatching = true;
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ import modes.GameMode;
|
|||
import modes.NullMode;
|
||||
import modes.GameMode.GameModeFactory;
|
||||
import src.Renderer;
|
||||
import src.Analytics;
|
||||
|
||||
class MarbleWorld extends Scheduler {
|
||||
public var collisionWorld:CollisionWorld;
|
||||
|
|
@ -1418,6 +1419,15 @@ class MarbleWorld extends Scheduler {
|
|||
this.finishYaw = this.marble.camera.CameraYaw;
|
||||
this.finishPitch = this.marble.camera.CameraPitch;
|
||||
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) {
|
||||
var myScore = {
|
||||
name: "Player",
|
||||
|
|
|
|||
13
src/Util.hx
13
src/Util.hx
|
|
@ -445,4 +445,17 @@ class Util {
|
|||
|
||||
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