Writing:Assorted Features
Writing |
---|
Introduction |
This section documents miscellaneous features which don’t fit neatly anywhere else.
Portal URLs
A portal URL is a URL that acts as a linking page. It’s an ordinary URL, which may be put on any web page or distributed however you like. When a player clicks it, the page is added to the player’s linking booklet (if necessary) and the page is displayed as the current focus.
(The player is not automatically linked into the Age; the link is just offered.)
Example: http://seltani.net/portlink/522f776d6b3d3010e9c46a18 is a link to the global Korvichtav instance.
The exact behavior of the link:
- If the player is signed in and playing the game (in another browser window), the link will display a message that the page has been added. The page will then appear as the focus in the open game window.
- If the player is signed in, but does not have Seltani open, the link will enter the game. When the game world appears, the page will be focussed.
- If the player is not signed in, the link will go to the sign-in page. The player will see an footnote about having found a linking page. Once the player signs in (or registers a new account), and enters the game world, the page will be focussed.
To create a portal URL, you must create a portal list in the build interface, and set its external
flag to true
. (It doesn’t matter which world you build the list in, or whether it’s publicly accessible.) Add portals to the list in the usual way. Each portal will display a URL which you may copy and use.
It’s a bad idea to put a portal URL into an Age as an external URL. It will launch an unnecessary browser window and generally confuse the player.
The portal’s instance acts in the usual way – you may create a URL that links to the global instance, or the player’s personal instance, or your personal instance. However, portal URLs may not use the “player’s current” instance option. (The player is entering the game from the outside, so there is no current instance!)
Because the URL copies the link, it will not work for uncopyable worlds. If a world is marked copyable: false
, you may extract a URL for it, but it will not be usable.
The portal URL will remain valid as long as the portal remains in the list, and the list is marked external: true
. If you change external
to false
, the URL will stop working temporarily. If you delete the portal from the list, the URL will stop working forever. (Adding the world back to the portlist will generate a new URL.)
If you change the world (or instance) of the portal, the URL will remain valid, but will silently change to refer to the new world (instance).
Cross-Age property access
When you access properties directly, or using the realm
or locations
accessors, you are always reading (or writing) properties of the current Age. They may be world-level or instance-level versions of the property, but they are associated only with your Age. Different Ages normally never affect each other.
But sometimes you want them to. For example, you might want to imitate the “Relto pages” of MOUL -- artifacts (in various Ages) which modify your Relto Age when you touch them. We can accomplish this via the worlds.realm()
function.
Passing information between your own worlds always works. To communicate with another Writer’s Age, you need their permission. We’ll start with the simple case.
To make this work, create a portal list in your world, containing exactly one portal, which leads to another of your worlds. Then, in the code of the first world, you may refer to worlds.realm("key")
, where “key” is the identifying key of the portal list.
This returns a realm proxy object for the target Age. You can use this just like the realm
object, except that it contains realm properties for that world rather than the current one.
worlds.realm("key").prop += 1 if worlds.realm("key").flag: ... del worlds.realm("key").prop
As with the realm
object, setting or deleting a property this way always affects the instance, not the world definition.
(There is no accessor for location properties of the target Age. We may add that feature if it seems important.)
The portal specifies its destination instance in the usual way. You may make a property reference to the global instance, the personal instance of the acting player, a specific personal instance, or the “current” instance (the scope of the acting player).
If the target world (and instance) has never been visited by anybody, the property reference will create the instance. This will invoke its on_init
hook if one exists (but not on_wake
).
Note that the portlist you use for property access does not have to be available to players as a linking book. It just has to be part of the world definition.
What if the portlist contains more than one portal? worlds.realm("key")
will always use the first one. You can refer to other entries by writing worlds.realm("key", N)
where N
is a (zero-based) index number. However, this is an error-prone technique, because ordering a portlist is a nuisance. It’s easier to use a singleton portlist.
Access to Ages of other Writers
If you try to use worlds.realm()
to refer to another Writer’s Age, you’ll get an error: “Cannot access another creator’s world without permission.” This is a basic precaution against vandalism.
Before this can work, the other party must give you permission. Even then, it will be limited permission: the ability to read or write one or more particular realm properties. If you have write permission, you will be limited to writing particular types (perhaps only ints, or only booleans).
To see how this works, we’ll look at it from the other end. Imagine that someone has approached you with a proposal: a lever in their Age will open or close a door in your Age.
You construct a door that relies on a boolean property realm.doorflag
. The other person then goes through the procedure in the previous section. They create a portlist named “linkportal”, add a portal targetting your Age, and write lever code containing the line:
worlds.realm("linkportal").doorflag = True
This line will not work until you give it permission. To do this, you will first need a link (in your booklet) to your partner’s world. (The destination and instance don’t matter here, just the world.)
Once you have this, go to the world page in the build interface, and hit the “Cross-world access permissions” link. This takes you to a new screen, with an empty table titled “Property access entries”. Hit the “New Entry” button.
An access entry has three lines: a named world, a realm property, and a type. Your newly-created entry has default values “The Surface”, prop_0
, and int
.
Press the “World” button on the left to select your partner’s Age. At the same time, edit the property name entry to be doorflag
, and the type field to be bool, read
. (With the comma.) Save this.
With this entry in place, your partner’s lever code will work. The expression worlds.realm("linkportal").doorflag
can be read, or assigned with a new boolean value (True
or False
). No other properties will be available; only doorflag
.
To grant access to more properties, create more permission entries. You may have as many as you need, for the same world or different worlds. (To make this easier, the “Duplicate” button clones an existing entry, copying the world and type fields.)
Note that the entry names a world, but not an instance. It gives permission to any and all instances of that world. (The other player will select a particular instance of your world to communicate with. That’s determined by their “linkportal” portlist. They will then be able to set or delete realm instance properties for that instance.)
The type field requires further explanation. It must be a comma-separated list of strings from this collection: read, NoneType, bool, int, float, str, list, dict, text, gentext, code, ObjectId, datetime
. As you see, this is the list of basic property types, plus the special value read
.
read
means that the named world has permission to read the property, whatever its type. The other strings give permission to set the property to values of the given type. We used bool, read
in our example, so the lever code can read the property or set it to a boolean value. You may name several types; the line int, float
allows the named world to set integer or floating-point numbers.
If the type field includes any write permission, that implies delete permission. The lever code above could do
del worlds.realm("linkportal").doorflag
This would delete the realm instance doorflag
property, perhaps revealing a realm world-defined property.
The behavior of write permission gets funky when it comes to mutable types. If listprop
is a list of numbers, the line
worlds.realm("linkportal").listprop[0] = 1
...is technically not a write access. It’s a read access followed by a change to the read-in value. To ensure such code behaves reasonably, these mutations only work if your code has both read and list
-write access. If your permissions are read-only (or disallow list
writing), this assignment will succeed, but the list will act as a temporary variable. It will not be written back to the listprop
database entry.
The same is true of properties containing dicts, and dict
-write access.
And now, a word of warning about the dict, code, text, gentext
types.
If you give someone permission to set a code
property in your world, you're effectively giving them permission to do anything to your world. A code snippet can modify any instance property, including location descriptions. It can rewrite your Age! (Not the world definition in the build interface, but all of any instance.)
Furthermore, text snippets can include code (in action links). So text
permission is really as bad as code
permission. The same goes for gentext
permission. And a code property is really a Python dict under the hood, so dict
permission is really the same thing!
Maybe you're working on a collaborative project with a friend. That's fine; if you trust them, you trust them. But if you're working with semi-strangers, remember: any of dict, code, text, gentext
permission is effectively hack permission. The basic types -- bool, int, str
and so on -- are safely limited.