Add the `broadcast_console_to_ops` configuration option matching
vanilla's `broadcast-console-to-ops` server property. When set to
`false`, suppresses console and RCON command output from being
broadcast to online operators.
- Add `broadcast_console_to_ops` field to `CommandsConfig` (defaults
to `true` for vanilla compatibility)
- Track the setting via an `AtomicBool` in the command module
- Replace hardcoded `true` in `should_broadcast_console_to_ops` for
Console and RCON senders with the configurable value
- Initialize the setting during server startup from advanced config
Co-authored-by: Alexander Medvedev <lilalexmed@proton.me>
* fix(lighting): stop sky light updates from looping on unloaded chunks
The sky light propagation could spin forever when a light update happened
next to a chunk that wasn't loaded. Writes to an unloaded chunk are dropped
silently and reads come back as 0, so the "this neighbor is darker than us"
check stayed true on every pass and the same position kept getting queued
again. In practice this hangs a tick thread when a block is broken near the
edge of the loaded area.
Skip neighbors whose chunk isn't loaded in both the increase and decrease
passes, matching how the border of loaded chunks already behaves: light
doesn't bleed into chunks that aren't there yet.
* Use Level::is_chunk_loaded for the unloaded-chunk guard
Block entities lived in one flat DashMap keyed by BlockPos, and every world
tick rebuilt the tick list by iterating the entire map and filtering it down
to the active chunks. That is O(all loaded block entities): the more of the
world that has been explored (chests, signs, hoppers... all count), the
longer every tick spends walking block entities it is not going to tick, so
TPS slowly bleeds away on long-running worlds.
Key the map by chunk instead (chunk -> its block entities). Ticking now walks
only the handful of active chunks and their entries, inserts/removals/lookups
compute the chunk key up front, and unloading a chunk drops its whole bucket.
Behaviour is unchanged - the same block entities get ticked - it just stops
scanning the ones that are loaded but nowhere near a player.
The world tick iterated every entity in the world and ticked all of them,
regardless of where they were - so entities sitting in loaded-but-not-active
chunks kept running their AI, movement and player-collision checks every tick.
That is O(all loaded entities) and grows as a world is explored, bleeding TPS
on long-running servers.
Skip entities whose chunk isn't in the active (ticking) set - the same set
block-entity ticking and mob spawning already use, and matching vanilla, which
only ticks entities within the simulation distance. The check uses the live
position rather than the cached chunk_pos, since fast movers (minecarts,
projectiles) update pos directly and leave chunk_pos stale.
Players are ticked separately and aren't in this list, so they're unaffected.
* fix: prevent capacity overflow crash when flying with elytra or in creative mode
The NoiseBasedCountPlacementModifier::get_count() can return negative i32
values when foliage noise sampling produces negative results at certain
world coordinates. The previous code cast this negative i32 directly to
usize, causing an integer wrap to ~18 quintillion, which triggered a
capacity overflow panic in Vec allocation during chunk feature generation.
This aligns with vanilla Minecraft behavior which also clamps the count
to a minimum of 0 before using it.
Closes#2345
* Hi
* implementing the advancement configuration for saving or not the advancements
* adding a test for not loading files when save is disabled
* cargo fmt
* fix merge
* Fix variable name for players directory
* sin/cos util
* 4 nieghbor check
* formating
* air helper
* carver wrapper
* fmt
* clippy
* clipp2
* remove local y
* out of bound guard
* fluid tick
* surface rule
* wire up
* math correct
* start fix
* bring back xoroshiro oops
* scheduler guard
* simplify test
* clean test
* comment
* crash fix
* clippy
* clippy
* i hate you codex
* sin cos
* im stupid f32
Rebased onto current master after #2296 (PlayerRespawnEvent).
- Fire PlayerChangeWorldEvent (cancellable) before the cross-dimension
transfer in World::respawn_player; a plugin may redirect new_world,
override position/yaw/pitch, or cancel to keep the player in the
current world.
- Use the safe transfer ordering (remove_player -> unload_watched_chunks
-> change_world -> set_world -> publish) so no observer sees the player
in a world whose chunk manager doesn't match, and update the entity's
world reference (set_world) to fix the latent stale-world bug.
- When cancelled or the target world can't be resolved, fall back to the
current world's spawn.
- The non-cancellable PlayerRespawnEvent (#2296) now fires after this
event and observes the resolved world automatically; document the
ordering and mutation-vs-cancellation semantics on both events.
* refactor: less manual BlockMetadata impls
* refactor: added BlockId type
BlockId is a wrapper for u16: it is valid for any u16 that is the id of a Block.
- Changed the Block.id field type to BlockId
- added named BlockId constants
- BlockMetadata::ids() now returns Box<[BlockId]>
- adjusted pumpkin-macros to use BlockId constants
- fixed some methods that were comparing blockstate ids or item ids (u16) against block ids (previously u16)
TODO: check if unsafe blocks can be removed; The compiler might understand that BlockId is always a valid index into mappings::TYPE_FROM_RAW_ID
* refactor: added BlockStateId type
A BlockStateId is a safe wrapper around the numerical index of a BlockState in pumpkin-data. They help avoiding validity checks (outside of IO and, currently, plugins), and make it easier for other contributors to reason about what they're comparing; BlockStateIds, BlockIds or Item ids (still u16).
pumpkin-data::BlockStateId replaces RawBlockState and BlockStateId from pumpkin-world.
- added the BlockStateId wrapper type
- made (almost; plugins) every function interacting with block states or block state ids use the wrapper type
- changed codegen logic to create/work with BlockStateIds
- made ChunkPalette parsing check BlockStateId validity (pumpkin-world::chunk::format::ChunkSectionBlockStates)
TODO: check if unsafe blocks can be removed; The compiler might understand that BlockStateId is always a valid index into mappings::BLOCK_ID_FROM_STATE_ID and mappings::STATE_FROM_STATE_ID
* refactor: imports
changed every use pumpkin_world::BlockStateId to pumpkin_data::BlockStateId
* refactor: Block- & BlockStateId
finishing touches;
- rebased on latest mater
- ensure there are no bound checks on Block(State)Id conversions, making them extremely cheap
- this required unsafe std::hint::assert_unchecked annotations because the compiler is (occasionally) stupid
- on debug builds the bound checks still exist because of ub_checks (see rust unstable book for the feature of the same name)
- added Safety notes to hopefully prevent anyone from enabling the creation of invalid Block(State)Ids in the future
- fixed a benchmark
The `Swap` branch of `internal_on_slot_click` indexed
`self.get_behaviour().slots[slot_index as usize]` without first checking
`slot_index >= 0`, unlike every sibling branch (Pickup, QuickMove, Throw,
Clone, QuickCraft) which all guard with `if slot_index < 0 { return; }`.
`slot_index` originates from the client `SClickSlot` packet (`i16` slot
field, widened to `i32`). The upstream validation in `Player::on_slot_click`
relies on `ScreenHandler::is_slot_valid`, whose check
`slot == -1 || slot == -999 || slot < slots.len() as i32` returns `true`
for ANY negative slot (a negative value is always `< slots.len()`), so
negative indices are not filtered out before reaching the handler. A
crafted Swap packet with a negative slot (e.g. -5) and a hotbar button in
`0..9` (or 40) therefore evaluated `slots[(-5) as usize]`, an enormous
index, causing an out-of-bounds Vec panic and crashing the server.
Add the same `if slot_index < 0 { return; }` guard at the top of the Swap
branch, matching the sibling branches exactly. Minimal, low-risk: legitimate
Swap clicks always carry a non-negative container slot, so behaviour is
unchanged for valid input.
BlockPalette::liquid_block_count() returned the inverse of what it
should: the Homogeneous arm returned 0 when the section was liquid and
the full VOLUME (4096) when it was not, and the Heterogeneous arm summed
the counts of non-liquid blocks (filtering on !is_liquid).
Flip both arms to actually count liquid blocks, mirroring the correct
sibling non_air_block_count(): Homogeneous yields VOLUME when the single
block is liquid (0 otherwise) and Heterogeneous filters on is_liquid.
This value is sent to clients as the fluid count in chunk data
(MC 26.1+), so the inversion reported wrong fluid counts to players.