mod indexing;
#[cfg(test)]
mod tests;
use crate::component::{ComponentStorage, ComponentTrait};
use soroban_sdk::{contracttype, Bytes, Env, Map, Symbol, Vec};
pub type EntityId = u32;
#[contracttype]
#[derive(Clone, Debug)]
pub struct SimpleWorld {
pub(crate) next_entity_id: u32,
pub(crate) components: Map<(u32, Symbol), Bytes>,
pub(crate) sparse_components: Map<(u32, Symbol), Bytes>,
pub(crate) entity_components: Map<u32, Vec<Symbol>>,
pub(crate) table_index: Map<Symbol, Vec<u32>>,
pub(crate) all_index: Map<Symbol, Vec<u32>>,
pub(crate) version: u64,
}
impl SimpleWorld {
pub fn new(env: &Env) -> Self {
Self {
next_entity_id: 1,
components: Map::new(env),
sparse_components: Map::new(env),
entity_components: Map::new(env),
table_index: Map::new(env),
all_index: Map::new(env),
version: 0,
}
}
pub fn version(&self) -> u64 {
self.version
}
pub fn next_entity_id(&self) -> EntityId {
self.next_entity_id
}
pub fn env(&self) -> &Env {
self.components.env()
}
pub fn spawn_entity(&mut self) -> EntityId {
let id = self.next_entity_id;
self.next_entity_id += 1;
id
}
fn has_component_in_table(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
self.components
.contains_key((entity_id, component_type.clone()))
}
fn has_component_in_sparse(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
self.sparse_components
.contains_key((entity_id, component_type.clone()))
}
pub fn add_component(&mut self, entity_id: EntityId, component_type: Symbol, data: Bytes) {
self.add_component_with_storage(entity_id, component_type, data, ComponentStorage::Table);
}
pub fn add_component_with_storage(
&mut self,
entity_id: EntityId,
component_type: Symbol,
data: Bytes,
storage: ComponentStorage,
) {
self.version += 1;
let was_in_table = self.has_component_in_table(entity_id, &component_type);
let was_in_sparse = self.has_component_in_sparse(entity_id, &component_type);
match storage {
ComponentStorage::Table => {
self.components
.set((entity_id, component_type.clone()), data);
if was_in_sparse {
self.sparse_components
.remove((entity_id, component_type.clone()));
}
}
ComponentStorage::Sparse => {
self.sparse_components
.set((entity_id, component_type.clone()), data);
if was_in_table {
self.components.remove((entity_id, component_type.clone()));
}
}
}
let mut types = self
.entity_components
.get(entity_id)
.unwrap_or_else(|| Vec::new(self.components.env()));
let mut found = false;
for i in 0..types.len() {
if let Some(t) = types.get(i) {
if t == component_type {
found = true;
break;
}
}
}
if !found {
types.push_back(component_type.clone());
}
self.entity_components.set(entity_id, types);
indexing::push_index(&mut self.all_index, &component_type, entity_id);
match storage {
ComponentStorage::Table => {
indexing::push_index(&mut self.table_index, &component_type, entity_id);
}
ComponentStorage::Sparse => {
indexing::remove_from_index(&mut self.table_index, &component_type, entity_id);
}
}
}
pub fn get_component(&self, entity_id: EntityId, component_type: &Symbol) -> Option<Bytes> {
self.components
.get((entity_id, component_type.clone()))
.or_else(|| {
self.sparse_components
.get((entity_id, component_type.clone()))
})
}
pub fn remove_component(&mut self, entity_id: EntityId, component_type: &Symbol) -> bool {
self.version += 1;
let removed = self
.components
.remove((entity_id, component_type.clone()))
.or_else(|| {
self.sparse_components
.remove((entity_id, component_type.clone()))
});
if removed.is_some() {
if let Some(types) = self.entity_components.get(entity_id) {
let env = self.components.env();
let mut new_types = Vec::new(env);
for i in 0..types.len() {
if let Some(t) = types.get(i) {
if &t != component_type {
new_types.push_back(t);
}
}
}
if new_types.is_empty() {
self.entity_components.remove(entity_id);
} else {
self.entity_components.set(entity_id, new_types);
}
}
indexing::remove_from_index(&mut self.all_index, component_type, entity_id);
indexing::remove_from_index(&mut self.table_index, component_type, entity_id);
true
} else {
false
}
}
pub fn has_component(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
self.has_component_in_table(entity_id, component_type)
|| self.has_component_in_sparse(entity_id, component_type)
}
pub fn get_entities_with_component(&self, component_type: &Symbol, env: &Env) -> Vec<EntityId> {
self.table_index
.get(component_type.clone())
.unwrap_or_else(|| Vec::new(env))
}
pub fn get_table_entities_with_component(
&self,
component_type: &Symbol,
env: &Env,
) -> Vec<EntityId> {
self.table_index
.get(component_type.clone())
.unwrap_or_else(|| Vec::new(env))
}
pub fn get_all_entities_with_component(
&self,
component_type: &Symbol,
env: &Env,
) -> Vec<EntityId> {
self.all_index
.get(component_type.clone())
.unwrap_or_else(|| Vec::new(env))
}
pub fn table_component_count(&self, component_type: &Symbol) -> usize {
self.table_index
.get(component_type.clone())
.map(|entities| entities.len())
.unwrap_or(0)
.try_into()
.unwrap()
}
pub fn component_count(&self, component_type: &Symbol) -> usize {
self.all_index
.get(component_type.clone())
.map(|entities| entities.len())
.unwrap_or(0)
.try_into()
.unwrap()
}
pub fn get_typed<T: ComponentTrait>(&self, env: &Env, entity_id: EntityId) -> Option<T> {
let bytes = self.get_component(entity_id, &T::component_type())?;
T::deserialize(env, &bytes)
}
pub fn set_typed<T: ComponentTrait>(&mut self, env: &Env, entity_id: EntityId, component: &T) {
let symbol = T::component_type();
let data = component.serialize(env);
let storage = T::default_storage();
self.add_component_with_storage(entity_id, symbol, data, storage);
}
pub fn has_typed<T: ComponentTrait>(&self, entity_id: EntityId) -> bool {
self.has_component(entity_id, &T::component_type())
}
pub fn remove_typed<T: ComponentTrait>(&mut self, entity_id: EntityId) -> bool {
self.remove_component(entity_id, &T::component_type())
}
pub fn despawn_entity(&mut self, entity_id: EntityId) {
self.version += 1;
if let Some(types) = self.entity_components.get(entity_id) {
for i in 0..types.len() {
if let Some(t) = types.get(i) {
self.components.remove((entity_id, t.clone()));
self.sparse_components.remove((entity_id, t.clone()));
indexing::remove_from_index(&mut self.all_index, &t, entity_id);
indexing::remove_from_index(&mut self.table_index, &t, entity_id);
}
}
}
self.entity_components.remove(entity_id);
}
}