diff --git a/lib/data_objects/tetrio.dart b/lib/data_objects/tetrio.dart index fb60d76..0f14b9e 100644 --- a/lib/data_objects/tetrio.dart +++ b/lib/data_objects/tetrio.dart @@ -31,7 +31,8 @@ const Map rankCutoffs = { "c": 0.9, "c-": 0.95, "d+": 0.975, - "d": 1 + "d": 1, + "z": -1 }; const Map rankColors = { // thanks osk for const rankColors at https://ch.tetr.io/res/js/base.js:418 'x': Color(0xFFFF45FF), @@ -85,7 +86,7 @@ class TetrioPlayer { late bool verified; bool? badstanding; String? botmaster; - late Connections connections; + Connections? connections; late TetraLeagueAlpha tlSeason1; List sprint = []; List 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; - TetrioPlayer.fromJson(Map json, DateTime stateTime) { + TetrioPlayer.fromJson(Map json, DateTime stateTime, String id, String nick) { //developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio"); - userId = json['_id']; - username = json['username']; + userId = id; + username = nick; 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; if (json['badges'] != null) { json['badges'].forEach((v) { badges.add(Badge.fromJson(v)); }); } - xp = json['xp'].toDouble(); - gamesPlayed = json['gamesplayed']; - gamesWon = json['gameswon']; - gameTime = doubleSecondsToDuration(json['gametime'].toDouble()); + xp = json['xp'] != null ? json['xp'].toDouble() : -1; + gamesPlayed = json['gamesplayed'] ?? -1; + gamesWon = json['gameswon'] ?? -1; + gameTime = json['gametime'] != null && json['gametime'] != -1 ? doubleSecondsToDuration(json['gametime'].toDouble()) : const Duration(seconds: -1); country = json['country']; - supporterTier = json['supporter_tier']; - verified = json['verified']; + supporterTier = json['supporter_tier'] ?? 0; + verified = json['verified'] ?? false; tlSeason1 = TetraLeagueAlpha.fromJson(json['league'], stateTime); avatarRevision = json['avatar_revision']; bannerRevision = json['banner_revision']; 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; friendCount = json['friend_count'] ?? 0; badstanding = json['badstanding']; @@ -158,25 +155,25 @@ class TetrioPlayer { Map toJson() { final Map data = {}; - data['_id'] = userId; - data['username'] = username; + // data['_id'] = userId; + // data['username'] = username; data['role'] = role; if (registrationTime != null) data['ts'] = registrationTime?.toString(); - data['badges'] = badges.map((v) => v.toJson()).toList(); - data['xp'] = xp; - data['gamesplayed'] = gamesPlayed; - data['gameswon'] = gamesWon; - data['gametime'] = gameTime.inMicroseconds / 1000000; + if (badges.isNotEmpty) data['badges'] = badges.map((v) => v.toJson()).toList(); + if (xp >= 0) data['xp'] = xp; + if (gamesPlayed >= 0) data['gamesplayed'] = gamesPlayed; + if (gamesWon >= 0) data['gameswon'] = gamesWon; + if (!gameTime.isNegative) data['gametime'] = gameTime.inMicroseconds / 1000000; if (country != null) data['country'] = country; - data['supporter_tier'] = supporterTier; - data['verified'] = verified; + if (supporterTier > 0) data['supporter_tier'] = supporterTier; + if (verified) data['verified'] = verified; data['league'] = tlSeason1.toJson(); if (distinguishment != null) data['distinguishment'] = distinguishment?.toJson(); if (avatarRevision != null) data['avatar_revision'] = avatarRevision; if (bannerRevision != null) data['banner_revision'] = bannerRevision; - if (data['bio'] != null) data['bio'] = bio; - data['connections'] = connections.toJson(); - data['friend_count'] = friendCount; + if (bio != null) data['bio'] = bio; + if (connections != null) data['connections'] = connections!.toJson(); + if (friendCount > 0) data['friend_count'] = friendCount; if (badstanding != null) data['badstanding'] = badstanding; if (botmaster != null) data['botmaster'] = botmaster; //developer.log("TetrioPlayer.toJson: $data", name: "data_objects/tetrio"); @@ -741,7 +738,7 @@ class TetraLeagueAlpha { this.pps, this.vs, 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; 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 json, ts) { timestamp = ts; - gamesPlayed = json['gamesplayed']; - gamesWon = json['gameswon']; - rating = json['rating'].toDouble(); + gamesPlayed = json['gamesplayed'] ?? 0; + gamesWon = json['gameswon'] ?? 0; + rating = json['rating'] != null ? json['rating'].toDouble() : -1; glicko = json['glicko']?.toDouble(); - rd = json['rd']?.toDouble(); - rank = json['rank']; - bestRank = json['bestrank'].toString(); - apm = json['apm']?.toDouble(); - pps = json['pps']?.toDouble(); - vs = json['vs']?.toDouble(); - decaying = json['decaying']; - standing = json['standing']; - percentile = json['percentile'].toDouble(); - standingLocal = json['standing_local']; + rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd; + rank = json['rank'] != null ? json['rank']!.toString() : 'z'; + bestRank = json['bestrank'] != null ? json['bestrank']!.toString() : 'z'; + apm = json['apm'] != null ? json['apm']!.toDouble() : null; + pps = json['pps'] != null ? json['pps']!.toDouble() : null; + vs = json['vs'] != null ? json['vs']!.toDouble() : null; + decaying = json['decaying'] ?? false; + standing = json['standing'] ?? -1; + percentile = json['percentile'] != null ? json['percentile'].toDouble() : rankCutoffs[rank]; + standingLocal = json['standing_local'] ?? -1; prevRank = json['prev_rank']; - prevAt = json['prev_at']; + prevAt = json['prev_at'] ?? -1; nextRank = json['next_rank']; - nextAt = json['next_at']; - percentileRank = json['percentile_rank']; + nextAt = json['next_at'] ?? -1; + percentileRank = json['percentile_rank'] ?? rank; 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; - 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; } @override @@ -784,25 +780,25 @@ class TetraLeagueAlpha { Map toJson() { final Map data = {}; - data['gamesplayed'] = gamesPlayed; - data['gameswon'] = gamesWon; - data['rating'] = rating; + if (gamesPlayed > 0) data['gamesplayed'] = gamesPlayed; + if (gamesWon > 0) data['gameswon'] = gamesWon; + if (rating >= 0) data['rating'] = rating; if (glicko != null) data['glicko'] = glicko; - if (rd != null) data['rd'] = rd; - data['rank'] = rank; - data['bestrank'] = bestRank; + if (rd != null && rd != noTrRd) data['rd'] = rd; + if (rank != 'z') data['rank'] = rank; + if (bestRank != 'z') data['bestrank'] = bestRank; if (apm != null) data['apm'] = apm; if (pps != null) data['pps'] = pps; if (vs != null) data['vs'] = vs; - data['decaying'] = decaying; - data['standing'] = standing; - data['percentile'] = percentile; - data['standing_local'] = standingLocal; + if (decaying) data['decaying'] = decaying; + if (standing >= 0) data['standing'] = standing; + if (!rankCutoffs.containsValue(percentile)) data['percentile'] = percentile; + if (standingLocal >= 0) data['standing_local'] = standingLocal; 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; - data['next_at'] = nextAt; - data['percentile_rank'] = percentileRank; + if (nextAt >= 0) data['next_at'] = nextAt; + if (percentileRank != rank) data['percentile_rank'] = percentileRank; return data; } } diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 7c34412..48c7368 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -117,15 +117,30 @@ class TetrioService extends DB { xp: -1, supporterTier: 0, verified: false, - connections: Connections(), - 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), + connections: null, + 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: [], blitz: [] ); - history.add(state); - // _players.addEntries({state.userId: [state]}.entries); - await storeState(state, isFromHistory: true); + history.add(state); } + ensureDbIsOpen(); + final db = getDatabaseOrThrow(); + late List 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 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; } else { @@ -270,7 +285,7 @@ class TetrioService extends DB { if (results.isNotEmpty) { throw TetrioPlayerAlreadyExist(); } - final Map statesJson = {tetrioPlayer.state.millisecondsSinceEpoch.toString(): tetrioPlayer.toJson()}; + final Map statesJson = {(tetrioPlayer.state.millisecondsSinceEpoch ~/ 1000).toString(): tetrioPlayer.toJson()}; db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)}); _players.addEntries({ tetrioPlayer.userId: [tetrioPlayer] @@ -319,7 +334,7 @@ class TetrioService extends DB { } } - Future storeState(TetrioPlayer tetrioPlayer, {bool isFromHistory = false}) async { + Future storeState(TetrioPlayer tetrioPlayer) async { ensureDbIsOpen(); final db = getDatabaseOrThrow(); late List states; @@ -330,15 +345,15 @@ class TetrioService extends DB { await createPlayer(tetrioPlayer); states = await getPlayer(tetrioPlayer.userId); } - bool test = isFromHistory ? _players[tetrioPlayer.userId]!.last.checkForRetrivedHistory(tetrioPlayer) : _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer); - if (test == false) isFromHistory ? states.insert(0, tetrioPlayer) : states.add(tetrioPlayer); + bool test = _players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer); + if (test == false) states.add(tetrioPlayer); final Map statesJson = {}; 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)}, 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); } @@ -369,7 +384,7 @@ class TetrioService extends DB { } else { dynamic rawStates = results.first['jsonStates'] as String; 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.addEntries({states.last.userId: states}.entries); _tetrioStreamController.add(_players); @@ -395,9 +410,9 @@ class TetrioService extends DB { final response = await http.get(url); if (response.statusCode == 200) { - if (jsonDecode(response.body)['success']) { - TetrioPlayer player = TetrioPlayer.fromJson( - jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true)); + var json = jsonDecode(response.body); + if (json['success']) { + 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"); _playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player; return player; @@ -421,7 +436,7 @@ class TetrioService extends DB { // what the fuck am i doing here? var test = json.decode(row['jsonStates'] as String); List 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); return data; });