1use std::collections::HashMap;
2
3use fmc::{
4 bevy::math::{DQuat, DVec3},
5 blocks::{BlockPosition, Blocks},
6 database::Database,
7 interfaces::{
8 HeldInterfaceStack, InterfaceEventRegistration, InterfaceEvents, RegisterInterfaceNode,
9 },
10 items::ItemStack,
11 models::{AnimationPlayer, Model, Models},
12 networking::{NetworkEvent, NetworkMessage, Server},
13 physics::{Collider, Physics},
14 players::{Camera, Player},
15 prelude::*,
16 protocol::messages,
17 world::{
18 chunk::{Chunk, ChunkPosition},
19 WorldMap,
20 },
21};
22use serde::{Deserialize, Serialize};
23
24use crate::{
25 items::{crafting::CraftingGrid, DroppedItem},
26 world::WorldProperties,
27};
28
29use self::health::HealthBundle;
30
31mod hand;
32mod health;
33mod inventory_interface;
34mod movement;
35
36pub use hand::{HandHits, HandInteractions};
37pub use health::{HealEvent, Health, PlayerDamageEvent};
38
39pub struct PlayerPlugin;
40impl Plugin for PlayerPlugin {
41 fn build(&self, app: &mut App) {
42 app.add_event::<RespawnEvent>()
43 .add_plugins(inventory_interface::InventoryInterfacePlugin)
44 .add_plugins(health::HealthPlugin)
45 .add_plugins(hand::HandPlugin)
46 .add_plugins(movement::MovementPlugin)
47 .add_systems(
48 Update,
49 (
50 on_gamemode_update,
51 (add_players, apply_deferred).chain(),
52 respawn_players,
53 rotate_player_model,
54 discard_items.after(InterfaceEventRegistration),
55 ),
56 )
57 .add_systems(PostUpdate, save_player_data);
60 }
61}
62
63#[derive(Component, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
64pub enum GameMode {
65 Survival,
66 Creative,
67}
68
69#[derive(Component, Serialize, Deserialize, Deref, DerefMut, Clone)]
70pub struct Inventory {
71 #[deref]
72 inventory: Vec<ItemStack>,
73 equipped_item: usize,
74}
75
76impl Default for Inventory {
77 fn default() -> Self {
78 let capacity = 36;
79 let mut inventory = Vec::with_capacity(capacity);
80 inventory.resize_with(capacity, ItemStack::default);
81
82 Self {
83 inventory,
84 equipped_item: 0,
85 }
86 }
87}
88
89impl Inventory {
90 pub fn held_item_stack(&self) -> &ItemStack {
91 &self[self.equipped_item]
92 }
93
94 pub fn held_item_stack_mut(&mut self) -> &mut ItemStack {
95 let index = self.equipped_item;
96 &mut self[index]
97 }
98}
99
100#[derive(Component, Default, Serialize, Deserialize, Clone)]
104pub struct Equipment {
105 pub helmet: ItemStack,
106 pub chestplate: ItemStack,
107 pub leggings: ItemStack,
108 pub boots: ItemStack,
109}
110
111#[derive(Bundle)]
113pub struct PlayerBundle {
114 pub transform: Transform,
115 pub camera: Camera,
116 pub aabb: Collider,
117 pub inventory: Inventory,
118 pub equipment: Equipment,
119 pub crafting_table: CraftingGrid,
120 pub health_bundle: HealthBundle,
121 pub gamemode: GameMode,
122}
123
124impl Default for PlayerBundle {
125 fn default() -> Self {
126 Self {
127 transform: Transform::default(),
128 camera: Camera::default(),
129 aabb: Collider::from_min_max(DVec3::new(-0.3, 0.0, -0.3), DVec3::new(0.3, 1.8, 0.3)),
130 inventory: Inventory::default(),
131 equipment: Equipment::default(),
132 crafting_table: CraftingGrid::with_size(4),
133 health_bundle: HealthBundle::default(),
134 gamemode: GameMode::Survival,
135 }
136 }
137}
138
139impl From<PlayerSave> for PlayerBundle {
140 fn from(save: PlayerSave) -> Self {
141 PlayerBundle {
142 transform: Transform::from_translation(save.position),
143 camera: Camera::new(Transform {
144 translation: save.camera_position,
145 rotation: save.camera_rotation,
146 ..default()
147 }),
148 inventory: save.inventory,
149 equipment: save.equipment,
150 health_bundle: HealthBundle::from_health(save.health),
151 gamemode: save.game_mode,
152 ..default()
153 }
154 }
155}
156
157#[derive(Serialize, Deserialize)]
161pub struct PlayerSave {
162 position: DVec3,
163 camera_position: DVec3,
164 camera_rotation: DQuat,
165 inventory: Inventory,
166 equipment: Equipment,
167 health: Health,
168 game_mode: GameMode,
169}
170
171impl PlayerSave {
172 fn save(&self, username: &str, database: &Database) {
173 let conn = database.get_write_connection();
174
175 let mut stmt = conn
176 .prepare("INSERT OR REPLACE INTO players VALUES (?,?)")
177 .unwrap();
178 let json = serde_json::to_string(self).unwrap();
179
180 stmt.execute(rusqlite::params![username, json]).unwrap();
181 }
182
183 fn load(username: &str, database: &Database) -> Option<Self> {
184 let conn = database.get_read_connection();
185
186 let mut stmt = conn
187 .prepare("SELECT save FROM players WHERE name = ?")
188 .unwrap();
189 let mut rows = if let Ok(rows) = stmt.query([username]) {
190 rows
191 } else {
192 return None;
193 };
194
195 if let Some(row) = rows.next().unwrap() {
197 let json: String = row.get_unwrap(0);
198 let save: PlayerSave = serde_json::from_str(&json).unwrap();
199 return Some(save);
200 } else {
201 return None;
202 };
203 }
204}
205
206fn add_players(
207 mut commands: Commands,
208 net: Res<Server>,
209 database: Res<Database>,
210 models: Res<Models>,
211 mut respawn_events: EventWriter<RespawnEvent>,
212 mut registration_events: EventWriter<RegisterInterfaceNode>,
213 added_players: Query<(Entity, &Player), Added<Player>>,
214) {
215 for (player_entity, player) in added_players.iter() {
216 let bundle = if let Some(save) = PlayerSave::load(&player.username, &database) {
217 PlayerBundle::from(save)
218 } else {
219 respawn_events.send(RespawnEvent { player_entity });
220 PlayerBundle::default()
221 };
222
223 net.send_one(
224 player_entity,
225 messages::PlayerPosition {
226 position: bundle.transform.translation,
227 },
228 );
229
230 net.send_one(
231 player_entity,
232 messages::PlayerCameraPosition {
233 position: bundle.camera.translation.as_vec3(),
234 },
235 );
236
237 net.send_one(
238 player_entity,
239 messages::PlayerCameraRotation {
240 rotation: bundle.camera.rotation.as_quat(),
241 },
242 );
243
244 let model = models.get_by_name("player");
245
246 let mut animation_player = AnimationPlayer::default();
247 animation_player.set_move_animation(Some(model.animations["walk"]));
248 animation_player.set_idle_animation(Some(model.animations["idle"]));
249 animation_player.set_transition_time(0.15);
250
251 let model_entity = commands
252 .spawn(Model::Asset(model.id))
253 .set_parent(player_entity)
254 .id();
255 animation_player.set_target(model_entity);
256
257 let discard_items_entity = commands.spawn(DiscardItems).id();
258 registration_events.send(RegisterInterfaceNode {
259 player_entity,
260 node_path: "".to_owned(),
261 node_entity: discard_items_entity,
262 });
263
264 commands
265 .entity(player_entity)
266 .insert((bundle, animation_player))
267 .add_child(discard_items_entity);
268 }
269}
270
271fn save_player_data(
272 database: Res<Database>,
273 mut network_events: EventReader<NetworkEvent>,
274 mut players: Query<(
275 &Player,
276 &Transform,
277 &Camera,
278 &Inventory,
279 &Equipment,
280 &Health,
281 &GameMode,
282 )>,
283) {
284 for network_event in network_events.read() {
285 let NetworkEvent::Disconnected { entity } = network_event else {
286 continue;
287 };
288
289 let Ok((player, transform, camera, inventory, equipment, health, game_mode)) =
290 players.get_mut(*entity)
291 else {
292 continue;
293 };
294
295 PlayerSave {
296 position: transform.translation,
297 camera_position: camera.translation,
298 camera_rotation: camera.rotation,
299 inventory: inventory.clone(),
300 equipment: equipment.clone(),
301 health: health.clone(),
302 game_mode: *game_mode,
303 }
304 .save(&player.username, &database);
305 }
306}
307
308#[derive(Event)]
309pub struct RespawnEvent {
310 pub player_entity: Entity,
311}
312
313fn respawn_players(
320 net: Res<Server>,
321 world_properties: Res<WorldProperties>,
322 world_map: Res<WorldMap>,
323 database: Res<Database>,
324 mut player_query: Query<&mut Transform, With<Player>>,
325 mut heal_events: EventWriter<HealEvent>,
326 mut respawn_events: EventReader<RespawnEvent>,
327) {
328 for respawn_event in respawn_events.read() {
329 let blocks = Blocks::get();
330 let air = blocks.get_id("air");
331
332 let mut chunk_position = ChunkPosition::from(world_properties.spawn_point.center);
333 let spawn_position = 'outer: loop {
334 let chunk = futures_lite::future::block_on(Chunk::load(
335 chunk_position,
336 world_map.terrain_generator.clone(),
337 database.clone(),
338 ))
339 .1;
340
341 if chunk.is_uniform() && chunk[0] == air {
342 break BlockPosition::from(chunk_position);
343 }
344
345 for (i, block_column) in chunk.blocks.chunks_exact(Chunk::SIZE).enumerate() {
347 let mut count = 0;
348 for (j, block) in block_column.iter().enumerate() {
349 if count == 0 && *block == air {
350 count += 1;
351 } else if count == 1 && *block == air {
352 let mut spawn_position = BlockPosition::from(chunk_position)
353 + BlockPosition::from(i * Chunk::SIZE + j);
354 spawn_position.y -= 1;
355 break 'outer spawn_position;
356 } else {
357 count = 0;
358 }
359 }
360 }
361
362 chunk_position.y += Chunk::SIZE as i32;
363 };
364
365 let spawn_position = spawn_position.as_dvec3() + DVec3::new(0.5, 0.0, 0.5);
366
367 let mut player_transform = player_query.get_mut(respawn_event.player_entity).unwrap();
371 player_transform.translation = spawn_position;
372
373 heal_events.send(HealEvent {
374 player_entity: respawn_event.player_entity,
375 healing: u32::MAX,
376 });
377
378 net.send_one(
379 respawn_event.player_entity,
380 messages::PlayerPosition {
381 position: spawn_position,
382 },
383 );
384 }
385}
386
387fn rotate_player_model(
392 mut player_query: Query<&mut Transform, With<Player>>,
393 mut camera_rotation_events: EventReader<NetworkMessage<messages::PlayerCameraRotation>>,
394) {
395 for rotation_update in camera_rotation_events.read() {
396 let mut transform = player_query.get_mut(rotation_update.player_entity).unwrap();
397
398 let rotation = rotation_update.rotation.as_dquat();
399
400 let theta = rotation.y.atan2(rotation.w);
401 transform.rotation = DQuat::from_xyzw(0.0, theta.sin(), 0.0, theta.cos());
402 }
403}
404
405fn on_gamemode_update(
406 net: Res<Server>,
407 player_query: Query<(Entity, &GameMode), Changed<GameMode>>,
408 mut current_movement_function: Local<HashMap<Entity, Option<String>>>,
409) {
410 for (player_entity, gamemode) in player_query.iter() {
411 let current_movement_function = current_movement_function.entry(player_entity).or_default();
412 if let Some(name) = current_movement_function.take() {
413 net.send_one(player_entity, messages::Plugin::Disable(name));
414 }
415
416 match gamemode {
417 GameMode::Creative => {
418 let mut health_visibility = messages::InterfaceNodeVisibilityUpdate::default();
419 health_visibility.set_hidden("health".to_owned());
420 net.send_one(player_entity, health_visibility);
421
422 net.send_one(
423 player_entity,
424 messages::Plugin::Enable("creative".to_owned()),
425 );
426 *current_movement_function = Some("creative".to_owned());
427 }
428 GameMode::Survival => {
429 let mut health_visibility = messages::InterfaceNodeVisibilityUpdate::default();
430 health_visibility.set_visible("health".to_owned());
431 net.send_one(player_entity, health_visibility);
432
433 net.send_one(
434 player_entity,
435 messages::Plugin::Enable("movement".to_owned()),
436 );
437 *current_movement_function = Some("movement".to_owned());
438 }
439 }
440 }
441}
442
443#[derive(Component)]
444struct DiscardItems;
445
446fn discard_items(
447 mut commands: Commands,
448 mut inventory_query: Query<(&mut HeldInterfaceStack, &GlobalTransform, &Camera), With<Player>>,
449 mut interface_events: Query<
450 (&mut InterfaceEvents, &Parent),
451 (Changed<InterfaceEvents>, With<DiscardItems>),
452 >,
453) {
454 for (mut interface_events, parent) in interface_events.iter_mut() {
455 let (mut held_item, transform, camera) = inventory_query.get_mut(parent.get()).unwrap();
456 for event in interface_events.read() {
457 if let messages::InterfaceInteraction::PlaceItem { quantity, .. } = *event {
458 let discarded = held_item.take(quantity);
459 if discarded.size() == 0 {
460 continue;
461 }
462
463 let dropped_item_position =
464 transform.translation() + camera.translation + camera.forward();
465 commands.spawn((
466 DroppedItem::new(discarded),
467 Transform::from_translation(dropped_item_position),
468 Physics {
469 velocity: camera.forward() * 12.0,
470 ..default()
471 },
472 ));
473 }
474 }
475 }
476}