Coordinated Effects

From Multiverse

Jump to: navigation, search
The Client Scripting API is under development. During this phase of development, we are focusing on functionality and features, rather than maintaining 100% backwards compatibility for each release. If you write scripts using this API, be prepared to make change them until the Client Scripting API is more stable.


Contents

Introduction

A coordinated effect is a group of visual and sound effects that execute together as a result of a single specified event. A coordinated effect can include anything that the Multiverse Client is capable of producing, including animation, decals, particle systems, sounds, and projectiles.

In general, the client handles the details of complex visual effects, but the server performs gameplay-critical computations to prevent players from cheating with a hacked client. Thus, a client script creates a coordinated effect, but the client executes the script as a result of a message from the server. You use the Client Scripting API to write coordinated effects scripts.

For example, for a damage spell, the mechanism might be:

  1. User selects a target by clicking on it, and then presses a key to cast the spell (use the key bindings mechanism to associate an action with a key.)
  2. The script bound to that key sends a message to the server to invoke the spell ability.
  3. The code on the server for handling that ability validates the request, for example, to make sure the target is within range, the caster has enough mana, and so on.
  4. The ability code computes the effect of the script and applies it to the server side game state.
  5. The ability code sends an InvokeEffect message to all players within visual range, so that their clients can play the effect associated with that ability. This message can contain parameters for the script.
  6. The coordinated effect script runs on each client, playing a timed sequence of animations, particle effects, sounds, and so on, under the control of the script.

A client script can also invoke a coordinated effect from a key binding script, for example, by calling the function ClientAPI.InvokeEffect().

Implementing effects

A coordinated effect script is defined by a Python class. Invoking the effect creates an instance of the class and calls its ExecuteEffect() method.

To create a coordinated effect script, define a Python class with:

  • a class constructor
  • an ExecuteEffect() method.

In a future release, you will also be able to define:

  • an UpdateEffect() method.
  • a CancelEffect() method.

Class constructor

Usually, a server message invokes a coordinated effect, which calls the class constructor. An object's class constructor creates an instance of the object and initializes it.

A coordinated effect constructor has a single argument, the unique object ID that identifies the particular instance of the effect. The server uses the object ID to name a particular instance of an effect in later messages to update or cancel the effect.

The ExecuteEffect method

After creating an instance of a coordinated effect object, invoking the effect calls the ExecuteEffect() method. This method can take any arguments that the effect requires.

Pausing execution of the effect

The ExecuteEffect() method can use the Python yield statement to pause execution of the script. The yield statement takes one integer argument, the number of milliseconds that the script pauses before continuing.

For example, the statement:

yield 2500

will pause the effect script for 2.5 seconds before continuing. Use the yield statement to control the timing of various parts of the effect script.

Important: The ExecuteEffect() method must call yield directly to achieve the desired effect. If ExecuteEffect() calls another method or function that calls yield, the other method or function will be a normal generator function and won't pause the execution of the effect script.

Cleaning up after execution

Each coordinated effect object is executed only once, so before exiting ExecuteEffect() must clean up any resources it allocates. Standard Python and .NET garbage collection will clean up most objects, but many objects created with the Client Scripting API create and reference external resources. You must free such references explicitly with the Dispose() method.

The UpdateEffect method

This method is not currently used, but a future release of the Multiverse platform will use it to enable the server to send new data to an already running effect.

The CancelEffect method

This method is not currently used, but a future release of the Multiverse platform will use it to enable the server to cancel an already running effect.

Registering effects

To make an effect accessible to other scripts and the server, register it using ClientAPI.RegisterEffect().

Typically, you regsiter effects when you initially import the module containing the effects class.

Invoking coordinated effects

You can invoke a coordinated effect from the client or from the server.

Invoking with Client code

To invoke a coordiated effect on the player's client only, call ClientAPI.InvokeEffect(). This method does not communicate with the server, and so is not suitable for coordinated effects that you want other players to see.

For example, to invoke the simple example effect, use the following client script call:

effectArgs = {'message': 'This is a test log message.'}
ClientAPI.InvokeEffect("TestLogEffect", ClientAPI.GetLocalOID(), effectArgs)

The first argument to the method is a string which is the name of the effect. The second argument is a unique object ID that represents this instance of the effect. In a future release this object ID will be used to cancel or modify an effect that is already running. The third argument is a dictionary that contains the names and values of the arguments for the ExecuteEffect() method of the effect being invoked.

To invoke a coordinated effect that can also be seen by other players, use the ClientAPI.Network.SendTargetedCommand() method. For example:

ClientAPI.Network.SendTargetedCommand(ClientAPI.GetPlayerObject().OID, "/laugh")

Then define the command handler in a server script. For example, in multiverse/config/sampleworld/extensions_proxy.py you might define:

 class LaughCommand (ProxyPlugin.CommandParser):
   def parse(self, cmdEvent):
        playerOid = cmdEvent.getObjectOid()
        Log.debug("/laugh: oid=" + str(playerOid))
        AnimationClient.playSingleAnimation(playerOid,
                                            "laugh")
        return None  

This script calls AnimationClient.playSingleAnimation() which:

  • Creates a cooridnated effect named "PlayAnimation."
  • Sends the sourceOid parameter to the client coordinated effect script.
  • Adds an "animName" argument to pass to the client effects script.
  • Invokes the coordinated effect the sourceOid.

Invoking with server code

Here's a simple example of how to directly invoke a coordinated effect. You can put this example in proxy.py:

heartsEffect = CoordinatedEffect("SpellTargetEffect")
heartsEffect.sendTargetOid(1)
class ParticleExampleCommand (ProxyPlugin.CommandParser):
    def parse(self, cmdEvent):
        playerOid = cmdEvent.getObjectOid()
        targetOid = cmdEvent.getTarget()
        heartsEffect.invoke(playerOid, targetOid)
        
proxyPlugin.registerCommand("/cfxtest", ParticleExampleCommand())

For information on how to invoke a coordinated effect from the server, see Invoking a coordinated effect with an ability. For information on the coordinated effect server object, see the multiverse.mars.objects.CoordinatedEffect API Javadoc.

Simple example effect

The simple example coordinated effect included with the Multiverse Sampleworld writes a message to the client chat window and log file when it is invoked.

The Sampleworld asset repository includes:

  • Definition of the simple example effect in Scripts/TestLogEffect.py
  • A key binding (Alt-Ctrl-G) to invoke the effect.

The following sections explain the code in TestLogEffect.py.

The first line imports the ClientAPI module, required to access to the client scripting API.

import ClientAPI

The next line defines the name of the Python class:

class TestLogEffect:

The methods below are in the scope of the class.

    #
    # Class constructor
    #
    def __init__(self, oid):
        # save the instance oid for this instance of the effect
        self.OID = oid

An object constructor in Python is always called __init__().

    #
    # This method is called to cancel the effect.
    #  XXX - Not supported in Multiverse Platform Beta 2
    #
    def CancelEffect(self):
        pass

    #
    # This method is called to update the effect.
    #  XXX - Not supported in Multiverse Platform Beta 2
    #        
    def UpdateEffect(self):
        pass

The CancelEffect() and UpdateEffect() methods are not currently used, but will be used in a future version of the Client API to allow effects that can be updated by the server while executing, and effects that can be cancelled while playing.

    #
    # This method is called to execute the effect.
    #              
    def ExecuteEffect(self, message):
        # write the unique object ID of this effect instance, along with the 
        # message parameter to the client chat window and the log file
        ClientAPI.Write('Effect Instance: ' + str(self.OID) + ' Message: ' + message)

The ExecuteEffect() method does the work of the effect. In this case it just writes a message to the chat window.

# register the effect
ClientAPI.RegisterEffect("TestLogEffect", TestLogEffect)

The call to ClientAPI.RegisterEffect() must be in the top scope of the module, not the scope of the class definition, so that it is executed when the module is imported.

Writing and testing your own effects

This section provides a step by step walkthrough of the process of creating your own coordinated effect using an effect called MyEffect as an example. When testing a new effect, it is convenient to create a temporary key binding to execute the effect. Once the effect is running the way you want it, remove the key binding and write server code to invoke the effect as part of an ability; see Invoking a cooridnated effect with an ability for more information.

  1. Write your effect script in the file MyEffect.py. Some things to keep in mind when writing your effect:
    1. Import the ClientAPI module to gain access to the multiverse client API.
    2. Implement the functionality of your effect in the ExecuteEffect() method.
    3. Call ClientAPI.RegisterEffect() to register your effect with the system.
  2. Copy MyEffect.py to the Scripts directory of your asset repository.
  3. Import and register your effect. This is done by adding the following line to Startup.py:
    import MyEffect
  4. Create a key binding that calls ClientAPI.InvokeEffect() to run your effect. For information on creating a key binding, see Customizing Key Bindings.
  5. Run the client and use the key binding to execute your effect.

Debugging coordinated effects

Current debugging support for client scripts is primitive. Here are a few hints for debugging.

Use ClientAPI.Write() or ClientAPI.Log() at the start and end of your effects while debugging, so that you know that they are running to completion. Make sure to remove these statements once your effect is working, so that they don't clutter the logs, leave confusing chat messages for users, and lower performance.

When a script fails, the client will generate an exception write it to the log file (trace.txt). Search this file for the word "Exception" to find a stack trace that indicates where the error occurred, and what the problem was.

Other examples

Intermediate examples

TBD.

Advanced examples

Personal tools