WizardData 
The WizardData system provides persistent storage for all player and game data using RavenDB with a dual-database architecture.
Data is separated into two distinct databases based on access patterns and data ownership:
- PlayerDatabase: Individual player accounts and character data
 - WorldDatabase: Shared game configuration data used by development teams
 
TIP
The WorldDatabase fills gaps in client data. Despite the shared data systems philosophy, some data is left server-side only. Some examples include drop tables, NPC inventories, and zone transfer data.
RavenDB Singleton Pattern 
Both databases use the RavenDatabaseSingleton<T> pattern for connection management:
public class PlayerDatabase : RavenDatabaseSingleton<PlayerDatabase> {
    protected override string DatabaseName { get; } 
        = ConfigurationManager.Settings["Database.PlayerDatabaseName"];
    protected override string Url { get; } 
        = ConfigurationManager.Settings["Database.PlayerDatabaseUrl"];
}public class PlayerDatabase : RavenDatabaseSingleton<PlayerDatabase> {
    protected override string DatabaseName { get; } 
        = ConfigurationManager.Settings["Database.PlayerDatabaseName"];
    protected override string Url { get; } 
        = ConfigurationManager.Settings["Database.PlayerDatabaseUrl"];
}Embedded vs Remote
- Embedded: Local database file for development and small deployments
 - Remote: External RavenDB server for production environments
 - Automatic fallback to embedded when no URL is configured
 
Collection Pattern 
Data access is handled through static collection classes that provide CRUD operations and encapsulate database logic.
Collection Structure 
public static class AccountCollection {
    public const string CollectionName = "Accounts";
    private static readonly IDocumentStore s_store;
    
    static AccountCollection() {
        s_store = PlayerDatabase.Instance.Store;
    }
    
    public static bool CreateAccount(Account account) {
        using var session = s_store.OpenSession();
        
        if (session.Query<Account>(collectionName: CollectionName)
                   .Any(c => c.Username == account.Username)) {
            return false;
        }
        
        session.Store(account);
        var metadata = session.Advanced.GetMetadataFor(account);
        metadata[Raven.Client.Constants.Documents.Metadata.Collection] = CollectionName;
        
        session.SaveChanges();
        return true;
    }
}public static class AccountCollection {
    public const string CollectionName = "Accounts";
    private static readonly IDocumentStore s_store;
    
    static AccountCollection() {
        s_store = PlayerDatabase.Instance.Store;
    }
    
    public static bool CreateAccount(Account account) {
        using var session = s_store.OpenSession();
        
        if (session.Query<Account>(collectionName: CollectionName)
                   .Any(c => c.Username == account.Username)) {
            return false;
        }
        
        session.Store(account);
        var metadata = session.Advanced.GetMetadataFor(account);
        metadata[Raven.Client.Constants.Documents.Metadata.Collection] = CollectionName;
        
        session.SaveChanges();
        return true;
    }
}Creating New Collections 
When adding new data types, follow the established collection pattern:
1. Determine Database Target 
- PlayerDatabase: User-specific data that varies per player
 - WorldDatabase: Shared configuration data managed by developers
 
2. Create Collection Class 
public static class ExampleCollection {
    public const string CollectionName = "Examples";
    private static readonly IDocumentStore s_store;
    
    static ExampleCollection() {
        s_store = PlayerDatabase.Instance.Store; // or WorldDatabase.Instance.Store
    }
    
    public static bool CreateExample(ExampleModel example) {
        using var session = s_store.OpenSession();
        
        session.Store(example);
        var metadata = session.Advanced.GetMetadataFor(example);
        metadata[Raven.Client.Constants.Documents.Metadata.Collection] = CollectionName;
        
        session.SaveChanges();
        return true;
    }
    
    public static ExampleModel GetExample(string id) {
        using var session = s_store.OpenSession();
        return session.Load<ExampleModel>(id);
    }
}public static class ExampleCollection {
    public const string CollectionName = "Examples";
    private static readonly IDocumentStore s_store;
    
    static ExampleCollection() {
        s_store = PlayerDatabase.Instance.Store; // or WorldDatabase.Instance.Store
    }
    
    public static bool CreateExample(ExampleModel example) {
        using var session = s_store.OpenSession();
        
        session.Store(example);
        var metadata = session.Advanced.GetMetadataFor(example);
        metadata[Raven.Client.Constants.Documents.Metadata.Collection] = CollectionName;
        
        session.SaveChanges();
        return true;
    }
    
    public static ExampleModel GetExample(string id) {
        using var session = s_store.OpenSession();
        return session.Load<ExampleModel>(id);
    }
}3. Follow Conventions 
- Static class with collection name constant
 - Store assignment in static constructor
 - Metadata setting for proper RavenDB collection organization
 - Session disposal using 
usingstatements - Consistent method naming (Create, Get, Update, Delete patterns)
 
Database Utilities 
The DatabaseUtilities class provides common operations:
// Create accounts with hashed passwords
var account = DatabaseUtilities.CreateEmbeddedDatabaseAccount(
    username: "player123",
    email: "player@example.com", 
    plaintextPassword: "password",
    auth: AuthLevel.None
);// Create accounts with hashed passwords
var account = DatabaseUtilities.CreateEmbeddedDatabaseAccount(
    username: "player123",
    email: "player@example.com", 
    plaintextPassword: "password",
    auth: AuthLevel.None
);
Imlight