use cougr_core::scheduler::SimpleScheduler;
use cougr_core::{
query::SimpleQueryCache, CommandQueue, ComponentStorage, GameApp, Plugin, SimpleWorld,
};
use soroban_sdk::{symbol_short, Bytes, Env};
fn physics_system(world: &mut SimpleWorld, env: &Env) {
let entities = world.get_entities_with_component(&symbol_short!("pos"), env);
for i in 0..entities.len() {
let eid = entities.get(i).unwrap();
if let (Some(pos_data), Some(vel_data)) = (
world.get_component(eid, &symbol_short!("pos")),
world.get_component(eid, &symbol_short!("vel")),
) {
let px = pos_data.get(0).unwrap_or(0);
let vx = vel_data.get(0).unwrap_or(0);
let new_pos = Bytes::from_array(env, &[px.wrapping_add(vx)]);
world.add_component(eid, symbol_short!("pos"), new_pos);
}
}
}
fn scoring_system(world: &mut SimpleWorld, env: &Env) {
let entities = world.get_entities_with_component(&symbol_short!("scored"), env);
for i in 0..entities.len() {
let eid = entities.get(i).unwrap();
let current = world
.get_component(eid, &symbol_short!("score"))
.map(|b| b.get(0).unwrap_or(0))
.unwrap_or(0);
let new_score = Bytes::from_array(env, &[current + 1]);
world.add_component(eid, symbol_short!("score"), new_score);
}
}
fn cleanup_system(world: &mut SimpleWorld, env: &Env) {
let entities = world.get_entities_with_component(&symbol_short!("dead"), env);
for i in 0..entities.len() {
let eid = entities.get(i).unwrap();
world.despawn_entity(eid);
}
}
fn spawn_bullets_system(world: &mut SimpleWorld, env: &Env) {
let mut cmds = CommandQueue::new();
cmds.spawn();
let spawned = cmds.apply(world);
for id in &spawned {
let data = Bytes::from_array(env, &[0xFF]);
world.add_component(*id, symbol_short!("bullet"), data);
}
}
#[test]
fn test_full_game_tick_cycle() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let e1 = world.spawn_entity();
world.add_component(e1, symbol_short!("pos"), Bytes::from_array(&env, &[10]));
world.add_component(e1, symbol_short!("vel"), Bytes::from_array(&env, &[5]));
let e2 = world.spawn_entity();
world.add_component(e2, symbol_short!("pos"), Bytes::from_array(&env, &[20]));
world.add_component(e2, symbol_short!("vel"), Bytes::from_array(&env, &[3]));
physics_system(&mut world, &env);
let pos1 = world.get_component(e1, &symbol_short!("pos")).unwrap();
assert_eq!(pos1.get(0).unwrap(), 15);
let pos2 = world.get_component(e2, &symbol_short!("pos")).unwrap();
assert_eq!(pos2.get(0).unwrap(), 23);
let with_pos = world.get_entities_with_component(&symbol_short!("pos"), &env);
assert_eq!(with_pos.len(), 2);
}
#[test]
fn test_plugin_app_lifecycle() {
struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn name(&self) -> &'static str {
"physics"
}
fn build(&self, app: &mut GameApp) {
app.add_system("physics", physics_system);
}
}
struct ScoringPlugin;
impl Plugin for ScoringPlugin {
fn name(&self) -> &'static str {
"scoring"
}
fn build(&self, app: &mut GameApp) {
app.add_system("scoring", scoring_system);
}
}
let env = Env::default();
let mut app = GameApp::new(&env);
app.add_plugin(PhysicsPlugin);
app.add_plugin(ScoringPlugin);
assert_eq!(app.plugin_count(), 2);
assert_eq!(app.system_count(), 2);
let e1 = app.world_mut().spawn_entity();
app.world_mut()
.add_component(e1, symbol_short!("pos"), Bytes::from_array(&env, &[10]));
app.world_mut()
.add_component(e1, symbol_short!("vel"), Bytes::from_array(&env, &[5]));
app.world_mut()
.add_component(e1, symbol_short!("scored"), Bytes::from_array(&env, &[1]));
app.run(&env).unwrap();
let pos = app
.world()
.get_component(e1, &symbol_short!("pos"))
.unwrap();
assert_eq!(pos.get(0).unwrap(), 15);
let score = app
.world()
.get_component(e1, &symbol_short!("score"))
.unwrap();
assert_eq!(score.get(0).unwrap(), 1);
let world = app.into_world();
assert!(world.has_component(e1, &symbol_short!("pos")));
assert!(world.has_component(e1, &symbol_short!("score")));
}
#[test]
fn test_command_queue_within_system() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let shooter = world.spawn_entity();
world.add_component(
shooter,
symbol_short!("pos"),
Bytes::from_array(&env, &[50]),
);
spawn_bullets_system(&mut world, &env);
assert!(world.has_component(2, &symbol_short!("bullet")));
assert!(world.has_component(shooter, &symbol_short!("pos")));
}
#[test]
fn test_multi_system_pipeline() {
let env = Env::default();
let mut scheduler = SimpleScheduler::new();
scheduler.add_system("physics", physics_system);
scheduler.add_system("scoring", scoring_system);
scheduler.add_system("cleanup", cleanup_system);
let mut world = SimpleWorld::new(&env);
let e1 = world.spawn_entity();
world.add_component(e1, symbol_short!("pos"), Bytes::from_array(&env, &[10]));
world.add_component(e1, symbol_short!("vel"), Bytes::from_array(&env, &[5]));
world.add_component(e1, symbol_short!("scored"), Bytes::from_array(&env, &[1]));
let e2 = world.spawn_entity();
world.add_component(e2, symbol_short!("pos"), Bytes::from_array(&env, &[99]));
world.add_component(e2, symbol_short!("dead"), Bytes::from_array(&env, &[1]));
scheduler.run_all(&mut world, &env).unwrap();
let pos = world.get_component(e1, &symbol_short!("pos")).unwrap();
assert_eq!(pos.get(0).unwrap(), 15);
assert!(world.has_component(e1, &symbol_short!("score")));
assert!(!world.has_component(e2, &symbol_short!("pos")));
assert!(!world.has_component(e2, &symbol_short!("dead")));
}
#[test]
fn test_table_vs_sparse_mixed_storage() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let e1 = world.spawn_entity();
let e2 = world.spawn_entity();
let e3 = world.spawn_entity();
let data = Bytes::from_array(&env, &[1]);
world.add_component(e1, symbol_short!("pos"), data.clone());
world.add_component_with_storage(
e1,
symbol_short!("tag"),
data.clone(),
ComponentStorage::Sparse,
);
world.add_component(e2, symbol_short!("pos"), data.clone());
world.add_component_with_storage(
e3,
symbol_short!("tag"),
data.clone(),
ComponentStorage::Sparse,
);
let table_pos = world.get_table_entities_with_component(&symbol_short!("pos"), &env);
assert_eq!(table_pos.len(), 2);
let all_tag = world.get_all_entities_with_component(&symbol_short!("tag"), &env);
assert_eq!(all_tag.len(), 2);
let table_tag = world.get_table_entities_with_component(&symbol_short!("tag"), &env);
assert_eq!(table_tag.len(), 0);
assert!(world.has_component(e1, &symbol_short!("pos")));
assert!(world.has_component(e1, &symbol_short!("tag")));
assert!(world.has_component(e3, &symbol_short!("tag")));
assert!(!world.has_component(e3, &symbol_short!("pos")));
}
#[test]
fn test_entity_complete_lifecycle() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let e = world.spawn_entity();
assert_eq!(e, 1);
world.add_component(e, symbol_short!("pos"), Bytes::from_array(&env, &[10, 20]));
world.add_component(e, symbol_short!("hp"), Bytes::from_array(&env, &[100]));
world.add_component(e, symbol_short!("name"), Bytes::from_array(&env, &[65, 66]));
assert!(world.has_component(e, &symbol_short!("pos")));
assert!(world.has_component(e, &symbol_short!("hp")));
assert!(world.has_component(e, &symbol_short!("name")));
world.remove_component(e, &symbol_short!("name"));
assert!(!world.has_component(e, &symbol_short!("name")));
assert!(world.has_component(e, &symbol_short!("pos")));
world.add_component(e, symbol_short!("hp"), Bytes::from_array(&env, &[50]));
let hp = world.get_component(e, &symbol_short!("hp")).unwrap();
assert_eq!(hp.get(0).unwrap(), 50);
world.despawn_entity(e);
assert!(!world.has_component(e, &symbol_short!("pos")));
assert!(!world.has_component(e, &symbol_short!("hp")));
}
#[test]
fn test_query_cache_with_mutations() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let mut cache = SimpleQueryCache::new(symbol_short!("pos"), &env);
let results = cache.execute(&world, &env);
assert_eq!(results.len(), 0);
let e1 = world.spawn_entity();
world.add_component(e1, symbol_short!("pos"), Bytes::from_array(&env, &[1]));
assert!(!cache.is_valid(world.version()));
let results = cache.execute(&world, &env);
assert_eq!(results.len(), 1);
assert!(cache.is_valid(world.version()));
let results2 = cache.execute(&world, &env);
assert_eq!(results2.len(), 1);
let e2 = world.spawn_entity();
world.add_component(e2, symbol_short!("pos"), Bytes::from_array(&env, &[2]));
let results3 = cache.execute(&world, &env);
assert_eq!(results3.len(), 2);
world.despawn_entity(e1);
let results4 = cache.execute(&world, &env);
assert_eq!(results4.len(), 1);
}
#[test]
fn test_scheduler_system_ordering() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let e = world.spawn_entity();
world.add_component(e, symbol_short!("pos"), Bytes::from_array(&env, &[10]));
world.add_component(e, symbol_short!("vel"), Bytes::from_array(&env, &[5]));
world.add_component(e, symbol_short!("scored"), Bytes::from_array(&env, &[1]));
let mut scheduler = SimpleScheduler::new();
scheduler.add_system("physics", physics_system);
scheduler.add_system("scoring", scoring_system);
scheduler.run_all(&mut world, &env).unwrap();
let pos = world.get_component(e, &symbol_short!("pos")).unwrap();
assert_eq!(pos.get(0).unwrap(), 15);
let score = world.get_component(e, &symbol_short!("score")).unwrap();
assert_eq!(score.get(0).unwrap(), 1);
scheduler.run_all(&mut world, &env).unwrap();
let pos = world.get_component(e, &symbol_short!("pos")).unwrap();
assert_eq!(pos.get(0).unwrap(), 20); let score = world.get_component(e, &symbol_short!("score")).unwrap();
assert_eq!(score.get(0).unwrap(), 2);
}
#[test]
fn test_command_queue_mixed_operations() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let e1 = world.spawn_entity();
world.add_component(e1, symbol_short!("old"), Bytes::from_array(&env, &[1]));
let mut queue = CommandQueue::new();
queue.spawn(); queue.add_component(e1, symbol_short!("new"), Bytes::from_array(&env, &[2]));
queue.remove_component(e1, symbol_short!("old"));
queue.spawn();
let spawned = queue.apply(&mut world);
assert_eq!(spawned.len(), 2);
assert_eq!(spawned[0], 2);
assert_eq!(spawned[1], 3);
assert!(world.has_component(e1, &symbol_short!("new")));
assert!(!world.has_component(e1, &symbol_short!("old")));
}