mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Refactored Tournament to use matchExecutor and ResultBroadcaster. Added turnTime and players are now added through Tournament creation instead of on MatchMaker/ScoreSystem creation
This commit is contained in:
@@ -2,11 +2,12 @@ package org.toop.framework.networking.server;
|
||||
|
||||
import org.toop.framework.gameFramework.model.game.TurnBasedGame;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface GameServer<GAMETYPE, CLIENT, CHALLENGEIDTYPE> {
|
||||
GameResultFuture startGame(String gameType, CLIENT... clients);
|
||||
GameResultFuture startGame(String gameType, Duration turnTime, CLIENT... clients);
|
||||
|
||||
void addClient(CLIENT client);
|
||||
void removeClient(CLIENT client);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.toop.framework.networking.server;
|
||||
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MatchExecutor {
|
||||
GameResultFuture submit(String gameType, Duration turnTime, NettyClient... clients);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore;
|
||||
import org.toop.framework.networking.server.tournaments.*;
|
||||
import org.toop.framework.networking.server.tournaments.matchmakers.RoundRobinMatchMaker;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.BasicScoreSystem;
|
||||
import org.toop.framework.networking.server.tournaments.shufflers.RandomShuffle;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.IntegerScoreSystem;
|
||||
import org.toop.framework.utils.ImmutablePair;
|
||||
|
||||
import java.util.*;
|
||||
@@ -111,7 +111,7 @@ public class Server implements GameServer<TurnBasedGame, NettyClient, Long> {
|
||||
public void acceptChallenge(Long challengeId) {
|
||||
for (var challenge : gameChallenges) {
|
||||
if (challenge.id() == challengeId) {
|
||||
startGame(challenge.acceptChallenge(), challenge.getUsers());
|
||||
startGame(challenge.acceptChallenge(), Duration.ofSeconds(10), challenge.getUsers());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class Server implements GameServer<TurnBasedGame, NettyClient, Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameResultFuture startGame(String gameType, NettyClient... clients) {
|
||||
public GameResultFuture startGame(String gameType, Duration turnTime, NettyClient... clients) {
|
||||
if (!gameTypesStore.all().containsKey(gameType)) return null;
|
||||
|
||||
try {
|
||||
@@ -250,7 +250,7 @@ public class Server implements GameServer<TurnBasedGame, NettyClient, Long> {
|
||||
userNames.remove(first);
|
||||
userNames.remove(second);
|
||||
|
||||
startGame(key, getUser(userLeft), getUser(userRight));
|
||||
startGame(key, Duration.ofSeconds(10), getUser(userLeft), getUser(userRight));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,33 +294,26 @@ public class Server implements GameServer<TurnBasedGame, NettyClient, Long> {
|
||||
var tournamentUsers = new ArrayList<>(onlineUsers());
|
||||
tournamentUsers.removeIf(admins::contains);
|
||||
|
||||
var matchMaker = new RoundRobinMatchMaker(tournamentUsers);
|
||||
if (shuffle) {
|
||||
matchMaker.shuffle(new RandomShuffle()); // Remove if not wanting to shuffle
|
||||
}
|
||||
|
||||
Tournament tournament = new Tournament.Builder()
|
||||
.server(this)
|
||||
.matchExecutor(this::startGame)
|
||||
.tournamentRunner(new AsyncTournamentRunner())
|
||||
.matchMaker(matchMaker)
|
||||
.scoreSystem(new BasicScoreSystem(tournamentUsers))
|
||||
.matchMaker(new RoundRobinMatchMaker())
|
||||
.scoreSystem(new BasicScoreSystem())
|
||||
.resultBroadcaster(this::endTournament)
|
||||
.turnTimeout(Duration.ofSeconds(5))
|
||||
.addPlayers(tournamentUsers.toArray(NettyClient[]::new))
|
||||
.addAdmins(admins.toArray(NettyClient[]::new))
|
||||
.build();
|
||||
|
||||
try {
|
||||
new Thread(() -> tournament.run(gameType)).start();
|
||||
} catch (IllegalArgumentException e) {
|
||||
admins.forEach(c -> c.send("ERR not enough clients to start a tournament"));
|
||||
} catch (RuntimeException e) {
|
||||
admins.forEach(c -> c.send("ERR no matches could be created to start a tournament with"));
|
||||
}
|
||||
new Thread(() -> tournament.run(gameType)).start();
|
||||
}
|
||||
|
||||
public void endTournament(Map<NettyClient, Integer> score, String gameType) {
|
||||
public void endTournament(IntegerScoreSystem score) {
|
||||
|
||||
List<String> u = new ArrayList<>();
|
||||
List<Integer> s = new ArrayList<>();
|
||||
|
||||
for (var entry : score.entrySet()) {
|
||||
for (var entry : score.getScore().entrySet()) {
|
||||
u.add(entry.getKey().name());
|
||||
s.add(entry.getValue());
|
||||
}
|
||||
@@ -332,7 +325,7 @@ public class Server implements GameServer<TurnBasedGame, NettyClient, Long> {
|
||||
|
||||
String msg = String.format(
|
||||
"SVR RESULTS {GAMETYPE: \"%s\", USERS: %s, SCORES: %s, TOURNAMENT: 1}",
|
||||
gameType,
|
||||
"none", // TODO gametype
|
||||
users,
|
||||
scores
|
||||
);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package org.toop.framework.networking.server.tournaments;
|
||||
|
||||
import org.toop.framework.networking.server.GameResultFuture;
|
||||
import org.toop.framework.networking.server.Server;
|
||||
import org.toop.framework.networking.server.MatchExecutor;
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.matchmakers.MatchMaker;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.ScoreSystem;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.IntegerScoreSystem;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@@ -13,9 +14,11 @@ public class AsyncTournamentRunner implements TournamentRunner {
|
||||
|
||||
@Override
|
||||
public void run(
|
||||
Server server,
|
||||
MatchExecutor matchRunner,
|
||||
MatchMaker matchMaker,
|
||||
ScoreSystem scoreSystem,
|
||||
IntegerScoreSystem scoreSystem,
|
||||
ResultBroadcaster<IntegerScoreSystem> broadcaster,
|
||||
Duration turnTime,
|
||||
String gameType
|
||||
) {
|
||||
|
||||
@@ -52,8 +55,8 @@ public class AsyncTournamentRunner implements TournamentRunner {
|
||||
CompletableFuture<Void> f =
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
GameResultFuture game = server.startGame(gameType, a, b);
|
||||
scoreSystem.matchEndAwait(game);
|
||||
GameResultFuture game = matchRunner.submit(gameType, turnTime, a, b);
|
||||
scoreSystem.result(match, game.result().join());
|
||||
} finally {
|
||||
a.clearGame();
|
||||
b.clearGame();
|
||||
@@ -70,7 +73,7 @@ public class AsyncTournamentRunner implements TournamentRunner {
|
||||
Thread.sleep(10); // Safety
|
||||
}
|
||||
|
||||
server.endTournament(scoreSystem.getScore(), gameType);
|
||||
broadcaster.broadcast(scoreSystem);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
package org.toop.framework.networking.server.tournaments;
|
||||
|
||||
import org.toop.framework.networking.server.GameResultFuture;
|
||||
import org.toop.framework.networking.server.Server;
|
||||
import org.toop.framework.networking.server.MatchExecutor;
|
||||
import org.toop.framework.networking.server.tournaments.matchmakers.MatchMaker;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.ScoreSystem;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.IntegerScoreSystem;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class BasicTournamentRunner implements TournamentRunner {
|
||||
@Override
|
||||
public void run(Server server, MatchMaker matchMaker, ScoreSystem scoreSystem, String gameType) {
|
||||
public void run(
|
||||
MatchExecutor matchExecutor,
|
||||
MatchMaker matchMaker,
|
||||
IntegerScoreSystem scoreSystem,
|
||||
ResultBroadcaster<IntegerScoreSystem> broadcaster,
|
||||
Duration turnTime,
|
||||
String gameType
|
||||
) {
|
||||
ExecutorService threadPool = Executors.newSingleThreadExecutor();
|
||||
try {
|
||||
threadPool.execute(() -> {
|
||||
for (TournamentMatch match : matchMaker) {
|
||||
// Play game and await the results
|
||||
GameResultFuture game = server.startGame(gameType, match.getClient0(), match.getClient1());
|
||||
scoreSystem.matchEndAwait(game);
|
||||
GameResultFuture game = matchExecutor.submit(gameType, turnTime, match.getClient0(), match.getClient1());
|
||||
scoreSystem.result(match, game.result().join());
|
||||
|
||||
match.getClient0().clearGame();
|
||||
match.getClient1().clearGame();
|
||||
}
|
||||
|
||||
server.endTournament(scoreSystem.getScore(), gameType);
|
||||
broadcaster.broadcast(scoreSystem);
|
||||
});
|
||||
} finally {
|
||||
threadPool.shutdown();
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.toop.framework.networking.server.tournaments;
|
||||
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.ScoreSystem;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResultBroadcaster<T extends ScoreSystem<?, ?, ?>> {
|
||||
void broadcast(T scoreSystem);
|
||||
}
|
||||
@@ -1,44 +1,62 @@
|
||||
package org.toop.framework.networking.server.tournaments;
|
||||
|
||||
import org.toop.framework.networking.server.Server;
|
||||
import org.toop.framework.networking.server.MatchExecutor;
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.matchmakers.MatchMaker;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.ScoreSystem;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.IntegerScoreSystem;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Tournament {
|
||||
private final Server server;
|
||||
|
||||
private final ScoreSystem scoreSystem;
|
||||
private final MatchExecutor matchExecutor;
|
||||
private final IntegerScoreSystem scoreSystem;
|
||||
private final TournamentRunner tournamentRunner;
|
||||
private final MatchMaker matchMaker;
|
||||
private final ResultBroadcaster<IntegerScoreSystem> broadcaster;
|
||||
private final NettyClient[] players;
|
||||
private final Duration turnTime;
|
||||
|
||||
private Tournament(Tournament.Builder builder) {
|
||||
server = builder.server;
|
||||
matchExecutor = builder.matchExecutor;
|
||||
scoreSystem = builder.scoreSystem;
|
||||
tournamentRunner = builder.tournamentRunner;
|
||||
matchMaker = builder.matchMaker;
|
||||
broadcaster = builder.broadcaster;
|
||||
players = builder.players;
|
||||
turnTime = builder.turnTime;
|
||||
}
|
||||
|
||||
public void run(String gameType) throws IllegalArgumentException {
|
||||
if (server.gameTypes().stream().noneMatch(e -> e.equalsIgnoreCase(gameType)))
|
||||
throw new IllegalArgumentException("Invalid game type");
|
||||
|
||||
tournamentRunner.run(server, matchMaker, scoreSystem, gameType);
|
||||
Arrays.stream(players).forEach(e -> {
|
||||
matchMaker.addPlayer(e);
|
||||
scoreSystem.addPlayer(e);
|
||||
});
|
||||
|
||||
tournamentRunner.run(matchExecutor, matchMaker, scoreSystem, broadcaster, turnTime, gameType);
|
||||
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Server server;
|
||||
private ScoreSystem scoreSystem;
|
||||
private MatchExecutor matchExecutor;
|
||||
private IntegerScoreSystem scoreSystem;
|
||||
private TournamentRunner tournamentRunner;
|
||||
private MatchMaker matchMaker;
|
||||
private ResultBroadcaster<IntegerScoreSystem> broadcaster;
|
||||
private NettyClient[] players;
|
||||
private NettyClient[] observors;
|
||||
private NettyClient[] admins;
|
||||
private Duration turnTime = Duration.ofSeconds(10);
|
||||
|
||||
public Builder server(Server server) {
|
||||
this.server = server;
|
||||
public Builder matchExecutor(MatchExecutor matchExecutor) {
|
||||
this.matchExecutor = matchExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder scoreSystem(ScoreSystem scoreSystem) {
|
||||
public Builder scoreSystem(IntegerScoreSystem scoreSystem) {
|
||||
this.scoreSystem = scoreSystem;
|
||||
return this;
|
||||
}
|
||||
@@ -53,11 +71,38 @@ public class Tournament {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder resultBroadcaster(ResultBroadcaster<IntegerScoreSystem> broadcaster) {
|
||||
this.broadcaster = broadcaster;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addPlayers(NettyClient[] players) {
|
||||
this.players = players;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addObservers(NettyClient[] observors) { // TODO
|
||||
this.observors = observors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addAdmins(NettyClient[] admins) { // TODO
|
||||
this.admins = admins;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder turnTimeout(Duration turnTime) {
|
||||
this.turnTime = turnTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Tournament build() {
|
||||
Objects.requireNonNull(server, "server");
|
||||
Objects.requireNonNull(matchExecutor, "matchExecutor");
|
||||
Objects.requireNonNull(scoreSystem, "scoreSystem");
|
||||
Objects.requireNonNull(tournamentRunner, "tournamentRunner");
|
||||
Objects.requireNonNull(matchMaker, "matchMaker");
|
||||
Objects.requireNonNull(broadcaster, "resultBroadcaster"); // TODO is not always necessary and needs to be more generic, not just at the end
|
||||
Objects.requireNonNull(players, "players");
|
||||
return new Tournament(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ public class TournamentMatch extends ImmutablePair<NettyClient, NettyClient> {
|
||||
super(a, b);
|
||||
}
|
||||
|
||||
NettyClient getClient0() {
|
||||
public NettyClient getClient0() {
|
||||
return getLeft();
|
||||
}
|
||||
|
||||
NettyClient getClient1() {
|
||||
public NettyClient getClient1() {
|
||||
return getRight();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.toop.framework.networking.server.tournaments;
|
||||
|
||||
import org.toop.framework.networking.server.Server;
|
||||
import org.toop.framework.networking.server.MatchExecutor;
|
||||
import org.toop.framework.networking.server.tournaments.matchmakers.MatchMaker;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.ScoreSystem;
|
||||
import org.toop.framework.networking.server.tournaments.scoresystems.IntegerScoreSystem;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public interface TournamentRunner {
|
||||
void run(Server server, MatchMaker matchMaker, ScoreSystem scoreSystem, String gameType);
|
||||
void run(MatchExecutor matchExecutor, MatchMaker matchMaker, IntegerScoreSystem scoreSystem,
|
||||
ResultBroadcaster<IntegerScoreSystem> broadcaster, Duration turnTime, String gameType);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.toop.framework.networking.server.tournaments.shufflers.Shuffler;
|
||||
import java.util.List;
|
||||
|
||||
public interface MatchMaker extends Iterable<TournamentMatch> {
|
||||
void addPlayer(NettyClient player);
|
||||
List<NettyClient> getPlayers();
|
||||
void shuffle(Shuffler shuffler);
|
||||
}
|
||||
|
||||
@@ -4,20 +4,25 @@ import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.TournamentMatch;
|
||||
import org.toop.framework.networking.server.tournaments.shufflers.Shuffler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class RoundRobinMatchMaker implements MatchMaker {
|
||||
|
||||
private final List<NettyClient> players;
|
||||
private final List<NettyClient> players = new ArrayList<>();
|
||||
|
||||
public RoundRobinMatchMaker(List<NettyClient> players) {
|
||||
this.players = players;
|
||||
public RoundRobinMatchMaker() {} // TODO let user decide store type
|
||||
|
||||
@Override
|
||||
public void addPlayer(NettyClient player) {
|
||||
players.addLast(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shuffle(Shuffler shuffler) {
|
||||
if (players.size() < 2) return;
|
||||
shuffler.shuffle(players);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,35 @@
|
||||
package org.toop.framework.networking.server.tournaments.scoresystems;
|
||||
|
||||
import org.toop.framework.networking.server.GameResultFuture;
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.TournamentMatch;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BasicScoreSystem implements ScoreSystem {
|
||||
public class BasicScoreSystem implements IntegerScoreSystem {
|
||||
|
||||
private final Map<NettyClient, Integer> scores = new ConcurrentHashMap<>();
|
||||
private final int INIT_SCORE = 0;
|
||||
private final int WIN_POINTS = 1;
|
||||
|
||||
public BasicScoreSystem(List<NettyClient> store) {
|
||||
for (NettyClient c : store) {
|
||||
scores.putIfAbsent(c, getInitScore());
|
||||
}
|
||||
public BasicScoreSystem() {} // TODO let user decide store type
|
||||
|
||||
@Override
|
||||
public void addPlayer(NettyClient user) {
|
||||
scores.putIfAbsent(user, INIT_SCORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void matchEndAwait(GameResultFuture result) {
|
||||
|
||||
if (result.game().users().length < 2) return;
|
||||
|
||||
switch (result.result().join()) {
|
||||
case 0 -> givePoints(result.game().users()[0]);
|
||||
case 1 -> givePoints(result.game().users()[1]);
|
||||
public void result(TournamentMatch match, Integer result) {
|
||||
switch (result) {
|
||||
case 0 -> scores.merge(match.getClient0(), WIN_POINTS, Integer::sum);
|
||||
case 1 -> scores.merge(match.getClient1(), WIN_POINTS, Integer::sum);
|
||||
case -1 -> {} // Draw
|
||||
default -> {}
|
||||
default -> throw new IllegalArgumentException("Unknown result: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private void givePoints(NettyClient client) {
|
||||
int clientScore = scores.get(client);
|
||||
scores.put(client, clientScore + getWinPointAmount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<NettyClient, Integer> getScore() {
|
||||
return scores;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.toop.framework.networking.server.tournaments.scoresystems;
|
||||
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.TournamentMatch;
|
||||
|
||||
public interface IntegerScoreSystem extends ScoreSystem<TournamentMatch, Integer, NettyClient> {}
|
||||
@@ -1,19 +1,9 @@
|
||||
package org.toop.framework.networking.server.tournaments.scoresystems;
|
||||
|
||||
import org.toop.framework.networking.server.GameResultFuture;
|
||||
import org.toop.framework.networking.server.client.NettyClient;
|
||||
import org.toop.framework.networking.server.tournaments.TournamentMatch;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ScoreSystem {
|
||||
void matchEndAwait(GameResultFuture result);
|
||||
Map<NettyClient, Integer> getScore();
|
||||
|
||||
default int getWinPointAmount() {
|
||||
return 1;
|
||||
}
|
||||
default int getInitScore() {
|
||||
return 0;
|
||||
}
|
||||
public interface ScoreSystem<MATCHTYPE, SCORETYPE, USERTYPE> {
|
||||
void addPlayer(USERTYPE user);
|
||||
void result(MATCHTYPE match, SCORETYPE result);
|
||||
Map<USERTYPE, SCORETYPE> getScore();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user