Skip to content

Commit cb6c3f6

Browse files
committed
First commit
Gamemode hardcoded to survival for testing
1 parent d574826 commit cb6c3f6

File tree

6 files changed

+136
-5
lines changed

6 files changed

+136
-5
lines changed

feather/common/src/block_break.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use base::{ValidBlockPosition, ItemStack};
2+
use ecs::{SystemExecutor, SysResult, EntityBuilder};
3+
use quill_common::entity_init::EntityInit;
4+
5+
use crate::{Game, World};
6+
7+
pub type BlockBreaker = Option<ActiveBlockBreaker>;
8+
pub struct ActiveBlockBreaker {
9+
pub position: ValidBlockPosition,
10+
pub drop_item: bool,
11+
pub ticks_remaining: u32,
12+
}
13+
impl ActiveBlockBreaker {
14+
pub fn tick(&mut self) -> bool {
15+
self.ticks_remaining = self.ticks_remaining.saturating_sub(1);
16+
self.ticks_remaining == 0
17+
}
18+
pub fn break_block(self, game: &mut Game) -> SysResult {
19+
let target_block = match game.block(self.position) {
20+
Some(b) => b,
21+
None => anyhow::bail!("cannot break unloaded block")
22+
};
23+
game.break_block(self.position);
24+
if let Some(_item_drop) = base::Item::from_name(target_block.kind().name()) {
25+
if !self.drop_item {
26+
return Ok(())
27+
}
28+
let mut item_entity = EntityBuilder::new();
29+
crate::entities::item::build_default(&mut item_entity);
30+
let builder = game.create_entity_builder(self.position.position(), EntityInit::Item);
31+
game.spawn_entity(builder);
32+
}
33+
Ok(())
34+
}
35+
pub fn new_player(_world: &mut World, block_pos: ValidBlockPosition, _mainhand: Option<&ItemStack>, _offhand: Option<&ItemStack>) -> Option<Self> {
36+
// TODO
37+
Some(Self {
38+
position: block_pos,
39+
drop_item: true,
40+
ticks_remaining: 20,
41+
})
42+
}
43+
}
44+
45+
pub fn register(systems: &mut SystemExecutor<Game>) {
46+
systems.add_system(process_block_breaking);
47+
}
48+
49+
fn process_block_breaking(game: &mut Game) -> SysResult {
50+
let mut break_queue = vec![];
51+
for (entity, breaker) in game.ecs.query::<&mut BlockBreaker>().iter() {
52+
if let Some(active) = breaker {
53+
if active.tick() {
54+
break_queue.push(entity);
55+
}
56+
}
57+
}
58+
for entity in break_queue.into_iter() {
59+
// Set block breakers to None
60+
let breaker = {
61+
game.ecs.get_mut::<BlockBreaker>(entity)?.take().unwrap()
62+
};
63+
breaker.break_block(game)?;
64+
}
65+
Ok(())
66+
}

feather/common/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@ pub mod entities;
3232

3333
pub mod interactable;
3434

35+
pub mod block_break;
36+
3537
/// Registers gameplay systems with the given `Game` and `SystemExecutor`.
3638
pub fn register(game: &mut Game, systems: &mut SystemExecutor<Game>) {
3739
view::register(game, systems);
3840
chunk::loading::register(game, systems);
3941
chunk::entities::register(systems);
4042
interactable::register(game);
43+
block_break::register(systems);
4144

4245
game.add_entity_spawn_callback(entities::add_entity_components);
4346
}

feather/server/src/client.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use common::{
2020
use libcraft_items::InventorySlot;
2121
use packets::server::{Particle, SetSlot, SpawnLivingEntity, UpdateLight, WindowConfirmation};
2222
use protocol::packets::server::{
23-
EntityPosition, EntityPositionAndRotation, EntityTeleport, HeldItemChange, PlayerAbilities,
23+
EntityPosition, EntityPositionAndRotation, EntityTeleport, HeldItemChange, PlayerAbilities, PlayerDiggingStatus, AcknowledgePlayerDigging, BlockBreakAnimation,
2424
};
2525
use protocol::{
2626
packets::{
@@ -602,6 +602,23 @@ impl Client {
602602
self.send_packet(HeldItemChange { slot });
603603
}
604604

605+
pub fn acknowledge_player_digging(&self, position: ValidBlockPosition, block: BlockId, status: PlayerDiggingStatus, successful: bool) {
606+
self.send_packet(AcknowledgePlayerDigging {
607+
position,
608+
block,
609+
status,
610+
successful,
611+
})
612+
}
613+
614+
pub fn block_break_animation(&self, entity_id: i32, position: ValidBlockPosition, destroy_stage: u8) {
615+
self.send_packet(BlockBreakAnimation {
616+
entity_id,
617+
position,
618+
destroy_stage,
619+
})
620+
}
621+
605622
fn register_entity(&self, network_id: NetworkId) {
606623
self.sent_entities.borrow_mut().insert(network_id);
607624
}

feather/server/src/packet_handlers/interaction.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::{ClientId, NetworkId, Server};
2+
use base::Gamemode;
23
use base::inventory::{SLOT_HOTBAR_OFFSET, SLOT_OFFHAND};
4+
use common::block_break::{BlockBreaker, ActiveBlockBreaker};
35
use common::entities::player::HotbarSlot;
46
use common::interactable::InteractableRegistry;
57
use common::{Game, Window};
@@ -119,8 +121,32 @@ pub fn handle_player_digging(
119121
) -> SysResult {
120122
log::trace!("Got player digging with status {:?}", packet.status);
121123
match packet.status {
122-
PlayerDiggingStatus::StartDigging | PlayerDiggingStatus::CancelDigging => {
123-
game.break_block(packet.position);
124+
PlayerDiggingStatus::StartDigging => {
125+
if matches!(*game.ecs.get::<Gamemode>(player)?, Gamemode::Creative | Gamemode::Spectator) {
126+
game.break_block(packet.position);
127+
} else {
128+
let mut breaker = game.ecs.get_mut::<BlockBreaker>(player)?;
129+
let window = game.ecs.get::<Window>(player)?;
130+
let hotbar_slot = game.ecs.get::<HotbarSlot>(player)?.get();
131+
let main = window.item(SLOT_HOTBAR_OFFSET + hotbar_slot)?;
132+
let offh = window.item(SLOT_OFFHAND)?;
133+
let _ = breaker.insert(ActiveBlockBreaker::new_player(&mut game.world, packet.position, main.item_stack(), offh.item_stack()).unwrap());
134+
}
135+
136+
Ok(())
137+
},
138+
PlayerDiggingStatus::CancelDigging => {
139+
game.ecs.get_mut::<BlockBreaker>(player)?.take();
140+
let client = server.clients.get(*game.ecs.get(player)?).unwrap();
141+
let block = match game.block(packet.position) { Some(s)=>s,None=>return Ok(())};
142+
client.acknowledge_player_digging(packet.position, block, protocol::packets::server::PlayerDiggingStatus::Cancelled, false);
143+
Ok(())
144+
},
145+
PlayerDiggingStatus::FinishDigging => {
146+
let success = game.ecs.get::<BlockBreaker>(player)?.is_some();
147+
let client = server.clients.get(*game.ecs.get(player)?).unwrap();
148+
let block = match game.block(packet.position) { Some(s)=>s,None=>return Ok(())};
149+
client.acknowledge_player_digging(packet.position, block, protocol::packets::server::PlayerDiggingStatus::Finished, success);
124150
Ok(())
125151
}
126152
PlayerDiggingStatus::SwapItemInHand => {
@@ -145,7 +171,10 @@ pub fn handle_player_digging(
145171

146172
Ok(())
147173
}
148-
_ => Ok(()),
174+
PlayerDiggingStatus::DropItemStack => Ok(()),
175+
PlayerDiggingStatus::DropItem => Ok(()),
176+
PlayerDiggingStatus::ShootArrow => Ok(()),
177+
149178
}
150179
}
151180

feather/server/src/systems/player_join.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use common::block_break::BlockBreaker;
12
use libcraft_items::InventorySlot;
23
use log::debug;
34

@@ -108,7 +109,7 @@ fn accept_new_player(game: &mut Game, server: &mut Server, client_id: ClientId)
108109
Position::default().chunk(),
109110
server.options.view_distance,
110111
))
111-
.add(gamemode)
112+
.add(Gamemode::Survival)
112113
.add(previous_gamemode)
113114
.add(Name::new(client.username()))
114115
.add(client.uuid())
@@ -123,6 +124,7 @@ fn accept_new_player(game: &mut Game, server: &mut Server, client_id: ClientId)
123124
.map(|data| data.animal.health)
124125
.unwrap_or(20.0),
125126
))
127+
.add(BlockBreaker::None)
126128
.add(abilities.walk_speed)
127129
.add(abilities.fly_speed)
128130
.add(abilities.is_flying)

libcraft/items/src/inventory_slot.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,20 @@ impl InventorySlot {
188188
InventorySlot::Empty => None,
189189
}
190190
}
191+
192+
pub fn item_stack(&self) -> Option<&ItemStack> {
193+
match self {
194+
InventorySlot::Filled(f) => Some(f),
195+
InventorySlot::Empty => None,
196+
}
197+
}
198+
199+
pub fn item_stack_mut(&mut self) -> Option<&mut ItemStack> {
200+
match self {
201+
InventorySlot::Filled(f) => Some(f),
202+
InventorySlot::Empty => None,
203+
}
204+
}
191205
}
192206

193207
#[cfg(test)]

0 commit comments

Comments
 (0)