diff --git a/build.gradle b/build.gradle index 49080ff..7336c6e 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,7 @@ dependencies { compileOnly "com.github.MilkBowl:VaultAPI:1.7" compileOnly "me.clip:placeholderapi:2.10.6" implementation "com.dumbdogdiner:stickyapi:2.0.0" + implementation 'com.atlassian.commonmark:commonmark:0.15.2' compileOnly 'net.luckperms:api:5.2' compileOnly 'com.dumbdogdiner.closedsource-package-mirror:stafffacilities:4.8.5' //compileOnly 'me.xtomyserrax:StaffFacilities' diff --git a/src/main/java/com/dumbdogdiner/stickycommands/StickyCommands.java b/src/main/java/com/dumbdogdiner/stickycommands/StickyCommands.java index 083a1ef..4256e42 100644 --- a/src/main/java/com/dumbdogdiner/stickycommands/StickyCommands.java +++ b/src/main/java/com/dumbdogdiner/stickycommands/StickyCommands.java @@ -222,7 +222,7 @@ boolean registerCommands() { commandList.add(new PowerToolCommand(this)); commandList.add(new PlayerTimeCommand(this)); commandList.add(new SmiteCommand(this)); - ; + commandList.add(new RulesCommand(this)); CommandUtil.registerCommands(getServer(), commandList); return true; diff --git a/src/main/java/com/dumbdogdiner/stickycommands/commands/RulesCommand.java b/src/main/java/com/dumbdogdiner/stickycommands/commands/RulesCommand.java new file mode 100644 index 0000000..488f4e2 --- /dev/null +++ b/src/main/java/com/dumbdogdiner/stickycommands/commands/RulesCommand.java @@ -0,0 +1,124 @@ +package com.dumbdogdiner.stickycommands.commands; + +import com.dumbdogdiner.stickyapi.bukkit.command.StickyPluginCommand; +import com.dumbdogdiner.stickyapi.bukkit.item.generator.BookGenerator; +import com.dumbdogdiner.stickyapi.bukkit.plugin.StickyPlugin; +import com.dumbdogdiner.stickyapi.bukkit.util.ServerUtil; +import com.dumbdogdiner.stickyapi.common.arguments.Arguments; +import com.dumbdogdiner.stickyapi.common.book.chat.JsonComponent; +import com.dumbdogdiner.stickyapi.common.book.commonmarkextensions.JsonComponentWriter; +import com.dumbdogdiner.stickyapi.common.book.commonmarkextensions.MCFormatExtension; +import com.dumbdogdiner.stickyapi.common.book.commonmarkextensions.MarkdownJsonRenderer; +import com.dumbdogdiner.stickyapi.common.command.ExitCode; +import com.dumbdogdiner.stickyapi.common.translation.Translation; +import com.dumbdogdiner.stickyapi.common.util.BookUtil; +import com.dumbdogdiner.stickycommands.StickyCommands; +import org.bukkit.Material; +import org.bukkit.command.CommandException; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.permissions.Permission; +import org.commonmark.node.Document; +import org.commonmark.parser.Parser; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class RulesCommand extends StickyPluginCommand { + public static final String PERMISSION = "stickycommands.rules"; + public static final String RULEBOOK_LOCAL_FILENAME = "rulebook.md"; + public static final String RULEBOOK_URL_CONFIG_PATH = "rulebook-url"; + + public RulesCommand(@NotNull StickyPlugin owner) { + super( + /* name: */ "rules", + /* aliases: */ Collections.singletonList("rulebook"), + /* owner: */ owner, + /* permission: */ new Permission(PERMISSION) + ); + // settings not settable in super call + requiresPlayer = true; + } + + private static Reader getRulebookReader() { + var plugin = StickyCommands.getInstance(); + try { + var configUrl = plugin.getConfig().getString(RULEBOOK_URL_CONFIG_PATH); + if (configUrl != null) { + var url = new URL(configUrl); + return new InputStreamReader(url.openStream()); + } + var dataFolder = plugin.getDataFolder(); + var rulebook = new File(dataFolder, "rulebook.md"); + if (!rulebook.exists()) { + plugin.saveResource(RULEBOOK_LOCAL_FILENAME, true); + return new FileReader(rulebook); + } + } catch (Exception e) { + e.printStackTrace(); + } + var resource = plugin.getResource(RULEBOOK_LOCAL_FILENAME); + if (resource == null) { + return new StringReader("The rulebook could not be loaded."); + } else { + return new InputStreamReader(resource); + } + } + + private static ItemStack generateDefault() throws IOException { + var config = StickyCommands.getInstance().getConfig(); + var title = config.getString("rulebook-title"); + if (title == null) title = "&ddddMC Survival Handbook"; + var author = config.getString("rulebook-author"); + if (author == null) author = "Stixil"; + try (var reader = getRulebookReader()) { + return generate( + reader, + Translation.translateColors("&", title), + Translation.translateColors("&", author) + ); + } + } + + private static ItemStack generate(Reader reader, String title, String author) throws IOException { + var builder = Parser.builder(); + ((MCFormatExtension) MCFormatExtension.create()).extend(builder); + var cmparser = builder.build(); + var bookGenerator = new BookGenerator(Material.WRITTEN_BOOK); + var sections = BookUtil.splitDocumentByBreaks((Document) cmparser.parseReader(reader)); + sections.stream() + .map(section -> BookUtil.splitBookPages(renderDocument(section))) + .flatMap(List::stream) + .forEach(page -> bookGenerator.addPage(page.toJson())); + return bookGenerator.setTitle(title).setAuthor(author).toItemStack(1); + } + + private static JsonComponent renderDocument(Document document) { + var component = new JsonComponent(); + var writer = new JsonComponentWriter(component); + new MarkdownJsonRenderer(writer).render(document); + return component; + } + + @Override + public ExitCode execute(@NotNull CommandSender commandSender, @NotNull String s, @NotNull Arguments arguments, @NotNull Map map) { + try { + ((Player) commandSender).getInventory().addItem(generateDefault()); + } catch (IOException e) { + e.printStackTrace(); + return ExitCode.EXIT_ERROR; + } + + return ExitCode.EXIT_SUCCESS; + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] strings) throws IllegalArgumentException, CommandException { + return Collections.emptyList(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index cc1b24d..6959560 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -17,6 +17,13 @@ debug: false #The name of the server StickyCommands is running on server: "lobby" +# If the rulebook is hosted online, put its URL here, otherwise, leave blank and rulebook.md in the config will be loaded. +rulebook-url: +# The title of the rulebook +rulebook-title: "&dddMC Survival Handbook" +# The author of the rulebook +rulebook-author: Stixil + database: host: localhost port: 3306 diff --git a/src/main/resources/rulebook.md b/src/main/resources/rulebook.md new file mode 100644 index 0000000..dc56fdb --- /dev/null +++ b/src/main/resources/rulebook.md @@ -0,0 +1,97 @@ +# Dumb Dog Diner MC Survival Handbook + +## $red The Rules$ + +1. Absolutely no griefing, theft, or intentional distress! +2. No cheating/hacking allowed. Optifine and similar are okay. +3. No floating trees. +4. No fully-automatic redstone farms. +5. No harassment, bullying, or abuse. +6. No AFK fishing. + +--- + +## $dark_green Gameplay$ + +The world border for all worlds is 8,000 blocks from the center. Because of how +$dark_red Nether Portal$ travel works, you will not be able to light them past 1,000 blocks +from the center. You may build wherever you like! Try /rtp to help find a spot! + +This server uses a $red Death Chest$ system. Upon death, a campfire with your items +will spawn at your death location. It will last for 10 minutes before your items +drop on the ground! You can have up to 3 $red Death Chests$ at a time. + +--- + +## $dark_aqua Trading$ + +We use chest chops for player to trade good for $gold Coins$. Coins may be +earned through selling goods to other players, paying/receiving money for a +service, or by selling goods to the server itself. + +Chest shops can buy or sell items! To make a shop, place a chest and shift-click +with the item you want to sell or buy. Regular users may have up to 6 shops at a +time. + +--- + +## $red Commands$ + +Tab-complete is set up to only show commands you have access to. Here are some +useful ones: + +- $blue /help$ - Opens the help menu +- $blue /servers$ - Opens the server menu +- $blue /rules$ - Grants this book +- $blue /options$ - Opens the player options menu +- $blue /tpa$ - Sends a teleport request to another player +- $blue /rtp$ - Start a random teleport +- $blue /pw$ - Opens the playerwarps personal home menu +- $blue /helpme$ - Pings an online staff member for assistance + +--- + +## $dark_purple Trading Commands$ + +- $blue /bal$ - Check your balance +- $blue /pay$ - Pay another player +- $blue /sell$ - Sell the item in your hand to the server +- $blue /worth$ - Shows the item price when selling to the server +- $blue /value$ - Shows the average selling price for an item in player shops + +--- + +## $dark_aqua Extra Commands$ + +- $blue /donate, /vip$ - Gives link to the server Patreon page +- $blue /discord$ - Gives link to the server Discord +- $blue /twitter, /twitch$ - Gives social media links +- $blue /merch$ - Gives merch link +- $blue /afk$ - Set your status to AFK + +--- + +## $light_purple Getting Help$ + +If you ever need help getting your way around the server, please ask any staff! +**$blue /helpme$** + +Please join the Discord server to get technical help! **$blue /discord$** + +Please report any bugs, griefing, etc. that you come across or are a victim of. +A staff member will be happy to assist you. The help menu has shortcuts that +make getting help easy! **$blue /help$** + +--- + +## $gold Credits$ + +$dark_purple **dddMC**$ is fully funded through player donations. Please consider helping +support us on Patreon! $blue **/donate /patreon**$ + +Patrons will receive a VIP rank. More +ranks will be added in the future. + +We hope you enjoy your time playing on the server! + +\- Stixil, $dark_purple **dddMC 2020**$