mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
control: horizon-ctl summon spawns server-ticking mobs (fixes #9207)
When running single player on an integrated server, `horizon-ctl summon` spawned mobs directly into the client window's entity list rather than delegating to the server thread. Because the client window does not tick mobs (running `tick_remote` instead of `tick_world`), summoned mobs never executed AI goals, took no environmental damage, and were not visible to server queries. Delegate `summon` to the integrated server in the owner player's level when `self.integrated` is active, matching other control world commands and allowing the server to tick the mob and broadcast it via standard entity tracking. Managed by Axxio. Co-Authored-By: Antigravity <[email protected]>
This commit is contained in:
1 parent
6c5cd897fc
commit
9f346f9794
2 files changed
+29
-10
No files matched your search
@@ -851,7 +851,24 @@ impl Game<'_> {
|
||||
let baby = words.contains(&"baby");
|
||||
let nums: Vec<f64> = words[2..].iter().filter_map(|w| w.parse().ok()).collect();
|
||||
let at = (nums.len() >= 3).then(|| [nums[0], nums[1], nums[2]]);
|
||||
let id = self.summon(arg(1)?, at, baby)?;
|
||||
let kind = arg(1)?.to_owned();
|
||||
// Single player on its server thread: spawn in the owner's level
|
||||
// so the mob enters the server tick loop and is tracked to clients.
|
||||
if let Some(server) = &self.integrated {
|
||||
let at = match at {
|
||||
Some(a) => a,
|
||||
None => self.default_summon_position()?,
|
||||
};
|
||||
let id = server
|
||||
.call(move |g| {
|
||||
g.in_owner_level(|g| {
|
||||
g.summon(&kind, Some(at), baby).map_err(|e| e.to_string())
|
||||
})
|
||||
})
|
||||
.ok_or(super::integrated::NO_ANSWER)??;
|
||||
return Ok(Some(json!({ "ok": true, "id": id })));
|
||||
}
|
||||
let id = self.summon(&kind, at, baby)?;
|
||||
Ok(Some(json!({ "ok": true, "id": id })))
|
||||
}
|
||||
"mobs" => {
|
||||
|
||||
@@ -691,19 +691,21 @@ impl Game<'_> {
|
||||
self.native_living_conversion_post(from.id, &from_kind, id, &transformed_kind);
|
||||
Some(id)
|
||||
}
|
||||
/// Default position for a summon command (3 blocks in front of the player).
|
||||
pub(super) fn default_summon_position(&self) -> Result<[f64; 3]> {
|
||||
let p = self.player.as_ref().ok_or("no player yet")?;
|
||||
let d = view_vector(self.yaw, 0.0);
|
||||
Ok([
|
||||
p.position.x + d[0] * 3.0,
|
||||
p.position.y,
|
||||
p.position.z + d[2] * 3.0,
|
||||
])
|
||||
}
|
||||
/// The `summon` command: spawn a mob in front of the player or at a position.
|
||||
pub(super) fn summon(&mut self, kind: &str, at: Option<[f64; 3]>, baby: bool) -> Result<u32> {
|
||||
let at = match at {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
let p = self.player.as_ref().ok_or("no player yet")?;
|
||||
let d = view_vector(self.yaw, 0.0);
|
||||
[
|
||||
p.position.x + d[0] * 3.0,
|
||||
p.position.y,
|
||||
p.position.z + d[2] * 3.0,
|
||||
]
|
||||
}
|
||||
None => self.default_summon_position()?,
|
||||
};
|
||||
let id = self
|
||||
.spawn_mob_spawned(kind, at)
|
||||
|
||||
Reference in new issue
Block a user