Skip to content

Commit e77e814

Browse files
authored
Add manifest command (#51)
* Initial implementation of manifest command * Improved manifest command logic and changed how its embeds look * Removed metadata arguments and improved the Manifest class so it's easier to use * Updated changelog * Fixed misspelling in changelog * Fixed formatting of text in embed
1 parent f106f37 commit e77e814

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Added `manifest` command that generates Bedrock manifests
12+
913
### Changed
14+
1015
- Improved emoji parsing logic for the `vote` command
1116

1217
## [0.14.0] - 2021-08-25
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from discord.ext.commands import Bot
2+
3+
from commanderbot_ext.ext.manifest.manifest_cog import ManifestCog
4+
5+
6+
def setup(bot: Bot):
7+
bot.add_cog(ManifestCog(bot))
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import json
2+
import uuid
3+
from enum import Enum
4+
from typing import Optional
5+
6+
7+
class PackType(Enum):
8+
ADDON = "addon"
9+
BEHAVIOR = "behavior"
10+
DATA = "data"
11+
RESOURCE = "resource"
12+
SKIN = "skin"
13+
14+
@classmethod
15+
def values(cls) -> list[str]:
16+
return [i.value for i in cls]
17+
18+
19+
class ModuleType(Enum):
20+
DATA = "data"
21+
RESOURCE = "resources"
22+
SKIN = "skin_pack"
23+
24+
25+
class Manifest:
26+
"""
27+
A complete manifest
28+
"""
29+
30+
def __init__(
31+
self,
32+
module_type: ModuleType,
33+
name: str,
34+
description: str,
35+
min_engine_version: list[int],
36+
):
37+
self.module_type: ModuleType = module_type
38+
self.name: str = name
39+
self.description: str = description
40+
self.min_engine_version: list[int] = min_engine_version
41+
42+
self.pack_uuid: str = str(uuid.uuid4())
43+
self.module_uuid: str = str(uuid.uuid4())
44+
self.dependency_uuid: Optional[str] = None
45+
46+
def as_json(self) -> str:
47+
"""
48+
Serializes this manifest as a Json string
49+
"""
50+
manifest = {
51+
"format_version": 2,
52+
"header": {
53+
"name": self.name,
54+
"description": self.description,
55+
"uuid": self.pack_uuid,
56+
"version": [1, 0, 0],
57+
"min_engine_version": self.min_engine_version,
58+
},
59+
"modules": [
60+
{
61+
"type": self.module_type.value,
62+
"uuid": self.module_uuid,
63+
"version": [1, 0, 0],
64+
}
65+
],
66+
}
67+
68+
# Add dependency if the UUID exists
69+
if self.dependency_uuid:
70+
manifest["dependencies"] = [
71+
{
72+
"uuid": self.dependency_uuid,
73+
"version": [1, 0, 0],
74+
}
75+
]
76+
77+
# Serialize object as a json string
78+
return json.dumps(manifest, indent=4)
79+
80+
81+
def add_dependency(manifest: Manifest, dependent_manifest: Manifest):
82+
"""
83+
Adds 'dependent_manifest' as a dependency to 'manifest'
84+
"""
85+
manifest.dependency_uuid = dependent_manifest.pack_uuid
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from typing import Optional
2+
3+
from discord import Embed
4+
from discord.ext.commands import Bot, Cog, Context, command
5+
6+
from commanderbot_ext.ext.manifest.manifest import (
7+
Manifest,
8+
ModuleType,
9+
PackType,
10+
add_dependency,
11+
)
12+
13+
14+
class ManifestCog(Cog, name="commanderbot_ext.ext.manifest"):
15+
def __init__(self, bot: Bot):
16+
self.bot: Bot = bot
17+
self.default_manifest_version = [1, 17, 0]
18+
19+
def get_version(self, version_str: Optional[str]) -> list[int]:
20+
"""
21+
Parses 'version_str' and either returns it as a list of 3 ints or
22+
returns the default min engine version
23+
"""
24+
version: list[int] = self.default_manifest_version
25+
if version_str:
26+
found_version_numbers: list[int] = []
27+
for i in version_str.split("."):
28+
if not i.isnumeric():
29+
break
30+
found_version_numbers.append(int(i))
31+
32+
if len(found_version_numbers) == 3:
33+
version = found_version_numbers
34+
35+
return version
36+
37+
@command(name="manifest", brief="Generate a Bedrock manifest")
38+
async def cmd_manifest(
39+
self,
40+
ctx: Context,
41+
pack_type: str,
42+
name: Optional[str],
43+
description: Optional[str],
44+
min_engine_version: Optional[str],
45+
):
46+
# Parse required pack type argument and create a list of modules from it
47+
modules: list[ModuleType] = []
48+
pack_type = pack_type.strip().lower()
49+
if pack_type == PackType.ADDON.value:
50+
modules.append(ModuleType.DATA)
51+
modules.append(ModuleType.RESOURCE)
52+
elif pack_type == PackType.BEHAVIOR.value or pack_type == PackType.DATA.value:
53+
modules.append(ModuleType.DATA)
54+
elif pack_type == PackType.RESOURCE.value:
55+
modules.append(ModuleType.RESOURCE)
56+
elif pack_type == PackType.SKIN.value:
57+
modules.append(ModuleType.SKIN)
58+
else:
59+
available_pack_types = [f"`{i}`" for i in PackType.values()]
60+
await ctx.reply(
61+
f"**{pack_type}** is not a valid pack type\n"
62+
f"Available pack types: {' '.join(available_pack_types)}"
63+
)
64+
return
65+
66+
# Parse optional arguments
67+
pack_name = name if name else "pack.name"
68+
pack_description = description if description else "pack.description"
69+
engine_version = self.get_version(min_engine_version)
70+
71+
# Create a list of manifests from modules
72+
manifests: list[Manifest] = []
73+
for module in modules:
74+
manifests.append(
75+
Manifest(module, pack_name, pack_description, engine_version)
76+
)
77+
78+
# If we're generating a complete addon, make the behavior pack dependent
79+
# on the resource pack
80+
if len(manifests) == 2:
81+
add_dependency(manifests[0], manifests[1])
82+
83+
# Send embed
84+
manifest_embed = Embed(title="Generated manifest", color=0x00ACED)
85+
description_text = ""
86+
for manifest in manifests:
87+
# Get the common name for a manifest using each kind of module
88+
common_name: str = ""
89+
if manifest.module_type == ModuleType.DATA:
90+
common_name = "Behavior pack"
91+
elif manifest.module_type == ModuleType.RESOURCE:
92+
common_name = "Resource pack"
93+
elif manifest.module_type == ModuleType.SKIN:
94+
common_name = "Skin pack"
95+
96+
formatted_manifest_json: str = f"```json\n{manifest.as_json()}\n```"
97+
description_text += f"**{common_name}**\n{formatted_manifest_json}\n"
98+
99+
manifest_embed.description = description_text
100+
await ctx.send(embed=manifest_embed)

0 commit comments

Comments
 (0)