feat(plugin-api): add get biome
This commit is contained in:
parent
e2ff17d6f0
commit
95893b7463
37
pumpkin-codegen/src/wit/biome.rs
Normal file
37
pumpkin-codegen/src/wit/biome.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use semver::Version;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs;
|
||||||
|
use wit_encoder::{Enum, Interface, Package, PackageName, TypeDef, TypeDefKind};
|
||||||
|
|
||||||
|
pub fn build() -> String {
|
||||||
|
let json: BTreeMap<String, serde_json::Value> =
|
||||||
|
serde_json::from_str(&fs::read_to_string("../assets/biome.json").unwrap())
|
||||||
|
.expect("Failed to parse biome.json");
|
||||||
|
|
||||||
|
let mut package = Package::new(PackageName::new(
|
||||||
|
"pumpkin",
|
||||||
|
"plugin",
|
||||||
|
Some(Version::new(0, 1, 0)),
|
||||||
|
));
|
||||||
|
let mut interface = Interface::new("biomes");
|
||||||
|
|
||||||
|
let mut biome_enum = Enum::empty();
|
||||||
|
let mut biome_vec = json.keys().collect::<Vec<_>>();
|
||||||
|
biome_vec.sort();
|
||||||
|
|
||||||
|
for raw_name in biome_vec {
|
||||||
|
let name = raw_name
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(raw_name)
|
||||||
|
.replace('_', "-");
|
||||||
|
biome_enum.case(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface.type_def(TypeDef::new(
|
||||||
|
"biome",
|
||||||
|
TypeDefKind::Enum(biome_enum),
|
||||||
|
));
|
||||||
|
package.interface(interface);
|
||||||
|
|
||||||
|
package.to_string()
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod bedrock_packet;
|
pub mod bedrock_packet;
|
||||||
|
pub mod biome;
|
||||||
pub mod data_component;
|
pub mod data_component;
|
||||||
pub mod enchantment;
|
pub mod enchantment;
|
||||||
pub mod entity_type;
|
pub mod entity_type;
|
||||||
@ -30,6 +31,7 @@ pub fn main() {
|
|||||||
(bedrock_packet::build, "bedrock-packets.wit"),
|
(bedrock_packet::build, "bedrock-packets.wit"),
|
||||||
(data_component::build, "data-components.wit"),
|
(data_component::build, "data-components.wit"),
|
||||||
(enchantment::build, "enchantments.wit"),
|
(enchantment::build, "enchantments.wit"),
|
||||||
|
(biome::build, "biomes.wit"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (build_fn, file) in build_functions {
|
for (build_fn, file) in build_functions {
|
||||||
|
|||||||
71
pumpkin-plugin-wit/v0.1/biomes.wit
Normal file
71
pumpkin-plugin-wit/v0.1/biomes.wit
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package pumpkin:plugin@0.1.0;
|
||||||
|
|
||||||
|
interface biomes {
|
||||||
|
enum biome {
|
||||||
|
badlands,
|
||||||
|
bamboo-jungle,
|
||||||
|
basalt-deltas,
|
||||||
|
beach,
|
||||||
|
birch-forest,
|
||||||
|
cherry-grove,
|
||||||
|
cold-ocean,
|
||||||
|
crimson-forest,
|
||||||
|
dark-forest,
|
||||||
|
deep-cold-ocean,
|
||||||
|
deep-dark,
|
||||||
|
deep-frozen-ocean,
|
||||||
|
deep-lukewarm-ocean,
|
||||||
|
deep-ocean,
|
||||||
|
desert,
|
||||||
|
dripstone-caves,
|
||||||
|
end-barrens,
|
||||||
|
end-highlands,
|
||||||
|
end-midlands,
|
||||||
|
eroded-badlands,
|
||||||
|
flower-forest,
|
||||||
|
forest,
|
||||||
|
frozen-ocean,
|
||||||
|
frozen-peaks,
|
||||||
|
frozen-river,
|
||||||
|
grove,
|
||||||
|
ice-spikes,
|
||||||
|
jagged-peaks,
|
||||||
|
jungle,
|
||||||
|
lukewarm-ocean,
|
||||||
|
lush-caves,
|
||||||
|
mangrove-swamp,
|
||||||
|
meadow,
|
||||||
|
mushroom-fields,
|
||||||
|
nether-wastes,
|
||||||
|
ocean,
|
||||||
|
old-growth-birch-forest,
|
||||||
|
old-growth-pine-taiga,
|
||||||
|
old-growth-spruce-taiga,
|
||||||
|
pale-garden,
|
||||||
|
plains,
|
||||||
|
river,
|
||||||
|
savanna,
|
||||||
|
savanna-plateau,
|
||||||
|
small-end-islands,
|
||||||
|
snowy-beach,
|
||||||
|
snowy-plains,
|
||||||
|
snowy-slopes,
|
||||||
|
snowy-taiga,
|
||||||
|
soul-sand-valley,
|
||||||
|
sparse-jungle,
|
||||||
|
stony-peaks,
|
||||||
|
stony-shore,
|
||||||
|
sunflower-plains,
|
||||||
|
swamp,
|
||||||
|
taiga,
|
||||||
|
the-end,
|
||||||
|
the-void,
|
||||||
|
warm-ocean,
|
||||||
|
warped-forest,
|
||||||
|
windswept-forest,
|
||||||
|
windswept-gravelly-hills,
|
||||||
|
windswept-hills,
|
||||||
|
windswept-savanna,
|
||||||
|
wooded-badlands,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ interface %world {
|
|||||||
use particles.{particle};
|
use particles.{particle};
|
||||||
use sounds.{sound};
|
use sounds.{sound};
|
||||||
use entity-types.{entity-type};
|
use entity-types.{entity-type};
|
||||||
|
use biomes.{biome};
|
||||||
use uuid.{uuid};
|
use uuid.{uuid};
|
||||||
|
|
||||||
/// Defines how a block reacts when pushed by a piston.
|
/// Defines how a block reacts when pushed by a piston.
|
||||||
@ -258,6 +259,12 @@ interface %world {
|
|||||||
/// Sets the block light level at the specified position.
|
/// Sets the block light level at the specified position.
|
||||||
set-block-light: func(pos: block-pos, level: u8);
|
set-block-light: func(pos: block-pos, level: u8);
|
||||||
|
|
||||||
|
/// Returns the biome at the specified position.
|
||||||
|
get-biome: func(pos: block-pos) -> biome;
|
||||||
|
|
||||||
|
/// Spawns an entity of the specified type at the given position.
|
||||||
|
spawn-entity: func(entity-type: entity-type, pos: position) -> entity;
|
||||||
|
|
||||||
/// Returns a list of all entities in this world.
|
/// Returns a list of all entities in this world.
|
||||||
get-entities: func() -> list<entity>;
|
get-entities: func() -> list<entity>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,32 +72,23 @@ impl HostEntity for PluginHostState {
|
|||||||
entity: Resource<Entity>,
|
entity: Resource<Entity>,
|
||||||
) -> wasmtime::Result<entity_types::EntityType> {
|
) -> wasmtime::Result<entity_types::EntityType> {
|
||||||
let entity = entity_from_resource(self, &entity)?;
|
let entity = entity_from_resource(self, &entity)?;
|
||||||
let name = entity.get_entity().entity_type.resource_name.to_string();
|
let original_name = entity.get_entity().entity_type.resource_name;
|
||||||
// Standard WIT enum mapping: kebab-case
|
|
||||||
let _wit_name = name.replace('_', "-");
|
|
||||||
// We need to find the variant in the generated enum.
|
|
||||||
// Since we don't have a direct mapping function yet, we use debug format or similar if available,
|
|
||||||
// but for now let's just use the fact that it's an enum.
|
|
||||||
// Actually, the easiest way is to use the generated `from_name` or similar if we added it,
|
|
||||||
// but here we are returning it TO the guest.
|
|
||||||
|
|
||||||
// Wait, I need to CONSTRUCT the EntityType enum.
|
let mut names: Vec<String> = serde_json::from_str::<
|
||||||
// I can't easily do it by string unless I have a mapping.
|
std::collections::BTreeMap<String, serde_json::Value>,
|
||||||
// But wit-bindgen generates the enum.
|
>(&std::fs::read_to_string("assets/entities.json")?)?
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
names.sort();
|
||||||
|
|
||||||
// Let's assume there is a way to construct it.
|
let index = names
|
||||||
// Actually, I can use the fact that it's a simple enum.
|
.iter()
|
||||||
// I'll use a hack for now if I don't have a better way, but wait!
|
.position(|n| n == original_name)
|
||||||
// I can just use `serde_json` to parse the string into the enum if it implements Deserialize!
|
.ok_or_else(|| wasmtime::Error::msg(format!("Unknown entity type: {original_name}")))?;
|
||||||
// Or just a match.
|
|
||||||
|
|
||||||
// Better: let's see if pumpkin_data has a way.
|
// Safety: The WIT enum is generated from the sorted keys of assets/entities.json.
|
||||||
// For now, I'll just return a placeholder or implement a basic match for the most common ones
|
Ok(unsafe { std::mem::transmute::<u8, entity_types::EntityType>(index as u8) })
|
||||||
// OR better: use the generated code.
|
|
||||||
|
|
||||||
Err(wasmtime::Error::msg(
|
|
||||||
"get_type implementation pending full enum mapping",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
async fn get_position(&mut self, entity: Resource<Entity>) -> wasmtime::Result<Position> {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ impl pumpkin::plugin::java_packets::Host for PluginHostState {}
|
|||||||
impl pumpkin::plugin::bedrock_packets::Host for PluginHostState {}
|
impl pumpkin::plugin::bedrock_packets::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::data_components::Host for PluginHostState {}
|
impl pumpkin::plugin::data_components::Host for PluginHostState {}
|
||||||
impl pumpkin::plugin::enchantments::Host for PluginHostState {}
|
impl pumpkin::plugin::enchantments::Host for PluginHostState {}
|
||||||
|
impl pumpkin::plugin::biomes::Host for PluginHostState {}
|
||||||
|
|
||||||
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
|
pub fn add_to_linker(linker: &mut Linker<PluginHostState>) -> wasmtime::Result<()> {
|
||||||
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
Plugin::add_to_linker::<_, HasSelf<_>>(linker, |state: &mut PluginHostState| state)?;
|
||||||
|
|||||||
@ -403,6 +403,69 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_biome(
|
||||||
|
&mut self,
|
||||||
|
world: Resource<World>,
|
||||||
|
pos: WitBlockPos,
|
||||||
|
) -> wasmtime::Result<pumpkin::plugin::biomes::Biome> {
|
||||||
|
let world_ref = self.get_world_res(&world)?;
|
||||||
|
let internal_pos = BlockPos::new(pos.x, pos.y, pos.z);
|
||||||
|
let biome = world_ref.provider.get_biome(&internal_pos);
|
||||||
|
|
||||||
|
let mut names: Vec<String> = serde_json::from_str::<
|
||||||
|
std::collections::BTreeMap<String, serde_json::Value>,
|
||||||
|
>(&std::fs::read_to_string("assets/biome.json")?)?
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
names.sort();
|
||||||
|
|
||||||
|
let index = names
|
||||||
|
.iter()
|
||||||
|
.position(|n| n.strip_prefix("minecraft:").unwrap_or(n) == biome.registry_id)
|
||||||
|
.ok_or_else(|| wasmtime::Error::msg(format!("Unknown biome: {}", biome.registry_id)))?;
|
||||||
|
|
||||||
|
// Safety: The WIT enum is generated from the sorted keys of assets/biome.json.
|
||||||
|
Ok(unsafe { std::mem::transmute::<u8, pumpkin::plugin::biomes::Biome>(index as u8) })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_entity(
|
||||||
|
&mut self,
|
||||||
|
world: Resource<World>,
|
||||||
|
entity_type: pumpkin::plugin::entity_types::EntityType,
|
||||||
|
pos: pumpkin::plugin::common::Position,
|
||||||
|
) -> wasmtime::Result<Resource<pumpkin::plugin::world::Entity>> {
|
||||||
|
let world_ref = self.get_world_res(&world)?;
|
||||||
|
let world_provider = world_ref.provider.clone();
|
||||||
|
|
||||||
|
let mut names: Vec<String> = serde_json::from_str::<
|
||||||
|
std::collections::BTreeMap<String, serde_json::Value>,
|
||||||
|
>(&std::fs::read_to_string("assets/entities.json")?)?
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
names.sort();
|
||||||
|
|
||||||
|
let type_name = names.get(entity_type as usize).ok_or_else(|| {
|
||||||
|
wasmtime::Error::msg(format!("Invalid entity type index: {}", entity_type as u8))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let internal_type = pumpkin_data::entity::EntityType::from_name(type_name)
|
||||||
|
.ok_or_else(|| wasmtime::Error::msg(format!("Invalid entity type: {type_name}")))?;
|
||||||
|
|
||||||
|
let internal_pos = pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2);
|
||||||
|
let entity = crate::entity::r#type::from_type(
|
||||||
|
internal_type,
|
||||||
|
internal_pos,
|
||||||
|
&world_provider,
|
||||||
|
uuid::Uuid::new_v4(),
|
||||||
|
);
|
||||||
|
|
||||||
|
world_provider.spawn_entity(entity.clone()).await;
|
||||||
|
|
||||||
|
self.add_entity(entity)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_entities(
|
async fn get_entities(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: Resource<World>,
|
world: Resource<World>,
|
||||||
|
|||||||
@ -3444,6 +3444,23 @@ impl World {
|
|||||||
.set_sky_light_level(&self.level, position, light_level);
|
.set_sky_light_level(&self.level, position, light_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_biome(&self, position: &BlockPos) -> &'static Biome {
|
||||||
|
let chunk_pos = position.chunk_position();
|
||||||
|
if let Some(chunk) = self.level.loaded_chunks.get(&chunk_pos) {
|
||||||
|
let id = chunk
|
||||||
|
.section
|
||||||
|
.get_rough_biome_absolute_y(
|
||||||
|
(position.0.x & 15) as usize,
|
||||||
|
position.0.y,
|
||||||
|
(position.0.z & 15) as usize,
|
||||||
|
)
|
||||||
|
.unwrap_or(0);
|
||||||
|
Biome::from_id(id).unwrap_or(&Biome::PLAINS)
|
||||||
|
} else {
|
||||||
|
&Biome::PLAINS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn schedule_block_tick(
|
pub fn schedule_block_tick(
|
||||||
&self,
|
&self,
|
||||||
block: &Block,
|
block: &Block,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user