17 Commits

Author SHA1 Message Date
b95cc294ab Adjusts the README a bit 2023-04-07 15:54:32 +02:00
458dbc2beb Fixes displaying player names in placeholders 2023-04-07 15:37:50 +02:00
efaca03434 Revert "Removes group record for now"
This reverts commit fe016fd6
2023-04-07 15:23:47 +02:00
58b5b422f0 Merge branch 'master' of https://github.com/SunNetservers/Dropper
 Conflicts:
	src/main/java/net/knarcraft/dropper/placeholder/DropperRecordExpansion.java
2023-04-07 15:22:15 +02:00
483a0a16dc Merge branch 'group-placeholders' 2023-04-07 15:21:00 +02:00
e1c4a6a97c Fixes various issues
Makes ArenaRecord abstract to make serialization possible
Makes IntegerRecord and LongRecord serializable
Adds a null check when summing records
Fixes records not being saved, as a copy was edited
2023-04-07 15:15:41 +02:00
5be6f0d00e Fixes equals check for arena ids 2023-04-07 14:16:47 +02:00
f6a272b0c0 Implements group record placeholders 2023-04-07 13:42:45 +02:00
46e52812af Merge pull request #18 from SunNetservers/dev
Placeholders, and adjustments for multiple players
2023-04-06 22:23:39 +00:00
fe016fd620 Removes group record for now 2023-04-07 00:19:54 +02:00
8e9b274fc0 Adds unfinished code for group record placeholders 2023-04-07 00:17:57 +02:00
2b9cfeebb1 Implements placeholders for arenas 2023-04-06 17:07:36 +02:00
096f23d468 Generifies arena records, and prepares for PlaceholderAPI 2023-04-06 00:43:33 +02:00
3ebf5fa924 Disables player collisions, and makes players invisible in the arena 2023-04-05 23:15:19 +02:00
579c1ea0f9 Merge branch 'dev' 2023-04-01 01:20:43 +02:00
d41154281b Allows players to pass through wall signs 2023-03-31 23:46:17 +02:00
f852de7309 Fixes a bug where newly created dropper arenas had a null reference to the handler 2023-03-31 23:20:11 +02:00
21 changed files with 877 additions and 93 deletions

View File

@ -18,22 +18,22 @@ To modify
## Commands
| Command | Alias | Arguments | Description |
|-----------------------------------------|----------|-----------------------------|-------------------------------------------------------------------------------------|
| /dropperList | /dlist | | Lists available dropper arenas. |
| [/dropperJoin](#/dropperJoin) | /djoin | \<arena> \[mode] | Joins the selected arena. |
| /dropperLeave | /dleave | | Leaves the current dropper arena. |
| /dropperCreate | /dcreate | \<name> | Creates a new dropper arena with the given name. The spawn is set to your location. |
| /dropperRemove | /dremove | \<arena> | Removes the specified dropper arena. |
| [/dropperEdit](#/dropperEdit) | /dedit | \<arena> \<option> \[value] | Gets or sets a dropper arena option. |
| /dropperReload | /dreload | | Reloads all data from disk. |
| [/dropperGroupSet](#/dropperGroupSet) | /dgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
| /dropperGroupList | /dglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
| [/dropperGroupSwap](#/dropperGroupSwap) | /dgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |
| Command | Alias | Arguments | Description |
|----------------------------------------|----------|-----------------------------|-------------------------------------------------------------------------------------|
| /dropperList | /dlist | | Lists available dropper arenas. |
| [/dropperJoin](#dropperJoin) | /djoin | \<arena> \[mode] | Joins the selected arena. |
| /dropperLeave | /dleave | | Leaves the current dropper arena. |
| /dropperCreate | /dcreate | \<name> | Creates a new dropper arena with the given name. The spawn is set to your location. |
| /dropperRemove | /dremove | \<arena> | Removes the specified dropper arena. |
| [/dropperEdit](#dropperEdit) | /dedit | \<arena> \<option> \[value] | Gets or sets a dropper arena option. |
| /dropperReload | /dreload | | Reloads all data from disk. |
| [/dropperGroupSet](#dropperGroupSet) | /dgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
| /dropperGroupList | /dglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
| [/dropperGroupSwap](#dropperGroupSwap) | /dgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |
## Command explanation
### Command explanation
### /dropperJoin
#### /dropperJoin
This command is used for joining a dropper arena.
@ -44,7 +44,7 @@ This command is used for joining a dropper arena.
| arena | The name of the arena to join. |
| mode | Additional challenge modes can be played after an arena has been cleared once. Available modes: inverted and random. |
### /dropperEdit
#### /dropperEdit
This command allows editing the specified property for the specified dropper arena.
@ -67,7 +67,7 @@ These are all the options that can be changed for an arena.
| horizontalVelocity | The horizontal velocity (technically fly speed) set for players in the arena. It must be between 0 and 1, and cannot be 0. Decimals are allowed. |
| winBlockType | The type of block players must hit to win the arena. It can be any material as long as it's a block, and not a type of air. |
### /dropperGroupSet
#### /dropperGroupSet
This command is used to set the group of an arena
@ -78,7 +78,7 @@ will be used again if you specify the "potato" group for another arena. You use
its group. If the group has no arenas, it will be automatically removed. If the arena already is in a group, it will be
moved to the new group.
### /dropperGroupSwap
#### /dropperGroupSwap
This command is used for changing the order of arenas within a group.
@ -98,3 +98,21 @@ You could use `/droppergroupswap Sea Savanna` to change the order to:
2. Savanna
3. Nether
4. Sea
## Record placeholders
Player records can be displayed on a leaderboard by using PlaceholderAPI. If you want to display a sign-based
leaderboard, you can use the [Placeholder Signs](https://git.knarcraft.net/EpicKnarvik97/PlaceholderSigns) plugin. The
format for the built-in placeholders is as follows:
`%dropper_record_recordType_gameModeType_identifierType_identifier_recordPlacing_infoType%`
| Variable | Values | Description |
|----------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| dropper_record | | Denotes that it's a placeholder for a dropper record. Must be present as-is. |
| recordType | deaths / time | Selects the type of record to get (deaths or time). |
| gameModeType | default / inverted / random | Selects the game-mode to get the record for. |
| identifierType | arena / group | The type of thing the following identifier points to (an arena or an arena group). |
| identifier | ? | An identifier (the name or UUID) for an arena or a group (whichever was chosen as identifierType). |
| recordPlacing | 1 / 2 / 3 / ... | The position of the record to get (1 = first place, 2 = second place, etc.). |
| infoType | player / value / combined | The type of info to get. Player gets the player name, Value gets the value of the achieved record. Combined gets "Player: Record". |

10
pom.xml
View File

@ -58,6 +58,10 @@
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
</repositories>
<dependencies>
@ -79,5 +83,11 @@
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -6,6 +6,8 @@ import net.knarcraft.dropper.arena.DropperArenaHandler;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession;
import net.knarcraft.dropper.arena.record.IntegerRecord;
import net.knarcraft.dropper.arena.record.LongRecord;
import net.knarcraft.dropper.command.CreateArenaCommand;
import net.knarcraft.dropper.command.EditArenaCommand;
import net.knarcraft.dropper.command.EditArenaTabCompleter;
@ -25,7 +27,9 @@ import net.knarcraft.dropper.listener.CommandListener;
import net.knarcraft.dropper.listener.DamageListener;
import net.knarcraft.dropper.listener.MoveListener;
import net.knarcraft.dropper.listener.PlayerLeaveListener;
import net.knarcraft.dropper.placeholder.DropperRecordExpansion;
import net.knarcraft.dropper.property.ArenaGameMode;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
@ -94,6 +98,8 @@ public final class Dropper extends JavaPlugin {
ConfigurationSerialization.registerClass(DropperArenaData.class);
ConfigurationSerialization.registerClass(DropperArenaGroup.class);
ConfigurationSerialization.registerClass(ArenaGameMode.class);
ConfigurationSerialization.registerClass(LongRecord.class);
ConfigurationSerialization.registerClass(IntegerRecord.class);
}
@Override
@ -121,6 +127,12 @@ public final class Dropper extends JavaPlugin {
registerCommand("dropperGroupSet", new GroupSetCommand(), null);
registerCommand("dropperGroupSwap", new GroupSwapCommand(), null);
registerCommand("dropperGroupList", new GroupListCommand(), null);
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
if (!new DropperRecordExpansion(this).register()) {
getLogger().log(Level.WARNING, "Unable to register PlaceholderAPI expansion!");
}
}
}
@Override

View File

@ -1,6 +1,5 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.property.ArenaGameMode;
import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.Location;
@ -61,7 +60,7 @@ public class DropperArena {
*/
private final DropperArenaData dropperArenaData;
private static DropperArenaHandler dropperArenaHandler = null;
private final DropperArenaHandler dropperArenaHandler;
/**
* Instantiates a new dropper arena
@ -74,10 +73,12 @@ public class DropperArena {
* @param playerHorizontalVelocity <p>The velocity to use for players' horizontal velocity (-1 to 1)</p>
* @param winBlockType <p>The material of the block players have to hit to win this dropper arena</p>
* @param dropperArenaData <p>The arena data keeping track of which players have done what in this arena</p>
* @param arenaHandler <p>The arena handler used for saving any changes</p>
*/
public DropperArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
@Nullable Location exitLocation, double playerVerticalVelocity, float playerHorizontalVelocity,
@NotNull Material winBlockType, @NotNull DropperArenaData dropperArenaData) {
@NotNull Material winBlockType, @NotNull DropperArenaData dropperArenaData,
@NotNull DropperArenaHandler arenaHandler) {
this.arenaId = arenaId;
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
@ -86,10 +87,7 @@ public class DropperArena {
this.playerHorizontalVelocity = playerHorizontalVelocity;
this.winBlockType = winBlockType;
this.dropperArenaData = dropperArenaData;
if (dropperArenaHandler == null) {
dropperArenaHandler = Dropper.getInstance().getArenaHandler();
}
this.dropperArenaHandler = arenaHandler;
}
/**
@ -100,8 +98,10 @@ public class DropperArena {
*
* @param arenaName <p>The name of the arena</p>
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
* @param arenaHandler <p>The arena handler used for saving any changes</p>
*/
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation) {
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation,
@NotNull DropperArenaHandler arenaHandler) {
this.arenaId = UUID.randomUUID();
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
@ -116,6 +116,7 @@ public class DropperArena {
this.dropperArenaData = new DropperArenaData(this.arenaId, recordRegistries, new HashMap<>());
this.winBlockType = Material.WATER;
this.dropperArenaHandler = arenaHandler;
}
/**

View File

@ -103,6 +103,12 @@ public record DropperArenaData(@NotNull UUID arenaId,
Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData =
(Map<ArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted");
if (recordsRegistry == null) {
recordsRegistry = new HashMap<>();
} else if (playersCompletedData == null) {
playersCompletedData = new HashMap<>();
}
// Convert the serializable UUIDs to normal UUIDs
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
@ -111,6 +117,10 @@ public record DropperArenaData(@NotNull UUID arenaId,
playersCompleted.add(completedId.uuid());
}
allPlayersCompleted.put(arenaGameMode, playersCompleted);
if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
recordsRegistry.put(arenaGameMode, new DropperArenaRecordsRegistry(serializableUUID.uuid()));
}
}
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted);
}

View File

@ -160,7 +160,7 @@ public class DropperArenaGroup implements ConfigurationSerializable {
for (UUID anArenaId : this.getArenas()) {
// If the target arena is reached, allow, as all previous arenas must have been cleared
if (arenaId == anArenaId) {
if (arenaId.equals(anArenaId)) {
return true;
}

View File

@ -1,15 +1,24 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.arena.record.IntegerRecord;
import net.knarcraft.dropper.arena.record.LongRecord;
import net.knarcraft.dropper.arena.record.SummableArenaRecord;
import net.knarcraft.dropper.container.SerializableUUID;
import net.knarcraft.dropper.property.RecordResult;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
* A registry keeping track of all records
@ -17,16 +26,16 @@ import java.util.stream.Stream;
public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
private final UUID arenaId;
private final @NotNull Map<UUID, Number> leastDeaths;
private final @NotNull Map<UUID, Number> shortestTimeMilliSeconds;
private final @NotNull Set<IntegerRecord> leastDeaths;
private final @NotNull Set<LongRecord> shortestTimeMilliSeconds;
/**
* Instantiates a new empty records registry
*/
public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
this.arenaId = arenaId;
this.leastDeaths = new HashMap<>();
this.shortestTimeMilliSeconds = new HashMap<>();
this.leastDeaths = new HashSet<>();
this.shortestTimeMilliSeconds = new HashSet<>();
}
/**
@ -35,11 +44,11 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
* @param leastDeaths <p>The existing least death records to use</p>
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
*/
private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Map<UUID, Integer> leastDeaths,
@NotNull Map<UUID, Long> shortestTimeMilliSeconds) {
private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
@NotNull Set<LongRecord> shortestTimeMilliSeconds) {
this.arenaId = arenaId;
this.leastDeaths = new HashMap<>(leastDeaths);
this.shortestTimeMilliSeconds = new HashMap<>(shortestTimeMilliSeconds);
this.leastDeaths = new HashSet<>(leastDeaths);
this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds);
}
/**
@ -47,12 +56,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
*
* @return <p>Existing death records</p>
*/
public Map<UUID, Integer> getLeastDeathsRecords() {
Map<UUID, Integer> leastDeathRecords = new HashMap<>();
for (Map.Entry<UUID, Number> entry : this.leastDeaths.entrySet()) {
leastDeathRecords.put(entry.getKey(), entry.getValue().intValue());
}
return leastDeathRecords;
public Set<SummableArenaRecord<Integer>> getLeastDeathsRecords() {
return new HashSet<>(this.leastDeaths);
}
/**
@ -60,12 +65,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
*
* @return <p>Existing time records</p>
*/
public Map<UUID, Long> getShortestTimeMilliSecondsRecords() {
Map<UUID, Long> leastTimeRecords = new HashMap<>();
for (Map.Entry<UUID, Number> entry : this.shortestTimeMilliSeconds.entrySet()) {
leastTimeRecords.put(entry.getKey(), entry.getValue().longValue());
}
return leastTimeRecords;
public Set<SummableArenaRecord<Long>> getShortestTimeMilliSecondsRecords() {
return new HashSet<>(this.shortestTimeMilliSeconds);
}
/**
@ -76,7 +77,9 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
* @return <p>The result explaining what type of record was achieved</p>
*/
public @NotNull RecordResult registerDeathRecord(@NotNull UUID playerId, int deaths) {
return registerRecord(leastDeaths, playerId, deaths);
Consumer<Integer> consumer = (value) -> leastDeaths.add(new IntegerRecord(playerId, value));
Set<ArenaRecord<Integer>> asInt = new HashSet<>(leastDeaths);
return registerRecord(asInt, consumer, playerId, deaths);
}
/**
@ -87,7 +90,9 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
* @return <p>The result explaining what type of record was achieved</p>
*/
public @NotNull RecordResult registerTimeRecord(@NotNull UUID playerId, long milliseconds) {
return registerRecord(shortestTimeMilliSeconds, playerId, milliseconds);
Consumer<Long> consumer = (value) -> shortestTimeMilliSeconds.add(new LongRecord(playerId, value));
Set<ArenaRecord<Long>> asLong = new HashSet<>(shortestTimeMilliSeconds);
return registerRecord(asLong, consumer, playerId, milliseconds);
}
/**
@ -101,29 +106,33 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
* Registers a new record if applicable
*
* @param existingRecords <p>The map of existing records to use</p>
* @param recordSetter <p>The consumer used to set a new record</p>
* @param playerId <p>The id of the player that potentially achieved a record</p>
* @param amount <p>The amount of whatever the player achieved</p>
* @return <p>The result of the player's record attempt</p>
*/
private @NotNull RecordResult registerRecord(@NotNull Map<UUID, Number> existingRecords, @NotNull UUID playerId,
Number amount) {
private <T extends Comparable<T>> @NotNull RecordResult registerRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
@NotNull Consumer<T> recordSetter,
@NotNull UUID playerId, T amount) {
RecordResult result;
Stream<Map.Entry<UUID, Number>> records = existingRecords.entrySet().stream();
long amountLong = amount.longValue();
if (records.allMatch((entry) -> amountLong < entry.getValue().longValue())) {
if (existingRecords.stream().allMatch((entry) -> amount.compareTo(entry.getRecord()) < 0)) {
// If the given value is less than all other values, that's a world record!
result = RecordResult.WORLD_RECORD;
existingRecords.put(playerId, amount);
recordSetter.accept(amount);
save();
} else if (existingRecords.containsKey(playerId) && amountLong < existingRecords.get(playerId).longValue()) {
return result;
}
ArenaRecord<T> playerRecord = getRecord(existingRecords, playerId);
if (playerRecord != null && amount.compareTo(playerRecord.getRecord()) < 0) {
// If the given value is less than the player's previous value, that's a personal best!
result = RecordResult.PERSONAL_BEST;
existingRecords.put(playerId, amount);
recordSetter.accept(amount);
save();
} else {
// Make sure to save the record if this is the user's first attempt
if (!existingRecords.containsKey(playerId)) {
if (playerRecord == null) {
recordSetter.accept(amount);
save();
}
result = RecordResult.NONE;
@ -132,23 +141,32 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
return result;
}
/**
* Gets the record stored for the given player
*
* @param existingRecords <p>The existing records to look through</p>
* @param playerId <p>The id of the player to look for</p>
* @param <T> <p>The type of the stored record</p>
* @return <p>The record, or null if not found</p>
*/
private <T extends Comparable<T>> @Nullable ArenaRecord<T> getRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
@NotNull UUID playerId) {
AtomicReference<ArenaRecord<T>> record = new AtomicReference<>();
existingRecords.forEach((item) -> {
if (item.getUserId().equals(playerId)) {
record.set(item);
}
});
return record.get();
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("arenaId", new SerializableUUID(this.arenaId));
Map<SerializableUUID, Number> leastDeaths = new HashMap<>();
for (Map.Entry<UUID, Number> entry : this.leastDeaths.entrySet()) {
leastDeaths.put(new SerializableUUID(entry.getKey()), entry.getValue());
}
data.put("leastDeaths", leastDeaths);
Map<SerializableUUID, Number> shortestTimeMilliSeconds = new HashMap<>();
for (Map.Entry<UUID, Number> entry : this.shortestTimeMilliSeconds.entrySet()) {
shortestTimeMilliSeconds.put(new SerializableUUID(entry.getKey()), entry.getValue());
}
data.put("shortestTime", shortestTimeMilliSeconds);
data.put("leastDeaths", this.leastDeaths);
data.put("shortestTime", this.shortestTimeMilliSeconds);
return data;
}
@ -161,20 +179,13 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
@SuppressWarnings({"unused", "unchecked"})
public static DropperArenaRecordsRegistry deserialize(Map<String, Object> data) {
UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
Map<SerializableUUID, Integer> leastDeathsData =
(Map<SerializableUUID, Integer>) data.getOrDefault("leastDeaths", new HashMap<>());
Map<UUID, Integer> leastDeaths = new HashMap<>();
for (Map.Entry<SerializableUUID, Integer> entry : leastDeathsData.entrySet()) {
leastDeaths.put(entry.getKey().uuid(), entry.getValue());
}
Map<SerializableUUID, Number> shortestTimeMillisecondsData =
(Map<SerializableUUID, Number>) data.getOrDefault("shortestTime", new HashMap<>());
Map<UUID, Long> shortestTimeMilliseconds = new HashMap<>();
for (Map.Entry<SerializableUUID, Number> entry : shortestTimeMillisecondsData.entrySet()) {
shortestTimeMilliseconds.put(entry.getKey().uuid(), entry.getValue().longValue());
}
Set<IntegerRecord> leastDeaths =
(Set<IntegerRecord>) data.getOrDefault("leastDeaths", new HashMap<>());
Set<LongRecord> shortestTimeMilliseconds =
(Set<LongRecord>) data.getOrDefault("shortestTime", new HashMap<>());
leastDeaths.removeIf(Objects::isNull);
shortestTimeMilliseconds.removeIf(Objects::isNull);
return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
}

View File

@ -4,6 +4,8 @@ import net.knarcraft.dropper.property.ArenaGameMode;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
/**
@ -19,6 +21,7 @@ public class PlayerEntryState {
private final boolean originalAllowFlight;
private final boolean originalInvulnerable;
private final boolean originalIsSwimming;
private final boolean originalCollideAble;
private final ArenaGameMode arenaGameMode;
/**
@ -36,6 +39,7 @@ public class PlayerEntryState {
this.originalInvulnerable = player.isInvulnerable();
this.originalIsSwimming = player.isSwimming();
this.arenaGameMode = arenaGameMode;
this.originalCollideAble = player.isCollidable();
}
/**
@ -48,6 +52,8 @@ public class PlayerEntryState {
this.player.setFlying(true);
this.player.setGameMode(GameMode.ADVENTURE);
this.player.setSwimming(false);
this.player.setCollidable(false);
this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, PotionEffect.INFINITE_DURATION, 3));
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
if (arenaGameMode == ArenaGameMode.INVERTED) {
@ -67,6 +73,8 @@ public class PlayerEntryState {
this.player.setFlySpeed(this.originalFlySpeed);
this.player.setInvulnerable(this.originalInvulnerable);
this.player.setSwimming(this.originalIsSwimming);
this.player.setCollidable(this.originalCollideAble);
this.player.removePotionEffect(PotionEffectType.INVISIBILITY);
}
/**

View File

@ -0,0 +1,76 @@
package net.knarcraft.dropper.arena.record;
import net.knarcraft.dropper.container.SerializableUUID;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
* A record stored for an arena
*/
public abstract class ArenaRecord<K extends Comparable<K>> implements Comparable<ArenaRecord<K>>, ConfigurationSerializable {
private final UUID userId;
private final K record;
/**
* @param userId <p>The id of the player that achieved the record</p>
* @param record <p>The record achieved</p>
*/
public ArenaRecord(UUID userId, K record) {
this.userId = userId;
this.record = record;
}
/**
* Gets the id of the user this record belongs to
*
* @return <p>The record's achiever</p>
*/
public UUID getUserId() {
return userId;
}
/**
* Gets the value of the stored record
*
* @return <p>The record value</p>
*/
public K getRecord() {
return record;
}
@Override
public boolean equals(Object other) {
return other instanceof ArenaRecord<?> && userId.equals(((ArenaRecord<?>) other).userId);
}
@Override
public int compareTo(@NotNull ArenaRecord<K> other) {
return record.compareTo(other.record);
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("userId", new SerializableUUID(getUserId()));
data.put("record", record);
return data;
}
@Override
public int hashCode() {
return Objects.hash(userId, record);
}
@Override
public String toString() {
return userId + ": " + record;
}
}

View File

@ -0,0 +1,38 @@
package net.knarcraft.dropper.arena.record;
import net.knarcraft.dropper.container.SerializableUUID;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/**
* A record storing an integer
*/
public class IntegerRecord extends SummableArenaRecord<Integer> {
/**
* @param userId <p>The id of the player that achieved the record</p>
* @param record <p>The record achieved</p>
*/
public IntegerRecord(UUID userId, Integer record) {
super(userId, record);
}
@Override
public SummableArenaRecord<Integer> sum(Integer value) {
return new IntegerRecord(this.getUserId(), this.getRecord() + value);
}
/**
* Deserializes the saved arena record
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized data</p>
*/
@SuppressWarnings("unused")
public static IntegerRecord deserialize(@NotNull Map<String, Object> data) {
return new IntegerRecord(((SerializableUUID) data.get("userId")).uuid(), (Integer) data.get("record"));
}
}

View File

@ -0,0 +1,38 @@
package net.knarcraft.dropper.arena.record;
import net.knarcraft.dropper.container.SerializableUUID;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/**
* A record storing a Long
*/
public class LongRecord extends SummableArenaRecord<Long> {
/**
* @param userId <p>The id of the player that achieved the record</p>
* @param record <p>The record achieved</p>
*/
public LongRecord(UUID userId, Long record) {
super(userId, record);
}
@Override
public SummableArenaRecord<Long> sum(Long value) {
return new LongRecord(this.getUserId(), this.getRecord() + value);
}
/**
* Deserializes the saved arena record
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized data</p>
*/
@SuppressWarnings("unused")
public static LongRecord deserialize(@NotNull Map<String, Object> data) {
return new LongRecord(((SerializableUUID) data.get("userId")).uuid(), ((Number) data.get("record")).longValue());
}
}

View File

@ -0,0 +1,28 @@
package net.knarcraft.dropper.arena.record;
import java.util.UUID;
/**
* A type of arena record which can be summed together
*
* @param <K> <p>The type of the stored value</p>
*/
public abstract class SummableArenaRecord<K extends Comparable<K>> extends ArenaRecord<K> {
/**
* @param userId <p>The id of the player that achieved the record</p>
* @param record <p>The record achieved</p>
*/
public SummableArenaRecord(UUID userId, K record) {
super(userId, record);
}
/**
* Returns a summable record with the resulting sum
*
* @param value <p>The value to add to the existing value</p>
* @return <p>A record with the sum of this record and the given value</p>
*/
public abstract SummableArenaRecord<K> sum(K value);
}

View File

@ -2,6 +2,7 @@ package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaHandler;
import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -35,14 +36,16 @@ public class CreateArenaCommand implements CommandExecutor {
return false;
}
DropperArena existingArena = Dropper.getInstance().getArenaHandler().getArena(arenaName);
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
DropperArena existingArena = arenaHandler.getArena(arenaName);
if (existingArena != null) {
commandSender.sendMessage("There already exists a dropper arena with that name!");
return false;
}
DropperArena arena = new DropperArena(arenaName, player.getLocation());
Dropper.getInstance().getArenaHandler().addArena(arena);
DropperArena arena = new DropperArena(arenaName, player.getLocation(), arenaHandler);
arenaHandler.addArena(arena);
commandSender.sendMessage("The arena was successfully created!");
return true;
}

View File

@ -6,6 +6,7 @@ import net.knarcraft.dropper.arena.DropperArenaSession;
import net.knarcraft.dropper.property.ArenaGameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -84,8 +85,9 @@ public class MoveListener implements Listener {
// Check if the player is about to hit a non-air and non-liquid block
for (Block block : getBlocksBeneathLocation(toLocation, solidDepth)) {
if (!block.getType().isAir() && block.getType() != Material.STRUCTURE_VOID &&
block.getType() != Material.WATER && block.getType() != Material.LAVA) {
Material blockType = block.getType();
if (!blockType.isAir() && blockType != Material.STRUCTURE_VOID && blockType != Material.WATER &&
blockType != Material.LAVA && !Tag.WALL_SIGNS.isTagged(blockType)) {
arenaSession.triggerLoss();
return true;
}

View File

@ -0,0 +1,238 @@
package net.knarcraft.dropper.placeholder;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler;
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.placeholder.parsing.InfoType;
import net.knarcraft.dropper.placeholder.parsing.RecordType;
import net.knarcraft.dropper.placeholder.parsing.SelectionType;
import net.knarcraft.dropper.property.ArenaGameMode;
import net.knarcraft.dropper.util.DropperGroupRecordHelper;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* A placeholder expansion for dropper record placeholders
*/
public class DropperRecordExpansion extends PlaceholderExpansion {
private final Dropper plugin;
public DropperRecordExpansion(Dropper plugin) {
this.plugin = plugin;
}
@Override
public String getIdentifier() {
return "dropper";
}
@Override
public String getAuthor() {
return "EpicKnarvik97";
}
@Override
public String getVersion() {
return "1.0.0";
}
@Override
public boolean persist() {
return true;
}
@Override
public String onRequest(OfflinePlayer player, String parameters) {
String[] parts = parameters.split("_");
// Record is used as the prefix for all record placeholders in case more placeholder types are added
if (parts.length < 7 || !parts[0].equals("record")) {
return parameters;
}
RecordType recordType = RecordType.getFromString(parts[1]);
ArenaGameMode gameMode = ArenaGameMode.matchGamemode(parts[2]);
SelectionType selectionType = SelectionType.getFromString(parts[3]);
String identifier = parts[4];
int recordNumber = Integer.parseInt(parts[5]) - 1;
InfoType infoType = InfoType.getFromString(parts[6]);
if (recordType == null || infoType == null) {
return parameters;
}
String info = null;
DropperArenaHandler arenaHandler = plugin.getArenaHandler();
if (selectionType == SelectionType.GROUP) {
info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType);
} else if (selectionType == SelectionType.ARENA) {
info = getArenaRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType);
}
return Objects.requireNonNullElse(info, parameters);
}
/**
* Gets a piece of record information from a dropper arena group
*
* @param arenaHandler <p>The arena handler to get the group from</p>
* @param identifier <p>The identifier (name/uuid) selecting the group</p>
* @param gameMode <p>The game-mode to get a record for</p>
* @param recordType <p>The type of record to get</p>
* @param recordNumber <p>The placing of the record to get (1st place, 2nd place, etc.)</p>
* @param infoType <p>The type of info (player, value, combined) to get</p>
* @return <p>The selected information about the record, or null if not found</p>
*/
private @Nullable String getGroupRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) {
// Allow specifying the group UUID or the arena name
DropperArenaGroup group;
try {
group = arenaHandler.getGroup(UUID.fromString(identifier));
} catch (IllegalArgumentException exception) {
group = arenaHandler.getGroup(identifier);
}
if (group == null) {
return null;
}
ArenaRecord<?> record;
if (recordType == RecordType.DEATHS) {
record = getRecord(DropperGroupRecordHelper.getCombinedDeaths(group, gameMode), recordNumber);
} else {
record = getRecord(DropperGroupRecordHelper.getCombinedTime(group, gameMode), recordNumber);
}
// If a record number is not found, leave it blank, so it looks neat
if (record == null) {
return "";
}
return getRecordData(infoType, record);
}
/**
* Gets a piece of record information from a dropper arena
*
* @param arenaHandler <p>The arena handler to get the arena from</p>
* @param identifier <p>The identifier (name/uuid) selecting the arena</p>
* @param gameMode <p>The game-mode to get a record for</p>
* @param recordType <p>The type of record to get</p>
* @param recordNumber <p>The placing of the record to get (1st place, 2nd place, etc.)</p>
* @param infoType <p>The type of info (player, value, combined) to get</p>
* @return <p>The selected information about the record, or null if not found</p>
*/
private @Nullable String getArenaRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) {
// Allow specifying the arena UUID or the arena name
DropperArena arena;
try {
arena = arenaHandler.getArena(UUID.fromString(identifier));
} catch (IllegalArgumentException exception) {
arena = arenaHandler.getArena(identifier);
}
if (arena == null) {
return null;
}
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> registries = arena.getData().recordRegistries();
DropperArenaRecordsRegistry recordsRegistry = registries.get(gameMode);
ArenaRecord<?> record = getRecord(recordsRegistry, recordType, recordNumber);
// If a record number is not found, leave it blank, so it looks neat
if (record == null) {
return "";
}
return getRecordData(infoType, record);
}
/**
* Gets the specified record
*
* @param recordsRegistry <p>The records registry to get the record from</p>
* @param recordType <p>The type of record to get</p>
* @param recordNumber <p>The placing of the record to get (1st place, 2nd place, etc.)</p>
* @return <p>The record, or null if not found</p>
*/
private @Nullable ArenaRecord<?> getRecord(@NotNull DropperArenaRecordsRegistry recordsRegistry,
@NotNull RecordType recordType, int recordNumber) {
return switch (recordType) {
case TIME -> getRecord(new HashSet<>(recordsRegistry.getShortestTimeMilliSecondsRecords()), recordNumber);
case DEATHS -> getRecord(new HashSet<>(recordsRegistry.getLeastDeathsRecords()), recordNumber);
};
}
/**
* Gets the record at the given index
*
* @param records <p>The records to search through</p>
* @param index <p>The index of the record to get</p>
* @param <K> <p>The type of record in the record list</p>
* @return <p>The record, or null if index is out of bounds</p>
*/
private <K extends Comparable<K>> @Nullable ArenaRecord<K> getRecord(Set<ArenaRecord<K>> records, int index) {
List<ArenaRecord<K>> sorted = getSortedRecords(records);
if (index < sorted.size() && index >= 0) {
return sorted.get(index);
} else {
return null;
}
}
/**
* Gets a piece of data from a record as a string
*
* @param infoType <p>The type of info to get data for</p>
* @param arenaRecord <p>The record to get the data from</p>
* @return <p>The requested data as a string, or null</p>
*/
private String getRecordData(@NotNull InfoType infoType, @NotNull ArenaRecord<?> arenaRecord) {
return switch (infoType) {
case PLAYER -> getPlayerName(arenaRecord.getUserId());
case VALUE -> arenaRecord.getRecord().toString();
case COMBINED -> getPlayerName(arenaRecord.getUserId()) + ": " + arenaRecord.getRecord().toString();
};
}
/**
* Gets the given set of records as a sorted list
*
* @param recordSet <p>The set of records to sort</p>
* @param <K> <p>The type of the records</p>
* @return <p>The sorted records</p>
*/
private <K extends Comparable<K>> @NotNull List<ArenaRecord<K>> getSortedRecords(
@NotNull Set<ArenaRecord<K>> recordSet) {
List<ArenaRecord<K>> records = new ArrayList<>(recordSet);
Collections.sort(records);
return records;
}
/**
* Gets the name of a player, given the player's UUID
*
* @param playerId <p>The id of the player to get the name for</p>
* @return <p>The name of the player, or a string representation of the UUID if not found</p>
*/
private String getPlayerName(@NotNull UUID playerId) {
return Bukkit.getOfflinePlayer(playerId).getName();
}
}

View File

@ -0,0 +1,42 @@
package net.knarcraft.dropper.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The type of information returned by a placeholder
*/
public enum InfoType {
/**
* The player that achieved the record
*/
PLAYER,
/**
* The value of the record, whatever it is
*/
VALUE,
/**
* A combined PLAYER: VALUE
*/
COMBINED,
;
/**
* Gets the info type specified in the given string
*
* @param type <p>The string specifying the info type</p>
* @return <p>The info type, or null if not found</p>
*/
public static @Nullable InfoType getFromString(@NotNull String type) {
for (InfoType infoType : InfoType.values()) {
if (infoType.name().equalsIgnoreCase(type)) {
return infoType;
}
}
return null;
}
}

View File

@ -0,0 +1,37 @@
package net.knarcraft.dropper.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A type of record a player can achieve
*/
public enum RecordType {
/**
* A least-deaths record
*/
DEATHS,
/**
*
*/
TIME,
;
/**
* Gets the record type specified in the given string
*
* @param type <p>The string specifying the record type</p>
* @return <p>The record type, or null if not found</p>
*/
public static @Nullable RecordType getFromString(@NotNull String type) {
for (RecordType recordType : RecordType.values()) {
if (recordType.name().equalsIgnoreCase(type)) {
return recordType;
}
}
return null;
}
}

View File

@ -0,0 +1,37 @@
package net.knarcraft.dropper.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A type of selection performed by a placeholder
*/
public enum SelectionType {
/**
* The identifier is trying to select a group
*/
GROUP,
/**
* The identifier is trying to select an arena
*/
ARENA,
;
/**
* Gets the selection type specified in the given string
*
* @param type <p>The string specifying the selection type</p>
* @return <p>The selection type, or null if not found</p>
*/
public static @Nullable SelectionType getFromString(@NotNull String type) {
for (SelectionType selectionType : SelectionType.values()) {
if (selectionType.name().equalsIgnoreCase(type)) {
return selectionType;
}
}
return null;
}
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A helper class for saving and loading arenas
@ -152,9 +153,10 @@ public final class ArenaStorageHelper {
ArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey()));
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
ArenaStorageKey.WIN_BLOCK_TYPE.getKey());
Logger logger = Dropper.getInstance().getLogger();
if (arenaName == null || spawnLocation == null) {
Dropper.getInstance().getLogger().log(Level.SEVERE, "Could not load the arena at configuration " +
logger.log(Level.SEVERE, "Could not load the arena at configuration " +
"section " + configurationSection.getName() + ". Please check the arenas storage file for issues.");
return null;
}
@ -164,7 +166,7 @@ public final class ArenaStorageHelper {
DropperArenaData arenaData = loadArenaData(arenaId);
if (arenaData == null) {
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to load arena data for " + arenaId);
logger.log(Level.SEVERE, "Unable to load arena data for " + arenaId);
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries = new HashMap<>();
for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) {
@ -174,7 +176,7 @@ public final class ArenaStorageHelper {
}
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
winBlockType.material(), arenaData);
winBlockType.material(), arenaData, Dropper.getInstance().getArenaHandler());
}
/**

View File

@ -0,0 +1,171 @@
package net.knarcraft.dropper.util;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler;
import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.arena.record.SummableArenaRecord;
import net.knarcraft.dropper.property.ArenaGameMode;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
/**
* A helper class for getting combined record data for a dropper group
*/
public final class DropperGroupRecordHelper {
private DropperGroupRecordHelper() {
}
/**
* Gets the combined least-death records for the given group and game-mode
*
* @param group <p>The group to get records from</p>
* @param gameMode <p>The game-mode to get records for</p>
* @return <p>The combined death records</p>
*/
public static @NotNull Set<ArenaRecord<Integer>> getCombinedDeaths(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode) {
Map<UUID, SummableArenaRecord<Integer>> records = new HashMap<>();
@NotNull BiFunction<DropperArena, ArenaGameMode, Set<SummableArenaRecord<Integer>>> recordSupplier =
(arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getLeastDeathsRecords();
return getCombined(group, gameMode, records, recordSupplier);
}
/**
* Gets the combined least-time records for the given group and game-mode
*
* @param group <p>The group to get records from</p>
* @param gameMode <p>The game-mode to get records for</p>
* @return <p>The combined least-time records</p>
*/
public static @NotNull Set<ArenaRecord<Long>> getCombinedTime(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode) {
Map<UUID, SummableArenaRecord<Long>> records = new HashMap<>();
@NotNull BiFunction<DropperArena, ArenaGameMode, Set<SummableArenaRecord<Long>>> recordSupplier =
(arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getShortestTimeMilliSecondsRecords();
return getCombined(group, gameMode, records, recordSupplier);
}
/**
* Gets the combined records for a group and game-mode
*
* @param group <p>The group to get combined records for</p>
* @param gameMode <p>The game-mode to get records for</p>
* @param records <p>The map to store the combined records to</p>
* @param recordSupplier <p>The function that supplies records of this type</p>
* @param <K> <p>The type of the records to combine</p>
* @return <p>The combined records</p>
*/
private static <K extends Comparable<K>> @NotNull Set<ArenaRecord<K>> getCombined(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode,
@NotNull Map<UUID,
SummableArenaRecord<K>> records,
@NotNull BiFunction<DropperArena,
ArenaGameMode,
Set<SummableArenaRecord<K>>> recordSupplier) {
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
// Get all arenas in the group
Set<DropperArena> arenas = getArenas(arenaHandler, group);
// Calculate the combined records
Map<UUID, Integer> recordsFound = new HashMap<>();
combineRecords(arenas, gameMode, records, recordsFound, recordSupplier);
// Filter out any players that haven't played through all arenas
filterRecords(records, recordsFound, arenas.size());
return new HashSet<>(records.values());
}
/**
* Filters away any records that belong to users who haven't set records for all arenas in the group
*
* @param records <p>The records to filter</p>
* @param recordsFound <p>The map of how many records have been registered for each user</p>
* @param arenas <p>The number of arenas in the group</p>
* @param <K> <p>The type of the given records</p>
*/
private static <K extends Comparable<K>> void filterRecords(@NotNull Map<UUID, SummableArenaRecord<K>> records,
@NotNull Map<UUID, Integer> recordsFound, int arenas) {
for (UUID userId : recordsFound.keySet()) {
if (recordsFound.get(userId) != arenas) {
records.remove(userId);
}
}
}
/**
* Gets all arenas in the given group
*
* @param arenaHandler <p>The arena handler to get arenas from</p>
* @param group <p>The group to get arenas for</p>
* @return <p>The arenas found in the group</p>
*/
private static @NotNull Set<DropperArena> getArenas(@NotNull DropperArenaHandler arenaHandler,
@NotNull DropperArenaGroup group) {
// Get all arenas in the group
Set<DropperArena> arenas = new HashSet<>();
for (UUID arenaId : group.getArenas()) {
DropperArena arena = arenaHandler.getArena(arenaId);
if (arena != null) {
arenas.add(arena);
}
}
return arenas;
}
/**
* Combines arena records
*
* @param arenas <p>The arenas whose records should be combined</p>
* @param gameMode <p>The game-mode to combine records for</p>
* @param combinedRecords <p>The map to store the combined records to</p>
* @param recordsFound <p>The map used to store the number of records registered for each player</p>
* @param recordSupplier <p>The function that supplies record data of this type</p>
* @param <K> <p>The type of record to combine</p>
*/
private static <K extends Comparable<K>> void combineRecords(@NotNull Set<DropperArena> arenas,
@NotNull ArenaGameMode gameMode,
@NotNull Map<UUID,
SummableArenaRecord<K>> combinedRecords,
@NotNull Map<UUID, Integer> recordsFound,
@NotNull BiFunction<DropperArena, ArenaGameMode,
Set<SummableArenaRecord<K>>> recordSupplier) {
for (DropperArena arena : arenas) {
Set<SummableArenaRecord<K>> existingRecords = recordSupplier.apply(arena, gameMode);
// For each arena's record registry, calculate the combined records
for (SummableArenaRecord<K> value : existingRecords) {
if (value == null) {
continue;
}
UUID userId = value.getUserId();
// Bump the number of records found for the user
if (!recordsFound.containsKey(userId)) {
recordsFound.put(userId, 0);
}
recordsFound.put(userId, recordsFound.get(userId) + 1);
// Put the value, or the sum with the existing value, into combined records
if (!combinedRecords.containsKey(userId)) {
combinedRecords.put(value.getUserId(), value);
} else {
combinedRecords.put(userId, combinedRecords.get(userId).sum(value.getRecord()));
}
}
}
}
}

View File

@ -3,6 +3,8 @@ version: '${project.version}'
main: net.knarcraft.dropper.Dropper
api-version: 1.19
description: A plugin for dropper mini-games
softdepend:
- PlaceholderAPI
# Note to self: Aliases must be lowercase!
commands: