diff --git a/lib/data_objects/tetrio_multiplayer_replay.dart b/lib/data_objects/tetrio_multiplayer_replay.dart index b4ea465..d134920 100644 --- a/lib/data_objects/tetrio_multiplayer_replay.dart +++ b/lib/data_objects/tetrio_multiplayer_replay.dart @@ -5,6 +5,29 @@ import 'tetrio.dart'; // I want to implement those fancy TWC stats // So, i'm going to read replay for things +int biggestSpikeFromReplay(events){ + int previousIGEeventFrame = -1; + int spikeCounter = 0; + int biggestSpike = 0; + for (var event in events){ + if (event["type"] == "ige" && event["data"]["data"]["type"] == "interaction"){ + if (previousIGEeventFrame.isNegative){ + spikeCounter = event['data']['data']['data']['amt']; + biggestSpike = spikeCounter; + }else{ + if (event['data']['data']['frame'] - previousIGEeventFrame < 60){ + spikeCounter = spikeCounter + event['data']['data']['data']['amt'] as int; + }else{ + spikeCounter = event['data']['data']['data']['amt']; + } + biggestSpike = max(biggestSpike, spikeCounter); + } + previousIGEeventFrame = event['data']['data']['frame']; + } + } + return biggestSpike; +} + class Garbage{ // charsys where??? late int sent; late int recived; @@ -43,11 +66,11 @@ class ReplayStats{ late int score; late int topCombo; late int topBtB; + late int topSpike; late int tspins; late Clears clears; late Garbage garbage; late Finesse finesse; - late int kills; double get finessePercentage => finesse.perfectPieces / piecesPlaced; @@ -60,14 +83,14 @@ class ReplayStats{ required this.score, required this.topCombo, required this.topBtB, + required this.topSpike, required this.tspins, required this.clears, required this.garbage, required this.finesse, - required this.kills, }); - ReplayStats.fromJson(Map json){ + ReplayStats.fromJson(Map json, int inputTopSpike){ seed = json['seed']; linesCleared = json['lines']; piecesPlaced = json['piecesplaced']; @@ -76,11 +99,11 @@ class ReplayStats{ score = json['score']; topCombo = json['topcombo']; topBtB = json['topbtb']; + topSpike = inputTopSpike; tspins = json['tspins']; clears = Clears.fromJson(json['clears']); garbage = Garbage.fromJson(json['garbage']); finesse = Finesse.fromJson(json['finesse']); - kills = json['kills']; } ReplayStats.createEmpty(){ @@ -92,11 +115,11 @@ class ReplayStats{ score = 0; topCombo = 0; topBtB = 0; + topSpike = 0; tspins = 0; clears = Clears(singles: 0, doubles: 0, triples: 0, quads: 0, pentas: 0, allClears: 0, tSpinZeros: 0, tSpinSingles: 0, tSpinDoubles: 0, tSpinTriples: 0, tSpinPentas: 0, tSpinQuads: 0, tSpinMiniZeros: 0, tSpinMiniSingles: 0, tSpinMiniDoubles: 0); garbage = Garbage(sent: 0, recived: 0, attack: 0, cleared: 0); finesse = Finesse(combo: 0, faults: 0, perfectPieces: 0); - kills = 0; } ReplayStats operator + (ReplayStats other){ @@ -108,11 +131,12 @@ class ReplayStats{ score: score + other.score, topCombo: max(topCombo, other.topCombo), topBtB: max(topBtB, other.topBtB), + topSpike: max(topSpike, other.topSpike), tspins: tspins + other.tspins, clears: clears + other.clears, garbage: garbage + other.garbage, - finesse: finesse + other.finesse, - kills: kills + other.kills); + finesse: finesse + other.finesse + ); } } @@ -122,6 +146,7 @@ class ReplayData{ late List endcontext; late List> stats; late List totalStats; + late List> roundWinners; late List roundLengths; // in frames late int totalLength; // in frames @@ -130,6 +155,7 @@ class ReplayData{ required this.endcontext, required this.stats, required this.totalStats, + required this.roundWinners, required this.roundLengths, required this.totalLength }){ @@ -143,24 +169,20 @@ class ReplayData{ roundLengths = []; totalLength = 0; stats = []; + roundWinners = []; totalStats = [ReplayStats.createEmpty(), ReplayStats.createEmpty()]; - + int firstInEndContext = json["data"][0]["board"].indexWhere((element) => element["id"] == endcontext[0].userId); + int secondInEndContext = json["data"][0]["board"].indexWhere((element) => element["id"] == endcontext[1].userId); for(var round in json['data']) { roundLengths.add(max(round['replays'][0]['frames'], round['replays'][1]['frames'])); totalLength = totalLength + max(round['replays'][0]['frames'], round['replays'][1]['frames']); - ReplayStats playerOne = ReplayStats.fromJson(round['replays'][0]['events'].last['data']['export']['stats']); - ReplayStats playerTwo = ReplayStats.fromJson(round['replays'][1]['events'].last['data']['export']['stats']); + int winner = round['board'].indexWhere((element) => element["success"] == true); + roundWinners.add([round['board'][winner]["id"], round['board'][winner]["username"]]); + ReplayStats playerOne = ReplayStats.fromJson(round['replays'][firstInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][secondInEndContext]['events'])); // (events contain recived attacks) + ReplayStats playerTwo = ReplayStats.fromJson(round['replays'][secondInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][firstInEndContext]['events'])); stats.add([playerOne, playerTwo]); totalStats[0] = totalStats[0] + playerOne; totalStats[1] = totalStats[1] + playerTwo; - // print(round['replays'][0]['events'].last['data']['export']['stats']); - // for (var event in round['replays'][0]['events']){ - // if (event["type"] == "ige" && event["data"]["data"]["type"] == "interaction_confirm"){ - // print(event['data']["data"]["data"]); // lol - // } - // } } - // Сами по себе эти ивенты ничего не дают, - // Хотя можно попробовать вычислить biggest spike } } \ No newline at end of file diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index e8a53db..45103bb 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -65,6 +65,7 @@ class TetrioService extends DB { Map> _players = {}; final Map _playersCache = {}; final Map> _recordsCache = {}; + final Map _replaysCache = {}; final Map _leaderboardsCache = {}; final Map> _newsCache = {}; final Map> _topTRcache = {}; @@ -121,6 +122,12 @@ class TetrioService extends DB { } Future> szyGetReplay(String replayID) async { + try{ + var cached = _replaysCache.entries.firstWhere((element) => element.key == replayID); + return cached.value; + }catch (e){ + // actually going to obtain + } Uri url = Uri.https('inoue.szy.lol', '/api/replay/$replayID'); var downloadPath = await getDownloadsDirectory(); downloadPath ??= Platform.isAndroid ? Directory("/storage/emulated/0/Download") : await getApplicationDocumentsDirectory(); @@ -132,6 +139,7 @@ class TetrioService extends DB { switch (response.statusCode) { case 200: developer.log("szyDownload: Replay downloaded", name: "services/tetrio_crud", error: response.statusCode); + _replaysCache[replayID] = [response.body, response.bodyBytes]; return [response.body, response.bodyBytes]; case 404: throw SzyNotFound(); @@ -157,7 +165,6 @@ class TetrioService extends DB { } Future SaveReplay(String replayID) async { - Uri url = Uri.https('inoue.szy.lol', '/api/replay/$replayID'); var downloadPath = await getDownloadsDirectory(); downloadPath ??= Platform.isAndroid ? Directory("/storage/emulated/0/Download") : await getApplicationDocumentsDirectory(); var replayFile = File("${downloadPath.path}/$replayID.ttrm"); diff --git a/lib/views/compare_view.dart b/lib/views/compare_view.dart index ea2e5d1..e8dee0e 100644 --- a/lib/views/compare_view.dart +++ b/lib/views/compare_view.dart @@ -801,6 +801,7 @@ class CompareThingy extends StatelessWidget { final bool higherIsBetter; final int? fractionDigits; final String? postfix; + final String? prefix; const CompareThingy( {super.key, required this.greenSide, @@ -808,6 +809,7 @@ class CompareThingy extends StatelessWidget { required this.label, required this.higherIsBetter, this.fractionDigits, + this.prefix, this.postfix}); String verdict(num greenSide, num redSide, int fraction) { @@ -845,7 +847,7 @@ class CompareThingy extends StatelessWidget { ], )), child: Text( - f.format(greenSide) + (postfix ?? ""), + (prefix ?? "") + f.format(greenSide) + (postfix ?? ""), style: const TextStyle( fontSize: 22, shadows: [ @@ -899,7 +901,7 @@ class CompareThingy extends StatelessWidget { ], )), child: Text( - f.format(redSide) + (postfix ?? ""), + (prefix ?? "") + f.format(redSide) + (postfix ?? ""), style: const TextStyle( fontSize: 22, shadows: [ diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 24e4592..c0abe19 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -269,10 +269,6 @@ class _MainState extends State with SingleTickerProviderStateMixin { value: "history", child: Text(t.fetchAndsaveTLHistory), ), - PopupMenuItem( - value: "test", - child: Text("Test replay reading"), - ), PopupMenuItem( value: "/states", child: Text(t.showStoredData), @@ -294,11 +290,6 @@ class _MainState extends State with SingleTickerProviderStateMixin { case "history": changePlayer(_searchFor, fetchHistory: true); break; - case "test": - var replayfile = File("/home/dan63047/Загрузки/659337dd1eef65e513c5dc8d.ttrm").readAsStringSync(); - var testObject = ReplayData.fromJson(jsonDecode(replayfile)); - print("lol"); - break; default: context.go(value); } diff --git a/lib/views/tl_match_view.dart b/lib/views/tl_match_view.dart index d7e0036..14d6972 100644 --- a/lib/views/tl_match_view.dart +++ b/lib/views/tl_match_view.dart @@ -201,13 +201,18 @@ class TlMatchResultState extends State { case ConnectionState.none: case ConnectionState.waiting: case ConnectionState.active: - return CircularProgressIndicator(); + return const LinearProgressIndicator(); case ConnectionState.done: if (!snapshot.hasError){ - var time = framesToTime(snapshot.data!.totalLength); - return Center(child: Text("Match Length: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}")); + if (roundSelector.isNegative){ + var time = framesToTime(snapshot.data!.totalLength); + return Center(child: Text("Match Length: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}", textAlign: TextAlign.center)); + }else{ + var time = framesToTime(snapshot.data!.roundLengths[roundSelector]); + return Center(child: Text("Round Length: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}\nWinner: ${snapshot.data!.roundWinners[roundSelector][1]}", textAlign: TextAlign.center,)); + } }else{ - return Text("skill issue"); + return const Text("skill issue", textAlign: TextAlign.center); } } @@ -247,27 +252,47 @@ class TlMatchResultState extends State { case ConnectionState.none: case ConnectionState.waiting: case ConnectionState.active: - return LinearProgressIndicator(); + return const LinearProgressIndicator(); case ConnectionState.done: if (!snapshot.hasError){ - var greenSidePlayer = snapshot.data!.endcontext.indexWhere(((element) => element.userId == widget.initPlayerId)); - var redSidePlayer = snapshot.data!.endcontext.indexWhere(((element) => element.userId != widget.initPlayerId)); + var greenSidePlayer = snapshot.data!.endcontext.indexWhere((element) => element.userId == widget.initPlayerId); + var redSidePlayer = snapshot.data!.endcontext.indexWhere((element) => element.userId != widget.initPlayerId); return Column(children: [ - CompareThingy(greenSide: snapshot.data!.totalStats[greenSidePlayer].piecesPlaced, - redSide: snapshot.data!.totalStats[redSidePlayer].piecesPlaced, + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].piecesPlaced : snapshot.data!.stats[roundSelector][greenSidePlayer].piecesPlaced, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].piecesPlaced : snapshot.data!.stats[roundSelector][redSidePlayer].piecesPlaced, label: "Pieces Placed", higherIsBetter: true), - CompareThingy(greenSide: snapshot.data!.totalStats[greenSidePlayer].linesCleared, - redSide: snapshot.data!.totalStats[redSidePlayer].linesCleared, + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].linesCleared : snapshot.data!.stats[roundSelector][greenSidePlayer].linesCleared, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].linesCleared : snapshot.data!.stats[roundSelector][redSidePlayer].linesCleared, label: "Lines Cleared", higherIsBetter: true), - CompareThingy(greenSide: snapshot.data!.totalStats[greenSidePlayer].finessePercentage * 100, - redSide: snapshot.data!.totalStats[redSidePlayer].finessePercentage * 100, + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].finessePercentage * 100 : snapshot.data!.stats[roundSelector][greenSidePlayer].finessePercentage * 100, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].finessePercentage * 100 : snapshot.data!.stats[roundSelector][redSidePlayer].finessePercentage * 100, label: "Finnese", postfix: "%", fractionDigits: 2, higherIsBetter: true), - CompareThingy(greenSide: snapshot.data!.totalStats[greenSidePlayer].topCombo, - redSide: snapshot.data!.totalStats[redSidePlayer].topCombo, + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topSpike : snapshot.data!.stats[roundSelector][greenSidePlayer].topSpike, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topSpike : snapshot.data!.stats[roundSelector][redSidePlayer].topSpike, + label: "Best Spike", higherIsBetter: true), + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topCombo : snapshot.data!.stats[roundSelector][greenSidePlayer].topCombo, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topCombo : snapshot.data!.stats[roundSelector][redSidePlayer].topCombo, label: "Best Combo", higherIsBetter: true), - CompareThingy(greenSide: snapshot.data!.totalStats[greenSidePlayer].topBtB, - redSide: snapshot.data!.totalStats[redSidePlayer].topBtB, + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topBtB : snapshot.data!.stats[roundSelector][greenSidePlayer].topBtB, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topBtB : snapshot.data!.stats[roundSelector][redSidePlayer].topBtB, label: "Best BtB", higherIsBetter: true), + const Divider(), + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text("Garbage", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + ), + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.sent : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.sent, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.sent : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.sent, + label: "Sent", higherIsBetter: true), + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.recived, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.recived, + label: "Recived", higherIsBetter: true), + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.attack, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.attack, + label: "Attack", higherIsBetter: true), + CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.cleared : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.cleared, + redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.cleared : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.cleared, + label: "Cleared", higherIsBetter: true), ],); }else{ return Text("skill issue"); @@ -412,17 +437,17 @@ class TlMatchResultState extends State { CompareThingy( greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.das, redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.das, - label: "DAS", fractionDigits: 1, + label: "DAS", fractionDigits: 1, postfix: "F", higherIsBetter: false), CompareThingy( greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.arr, redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.arr, - label: "ARR", fractionDigits: 1, + label: "ARR", fractionDigits: 1, postfix: "F", higherIsBetter: false), CompareThingy( greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.sdf, redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.sdf, - label: "SDF", + label: "SDF", prefix: "x", higherIsBetter: true), CompareBoolThingy( greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.safeLock, diff --git a/pubspec.yaml b/pubspec.yaml index 937d971..c738b49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -166,6 +166,7 @@ flutter: - res/tetrio_badges/sfu_raccoon_1.png - res/tetrio_badges/sfu_raccoon_2.png - res/tetrio_badges/sfu_raccoon_3.png + - res/tetrio_badges/streamersuperlobby.png - res/tetrio_badges/superlobby.png - res/tetrio_badges/superlobby2.png - res/tetrio_badges/taws_u50_1.png @@ -185,6 +186,10 @@ flutter: - res/tetrio_badges/ttsdtc_1.png - res/tetrio_badges/ttsdtc_2.png - res/tetrio_badges/ttsdtc_3.png + - res/tetrio_badges/twc23_1.png + - res/tetrio_badges/twc23_2.png + - res/tetrio_badges/twc23_3.png + - res/tetrio_badges/twc23_4.png - res/tetrio_badges/ubcea_1.png - res/tetrio_badges/ubcea_2.png - res/tetrio_badges/ubcea_3.png diff --git a/res/tetrio_badges/streamersuperlobby.png b/res/tetrio_badges/streamersuperlobby.png new file mode 100644 index 0000000..21af4e3 Binary files /dev/null and b/res/tetrio_badges/streamersuperlobby.png differ diff --git a/res/tetrio_badges/twc23_1.png b/res/tetrio_badges/twc23_1.png new file mode 100644 index 0000000..3411c01 Binary files /dev/null and b/res/tetrio_badges/twc23_1.png differ diff --git a/res/tetrio_badges/twc23_2.png b/res/tetrio_badges/twc23_2.png new file mode 100644 index 0000000..ecab68c Binary files /dev/null and b/res/tetrio_badges/twc23_2.png differ diff --git a/res/tetrio_badges/twc23_3.png b/res/tetrio_badges/twc23_3.png new file mode 100644 index 0000000..4824d57 Binary files /dev/null and b/res/tetrio_badges/twc23_3.png differ diff --git a/res/tetrio_badges/twc23_4.png b/res/tetrio_badges/twc23_4.png new file mode 100644 index 0000000..d033d1e Binary files /dev/null and b/res/tetrio_badges/twc23_4.png differ