mirror of
https://github.com/thilo-behnke/wasm-pong.git
synced 2026-07-28 10:41:11 +00:00
dev-ops/fix-and-cleanup-unit-tests
This commit is contained in:
@@ -3,8 +3,8 @@ mod utils;
|
||||
use pong::collision::collision::{Collision, CollisionDetector};
|
||||
use pong::game_field::{Field, Input, InputType};
|
||||
use pong::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use pong::geom::vector::Vector;
|
||||
use pong::geom::shape::ShapeType;
|
||||
use pong::geom::vector::Vector;
|
||||
use pong::pong::pong_events::DefaultPongEventWriter;
|
||||
use pong::utils::utils::{DefaultLoggerFactory, Logger};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
pub mod collision {
|
||||
pub mod detection {
|
||||
use crate::collision::collision::{Collision, CollisionRegistry, Collisions};
|
||||
use crate::game_object::game_object::GameObject;
|
||||
use crate::utils::utils::{Logger, LoggerFactory};
|
||||
use std::cell::{RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct CollisionDetectorConfig {
|
||||
@@ -86,84 +85,157 @@ pub mod collision {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CollisionRegistry: Debug {
|
||||
fn get_collisions(&self) -> Vec<&Collision>;
|
||||
fn get_collisions_by_id(&self, id: u16) -> Vec<&Collision>;
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::collision::collision::Collision;
|
||||
use crate::collision::detection::{CollisionDetector, CollisionGroup};
|
||||
use crate::game_object::game_object::GameObject;
|
||||
use crate::geom::shape::ShapeType;
|
||||
use crate::geom::utils::BoundingBox;
|
||||
use crate::geom::vector::Vector;
|
||||
use crate::utils::utils::DefaultLoggerFactory;
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Collisions {
|
||||
pub state: Vec<Collision>,
|
||||
}
|
||||
|
||||
impl Collisions {
|
||||
pub fn new(collisions: Vec<Collision>) -> Collisions {
|
||||
Collisions { state: collisions }
|
||||
#[rstest]
|
||||
#[case(vec![], vec![])]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.))
|
||||
],
|
||||
vec![Collision(1, 2)]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 60., y: 65.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
],
|
||||
vec![Collision(1, 2)]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 80., y: 80.}, 20., 20.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 50., 50.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 500., y: 50.}, 50., 50.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 60., y: 65.}, 20., 20.)),
|
||||
MockGameObject::new(2, "c", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
pub fn should_detect_collisions(
|
||||
#[case] objs: Vec<Rc<RefCell<Box<dyn GameObject>>>>,
|
||||
#[case] expected_collisions: Vec<Collision>,
|
||||
) {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let mut detector = CollisionDetector::new(&logger);
|
||||
detector.set_groups(vec![CollisionGroup(String::from("a"), String::from("b"))]);
|
||||
let res = detector.detect_collisions(objs);
|
||||
assert_eq!(
|
||||
res.get_collisions(),
|
||||
expected_collisions.iter().collect::<Vec<&Collision>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl CollisionRegistry for Collisions {
|
||||
fn get_collisions(&self) -> Vec<&Collision> {
|
||||
self.state.iter().collect()
|
||||
#[derive(Debug)]
|
||||
pub struct MockGameObject {
|
||||
id: u16,
|
||||
obj_type: String,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
fn get_collisions_by_id(&self, id: u16) -> Vec<&Collision> {
|
||||
self.state
|
||||
.iter()
|
||||
.filter(|c| c.0 == id || c.1 == id)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CollisionHandlerRegistry {
|
||||
handlers: HashMap<
|
||||
(String, String),
|
||||
fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
>,
|
||||
}
|
||||
|
||||
type CollisionCallback = fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>);
|
||||
|
||||
impl CollisionHandlerRegistry {
|
||||
pub fn new() -> CollisionHandlerRegistry {
|
||||
CollisionHandlerRegistry {
|
||||
handlers: HashMap::new(),
|
||||
impl MockGameObject {
|
||||
pub fn new(
|
||||
id: u16,
|
||||
obj_type: &str,
|
||||
bounding_box: BoundingBox,
|
||||
) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
Rc::new(RefCell::new(Box::new(MockGameObject {
|
||||
id,
|
||||
obj_type: String::from(obj_type),
|
||||
bounding_box,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, mapping: (String, String), callback: CollisionCallback) {
|
||||
if self.handlers.contains_key(&mapping) {
|
||||
panic!(
|
||||
"Collision handler for mapping {:?} is already registered.",
|
||||
mapping
|
||||
)
|
||||
impl GameObject for MockGameObject {
|
||||
fn id(&self) -> u16 {
|
||||
self.id
|
||||
}
|
||||
self.handlers.insert(mapping, callback);
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&self,
|
||||
mapping: &(String, String),
|
||||
values: (
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
),
|
||||
) -> bool {
|
||||
let regular = self.handlers.get(&mapping);
|
||||
if let Some(callback) = regular {
|
||||
callback(values.0, values.1);
|
||||
return true;
|
||||
fn obj_type(&self) -> &str {
|
||||
&*self.obj_type
|
||||
}
|
||||
let inverse = self.handlers.get(&(mapping.clone().1, mapping.clone().0));
|
||||
if let Some(callback) = inverse {
|
||||
callback(values.1, values.0);
|
||||
return true;
|
||||
|
||||
fn shape(&self) -> &ShapeType {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pos(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pos_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn orientation(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn orientation_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update_pos(&mut self, _ms_diff: f64) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
self.bounding_box.clone()
|
||||
}
|
||||
|
||||
fn vel(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn vel_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_static(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, _is_dirty: bool) {
|
||||
todo!()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Collision(pub u16, pub u16);
|
||||
pub mod handler {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use crate::game_object::game_object::GameObject;
|
||||
use crate::utils::utils::{Logger, LoggerFactory};
|
||||
|
||||
pub struct CollisionHandler {
|
||||
logger: Box<dyn Logger>,
|
||||
@@ -223,4 +295,171 @@ pub mod collision {
|
||||
// obj_a.pos_mut().add(&b_to_a);
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct CollisionHandlerRegistry {
|
||||
handlers: HashMap<
|
||||
(String, String),
|
||||
fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>),
|
||||
>,
|
||||
}
|
||||
|
||||
type CollisionCallback =
|
||||
fn(&Rc<RefCell<Box<dyn GameObject>>>, &Rc<RefCell<Box<dyn GameObject>>>);
|
||||
|
||||
impl CollisionHandlerRegistry {
|
||||
pub fn new() -> CollisionHandlerRegistry {
|
||||
CollisionHandlerRegistry {
|
||||
handlers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, mapping: (String, String), callback: CollisionCallback) {
|
||||
if self.handlers.contains_key(&mapping) {
|
||||
panic!(
|
||||
"Collision handler for mapping {:?} is already registered.",
|
||||
mapping
|
||||
)
|
||||
}
|
||||
self.handlers.insert(mapping, callback);
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&self,
|
||||
mapping: &(String, String),
|
||||
values: (
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
&Rc<RefCell<Box<dyn GameObject>>>,
|
||||
),
|
||||
) -> bool {
|
||||
let regular = self.handlers.get(&mapping);
|
||||
if let Some(callback) = regular {
|
||||
callback(values.0, values.1);
|
||||
return true;
|
||||
}
|
||||
let inverse = self.handlers.get(&(mapping.clone().1, mapping.clone().0));
|
||||
if let Some(callback) = inverse {
|
||||
callback(values.1, values.0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::collision::handler::CollisionHandler;
|
||||
use crate::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::shape::Shape;
|
||||
use crate::geom::vector::Vector;
|
||||
use crate::utils::utils::DefaultLoggerFactory;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), true),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true)
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true)
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-1., 0.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 1.), Vector::new(1., 1.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true)
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-2., 1.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 1.), Vector::new(0., 1.), true)
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-2., 1.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
pub fn should_handle_collision(
|
||||
#[case] obj_a: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] obj_b: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let mut handler = CollisionHandler::new(&logger);
|
||||
handler.register((String::from("obj"), String::from("obj")), |_a, _b| {
|
||||
let mut a_mut = RefCell::borrow_mut(_a);
|
||||
let mut vel_inverted = a_mut.vel().clone();
|
||||
vel_inverted.invert();
|
||||
*a_mut.vel_mut() = vel_inverted;
|
||||
});
|
||||
let expected_vel_a = Vector::inverted(RefCell::borrow(&obj_a).vel());
|
||||
let res = handler.handle(&obj_a, &obj_b);
|
||||
assert_eq!(true, res);
|
||||
assert_eq!(RefCell::borrow(&obj_a).pos(), RefCell::borrow(&obj_a).pos());
|
||||
assert_eq!(RefCell::borrow(&obj_a).vel(), &expected_vel_a);
|
||||
assert_eq!(RefCell::borrow(&obj_b).pos(), RefCell::borrow(&obj_b).pos());
|
||||
assert_eq!(RefCell::borrow(&obj_a).vel(), RefCell::borrow(&obj_a).vel());
|
||||
}
|
||||
|
||||
fn create_game_obj(
|
||||
id: u16,
|
||||
vel: Vector,
|
||||
orientation: Vector,
|
||||
is_static: bool,
|
||||
) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
Rc::new(RefCell::new(Box::new(DefaultGameObject::new(
|
||||
id,
|
||||
"obj".to_string(),
|
||||
Box::new(DefaultGeomComp::new(Shape::rect(
|
||||
Vector::zero(),
|
||||
orientation,
|
||||
20.,
|
||||
20.,
|
||||
))),
|
||||
Box::new(DefaultPhysicsComp::new(vel, is_static)),
|
||||
))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod collision {
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait CollisionRegistry: Debug {
|
||||
fn get_collisions(&self) -> Vec<&Collision>;
|
||||
fn get_collisions_by_id(&self, id: u16) -> Vec<&Collision>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Collisions {
|
||||
pub state: Vec<Collision>,
|
||||
}
|
||||
|
||||
impl Collisions {
|
||||
pub fn new(collisions: Vec<Collision>) -> Collisions {
|
||||
Collisions { state: collisions }
|
||||
}
|
||||
}
|
||||
|
||||
impl CollisionRegistry for Collisions {
|
||||
fn get_collisions(&self) -> Vec<&Collision> {
|
||||
self.state.iter().collect()
|
||||
}
|
||||
fn get_collisions_by_id(&self, id: u16) -> Vec<&Collision> {
|
||||
self.state
|
||||
.iter()
|
||||
.filter(|c| c.0 == id || c.1 == id)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Collision(pub u16, pub u16);
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ pub mod event {
|
||||
pub struct NoopEventWriterImpl {}
|
||||
impl EventWriterImpl for NoopEventWriterImpl {
|
||||
fn write(&mut self, _event: Event) -> Result<(), String> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::cell::{RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::collision::collision::{
|
||||
CollisionDetector, CollisionGroup, CollisionHandler, CollisionRegistry, Collisions,
|
||||
CollisionRegistry, Collisions,
|
||||
};
|
||||
use crate::collision::detection::{CollisionDetector, CollisionGroup};
|
||||
use crate::collision::handler::{CollisionHandler};
|
||||
use crate::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::shape::{Shape};
|
||||
use crate::geom::shape::Shape;
|
||||
use crate::geom::vector::Vector;
|
||||
use crate::pong::pong_collisions::{
|
||||
handle_ball_bounds_collision, handle_player_ball_collision, handle_player_bound_collision,
|
||||
@@ -191,15 +193,17 @@ impl Field {
|
||||
{
|
||||
for obj in self.objs.iter().filter(|o| RefCell::borrow(o).is_dirty()) {
|
||||
let mut obj = RefCell::borrow_mut(obj);
|
||||
let event_write_res = self.event_writer
|
||||
.write(PongEventType::GameObjUpdate(GameObjUpdate {
|
||||
obj_id: &obj.id().to_string(),
|
||||
vel: obj.vel(),
|
||||
orientation: obj.orientation(),
|
||||
pos: obj.pos(),
|
||||
}));
|
||||
let event_write_res =
|
||||
self.event_writer
|
||||
.write(PongEventType::GameObjUpdate(GameObjUpdate {
|
||||
obj_id: &obj.id().to_string(),
|
||||
vel: obj.vel(),
|
||||
orientation: obj.orientation(),
|
||||
pos: obj.pos(),
|
||||
}));
|
||||
if let Err(e) = event_write_res {
|
||||
self.logger.log(&*format!("Failed to write event logs: {}", e))
|
||||
self.logger
|
||||
.log(&*format!("Failed to write event logs: {}", e))
|
||||
}
|
||||
obj.set_dirty(false);
|
||||
}
|
||||
@@ -344,3 +348,49 @@ impl Bounds {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::RefCell;
|
||||
use crate::game_field::{Field, Input, InputType};
|
||||
|
||||
#[test]
|
||||
fn player_input_update_pos_up() {
|
||||
let height = 1000;
|
||||
let mut field = Field::mock(1000, height);
|
||||
field.add_player(1, 50, height / 2);
|
||||
let inputs = vec![Input {
|
||||
input: InputType::UP,
|
||||
obj_id: 1,
|
||||
player: 1,
|
||||
}];
|
||||
field.tick(inputs, 1.);
|
||||
let player = RefCell::borrow(
|
||||
field
|
||||
.objs()
|
||||
.iter()
|
||||
.find(|o| RefCell::borrow(o).obj_type() == "player")
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(player.pos().y, 530.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn player_input_update_pos_down() {
|
||||
let height = 1000;
|
||||
let mut field = Field::mock(1000, height);
|
||||
field.add_player(1, 50, height / 2);
|
||||
let inputs = vec![Input {
|
||||
input: InputType::DOWN,
|
||||
obj_id: 1,
|
||||
player: 1,
|
||||
}];
|
||||
field.tick(inputs, 1.);
|
||||
let objs = field.objs();
|
||||
let player = objs
|
||||
.iter()
|
||||
.find(|o| RefCell::borrow(o).obj_type() == "player")
|
||||
.unwrap();
|
||||
assert_eq!(RefCell::borrow(player).pos().y, 470.);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
pub mod game_object {
|
||||
use crate::game_object::components::{GeomComp, PhysicsComp};
|
||||
use crate::geom::geom::{BoundingBox};
|
||||
use crate::geom::shape::{ShapeType};
|
||||
use std::fmt::Debug;
|
||||
use crate::geom::shape::ShapeType;
|
||||
use crate::geom::utils::BoundingBox;
|
||||
use crate::geom::vector::Vector;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait GameObject: Debug {
|
||||
fn id(&self) -> u16;
|
||||
@@ -22,7 +22,6 @@ pub mod game_object {
|
||||
fn set_dirty(&mut self, is_dirty: bool);
|
||||
}
|
||||
|
||||
// #[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Debug)]
|
||||
pub struct DefaultGameObject {
|
||||
pub id: u16,
|
||||
@@ -120,16 +119,50 @@ pub mod game_object {
|
||||
self.dirty = is_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use crate::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::shape::Shape;
|
||||
use crate::geom::vector::Vector;
|
||||
|
||||
#[rstest]
|
||||
#[case(Vector::new(100., 100.), Vector::new(-1., 1.), Vector::new(99.9, 100.1), 0.1)]
|
||||
#[case(Vector::new(300., 400.), Vector::new(-5., 0.), Vector::new(299.5, 400.), 0.1)]
|
||||
#[case(Vector::new(300., 400.), Vector::new(-5., 0.), Vector::new(299.935, 400.), 0.013)]
|
||||
pub fn should_update_pos(
|
||||
#[case] start_pos: Vector,
|
||||
#[case] vel: Vector,
|
||||
#[case] expected_pos: Vector,
|
||||
#[case] ms_diff: f64,
|
||||
) {
|
||||
let mut obj = DefaultGameObject::new(
|
||||
1,
|
||||
"obj".to_string(),
|
||||
Box::new(DefaultGeomComp::new(Shape::rect(
|
||||
Vector::new(start_pos.x as f64, start_pos.y as f64),
|
||||
Vector::new(1., 0.),
|
||||
0.,
|
||||
0.,
|
||||
))),
|
||||
Box::new(DefaultPhysicsComp::new(vel, false)),
|
||||
);
|
||||
obj.update_pos(ms_diff);
|
||||
assert_eq!(*obj.pos(), expected_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod components {
|
||||
use crate::geom::geom::{BoundingBox};
|
||||
use crate::geom::shape::{
|
||||
get_bounding_box, get_center, get_center_mut, get_orientation, get_orientation_mut,
|
||||
ShapeType,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use crate::geom::utils::BoundingBox;
|
||||
use crate::geom::vector::Vector;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait GeomComp: Debug {
|
||||
fn shape(&self) -> &ShapeType;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod vector {
|
||||
use serde::{Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Vector {
|
||||
@@ -165,9 +165,9 @@ pub mod vector {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::geom::vector::Vector;
|
||||
use rstest::rstest;
|
||||
use std::f64::consts::FRAC_PI_4;
|
||||
use crate::geom::vector::Vector;
|
||||
|
||||
#[rstest]
|
||||
#[case(1., 0., 1.)]
|
||||
@@ -214,7 +214,10 @@ pub mod vector {
|
||||
#[case(Vector::new(1., 0.), Vector::new(0., -1.))]
|
||||
#[case(Vector::new(0., 1.), Vector::new(1., 0.))]
|
||||
#[case(Vector::new(7., 7.), Vector::new(7., -7.))]
|
||||
pub fn should_get_orthogonal_clockwise(#[case] mut vector: Vector, #[case] expected: Vector) {
|
||||
pub fn should_get_orthogonal_clockwise(
|
||||
#[case] mut vector: Vector,
|
||||
#[case] expected: Vector,
|
||||
) {
|
||||
vector.orthogonal_clockwise();
|
||||
assert_eq!(vector, expected);
|
||||
}
|
||||
@@ -298,7 +301,7 @@ pub mod vector {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod geom {
|
||||
pub mod utils {
|
||||
use crate::geom::vector::Vector;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -399,12 +402,62 @@ pub mod geom {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::geom::utils::BoundingBox;
|
||||
use crate::geom::vector::Vector;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(10., 10.), true)]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(8., 8.), true)]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(20., 20.), false)]
|
||||
pub fn should_correctly_determine_if_point_is_within_box(
|
||||
#[case] bounding_box: BoundingBox,
|
||||
#[case] point: Vector,
|
||||
#[case] expected: bool,
|
||||
) {
|
||||
let res = bounding_box.is_point_within(&point);
|
||||
assert_eq!(res, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(8., 8.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(4.9, 4.9), 5., 5.),
|
||||
false
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(5., 5.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
pub fn should_correctly_determine_if_overlap(
|
||||
#[case] bounding_box_a: BoundingBox,
|
||||
#[case] bounding_box_b: BoundingBox,
|
||||
#[case] expected: bool,
|
||||
) {
|
||||
let res = bounding_box_a.overlaps(&bounding_box_b);
|
||||
assert_eq!(res, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod shape {
|
||||
use crate::geom::geom::{BoundingBox};
|
||||
use std::fmt::Debug;
|
||||
use crate::geom::utils::BoundingBox;
|
||||
use crate::geom::vector::Vector;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ShapeType {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
pub mod pong_collisions {
|
||||
use crate::game_object::game_object::GameObject;
|
||||
use crate::geom::vector::{Vector};
|
||||
use crate::geom::shape::ShapeType;
|
||||
use std::cell::{RefCell};
|
||||
use crate::geom::vector::Vector;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn handle_player_ball_collision(
|
||||
@@ -77,6 +77,84 @@ pub mod pong_collisions {
|
||||
|
||||
player.set_dirty(true);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::game_field::{Bound, Field};
|
||||
use crate::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use crate::geom::vector::Vector;
|
||||
use crate::pong::pong_collisions::handle_player_bound_collision;
|
||||
use crate::pong::pong_events::NoopPongEventWriter;
|
||||
use crate::utils::utils::DefaultLoggerFactory;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 0, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM),
|
||||
// expected
|
||||
create_player(1, 10, 61, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 1, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM),
|
||||
// expected
|
||||
create_player(1, 10, 61, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 601, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP),
|
||||
// expected
|
||||
create_player(1, 10, 539, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 599, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP),
|
||||
// expected
|
||||
create_player(1, 10, 539, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP)
|
||||
)]
|
||||
pub fn should_correctly_handle_player_bounds_collision(
|
||||
#[case] player: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] bounds: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] player_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] bounds_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
handle_player_bound_collision(&player, &bounds);
|
||||
assert_eq!(player_expected.borrow().pos(), player.borrow().pos());
|
||||
assert_eq!(bounds_expected.borrow().pos(), bounds.borrow().pos());
|
||||
}
|
||||
|
||||
fn create_player(id: u16, x: u16, y: u16, orientation: Vector) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let event_writer = NoopPongEventWriter::new();
|
||||
let field = Field::new(logger, event_writer);
|
||||
let mut player = DefaultGameObject::player(id, x, y, &field);
|
||||
let player_orientation = player.orientation_mut();
|
||||
player_orientation.x = orientation.x;
|
||||
player_orientation.y = orientation.y;
|
||||
Rc::new(RefCell::new(player))
|
||||
}
|
||||
|
||||
fn get_bound(bound: Bound) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let event_writer = NoopPongEventWriter::new();
|
||||
let field = Field::new(logger, event_writer);
|
||||
let bounds = DefaultGameObject::bounds(field.width, field.height);
|
||||
return Rc::new(RefCell::new(
|
||||
bounds.into_iter().find(|b| b.0 == bound).unwrap().inner(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod pong_events {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use pong::geom::geom::{BoundingBox};
|
||||
use rstest::rstest;
|
||||
use pong::geom::vector::Vector;
|
||||
|
||||
#[rstest]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(10., 10.), true)]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(8., 8.), true)]
|
||||
#[case(BoundingBox::create(&Vector::new(10., 10.), 5., 5.), Vector::new(20., 20.), false)]
|
||||
pub fn should_correctly_determine_if_point_is_within_box(
|
||||
#[case] bounding_box: BoundingBox,
|
||||
#[case] point: Vector,
|
||||
#[case] expected: bool,
|
||||
) {
|
||||
let res = bounding_box.is_point_within(&point);
|
||||
assert_eq!(res, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(8., 8.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(4.9, 4.9), 5., 5.),
|
||||
false
|
||||
)]
|
||||
#[case(
|
||||
BoundingBox::create(&Vector::new(10., 10.), 5., 5.),
|
||||
BoundingBox::create(&Vector::new(5., 5.), 5., 5.),
|
||||
true
|
||||
)]
|
||||
pub fn should_correctly_determine_if_overlap(
|
||||
#[case] bounding_box_a: BoundingBox,
|
||||
#[case] bounding_box_b: BoundingBox,
|
||||
#[case] expected: bool,
|
||||
) {
|
||||
let res = bounding_box_a.overlaps(&bounding_box_b);
|
||||
assert_eq!(res, expected);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
use pong::collision::collision::CollisionHandler;
|
||||
use pong::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use pong::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use pong::geom::vector::Vector;
|
||||
use pong::geom::shape::Shape;
|
||||
use pong::utils::utils::DefaultLoggerFactory;
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), true),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-1., 0.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 1.), Vector::new(1., 1.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-2., 1.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(1., 0.), Vector::new(1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 1.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
#[case(
|
||||
create_game_obj(1, Vector::new(-2., 1.), Vector::new(-1., 0.), false),
|
||||
create_game_obj(2, Vector::new(0., 0.), Vector::new(0., 1.), true),
|
||||
)]
|
||||
pub fn should_handle_collision(
|
||||
#[case] obj_a: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] obj_b: Rc<RefCell<Box<dyn GameObject>>>
|
||||
) {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let mut handler = CollisionHandler::new(&logger);
|
||||
handler.register((String::from("obj"), String::from("obj")), |_a, _b| {
|
||||
let mut a_mut = RefCell::borrow_mut(_a);
|
||||
let mut vel_inverted = a_mut.vel().clone();
|
||||
vel_inverted.invert();
|
||||
*a_mut.vel_mut() = vel_inverted;
|
||||
});
|
||||
let expected_vel_a = Vector::inverted(RefCell::borrow(&obj_a).vel());
|
||||
let res = handler.handle(&obj_a, &obj_b);
|
||||
assert_eq!(true, res);
|
||||
assert_eq!(RefCell::borrow(&obj_a).pos(), RefCell::borrow(&obj_a).pos());
|
||||
assert_eq!(RefCell::borrow(&obj_a).vel(), &expected_vel_a);
|
||||
assert_eq!(RefCell::borrow(&obj_b).pos(), RefCell::borrow(&obj_b).pos());
|
||||
assert_eq!(RefCell::borrow(&obj_a).vel(), RefCell::borrow(&obj_a).vel());
|
||||
}
|
||||
|
||||
fn create_game_obj(
|
||||
id: u16,
|
||||
vel: Vector,
|
||||
orientation: Vector,
|
||||
is_static: bool,
|
||||
) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
Rc::new(RefCell::new(Box::new(DefaultGameObject::new(
|
||||
id,
|
||||
"obj".to_string(),
|
||||
Box::new(DefaultGeomComp::new(Shape::rect(
|
||||
Vector::zero(),
|
||||
orientation,
|
||||
20.,
|
||||
20.,
|
||||
))),
|
||||
Box::new(DefaultPhysicsComp::new(vel, is_static)),
|
||||
))))
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
use pong::collision::collision::{Collision, CollisionDetector, CollisionGroup};
|
||||
use pong::game_object::game_object::GameObject;
|
||||
use pong::geom::geom::{BoundingBox};
|
||||
use pong::geom::shape::ShapeType;
|
||||
use pong::utils::utils::DefaultLoggerFactory;
|
||||
use rstest::rstest;
|
||||
use std::cell::{RefCell};
|
||||
use std::rc::Rc;
|
||||
use pong::geom::vector::Vector;
|
||||
|
||||
#[rstest]
|
||||
#[case(vec![], vec![])]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.))
|
||||
],
|
||||
vec![Collision(1, 2)]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 60., y: 65.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
],
|
||||
vec![Collision(1, 2)]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 80., y: 80.}, 20., 20.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 50., y: 50.}, 50., 50.)),
|
||||
MockGameObject::new(2, "b", BoundingBox::create(&Vector{x: 500., y: 50.}, 50., 50.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
#[case(
|
||||
vec![
|
||||
MockGameObject::new(1, "a", BoundingBox::create(&Vector{x: 60., y: 65.}, 20., 20.)),
|
||||
MockGameObject::new(2, "c", BoundingBox::create(&Vector{x: 50., y: 50.}, 20., 20.)),
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
pub fn should_detect_collisions(
|
||||
#[case] objs: Vec<Rc<RefCell<Box<dyn GameObject>>>>,
|
||||
#[case] expected_collisions: Vec<Collision>,
|
||||
) {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let mut detector = CollisionDetector::new(&logger);
|
||||
detector.set_groups(vec![CollisionGroup(String::from("a"), String::from("b"))]);
|
||||
let res = detector.detect_collisions(objs);
|
||||
assert_eq!(
|
||||
res.get_collisions(),
|
||||
expected_collisions.iter().collect::<Vec<&Collision>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MockGameObject {
|
||||
id: u16,
|
||||
obj_type: String,
|
||||
bounding_box: BoundingBox
|
||||
}
|
||||
|
||||
impl MockGameObject {
|
||||
pub fn new(
|
||||
id: u16,
|
||||
obj_type: &str,
|
||||
bounding_box: BoundingBox,
|
||||
) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
Rc::new(RefCell::new(Box::new(MockGameObject {
|
||||
id,
|
||||
obj_type: String::from(obj_type),
|
||||
bounding_box
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
impl GameObject for MockGameObject {
|
||||
fn id(&self) -> u16 {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn obj_type(&self) -> &str {
|
||||
&*self.obj_type
|
||||
}
|
||||
|
||||
fn shape(&self) -> &ShapeType {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pos(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn pos_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn orientation(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn orientation_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn update_pos(&mut self, _ms_diff: f64) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
self.bounding_box.clone()
|
||||
}
|
||||
|
||||
fn vel(&self) -> &Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn vel_mut(&mut self) -> &mut Vector {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_static(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self, _is_dirty: bool) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod game_field_tests {
|
||||
use pong::game_field::{Field, Input, InputType};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[test]
|
||||
fn player_input_update_pos_up() {
|
||||
let height = 1000;
|
||||
let mut field = Field::mock(1000, height);
|
||||
field.add_player(1, 50, height / 2);
|
||||
let inputs = vec![Input {
|
||||
input: InputType::UP,
|
||||
obj_id: 1,
|
||||
player: 1
|
||||
}];
|
||||
field.tick(inputs, 1_000.);
|
||||
let player = RefCell::borrow(
|
||||
field
|
||||
.objs()
|
||||
.iter()
|
||||
.find(|o| RefCell::borrow(o).obj_type() == "player")
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(player.pos().y, height as f64 / 2. + 1.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn player_input_update_pos_down() {
|
||||
let height = 1000;
|
||||
let mut field = Field::mock(1000, height);
|
||||
field.add_player(1, 50, height / 2);
|
||||
let inputs = vec![Input {
|
||||
input: InputType::DOWN,
|
||||
obj_id: 1,
|
||||
player: 1
|
||||
}];
|
||||
field.tick(inputs, 1_000.);
|
||||
let objs = field.objs();
|
||||
let player = objs
|
||||
.iter()
|
||||
.find(|o| RefCell::borrow(o).obj_type() == "player")
|
||||
.unwrap();
|
||||
assert_eq!(RefCell::borrow(player).pos().y, height as f64 / 2. - 1.);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
use pong::game_object::components::{DefaultGeomComp, DefaultPhysicsComp};
|
||||
use pong::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use pong::geom::vector::Vector;
|
||||
use pong::geom::shape::Shape;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(Vector::new(100., 100.), Vector::new(-1., 1.), Vector::new(99.9, 100.1), 0.1)]
|
||||
#[case(Vector::new(300., 400.), Vector::new(-5., 0.), Vector::new(299.5, 400.), 0.1)]
|
||||
#[case(Vector::new(300., 400.), Vector::new(-5., 0.), Vector::new(299.5, 400.), 0.013)]
|
||||
pub fn should_update_pos(
|
||||
#[case] start_pos: Vector,
|
||||
#[case] vel: Vector,
|
||||
#[case] expected_pos: Vector,
|
||||
#[case] ms_diff: f64,
|
||||
) {
|
||||
let mut obj = DefaultGameObject::new(
|
||||
1,
|
||||
"obj".to_string(),
|
||||
Box::new(DefaultGeomComp::new(Shape::rect(
|
||||
Vector::new(start_pos.x as f64, start_pos.y as f64),
|
||||
Vector::new(1., 0.),
|
||||
0.,
|
||||
0.,
|
||||
))),
|
||||
Box::new(DefaultPhysicsComp::new(vel, false)),
|
||||
);
|
||||
obj.update_pos(ms_diff);
|
||||
assert_eq!(*obj.pos(), expected_pos);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
use pong::game_field::{Bound, Field};
|
||||
use pong::game_object::game_object::{DefaultGameObject, GameObject};
|
||||
use pong::geom::vector::Vector;
|
||||
use pong::pong::pong_collisions::handle_player_bound_collision;
|
||||
use pong::utils::utils::{DefaultLoggerFactory};
|
||||
use rstest::rstest;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use pong::pong::pong_events::NoopPongEventWriter;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 0, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM),
|
||||
// expected
|
||||
create_player(1, 10, 61, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 1, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM),
|
||||
// expected
|
||||
create_player(1, 10, 61, Vector::new(0., -1.)),
|
||||
get_bound(Bound::BOTTOM)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 601, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP),
|
||||
// expected
|
||||
create_player(1, 10, 539, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP)
|
||||
)]
|
||||
#[case(
|
||||
// given
|
||||
create_player(1, 10, 599, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP),
|
||||
// expected
|
||||
create_player(1, 10, 539, Vector::new(0., 1.)),
|
||||
get_bound(Bound::TOP)
|
||||
)]
|
||||
pub fn should_correctly_handle_player_bounds_collision(
|
||||
#[case] player: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] bounds: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] player_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
#[case] bounds_expected: Rc<RefCell<Box<dyn GameObject>>>,
|
||||
) {
|
||||
handle_player_bound_collision(&player, &bounds);
|
||||
assert_eq!(player_expected.borrow().pos(), player.borrow().pos());
|
||||
assert_eq!(bounds_expected.borrow().pos(), bounds.borrow().pos());
|
||||
}
|
||||
|
||||
fn create_player(id: u16, x: u16, y: u16, orientation: Vector) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let event_writer = NoopPongEventWriter::new();
|
||||
let field = Field::new(logger, event_writer);
|
||||
let mut player = DefaultGameObject::player(id, x, y, &field);
|
||||
let player_orientation = player.orientation_mut();
|
||||
player_orientation.x = orientation.x;
|
||||
player_orientation.y = orientation.y;
|
||||
Rc::new(RefCell::new(player))
|
||||
}
|
||||
|
||||
fn get_bound(bound: Bound) -> Rc<RefCell<Box<dyn GameObject>>> {
|
||||
let logger = DefaultLoggerFactory::noop();
|
||||
let event_writer = NoopPongEventWriter::new();
|
||||
let field = Field::new(logger, event_writer);
|
||||
let bounds = DefaultGameObject::bounds(field.width, field.height);
|
||||
return Rc::new(RefCell::new(
|
||||
bounds.into_iter().find(|b| b.0 == bound).unwrap().inner(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::convert::Infallible;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::{sink::SinkExt, stream::StreamExt};
|
||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||
use hyper::server::conn::AddrStream;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper_tungstenite::{HyperWebsocket};
|
||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||
use hyper_tungstenite::tungstenite::{Error, Message};
|
||||
use hyper_tungstenite::HyperWebsocket;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -25,8 +25,16 @@ pub struct HttpServer {
|
||||
session_manager: Arc<Mutex<SessionManager>>,
|
||||
}
|
||||
impl HttpServer {
|
||||
pub fn new(addr: [u8; 4], port: u16, kafka_host: &str, kafka_topic_manager_host: &str) -> HttpServer {
|
||||
let session_manager = Arc::new(Mutex::new(SessionManager::new(kafka_host, kafka_topic_manager_host)));
|
||||
pub fn new(
|
||||
addr: [u8; 4],
|
||||
port: u16,
|
||||
kafka_host: &str,
|
||||
kafka_topic_manager_host: &str,
|
||||
) -> HttpServer {
|
||||
let session_manager = Arc::new(Mutex::new(SessionManager::new(
|
||||
kafka_host,
|
||||
kafka_topic_manager_host,
|
||||
)));
|
||||
HttpServer {
|
||||
addr,
|
||||
port,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::{Body, Client, Method, Request, Uri};
|
||||
use kafka::client::{ProduceMessage};
|
||||
use kafka::client::ProduceMessage;
|
||||
use kafka::consumer::{Consumer, FetchOffset, GroupOffsetStorage, MessageSet};
|
||||
use kafka::producer::{Partitioner, Producer, Record, RequiredAcks, Topics};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -14,12 +14,12 @@ pub async fn main() {
|
||||
let kafka_host_env = std::env::var("KAFKA_HOST");
|
||||
let kafka_host = match kafka_host_env {
|
||||
Ok(val) => val,
|
||||
Err(_) => "localhost:9093".to_owned()
|
||||
Err(_) => "localhost:9093".to_owned(),
|
||||
};
|
||||
let kafka_partition_manager_host_env = std::env::var("KAFKA_TOPIC_MANAGER_HOST");
|
||||
let kafka_topic_manager_host = match kafka_partition_manager_host_env {
|
||||
Ok(val) => val,
|
||||
Err(_) => "localhost:7243".to_owned()
|
||||
Err(_) => "localhost:7243".to_owned(),
|
||||
};
|
||||
|
||||
HttpServer::new([0, 0, 0, 0], 4000, &kafka_host, &kafka_topic_manager_host)
|
||||
|
||||
@@ -4,8 +4,8 @@ use pong::event::event::{Event, EventReader, EventWriter};
|
||||
|
||||
use crate::hash::Hasher;
|
||||
use crate::kafka::{
|
||||
KafkaDefaultEventWriterImpl, KafkaSessionEventReaderImpl,
|
||||
KafkaSessionEventWriterImpl, KafkaTopicManager,
|
||||
KafkaDefaultEventWriterImpl, KafkaSessionEventReaderImpl, KafkaSessionEventWriterImpl,
|
||||
KafkaTopicManager,
|
||||
};
|
||||
use crate::player::Player;
|
||||
|
||||
@@ -48,7 +48,10 @@ impl SessionManager {
|
||||
println!("Successfully created session: {:?}", session);
|
||||
let write_res = self.write_to_producer(session_created(session.clone(), player.clone()));
|
||||
if let Err(e) = write_res {
|
||||
eprintln!("Failed to write session created event for {:?} to producer: {}", session, e);
|
||||
eprintln!(
|
||||
"Failed to write session created event for {:?} to producer: {}",
|
||||
session, e
|
||||
);
|
||||
}
|
||||
self.sessions.push(session.clone());
|
||||
Ok(session)
|
||||
@@ -86,9 +89,13 @@ impl SessionManager {
|
||||
session.clone()
|
||||
};
|
||||
{
|
||||
let write_res = self.write_to_producer(session_joined(updated_session.clone(), player.clone()));
|
||||
let write_res =
|
||||
self.write_to_producer(session_joined(updated_session.clone(), player.clone()));
|
||||
if let Err(e) = write_res {
|
||||
eprintln!("Failed to write session joined event for {:?} to producer: {}", updated_session, e);
|
||||
eprintln!(
|
||||
"Failed to write session joined event for {:?} to producer: {}",
|
||||
updated_session, e
|
||||
);
|
||||
}
|
||||
};
|
||||
println!("sessions = {:?}", self.sessions);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
Reference in New Issue
Block a user