From 2612506b9688817a0701976f8d2c7788a9a894c8 Mon Sep 17 00:00:00 2001 From: Oran9eUtan Date: Tue, 6 Jul 2021 18:59:37 +0200 Subject: [PATCH] feat: Chat configuration Configuration is now generated with a beet plugin. This allows for configuration via chat and book --- beet.yml | 2 + .../oran9eutan/functions/hats/cfg.mcfunction | 1 - .../hats/cfg/disable/convert_dogs.mcfunction | 6 - .../hats/cfg/disable/dress_mobs.mcfunction | 6 - .../hats/cfg/disable/fix_old_hats.mcfunction | 9 - .../hats/cfg/enable/convert_dogs.mcfunction | 6 - .../hats/cfg/enable/dress_mobs.mcfunction | 6 - .../hats/cfg/enable/fix_old_hats.mcfunction | 9 - .../event/player_dropped_hat_item.mcfunction | 4 +- .../functions/hats/install.mcfunction | 14 +- .../oran9eutan/functions/hats/load.mcfunction | 5 +- .../convert_dogs_near_players.mcfunction | 2 +- ...id_player_triggered_fix_old_hat.mcfunction | 4 - .../hats/loops/dress_mobs.mcfunction | 2 +- .../data/oran9eutan/loot_tables/hats/cfg.json | 23 -- hats/plugins/configuration.py | 219 ++++++++++++++++++ hats/registry/settings.py | 99 ++++++++ hats/registry/settings.yml | 20 ++ 18 files changed, 348 insertions(+), 89 deletions(-) delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/disable/convert_dogs.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/disable/dress_mobs.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/disable/fix_old_hats.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/enable/convert_dogs.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/enable/dress_mobs.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/cfg/enable/fix_old_hats.mcfunction delete mode 100644 datapack/data/oran9eutan/functions/hats/loops/did_player_triggered_fix_old_hat.mcfunction delete mode 100644 datapack/data/oran9eutan/loot_tables/hats/cfg.json create mode 100644 hats/plugins/configuration.py create mode 100644 hats/registry/settings.py create mode 100644 hats/registry/settings.yml diff --git a/beet.yml b/beet.yml index ff3f1ad..20df5a9 100644 --- a/beet.yml +++ b/beet.yml @@ -14,6 +14,7 @@ pipeline: - beet.contrib.installation_advancement - babelbox.integration.beet - mcanitexgen.integration.beet + - hats.plugins.configuration - hats.plugins.hat_loot_tables - hats.plugins.item_models - hats.plugins.hat_recipes @@ -35,6 +36,7 @@ meta: render: data_pack: advancements: ["oran9eutan:*"] + functions: ["oran9eutan:*"] installation_advancement: author_skull_owner: Oran9eUtan icon: diff --git a/datapack/data/oran9eutan/functions/hats/cfg.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg.mcfunction deleted file mode 100644 index 26c6f11..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg.mcfunction +++ /dev/null @@ -1 +0,0 @@ -loot give @s loot oran9eutan:hats/cfg \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/disable/convert_dogs.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/disable/convert_dogs.mcfunction deleted file mode 100644 index 42f4ef5..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/disable/convert_dogs.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# Disables Players from converting dogs into hats and back - -scoreboard players set #opt_convert_dogs hats.cfg 0 -schedule clear oran9eutan:hats/loops/convert_dogs_near_players - -tellraw @s [{"text":"Disabled","color":"red"},{"text":" option \"Convert dogs\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/disable/dress_mobs.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/disable/dress_mobs.mcfunction deleted file mode 100644 index 68fa816..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/disable/dress_mobs.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# Disables randomly dressing mobs with hats - -scoreboard players set #opt_dress_mobs hats.cfg 0 -schedule clear oran9eutan:hats/loops/dress_mobs - -tellraw @s [{"text":"Disabled","color":"red"},{"text":" option \"Dress mobs\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/disable/fix_old_hats.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/disable/fix_old_hats.mcfunction deleted file mode 100644 index e89af14..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/disable/fix_old_hats.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# Disables option to allow players to fix their old hats - -scoreboard players set #opt_enable_fix_old_hats hats.cfg 0 -scoreboard players reset @a hats.fix_old_hat - -scoreboard players reset @a hats.fix_old_hat -schedule clear oran9eutan:hats/loops/did_player_triggered_fix_old_hat - -tellraw @s [{"text":"Disabled","color":"red"},{"text":" option \"Fix old hats\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/enable/convert_dogs.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/enable/convert_dogs.mcfunction deleted file mode 100644 index 2e5633d..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/enable/convert_dogs.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# Enables Players to convert dogs into hats and back - -scoreboard players set #opt_convert_dogs hats.cfg 1 -function oran9eutan:hats/loops/convert_dogs_near_players - -tellraw @s [{"text":"Enabled ","color":"green"},{"text":"option \"Convert dogs\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/enable/dress_mobs.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/enable/dress_mobs.mcfunction deleted file mode 100644 index 95d76ba..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/enable/dress_mobs.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -# Enables randomly dressing mobs with hats - -scoreboard players set #opt_dress_mobs hats.cfg 1 -function oran9eutan:hats/loops/dress_mobs - -tellraw @s [{"text":"Enabled ","color":"green"},{"text":"option \"Dress mobs\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/cfg/enable/fix_old_hats.mcfunction b/datapack/data/oran9eutan/functions/hats/cfg/enable/fix_old_hats.mcfunction deleted file mode 100644 index a2f45de..0000000 --- a/datapack/data/oran9eutan/functions/hats/cfg/enable/fix_old_hats.mcfunction +++ /dev/null @@ -1,9 +0,0 @@ -# Enables players to fix their old hats - -scoreboard players set #opt_enable_fix_old_hats hats.cfg 1 -scoreboard players reset @a hats.fix_old_hat - -scoreboard players enable @a hats.fix_old_hat -function oran9eutan:hats/loops/did_player_triggered_fix_old_hat - -tellraw @s [{"text":"Enabled ","color":"green"},{"text":"option \"Fix old hats\"","color":"gold"}] \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/event/player_dropped_hat_item.mcfunction b/datapack/data/oran9eutan/functions/hats/event/player_dropped_hat_item.mcfunction index faf8dba..83f092c 100644 --- a/datapack/data/oran9eutan/functions/hats/event/player_dropped_hat_item.mcfunction +++ b/datapack/data/oran9eutan/functions/hats/event/player_dropped_hat_item.mcfunction @@ -2,9 +2,9 @@ # executor: Player getting advancement # Convert all nearby dog hat items into dogs -execute if score #opt_convert_dogs hats.cfg matches 1 as @e[type=item,distance=..2,nbt={Item:{tag:{Tags:["hats.hat.type.dog"]}}}] at @s if data entity @s Item.tag.dog_data run function oran9eutan:hats/dog_mechanism/copy_dog_data_from_item +execute if {{ hats.setting["dog_conversion"].is_true }} as @e[type=item,distance=..2,nbt={Item:{tag:{Tags:["hats.hat.type.dog"]}}}] at @s if data entity @s Item.tag.dog_data run function oran9eutan:hats/dog_mechanism/copy_dog_data_from_item # Reset trigger scoreboard players set @s hats.dropLthrHlm 0 scoreboard players set @s hats.dropStick 0 -advancement revoke @s only oran9eutan:hats/event/player_dropped_hat_item \ No newline at end of file +advancement revoke @s only oran9eutan:hats/event/player_dropped_hat_item diff --git a/datapack/data/oran9eutan/functions/hats/install.mcfunction b/datapack/data/oran9eutan/functions/hats/install.mcfunction index ecfb767..bbc6fc0 100644 --- a/datapack/data/oran9eutan/functions/hats/install.mcfunction +++ b/datapack/data/oran9eutan/functions/hats/install.mcfunction @@ -6,18 +6,8 @@ scoreboard objectives add hats.dropLthrHlm minecraft.dropped:minecraft.leather_h scoreboard objectives add hats.dropStick minecraft.dropped:minecraft.stick scoreboard objectives add hats.fix_old_hat trigger -#region: Init settings - -#define score_holder #opt_convert_dogs Should Players be able to convert dogs to hats and back -execute unless score #opt_convert_dogs hats.cfg matches -2147483648.. run scoreboard players set #opt_convert_dogs hats.cfg 1 - -#define score_holder #opt_dress_mobs Randomly dress mobs in the world with hats -execute unless score #opt_dress_mobs hats.cfg matches -2147483648.. run scoreboard players set #opt_dress_mobs hats.cfg 1 - -#define score_holder #opt_enable_fix_old_hats Should players be able to fix their old hats -execute unless score #opt_enable_fix_old_hats hats.cfg matches -2147483648.. run scoreboard players set #opt_enable_fix_old_hats hats.cfg 0 - -#endregion +# Initialize settings +function oran9eutan:hats/cfg/init # Flag datapack as installed data modify storage oran9eutan.hats version set value {"major": 2, "minor": 3, "patch": 1} diff --git a/datapack/data/oran9eutan/functions/hats/load.mcfunction b/datapack/data/oran9eutan/functions/hats/load.mcfunction index d9692ac..352e55a 100644 --- a/datapack/data/oran9eutan/functions/hats/load.mcfunction +++ b/datapack/data/oran9eutan/functions/hats/load.mcfunction @@ -6,6 +6,5 @@ scoreboard objectives add hats.cfg dummy execute unless data storage minecraft:oran9eutan.hats {"version":{"major": 2, "minor": 3, "patch": 1}} run function oran9eutan:hats/install #Start looping functions -execute if score #opt_convert_dogs hats.cfg matches 1 run function oran9eutan:hats/loops/convert_dogs_near_players -execute if score #opt_dress_mobs hats.cfg matches 1 run function oran9eutan:hats/loops/dress_mobs -execute if score #opt_enable_fix_old_hats hats.cfg matches 1 run function oran9eutan:hats/loops/did_player_triggered_fix_old_hat +execute if {{ hats.setting["dog_conversion"].is_true }} run function oran9eutan:hats/loops/convert_dogs_near_players +execute if {{ hats.setting["dress_mobs"].is_true }} run function oran9eutan:hats/loops/dress_mobs diff --git a/datapack/data/oran9eutan/functions/hats/loops/convert_dogs_near_players.mcfunction b/datapack/data/oran9eutan/functions/hats/loops/convert_dogs_near_players.mcfunction index 1614517..1f57006 100644 --- a/datapack/data/oran9eutan/functions/hats/loops/convert_dogs_near_players.mcfunction +++ b/datapack/data/oran9eutan/functions/hats/loops/convert_dogs_near_players.mcfunction @@ -4,4 +4,4 @@ execute as @a[tag=!global.ignore] at @s if predicate oran9eutan:hats/player/can_convert_dogs if entity @e[tag=!global.ignore,type=minecraft:wolf,distance=..2,predicate=oran9eutan:hats/entity/is_sitting] run function oran9eutan:hats/dog_mechanism/convert_owned_dogs_nearby # Loop if enabled -execute if score #opt_convert_dogs hats.cfg matches 1 run schedule function oran9eutan:hats/loops/convert_dogs_near_players 1s \ No newline at end of file +execute if {{ hats.setting["dog_conversion"].is_true }} run schedule function oran9eutan:hats/loops/convert_dogs_near_players 1s diff --git a/datapack/data/oran9eutan/functions/hats/loops/did_player_triggered_fix_old_hat.mcfunction b/datapack/data/oran9eutan/functions/hats/loops/did_player_triggered_fix_old_hat.mcfunction deleted file mode 100644 index 0a76743..0000000 --- a/datapack/data/oran9eutan/functions/hats/loops/did_player_triggered_fix_old_hat.mcfunction +++ /dev/null @@ -1,4 +0,0 @@ -execute as @a[tag=!global.ignore] if score @s hats.fix_old_hat matches 1.. run function oran9eutan:hats/fix_old_hats/fix_selected_hat - -# loop -execute if score #opt_enable_fix_old_hats hats.cfg matches 1 run schedule function oran9eutan:hats/loops/did_player_triggered_fix_old_hat 5t \ No newline at end of file diff --git a/datapack/data/oran9eutan/functions/hats/loops/dress_mobs.mcfunction b/datapack/data/oran9eutan/functions/hats/loops/dress_mobs.mcfunction index aa5b3e3..dbe0971 100644 --- a/datapack/data/oran9eutan/functions/hats/loops/dress_mobs.mcfunction +++ b/datapack/data/oran9eutan/functions/hats/loops/dress_mobs.mcfunction @@ -2,4 +2,4 @@ execute as @a[tag=!global.ignore] at @s positioned ^ ^ ^25 run function oran9eutan:hats/dress/dress_mobs_near_player # loop -execute if score #opt_dress_mobs hats.cfg matches 1 run schedule function oran9eutan:hats/loops/dress_mobs 5s \ No newline at end of file +execute if {{ hats.setting["dress_mobs"].is_true }} run schedule function oran9eutan:hats/loops/dress_mobs 5s diff --git a/datapack/data/oran9eutan/loot_tables/hats/cfg.json b/datapack/data/oran9eutan/loot_tables/hats/cfg.json deleted file mode 100644 index 147c6f4..0000000 --- a/datapack/data/oran9eutan/loot_tables/hats/cfg.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "pools": [ - { - "rolls": 1, - "entries": [ - { - "type": "minecraft:item", - "name": "minecraft:written_book", - "functions": [ - { - "function": "minecraft:set_nbt", - "tag": "{title: \"Hats Config\", author: \"Oran9eUtan\"}" - }, - { - "function": "minecraft:set_nbt", - "tag": "{pages:['[\"\",{\"text\":\"Settings:\\\\n- \"},{\"text\":\"Dress mobs\",\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Mobs will be randomly equiped with hats\"}},{\"text\":\": \"},{\"text\":\"\\\\u2714\",\"color\":\"dark_green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/enable\/dress_mobs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Enable\"}},{\"text\":\"\u00D7\",\"color\":\"red\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/disable\/dress_mobs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Disable\"}},{\"text\":\"\\\\u27f2\",\"color\":\"aqua\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/enable\/dress_mobs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Reset\"}},{\"text\":\"\\\\n- \",\"color\":\"reset\"},{\"text\":\"Convert dogs\",\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Allow players to convert their dogs into hats and back\"}},{\"text\":\": \"},{\"text\":\"\\\\u2714\",\"color\":\"dark_green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/enable\/convert_dogs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Allow\"}},{\"text\":\"\u00D7\",\"color\":\"red\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/disable\/convert_dogs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Disable\"}},{\"text\":\"\\\\u27f2\",\"color\":\"aqua\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/enable\/convert_dogs\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Reset\"}},{\"text\":\"\\\\n- \",\"color\":\"reset\"},{\"text\":\"Fix old hats\",\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Allow Players to fix their old hats\"}},{\"text\":\": \"},{\"text\":\"\\\\u2714\",\"color\":\"dark_green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/enable\/fix_old_hats\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Allow\"}},{\"text\":\"\u00D7\",\"color\":\"red\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/disable\/fix_old_hats\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Disable\"}},{\"text\":\"\\\\u27f2\",\"color\":\"aqua\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"\/function oran9eutan:hats\/cfg\/disable\/fix_old_hats\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":\"Reset\"}}]']}" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/hats/plugins/configuration.py b/hats/plugins/configuration.py new file mode 100644 index 0000000..affc64e --- /dev/null +++ b/hats/plugins/configuration.py @@ -0,0 +1,219 @@ +import json +from logging import getLogger +from typing import Any, Optional + +from beet import Context +from beet.library.data_pack import DataPack, Function, LootTable + +from hats.registry.settings import Section, Setting, SettingsRegistry, SettingType + +logger = getLogger(__name__) + + +def beet_default(ctx: Context): + namespace = ctx.meta["namespace"] + config = ctx.meta["hats"] + registry = SettingsRegistry.get() + + config["setting"] = { + s.id: { + "is_true": f"score {s.scoreboard_holder} {registry.scoreboard} matches 1", + "is_false": f"score {s.scoreboard_holder} {registry.scoreboard} matches 0", + } + for s in registry.settings.values() + } + + create_chat_config(ctx, registry, namespace) + ctx.data.loot_tables[f"{namespace}/cfg"] = create_config_book_loot_table( + ctx, registry, namespace + ) + ctx.data.functions[f"{namespace}/cfg_book"] = Function( + ["loot give @s loot oran9eutan:hats/cfg"] + ) + + +def create_chat_config(ctx: Context, registry: SettingsRegistry, namespace: str): + ctx.data.functions[f"{namespace}/cfg/init"] = create_init_settings_function(registry) + + for section in registry.sections.values(): + ctx.data.merge(create_section(section, registry, ctx.project_name, namespace)) + + ctx.data.functions[f"{namespace}/cfg"] = Function( + [f"function {registry.main_function(namespace)}", cmd_blank_line] + ) + + +def create_config_book_loot_table(ctx: Context, registry: SettingsRegistry, namespace: str): + CHECK = "\u2714" + CROSS = "\u00D7" + LOOP = "\u27f2" + + pages = [] + for section in registry.sections.values(): + # Section header + page = [f"{section.title}:\n"] + + # Interactive settings + for setting in section.settings.values(): + if setting.type == SettingType.BOOL: + enable_function = f"{section.section_root(namespace)}/enable_{setting.id}" + disable_function = f"{section.section_root(namespace)}/disable_{setting.id}" + reset_function = enable_function if setting.default else disable_function + + page += [ + "- ", + textcomp_interactive(setting.title, hover_text=setting.description), + ": ", + textcomp_interactive(CHECK, "dark_green", "Enable", enable_function), + textcomp_interactive(CROSS, "red", "Disable", disable_function), + textcomp_interactive(f"{LOOP}\n", "aqua", "Reset", reset_function), + ] + + pages.append(json.dumps(page)) + + return LootTable( + { + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:written_book", + "functions": [ + { + "function": "minecraft:set_nbt", + "tag": f'{{title: "{ctx.project_name} Configuration", author: "{ctx.project_author}"}}', + }, + {"function": "minecraft:set_nbt", "tag": f"{{pages:{pages}}}"}, + ], + } + ], + } + ] + } + ) + + +def textcomp_interactive( + text: str, + color: Optional[str] = None, + hover_text: Optional[str] = None, + on_click: Optional[str] = None, +): + textcomp = {"text": text} + + if color: + textcomp["color"] = color + if hover_text: + textcomp["hoverEvent"] = {"action": "show_text", "contents": hover_text} + if on_click: + textcomp["clickEvent"] = {"action": "run_command", "value": f"/function {on_click}"} + + return textcomp + + +def create_init_settings_function(registry: SettingsRegistry): + lines = [] + for setting in registry.settings.values(): + if setting.type == SettingType.BOOL: + lines.append( + f"execute unless score {setting.scoreboard_holder} {registry.scoreboard} matches -2147483648.. run {cmd_set_score(setting.scoreboard_holder, registry.scoreboard, 1 if setting.default else 0)}" + ) + + return Function(lines) + + +def create_section(section: Section, registry: SettingsRegistry, project_name: str, namespace: str): + data = DataPack() + + section_root = section.section_root(namespace) + project_title = f"{project_name} config: " + + # Show header + lines = [ + f'tellraw @s ["",{{"text":"{project_title}","color":"gold"}}, {{"text": "{section.title}"}}]', + f"tellraw @s {{\"text\":\"{'-'*len(project_title)}\"}}", + ] + + # List settings + for setting in section.settings.values(): + lines += cmds_show_setting_config(setting, registry, section_root) + data.functions[f"{section_root}/enable_{setting.id}"] = create_set_bool_setting_function( + setting, registry.scoreboard, True, section_root + ) + data.functions[f"{section_root}/disable_{setting.id}"] = create_set_bool_setting_function( + setting, registry.scoreboard, False, section_root + ) + + # Fill chat window + lines += [cmd_blank_line for _ in range(section.MAX_LINES - len(lines) + 2)] + + data.functions[section.main_function(namespace)] = Function(lines) + return data + + +def create_set_bool_setting_function( + setting: Setting, scoreboard: str, value: bool, section_root: str +): + lines = [ + cmd_set_score(setting.scoreboard_holder, scoreboard, 1 if value else 0), + f"function {section_root}/main", + ] + + if value and setting.on_enable: + lines.append(setting.on_enable) + elif not value and setting.on_disable: + lines.append(setting.on_disable) + + return Function(lines) + + +def textcomp_set_bool_setting(setting: Setting, value: bool, change_function: str): + textcomp = { + "text": setting.title, + "clickEvent": {"action": "run_command", "value": f"/function {change_function}"}, + "hoverEvent": {"action": "show_text", "contents": setting.description}, + } + + if value: + textcomp["strikethrough"] = True + textcomp["color"] = "gray" + + return textcomp + + +def cmd_set_score(scoreboard_holder: str, scoreboard: str, value: int): + return f"scoreboard players set {scoreboard_holder} {scoreboard} {value}" + + +def cmd_if_score(scoreboard_holder: str, scoreboard: str, value: int, then: Any): + return f"execute as @s if score {scoreboard_holder} {scoreboard} matches {value} run {then}" + + +def cmds_show_setting_config(setting: Setting, registry: SettingsRegistry, section_root: str): + if setting.type == SettingType.BOOL: + textcomp_enable = textcomp_set_bool_setting( + setting, True, f"{section_root}/enable_{setting.id}" + ) + textcomp_disable = textcomp_set_bool_setting( + setting, False, f"{section_root}/disable_{setting.id}" + ) + + return [ + cmd_if_score( + setting.scoreboard_holder, + registry.scoreboard, + 0, + f'tellraw @s [{{"text": "- "}}, {json.dumps(textcomp_enable)}]', + ), + cmd_if_score( + setting.scoreboard_holder, + registry.scoreboard, + 1, + f'tellraw @s [{{"text": "- "}}, {json.dumps(textcomp_disable)}]', + ), + ] + + +cmd_blank_line = f'tellraw @s [""]' diff --git a/hats/registry/settings.py b/hats/registry/settings.py new file mode 100644 index 0000000..73ef3ce --- /dev/null +++ b/hats/registry/settings.py @@ -0,0 +1,99 @@ +from collections import defaultdict +from dataclasses import dataclass, field +from enum import Enum +from pathlib import Path +from typing import Any, Iterable, Optional, Set + +import yaml + + +class SettingType(Enum): + BOOL = "bool" + + @staticmethod + def from_str(value: str): + return {SettingType.BOOL.value: SettingType.BOOL}[value] + + +@dataclass(unsafe_hash=True) +class Setting: + id: str + title: str + description: str + type: SettingType + default: Optional[Any] + on_enable: Optional[str] + on_disable: Optional[str] + + @property + def scoreboard_holder(self): + return f"#cfg.{self.id}" + + @classmethod + def from_json(cls, id: str, json: dict): + return Setting( + id, + json["title"], + json["description"], + SettingType.from_str(json["type"]), + json.get("default"), + json.get("on_enable"), + json.get("on_disable"), + ) + + +@dataclass +class Section: + id: str + title: str + settings: dict[str, Setting] + + MAX_LINES = 18 + + @classmethod + def from_json(cls, id: str, json: dict): + return Section( + id, + json["title"], + dict(map(lambda s: (s[0], Setting.from_json(s[0], s[1])), json["settings"].items())), + ) + + def section_root(self, namespace: str): + return f"{namespace}/cfg/{self.id}" + + def main_function(self, namespace: str): + return f"{self.section_root(namespace)}/main" + + +class SettingsRegistry: + PATH = Path("hats/registry/settings.yml") + + def __init__( + self, + main_section: Section, + sections: dict[str, Section], + settings: dict[str, Setting], + scoreboard: str, + ): + self.settings = settings + self.sections = sections + self.scoreboard = scoreboard + self.main_section = main_section + + @classmethod + def from_json(cls, json: dict): + sections = {s[0]: Section.from_json(s[0], s[1]) for s in json["sections"].items()} + + settings = {} + for section in sections.values(): + settings.update(section.settings) + + return SettingsRegistry(sections[json["main"]], sections, settings, json["scoreboard"]) + + @classmethod + def get(cls): + with cls.PATH.open("r") as f: + return cls.from_json(yaml.safe_load(f)) + + def main_function(self, namespace: str): + return f"{self.main_section.main_function(namespace)}" diff --git a/hats/registry/settings.yml b/hats/registry/settings.yml new file mode 100644 index 0000000..c77e079 --- /dev/null +++ b/hats/registry/settings.yml @@ -0,0 +1,20 @@ +main: general +scoreboard: hats.cfg +sections: + general: + title: "General" + settings: + dress_mobs: + title: Dress mobs + description: Randomly equip mobs with hats + type: bool + default: True + on_enable: function oran9eutan:hats/loops/dress_mobs + on_disable: schedule clear oran9eutan:hats/loops/dress_mobs + dog_conversion: + title: Dog conversion + description: Dogs can be converted to hats and back + type: bool + default: True + on_enable: function oran9eutan:hats/loops/convert_dogs_near_players + on_disable: schedule clear oran9eutan:hats/loops/convert_dogs_near_players