Complete the cow sphere™

This commit is contained in:
Ryan 2022-05-18 03:05:10 -07:00
parent 1570c95ac8
commit f875ed07ce
8 changed files with 118 additions and 51 deletions

View file

@ -68,7 +68,7 @@ impl Config for Game {
}
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
let world_id = worlds.create(DimensionId::default());
let world_id = worlds.create(DimensionId::default()).0;
let mut world = worlds.get_mut(world_id).unwrap();
let size = 5;

View file

@ -1,15 +1,18 @@
use std::f64::consts::TAU;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use log::LevelFilter;
use valence::block::BlockState;
use valence::client::GameMode;
use valence::config::{Config, ServerListPing};
use valence::text::Color;
use valence::util::to_yaw_and_pitch;
use valence::{
async_trait, ChunkPos, ClientMut, DimensionId, EntityType, Server, ShutdownResult, Text,
async_trait, ClientMut, DimensionId, EntityId, EntityType, Server, ShutdownResult, Text,
TextFormat, WorldId, WorldsMut,
};
use vek::{Mat3, Vec3};
pub fn main() -> ShutdownResult {
env_logger::Builder::new()
@ -19,11 +22,13 @@ pub fn main() -> ShutdownResult {
valence::start_server(Game {
player_count: AtomicUsize::new(0),
cows: Mutex::new(Vec::new()),
})
}
struct Game {
player_count: AtomicUsize,
cows: Mutex<Vec<EntityId>>,
}
const MAX_PLAYERS: usize = 10;
@ -68,8 +73,7 @@ impl Config for Game {
}
fn init(&self, _server: &Server, mut worlds: WorldsMut) {
let world_id = worlds.create(DimensionId::default());
let mut world = worlds.get_mut(world_id).unwrap();
let mut world = worlds.create(DimensionId::default()).1;
world.meta.set_flat(true);
let size = 5;
@ -79,13 +83,11 @@ impl Config for Game {
}
}
let entity_id = world.entities.create();
let mut entity = world.entities.get_mut(entity_id).unwrap();
entity.set_type(EntityType::Cow);
entity.set_position([0.0, 100.0, 0.0]);
//entity.set_yaw(30.0);
//entity.set_pitch(0.0);
self.cows.lock().unwrap().extend((0..200).map(|_| {
let (id, mut e) = world.entities.create();
e.set_type(EntityType::Cow);
id
}));
}
fn update(&self, server: &Server, mut worlds: WorldsMut) {
@ -105,15 +107,54 @@ impl Config for Game {
}
});
for (_, mut e) in world.entities.iter_mut() {
let time = server.current_tick() as f64 / server.tick_rate() as f64;
let time = server.current_tick() as f64 / server.tick_rate() as f64;
if e.typ() == EntityType::Cow {
e.set_position(e.position() + [0.0, 0.0, 0.02]);
let yaw = (time % 1.0 * 360.0) as f32;
e.set_yaw(yaw);
e.set_head_yaw(yaw);
}
let rot = Mat3::rotation_x(time * TAU * 0.1)
.rotated_y(time * TAU * 0.2)
.rotated_z(time * TAU * 0.3);
let cows = self.cows.lock().unwrap();
let cow_count = cows.len();
let radius = 6.0 + ((time * TAU / 2.5).sin() + 1.0) / 2.0 * 10.0;
// TODO: use eye position.
let player_pos = world
.clients
.iter()
.next()
.map(|c| c.1.position())
.unwrap_or_default();
for (cow_id, p) in cows.iter().cloned().zip(fibonacci_spiral(cow_count)) {
let mut cow = world.entities.get_mut(cow_id).expect("missing cow");
let rotated = p * rot;
let transformed = rotated * radius + [0.0, 100.0, 0.0];
let yaw = f32::atan2(rotated.z as f32, rotated.x as f32).to_degrees() - 90.0;
let (looking_yaw, looking_pitch) =
to_yaw_and_pitch((player_pos - transformed).normalized());
cow.set_position(transformed);
cow.set_yaw(yaw);
cow.set_pitch(looking_pitch);
cow.set_head_yaw(looking_yaw);
}
}
}
/// Distributes N points on the surface of a unit sphere.
fn fibonacci_spiral(n: usize) -> impl Iterator<Item = Vec3<f64>> {
(0..n).map(move |i| {
let golden_ratio = (1.0 + 5_f64.sqrt()) / 2.0;
// Map to unit square
let x = i as f64 / golden_ratio % 1.0;
let y = i as f64 / n as f64;
// Map from unit square to unit sphere.
let theta = x * TAU;
let phi = (1.0 - 2.0 * y).acos();
Vec3::new(theta.cos() * phi.sin(), theta.sin() * phi.sin(), phi.cos())
})
}

View file

@ -77,8 +77,9 @@ impl<'a> ClientsMut<'a> {
ClientsMut(self.0)
}
pub(crate) fn create(&mut self, client: Client) -> ClientId {
ClientId(self.0.sm.insert(client))
pub(crate) fn create(&mut self, client: Client) -> (ClientId, ClientMut) {
let (id, client) = self.0.sm.insert(client);
(ClientId(id), ClientMut(client))
}
pub fn delete(&mut self, client: ClientId) -> bool {

View file

@ -98,7 +98,7 @@ pub trait Config: Any + Send + Sync + UnwindSafe + RefUnwindSafe {
/// An unspecified value is returned that should be adequate in most
/// situations.
fn outgoing_packet_capacity(&self) -> usize {
128
512
}
/// Called once at startup to get a handle to the tokio runtime the server

View file

@ -81,13 +81,9 @@ impl<'a> EntitiesMut<'a> {
///
/// To actually see the new entity, set its position to somewhere nearby and
/// [set its type](EntityData::set_type) to something visible.
pub fn create(&mut self) -> EntityId {
loop {
let uuid = Uuid::from_bytes(rand::random());
if let Some(entity) = self.create_with_uuid(uuid) {
return entity;
}
}
pub fn create(&mut self) -> (EntityId, EntityMut) {
self.create_with_uuid(Uuid::from_bytes(rand::random()))
.expect("UUID collision")
}
/// Like [`create`](Entities::create), but requires specifying the new
@ -95,11 +91,11 @@ impl<'a> EntitiesMut<'a> {
///
/// The provided UUID must not conflict with an existing entity UUID in this
/// world. If it does, `None` is returned and the entity is not spawned.
pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<EntityId> {
pub fn create_with_uuid(&mut self, uuid: Uuid) -> Option<(EntityId, EntityMut)> {
match self.0.uuid_to_entity.entry(uuid) {
Entry::Occupied(_) => None,
Entry::Vacant(ve) => {
let entity = EntityId(self.0.sm.insert(Entity {
let (id, entity) = self.0.sm.insert(Entity {
flags: EntityFlags(0),
meta: EntityMeta::new(EntityType::Marker),
new_position: Vec3::default(),
@ -107,16 +103,15 @@ impl<'a> EntitiesMut<'a> {
yaw: 0.0,
pitch: 0.0,
head_yaw: 0.0,
head_pitch: 0.0,
velocity: Vec3::default(),
uuid,
}));
});
ve.insert(entity);
ve.insert(EntityId(id));
// TODO: insert into partition.
Some(entity)
Some((EntityId(id), EntityMut(entity)))
}
}
}
@ -188,7 +183,6 @@ pub struct Entity {
yaw: f32,
pitch: f32,
head_yaw: f32,
head_pitch: f32,
velocity: Vec3<f32>,
uuid: Uuid,
}

View file

@ -76,11 +76,11 @@ impl<T> SlotMap<T> {
self.count as usize
}
pub fn insert(&mut self, val: T) -> Key {
pub fn insert(&mut self, val: T) -> (Key, &mut T) {
self.insert_with(|_| val)
}
pub fn insert_with(&mut self, f: impl FnOnce(Key) -> T) -> Key {
pub fn insert_with(&mut self, f: impl FnOnce(Key) -> T) -> (Key, &mut T) {
assert!(self.count < u32::MAX, "SlotMap: too many items inserted");
if self.next_free_head == self.slots.len() as u32 {
@ -93,7 +93,12 @@ impl<T> SlotMap<T> {
value: f(key),
version: key.version(),
});
key
let value = match self.slots.last_mut() {
Some(Slot::Occupied { value, .. }) => value,
_ => unreachable!(),
};
(key, value)
} else {
let slot = &mut self.slots[self.next_free_head as usize];
@ -109,9 +114,15 @@ impl<T> SlotMap<T> {
version: key.version(),
};
let value = match slot {
Slot::Occupied { value, .. } => value,
Slot::Free { .. } => unreachable!(),
};
self.next_free_head = next_free;
self.count += 1;
key
(key, value)
}
}
@ -234,14 +245,14 @@ mod tests {
fn insert_remove() {
let mut sm = SlotMap::new();
let k0 = sm.insert(10);
let k1 = sm.insert(20);
let k2 = sm.insert(30);
let k0 = sm.insert(10).0;
let k1 = sm.insert(20).0;
let k2 = sm.insert(30).0;
assert_eq!(sm.remove(k1), Some(20));
assert_eq!(sm.get(k1), None);
assert_eq!(sm.get(k2), Some(&30));
let k3 = sm.insert(40);
let k3 = sm.insert(40).0;
assert_eq!(sm.get(k0), Some(&10));
assert_eq!(sm.get_mut(k3), Some(&mut 40));
assert_eq!(sm.remove(k0), Some(10));
@ -254,9 +265,9 @@ mod tests {
fn retain() {
let mut sm = SlotMap::new();
let k0 = sm.insert(10);
let k1 = sm.insert(20);
let k2 = sm.insert(30);
let k0 = sm.insert(10).0;
let k1 = sm.insert(20).0;
let k2 = sm.insert(30).0;
sm.retain(|k, _| k == k1);

View file

@ -53,3 +53,21 @@ where
aabb
}
/// Takes a normalized direction vector and returns a (yaw, pitch) tuple in degrees.
///
/// This function is the inverse of [`from_yaw_and_pitch`].
pub fn to_yaw_and_pitch(d: Vec3<f64>) -> (f32, f32) {
debug_assert!(d.is_normalized(), "the given vector should be normalized");
let yaw = f32::atan2(d.z as f32, d.x as f32).to_degrees() - 90.0;
let pitch = -(d.y as f32).asin().to_degrees();
(yaw, pitch)
}
/// Takes yaw and pitch angles (in degrees) and returns a normalized direction vector.
///
/// This function is the inverse of [`to_yaw_and_pitch`].
pub fn from_yaw_and_pitch(yaw: f32, pitch: f32) -> Vec3<f64> {
todo!()
}

View file

@ -55,8 +55,8 @@ impl<'a> WorldsMut<'a> {
WorldsMut(self.0)
}
pub fn create(&mut self, dim: DimensionId) -> WorldId {
WorldId(self.0.sm.insert(World {
pub fn create(&mut self, dim: DimensionId) -> (WorldId, WorldMut) {
let (id, world) = self.0.sm.insert(World {
clients: Clients::new(),
entities: Entities::new(),
chunks: Chunks::new(
@ -67,7 +67,9 @@ impl<'a> WorldsMut<'a> {
dimension: dim,
is_flat: false,
},
}))
});
(WorldId(id), WorldMut::new(world))
}
/// Deletes a world from the server. Any [`WorldId`] referring to the