From 06c9ec49760c2765681f2ad5959683b6798d86c2 Mon Sep 17 00:00:00 2001 From: John Etherfield Date: Mon, 5 Oct 2026 19:41:01 +0200 Subject: [PATCH] feat: add full server rules to /rules book --- org/examp/john/rules/RulesCommand.class | Bin 0 -> 11529 bytes .../mixin/client/PlayerTabOverlayMixin.java | 49 +++++++ .../resources/localchat.client.mixins.json | 1 + src/main/java/org/examp/john/Localchat.java | 2 + .../org/examp/john/rules/RulesCommand.java | 129 +++++++++++++++--- .../java/org/examp/john/rules/RulesData.java | 79 ++++++++++- .../org/examp/john/rules/RulesLoader.java | 6 +- 7 files changed, 240 insertions(+), 26 deletions(-) create mode 100644 org/examp/john/rules/RulesCommand.class create mode 100644 src/client/java/org/examp/john/mixin/client/PlayerTabOverlayMixin.java diff --git a/org/examp/john/rules/RulesCommand.class b/org/examp/john/rules/RulesCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..ea12c2717696a785f5b5a14a45490db85c371d5b GIT binary patch literal 11529 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 cir) { + if (playerInfo.getGameMode() == GameType.SPECTATOR) { + Component original = cir.getReturnValue(); + cir.setReturnValue(original.copy().withStyle(original.getStyle().withItalic(false))); + } + } + + /** + * Перехоплюємо виклик text(Font, Component, x, y, color) в extractRenderState. + * Якщо колір = -1862270977 (сірий для spectator), замінюємо на -1 (білий). + */ + @Redirect( + method = "extractRenderState", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/GuiGraphicsExtractor;text(Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Component;III)V" + ) + ) + private void forceWhiteText(GuiGraphicsExtractor instance, Font font, Component text, int x, int y, int color) { + if (color == -1862270977) { + color = -1; // білий + } + instance.text(font, text, x, y, color); + } +} \ No newline at end of file diff --git a/src/client/resources/localchat.client.mixins.json b/src/client/resources/localchat.client.mixins.json index b19dc56..f4006f2 100644 --- a/src/client/resources/localchat.client.mixins.json +++ b/src/client/resources/localchat.client.mixins.json @@ -4,6 +4,7 @@ "package": "org.examp.john.mixin.client", "compatibilityLevel": "JAVA_25", "client": [ + "PlayerTabOverlayMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/java/org/examp/john/Localchat.java b/src/main/java/org/examp/john/Localchat.java index b37a737..37321c4 100644 --- a/src/main/java/org/examp/john/Localchat.java +++ b/src/main/java/org/examp/john/Localchat.java @@ -1,6 +1,7 @@ package org.examp.john; import net.fabricmc.api.ModInitializer; +import org.examp.john.rules.RulesCommand; import org.examp.john.stealth.StealthCommand; import org.examp.john.stealth.StealthStorage; import org.slf4j.Logger; @@ -15,5 +16,6 @@ public class Localchat implements ModInitializer { StealthStorage.load(); ChatHandler.register(); StealthCommand.register(); + RulesCommand.register(); } } diff --git a/src/main/java/org/examp/john/rules/RulesCommand.java b/src/main/java/org/examp/john/rules/RulesCommand.java index e5ffa14..a0e2f1e 100644 --- a/src/main/java/org/examp/john/rules/RulesCommand.java +++ b/src/main/java/org/examp/john/rules/RulesCommand.java @@ -22,11 +22,9 @@ import java.util.Map; import java.util.UUID; /** - * Реєструє команду /rules. Повністю server-side: не потребує жодного мода - * на клієнті гравця. Книга створюється програмно через Data Components API - * і відкривається через openItemGui (ClientboundOpenBookPacket). - * Книга НЕ залишається в інвентарі — тимчасово ставиться в руку, - * відкривається, одразу повертається оригінальний предмет. + * Команда /rules. Повністю server-side: клієнт не потребує мода. + * Книга створюється через Data Components API (WrittenBookContent). + * Відкривається через openItemGui — книга НЕ залишається в інвентарі. */ public class RulesCommand { @@ -87,12 +85,23 @@ public class RulesCommand { player.setItemInHand(hand, originalItem); } + /** + * Створює книгу правил: + * 1. Сторінка 0 — Легенда + Зміст + * 2. Кожен розділ починається на новій сторінці + * 3. Пагінація по ~280 символів/сторінку (реальний ліміт книги) + */ private static ItemStack createRuleBook(RulesData.RulesRoot rules) { ItemStack book = new ItemStack(Items.WRITTEN_BOOK); List> pages = new ArrayList<>(); + + // Сторінка 0: Легенда + Зміст + pages.add(Filterable.passThrough(buildLegendAndContentsPage(rules))); + + // Сторінки з правилами (кожен розділ починається на новій сторінці) for (RulesData.Section section : rules.sections) { - pages.add(Filterable.passThrough(buildPageComponent(section))); + pages.addAll(buildSectionPages(section)); } WrittenBookContent content = new WrittenBookContent( @@ -107,21 +116,101 @@ public class RulesCommand { return book; } - private static Component buildPageComponent(RulesData.Section section) { - StringBuilder pageText = new StringBuilder(); - pageText.append("§l§n").append(section.title).append("§r\n\n"); + /** Сторінка 0: Легенда + Зміст */ + private static Component buildLegendAndContentsPage(RulesData.RulesRoot rules) { + StringBuilder sb = new StringBuilder(); + sb.append("§l§nПравила серверу§r\n\n"); + sb.append("§l§nЛегенда важливості§r\n"); + sb.append("§c⚠§r — Тяжке порушення (бан 5+ днів або перманент)\n"); + sb.append("§e⚠§r — Середнє порушення (бан 1–5 днів)\n"); + sb.append("§a⚠§r — Легке порушення (попередження/мут)\n\n"); + sb.append("§l§nЗміст§r\n\n"); - if (section.entries != null) { - for (RulesData.Entry entry : section.entries) { - pageText.append(entry.severity.colorCode) - .append("[!] §r") - .append(entry.number) - .append(". ") - .append(entry.text) - .append("\n\n"); - } + int page = 1; + for (RulesData.Section section : rules.sections) { + sb.append(" §e► §r§f").append(section.title).append(" §7(стор. ").append(page).append(")§r\n"); + page += estimatePagesForSection(section); } - return Component.literal(pageText.toString()); + sb.append("\n§7Стрілки в книзі — навігація.§r"); + return Component.literal(sb.toString()); } -} + + /** Оцінює скільки сторінок займе розділ (~180 видимих символів/сторінку — фізичний ліміт книги) */ + private static int estimatePagesForSection(RulesData.Section section) { + if (section.entries == null || section.entries.isEmpty()) return 1; + + final int CHARS_PER_PAGE = 180; + int totalVisible = section.title.length() + 30; + + for (RulesData.Entry entry : section.entries) { + totalVisible += visibleLength(entry.number) + visibleLength(entry.text) + 30; + } + + return Math.max(1, (totalVisible + CHARS_PER_PAGE - 1) / CHARS_PER_PAGE); + } + + /** Повертає довжину рядка БЕЗ §-кодів (тільки видимі символи) */ + private static int visibleLength(String s) { + if (s == null) return 0; + int len = 0; + boolean inCode = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '§') { + inCode = true; + continue; + } + if (inCode) { + inCode = false; + continue; + } + len++; + } + return len; + } + + /** Будує сторінки для одного розділу з пагінацією */ + private static List> buildSectionPages(RulesData.Section section) { + List> pages = new ArrayList<>(); + + if (section.entries == null || section.entries.isEmpty()) { + pages.add(Filterable.passThrough(Component.literal("§l§n" + section.title + "§r\n\n§7(розділ порожній)§r"))); + return pages; + } + + final int CHARS_PER_PAGE = 180; + StringBuilder currentPage = new StringBuilder(); + int currentVisible = 0; + boolean firstPage = true; + + for (RulesData.Entry entry : section.entries) { + String symbol = entry.severity != null ? entry.severity.colorCode + "⚠§r" : "§a⚠§r"; + String entryText = symbol + " " + entry.number + ". " + entry.text + "\n\n"; + int entryVisible = visibleLength(entryText); + + if (firstPage) { + String header = "§l§n" + section.title + "§r\n\n"; + currentPage.append(header); + currentVisible = visibleLength(header); + firstPage = false; + } + + // Якщо правило не вміщується на поточній сторінці — переносимо ЦІЛЕ правило на нову сторінку + if (currentVisible + entryVisible > CHARS_PER_PAGE && currentVisible > 0) { + pages.add(Filterable.passThrough(Component.literal(currentPage.toString()))); + currentPage = new StringBuilder(); + currentVisible = 0; + } + + currentPage.append(entryText); + currentVisible += entryVisible; + } + + if (currentVisible > 0) { + pages.add(Filterable.passThrough(Component.literal(currentPage.toString()))); + } + + return pages; + } +} \ No newline at end of file diff --git a/src/main/java/org/examp/john/rules/RulesData.java b/src/main/java/org/examp/john/rules/RulesData.java index ec72582..f1f30fc 100644 --- a/src/main/java/org/examp/john/rules/RulesData.java +++ b/src/main/java/org/examp/john/rules/RulesData.java @@ -1,5 +1,6 @@ package org.examp.john.rules; +import java.util.ArrayList; import java.util.List; /** @@ -49,8 +50,8 @@ public class RulesData { /** Один пункт правила всередині розділу. */ public static class Entry { - public String number; // напр. "1.1" або "IV" - public String text; // текст правила / опис порушення + public String number; + public String text; public Severity severity; public Entry(String number, String text, Severity severity) { @@ -79,4 +80,78 @@ public class RulesData { this.sections = sections; } } + + /** Повний набір правил за замовчуванням для створення конфігу при першому запуску. */ + public static RulesRoot createDefaultRules() { + List
sections = new ArrayList<>(); + + // 1. ЧЕСНА ГРА + sections.add(new Section("1. ЧЕСНА ГРА", List.of( + new Entry("1.1", "Чити та заборонені модифікації. Заборонено використовувати чити, модифікації, ресурспаки або інші засоби, які дають перевагу.", Severity.HEAVY), + new Entry("1.2", "Використання багів. Заборонено навмисно використовувати помилки та баги сервера для отримання переваги.", Severity.HEAVY), + new Entry("1.3", "Дюпи піску та подібні механіки дозволені. Заборонено дюпи, що ламають економіку або стабільність сервера.", Severity.HEAVY), + new Entry("1.4", "Обман адміністрації. Заборонено навмисно вводити адміністрацію в оману щодо обставин порушення.", Severity.HEAVY), + new Entry("1.5", "Порушення стабільності сервера. Заборонено створювати навантаження, що спричиняють нестабільність.", Severity.HEAVY), + new Entry("1.6", "Реальні гроші. Заборонено купувати/продавати ігрові ресурси за реальні гроші.", Severity.HEAVY), + new Entry("1.7", "Кількість акаунтів. Один гравець — не більше 5 акаунтів.", Severity.HEAVY), + new Entry("1.8", "Продаж або передача акаунта. Заборонено.", Severity.HEAVY), + new Entry("1.9", "Злам акаунтів. Заборонено намагатися отримати доступ до чужих акаунтів.", Severity.HEAVY), + new Entry("1.10", "Підбурювання до порушень. Заборонено закликати інших гравців порушувати правила.", Severity.MEDIUM), + new Entry("1.11", "Шкідлива дезінформація. Заборонено поширювати неправду, що шкодить гравцям/проєкту.", Severity.MEDIUM), + new Entry("1.12", "Персональні дані. Заборонено використовувати/поширювати дані інших без згоди.", Severity.HEAVY), + new Entry("1.13", "Обхід покарання. Заборонено обходити бан/мут через інші акаунти.", Severity.HEAVY), + new Entry("1.14", "Шкідливі дії проти адміністрації. Заборонено навмисно шкодити адмінам/персоналу.", Severity.HEAVY), + new Entry("1.15", "Атаки на сервер та лаунчер. Заборонено втручатися в роботу інфраструктури.", Severity.HEAVY) + ))); + + // 2. СПІЛКУВАННЯ + sections.add(new Section("2. СПІЛКУВАННЯ", List.of( + new Entry("2.1", "Мова спілкування. Основні: українська, англійська. Російська заборонена в публічному чаті.", Severity.MEDIUM), + new Entry("2.2", "Образи, приниження, провокації. Заборонено ображати гравців, їхніх родичів або адміністрацію. Порнографія заборонена.", Severity.MEDIUM), + new Entry("2.3", "Ненормативна лексика. Заборонено надмірне використання.", Severity.LIGHT), + new Entry("2.4", "Caps Lock. Заборонено надмірне використання, якщо заважає спілкуванню.", Severity.LIGHT), + new Entry("2.5", "Реклама. Заборонено рекламувати сторонні проєкти без дозволу адміністрації (у тому числі в ПМ).", Severity.HEAVY), + new Entry("2.6", "Провокації та конфлікти. Заборонено навмисно розпалювати конфлікти.", Severity.HEAVY), + new Entry("2.7", "Публічні звинувачення адміністрації. Заборонено безпідставно дискредитувати модерацію. Критику — через офіційні канали.", Severity.LIGHT) + ))); + + // 3. ТЕРИТОРІЇ ТА БУДІВНИЦТВО + sections.add(new Section("3. ТЕРИТОРІЇ ТА БУДІВНИЦТВО", List.of( + new Entry("3.1", "Територія гравця. Гравець розпоряджається своєю територією. Втручання без дозволу — порушення.", Severity.MEDIUM), + new Entry("3.2", "Гріферство. Заборонено руйнувати будівлі, псувати територію, знищувати майно без дозволу.", Severity.HEAVY), + new Entry("3.3", "Неприйнятні споруди. Заборонено: образливий/провокаційний/порнографічний/політичний/нацистський/наркотичний контент.", Severity.MEDIUM), + new Entry("3.4", "Псування світу. Заборонено створювати безлад: хаотичне знищення ландшафту, залишення занедбаних будівель, розливання рідин, безглузді стовпи.", Severity.MEDIUM), + new Entry("3.5", "Контейнери з чужими ресурсами. Заборонено знищувати скрині/шалкери з чужими речами без причини.", Severity.MEDIUM), + new Entry("3.6", "Назви предметів. Не повинні містити заборонений контент.", Severity.LIGHT) + ))); + + // 4. НІКНЕЙМИ, СКІНИ ТА ПРОФІЛІ + sections.add(new Section("4. НІКНЕЙМИ, СКІНИ ТА ПРОФІЛІ", List.of( + new Entry("4.1", "Заборонені нікнейми: образи, провокації, порнографія, реклама, наркотики, неприйнятний контент.", Severity.HEAVY), + new Entry("4.2", "Заборонені префікси: образливий, провокаційний, порнографічний, рекламний контент.", Severity.MEDIUM), + new Entry("4.3", "Скіни та плащі. Заборонено порнографічний/неприйнятний контент.", Severity.MEDIUM), + new Entry("4.4", "Імітація адміністрації. Заборонено виглядати як адмін/команда проєкту або відомі медіа-особи.", Severity.HEAVY), + new Entry("4.5", "Імітація іншого гравця. Заборонено створювати схожі ніки з метою обману.", Severity.HEAVY), + new Entry("4.6", "Вхід під чужим акаунтом. Заборонено.", Severity.HEAVY) + ))); + + // 5. PvP ТА ВЗАЄМОДІЯ + sections.add(new Section("5. PvP ТА ВЗАЄМОДІЯ", List.of( + new Entry("5.1", "Перебування на чужій території. Піти після другого прохання власника. Виняток — обов'язки персоналу.", Severity.LIGHT) + ))); + + // 6. ТЕХНІЧНІ ТА ІНШІ ПОРУШЕННЯ + sections.add(new Section("6. ТЕХНІЧНІ ТА ІНШІ ПОРУШЕННЯ", List.of( + new Entry("6.1", "Втручання в роботу проєкту. Заборонено втручатися в сервер/лаунчер для переваги/обходу захисту.", Severity.HEAVY), + new Entry("6.2", "Обхід систем захисту. Заборонено обходити авторизацію/захист через сторонні клієнти.", Severity.HEAVY), + new Entry("6.3", "Порушення, не передбачені правилами. Адміністрація може тимчасово обмежити доступ для з'ясування.", Severity.MEDIUM) + ))); + + // 7. ІВЕНТИ + sections.add(new Section("7. ІВЕНТИ", List.of( + new Entry("7.1", "Правила івентів. Необхідно дотримуватися окремих правил івенту. Порушення карається за умовами івенту.", Severity.LIGHT) + ))); + + return new RulesRoot(sections); + } } \ No newline at end of file diff --git a/src/main/java/org/examp/john/rules/RulesLoader.java b/src/main/java/org/examp/john/rules/RulesLoader.java index 7c0fb06..2882984 100644 --- a/src/main/java/org/examp/john/rules/RulesLoader.java +++ b/src/main/java/org/examp/john/rules/RulesLoader.java @@ -65,14 +65,12 @@ public class RulesLoader { private static void createDefaultConfig() throws IOException { Files.createDirectories(CONFIG_PATH.getParent()); - RulesData.Entry defaultEntry = new RulesData.Entry("1.1", "Пункт 1", RulesData.Severity.LIGHT); - RulesData.Section defaultSection = new RulesData.Section("Правила", Collections.singletonList(defaultEntry)); - RulesData.RulesRoot defaultRoot = new RulesData.RulesRoot(Collections.singletonList(defaultSection)); + RulesData.RulesRoot defaultRoot = RulesData.createDefaultRules(); try (var writer = Files.newBufferedWriter(CONFIG_PATH, StandardCharsets.UTF_8)) { new GsonBuilder().setPrettyPrinting().create().toJson(defaultRoot, writer); } - Localchat.magicModLog.info("Створено конфіг за замовчуванням: {}", CONFIG_PATH.toAbsolutePath()); + Localchat.magicModLog.info("Створено конфіг правил за замовчуванням: {}", CONFIG_PATH.toAbsolutePath()); } } \ No newline at end of file