mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Add performance tests (#113)
Adds the performance_tests/ directory. In the future we could use our own fake client software instead of rust-mc-bot. This would make it easier to run the tests.
This commit is contained in:
parent
a53738355f
commit
f58e6662dd
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -8,3 +8,7 @@ Cargo.lock
|
|||
/extractor/out
|
||||
/extractor/classes
|
||||
/extractor/run
|
||||
/performance_tests/rust-mc-bot
|
||||
flamegraph.svg
|
||||
perf.data
|
||||
perf.data.old
|
||||
|
|
|
@ -68,7 +68,12 @@ rayon = "1.5.3"
|
|||
num = "0.4.0"
|
||||
|
||||
[workspace]
|
||||
members = ["valence_nbt", "packet_inspector"]
|
||||
members = [
|
||||
"valence_nbt",
|
||||
"packet_inspector",
|
||||
"performance_tests/players"
|
||||
]
|
||||
exclude = ["performance_tests/rust-mc-bot"]
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
|
34
performance_tests/README.md
Normal file
34
performance_tests/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Performance Tests
|
||||
|
||||
Run the server
|
||||
|
||||
```shell
|
||||
cargo r -r -p players
|
||||
```
|
||||
|
||||
In a separate terminal, start [rust-mc-bot](https://github.com/Eoghanmc22/rust-mc-bot).
|
||||
This command should connect 1000 clients to the server.
|
||||
|
||||
```shell
|
||||
cargo r -r -- 127.0.0.1:25565 1000
|
||||
|
||||
# If rust-mc-bot was cloned in the performance_tests directory, do
|
||||
cargo r -r -p rust-mc-bot -- 127.0.0.1:25565 1000
|
||||
```
|
||||
|
||||
If the delta time is consistently >50ms, the server is running behind schedule.
|
||||
|
||||
# Flamegraph
|
||||
|
||||
To start capturing a [flamegraph](https://github.com/flamegraph-rs/flamegraph),
|
||||
run the server like this:
|
||||
|
||||
```shell
|
||||
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph -p players
|
||||
```
|
||||
|
||||
Run rust-mc-bot as above, and then stop the server after a few seconds. Flamegraph will take its own sweet time to
|
||||
generate a flamegraph.svg in the current directory. You can then open that file in your internet browser of choice.
|
||||
|
||||
NOTE: The indiscriminate use of `rayon` in Valence appears to have made the flamegraph basically unreadable. This
|
||||
situation should change soon.
|
9
performance_tests/players/Cargo.toml
Normal file
9
performance_tests/players/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "players"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.9.1"
|
||||
log = "0.4.17"
|
||||
valence = { path = "../.." }
|
190
performance_tests/players/src/main.rs
Normal file
190
performance_tests/players/src/main.rs
Normal file
|
@ -0,0 +1,190 @@
|
|||
use std::net::SocketAddr;
|
||||
use std::time::Instant;
|
||||
|
||||
use log::LevelFilter;
|
||||
use valence::async_trait;
|
||||
use valence::block::BlockState;
|
||||
use valence::chunk::{Chunk, UnloadedChunk};
|
||||
use valence::client::{handle_event_default, ClientEvent};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
use valence::dimension::{Dimension, DimensionId};
|
||||
use valence::entity::{EntityId, EntityKind};
|
||||
use valence::player_list::PlayerListId;
|
||||
use valence::server::{Server, SharedServer, ShutdownResult};
|
||||
|
||||
pub fn main() -> ShutdownResult {
|
||||
env_logger::Builder::new()
|
||||
.filter_module("valence", LevelFilter::Trace)
|
||||
.parse_default_env()
|
||||
.init();
|
||||
|
||||
valence::start_server(
|
||||
Game,
|
||||
ServerState {
|
||||
player_list: None,
|
||||
time: None,
|
||||
millis_sum: 0.0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const WITH_PLAYER_ENTITIES: bool = true;
|
||||
const MAX_PLAYERS: usize = 10_000;
|
||||
|
||||
struct Game;
|
||||
|
||||
struct ServerState {
|
||||
player_list: Option<PlayerListId>,
|
||||
time: Option<Instant>,
|
||||
millis_sum: f64,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Config for Game {
|
||||
type ServerState = ServerState;
|
||||
type ClientState = EntityId;
|
||||
type EntityState = ();
|
||||
type WorldState = ();
|
||||
type ChunkState = ();
|
||||
type PlayerListState = ();
|
||||
|
||||
fn max_connections(&self) -> usize {
|
||||
MAX_PLAYERS + 64
|
||||
}
|
||||
|
||||
fn online_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn outgoing_packet_capacity(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> Vec<Dimension> {
|
||||
vec![Dimension {
|
||||
natural: false,
|
||||
ambient_light: 1.0,
|
||||
fixed_time: None,
|
||||
effects: Default::default(),
|
||||
min_y: 0,
|
||||
height: 256,
|
||||
}]
|
||||
}
|
||||
|
||||
async fn server_list_ping(
|
||||
&self,
|
||||
_shared: &SharedServer<Self>,
|
||||
_remote_addr: SocketAddr,
|
||||
_protocol_version: i32,
|
||||
) -> ServerListPing {
|
||||
ServerListPing::Respond {
|
||||
online_players: -1,
|
||||
max_players: -1,
|
||||
player_sample: Default::default(),
|
||||
description: "Test server!".into(),
|
||||
favicon_png: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&self, server: &mut Server<Self>) {
|
||||
server.state.player_list = Some(server.player_lists.insert(()).0);
|
||||
|
||||
let (_, world) = server.worlds.insert(DimensionId::default(), ());
|
||||
|
||||
let size = 5;
|
||||
for chunk_z in -size..size {
|
||||
for chunk_x in -size..size {
|
||||
let mut chunk = UnloadedChunk::new(16);
|
||||
for z in 0..16 {
|
||||
for x in 0..16 {
|
||||
chunk.set_block_state(x, 0, z, BlockState::GRASS_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
world.chunks.insert([chunk_x, chunk_z], chunk, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&self, server: &mut Server<Self>) {
|
||||
if let Some(time) = &mut server.state.time {
|
||||
let millis = time.elapsed().as_secs_f64() * 1000.0;
|
||||
let tick = server.shared.current_tick();
|
||||
let players = server.clients.len();
|
||||
let delay = 20;
|
||||
|
||||
server.state.millis_sum += millis;
|
||||
|
||||
if tick % delay == 0 {
|
||||
let avg = server.state.millis_sum / delay as f64;
|
||||
println!("Avg delta: {avg:.3}ms tick={tick} players={players}");
|
||||
server.state.millis_sum = 0.0;
|
||||
}
|
||||
}
|
||||
server.state.time = Some(Instant::now());
|
||||
|
||||
let (world_id, _) = server.worlds.iter_mut().next().unwrap();
|
||||
|
||||
server.clients.retain(|_, client| {
|
||||
if client.created_this_tick() {
|
||||
log::info!("Client \"{}\" joined", client.username());
|
||||
|
||||
client.spawn(world_id);
|
||||
client.set_flat(true);
|
||||
client.teleport([0.0, 1.0, 0.0], 0.0, 0.0);
|
||||
|
||||
if WITH_PLAYER_ENTITIES {
|
||||
client.set_player_list(server.state.player_list.clone());
|
||||
if let Some(id) = &server.state.player_list {
|
||||
server.player_lists.get_mut(id).insert(
|
||||
client.uuid(),
|
||||
client.username(),
|
||||
client.textures().cloned(),
|
||||
client.game_mode(),
|
||||
0,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
match server
|
||||
.entities
|
||||
.insert_with_uuid(EntityKind::Player, client.uuid(), ())
|
||||
{
|
||||
Some((id, _)) => client.state = id,
|
||||
None => {
|
||||
client.disconnect("Conflicting UUID");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if client.is_disconnected() {
|
||||
log::info!("Client \"{}\" disconnected", client.username());
|
||||
|
||||
if WITH_PLAYER_ENTITIES {
|
||||
if let Some(id) = &server.state.player_list {
|
||||
server.player_lists.get_mut(id).remove(client.uuid());
|
||||
}
|
||||
server.entities.remove(client.state);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if WITH_PLAYER_ENTITIES {
|
||||
if let Some(entity) = server.entities.get_mut(client.state) {
|
||||
while handle_event_default(client, entity).is_some() {}
|
||||
}
|
||||
} else {
|
||||
while let Some(event) = client.pop_event() {
|
||||
if let ClientEvent::SettingsChanged { view_distance, .. } = event {
|
||||
client.set_view_distance(view_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
}
|
|
@ -870,7 +870,7 @@ impl<C: Config> Client<C> {
|
|||
C2sPlayPacket::ClickContainer(p) => {
|
||||
if p.slot_idx == -999 {
|
||||
// client is trying to drop the currently held stack
|
||||
let held = std::mem::replace(&mut self.cursor_held_item, None);
|
||||
let held = mem::replace(&mut self.cursor_held_item, None);
|
||||
match held {
|
||||
None => {}
|
||||
Some(stack) => self.events.push_back(ClientEvent::DropItemStack { stack }),
|
||||
|
|
|
@ -72,13 +72,13 @@ impl<C: Config> Entities<C> {
|
|||
&mut self,
|
||||
kind: EntityKind,
|
||||
uuid: Uuid,
|
||||
data: C::EntityState,
|
||||
state: C::EntityState,
|
||||
) -> Option<(EntityId, &mut Entity<C>)> {
|
||||
match self.uuid_to_entity.entry(uuid) {
|
||||
Entry::Occupied(_) => None,
|
||||
Entry::Vacant(ve) => {
|
||||
let (k, e) = self.slab.insert(Entity {
|
||||
state: data,
|
||||
state,
|
||||
variants: TrackedData::new(kind),
|
||||
events: Vec::new(),
|
||||
bits: EntityBits::new(),
|
||||
|
|
Loading…
Reference in a new issue