1.6.4, many fixes

This commit is contained in:
dan63047 2024-08-18 02:39:20 +03:00
parent 2376c0eb58
commit 67da831cd2
10 changed files with 208 additions and 106 deletions

View File

@ -1,10 +1,12 @@
// p1nkl0bst3r data objects // p1nkl0bst3r data objects
class Cutoffs{ class Cutoffs{
DateTime ts;
Map<String, double> tr; Map<String, double> tr;
Map<String, double> glicko; Map<String, double> glicko;
Map<String, double> gxe;
Cutoffs(this.tr, this.glicko); Cutoffs(this.ts, this.tr, this.glicko, this.gxe);
} }
class TopTr{ class TopTr{

View File

@ -42,26 +42,26 @@ const Map<String, double> rankCutoffs = {
"z": -1, "z": -1,
"": 0.5 "": 0.5
}; };
const Map<String, double> rankTargets = { // const Map<String, double> rankTargets = {
"x": 24503.75, // where that comes from? // "x": 24503.75, // where that comes from?
"u": 23038, // "u": 23038,
"ss": 21583, // "ss": 21583,
"s+": 20128, // "s+": 20128,
"s": 18673, // "s": 18673,
"s-": 16975, // "s-": 16975,
"a+": 15035, // "a+": 15035,
"a": 13095, // "a": 13095,
"a-": 11155, // "a-": 11155,
"b+": 9215, // "b+": 9215,
"b": 7275, // "b": 7275,
"b-": 5335, // "b-": 5335,
"c+": 3880, // "c+": 3880,
"c": 2425, // "c": 2425,
"c-": 1213, // "c-": 1213,
"d+": 606, // "d+": 606,
"d": 0, // "d": 0,
}; // };
DateTime seasonStart = DateTime.utc(2024, 08, 16, 18); // DateTime seasonStart = DateTime.utc(2024, 08, 16, 18);
//DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15); //DateTime seasonEnd = DateTime.utc(2024, 07, 26, 15);
enum Stats { enum Stats {
tr, tr,
@ -123,7 +123,8 @@ const Map<Stats, String> chartsShortTitles = {
Stats.openerMinusInfDS: "Opener - Inf. DS" Stats.openerMinusInfDS: "Opener - Inf. DS"
}; };
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:458
'x+': Color(0xFF643C8D),
'x': Color(0xFFFF45FF), 'x': Color(0xFFFF45FF),
'u': Color(0xFFFF3813), 'u': Color(0xFFFF3813),
'ss': Color(0xFFDB8B1F), 'ss': Color(0xFFDB8B1F),
@ -1422,10 +1423,10 @@ class TetraLeague {
timestamp = ts; timestamp = ts;
gamesPlayed = json['gamesplayed'] ?? 0; gamesPlayed = json['gamesplayed'] ?? 0;
gamesWon = json['gameswon'] ?? 0; gamesWon = json['gameswon'] ?? 0;
tr = json['tr'] != null ? json['tr'].toDouble() : -1; tr = json['tr'] != null ? json['tr'].toDouble() : json['rating'] != null ? json['rating'].toDouble() : -1;
glicko = json['glicko']?.toDouble(); glicko = json['glicko']?.toDouble();
rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd; rd = json['rd'] != null ? json['rd']!.toDouble() : noTrRd;
gxe = json['gxe'].toDouble(); gxe = json['gxe'] != null ? json['gxe'].toDouble() : -1;
rank = json['rank'] != null ? json['rank']!.toString() : 'z'; rank = json['rank'] != null ? json['rank']!.toString() : 'z';
bestRank = json['bestrank'] != null ? json['bestrank']!.toString() : 'z'; bestRank = json['bestrank'] != null ? json['bestrank']!.toString() : 'z';
apm = json['apm']?.toDouble(); apm = json['apm']?.toDouble();
@ -1721,6 +1722,11 @@ class TetrioPlayersLeaderboard {
TetrioPlayersLeaderboard(this.type, this.leaderboard); TetrioPlayersLeaderboard(this.type, this.leaderboard);
@override
String toString(){
return "$type leaderboard: ${leaderboard.length} players";
}
List<TetrioPlayerFromLeaderboard> getStatRanking(List<TetrioPlayerFromLeaderboard> leaderboard, Stats stat, {bool reversed = false, String country = ""}){ List<TetrioPlayerFromLeaderboard> getStatRanking(List<TetrioPlayerFromLeaderboard> leaderboard, Stats stat, {bool reversed = false, String country = ""}){
List<TetrioPlayerFromLeaderboard> lb = List.from(leaderboard); List<TetrioPlayerFromLeaderboard> lb = List.from(leaderboard);
if (country.isNotEmpty){ if (country.isNotEmpty){
@ -2425,6 +2431,10 @@ class TetrioPlayersLeaderboard {
leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry, ts)); leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry, ts));
} }
} }
addPlayers(List<TetrioPlayerFromLeaderboard> list){
leaderboard.addAll(list);
}
} }
class TetrioPlayerFromLeaderboard { class TetrioPlayerFromLeaderboard {

View File

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
class UserAgentClient extends http.BaseClient { class UserAgentClient extends http.BaseClient {
@ -9,6 +11,7 @@ class UserAgentClient extends http.BaseClient {
@override @override
Future<http.StreamedResponse> send(http.BaseRequest request) { Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers['user-agent'] = userAgent; request.headers['user-agent'] = userAgent;
request.headers['X-Session-ID'] = "${Random().nextInt(1<<32)}";
return _inner.send(request); return _inner.send(request);
} }
} }

View File

@ -411,12 +411,7 @@ class TetrioService extends DB {
Cutoffs? cached = _cache.get("", Cutoffs); Cutoffs? cached = _cache.get("", Cutoffs);
if (cached != null) return cached; if (cached != null) return cached;
Uri url; Uri url = Uri.https('ts.dan63.by', 'beanserver_blaster/cutoffs.json', {"users": null});
if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLCutoffs"});
} else {
url = Uri.https('api.p1nkl0bst3r.xyz', 'rankcutoff', {"users": null});
}
try{ try{
final response = await client.get(url); final response = await client.get(url);
@ -424,13 +419,14 @@ class TetrioService extends DB {
switch (response.statusCode) { switch (response.statusCode) {
case 200: case 200:
Map<String, dynamic> rawData = jsonDecode(response.body); Map<String, dynamic> rawData = jsonDecode(response.body);
Map<String, dynamic> data = rawData["cutoffs"] as Map<String, dynamic>; Map<String, dynamic> data = rawData["data"] as Map<String, dynamic>;
Cutoffs result = Cutoffs({}, {}); Cutoffs result = Cutoffs(DateTime.fromMillisecondsSinceEpoch(rawData["created"]), {}, {}, {});
for (String rank in data.keys){ for (String rank in data.keys){
result.tr[rank] = data[rank]["rating"]; result.tr[rank] = data[rank]["tr"];
result.glicko[rank] = data[rank]["glicko"]; result.glicko[rank] = data[rank]["glicko"];
result.gxe[rank] = data[rank]["gxe"];
} }
_cache.store(result, rawData["ts"] + 300000); _cache.store(result, rawData["cache_until"]);
return result; return result;
case 404: case 404:
developer.log("fetchCutoffs: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode); developer.log("fetchCutoffs: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
@ -466,7 +462,7 @@ class TetrioService extends DB {
if (kIsWeb) { if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLTopOne"}); url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLTopOne"});
} else { } else {
url = Uri.https('ch.tetr.io', 'api/users/lists/league', {"after": "25000", "limit": "1"}); url = Uri.https('ch.tetr.io', 'api/users/by/league', {"after": "25000:0:0", "limit": "1"});
} }
try{ try{
@ -475,7 +471,7 @@ class TetrioService extends DB {
switch (response.statusCode) { switch (response.statusCode) {
case 200: case 200:
var rawJson = jsonDecode(response.body); var rawJson = jsonDecode(response.body);
TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["users"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"])); TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["entries"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"]));
_cache.store(result, rawJson["cache"]["cached_until"]); _cache.store(result, rawJson["cache"]["cached_until"]);
return result; return result;
case 404: case 404:
@ -640,15 +636,17 @@ class TetrioService extends DB {
} }
/// Retrieves full Tetra League leaderboard from Tetra Channel api. Returns a leaderboard object. Throws an exception if fails to retrieve. /// Retrieves full Tetra League leaderboard from Tetra Channel api. Returns a leaderboard object. Throws an exception if fails to retrieve.
Future<TetrioPlayersLeaderboard> fetchTLLeaderboard() async { Future<TetrioPlayersLeaderboard> fetchTLLeaderboard({double? after}) async {
TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard); TetrioPlayersLeaderboard? cached = _cache.get("league${after != null ? after.toString() : ""}", TetrioPlayersLeaderboard);
if (cached != null) return cached; if (cached != null) return cached;
Uri url; Uri url;
if (kIsWeb) { if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"}); url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"});
} else { } else {
url = Uri.https('ch.tetr.io', 'api/users/by/league'); url = Uri.https('ch.tetr.io', 'api/users/by/league', {
"limit": "100",
if (after != null) "after": "$after:0:0"
});
} }
try{ try{
final response = await client.get(url); final response = await client.get(url);
@ -688,6 +686,20 @@ class TetrioService extends DB {
} }
} }
Stream<TetrioPlayersLeaderboard> fetchFullLeaderboard() async* {
late double after;
int lbLength = 100;
TetrioPlayersLeaderboard leaderboard = await fetchTLLeaderboard();
after = leaderboard.leaderboard.last.tr;
while (lbLength == 100){
TetrioPlayersLeaderboard pseudoLb = await fetchTLLeaderboard(after: after);
leaderboard.addPlayers(pseudoLb.leaderboard);
lbLength = pseudoLb.leaderboard.length;
after = pseudoLb.leaderboard.last.tr;
yield leaderboard;
}
}
// i want to know progress, so i trying to figure out this thing: // i want to know progress, so i trying to figure out this thing:
// Stream<TetrioPlayersLeaderboard> fetchTLLeaderboardAsStream() async { // Stream<TetrioPlayersLeaderboard> fetchTLLeaderboardAsStream() async {
// TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard); // TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard);

View File

@ -11,6 +11,7 @@ import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:syncfusion_flutter_charts/charts.dart'; import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:tetra_stats/data_objects/tetra_stats.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart' show prefs, teto; import 'package:tetra_stats/main.dart' show prefs, teto;
@ -156,8 +157,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(title); if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(title);
// Requesting Tetra League (alpha), records, news and top TR of player // Requesting Tetra League (alpha), records, news and top TR of player
late List<dynamic> requests; List<dynamic> requests;
late Summaries summaries; Summaries summaries = await teto.fetchSummaries(_searchFor);
late TetraLeagueBetaStream tlStream; late TetraLeagueBetaStream tlStream;
late News news; late News news;
// late SingleplayerStream recentSprint; // late SingleplayerStream recentSprint;
@ -166,20 +167,20 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
// late SingleplayerStream blitz; // late SingleplayerStream blitz;
late SingleplayerStream recentZenith; late SingleplayerStream recentZenith;
late SingleplayerStream recentZenithEX; late SingleplayerStream recentZenithEX;
// late TetrioPlayerFromLeaderboard? topOne; late TetrioPlayerFromLeaderboard? topOne;
// late TopTr? topTR; // late TopTr? topTR;
requests = await Future.wait([ // all at once (8 requests to oskware in total) requests = await Future.wait([
teto.fetchSummaries(_searchFor), teto.fetchSummaries(_searchFor),
teto.fetchTLStream(_searchFor), teto.fetchTLStream(_searchFor),
teto.fetchNews(_searchFor), teto.fetchNews(_searchFor),
teto.fetchStream(_searchFor, "zenith/recent"), teto.fetchStream(_searchFor, "zenith/recent"),
teto.fetchStream(_searchFor, "zenithex/recent"), teto.fetchStream(_searchFor, "zenithex/recent"),
//teto.fetchStream(_searchFor, "40l/top"), teto.fetchCutoffs(),
//teto.fetchStream(_searchFor, "blitz/top"), (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
]); ]);
//prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]), //prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]),
//(me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
//(me.tlSeason1.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR //(summaries.league.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR
summaries = requests[0] as Summaries; summaries = requests[0] as Summaries;
tlStream = requests[1] as TetraLeagueBetaStream; tlStream = requests[1] as TetraLeagueBetaStream;
// records = requests[1] as UserRecords; // records = requests[1] as UserRecords;
@ -189,7 +190,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
// recent = requests[3] as SingleplayerStream; // recent = requests[3] as SingleplayerStream;
// sprint = requests[4] as SingleplayerStream; // sprint = requests[4] as SingleplayerStream;
// blitz = requests[5] as SingleplayerStream; // blitz = requests[5] as SingleplayerStream;
// topOne = requests[7] as TetrioPlayerFromLeaderboard?; topOne = requests[6] as TetrioPlayerFromLeaderboard?;
// topTR = requests[8] as TopTr?; // No TR - no Top TR // topTR = requests[8] as TopTr?; // No TR - no Top TR
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId); meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
@ -202,17 +203,17 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!); if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
} }
} }
//Map<String, double>? cutoffs = prefs.getBool("showPositions") == true ? everyone!.cutoffs : (requests[6] as Cutoffs?)?.tr; Map<String, double>? cutoffs = (requests[5] as Cutoffs?)?.tr;
//Map<String, double>? cutoffsGlicko = prefs.getBool("showPositions") == true ? everyone!.cutoffsGlicko : (requests[6] as Cutoffs?)?.glicko; Map<String, double>? cutoffsGlicko = (requests[5] as Cutoffs?)?.glicko;
// if (me.tlSeason1.gamesPlayed > 9) { if (summaries.league.gamesPlayed > 9) {
// thatRankCutoff = cutoffs?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank]; thatRankCutoff = cutoffs?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank];
// thatRankGlickoCutoff = cutoffsGlicko?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank]; thatRankGlickoCutoff = cutoffsGlicko?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank];
// nextRankCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.tr??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)]; nextRankCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.tr??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)];
// nextRankGlickoCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)]; nextRankGlickoCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)];
// } }
// if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0]; // if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0];
// Making list of Tetra League matches // Making list of Tetra League matches
bool isTracking = await teto.isPlayerTracking(me.userId); bool isTracking = await teto.isPlayerTracking(me.userId);
@ -270,7 +271,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
} }
} }
//states.addAll(await teto.getPlayer(me.userId)); states.addAll(await teto.getPlayer(me.userId));
for (var element in states) { // For graphs I need only unique entries for (var element in states) { // For graphs I need only unique entries
if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!); if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!);
if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!); if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1!);
@ -475,12 +476,12 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
//lastMatchPlayed: snapshot.data![11], //lastMatchPlayed: snapshot.data![11],
bot: snapshot.data![0].role == "bot", bot: snapshot.data![0].role == "bot",
guest: snapshot.data![0].role == "anon", guest: snapshot.data![0].role == "anon",
//thatRankCutoff: thatRankCutoff, thatRankCutoff: thatRankCutoff,
//thatRankCutoffGlicko: thatRankGlickoCutoff, thatRankCutoffGlicko: thatRankGlickoCutoff,
//thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null, //thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null,
//nextRankCutoff: nextRankCutoff, nextRankCutoff: nextRankCutoff,
//nextRankCutoffGlicko: nextRankGlickoCutoff, nextRankCutoffGlicko: nextRankGlickoCutoff,
//nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null, //nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
//averages: rankAverages, //averages: rankAverages,
//lbPositions: meAmongEveryone //lbPositions: meAmongEveryone
), ),
@ -516,12 +517,12 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
//lastMatchPlayed: snapshot.data![11], //lastMatchPlayed: snapshot.data![11],
bot: snapshot.data![0].role == "bot", bot: snapshot.data![0].role == "bot",
guest: snapshot.data![0].role == "anon", guest: snapshot.data![0].role == "anon",
//thatRankCutoff: thatRankCutoff, thatRankCutoff: thatRankCutoff,
//thatRankCutoffGlicko: thatRankGlickoCutoff, thatRankCutoffGlicko: thatRankGlickoCutoff,
//thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null, //thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null,
//nextRankCutoff: nextRankCutoff, nextRankCutoff: nextRankCutoff,
//nextRankCutoffGlicko: nextRankGlickoCutoff, nextRankCutoffGlicko: nextRankGlickoCutoff,
//nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null, //nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
//averages: rankAverages, //averages: rankAverages,
//lbPositions: meAmongEveryone //lbPositions: meAmongEveryone
), ),

View File

@ -170,7 +170,7 @@ class DestinationLeaderboards extends StatefulWidget{
class _DestinationLeaderboardsState extends State<DestinationLeaderboards> { class _DestinationLeaderboardsState extends State<DestinationLeaderboards> {
Cards rightCard = Cards.tetraLeague; Cards rightCard = Cards.tetraLeague;
Duration postSeasonLeft = seasonStart.difference(DateTime.now()); //Duration postSeasonLeft = seasonStart.difference(DateTime.now());
final List<String> leaderboards = ["Tetra League", "Quick Play", "Quick Play Expert"]; final List<String> leaderboards = ["Tetra League", "Quick Play", "Quick Play Expert"];
@override @override
@ -245,7 +245,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
final List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"]; final List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"];
int _chartsIndex = 0; int _chartsIndex = 0;
late List<DropdownMenuItem<List<_HistoryChartSpot>>> chartsData; late List<DropdownMenuItem<List<_HistoryChartSpot>>> chartsData;
Duration postSeasonLeft = seasonStart.difference(DateTime.now()); //Duration postSeasonLeft = seasonStart.difference(DateTime.now());
@override @override
void initState(){ void initState(){
@ -611,7 +611,7 @@ class RecordSummary extends StatelessWidget{
class _DestinationHomeState extends State<DestinationHome> { class _DestinationHomeState extends State<DestinationHome> {
Cards rightCard = Cards.overview; Cards rightCard = Cards.overview;
CardMod cardMod = CardMod.info; CardMod cardMod = CardMod.info;
Duration postSeasonLeft = seasonStart.difference(DateTime.now()); //Duration postSeasonLeft = seasonStart.difference(DateTime.now());
late Map<Cards, List<ButtonSegment<CardMod>>> modeButtons; late Map<Cards, List<ButtonSegment<CardMod>>> modeButtons;
late MapEntry? closestAverageBlitz; late MapEntry? closestAverageBlitz;
late bool blitzBetterThanClosestAverage; late bool blitzBetterThanClosestAverage;
@ -849,7 +849,7 @@ class _DestinationHomeState extends State<DestinationHome> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text(t.tetraLeague, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)), Text(t.tetraLeague, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center) //Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center)
], ],
), ),
), ),
@ -1021,7 +1021,7 @@ class _DestinationHomeState extends State<DestinationHome> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text(t.quickPlay, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)), Text(t.quickPlay, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
Text("Leaderboard reset in ${countdown(postSeasonLeft)}", textAlign: TextAlign.center), //Text("Leaderboard reset in ${countdown(postSeasonLeft)}", textAlign: TextAlign.center),
], ],
), ),
), ),

View File

@ -1,9 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/data_objects/tetra_stats.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/utils/text_shadow.dart';
import 'package:tetra_stats/views/compare_view.dart';
import 'package:tetra_stats/views/rank_averages_view.dart'; import 'package:tetra_stats/views/rank_averages_view.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:tetra_stats/main.dart' show teto; import 'package:tetra_stats/main.dart' show teto;
@ -17,14 +22,9 @@ class RankAveragesView extends StatefulWidget {
late String oldWindowTitle; late String oldWindowTitle;
class RanksAverages extends State<RankAveragesView> { class RanksAverages extends State<RankAveragesView> {
Map<String, List<dynamic>> averages = {};
@override @override
void initState() { void initState() {
teto.fetchTLLeaderboard().then((value){
averages = value.averages;
setState(() {});
});
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${t.rankAveragesViewTitle}"); windowManager.setTitle("Tetra Stats: ${t.rankAveragesViewTitle}");
@ -40,33 +40,108 @@ class RanksAverages extends State<RankAveragesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool bigScreen = MediaQuery.of(context).size.width >= 700;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.rankAveragesViewTitle), title: Text(t.rankAveragesViewTitle),
), ),
backgroundColor: Colors.black, backgroundColor: Colors.black,
body: SafeArea( body: SafeArea(
child: averages.isEmpty ? const Center(child: Text('Fetching...')) : ListView.builder( child: FutureBuilder<Cutoffs?>(future: teto.fetchCutoffs(), builder: (context, snapshot){
itemCount: averages.length, switch (snapshot.connectionState) {
itemBuilder: (context, index){ case ConnectionState.none:
List<String> keys = averages.keys.toList(); case ConnectionState.waiting:
return ListTile( case ConnectionState.active:
leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48), return const Center(child: CircularProgressIndicator(color: Colors.white));
title: Text(t.players(n: averages[keys[index]]?[1]["players"]), style: const TextStyle(fontFamily: "Eurostile Round Extended")), case ConnectionState.done:
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM", if (snapshot.hasData){
style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey, fontSize: 13)), return Container(
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: const TextStyle(fontSize: 28, fontFamily: "Eurostile Round")), alignment: Alignment.center,
onTap: (){ child: SingleChildScrollView(
if (averages[keys[index]]?[1]["players"] > 0) { scrollDirection: Axis.horizontal,
Navigator.push( child: Container(
context, alignment: Alignment.center,
MaterialPageRoute( width: MediaQuery.of(context).size.width,
builder: (context) => RankView(rank: averages[keys[index]]!), constraints: const BoxConstraints(maxWidth: 900, minWidth: 610),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
border: TableBorder.all(color: Colors.grey.shade900),
columnWidths: const {0: FixedColumnWidth(48)},
children: [
TableRow(
children: [
Text(t.rank, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text("TR", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text("Glicko", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text("Glixare", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text("S1 TR", textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
),
]
),
for (String rank in snapshot.data!.tr.keys) TableRow(
decoration: BoxDecoration(gradient: LinearGradient(colors: [rankColors[rank]!.withAlpha(100), rankColors[rank]!.withAlpha(200)])),
children: [
Container(decoration: BoxDecoration(boxShadow: [BoxShadow(color: Colors.black.withAlpha(132), blurRadius: 32.0, blurStyle: BlurStyle.inner)]), child: Image.asset("res/tetrio_tl_alpha_ranks/$rank.png", height: 48)),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(f2.format(snapshot.data!.tr[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(f2.format(snapshot.data!.glicko[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(f2.format(snapshot.data!.gxe[rank]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(f2.format(snapshot.data!.gxe[rank]!*250), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
),
]
)
],
),
Text(t.sprintAndBlitsRelevance(date: timestamp(snapshot.data!.ts)))
],
),
),
),
), ),
); );
} }
}, if (snapshot.hasError){
return Center(child:
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
if (snapshot.stackTrace != null) Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
),
],
)
); );
}
return const Text("end of FutureBuilder");
}
}) })
), ),
); );

View File

@ -61,7 +61,7 @@ class TLProgress extends StatelessWidget{
if (tlData.nextAt > 0 && nextRankTRcutoff != null) const TextSpan(text: "\n"), if (tlData.nextAt > 0 && nextRankTRcutoff != null) const TextSpan(text: "\n"),
if (nextRankTRcutoff != null) TextSpan(text: "${f2.format(nextRankTRcutoff)} (${comparef2.format(nextRankTRcutoff!-tlData.tr)}) TR"), if (nextRankTRcutoff != null) TextSpan(text: "${f2.format(nextRankTRcutoff)} (${comparef2.format(nextRankTRcutoff!-tlData.tr)}) TR"),
if ((tlData.nextAt > 0 || nextRankTRcutoff != null) && nextRankGlickoCutoff != null) const TextSpan(text: "\n"), if ((tlData.nextAt > 0 || nextRankTRcutoff != null) && nextRankGlickoCutoff != null) const TextSpan(text: "\n"),
if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x" && tlData.rank != "z") || tlData.percentileRank != "x"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x")) ? Colors.greenAccent : null)) if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x+" && tlData.rank != "z") || tlData.percentileRank != "x+"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x+")) ? Colors.greenAccent : null))
] ]
) )
) )

View File

@ -148,7 +148,7 @@ class _ZenithThingyState extends State<ZenithThingy> {
const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))), const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))),
Padding( Padding(
padding: const EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: Text("${getMoreNormalTime(record!.stats.finalTime)}%", style: TextStyle( child: Text("${getMoreNormalTime(record!.stats.finalTime)}", style: TextStyle(
shadows: textShadow, shadows: textShadow,
fontFamily: "Eurostile Round Extended", fontFamily: "Eurostile Round Extended",
fontSize: 36, fontSize: 36,
@ -158,7 +158,6 @@ class _ZenithThingyState extends State<ZenithThingy> {
) )
], ],
), ),
Text("Total time: ${getMoreNormalTime(record!.stats.finalTime)}", style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
Table( Table(
columnWidths: const { columnWidths: const {
0: FixedColumnWidth(36) 0: FixedColumnWidth(36)

View File

@ -2,7 +2,7 @@ name: tetra_stats
description: Track your and other player stats in TETR.IO description: Track your and other player stats in TETR.IO
publish_to: 'none' publish_to: 'none'
version: 1.6.3+29 version: 1.6.4+30
environment: environment:
sdk: '>=3.0.0' sdk: '>=3.0.0'