Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
84205f3
Increase score limit to 200 and adjust weight factors for improved sc…
ZayrexDev Sep 5, 2026
b903901
Disable danser logo display
ZayrexDev Sep 5, 2026
8a11a8b
Refine score calculation logic and enhance score filtering criteria
ZayrexDev Sep 5, 2026
afccb6e
Adjust best index factor calculation and refine mod penalty logic
ZayrexDev Sep 5, 2026
e34b08c
Adjust best index factor calculation and refine mod penalty logic
ZayrexDev Sep 5, 2026
c0f76e7
Add option to retrieve all user weights and increase score limit for …
ZayrexDev Sep 5, 2026
bf2c96a
Add option to retrieve all user weights and increase score limit for …
ZayrexDev Sep 5, 2026
70d990b
Adjust weight normalization threshold and refine best index factor ca…
ZayrexDev Sep 5, 2026
395b47c
Add support for additional score attributes: approach rate, circle si…
ZayrexDev Sep 5, 2026
b784c68
Refactor error handling in Beatmap rendering and update osuParser dep…
ZayrexDev Sep 5, 2026
8f8e317
Calculate and display score percentages in ScoreController
ZayrexDev Sep 5, 2026
9dae5bf
Refactor mod weight factor checks in ScoreController to use has() met…
ZayrexDev Sep 5, 2026
92ab44d
Reduced weight sharping
ZayrexDev Sep 7, 2026
ac409f7
Update score type parameter from 'bo' to 'bp' in BeatmapController an…
ZayrexDev Sep 7, 2026
1d5e734
Refactor weight calculation logic in ScoreController to improve handl…
ZayrexDev Sep 7, 2026
228f1e5
Bump version to 1.11.6 in pom.xml
ZayrexDev Sep 7, 2026
72fb297
Add support for mod parameter in getUserScore method in OsuAPI and Sc…
ZayrexDev Sep 9, 2026
6ad7298
Update findAvailableScore method to include maxLimit parameter and ad…
ZayrexDev Sep 9, 2026
d7c9f1f
Changed mod factor
ZayrexDev Sep 12, 2026
fdb0b3b
Update getModWeightFactor method to initialize factor to 1.0
ZayrexDev Sep 12, 2026
386d22d
Fix username encoding in getUser method to replace spaces with unders…
ZayrexDev Sep 12, 2026
4dae1e4
Add series score tracking for multiplayer matches and enhance versus …
ZayrexDev Sep 13, 2026
898b817
Add IMAGE_RENDER_TIMEOUT error code and handle timeout exceptions in …
ZayrexDev Sep 13, 2026
03b6de0
Enhance rank validation in ScoreFilter to support SSH and SS ranks
ZayrexDev Sep 13, 2026
0f2a34d
Enhance rank validation in ScoreFilter to support additional operator…
ZayrexDev Sep 13, 2026
6d5d476
Add extra hit stats check in ScoreFormatUtil and update score display…
ZayrexDev Sep 13, 2026
3bf4536
Update osuParser version to 1.5.4 and enhance score filtering logic w…
ZayrexDev Sep 13, 2026
ea51b3d
Add upload_queued status to ReplayService and update ReplayController…
ZayrexDev Sep 13, 2026
5150b0d
Bump version to 1.11.9 in pom.xml
ZayrexDev Sep 13, 2026
cc3f134
Update flag image source in single-score.html
ZayrexDev Sep 14, 2026
816b7e7
Update single-score.html
ZayrexDev Sep 14, 2026
de97954
Enhance performance calculations in AnalyzeController and MissVisuali…
ZayrexDev Sep 15, 2026
d4cbdef
Enhance performance calculations in AnalyzeController and MissVisuali…
ZayrexDev Sep 15, 2026
34b0c7d
Refactor PPLoss calculation in AnalyzeController and MissVisualizeSer…
ZayrexDev Sep 15, 2026
2ba7daa
Update avatar URL handling in LocalScoreService and simplify single-s…
ZayrexDev Sep 16, 2026
9b2ce4d
Refactor performance calculations in AnalyzeController and MissVisual…
ZayrexDev Sep 16, 2026
5ebb7bb
Refactor layout in single-score.html to simplify structure and improv…
ZayrexDev Sep 16, 2026
d256bee
Add hasArtistUnicode method and refactor song title layout in single-…
ZayrexDev Sep 16, 2026
2dcbbea
Add support for JSON response in getUserInfo method and implement han…
ZayrexDev Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ mvn -U clean compile exec:java
### 4) Call an Endpoint

```shell
curl "http://localhost:8721/bo?u=12345678&n=20" --output best_of_20.png
curl "http://localhost:8721/bp?u=12345678&n=20" --output best_of_20.png
```

## Endpoints
Expand Down Expand Up @@ -169,16 +169,17 @@ persisting it. After osuRenderer uploads the completed MP4, `/status` returns th
QQ `qqFile` identifier to SeiraCore. Deploy both service hops behind TLS because
the render request can contain a bearer credential.

| Method | Path | Purpose | Params / POST Body | Response |
|--------|-----------------------------------------|----------------------------------------|---------------------------------------------------|-------------|
| GET | `/replays/status` | Replay renderer overview | none | JSON |
| POST | `/cache/control` | Control cache across oStella and workers | `{"operation":"QUERY","type":"BEATMAPSET","id":12345}` | JSON |
| POST | `/replays/renders/score/{scoreId}` | Queue single replay render | path `scoreId` | `202` JSON |
| POST | `/replays/renders/showcase/scores` | Queue multi-score showcase render | POST Body `{"ids":[score ids]}` | `202` JSON |
| POST | `/replays/renders/showcase/{beatmapId}` | Queue multi-score showcase render | path `beatmapId` + POST Body `{"ids":[user ids]}` | `202` JSON |
| GET | `/replays/{jobId}/status` | Get render job state | path `{jobId}` | JSON |
| GET | `/replays/{jobId}/video` | Download rendered video | path `{jobId}` | `video/mp4` |
| DELETE | `/replays/{jobId}/video` | Remove rendered video and job metadata | path `{jobId}` | text |
| Method | Path | Purpose | Params / POST Body | Response |
|--------|-----------------------------------------|------------------------------------------|--------------------------------------------------------|-------------|
| GET | `/replays/status` | Replay renderer overview | none | JSON |
| POST | `/cache/control` | Control cache across oStella and workers | `{"operation":"QUERY","type":"BEATMAPSET","id":12345}` | JSON |
| POST | `/replays/renders/score/{scoreId}` | Queue single replay render | path `scoreId` | `202` JSON |
| POST | `/replays/renders/showcase/scores` | Queue multi-score showcase render | POST Body `{"ids":[score ids]}` | `202` JSON |
| POST | `/replays/renders/showcase/{beatmapId}` | Queue multi-score showcase render | path `beatmapId` + POST Body `{"ids":[user ids]}` | `202` JSON |
| GET | `/replays/{jobId}/status` | Get render job state | path `{jobId}` | JSON |
| POST | `/replays/{jobId}/cancel` | Cancel a queued or active render job | path `{jobId}` | JSON |
| GET | `/replays/{jobId}/video` | Download rendered video | path `{jobId}` | `video/mp4` |
| DELETE | `/replays/{jobId}/video` | Remove rendered video and job metadata | path `{jobId}` | text |

### Miscellaneous

Expand Down Expand Up @@ -252,12 +253,12 @@ You can also look up the score of a beatmap, or the beatmap index of a beatmapse
#### Looking up beatmaps, beatmapset, or scores by a user and index (e.g., best-of-N):

- `of` = score type
- `i` = index (for `bo` `rs` `rp`, which score index to return)
- `i` = index (for `bp` `rs` `rp`, which score index to return)
- `u` = user ID

##### Score Types for `of` parameter:

- `bo` - best scores
- `bp` - best scores
- `rs` - recent scores
- `rp` - recent **passed** scores
- `mp` - current multiplayer playlist item
Expand All @@ -267,7 +268,7 @@ You can also look up the score of a beatmap, or the beatmap index of a beatmapse
- `/beatmaps/lookup?m=12345678` - Look up beatmap by map ID
- `/beatmaps/lookup?ms=12345678&i=0` - Look up the first beatmap of a beatmapset
- `/beatmaps/lookup?s=12345678` - Look up the beatmap of a score
- `/beatmaps/lookup?of=bo&i=0&u=12345678` - Look up the beatmap of a user's best score #1
- `/beatmaps/lookup?of=bp&i=0&u=12345678` - Look up the beatmap of a user's best score #1
- `/beatmapsets/lookup?ms=12345678` - Look up beatmapset by mapset ID
- `/beatmapsets/lookup?m=12345678` - Look up the beatmapset of a beatmap
- `/beatmapsets/lookup?of=mp` - Look up the beatmapset of the current multiplayer playlist item
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.BotSeira</groupId>
<artifactId>oStella</artifactId>
<version>1.11.5</version>
<version>1.12.0</version>

<properties>
<maven.compiler.source>25</maven.compiler.source>
Expand Down Expand Up @@ -118,7 +118,7 @@
<dependency>
<groupId>com.github.BotSeira</groupId>
<artifactId>osuParser</artifactId>
<version>v1.5.2</version>
<version>v1.5.6</version>
</dependency>

<!-- Source: https://mvnrepository.com/artifact/org.tukaani/xz -->
Expand Down
62 changes: 59 additions & 3 deletions src/main/java/xyz/zcraft/ostella/data/MultiplayerResultData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import java.util.Comparator;
import java.util.List;
import java.util.Map;

public record MultiplayerResultData(long roomId, String roomName, long playlistItemId, String playedAt,
String client, String scoringType, String teamType,
long totalScore, long averageScore, double teamLeadPercent, String winningTeam,
SeriesScore seriesScore,
BeatmapInfo beatmap, UserInfo queuedBy, List<PlayerResult> players,
List<TeamResult> teams, List<PlayerResult> unassignedPlayers) {
public MultiplayerResultData(
Expand All @@ -20,6 +22,7 @@ public MultiplayerResultData(
long averageScore,
double teamLeadPercent,
String winningTeam,
SeriesScore seriesScore,
BeatmapInfo beatmap,
UserInfo queuedBy,
List<PlayerResult> players,
Expand All @@ -37,6 +40,7 @@ public MultiplayerResultData(
this.averageScore = averageScore;
this.teamLeadPercent = teamLeadPercent;
this.winningTeam = winningTeam;
this.seriesScore = seriesScore == null ? SeriesScore.empty() : seriesScore;
this.beatmap = beatmap;
this.queuedBy = queuedBy;
this.players = List.copyOf(players);
Expand All @@ -45,22 +49,61 @@ public MultiplayerResultData(
}

public boolean isTeamVs() {
return teams.size() == 2;
return teams.size() == 2 && !isTeamDuel();
}

public boolean isDuel() {
return !isTeamVs() && players.size() == 2;
return isTeamDuel() || (teams.isEmpty() && players.size() == 2);
}

public List<PlayerResult> duelPlayers() {
if (!isDuel()) {
return players;
}
return players.stream()
.sorted(Comparator.comparingLong(PlayerResult::userId))
.sorted(Comparator
.comparingInt((PlayerResult player) -> teamOrder(player.team()))
.thenComparingLong(PlayerResult::userId))
.toList();
}

public List<VersusScore> versusScores() {
if (teams.size() == 2) {
return teams.stream()
.map(team -> new VersusScore(
team.key(),
team.name(),
"red".equals(team.key()) ? seriesScore.redWins() : seriesScore.blueWins()
))
.toList();
}
if (!isDuel()) {
return List.of();
}
return duelPlayers().stream()
.map(player -> new VersusScore(
null,
player.username(),
seriesScore.playerWins().getOrDefault(player.userId(), 0)
))
.toList();
}

private static int teamOrder(String team) {
return switch (team == null ? "" : team) {
case "red" -> 0;
case "blue" -> 1;
default -> 2;
};
}

private boolean isTeamDuel() {
return players.size() == 2
&& teams.size() == 2
&& teams.get(0).players().size() == 1
&& teams.get(1).players().size() == 1;
}

public record BeatmapInfo(long id, String title, String titleUnicode, String artist, String creator,
String difficultyName,
Double difficultyRating, Double bpm, Long totalLength, String ruleset, String coverUrl) {
Expand All @@ -81,4 +124,17 @@ public record TeamResult(String key, String name, long totalScore, List<PlayerRe
players = List.copyOf(players);
}
}

public record SeriesScore(Map<Long, Integer> playerWins, int redWins, int blueWins) {
public SeriesScore {
playerWins = playerWins == null ? Map.of() : Map.copyOf(playerWins);
}

public static SeriesScore empty() {
return new SeriesScore(Map.of(), 0, 0);
}
}

public record VersusScore(String key, String name, int wins) {
}
}
Loading
Loading