mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
add gametick event and find_one_entity
This commit is contained in:
parent
438d633b83
commit
efd8749573
6 changed files with 98 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -235,6 +235,7 @@ dependencies = [
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-protocol",
|
"azalea-protocol",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -26,7 +26,10 @@ use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
use tokio::{
|
||||||
|
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
|
||||||
|
time::{self, MissedTickBehavior},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
|
@ -38,6 +41,8 @@ pub struct ClientState {
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Login,
|
Login,
|
||||||
Chat(ChatPacket),
|
Chat(ChatPacket),
|
||||||
|
/// A game tick, happens 20 times per second.
|
||||||
|
GameTick,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -153,20 +158,22 @@ impl Client {
|
||||||
conn: conn.clone(),
|
conn: conn.clone(),
|
||||||
state: Arc::new(Mutex::new(ClientState::default())),
|
state: Arc::new(Mutex::new(ClientState::default())),
|
||||||
};
|
};
|
||||||
// let client = Arc::new(Mutex::new(client));
|
|
||||||
// let weak_client = Arc::<_>::downgrade(&client);
|
|
||||||
|
|
||||||
// just start up the game loop and we're ready!
|
// just start up the game loop and we're ready!
|
||||||
// tokio::spawn(Self::game_loop(conn, tx, handler, state))
|
|
||||||
|
|
||||||
let game_loop_state = client.state.clone();
|
let game_loop_state = client.state.clone();
|
||||||
|
|
||||||
tokio::spawn(Self::game_loop(conn, tx, game_loop_state));
|
tokio::spawn(Self::protocol_loop(
|
||||||
|
conn.clone(),
|
||||||
|
tx.clone(),
|
||||||
|
game_loop_state.clone(),
|
||||||
|
));
|
||||||
|
tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state));
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn game_loop(
|
async fn protocol_loop(
|
||||||
conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
||||||
tx: UnboundedSender<Event>,
|
tx: UnboundedSender<Event>,
|
||||||
state: Arc<Mutex<ClientState>>,
|
state: Arc<Mutex<ClientState>>,
|
||||||
|
@ -487,6 +494,33 @@ impl Client {
|
||||||
self.event_receiver.recv().await
|
self.event_receiver.recv().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs game_tick every 50 milliseconds.
|
||||||
|
async fn game_tick_loop(
|
||||||
|
conn: Arc<tokio::sync::Mutex<GameConnection>>,
|
||||||
|
tx: UnboundedSender<Event>,
|
||||||
|
state: Arc<Mutex<ClientState>>,
|
||||||
|
) {
|
||||||
|
let mut game_tick_interval = time::interval(time::Duration::from_millis(50));
|
||||||
|
// TODO: Minecraft bursts up to 10 ticks and then skips, we should too
|
||||||
|
game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
|
||||||
|
loop {
|
||||||
|
game_tick_interval.tick().await;
|
||||||
|
Self::game_tick(&conn, &tx, &state).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs every 50 milliseconds.
|
||||||
|
async fn game_tick(
|
||||||
|
conn: &Arc<tokio::sync::Mutex<GameConnection>>,
|
||||||
|
tx: &UnboundedSender<Event>,
|
||||||
|
state: &Arc<Mutex<ClientState>>,
|
||||||
|
) {
|
||||||
|
if state.lock().unwrap().world.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tx.send(Event::GameTick).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the `World` the client is in.
|
/// Gets the `World` the client is in.
|
||||||
///
|
///
|
||||||
/// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`.
|
/// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`.
|
||||||
|
|
|
@ -79,6 +79,40 @@ impl EntityStorage {
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(entity_id);
|
.insert(entity_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an iterator over all entities.
|
||||||
|
#[inline]
|
||||||
|
pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> {
|
||||||
|
self.by_id.values()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_one_entity<F>(&self, mut f: F) -> Option<&Entity>
|
||||||
|
where
|
||||||
|
F: FnMut(&Entity) -> bool,
|
||||||
|
{
|
||||||
|
for entity in self.entities() {
|
||||||
|
if f(entity) {
|
||||||
|
return Some(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_one_entity_in_chunk<F>(&self, chunk: &ChunkPos, mut f: F) -> Option<&Entity>
|
||||||
|
where
|
||||||
|
F: FnMut(&Entity) -> bool,
|
||||||
|
{
|
||||||
|
if let Some(entities) = self.by_chunk.get(chunk) {
|
||||||
|
for entity_id in entities {
|
||||||
|
if let Some(entity) = self.by_id.get(entity_id) {
|
||||||
|
if f(entity) {
|
||||||
|
return Some(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EntityStorage {
|
impl Default for EntityStorage {
|
||||||
|
|
|
@ -87,6 +87,19 @@ impl World {
|
||||||
pub fn entity_by_id(&self, id: u32) -> Option<&Entity> {
|
pub fn entity_by_id(&self, id: u32) -> Option<&Entity> {
|
||||||
self.entity_storage.get_by_id(id)
|
self.entity_storage.get_by_id(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an iterator over all entities.
|
||||||
|
#[inline]
|
||||||
|
pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> {
|
||||||
|
self.entity_storage.entities()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_one_entity<F>(&self, mut f: F) -> Option<&Entity>
|
||||||
|
where
|
||||||
|
F: FnMut(&Entity) -> bool,
|
||||||
|
{
|
||||||
|
self.entity_storage.find_one_entity(|entity| f(entity))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<&ChunkPos> for World {
|
impl Index<&ChunkPos> for World {
|
||||||
|
|
|
@ -10,3 +10,4 @@ azalea-client = {path = "../azalea-client"}
|
||||||
azalea-core = {path = "../azalea-core"}
|
azalea-core = {path = "../azalea-core"}
|
||||||
azalea-protocol = {path = "../azalea-protocol"}
|
azalea-protocol = {path = "../azalea-protocol"}
|
||||||
tokio = "^1.19.2"
|
tokio = "^1.19.2"
|
||||||
|
uuid = "^1.1.2"
|
||||||
|
|
|
@ -20,12 +20,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
match e {
|
match e {
|
||||||
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
|
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
|
||||||
Event::Login => {}
|
Event::Login => {}
|
||||||
Event::Chat(_p) => {
|
Event::GameTick => {
|
||||||
let world = client.world();
|
let world = client.world();
|
||||||
let b = world.get_block_state(&BlockPos::new(0, 0, 0)).unwrap();
|
if let Some(b) = world.find_one_entity(|e| {
|
||||||
// let world = state.world.as_ref().unwrap();
|
e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd")
|
||||||
// world.
|
}) {
|
||||||
println!("{:?}", b);
|
// let world = state.world.as_ref().unwrap();
|
||||||
|
// world.
|
||||||
|
println!("{:?}", b);
|
||||||
|
}
|
||||||
// world.get_block_state(state.player.entity.pos);
|
// world.get_block_state(state.player.entity.pos);
|
||||||
// println!("{}", p.message.to_ansi(None));
|
// println!("{}", p.message.to_ansi(None));
|
||||||
// if p.message.to_ansi(None) == "<py5> ok" {
|
// if p.message.to_ansi(None) == "<py5> ok" {
|
||||||
|
@ -35,6 +38,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// println!("block state: {:?}", c);
|
// println!("block state: {:?}", c);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue