Skip to content

Commit 7de8cda

Browse files
refactor(elections): deduplicate NPCs with abstract bases, fix ElectionDisplay thread safety
Extract AbstractCandidateNPC and AbstractCurrentMayorNPC to eliminate duplicated NPC logic. Fix ElectionDisplay HashMap to ConcurrentHashMap with player cleanup.
1 parent a1d126c commit 7de8cda

12 files changed

Lines changed: 217 additions & 471 deletions

type.hub/src/main/java/net/swofty/type/hub/ElectionDisplay.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,54 @@
44
import net.swofty.commons.StringUtility;
55
import net.swofty.type.generic.HypixelGenericLoader;
66
import net.swofty.type.generic.entity.hologram.PlayerHolograms;
7+
import net.swofty.type.generic.i18n.I18n;
78
import net.swofty.type.generic.user.HypixelPlayer;
89
import net.swofty.type.skyblockgeneric.calendar.CalendarEvent;
910
import net.swofty.type.skyblockgeneric.calendar.SkyBlockCalendar;
11+
import net.swofty.type.skyblockgeneric.elections.ElectionData;
1012
import net.swofty.type.skyblockgeneric.elections.ElectionManager;
11-
import net.swofty.type.skyblockgeneric.elections.SkyBlockMayor;
1213

1314
import java.util.ArrayList;
14-
import java.util.HashMap;
1515
import java.util.List;
16+
import java.util.Locale;
1617
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.UUID;
20+
import java.util.concurrent.ConcurrentHashMap;
21+
import java.util.stream.Collectors;
1722

1823
public class ElectionDisplay {
1924

20-
private static final Map<HypixelPlayer, PlayerHolograms.ExternalPlayerHologram> holos = new HashMap<>();
25+
private static final Map<HypixelPlayer, PlayerHolograms.ExternalPlayerHologram> holos = new ConcurrentHashMap<>();
2126

2227
public static void addAndUpdate() {
28+
Set<UUID> loadedUuids = HypixelGenericLoader.getLoadedPlayers().stream()
29+
.map(HypixelPlayer::getUuid)
30+
.collect(Collectors.toSet());
31+
holos.keySet().removeIf(p -> !loadedUuids.contains(p.getUuid()));
32+
2333
HypixelGenericLoader.getLoadedPlayers().forEach(player -> {
34+
Locale l = player.getLocale();
2435
long timeLeft = SkyBlockCalendar.ticksUntilEvent(CalendarEvent.ELECTION_CLOSE);
2536
String timeLeftFormatted = StringUtility.formatTimeLeft(timeLeft * 50L);
2637

2738
List<String> message = new ArrayList<>(List.of(
28-
"§e§lMAYOR ELECTIONS",
29-
"§bYear " + SkyBlockCalendar.getYear(),
30-
"§eTime left: §a" + timeLeftFormatted
39+
I18n.string("gui_election.display.title", l),
40+
I18n.string("gui_election.display.year", l, Map.of("year", String.valueOf(SkyBlockCalendar.getYear()))),
41+
I18n.string("gui_election.display.time_left", l, Map.of("time", timeLeftFormatted))
3142
));
3243

3344
String vote = ElectionManager.getPlayerVote(player.getUuid());
3445
if (vote != null) {
35-
SkyBlockMayor mayor = SkyBlockMayor.valueOf(vote);
36-
String colouredName = mayor.getColor() + mayor.getDisplayName();
37-
message.add("§eYour vote: §f" + colouredName);
38-
message.add("§e§lCLICK TO SWITCH");
46+
ElectionData data = ElectionManager.getElectionData();
47+
ElectionData.CandidateData candidateData = data.getCandidates().stream()
48+
.filter(c -> c.getMayorName().equals(vote))
49+
.findFirst().orElse(null);
50+
if (candidateData != null) {
51+
message.add(I18n.string("gui_election.display.your_vote", l,
52+
Map.of("candidate", candidateData.getColoredName())));
53+
message.add(I18n.string("gui_election.display.click_switch", l));
54+
}
3955
}
4056

4157
if (!holos.containsKey(player)) {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package net.swofty.type.hub.npcs.election;
2+
3+
import net.minestom.server.coordinate.Pos;
4+
import net.swofty.type.generic.entity.npc.HypixelNPC;
5+
import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration;
6+
import net.swofty.type.generic.event.custom.NPCInteractEvent;
7+
import net.swofty.type.generic.i18n.I18n;
8+
import net.swofty.type.generic.user.HypixelPlayer;
9+
import net.swofty.type.skyblockgeneric.elections.ElectionData;
10+
import net.swofty.type.skyblockgeneric.elections.ElectionManager;
11+
import net.swofty.type.skyblockgeneric.elections.SkyBlockMayor;
12+
import org.jspecify.annotations.NonNull;
13+
14+
import java.util.List;
15+
16+
public abstract class AbstractCandidateNPC extends HypixelNPC {
17+
private final int index;
18+
19+
private static ElectionData.CandidateData getCandidateData(int index) {
20+
ElectionData data = ElectionManager.getElectionData();
21+
if (!data.isElectionOpen()) return null;
22+
List<ElectionData.CandidateData> candidates = data.getCandidates();
23+
if (candidates.size() <= index) return null;
24+
return candidates.get(index);
25+
}
26+
27+
protected AbstractCandidateNPC(int index, Pos candidatePosition) {
28+
super(new HumanConfiguration() {
29+
@Override
30+
public String[] holograms(HypixelPlayer player) {
31+
ElectionData.CandidateData candidateData = getCandidateData(index);
32+
if (candidateData == null) return new String[]{
33+
I18n.string("npcs_hub.election.mayor_unknown", player.getLocale()),
34+
I18n.string("npcs_hub.election.candidate_label", player.getLocale()),
35+
I18n.string("npcs_hub.election.click", player.getLocale())
36+
};
37+
String color = candidateData.getColor();
38+
SkyBlockMayor mayor = candidateData.getMayorEnum();
39+
String name = mayor != null ? mayor.getDisplayName() : candidateData.getMayorName();
40+
return new String[]{
41+
color + name,
42+
color + I18n.string("npcs_hub.election.candidate_label", player.getLocale()),
43+
I18n.string("npcs_hub.election.click", player.getLocale())
44+
};
45+
}
46+
47+
@Override
48+
public String signature(HypixelPlayer player) {
49+
ElectionData.CandidateData candidateData = getCandidateData(index);
50+
if (candidateData == null) return "";
51+
SkyBlockMayor mayor = candidateData.getMayorEnum();
52+
return mayor != null ? mayor.getSignature() : "";
53+
}
54+
55+
@Override
56+
public boolean visible(HypixelPlayer player) {
57+
return getCandidateData(index) != null;
58+
}
59+
60+
@Override
61+
public String texture(HypixelPlayer player) {
62+
ElectionData.CandidateData candidateData = getCandidateData(index);
63+
if (candidateData == null) return "";
64+
SkyBlockMayor mayor = candidateData.getMayorEnum();
65+
return mayor != null ? mayor.getTexture() : "";
66+
}
67+
68+
@Override
69+
public Pos position(HypixelPlayer player) {
70+
return candidatePosition;
71+
}
72+
73+
@Override
74+
public boolean looking(HypixelPlayer player) {
75+
return true;
76+
}
77+
78+
@Override
79+
public @NonNull String chatName() {
80+
ElectionData.CandidateData candidateData = getCandidateData(index);
81+
if (candidateData == null) return "Mayor ???";
82+
String color = candidateData.getColor();
83+
SkyBlockMayor mayor = candidateData.getMayorEnum();
84+
String name = mayor != null ? mayor.getDisplayName() : candidateData.getMayorName();
85+
return color + "Mayor " + name;
86+
}
87+
});
88+
this.index = index;
89+
}
90+
91+
@Override
92+
public void onClick(NPCInteractEvent event) {
93+
if (getCandidateData(index) == null) return;
94+
sendNPCMessage(event.player(), I18n.string("npcs_hub.election.running", event.player().getLocale()));
95+
}
96+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package net.swofty.type.hub.npcs.election;
2+
3+
import net.minestom.server.coordinate.Pos;
4+
import net.swofty.type.generic.entity.npc.HypixelNPC;
5+
import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration;
6+
import net.swofty.type.generic.event.custom.NPCInteractEvent;
7+
import net.swofty.type.generic.i18n.I18n;
8+
import net.swofty.type.generic.user.HypixelPlayer;
9+
import net.swofty.type.skyblockgeneric.elections.ElectionManager;
10+
import net.swofty.type.skyblockgeneric.elections.SkyBlockMayor;
11+
import net.swofty.type.skyblockgeneric.gui.inventories.election.MayorMenuView;
12+
13+
public abstract class AbstractCurrentMayorNPC extends HypixelNPC {
14+
15+
protected AbstractCurrentMayorNPC(Pos mayorPosition) {
16+
super(new HumanConfiguration() {
17+
@Override
18+
public String[] holograms(HypixelPlayer player) {
19+
SkyBlockMayor mayor = ElectionManager.getCurrentMayor();
20+
if (mayor == null) return new String[]{
21+
I18n.string("npcs_hub.election.mayor_unknown", player.getLocale()),
22+
I18n.string("npcs_hub.election.click", player.getLocale())
23+
};
24+
return new String[]{
25+
"Mayor " + mayor.getDisplayName(),
26+
I18n.string("npcs_hub.election.click", player.getLocale())
27+
};
28+
}
29+
30+
@Override
31+
public String signature(HypixelPlayer player) {
32+
SkyBlockMayor mayor = ElectionManager.getCurrentMayor();
33+
return mayor != null ? mayor.getSignature() : "";
34+
}
35+
36+
@Override
37+
public String texture(HypixelPlayer player) {
38+
SkyBlockMayor mayor = ElectionManager.getCurrentMayor();
39+
return mayor != null ? mayor.getTexture() : "";
40+
}
41+
42+
@Override
43+
public Pos position(HypixelPlayer player) {
44+
return mayorPosition;
45+
}
46+
47+
@Override
48+
public boolean looking(HypixelPlayer player) {
49+
return true;
50+
}
51+
});
52+
}
53+
54+
@Override
55+
public void onClick(NPCInteractEvent event) {
56+
SkyBlockMayor mayor = ElectionManager.getCurrentMayor();
57+
if (mayor == null) {
58+
event.player().sendMessage(I18n.string("npcs_hub.election.hello", event.player().getLocale()));
59+
return;
60+
}
61+
event.player().openView(new MayorMenuView());
62+
}
63+
}
Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,9 @@
11
package net.swofty.type.hub.npcs.election;
22

33
import net.minestom.server.coordinate.Pos;
4-
import net.swofty.type.generic.entity.npc.HypixelNPC;
5-
import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration;
6-
import net.swofty.type.generic.event.custom.NPCInteractEvent;
7-
import net.swofty.type.generic.user.HypixelPlayer;
8-
import net.swofty.type.skyblockgeneric.elections.ElectionData;
9-
import net.swofty.type.skyblockgeneric.elections.ElectionManager;
10-
import net.swofty.type.skyblockgeneric.elections.SkyBlockMayor;
11-
import org.jspecify.annotations.NonNull;
12-
13-
import java.util.List;
14-
15-
public class NPCCandidate1 extends HypixelNPC {
16-
private static final int INDEX = 0;
17-
18-
private static SkyBlockMayor getCandidate() {
19-
ElectionData data = ElectionManager.getElectionData();
20-
if (!data.isElectionOpen()) return null;
21-
List<ElectionData.CandidateData> candidates = data.getCandidates();
22-
if (candidates.size() <= INDEX) return null;
23-
return candidates.get(INDEX).getMayorEnum().setColorFromIndex(INDEX);
24-
}
254

5+
public class NPCCandidate1 extends AbstractCandidateNPC {
266
public NPCCandidate1() {
27-
super(new HumanConfiguration() {
28-
@Override
29-
public String[] holograms(HypixelPlayer player) {
30-
SkyBlockMayor mayor = getCandidate();
31-
if (mayor == null) return new String[]{"Mayor ???", "Candidate", "§e§lCLICK"};
32-
return new String[]{mayor.getColor() + mayor.getDisplayName(), mayor.getColor() + "Candidate", "§e§lCLICK"};
33-
}
34-
35-
@Override
36-
public String signature(HypixelPlayer player) {
37-
SkyBlockMayor mayor = getCandidate();
38-
return mayor != null ? mayor.getSignature() : "";
39-
}
40-
41-
@Override
42-
public boolean visible(HypixelPlayer player) {
43-
return getCandidate() != null;
44-
}
45-
46-
@Override
47-
public String texture(HypixelPlayer player) {
48-
SkyBlockMayor mayor = getCandidate();
49-
return mayor != null ? mayor.getTexture() : "";
50-
}
51-
52-
@Override
53-
public Pos position(HypixelPlayer player) {
54-
return new Pos(-11.5, 50.0625, 34.5, -90, 0);
55-
}
56-
57-
@Override
58-
public boolean looking(HypixelPlayer player) {
59-
return true;
60-
}
61-
62-
@Override
63-
public @NonNull String chatName() {
64-
SkyBlockMayor mayor = getCandidate();
65-
return mayor != null ? mayor.getColor() + "Mayor " + mayor.getDisplayName() : "Mayor ???";
66-
}
67-
});
68-
}
69-
70-
@Override
71-
public void onClick(NPCInteractEvent event) {
72-
SkyBlockMayor mayor = getCandidate();
73-
if (mayor == null) return;
74-
sendNPCMessage(event.player(), "Running for mayor!");
7+
super(0, new Pos(-11.5, 50.0625, 34.5, -90, 0));
758
}
769
}
Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,9 @@
11
package net.swofty.type.hub.npcs.election;
22

33
import net.minestom.server.coordinate.Pos;
4-
import net.swofty.type.generic.entity.npc.HypixelNPC;
5-
import net.swofty.type.generic.entity.npc.configuration.HumanConfiguration;
6-
import net.swofty.type.generic.event.custom.NPCInteractEvent;
7-
import net.swofty.type.generic.user.HypixelPlayer;
8-
import net.swofty.type.skyblockgeneric.elections.ElectionData;
9-
import net.swofty.type.skyblockgeneric.elections.ElectionManager;
10-
import net.swofty.type.skyblockgeneric.elections.SkyBlockMayor;
11-
import org.jspecify.annotations.NonNull;
12-
13-
import java.util.List;
14-
15-
public class NPCCandidate2 extends HypixelNPC {
16-
private static final int INDEX = 1;
17-
18-
private static SkyBlockMayor getCandidate() {
19-
ElectionData data = ElectionManager.getElectionData();
20-
if (!data.isElectionOpen()) return null;
21-
List<ElectionData.CandidateData> candidates = data.getCandidates();
22-
if (candidates.size() <= INDEX) return null;
23-
return candidates.get(INDEX).getMayorEnum().setColorFromIndex(INDEX);
24-
}
254

5+
public class NPCCandidate2 extends AbstractCandidateNPC {
266
public NPCCandidate2() {
27-
super(new HumanConfiguration() {
28-
@Override
29-
public String[] holograms(HypixelPlayer player) {
30-
SkyBlockMayor mayor = getCandidate();
31-
if (mayor == null) return new String[]{"Mayor ???", "Candidate", "§e§lCLICK"};
32-
return new String[]{mayor.getColor() + mayor.getDisplayName(), mayor.getColor() + "Candidate", "§e§lCLICK"};
33-
}
34-
35-
@Override
36-
public String signature(HypixelPlayer player) {
37-
SkyBlockMayor mayor = getCandidate();
38-
return mayor != null ? mayor.getSignature() : "";
39-
}
40-
41-
@Override
42-
public boolean visible(HypixelPlayer player) {
43-
return getCandidate() != null;
44-
}
45-
46-
@Override
47-
public String texture(HypixelPlayer player) {
48-
SkyBlockMayor mayor = getCandidate();
49-
return mayor != null ? mayor.getTexture() : "";
50-
}
51-
52-
@Override
53-
public Pos position(HypixelPlayer player) {
54-
return new Pos(-8.5, 50.0625, 25.5, -45, 0);
55-
}
56-
57-
@Override
58-
public boolean looking(HypixelPlayer player) {
59-
return true;
60-
}
61-
62-
@Override
63-
public @NonNull String chatName() {
64-
SkyBlockMayor mayor = getCandidate();
65-
return mayor != null ? mayor.getColor() + "Mayor " + mayor.getDisplayName() : "Mayor ???";
66-
}
67-
});
68-
}
69-
70-
@Override
71-
public void onClick(NPCInteractEvent event) {
72-
SkyBlockMayor mayor = getCandidate();
73-
if (mayor == null) return;
74-
sendNPCMessage(event.player(), "Running for mayor!");
7+
super(1, new Pos(-8.5, 50.0625, 25.5, -45, 0));
758
}
769
}

0 commit comments

Comments
 (0)