Skip to content

Commit e11695f

Browse files
test: add more tests
1 parent 6d9bfca commit e11695f

14 files changed

Lines changed: 1392 additions & 69 deletions

shared/src/main/java/net/pistonmaster/pistonqueue/shared/queue/QueueListenerShared.java

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import net.pistonmaster.pistonqueue.shared.events.PQPreLoginEvent;
2626
import net.pistonmaster.pistonqueue.shared.events.PQServerPreConnectEvent;
2727
import net.pistonmaster.pistonqueue.shared.plugin.PistonQueuePlugin;
28+
import net.pistonmaster.pistonqueue.shared.queue.logic.KickEventHandler;
2829
import net.pistonmaster.pistonqueue.shared.queue.logic.QueueAvailabilityCalculator;
2930
import net.pistonmaster.pistonqueue.shared.queue.logic.QueueCleaner;
3031
import net.pistonmaster.pistonqueue.shared.queue.logic.QueueConnector;
@@ -33,8 +34,10 @@
3334
import net.pistonmaster.pistonqueue.shared.queue.logic.QueueMoveProcessor;
3435
import net.pistonmaster.pistonqueue.shared.queue.logic.QueuePlacementCoordinator;
3536
import net.pistonmaster.pistonqueue.shared.queue.logic.QueueRecoveryHandler;
37+
import net.pistonmaster.pistonqueue.shared.queue.logic.ShadowBanKickHandler;
3638
import net.pistonmaster.pistonqueue.shared.queue.logic.ShadowBanService;
3739
import net.pistonmaster.pistonqueue.shared.queue.logic.StorageShadowBanService;
40+
import net.pistonmaster.pistonqueue.shared.queue.logic.UsernameValidator;
3841
import net.pistonmaster.pistonqueue.shared.utils.StorageTool;
3942
import net.pistonmaster.pistonqueue.shared.wrapper.PlayerWrapper;
4043

@@ -50,10 +53,18 @@ public abstract class QueueListenerShared {
5053
private final QueueEnvironment queueEnvironment;
5154
private final QueuePlacementCoordinator queuePlacementCoordinator;
5255
private final QueueMoveProcessor queueMoveProcessor;
56+
private final UsernameValidator usernameValidator;
57+
private final ShadowBanKickHandler shadowBanKickHandler;
58+
private final KickEventHandler kickEventHandler;
5359

5460
protected QueueListenerShared(PistonQueuePlugin plugin) {
5561
this.plugin = plugin;
5662
this.queueEnvironment = new QueueEnvironment(plugin, this::currentConfig, onlineServers);
63+
Config config = currentConfig();
64+
this.usernameValidator = new UsernameValidator(config);
65+
this.shadowBanKickHandler = new ShadowBanKickHandler(config);
66+
this.kickEventHandler = new KickEventHandler(config, queueEnvironment);
67+
5768
QueueAvailabilityCalculator availabilityCalculator = new QueueAvailabilityCalculator();
5869
QueueEntryFactory queueEntryFactory = new QueueEntryFactory(queueEnvironment);
5970
this.queuePlacementCoordinator = new QueuePlacementCoordinator(queueEnvironment, availabilityCalculator, queueEntryFactory);
@@ -65,53 +76,15 @@ protected QueueListenerShared(PistonQueuePlugin plugin) {
6576
}
6677

6778
protected void onPreLogin(PQPreLoginEvent event) {
68-
if (event.isCancelled())
69-
return;
70-
71-
Config config = currentConfig();
72-
if (config.ENABLE_USERNAME_REGEX && !event.getUsername().matches(config.USERNAME_REGEX)) {
73-
event.setCancelled(config.USERNAME_REGEX_MESSAGE.replace("%regex%", config.USERNAME_REGEX));
74-
}
79+
usernameValidator.validateUsername(event);
7580
}
7681

7782
protected void onPostLogin(PlayerWrapper player) {
78-
Config config = currentConfig();
79-
if (StorageTool.isShadowBanned(player.getName()) && config.SHADOW_BAN_TYPE == BanType.KICK) {
80-
player.disconnect(config.SERVER_DOWN_KICK_MESSAGE);
81-
}
83+
shadowBanKickHandler.handleShadowBanKick(player);
8284
}
8385

8486
protected void onKick(PQKickedFromServerEvent event) {
85-
Config config = currentConfig();
86-
QueueGroup group = queueEnvironment.resolveGroupForTarget(event.getKickedFrom());
87-
boolean kickedFromProtectedTarget = group.getTargetServers().contains(event.getKickedFrom());
88-
if (config.IF_TARGET_DOWN_SEND_TO_QUEUE && kickedFromProtectedTarget) {
89-
String kickReason = event.getKickReason()
90-
.map(s -> s.toLowerCase(Locale.ROOT))
91-
.orElse("unknown reason");
92-
93-
config.DOWN_WORD_LIST.stream()
94-
.filter(word -> kickReason.contains(word.toLowerCase(Locale.ROOT)))
95-
.findFirst()
96-
.ifPresent(word -> {
97-
event.setCancelServer(group.getQueueServer());
98-
99-
event.getPlayer().sendMessage(config.IF_TARGET_DOWN_SEND_TO_QUEUE_MESSAGE);
100-
101-
QueueType queueType = config.getQueueType(event.getPlayer());
102-
Lock writeLock = queueType.getQueueLock().writeLock();
103-
writeLock.lock();
104-
try {
105-
queueType.getQueueMap().put(event.getPlayer().getUniqueId(), new QueueType.QueuedPlayer(event.getKickedFrom(), QueueType.QueueReason.SERVER_DOWN));
106-
} finally {
107-
writeLock.unlock();
108-
}
109-
});
110-
}
111-
112-
if (config.ENABLE_KICK_MESSAGE && event.willDisconnect()) {
113-
event.setKickMessage(config.KICK_MESSAGE);
114-
}
87+
kickEventHandler.handleKick(event);
11588
}
11689

11790
protected void onPreConnect(PQServerPreConnectEvent event) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* #%L
3+
* PistonQueue
4+
* %%
5+
* Copyright (C) 2021 AlexProgrammerDE
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package net.pistonmaster.pistonqueue.shared.queue.logic;
21+
22+
import net.pistonmaster.pistonqueue.shared.config.Config;
23+
import net.pistonmaster.pistonqueue.shared.events.PQKickedFromServerEvent;
24+
25+
import net.pistonmaster.pistonqueue.shared.queue.QueueGroup;
26+
import net.pistonmaster.pistonqueue.shared.queue.QueueType;
27+
28+
import java.util.Locale;
29+
import java.util.Objects;
30+
import java.util.concurrent.locks.Lock;
31+
32+
/**
33+
* Handles kick events and potentially redirects players to the queue.
34+
*/
35+
public final class KickEventHandler {
36+
private final Config config;
37+
private final QueueEnvironment queueEnvironment;
38+
39+
public KickEventHandler(Config config, QueueEnvironment queueEnvironment) {
40+
this.config = Objects.requireNonNull(config, "config");
41+
this.queueEnvironment = Objects.requireNonNull(queueEnvironment, "queueEnvironment");
42+
}
43+
44+
/**
45+
* Handles a kick event, potentially redirecting the player to the queue if they were kicked
46+
* from a protected target server due to it being down.
47+
*
48+
* @param event the kick event
49+
*/
50+
public void handleKick(PQKickedFromServerEvent event) {
51+
handleQueueRedirection(event);
52+
handleKickMessage(event);
53+
}
54+
55+
private void handleQueueRedirection(PQKickedFromServerEvent event) {
56+
QueueGroup group = queueEnvironment.resolveGroupForTarget(event.getKickedFrom());
57+
boolean kickedFromProtectedTarget = group.getTargetServers().contains(event.getKickedFrom());
58+
59+
if (config.IF_TARGET_DOWN_SEND_TO_QUEUE && kickedFromProtectedTarget) {
60+
String kickReason = event.getKickReason()
61+
.map(s -> s.toLowerCase(Locale.ROOT))
62+
.orElse("unknown reason");
63+
64+
config.DOWN_WORD_LIST.stream()
65+
.filter(word -> kickReason.contains(word.toLowerCase(Locale.ROOT)))
66+
.findFirst()
67+
.ifPresent(word -> {
68+
event.setCancelServer(group.getQueueServer());
69+
event.getPlayer().sendMessage(config.IF_TARGET_DOWN_SEND_TO_QUEUE_MESSAGE);
70+
71+
QueueType queueType = config.getQueueType(event.getPlayer());
72+
Lock writeLock = queueType.getQueueLock().writeLock();
73+
writeLock.lock();
74+
try {
75+
queueType.getQueueMap().put(event.getPlayer().getUniqueId(),
76+
new QueueType.QueuedPlayer(event.getKickedFrom(), QueueType.QueueReason.SERVER_DOWN));
77+
} finally {
78+
writeLock.unlock();
79+
}
80+
});
81+
}
82+
}
83+
84+
private void handleKickMessage(PQKickedFromServerEvent event) {
85+
if (config.ENABLE_KICK_MESSAGE && event.willDisconnect()) {
86+
event.setKickMessage(config.KICK_MESSAGE);
87+
}
88+
}
89+
}

shared/src/main/java/net/pistonmaster/pistonqueue/shared/queue/logic/QueueConnector.java

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,12 @@ public QueueConnector(QueueEnvironment environment, QueueAvailabilityCalculator
6060

6161
public void connectPlayers(QueueGroup group, QueueType type) {
6262
Config config = environment.config();
63-
int freeSlots = availabilityCalculator.getFreeSlots(type);
63+
int freeSlots = calculateEffectiveFreeSlots(config, type);
6464

6565
if (freeSlots <= 0) {
6666
return;
6767
}
6868

69-
if (freeSlots > config.MAX_PLAYERS_PER_MOVE) {
70-
freeSlots = config.MAX_PLAYERS_PER_MOVE;
71-
}
72-
7369
int movesLeft = freeSlots;
7470
Set<UUID> processedThisCycle = new HashSet<>();
7571

@@ -91,34 +87,15 @@ public void connectPlayers(QueueGroup group, QueueType type) {
9187
}
9288
PlayerWrapper player = optional.get();
9389

94-
player.sendMessage(config.JOINING_TARGET_SERVER);
95-
player.resetPlayerList();
96-
97-
if (shadowBanService.isShadowBanned(player.getName())
98-
&& (config.SHADOW_BAN_TYPE == BanType.LOOP
99-
|| (config.SHADOW_BAN_TYPE == BanType.PERCENT && ThreadLocalRandom.current().nextInt(100) >= config.PERCENT))) {
90+
if (shouldSkipPlayerDueToShadowBan(config, player)) {
10091
player.sendMessage(config.SHADOW_BAN_MESSAGE);
101-
10292
requeuePlayer(type, entry);
103-
10493
continue;
10594
}
10695

107-
indexPositionTime(type);
108-
109-
Map<Integer, Instant> cache = type.getPositionCache().get(entry.getKey());
110-
if (cache != null) {
111-
Lock durationWriteLock = type.getDurationLock().writeLock();
112-
durationWriteLock.lock();
113-
try {
114-
cache.forEach((position, instant) ->
115-
type.getDurationFromPosition().put(position, Duration.between(instant, Instant.now())));
116-
} finally {
117-
durationWriteLock.unlock();
118-
}
119-
}
120-
121-
player.connect(entry.getValue().targetServer());
96+
preparePlayerForConnection(config, player);
97+
recordPositionDuration(type, entry.getKey());
98+
connectPlayer(player, entry.getValue().targetServer());
12299
type.getActiveTransfers().remove(entry.getKey());
123100

124101
movesLeft--;
@@ -129,6 +106,48 @@ public void connectPlayers(QueueGroup group, QueueType type) {
129106
}
130107
}
131108

109+
int calculateEffectiveFreeSlots(Config config, QueueType type) {
110+
int freeSlots = availabilityCalculator.getFreeSlots(type);
111+
if (freeSlots <= 0) {
112+
return 0;
113+
}
114+
return Math.min(freeSlots, config.MAX_PLAYERS_PER_MOVE);
115+
}
116+
117+
boolean shouldSkipPlayerDueToShadowBan(Config config, PlayerWrapper player) {
118+
if (!shadowBanService.isShadowBanned(player.getName())) {
119+
return false;
120+
}
121+
122+
return config.SHADOW_BAN_TYPE == BanType.LOOP
123+
|| (config.SHADOW_BAN_TYPE == BanType.PERCENT && ThreadLocalRandom.current().nextInt(100) >= config.PERCENT);
124+
}
125+
126+
void preparePlayerForConnection(Config config, PlayerWrapper player) {
127+
player.sendMessage(config.JOINING_TARGET_SERVER);
128+
player.resetPlayerList();
129+
}
130+
131+
void recordPositionDuration(QueueType type, UUID playerId) {
132+
indexPositionTime(type);
133+
134+
Map<Integer, Instant> cache = type.getPositionCache().get(playerId);
135+
if (cache != null) {
136+
Lock durationWriteLock = type.getDurationLock().writeLock();
137+
durationWriteLock.lock();
138+
try {
139+
cache.forEach((position, instant) ->
140+
type.getDurationFromPosition().put(position, Duration.between(instant, Instant.now())));
141+
} finally {
142+
durationWriteLock.unlock();
143+
}
144+
}
145+
}
146+
147+
void connectPlayer(PlayerWrapper player, String targetServer) {
148+
player.connect(targetServer);
149+
}
150+
132151
private void sendXPSoundToQueueType(QueueGroup group, QueueType type) {
133152
ByteArrayDataOutput out = ByteStreams.newDataOutput();
134153
out.writeUTF("xpV2");
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* #%L
3+
* PistonQueue
4+
* %%
5+
* Copyright (C) 2021 AlexProgrammerDE
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package net.pistonmaster.pistonqueue.shared.queue.logic;
21+
22+
import net.pistonmaster.pistonqueue.shared.config.Config;
23+
import net.pistonmaster.pistonqueue.shared.queue.BanType;
24+
import net.pistonmaster.pistonqueue.shared.utils.StorageTool;
25+
import net.pistonmaster.pistonqueue.shared.wrapper.PlayerWrapper;
26+
27+
import java.util.Objects;
28+
29+
/**
30+
* Handles shadow ban kicks for players after they log in.
31+
*/
32+
public final class ShadowBanKickHandler {
33+
private final Config config;
34+
35+
public ShadowBanKickHandler(Config config) {
36+
this.config = Objects.requireNonNull(config, "config");
37+
}
38+
39+
/**
40+
* Checks if the player should be kicked due to shadow ban and disconnects them if so.
41+
*
42+
* @param player the player who just logged in
43+
*/
44+
public void handleShadowBanKick(PlayerWrapper player) {
45+
if (StorageTool.isShadowBanned(player.getName()) && config.SHADOW_BAN_TYPE == BanType.KICK) {
46+
player.disconnect(config.SERVER_DOWN_KICK_MESSAGE);
47+
}
48+
}
49+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* #%L
3+
* PistonQueue
4+
* %%
5+
* Copyright (C) 2021 AlexProgrammerDE
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package net.pistonmaster.pistonqueue.shared.queue.logic;
21+
22+
import net.pistonmaster.pistonqueue.shared.config.Config;
23+
import net.pistonmaster.pistonqueue.shared.events.PQPreLoginEvent;
24+
25+
import java.util.Objects;
26+
27+
/**
28+
* Handles username validation for pre-login events.
29+
*/
30+
public final class UsernameValidator {
31+
private final Config config;
32+
33+
public UsernameValidator(Config config) {
34+
this.config = Objects.requireNonNull(config, "config");
35+
}
36+
37+
/**
38+
* Validates the username in the pre-login event and cancels it if it doesn't match the regex.
39+
*
40+
* @param event the pre-login event
41+
*/
42+
public void validateUsername(PQPreLoginEvent event) {
43+
if (event.isCancelled()) {
44+
return;
45+
}
46+
47+
if (config.ENABLE_USERNAME_REGEX && !event.getUsername().matches(config.USERNAME_REGEX)) {
48+
event.setCancelled(config.USERNAME_REGEX_MESSAGE.replace("%regex%", config.USERNAME_REGEX));
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)