Skip to content

Commit

Permalink
Added unique block standard functions
Browse files Browse the repository at this point in the history
  • Loading branch information
OfficialKris committed Dec 24, 2024
1 parent f47c409 commit f8bea62
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 63 deletions.
8 changes: 8 additions & 0 deletions pumpkin-inventory/src/open_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ impl OpenContainer {
self.container.lock().await.clear_all_slots();
}

pub fn clear_all_players(&mut self) {
self.players = vec![];
}

pub fn all_player_ids(&self) -> Vec<i32> {
self.players.clone()
}
Expand All @@ -76,6 +80,10 @@ impl OpenContainer {
self.location
}

pub async fn set_location(&mut self, location: Option<WorldPosition>) {
self.location = location;
}

pub fn get_block(&self) -> Option<Block> {
self.block.clone()
}
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/block/blocks/chest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl PumpkinBlock for ChestBlock {
location: WorldPosition,
server: &Server,
) {
super::standard_on_destroy_with_container(block, player, location, server).await;
super::standard_on_broken_with_container(block, player, location, server).await;
}

async fn on_close<'a>(
Expand Down
55 changes: 12 additions & 43 deletions pumpkin/src/block/blocks/crafting_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl PumpkinBlock for CraftingTableBlock {
location: WorldPosition,
server: &Server,
) {
super::standard_on_destroy_with_container(block, player, location, server).await;
super::standard_on_broken_with_container(block, player, location, server).await;
}

async fn on_close<'a>(
Expand All @@ -56,14 +56,15 @@ impl PumpkinBlock for CraftingTableBlock {
container: &OpenContainer,
) {
let entity_id = player.entity_id();

for player_id in container.all_player_ids() {
if entity_id == player_id {
container.clear_all_slots().await;
}
}

// TODO: should re-add all items to player or drop?
// TODO: items should be re-added to player inventory or dropped dependending on if they are in movement.
// TODO: unique containers should be implemented as a separate stack internally (optimizes large player servers for example)
// TODO: ephemeral containers (crafting tables) might need to be separate data structure than stored (ender chest)
}
}

Expand All @@ -75,45 +76,13 @@ impl CraftingTableBlock {
location: WorldPosition,
server: &Server,
) {
//TODO: Adjust /craft command to real crafting table
let entity_id = player.entity_id();
let mut open_containers = server.open_containers.write().await;
let mut id_to_use = -1;

for (id, container) in open_containers.iter() {
if let Some(a_block) = container.get_block() {
if a_block.id == block.id && container.all_player_ids().is_empty() {
id_to_use = *id as i64;
}
}
}

if id_to_use == -1 {
let new_id = server.new_container_id();

log::info!("New ct ID: {}", new_id);

let open_container = OpenContainer::new_empty_container::<CraftingTable>(
entity_id,
Some(location),
Some(block.clone()),
);

open_containers.insert(new_id.into(), open_container);

player.open_container.store(Some(new_id.into()));
} else {
log::info!("Using previous ct ID: {}", id_to_use);
if let Some(ender_chest) = open_containers.get_mut(&(id_to_use as u64)) {
ender_chest.add_player(entity_id);
player
.open_container
.store(Some(id_to_use.try_into().unwrap()));
}
}
drop(open_containers);
player
.open_container(server, WindowType::CraftingTable)
.await;
super::standard_open_container_unique::<CraftingTable>(
block,
player,
location,
server,
WindowType::CraftingTable,
)
.await;
}
}
2 changes: 1 addition & 1 deletion pumpkin/src/block/blocks/furnace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl PumpkinBlock for FurnaceBlock {
location: WorldPosition,
server: &Server,
) {
super::standard_on_destroy_with_container(block, player, location, server).await;
super::standard_on_broken_with_container(block, player, location, server).await;
}
}

Expand Down
75 changes: 58 additions & 17 deletions pumpkin/src/block/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ pub(crate) mod furnace;
pub(crate) mod jukebox;

/// The standard destroy with container removes the player forcibly from the container,
/// drops items to the floor and back to the player's inventory if the item stack is in movement.
pub async fn standard_on_destroy_with_container(
/// drops items to the floor, and back to the player's inventory if the item stack is in movement.
pub async fn standard_on_broken_with_container(
block: &Block,
player: &Player,
location: WorldPosition,
server: &Server,
) {
// TODO: drop all items and back to players inventory if in motion
let entity_id = player.entity_id();
if let Some(container_id) = server.get_container_id(location, block.clone()).await {
if let Some(all_container_ids) = server.get_all_container_ids(location, block.clone()).await {
let mut open_containers = server.open_containers.write().await;
if let Some(container) = open_containers.get_mut(&u64::from(container_id)) {
log::info!("Good ct ID: {}", container_id);
container.remove_player(entity_id);
container.clear_all_slots().await;
player.open_container.store(None);
close_all_in_container(player, container).await;
for individual_id in all_container_ids {
if let Some(container) = open_containers.get_mut(&u64::from(individual_id)) {
container.clear_all_slots().await;
player.open_container.store(None);
close_all_in_container(player, container).await;
container.clear_all_players();
}
}
}
}
Expand All @@ -42,31 +42,72 @@ pub async fn standard_open_container<C: Container + Default + 'static>(
window_type: WindowType,
) {
let entity_id = player.entity_id();
let mut open_containers = server.open_containers.write().await;
// If container exists, add player to container, otherwise create new container
if let Some(container_id) = server.get_container_id(location, block.clone()).await {
let mut open_containers = server.open_containers.write().await;
log::debug!("Using previous standard container ID: {}", container_id);
if let Some(container) = open_containers.get_mut(&u64::from(container_id)) {
log::info!("Good ID: {}", container_id);
container.add_player(entity_id);
player.open_container.store(Some(container_id.into()));
}
} else {
let mut open_containers = server.open_containers.write().await;

let new_id = server.new_container_id();
log::info!("New ID: {}", new_id);
log::debug!("Creating new standard container ID: {}", new_id);
let open_container =
OpenContainer::new_empty_container::<C>(entity_id, Some(location), Some(block.clone()));
open_containers.insert(new_id.into(), open_container);
player.open_container.store(Some(new_id.into()));
}
player.open_container(server, window_type).await;
}

pub async fn standard_open_container_unique<C: Container + Default + 'static>(
block: &Block,
player: &Player,
location: WorldPosition,
server: &Server,
window_type: WindowType,
) {
let entity_id = player.entity_id();
let mut open_containers = server.open_containers.write().await;
let mut id_to_use = -1;

// TODO: we can do better than brute force
for (id, container) in open_containers.iter() {
if let Some(a_block) = container.get_block() {
if a_block.id == block.id && container.all_player_ids().is_empty() {
id_to_use = *id as i64;
}
}
}

if id_to_use == -1 {
let new_id = server.new_container_id();
log::debug!("Creating new unqiue container ID: {}", new_id);
let open_container =
OpenContainer::new_empty_container::<C>(entity_id, Some(location), Some(block.clone()));

open_containers.insert(new_id.into(), open_container);

player.open_container.store(Some(new_id.into()));
} else {
log::debug!("Using previous unqiue container ID: {}", id_to_use);
if let Some(unique_container) = open_containers.get_mut(&(id_to_use as u64)) {
unique_container.set_location(Some(location)).await;
unique_container.add_player(entity_id);
player
.open_container
.store(Some(id_to_use.try_into().unwrap()));
}
}
drop(open_containers);
player.open_container(server, window_type).await;
}

pub async fn close_all_in_container(player: &Player, container: &OpenContainer) {
for id in container.all_player_ids() {
if let Some(y) = player.world().get_player_by_entityid(id).await {
y.close_container().await;
if let Some(remote_player) = player.world().get_player_by_entityid(id).await {
remote_player.close_container().await;
}
}
}
29 changes: 28 additions & 1 deletion pumpkin/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub struct Server {
/// Caches game registries for efficient access.
pub cached_registry: Vec<Registry>,
/// Tracks open containers used for item interactions.
// TODO: should have per player open_containers
pub open_containers: RwLock<HashMap<u64, OpenContainer>>,
pub drag_handler: DragHandler,
/// Assigns unique IDs to entities.
Expand Down Expand Up @@ -194,6 +195,8 @@ impl Server {
.cloned()
}

/// Returns the first id with a matching location and block type. If this is used with unique
/// blocks, the output will return a random result.
pub async fn get_container_id(&self, location: WorldPosition, block: Block) -> Option<u32> {
let open_containers = self.open_containers.read().await;
// TODO: do better than brute force
Expand All @@ -207,13 +210,37 @@ impl Server {
}
}
}
log::debug!("No container found... this should not happen.");
log::error!("No container found... this should not happen.");

drop(open_containers);

None
}

pub async fn get_all_container_ids(
&self,
location: WorldPosition,
block: Block,
) -> Option<Vec<u32>> {
let open_containers = self.open_containers.read().await;
let mut matching_container_ids: Vec<u32> = vec![];
// TODO: do better than brute force
for (id, container) in open_containers.iter() {
if container.is_location(location) {
if let Some(container_block) = container.get_block() {
if container_block.id == block.id {
log::debug!("Found matching container id: {}", id);
matching_container_ids.push(*id as u32);
}
}
}
}

drop(open_containers);

Some(matching_container_ids)
}

/// Broadcasts a packet to all players in all worlds.
///
/// This function sends the specified packet to every connected player in every world managed by the server.
Expand Down

0 comments on commit f8bea62

Please sign in to comment.