209 lines
9.0 KiB
Java
209 lines
9.0 KiB
Java
package eric.Roullette.controller;
|
|
|
|
// import com.fasterxml.jackson.core.type.TypeReference;
|
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
import io.javalin.Javalin;
|
|
import io.javalin.http.Context;
|
|
import eric.Roullette.service.GameService;
|
|
import eric.Roullette.service.SpotifyAuthService;
|
|
import eric.Roullette.websocket.GameWebSocketHandler;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.RequestBody;
|
|
import okhttp3.Response;
|
|
import okhttp3.MediaType;
|
|
// import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|
// import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
// import org.slf4j.Logger;
|
|
// import org.slf4j.LoggerFactory;
|
|
import java.awt.*;
|
|
// import java.io.IOException;
|
|
// import java.util.List;
|
|
import java.util.Map;
|
|
// import java.util.Objects;
|
|
// import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
// import java.util.concurrent.TimeUnit;
|
|
|
|
public class GameController {
|
|
private final GameService gameService;
|
|
private final SpotifyAuthService authService;
|
|
private final GameWebSocketHandler webSocketHandler;
|
|
private final OkHttpClient httpClient = new OkHttpClient();
|
|
private String accessToken = "";
|
|
private Map<String, String> userAccessTokens = new ConcurrentHashMap<>();
|
|
// Map<username, accessToken>
|
|
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GameController.class);
|
|
|
|
public GameController(Javalin app, GameService gs, SpotifyAuthService sas, GameWebSocketHandler wsHandler) {
|
|
this.gameService = gs;
|
|
this.authService = sas;
|
|
this.webSocketHandler = wsHandler;
|
|
app.post("/api/create-game", this::createGame);
|
|
app.post("/api/join-game", this::joinGame);
|
|
app.get("/api/game/{gameId}/players", this::getPlayers);
|
|
app.post("/api/game/{gameId}/start-round", this::startRound);
|
|
app.post("/api/game/{gameId}/guess", this::guess);
|
|
app.post("/api/spotify/play", this::playTrack);
|
|
app.get("/api/spotify/devices", ctx -> {
|
|
String username = ctx.queryParam("username");
|
|
if (username == null) {
|
|
ctx.status(400).result("username fehlt");
|
|
return;
|
|
}
|
|
// spieler in map hinzufügen
|
|
|
|
String playerAccessToken = null;
|
|
if(!userAccessTokens.containsKey(username)) {
|
|
playerAccessToken = authService.getAccessTokenForUser(username);
|
|
if (playerAccessToken != null) {
|
|
userAccessTokens.put(username, playerAccessToken);
|
|
}
|
|
}
|
|
|
|
if (accessToken == null) {
|
|
ctx.status(401).result("Zugriffstoken fehlt oder ist ungültig");
|
|
return;
|
|
}
|
|
|
|
var devices = authService.getDevices(accessToken);
|
|
ctx.json(devices);
|
|
|
|
});
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
private void createGame(Context ctx) throws InterruptedException {
|
|
Map<String, Object> body = (Map<String, Object>) ctx.bodyAsClass(Map.class);
|
|
String user = (String) body.get("username");
|
|
if (user == null || user.isBlank()) {
|
|
ctx.status(400).result("username fehlt");
|
|
return;
|
|
}
|
|
|
|
String gameId;
|
|
do {
|
|
gameId = String.format("%04d", ThreadLocalRandom.current().nextInt(0, 10000));
|
|
} while (gameService.gameExists(gameId));
|
|
gameService.createGame(gameId);
|
|
gameService.addPlayer(gameId, user);
|
|
gameService.broadcastPlayers(gameId);
|
|
|
|
ctx.json(Map.of("status", "ok", "gameId", gameId));
|
|
}
|
|
|
|
private void joinGame(Context ctx) throws InterruptedException {
|
|
Map<String, String> body = ctx.bodyAsClass(Map.class);
|
|
String user = body.get("username");
|
|
String gameId = body.get("gameId");
|
|
if (user == null || gameId == null) {
|
|
ctx.status(400).result("username oder gameId fehlt");
|
|
return;
|
|
}
|
|
gameService.addPlayer(gameId, user);
|
|
gameService.broadcastPlayers(gameId);
|
|
ctx.json(Map.of("status", "ok"));
|
|
}
|
|
|
|
private void getPlayers(Context ctx) {
|
|
String gameId = ctx.pathParam("gameId");
|
|
var game = gameService.getOrCreateGame(gameId);
|
|
ctx.json(Map.of(
|
|
"players", game.players()
|
|
));
|
|
}
|
|
|
|
private void startRound(Context ctx) {
|
|
String gameId = ctx.pathParam("gameId");
|
|
ctx.json(Map.of("status", "ok"));
|
|
webSocketHandler.broadcastRoundStart(gameId);
|
|
}
|
|
|
|
private void guess(Context ctx) {
|
|
String gameId = ctx.pathParam("gameId");
|
|
Map<String, String> body = ctx.bodyAsClass(Map.class);
|
|
String guess = body.get("guess");
|
|
String user = body.get("username");
|
|
GameService.Game game = gameService.getOrCreateGame(gameId);
|
|
String owner = game.currentOwner();
|
|
if (owner == null || guess == null) {
|
|
ctx.status(400).result("ungültig");
|
|
return;
|
|
}
|
|
boolean correct = guess.equals(owner);
|
|
if (correct) game.scores().merge(user, 1, Integer::sum);
|
|
ctx.json(Map.of(
|
|
"correct", correct,
|
|
"owner", owner,
|
|
"scores", game.scores()
|
|
));
|
|
}
|
|
// Java
|
|
private void playTrack(Context ctx) {
|
|
Map<String, String> body = ctx.bodyAsClass(Map.class);
|
|
String username = body.get("username");
|
|
String deviceId = body.get("device_id");
|
|
String trackUri = body.get("track_uri");
|
|
if (username == null || deviceId == null || trackUri == null) {
|
|
ctx.status(400).result("Fehlende Parameter: username, device_id oder track_uri");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
//String accessToken = authService.getAccessTokenForUser(username);
|
|
String accessToken = userAccessTokens.get(username);
|
|
OkHttpClient client = httpClient;
|
|
String trackId = trackUri.split(":")[2];
|
|
Request getTrack = new Request.Builder()
|
|
.url("https://api.spotify.com/v1/tracks/" + trackId)
|
|
.addHeader("Authorization", "Bearer " + accessToken)
|
|
.build();
|
|
try (Response trackResp = client.newCall(getTrack).execute()) {
|
|
if (!trackResp.isSuccessful()) {
|
|
//ctx.status(trackResp.code()).result("Fehler beim Laden der Track-Details: " + trackResp.body().string());
|
|
return;
|
|
}
|
|
assert trackResp.body() != null;
|
|
var node = new com.fasterxml.jackson.databind.ObjectMapper().readTree(trackResp.body().string());
|
|
long durationMs = node.get("duration_ms").asLong();
|
|
long startOffset = durationMs / 2;
|
|
|
|
ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
|
|
jsonNode.putArray("uris").add(trackUri);
|
|
jsonNode.put("position_ms", startOffset);
|
|
String jsonBody = jsonNode.toString();
|
|
|
|
Request playReq = new Request.Builder()
|
|
.url("https://api.spotify.com/v1/me/player/play?device_id=" + deviceId)
|
|
.addHeader("Authorization", "Bearer " + accessToken)
|
|
.put(RequestBody.create(jsonBody, MediaType.parse("application/json")))
|
|
.build();
|
|
|
|
try (Response playResp = client.newCall(playReq).execute()) {
|
|
// ctx.status(playResp.code());
|
|
// ctx.header("Retry-After", playResp.header("Retry-After") != null ? playResp.header("Retry-After") : "");
|
|
// ctx.header("x-rate-limit-limit", playResp.header("x-rate-limit-limit"));
|
|
// ctx.header("x-rate-limit-remaining", playResp.header("x-rate-limit-remaining"));
|
|
// ctx.header("x-rate-limit-reset", playResp.header("x-rate-limit-reset"));
|
|
// ctx.result(playResp.body().string());
|
|
if (playResp.isSuccessful()) {
|
|
ctx.status(204).result("Track erfolgreich abgespielt");
|
|
} else {
|
|
//ctx.status(playResp.code()).result("Fehler beim Abspielen: " + playResp.body().string());
|
|
System.out.println("Fehler beim Abspielen des Tracks");
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("Fehler beim Abspielen des Tracks", e);
|
|
ctx.status(500).result("Interner Fehler: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
} |