Skip to content

Commit

Permalink
Implement scoreboard filters (#1118)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Herrera <[email protected]>
  • Loading branch information
Pablete1234 authored Feb 4, 2023
1 parent 91f2d10 commit 5c294a6
Show file tree
Hide file tree
Showing 17 changed files with 542 additions and 341 deletions.
13 changes: 12 additions & 1 deletion core/src/main/java/tc/oc/pgm/blitz/BlitzConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ public class BlitzConfig {
private final int lives;
private final boolean broadcastLives;
private final Filter filter;
private final Filter scoreboardFilter;
private final Filter joinFilter;

public BlitzConfig(int lives, boolean broadcastLives, Filter filter, Filter joinFilter) {
public BlitzConfig(
int lives,
boolean broadcastLives,
Filter filter,
Filter scoreboardFilter,
Filter joinFilter) {
assertTrue(lives > 0, "lives must be greater than zero");

this.lives = lives;
this.broadcastLives = broadcastLives;
this.filter = filter;
this.scoreboardFilter = scoreboardFilter;
this.joinFilter = joinFilter;
}

Expand All @@ -38,6 +45,10 @@ public Filter getFilter() {
return this.filter;
}

public Filter getScoreboardFilter() {
return scoreboardFilter;
}

public Filter getJoinFilter() {
return joinFilter;
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.util.Vector;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.MatchModule;
import tc.oc.pgm.api.match.MatchScope;
Expand Down Expand Up @@ -55,6 +56,10 @@ public BlitzConfig getConfig() {
return this.config;
}

public Filter getScoreboardFilter() {
return config.getScoreboardFilter();
}

/** Whether or not the player participated in the match and was eliminated. */
public boolean isPlayerEliminated(UUID player) {
return this.eliminatedPlayers.contains(player);
Expand Down
8 changes: 7 additions & 1 deletion core/src/main/java/tc/oc/pgm/blitz/BlitzModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.filters.FilterModule;
import tc.oc.pgm.filters.matcher.StaticFilter;
import tc.oc.pgm.filters.parse.DynamicFilterValidation;
import tc.oc.pgm.filters.parse.FilterParser;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
Expand Down Expand Up @@ -57,6 +58,7 @@ public BlitzModule parse(MapFactory factory, Logger logger, Document doc)
int lives = Integer.MAX_VALUE;
boolean broadcastLives = false;
Filter filter = null;
Filter scoreboardFilter = null;
Filter joinFilter = null;

FilterParser filters = factory.getFilters();
Expand All @@ -66,11 +68,15 @@ public BlitzModule parse(MapFactory factory, Logger logger, Document doc)
XMLUtils.parseNumberInRange(
Node.fromChildOrAttr(blitzEl, "lives"), Integer.class, Range.atLeast(1), 1);
filter = filters.parseProperty(blitzEl, "filter", StaticFilter.ALLOW);
scoreboardFilter =
filters.parseProperty(
blitzEl, "scoreboard-filter", StaticFilter.ALLOW, DynamicFilterValidation.PARTY);
joinFilter = filters.parseProperty(blitzEl, "join-filter", StaticFilter.DENY);
}

if (lives != Integer.MAX_VALUE) {
return new BlitzModule(new BlitzConfig(lives, broadcastLives, filter, joinFilter));
return new BlitzModule(
new BlitzConfig(lives, broadcastLives, filter, scoreboardFilter, joinFilter));
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public static ControlPointDefinition parseControlPoint(
XMLUtils.parseNumber(
el.getAttribute("points-growth"), Float.class, Float.POSITIVE_INFINITY);
boolean showProgress = XMLUtils.parseBoolean(el.getAttribute("show-progress"), koth || pd);
ShowOptions options = ShowOptions.parse(el);
ShowOptions options = ShowOptions.parse(filterParser, el);
Boolean required = XMLUtils.parseBoolean(el.getAttribute("required"), null);

ControlPointDefinition.CaptureCondition captureCondition =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/tc/oc/pgm/core/CoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public CoreModule parse(MapFactory context, Logger logger, Document doc)
}

boolean showProgress = XMLUtils.parseBoolean(coreEl.getAttribute("show-progress"), false);
ShowOptions options = ShowOptions.parse(coreEl);
ShowOptions options = ShowOptions.parse(context.getFilters(), coreEl);
Boolean required = XMLUtils.parseBoolean(coreEl.getAttribute("required"), null);
ProximityMetric proximityMetric =
ProximityMetric.parse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public DestroyableModule parse(MapFactory context, Logger logger, Document doc)
XMLUtils.parseBoolean(destroyableEl.getAttribute("show-progress"), false);
boolean sparks = XMLUtils.parseBoolean(destroyableEl.getAttribute("sparks"), false);
boolean repairable = XMLUtils.parseBoolean(destroyableEl.getAttribute("repairable"), true);
ShowOptions options = ShowOptions.parse(destroyableEl);
ShowOptions options = ShowOptions.parse(context.getFilters(), destroyableEl);
Boolean required = XMLUtils.parseBoolean(destroyableEl.getAttribute("required"), null);
ProximityMetric proximityMetric =
ProximityMetric.parse(
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/tc/oc/pgm/flag/FlagParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public FlagDefinition parseFlag(Element el) throws InvalidXMLException {

String id = el.getAttributeValue("id");
String name = el.getAttributeValue("name");
ShowOptions options = ShowOptions.parse(el);
ShowOptions options = ShowOptions.parse(filterParser, el);
Boolean required = XMLUtils.parseBoolean(el.getAttribute("required"), null);
DyeColor color = XMLUtils.parseDyeColor(el.getAttribute("color"), null);
FeatureReference<TeamFactory> owner =
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/tc/oc/pgm/goals/Goal.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.bukkit.DyeColor;
import org.jetbrains.annotations.Nullable;
import tc.oc.pgm.api.feature.Feature;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.party.Competitor;
import tc.oc.pgm.api.party.Party;
Expand Down Expand Up @@ -50,6 +51,8 @@ default boolean isCompleted(Optional<? extends Competitor> competitor) {
*/
boolean hasShowOption(ShowOption option);

Filter getScoreboardFilter();

boolean isRequired();

/**
Expand Down
19 changes: 16 additions & 3 deletions core/src/main/java/tc/oc/pgm/goals/ShowOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,45 @@
import java.util.EnumSet;
import java.util.Set;
import org.jdom2.Element;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.filters.matcher.StaticFilter;
import tc.oc.pgm.filters.parse.DynamicFilterValidation;
import tc.oc.pgm.filters.parse.FilterParser;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.XMLUtils;

public class ShowOptions {

private final Set<ShowOption> options;
private final Filter scoreboardFilter;

private ShowOptions(Set<ShowOption> options) {
private ShowOptions(Set<ShowOption> options, Filter scoreboardFilter) {
this.options = options;
this.scoreboardFilter = scoreboardFilter;
}

public static ShowOptions parse(Element el) throws InvalidXMLException {
public static ShowOptions parse(FilterParser parser, Element el) throws InvalidXMLException {
Set<ShowOption> options = EnumSet.noneOf(ShowOption.class);
boolean show = XMLUtils.parseBoolean(el.getAttribute("show"), true);
for (ShowOption option : ShowOption.values()) {
if (XMLUtils.parseBoolean(el.getAttribute(option.getName()), show)) {
options.add(option);
}
}
return new ShowOptions(options);
Filter scoreboardFilter =
parser.parseProperty(
el, "scoreboard-filter", StaticFilter.ALLOW, DynamicFilterValidation.MATCH);
return new ShowOptions(options, scoreboardFilter);
}

public boolean hasOption(ShowOption option) {
return options.contains(option);
}

public Filter getScoreboardFilter() {
return scoreboardFilter;
}

@Override
public String toString() {
return options.toString();
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/tc/oc/pgm/goals/SimpleGoal.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.bukkit.Color;
import org.bukkit.DyeColor;
import org.jetbrains.annotations.Nullable;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.map.MapProtos;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.party.Competitor;
Expand Down Expand Up @@ -93,6 +94,10 @@ public boolean hasShowOption(ShowOption flag) {
return this.definition.hasShowOption(flag);
}

public Filter getScoreboardFilter() {
return this.definition.getShowOptions().getScoreboardFilter();
}

@Override
public boolean isRequired() {
Boolean required = getDefinition().isRequired();
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/tc/oc/pgm/score/ScoreConfig.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package tc.oc.pgm.score;

import tc.oc.pgm.api.filter.Filter;

public class ScoreConfig {
public int scoreLimit = -1;
public int deathScore;
public int killScore;
public int mercyLimit;
public int mercyLimitMin;
public Filter scoreboardFilter;
}
5 changes: 5 additions & 0 deletions core/src/main/java/tc/oc/pgm/score/ScoreMatchModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.Vector;
import tc.oc.pgm.api.PGM;
import tc.oc.pgm.api.filter.Filter;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.match.MatchModule;
import tc.oc.pgm.api.match.MatchScope;
Expand Down Expand Up @@ -80,6 +81,10 @@ public boolean hasMercyRule() {
return this.mercyRule != null;
}

public Filter getScoreboardFilter() {
return this.config.scoreboardFilter;
}

public int getScoreLimit() {
assertTrue(hasScoreLimit());

Expand Down
13 changes: 11 additions & 2 deletions core/src/main/java/tc/oc/pgm/score/ScoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import tc.oc.pgm.blitz.BlitzModule;
import tc.oc.pgm.filters.FilterModule;
import tc.oc.pgm.filters.matcher.StaticFilter;
import tc.oc.pgm.filters.parse.DynamicFilterValidation;
import tc.oc.pgm.regions.RegionModule;
import tc.oc.pgm.regions.RegionParser;
import tc.oc.pgm.util.Version;
Expand Down Expand Up @@ -108,8 +109,7 @@ public ScoreModule parse(MapFactory factory, Logger logger, Document doc)
}

// For backwards compatibility, default kill/death points to 1 if proto is old and <king/>
// tag
// is not present
// tag is not present
boolean scoreKillsByDefault =
proto.isOlderThan(MapProtos.DEFAULT_SCORES_TO_ZERO) && scoreEl.getChild("king") == null;
config.deathScore =
Expand All @@ -119,6 +119,15 @@ public ScoreModule parse(MapFactory factory, Logger logger, Document doc)
XMLUtils.parseNumber(
scoreEl.getChild("kills"), Integer.class, scoreKillsByDefault ? 1 : 0);

config.scoreboardFilter =
factory
.getFilters()
.parseProperty(
scoreEl,
"scoreboard-filter",
StaticFilter.ALLOW,
DynamicFilterValidation.PARTY);

for (Element scoreBoxEl : scoreEl.getChildren("box")) {
int points =
XMLUtils.parseNumber(
Expand Down
115 changes: 115 additions & 0 deletions core/src/main/java/tc/oc/pgm/scoreboard/RenderContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package tc.oc.pgm.scoreboard;

import static net.kyori.adventure.text.Component.empty;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import tc.oc.pgm.api.match.Match;
import tc.oc.pgm.api.party.Competitor;
import tc.oc.pgm.api.party.Party;
import tc.oc.pgm.blitz.BlitzMatchModule;
import tc.oc.pgm.goals.Goal;
import tc.oc.pgm.goals.GoalMatchModule;
import tc.oc.pgm.goals.ShowOption;
import tc.oc.pgm.score.ScoreMatchModule;
import tc.oc.pgm.wool.WoolMatchModule;

class RenderContext {
public final @NotNull Match match;
public final @NotNull Party viewer;
public final boolean hasScores;
public final boolean isBlitz;
public final boolean isCompactWool;
public final GoalMatchModule gmm;
public final Set<Competitor> competitorsWithGoals;
public final List<Goal<?>> sharedGoals;
public final boolean isSuperCompact;

private final List<Component> rows = new ArrayList<>(SidebarRenderer.MAX_ROWS);

private boolean addSpace = false;

public RenderContext(@NotNull Match match, @NotNull Party viewer) {
this.match = match;
this.viewer = viewer;
this.hasScores = match.getModule(ScoreMatchModule.class) != null;
this.isBlitz = match.getModule(BlitzMatchModule.class) != null;
this.isCompactWool = isCompactWool();

this.gmm = match.needModule(GoalMatchModule.class);
this.competitorsWithGoals = new HashSet<>();
this.sharedGoals = new ArrayList<>();

// Count the rows used for goals
for (Goal<?> goal : gmm.getGoals()) {
if (goal.hasShowOption(ShowOption.SHOW_SIDEBAR)
&& goal.getScoreboardFilter().response(match)) {
if (goal.isShared()) {
sharedGoals.add(goal);
} else {
competitorsWithGoals.addAll(gmm.getCompetitors(goal));
}
}
}
this.isSuperCompact = isSuperCompact();
}

public void startSection() {
addSpace = rows.size() > 0;
}

public void addRow(Component row) {
if (addSpace) {
this.rows.add(empty());
addSpace = false;
}
this.rows.add(row);
}

public List<Component> getResult() {
// Needs at least one empty row for scoreboard to show
if (rows.isEmpty()) {
rows.add(empty());
}
return rows;
}

public int size() {
return rows.size();
}

public boolean isFull() {
return rows.size() >= SidebarRenderer.MAX_ROWS;
}

public boolean isEmpty() {
return rows.isEmpty();
}

// Determines if wool objectives should be given their own rows, or all shown on 1 row.
private boolean isCompactWool() {
WoolMatchModule wmm = match.getModule(WoolMatchModule.class);
return wmm != null
&& !(wmm.getWools().keySet().size() * 2 - 1 + wmm.getWools().values().size()
< SidebarRenderer.MAX_ROWS);
}

// Determines if all the map objectives can fit onto the scoreboard with empty rows in between.
private boolean isSuperCompact() {
int rowsUsed = competitorsWithGoals.size() * 2 - 1;

if (isCompactWool()) {
WoolMatchModule wmm = match.needModule(WoolMatchModule.class);
rowsUsed += wmm.getWools().keySet().size();
} else {
GoalMatchModule gmm = match.needModule(GoalMatchModule.class);
rowsUsed += gmm.getGoals().size();
}

return !(rowsUsed < SidebarRenderer.MAX_ROWS);
}
}
Loading

0 comments on commit 5c294a6

Please sign in to comment.