Worldlets are files stored on the server that contain definitions of your game world. You are free to use them exclusively, rely on a more traditional game-building approach with commands, or even combine both methods.
What are worldlets?
A worldlet is simply a text file on the server. This file contains instructions to create or update parts of the world.
This feature may seem simple, but the key point is “create and update”. For example, if the file contains the definition for a room that already exists, the existing room in the world is updated according to the file. This allows you to apply and reapply the same files and see the world evolve (without duplication).
Why is this useful?
Suppose you’ve created several rooms but repeatedly made the same spelling error in their descriptions. Normally, you’d have to manually update the description in each room—assuming you have an efficient way to edit them—one by one, which can be time-consuming.
If your rooms are defined in worldlets, you only need to open the relevant files in your favorite editor, perform a “find and replace” (most editors support this across multiple files), then save and apply the worldlets. Voilà!
Planning ahead in world building
It’s tempting, when you find a great game engine (hopefully Pythelix!), to immediately start building a game full of rooms, NPCs, vehicles, spells, spaceships, dancing fluffies, and more. However, if you intend to manage a game with builders, it’s important to plan ahead.
How would builders contribute? Traditionally, the answer was straightforward: give them a builder role (or the necessary permissions), train them on building commands, and hope they use them effectively. This isn’t a bad approach, but other workflows exist.
You might decide never to share building commands directly. Instead, share individual worldlets (one or two files) with each builder. Each builder manages their assigned worldlet, builds and tests on their own computer, and then sends the completed worldlet to you, the administrator, for review and application. This way, they only access what you allow, and still can test their work.
If they encounter repeated mistakes (like spelling errors), they can correct them in their worldlet, resend it, and after your review, you can reapply it to fix the issues.
Additional possible workflows:
- Test server: Maintain a test server accessible only to builders. They can push their worldlets here to check for conflicts. Once cleared, you push the worldlet to the main server. The test server is just a copy of the main server, with restricted access.
- Version control: Use a system like Git to version your worldlets, allowing easy tracking of changes and avoiding redundant reviews. Builders can use this too, but may not always care about version history.
- Shared folder access: If you trust your builders, you might give them access to a shared Dropbox folder or similar service where they can directly modify their worldlets, which are then automatically applied in the game. (Dropbox is one example, but there are many other solutions.)
These strategies illustrate how worldlets support collaboration.
You don’t have to use worldlets for everything!
Worldlets follow specific rules and are intended primarily for world deployment (think lore only, not the complete game). For example, player accounts are created in your database and should not be included in worldlets. They are not backups; they exist to simplify world deployment.
Explain the difference between worldlet and database in details
The database is a tool to save your game. When you modify an entity (say, a room), you save this modification in the database. When you create a player account, you do so in the database.
Worldlets are just files: for each entry in a worldlet, the game asks: “does this entity already exist in database? If not, create it. If so, update it”.
That’s why player accounts aren’t found in the worldlet: players create their account in game. The worldlet knows nothing about which player accounts exist. It contains menus, commands, rooms, objects, NPCs and probably other things, but it doesn’t store everything from a game: it’s not a storage. The database stores. The worldlet is applied to affect storage. Never the other way around.
If you are confused, think of worldlets as blueprints: they don’t hold the game but help to recreate it. A game, however, is everything which has been stored, not just a simple blueprint.
Where to find worldlets and how to edit them?
Worldlets are located in the server code under the worldlets directory. This directory contains both subdirectories and .txt files. When the server starts, it reads every worldlet file in the directory and its subdirectories and applies them.
By default, the directory includes some example files:
command.txt: worldlet containing your commands. You can split this into multiple files if needed.room.txt: worldlet containing your rooms.character.txt: worldlet containing character-related entities.menu.txt: worldlet containing menus (login, account creation, etc.).admin: directory containing worldlets for administrators.
Your game world consists of entities. An entity is a single piece of information (a room, item, character, vehicle, etc.). Entities can be practical or logical (e.g., a race, event, skill, or spell).
Here’s an example from room.txt (open worldlets/room.txt in your favorite editor):
!room/bakery!
parent = "generic/room"
title = "A bakery"
description = Description("""
The warm, inviting scent of freshly baked bread and sweet pastries fills
the air upon entering this cozy little shop. A fine dusting of flour clings
lightly to the wooden floorboards and countertops. Shelves and display cases
brim with golden-baked goods—loaves of crusty bread, delicate pastries,
and confections in all shapes and sizes. Icing glistens under soft lighting,
while nuts, berries, and chocolate chips adorn many of the treats with artistic
precision.
At the back of the shop, an antique wooden cash register rests
atop a counter, its brass details dulled slightly with age and use.
""")
Let’s break it down:
- The entity key is between exclamation marks at the top (
!room/bakery!here). Each entity key is unique (defining an entity with an existing key updates it). Using a path-like key such as!room/bakery!helps avoid conflicts. Here,room/bakeryis the key and the slash acts as a path separator; you can use another separator as long as you’re consistent. parent = "generic/room"indicates that this entity has a parent with key"generic/room"(defined elsewhere). To learn more, see the entities documentation.title = "A bakery"sets thetitleattribute to"A bakery". Note the quotation marks—they are needed because attribute values can be various types (text, numbers, lists, other entities, etc.).description = Description(""" ... """)usesDescription()to create a description object from a multiline string (triple quotes). This is a bit more advanced, but the point is, it creates an attribute on the room.
A single file can contain many entities (noted by multiple !entity key! declarations).
Attributes in entities
To recap, the syntax for attributes is:
attribute_name = attribute_value
attribute_nameshould be a valid name (no spaces, use underscores if needed; it cannot start with a digit but can contain Unicode characters such asé).
Attribute values accept any value valid in Pythello, the scripting language. For example:
price = 300— integer value 300.volume = 15.8— floating-point number 15.8.title = "some title"— string.-
tips = """ ... on multiple lines ... """— multiline string (triple quotes on opening and closing lines):tips = """ Some tip on multiple lines. """ friend = !room/demo/fruit_stand!— reference to another entity with keyroom/demo/fruit_stand. The!entity key!syntax is a Pythello shortcut.food = ["white bread", "cookie", "croissant"]— a list of strings (can be multiline, but the opening bracket must be on the starting line).info = {"price": 31, "weight": 72}— a dictionary.
and so on.
All valid scripting values (numbers, strings, lists, dictionaries, entities, function calls, operations, times, durations, etc.) are valid attributes. Attributes are evaluated when the worldlet is applied, so be cautious with function calls. For example:
choice = random.randint(1, 6)
This will assign an integer between 1 and 6 to choice, but the value will reset each time the worldlet is reapplied, which may not be desired.
To learn more about value syntax, see the scripting documentation.
Methods in entities
Entities can also have methods, representing behavior. If you’ve used LambdaMOO, methods are similar to verbs (while attributes map to properties). Note that Pythelix methods are strictly behavioral; unlike LambdaMOO verbs, they do not serve as commands.
An entity can have zero, one, or multiple methods.
Methods are more complex, so it’s advisable to read the methods documentation. In worldlets, methods look like this:
!entity_key!
attr1 = value1
attr2 = value2
...
def method_name():
code on
several
lines
def another_method(value):
Some
other
code
...
To define a method, use:
- The
defkeyword. - A space.
- The method name;
- The method arguments between parents (
()to mean no argument). - A colon.
Example: def greet():, followed by the method’s code on one or more lines. When Pythelix sees a line starting a new entity (!another entity!), starting a new method (def another_method():), or end-of-file, and considers the method body complete, it adds the method to the entity.
Example:
!room/bakery!
parent = "generic/room"
title = "A bakery"
def spill(author):
author.msg("You swipe the merchandise and throw it to the ground. How rude!")
author.location.announce(f"{author} swipes the merchandise and throws it to the ground. Really!")
This spill method sends a message to the action’s author and announces it to the room (excluding the author).
Methods can have arguments, specified in parentheses after the method name:
def spill(author):
Multiple arguments are also supported:
def roll_dice(min, max):
Here, roll_dice takes two arguments, min and max. You can add type annotations (similar to Python) for argument types:
def roll_dice(min: int, max: int):
Now, min and max are expected to be integers. If called with incorrect argument types, an error is raised or the call rejected.
Arguments can also be entities, with type hints:
def spill(author: Entity["generic/player"]):
This means spill expects author to be an entity whose parent (or ancestor) key is "generic/player". This syntax resembles Python’s typing but is designed to avoid confusion.
Specifying type hints is strongly recommended. You can also specify a return type:
def roll_dice(min: int, max: int) -> str:
This method takes two integers and returns a string, aiding error detection.
Default argument values can be set with =:
def roll_dice(min: int = 1, max: int = 6) -> str:
Calling this method with no arguments uses defaults; you can override one or both arguments.
Contrary to Python, the indentation is not necessary. The documentation usually provides it for clarity. But nothing prevents you from writing:
def spill(author, number):
if number == 1:
author.msg("You swipe the merchandise and throw it to the ground. How rude!")
author.location.announce(f"{author} swipes the merchandise and throws it to the ground. Really!")
else:
author.msg("You swipe the merchandise and throw absolutely everything to the ground. How rude!")
author.location.announce(f"{author} swipes the merchandise and throws everything to the ground. Really!")
endif
That’s why closing keywords (like endif and done) are necessary. The indentation in Pythello is not required.
See the methods documentation for more details on syntax.
Reapplying worldlets
As mentioned, all worldlets are applied automatically when the server starts. Often, though, you’d want to apply one or more worldlets without restarting.
There are several options:
From a logged-in administrator (probably the first character you created in Pythelix), you can simply enter the apply command.
> apply
Alternatively, Pythelix provides the apply (or apply.bat) scripts for this purpose.
If running the binary version, look inside the bin directory for apply (Unix) and apply.bat (Windows). You can use these in the command line, specifying the file or directory to apply:
./apply path/to/file/or/directory
The path can point to a single .txt file or a directory (all worldlets inside will be applied). Upon success, the script reports how many entities were created or updated. If errors occur, Pythelix will not apply but will report the problem and, whenever possible, the line.
You can also double-click on the script (or execute it with no argument). All worldlets will be applied.
If running from source, run:
mix apply path/to/worldlet
In both cases, the server must be running (./server or server.bat for the binary, or ./dev / dev.bat from source). apply queues the task in the server. Worldlets are applied between commands, ensuring the game remains consistent during the process.
If you have started a Pythello console (with script, script.bat or mix script), you can also use the apply function. It’s a builtin, so you can type in your console apply() to apply all worldlets (note the parenthesis).
That also means you can, from the game (with an administrator account), run it like this:
> py apply()