Compare commits

...

6 Commits
v1.1 ... master

Author SHA1 Message Date
d7e950c53e Improves display of time records 2024-04-04 15:50:38 +02:00
c8fbdec64f Fixes some minor issues
Fixes the tab-completion for the randomly inverted game-mode not being properly matched.
Prevents the usage text from displaying for some join errors
2024-04-04 04:28:44 +02:00
09f29e5f67
Merge pull request #30 from SunNetservers/dev
Arena saving bugfix
2024-04-04 03:07:30 +02:00
7213bd0c70 Fixes README formatting 2024-04-04 03:03:57 +02:00
a23d0c769f Bumps version 2024-04-04 03:01:28 +02:00
142cd5339b Fixes a bug in arena saving
Saving a single arena caused all other arena data of that type to be deleted.
2024-04-04 03:00:03 +02:00
14 changed files with 86 additions and 107 deletions

View File

@ -204,26 +204,26 @@ This command allows editing the specified property for the specified parkour are
These are all the options that can be changed for an arena.
| Option | Details |
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | The name of the arena. Used mainly to select the arena in commands. Note that underscore (_) cannot be used if you want to utilize placeholders, as it's used to split placeholder arguments. |
| spawnLocation | The spawn location of any player joining the arena. Use `56.546,64.0,44.45` to specify coordinates, or `here`, `this` or any other string to select your current location. |
| exitLocation | The location players will be sent to when exiting the arena. If not set, the player will be sent to where they joined from. Valid values are the same as for spawnLocation. |
| 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. |
| winLocation | The location players must reach to win the arena (see spawnLocation for valid values). If set, this overrides, and is used instead of, the win block type. |
| checkpointAdd | Adds a new checkpoint to the arena's checkpoints (see spawnLocation for valid values). |
| checkpointClear | Clears all current checkpoints. Give any value to execute. If not given a value, current checkpoints are shown. |
| killPlaneBlocks | A comma-separated list of materials which will force a loss when stepped on. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. |
| obstacleBlocks | A comma-separated list of materials which will force a loss when touched from any direction. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. |
| Option | Details |
|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | The name of the arena. Used mainly to select the arena in commands. Note that underscore (_) cannot be used if you want to utilize placeholders, as it's used to split placeholder arguments. |
| spawnLocation | The spawn location of any player joining the arena. Use `56.546,64.0,44.45` to specify coordinates, or `here`, `this` or any other string to select your current location. |
| exitLocation | The location players will be sent to when exiting the arena. If not set, the player will be sent to where they joined from. Valid values are the same as for spawnLocation. |
| 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. |
| winLocation | The location players must reach to win the arena (see spawnLocation for valid values). If set, this overrides, and is used instead of, the win block type. |
| checkpointAdd | Adds a new checkpoint to the arena's checkpoints (see spawnLocation for valid values). |
| checkpointClear | Clears all current checkpoints. Give any value to execute. If not given a value, current checkpoints are shown. |
| killPlaneBlocks | A comma-separated list of materials which will force a loss when stepped on. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. |
| obstacleBlocks | A comma-separated list of materials which will force a loss when touched from any direction. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. |
## Configuration options
### Shared
| Name | Type | Default | Description |
|-----------------------------------|---------------------|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| liquidHitBoxDepth | -1 < decimal < 0 | -0.8 | This decides how far inside a non-solid block the player must go before detection triggers (-1, 0). The closer to -1 it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases. |
| solidHitBoxDistance | 0 < decimal < 1 | 0.2 | This decides the distance the player must be from a block below them before a hit triggers (0, 1). If too low, the likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near. |
| Name | Type | Default | Description |
|---------------------|------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| liquidHitBoxDepth | -1 < decimal < 0 | -0.8 | This decides how far inside a non-solid block the player must go before detection triggers (-1, 0). The closer to -1 it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases. |
| solidHitBoxDistance | 0 < decimal < 1 | 0.2 | This decides the distance the player must be from a block below them before a hit triggers (0, 1). If too low, the likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near. |
### Dropper

View File

@ -6,7 +6,7 @@
<groupId>net.knarcraft</groupId>
<artifactId>MiniGames</artifactId>
<version>1.1</version>
<version>1.2</version>
<packaging>jar</packaging>
<name>MiniGames</name>
@ -105,12 +105,6 @@
<version>24.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>

View File

@ -104,7 +104,7 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
for (PlayerEntryState entryState : entryStates) {
this.entryStates.put(entryState.getPlayerId(), entryState);
}
if (this.entryStates.size() > 0) {
if (!this.entryStates.isEmpty()) {
MiniGames.log(Level.WARNING, entryStates.size() + " un-exited sessions found. This happens if " +
"players leave in the middle of a game, or if the server crashes. MiniGames will do its best " +
"to fix the players' states.");

View File

@ -34,11 +34,11 @@ public enum DropperArenaGameMode implements ConfigurationSerializable, ArenaGame
* @param gameMode <p>The game-mode string to match</p>
* @return <p>The specified arena game-mode</p>
*/
public static @NotNull DropperArenaGameMode matchGamemode(@NotNull String gameMode) {
public static @NotNull DropperArenaGameMode matchGameMode(@NotNull String gameMode) {
String sanitized = gameMode.trim().toLowerCase();
if (sanitized.matches("(invert(ed)?|inverse)")) {
return DropperArenaGameMode.INVERTED;
} else if (sanitized.matches("rand(om)?")) {
} else if (sanitized.matches("rand(om)?_?(inverted)?")) {
return DropperArenaGameMode.RANDOM_INVERTED;
} else {
return DropperArenaGameMode.DEFAULT;

View File

@ -44,6 +44,13 @@ public abstract class ArenaRecord<K extends Comparable<K>> implements Comparable
return record;
}
/**
* Gets this as a string that should be printed on a sign
*
* @return <p>This as a string</p>
*/
public abstract String getAsString();
@Override
public boolean equals(Object other) {
return other instanceof ArenaRecord<?> && userId.equals(((ArenaRecord<?>) other).userId);
@ -70,7 +77,7 @@ public abstract class ArenaRecord<K extends Comparable<K>> implements Comparable
@Override
public String toString() {
return userId + ": " + record;
return userId + ":" + record;
}
}

View File

@ -19,6 +19,11 @@ public class IntegerRecord extends SummableArenaRecord<Integer> {
super(userId, record);
}
@Override
public String getAsString() {
return String.valueOf(this.getRecord());
}
@Override
public SummableArenaRecord<Integer> sum(Integer value) {
return new IntegerRecord(this.getUserId(), this.getRecord() + value);

View File

@ -7,7 +7,7 @@ import java.util.Map;
import java.util.UUID;
/**
* A record storing a Long
* A record storing a Long time
*/
public class LongRecord extends SummableArenaRecord<Long> {
@ -29,6 +29,22 @@ public class LongRecord extends SummableArenaRecord<Long> {
return new LongRecord(this.getUserId(), this.getRecord() + value);
}
@Override
public String getAsString() {
int seconds = (int) Math.floor(getRecord() / 1000.0);
int minutes = 0;
if (seconds > 60) {
minutes = (int) Math.floor(seconds / 60.0);
seconds = seconds % 60;
}
if (minutes > 0) {
return minutes + "m" + seconds + "s";
} else {
return seconds + "s";
}
}
/**
* Deserializes the saved arena record
*

View File

@ -38,7 +38,7 @@ public class JoinDropperArenaCommand implements CommandExecutor {
// Disallow joining if the player is already in a mini-game arena
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
stringFormatter.displayErrorMessage(commandSender, MiniGameMessage.ERROR_ALREADY_PLAYING);
return false;
return true;
}
// Make sure the arena exists
@ -51,10 +51,11 @@ public class JoinDropperArenaCommand implements CommandExecutor {
// Deny vehicles as allowing this is tricky, and will cause problems in some cases
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) {
stringFormatter.displayErrorMessage(commandSender, MiniGameMessage.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER);
return false;
return true;
}
return joinArena(specifiedArena, player, arguments);
joinArena(specifiedArena, player, arguments);
return true;
}
/**
@ -63,13 +64,12 @@ public class JoinDropperArenaCommand implements CommandExecutor {
* @param specifiedArena <p>The arena the player wants to join</p>
* @param player <p>The player joining the arena</p>
* @param arguments <p>The arguments given</p>
* @return <p>Whether the arena was joined successfully</p>
*/
private boolean joinArena(DropperArena specifiedArena, Player player, String[] arguments) {
private void joinArena(DropperArena specifiedArena, Player player, String[] arguments) {
// Find the specified game-mode
DropperArenaGameMode gameMode;
if (arguments.length > 1) {
gameMode = DropperArenaGameMode.matchGamemode(arguments[1]);
gameMode = DropperArenaGameMode.matchGameMode(arguments[1]);
} else {
gameMode = DropperArenaGameMode.DEFAULT;
}
@ -77,7 +77,7 @@ public class JoinDropperArenaCommand implements CommandExecutor {
// Make sure the player has beaten the necessary levels
DropperArenaGroup arenaGroup = MiniGames.getInstance().getDropperArenaHandler().getGroup(specifiedArena.getArenaId());
if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
return false;
return;
}
StringFormatter stringFormatter = MiniGames.getInstance().getStringFormatter();
@ -87,7 +87,7 @@ public class JoinDropperArenaCommand implements CommandExecutor {
gameMode != DropperArenaGameMode.DEFAULT &&
specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) {
stringFormatter.displayErrorMessage(player, MiniGameMessage.ERROR_NORMAL_MODE_REQUIRED);
return false;
return;
}
// Register the player's session
@ -103,14 +103,12 @@ public class JoinDropperArenaCommand implements CommandExecutor {
if (!teleported) {
stringFormatter.displayErrorMessage(player, MiniGameMessage.ERROR_ARENA_TELEPORT_FAILED);
newSession.triggerQuit(false, true);
return false;
} else {
// Update the player's state to follow the arena's rules
newSession.getEntryState().setArenaState();
player.getInventory().addItem(GUIHelper.getGUIOpenItem());
stringFormatter.displaySuccessMessage(player, MiniGameMessage.SUCCESS_ARENA_JOINED);
return true;
}
}

View File

@ -38,7 +38,7 @@ public class JoinParkourArenaCommand implements CommandExecutor {
// Disallow joining if the player is already in a mini-game arena
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
stringFormatter.displayErrorMessage(commandSender, MiniGameMessage.ERROR_ALREADY_PLAYING);
return false;
return true;
}
// Make sure the arena exists
@ -51,10 +51,11 @@ public class JoinParkourArenaCommand implements CommandExecutor {
// Deny vehicles as allowing this is tricky, and will cause problems in some cases
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) {
stringFormatter.displayErrorMessage(commandSender, MiniGameMessage.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER);
return false;
return true;
}
return joinArena(specifiedArena, player, arguments);
joinArena(specifiedArena, player, arguments);
return true;
}
/**
@ -63,9 +64,8 @@ public class JoinParkourArenaCommand implements CommandExecutor {
* @param specifiedArena <p>The arena the player wants to join</p>
* @param player <p>The player joining the arena</p>
* @param arguments <p>The arguments given</p>
* @return <p>Whether the arena was joined successfully</p>
*/
private boolean joinArena(ParkourArena specifiedArena, Player player, String[] arguments) {
private void joinArena(ParkourArena specifiedArena, Player player, String[] arguments) {
// Find the specified game-mode
ParkourArenaGameMode gameMode;
if (arguments.length > 1) {
@ -78,13 +78,13 @@ public class JoinParkourArenaCommand implements CommandExecutor {
if (specifiedArena.hasNoCheckpoints() && gameMode == ParkourArenaGameMode.HARDCORE) {
MiniGames.getInstance().getStringFormatter().displayErrorMessage(player,
MiniGameMessage.ERROR_HARDCORE_NO_CHECKPOINTS);
return false;
return;
}
// Make sure the player has beaten the necessary levels
ParkourArenaGroup arenaGroup = MiniGames.getInstance().getParkourArenaHandler().getGroup(specifiedArena.getArenaId());
if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
return false;
return;
}
// Register the player's session
@ -101,7 +101,6 @@ public class JoinParkourArenaCommand implements CommandExecutor {
MiniGames.getInstance().getStringFormatter().displayErrorMessage(player,
MiniGameMessage.ERROR_ARENA_TELEPORT_FAILED);
newSession.triggerQuit(false, true);
return false;
} else {
// Update the player's state to follow the arena's rules
newSession.getEntryState().setArenaState();
@ -109,7 +108,6 @@ public class JoinParkourArenaCommand implements CommandExecutor {
player.getInventory().addItem(GUIHelper.getGUIOpenItem());
MiniGames.getInstance().getStringFormatter().displaySuccessMessage(player,
MiniGameMessage.SUCCESS_ARENA_JOINED);
return true;
}
}

View File

@ -26,7 +26,7 @@ public class DropperRecordExpansion extends RecordExpansion {
@Override
protected @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode) {
return DropperArenaGameMode.matchGamemode(gameMode);
return DropperArenaGameMode.matchGameMode(gameMode);
}
}

View File

@ -313,8 +313,8 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
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();
case VALUE -> arenaRecord.getAsString();
case COMBINED -> getPlayerName(arenaRecord.getUserId()) + ": " + arenaRecord.getAsString();
};
}

View File

@ -20,6 +20,7 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.io.IOException;
@ -109,8 +110,11 @@ public final class DropperArenaStorageHelper {
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveSingleDropperArena(DropperArena arena) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(dropperArenasConfigurationSection);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(dropperArenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(dropperArenasConfigurationSection);
if (arenaSection == null) {
arenaSection = configuration.createSection(dropperArenasConfigurationSection);
}
saveDropperArena(arenaSection, arena);
configuration.save(dropperArenaFile);
}
@ -246,9 +250,14 @@ public final class DropperArenaStorageHelper {
* @return <p>The loaded arena data</p>
*/
private static @Nullable DropperArenaData loadDropperArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getDropperArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (DropperArenaData) configuration.get(DropperArenaStorageKey.DATA.getKey());
try {
File arenaDataFile = getDropperArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (DropperArenaData) configuration.get(DropperArenaStorageKey.DATA.getKey());
} catch (YAMLException exception) {
MiniGames.log(Level.SEVERE, "Unable to load arena data from arena " + arenaId);
return null;
}
}
/**

View File

@ -111,8 +111,11 @@ public final class ParkourArenaStorageHelper {
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveSingleParkourArena(ParkourArena arena) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(parkourArenasConfigurationSection);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(parkourArenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(parkourArenasConfigurationSection);
if (arenaSection == null) {
arenaSection = configuration.createSection(parkourArenasConfigurationSection);
}
saveParkourArena(arenaSection, arena);
configuration.save(parkourArenaFile);
}

View File

@ -1,51 +0,0 @@
package net.knarcraft.minigames.arena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Tests for arena dropper groups
*/
public class MiniGamesArenaGroupTest {
@Test
public void swapTest() {
/*
This test makes sure the order of arenas is as expected when the arenas are added to a group. It also makes
sure that swapping two items works as expected.
*/
DropperArenaGroup arenaGroup = new DropperArenaGroup("test");
UUID arena1Id = UUID.randomUUID();
UUID arena2Id = UUID.randomUUID();
UUID arena3Id = UUID.randomUUID();
UUID arena4Id = UUID.randomUUID();
arenaGroup.addArena(arena1Id);
arenaGroup.addArena(arena2Id);
arenaGroup.addArena(arena3Id);
arenaGroup.addArena(arena4Id);
List<UUID> initialOrder = new ArrayList<>();
initialOrder.add(arena1Id);
initialOrder.add(arena2Id);
initialOrder.add(arena3Id);
initialOrder.add(arena4Id);
Assertions.assertEquals(initialOrder, arenaGroup.getArenas());
arenaGroup.swapArenas(1, 3);
List<UUID> swapped = new ArrayList<>();
swapped.add(arena1Id);
swapped.add(arena4Id);
swapped.add(arena3Id);
swapped.add(arena2Id);
Assertions.assertEquals(swapped, arenaGroup.getArenas());
}
}