Skip to content

Commit 80df152

Browse files
refactor: introduce event-driven page completion handling (#82)
* chore: remove some comments * feat(page): add PageDiscoveryCompletedEvent * chore(page): update constructor handling * feat(page): add listener for the new discovery event * chore(game): add new event listener and cleanup code * chore: use smart casts instead of a direct cast * docs(page): improve documentation * docs(page): improve grammar --------- Co-authored-by: theEvilReaper <theEvilReaper@users.noreply.github.com>
1 parent 47ba305 commit 80df152

8 files changed

Lines changed: 86 additions & 35 deletions

File tree

common/src/main/java/net/onelitefeather/cygnus/common/page/PageCalculation.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,27 @@
44
import net.onelitefeather.cygnus.common.config.GameConfig;
55

66
/**
7-
* This class calculates the amount of pages that should be allocated for the dynamic page system.
8-
* The amount of pages is calculated based on the amount of players online.
9-
* If the amount of players online is less than 4, the minimum amount of pages will be returned.
10-
* Otherwise, the amount of pages will be calculated based on the amount of players online.
7+
* Utility class for calculating the number of pages allocated for the dynamic page system.
8+
* The page count is based on the number of current online players.
119
*
1210
* @author theEvilReaper
13-
* @version 1.0.0
11+
* @version 1.1.0
1412
* @since 1.0.0
1513
*/
14+
1615
public final class PageCalculation {
1716

1817
private static final int PLAYER_SIZE_FOR_DYNAMIC_PAGE_ALLOCATION = 4;
1918
private static final int PAGE_COUNT_MULTIPLIER = 2;
2019

2120
/**
22-
* Calculates the amount of pages that should be allocated for the dynamic page system.
21+
* Calculates the number of pages to allocate for the dynamic page system.
22+
* <p>
23+
* If the number of online players (excluding one) is less than {@value #PLAYER_SIZE_FOR_DYNAMIC_PAGE_ALLOCATION},
24+
* {@link GameConfig#MIN_PAGE_COUNT} is returned. Otherwise, the page count is determined by
25+
* multiplying the adjusted player count by {@value #PAGE_COUNT_MULTIPLIER}.
2326
*
24-
* @return the amount of pages that should be allocated
27+
* @return the number of pages to allocate, at least {@link GameConfig#MIN_PAGE_COUNT}
2528
*/
2629
public static int calculatePageAmount() {
2730
int currentPlayers = MinecraftServer.getConnectionManager().getOnlinePlayers().size();

common/src/main/java/net/onelitefeather/cygnus/common/page/PageEntity.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@
2222
import java.util.concurrent.CompletableFuture;
2323

2424
/**
25-
* The {@link PageEntity} is a custom entity which represents a page that can be collected by the player.
26-
* The entity is used to display the page and to interact with it.
27-
* To improve the interaction the entity has a hitbox which is used to detect the interaction.
25+
* Represents a collectible page entity that can be picked up by a player.
26+
* The entity consists of a visual display and an interaction hitbox to improve pickup detection.
2827
*
2928
* @author theEvilReaper
30-
* @version 1.0.0
29+
* @version 1.1.0
3130
* @since 1.0.0
32-
**/
31+
*/
3332
@SuppressWarnings("java:S3252")
3433
public final class PageEntity extends Entity {
3534

common/src/main/java/net/onelitefeather/cygnus/common/page/PageProvider.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import net.kyori.adventure.text.Component;
44
import net.kyori.adventure.text.format.NamedTextColor;
55
import net.minestom.server.entity.Player;
6+
import net.minestom.server.event.EventDispatcher;
67
import net.minestom.server.instance.Instance;
78
import net.minestom.server.utils.Direction;
89
import net.minestom.server.utils.validate.Check;
910
import net.onelitefeather.cygnus.common.Messages;
11+
import net.onelitefeather.cygnus.common.page.event.PageDiscoveryCompletedEvent;
1012
import net.onelitefeather.cygnus.common.util.Helper;
1113
import net.theevilreaper.aves.util.Broadcaster;
12-
import net.theevilreaper.aves.util.functional.VoidConsumer;
14+
import net.theevilreaper.xerus.api.phase.GamePhase;
1315
import org.slf4j.Logger;
1416
import org.slf4j.LoggerFactory;
1517

@@ -26,8 +28,10 @@
2628
import static net.onelitefeather.cygnus.common.config.GameConfig.MIN_ACTIVE_PAGE_COUNT;
2729

2830
/**
31+
* Handles the logic to manage and spawn pages during the {@link GamePhase}.
32+
*
2933
* @author theEvilReaper
30-
* @version 1.0.0
34+
* @version 1.1.0
3135
* @since 1.0.0
3236
**/
3337
@SuppressWarnings("java:S3252")
@@ -37,7 +41,6 @@ public final class PageProvider {
3741

3842
private static final Lock CACHE_LOCK = new ReentrantLock();
3943
private static final Lock PAGE_LOCK = new ReentrantLock();
40-
private final VoidConsumer pageFinishFunction;
4144
private final List<PageResource> globalCache;
4245
private final Map<UUID, PageEntity> activePages;
4346
private final Map<UUID, PageResource> usedResources;
@@ -47,8 +50,7 @@ public final class PageProvider {
4750

4851
private Component pageStatus = Component.empty();
4952

50-
public PageProvider(VoidConsumer pageFinishFunction) {
51-
this.pageFinishFunction = pageFinishFunction;
53+
public PageProvider() {
5254
this.globalCache = new ArrayList<>();
5355
this.usedResources = new HashMap<>();
5456
this.activePages = new HashMap<>();
@@ -57,13 +59,18 @@ public PageProvider(VoidConsumer pageFinishFunction) {
5759
this.currentPageCount = 1;
5860
}
5961

60-
public void loadPageData(Set<PageResource> positions) {
62+
/**
63+
* Loads the required page data from the given set of {@link PageResource}s.
64+
*
65+
* @param resources given set of page resources
66+
*/
67+
public void loadPageData(Set<PageResource> resources) {
6168
Check.argCondition(!globalCache.isEmpty(), "Can't load pages twice");
6269

63-
if (positions.isEmpty()) {
70+
if (resources.isEmpty()) {
6471
throw new IllegalStateException("Can't load a map without any pages");
6572
}
66-
this.globalCache.addAll(positions);
73+
this.globalCache.addAll(resources);
6774
}
6875

6976
public void collectStartPages(Instance instance) {
@@ -90,13 +97,21 @@ public void collectStartPages(Instance instance) {
9097
LOGGER.info("This current page count is {}", currentPageCount);
9198
}
9299

100+
/**
101+
* Sets the max page amount.
102+
*
103+
* @param maxPageAmount to set
104+
*/
93105
public void setMaxPageAmount(int maxPageAmount) {
94106
if (this.maxPageAmount != 0) {
95107
throw new IllegalStateException("The max page amount can't be set twice");
96108
}
97109
this.maxPageAmount = maxPageAmount;
98110
}
99111

112+
/**
113+
* Spawns all pages that are currently in the active page map.
114+
*/
100115
public void spawn() {
101116
this.updatePageDisplay();
102117
try {
@@ -119,7 +134,6 @@ public void cleanUp() {
119134
}
120135

121136
public void triggerTTLHandling(UUID uuid) {
122-
// Fix #8: isEmpty-Check VOR removeEntity, damit activePages.get(uuid) noch gültig ist
123137
if (this.globalCache.isEmpty()) {
124138
this.activePages.get(uuid).enableInteraction();
125139
return;
@@ -140,7 +154,6 @@ public void triggerTTLHandling(UUID uuid) {
140154

141155
pageEntity.teleport(Helper.updatePosition(newPos.position().asPos(), newPos.face()));
142156

143-
// Fix #6: activePages.put unter PAGE_LOCK statt CACHE_LOCK
144157
try {
145158
PAGE_LOCK.lock();
146159
this.activePages.put(pageEntity.getHitBoxUUID(), pageEntity);
@@ -161,7 +174,7 @@ public void triggerPageFound(Player player, UUID uuid) {
161174
updatePageData(pageEntity);
162175

163176
if (this.currentFoundedPageCount >= maxPageAmount) {
164-
this.pageFinishFunction.apply();
177+
EventDispatcher.call(new PageDiscoveryCompletedEvent());
165178
}
166179
}
167180

common/src/main/java/net/onelitefeather/cygnus/common/page/PageResource.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
* during the GamePhase. It includes a string variable that represents the face used in the setup process.
99
* When spawning a page entity, it uses the opposite face from the provided face.
1010
*
11-
* @param position The position at which to spawn a page.
12-
* @param face The face used in the resource setup process.
11+
* @param position at which to spawn a page
12+
* @param face used in the resource setup process
1313
*/
14-
public record PageResource(Point position, Direction face) { }
14+
public record PageResource(Point position, Direction face) {
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package net.onelitefeather.cygnus.common.page.event;
2+
3+
import net.minestom.server.event.Event;
4+
5+
/**
6+
* Event will be fired when each available page has been discovered by the players.
7+
*
8+
* @author theEvilReaper
9+
* @version 1.0.0
10+
* @since 2.3.1
11+
*/
12+
public class PageDiscoveryCompletedEvent implements Event {
13+
}

common/src/test/java/net/onelitefeather/cygnus/common/page/PageProviderTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void testPageTwiceLoading() {
1515
Set<PageResource> pageResources = Set.of(
1616
new PageResource(Pos.ZERO, Direction.NORTH)
1717
);
18-
PageProvider pageProvider = new PageProvider(() -> {});
18+
PageProvider pageProvider = new PageProvider();
1919
assertNotNull(pageProvider);
2020

2121
pageProvider.loadPageData(pageResources);
@@ -31,7 +31,7 @@ void testPageTwiceLoading() {
3131

3232
@Test
3333
void testEmptyPageResourceUsage() {
34-
PageProvider pageProvider = new PageProvider(() -> {});
34+
PageProvider pageProvider = new PageProvider();
3535
assertNotNull(pageProvider);
3636
Set<PageResource> pageResources = Set.of();
3737

game/src/main/java/net/onelitefeather/cygnus/Cygnus.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package net.onelitefeather.cygnus;
22

3+
import net.onelitefeather.cygnus.common.page.event.PageDiscoveryCompletedEvent;
34
import net.onelitefeather.cygnus.event.GameStartEvent;
45
import net.onelitefeather.cygnus.listener.game.GameStartListener;
6+
import net.onelitefeather.cygnus.listener.page.PageDiscoveryCompleteListener;
57
import net.onelitefeather.cygnus.map.GameMapProvider;
68
import net.onelitefeather.cygnus.map.event.GameMapLoadedEvent;
79
import net.theevilreaper.aves.map.provider.AbstractMapProvider;
@@ -96,7 +98,7 @@ public Cygnus() {
9698
this.staminaService = new StaminaService();
9799
this.gameConfig = new GameConfigReader(path).getConfig();
98100
MinecraftServer.getConnectionManager().setPlayerProvider(CygnusPlayer::new);
99-
this.pageProvider = new PageProvider(this::handleAllPageFound);
101+
this.pageProvider = new PageProvider();
100102
this.mapProvider = new GameMapProvider(path);
101103
this.view = new GameViewImpl(this::getViewComponent);
102104
this.createTeams(this.gameConfig, this.teamService, this.ambientProvider);
@@ -112,12 +114,6 @@ private void initCommands() {
112114
manager.register(new StartCommand(this.linearPhaseSeries));
113115
}
114116

115-
private void handleAllPageFound() {
116-
var gamePhase = (GamePhase) this.linearPhaseSeries.getCurrentPhase();
117-
gamePhase.setFinishEvent(new GameFinishEvent(GameFinishEvent.Reason.ALL_PAGES_FOUND));
118-
gamePhase.finish();
119-
}
120-
121117
private void initListener() {
122118
Supplier<Phase> phaseSupplier = this.linearPhaseSeries::getCurrentPhase;
123119
var manager = MinecraftServer.getGlobalEventHandler();
@@ -155,6 +151,7 @@ private void registerGameListener() {
155151
SlenderReviveEvent.class, new SlenderReviveListener(((GameMapProvider) this.mapProvider).getGameMap(), this.staminaService));
156152
manager.addListener(GamePreLaunchEvent.class, new GamePreLaunchListener(this.pageProvider::setMaxPageAmount));
157153
manager.addListener(StaminaStateChangeEvent.class, new StaminaStateChangeListener());
154+
manager.addListener(PageDiscoveryCompletedEvent.class, new PageDiscoveryCompleteListener(this.linearPhaseSeries));
158155
MinecraftServer.getPacketListenerManager().setPlayListener(ClientEntityActionPacket.class, CygnusEntityActionListener::listener);
159156
}
160157

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package net.onelitefeather.cygnus.listener.page;
2+
3+
import net.onelitefeather.cygnus.common.page.event.PageDiscoveryCompletedEvent;
4+
import net.onelitefeather.cygnus.event.GameFinishEvent;
5+
import net.onelitefeather.cygnus.phase.GamePhase;
6+
import net.theevilreaper.xerus.api.phase.LinearPhaseSeries;
7+
import net.theevilreaper.xerus.api.phase.TimedPhase;
8+
9+
import java.util.function.Consumer;
10+
11+
public final class PageDiscoveryCompleteListener implements Consumer<PageDiscoveryCompletedEvent> {
12+
13+
private final LinearPhaseSeries<TimedPhase> phaseSeries;
14+
15+
public PageDiscoveryCompleteListener(LinearPhaseSeries<TimedPhase> phaseSeries) {
16+
this.phaseSeries = phaseSeries;
17+
}
18+
19+
@Override
20+
public void accept(PageDiscoveryCompletedEvent event) {
21+
if (!(this.phaseSeries.getCurrentPhase() instanceof GamePhase gamePhase)) return;
22+
gamePhase.setFinishEvent(new GameFinishEvent(GameFinishEvent.Reason.ALL_PAGES_FOUND));
23+
gamePhase.finish();
24+
}
25+
}

0 commit comments

Comments
 (0)