Multi-user support in Candella is provided by the Multiuser framework. This framework adds the necessary classes and utilities to create and manage multiple users, as well as manage data for specific users.
What is a Candella user?
A user is defined by a username and is represented as the
CAUser data class. In most cases, developers will not need to create an instance of this class as core services and applications will provide this automatically.
CAUser.usernamecontains the username of the user. Usernames must be in lowercase with no spaces or special characters.
CAUser.display_namecontains the user-facing display name for the user. This display name is used by core services such as Celeste Shell to display the user's name. By default, this name defaults to the username.
CAUser provides methods for checking equality and string representation.
The userland folder
Each user gets a corresponding file in the
.causerland directory. This directory is located in the save directory of the Ren'Py project. For instance, if Candella were installed in Doki Doki Literature Club!, the userland folder is located at the following path:
%AppData%\RenPy\DDLC\.causerland # Windows ~/Library/RenPy/DDLC/.causerland # macOS ~/.renpy/DDLC/.causerland # Linux
Users and app/service developers typically don't need to worry about the user file as classes exists for managing data in the userland folder.
Reading/writing data for the current user
Apps and services provide their own means of accessing sandboxed data. These classes ensure that the app or service can locally access only their data. App/service data is registered under its bundle ID.
For Candella apps, the
AppStorage class is used to manage data. More information using AppStorage can be found in the documentation for creating Candella apps.
Likewise, core services in Candella utilize the
ServiceStorage class. Like
AppStorage, data is separated by bundle ID and is accessible via the
data field of the
There are three methods available for managing service data:
ServiceStorage.get_entry(field, raise_falsy=False)will fetch the value for a field or return
Noneif no value for the field was found. If
raise_falsyis set to
True, the method will instead raise an exception.
ServiceStorage.set_entry(field, value)will write the value
valueinto the specified
ServiceStorage.commit()will commit all written changes to the current user's data file.
Do not store sensitive information in service storage unless you are using cryptography to encrypt the information. Service storage is provided in the user's data file in a human-readable format and may be easily compromised if not encrypted properly.
Global scope access
There may be instances where you need to access application data outside of the app class. There are two static methods in the
CAUserData class to handle this:
Returns the application data for the current user.
- bundle_id (str): The app's bundle ID.
- app_data (dict): The user data for the specified app as a dictionary. If no data was found for the app, an empty dictionary will be returned.
If the app doesn't have permissions to open the data, CAUserDataPermissionError is raised. If the user file doesn't exist, FileNotFoundError is raised. Otherwise, any other exceptions from file opening and parsing may be thrown.
Writes the specified app data to the current user's file.
- bundle_id (str): The app's bundle ID
- data (dict): The data to write to the file for that bundle ID.
If the app doesn't have permissions to write the data, CAUserDataPermissionError is raised. If the user file doesn't exist, FileNotFoundError is raised. Otherwise, any other exceptions from file opening and writing may be thrown.