refactor: remove legacy .lpschem format (TODO 117)

- Delete schematic/legacy/lpschem/ (LPSchematicLegacy + 5 pojo Gson models)
- Delete schematic/convert/LpschemConverter and /lp schematic convert command
- Remove SchematicClipboardBuilder.fromLegacy, convert branches in dispatcher/
  tab-completer/help, locales (ru/en), SCHEMATICS.md and yml comments
- Kept only .schem (WorldEdit) + .nbt
- Build green: checkstyle 0 errors, PMD 33
This commit is contained in:
loki5512344 2026-08-05 18:58:18 +02:00
parent de47bce56e
commit 9f70e2e586
Signed by: boba
GPG key ID: 253067914055423B
20 changed files with 5 additions and 361 deletions

View file

@ -197,7 +197,7 @@
### Наследие форка (LoParkour = форк «Walk in the Park» / Infinite Parkour Efnilite)
- **117. Удалить формат `.lpschem` полностью, оставить только `.schem` (WorldEdit) + `.nbt`.** Выпилить: пакет `schematic/legacy/lpschem/`, `LpschemConverter`, конвертер-команду, `LPSchematicLegacy`, примеры `*.lpschem` из `resources/schematics/`, все ветки `.lpschem` в `SchematicManager`/`SchematicCreator`. Формат устарел, это двойная поддержка (аудита: п.31, 53-56, 94-97) — один формат схем, один путь пасты/валидации.
- ~~**117. Удалить формат `.lpschem` полностью...**~~ ✅ **ВЫПОЛНЕНО** — пакет `schematic/legacy/lpschem/` (LPSchematicLegacy + pojo/), `LpschemConverter`, конвертер-команда (`/lp schematic convert`), `fromLegacy`, ветки convert в dispatcher/tab/messenger/локалях и упоминания в док. Остались только `.schem` (WorldEdit) + `.nbt`.
- **118. Переименовать PAPI-id `witp` → `loparkour`** (наследие оригинала; также поменять примеры в scoreboard/docs).
- **119. Command block support** — у IP и IP-Reborn есть (запуск команд `/parkour` из командного блока), у нас нет. → Поддержать `CommandBlock` sender в `LoParkourCommand`.
- **120. Расширить PAPI-плейсхолдеры до 15+** (у IP 15+): score, record, время, режим, ранк, стиль, скорость, статистика адаптива и т.д.

View file

@ -165,7 +165,7 @@ dev.loki.loparkour
ScoreboardManager).
- **`menu/core/`** — корень (LPMenu, Menus, MenuStub), `screen/` (MainMenu, DynamicMenu, ParkourOption);
`util/gui/` переехали в **`menu/gui/`** (GUI migration-стабы).
- **`schematic/legacy/lpschem/`** — `LPSchematicLegacy` + `pojo/` (5 Gson-моделей).
- **`schematic/legacy/lpschem/`** — ~~`LPSchematicLegacy` + `pojo/`~~ **удалено** (п.117 TODO): формат `.lpschem` выпилен, остались `.schem` (WorldEdit) + `.nbt`.
Пересекающие пакеты классы стали `public` (сигнатуры методов не менялись, только видимость). Оставшиеся
папки с 4–6 файлами — цельные по смыслу (jump-расчёты, block-placement, player-core, pojo и т.п.) — не дробятся.

View file

@ -17,7 +17,7 @@ class CommandTabCompleter {
private static final List<String> SCHEMATIC_DIFFICULTIES = List.of("0.0", "0.25", "0.5", "0.75", "1.0");
private static final List<String> SCHEMATIC_SUBCOMMANDS = List.of(
"wand", "pos1", "pos2", "create", "convert", "paste", "list", "reload"
"wand", "pos1", "pos2", "create", "paste", "list", "reload"
);
private CommandTabCompleter() {

View file

@ -14,7 +14,7 @@ import java.util.HashMap;
import java.util.Map;
/**
* Admin schematic tools: wand, create, convert, list, paste, reload.
* Admin schematic tools: wand, create, list, paste, reload.
*/
public class SchematicCommandHandler {

View file

@ -38,7 +38,6 @@ final class SchematicDispatcher {
LoParkour.getSchematicManager().reload();
SchematicMessenger.send(player, LoParkour.PREFIX + Locales.getString(player, "schematic.reloaded"));
}
case "convert" -> SchematicHandler.convert(sender, player, cooldowns);
default -> SchematicMessenger.sendHelp(player);
}
}

View file

@ -1,9 +1,7 @@
package dev.loki.loparkour.command.schematic;
import dev.loki.loparkour.LoParkour;
import dev.loki.loparkour.command.player.PlayerCommandHandler;
import dev.loki.loparkour.config.locale.Locales;
import dev.loki.loparkour.schematic.convert.LpschemConverter;
import dev.loki.loparkour.schematic.core.ParkourSchematic;
import dev.loki.loparkour.schematic.create.SchematicCreator;
import dev.loki.loparkour.util.particle.ParticleUtil;
@ -73,27 +71,6 @@ final class SchematicHandler {
.formatted(name));
}
static void convert(CommandSender sender, Player player, PlayerCommandHandler cooldowns) {
if (!cooldowns.cooldown(sender, "schematic-convert", 5000)) {
return;
}
LpschemConverter.ConvertResult result = LpschemConverter.convertAll();
if (result.converted() > 0) {
LoParkour.getSchematicManager().reload();
}
SchematicMessenger.send(player, LoParkour.PREFIX + Locales.getString(player, "schematic.convert_done")
.formatted(result.converted(), result.failed()));
for (String line : result.messages()) {
if (result.messages().size() <= 12) {
SchematicMessenger.send(player, "&#A0A0A0 " + line);
}
}
if (result.messages().size() > 12) {
SchematicMessenger.send(player, Locales.getString(player, "schematic.convert_truncated")
.formatted(result.messages().size() - 12));
}
}
static void posCommand(Player player, Location[] existing, Location loc, int index) {
SchematicCommandHandler.setPos(player, loc, index);
String label = index == 0 ? "1" : "2";

View file

@ -24,7 +24,6 @@ final class SchematicMessenger {
send(player, Locales.getString(player, "schematic.help.line_pos2"));
send(player, Locales.getString(player, "schematic.help.line_create"));
send(player, Locales.getString(player, "schematic.help.line_create_named"));
send(player, Locales.getString(player, "schematic.help.line_convert"));
send(player, Locales.getString(player, "schematic.help.line_paste"));
send(player, Locales.getString(player, "schematic.help.line_list"));
send(player, Locales.getString(player, "schematic.help.line_reload"));

View file

@ -1,115 +0,0 @@
package dev.loki.loparkour.schematic.convert;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import dev.loki.loparkour.LoParkour;
import dev.loki.loparkour.schematic.config.SchematicDifficultyConfig;
import dev.loki.loparkour.schematic.legacy.lpschem.LPSchematicLegacy;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicMetadata;
import dev.loki.loparkour.schematic.schem.SchematicClipboardBuilder;
import dev.loki.loparkour.schematic.schem.SchemWriter;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Converts legacy {@code .lpschem} files to {@code .schem} in the schematics folder.
*/
public final class LpschemConverter {
private LpschemConverter() {
}
public record ConvertResult(int converted, int failed, @NotNull List<String> messages) {
}
@NotNull
public static ConvertResult convertAll() {
List<File> sources = new ArrayList<>();
collect(LoParkour.getInFolder("schematics"), sources);
collect(LoParkour.getInFolder("schematics-new"), sources);
File outDir = LoParkour.getInFolder("schematics");
if (!outDir.exists()) {
outDir.mkdirs();
}
int ok = 0;
int failed = 0;
List<String> messages = new ArrayList<>();
for (File file : sources) {
try {
String msg = convertFile(file, outDir);
messages.add(msg);
ok++;
} catch (IOException e) {
failed++;
messages.add(file.getName() + ": " + e.getMessage());
LoParkour.getPlugin().getLogger().warning("lpschem convert failed: " + file.getName() + " — " + e.getMessage());
}
}
if (sources.isEmpty()) {
messages.add("No .lpschem files found in schematics/ or schematics-new/");
}
return new ConvertResult(ok, failed, messages);
}
@NotNull
private static String convertFile(@NotNull File lpschemFile, @NotNull File outDir) throws IOException {
LPSchematicLegacy legacy = LPSchematicLegacy.load(lpschemFile);
SchematicMetadata meta = legacy.getMetadata();
String stem = resolveStem(meta, lpschemFile);
File outFile = new File(outDir, stem + ".schem");
double difficulty = meta.getDifficulty() > 0 ? meta.getDifficulty() : 0.5;
boolean wroteFile = false;
if (!outFile.exists()) {
Clipboard clipboard = SchematicClipboardBuilder.fromLegacy(legacy);
SchemWriter.save(clipboard, outFile);
wroteFile = true;
}
SchematicDifficultyConfig.setDifficulty(stem, difficulty);
return wroteFile
? stem + ".schem (" + difficulty + ")"
: stem + " (yaml updated, .schem exists)";
}
@NotNull
private static String resolveStem(@NotNull SchematicMetadata meta, @NotNull File file) {
String name = meta.getName();
if (name != null && !name.isBlank() && name.matches("^[a-zA-Z0-9_-]+$")) {
return name.toLowerCase(Locale.ROOT).startsWith("parkour-")
? name.toLowerCase(Locale.ROOT)
: "parkour-" + name.toLowerCase(Locale.ROOT);
}
String base = file.getName();
if (base.toLowerCase(Locale.ROOT).endsWith(".lpschem")) {
base = base.substring(0, base.length() - 7);
}
if (!base.toLowerCase(Locale.ROOT).startsWith("parkour-")) {
base = "parkour-" + base;
}
return base.toLowerCase(Locale.ROOT);
}
private static void collect(@NotNull File dir, @NotNull List<File> out) {
if (!dir.isDirectory()) {
return;
}
File[] files = dir.listFiles((d, name) -> name.toLowerCase(Locale.ROOT).endsWith(".lpschem"));
if (files != null) {
for (File f : files) {
out.add(f);
}
}
}
}

View file

@ -1,94 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicDimensions;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicLogic;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicMarkers;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicMetadata;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicVisuals;
import java.util.zip.GZIPInputStream;
/**
* Read-only loader for deprecated gzip+JSON {@code .lpschem} files (format v2).
*/
public final class LPSchematicLegacy {
private static final Gson GSON = new GsonBuilder().create();
private static final int FORMAT_VERSION = 2;
private final SchematicMetadata metadata;
private final SchematicDimensions dimensions;
private final List<String> palette;
private final int[] blocks;
private LPSchematicLegacy(
SchematicMetadata metadata,
SchematicDimensions dimensions,
List<String> palette,
int[] blocks
) {
this.metadata = metadata;
this.dimensions = dimensions;
this.palette = palette;
this.blocks = blocks;
}
@NotNull
public static LPSchematicLegacy load(@NotNull File file) throws IOException {
StringBuilder json = new StringBuilder();
try (GZIPInputStream gzip = new GZIPInputStream(new FileInputStream(file));
BufferedReader reader = new BufferedReader(new InputStreamReader(gzip, java.nio.charset.StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
json.append(line);
}
}
SchematicData data = GSON.fromJson(json.toString(), SchematicData.class);
if (data == null || data.formatVersion != FORMAT_VERSION) {
throw new IOException("Unsupported .lpschem format in " + file.getName());
}
return new LPSchematicLegacy(data.metadata, data.dimensions, data.palette, data.blocks);
}
@NotNull
public SchematicMetadata getMetadata() {
return metadata;
}
@NotNull
public SchematicDimensions getDimensions() {
return dimensions;
}
@NotNull
public List<String> getPalette() {
return palette;
}
@NotNull
public int[] getBlockArray() {
return blocks;
}
private static class SchematicData {
int formatVersion;
SchematicMetadata metadata;
SchematicDimensions dimensions;
List<String> palette;
int[] blocks;
SchematicMarkers markers;
SchematicVisuals visuals;
SchematicLogic logic;
}
}

View file

@ -1,9 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem.pojo;
/** Gson POJO for legacy {@code .lpschem}. */
public class SchematicDimensions {
public int width;
public int height;
public int length;
}

View file

@ -1,5 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem.pojo;
/** Gson POJO for legacy {@code .lpschem} (optional section). */
public class SchematicLogic {
}

View file

@ -1,17 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem.pojo;
import java.util.List;
/** Gson POJO for legacy {@code .lpschem}. */
public class SchematicMarkers {
public Vector3i start;
public Vector3i end;
public List<Vector3i> checkpoints;
public static class Vector3i {
public int x;
public int y;
public int z;
}
}

View file

@ -1,21 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem.pojo;
/** Gson POJO for legacy {@code .lpschem}. */
public class SchematicMetadata {
private String name;
private String author;
private double difficulty;
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
public double getDifficulty() {
return difficulty;
}
}

View file

@ -1,5 +0,0 @@
package dev.loki.loparkour.schematic.legacy.lpschem.pojo;
/** Gson POJO for legacy {@code .lpschem} (optional section). */
public class SchematicVisuals {
}

View file

@ -6,16 +6,12 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.world.block.BlockState;
import dev.loki.loparkour.schematic.legacy.lpschem.LPSchematicLegacy;
import dev.loki.loparkour.schematic.legacy.lpschem.pojo.SchematicDimensions;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public final class SchematicClipboardBuilder {
private SchematicClipboardBuilder() {
@ -60,45 +56,4 @@ public final class SchematicClipboardBuilder {
}
return clipboard;
}
@NotNull
public static Clipboard fromLegacy(@NotNull LPSchematicLegacy legacy) {
SchematicDimensions dim = legacy.getDimensions();
BlockVector3 min = BlockVector3.at(0, 0, 0);
BlockVector3 max = BlockVector3.at(dim.width - 1, dim.height - 1, dim.length - 1);
CuboidRegion region = new CuboidRegion(min, max);
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
List<String> palette = legacy.getPalette();
int[] blocks = legacy.getBlockArray();
int width = dim.width;
int length = dim.length;
for (int y = 0; y < dim.height; y++) {
for (int z = 0; z < length; z++) {
for (int x = 0; x < width; x++) {
int index = x + (z * width) + (y * width * length);
int paletteIndex = blocks[index];
if (paletteIndex <= 0 || paletteIndex >= palette.size()) {
continue;
}
String blockData = palette.get(paletteIndex);
if (blockData == null || blockData.isBlank()) {
continue;
}
try {
BlockData data = org.bukkit.Bukkit.createBlockData(blockData);
BlockState state = BukkitAdapter.adapt(data);
if (state.getBlockType().getMaterial().isAir()) {
continue;
}
clipboard.setBlock(BlockVector3.at(x, y, z), state);
} catch (IllegalArgumentException ignored) {
// Skip invalid states
}
}
}
}
return clipboard;
}
}

View file

@ -8,7 +8,6 @@ Jump structures are loaded from `plugins/LoParkour/schematics/` and matched to a
|-----------|--------|--------|
| `.nbt` | Vanilla structure block, `/structure save` | Pasted with Bukkit `StructureManager` |
| `.schem`, `.schematic` | WorldEdit (`//copy`, `//schem save`) | Read via embedded WorldEdit clipboard IO |
| `.lpschem` | **Legacy LoParkour format** | Not used in-game; convert with `/lp schematic convert` |
During parkour generation, when the profile rolls **schematic** type, the plugin picks a random file whose `difficulty` in YAML is **≤** the player's schematic setting (0.0–1.0).
@ -37,16 +36,6 @@ Difficulty examples: `0.25`, `0.5`, `0.75`, `1.0` (must be between 0 and 1).
4. `/lp schematic reload` — reload files without restart
## Converting old `.lpschem` files
Place `.lpschem` files in `plugins/LoParkour/schematics/` or legacy `schematics-new/`, then:
```
/lp schematic convert
```
Each file becomes `parkour-<id>.schem` in `schematics/` and gets a `difficulty:` entry (from the old metadata, or `0.5` if missing). Original `.lpschem` files are kept; delete them manually after checking.
## Admin commands
| Command | Description |
@ -55,7 +44,6 @@ Each file becomes `parkour-<id>.schem` in `schematics/` and gets a `difficulty:`
| `/lp schematic pos1` / `pos2` | Set corners at your feet |
| `/lp create <difficulty>` | Save selection → `.schem` + YAML |
| `/lp schematic create <name> <difficulty>` | Save with custom name |
| `/lp schematic convert` | Convert all `.lpschem` in schematics folders |
| `/lp schematic list` | List loaded structures |
| `/lp schematic paste <name>` | Test-paste at your location |
| `/lp schematic reload` | Reload disk files |
@ -76,4 +64,3 @@ flowchart LR
- `plugins/LoParkour/schematics/` — `.nbt`, `.schem`, `.schematic` (active)
- `plugins/LoParkour/schematics/schematics.yml` — difficulty map
- `plugins/LoParkour/schematics-new/` — legacy; scanned only for `.lpschem` convert

View file

@ -63,7 +63,7 @@ generation:
advanced:
# -= Spawn platform =-
# Start area is a flat square platform at the session cell (no .lpschem island).
# Start area is a flat square platform at the session cell (no structure island).
island:
# Legacy key (unused for spawn). Course schematics use schematics.yml / schematics-new.

View file

@ -354,7 +354,6 @@ schematic:
line_pos2: "&#FF4444/LoParkour schematic pos2 &#404040- &#A0A0A0Set position 2"
line_create: "&#FF4444/LoParkour create <difficulty> &#404040- &#A0A0A0Save selection to .schem + yml"
line_create_named: "&#FF4444/LoParkour schematic create <name> <difficulty> &#404040- &#A0A0A0Named save"
line_convert: "&#FF4444/LoParkour schematic convert &#404040- &#A0A0A0Convert .lpschem to .schem"
line_paste: "&#FF4444/LoParkour schematic paste <name> &#404040- &#A0A0A0Test-paste"
line_list: "&#FF4444/LoParkour schematic list &#404040- &#A0A0A0List loaded"
line_reload: "&#FF4444/LoParkour schematic reload &#404040- &#A0A0A0Reload from disk"
@ -370,8 +369,6 @@ schematic:
selection_incomplete: "&#FF4444Selection incomplete. Use wand or pos1/pos2 first."
created: "&#A0A0A0Added &#FF4444%s &#404040(diff %s) as &#FF4444%s"
create_failed: "&#FF4444Create failed: %s"
convert_done: "&#A0A0A0Converted %s file(s), %s failed."
convert_truncated: "&#A0A0A0...and %s more (see console)."
not_found: "&#FF4444Schematic '%s' not found."
pasted: "&#A0A0A0Pasted &#FF4444%s&#A0A0A0."

View file

@ -353,7 +353,6 @@ schematic:
line_pos2: "&#FF4444/LoParkour schematic pos2 &#404040- &#A0A0A0Позиция 2"
line_create: "&#FF4444/LoParkour create <сложность> &#404040- &#A0A0A0Сохранить в .schem + yml"
line_create_named: "&#FF4444/LoParkour schematic create <имя> <сложность> &#404040- &#A0A0A0С именем"
line_convert: "&#FF4444/LoParkour schematic convert &#404040- &#A0A0A0.lpschem → .schem"
line_paste: "&#FF4444/LoParkour schematic paste <имя> &#404040- &#A0A0A0Тестовая вставка"
line_list: "&#FF4444/LoParkour schematic list &#404040- &#A0A0A0Список"
line_reload: "&#FF4444/LoParkour schematic reload &#404040- &#A0A0A0Перезагрузка"
@ -369,8 +368,6 @@ schematic:
selection_incomplete: "&#FF4444Сначала выделите область (палочка или pos1/pos2)."
created: "&#A0A0A0Добавлено &#FF4444%s &#404040(сложн. %s) → &#FF4444%s"
create_failed: "&#FF4444Ошибка: %s"
convert_done: "&#A0A0A0Конвертировано: %s, ошибок: %s."
convert_truncated: "&#A0A0A0...ещё %s (см. консоль)."
not_found: "&#FF4444Схема '%s' не найдена."
pasted: "&#A0A0A0Вставлено: &#FF4444%s&#A0A0A0."

View file

@ -2,7 +2,6 @@
# plugins/LoParkour/schematics/
# .nbt — structure block export
# .schem / .schematic — WorldEdit or /lp create
# .lpschem — legacy; run /lp schematic convert
# In-game: /lp schematic wand → select → /lp create <difficulty>
# Docs: plugins/LoParkour/docs/SCHEMATICS.md