geforkt von SteamWar/BungeeCore
Update to new ranked system
Dieser Commit ist enthalten in:
Ursprung
44e9c084c7
Commit
9039928eb4
@ -21,11 +21,11 @@ package de.steamwar.bungeecore.commands;
|
||||
|
||||
import de.steamwar.bungeecore.ArenaMode;
|
||||
import de.steamwar.bungeecore.Message;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.UserElo;
|
||||
import de.steamwar.command.SWCommand;
|
||||
import de.steamwar.command.SWCommandUtils;
|
||||
import de.steamwar.command.TypeMapper;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.UserElo;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
@ -67,18 +67,10 @@ public class RankCommand extends SWCommand {
|
||||
} else {
|
||||
Message.send("RANK_UNPLACED", player);
|
||||
}
|
||||
Message.send("RANK_EMBLEM", player, getEmblemProgression(player, mode.getChatName(), user.getId()));
|
||||
Message.send("RANK_EMBLEM", player, UserElo.getEmblemProgression(mode.getChatName(), user.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getEmblemProgression(ProxiedPlayer player, String gameMode, int userId) {
|
||||
int fightsOfSeason = UserElo.getFightsOfSeason(userId, gameMode);
|
||||
if (fightsOfSeason < 10)
|
||||
return Message.parse("RANK_NEEDED_FIGHTS_LEFT", player, "§8✧ ✦ ✶ ✷ ✸ ✹ ❂", 10 - fightsOfSeason);
|
||||
|
||||
return UserElo.getEmblemProgression(gameMode, userId);
|
||||
}
|
||||
|
||||
@Mapper(value = "player", local = true)
|
||||
public TypeMapper<String> playerTypeMapper() {
|
||||
return SWCommandUtils.createMapper(s -> s, s -> BungeeCord.getInstance().getPlayers().stream().map(ProxiedPlayer::getName).collect(Collectors.toList()));
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2022 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@ -20,29 +20,35 @@
|
||||
package de.steamwar.bungeecore.network.handlers;
|
||||
|
||||
import de.steamwar.bungeecore.ArenaMode;
|
||||
import de.steamwar.bungeecore.BungeeCore;
|
||||
import de.steamwar.network.packets.PacketHandler;
|
||||
import de.steamwar.network.packets.common.FightEndsPacket;
|
||||
import de.steamwar.sql.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.BungeeTitle;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.Title;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FightEndsHandler extends PacketHandler {
|
||||
|
||||
private Map<String, LinkedList<Game>> gameModeGames = new HashMap<>();
|
||||
private static final int MEDIAN_ELO_GAIN = 20;
|
||||
private static final double WIN_FACTOR = 1.0;
|
||||
private static final double DRAW_WIN_FACTOR = 0.2;
|
||||
private static final double LOSE_FACTOR = -1.0;
|
||||
private static final double DRAW_LOSE_FACTOR = -0.2;
|
||||
private static final long REMATCH_LIFETIME = 1L * 60 * 60 * 1000;
|
||||
|
||||
private int K = 20;
|
||||
|
||||
private long defaultFightRange = 1000 /* Milliseconds */ * 60 /* Seconds */ * 15L /* Minutes */;
|
||||
private Map<String, Long> fightRanges = new HashMap<>();
|
||||
private long defaultFightCount = 1;
|
||||
private Map<String, Long> fightCounts = new HashMap<>();
|
||||
|
||||
{
|
||||
fightRanges.put("miniwargear", 1000 /* Milliseconds */ * 60 /* Seconds */ * 30L /* Minutes */);
|
||||
fightCounts.put("miniwargear", 3L);
|
||||
}
|
||||
private Map<String, LinkedList<Game>> gameModeGames = new HashMap<>();
|
||||
|
||||
@Handler
|
||||
public void handle(FightEndsPacket fightEndsPacket) {
|
||||
@ -50,13 +56,6 @@ public class FightEndsHandler extends PacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
int bluePlayerSize = fightEndsPacket.getBluePlayers().size();
|
||||
int redPlayerSize = fightEndsPacket.getRedPlayers().size();
|
||||
double playerRatio = bluePlayerSize > redPlayerSize ? (double) redPlayerSize / bluePlayerSize : (double) bluePlayerSize / redPlayerSize;
|
||||
if (playerRatio < 0.6) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean bluePublic = SchematicNode.getSchematicNode(fightEndsPacket.getBlueSchem()).getOwner() == 0;
|
||||
boolean redPublic = SchematicNode.getSchematicNode(fightEndsPacket.getRedSchem()).getOwner() == 0;
|
||||
|
||||
@ -64,15 +63,6 @@ public class FightEndsHandler extends PacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
double blueResult;
|
||||
if (fightEndsPacket.getWin() == 0) {
|
||||
blueResult = 0.5;
|
||||
} else if (fightEndsPacket.getWin() == 1) {
|
||||
blueResult = 1;
|
||||
} else {
|
||||
blueResult = 0;
|
||||
}
|
||||
|
||||
// Die nächsten Zeilen filtern ein Fight innerhalb eines Teams nicht gewertet wird, bzw auch wenn nur Teile beider Teams im
|
||||
// gleichen Team sind dieser ungewertet ist.
|
||||
Set<Integer> teamsIds = fightEndsPacket.getBluePlayers().stream().map(SteamwarUser::get).map(SteamwarUser::getTeam).collect(Collectors.toSet());
|
||||
@ -82,60 +72,172 @@ public class FightEndsHandler extends PacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (teamComboExistedAlready(fightEndsPacket.getBluePlayers(), fightEndsPacket.getRedPlayers(), fightEndsPacket.getGameMode())) {
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
gameModeGames.computeIfAbsent(fightEndsPacket.getGameMode(), s -> new LinkedList<>()).add(new Game(fightEndsPacket.getBluePlayers(), fightEndsPacket.getRedPlayers()));
|
||||
}
|
||||
calcSchemElo(fightEndsPacket);
|
||||
calcUserElo(fightEndsPacket);
|
||||
}
|
||||
|
||||
int blueSchemElo = SchemElo.getCurrentElo(fightEndsPacket.getBlueSchem());
|
||||
int redSchemElo = SchemElo.getCurrentElo(fightEndsPacket.getRedSchem());
|
||||
private void calcUserElo(FightEndsPacket fightEndsPacket) {
|
||||
int blueTeamSize = fightEndsPacket.getBluePlayers().size();
|
||||
int redTeamSize = fightEndsPacket.getRedPlayers().size();
|
||||
|
||||
int blueTeamElo = fightEndsPacket.getBluePlayers().stream().mapToInt(player -> UserElo.getEloOrDefault(player, fightEndsPacket.getGameMode())).sum();
|
||||
int redTeamElo = fightEndsPacket.getRedPlayers().stream().mapToInt(player -> UserElo.getEloOrDefault(player, fightEndsPacket.getGameMode())).sum();
|
||||
|
||||
calculateEloOfTeam(fightEndsPacket.getBlueSchem(), blueSchemElo, redSchemElo, blueTeamElo, redTeamElo, blueResult, fightEndsPacket.getBluePlayers(), fightEndsPacket.getGameMode(), bluePublic || redPublic);
|
||||
calculateEloOfTeam(fightEndsPacket.getRedSchem(), redSchemElo, blueSchemElo, redTeamElo, blueTeamElo, 1 - blueResult, fightEndsPacket.getRedPlayers(), fightEndsPacket.getGameMode(), bluePublic || redPublic);
|
||||
double blueFactor = getBluePlayerFactor(blueTeamSize, redTeamSize) * getTimeFactor(fightEndsPacket.getDuration()) * getBlueEloFactor(blueTeamElo, redTeamElo) * getRematchFactor(fightEndsPacket);
|
||||
double redFactor = getRedPlayerFactor(blueTeamSize, redTeamSize) * getTimeFactor(fightEndsPacket.getDuration()) * getRedEloFactor(blueTeamElo, redTeamElo) * getRematchFactor(fightEndsPacket);
|
||||
|
||||
if (fightEndsPacket.getWin() == 1) {
|
||||
blueFactor *= WIN_FACTOR;
|
||||
redFactor *= LOSE_FACTOR;
|
||||
} else if (fightEndsPacket.getWin() == 2) {
|
||||
blueFactor *= LOSE_FACTOR;
|
||||
redFactor *= WIN_FACTOR;
|
||||
} else {
|
||||
if (redFactor == blueFactor) {
|
||||
blueFactor *= 0;
|
||||
redFactor *= 0;
|
||||
} else if (blueFactor > redFactor) {
|
||||
blueFactor *= DRAW_WIN_FACTOR;
|
||||
redFactor *= DRAW_LOSE_FACTOR;
|
||||
} else {
|
||||
blueFactor *= DRAW_LOSE_FACTOR;
|
||||
redFactor *= DRAW_WIN_FACTOR;
|
||||
}
|
||||
}
|
||||
|
||||
update(fightEndsPacket.getBluePlayers(), fightEndsPacket.getGameMode(), blueFactor);
|
||||
update(fightEndsPacket.getRedPlayers(), fightEndsPacket.getGameMode(), redFactor);
|
||||
}
|
||||
|
||||
private void calculateEloOfTeam(int schemId, int eloSchemOwn, int eloSchemEnemy, int eloTeamOwn, int eloTeamEnemy, double result, List<Integer> players, String gameMode, boolean noPlayerRank) {
|
||||
double winSchemExpectation = calsWinExpectation(eloSchemOwn, eloSchemEnemy);
|
||||
SchemElo.setElo(schemId, (int) Math.round(eloSchemOwn + K * (result - winSchemExpectation)));
|
||||
|
||||
if (noPlayerRank) return;
|
||||
double winTeamExpectation = calsWinExpectation(eloTeamOwn, eloTeamEnemy);
|
||||
private void update(List<Integer> players, String gameMode, double factor) {
|
||||
for (int player : players) {
|
||||
int playerElo = UserElo.getEloOrDefault(player, gameMode);
|
||||
int fights = UserElo.getFightsOfSeason(player, gameMode);
|
||||
UserElo.setElo(player, gameMode, (int) Math.round(playerElo + getK(fights) * (result - winTeamExpectation)));
|
||||
int eloGain = (int) Math.round(MEDIAN_ELO_GAIN * factor);
|
||||
playerElo += eloGain;
|
||||
if (playerElo < 0) playerElo = 0;
|
||||
|
||||
int oldProgression = UserElo.getProgression(player, gameMode);
|
||||
UserElo.setElo(player, gameMode, playerElo);
|
||||
int newProgression = UserElo.getProgression(player, gameMode);
|
||||
|
||||
BaseComponent[] eloGainComponent = TextComponent.fromLegacyText(((eloGain > 0) ? "§a+" : (eloGain == 0 ? "§7" : "§c")) + eloGain);
|
||||
if (oldProgression == newProgression) {
|
||||
send(player(player), UserElo.toEmblem(oldProgression).trim(), eloGainComponent);
|
||||
continue;
|
||||
}
|
||||
animate(player(player), UserElo.toEmblem(oldProgression).trim(), UserElo.toEmblem(newProgression).trim(), (oldProgression < newProgression) ? "§a" : "§c", eloGainComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void animate(ProxiedPlayer player, String oldEmblem, String newEmblem, String arrowColor, BaseComponent[] eloGainComponent) {
|
||||
String finalOldEmblem = (oldEmblem.isEmpty() ? "/" : oldEmblem);
|
||||
String finalNewEmblem = (newEmblem.isEmpty() ? "/" : newEmblem);
|
||||
|
||||
TaskScheduler scheduler = ProxyServer.getInstance().getScheduler();
|
||||
scheduler.schedule(BungeeCore.get(), () -> {
|
||||
send(player, "§8" + finalOldEmblem, eloGainComponent);
|
||||
}, 0, TimeUnit.SECONDS);
|
||||
scheduler.schedule(BungeeCore.get(), () -> {
|
||||
send(player, "§8" + finalOldEmblem + arrowColor + " >", eloGainComponent);
|
||||
}, 500, TimeUnit.MILLISECONDS);
|
||||
scheduler.schedule(BungeeCore.get(), () -> {
|
||||
send(player, "§8" + finalOldEmblem + arrowColor + " >>", eloGainComponent);
|
||||
}, 1000, TimeUnit.MILLISECONDS);
|
||||
scheduler.schedule(BungeeCore.get(), () -> {
|
||||
send(player, "§8" + finalOldEmblem + arrowColor + " >>>", eloGainComponent);
|
||||
}, 1500, TimeUnit.MILLISECONDS);
|
||||
scheduler.schedule(BungeeCore.get(), () -> {
|
||||
send(player, "§8" + finalOldEmblem + arrowColor + " >>> §8" + finalNewEmblem, eloGainComponent);
|
||||
}, 2000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void send(ProxiedPlayer player, String text, BaseComponent[] eloGainComponent) {
|
||||
Title title = new BungeeTitle().title(TextComponent.fromLegacyText(text))
|
||||
.subTitle(eloGainComponent)
|
||||
.fadeIn(5)
|
||||
.stay(40)
|
||||
.fadeOut(5);
|
||||
title.send(player);
|
||||
}
|
||||
|
||||
private ProxiedPlayer player(int userId) {
|
||||
return ProxyServer.getInstance().getPlayer(SteamwarUser.get(userId).getUUID());
|
||||
}
|
||||
|
||||
private void calcSchemElo(FightEndsPacket fightEndsPacket) {
|
||||
double blueResult;
|
||||
if (fightEndsPacket.getWin() == 0) {
|
||||
blueResult = 0.5;
|
||||
} else if (fightEndsPacket.getWin() == 1) {
|
||||
blueResult = 1;
|
||||
} else {
|
||||
blueResult = 0;
|
||||
}
|
||||
|
||||
int blueSchemElo = SchemElo.getCurrentElo(fightEndsPacket.getBlueSchem());
|
||||
int redSchemElo = SchemElo.getCurrentElo(fightEndsPacket.getRedSchem());
|
||||
calcSchemElo(fightEndsPacket.getBlueSchem(), blueSchemElo, redSchemElo, blueResult);
|
||||
calcSchemElo(fightEndsPacket.getRedSchem(), redSchemElo, blueSchemElo, 1 - blueResult);
|
||||
}
|
||||
|
||||
private void calcSchemElo(int eloSchemOwn, int eloSchemEnemy, int schemId, double result) {
|
||||
double winSchemExpectation = calsWinExpectation(eloSchemOwn, eloSchemEnemy);
|
||||
SchemElo.setElo(schemId, (int) Math.round(eloSchemOwn + K * (result - winSchemExpectation)));
|
||||
}
|
||||
|
||||
private double calsWinExpectation(int eloOwn, int eloEnemy) {
|
||||
return 1 / (1 + Math.pow(10, (eloEnemy - eloOwn) / 600f));
|
||||
}
|
||||
|
||||
private double getK(int fights) {
|
||||
return K * Math.max(1.3 - (fights / 200.0), 0.8);
|
||||
private double getBluePlayerFactor(int blueTeam, int redTeam) {
|
||||
return redTeam / (double) blueTeam;
|
||||
}
|
||||
|
||||
private boolean teamComboExistedAlready(List<Integer> bluePlayers, List<Integer> redPlayers, String gameMode) {
|
||||
if (!gameModeGames.containsKey(gameMode)) {
|
||||
return false;
|
||||
private double getRedPlayerFactor(int blueTeam, int redTeam) {
|
||||
return blueTeam / (double) redTeam;
|
||||
}
|
||||
|
||||
private double getTimeFactor(int duration) {
|
||||
if (duration <= 10) {
|
||||
return 0.5;
|
||||
}
|
||||
LinkedList<Game> games = gameModeGames.get(gameMode);
|
||||
long lifetime = fightRanges.getOrDefault(gameMode, defaultFightRange);
|
||||
if (duration <= 60) {
|
||||
return 0.8;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
private double getBlueEloFactor(int blueElo, int redElo) {
|
||||
if (blueElo == 0) blueElo = 1;
|
||||
if (redElo == 0) redElo = 1;
|
||||
return sigmoid(redElo / (double) blueElo);
|
||||
}
|
||||
|
||||
private double getRedEloFactor(int blueElo, int redElo) {
|
||||
if (blueElo == 0) blueElo = 1;
|
||||
if (redElo == 0) redElo = 1;
|
||||
return sigmoid(blueElo / (double) redElo);
|
||||
}
|
||||
|
||||
private double sigmoid(double x) {
|
||||
return 1 / (1 + Math.exp(-2 * (x - 1))) * 2;
|
||||
}
|
||||
|
||||
private double getRematchFactor(FightEndsPacket fightEndsPacket) {
|
||||
gameModeGames.computeIfAbsent(fightEndsPacket.getGameMode(), s -> new LinkedList<>()).add(new Game(fightEndsPacket.getBluePlayers(), fightEndsPacket.getRedPlayers()));
|
||||
|
||||
LinkedList<Game> games = gameModeGames.get(fightEndsPacket.getGameMode());
|
||||
while (!games.isEmpty()) {
|
||||
Game game = games.getFirst();
|
||||
if (game.livedMillis() > lifetime) {
|
||||
if (game.livedMillis() > REMATCH_LIFETIME) {
|
||||
games.removeFirst();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return games.stream().filter(game -> game.isSame(bluePlayers, redPlayers)).count() > fightCounts.getOrDefault(gameMode, defaultFightCount);
|
||||
|
||||
long rematchCount = games.stream().filter(game -> game.isSame(fightEndsPacket.getBluePlayers(), fightEndsPacket.getRedPlayers())).count();
|
||||
return 1.0 / rematchCount;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
@ -642,7 +642,6 @@ RANK_HEADER=§7§lMode {0}
|
||||
RANK_UNPLACED=§eunranked
|
||||
RANK_PLACED=§e{0}§8. §7with §e{1} §7Elo§8.
|
||||
RANK_EMBLEM=§eEmblem§8: {0}
|
||||
RANK_NEEDED_FIGHTS_LEFT={0} §8(§e{1}§7 fights needed§8)
|
||||
|
||||
#Fabric Mod Sender
|
||||
MODIFICATION_BAN_MESSAGE=You tried to bypass / modify the FabricModSender!
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren