ProjectAres/PGM/src/main/java/tc/oc/pgm/quota/QuotaMatchModule.java

150 lines
5.0 KiB
Java

package tc.oc.pgm.quota;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Range;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import java.time.Duration;
import java.time.Instant;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.formatting.PeriodFormats;
import tc.oc.commons.bukkit.chat.Links;
import tc.oc.pgm.join.JoinDenied;
import tc.oc.pgm.join.JoinHandler;
import tc.oc.pgm.join.JoinMatchModule;
import tc.oc.pgm.join.JoinMethod;
import tc.oc.pgm.join.JoinRequest;
import tc.oc.pgm.join.JoinResult;
import tc.oc.pgm.match.MatchModule;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.module.ModuleDescription;
@ModuleDescription(name = "Match Quota")
public class QuotaMatchModule extends MatchModule implements JoinHandler {
class QuotaStatus {
final Quota quota;
final Instant now;
int matchesPlayed;
Instant earliestJoinTime;
QuotaStatus(Quota quota, Instant now) {
this.quota = quota;
this.now = now;
}
int matchesRemaining() {
return Math.max(0, quota.maximum() - matchesPlayed);
}
Collection<BaseComponent> format() {
final ImmutableList.Builder<BaseComponent> lines = ImmutableList.builder();
lines.add(new TranslatableComponent(
"matchQuota.matchCounts",
new Component(String.valueOf(matchesPlayed), ChatColor.AQUA),
new Component(String.valueOf(quota.maximum()), ChatColor.AQUA)
));
if(matchesRemaining() == 0) {
lines.add(new TranslatableComponent(
"matchQuota.nextMatch",
new Component(PeriodFormats.briefNaturalApproximate(now, earliestJoinTime), ChatColor.AQUA)
));
}
if(!quota.premium()) {
Range<Integer> premiumRange = getConfig().getPremiumMaximum();
if(premiumRange != null) {
if(premiumRange.upperEndpoint() == Integer.MAX_VALUE) {
lines.add(Links.shopPlug("shop.plug.rankedMatches.unlimited"));
} else {
BaseComponent premiumLimit = new Component(String.valueOf(premiumRange.upperEndpoint()), ChatColor.AQUA);
if(premiumRange.upperEndpoint().equals(premiumRange.lowerEndpoint())) {
lines.add(Links.shopPlug("shop.plug.rankedMatches.uniform", premiumLimit));
} else {
lines.add(Links.shopPlug("shop.plug.rankedMatches.upto", premiumLimit));
}
}
}
}
return lines.build();
}
}
private final QuotaConfig config;
@Inject QuotaMatchModule(QuotaConfig config) {
this.config = config;
}
@Override
public void load() {
super.load();
getMatch().needMatchModule(JoinMatchModule.class).registerHandler(this);
}
public QuotaConfig getConfig() {
return config;
}
public @Nullable Quota getQuota(MatchPlayer player) {
for(Quota quota : getConfig().getQuotas()) {
if(quota.appliesTo(player)) return quota;
}
return null;
}
public QuotaStatus getQuotaStatus(MatchPlayer player, Quota quota, Instant now) {
Duration interval = quota.interval();
QuotaStatus status = new QuotaStatus(quota, now);
status.matchesPlayed = 0;
status.earliestJoinTime = now;
// Note that this list is reverse-chrono order
for(Instant joinTime : player.recentMatchCommitments()) {
Instant expireTime = joinTime.plus(interval);
if(expireTime.isAfter(now)) {
if(++status.matchesPlayed == quota.maximum()) {
status.earliestJoinTime = expireTime;
}
} else {
break;
}
}
return status;
}
public @Nullable QuotaStatus getQuotaStatus(MatchPlayer player) {
Quota quota = getQuota(player);
return quota == null ? null : getQuotaStatus(player, quota, Instant.now());
}
public void sendQuotaInfo(MatchPlayer player) {
final QuotaStatus status = getQuotaStatus(player);
if(status != null) {
status.format().forEach(player::sendMessage);
}
}
@Override
public @Nullable JoinResult queryJoin(MatchPlayer joining, JoinRequest request) {
if(request.method() == JoinMethod.USER && !getMatch().hasEverParticipated(joining.getPlayerId())) {
final QuotaStatus status = getQuotaStatus(joining);
if(status != null && status.matchesRemaining() <= 0) {
return JoinDenied.error("matchQuota.outOfMatches")
.also(status.format());
}
}
return null;
}
}