mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
misc post party changes
This commit is contained in:
parent
302e2e92b0
commit
10c47128ce
@ -88,6 +88,9 @@ command-transform-invalid-presence = Cannot transform in the current presence
|
||||
command-aura-invalid-buff-parameters = Invalid buff parameters for aura
|
||||
command-aura-spawn = Spawned new aura attached to entity
|
||||
command-aura-spawn-new-entity = Spawned new aura
|
||||
command-reloaded-chunks = Reloaded { $reloaded } chunks
|
||||
command-server-no-experimental-terrain-persistence = Server was compiled without terrain persistence enabled
|
||||
command-experimental-terrain-persistence-disabled = Experimental terrain persistence is disabled
|
||||
|
||||
# Unreachable/untestable but added for consistency
|
||||
|
||||
|
@ -325,6 +325,7 @@ pub enum ServerChatCommand {
|
||||
Buff,
|
||||
Build,
|
||||
Campfire,
|
||||
ClearPersistedTerrain,
|
||||
CreateLocation,
|
||||
DebugColumn,
|
||||
DebugWays,
|
||||
@ -532,6 +533,11 @@ impl ServerChatCommand {
|
||||
Some(Admin),
|
||||
),
|
||||
ServerChatCommand::Campfire => cmd(vec![], "Spawns a campfire", Some(Admin)),
|
||||
ServerChatCommand::ClearPersistedTerrain => cmd(
|
||||
vec![Integer("radius", 6, Required)],
|
||||
"Clears nearby persisted terrain",
|
||||
Some(Admin),
|
||||
),
|
||||
ServerChatCommand::DebugColumn => cmd(
|
||||
vec![Integer("x", 15000, Required), Integer("y", 15000, Required)],
|
||||
"Prints some debug information about a column",
|
||||
@ -632,9 +638,11 @@ impl ServerChatCommand {
|
||||
Some(Moderator),
|
||||
),
|
||||
ServerChatCommand::Kill => cmd(vec![], "Kill yourself", None),
|
||||
ServerChatCommand::KillNpcs => {
|
||||
cmd(vec![Flag("--also-pets")], "Kill the NPCs", Some(Admin))
|
||||
},
|
||||
ServerChatCommand::KillNpcs => cmd(
|
||||
vec![Float("radius", 100.0, Optional), Flag("--also-pets")],
|
||||
"Kill the NPCs",
|
||||
Some(Admin),
|
||||
),
|
||||
ServerChatCommand::Kit => cmd(
|
||||
vec![Enum("kit_name", KITS.to_vec(), Required)],
|
||||
"Place a set of items into your inventory.",
|
||||
@ -715,8 +723,8 @@ impl ServerChatCommand {
|
||||
Some(Admin),
|
||||
),
|
||||
ServerChatCommand::ReloadChunks => cmd(
|
||||
vec![],
|
||||
"Reloads all chunks loaded on the server",
|
||||
vec![Integer("radius", 6, Optional)],
|
||||
"Reloads chunks loaded on the server",
|
||||
Some(Admin),
|
||||
),
|
||||
ServerChatCommand::RemoveLights => cmd(
|
||||
@ -980,6 +988,7 @@ impl ServerChatCommand {
|
||||
ServerChatCommand::Buff => "buff",
|
||||
ServerChatCommand::Build => "build",
|
||||
ServerChatCommand::Campfire => "campfire",
|
||||
ServerChatCommand::ClearPersistedTerrain => "clear_persisted_terrain",
|
||||
ServerChatCommand::DebugColumn => "debug_column",
|
||||
ServerChatCommand::DebugWays => "debug_ways",
|
||||
ServerChatCommand::DisconnectAllPlayers => "disconnect_all_players",
|
||||
|
@ -540,12 +540,15 @@ impl State {
|
||||
}
|
||||
|
||||
/// Removes every chunk of the terrain.
|
||||
pub fn clear_terrain(&mut self) {
|
||||
pub fn clear_terrain(&mut self) -> usize {
|
||||
let removed_chunks = &mut self.ecs.write_resource::<TerrainChanges>().removed_chunks;
|
||||
|
||||
self.terrain_mut().drain().for_each(|(key, _)| {
|
||||
removed_chunks.insert(key);
|
||||
});
|
||||
self.terrain_mut()
|
||||
.drain()
|
||||
.map(|(key, _)| {
|
||||
removed_chunks.insert(key);
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Insert the provided chunk into this state's terrain.
|
||||
@ -570,7 +573,7 @@ impl State {
|
||||
|
||||
/// Remove the chunk with the given key from this state's terrain, if it
|
||||
/// exists.
|
||||
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
||||
pub fn remove_chunk(&mut self, key: Vec2<i32>) -> bool {
|
||||
if self
|
||||
.ecs
|
||||
.write_resource::<TerrainGrid>()
|
||||
@ -581,6 +584,10 @@ impl State {
|
||||
.write_resource::<TerrainChanges>()
|
||||
.removed_chunks
|
||||
.insert(key);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,12 +53,13 @@ use common::{
|
||||
parse_cmd_args,
|
||||
resources::{BattleMode, PlayerPhysicsSettings, ProgramTime, Secs, Time, TimeOfDay, TimeScale},
|
||||
rtsim::{Actor, Role},
|
||||
spiral::Spiral2d,
|
||||
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
||||
tether::Tethered,
|
||||
uid::Uid,
|
||||
vol::ReadVol,
|
||||
weather, Damage, DamageKind, DamageSource, Explosion, GroupTarget, LoadoutBuilder,
|
||||
RadiusEffect,
|
||||
weather, CachedSpatialGrid, Damage, DamageKind, DamageSource, Explosion, GroupTarget,
|
||||
LoadoutBuilder, RadiusEffect,
|
||||
};
|
||||
use common_net::{
|
||||
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral},
|
||||
@ -145,6 +146,7 @@ fn do_command(
|
||||
ServerChatCommand::Buff => handle_buff,
|
||||
ServerChatCommand::Build => handle_build,
|
||||
ServerChatCommand::Campfire => handle_spawn_campfire,
|
||||
ServerChatCommand::ClearPersistedTerrain => handle_clear_persisted_terrain,
|
||||
ServerChatCommand::DebugColumn => handle_debug_column,
|
||||
ServerChatCommand::DebugWays => handle_debug_ways,
|
||||
ServerChatCommand::DisconnectAllPlayers => handle_disconnect_all_players,
|
||||
@ -2010,6 +2012,56 @@ fn handle_spawn_campfire(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "persistent_world")]
|
||||
fn handle_clear_persisted_terrain(
|
||||
server: &mut Server,
|
||||
_client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: Vec<String>,
|
||||
action: &ServerChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
let Some(radius) = parse_cmd_args!(args, i32) else {
|
||||
return Err(Content::Plain(action.help_string()));
|
||||
};
|
||||
let radius = radius.min(64);
|
||||
|
||||
let pos = position(server, target, "target")?;
|
||||
let chunk_key = server.state.terrain().pos_key(pos.0.as_());
|
||||
|
||||
let mut terrain_persistence2 = server
|
||||
.state
|
||||
.ecs()
|
||||
.try_fetch_mut::<crate::terrain_persistence::TerrainPersistence>();
|
||||
if let Some(ref mut terrain_persistence) = terrain_persistence2 {
|
||||
for offset in Spiral2d::with_radius(radius) {
|
||||
let chunk_key = chunk_key + offset;
|
||||
terrain_persistence.clear_chunk(chunk_key);
|
||||
}
|
||||
|
||||
drop(terrain_persistence2);
|
||||
reload_chunks_inner(server, pos.0, Some(radius));
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Content::localized(
|
||||
"command-experimental-terrain-persistence-disabled",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "persistent_world"))]
|
||||
fn handle_clear_persisted_terrain(
|
||||
_server: &mut Server,
|
||||
_client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
_args: Vec<String>,
|
||||
_action: &ServerChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
Err(Content::localized(
|
||||
"command-server-no-experimental-terrain-persistence",
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_safezone(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
@ -2413,16 +2465,21 @@ fn parse_alignment(owner: Uid, alignment: &str) -> CmdResult<Alignment> {
|
||||
fn handle_kill_npcs(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: Vec<String>,
|
||||
_action: &ServerChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
let kill_pets = if let Some(kill_option) = parse_cmd_args!(args, String) {
|
||||
let (radius, options) = parse_cmd_args!(args, f32, String);
|
||||
let kill_pets = if let Some(kill_option) = options {
|
||||
kill_option.contains("--also-pets")
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let position = radius
|
||||
.map(|_| position(server, target, "target"))
|
||||
.transpose()?;
|
||||
|
||||
let to_kill = {
|
||||
let ecs = server.state.ecs();
|
||||
let entities = ecs.entities();
|
||||
@ -2432,40 +2489,78 @@ fn handle_kill_npcs(
|
||||
let alignments = ecs.read_storage::<Alignment>();
|
||||
let rtsim_entities = ecs.read_storage::<common::rtsim::RtSimEntity>();
|
||||
let mut rtsim = ecs.write_resource::<crate::rtsim::RtSim>();
|
||||
let spatial_grid;
|
||||
|
||||
(
|
||||
&entities,
|
||||
&healths,
|
||||
!&players,
|
||||
alignments.maybe(),
|
||||
&positions,
|
||||
)
|
||||
.join()
|
||||
.filter_map(|(entity, _health, (), alignment, pos)| {
|
||||
let should_kill = kill_pets
|
||||
|| if let Some(Alignment::Owned(owned)) = alignment {
|
||||
ecs.entity_from_uid(*owned)
|
||||
.map_or(true, |owner| !players.contains(owner))
|
||||
} else {
|
||||
true
|
||||
};
|
||||
let mut iter_a;
|
||||
let mut iter_b;
|
||||
|
||||
if should_kill {
|
||||
if let Some(rtsim_entity) = rtsim_entities.get(entity).copied() {
|
||||
rtsim.hook_rtsim_actor_death(
|
||||
&ecs.read_resource::<Arc<world::World>>(),
|
||||
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
|
||||
Actor::Npc(rtsim_entity.0),
|
||||
Some(pos.0),
|
||||
None,
|
||||
);
|
||||
}
|
||||
Some(entity)
|
||||
let iter: &mut dyn Iterator<
|
||||
Item = (
|
||||
EcsEntity,
|
||||
&comp::Health,
|
||||
(),
|
||||
Option<&comp::Alignment>,
|
||||
&comp::Pos,
|
||||
),
|
||||
> = if let (Some(radius), Some(position)) = (radius, position) {
|
||||
spatial_grid = ecs.read_resource::<CachedSpatialGrid>();
|
||||
iter_a = spatial_grid
|
||||
.0
|
||||
.in_circle_aabr(position.0.xy(), radius)
|
||||
.filter_map(|entity| {
|
||||
(
|
||||
&entities,
|
||||
&healths,
|
||||
!&players,
|
||||
alignments.maybe(),
|
||||
&positions,
|
||||
)
|
||||
.lend_join()
|
||||
.get(entity, &entities)
|
||||
})
|
||||
.filter(move |(_, _, _, _, pos)| {
|
||||
pos.0.distance_squared(position.0) <= radius.powi(2)
|
||||
});
|
||||
|
||||
&mut iter_a as _
|
||||
} else {
|
||||
iter_b = (
|
||||
&entities,
|
||||
&healths,
|
||||
!&players,
|
||||
alignments.maybe(),
|
||||
&positions,
|
||||
)
|
||||
.join();
|
||||
|
||||
&mut iter_b as _
|
||||
};
|
||||
|
||||
iter.filter_map(|(entity, _health, (), alignment, pos)| {
|
||||
let should_kill = kill_pets
|
||||
|| if let Some(Alignment::Owned(owned)) = alignment {
|
||||
ecs.entity_from_uid(*owned)
|
||||
.map_or(true, |owner| !players.contains(owner))
|
||||
} else {
|
||||
None
|
||||
true
|
||||
};
|
||||
|
||||
if should_kill {
|
||||
if let Some(rtsim_entity) = rtsim_entities.get(entity).copied() {
|
||||
rtsim.hook_rtsim_actor_death(
|
||||
&ecs.read_resource::<Arc<world::World>>(),
|
||||
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
|
||||
Actor::Npc(rtsim_entity.0),
|
||||
Some(pos.0),
|
||||
None,
|
||||
);
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
Some(entity)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let count = to_kill.len();
|
||||
for entity in to_kill {
|
||||
@ -3605,20 +3700,60 @@ fn parse_skill_tree(skill_tree: &str) -> CmdResult<comp::skillset::SkillGroupKin
|
||||
}
|
||||
}
|
||||
|
||||
fn reload_chunks_inner(server: &mut Server, pos: Vec3<f32>, radius: Option<i32>) -> usize {
|
||||
let mut removed = 0;
|
||||
|
||||
if let Some(radius) = radius {
|
||||
let chunk_key = server.state.terrain().pos_key(pos.as_());
|
||||
|
||||
for key_offset in Spiral2d::with_radius(radius) {
|
||||
let chunk_key = chunk_key + key_offset;
|
||||
|
||||
#[cfg(feature = "persistent_world")]
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.try_fetch_mut::<crate::terrain_persistence::TerrainPersistence>()
|
||||
.map(|mut terrain_persistence| terrain_persistence.unload_chunk(chunk_key));
|
||||
if server.state.remove_chunk(chunk_key) {
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#[cfg(feature = "persistent_world")]
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.try_fetch_mut::<crate::terrain_persistence::TerrainPersistence>()
|
||||
.map(|mut terrain_persistence| terrain_persistence.unload_all());
|
||||
removed = server.state.clear_terrain();
|
||||
}
|
||||
|
||||
removed
|
||||
}
|
||||
|
||||
fn handle_reload_chunks(
|
||||
server: &mut Server,
|
||||
_client: EcsEntity,
|
||||
_target: EcsEntity,
|
||||
_args: Vec<String>,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
args: Vec<String>,
|
||||
_action: &ServerChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
#[cfg(feature = "persistent_world")]
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.try_fetch_mut::<crate::terrain_persistence::TerrainPersistence>()
|
||||
.map(|mut terrain_persistence| terrain_persistence.unload_all());
|
||||
server.state.clear_terrain();
|
||||
let radius = parse_cmd_args!(args, i32);
|
||||
|
||||
let pos = position(server, target, "target")?.0;
|
||||
let removed = reload_chunks_inner(server, pos, radius.map(|radius| radius.min(64)));
|
||||
|
||||
server.notify_client(
|
||||
client,
|
||||
ServerGeneral::server_msg(
|
||||
ChatType::CommandInfo,
|
||||
Content::localized_with_args("command-reloaded-chunks", [(
|
||||
"reloaded",
|
||||
removed.to_string(),
|
||||
)]),
|
||||
),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2267,6 +2267,7 @@ pub fn transform_entity(
|
||||
set_or_remove_component(server, entity, Some(skill_set))?;
|
||||
set_or_remove_component(server, entity, Some(poise))?;
|
||||
set_or_remove_component(server, entity, health)?;
|
||||
set_or_remove_component(server, entity, Some(comp::Energy::new(body)))?;
|
||||
set_or_remove_component(server, entity, Some(body))?;
|
||||
set_or_remove_component(server, entity, Some(body.mass()))?;
|
||||
set_or_remove_component(server, entity, Some(body.density()))?;
|
||||
|
@ -15,7 +15,7 @@ use std::{
|
||||
use tracing::{debug, error, info, warn};
|
||||
use vek::*;
|
||||
|
||||
const MAX_BLOCK_CACHE: usize = 5_000_000;
|
||||
const MAX_BLOCK_CACHE: usize = 64_000_000;
|
||||
|
||||
pub struct TerrainPersistence {
|
||||
path: PathBuf,
|
||||
@ -157,11 +157,8 @@ impl TerrainPersistence {
|
||||
|
||||
pub fn unload_chunk(&mut self, key: Vec2<i32>) {
|
||||
if let Some(LoadedChunk { chunk, modified }) = self.chunks.remove(&key) {
|
||||
match (self.cached_chunks.peek(&key), modified) {
|
||||
(Some(_), false) => {},
|
||||
_ => {
|
||||
self.cached_chunks.insert(key, chunk.clone());
|
||||
},
|
||||
if modified || self.cached_chunks.peek(&key).is_none() {
|
||||
self.cached_chunks.insert(key, chunk.clone());
|
||||
}
|
||||
|
||||
// Prevent any uneccesarry IO when nothing in this chunk has changed
|
||||
@ -169,22 +166,38 @@ impl TerrainPersistence {
|
||||
return;
|
||||
}
|
||||
|
||||
let bytes = match bincode::serialize::<version::Current>(&chunk.prepare_raw()) {
|
||||
Err(err) => {
|
||||
error!("Failed to serialize chunk data: {:?}", err);
|
||||
return;
|
||||
},
|
||||
Ok(bytes) => bytes,
|
||||
};
|
||||
if chunk.blocks.is_empty() {
|
||||
let path = self.path_for(key);
|
||||
|
||||
let atomic_file =
|
||||
AtomicFile::new(self.path_for(key), OverwriteBehavior::AllowOverwrite);
|
||||
if let Err(err) = atomic_file.write(|file| file.write_all(&bytes)) {
|
||||
error!("Failed to write chunk data to file: {:?}", err);
|
||||
if let Err(error) = std::fs::remove_file(&path) {
|
||||
error!(?error, ?path, "Failed to remove file for empty chunk");
|
||||
}
|
||||
} else {
|
||||
let bytes = match bincode::serialize::<version::Current>(&chunk.prepare_raw()) {
|
||||
Err(err) => {
|
||||
error!("Failed to serialize chunk data: {:?}", err);
|
||||
return;
|
||||
},
|
||||
Ok(bytes) => bytes,
|
||||
};
|
||||
|
||||
let atomic_file =
|
||||
AtomicFile::new(self.path_for(key), OverwriteBehavior::AllowOverwrite);
|
||||
if let Err(err) = atomic_file.write(|file| file.write_all(&bytes)) {
|
||||
error!("Failed to write chunk data to file: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_chunk(&mut self, chunk: Vec2<i32>) {
|
||||
self.cached_chunks.remove(&chunk);
|
||||
self.chunks.insert(chunk, LoadedChunk {
|
||||
chunk: Chunk::default(),
|
||||
modified: true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn unload_all(&mut self) {
|
||||
for key in self.chunks.keys().copied().collect::<Vec<_>>() {
|
||||
self.unload_chunk(key);
|
||||
@ -202,10 +215,6 @@ impl TerrainPersistence {
|
||||
.insert(pos - key * TerrainChunk::RECT_SIZE.map(|e| e as i32), block);
|
||||
if old_block != Some(block) {
|
||||
loaded_chunk.modified = true;
|
||||
|
||||
if old_block.is_none() {
|
||||
self.cached_chunks.limiter_mut().add_block();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,7 +260,7 @@ impl Limiter<Vec2<i32>, Chunk> for ByBlockLimiter {
|
||||
type KeyToInsert<'a> = Vec2<i32>;
|
||||
type LinkType = u32;
|
||||
|
||||
fn is_over_the_limit(&self, _length: usize) -> bool { false }
|
||||
fn is_over_the_limit(&self, _length: usize) -> bool { self.counted_blocks > self.block_limit }
|
||||
|
||||
fn on_insert(
|
||||
&mut self,
|
||||
@ -306,10 +315,6 @@ impl ByBlockLimiter {
|
||||
counted_blocks: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// This function should only be used when it is guaranteed that a block has
|
||||
/// been added
|
||||
fn add_block(&mut self) { self.counted_blocks += 1; }
|
||||
}
|
||||
|
||||
/// # Adding a new chunk format version
|
||||
|
Loading…
Reference in New Issue
Block a user