mirror of
				https://github.com/SunNetservers/MiniGames.git
				synced 2025-11-04 11:43:45 +01:00 
			
		
		
		
	Merge branch 'group-placeholders'
This commit is contained in:
		
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							@@ -97,4 +97,21 @@ You could use `/droppergroupswap Sea Savanna` to change the order to:
 | 
				
			|||||||
1. Forest
 | 
					1. Forest
 | 
				
			||||||
2. Savanna
 | 
					2. Savanna
 | 
				
			||||||
3. Nether
 | 
					3. Nether
 | 
				
			||||||
4. Sea
 | 
					4. Sea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Record display placeholders
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Player records can be displayed on a leaderboard by using PlaceholderAPI. 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
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								pom.xml
									
									
									
									
									
								
							@@ -58,6 +58,10 @@
 | 
				
			|||||||
            <id>spigot-repo</id>
 | 
					            <id>spigot-repo</id>
 | 
				
			||||||
            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
 | 
					            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
 | 
				
			||||||
        </repository>
 | 
					        </repository>
 | 
				
			||||||
 | 
					        <repository>
 | 
				
			||||||
 | 
					            <id>placeholderapi</id>
 | 
				
			||||||
 | 
					            <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
 | 
				
			||||||
 | 
					        </repository>
 | 
				
			||||||
    </repositories>
 | 
					    </repositories>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <dependencies>
 | 
					    <dependencies>
 | 
				
			||||||
@@ -79,5 +83,11 @@
 | 
				
			|||||||
            <version>5.9.2</version>
 | 
					            <version>5.9.2</version>
 | 
				
			||||||
            <scope>test</scope>
 | 
					            <scope>test</scope>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>me.clip</groupId>
 | 
				
			||||||
 | 
					            <artifactId>placeholderapi</artifactId>
 | 
				
			||||||
 | 
					            <version>2.10.0</version>
 | 
				
			||||||
 | 
					            <scope>provided</scope>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
    </dependencies>
 | 
					    </dependencies>
 | 
				
			||||||
</project>
 | 
					</project>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ import net.knarcraft.dropper.arena.DropperArenaHandler;
 | 
				
			|||||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
 | 
					import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
 | 
				
			||||||
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
 | 
					import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
 | 
				
			||||||
import net.knarcraft.dropper.arena.DropperArenaSession;
 | 
					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.CreateArenaCommand;
 | 
				
			||||||
import net.knarcraft.dropper.command.EditArenaCommand;
 | 
					import net.knarcraft.dropper.command.EditArenaCommand;
 | 
				
			||||||
import net.knarcraft.dropper.command.EditArenaTabCompleter;
 | 
					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.DamageListener;
 | 
				
			||||||
import net.knarcraft.dropper.listener.MoveListener;
 | 
					import net.knarcraft.dropper.listener.MoveListener;
 | 
				
			||||||
import net.knarcraft.dropper.listener.PlayerLeaveListener;
 | 
					import net.knarcraft.dropper.listener.PlayerLeaveListener;
 | 
				
			||||||
 | 
					import net.knarcraft.dropper.placeholder.DropperRecordExpansion;
 | 
				
			||||||
import net.knarcraft.dropper.property.ArenaGameMode;
 | 
					import net.knarcraft.dropper.property.ArenaGameMode;
 | 
				
			||||||
 | 
					import org.bukkit.Bukkit;
 | 
				
			||||||
import org.bukkit.command.CommandExecutor;
 | 
					import org.bukkit.command.CommandExecutor;
 | 
				
			||||||
import org.bukkit.command.PluginCommand;
 | 
					import org.bukkit.command.PluginCommand;
 | 
				
			||||||
import org.bukkit.command.TabCompleter;
 | 
					import org.bukkit.command.TabCompleter;
 | 
				
			||||||
@@ -94,6 +98,8 @@ public final class Dropper extends JavaPlugin {
 | 
				
			|||||||
        ConfigurationSerialization.registerClass(DropperArenaData.class);
 | 
					        ConfigurationSerialization.registerClass(DropperArenaData.class);
 | 
				
			||||||
        ConfigurationSerialization.registerClass(DropperArenaGroup.class);
 | 
					        ConfigurationSerialization.registerClass(DropperArenaGroup.class);
 | 
				
			||||||
        ConfigurationSerialization.registerClass(ArenaGameMode.class);
 | 
					        ConfigurationSerialization.registerClass(ArenaGameMode.class);
 | 
				
			||||||
 | 
					        ConfigurationSerialization.registerClass(LongRecord.class);
 | 
				
			||||||
 | 
					        ConfigurationSerialization.registerClass(IntegerRecord.class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -121,6 +127,12 @@ public final class Dropper extends JavaPlugin {
 | 
				
			|||||||
        registerCommand("dropperGroupSet", new GroupSetCommand(), null);
 | 
					        registerCommand("dropperGroupSet", new GroupSetCommand(), null);
 | 
				
			||||||
        registerCommand("dropperGroupSwap", new GroupSwapCommand(), null);
 | 
					        registerCommand("dropperGroupSwap", new GroupSwapCommand(), null);
 | 
				
			||||||
        registerCommand("dropperGroupList", new GroupListCommand(), 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
 | 
					    @Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,6 +103,12 @@ public record DropperArenaData(@NotNull UUID arenaId,
 | 
				
			|||||||
        Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData =
 | 
					        Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData =
 | 
				
			||||||
                (Map<ArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted");
 | 
					                (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
 | 
					        // Convert the serializable UUIDs to normal UUIDs
 | 
				
			||||||
        Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
 | 
					        Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
 | 
				
			||||||
        for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
 | 
					        for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
 | 
				
			||||||
@@ -111,6 +117,10 @@ public record DropperArenaData(@NotNull UUID arenaId,
 | 
				
			|||||||
                playersCompleted.add(completedId.uuid());
 | 
					                playersCompleted.add(completedId.uuid());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            allPlayersCompleted.put(arenaGameMode, playersCompleted);
 | 
					            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);
 | 
					        return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -160,7 +160,7 @@ public class DropperArenaGroup implements ConfigurationSerializable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        for (UUID anArenaId : this.getArenas()) {
 | 
					        for (UUID anArenaId : this.getArenas()) {
 | 
				
			||||||
            // If the target arena is reached, allow, as all previous arenas must have been cleared
 | 
					            // If the target arena is reached, allow, as all previous arenas must have been cleared
 | 
				
			||||||
            if (arenaId == anArenaId) {
 | 
					            if (arenaId.equals(anArenaId)) {
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,24 @@
 | 
				
			|||||||
package net.knarcraft.dropper.arena;
 | 
					package net.knarcraft.dropper.arena;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import net.knarcraft.dropper.Dropper;
 | 
					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.container.SerializableUUID;
 | 
				
			||||||
import net.knarcraft.dropper.property.RecordResult;
 | 
					import net.knarcraft.dropper.property.RecordResult;
 | 
				
			||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
 | 
					import org.bukkit.configuration.serialization.ConfigurationSerializable;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					import org.jetbrains.annotations.Nullable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.UUID;
 | 
					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
 | 
					 * A registry keeping track of all records
 | 
				
			||||||
@@ -17,16 +26,16 @@ import java.util.stream.Stream;
 | 
				
			|||||||
public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
					public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final UUID arenaId;
 | 
					    private final UUID arenaId;
 | 
				
			||||||
    private final @NotNull Map<UUID, Number> leastDeaths;
 | 
					    private final @NotNull Set<IntegerRecord> leastDeaths;
 | 
				
			||||||
    private final @NotNull Map<UUID, Number> shortestTimeMilliSeconds;
 | 
					    private final @NotNull Set<LongRecord> shortestTimeMilliSeconds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Instantiates a new empty records registry
 | 
					     * Instantiates a new empty records registry
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
 | 
					    public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
 | 
				
			||||||
        this.arenaId = arenaId;
 | 
					        this.arenaId = arenaId;
 | 
				
			||||||
        this.leastDeaths = new HashMap<>();
 | 
					        this.leastDeaths = new HashSet<>();
 | 
				
			||||||
        this.shortestTimeMilliSeconds = new HashMap<>();
 | 
					        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 leastDeaths              <p>The existing least death records to use</p>
 | 
				
			||||||
     * @param shortestTimeMilliSeconds <p>The existing leash time 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,
 | 
					    private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
 | 
				
			||||||
                                        @NotNull Map<UUID, Long> shortestTimeMilliSeconds) {
 | 
					                                        @NotNull Set<LongRecord> shortestTimeMilliSeconds) {
 | 
				
			||||||
        this.arenaId = arenaId;
 | 
					        this.arenaId = arenaId;
 | 
				
			||||||
        this.leastDeaths = new HashMap<>(leastDeaths);
 | 
					        this.leastDeaths = new HashSet<>(leastDeaths);
 | 
				
			||||||
        this.shortestTimeMilliSeconds = new HashMap<>(shortestTimeMilliSeconds);
 | 
					        this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -47,12 +56,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return <p>Existing death records</p>
 | 
					     * @return <p>Existing death records</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public Map<UUID, Integer> getLeastDeathsRecords() {
 | 
					    public Set<SummableArenaRecord<Integer>> getLeastDeathsRecords() {
 | 
				
			||||||
        Map<UUID, Integer> leastDeathRecords = new HashMap<>();
 | 
					        return new HashSet<>(this.leastDeaths);
 | 
				
			||||||
        for (Map.Entry<UUID, Number> entry : this.leastDeaths.entrySet()) {
 | 
					 | 
				
			||||||
            leastDeathRecords.put(entry.getKey(), entry.getValue().intValue());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return leastDeathRecords;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -60,12 +65,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return <p>Existing time records</p>
 | 
					     * @return <p>Existing time records</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public Map<UUID, Long> getShortestTimeMilliSecondsRecords() {
 | 
					    public Set<SummableArenaRecord<Long>> getShortestTimeMilliSecondsRecords() {
 | 
				
			||||||
        Map<UUID, Long> leastTimeRecords = new HashMap<>();
 | 
					        return new HashSet<>(this.shortestTimeMilliSeconds);
 | 
				
			||||||
        for (Map.Entry<UUID, Number> entry : this.shortestTimeMilliSeconds.entrySet()) {
 | 
					 | 
				
			||||||
            leastTimeRecords.put(entry.getKey(), entry.getValue().longValue());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return leastTimeRecords;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -76,7 +77,9 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			|||||||
     * @return <p>The result explaining what type of record was achieved</p>
 | 
					     * @return <p>The result explaining what type of record was achieved</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public @NotNull RecordResult registerDeathRecord(@NotNull UUID playerId, int deaths) {
 | 
					    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>
 | 
					     * @return <p>The result explaining what type of record was achieved</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public @NotNull RecordResult registerTimeRecord(@NotNull UUID playerId, long milliseconds) {
 | 
					    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
 | 
					     * Registers a new record if applicable
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param existingRecords <p>The map of existing records to use</p>
 | 
					     * @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 playerId        <p>The id of the player that potentially achieved a record</p>
 | 
				
			||||||
     * @param amount          <p>The amount of whatever the player achieved</p>
 | 
					     * @param amount          <p>The amount of whatever the player achieved</p>
 | 
				
			||||||
     * @return <p>The result of the player's record attempt</p>
 | 
					     * @return <p>The result of the player's record attempt</p>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private @NotNull RecordResult registerRecord(@NotNull Map<UUID, Number> existingRecords, @NotNull UUID playerId,
 | 
					    private <T extends Comparable<T>> @NotNull RecordResult registerRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
 | 
				
			||||||
                                                 Number amount) {
 | 
					                                                                           @NotNull Consumer<T> recordSetter,
 | 
				
			||||||
 | 
					                                                                           @NotNull UUID playerId, T amount) {
 | 
				
			||||||
        RecordResult result;
 | 
					        RecordResult result;
 | 
				
			||||||
        Stream<Map.Entry<UUID, Number>> records = existingRecords.entrySet().stream();
 | 
					        if (existingRecords.stream().allMatch((entry) -> amount.compareTo(entry.getRecord()) < 0)) {
 | 
				
			||||||
        long amountLong = amount.longValue();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (records.allMatch((entry) -> amountLong < entry.getValue().longValue())) {
 | 
					 | 
				
			||||||
            // If the given value is less than all other values, that's a world record!
 | 
					            // If the given value is less than all other values, that's a world record!
 | 
				
			||||||
            result = RecordResult.WORLD_RECORD;
 | 
					            result = RecordResult.WORLD_RECORD;
 | 
				
			||||||
            existingRecords.put(playerId, amount);
 | 
					            recordSetter.accept(amount);
 | 
				
			||||||
            save();
 | 
					            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!
 | 
					            // If the given value is less than the player's previous value, that's a personal best!
 | 
				
			||||||
            result = RecordResult.PERSONAL_BEST;
 | 
					            result = RecordResult.PERSONAL_BEST;
 | 
				
			||||||
            existingRecords.put(playerId, amount);
 | 
					            recordSetter.accept(amount);
 | 
				
			||||||
            save();
 | 
					            save();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Make sure to save the record if this is the user's first attempt
 | 
					            // 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();
 | 
					                save();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            result = RecordResult.NONE;
 | 
					            result = RecordResult.NONE;
 | 
				
			||||||
@@ -132,23 +141,32 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			|||||||
        return result;
 | 
					        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
 | 
					    @NotNull
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Map<String, Object> serialize() {
 | 
					    public Map<String, Object> serialize() {
 | 
				
			||||||
        Map<String, Object> data = new HashMap<>();
 | 
					        Map<String, Object> data = new HashMap<>();
 | 
				
			||||||
        data.put("arenaId", new SerializableUUID(this.arenaId));
 | 
					        data.put("arenaId", new SerializableUUID(this.arenaId));
 | 
				
			||||||
 | 
					        data.put("leastDeaths", this.leastDeaths);
 | 
				
			||||||
        Map<SerializableUUID, Number> leastDeaths = new HashMap<>();
 | 
					        data.put("shortestTime", this.shortestTimeMilliSeconds);
 | 
				
			||||||
        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);
 | 
					 | 
				
			||||||
        return data;
 | 
					        return data;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,20 +179,13 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
 | 
				
			|||||||
    @SuppressWarnings({"unused", "unchecked"})
 | 
					    @SuppressWarnings({"unused", "unchecked"})
 | 
				
			||||||
    public static DropperArenaRecordsRegistry deserialize(Map<String, Object> data) {
 | 
					    public static DropperArenaRecordsRegistry deserialize(Map<String, Object> data) {
 | 
				
			||||||
        UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
 | 
					        UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
 | 
				
			||||||
        Map<SerializableUUID, Integer> leastDeathsData =
 | 
					        Set<IntegerRecord> leastDeaths =
 | 
				
			||||||
                (Map<SerializableUUID, Integer>) data.getOrDefault("leastDeaths", new HashMap<>());
 | 
					                (Set<IntegerRecord>) data.getOrDefault("leastDeaths", new HashMap<>());
 | 
				
			||||||
        Map<UUID, Integer> leastDeaths = new HashMap<>();
 | 
					        Set<LongRecord> shortestTimeMilliseconds =
 | 
				
			||||||
        for (Map.Entry<SerializableUUID, Integer> entry : leastDeathsData.entrySet()) {
 | 
					                (Set<LongRecord>) data.getOrDefault("shortestTime", new HashMap<>());
 | 
				
			||||||
            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());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        leastDeaths.removeIf(Objects::isNull);
 | 
				
			||||||
 | 
					        shortestTimeMilliseconds.removeIf(Objects::isNull);
 | 
				
			||||||
        return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
 | 
					        return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@ import net.knarcraft.dropper.property.ArenaGameMode;
 | 
				
			|||||||
import org.bukkit.GameMode;
 | 
					import org.bukkit.GameMode;
 | 
				
			||||||
import org.bukkit.Location;
 | 
					import org.bukkit.Location;
 | 
				
			||||||
import org.bukkit.entity.Player;
 | 
					import org.bukkit.entity.Player;
 | 
				
			||||||
 | 
					import org.bukkit.potion.PotionEffect;
 | 
				
			||||||
 | 
					import org.bukkit.potion.PotionEffectType;
 | 
				
			||||||
import org.jetbrains.annotations.NotNull;
 | 
					import org.jetbrains.annotations.NotNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -19,6 +21,7 @@ public class PlayerEntryState {
 | 
				
			|||||||
    private final boolean originalAllowFlight;
 | 
					    private final boolean originalAllowFlight;
 | 
				
			||||||
    private final boolean originalInvulnerable;
 | 
					    private final boolean originalInvulnerable;
 | 
				
			||||||
    private final boolean originalIsSwimming;
 | 
					    private final boolean originalIsSwimming;
 | 
				
			||||||
 | 
					    private final boolean originalCollideAble;
 | 
				
			||||||
    private final ArenaGameMode arenaGameMode;
 | 
					    private final ArenaGameMode arenaGameMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -36,6 +39,7 @@ public class PlayerEntryState {
 | 
				
			|||||||
        this.originalInvulnerable = player.isInvulnerable();
 | 
					        this.originalInvulnerable = player.isInvulnerable();
 | 
				
			||||||
        this.originalIsSwimming = player.isSwimming();
 | 
					        this.originalIsSwimming = player.isSwimming();
 | 
				
			||||||
        this.arenaGameMode = arenaGameMode;
 | 
					        this.arenaGameMode = arenaGameMode;
 | 
				
			||||||
 | 
					        this.originalCollideAble = player.isCollidable();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -48,6 +52,8 @@ public class PlayerEntryState {
 | 
				
			|||||||
        this.player.setFlying(true);
 | 
					        this.player.setFlying(true);
 | 
				
			||||||
        this.player.setGameMode(GameMode.ADVENTURE);
 | 
					        this.player.setGameMode(GameMode.ADVENTURE);
 | 
				
			||||||
        this.player.setSwimming(false);
 | 
					        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 playing on the inverted game-mode, negate the horizontal velocity to swap the controls
 | 
				
			||||||
        if (arenaGameMode == ArenaGameMode.INVERTED) {
 | 
					        if (arenaGameMode == ArenaGameMode.INVERTED) {
 | 
				
			||||||
@@ -67,6 +73,8 @@ public class PlayerEntryState {
 | 
				
			|||||||
        this.player.setFlySpeed(this.originalFlySpeed);
 | 
					        this.player.setFlySpeed(this.originalFlySpeed);
 | 
				
			||||||
        this.player.setInvulnerable(this.originalInvulnerable);
 | 
					        this.player.setInvulnerable(this.originalInvulnerable);
 | 
				
			||||||
        this.player.setSwimming(this.originalIsSwimming);
 | 
					        this.player.setSwimming(this.originalIsSwimming);
 | 
				
			||||||
 | 
					        this.player.setCollidable(this.originalCollideAble);
 | 
				
			||||||
 | 
					        this.player.removePotionEffect(PotionEffectType.INVISIBILITY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,244 @@
 | 
				
			|||||||
 | 
					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.bukkit.entity.Player;
 | 
				
			||||||
 | 
					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) {
 | 
				
			||||||
 | 
					        Player player = Bukkit.getPlayer(playerId);
 | 
				
			||||||
 | 
					        if (player != null) {
 | 
				
			||||||
 | 
					            return player.getName();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return playerId.toString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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()));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,6 +3,8 @@ version: '${project.version}'
 | 
				
			|||||||
main: net.knarcraft.dropper.Dropper
 | 
					main: net.knarcraft.dropper.Dropper
 | 
				
			||||||
api-version: 1.19
 | 
					api-version: 1.19
 | 
				
			||||||
description: A plugin for dropper mini-games
 | 
					description: A plugin for dropper mini-games
 | 
				
			||||||
 | 
					softdepend:
 | 
				
			||||||
 | 
					  - PlaceholderAPI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Note to self: Aliases must be lowercase!
 | 
					# Note to self: Aliases must be lowercase!
 | 
				
			||||||
commands:
 | 
					commands:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user