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}