Optimized data storing and history saving

This commit is contained in:
dan63047 2023-07-18 20:53:43 +03:00
parent 501832c9aa
commit 248b708276
2 changed files with 88 additions and 77 deletions

View File

@ -31,7 +31,8 @@ const Map<String, double> rankCutoffs = {
"c": 0.9, "c": 0.9,
"c-": 0.95, "c-": 0.95,
"d+": 0.975, "d+": 0.975,
"d": 1 "d": 1,
"z": -1
}; };
const Map<String, Color> rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:418 const Map<String, Color> rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:418
'x': Color(0xFFFF45FF), 'x': Color(0xFFFF45FF),
@ -85,7 +86,7 @@ class TetrioPlayer {
late bool verified; late bool verified;
bool? badstanding; bool? badstanding;
String? botmaster; String? botmaster;
late Connections connections; Connections? connections;
late TetraLeagueAlpha tlSeason1; late TetraLeagueAlpha tlSeason1;
List<RecordSingle?> sprint = []; List<RecordSingle?> sprint = [];
List<RecordSingle?> blitz = []; List<RecordSingle?> blitz = [];
@ -122,34 +123,30 @@ class TetrioPlayer {
double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1; double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1;
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime) { TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime, String id, String nick) {
//developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio"); //developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio");
userId = json['_id']; userId = id;
username = json['username']; username = nick;
state = stateTime; state = stateTime;
if (json['role'] == "retrived from p1nkl0bst3r api"){ //i fucked my own db lol i remove it later
role = "p1nkl0bst3r";
}else{
role = json['role']; role = json['role'];
}
registrationTime = json['ts'] != null ? DateTime.parse(json['ts']) : null; registrationTime = json['ts'] != null ? DateTime.parse(json['ts']) : null;
if (json['badges'] != null) { if (json['badges'] != null) {
json['badges'].forEach((v) { json['badges'].forEach((v) {
badges.add(Badge.fromJson(v)); badges.add(Badge.fromJson(v));
}); });
} }
xp = json['xp'].toDouble(); xp = json['xp'] != null ? json['xp'].toDouble() : -1;
gamesPlayed = json['gamesplayed']; gamesPlayed = json['gamesplayed'] ?? -1;
gamesWon = json['gameswon']; gamesWon = json['gameswon'] ?? -1;
gameTime = doubleSecondsToDuration(json['gametime'].toDouble()); gameTime = json['gametime'] != null && json['gametime'] != -1 ? doubleSecondsToDuration(json['gametime'].toDouble()) : const Duration(seconds: -1);
country = json['country']; country = json['country'];
supporterTier = json['supporter_tier']; supporterTier = json['supporter_tier'] ?? 0;
verified = json['verified']; verified = json['verified'] ?? false;
tlSeason1 = TetraLeagueAlpha.fromJson(json['league'], stateTime); tlSeason1 = TetraLeagueAlpha.fromJson(json['league'], stateTime);
avatarRevision = json['avatar_revision']; avatarRevision = json['avatar_revision'];
bannerRevision = json['banner_revision']; bannerRevision = json['banner_revision'];
bio = json['bio']; bio = json['bio'];
connections = Connections.fromJson(json['connections']); if (json['connections'] != null && json['connections'].isNotEmpty) connections = Connections.fromJson(json['connections']);
distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null; distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null;
friendCount = json['friend_count'] ?? 0; friendCount = json['friend_count'] ?? 0;
badstanding = json['badstanding']; badstanding = json['badstanding'];
@ -158,25 +155,25 @@ class TetrioPlayer {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = userId; // data['_id'] = userId;
data['username'] = username; // data['username'] = username;
data['role'] = role; data['role'] = role;
if (registrationTime != null) data['ts'] = registrationTime?.toString(); if (registrationTime != null) data['ts'] = registrationTime?.toString();
data['badges'] = badges.map((v) => v.toJson()).toList(); if (badges.isNotEmpty) data['badges'] = badges.map((v) => v.toJson()).toList();
data['xp'] = xp; if (xp >= 0) data['xp'] = xp;
data['gamesplayed'] = gamesPlayed; if (gamesPlayed >= 0) data['gamesplayed'] = gamesPlayed;
data['gameswon'] = gamesWon; if (gamesWon >= 0) data['gameswon'] = gamesWon;
data['gametime'] = gameTime.inMicroseconds / 1000000; if (!gameTime.isNegative) data['gametime'] = gameTime.inMicroseconds / 1000000;
if (country != null) data['country'] = country; if (country != null) data['country'] = country;
data['supporter_tier'] = supporterTier; if (supporterTier > 0) data['supporter_tier'] = supporterTier;
data['verified'] = verified; if (verified) data['verified'] = verified;
data['league'] = tlSeason1.toJson(); data['league'] = tlSeason1.toJson();
if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson(); if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson();
if (avatarRevision != null) data['avatar_revision'] = avatarRevision; if (avatarRevision != null) data['avatar_revision'] = avatarRevision;
if (bannerRevision != null) data['banner_revision'] = bannerRevision; if (bannerRevision != null) data['banner_revision'] = bannerRevision;
if (data['bio'] != null) data['bio'] = bio; if (bio != null) data['bio'] = bio;
data['connections'] = connections.toJson(); if (connections != null) data['connections'] = connections!.toJson();
data['friend_count'] = friendCount; if (friendCount > 0) data['friend_count'] = friendCount;
if (badstanding != null) data['badstanding'] = badstanding; if (badstanding != null) data['badstanding'] = badstanding;
if (botmaster != null) data['botmaster'] = botmaster; if (botmaster != null) data['botmaster'] = botmaster;
//developer.log("TetrioPlayer.toJson: $data", name: "data_objects/tetrio"); //developer.log("TetrioPlayer.toJson: $data", name: "data_objects/tetrio");
@ -741,7 +738,7 @@ class TetraLeagueAlpha {
this.pps, this.pps,
this.vs, this.vs,
this.records}){ this.records}){
nerdStats = (apm != null && pps != null && apm != null) ? NerdStats(apm!, pps!, vs!) : null; nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null;
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null;
playstyle =(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null; playstyle =(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null;
} }
@ -750,29 +747,28 @@ class TetraLeagueAlpha {
TetraLeagueAlpha.fromJson(Map<String, dynamic> json, ts) { TetraLeagueAlpha.fromJson(Map<String, dynamic> json, ts) {
timestamp = ts; timestamp = ts;
gamesPlayed = json['gamesplayed']; gamesPlayed = json['gamesplayed'] ?? 0;
gamesWon = json['gameswon']; gamesWon = json['gameswon'] ?? 0;
rating = json['rating'].toDouble(); rating = json['rating'] != null ? json['rating'].toDouble() : -1;
glicko = json['glicko']?.toDouble(); glicko = json['glicko']?.toDouble();
rd = json['rd']?.toDouble(); rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd;
rank = json['rank']; rank = json['rank'] != null ? json['rank']!.toString() : 'z';
bestRank = json['bestrank'].toString(); bestRank = json['bestrank'] != null ? json['bestrank']!.toString() : 'z';
apm = json['apm']?.toDouble(); apm = json['apm'] != null ? json['apm']!.toDouble() : null;
pps = json['pps']?.toDouble(); pps = json['pps'] != null ? json['pps']!.toDouble() : null;
vs = json['vs']?.toDouble(); vs = json['vs'] != null ? json['vs']!.toDouble() : null;
decaying = json['decaying']; decaying = json['decaying'] ?? false;
standing = json['standing']; standing = json['standing'] ?? -1;
percentile = json['percentile'].toDouble(); percentile = json['percentile'] != null ? json['percentile'].toDouble() : rankCutoffs[rank];
standingLocal = json['standing_local']; standingLocal = json['standing_local'] ?? -1;
prevRank = json['prev_rank']; prevRank = json['prev_rank'];
prevAt = json['prev_at']; prevAt = json['prev_at'] ?? -1;
nextRank = json['next_rank']; nextRank = json['next_rank'];
nextAt = json['next_at']; nextAt = json['next_at'] ?? -1;
percentileRank = json['percentile_rank']; percentileRank = json['percentile_rank'] ?? rank;
nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null; nerdStats = (apm != null && pps != null && vs != null) ? NerdStats(apm!, pps!, vs!) : null;
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null; estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null;
playstyle = playstyle = (nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null;
(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null;
} }
@override @override
@ -784,25 +780,25 @@ class TetraLeagueAlpha {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{}; final Map<String, dynamic> data = <String, dynamic>{};
data['gamesplayed'] = gamesPlayed; if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed;
data['gameswon'] = gamesWon; if (gamesWon > 0) data['gameswon'] = gamesWon;
data['rating'] = rating; if (rating >= 0) data['rating'] = rating;
if (glicko != null) data['glicko'] = glicko; if (glicko != null) data['glicko'] = glicko;
if (rd != null) data['rd'] = rd; if (rd != null && rd != noTrRd) data['rd'] = rd;
data['rank'] = rank; if (rank != 'z') data['rank'] = rank;
data['bestrank'] = bestRank; if (bestRank != 'z') data['bestrank'] = bestRank;
if (apm != null) data['apm'] = apm; if (apm != null) data['apm'] = apm;
if (pps != null) data['pps'] = pps; if (pps != null) data['pps'] = pps;
if (vs != null) data['vs'] = vs; if (vs != null) data['vs'] = vs;
data['decaying'] = decaying; if (decaying) data['decaying'] = decaying;
data['standing'] = standing; if (standing >= 0) data['standing'] = standing;
data['percentile'] = percentile; if (!rankCutoffs.containsValue(percentile)) data['percentile'] = percentile;
data['standing_local'] = standingLocal; if (standingLocal >= 0) data['standing_local'] = standingLocal;
if (prevRank != null) data['prev_rank'] = prevRank; if (prevRank != null) data['prev_rank'] = prevRank;
data['prev_at'] = prevAt; if (prevAt >= 0) data['prev_at'] = prevAt;
if (nextRank != null) data['next_rank'] = nextRank; if (nextRank != null) data['next_rank'] = nextRank;
data['next_at'] = nextAt; if (nextAt >= 0) data['next_at'] = nextAt;
data['percentile_rank'] = percentileRank; if (percentileRank != rank) data['percentile_rank'] = percentileRank;
return data; return data;
} }
} }

View File

@ -117,15 +117,30 @@ class TetrioService extends DB {
xp: -1, xp: -1,
supporterTier: 0, supporterTier: 0,
verified: false, verified: false,
connections: Connections(), connections: null,
tlSeason1: TetraLeagueAlpha(timestamp: DateTime.parse(entry[9]), apm: entry[6], pps: entry[7], vs: entry[8], glicko: entry[4], rd: noTrRd, gamesPlayed: entry[1], gamesWon: entry[2], bestRank: "z", decaying: false, rating: entry[3], rank: entry[5], percentileRank: entry[5], percentile: rankCutoffs[entry[5]]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1), tlSeason1: TetraLeagueAlpha(timestamp: DateTime.parse(entry[9]), apm: entry[6] != '' ? entry[6] : null, pps: entry[7] != '' ? entry[7] : null, vs: entry[8] != '' ? entry[8] : null, glicko: entry[4], rd: noTrRd, gamesPlayed: entry[1], gamesWon: entry[2], bestRank: "z", decaying: false, rating: entry[3], rank: entry[5], percentileRank: entry[5], percentile: rankCutoffs[entry[5]]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
sprint: [], sprint: [],
blitz: [] blitz: []
); );
history.add(state); history.add(state);
// _players.addEntries({state.userId: [state]}.entries);
await storeState(state, isFromHistory: true);
} }
ensureDbIsOpen();
final db = getDatabaseOrThrow();
late List<TetrioPlayer> states;
try{
states = _players[id]!;
}catch(e){
var player = await fetchPlayer(id);
await createPlayer(player);
states = _players[id]!;
}
states.insertAll(0, history.reversed);
final Map<String, dynamic> statesJson = {};
for (var e in states) {
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
}
await db.update(tetrioUsersTable, {idCol: id, nickCol: nick, statesCol: jsonEncode(statesJson)}, where: '$idCol = ?', whereArgs: [id]);
_tetrioStreamController.add(_players);
return history; return history;
} }
else { else {
@ -270,7 +285,7 @@ class TetrioService extends DB {
if (results.isNotEmpty) { if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
} }
final Map<String, dynamic> statesJson = {tetrioPlayer.state.millisecondsSinceEpoch.toString(): tetrioPlayer.toJson()}; final Map<String, dynamic> statesJson = {(tetrioPlayer.state.millisecondsSinceEpoch ~/ 1000).toString(): tetrioPlayer.toJson()};
db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}); db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)});
_players.addEntries({ _players.addEntries({
tetrioPlayer.userId: [tetrioPlayer] tetrioPlayer.userId: [tetrioPlayer]
@ -319,7 +334,7 @@ class TetrioService extends DB {
} }
} }
Future<void> storeState(TetrioPlayer tetrioPlayer, {bool isFromHistory = false}) async { Future<void> storeState(TetrioPlayer tetrioPlayer) async {
ensureDbIsOpen(); ensureDbIsOpen();
final db = getDatabaseOrThrow(); final db = getDatabaseOrThrow();
late List<TetrioPlayer> states; late List<TetrioPlayer> states;
@ -330,15 +345,15 @@ class TetrioService extends DB {
await createPlayer(tetrioPlayer); await createPlayer(tetrioPlayer);
states = await getPlayer(tetrioPlayer.userId); states = await getPlayer(tetrioPlayer.userId);
} }
bool test = isFromHistory ? _players[tetrioPlayer.userId]!.last.checkForRetrivedHistory(tetrioPlayer) : _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer); bool test = _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer);
if (test == false) isFromHistory ? states.insert(0, tetrioPlayer) : states.add(tetrioPlayer); if (test == false) states.add(tetrioPlayer);
final Map<String, dynamic> statesJson = {}; final Map<String, dynamic> statesJson = {};
for (var e in states) { for (var e in states) {
statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries); statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
} }
await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}, await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]); where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
isFromHistory ? _players[tetrioPlayer.userId]!.insert(0, tetrioPlayer) : _players[tetrioPlayer.userId]!.add(tetrioPlayer); _players[tetrioPlayer.userId]!.add(tetrioPlayer);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
@ -369,7 +384,7 @@ class TetrioService extends DB {
} else { } else {
dynamic rawStates = results.first['jsonStates'] as String; dynamic rawStates = results.first['jsonStates'] as String;
rawStates = json.decode(rawStates); rawStates = json.decode(rawStates);
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k))))); rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), id, results.first[nickCol] as String)));
_players.removeWhere((key, value) => key == id); _players.removeWhere((key, value) => key == id);
_players.addEntries({states.last.userId: states}.entries); _players.addEntries({states.last.userId: states}.entries);
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
@ -395,9 +410,9 @@ class TetrioService extends DB {
final response = await http.get(url); final response = await http.get(url);
if (response.statusCode == 200) { if (response.statusCode == 200) {
if (jsonDecode(response.body)['success']) { var json = jsonDecode(response.body);
TetrioPlayer player = TetrioPlayer.fromJson( if (json['success']) {
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true)); TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username']);
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud"); developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player; _playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
return player; return player;
@ -421,7 +436,7 @@ class TetrioService extends DB {
// what the fuck am i doing here? // what the fuck am i doing here?
var test = json.decode(row['jsonStates'] as String); var test = json.decode(row['jsonStates'] as String);
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k))))); test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), row[idCol] as String, row[nickCol] as String)));
data.addEntries({states.last.userId: states}.entries); data.addEntries({states.last.userId: states}.entries);
return data; return data;
}); });