From 25be5f86869b6dc23d484e4a27f895f8866368cd Mon Sep 17 00:00:00 2001 From: Andrew Gazelka Date: Sun, 17 Nov 2024 10:30:43 -0800 Subject: [PATCH] feat(teams): Add support for team-specific player skins and expand team types - Add Green and Yellow teams alongside existing Red and Blue teams - Implement team-specific knight skins with Mojang signatures - Update rank system to accept team parameter in commands - Add MineSkin API documentation link - Add `paste` crate dependency for macro support - Refactor skin loading system to use team-specific file paths --- CONTRIBUTING.md | 3 + Cargo.lock | 1 + Cargo.toml | 1 + crates/hyperion-rank-tree/Cargo.toml | 1 + crates/hyperion-rank-tree/src/inventory.rs | 5 +- crates/hyperion-rank-tree/src/lib.rs | 6 +- crates/hyperion-rank-tree/src/skin.rs | 103 +++++++++++++----- .../src/skin/blue/knight.toml | 2 + .../src/skin/green/knight.toml | 2 + .../src/skin/red/knight.toml | 2 + .../src/skin/yellow/knight.toml | 2 + events/proof-of-concept/src/command/rank.rs | 6 +- 12 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 crates/hyperion-rank-tree/src/skin/blue/knight.toml create mode 100644 crates/hyperion-rank-tree/src/skin/green/knight.toml create mode 100644 crates/hyperion-rank-tree/src/skin/red/knight.toml create mode 100644 crates/hyperion-rank-tree/src/skin/yellow/knight.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9cce1be9..2bdeaca9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,9 @@ Essential tools to enhance your development workflow: You can use https://github.com/andrewgazelka/mineskin-cli to use Mineskin from CLI. +https://docs.mineskin.org/docs/guides/getting-started/ + + ## Protocol Documentation We currently target Minecraft 1.20.1 protocol specification: diff --git a/Cargo.lock b/Cargo.lock index f61914ce..9ff49410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1847,6 +1847,7 @@ dependencies = [ "hyperion", "hyperion-inventory", "hyperion-item", + "paste", "toml", "valence_protocol", ] diff --git a/Cargo.toml b/Cargo.toml index d15014b0..1e51fafb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ tikv-jemallocator = '0.6.0' tokio = '1.40.0' toml = '0.8.14' uuid = '1.8.0' +paste = "1.0.15" [workspace.dependencies.bvh] git = 'https://github.com/andrewgazelka/bvh-data' diff --git a/crates/hyperion-rank-tree/Cargo.toml b/crates/hyperion-rank-tree/Cargo.toml index 36e6d737..cc01553a 100644 --- a/crates/hyperion-rank-tree/Cargo.toml +++ b/crates/hyperion-rank-tree/Cargo.toml @@ -13,6 +13,7 @@ toml = { workspace = true } valence_protocol = { workspace = true } clap = { workspace = true } flecs_ecs = { workspace = true } +paste = { workspace = true } hyperion-item.workspace = true [lints] diff --git a/crates/hyperion-rank-tree/src/inventory.rs b/crates/hyperion-rank-tree/src/inventory.rs index 676e7147..ea56226f 100644 --- a/crates/hyperion-rank-tree/src/inventory.rs +++ b/crates/hyperion-rank-tree/src/inventory.rs @@ -8,9 +8,10 @@ use crate::{Handles, Rank, Team}; impl Team { pub const fn build_item(self) -> ItemBuilder { let kind = match self { - Self::Red => ItemKind::RedTerracotta, - Self::White => ItemKind::WhiteTerracotta, Self::Blue => ItemKind::BlueTerracotta, + Self::Green => ItemKind::GreenTerracotta, + Self::Red => ItemKind::RedTerracotta, + Self::Yellow => ItemKind::YellowTerracotta, }; ItemBuilder::new(kind) diff --git a/crates/hyperion-rank-tree/src/lib.rs b/crates/hyperion-rank-tree/src/lib.rs index 361c990f..91f81965 100644 --- a/crates/hyperion-rank-tree/src/lib.rs +++ b/crates/hyperion-rank-tree/src/lib.rs @@ -28,10 +28,12 @@ pub enum Rank { Builder, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum, PartialOrd, Ord)] pub enum Team { - Red, - White, Blue, + Green, + Red, + Yellow, } #[derive(Component)] diff --git a/crates/hyperion-rank-tree/src/skin.rs b/crates/hyperion-rank-tree/src/skin.rs index 724f2ca0..bc5a7779 100644 --- a/crates/hyperion-rank-tree/src/skin.rs +++ b/crates/hyperion-rank-tree/src/skin.rs @@ -2,38 +2,89 @@ use std::sync::LazyLock; use hyperion::simulation::skin::PlayerSkin; -use crate::Rank; - -macro_rules! define_skin { - ($name:ident, $path:literal) => { - static $name: LazyLock = LazyLock::new(|| { - let skin = include_str!($path); - toml::from_str(skin).unwrap() - }); +use crate::{Rank, Team}; + +macro_rules! define_skins { + ($($rank:ident => $file:literal),* $(,)?) => { + $( + define_team_skins!($rank => $file); + )* }; } -define_skin!(STICK_SKIN, "skin/stick.toml"); -define_skin!(SWORDSMAN_SKIN, "skin/swordsman.toml"); -define_skin!(KNIGHT_SKIN, "skin/knight.toml"); -define_skin!(ARCHER_SKIN, "skin/archer.toml"); -define_skin!(MAGE_SKIN, "skin/mage.toml"); -define_skin!(BUILDER_SKIN, "skin/builder.toml"); -define_skin!(MINER_SKIN, "skin/miner.toml"); -define_skin!(EXCAVATOR_SKIN, "skin/excavator.toml"); +macro_rules! define_team_skins { + ($rank:ident => $file:literal) => { + paste::paste! { + static []: LazyLock = LazyLock::new(|| { + let skin = include_str!(concat!("skin/red/", $file, ".toml")); + toml::from_str(skin).unwrap() + }); + static []: LazyLock = LazyLock::new(|| { + let skin = include_str!(concat!("skin/blue/", $file, ".toml")); + toml::from_str(skin).unwrap() + }); + static []: LazyLock = LazyLock::new(|| { + let skin = include_str!(concat!("skin/green/", $file, ".toml")); + toml::from_str(skin).unwrap() + }); + static []: LazyLock = LazyLock::new(|| { + let skin = include_str!(concat!("skin/yellow/", $file, ".toml")); + toml::from_str(skin).unwrap() + }); + } + }; +} + +define_skins! { + // STICK_SKIN => "stick", + // SWORDSMAN_SKIN => "swordsman", + KNIGHT_SKIN => "knight", + // ARCHER_SKIN => "archer", + // MAGE_SKIN => "mage", + // BUILDER_SKIN => "builder", + // MINER_SKIN => "miner", + // EXCAVATOR_SKIN => "excavator" +} impl Rank { #[must_use] - pub fn skin(&self) -> &'static PlayerSkin { - match self { - Self::Stick => &STICK_SKIN, - Self::Sword => &SWORDSMAN_SKIN, - Self::Knight => &KNIGHT_SKIN, - Self::Archer => &ARCHER_SKIN, - Self::Mage => &MAGE_SKIN, - Self::Builder => &BUILDER_SKIN, - Self::Miner => &MINER_SKIN, - Self::Excavator => &EXCAVATOR_SKIN, + pub fn skin(&self, team: Team) -> &'static PlayerSkin { + match (team, self) { + // (Team::Red, Self::Stick) => &RED_STICK_SKIN, + // (Team::Red, Self::Sword) => &RED_SWORDSMAN_SKIN, + // (Team::Red, Self::Archer) => &RED_ARCHER_SKIN, + // (Team::Red, Self::Mage) => &RED_MAGE_SKIN, + // (Team::Red, Self::Builder) => &RED_BUILDER_SKIN, + // (Team::Red, Self::Miner) => &RED_MINER_SKIN, + // (Team::Red, Self::Excavator) => &RED_EXCAVATOR_SKIN, + + // (Team::Blue, Self::Stick) => &BLUE_STICK_SKIN, + // (Team::Blue, Self::Sword) => &BLUE_SWORDSMAN_SKIN, + (Team::Blue, Self::Knight) => &BLUE_KNIGHT_SKIN, + // (Team::Blue, Self::Archer) => &BLUE_ARCHER_SKIN, + // (Team::Blue, Self::Mage) => &BLUE_MAGE_SKIN, + // (Team::Blue, Self::Builder) => &BLUE_BUILDER_SKIN, + // (Team::Blue, Self::Miner) => &BLUE_MINER_SKIN, + // (Team::Blue, Self::Excavator) => &BLUE_EXCAVATOR_SKIN, + + // (Team::Green, Self::Stick) => &GREEN_STICK_SKIN, + // (Team::Green, Self::Sword) => &GREEN_SWORDSMAN_SKIN, + (Team::Green, Self::Knight) => &GREEN_KNIGHT_SKIN, + // (Team::Green, Self::Archer) => &GREEN_ARCHER_SKIN, + // (Team::Green, Self::Mage) => &GREEN_MAGE_SKIN, + // (Team::Green, Self::Builder) => &GREEN_BUILDER_SKIN, + // (Team::Green, Self::Miner) => &GREEN_MINER_SKIN, + // (Team::Green, Self::Excavator) => &GREEN_EXCAVATOR_SKIN, + + // (Team::Yellow, Self::Stick) => &YELLOW_STICK_SKIN, + // (Team::Yellow, Self::Sword) => &YELLOW_SWORDSMAN_SKIN, + (Team::Yellow, Self::Knight) => &YELLOW_KNIGHT_SKIN, + // (Team::Yellow, Self::Archer) => &YELLOW_ARCHER_SKIN, + // (Team::Yellow, Self::Mage) => &YELLOW_MAGE_SKIN, + // (Team::Yellow, Self::Builder) => &YELLOW_BUILDER_SKIN, + // (Team::Yellow, Self::Miner) => &YELLOW_MINER_SKIN, + // (Team::Yellow, Self::Excavator) => &YELLOW_EXCAVATOR_SKIN, + _ => &RED_KNIGHT_SKIN, // Default fallback } } } diff --git a/crates/hyperion-rank-tree/src/skin/blue/knight.toml b/crates/hyperion-rank-tree/src/skin/blue/knight.toml new file mode 100644 index 00000000..2c2a8543 --- /dev/null +++ b/crates/hyperion-rank-tree/src/skin/blue/knight.toml @@ -0,0 +1,2 @@ +textures = """ewogICJ0aW1lc3RhbXAiIDogMTczMTg2NzM0MDIzNSwKICAicHJvZmlsZUlkIiA6ICI2NGY0MGFiNzFmM2E0NGZiYjg0N2I5ZWFhOWZjNDRlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJvZGF2aWRjZXNhciIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS8yZTUyODdkYThiMjZlMWQ5NWY0NjEzNTg3MWNlMjVjNmIyZTNkMzg3Y2Y5MWIwZTFjMzAyMDFkYTkwN2MzNzA0IgogICAgfQogIH0KfQ==""" +signature = """f8oKMk3G7psCsQH4Yjipq0Qav09KNKXMV5m47B/adad+Un2t5SVvv7NlpR3P1+gzQlcyIuZjue6nSIN38UlfIWSGq1LNTYapgmIf7fx8bEUU2dCi2rmds11krt+82P+6a/40C+99qa9gbemv9yCVc+aJcAFZpbGT8PMx23kqF7vVD+dZth30p40KNJXf+CG3RyOCUiAznCLQEp3eDdjVEbisazIDSqIAmxpFZ9phPQhtoTsL/EWrSW2mdSLfn2W/dvF8n6Y7jR+v2dCBQ9PiDZ9KzlNEzsqRoWqwLhEmyIOnMkOo0GYe5s0+zkbCULXAZO4p0WNuT/5CArdKkenXjmvbmADmlaX7bT8pYYeOG6Hx1+mhiiA8ZHufxTzzJ9NyRu5q5c3P94sFSjAkG1frSRv0fRxPNg4CDnm8FySjYL33pf3rHDBaEE0oLD8wcQooJoDirea1NC+cGraT9/nnfKnkUUAxXLUYP0yNVio9R8RcgN62pkPfwevx1mUzguqH688w7elqKp/YJeyxc50aq6LiKyy9BGnAE4WZilQ3kNujC401WBqohiztLqEju95Crvxhu9yy/cO+yOsfoh405+Vc8kecY9H+hJI1M7L9BPnlcl2fV1ONykA8wPT5wdcnLdxGfIwxfBwG8EC9AUa4yYALZnlO5rbdzSZIc1xDsao=""" \ No newline at end of file diff --git a/crates/hyperion-rank-tree/src/skin/green/knight.toml b/crates/hyperion-rank-tree/src/skin/green/knight.toml new file mode 100644 index 00000000..051df34d --- /dev/null +++ b/crates/hyperion-rank-tree/src/skin/green/knight.toml @@ -0,0 +1,2 @@ +textures = """ewogICJ0aW1lc3RhbXAiIDogMTczMTg2NzQ1NjI5MiwKICAicHJvZmlsZUlkIiA6ICJiODU0NWMxMDlhZjE0ZGRjYmY4ZjhmZjg4ZTU2NzI4OSIsCiAgInByb2ZpbGVOYW1lIiA6ICJQdGFrb3B5c2tDWiIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS82YTBkNTk2NTM0NDdjODI4YzM4YjRhYTdhMDBmYzkxZThkNjVkMDRhYzI5NmJiMzkxNWU3NTMxZWYyZWU0YWEiCiAgICB9CiAgfQp9""" +signature = """XUcZkbvKDBIjTX6cUX+cTrsZWFhgqstjHDAONDoY9UK5UObH3fUTmzPIkeOwCxZ/9luX8CxtXZeLePE8ASLnRHRTeC54BkcCLiRoVEbgPjd6vcHOT4NFmzkfb4Yb3aZ2Y4L1bAEaaUNVtYqudBT5C12mMQnpNs0Os0uzk9EtEE3efLQxZHf0DdcHd+2zRa5GlRCF+wZZMkjQrwccsjhZ+myd25B0NfUrJ+ln9bWu+bDq17jpTxcnas/HMKI3v+UkN737sigY+zW8NXjuFwKoPGoK4pTHobHTxotkKg2b2tMj+GL3lqpwtXDfKMdbPMzvq3XvxDVCWvNb3zkDOBIfWogQf/m1Va6kFO6Kk8Hxh3OhDyx7WDsfVNitKmYITZ+uKAGM4dPfEb+Xqk7ZNK7YpuaD3pFf19B5dQQUhMjnUYuO14+RWMedlQL8RjFfUZcV81gtGvaAYPt2yG/sRFqF/1w0HkRRBcsyhX00o+LUSMES+6eObZw3oRo+CjRebeShZufUQI2TcR/A70WU8iwA6WKS6qkUrAJWLC/+xk/rgsnJkiIIAWzpls3Ka6l8jag57kTunSWsOy04mpIURg4tEey/rseTstC/jtNZCZ5t0Xi19MP2vgkNZHthWf5g2VCYUsw3nACtaEy8CCnKBK0+J9x+QxE/bipo3DEJtsJjD+M=""" \ No newline at end of file diff --git a/crates/hyperion-rank-tree/src/skin/red/knight.toml b/crates/hyperion-rank-tree/src/skin/red/knight.toml new file mode 100644 index 00000000..d6a1400e --- /dev/null +++ b/crates/hyperion-rank-tree/src/skin/red/knight.toml @@ -0,0 +1,2 @@ +textures = """ewogICJ0aW1lc3RhbXAiIDogMTczMTg2NzUwNTExMCwKICAicHJvZmlsZUlkIiA6ICJmZDlhMmNhNGNhMWM0YTkwYWEyODkyNThkYmM3MmFiNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJJRnJhbmtvWCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9jMzA2NjE4MDdiYjRlMTNkMGM2Y2NlZTBmOTUzMmMyM2UzNzMxYzY5MGZmMzAxYTYzNDRhNjdlNWI5Nzg4NTQ4IgogICAgfQogIH0KfQ==""" +signature = """LavI8qtb4yMnRdtmqsUyx0lVuttXj0Tv2h70JfCG2gerT9+z0F54itE97TBK/tBsM9bDtbvwSBJ3+VYohh/THyKw+2KWnzgC5BhMY80PZsbcwzF6m5bVvCDf70XsFFZIn5875pAKLdGis+6iU+XxH9KowPdmcvmprkSFi8gHEvBo2feegFJNDH6RL/9It4Fr8bHtaRq44wLZcX6wRAbZudV+74CvCAnCZLczWDpAGSF+vswK5+fYPytt5dS43i754LZS5Po1jKWel/RCXZGT41mbYEeWPUbwey/BPw0zyzFqeO1hnKRxOCGD8gCVQov/cYZ5qBDXU9Bttj/o4qFPQF9+eBmQUXJW0mrpZZiBanj4FIHMgqgi+UjAh0jcPCieAFpBa29sTKEuWrVQmUWWmsnrNx9/vYZ0zF/svoebUs86cdiXcy6sbpabnYISkRKVyuF08wl9xN6kpWXIDDIrecLz4ThPK3DDoaY2/bKdtHeJlF1D6uKBxaZlTsMjN46GmTOnu3i3Yc+/4bGIBu9h1BiQkYWVNTWn2dKJtZar+LoyQfo2p+2cYxR/aDgKl7gIelRl1KEfrUBT7UUT5NiHtfDgVlrDvOy4rjrBGcmA9mMi7YqKhTXnz7/ue2cn2fKmD5+dZa8SFpwzODO7Bf8R92s7IUPRxZ24jo49qqJQfkE=""" \ No newline at end of file diff --git a/crates/hyperion-rank-tree/src/skin/yellow/knight.toml b/crates/hyperion-rank-tree/src/skin/yellow/knight.toml new file mode 100644 index 00000000..73752aa1 --- /dev/null +++ b/crates/hyperion-rank-tree/src/skin/yellow/knight.toml @@ -0,0 +1,2 @@ +textures = """ewogICJ0aW1lc3RhbXAiIDogMTczMTg2NjU5MjgyNiwKICAicHJvZmlsZUlkIiA6ICI3ZGEyYWIzYTkzY2E0OGVlODMwNDhhZmMzYjgwZTY4ZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJHb2xkYXBmZWwiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGQ4ZDlkNGJmMzdlODk3MGIxZDFjNzEwMDQzMDAwYjg4ZGY2ZWI2ZDNjZmI3MWIyNjczYzBhYzY0YjQwOTcyZSIKICAgIH0KICB9Cn0=""" +signature = """xp/1EVb4IJ/eQNS6MvPcNzFo9gAHSn5GEiXLsEfWlJ0ks3U5pJknneZqQBpqEBDKk+Y6ymEpRP0oCsvHwk3dhH2JtbIheecA848zwrbwNSVMmKk8HFvcThU4g6qVqgX61li76DfvhVF3vse3jmFbzxCgIWwllTqsdlHR6cecftPSqoUd/n2Lr94k50aa21dEeoJgCKCLV6gbolXDBd7Q9q6+Fg3yuZxj3fMyz6GV6O+Uxf5tOttrZJQWc0iI3Jd0rwKf7Eb/kFOXVJRmXGCn75tsExtrdCm020KbgmCjNM8Yoggxem1k7P1VYsFyLLaeFNFIw0nxBG08meKsubDpMVN8As44xmOXLV1mTckUNntthMngyZZYEvi9hD3spPLsamnhq5+anuixRdK4l+TRAnJJuNrF7Wm7Wl+mYLIkoRpnez3sD+FawFs0s3dI7U5YL2L3yi2fNquAfWwQwjPjqXZP+3nNijH5/lNRnV9aRedCTeycIgEKjMr+pYaSTLCEG7zllFTZzLe0QjPakrzlBD3PdSbt5E7ibG4KNibHNmvX80ekqMNA1/YkxoKuXCok8e0IUGgFuoobLPtu5itYdkz4JkJu2deuAYaLaBNxvn7oTFh9yB8GkSM7/5etcDWIl38Zm2A/dWLZ82Cn+bF/MkIYiw8jKt+zQbk0KI6hUVo=""" \ No newline at end of file diff --git a/events/proof-of-concept/src/command/rank.rs b/events/proof-of-concept/src/command/rank.rs index a4b13e8f..65f4ebff 100644 --- a/events/proof-of-concept/src/command/rank.rs +++ b/events/proof-of-concept/src/command/rank.rs @@ -19,21 +19,21 @@ use hyperion::{ }; use hyperion_clap::MinecraftCommand; use hyperion_inventory::PlayerInventory; -use hyperion_rank_tree::Team; use hyperion_utils::EntityExt; #[derive(Parser, Debug)] #[command(name = "rank")] pub struct RankCommand { rank: hyperion_rank_tree::Rank, + team: hyperion_rank_tree::Team, } impl MinecraftCommand for RankCommand { fn execute(self, world: &World, caller: Entity) { let rank = self.rank; + let team = self.team; let msg = format!("Setting rank to {rank:?}"); let chat = agnostic::chat(msg); - let team = Team::Red; world.get::<&Compose>(|compose| { caller.entity_view(world).get::<( &NetworkStreamRef, @@ -67,7 +67,7 @@ impl MinecraftCommand for RankCommand { ) .unwrap(); - let skin = rank.skin(); + let skin = rank.skin(team); let property = Property { name: "textures".to_string(), value: skin.textures.clone(),