fix: unix rlimit

This commit is contained in:
Alexander Medvedev 2026-09-03 20:13:34 +02:00
parent f8d1627697
commit 9258bb9023
32 changed files with 401 additions and 155 deletions

1
Cargo.lock generated
View File

@ -3475,6 +3475,7 @@ dependencies = [
"hyper",
"image",
"indexmap",
"libc",
"libloading",
"notify",
"num-bigint 0.5.1",

View File

@ -163,6 +163,7 @@ heck = { version = "0.5", default-features = false }
hmac = { version = "=0.13.0", default-features = false }
indexmap = { version = "2.14", default-features = false, features = ["std"] }
itertools = { version = "0.15.0", default-features = false, features = ["use_std"] }
libc = { version = "0.2", default-features = false }
libloading = { version = "0.9", default-features = false, features = ["std"] }
lru = { version = "0.18.3", default-features = false }
lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] }

View File

@ -17,7 +17,7 @@ pub struct QueryConfig {
impl Default for QueryConfig {
fn default() -> Self {
Self {
enabled: true,
enabled: false,
address: SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 25565),
}
}

View File

@ -379,4 +379,3 @@ impl ClientPacket for CSpawnLivingEntity {
Ok(())
}
}

View File

@ -100,6 +100,7 @@ rustc-hash.workspace = true
tokio-util = { workspace = true, features = ["rt"] }
# System details & Rustc version
libc = { workspace = true }
sysinfo.workspace = true
rustc_version_runtime.workspace = true

View File

@ -59,7 +59,8 @@ impl BlockBehaviour for FarmlandBlock {
let mut props = FarmlandProperties::default(args.block);
let mut new_moisture = 7;
if let Some(server) = args.world.server.upgrade() {
let mut event = crate::plugin::api::events::block::moisture_change::MoistureChangeEvent::new(
let mut event =
crate::plugin::api::events::block::moisture_change::MoistureChangeEvent::new(
*args.position,
args.world.clone(),
new_moisture,

View File

@ -102,7 +102,8 @@ impl BlockBehaviour for LeavesBlock {
let props = OakLeavesLikeProperties::from_state_id(state_id);
if !props.persistent && props.distance == DECAY_DISTANCE {
if let Some(server) = args.world.server.upgrade() {
let mut event = crate::plugin::api::events::block::leaves_decay::LeavesDecayEvent::new(
let mut event =
crate::plugin::api::events::block::leaves_decay::LeavesDecayEvent::new(
*args.position,
args.world.clone(),
);

View File

@ -74,7 +74,8 @@ trait CropBlockBase: PlantBlockBase {
if rand::rng().random_range(0..=(25.0 / f).floor() as i64) == 0 {
let new_state_id = self.state_with_age(block, state, age + 1);
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::block::block_grow::BlockGrowEvent::new(
let mut event =
crate::plugin::api::events::block::block_grow::BlockGrowEvent::new(
world.clone(),
block,
state,

View File

@ -56,10 +56,7 @@ impl BlockBehaviour for SweetBerryBushBlock {
}
props.age = 1;
for stack in drops {
args.world.drop_stack(
args.position,
stack,
);
args.world.drop_stack(args.position, stack);
}
args.world.set_block_state(
args.position,

View File

@ -134,7 +134,8 @@ impl CrafterBlock {
let mut result_stack = ItemStack::new(recipe_result.count, item);
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::block::crafter_craft::CrafterCraftEvent::new(
let mut event =
crate::plugin::api::events::block::crafter_craft::CrafterCraftEvent::new(
*pos,
world.clone(),
result_stack.clone(),

View File

@ -465,7 +465,8 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
if fuel_event.cancelled {
false
} else {
self.fuel.store(fuel_event.fuel_power as i32, Ordering::Relaxed);
self.fuel
.store(fuel_event.fuel_power as i32, Ordering::Relaxed);
items[4].decrement(1);
true
}
@ -506,12 +507,15 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity {
}
} else if brewable && self.fuel.load(Ordering::Relaxed) > 0 {
let brew_time = if let Some(server) = world.server.upgrade() {
let mut start_event = crate::plugin::api::events::block::brewing_start::BrewingStartEvent::new(
let mut start_event =
crate::plugin::api::events::block::brewing_start::BrewingStartEvent::new(
self.position,
world.clone(),
400,
);
server.plugin_manager.fire_blocking(&server, &mut start_event);
server
.plugin_manager
.fire_blocking(&server, &mut start_event);
if start_event.cancelled {
return;
}

View File

@ -527,7 +527,8 @@ impl BlockRegistry {
}
if behaviour.is_bonemeal_success(args) {
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::block::block_fertilize::BlockFertilizeEvent::new(
let mut event =
crate::plugin::api::events::block::block_fertilize::BlockFertilizeEvent::new(
*position,
world.clone(),
None,

View File

@ -8,6 +8,7 @@ use crate::command::string_reader::StringReader;
use crate::command::suggestion::suggestions::{Suggestions, SuggestionsBuilder};
use pumpkin_data::entity::EntityType;
use pumpkin_data::translation;
use pumpkin_protocol::java::client::play::SuggestionProviders;
use pumpkin_util::identifier::Identifier;
use pumpkin_util::text::TextComponent;
use std::any::Any;
@ -144,6 +145,7 @@ impl ArgumentType for ResourceArgument {
if self.0 == ENTITY_TYPE_REGISTRY {
let entity_types = EntityType::ALL
.iter()
.filter(|entity_type| entity_type.summonable)
.map(|entity_type| format!("minecraft:{}", entity_type.resource_name));
suggestions_builder
.filter_and_suggest_iter(entity_types)
@ -216,6 +218,10 @@ impl ArgumentType for ResourceArgument {
identifier: self.0.clone(),
}
}
fn override_suggestion_providers(&self) -> Option<SuggestionProviders> {
(self.0 == ENTITY_TYPE_REGISTRY).then_some(SuggestionProviders::SummonableEntities)
}
}
impl ResourceArgument {
@ -228,9 +234,20 @@ impl ResourceArgument {
TextComponent::text(format!("Could not find argument with name '{name}'")),
);
let node = context
.tree
.nodes
.iter()
.find_map(|node| {
.rev()
.find_map(|parsed| {
if let AttachedNode::Argument(cur) = &context.tree[parsed.node]
&& cur.meta.name == name
{
Some(cur)
} else {
None
}
})
.or_else(|| {
context.tree.iter().find_map(|node| {
if let AttachedNode::Argument(cur) = node
&& cur.meta.name == name
{
@ -239,6 +256,7 @@ impl ResourceArgument {
None
}
})
})
.ok_or(missing_argument.clone())?;
let invalid_argument =
DISPATCHER_PARSE_EXCEPTION.create_without_context(TextComponent::text(format!(

View File

@ -1,22 +1,41 @@
use pumpkin_data::translation;
use pumpkin_util::PermissionLvl;
use pumpkin_util::difficulty::Difficulty;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry};
use pumpkin_util::text::TextComponent;
use uuid::Uuid;
use crate::command::argument_builder::{ArgumentBuilder, argument, command};
use crate::command::argument_types::coordinates::vec3::Vec3ArgumentType;
use crate::command::argument_types::nbt::NbtCompoundArgumentType;
use crate::command::argument_types::resource::{ENTITY_TYPE_ARGUMENT, ResourceArgument};
use crate::command::context::command_context::CommandContext;
use crate::command::errors::error_types::CommandErrorType;
use crate::command::node::dispatcher::CommandDispatcher;
use crate::command::node::{CommandExecutor, CommandExecutorResult};
use crate::entity::r#type::from_type;
use crate::world::World;
const DESCRIPTION: &str = "Spawns an entity at position.";
const PERMISSION: &str = "minecraft:command.summon";
const ERROR_FAILED_PEACEFUL: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMANDS_SUMMON_FAILED_PEACEFUL,
translation::bedrock::COMMANDS_SUMMON_FAILED,
);
const ERROR_DUPLICATE_UUID: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMANDS_SUMMON_FAILED_UUID,
translation::bedrock::COMMANDS_SUMMON_FAILED,
);
const INVALID_POSITION: CommandErrorType<0> = CommandErrorType::new(
translation::java::COMMANDS_SUMMON_INVALIDPOSITION,
translation::bedrock::COMMANDS_SUMMON_OUTOFWORLD,
);
struct SummonExecutor {
has_pos: bool,
has_nbt: bool,
}
impl CommandExecutor for SummonExecutor {
@ -28,8 +47,42 @@ impl CommandExecutor for SummonExecutor {
context.source.position
};
let block_pos = BlockPos::floored_v(pos);
if !World::is_valid(block_pos) {
return Err(INVALID_POSITION.create_without_context());
}
let world = context.source.world();
let entity = from_type(entity_type, pos, world, Uuid::new_v4());
let difficulty = world.level_info.load().difficulty;
if difficulty == Difficulty::Peaceful && !entity_type.category.is_friendly {
return Err(ERROR_FAILED_PEACEFUL.create_without_context());
}
let nbt = if self.has_nbt {
Some(NbtCompoundArgumentType::get(context, "nbt")?)
} else {
None
};
let uuid = nbt
.and_then(|nbt| nbt.get_uuid("UUID"))
.unwrap_or_else(Uuid::new_v4);
let entity = from_type(entity_type, pos, world, uuid);
if let Some(nbt) = nbt {
entity.read_nbt_non_mut(nbt);
entity.get_entity().set_pos(pos);
}
if world
.entities
.load()
.iter()
.any(|e| e.get_entity().entity_uuid == entity.get_entity().entity_uuid)
{
return Err(ERROR_DUPLICATE_UUID.create_without_context());
}
let name = entity.get_display_name();
world.spawn_entity(entity);
@ -56,11 +109,69 @@ pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistr
dispatcher.register(
command("summon", DESCRIPTION).requires(PERMISSION).then(
argument("entity", ENTITY_TYPE_ARGUMENT.clone())
.executes(SummonExecutor { has_pos: false })
.executes(SummonExecutor {
has_pos: false,
has_nbt: false,
})
.then(
argument("pos", Vec3ArgumentType::Default)
.executes(SummonExecutor { has_pos: true }),
.executes(SummonExecutor {
has_pos: true,
has_nbt: false,
})
.then(
argument("nbt", NbtCompoundArgumentType).executes(SummonExecutor {
has_pos: true,
has_nbt: true,
}),
),
),
),
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::command::commands::fetchprofile;
use crate::command::context::command_source::CommandSource;
use std::sync::Arc;
#[test]
fn parse_summon_cat() {
let mut dispatcher = CommandDispatcher::new();
let registry = PermissionRegistry::default();
// Register fetchprofile first to ensure its "entity" argument doesn't collide
fetchprofile::register(&mut dispatcher, &registry);
register(&mut dispatcher, &registry);
let source = Arc::new(CommandSource::dummy());
let result = dispatcher.parse_input("summon cat", &source);
assert!(result.errors.is_empty());
let context = result.context.build("summon cat");
let entity_type = ResourceArgument::get_summonable_entity_type(&context, "entity")
.expect("Should resolve summonable cat entity type");
assert_eq!(entity_type.resource_name, "cat");
}
#[test]
fn parse_summon_cat_with_pos_and_nbt() {
let mut dispatcher = CommandDispatcher::new();
let registry = PermissionRegistry::default();
fetchprofile::register(&mut dispatcher, &registry);
register(&mut dispatcher, &registry);
let source = Arc::new(CommandSource::dummy());
let result = dispatcher.parse_input("summon cat ~ ~ ~ {NoAI:1b}", &source);
assert!(result.errors.is_empty());
let context = result.context.build("summon cat ~ ~ ~ {NoAI:1b}");
let entity_type = ResourceArgument::get_summonable_entity_type(&context, "entity")
.expect("Should resolve summonable cat entity type");
assert_eq!(entity_type.resource_name, "cat");
let nbt =
NbtCompoundArgumentType::get(&context, "nbt").expect("Should resolve NBT argument");
assert_eq!(nbt.get_byte("NoAI"), Some(1));
}
}

View File

@ -480,7 +480,15 @@ impl CommandSource {
/// server (i.e. this is a dummy [`CommandSource`].)
#[must_use]
pub fn has_permission(&self, permission: &str) -> bool {
self.output.has_permission(self.server(), permission)
self.server.as_ref().map_or(
matches!(
self.output,
crate::command::CommandSender::Console
| crate::command::CommandSender::Rcon(_)
| crate::command::CommandSender::Dummy
),
|server| self.output.has_permission(server, permission),
)
}
/// Returns whether this source has the permission provided.

View File

@ -606,7 +606,8 @@ impl CombatTracker {
)
}
} else if let Some(killer_name) = kill_credit_name.or_else(|| {
self.get_killer_entry().and_then(|k| k.attacker_name.clone())
self.get_killer_entry()
.and_then(|k| k.attacker_name.clone())
}) {
pumpkin_util::text::TextComponent::translate_cross(
format!("death.attack.{msg_id}.player"),

View File

@ -1666,9 +1666,7 @@ impl LivingEntity {
source: Option<&dyn EntityBase>,
cause: Option<&dyn EntityBase>,
) -> TextComponent {
let kill_credit = dyn_self
.get_living_entity()
.and_then(Self::get_kill_credit);
let kill_credit = dyn_self.get_living_entity().and_then(Self::get_kill_credit);
let kill_credit_name = kill_credit.as_ref().map(|c| c.get_display_name());
if let Some(living) = dyn_self.get_living_entity() {
@ -1687,7 +1685,11 @@ impl LivingEntity {
format!("death.attack.{}", damage_type.message_id),
[dyn_self.get_display_name(), cause.get_display_name()],
)
} else if let Some(killer) = cause.or(source).map(|c| c.get_display_name()).or(kill_credit_name) {
} else if let Some(killer) = cause
.or(source)
.map(|c| c.get_display_name())
.or(kill_credit_name)
{
TextComponent::translate_cross(
format!("death.attack.{}.player", damage_type.message_id),
format!("death.attack.{}.player", damage_type.message_id),
@ -1887,7 +1889,8 @@ impl LivingEntity {
if let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
&& let Some(server) = world.server.upgrade()
{
let mut event = crate::plugin::api::events::entity::entity_death::PlayerDeathEvent::new(
let mut event =
crate::plugin::api::events::entity::entity_death::PlayerDeathEvent::new(
player_arc,
final_death_message.clone(),
0,
@ -1902,9 +1905,7 @@ impl LivingEntity {
player.handle_killed(&final_death_message);
}
if show_death_messages
&& let Some(server) = world.server.upgrade()
{
if show_death_messages && let Some(server) = world.server.upgrade() {
for player in server.get_all_players() {
player.send_system_message(&final_death_message);
}
@ -2639,7 +2640,13 @@ impl LivingEntity {
| DamageType::STALAGMITE
)
{
let damager_pos = position.map(|p| BlockPos(Vector3::new(p.x.floor() as i32, p.y.floor() as i32, p.z.floor() as i32)));
let damager_pos = position.map(|p| {
BlockPos(Vector3::new(
p.x.floor() as i32,
p.y.floor() as i32,
p.z.floor() as i32,
))
});
let mut by_block_event =
crate::plugin::api::events::entity::entity_damage_by_block::EntityDamageByBlockEvent {
entity_id: self.entity.entity_id,
@ -2897,8 +2904,7 @@ impl LivingEntity {
if let Some(attacker) = cause.or(source) {
let attacker_id = attacker.get_entity().entity_id;
self.last_attacker_id
.store(attacker_id, Relaxed);
self.last_attacker_id.store(attacker_id, Relaxed);
self.last_attacked_time
.store(self.entity.age.load(Relaxed), Relaxed);

View File

@ -3371,7 +3371,8 @@ impl Entity {
&& let Some(player) = holder.get_player()
&& let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
{
let mut event = crate::plugin::api::events::player::player_leash_entity::PlayerLeashEntityEvent {
let mut event =
crate::plugin::api::events::player::player_leash_entity::PlayerLeashEntityEvent {
player: player_arc,
entity_id: self.entity_id,
holder_id: holder_entity_id,
@ -3416,7 +3417,8 @@ impl Entity {
pub fn unleash(&self) {
let world = self.world.load();
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::entity::entity_unleash::EntityUnleashEvent::new(
let mut event =
crate::plugin::api::events::entity::entity_unleash::EntityUnleashEvent::new(
self.entity_id,
"unleashed".to_string(),
);

View File

@ -1607,7 +1607,8 @@ impl Player {
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
&& let Some(server) = self.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_spawn_change::PlayerSpawnChangeEvent {
let mut event =
crate::plugin::api::events::player::player_spawn_change::PlayerSpawnChangeEvent {
player: player_arc,
new_spawn: Some(block_pos),
forced,
@ -1628,7 +1629,12 @@ impl Player {
_ => 0,
};
self.client.try_enqueue_packet_editioned(
&CPlayerSpawnPosition::new(final_block_pos, yaw, pitch, dimension.minecraft_name.to_owned()),
&CPlayerSpawnPosition::new(
final_block_pos,
yaw,
pitch,
dimension.minecraft_name.to_owned(),
),
&pumpkin_protocol::bedrock::client::CSetSpawnPosition {
spawn_position_type:
pumpkin_protocol::bedrock::client::SpawnPositionType::PlayerRespawn,
@ -2315,7 +2321,8 @@ impl Player {
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
&& let Some(server) = self.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_open_sign::PlayerOpenSignEvent {
let mut event =
crate::plugin::api::events::player::player_open_sign::PlayerOpenSignEvent {
player: player_arc,
block_pos: location,
is_front: is_front_text,
@ -2334,7 +2341,8 @@ impl Player {
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
&& let Some(server) = self.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_velocity::PlayerVelocityEvent {
let mut event =
crate::plugin::api::events::player::player_velocity::PlayerVelocityEvent {
player: player_arc,
velocity,
cancelled: false,
@ -2391,7 +2399,8 @@ impl Player {
&& let Some(server) = self.world().server.upgrade()
{
let link_strings = links.iter().map(|l| l.url.clone()).collect();
let mut event = crate::plugin::api::events::player::player_links_send::PlayerLinksSendEvent::new(
let mut event =
crate::plugin::api::events::player::player_links_send::PlayerLinksSendEvent::new(
player_arc,
link_strings,
);
@ -3089,7 +3098,8 @@ impl Player {
}
pub fn increment_stat(&self, category: statistics::StatisticCategory, stat: i32, amount: i32) {
let final_amount = if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
let final_amount = if let Some(player_arc) =
self.world().get_player_by_uuid(self.gameprofile.id)
&& let Some(server) = self.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_statistic_increment::PlayerStatisticIncrementEvent {
@ -5228,7 +5238,8 @@ impl Player {
if let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id)
&& let Some(server) = self.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_item_mend::PlayerItemMendEvent {
let mut event =
crate::plugin::api::events::player::player_item_mend::PlayerItemMendEvent {
player: player_arc,
item_name: stack.item.registry_key.to_string(),
repair_amount: repaired,

View File

@ -71,7 +71,8 @@ impl FireworkRocketEntity {
pub fn explode_and_remove(&self, world: &World) {
let entity = self.get_entity();
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::entity::firework_explode::FireworkExplodeEvent {
let mut event =
crate::plugin::api::events::entity::firework_explode::FireworkExplodeEvent {
entity_id: entity.entity_id,
cancelled: false,
};

View File

@ -244,9 +244,13 @@ impl EntityBase for SplashPotionEntity {
}
}
let affected_ids: Vec<i32> = affected.iter().map(|(c, _)| c.get_entity().entity_id).collect();
let affected_ids: Vec<i32> = affected
.iter()
.map(|(c, _)| c.get_entity().entity_id)
.collect();
if let Some(server) = world.server.upgrade() {
let mut event = crate::plugin::api::events::entity::potion_splash::PotionSplashEvent::new(
let mut event =
crate::plugin::api::events::entity::potion_splash::PotionSplashEvent::new(
self.get_entity().entity_id,
block_pos,
stack.item.registry_key.to_string(),

View File

@ -120,7 +120,8 @@ impl ItemBehaviour for BrushItem {
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
&& let Some(server) = player.world().server.upgrade()
{
let mut event = crate::plugin::api::events::block::block_brush::BlockBrushEvent::new(
let mut event =
crate::plugin::api::events::block::block_brush::BlockBrushEvent::new(
location,
world.clone(),
player_arc,

View File

@ -194,7 +194,8 @@ fn handle_beehive(
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
&& let Some(server) = player.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_harvest_block::PlayerHarvestBlockEvent {
let mut event =
crate::plugin::api::events::player::player_harvest_block::PlayerHarvestBlockEvent {
player: player_arc,
block_pos: *location,
harvested_items: drops.clone(),

View File

@ -68,7 +68,8 @@ impl ItemBehaviour for TridentItem {
if let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id)
&& let Some(server) = world.server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_riptide::PlayerRiptideEvent {
let mut event =
crate::plugin::api::events::player::player_riptide::PlayerRiptideEvent {
player: player_arc,
item_name: "trident".to_string(),
cancelled: false,

View File

@ -251,6 +251,9 @@ impl PumpkinServer {
) -> Self {
let server = Server::new(basic_config, advanced_config, vanilla_data).await;
#[cfg(target_family = "unix")]
adjust_file_descriptor_limit();
let rcon = server.advanced_config.networking.rcon.clone();
if rcon.enabled {
@ -615,6 +618,15 @@ impl PumpkinServer {
});
}
Err(e) => {
#[cfg(target_family = "unix")]
if e.raw_os_error() == Some(libc::EMFILE) {
error!(
"Too many open files! Server reached file descriptor limit. \
New connections cannot be accepted until existing connections close or `ulimit -n` is increased."
);
sleep(Duration::from_millis(500)).await;
return true;
}
error!("Failed to accept Java client connection: {e}");
sleep(Duration::from_millis(50)).await;
}
@ -826,3 +838,58 @@ fn scrub_address(ip: &str) -> String {
.map(|ch| if ch == '.' || ch == ':' { ch } else { 'x' })
.collect()
}
#[cfg(target_family = "unix")]
fn adjust_file_descriptor_limit() {
let mut rlim = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
// SAFETY: Passing a valid mutable pointer to a stack-allocated `rlimit` struct.
if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &raw mut rlim) } != 0 {
return;
}
let max_target = if rlim.rlim_max == libc::RLIM_INFINITY {
1_048_576
} else {
rlim.rlim_max
};
if rlim.rlim_cur < max_target {
let old_limit = rlim.rlim_cur;
rlim.rlim_cur = max_target;
// SAFETY: Calling `setrlimit` with a valid resource and valid pointer to initialized `rlimit`.
let res = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &raw const rlim) };
if res == 0 {
debug!("Increased open file descriptor limit from {old_limit} to {max_target}");
} else {
// Fallback: try setting to a reasonable high value (65,536) if max_target was rejected by the OS.
let fallback = 65_536.min(max_target);
if fallback > old_limit {
rlim.rlim_cur = fallback;
// SAFETY: Calling `setrlimit` with a valid resource and valid pointer to initialized `rlimit`.
if unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &raw const rlim) } == 0 {
debug!("Increased open file descriptor limit from {old_limit} to {fallback}");
}
}
}
}
let mut current_rlim = libc::rlimit {
rlim_cur: 0,
rlim_max: 0,
};
// SAFETY: Passing a valid mutable pointer to a stack-allocated `rlimit` struct.
if unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &raw mut current_rlim) } == 0
&& current_rlim.rlim_cur < 4096
{
warn!(
"Open file descriptor limit is low ({}). Supporting >1000 concurrent players may fail with 'Too many open files'. \
Consider increasing the limit with `ulimit -n 65535` or setting `LimitNOFILE=65535` in systemd.",
current_rlim.rlim_cur
);
}
}

View File

@ -160,8 +160,7 @@ impl ChunkSender {
epoch: u32,
version: JavaMinecraftVersion,
) -> Option<PreparedBatch> {
if version >= JavaMinecraftVersion::V_1_20_2
&& self.in_flight_batches >= self.max_in_flight
if version >= JavaMinecraftVersion::V_1_20_2 && self.in_flight_batches >= self.max_in_flight
{
return None;
}

View File

@ -149,14 +149,18 @@ impl PendingConnection {
server: &Arc<Server>,
profile: &GameProfile,
) -> Option<PacketHandlerResult> {
let mut pre_login_event = crate::plugin::api::events::player::async_player_pre_login::AsyncPlayerPreLoginEvent {
let mut pre_login_event =
crate::plugin::api::events::player::async_player_pre_login::AsyncPlayerPreLoginEvent {
player_name: profile.name.clone(),
player_uuid: profile.id,
ip_address: self.address,
kick_message: TextComponent::text("Disconnected"),
cancelled: false,
};
server.plugin_manager.fire(server, &mut pre_login_event).await;
server
.plugin_manager
.fire(server, &mut pre_login_event)
.await;
if pre_login_event.cancelled {
self.kick(pre_login_event.kick_message).await;
return Some(PacketHandlerResult::Stop);

View File

@ -1183,7 +1183,9 @@ impl JavaClient {
player.clone(),
ch.to_string(),
);
server.plugin_manager.fire_blocking(server, &mut unreg_event);
server
.plugin_manager
.fire_blocking(server, &mut unreg_event);
}
}
}

View File

@ -16,7 +16,8 @@ impl JavaClient {
if let Some(player_arc) = player.world().get_player_by_uuid(player.gameprofile.id)
&& let Some(server) = player.world().server.upgrade()
{
let mut event = crate::plugin::api::events::player::player_edit_book::PlayerEditBookEvent {
let mut event =
crate::plugin::api::events::player::player_edit_book::PlayerEditBookEvent {
player: player_arc,
slot,
pages: pages.clone(),

View File

@ -169,7 +169,9 @@ impl JavaClient {
world.clone(),
player.inventory().held_item(),
);
server_arc.plugin_manager.fire_blocking(&server_arc, &mut abort_event);
server_arc
.plugin_manager
.fire_blocking(&server_arc, &mut abort_event);
}
player.mining.store(false, Ordering::Relaxed);

View File

@ -930,4 +930,3 @@ pub(crate) fn from_wit_entity_type(
.copied()
.ok_or_else(|| wasmtime::Error::msg(format!("Invalid entity type index: {index}")))
}

View File

@ -1024,8 +1024,7 @@ impl pumpkin::plugin::world::HostWorld for PluginHostState {
let world_ref = self.get_world_res(&world)?;
let world_provider = world_ref.provider.clone();
let internal_type =
super::entity::from_wit_entity_type(entity_type)?;
let internal_type = super::entity::from_wit_entity_type(entity_type)?;
let internal_pos = pumpkin_util::math::vector3::Vector3::new(pos.0, pos.1, pos.2);
let entity = crate::entity::r#type::from_type(