Folder refactoring fixed the broken file
This commit is contained in:
parent
574f9fc4b5
commit
261c4a5cab
24 changed files with 385 additions and 150 deletions
|
|
@ -1,7 +0,0 @@
|
|||
package dev.loki.loparkour;
|
||||
|
||||
/**
|
||||
* @deprecated Replaced by {@link LoParkourCommand}. Delete this file.
|
||||
*/
|
||||
@Deprecated
|
||||
class Command extends LoParkourCommand {}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package dev.loki.loparkour;
|
||||
|
||||
/**
|
||||
* @deprecated Split into:
|
||||
* - {@link dev.loki.loparkour.listener.PlayerConnectionListener}
|
||||
* - {@link dev.loki.loparkour.listener.ParkourRestrictionListener}
|
||||
* - {@link dev.loki.loparkour.listener.SchematicWandListener}
|
||||
*
|
||||
* Delete this file after verifying compilation.
|
||||
*/
|
||||
@Deprecated
|
||||
class Events {}
|
||||
|
|
@ -14,6 +14,7 @@ import dev.loki.loparkour.mode.Modes;
|
|||
import dev.loki.loparkour.mode.SpectatorMode;
|
||||
import dev.loki.loparkour.mode.SpeedrunMode;
|
||||
import dev.loki.loparkour.player.ParkourUser;
|
||||
import dev.loki.loparkour.player.UserRegistry;
|
||||
import dev.loki.loparkour.reward.Rewards;
|
||||
import dev.loki.loparkour.schematic.lpschem.LPSchematicManager;
|
||||
import dev.loki.loparkour.storage.Storage;
|
||||
|
|
@ -243,8 +244,7 @@ public final class LoParkour extends LoPlugin {
|
|||
metrics.addCustomChart(new SimplePie("using_rewards", () -> Boolean.toString(Rewards.REWARDS_ENABLED)));
|
||||
metrics.addCustomChart(new SimplePie("locale_count", () -> Integer.toString(Locales.locales.size())));
|
||||
metrics.addCustomChart(new SingleLineChart("player_joins", () -> {
|
||||
int joins = ParkourUser.joinCount;
|
||||
ParkourUser.joinCount = 0;
|
||||
int joins = UserRegistry.getJoinCount();
|
||||
return joins;
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,12 @@ public enum Config {
|
|||
|
||||
/**
|
||||
* Updates the file so all keys are present.
|
||||
* Any key that exists in the bundled default but is missing on disk
|
||||
* will be appended to the correct section automatically.
|
||||
*/
|
||||
public void update() {
|
||||
try {
|
||||
// TODO: ConfigUpdater.update(LoParkour.getPlugin(), fileName, path, ignoredSections);
|
||||
ConfigUpdater.update(LoParkour.getPlugin(), fileName, path, ignoredSections);
|
||||
} catch (Exception ex) {
|
||||
LoParkour.getPlugin().getLogger().log(java.util.logging.Level.SEVERE,
|
||||
"Error while trying to update config file: " + fileName, ex);
|
||||
|
|
|
|||
287
src/main/java/dev/loki/loparkour/config/ConfigUpdater.java
Normal file
287
src/main/java/dev/loki/loparkour/config/ConfigUpdater.java
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
package dev.loki.loparkour.config;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Automatically adds missing keys from the default (bundled) config
|
||||
* into the player's config file on disk, preserving all existing values,
|
||||
* comments, and blank lines.
|
||||
*
|
||||
* <p>Algorithm:
|
||||
* <ol>
|
||||
* <li>Load the default config from the jar as a list of raw lines.</li>
|
||||
* <li>Load the on-disk config as a flat key→value map.</li>
|
||||
* <li>Walk the default lines; for every key that is missing on disk,
|
||||
* emit the default line (with its comment header) into the output.</li>
|
||||
* <li>Rewrite the on-disk file with the merged result.</li>
|
||||
* </ol>
|
||||
*
|
||||
* Sections listed in {@code ignoredSections} are never touched — their
|
||||
* default content is never injected (user manages them freely).
|
||||
*/
|
||||
public final class ConfigUpdater {
|
||||
|
||||
private ConfigUpdater() {}
|
||||
|
||||
/**
|
||||
* Updates {@code diskFile} by injecting any keys that exist in the
|
||||
* bundled default but are absent on disk.
|
||||
*
|
||||
* @param plugin the plugin (used to read the bundled resource)
|
||||
* @param resourceName path inside the jar, e.g. {@code "config.yml"}
|
||||
* @param diskFile the on-disk file to update
|
||||
* @param ignoredSections top-level sections whose keys should never be added
|
||||
* (pass {@code null} or empty list to ignore nothing)
|
||||
*/
|
||||
public static void update(
|
||||
@NotNull Plugin plugin,
|
||||
@NotNull String resourceName,
|
||||
@NotNull File diskFile,
|
||||
@Nullable List<String> ignoredSections
|
||||
) throws IOException {
|
||||
|
||||
List<String> defaultLines = readResource(plugin, resourceName);
|
||||
if (defaultLines == null) {
|
||||
plugin.getLogger().warning("[ConfigUpdater] Resource not found in jar: " + resourceName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Flat key set of what already exists on disk (dot-separated paths)
|
||||
Set<String> diskKeys = flatKeys(diskFile);
|
||||
|
||||
// Ignored section prefixes (e.g. "styles" → skip any path starting with "styles.")
|
||||
Set<String> ignored = normalizedIgnored(ignoredSections);
|
||||
|
||||
List<String> diskLines = readLines(diskFile);
|
||||
List<String> outputLines = merge(defaultLines, diskLines, diskKeys, ignored);
|
||||
|
||||
// Only write if something actually changed
|
||||
if (!outputLines.equals(diskLines)) {
|
||||
writeLines(diskFile, outputLines);
|
||||
plugin.getLogger().info("[ConfigUpdater] Updated " + resourceName
|
||||
+ " — added " + countNew(diskLines, outputLines) + " new key(s).");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Merge ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Merges default lines into disk lines.
|
||||
*
|
||||
* <p>Strategy: walk disk lines first, emit them as-is. Then walk default
|
||||
* lines; for each key block (comment + key line) whose key is absent on
|
||||
* disk and not ignored, append it at the correct indentation level.
|
||||
*/
|
||||
private static List<String> merge(
|
||||
List<String> defaultLines,
|
||||
List<String> diskLines,
|
||||
Set<String> diskKeys,
|
||||
Set<String> ignored
|
||||
) {
|
||||
// We need to insert missing keys at the right position.
|
||||
// Build a copy of disk lines we will extend.
|
||||
List<String> result = new ArrayList<>(diskLines);
|
||||
|
||||
// Collect blocks from the default file: each block = list of comment
|
||||
// lines + the key line itself. We track the "path stack" to know the
|
||||
// full dotted path of every key.
|
||||
Deque<String> pathStack = new ArrayDeque<>();
|
||||
List<String> pendingComments = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < defaultLines.size(); i++) {
|
||||
String raw = defaultLines.get(i);
|
||||
String trimmed = raw.stripLeading();
|
||||
|
||||
// Blank line → flush pending comments, keep as separator
|
||||
if (trimmed.isBlank()) {
|
||||
pendingComments.add(raw);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Comment line → accumulate
|
||||
if (trimmed.startsWith("#")) {
|
||||
pendingComments.add(raw);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key line
|
||||
int indent = raw.length() - trimmed.length();
|
||||
int depth = indent / 2; // assume 2-space indentation
|
||||
|
||||
// Pop stack back to current depth
|
||||
while (pathStack.size() > depth) pathStack.pollLast();
|
||||
|
||||
// Parse key name (everything before ':')
|
||||
String keyPart = trimmed.split(":")[0].trim();
|
||||
pathStack.addLast(keyPart);
|
||||
String fullPath = String.join(".", pathStack);
|
||||
|
||||
boolean isSection = trimmed.endsWith(":") || trimmed.matches(".*:\\s*$");
|
||||
boolean isIgnored = isIgnored(fullPath, ignored);
|
||||
|
||||
if (!isIgnored && !diskKeys.contains(fullPath) && !isSection) {
|
||||
// This key is missing on disk — inject it
|
||||
// Find the best insertion point: after the last line that
|
||||
// belongs to the parent section on disk
|
||||
int insertAt = findInsertionPoint(result, pathStack, depth);
|
||||
|
||||
// Insert blank separator + comments + key line
|
||||
List<String> block = new ArrayList<>();
|
||||
if (insertAt == result.size() || !result.get(Math.max(0, insertAt - 1)).isBlank()) {
|
||||
block.add("");
|
||||
}
|
||||
block.addAll(pendingComments);
|
||||
block.add(raw);
|
||||
result.addAll(insertAt, block);
|
||||
}
|
||||
|
||||
// Clear pending comments regardless
|
||||
pendingComments.clear();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the line index where a missing key should be inserted.
|
||||
* Inserts after the last line of its parent section.
|
||||
*/
|
||||
private static int findInsertionPoint(List<String> lines, Deque<String> pathStack, int depth) {
|
||||
if (depth == 0) {
|
||||
// Top-level key → append at end
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
// Parent path
|
||||
List<String> parts = new ArrayList<>(pathStack);
|
||||
String parentKey = parts.get(depth - 1);
|
||||
int parentIndent = (depth - 1) * 2;
|
||||
|
||||
// Find the parent key line in the result
|
||||
int parentLine = -1;
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
String l = lines.get(i);
|
||||
int ind = l.length() - l.stripLeading().length();
|
||||
if (ind == parentIndent && l.stripLeading().startsWith(parentKey + ":")) {
|
||||
parentLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentLine < 0) return lines.size();
|
||||
|
||||
// Find end of parent section: next line with same or lower indent that isn't blank/comment
|
||||
for (int i = parentLine + 1; i < lines.size(); i++) {
|
||||
String l = lines.get(i);
|
||||
if (l.isBlank() || l.stripLeading().startsWith("#")) continue;
|
||||
int ind = l.length() - l.stripLeading().length();
|
||||
if (ind <= parentIndent) return i;
|
||||
}
|
||||
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
// ── IO helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
/** Reads a resource from the jar as a list of lines. Returns null if not found. */
|
||||
@Nullable
|
||||
private static List<String> readResource(Plugin plugin, String name) {
|
||||
try (InputStream in = plugin.getResource(name)) {
|
||||
if (in == null) return null;
|
||||
return readLines(in);
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to read resource: " + name, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> readLines(File file) throws IOException {
|
||||
try (BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
return readLines(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> readLines(InputStream in) throws IOException {
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||
return readLines(r);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> readLines(BufferedReader reader) throws IOException {
|
||||
List<String> lines = new ArrayList<>();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) lines.add(line);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static void writeLines(File file, List<String> lines) throws IOException {
|
||||
try (BufferedWriter w = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
w.write(lines.get(i));
|
||||
if (i < lines.size() - 1) w.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Key extraction ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns the flat set of dotted-path keys present in the file.
|
||||
* Only leaf keys (not sections) are returned.
|
||||
*/
|
||||
private static Set<String> flatKeys(File file) throws IOException {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
Deque<String> stack = new ArrayDeque<>();
|
||||
|
||||
List<String> lines = readLines(file);
|
||||
for (String raw : lines) {
|
||||
String trimmed = raw.stripLeading();
|
||||
if (trimmed.isBlank() || trimmed.startsWith("#")) continue;
|
||||
|
||||
int indent = raw.length() - trimmed.length();
|
||||
int depth = indent / 2;
|
||||
|
||||
while (stack.size() > depth) stack.pollLast();
|
||||
|
||||
if (!trimmed.contains(":")) continue;
|
||||
String keyPart = trimmed.split(":")[0].trim();
|
||||
stack.addLast(keyPart);
|
||||
|
||||
boolean isSection = trimmed.matches("[^:]+:\\s*$");
|
||||
if (!isSection) {
|
||||
keys.add(String.join(".", stack));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
private static Set<String> normalizedIgnored(@Nullable List<String> list) {
|
||||
if (list == null || list.isEmpty()) return Collections.emptySet();
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
for (String s : list) result.add(s.toLowerCase(Locale.ROOT).trim());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isIgnored(String fullPath, Set<String> ignored) {
|
||||
String lower = fullPath.toLowerCase(Locale.ROOT);
|
||||
for (String prefix : ignored) {
|
||||
if (lower.equals(prefix) || lower.startsWith(prefix + ".")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int countNew(List<String> before, List<String> after) {
|
||||
return Math.max(0, after.size() - before.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package dev.loki.loparkour.generator;
|
||||
|
||||
/**
|
||||
* @deprecated Logic merged into {@link BlockPlacer}. Delete this file.
|
||||
*/
|
||||
@Deprecated
|
||||
class BlockSelector {}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package dev.loki.loparkour.generator;
|
||||
|
||||
/**
|
||||
* @deprecated Tick logic was merged back into {@link ParkourGenerator#tick()}.
|
||||
* Delete this file.
|
||||
*/
|
||||
@Deprecated
|
||||
class GeneratorTick {}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.loki.loparkour.generator;
|
||||
package dev.loki.loparkour.generator;
|
||||
|
||||
import dev.loki.loparkour.LoParkour;
|
||||
import dev.loki.loparkour.config.Config;
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package dev.loki.loparkour.generator;
|
||||
|
||||
/**
|
||||
* @deprecated Score logic lives in {@link GeneratorLifecycle}.
|
||||
* This class was created but never wired in. Delete this file.
|
||||
*/
|
||||
@Deprecated
|
||||
class ScoreManager {}
|
||||
|
|
@ -54,12 +54,11 @@ public class EffectManager {
|
|||
);
|
||||
viewers.forEach(p -> p.player.spawnParticle(
|
||||
Option.PARTICLE_TYPE, center, 5, 0.1, 0.1, 0.1, 0,
|
||||
Option.PARTICLE_DATA != null ? Option.PARTICLE_DATA.data() : null
|
||||
Option.PARTICLE_DATA
|
||||
));
|
||||
}
|
||||
case CIRCLE -> ParticleUtil.circle(
|
||||
min.clone().add(0.5, 0.5, 0.5), max.getWorld(), Option.PARTICLE_TYPE,
|
||||
viewers.stream().map(p -> p.player).toList(), 0.5, 8
|
||||
min.clone().add(0.5, 0.5, 0.5), Option.PARTICLE_TYPE, 8, 1
|
||||
);
|
||||
case BOX -> ParticleUtil.box(
|
||||
org.bukkit.util.BoundingBox.of(min, max), max.getWorld(), Option.PARTICLE_TYPE,
|
||||
|
|
@ -106,7 +106,7 @@ public class PAPIHook extends PlaceholderExpansion {
|
|||
ParkourGenerator generator = pp.session.generator;
|
||||
switch (params) {
|
||||
case "score", "current_score" -> {
|
||||
return Integer.toString(generator.score);
|
||||
return Integer.toString(generator.state.score);
|
||||
}
|
||||
case "time", "current_time" -> {
|
||||
return generator.getFormattedTime();
|
||||
|
|
@ -134,7 +134,7 @@ public class PAPIHook extends PlaceholderExpansion {
|
|||
String replaced = params.replace("score_until_", "");
|
||||
int interval = Integer.parseInt(replaced);
|
||||
if (interval > 0) {
|
||||
return Integer.toString(interval - (generator.totalScore % interval)); // 100 - (5 % 100) = 95
|
||||
return Integer.toString(interval - (generator.state.score % interval));
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class ParkourSettingsMenu extends LPMenu {
|
|||
open(pp);
|
||||
})
|
||||
.setItem(12, toggleItem(locale, "special_blocks", pp.useSpecialBlocks), e -> {
|
||||
if (pp.session.generator.score == 0) {
|
||||
if (pp.session.generator.state.score == 0) {
|
||||
pp.useSpecialBlocks = !pp.useSpecialBlocks;
|
||||
pp.updateGeneratorSettings(pp.session.generator);
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ public class ParkourSettingsMenu extends LPMenu {
|
|||
.setItem(19, localeItem(player, "settings.parkour_settings.styles.item"),
|
||||
e -> openStyleMenu(pp))
|
||||
.setItem(20, schemDiffItem(pp), e -> {
|
||||
if (pp.session.generator.score == 0) {
|
||||
if (pp.session.generator.state.score == 0) {
|
||||
List<Double> diffs = List.of(0.0, 0.25, 0.5, 0.75, 1.0);
|
||||
int idx = diffs.indexOf(pp.schematicDifficulty);
|
||||
pp.schematicDifficulty = diffs.get((idx + 1) % diffs.size());
|
||||
|
|
|
|||
20
src/main/java/dev/loki/loparkour/mode/ElytraGenerator.java
Normal file
20
src/main/java/dev/loki/loparkour/mode/ElytraGenerator.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package dev.loki.loparkour.mode;
|
||||
|
||||
import dev.loki.loparkour.generator.ParkourGenerator;
|
||||
import dev.loki.loparkour.session.Session;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Generator for Elytra mode parkour
|
||||
*/
|
||||
public class ElytraGenerator extends ParkourGenerator {
|
||||
|
||||
public ElytraGenerator(@NotNull Session session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mode getMode() {
|
||||
return Modes.ELYTRA;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ import java.util.function.BiConsumer;
|
|||
*/
|
||||
public class ParkourPlayer extends ParkourUser {
|
||||
|
||||
public static final Map<String, OptionContainer> PLAYER_COLUMNS = PlayerSettingsManager.getColumnMappings();
|
||||
public static final Map<String, PlayerSettingsManager.OptionContainer> PLAYER_COLUMNS = PlayerSettingsManager.getColumnMappings();
|
||||
|
||||
public @Expose Double schematicDifficulty;
|
||||
public @Expose Integer blockLead;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import java.util.function.BiConsumer;
|
|||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
class PlayerSettingsManager {
|
||||
public class PlayerSettingsManager {
|
||||
|
||||
private static final Map<String, OptionContainer> COLUMN_MAPPINGS = new HashMap<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ public class UserRegistry {
|
|||
}
|
||||
|
||||
public static int getJoinCount() {
|
||||
return joinCount;
|
||||
int count = joinCount;
|
||||
joinCount = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void resetScoreboard(ParkourUser user) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue