rush_ecs_svm/state/
world.rs

1use borsh::{BorshDeserialize, BorshSerialize};
2use rush_ecs_core::blueprint::{Entity, Region};
3use shank::ShankAccount;
4use solana_program::{msg, pubkey::Pubkey};
5use spl_discriminator::{ArrayDiscriminator, SplDiscriminate};
6use std::collections::BTreeMap;
7
8// OPT-OUT: didn't use #[seeds()] because ShankAccount seeds
9// helper attribute is buggy. PDA is generated offchain
10// instead and seeds are validated
11
12#[derive(
13    Clone,
14    BorshSerialize,
15    BorshDeserialize,
16    Debug,
17    Default,
18    Eq,
19    PartialEq,
20    ShankAccount,
21    SplDiscriminate,
22)]
23#[discriminator_hash_input("rush_ecs_store::state::World")]
24pub struct World {
25    /// Identifier for this specific structure
26    pub discriminator: [u8; 8],
27
28    /// Description of the world
29    pub name: String,
30    /// Description of the world
31    pub description: String,
32    /// Onchain record of what Entity types exist in the world
33    pub entities: Vec<Entity>,
34    /// Onchain record of what Regions exist in the world
35    pub regions: Vec<Region>,
36    /// Source of truth for what Instances exist in the world
37    pub instances: BTreeMap<Region, BTreeMap<Entity, u64>>,
38    /// Determines if the World is already launched and
39    /// instances can now be Created, Updated, and Deleted
40    /// outside of the CreateWorld (Initialization) Instruction
41    pub is_launched: bool,
42    /// Overaching authority who has access to state changing
43    /// operations
44    pub world_authority: Pubkey,
45
46    /// Canonical bump for World
47    pub bump: u8,
48}
49
50impl World {
51    /// Is `true` if World is initialized
52    pub fn is_initialized(&self) -> bool {
53        self.discriminator.as_slice() == World::SPL_DISCRIMINATOR_SLICE
54    }
55
56    /// Is `true` if World is uninitialized
57    pub fn is_uninitialized(&self) -> bool {
58        self.discriminator.as_slice() == ArrayDiscriminator::UNINITIALIZED.as_slice()
59    }
60
61    /// Create new World state
62    ///
63    /// # Arguments
64    ///
65    /// * `name` - A [`String`] that holds the name of the world
66    /// * `description` - A [`String`] that holds the description of the world
67    /// * `world_authority` - A [`Pubkey`] of the authority who can change the world's account data
68    /// * `regions` - A Vector of the new type [`Region`] holding all the existing regions in the world
69    /// * `entities` - A Vector of the new type [`Entity`] holding all the existing entities in the world
70    /// * `bump` - A [`u8`] holding the canonical bump of the World's onchain account used for PDA derivation
71    /// * `preload` - A [`bool`] that allows preloading of [`BTreeMap`] keys in the [`World`] `instances` property
72    ///
73    pub fn new(
74        name: String,
75        description: String,
76        world_authority: Pubkey,
77        regions: Vec<Region>,
78        entities: Vec<Entity>,
79        bump: u8,
80        preload: bool,
81    ) -> Self {
82        let mut instances = BTreeMap::new();
83
84        // preload regions and entities in instances BTreeMap
85        if preload {
86            for r in regions.iter() {
87                instances.insert(r.clone(), BTreeMap::new());
88                // unwrap is ok, None case already checked
89                let curr_region_mut = instances.get_mut(r).unwrap();
90
91                for e in entities.iter() {
92                    curr_region_mut.insert(e.clone(), u64::MIN);
93                }
94            }
95        }
96
97        Self {
98            name,
99            description,
100            world_authority,
101            regions,
102            entities,
103            bump,
104            discriminator: World::SPL_DISCRIMINATOR.into(),
105            is_launched: false,
106            instances,
107        }
108    }
109}