diff --git a/assets/dye_colors.json b/assets/dye_colors.json new file mode 100644 index 000000000..ebae74328 --- /dev/null +++ b/assets/dye_colors.json @@ -0,0 +1 @@ +[{"id":0,"name":"white","map_color_id":8,"terracotta_color_id":36,"texture_diffuse_color":16383998,"firework_color":15790320,"text_color":16777215},{"id":1,"name":"orange","map_color_id":15,"terracotta_color_id":37,"texture_diffuse_color":16351261,"firework_color":15435844,"text_color":16738335},{"id":2,"name":"magenta","map_color_id":16,"terracotta_color_id":38,"texture_diffuse_color":13061821,"firework_color":12801229,"text_color":16711935},{"id":3,"name":"light_blue","map_color_id":17,"terracotta_color_id":39,"texture_diffuse_color":3847130,"firework_color":6719955,"text_color":10141901},{"id":4,"name":"yellow","map_color_id":18,"terracotta_color_id":40,"texture_diffuse_color":16701501,"firework_color":14602026,"text_color":16776960},{"id":5,"name":"lime","map_color_id":19,"terracotta_color_id":41,"texture_diffuse_color":8439583,"firework_color":4312372,"text_color":12582656},{"id":6,"name":"pink","map_color_id":20,"terracotta_color_id":42,"texture_diffuse_color":15961002,"firework_color":14188952,"text_color":16738740},{"id":7,"name":"gray","map_color_id":21,"terracotta_color_id":43,"texture_diffuse_color":4673362,"firework_color":4408131,"text_color":8421504},{"id":8,"name":"light_gray","map_color_id":22,"terracotta_color_id":44,"texture_diffuse_color":10329495,"firework_color":11250603,"text_color":13882323},{"id":9,"name":"cyan","map_color_id":23,"terracotta_color_id":45,"texture_diffuse_color":1481884,"firework_color":2651799,"text_color":65535},{"id":10,"name":"purple","map_color_id":24,"terracotta_color_id":46,"texture_diffuse_color":8991416,"firework_color":8073150,"text_color":10494192},{"id":11,"name":"blue","map_color_id":25,"terracotta_color_id":47,"texture_diffuse_color":3949738,"firework_color":2437522,"text_color":255},{"id":12,"name":"brown","map_color_id":26,"terracotta_color_id":48,"texture_diffuse_color":8606770,"firework_color":5320730,"text_color":9127187},{"id":13,"name":"green","map_color_id":27,"terracotta_color_id":49,"texture_diffuse_color":6192150,"firework_color":3887386,"text_color":65280},{"id":14,"name":"red","map_color_id":28,"terracotta_color_id":50,"texture_diffuse_color":11546150,"firework_color":11743532,"text_color":16711680},{"id":15,"name":"black","map_color_id":29,"terracotta_color_id":51,"texture_diffuse_color":1908001,"firework_color":1973019,"text_color":0}] \ No newline at end of file diff --git a/assets/map_colors.json b/assets/map_colors.json new file mode 100644 index 000000000..c710526b7 --- /dev/null +++ b/assets/map_colors.json @@ -0,0 +1 @@ +[{"id":0,"name":"none","col":0,"hex":"#000000","rgb":[0,0,0]},{"id":1,"name":"grass","col":8368696,"hex":"#7FB238","rgb":[127,178,56]},{"id":2,"name":"sand","col":16247203,"hex":"#F7E9A3","rgb":[247,233,163]},{"id":3,"name":"wool","col":13092807,"hex":"#C7C7C7","rgb":[199,199,199]},{"id":4,"name":"fire","col":16711680,"hex":"#FF0000","rgb":[255,0,0]},{"id":5,"name":"ice","col":10526975,"hex":"#A0A0FF","rgb":[160,160,255]},{"id":6,"name":"metal","col":10987431,"hex":"#A7A7A7","rgb":[167,167,167]},{"id":7,"name":"plant","col":31744,"hex":"#007C00","rgb":[0,124,0]},{"id":8,"name":"snow","col":16777215,"hex":"#FFFFFF","rgb":[255,255,255]},{"id":9,"name":"clay","col":10791096,"hex":"#A4A8B8","rgb":[164,168,184]},{"id":10,"name":"dirt","col":9923917,"hex":"#976D4D","rgb":[151,109,77]},{"id":11,"name":"stone","col":7368816,"hex":"#707070","rgb":[112,112,112]},{"id":12,"name":"water","col":4210943,"hex":"#4040FF","rgb":[64,64,255]},{"id":13,"name":"wood","col":9402184,"hex":"#8F7748","rgb":[143,119,72]},{"id":14,"name":"quartz","col":16776437,"hex":"#FFFCF5","rgb":[255,252,245]},{"id":15,"name":"color_orange","col":14188339,"hex":"#D87F33","rgb":[216,127,51]},{"id":16,"name":"color_magenta","col":11685080,"hex":"#B24CD8","rgb":[178,76,216]},{"id":17,"name":"color_light_blue","col":6724056,"hex":"#6699D8","rgb":[102,153,216]},{"id":18,"name":"color_yellow","col":15066419,"hex":"#E5E533","rgb":[229,229,51]},{"id":19,"name":"color_light_green","col":8375321,"hex":"#7FCC19","rgb":[127,204,25]},{"id":20,"name":"color_pink","col":15892389,"hex":"#F27FA5","rgb":[242,127,165]},{"id":21,"name":"color_gray","col":5000268,"hex":"#4C4C4C","rgb":[76,76,76]},{"id":22,"name":"color_light_gray","col":10066329,"hex":"#999999","rgb":[153,153,153]},{"id":23,"name":"color_cyan","col":5013401,"hex":"#4C7F99","rgb":[76,127,153]},{"id":24,"name":"color_purple","col":8339378,"hex":"#7F3FB2","rgb":[127,63,178]},{"id":25,"name":"color_blue","col":3361970,"hex":"#334CB2","rgb":[51,76,178]},{"id":26,"name":"color_brown","col":6704179,"hex":"#664C33","rgb":[102,76,51]},{"id":27,"name":"color_green","col":6717235,"hex":"#667F33","rgb":[102,127,51]},{"id":28,"name":"color_red","col":10040115,"hex":"#993333","rgb":[153,51,51]},{"id":29,"name":"color_black","col":1644825,"hex":"#191919","rgb":[25,25,25]},{"id":30,"name":"gold","col":16445005,"hex":"#FAEE4D","rgb":[250,238,77]},{"id":31,"name":"diamond","col":6085589,"hex":"#5CDBD5","rgb":[92,219,213]},{"id":32,"name":"lapis","col":4882687,"hex":"#4A80FF","rgb":[74,128,255]},{"id":33,"name":"emerald","col":55610,"hex":"#00D93A","rgb":[0,217,58]},{"id":34,"name":"podzol","col":8476209,"hex":"#815631","rgb":[129,86,49]},{"id":35,"name":"nether","col":7340544,"hex":"#700200","rgb":[112,2,0]},{"id":36,"name":"terracotta_white","col":13742497,"hex":"#D1B1A1","rgb":[209,177,161]},{"id":37,"name":"terracotta_orange","col":10441252,"hex":"#9F5224","rgb":[159,82,36]},{"id":38,"name":"terracotta_magenta","col":9787244,"hex":"#95576C","rgb":[149,87,108]},{"id":39,"name":"terracotta_light_blue","col":7367818,"hex":"#706C8A","rgb":[112,108,138]},{"id":40,"name":"terracotta_yellow","col":12223780,"hex":"#BA8524","rgb":[186,133,36]},{"id":41,"name":"terracotta_light_green","col":6780213,"hex":"#677535","rgb":[103,117,53]},{"id":42,"name":"terracotta_pink","col":10505550,"hex":"#A04D4E","rgb":[160,77,78]},{"id":43,"name":"terracotta_gray","col":3746083,"hex":"#392923","rgb":[57,41,35]},{"id":44,"name":"terracotta_light_gray","col":8874850,"hex":"#876B62","rgb":[135,107,98]},{"id":45,"name":"terracotta_cyan","col":5725276,"hex":"#575C5C","rgb":[87,92,92]},{"id":46,"name":"terracotta_purple","col":8014168,"hex":"#7A4958","rgb":[122,73,88]},{"id":47,"name":"terracotta_blue","col":4996700,"hex":"#4C3E5C","rgb":[76,62,92]},{"id":48,"name":"terracotta_brown","col":4993571,"hex":"#4C3223","rgb":[76,50,35]},{"id":49,"name":"terracotta_green","col":5001770,"hex":"#4C522A","rgb":[76,82,42]},{"id":50,"name":"terracotta_red","col":9321518,"hex":"#8E3C2E","rgb":[142,60,46]},{"id":51,"name":"terracotta_black","col":2430480,"hex":"#251610","rgb":[37,22,16]},{"id":52,"name":"crimson_nylium","col":12398641,"hex":"#BD3031","rgb":[189,48,49]},{"id":53,"name":"crimson_stem","col":9715553,"hex":"#943F61","rgb":[148,63,97]},{"id":54,"name":"crimson_hyphae","col":6035741,"hex":"#5C191D","rgb":[92,25,29]},{"id":55,"name":"warped_nylium","col":1474182,"hex":"#167E86","rgb":[22,126,134]},{"id":56,"name":"warped_stem","col":3837580,"hex":"#3A8E8C","rgb":[58,142,140]},{"id":57,"name":"warped_hyphae","col":5647422,"hex":"#562C3E","rgb":[86,44,62]},{"id":58,"name":"warped_wart_block","col":1356933,"hex":"#14B485","rgb":[20,180,133]},{"id":59,"name":"deepslate","col":6579300,"hex":"#646464","rgb":[100,100,100]},{"id":60,"name":"raw_iron","col":14200723,"hex":"#D8AF93","rgb":[216,175,147]},{"id":61,"name":"glow_lichen","col":8365974,"hex":"#7FA796","rgb":[127,167,150]},{"id":62,"name":"none","col":0,"hex":"#000000","rgb":[0,0,0]},{"id":63,"name":"none","col":0,"hex":"#000000","rgb":[0,0,0]}] \ No newline at end of file diff --git a/assets/map_decorations.json b/assets/map_decorations.json new file mode 100644 index 000000000..04eda4109 --- /dev/null +++ b/assets/map_decorations.json @@ -0,0 +1 @@ +[{"name":"player","asset_name":"player","show_on_item_frame":false,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"frame","asset_name":"frame","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"red_marker","asset_name":"red_marker","show_on_item_frame":false,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"blue_marker","asset_name":"blue_marker","show_on_item_frame":false,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"target_x","asset_name":"target_x","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":false},{"name":"target_point","asset_name":"target_point","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":false},{"name":"player_off_map","asset_name":"player_off_map","show_on_item_frame":false,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"player_off_limits","asset_name":"player_off_limits","show_on_item_frame":false,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"mansion","asset_name":"woodland_mansion","show_on_item_frame":true,"map_color":5393476,"exploration_map_element":true,"track_count":false},{"name":"monument","asset_name":"ocean_monument","show_on_item_frame":true,"map_color":3830373,"exploration_map_element":true,"track_count":false},{"name":"banner_white","asset_name":"white_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_orange","asset_name":"orange_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_magenta","asset_name":"magenta_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_light_blue","asset_name":"light_blue_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_yellow","asset_name":"yellow_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_lime","asset_name":"lime_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_pink","asset_name":"pink_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_gray","asset_name":"gray_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_light_gray","asset_name":"light_gray_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_cyan","asset_name":"cyan_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_purple","asset_name":"purple_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_blue","asset_name":"blue_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_brown","asset_name":"brown_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_green","asset_name":"green_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_red","asset_name":"red_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"banner_black","asset_name":"black_banner","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":true},{"name":"red_x","asset_name":"red_x","show_on_item_frame":true,"map_color":-1,"exploration_map_element":false,"track_count":false},{"name":"village_desert","asset_name":"desert_village","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"village_plains","asset_name":"plains_village","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"village_savanna","asset_name":"savanna_village","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"village_snowy","asset_name":"snowy_village","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"village_taiga","asset_name":"taiga_village","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"jungle_temple","asset_name":"jungle_temple","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"swamp_hut","asset_name":"swamp_hut","show_on_item_frame":true,"map_color":10066329,"exploration_map_element":true,"track_count":false},{"name":"trial_chambers","asset_name":"trial_chambers","show_on_item_frame":true,"map_color":12741452,"exploration_map_element":true,"track_count":false}] \ No newline at end of file diff --git a/crates/pumpkin-data/Cargo.toml b/crates/pumpkin-data/Cargo.toml index 254e346be..ce9f0b243 100644 --- a/crates/pumpkin-data/Cargo.toml +++ b/crates/pumpkin-data/Cargo.toml @@ -49,9 +49,15 @@ default = [ "configured_feature", "slot_ranges", "bedrock_creative", - "bedrock_biome" + "bedrock_biome", + "map_color", + "map_decoration", + "dye_color" ] +map_color = [] +map_decoration = [] +dye_color = [] advancement = [] bedrock_creative = [] bedrock_biome = [] diff --git a/crates/pumpkin-data/src/generated/dye_color.rs b/crates/pumpkin-data/src/generated/dye_color.rs new file mode 100644 index 000000000..e82870e01 --- /dev/null +++ b/crates/pumpkin-data/src/generated/dye_color.rs @@ -0,0 +1,214 @@ +/* This file is generated. Do not edit manually. */ +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub enum DyeColor { + White, + Orange, + Magenta, + LightBlue, + Yellow, + Lime, + Pink, + Gray, + LightGray, + Cyan, + Purple, + Blue, + Brown, + Green, + Red, + #[default] + Black, +} +impl DyeColor { + #[must_use] + pub const fn id(&self) -> u8 { + match self { + Self::White => 0u8, + Self::Orange => 1u8, + Self::Magenta => 2u8, + Self::LightBlue => 3u8, + Self::Yellow => 4u8, + Self::Lime => 5u8, + Self::Pink => 6u8, + Self::Gray => 7u8, + Self::LightGray => 8u8, + Self::Cyan => 9u8, + Self::Purple => 10u8, + Self::Blue => 11u8, + Self::Brown => 12u8, + Self::Green => 13u8, + Self::Red => 14u8, + Self::Black => 15u8, + } + } + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::White => "white", + Self::Orange => "orange", + Self::Magenta => "magenta", + Self::LightBlue => "light_blue", + Self::Yellow => "yellow", + Self::Lime => "lime", + Self::Pink => "pink", + Self::Gray => "gray", + Self::LightGray => "light_gray", + Self::Cyan => "cyan", + Self::Purple => "purple", + Self::Blue => "blue", + Self::Brown => "brown", + Self::Green => "green", + Self::Red => "red", + Self::Black => "black", + } + } + #[must_use] + pub const fn map_color_id(&self) -> u8 { + match self { + Self::White => 8u8, + Self::Orange => 15u8, + Self::Magenta => 16u8, + Self::LightBlue => 17u8, + Self::Yellow => 18u8, + Self::Lime => 19u8, + Self::Pink => 20u8, + Self::Gray => 21u8, + Self::LightGray => 22u8, + Self::Cyan => 23u8, + Self::Purple => 24u8, + Self::Blue => 25u8, + Self::Brown => 26u8, + Self::Green => 27u8, + Self::Red => 28u8, + Self::Black => 29u8, + } + } + #[must_use] + pub const fn terracotta_color_id(&self) -> u8 { + match self { + Self::White => 36u8, + Self::Orange => 37u8, + Self::Magenta => 38u8, + Self::LightBlue => 39u8, + Self::Yellow => 40u8, + Self::Lime => 41u8, + Self::Pink => 42u8, + Self::Gray => 43u8, + Self::LightGray => 44u8, + Self::Cyan => 45u8, + Self::Purple => 46u8, + Self::Blue => 47u8, + Self::Brown => 48u8, + Self::Green => 49u8, + Self::Red => 50u8, + Self::Black => 51u8, + } + } + #[must_use] + pub const fn texture_diffuse_color(&self) -> u32 { + match self { + Self::White => 16383998u32, + Self::Orange => 16351261u32, + Self::Magenta => 13061821u32, + Self::LightBlue => 3847130u32, + Self::Yellow => 16701501u32, + Self::Lime => 8439583u32, + Self::Pink => 15961002u32, + Self::Gray => 4673362u32, + Self::LightGray => 10329495u32, + Self::Cyan => 1481884u32, + Self::Purple => 8991416u32, + Self::Blue => 3949738u32, + Self::Brown => 8606770u32, + Self::Green => 6192150u32, + Self::Red => 11546150u32, + Self::Black => 1908001u32, + } + } + #[must_use] + pub const fn firework_color(&self) -> u32 { + match self { + Self::White => 15790320u32, + Self::Orange => 15435844u32, + Self::Magenta => 12801229u32, + Self::LightBlue => 6719955u32, + Self::Yellow => 14602026u32, + Self::Lime => 4312372u32, + Self::Pink => 14188952u32, + Self::Gray => 4408131u32, + Self::LightGray => 11250603u32, + Self::Cyan => 2651799u32, + Self::Purple => 8073150u32, + Self::Blue => 2437522u32, + Self::Brown => 5320730u32, + Self::Green => 3887386u32, + Self::Red => 11743532u32, + Self::Black => 1973019u32, + } + } + #[must_use] + pub const fn text_color(&self) -> u32 { + match self { + Self::White => 16777215u32, + Self::Orange => 16738335u32, + Self::Magenta => 16711935u32, + Self::LightBlue => 10141901u32, + Self::Yellow => 16776960u32, + Self::Lime => 12582656u32, + Self::Pink => 16738740u32, + Self::Gray => 8421504u32, + Self::LightGray => 13882323u32, + Self::Cyan => 65535u32, + Self::Purple => 10494192u32, + Self::Blue => 255u32, + Self::Brown => 9127187u32, + Self::Green => 65280u32, + Self::Red => 16711680u32, + Self::Black => 0u32, + } + } + #[must_use] + pub const fn by_id(id: u8) -> Option { + match id { + 0u8 => Some(Self::White), + 1u8 => Some(Self::Orange), + 2u8 => Some(Self::Magenta), + 3u8 => Some(Self::LightBlue), + 4u8 => Some(Self::Yellow), + 5u8 => Some(Self::Lime), + 6u8 => Some(Self::Pink), + 7u8 => Some(Self::Gray), + 8u8 => Some(Self::LightGray), + 9u8 => Some(Self::Cyan), + 10u8 => Some(Self::Purple), + 11u8 => Some(Self::Blue), + 12u8 => Some(Self::Brown), + 13u8 => Some(Self::Green), + 14u8 => Some(Self::Red), + 15u8 => Some(Self::Black), + _ => None, + } + } + #[must_use] + pub fn by_name(name: &str) -> Option { + match name { + "white" => Some(Self::White), + "orange" => Some(Self::Orange), + "magenta" => Some(Self::Magenta), + "light_blue" => Some(Self::LightBlue), + "yellow" => Some(Self::Yellow), + "lime" => Some(Self::Lime), + "pink" => Some(Self::Pink), + "gray" => Some(Self::Gray), + "light_gray" => Some(Self::LightGray), + "cyan" => Some(Self::Cyan), + "purple" => Some(Self::Purple), + "blue" => Some(Self::Blue), + "brown" => Some(Self::Brown), + "green" => Some(Self::Green), + "red" => Some(Self::Red), + "black" => Some(Self::Black), + _ => None, + } + } +} diff --git a/crates/pumpkin-data/src/generated/map_color.rs b/crates/pumpkin-data/src/generated/map_color.rs new file mode 100644 index 000000000..8db00859c --- /dev/null +++ b/crates/pumpkin-data/src/generated/map_color.rs @@ -0,0 +1,536 @@ +/* This file is generated. Do not edit manually. */ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MapColor { + pub id: u8, + pub name: &'static str, + pub col: u32, + pub rgb: (u8, u8, u8), +} +impl MapColor { + pub const NONE_0: MapColor = MapColor { + id: 0u8, + name: "none", + col: 0u32, + rgb: (0u8, 0u8, 0u8), + }; + pub const GRASS: MapColor = MapColor { + id: 1u8, + name: "grass", + col: 8368696u32, + rgb: (127u8, 178u8, 56u8), + }; + pub const SAND: MapColor = MapColor { + id: 2u8, + name: "sand", + col: 16247203u32, + rgb: (247u8, 233u8, 163u8), + }; + pub const WOOL: MapColor = MapColor { + id: 3u8, + name: "wool", + col: 13092807u32, + rgb: (199u8, 199u8, 199u8), + }; + pub const FIRE: MapColor = MapColor { + id: 4u8, + name: "fire", + col: 16711680u32, + rgb: (255u8, 0u8, 0u8), + }; + pub const ICE: MapColor = MapColor { + id: 5u8, + name: "ice", + col: 10526975u32, + rgb: (160u8, 160u8, 255u8), + }; + pub const METAL: MapColor = MapColor { + id: 6u8, + name: "metal", + col: 10987431u32, + rgb: (167u8, 167u8, 167u8), + }; + pub const PLANT: MapColor = MapColor { + id: 7u8, + name: "plant", + col: 31744u32, + rgb: (0u8, 124u8, 0u8), + }; + pub const SNOW: MapColor = MapColor { + id: 8u8, + name: "snow", + col: 16777215u32, + rgb: (255u8, 255u8, 255u8), + }; + pub const CLAY: MapColor = MapColor { + id: 9u8, + name: "clay", + col: 10791096u32, + rgb: (164u8, 168u8, 184u8), + }; + pub const DIRT: MapColor = MapColor { + id: 10u8, + name: "dirt", + col: 9923917u32, + rgb: (151u8, 109u8, 77u8), + }; + pub const STONE: MapColor = MapColor { + id: 11u8, + name: "stone", + col: 7368816u32, + rgb: (112u8, 112u8, 112u8), + }; + pub const WATER: MapColor = MapColor { + id: 12u8, + name: "water", + col: 4210943u32, + rgb: (64u8, 64u8, 255u8), + }; + pub const WOOD: MapColor = MapColor { + id: 13u8, + name: "wood", + col: 9402184u32, + rgb: (143u8, 119u8, 72u8), + }; + pub const QUARTZ: MapColor = MapColor { + id: 14u8, + name: "quartz", + col: 16776437u32, + rgb: (255u8, 252u8, 245u8), + }; + pub const COLOR_ORANGE: MapColor = MapColor { + id: 15u8, + name: "color_orange", + col: 14188339u32, + rgb: (216u8, 127u8, 51u8), + }; + pub const COLOR_MAGENTA: MapColor = MapColor { + id: 16u8, + name: "color_magenta", + col: 11685080u32, + rgb: (178u8, 76u8, 216u8), + }; + pub const COLOR_LIGHT_BLUE: MapColor = MapColor { + id: 17u8, + name: "color_light_blue", + col: 6724056u32, + rgb: (102u8, 153u8, 216u8), + }; + pub const COLOR_YELLOW: MapColor = MapColor { + id: 18u8, + name: "color_yellow", + col: 15066419u32, + rgb: (229u8, 229u8, 51u8), + }; + pub const COLOR_LIGHT_GREEN: MapColor = MapColor { + id: 19u8, + name: "color_light_green", + col: 8375321u32, + rgb: (127u8, 204u8, 25u8), + }; + pub const COLOR_PINK: MapColor = MapColor { + id: 20u8, + name: "color_pink", + col: 15892389u32, + rgb: (242u8, 127u8, 165u8), + }; + pub const COLOR_GRAY: MapColor = MapColor { + id: 21u8, + name: "color_gray", + col: 5000268u32, + rgb: (76u8, 76u8, 76u8), + }; + pub const COLOR_LIGHT_GRAY: MapColor = MapColor { + id: 22u8, + name: "color_light_gray", + col: 10066329u32, + rgb: (153u8, 153u8, 153u8), + }; + pub const COLOR_CYAN: MapColor = MapColor { + id: 23u8, + name: "color_cyan", + col: 5013401u32, + rgb: (76u8, 127u8, 153u8), + }; + pub const COLOR_PURPLE: MapColor = MapColor { + id: 24u8, + name: "color_purple", + col: 8339378u32, + rgb: (127u8, 63u8, 178u8), + }; + pub const COLOR_BLUE: MapColor = MapColor { + id: 25u8, + name: "color_blue", + col: 3361970u32, + rgb: (51u8, 76u8, 178u8), + }; + pub const COLOR_BROWN: MapColor = MapColor { + id: 26u8, + name: "color_brown", + col: 6704179u32, + rgb: (102u8, 76u8, 51u8), + }; + pub const COLOR_GREEN: MapColor = MapColor { + id: 27u8, + name: "color_green", + col: 6717235u32, + rgb: (102u8, 127u8, 51u8), + }; + pub const COLOR_RED: MapColor = MapColor { + id: 28u8, + name: "color_red", + col: 10040115u32, + rgb: (153u8, 51u8, 51u8), + }; + pub const COLOR_BLACK: MapColor = MapColor { + id: 29u8, + name: "color_black", + col: 1644825u32, + rgb: (25u8, 25u8, 25u8), + }; + pub const GOLD: MapColor = MapColor { + id: 30u8, + name: "gold", + col: 16445005u32, + rgb: (250u8, 238u8, 77u8), + }; + pub const DIAMOND: MapColor = MapColor { + id: 31u8, + name: "diamond", + col: 6085589u32, + rgb: (92u8, 219u8, 213u8), + }; + pub const LAPIS: MapColor = MapColor { + id: 32u8, + name: "lapis", + col: 4882687u32, + rgb: (74u8, 128u8, 255u8), + }; + pub const EMERALD: MapColor = MapColor { + id: 33u8, + name: "emerald", + col: 55610u32, + rgb: (0u8, 217u8, 58u8), + }; + pub const PODZOL: MapColor = MapColor { + id: 34u8, + name: "podzol", + col: 8476209u32, + rgb: (129u8, 86u8, 49u8), + }; + pub const NETHER: MapColor = MapColor { + id: 35u8, + name: "nether", + col: 7340544u32, + rgb: (112u8, 2u8, 0u8), + }; + pub const TERRACOTTA_WHITE: MapColor = MapColor { + id: 36u8, + name: "terracotta_white", + col: 13742497u32, + rgb: (209u8, 177u8, 161u8), + }; + pub const TERRACOTTA_ORANGE: MapColor = MapColor { + id: 37u8, + name: "terracotta_orange", + col: 10441252u32, + rgb: (159u8, 82u8, 36u8), + }; + pub const TERRACOTTA_MAGENTA: MapColor = MapColor { + id: 38u8, + name: "terracotta_magenta", + col: 9787244u32, + rgb: (149u8, 87u8, 108u8), + }; + pub const TERRACOTTA_LIGHT_BLUE: MapColor = MapColor { + id: 39u8, + name: "terracotta_light_blue", + col: 7367818u32, + rgb: (112u8, 108u8, 138u8), + }; + pub const TERRACOTTA_YELLOW: MapColor = MapColor { + id: 40u8, + name: "terracotta_yellow", + col: 12223780u32, + rgb: (186u8, 133u8, 36u8), + }; + pub const TERRACOTTA_LIGHT_GREEN: MapColor = MapColor { + id: 41u8, + name: "terracotta_light_green", + col: 6780213u32, + rgb: (103u8, 117u8, 53u8), + }; + pub const TERRACOTTA_PINK: MapColor = MapColor { + id: 42u8, + name: "terracotta_pink", + col: 10505550u32, + rgb: (160u8, 77u8, 78u8), + }; + pub const TERRACOTTA_GRAY: MapColor = MapColor { + id: 43u8, + name: "terracotta_gray", + col: 3746083u32, + rgb: (57u8, 41u8, 35u8), + }; + pub const TERRACOTTA_LIGHT_GRAY: MapColor = MapColor { + id: 44u8, + name: "terracotta_light_gray", + col: 8874850u32, + rgb: (135u8, 107u8, 98u8), + }; + pub const TERRACOTTA_CYAN: MapColor = MapColor { + id: 45u8, + name: "terracotta_cyan", + col: 5725276u32, + rgb: (87u8, 92u8, 92u8), + }; + pub const TERRACOTTA_PURPLE: MapColor = MapColor { + id: 46u8, + name: "terracotta_purple", + col: 8014168u32, + rgb: (122u8, 73u8, 88u8), + }; + pub const TERRACOTTA_BLUE: MapColor = MapColor { + id: 47u8, + name: "terracotta_blue", + col: 4996700u32, + rgb: (76u8, 62u8, 92u8), + }; + pub const TERRACOTTA_BROWN: MapColor = MapColor { + id: 48u8, + name: "terracotta_brown", + col: 4993571u32, + rgb: (76u8, 50u8, 35u8), + }; + pub const TERRACOTTA_GREEN: MapColor = MapColor { + id: 49u8, + name: "terracotta_green", + col: 5001770u32, + rgb: (76u8, 82u8, 42u8), + }; + pub const TERRACOTTA_RED: MapColor = MapColor { + id: 50u8, + name: "terracotta_red", + col: 9321518u32, + rgb: (142u8, 60u8, 46u8), + }; + pub const TERRACOTTA_BLACK: MapColor = MapColor { + id: 51u8, + name: "terracotta_black", + col: 2430480u32, + rgb: (37u8, 22u8, 16u8), + }; + pub const CRIMSON_NYLIUM: MapColor = MapColor { + id: 52u8, + name: "crimson_nylium", + col: 12398641u32, + rgb: (189u8, 48u8, 49u8), + }; + pub const CRIMSON_STEM: MapColor = MapColor { + id: 53u8, + name: "crimson_stem", + col: 9715553u32, + rgb: (148u8, 63u8, 97u8), + }; + pub const CRIMSON_HYPHAE: MapColor = MapColor { + id: 54u8, + name: "crimson_hyphae", + col: 6035741u32, + rgb: (92u8, 25u8, 29u8), + }; + pub const WARPED_NYLIUM: MapColor = MapColor { + id: 55u8, + name: "warped_nylium", + col: 1474182u32, + rgb: (22u8, 126u8, 134u8), + }; + pub const WARPED_STEM: MapColor = MapColor { + id: 56u8, + name: "warped_stem", + col: 3837580u32, + rgb: (58u8, 142u8, 140u8), + }; + pub const WARPED_HYPHAE: MapColor = MapColor { + id: 57u8, + name: "warped_hyphae", + col: 5647422u32, + rgb: (86u8, 44u8, 62u8), + }; + pub const WARPED_WART_BLOCK: MapColor = MapColor { + id: 58u8, + name: "warped_wart_block", + col: 1356933u32, + rgb: (20u8, 180u8, 133u8), + }; + pub const DEEPSLATE: MapColor = MapColor { + id: 59u8, + name: "deepslate", + col: 6579300u32, + rgb: (100u8, 100u8, 100u8), + }; + pub const RAW_IRON: MapColor = MapColor { + id: 60u8, + name: "raw_iron", + col: 14200723u32, + rgb: (216u8, 175u8, 147u8), + }; + pub const GLOW_LICHEN: MapColor = MapColor { + id: 61u8, + name: "glow_lichen", + col: 8365974u32, + rgb: (127u8, 167u8, 150u8), + }; + pub const NONE_62: MapColor = MapColor { + id: 62u8, + name: "none", + col: 0u32, + rgb: (0u8, 0u8, 0u8), + }; + pub const NONE_63: MapColor = MapColor { + id: 63u8, + name: "none", + col: 0u32, + rgb: (0u8, 0u8, 0u8), + }; + pub const ALL: &'static [MapColor] = &[ + MapColor::NONE_0, + MapColor::GRASS, + MapColor::SAND, + MapColor::WOOL, + MapColor::FIRE, + MapColor::ICE, + MapColor::METAL, + MapColor::PLANT, + MapColor::SNOW, + MapColor::CLAY, + MapColor::DIRT, + MapColor::STONE, + MapColor::WATER, + MapColor::WOOD, + MapColor::QUARTZ, + MapColor::COLOR_ORANGE, + MapColor::COLOR_MAGENTA, + MapColor::COLOR_LIGHT_BLUE, + MapColor::COLOR_YELLOW, + MapColor::COLOR_LIGHT_GREEN, + MapColor::COLOR_PINK, + MapColor::COLOR_GRAY, + MapColor::COLOR_LIGHT_GRAY, + MapColor::COLOR_CYAN, + MapColor::COLOR_PURPLE, + MapColor::COLOR_BLUE, + MapColor::COLOR_BROWN, + MapColor::COLOR_GREEN, + MapColor::COLOR_RED, + MapColor::COLOR_BLACK, + MapColor::GOLD, + MapColor::DIAMOND, + MapColor::LAPIS, + MapColor::EMERALD, + MapColor::PODZOL, + MapColor::NETHER, + MapColor::TERRACOTTA_WHITE, + MapColor::TERRACOTTA_ORANGE, + MapColor::TERRACOTTA_MAGENTA, + MapColor::TERRACOTTA_LIGHT_BLUE, + MapColor::TERRACOTTA_YELLOW, + MapColor::TERRACOTTA_LIGHT_GREEN, + MapColor::TERRACOTTA_PINK, + MapColor::TERRACOTTA_GRAY, + MapColor::TERRACOTTA_LIGHT_GRAY, + MapColor::TERRACOTTA_CYAN, + MapColor::TERRACOTTA_PURPLE, + MapColor::TERRACOTTA_BLUE, + MapColor::TERRACOTTA_BROWN, + MapColor::TERRACOTTA_GREEN, + MapColor::TERRACOTTA_RED, + MapColor::TERRACOTTA_BLACK, + MapColor::CRIMSON_NYLIUM, + MapColor::CRIMSON_STEM, + MapColor::CRIMSON_HYPHAE, + MapColor::WARPED_NYLIUM, + MapColor::WARPED_STEM, + MapColor::WARPED_HYPHAE, + MapColor::WARPED_WART_BLOCK, + MapColor::DEEPSLATE, + MapColor::RAW_IRON, + MapColor::GLOW_LICHEN, + MapColor::NONE_62, + MapColor::NONE_63, + ]; + #[must_use] + pub const fn from_id(id: u8) -> Option<&'static MapColor> { + if (id as usize) < Self::ALL.len() { + Some(&Self::ALL[id as usize]) + } else { + None + } + } + #[must_use] + pub fn from_name(name: &str) -> Option<&'static MapColor> { + match name { + "none" => Some(&Self::NONE_0), + "grass" => Some(&Self::GRASS), + "sand" => Some(&Self::SAND), + "wool" => Some(&Self::WOOL), + "fire" => Some(&Self::FIRE), + "ice" => Some(&Self::ICE), + "metal" => Some(&Self::METAL), + "plant" => Some(&Self::PLANT), + "snow" => Some(&Self::SNOW), + "clay" => Some(&Self::CLAY), + "dirt" => Some(&Self::DIRT), + "stone" => Some(&Self::STONE), + "water" => Some(&Self::WATER), + "wood" => Some(&Self::WOOD), + "quartz" => Some(&Self::QUARTZ), + "color_orange" => Some(&Self::COLOR_ORANGE), + "color_magenta" => Some(&Self::COLOR_MAGENTA), + "color_light_blue" => Some(&Self::COLOR_LIGHT_BLUE), + "color_yellow" => Some(&Self::COLOR_YELLOW), + "color_light_green" => Some(&Self::COLOR_LIGHT_GREEN), + "color_pink" => Some(&Self::COLOR_PINK), + "color_gray" => Some(&Self::COLOR_GRAY), + "color_light_gray" => Some(&Self::COLOR_LIGHT_GRAY), + "color_cyan" => Some(&Self::COLOR_CYAN), + "color_purple" => Some(&Self::COLOR_PURPLE), + "color_blue" => Some(&Self::COLOR_BLUE), + "color_brown" => Some(&Self::COLOR_BROWN), + "color_green" => Some(&Self::COLOR_GREEN), + "color_red" => Some(&Self::COLOR_RED), + "color_black" => Some(&Self::COLOR_BLACK), + "gold" => Some(&Self::GOLD), + "diamond" => Some(&Self::DIAMOND), + "lapis" => Some(&Self::LAPIS), + "emerald" => Some(&Self::EMERALD), + "podzol" => Some(&Self::PODZOL), + "nether" => Some(&Self::NETHER), + "terracotta_white" => Some(&Self::TERRACOTTA_WHITE), + "terracotta_orange" => Some(&Self::TERRACOTTA_ORANGE), + "terracotta_magenta" => Some(&Self::TERRACOTTA_MAGENTA), + "terracotta_light_blue" => Some(&Self::TERRACOTTA_LIGHT_BLUE), + "terracotta_yellow" => Some(&Self::TERRACOTTA_YELLOW), + "terracotta_light_green" => Some(&Self::TERRACOTTA_LIGHT_GREEN), + "terracotta_pink" => Some(&Self::TERRACOTTA_PINK), + "terracotta_gray" => Some(&Self::TERRACOTTA_GRAY), + "terracotta_light_gray" => Some(&Self::TERRACOTTA_LIGHT_GRAY), + "terracotta_cyan" => Some(&Self::TERRACOTTA_CYAN), + "terracotta_purple" => Some(&Self::TERRACOTTA_PURPLE), + "terracotta_blue" => Some(&Self::TERRACOTTA_BLUE), + "terracotta_brown" => Some(&Self::TERRACOTTA_BROWN), + "terracotta_green" => Some(&Self::TERRACOTTA_GREEN), + "terracotta_red" => Some(&Self::TERRACOTTA_RED), + "terracotta_black" => Some(&Self::TERRACOTTA_BLACK), + "crimson_nylium" => Some(&Self::CRIMSON_NYLIUM), + "crimson_stem" => Some(&Self::CRIMSON_STEM), + "crimson_hyphae" => Some(&Self::CRIMSON_HYPHAE), + "warped_nylium" => Some(&Self::WARPED_NYLIUM), + "warped_stem" => Some(&Self::WARPED_STEM), + "warped_hyphae" => Some(&Self::WARPED_HYPHAE), + "warped_wart_block" => Some(&Self::WARPED_WART_BLOCK), + "deepslate" => Some(&Self::DEEPSLATE), + "raw_iron" => Some(&Self::RAW_IRON), + "glow_lichen" => Some(&Self::GLOW_LICHEN), + _ => None, + } + } +} diff --git a/crates/pumpkin-data/src/generated/map_decoration.rs b/crates/pumpkin-data/src/generated/map_decoration.rs new file mode 100644 index 000000000..92d40c304 --- /dev/null +++ b/crates/pumpkin-data/src/generated/map_decoration.rs @@ -0,0 +1,414 @@ +/* This file is generated. Do not edit manually. */ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MapDecorationType { + pub id: u32, + pub name: &'static str, + pub asset_name: &'static str, + pub show_on_item_frame: bool, + pub map_color: i32, + pub exploration_map_element: bool, + pub track_count: bool, +} +impl MapDecorationType { + pub const PLAYER: MapDecorationType = MapDecorationType { + id: 0u32, + name: "player", + asset_name: "player", + show_on_item_frame: false, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const FRAME: MapDecorationType = MapDecorationType { + id: 1u32, + name: "frame", + asset_name: "frame", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const RED_MARKER: MapDecorationType = MapDecorationType { + id: 2u32, + name: "red_marker", + asset_name: "red_marker", + show_on_item_frame: false, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BLUE_MARKER: MapDecorationType = MapDecorationType { + id: 3u32, + name: "blue_marker", + asset_name: "blue_marker", + show_on_item_frame: false, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const TARGET_X: MapDecorationType = MapDecorationType { + id: 4u32, + name: "target_x", + asset_name: "target_x", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: false, + }; + pub const TARGET_POINT: MapDecorationType = MapDecorationType { + id: 5u32, + name: "target_point", + asset_name: "target_point", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: false, + }; + pub const PLAYER_OFF_MAP: MapDecorationType = MapDecorationType { + id: 6u32, + name: "player_off_map", + asset_name: "player_off_map", + show_on_item_frame: false, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const PLAYER_OFF_LIMITS: MapDecorationType = MapDecorationType { + id: 7u32, + name: "player_off_limits", + asset_name: "player_off_limits", + show_on_item_frame: false, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const MANSION: MapDecorationType = MapDecorationType { + id: 8u32, + name: "mansion", + asset_name: "woodland_mansion", + show_on_item_frame: true, + map_color: 5393476i32, + exploration_map_element: true, + track_count: false, + }; + pub const MONUMENT: MapDecorationType = MapDecorationType { + id: 9u32, + name: "monument", + asset_name: "ocean_monument", + show_on_item_frame: true, + map_color: 3830373i32, + exploration_map_element: true, + track_count: false, + }; + pub const BANNER_WHITE: MapDecorationType = MapDecorationType { + id: 10u32, + name: "banner_white", + asset_name: "white_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_ORANGE: MapDecorationType = MapDecorationType { + id: 11u32, + name: "banner_orange", + asset_name: "orange_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_MAGENTA: MapDecorationType = MapDecorationType { + id: 12u32, + name: "banner_magenta", + asset_name: "magenta_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_LIGHT_BLUE: MapDecorationType = MapDecorationType { + id: 13u32, + name: "banner_light_blue", + asset_name: "light_blue_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_YELLOW: MapDecorationType = MapDecorationType { + id: 14u32, + name: "banner_yellow", + asset_name: "yellow_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_LIME: MapDecorationType = MapDecorationType { + id: 15u32, + name: "banner_lime", + asset_name: "lime_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_PINK: MapDecorationType = MapDecorationType { + id: 16u32, + name: "banner_pink", + asset_name: "pink_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_GRAY: MapDecorationType = MapDecorationType { + id: 17u32, + name: "banner_gray", + asset_name: "gray_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_LIGHT_GRAY: MapDecorationType = MapDecorationType { + id: 18u32, + name: "banner_light_gray", + asset_name: "light_gray_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_CYAN: MapDecorationType = MapDecorationType { + id: 19u32, + name: "banner_cyan", + asset_name: "cyan_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_PURPLE: MapDecorationType = MapDecorationType { + id: 20u32, + name: "banner_purple", + asset_name: "purple_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_BLUE: MapDecorationType = MapDecorationType { + id: 21u32, + name: "banner_blue", + asset_name: "blue_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_BROWN: MapDecorationType = MapDecorationType { + id: 22u32, + name: "banner_brown", + asset_name: "brown_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_GREEN: MapDecorationType = MapDecorationType { + id: 23u32, + name: "banner_green", + asset_name: "green_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_RED: MapDecorationType = MapDecorationType { + id: 24u32, + name: "banner_red", + asset_name: "red_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const BANNER_BLACK: MapDecorationType = MapDecorationType { + id: 25u32, + name: "banner_black", + asset_name: "black_banner", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: true, + }; + pub const RED_X: MapDecorationType = MapDecorationType { + id: 26u32, + name: "red_x", + asset_name: "red_x", + show_on_item_frame: true, + map_color: -1i32, + exploration_map_element: false, + track_count: false, + }; + pub const VILLAGE_DESERT: MapDecorationType = MapDecorationType { + id: 27u32, + name: "village_desert", + asset_name: "desert_village", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const VILLAGE_PLAINS: MapDecorationType = MapDecorationType { + id: 28u32, + name: "village_plains", + asset_name: "plains_village", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const VILLAGE_SAVANNA: MapDecorationType = MapDecorationType { + id: 29u32, + name: "village_savanna", + asset_name: "savanna_village", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const VILLAGE_SNOWY: MapDecorationType = MapDecorationType { + id: 30u32, + name: "village_snowy", + asset_name: "snowy_village", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const VILLAGE_TAIGA: MapDecorationType = MapDecorationType { + id: 31u32, + name: "village_taiga", + asset_name: "taiga_village", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const JUNGLE_TEMPLE: MapDecorationType = MapDecorationType { + id: 32u32, + name: "jungle_temple", + asset_name: "jungle_temple", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const SWAMP_HUT: MapDecorationType = MapDecorationType { + id: 33u32, + name: "swamp_hut", + asset_name: "swamp_hut", + show_on_item_frame: true, + map_color: 10066329i32, + exploration_map_element: true, + track_count: false, + }; + pub const TRIAL_CHAMBERS: MapDecorationType = MapDecorationType { + id: 34u32, + name: "trial_chambers", + asset_name: "trial_chambers", + show_on_item_frame: true, + map_color: 12741452i32, + exploration_map_element: true, + track_count: false, + }; + pub const ALL: &'static [MapDecorationType] = &[ + MapDecorationType::PLAYER, + MapDecorationType::FRAME, + MapDecorationType::RED_MARKER, + MapDecorationType::BLUE_MARKER, + MapDecorationType::TARGET_X, + MapDecorationType::TARGET_POINT, + MapDecorationType::PLAYER_OFF_MAP, + MapDecorationType::PLAYER_OFF_LIMITS, + MapDecorationType::MANSION, + MapDecorationType::MONUMENT, + MapDecorationType::BANNER_WHITE, + MapDecorationType::BANNER_ORANGE, + MapDecorationType::BANNER_MAGENTA, + MapDecorationType::BANNER_LIGHT_BLUE, + MapDecorationType::BANNER_YELLOW, + MapDecorationType::BANNER_LIME, + MapDecorationType::BANNER_PINK, + MapDecorationType::BANNER_GRAY, + MapDecorationType::BANNER_LIGHT_GRAY, + MapDecorationType::BANNER_CYAN, + MapDecorationType::BANNER_PURPLE, + MapDecorationType::BANNER_BLUE, + MapDecorationType::BANNER_BROWN, + MapDecorationType::BANNER_GREEN, + MapDecorationType::BANNER_RED, + MapDecorationType::BANNER_BLACK, + MapDecorationType::RED_X, + MapDecorationType::VILLAGE_DESERT, + MapDecorationType::VILLAGE_PLAINS, + MapDecorationType::VILLAGE_SAVANNA, + MapDecorationType::VILLAGE_SNOWY, + MapDecorationType::VILLAGE_TAIGA, + MapDecorationType::JUNGLE_TEMPLE, + MapDecorationType::SWAMP_HUT, + MapDecorationType::TRIAL_CHAMBERS, + ]; + #[must_use] + pub const fn from_id(id: u32) -> Option<&'static MapDecorationType> { + if (id as usize) < Self::ALL.len() { + Some(&Self::ALL[id as usize]) + } else { + None + } + } + #[must_use] + pub fn from_name(name: &str) -> Option<&'static MapDecorationType> { + match name { + "player" => Some(&Self::PLAYER), + "frame" => Some(&Self::FRAME), + "red_marker" => Some(&Self::RED_MARKER), + "blue_marker" => Some(&Self::BLUE_MARKER), + "target_x" => Some(&Self::TARGET_X), + "target_point" => Some(&Self::TARGET_POINT), + "player_off_map" => Some(&Self::PLAYER_OFF_MAP), + "player_off_limits" => Some(&Self::PLAYER_OFF_LIMITS), + "mansion" => Some(&Self::MANSION), + "monument" => Some(&Self::MONUMENT), + "banner_white" => Some(&Self::BANNER_WHITE), + "banner_orange" => Some(&Self::BANNER_ORANGE), + "banner_magenta" => Some(&Self::BANNER_MAGENTA), + "banner_light_blue" => Some(&Self::BANNER_LIGHT_BLUE), + "banner_yellow" => Some(&Self::BANNER_YELLOW), + "banner_lime" => Some(&Self::BANNER_LIME), + "banner_pink" => Some(&Self::BANNER_PINK), + "banner_gray" => Some(&Self::BANNER_GRAY), + "banner_light_gray" => Some(&Self::BANNER_LIGHT_GRAY), + "banner_cyan" => Some(&Self::BANNER_CYAN), + "banner_purple" => Some(&Self::BANNER_PURPLE), + "banner_blue" => Some(&Self::BANNER_BLUE), + "banner_brown" => Some(&Self::BANNER_BROWN), + "banner_green" => Some(&Self::BANNER_GREEN), + "banner_red" => Some(&Self::BANNER_RED), + "banner_black" => Some(&Self::BANNER_BLACK), + "red_x" => Some(&Self::RED_X), + "village_desert" => Some(&Self::VILLAGE_DESERT), + "village_plains" => Some(&Self::VILLAGE_PLAINS), + "village_savanna" => Some(&Self::VILLAGE_SAVANNA), + "village_snowy" => Some(&Self::VILLAGE_SNOWY), + "village_taiga" => Some(&Self::VILLAGE_TAIGA), + "jungle_temple" => Some(&Self::JUNGLE_TEMPLE), + "swamp_hut" => Some(&Self::SWAMP_HUT), + "trial_chambers" => Some(&Self::TRIAL_CHAMBERS), + _ => None, + } + } +} diff --git a/crates/pumpkin-data/src/lib.rs b/crates/pumpkin-data/src/lib.rs index a656a18c6..88b35df49 100644 --- a/crates/pumpkin-data/src/lib.rs +++ b/crates/pumpkin-data/src/lib.rs @@ -371,3 +371,18 @@ pub mod villager; #[rustfmt::skip] #[path = "generated/slot_ranges.rs"] pub mod slot_ranges; + +#[cfg(feature = "map_color")] +#[rustfmt::skip] +#[path = "generated/map_color.rs"] +pub mod map_color; + +#[cfg(feature = "map_decoration")] +#[rustfmt::skip] +#[path = "generated/map_decoration.rs"] +pub mod map_decoration; + +#[cfg(feature = "dye_color")] +#[rustfmt::skip] +#[path = "generated/dye_color.rs"] +pub mod dye_color; diff --git a/crates/pumpkin-plugin-wit b/crates/pumpkin-plugin-wit index 8e006930b..6d5dc27d6 160000 --- a/crates/pumpkin-plugin-wit +++ b/crates/pumpkin-plugin-wit @@ -1 +1 @@ -Subproject commit 8e006930be6fa6b5e60201c92d1c0393448ca606 +Subproject commit 6d5dc27d6bd7372e072e54af08881db4197a99ef diff --git a/crates/pumpkin/src/block/entities/map.rs b/crates/pumpkin/src/block/entities/map.rs new file mode 100644 index 000000000..d1a2a7ab2 --- /dev/null +++ b/crates/pumpkin/src/block/entities/map.rs @@ -0,0 +1,267 @@ +use std::any::Any; +use std::future::Future; +use std::pin::Pin; +use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; + +use pumpkin_nbt::compound::NbtCompound; +use pumpkin_nbt::tag::NbtTag; +use pumpkin_protocol::codec::var_int::VarInt; +use pumpkin_protocol::java::client::play::{CMapItemData, MapPatch}; +use pumpkin_util::math::position::BlockPos; + +use crate::block::entities::BlockEntity; + +pub const MAP_BLOCK_ENTITY_ID: &str = "minecraft:map"; + +pub struct MapBlockEntity { + position: BlockPos, + map_id: AtomicI32, + colors: Mutex>, + dirty: AtomicBool, +} + +impl MapBlockEntity { + #[must_use] + pub fn new(position: BlockPos, map_id: i32) -> Self { + Self { + position, + map_id: AtomicI32::new(map_id), + colors: Mutex::new(Box::new([0; 128 * 128])), + dirty: AtomicBool::new(true), + } + } + + #[must_use] + pub fn get_map_id(&self) -> i32 { + self.map_id.load(Ordering::Relaxed) + } + + pub fn set_map_id(&self, map_id: i32) { + self.map_id.store(map_id, Ordering::Relaxed); + self.dirty.store(true, Ordering::Relaxed); + } + + pub fn get_colors(&self) -> Vec { + let colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + colors.to_vec() + } + + pub fn set_colors(&self, new_colors: &[u8]) { + let mut colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if new_colors.len() == 128 * 128 * 3 { + // RGB -> map color byte conversion + for i in 0..(128 * 128) { + let r = new_colors[i * 3]; + let g = new_colors[i * 3 + 1]; + let b = new_colors[i * 3 + 2]; + colors[i] = rgb_to_map_color(r, g, b); + } + } else if new_colors.len() == 128 * 128 * 4 { + // RGBA -> map color byte conversion + for i in 0..(128 * 128) { + let r = new_colors[i * 4]; + let g = new_colors[i * 4 + 1]; + let b = new_colors[i * 4 + 2]; + colors[i] = rgb_to_map_color(r, g, b); + } + } else { + let len = new_colors.len().min(128 * 128); + colors[..len].copy_from_slice(&new_colors[..len]); + } + self.dirty.store(true, Ordering::Relaxed); + } + + pub fn set_pixel(&self, x: usize, y: usize, color: u8) { + if x < 128 && y < 128 { + let mut colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + colors[y * 128 + x] = color; + self.dirty.store(true, Ordering::Relaxed); + } + } + + pub fn get_pixel(&self, x: usize, y: usize) -> u8 { + if x < 128 && y < 128 { + let colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + colors[y * 128 + x] + } else { + 0 + } + } + + pub fn stream_frame(&self, frame_data: &[u8], server: Option<&crate::server::Server>) { + self.set_colors(frame_data); + if let Some(server) = server { + self.broadcast_map_data(server); + } + } + + pub fn broadcast_map_data(&self, server: &crate::server::Server) { + let map_id = self.get_map_id(); + let colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let data = MapPatch { + columns: 128, + rows: 128, + x: 0, + z: 0, + data: &colors[..], + }; + let packet = CMapItemData { + map_id: VarInt(map_id), + scale: 0, + locked: false, + icons: Some(&[]), + data: Some(data), + }; + server.broadcast_packet_all(&packet); + self.dirty.store(false, Ordering::Relaxed); + } +} + +impl BlockEntity for MapBlockEntity { + fn resource_location(&self) -> &'static str { + MAP_BLOCK_ENTITY_ID + } + + fn get_position(&self) -> BlockPos { + self.position + } + + fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self + where + Self: Sized, + { + let map_id = nbt.get_int("MapId").unwrap_or(0); + let entity = Self::new(position, map_id); + if let Some(colors_nbt) = nbt.get("Colors").and_then(|tag| tag.extract_byte_array()) { + let mut colors = entity + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let len = colors_nbt.len().min(128 * 128); + for (i, &b) in colors_nbt.iter().take(len).enumerate() { + colors[i] = b as u8; + } + } + entity + } + + fn write_nbt<'a>( + &'a self, + nbt: &'a mut NbtCompound, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + nbt.put_int("MapId", self.get_map_id()); + let colors = self + .colors + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let colors_i8: Vec = colors.iter().map(|&b| b as i8).collect(); + nbt.put("Colors", NbtTag::ByteArray(colors_i8.into_boxed_slice())); + }) + } + + fn is_dirty(&self) -> bool { + self.dirty.load(Ordering::Relaxed) + } + + fn clear_dirty(&self) { + self.dirty.store(false, Ordering::Relaxed); + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +/// Helper function to convert an RGB value to the closest Minecraft map color index. +fn rgb_to_map_color(r: u8, g: u8, b: u8) -> u8 { + use pumpkin_data::map_color::MapColor; + + // Brightness multipliers: 180 (low), 220 (normal), 255 (high), 135 (lowest) + const BRIGHTNESS: &[(u8, u32)] = &[(0, 180), (1, 220), (2, 255), (3, 135)]; + + let mut best_color: u8 = 0; + let mut min_diff: u32 = u32::MAX; + + let r32 = u32::from(r); + let g32 = u32::from(g); + let b32 = u32::from(b); + + for map_color in MapColor::ALL { + if map_color.id == 0 { + continue; + } + let (cr, cg, cb) = map_color.rgb; + for &(b_idx, mult) in BRIGHTNESS { + let pr = (u32::from(cr) * mult) / 255; + let pg = (u32::from(cg) * mult) / 255; + let pb = (u32::from(cb) * mult) / 255; + + let dr = r32.abs_diff(pr); + let dg = g32.abs_diff(pg); + let db = b32.abs_diff(pb); + + let diff = dr * dr + dg * dg + db * db; + if diff < min_diff { + min_diff = diff; + best_color = map_color.id * 4 + b_idx; + } + } + } + + best_color +} + +#[cfg(test)] +mod tests { + use super::*; + use pumpkin_util::math::position::BlockPos; + + #[test] + fn map_block_entity_pixels_and_colors() { + let pos = BlockPos::new(10, 64, -5); + let map_be = MapBlockEntity::new(pos, 42); + + assert_eq!(map_be.get_map_id(), 42); + assert_eq!(map_be.get_position(), pos); + + map_be.set_map_id(100); + assert_eq!(map_be.get_map_id(), 100); + + map_be.set_pixel(10, 20, 15); + assert_eq!(map_be.get_pixel(10, 20), 15); + + let mut custom_frame = vec![0u8; 128 * 128]; + custom_frame[0] = 5; + custom_frame[127] = 20; + map_be.set_colors(&custom_frame); + + assert_eq!(map_be.get_pixel(0, 0), 5); + assert_eq!(map_be.get_pixel(127, 0), 20); + + // Test RGB conversion frame + let mut rgb_frame = vec![0u8; 128 * 128 * 3]; + // Red pixel at (0, 0) + rgb_frame[0] = 255; + rgb_frame[1] = 0; + rgb_frame[2] = 0; + map_be.set_colors(&rgb_frame); + assert!(map_be.get_pixel(0, 0) > 0); + } +} diff --git a/crates/pumpkin/src/block/entities/mod.rs b/crates/pumpkin/src/block/entities/mod.rs index c831d3208..921944b52 100644 --- a/crates/pumpkin/src/block/entities/mod.rs +++ b/crates/pumpkin/src/block/entities/mod.rs @@ -30,6 +30,7 @@ pub mod hopper; pub mod jigsaw_block; pub mod jukebox; pub mod lectern; +pub mod map; pub mod mob_spawner; pub mod piston; pub mod shulker_box; @@ -277,6 +278,7 @@ pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option> conduit::ConduitBlockEntity::ID => { Some(Arc::new(conduit::ConduitBlockEntity::from_nbt(nbt, pos))) } + map::MAP_BLOCK_ENTITY_ID => Some(Arc::new(map::MapBlockEntity::from_nbt(nbt, pos))), campfire::CampfireBlockEntity::ID => { Some(Arc::new(campfire::CampfireBlockEntity::from_nbt(nbt, pos))) } @@ -439,6 +441,7 @@ pub fn create_block_entity( "potent_sulfur" => Some(Arc::new(potent_sulfur::PotentSulfurBlockEntity::new( position, ))), + "map" => Some(Arc::new(map::MapBlockEntity::new(position, 0))), _ => None, } } diff --git a/crates/pumpkin/src/block/entities/sign.rs b/crates/pumpkin/src/block/entities/sign.rs index d9ea19048..8d3af98ed 100644 --- a/crates/pumpkin/src/block/entities/sign.rs +++ b/crates/pumpkin/src/block/entities/sign.rs @@ -11,104 +11,7 @@ use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_util::math::position::BlockPos; use tokio::sync::Mutex; -#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] -#[repr(i8)] -pub enum DyeColor { - White = 0, - Orange = 1, - Magenta = 2, - LightBlue = 3, - Yellow = 4, - Lime = 5, - Pink = 6, - Gray = 7, - LightGray = 8, - Cyan = 9, - Purple = 10, - Blue = 11, - Brown = 12, - Green = 13, - Red = 14, - #[default] - Black = 15, -} - -impl From for String { - fn from(value: DyeColor) -> Self { - match value { - DyeColor::White => "white".to_string(), - DyeColor::Orange => "orange".to_string(), - DyeColor::Magenta => "magenta".to_string(), - DyeColor::LightBlue => "light_blue".to_string(), - DyeColor::Yellow => "yellow".to_string(), - DyeColor::Lime => "lime".to_string(), - DyeColor::Pink => "pink".to_string(), - DyeColor::Gray => "gray".to_string(), - DyeColor::LightGray => "light_gray".to_string(), - DyeColor::Cyan => "cyan".to_string(), - DyeColor::Purple => "purple".to_string(), - DyeColor::Blue => "blue".to_string(), - DyeColor::Brown => "brown".to_string(), - DyeColor::Green => "green".to_string(), - DyeColor::Red => "red".to_string(), - DyeColor::Black => "black".to_string(), - } - } -} - -impl From<&str> for DyeColor { - fn from(s: &str) -> Self { - match s { - "white" => Self::White, - "orange" => Self::Orange, - "magenta" => Self::Magenta, - "light_blue" => Self::LightBlue, - "yellow" => Self::Yellow, - "lime" => Self::Lime, - "pink" => Self::Pink, - "gray" => Self::Gray, - "light_gray" => Self::LightGray, - "cyan" => Self::Cyan, - "purple" => Self::Purple, - "blue" => Self::Blue, - "brown" => Self::Brown, - "green" => Self::Green, - "red" => Self::Red, - "black" => Self::Black, - _ => Self::default(), - } - } -} - -impl From for DyeColor { - fn from(s: i8) -> Self { - match s { - 0 => Self::White, - 1 => Self::Orange, - 2 => Self::Magenta, - 3 => Self::LightBlue, - 4 => Self::Yellow, - 5 => Self::Lime, - 6 => Self::Pink, - 7 => Self::Gray, - 8 => Self::LightGray, - 9 => Self::Cyan, - 10 => Self::Purple, - 11 => Self::Blue, - 12 => Self::Brown, - 13 => Self::Green, - 14 => Self::Red, - 15 => Self::Black, - _ => Self::default(), - } - } -} - -impl From for NbtTag { - fn from(value: DyeColor) -> Self { - Self::Byte(value as i8) - } -} +pub use pumpkin_data::dye_color::DyeColor; pub struct SignBlockEntity { pub front_text: Text, @@ -152,7 +55,7 @@ impl From for NbtTag { "has_glowing_text", value.has_glowing_text.load(Ordering::Relaxed), ); - nbt.put_string("color", value.get_color().into()); + nbt.put_string("color", value.get_color().name().to_string()); nbt.put_list( "messages", value @@ -184,7 +87,7 @@ impl From for Text { Self { has_glowing_text: AtomicBool::new(has_glowing_text), - color: AtomicI8::new(DyeColor::from(color) as i8), + color: AtomicI8::new(DyeColor::by_name(color).unwrap_or_default().id() as i8), messages: Arc::new(std::sync::Mutex::new([ get_message(0), get_message(1), @@ -200,17 +103,22 @@ impl Text { pub fn new(messages: [Box; 4]) -> Self { Self { has_glowing_text: AtomicBool::new(false), - color: AtomicI8::new(DyeColor::default() as i8), + color: AtomicI8::new(DyeColor::default().id() as i8), messages: Arc::new(std::sync::Mutex::new(messages)), } } pub fn get_color(&self) -> DyeColor { - self.color.load(Ordering::Relaxed).into() + let c = self.color.load(Ordering::Relaxed); + if c >= 0 { + DyeColor::by_id(c as u8).unwrap_or_default() + } else { + DyeColor::default() + } } pub fn set_color(&self, color: DyeColor) { - self.color.store(color as i8, Ordering::Relaxed); + self.color.store(color.id() as i8, Ordering::Relaxed); } } diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index 347768a22..fbac4612f 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -4133,15 +4133,26 @@ impl Player { let dx = pos.x - map_data.center_x as f64; let dz = pos.z - map_data.center_z as f64; - let icon_x = (dx / scale as f64 * 2.0).clamp(-128.0, 127.0) as i8; - let icon_z = (dz / scale as f64 * 2.0).clamp(-128.0, 127.0) as i8; + let raw_x = dx / scale as f64 * 2.0; + let raw_z = dz / scale as f64 * 2.0; + let is_off_map = !(-127.0..=127.0).contains(&raw_x) + || !(-127.0..=127.0).contains(&raw_z); + + let icon_x = raw_x.clamp(-128.0, 127.0) as i8; + let icon_z = raw_z.clamp(-128.0, 127.0) as i8; let yaw = self.living_entity.entity.yaw.load(); let icon_direction = ((((yaw * 16.0 / 360.0).round() as i32 + 8) % 16 + 16) % 16) as i8; + let decoration_type = if is_off_map { + &pumpkin_data::map_decoration::MapDecorationType::PLAYER_OFF_MAP + } else { + &pumpkin_data::map_decoration::MapDecorationType::PLAYER + }; + let icons = [MapIcon { - icon_type: VarInt(0), // White pointer + icon_type: VarInt(decoration_type.id as i32), x: icon_x, z: icon_z, direction: icon_direction, diff --git a/crates/pumpkin/src/item/items/dye.rs b/crates/pumpkin/src/item/items/dye.rs index 07b719b43..339d92f25 100644 --- a/crates/pumpkin/src/item/items/dye.rs +++ b/crates/pumpkin/src/item/items/dye.rs @@ -39,7 +39,7 @@ impl DyeItem { text: &Text, color_name: &str, ) -> BlockActionResult { - let dye_color = DyeColor::from(color_name); + let dye_color = DyeColor::by_name(color_name).unwrap_or_default(); text.set_color(dye_color); diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/state.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/state.rs index 0b67fbf26..6e816ce92 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/state.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/state.rs @@ -67,6 +67,14 @@ pub type ItemStackResource = WasmResource>; pub type BlockEntityResource = WasmResource>; +#[derive(Clone)] +pub struct ContainerBlockEntity { + pub provider: Arc, + pub inventory: Arc, +} + +pub type ContainerBlockEntityResource = WasmResource; + pub type OwnedConsumedArgs = HashMap; pub struct PluginHostState { @@ -293,6 +301,20 @@ impl PluginHostState { let resource = self.resource_table.push(BlockEntityResource { provider })?; Ok(wasmtime::component::Resource::new_own(resource.rep())) } + + pub fn add_container_block_entity( + &mut self, + provider: Arc, + inventory: Arc, + ) -> wasmtime::Result> { + let resource = self.resource_table.push(ContainerBlockEntityResource { + provider: ContainerBlockEntity { + provider, + inventory, + }, + })?; + Ok(wasmtime::component::Resource::new_own(resource.rep())) + } } pub struct PluginHttpHooks { diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs index 62d7b36c1..b0a759e2f 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/block_entity.rs @@ -2,25 +2,83 @@ use std::sync::Arc; use wasmtime::component::Resource; use crate::block::entities::BlockEntity as InternalBlockEntity; +use crate::block::entities::banner::BannerBlockEntity as InternalBannerBlockEntity; +use crate::block::entities::beacon::BeaconBlockEntity as InternalBeaconBlockEntity; +use crate::block::entities::beehive::BeehiveBlockEntity as InternalBeehiveBlockEntity; +use crate::block::entities::bell::BellBlockEntity as InternalBellBlockEntity; +use crate::block::entities::blasting_furnace::BlastingFurnaceBlockEntity as InternalBlastingFurnaceBlockEntity; +use crate::block::entities::brewing_stand::BrewingStandBlockEntity as InternalBrewingStandBlockEntity; use crate::block::entities::chest::ChestBlockEntity as InternalChestBlockEntity; +use crate::block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity as InternalChiseledBookshelfBlockEntity; use crate::block::entities::command_block::CommandBlockEntity as InternalCommandBlockEntity; +use crate::block::entities::comparator::ComparatorBlockEntity as InternalComparatorBlockEntity; +use crate::block::entities::crafter::CrafterBlockEntity as InternalCrafterBlockEntity; +use crate::block::entities::creaking_heart::CreakingHeartBlockEntity as InternalCreakingHeartBlockEntity; +use crate::block::entities::end_gateway::EndGatewayBlockEntity as InternalEndGatewayBlockEntity; +use crate::block::entities::furnace::FurnaceBlockEntity as InternalFurnaceBlockEntity; +use crate::block::entities::furnace_like_block_entity::CookingBlockEntityBase; +use crate::block::entities::hanging_sign::HangingSignBlockEntity as InternalHangingSignBlockEntity; +use crate::block::entities::hopper::HopperBlockEntity as InternalHopperBlockEntity; +use crate::block::entities::jigsaw_block::JigsawBlockEntity as InternalJigsawBlockEntity; use crate::block::entities::jukebox::JukeboxBlockEntity as InternalJukeboxBlockEntity; +use crate::block::entities::lectern::LecternBlockEntity as InternalLecternBlockEntity; +use crate::block::entities::map::MapBlockEntity as InternalMapBlockEntity; use crate::block::entities::mob_spawner::MobSpawnerBlockEntity as InternalMobSpawnerBlockEntity; +use crate::block::entities::piston::PistonBlockEntity as InternalPistonBlockEntity; +use crate::block::entities::sculk_shrieker::SculkShriekerBlockEntity as InternalSculkShriekerBlockEntity; use crate::block::entities::sign::{ DyeColor as InternalDyeColor, SignBlockEntity as InternalSignBlockEntity, Text as InternalText, }; +use crate::block::entities::skull::SkullBlockEntity as InternalSkullBlockEntity; +use crate::block::entities::smoker::SmokerBlockEntity as InternalSmokerBlockEntity; +use crate::block::entities::structure_block::StructureBlockBlockEntity as InternalStructureBlockBlockEntity; use crate::plugin::loader::wasm::wasm_host::{ - state::{BlockEntityResource, PluginHostState}, + state::{BlockEntityResource, ContainerBlockEntityResource, PluginHostState}, wit::v0_1::pumpkin::{ self, plugin::{ block_entity::{ - BlockEntity, ChestBlockEntity, CommandBlockEntity, DyeColor, HostBlockEntity, - HostChestBlockEntity, HostCommandBlockEntity, HostJukeboxBlockEntity, - HostMobSpawnerBlockEntity, HostSignBlockEntity, JukeboxBlockEntity, - MobSpawnerBlockEntity, SignBlockEntity, SignText, + BannerBlockEntity, BarrelBlockEntity, BeaconBlockEntity, BedBlockEntity, + BeehiveBlockEntity, BellBlockEntity, BlastingFurnaceBlockEntity, BlockEntity, + BrewingStandBlockEntity, BrushableBlockBlockEntity, + CalibratedSculkSensorBlockEntity, CampfireBlockEntity, ChestBlockEntity, + ChiseledBookshelfBlockEntity, CommandBlockEntity, ComparatorBlockEntity, + ConduitBlockEntity, ContainerBlockEntity, CopperGolemStatueBlockEntity, + CrafterBlockEntity, CreakingHeartBlockEntity, DaylightDetectorBlockEntity, + DecoratedPotBlockEntity, DispenserBlockEntity, DropperBlockEntity, DyeColor, + EnchantingTableBlockEntity, EndGatewayBlockEntity, EndPortalBlockEntity, + EnderChestBlockEntity, FurnaceBlockEntity, HangingSignBlockEntity, + HopperBlockEntity, HostBannerBlockEntity, HostBarrelBlockEntity, + HostBeaconBlockEntity, HostBedBlockEntity, HostBeehiveBlockEntity, + HostBellBlockEntity, HostBlastingFurnaceBlockEntity, HostBlockEntity, + HostBrewingStandBlockEntity, HostBrushableBlockBlockEntity, + HostCalibratedSculkSensorBlockEntity, HostCampfireBlockEntity, + HostChestBlockEntity, HostChiseledBookshelfBlockEntity, HostCommandBlockEntity, + HostComparatorBlockEntity, HostConduitBlockEntity, HostContainerBlockEntity, + HostCopperGolemStatueBlockEntity, HostCrafterBlockEntity, + HostCreakingHeartBlockEntity, HostDaylightDetectorBlockEntity, + HostDecoratedPotBlockEntity, HostDispenserBlockEntity, HostDropperBlockEntity, + HostEnchantingTableBlockEntity, HostEndGatewayBlockEntity, + HostEndPortalBlockEntity, HostEnderChestBlockEntity, HostFurnaceBlockEntity, + HostHangingSignBlockEntity, HostHopperBlockEntity, HostJigsawBlockEntity, + HostJukeboxBlockEntity, HostLecternBlockEntity, HostMapBlockEntity, + HostMobSpawnerBlockEntity, HostPistonBlockEntity, HostPotentSulfurBlockEntity, + HostSculkCatalystBlockEntity, HostSculkSensorBlockEntity, + HostSculkShriekerBlockEntity, HostShelfBlockEntity, HostShulkerBoxBlockEntity, + HostSignBlockEntity, HostSkullBlockEntity, HostSmokerBlockEntity, + HostStructureBlockBlockEntity, HostTestBlockBlockEntity, + HostTestInstanceBlockBlockEntity, HostTrappedChestBlockEntity, + HostTrialSpawnerBlockEntity, HostVaultBlockEntity, JigsawBlockEntity, + JukeboxBlockEntity, LecternBlockEntity, MapBlockEntity, MobSpawnerBlockEntity, + PistonBlockEntity, PotentSulfurBlockEntity, SculkCatalystBlockEntity, + SculkSensorBlockEntity, SculkShriekerBlockEntity, ShelfBlockEntity, + ShulkerBoxBlockEntity, SignBlockEntity, SignText, SkullBlockEntity, + SmokerBlockEntity, StructureBlockBlockEntity, TestBlockBlockEntity, + TestInstanceBlockBlockEntity, TrappedChestBlockEntity, TrialSpawnerBlockEntity, + VaultBlockEntity, }, common::BlockPos as WitBlockPos, + item_stack::ItemStack as WitHostItemStack, }, }, }; @@ -104,10 +162,7 @@ fn from_wasm_sign_text(text: SignText) -> InternalText { InternalText::from(pumpkin_nbt::tag::NbtTag::Compound({ let mut nbt = pumpkin_nbt::compound::NbtCompound::new(); nbt.put_bool("has_glowing_text", text.has_glowing_text); - nbt.put_string( - "color", - InternalDyeColor::from(from_wasm_dye_color(text.color) as i8).into(), - ); + nbt.put_string("color", from_wasm_dye_color(text.color).name().to_string()); nbt.put_list( "messages", messages @@ -159,6 +214,130 @@ impl HostBlockEntity for PluginHostState { } } +fn get_container_from_be( + state: &mut PluginHostState, + res: &Resource, +) -> wasmtime::Result> { + let entity = block_entity_from_resource(state, &Resource::new_own(res.rep()))?; + let provider = entity.clone(); + entity.get_inventory().map_or_else( + || Err(wasmtime::Error::msg("Block entity inventory not available")), + |inventory| state.add_container_block_entity(provider, inventory), + ) +} + +impl HostContainerBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let provider = container.provider.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_size(&mut self, res: Resource) -> wasmtime::Result { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + Ok(container.provider.inventory.size() as u32) + } + + async fn is_empty(&mut self, res: Resource) -> wasmtime::Result { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let inventory = container.provider.inventory.clone(); + Ok(inventory.is_empty().await) + } + + async fn get_stack( + &mut self, + res: Resource, + slot: u32, + ) -> wasmtime::Result>> { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let inventory = container.provider.inventory.clone(); + let stack = inventory.get_stack(slot as usize).await; + if stack.is_empty() { + Ok(None) + } else { + Ok(Some(self.add_item_stack(Arc::new( + tokio::sync::Mutex::new(stack), + ))?)) + } + } + + async fn set_stack( + &mut self, + res: Resource, + slot: u32, + stack_res: Option>, + ) -> wasmtime::Result<()> { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let inventory = container.provider.inventory.clone(); + + let stack = match stack_res { + Some(res) => { + let lock = self.get_item_stack(&res)?; + lock.lock().await.clone() + } + None => pumpkin_data::item_stack::ItemStack::EMPTY.clone(), + }; + + inventory.set_stack(slot as usize, stack).await; + Ok(()) + } + + async fn remove_stack( + &mut self, + res: Resource, + slot: u32, + ) -> wasmtime::Result>> { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let inventory = container.provider.inventory.clone(); + let removed = inventory.remove_stack(slot as usize).await; + if removed.is_empty() { + Ok(None) + } else { + Ok(Some(self.add_item_stack(Arc::new( + tokio::sync::Mutex::new(removed), + ))?)) + } + } + + async fn clear(&mut self, res: Resource) -> wasmtime::Result<()> { + let container = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid container block entity resource handle"))?; + let inventory = container.provider.inventory.clone(); + inventory.clear().await; + Ok(()) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + impl HostCommandBlockEntity for PluginHostState { async fn get_block_entity( &mut self, @@ -417,6 +596,13 @@ impl HostJukeboxBlockEntity for PluginHostState { self.add_block_entity(provider) } + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + async fn is_playing(&mut self, res: Resource) -> wasmtime::Result { let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; entity @@ -481,6 +667,13 @@ impl HostChestBlockEntity for PluginHostState { self.add_block_entity(provider) } + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + async fn viewer_count(&mut self, res: Resource) -> wasmtime::Result { let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; entity @@ -561,3 +754,1491 @@ impl HostMobSpawnerBlockEntity for PluginHostState { Ok(()) } } + +impl HostMapBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid map block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_map_id(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a map block entity")), + |map_be| Ok(map_be.get_map_id()), + ) + } + + async fn set_map_id( + &mut self, + res: Resource, + map_id: i32, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + map_be.set_map_id(map_id); + Ok(()) + } + + async fn get_colors(&mut self, res: Resource) -> wasmtime::Result> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + Ok(map_be.get_colors()) + } + + async fn set_colors( + &mut self, + res: Resource, + colors: Vec, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + map_be.set_colors(&colors); + Ok(()) + } + + async fn set_pixel( + &mut self, + res: Resource, + x: u32, + y: u32, + color: u8, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + map_be.set_pixel(x as usize, y as usize, color); + Ok(()) + } + + async fn get_pixel( + &mut self, + res: Resource, + x: u32, + y: u32, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + Ok(map_be.get_pixel(x as usize, y as usize)) + } + + async fn update(&mut self, res: Resource) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + let server = self + .server + .as_ref() + .ok_or_else(|| wasmtime::Error::msg("Server context not set"))?; + map_be.broadcast_map_data(server); + Ok(()) + } + + async fn stream_frame( + &mut self, + res: Resource, + frame_data: Vec, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + let map_be = entity + .as_any() + .downcast_ref::() + .ok_or_else(|| wasmtime::Error::msg("Not a map block entity"))?; + let server_opt = self.server.as_deref(); + map_be.stream_frame(&frame_data, server_opt); + Ok(()) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostHangingSignBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid hanging sign block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_front_text( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| Ok(to_wasm_sign_text(&sign.front_text)), + ) + } + + async fn set_front_text( + &mut self, + res: Resource, + text: SignText, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| { + let new_text = from_wasm_sign_text(text); + sign.front_text.has_glowing_text.store( + new_text + .has_glowing_text + .load(std::sync::atomic::Ordering::Relaxed), + std::sync::atomic::Ordering::Relaxed, + ); + sign.front_text.set_color(new_text.get_color()); + (*sign + .front_text + .messages + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner)) + .clone_from( + &new_text + .messages + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + Ok(()) + }, + ) + } + + async fn get_back_text( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| Ok(to_wasm_sign_text(&sign.back_text)), + ) + } + + async fn set_back_text( + &mut self, + res: Resource, + text: SignText, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| { + let new_text = from_wasm_sign_text(text); + sign.back_text.has_glowing_text.store( + new_text + .has_glowing_text + .load(std::sync::atomic::Ordering::Relaxed), + std::sync::atomic::Ordering::Relaxed, + ); + sign.back_text.set_color(new_text.get_color()); + (*sign + .back_text + .messages + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner)) + .clone_from( + &new_text + .messages + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner), + ); + Ok(()) + }, + ) + } + + async fn is_waxed(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| Ok(sign.is_waxed.load(std::sync::atomic::Ordering::Relaxed)), + ) + } + + async fn set_waxed( + &mut self, + res: Resource, + waxed: bool, + ) -> wasmtime::Result<()> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a hanging sign block entity")), + |sign| { + sign.is_waxed + .store(waxed, std::sync::atomic::Ordering::Relaxed); + Ok(()) + }, + ) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostTrappedChestBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid trapped chest block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn viewer_count( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + entity + .as_any() + .downcast_ref::() + .map_or_else( + || Err(wasmtime::Error::msg("Not a trapped chest block entity")), + |chest| Ok(chest.get_viewer_count() as u32), + ) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +macro_rules! impl_basic_block_entity { + ($trait_name:ident, $resource_name:ident, $name_str:expr) => { + impl $trait_name for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg(concat!("invalid ", $name_str, " resource handle")) + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn drop(&mut self, rep: Resource<$resource_name>) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } + } + }; +} + +macro_rules! impl_container_basic_block_entity { + ($trait_name:ident, $resource_name:ident, $name_str:expr) => { + impl $trait_name for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg(concat!("invalid ", $name_str, " resource handle")) + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn drop(&mut self, rep: Resource<$resource_name>) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } + } + }; +} + +macro_rules! impl_cooking_host_block_entity { + ($trait_name:ident, $resource_name:ident, $internal_type:ty, $name_str:expr) => { + impl $trait_name for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg(concat!("invalid ", $name_str, " resource handle")) + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_cooking_time_spent( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::<$internal_type>() + .map_or(0, |b| b.get_cooking_time_spent())) + } + + async fn get_cooking_total_time( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::<$internal_type>() + .map_or(0, |b| b.get_cooking_total_time())) + } + + async fn get_lit_time_remaining( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::<$internal_type>() + .map_or(0, |b| b.get_lit_time_remaining())) + } + + async fn get_lit_total_time( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::<$internal_type>() + .map_or(0, |b| b.get_lit_total_time())) + } + + async fn is_burning( + &mut self, + res: Resource<$resource_name>, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::<$internal_type>() + .is_some_and(|b| b.is_burning())) + } + + async fn drop(&mut self, rep: Resource<$resource_name>) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } + } + }; +} + +impl_cooking_host_block_entity!( + HostBlastingFurnaceBlockEntity, + BlastingFurnaceBlockEntity, + InternalBlastingFurnaceBlockEntity, + "blasting furnace block entity" +); +impl_cooking_host_block_entity!( + HostFurnaceBlockEntity, + FurnaceBlockEntity, + InternalFurnaceBlockEntity, + "furnace block entity" +); +impl_cooking_host_block_entity!( + HostSmokerBlockEntity, + SmokerBlockEntity, + InternalSmokerBlockEntity, + "smoker block entity" +); + +impl HostBannerBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid banner block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_custom_name( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .and_then(|b| b.custom_name.try_lock().ok().and_then(|g| g.clone()))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostBarrelBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid barrel block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn viewer_count(&mut self, _res: Resource) -> wasmtime::Result { + Ok(0) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostBeaconBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid beacon block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_primary_effect( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(-1, |b| { + b.primary_effect.load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn get_secondary_effect( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(-1, |b| { + b.secondary_effect + .load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn get_levels(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.levels.load(std::sync::atomic::Ordering::Relaxed))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostBeehiveBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid beehive block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_bee_count(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.bees + .try_lock() + .ok() + .and_then(|g| g.as_ref().map(|v| v.len() as u32)) + .unwrap_or(0) + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostBellBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid bell block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn is_ringing(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .is_some_and(|b| b.ringing.load())) + } + + async fn get_ring_ticks(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.ring_ticks.load())) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostBrewingStandBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid brewing stand block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_brew_time( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.brew_time.load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn get_fuel(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.fuel.load(std::sync::atomic::Ordering::Relaxed))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostChiseledBookshelfBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid chiseled bookshelf block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_last_interacted_slot( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(-1, |b| { + b.last_interacted_slot + .load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostComparatorBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid comparator block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_output_signal( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.output_signal.load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostCrafterBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid crafter block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_crafting_ticks_remaining( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.crafting_ticks_remaining + .load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn is_triggered(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .is_some_and(|b| b.triggered.load(std::sync::atomic::Ordering::Relaxed))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostCreakingHeartBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid creaking heart block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_creaking_uuid( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .and_then(|b| b.creaking_uuid.load().map(|u| u.to_string()))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostEndGatewayBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid end gateway block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_age(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.age.try_lock().ok().map_or(0, |g| *g))) + } + + async fn is_exact_teleport( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .is_some_and(|b| b.exact_teleport.try_lock().ok().is_some_and(|g| *g))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostEnderChestBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid ender chest block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn viewer_count( + &mut self, + _res: Resource, + ) -> wasmtime::Result { + Ok(0) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostShulkerBoxBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid shulker box block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn viewer_count( + &mut self, + _res: Resource, + ) -> wasmtime::Result { + Ok(0) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostHopperBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid hopper block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_cooldown(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.cooldown_time.load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostJigsawBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid jigsaw block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_name(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.name + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_target(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.target + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_pool(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.pool + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_final_state( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.final_state + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_selection_priority( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.selection_priority + .load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn get_placement_priority( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.placement_priority + .load(std::sync::atomic::Ordering::Relaxed) + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostLecternBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid lectern block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_container( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + get_container_from_be(self, &res) + } + + async fn get_page(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| { + b.page.load(std::sync::atomic::Ordering::Relaxed) as u32 + })) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostPistonBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid piston block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_progress(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0.0, |b| b.current_progress.load())) + } + + async fn is_extending(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .is_some_and(|b| b.extending)) + } + + async fn is_source(&mut self, res: Resource) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .is_some_and(|b| b.source)) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostSculkShriekerBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid sculk shrieker block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_warning_level( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.warning_level.try_lock().ok().map_or(0, |g| *g))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostSkullBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| wasmtime::Error::msg("invalid skull block entity resource handle"))?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_note_block_sound( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .and_then(|b| b.note_block_sound.try_lock().ok().and_then(|g| g.clone()))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl HostStructureBlockBlockEntity for PluginHostState { + async fn get_block_entity( + &mut self, + res: Resource, + ) -> wasmtime::Result> { + let entity = self + .resource_table + .get::(&Resource::new_own(res.rep())) + .map_err(|_| { + wasmtime::Error::msg("invalid structure block block entity resource handle") + })?; + let provider = entity.provider.clone(); + self.add_block_entity(provider) + } + + async fn get_name( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.name + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_author( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.author + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_mode( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or_else(String::new, |b| { + b.mode + .try_lock() + .ok() + .map_or_else(String::new, |g| g.clone()) + })) + } + + async fn get_integrity( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(1.0, |b| b.integrity.try_lock().ok().map_or(1.0, |g| *g))) + } + + async fn get_seed( + &mut self, + res: Resource, + ) -> wasmtime::Result { + let entity = block_entity_from_resource(self, &Resource::new_own(res.rep()))?; + Ok(entity + .as_any() + .downcast_ref::() + .map_or(0, |b| b.seed.try_lock().ok().map_or(0, |g| *g))) + } + + async fn drop(&mut self, rep: Resource) -> wasmtime::Result<()> { + let _ = self + .resource_table + .delete::(Resource::new_own(rep.rep())); + Ok(()) + } +} + +impl_basic_block_entity!(HostBedBlockEntity, BedBlockEntity, "bed block entity"); +impl_basic_block_entity!( + HostBrushableBlockBlockEntity, + BrushableBlockBlockEntity, + "brushable block block entity" +); +impl_basic_block_entity!( + HostCalibratedSculkSensorBlockEntity, + CalibratedSculkSensorBlockEntity, + "calibrated sculk sensor block entity" +); +impl_container_basic_block_entity!( + HostCampfireBlockEntity, + CampfireBlockEntity, + "campfire block entity" +); +impl_basic_block_entity!( + HostConduitBlockEntity, + ConduitBlockEntity, + "conduit block entity" +); +impl_basic_block_entity!( + HostCopperGolemStatueBlockEntity, + CopperGolemStatueBlockEntity, + "copper golem statue block entity" +); +impl_basic_block_entity!( + HostDaylightDetectorBlockEntity, + DaylightDetectorBlockEntity, + "daylight detector block entity" +); +impl_basic_block_entity!( + HostDecoratedPotBlockEntity, + DecoratedPotBlockEntity, + "decorated pot block entity" +); +impl_container_basic_block_entity!( + HostDispenserBlockEntity, + DispenserBlockEntity, + "dispenser block entity" +); +impl_container_basic_block_entity!( + HostDropperBlockEntity, + DropperBlockEntity, + "dropper block entity" +); +impl_basic_block_entity!( + HostEnchantingTableBlockEntity, + EnchantingTableBlockEntity, + "enchanting table block entity" +); +impl_basic_block_entity!( + HostEndPortalBlockEntity, + EndPortalBlockEntity, + "end portal block entity" +); +impl_basic_block_entity!( + HostPotentSulfurBlockEntity, + PotentSulfurBlockEntity, + "potent sulfur block entity" +); +impl_basic_block_entity!( + HostSculkCatalystBlockEntity, + SculkCatalystBlockEntity, + "sculk catalyst block entity" +); +impl_basic_block_entity!( + HostSculkSensorBlockEntity, + SculkSensorBlockEntity, + "sculk sensor block entity" +); +impl_container_basic_block_entity!(HostShelfBlockEntity, ShelfBlockEntity, "shelf block entity"); +impl_basic_block_entity!( + HostTestBlockBlockEntity, + TestBlockBlockEntity, + "test block block entity" +); +impl_basic_block_entity!( + HostTestInstanceBlockBlockEntity, + TestInstanceBlockBlockEntity, + "test instance block block entity" +); +impl_basic_block_entity!( + HostTrialSpawnerBlockEntity, + TrialSpawnerBlockEntity, + "trial spawner block entity" +); +impl_basic_block_entity!(HostVaultBlockEntity, VaultBlockEntity, "vault block entity"); diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs index d94e86617..510addee7 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs @@ -8,11 +8,57 @@ use pumpkin_world::world::BlockFlags; use std::sync::Arc; use wasmtime::component::Resource; +use crate::block::entities::banner::BannerBlockEntity as InternalBannerBlockEntity; +use crate::block::entities::barrel::BarrelBlockEntity as InternalBarrelBlockEntity; +use crate::block::entities::beacon::BeaconBlockEntity as InternalBeaconBlockEntity; +use crate::block::entities::bed::BedBlockEntity as InternalBedBlockEntity; +use crate::block::entities::beehive::BeehiveBlockEntity as InternalBeehiveBlockEntity; +use crate::block::entities::bell::BellBlockEntity as InternalBellBlockEntity; +use crate::block::entities::blasting_furnace::BlastingFurnaceBlockEntity as InternalBlastingFurnaceBlockEntity; +use crate::block::entities::brewing_stand::BrewingStandBlockEntity as InternalBrewingStandBlockEntity; +use crate::block::entities::brushable_block::BrushableBlockBlockEntity as InternalBrushableBlockBlockEntity; +use crate::block::entities::calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity as InternalCalibratedSculkSensorBlockEntity; +use crate::block::entities::campfire::CampfireBlockEntity as InternalCampfireBlockEntity; use crate::block::entities::chest::ChestBlockEntity as InternalChestBlockEntity; +use crate::block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity as InternalChiseledBookshelfBlockEntity; use crate::block::entities::command_block::CommandBlockEntity as InternalCommandBlockEntity; +use crate::block::entities::comparator::ComparatorBlockEntity as InternalComparatorBlockEntity; +use crate::block::entities::conduit::ConduitBlockEntity as InternalConduitBlockEntity; +use crate::block::entities::copper_golem_statue::CopperGolemStatueBlockEntity as InternalCopperGolemStatueBlockEntity; +use crate::block::entities::crafter::CrafterBlockEntity as InternalCrafterBlockEntity; +use crate::block::entities::creaking_heart::CreakingHeartBlockEntity as InternalCreakingHeartBlockEntity; +use crate::block::entities::daylight_detector::DaylightDetectorBlockEntity as InternalDaylightDetectorBlockEntity; +use crate::block::entities::decorated_pot::DecoratedPotBlockEntity as InternalDecoratedPotBlockEntity; +use crate::block::entities::dispenser::DispenserBlockEntity as InternalDispenserBlockEntity; +use crate::block::entities::dropper::DropperBlockEntity as InternalDropperBlockEntity; +use crate::block::entities::enchanting_table::EnchantingTableBlockEntity as InternalEnchantingTableBlockEntity; +use crate::block::entities::end_gateway::EndGatewayBlockEntity as InternalEndGatewayBlockEntity; +use crate::block::entities::end_portal::EndPortalBlockEntity as InternalEndPortalBlockEntity; +use crate::block::entities::ender_chest::EnderChestBlockEntity as InternalEnderChestBlockEntity; +use crate::block::entities::furnace::FurnaceBlockEntity as InternalFurnaceBlockEntity; +use crate::block::entities::hanging_sign::HangingSignBlockEntity as InternalHangingSignBlockEntity; +use crate::block::entities::hopper::HopperBlockEntity as InternalHopperBlockEntity; +use crate::block::entities::jigsaw_block::JigsawBlockEntity as InternalJigsawBlockEntity; use crate::block::entities::jukebox::JukeboxBlockEntity as InternalJukeboxBlockEntity; +use crate::block::entities::lectern::LecternBlockEntity as InternalLecternBlockEntity; +use crate::block::entities::map::MapBlockEntity as InternalMapBlockEntity; use crate::block::entities::mob_spawner::MobSpawnerBlockEntity as InternalMobSpawnerBlockEntity; +use crate::block::entities::piston::PistonBlockEntity as InternalPistonBlockEntity; +use crate::block::entities::potent_sulfur::PotentSulfurBlockEntity as InternalPotentSulfurBlockEntity; +use crate::block::entities::sculk_catalyst::SculkCatalystBlockEntity as InternalSculkCatalystBlockEntity; +use crate::block::entities::sculk_sensor::SculkSensorBlockEntity as InternalSculkSensorBlockEntity; +use crate::block::entities::sculk_shrieker::SculkShriekerBlockEntity as InternalSculkShriekerBlockEntity; +use crate::block::entities::shelf::ShelfBlockEntity as InternalShelfBlockEntity; +use crate::block::entities::shulker_box::ShulkerBoxBlockEntity as InternalShulkerBoxBlockEntity; use crate::block::entities::sign::SignBlockEntity as InternalSignBlockEntity; +use crate::block::entities::skull::SkullBlockEntity as InternalSkullBlockEntity; +use crate::block::entities::smoker::SmokerBlockEntity as InternalSmokerBlockEntity; +use crate::block::entities::structure_block::StructureBlockBlockEntity as InternalStructureBlockBlockEntity; +use crate::block::entities::test_block::TestBlockBlockEntity as InternalTestBlockBlockEntity; +use crate::block::entities::test_instance_block::TestInstanceBlockBlockEntity as InternalTestInstanceBlockBlockEntity; +use crate::block::entities::trapped_chest::TrappedChestBlockEntity as InternalTrappedChestBlockEntity; +use crate::block::entities::trial_spawner::TrialSpawnerBlockEntity as InternalTrialSpawnerBlockEntity; +use crate::block::entities::vault::VaultBlockEntity as InternalVaultBlockEntity; use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::common::Position as WitPosition; use crate::plugin::loader::wasm::wasm_host::wit::v0_1::pumpkin::plugin::world::{ BlockDirection as WitBlockDirection, BlockEntity, BlockEntityType, BlockFlags as WitBlockFlags, @@ -143,54 +189,72 @@ impl PluginHostState { block_entity: Arc, ) -> wasmtime::Result> { let be = block_entity; - if be - .as_any() - .downcast_ref::() - .is_some() - { - let res: Resource = self.add_block_entity(be)?; - Ok(Some(BlockEntityType::CommandBlockEntity( - Resource::new_own(res.rep()), - ))) - } else if be - .as_any() - .downcast_ref::() - .is_some() - { - let res: Resource = self.add_block_entity(be)?; - Ok(Some(BlockEntityType::SignBlockEntity(Resource::new_own( - res.rep(), - )))) - } else if be - .as_any() - .downcast_ref::() - .is_some() - { - let res: Resource = self.add_block_entity(be)?; - Ok(Some(BlockEntityType::JukeboxBlockEntity( - Resource::new_own(res.rep()), - ))) - } else if be - .as_any() - .downcast_ref::() - .is_some() - { - let res: Resource = self.add_block_entity(be)?; - Ok(Some(BlockEntityType::ChestBlockEntity(Resource::new_own( - res.rep(), - )))) - } else if be - .as_any() - .downcast_ref::() - .is_some() - { - let res: Resource = self.add_block_entity(be)?; - Ok(Some(BlockEntityType::MobSpawnerBlockEntity( - Resource::new_own(res.rep()), - ))) - } else { - Ok(None) + macro_rules! match_be { + ($( $internal_type:ty => $variant:ident ),* $(,)?) => { + $( + if be.as_any().downcast_ref::<$internal_type>().is_some() { + let res: Resource = self.add_block_entity(be)?; + return Ok(Some(BlockEntityType::$variant(Resource::new_own(res.rep())))); + } + )* + }; } + + match_be! { + InternalCommandBlockEntity => CommandBlockEntity, + InternalSignBlockEntity => SignBlockEntity, + InternalHangingSignBlockEntity => HangingSignBlockEntity, + InternalJukeboxBlockEntity => JukeboxBlockEntity, + InternalChestBlockEntity => ChestBlockEntity, + InternalTrappedChestBlockEntity => TrappedChestBlockEntity, + InternalMobSpawnerBlockEntity => MobSpawnerBlockEntity, + InternalMapBlockEntity => MapBlockEntity, + InternalBannerBlockEntity => BannerBlockEntity, + InternalBarrelBlockEntity => BarrelBlockEntity, + InternalBeaconBlockEntity => BeaconBlockEntity, + InternalBedBlockEntity => BedBlockEntity, + InternalBeehiveBlockEntity => BeehiveBlockEntity, + InternalBellBlockEntity => BellBlockEntity, + InternalBlastingFurnaceBlockEntity => BlastingFurnaceBlockEntity, + InternalBrewingStandBlockEntity => BrewingStandBlockEntity, + InternalBrushableBlockBlockEntity => BrushableBlockBlockEntity, + InternalCalibratedSculkSensorBlockEntity => CalibratedSculkSensorBlockEntity, + InternalCampfireBlockEntity => CampfireBlockEntity, + InternalChiseledBookshelfBlockEntity => ChiseledBookshelfBlockEntity, + InternalComparatorBlockEntity => ComparatorBlockEntity, + InternalConduitBlockEntity => ConduitBlockEntity, + InternalCopperGolemStatueBlockEntity => CopperGolemStatueBlockEntity, + InternalCrafterBlockEntity => CrafterBlockEntity, + InternalCreakingHeartBlockEntity => CreakingHeartBlockEntity, + InternalDaylightDetectorBlockEntity => DaylightDetectorBlockEntity, + InternalDecoratedPotBlockEntity => DecoratedPotBlockEntity, + InternalDispenserBlockEntity => DispenserBlockEntity, + InternalDropperBlockEntity => DropperBlockEntity, + InternalEnchantingTableBlockEntity => EnchantingTableBlockEntity, + InternalEndGatewayBlockEntity => EndGatewayBlockEntity, + InternalEndPortalBlockEntity => EndPortalBlockEntity, + InternalEnderChestBlockEntity => EnderChestBlockEntity, + InternalFurnaceBlockEntity => FurnaceBlockEntity, + InternalHopperBlockEntity => HopperBlockEntity, + InternalJigsawBlockEntity => JigsawBlockEntity, + InternalLecternBlockEntity => LecternBlockEntity, + InternalPistonBlockEntity => PistonBlockEntity, + InternalPotentSulfurBlockEntity => PotentSulfurBlockEntity, + InternalSculkCatalystBlockEntity => SculkCatalystBlockEntity, + InternalSculkSensorBlockEntity => SculkSensorBlockEntity, + InternalSculkShriekerBlockEntity => SculkShriekerBlockEntity, + InternalShelfBlockEntity => ShelfBlockEntity, + InternalShulkerBoxBlockEntity => ShulkerBoxBlockEntity, + InternalSkullBlockEntity => SkullBlockEntity, + InternalSmokerBlockEntity => SmokerBlockEntity, + InternalStructureBlockBlockEntity => StructureBlockBlockEntity, + InternalTestBlockBlockEntity => TestBlockBlockEntity, + InternalTestInstanceBlockBlockEntity => TestInstanceBlockBlockEntity, + InternalTrialSpawnerBlockEntity => TrialSpawnerBlockEntity, + InternalVaultBlockEntity => VaultBlockEntity, + } + + Ok(None) } } diff --git a/tools/pumpkin-codegen/src/dye_color.rs b/tools/pumpkin-codegen/src/dye_color.rs new file mode 100644 index 000000000..c42569cb7 --- /dev/null +++ b/tools/pumpkin-codegen/src/dye_color.rs @@ -0,0 +1,194 @@ +use std::fs; + +use heck::{ToPascalCase, ToShoutySnakeCase}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct DyeColorEntry { + id: u8, + name: String, + map_color_id: u8, + terracotta_color_id: u8, + texture_diffuse_color: u32, + firework_color: u32, + text_color: u32, +} + +pub fn build() -> TokenStream { + let dye_colors: Vec = + serde_json::from_str(&fs::read_to_string("../../assets/dye_colors.json").unwrap()) + .expect("Failed to parse dye_colors.json"); + + let mut variants = TokenStream::new(); + let mut id_match_arms = TokenStream::new(); + let mut name_match_arms = TokenStream::new(); + let mut to_id_arms = TokenStream::new(); + let mut to_name_arms = TokenStream::new(); + let mut map_color_id_arms = TokenStream::new(); + let mut terracotta_color_id_arms = TokenStream::new(); + let mut texture_diffuse_color_arms = TokenStream::new(); + let mut firework_color_arms = TokenStream::new(); + let mut text_color_arms = TokenStream::new(); + + for entry in &dye_colors { + let id = entry.id; + let name_str = &entry.name; + let variant_ident = format_ident!("{}", name_str.to_pascal_case()); + let map_color_id = entry.map_color_id; + let terracotta_color_id = entry.terracotta_color_id; + let texture_diffuse_color = entry.texture_diffuse_color; + let firework_color = entry.firework_color; + let text_color = entry.text_color; + + let is_default = if name_str == "black" { + quote! { #[default] } + } else { + quote! {} + }; + + variants.extend(quote! { + #is_default + #variant_ident, + }); + + id_match_arms.extend(quote! { + #id => Some(Self::#variant_ident), + }); + + name_match_arms.extend(quote! { + #name_str => Some(Self::#variant_ident), + }); + + to_id_arms.extend(quote! { + Self::#variant_ident => #id, + }); + + to_name_arms.extend(quote! { + Self::#variant_ident => #name_str, + }); + + map_color_id_arms.extend(quote! { + Self::#variant_ident => #map_color_id, + }); + + terracotta_color_id_arms.extend(quote! { + Self::#variant_ident => #terracotta_color_id, + }); + + texture_diffuse_color_arms.extend(quote! { + Self::#variant_ident => #texture_diffuse_color, + }); + + firework_color_arms.extend(quote! { + Self::#variant_ident => #firework_color, + }); + + text_color_arms.extend(quote! { + Self::#variant_ident => #text_color, + }); + } + + quote! { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] + pub enum DyeColor { + #variants + } + + impl DyeColor { + #[must_use] + pub const fn id(&self) -> u8 { + match self { + #to_id_arms + } + } + + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + #to_name_arms + } + } + + #[must_use] + pub const fn map_color_id(&self) -> u8 { + match self { + #map_color_id_arms + } + } + + #[must_use] + pub const fn terracotta_color_id(&self) -> u8 { + match self { + #terracotta_color_id_arms + } + } + + #[must_use] + pub const fn texture_diffuse_color(&self) -> u32 { + match self { + #texture_diffuse_color_arms + } + } + + #[must_use] + pub const fn firework_color(&self) -> u32 { + match self { + #firework_color_arms + } + } + + #[must_use] + pub const fn text_color(&self) -> u32 { + match self { + #text_color_arms + } + } + + #[must_use] + pub const fn by_id(id: u8) -> Option { + match id { + #id_match_arms + _ => None, + } + } + + #[must_use] + pub fn by_name(name: &str) -> Option { + match name { + #name_match_arms + _ => None, + } + } + } + + impl From for String { + fn from(value: DyeColor) -> Self { + value.name().to_string() + } + } + + impl From<&str> for DyeColor { + fn from(s: &str) -> Self { + DyeColor::by_name(s).unwrap_or_default() + } + } + + impl From for DyeColor { + fn from(s: i8) -> Self { + if s >= 0 { + DyeColor::by_id(s as u8).unwrap_or_default() + } else { + DyeColor::default() + } + } + } + + impl From for DyeColor { + fn from(s: u8) -> Self { + DyeColor::by_id(s).unwrap_or_default() + } + } + } +} diff --git a/tools/pumpkin-codegen/src/main.rs b/tools/pumpkin-codegen/src/main.rs index f409599a9..1d329f397 100644 --- a/tools/pumpkin-codegen/src/main.rs +++ b/tools/pumpkin-codegen/src/main.rs @@ -42,6 +42,7 @@ mod configured_feature; mod damage_type; mod data_component; mod dimension; +mod dye_color; mod effect; mod enchantments; mod entity_pose; @@ -55,6 +56,8 @@ mod game_rules; mod item; mod jukebox_song; pub mod loot; +mod map_color; +mod map_decoration; mod message_type; mod meta_data_type; mod noise_parameter; @@ -160,6 +163,9 @@ pub fn main() { ), (carver::build, "carver.rs"), (chest_loot::build, "chest_loot.rs"), + (map_color::build, "map_color.rs"), + (map_decoration::build, "map_decoration.rs"), + (dye_color::build, "dye_color.rs"), ]; build_functions.extend(remap::build()); diff --git a/tools/pumpkin-codegen/src/map_color.rs b/tools/pumpkin-codegen/src/map_color.rs new file mode 100644 index 000000000..507f9dc5d --- /dev/null +++ b/tools/pumpkin-codegen/src/map_color.rs @@ -0,0 +1,99 @@ +use std::fs; + +use heck::{ToPascalCase, ToShoutySnakeCase}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct MapColorEntry { + id: u8, + name: String, + col: u32, + hex: String, + rgb: (u8, u8, u8), +} + +pub fn build() -> TokenStream { + let colors: Vec = + serde_json::from_str(&fs::read_to_string("../../assets/map_colors.json").unwrap()) + .expect("Failed to parse map_colors.json"); + + let mut constants = TokenStream::new(); + let mut all_elements = TokenStream::new(); + let mut id_match_arms = TokenStream::new(); + let mut name_match_arms = TokenStream::new(); + + let mut seen_names = std::collections::HashSet::new(); + + for entry in &colors { + let id = entry.id; + let name = &entry.name; + let const_name = if name == "none" { + format_ident!("NONE_{}", id) + } else { + format_ident!("{}", name.to_shouty_snake_case()) + }; + + let col = entry.col; + let (r, g, b) = entry.rgb; + + constants.extend(quote! { + pub const #const_name: MapColor = MapColor { + id: #id, + name: #name, + col: #col, + rgb: (#r, #g, #b), + }; + }); + + all_elements.extend(quote! { + MapColor::#const_name, + }); + + id_match_arms.extend(quote! { + #id => Some(&Self::#const_name), + }); + + if seen_names.insert(name.clone()) { + name_match_arms.extend(quote! { + #name => Some(&Self::#const_name), + }); + } + } + + quote! { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct MapColor { + pub id: u8, + pub name: &'static str, + pub col: u32, + pub rgb: (u8, u8, u8), + } + + impl MapColor { + #constants + + pub const ALL: &'static [MapColor] = &[ + #all_elements + ]; + + #[must_use] + pub const fn from_id(id: u8) -> Option<&'static MapColor> { + if (id as usize) < Self::ALL.len() { + Some(&Self::ALL[id as usize]) + } else { + None + } + } + + #[must_use] + pub fn from_name(name: &str) -> Option<&'static MapColor> { + match name { + #name_match_arms + _ => None, + } + } + } + } +} diff --git a/tools/pumpkin-codegen/src/map_decoration.rs b/tools/pumpkin-codegen/src/map_decoration.rs new file mode 100644 index 000000000..ae9b714bc --- /dev/null +++ b/tools/pumpkin-codegen/src/map_decoration.rs @@ -0,0 +1,95 @@ +use std::fs; + +use heck::{ToPascalCase, ToShoutySnakeCase}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct MapDecorationEntry { + name: String, + asset_name: String, + show_on_item_frame: bool, + map_color: i32, + exploration_map_element: bool, + track_count: bool, +} + +pub fn build() -> TokenStream { + let decorations: Vec = + serde_json::from_str(&fs::read_to_string("../../assets/map_decorations.json").unwrap()) + .expect("Failed to parse map_decorations.json"); + + let mut constants = TokenStream::new(); + let mut all_elements = TokenStream::new(); + let mut name_match_arms = TokenStream::new(); + + for (index, entry) in decorations.iter().enumerate() { + let id = index as u32; + let name = &entry.name; + let const_name = format_ident!("{}", name.to_shouty_snake_case()); + let asset_name = &entry.asset_name; + let show_on_item_frame = entry.show_on_item_frame; + let map_color = entry.map_color; + let exploration_map_element = entry.exploration_map_element; + let track_count = entry.track_count; + + constants.extend(quote! { + pub const #const_name: MapDecorationType = MapDecorationType { + id: #id, + name: #name, + asset_name: #asset_name, + show_on_item_frame: #show_on_item_frame, + map_color: #map_color, + exploration_map_element: #exploration_map_element, + track_count: #track_count, + }; + }); + + all_elements.extend(quote! { + MapDecorationType::#const_name, + }); + + name_match_arms.extend(quote! { + #name => Some(&Self::#const_name), + }); + } + + quote! { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub struct MapDecorationType { + pub id: u32, + pub name: &'static str, + pub asset_name: &'static str, + pub show_on_item_frame: bool, + pub map_color: i32, + pub exploration_map_element: bool, + pub track_count: bool, + } + + impl MapDecorationType { + #constants + + pub const ALL: &'static [MapDecorationType] = &[ + #all_elements + ]; + + #[must_use] + pub const fn from_id(id: u32) -> Option<&'static MapDecorationType> { + if (id as usize) < Self::ALL.len() { + Some(&Self::ALL[id as usize]) + } else { + None + } + } + + #[must_use] + pub fn from_name(name: &str) -> Option<&'static MapDecorationType> { + match name { + #name_match_arms + _ => None, + } + } + } + } +}