Skip to content

Commit

Permalink
Merge pull request #382 from AnthonyTornetta/350-logic-system-crashin…
Browse files Browse the repository at this point in the history
…g-when-wires-are-broken

350 logic system crashing when wires are broken
  • Loading branch information
AnthonyTornetta authored Dec 21, 2024
2 parents 5584e47 + bc1a30b commit fff9d25
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 56 deletions.
2 changes: 1 addition & 1 deletion cosmos_core/src/events/block_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use bevy::prelude::App;
use bevy::prelude::Entity;
use bevy::prelude::Event;

#[derive(Debug, Event)]
#[derive(Debug, Event, Clone)]
/// Sent when a block is changed (destroyed or placed)
///
/// This is NOT SENT when a block's data is modified.
Expand Down
29 changes: 25 additions & 4 deletions cosmos_core/src/logic/logic_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
use bevy::{
prelude::{Component, Entity, EventWriter},
reflect::Reflect,
utils::HashSet,
utils::{HashMap, HashSet},
};

use crate::{
block::{block_direction::BlockDirection, block_face::ALL_BLOCK_FACES, block_rotation::BlockRotation, Block},
events::block_events::BlockChangedEvent,
logic::LogicConnection,
registry::Registry,
structure::{coordinates::BlockCoordinate, structure_block::StructureBlock, Structure},
Expand Down Expand Up @@ -45,6 +46,7 @@ impl LogicDriver {
port_type: PortType,
structure: &Structure,
entity: Entity,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
evw_queue_logic_output: &mut EventWriter<QueueLogicOutputEvent>,
Expand All @@ -63,6 +65,7 @@ impl LogicDriver {
None,
false,
structure,
events_by_coords,
&mut Port::all_for(coords),
blocks,
logic_blocks,
Expand All @@ -89,6 +92,7 @@ impl LogicDriver {
coords: BlockCoordinate,
structure: &Structure,
entity: Entity,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
logic_wire_colors: &Registry<LogicWireColor>,
Expand All @@ -103,6 +107,7 @@ impl LogicDriver {
PortType::Input,
structure,
entity,
events_by_coords,
blocks,
logic_blocks,
evw_queue_logic_output,
Expand All @@ -118,6 +123,7 @@ impl LogicDriver {
PortType::Output,
structure,
entity,
events_by_coords,
blocks,
logic_blocks,
evw_queue_logic_output,
Expand All @@ -142,6 +148,7 @@ impl LogicDriver {
Some(wire_color_id),
logic_block.connection_on(wire_face) == Some(crate::logic::LogicConnection::Wire(WireType::Bus)),
structure,
events_by_coords,
&mut Port::all_for(coords),
blocks,
logic_blocks,
Expand Down Expand Up @@ -173,6 +180,7 @@ impl LogicDriver {
coords: BlockCoordinate,
structure: &Structure,
entity: Entity,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
logic_wire_colors: &Registry<LogicWireColor>,
Expand All @@ -186,6 +194,9 @@ impl LogicDriver {
rotation.direction_of(input_face),
PortType::Input,
structure,
events_by_coords,
blocks,
logic_blocks,
evw_queue_logic_input,
)
}
Expand All @@ -197,16 +208,25 @@ impl LogicDriver {
rotation.direction_of(output_face),
PortType::Output,
structure,
events_by_coords,
blocks,
logic_blocks,
evw_queue_logic_input,
)
}

// A block with no wire faces will have an empty wire face colors iterator.
for wire_color_id in logic_block.wire_face_colors(logic_wire_colors) {
// Old group ID either comes from being the stored wire coordinate for a group, or searching all your neighbors.
let old_group_id = self
.logic_graph
.get_wire_group(coords, wire_color_id, logic_block, structure, blocks, logic_blocks);
let old_group_id = self.logic_graph.get_wire_group(
coords,
wire_color_id,
logic_block,
structure,
events_by_coords,
blocks,
logic_blocks,
);
let was_on = self.logic_graph.get_group(old_group_id).on();

// Setting new group IDs.
Expand All @@ -225,6 +245,7 @@ impl LogicDriver {
wire_color_id,
logic_block.connection_on(wire_face) == Some(LogicConnection::Wire(WireType::Bus)),
structure,
events_by_coords,
&mut visited,
blocks,
logic_blocks,
Expand Down
60 changes: 49 additions & 11 deletions cosmos_core/src/logic/logic_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy::{

use crate::{
block::{block_direction::BlockDirection, Block},
events::block_events::BlockChangedEvent,
registry::{identifiable::Identifiable, Registry},
structure::{coordinates::BlockCoordinate, structure_block::StructureBlock, Structure},
};
Expand Down Expand Up @@ -86,10 +87,6 @@ impl LogicGroup {
);
}
}

pub(crate) fn has_one_port(&self) -> bool {
(self.consumers.len() + self.producers.len()) == 1
}
}

#[derive(Debug, Default, Reflect)]
Expand Down Expand Up @@ -158,18 +155,36 @@ impl LogicGraph {
}))
}

/// `LogicGraph`'s `dfs_for_group` method needs to know which blocks are at each coordinate to properly search the graph.
///
/// However, several `BlockChangedEvent`s can occur on the same tick.
/// We use `events_by_coords` to track all the events the logic graph has alredy processed this tick, and pretend the blocks have already been changed in the structure.
fn block_at<'a>(
&self,
coords: BlockCoordinate,
structure: &'a Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &'a Registry<Block>,
) -> &'a Block {
if let Some(ev) = events_by_coords.get(&coords) {
return blocks.from_numeric_id(ev.new_block);
}
structure.block_at(coords, blocks)
}

pub fn dfs_for_group(
&self,
coords: BlockCoordinate,
encountered_from_direction: BlockDirection,
mut required_color_id: Option<u16>,
from_bus: bool,
structure: &Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
visited: &mut HashSet<Port>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
) -> Option<usize> {
let block = structure.block_at(coords, blocks);
let block = self.block_at(coords, structure, events_by_coords, blocks);
let Some(logic_block) = logic_blocks.from_id(block.unlocalized_name()) else {
// Not a logic block.
return None;
Expand Down Expand Up @@ -232,6 +247,7 @@ impl LogicGraph {
Some(wire_color_id),
wire_type == WireType::Bus,
structure,
events_by_coords,
visited,
blocks,
logic_blocks,
Expand All @@ -255,6 +271,7 @@ impl LogicGraph {
wire_color_id: u16,
coords: BlockCoordinate,
structure: &Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
visited: &mut HashSet<Port>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
Expand All @@ -270,6 +287,7 @@ impl LogicGraph {
Some(wire_color_id),
logic_block.connection_on(wire_face) == Some(LogicConnection::Wire(WireType::Bus)),
structure,
events_by_coords,
visited,
blocks,
logic_blocks,
Expand All @@ -286,6 +304,7 @@ impl LogicGraph {
wire_color_id: u16,
logic_block: &LogicBlock,
structure: &Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
) -> usize {
Expand All @@ -304,6 +323,7 @@ impl LogicGraph {
wire_color_id,
coords,
structure,
events_by_coords,
&mut Port::all_for(coords),
blocks,
logic_blocks,
Expand Down Expand Up @@ -365,22 +385,38 @@ impl LogicGraph {
direction: BlockDirection,
port_type: PortType,
structure: &Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
evw_queue_logic_input: &mut EventWriter<QueueLogicInputEvent>,
) {
// If the neighbor coordinates don't exist, no port is removed.
let Ok(neighbor_coords) = coords.step(direction) else {
return;
};

let port = Port::new(coords, direction);
let &group_id = match port_type {
PortType::Input => &mut self.input_port_group_id,
PortType::Output => &mut self.output_port_group_id,
}
.get(&port)
.unwrap_or_else(|| panic!("Port {port:?} to be removed should have a logic group."));
.expect("Port to be removed should exist.");

// Check if this port is the last block of its group, and delete the group if so.
if self
.groups
.get(&group_id)
.expect("Logic group with port should exist.")
.has_one_port()
.dfs_for_group(
neighbor_coords,
direction.inverse(),
None,
false,
structure,
events_by_coords,
&mut Port::all_for(coords),
blocks,
logic_blocks,
)
.is_none()
{
self.remove_group(group_id);
} else {
Expand Down Expand Up @@ -480,6 +516,7 @@ impl LogicGraph {
wire_color_id: u16,
from_bus: bool,
structure: &Structure,
events_by_coords: &HashMap<BlockCoordinate, BlockChangedEvent>,
visited: &mut HashSet<Port>,
blocks: &Registry<Block>,
logic_blocks: &Registry<LogicBlock>,
Expand All @@ -490,7 +527,7 @@ impl LogicGraph {
// Renaming on this portion already completed.
return false;
}
let block = structure.block_at(coords, blocks);
let block = self.block_at(coords, structure, events_by_coords, blocks);
let Some(logic_block) = logic_blocks.from_id(block.unlocalized_name()) else {
// Not a logic block.
return false;
Expand Down Expand Up @@ -553,6 +590,7 @@ impl LogicGraph {
wire_color_id,
wire_type == WireType::Bus,
structure,
events_by_coords,
visited,
blocks,
logic_blocks,
Expand Down
Loading

0 comments on commit fff9d25

Please sign in to comment.