evennia icon indicating copy to clipboard operation
evennia copied to clipboard

[Feature Request] External build system

Open Griatch opened this issue 5 years ago • 3 comments

Description of problem

The current out-of-the-box build systems are primarily:

  • In-game building via commands
  • Batch-code/Batch-command scripts loaded from in-game
  • Prototype-based OLC / external prototypes with spawn

There is no real procedural build process where a world can be built via files and kept up to date via external files without explicitly modifying the database afterwards. This feature request outlines a procedural type of build system.

Describe the solution you'd like

From the sketched whitepaper:


External creation

This is a system for using prototypes to defined a game grid explicitly. It allows for creating and maintaining a game in code rather than doing so by manipulating in-database directly. Changes to the prototype (followed by a server reload or in-game command) will update the game world accordingly.

The advantage of this is that a game world can be created in a more 'coded' manner, and modification is done in files. The disadvantage is that modifications in-game will cause a state different from the template and will override the template, which can potentially be confusing.

Format:

In-module prototypes are used, with special helper protfuncs and directives. Also, since a prototype file is a Python module, any other Python code is also allowed.

Special protfuncs

Remember that all protfuncs are possible to chain, so multiple protfuncs can apply to the same value.

  • '$ident(key [,area])' - this will become the prototype_key field and will automatically generate both a unique prototype key and a tag+category combination for quickly finding this object later. The system will check that the given ident+area combination is globally unique so one can find the object in the database later.
  • $link(key, [area]) - this is used to explicitly link objects together (usually for location and destination fields). The key/area combination is the one given with $ident.
  • $immutable() - the default is that if a value is changed in the database, the database takes precedence over what is in the prototype. With this protfunc set around the value, every re-sync of the prototype will always put the prototype's value back, no matter what the database says (it cannot stop it from being changed in the first place though; this needs to be controlled with locks/permissions).
  • '$once()' - this is the inverse of $immutable - this will only ever read once from the prototype and then never again. This is useful around random values that should not be re-randomized when the prototype is reloaded.

Helper functions

While each entry can be a little verbose, in practice one would create helper-functions in Python when building a larger area with a lot of details/objects. All such a function needs to do is to output a dict, so if customizing the function for the current area being worked on one could cut down a lot on the amount of coding needed.

Examples

WORLD = {

    "$ident(billiardroom, castle)":
        {
            "typeclass": "typeclasses.rooms.Room",
            "key": "The billiard room",
            "attrs": (
                ("desc", "$immutable(This is a nice room with a billiard table.)")
            )
        },

    # manually create an exit
    "$ident(billiardroom.exitnorth, castle)":
        {
            "typeclass": "typeclasses.exits.Exit",
            "key": "North exit",
            "attrs": (
                ("desc", "An exit leading north."),
            ),
            "location": "$link(billiardroom, castle)",
            "destination": "$link(towerroom, castle)"
        },

    # create an exit with a helper function
    "$ident(billiardroom.exitnorth, castle)":
        exit(
            key="South exit",
            desc="An exit leading south",
            location="$link(billiardroom, castle)",
            destination="$link(staircase, castle)",
        ),

    # one can use $random() or the raw python random()
    "$ident(billiardplayer, npcs)":
        {
            "typeclass": "typeclasses.npc.NPC",
            "key": '$once($choice(John, Adam, Sarah, Maria))',
            "attrs": (
                ("desc": "A friendly billiard player"),
                ("str": f"$once({random() * 10 + 1}),
                ("int": f"$once({random() * 10 + 2}),
                ("agi": f"$once({random() * 10 + 1}),
                ("mag": f"$once({random() * 10 + 1}),
            ),
            "location": "$link(billiardroom, castle)"
        },

    "$ident(poolcue, weapons)":
    {
        "typeclass": "typeclasses.weapons.Weapons",
        "key": "Pool queue",
        "desc": "A well-used pool cue.",
        "attrs": (
            ("base_damage": 10),
            ("variation": [2, 5])
        ),
        "location": "$ident(billiardplayer, npcs)"
    }
}

Technical

Each Template must still correspond to a database entity. This is synced against the templates so if the template is removed, the database entity is too.

The diffs are stored in the database so that only changed prototypes are compared with their in-database equivalents.


Describe alternatives you've considered

There are many possibilities, including making objects be in-memory only until modified. The suggested solution makes use of the existing prototype and protfunc systems and should be possible without massive restructuring of the database schema or functionality. It requires a new monitor mechanism to track diffs between database and the external templating system; some care needs to be given to just how this should work.

Additional context

This is likely not something for Evennia 1.0, maybe as a stretch goal...

Griatch avatar Jul 17 '20 08:07 Griatch

The disadvantage is that modifications in-game will cause a state different from the template and will override the template, which can potentially be confusing.

It seems like this proposal could be developed as a separate system, with integration into evennia and some refactoring probably addressing this. I'm definitely interested in taking a stab at this one, but it would probably have to be late September before I had any time to start coding. I can commit to helping plan it out, just not quite yet to writing code.

I came into evennia really early on, primarily looking for a feature like this - but I was okay without it once I fell in love with this code base. I'd love to see something implemented, even if it's just an API externalized for use.

tsal avatar Aug 01 '20 12:08 tsal

@tsal Hi there, you've been around in Evennia world even longer than I have :)

The prototype system, protfuncs and even prototype diff mechanisms already exists, the main issue with implementing this suggestion is to tie it together and handle sync between database and the external template.

To help actually creating files on this format, I suppose one could provide some syntax/code-completion definitions for common IDEs. Or a custom little external Python program. In principle one could also make an EvMenu-based in-game function too, for superusers/developers only, since it'd need to read/save output to files directly.

Griatch avatar Aug 05 '20 08:08 Griatch

Yep. And if we used an existing language / DSL, I think most of the heavy lifting will be done for us on the file side of things. Sync, as you mentioned, will be the most effort.

tsal avatar Aug 05 '20 09:08 tsal