/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
//////////////////////////////////////////////////////////////////////
//
// plPythonFileMod   - the 'special' Python File modifier.
//
// This modifier will handle the interface to python code that has been file-ized.
//
//////////////////////////////////////////////////////////////////////////
#include "plPythonFileMod.h"

#include "HeadSpin.h"
#include "hsStream.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plResMgr/plKeyFinder.h"
#include "pnKeyedObject/plKeyImp.h"
#include "pnKeyedObject/plUoid.h"

#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnKeyedObject/plKey.h"
#include "pnMessage/plTimeMsg.h"
#include "pnMessage/plCmdIfaceModMsg.h"
#include "plMessage/plInputEventMsg.h"
#include "plModifier/plLogicModifier.h"
#include "pfMessage/pfGUINotifyMsg.h"
#include "plMessage/plRoomLoadNotifyMsg.h"
#include "pfMessage/plClothingMsg.h"
#include "pfMessage/pfKIMsg.h"
#include "plMessage/plMemberUpdateMsg.h"
#include "plMessage/plAgeLoadedMsg.h"
#include "pnMessage/plRemoteAvatarInfoMsg.h"
#include "pnMessage/plPlayerPageMsg.h"
#include "plNetClient/plNetClientMgr.h"
#include "plNetTransport/plNetTransportMember.h"
#include "pnMessage/plSDLNotificationMsg.h"
#include "plMessage/plNetOwnershipMsg.h"
#include "plSDL/plSDL.h"
#include "plVault/plVault.h"
#include "plMessage/plCCRMsg.h"
#include "plMessage/plVaultNotifyMsg.h"
#include "plInputCore/plInputInterfaceMgr.h"
#include "plInputCore/plInputDevice.h"
#include "pfMessage/pfMarkerMsg.h"
#include "pfMessage/pfBackdoorMsg.h"
#include "plMessage/plAvatarMsg.h"
#include "plMessage/plLOSHitMsg.h"
#include "plMessage/plRenderMsg.h"
#include "pfMessage/pfMovieEventMsg.h"
#include "plMessage/plClimbEventMsg.h"
#include "plMessage/plCaptureRenderMsg.h"
#include "plGImage/plMipmap.h"
#include "plMessage/plAccountUpdateMsg.h"
#include "plAgeLoader/plAgeLoader.h"
#include "pfGameMgr/pfGameMgr.h"
#include "plMessage/plAIMsg.h"
#include "plAvatar/plAvBrainCritter.h"

#include "plProfile.h"

#include "cyPythonInterface.h"
#include "pyKey.h"
#include "cyDraw.h"
#include "cyPhysics.h"
#include "pySceneObject.h"
#include "cyMisc.h"
#include "cyCamera.h"
#include "pyNotify.h"
#include "cyAvatar.h"
#include "pyGeometry3.h"
#include "pyVault.h"
#include "pyVaultNode.h"
#include "pyVaultNodeRef.h"
#include "pyVaultAgeLinkNode.h"
#include "pyPlayer.h"
#include "pyNetLinkingMgr.h"
#include "pyAgeInfoStruct.h"
#include "pyAgeLinkStruct.h"
#include "pyImage.h"
#include "pyCritterBrain.h"

// GUI Control:
#include "pyGUIDialog.h"
#include "pyGUIControlButton.h"
#include "pyGUIControlCheckBox.h"
#include "pyGUIControlEditBox.h"
#include "pyGUIControlListBox.h"
#include "pyGUIControlRadioGroup.h"
#include "pyGUIControlTextBox.h"
#include "pyGUIControlValue.h"
#include "pyGUIControlDynamicText.h"
#include "pyGUIControlMultiLineEdit.h"
#include "pyGUIPopUpMenu.h"
#include "pyGUIControlClickMap.h"

// Game manager
#include "Games/pyGameMgrMsg.h"
#include "Games/pyGameCliMsg.h"

#include <locale>

#include "plPythonSDLModifier.h"

#include "plMessage/plTimerCallbackMsg.h"

plProfile_CreateTimer("Update", "Python", PythonUpdate);

/////////////////////////////////////////////////////////////////////////////
//
// fFunctionNames    - the actual names of the functions for On[event] types
//
const char* plPythonFileMod::fFunctionNames[] = 
{
    "OnFirstUpdate",        // kfunc_FirstUpdate
    "OnUpdate",             // kfunc_Update
    "OnNotify",             // kfunc_Notify
    "OnTimer",              // kfunc_AtTimer
    "OnControlKeyEvent",    // kfunc_OnKeyEvent
    "Load",                 // kfunc_Load
    "Save",                 // kfunc_Save
    "OnGUINotify",          // kfunc_GUINotify
    "OnPageLoad",           // kfunc_PageLoad
    "OnClothingUpdate",     // kfunc_ClothingUpdate
    "OnKIMsg",              // kfunc_KIMsg,
    "OnMemberUpdate",       // kfunc_MemberUpdate,
    "OnRemoteAvatarInfo",   // kfunc_RemoteAvatarInfo,
    "OnRTChat",             // kfunc_RTChat,
    "OnVaultEvent",         // kfunc_VaultEvent,
    "AvatarPage",           // kfunc_AvatarPage,
    "OnSDLNotify",          // kfunc_SDLNotify
    "OnOwnershipChanged",   // kfunc_OwnershipNotify
    "OnAgeVaultEvent",      // kfunc_AgeVaultEvent
    "OnInit",               // kfunc_Init,
    "OnCCRMsg",             // kfunc_OnCCRMsg,
    "OnServerInitComplete", // kfunc_OnServerInitComplete
    "OnVaultNotify",        // kfunc_OnVaultNotify
    "OnDefaultKeyCaught",   // kfunc_OnDefaultKeyCaught
    "OnMarkerMsg",          // kfunc_OnMarkerMsg,
    "OnBackdoorMsg",        // kfunc_OnBackdoorMsg,
    "OnBehaviorNotify",     // kfunc_OnBehaviorNotify,
    "OnLOSNotify",          // kfunc_OnLOSNotify,
    "BeginAgeUnLoad",       // kfunc_OnBeginAgeLoad,
    "OnMovieEvent",         // kfunc_OnMovieEvent,
    "OnScreenCaptureDone",  // kfunc_OnScreenCaptureDone,
    "OnClimbingBlockerEvent",// kFunc_OnClimbingBlockerEvent,
    "OnAvatarSpawn",        // kFunc_OnAvatarSpawn
    "OnAccountUpdate",      // kFunc_OnAccountUpdate
    "gotPublicAgeList",     // kfunc_gotPublicAgeList
    "OnGameMgrMsg",         // kfunc_OnGameMgrMsg
    "OnGameCliMsg",         // kfunc_OnGameCliMsg
    "OnAIMsg",              // kfunc_OnAIMsg
    nil
};

//// Callback From the Vault Events //////////////////////////////////////////////
class PythonVaultCallback : public VaultCallback
{
protected:
    plPythonFileMod* fPyFileMod;
    int     fFunctionIdx;

public:
    PythonVaultCallback( plPythonFileMod *pymod, int fidx )
    {
        fPyFileMod = pymod;
        fFunctionIdx = fidx;
    }

    void AddedChildNode ( RelVaultNode * parentNode, RelVaultNode * childNode )
    {
        // is there an 'OnVaultEvent' defined?
        if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil )
        {
            PyObject* ptuple = PyTuple_New(1);
            PyTuple_SetItem(ptuple, 0, pyVaultNodeRef::New(parentNode, childNode));
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFileMod->fPyFunctionInstances[fFunctionIdx],
                    (char*)fPyFileMod->fFunctionNames[fFunctionIdx],
                    "lO",pyVault::kVaultNodeRefAdded,ptuple);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                fPyFileMod->ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(ptuple);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            fPyFileMod->DisplayPythonOutput();
        }
    }

    void RemovingChildNode ( RelVaultNode * parentNode, RelVaultNode * childNode )
    {
        // is there an 'OnVaultEvent' defined?
        if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil )
        {
            PyObject* ptuple = PyTuple_New(1);
            PyTuple_SetItem(ptuple, 0, pyVaultNodeRef::New(parentNode, childNode));
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFileMod->fPyFunctionInstances[fFunctionIdx],
                    (char*)fPyFileMod->fFunctionNames[fFunctionIdx],
                    "lO",pyVault::kVaultRemovingNodeRef,ptuple);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                fPyFileMod->ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(ptuple);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            fPyFileMod->DisplayPythonOutput();
        }
    }

    void ChangedNode ( RelVaultNode * changedNode )
    {
        // is there an 'OnVaultEvent' defined?
        if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil )
        {
            PyObject* ptuple = PyTuple_New(1);
            PyTuple_SetItem(ptuple, 0, pyVaultNode::New(changedNode));
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFileMod->fPyFunctionInstances[fFunctionIdx],
                    (char*)fPyFileMod->fFunctionNames[fFunctionIdx],
                    "lO",pyVault::kVaultNodeSaved,ptuple);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                fPyFileMod->ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(ptuple);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            fPyFileMod->DisplayPythonOutput();
        }
    }
};

/////////////////////////////////////////////////////////////////////////////
//
//  Class      : pfPythonKeyCatcher
//  PARAMETERS : none
//
//  PURPOSE    : Small wrapper class to catch discarded key events and pass
//               them to a plPythonFileMod
//

class pfPythonKeyCatcher : public plDefaultKeyCatcher
{
    plPythonFileMod *fMod;

    public:
        pfPythonKeyCatcher( plPythonFileMod *mod ) : fMod( mod ) {}
        
        virtual void    HandleKeyEvent( plKeyEventMsg *event )
        {
            fMod->HandleDiscardedKey( event );
        }
};

hsBool plPythonFileMod::fAtConvertTime = false;

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : plPythonFileMod and ~plPythonFileMod
//  PARAMETERS : none
//
//  PURPOSE    : Constructor and destructor
//
plPythonFileMod::plPythonFileMod()
{
    fPythonFile = nil;
    fModule = nil;
    fLocalNotify= true;
    fIsFirstTimeEval = true;
    fVaultCallback = nil;
    fSDLMod = nil;
    fSelfKey = nil;
    fInstance = nil;
    fKeyCatcher = nil;
    fPipe = nil;
    fAmIAttachedToClone = false;

    // assume that all the functions are not available
    // ...if the functions are defined in the module, then we'll call 'em
    int i;
    for (i=0 ; i<kfunc_lastone; i++)
        fPyFunctionInstances[i] = nil;
}

plPythonFileMod::~plPythonFileMod()
{
    if ( !fAtConvertTime )      // if this is just an Add that's during a convert, then don't do anymore
    {
        // remove our reference to the instance (but only if we made one)
        if(fInstance)
        {
            if ( fInstance->ob_refcnt > 1)
                Py_DECREF(fInstance);
            //  then have the glue delete the instance of class
            PyObject* delInst = PythonInterface::GetModuleItem("glue_delInst",fModule);
            if ( delInst!=nil && PyCallable_Check(delInst) )
            {
                PyObject* retVal = PyObject_CallFunction(delInst,nil);
                if ( retVal == nil )
                {
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                // display any output
                DisplayPythonOutput();
            }
        }
        fInstance = nil;
    }

    // If we have a key catcher, get rid of it
    delete fKeyCatcher;
    fKeyCatcher = nil;

    // if we created a Vault callback, undo it and get rid of it
    if (fVaultCallback)
    {
        // Set the callback for the vault thingy
        VaultUnregisterCallback(fVaultCallback);
        delete fVaultCallback;
        fVaultCallback = nil;
    }

    if (fSelfKey)
    {
        Py_DECREF(fSelfKey);
        fSelfKey = nil;
    }

    // get rid of the python code
    if ( fPythonFile )
    {
        delete [] fPythonFile;
        fPythonFile = nil;
    }
    // then get rid of this module
    //  NOTE: fModule shouldn't be made in the plugin, only at runtime
    if ( !fModuleName.IsNull() && fModule )
    {
        //_PyModule_Clear(fModule);
        PyObject *m;
        PyObject *modules = PyImport_GetModuleDict();
        if( modules && (m = PyDict_GetItemString(modules, fModuleName.c_str())) && PyModule_Check(m))
        {
            hsStatusMessageF("Module %s removed from python dictionary",fModuleName.c_str());
            PyDict_DelItemString(modules, fModuleName.c_str());
        }
        else
        {
            hsStatusMessageF("Module %s not found in python dictionary. Already removed?",fModuleName.c_str());
        }
        // the above code should have unloaded the module from python, so it will delete itself, therefore
        // we need to set our pointer to nil to make sure we don't try to use it
        fModule = nil;
    }
    fModuleName = plString::Null;
}

#include "plPythonPack.h"

bool plPythonFileMod::ILoadPythonCode()
{

#ifndef PLASMA_EXTERNAL_RELEASE
    // get code from file and execute in module
    // see if the file exists first before trying to import it
    char pathandfile[256];
    sprintf(pathandfile, ".\\python\\%s.py",fPythonFile);
    wchar_t *wPathandfile = hsStringToWString(pathandfile);
    hsBool exists = PathDoesFileExist(wPathandfile);
    delete [] wPathandfile;
    if (exists)
    {
        char fromLoad[256];
        //sprintf(fromLoad,"from %s import *", fPythonFile);
        // ok... we can't really use import because Python remembers too much where global variables came from
        // ...and using execfile make it sure that globals are defined in this module and not in the imported module
        sprintf(fromLoad,"execfile('.\\\\python\\\\%s.py')", fPythonFile);
        if ( PythonInterface::RunString( fromLoad, fModule) )
        {
            // we've loaded the code into our module
        // now attach the glue python code to the end
            if ( !PythonInterface::RunString("execfile('.\\\\python\\\\plasma\\\\glue.py')", fModule) )
            {
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
                return false;
            }
            else
                return true;
        }
        DisplayPythonOutput();
        char errMsg[256];
        sprintf(errMsg,"Python file %s.py had errors!!! Could not load.",fPythonFile);
        PythonInterface::WriteToLog(errMsg);
        hsAssert(0,errMsg);
        return false;
    }
#endif  //PLASMA_EXTERNAL_RELEASE

    // Finally, try and find the file in the Python packfile
    // ... for the external users .pak file is only used
    PyObject* pythonCode = PythonPack::OpenPythonPacked(fPythonFile);
    if (pythonCode && PythonInterface::RunPYC(pythonCode, fModule))
        return true;

    DisplayPythonOutput();
    char errMsg[256];
    sprintf(errMsg,"Python file %s.py was not found.",fPythonFile);
    PythonInterface::WriteToLog(errMsg);
    hsAssert(0,errMsg);
    return false;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddTarget
//  PARAMETERS : sobj  - object to add as our target
//
//  PURPOSE    : Get the Key of our target
//
// NOTE: This modifier wasn't intended to have multiple targets
//
void plPythonFileMod::AddTarget(plSceneObject* sobj)
{
    plMultiModifier::AddTarget(sobj);
    plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
    // initialize the python stuff
    if ( !fAtConvertTime )      // if this is just an Add that's during a convert, then don't do anymore
    {
        // was there a python file module with this?
        if ( fPythonFile )
        {
            // has the module not been initialized yet
            if ( !fModule )
            {
                plKey pkey = sobj->GetKey();
                // nope, must be the first object. Then use it as the basis for the module
                fModuleName = IMakeModuleName(sobj);
                fModule = PythonInterface::CreateModule(fModuleName.c_str());

                // if we can't create the instance then there is nothing to do here
                if (!ILoadPythonCode())
                {
                    // things are getting off on a bad foot... just say there wasn't a module...
                    fModule = nil;
                    return;
                }

            // set the name of the file (in the global dictionary of the module)
                PyObject* dict = PyModule_GetDict(fModule);
                PyObject* pfilename = PyString_FromString(fPythonFile);
                PyDict_SetItemString(dict, "glue_name", pfilename);
            // next we need to:
            //  - create instance of class
                PyObject* getInst = PythonInterface::GetModuleItem("glue_getInst",fModule);
                fInstance = nil;
                if ( getInst!=nil && PyCallable_Check(getInst) )
                {
                    fInstance = PyObject_CallFunction(getInst,nil);
                    if ( fInstance == nil )
                        // if there was an error make sure that the stderr gets flushed so it can be seen
                        ReportError();
                }
                // display any output
                DisplayPythonOutput();
                if ( fInstance == nil )     // then there was an error
                {
                    // display any output (NOTE: this would be disabled in production)
                    char errMsg[256];
                    sprintf(errMsg,"Python file %s.py, instance not found.",fPythonFile);
                    PythonInterface::WriteToLog(errMsg);
                    hsAssert(0, errMsg);
                    return;         // if we can't create the instance then there is nothing to do here
                }

                // Add the SDL modifier
                if (plPythonSDLModifier::HasSDL(fPythonFile))
                {
                    plSceneObject* sceneObj = plSceneObject::ConvertNoRef(GetTarget(0)->GetKey()->ObjectIsLoaded());
                    if (sceneObj)
                    {
                        hsAssert(!fSDLMod, "Python SDL modifier already created");
                        fSDLMod = new plPythonSDLModifier(this);
                        sceneObj->AddModifier(fSDLMod);
                    }
                }

                //  - set the self.key and self.sceneobject in the instance of the class
                // create the pyKey for this modifier
                fSelfKey = pyKey::New(GetKey(),this);
                // set the selfKey as an attribute to their instance
                PyObject_SetAttrString(fInstance, "key", fSelfKey);
                // create the sceneobject
                PyObject* pSobj = pySceneObject::New(pkey, fSelfKey);
                // set the sceneobject as an attribute to their instance
                PyObject_SetAttrString(fInstance, "sceneobject", pSobj);
                Py_DECREF(pSobj);
                // set the isInitialStateLoaded to not loaded... yet
                PyObject* pInitialState = PyInt_FromLong(0);
                PyObject_SetAttrString(fInstance, "isInitialStateLoaded", pInitialState);
                Py_DECREF(pInitialState);
                // Give the SDL mod to Python
                if (fSDLMod)
                {
                    PyObject* pSDL = pySDLModifier::New(fSDLMod);
                    PyObject_SetAttrString(fInstance, "SDL", pSDL);
                    Py_DECREF(pSDL);
                }

                //  - set the parameters
                PyObject* setParams = PythonInterface::GetModuleItem("glue_setParam",fModule);
                PyObject* check_isNamed = PythonInterface::GetModuleItem("glue_isNamedAttribute",fModule);
                if ( setParams!=nil && PyCallable_Check(setParams) )
                {
                    // loop throught the parameters and set them by id
                    // (will need to create the appropiate Python object for each type)
                    int nparam;
                    for ( nparam=0; nparam<GetParameterListCount() ; nparam++ )
                    {
                        plPythonParameter parameter = GetParameterItem(nparam);
                        // create the python object that matches the type
                        // (NOTE: this relies on function created above to help create Plasma python objects)
                        PyObject* value = nil;      // assume that there is no conversion available
                        int isNamedAttr = 0;
                        PyObject* retvalue;
                        switch (parameter.fValueType)
                        {
                            case plPythonParameter::kInt:
                                value = PyInt_FromLong(parameter.datarecord.fIntNumber);
                                break;
                            case plPythonParameter::kFloat:
                                value = PyFloat_FromDouble(parameter.datarecord.fFloatNumber);
                                break;
                            case plPythonParameter::kbool:
                                value = PyInt_FromLong(parameter.datarecord.fBool);
                                break;
                            case plPythonParameter::kString:
                            case plPythonParameter::kAnimationName:
                                isNamedAttr = 0;
                                if ( check_isNamed!=nil && PyCallable_Check(check_isNamed) )
                                {
                                    retvalue = PyObject_CallFunction(check_isNamed,"l", parameter.fID);
                                    if ( retvalue == nil )
                                    {
                                        ReportError();
                                        DisplayPythonOutput();
                                    }
                                    if ( retvalue && PyInt_Check(retvalue) )
                                        isNamedAttr = PyInt_AsLong(retvalue);
                                    Py_XDECREF(retvalue);
                                    // is it a NamedActivator
                                    if ( isNamedAttr == 1 || isNamedAttr == 2)
                                    {
                                        if (plAgeLoader::GetInstance()->IsLoadingAge())
                                        {
                                            NamedComponent comp;
                                            comp.isActivator = (isNamedAttr == 1);
                                            comp.id = parameter.fID;
                                            comp.name = parameter.fString;
                                            
                                            fNamedCompQueue.Append(comp);
                                        }
                                        else
                                        {
                                            if (isNamedAttr == 1)
                                                IFindActivatorAndAdd(parameter.fString, parameter.fID);
                                            else
                                                IFindResponderAndAdd(parameter.fString, parameter.fID);
                                        }
                                    }
                                }
                                // if it wasn't a named string then must be normal string type
                                if ( isNamedAttr == 0 )
                                    if ( !parameter.fString.IsNull() )
                                        value = PyString_FromString(parameter.fString.c_str());
                                break;
                            case plPythonParameter::kSceneObject:
                            case plPythonParameter::kSceneObjectList:
                                if ( parameter.fObjectKey != nil )
                                {
                                    // create the sceneobject
                                    value = pySceneObject::New(parameter.fObjectKey, fSelfKey);
                                }
                                break;
                            case plPythonParameter::kActivatorList:
                            case plPythonParameter::kResponderList:
                            case plPythonParameter::kDynamicText:
                            case plPythonParameter::kGUIDialog:
                            case plPythonParameter::kExcludeRegion:
                            case plPythonParameter::kAnimation:
                            case plPythonParameter::kBehavior:
                            case plPythonParameter::kMaterial:
                            case plPythonParameter::kGUIPopUpMenu:
                            case plPythonParameter::kGUISkin:
                            case plPythonParameter::kWaterComponent:
                            case plPythonParameter::kSwimCurrentInterface:
                            case plPythonParameter::kClusterComponentList:
                            case plPythonParameter::kMaterialAnimation:
                            case plPythonParameter::kGrassShaderComponent:
                                if ( parameter.fObjectKey != nil )
                                {
                                    // create pyKey for the object
                                    value = pyKey::New(parameter.fObjectKey);
                                }
                                break;
                        }
                        // if there is a value that was converted then tell the Python code
                        if ( value != nil )
                        {
                            PyObject* retVal = PyObject_CallFunction(setParams,"lO", parameter.fID, value);
                            if ( retVal == nil )
                            {
                                // if there was an error make sure that the stderr gets flushed so it can be seen
                                ReportError();
                            }
                            Py_XDECREF(retVal);
                            Py_DECREF(value);
                        }
                    }
                }

                // check if we need to register named activators or responders
                if (fNamedCompQueue.Count() > 0)
                {
                    plgDispatch::Dispatch()->RegisterForExactType( plAgeLoadedMsg::Index(), GetKey() );
                }

            //  - find functions in class they've defined.
                PythonInterface::CheckInstanceForFunctions(fInstance,(char**)fFunctionNames,fPyFunctionInstances);
                // clear any errors created by checking for methods in a class
                PyErr_Clear();      // clear the error
            // register for messages that they have functions defined for
                // register for PageLoaded message if needed
                if ( fPyFunctionInstances[kfunc_PageLoad] != nil )
                {
                    // register for plRoomLoadNotifyMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plRoomLoadNotifyMsg::Index(), GetKey());
                }

                // register for ClothingUpdate message if needed
                if ( fPyFunctionInstances[kfunc_ClothingUpdate] != nil )
                {
                    // register for plRoomLoadNotifyMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plClothingUpdateBCMsg::Index(), GetKey());
                }

                // register for pfKIMsg message if needed
                if ( fPyFunctionInstances[kfunc_KIMsg] != nil )
                {
                    // register for pfKIMsg
                    plgDispatch::Dispatch()->RegisterForExactType(pfKIMsg::Index(), GetKey());
                }

                // register for Member update message if needed
                if ( fPyFunctionInstances[kfunc_MemberUpdate] != nil )
                {
                    // register for plMemberUpdateMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plMemberUpdateMsg::Index(), GetKey());
                }

                // register for Remote Avatar Info message if needed
                if ( fPyFunctionInstances[kfunc_RemoteAvatarInfo] != nil )
                {
                    // register for plRemoteAvatarInfoMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plRemoteAvatarInfoMsg::Index(), GetKey());
                }

                // register for CCR message if needed
                if ( fPyFunctionInstances[kfunc_OnCCRMsg] != nil )
                {
                    // register for plCCRCommunicationMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plCCRCommunicationMsg::Index(), GetKey());
                }

                // register for VaultNotify message if needed
                if ( fPyFunctionInstances[kfunc_OnVaultNotify] != nil )
                {
                    // register for plVaultNotifyMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plVaultNotifyMsg::Index(), GetKey());
                }

                // register for Owndership change notification message if needed
                if ( fPyFunctionInstances[kfunc_OwnershipNotify] != nil )
                {
                    // register for plNetOwnershipMsg
                    plgDispatch::Dispatch()->RegisterForExactType(plNetOwnershipMsg::Index(), GetKey());
                }

#ifndef PLASMA_EXTERNAL_RELEASE
                // register for Backdoor message if needed
                if ( fPyFunctionInstances[kfunc_OnBackdoorMsg] != nil )
                {
                    // register for pfDebugTriggerMsg
                    plgDispatch::Dispatch()->RegisterForExactType(pfBackdoorMsg::Index(), GetKey());
                }
#endif  //PLASMA_EXTERNAL_RELEASE

                // register for VaultCallback events if needed
                if ( fPyFunctionInstances[kfunc_VaultEvent] != nil )
                {
                    // create the callback object
                    // Set the callback for the vault thingy
                    fVaultCallback = new PythonVaultCallback( this, kfunc_VaultEvent );
                    VaultRegisterCallback(fVaultCallback);
                }

                // register ourselves to be the default key catcher if necessary
                if ( fPyFunctionInstances[kfunc_OnDefaultKeyCaught] != nil )
                {
                    // Make us a key catcher
                    fKeyCatcher = new pfPythonKeyCatcher( this );

                    // Tell the input interface manager to use our catcher
                    plInputInterfaceMgr::GetInstance()->SetDefaultKeyCatcher( fKeyCatcher );
                }

                // register for Marker messages if needed
                if ( fPyFunctionInstances[kfunc_OnMarkerMsg] != nil )
                {
                    plgDispatch::Dispatch()->RegisterForExactType(pfMarkerMsg::Index(), GetKey());
                }

                // if they are going to get LOS hit messages then we need to get the Pipeline pointer
                if ( fPyFunctionInstances[kfunc_OnLOSNotify] != nil )
                {
                    plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() );
                }
                
                // if this is a climbing-wall function, we need to register for climbing wall messages
                if ( fPyFunctionInstances[kfunc_OnClimbBlockerEvent] != nil)
                {
                    plgDispatch::Dispatch()->RegisterForExactType( plClimbEventMsg::Index(), GetKey() );    
                }
                if ( fPyFunctionInstances[kfunc_OnAvatarSpawn] != nil)
                {
                    plgDispatch::Dispatch()->RegisterForExactType( plAvatarSpawnNotifyMsg::Index(), GetKey() ); 
                }
                if ( fPyFunctionInstances[kfunc_OnAccountUpdate] != nil)
                {
                    plgDispatch::Dispatch()->RegisterForExactType( plAccountUpdateMsg::Index(), GetKey() ); 
                }
                if ( fPyFunctionInstances[kfunc_gotPublicAgeList] != nil)
                {
                    plgDispatch::Dispatch()->RegisterForExactType(plNetCommPublicAgeListMsg::Index(), GetKey());
                }
                if ( fPyFunctionInstances[kfunc_OnGameMgrMsg] != nil)
                {
                    pfGameMgr::GetInstance()->AddReceiver(GetKey());
                }
                if ( fPyFunctionInstances[kfunc_OnAIMsg] != nil)
                {
                    // the message that is spammed to anyone who will listen
                    plgDispatch::Dispatch()->RegisterForExactType(plAIBrainCreatedMsg::Index(), GetKey());
                }

                // As the last thing... call the OnInit function if they have one
                if ( fPyFunctionInstances[kfunc_Init] != nil )
                {
                    plProfile_BeginTiming(PythonUpdate);
                    // call it
                    PyObject* retVal = PyObject_CallMethod(
                            fPyFunctionInstances[kfunc_Init],
                            (char*)fFunctionNames[kfunc_Init], nil);
                    if ( retVal == nil )
                    {
#ifndef PLASMA_EXTERNAL_RELEASE
                        // for some reason this function didn't, remember that and not call it again
                        fPyFunctionInstances[kfunc_Init] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                        // if there was an error make sure that the stderr gets flushed so it can be seen
                        ReportError();
                    }
                    Py_XDECREF(retVal);
                    plProfile_EndTiming(PythonUpdate);
                    // display any output (NOTE: this would be disabled in production)
                    DisplayPythonOutput();
                }

                // display python output
                DisplayPythonOutput();
            }
            else
            {
            // else if module is already created... Then we are just adding an addition object to the already existing SceneObject
                if ( fInstance )        // make sure that we have an instance already also
                {
                    PyObject* dict = PyModule_GetDict(fModule);
                    // create pyKey for the object
                    PyObject* pkeyObj = pyKey::New(sobj->GetKey());
                    // need to get the instance, that holds the sceneobject that we are attached to
                    PyObject* getInst = PythonInterface::GetModuleItem("glue_getInst",fModule);
                    // get the sceneObject that should already be created
                    PyObject* pSceneObject = PyObject_GetAttrString(fInstance,"sceneobject");
                    // add our new object to the list of objects that are in the _selfObject
                    PyObject* retVal = PyObject_CallMethod(pSceneObject,"addKey","O",pkeyObj );
                    Py_XDECREF(retVal);
                    // GetAttrString put a ref on pSceneObject, but we're done with it now.
                    Py_XDECREF(pSceneObject); 
                    Py_DECREF(pkeyObj);
                }
            }
        }
    }
}

void plPythonFileMod::RemoveTarget(plSceneObject* so)
{
    // remove sdl modifier
    if (fSDLMod)
    {
        if (GetNumTargets() > 0)
        {
            plSceneObject* sceneObj = plSceneObject::ConvertNoRef(GetTarget(0)->GetKey()->ObjectIsLoaded());
            if (sceneObj && fSDLMod)
                sceneObj->RemoveModifier(fSDLMod);
        }
        delete fSDLMod;
        fSDLMod = nil;
    }

    plMultiModifier::RemoveTarget(so);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : HandleDiscardedKey
//  PARAMETERS : msg - the key event message that was discarded
//
//  PURPOSE    : API for processing discarded keys as the deafult key catcher
//

void    plPythonFileMod::HandleDiscardedKey( plKeyEventMsg *msg )
{
    if (!fPyFunctionInstances[kfunc_OnDefaultKeyCaught])
        return;

    plProfile_BeginTiming( PythonUpdate );

    PyObject* retVal = PyObject_CallMethod(
                fPyFunctionInstances[ kfunc_OnDefaultKeyCaught ],
                (char*)fFunctionNames[ kfunc_OnDefaultKeyCaught ],
                "ciiiii",
                msg->GetKeyChar(), 
                (int)msg->GetKeyDown(),
                (int)msg->GetRepeat(),
                (int)msg->GetShiftKeyDown(),
                (int)msg->GetCtrlKeyDown(),
                (int)msg->GetKeyCode() );
    if( retVal == nil )
    {
#ifndef PLASMA_EXTERNAL_RELEASE
        // for some reason this function didn't, remember that and not call it again
        fPyFunctionInstances[ kfunc_OnDefaultKeyCaught ] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
        // if there was an error make sure that the stderr gets flushed so it can be seen
        ReportError();
    }
    Py_XDECREF(retVal);

    plProfile_EndTiming( PythonUpdate );
    // display any output (NOTE: this would be disabled in production)
    DisplayPythonOutput();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IMakeModuleName
//  PARAMETERS : sobj  - object to add as our target
//
//  PURPOSE    : Get the Key of our target
//
// NOTE: This modifier wasn't intended to have multiple targets
//
plString plPythonFileMod::IMakeModuleName(plSceneObject* sobj)
{
    // Forgive my general crapulance...
    // This strips underscores out of module names 
    // so python won't truncate them... -S

    plKey pKey = GetKey();
    plKey sKey = sobj->GetKey();

    const char* pKeyName = pKey->GetName().c_str();
    const char* pSobjName = sKey->GetName().c_str();

    uint16_t len = pKey->GetName().GetSize();
    uint16_t slen = sKey->GetName().GetSize();

    hsAssert(len+slen < 256, "Warning: String length exceeds 256 characters.");
    char modulename[256];
    
    int i, k = 0;
    for(i = 0; i < slen; i++)
    {
        if(pSobjName[i] == '_') continue;

        modulename[k++] = pSobjName[i];
    }
    for(i = 0; i < len; i++)
    {
        if(pKeyName[i] == '_') continue;

        modulename[k++] = pKeyName[i];
    }

    modulename[k] = '\0';
    plString name = plString::FromUtf8(modulename);

    // check to see if we are attaching to a clone?
    plKeyImp* pKeyImp = (plKeyImp*)(sKey);
    if (pKeyImp->GetCloneOwner())
    {
        // we have an owner... so we must be a clone.
        // add the cloneID to the end of the module name
        // and set the fIAmAClone flag
        uint32_t cloneID = pKeyImp->GetUoid().GetCloneID();
        name += plString::Format("%d", cloneID);
        fAmIAttachedToClone = true;
    }

    // make sure that the actual modulue will be uniqie
    if ( !PythonInterface::IsModuleNameUnique(modulename) )
    {
        // if not unique then add the sequence number to the end of the modulename
        uint32_t seqID = pKeyImp->GetUoid().GetLocation().GetSequenceNumber();
        name += plString::Format("%d", seqID);
    }

    return name;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ISetKeyValue
//  PARAMETERS : key to responder, parameter id
//
//  PURPOSE    : set the param in the python file
//             : so named stuff works
//
void plPythonFileMod::ISetKeyValue(const plKey& key, int32_t id)
{
    PyObject* setParams = PythonInterface::GetModuleItem("glue_setParam",fModule);
    
    if ( setParams != nil && PyCallable_Check(setParams) )
    {
        if ( key != nil )
        {
            // create pyKey for the object
            PyObject* value = pyKey::New(key);

            if ( value != nil )
            {
                PyObject* retVal = PyObject_CallFunction(setParams,"lO", id, value);
                if ( retVal == nil )
                {
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                Py_DECREF(value);
            }
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IFindResponderAndAdd
//  PARAMETERS : ResponderName  - name of the responder to find
//
//  PURPOSE    : find a responder by name in all age and page locations
//             : and add to the Parameter list
//
void plPythonFileMod::IFindResponderAndAdd(const plString &responderName, int32_t id)
{
    if ( !responderName.IsNull() )
    {
        std::vector<plKey> keylist;
        const plLocation &loc = GetKey()->GetUoid().GetLocation();
        plKeyFinder::Instance().ReallyStupidResponderSearch(responderName,keylist,loc); // use the really stupid search to find the responder
        // the keylist will be filled with all the keys that correspond to that responder
        int list_size = keylist.size();
        int i;
        for ( i=0 ; i<list_size; i++ )
        {
            plPythonParameter parm(id);
            parm.SetToResponder(keylist[i]);
            AddParameter(parm);
            ISetKeyValue(keylist[i], id);
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IFindActivatorAndAdd
//  PARAMETERS : ResponderName  - name of the responder to find
//
//  PURPOSE    : find a responder by name in all age and page locations
//             : and add to the Parameter list
//
void plPythonFileMod::IFindActivatorAndAdd(const plString &activatorName, int32_t id)
{
    if ( !activatorName.IsNull() )
    {
        std::vector<plKey> keylist;
        const plLocation &loc = GetKey()->GetUoid().GetLocation();
        plKeyFinder::Instance().ReallyStupidActivatorSearch(activatorName,keylist, loc); // use the really stupid search to find the responder
        // the keylist will be filled with all the keys that correspond to that responder
        int list_size = keylist.size();
        // create the Python object that is the list, starts as empty
        int i;
        for ( i=0 ; i<list_size; i++ )
        {
            plPythonParameter parm(id);
            parm.SetToActivator(keylist[i]);
            AddParameter(parm);
            ISetKeyValue(keylist[i], id);

            // need to add ourselves as a receiver to their list
            // first see if it is an logicMod, then add to their receiver list
            plLogicModifier *logic = plLogicModifier::ConvertNoRef(keylist[i]->ObjectIsLoaded());
            if (logic)
            {
                logic->AddNotifyReceiver(this->GetKey());
            }
            else  // else might be a python file key
            {
                // next check to see if it is another PythonFileMod, and add to their notify list
                plPythonFileMod *pymod = plPythonFileMod::ConvertNoRef(keylist[i]->ObjectIsLoaded());
                if (pymod)
                {
                    pymod->AddToNotifyList(this->GetKey());
                }
                else  // else maybe its just not loaded yet
                {
                    // setup a ref notify when it does get loaded
                    hsgResMgr::ResMgr()->AddViaNotify(keylist[i],
                                                    new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, kAddNotify, 0),
                                                    plRefFlags::kPassiveRef);
                }
            }
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IEval
//  PARAMETERS : secs
//               del
//               dirty
//
//  PURPOSE    : This is where the main update work is done
//    Tasks:
//      - Call the Python code's Update function (if there)
//
hsBool plPythonFileMod::IEval(double secs, float del, uint32_t dirty)
{
    if ( fModule )
    {
        // if this is the first time at the Eval, then run Python init
        if ( fIsFirstTimeEval )
        {
            fIsFirstTimeEval = false;       // no longer the first time
            // now run the __init__ function if there is one.
            // is the Update function defined and working (as far as we know)?
            if ( fPyFunctionInstances[kfunc_FirstUpdate] != nil )
            {
                plProfile_BeginTiming(PythonUpdate);
                // call it
                PyObject* retVal = PyObject_CallMethod(
                        fPyFunctionInstances[kfunc_FirstUpdate],
                        (char*)fFunctionNames[kfunc_FirstUpdate], nil);
                if ( retVal == nil )
                {
#ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_FirstUpdate] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                plProfile_EndTiming(PythonUpdate);
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
            }
        }

        // is the Update function defined and working (as far as we know)?
        if ( fPyFunctionInstances[kfunc_Update] != nil )
        {
            plProfile_BeginTiming(PythonUpdate);
            // call it
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_Update],
                    (char*)fFunctionNames[kfunc_Update],
                    "df", secs, del);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_Update] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
        }
    }
    return true;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : MsgReceive
//  PARAMETERS : msg   - the message that came to us.
//
//  PURPOSE    : Handle all the different types of messages that we recv
//
hsBool plPythonFileMod::MsgReceive(plMessage* msg)
{
    // is it a ref message
    plGenRefMsg* genRefMsg = plGenRefMsg::ConvertNoRef(msg);
    if (genRefMsg)
    {
        // is it a ref for a named activator that we need to add to notify?
        if ((genRefMsg->GetContext() & plRefMsg::kOnCreate) && genRefMsg->fWhich == kAddNotify)
        {
            // which kind of activator is this
            plLogicModifier *logic = plLogicModifier::ConvertNoRef(genRefMsg->GetRef());
            if (logic)
            {
                logic->AddNotifyReceiver(this->GetKey());
            }
            else  // else might be a python file key
            {
                // next check to see if it is another PythonFileMod, and add to their notify list
                plPythonFileMod *pymod = plPythonFileMod::ConvertNoRef(genRefMsg->GetRef());
                if (pymod)
                {
                    pymod->AddToNotifyList(this->GetKey());
                }
            }
        }
    }

    plAgeLoadedMsg* ageLoadedMsg = plAgeLoadedMsg::ConvertNoRef(msg);
    if (ageLoadedMsg && ageLoadedMsg->fLoaded)
    {
        for (int i = 0; i < fNamedCompQueue.Count(); ++i)
        {
            NamedComponent comp = fNamedCompQueue[i];
            if (comp.isActivator)
                IFindActivatorAndAdd(comp.name, comp.id);
            else
                IFindResponderAndAdd(comp.name, comp.id);
        }

        fNamedCompQueue.Reset();

        plgDispatch::Dispatch()->UnRegisterForExactType( plAgeLoadedMsg::Index(), GetKey() );
    }

    // if this is a render message, then we are just trying to get a pointer to the Pipeline
    plRenderMsg *rMsg = plRenderMsg::ConvertNoRef( msg );
    if( rMsg != nil )
    {
        fPipe = rMsg->Pipeline();
        plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() );
        return true;
    }

    // are they looking for an Notify message? should be coming from a proActivator
    if (fPyFunctionInstances[kfunc_Notify] != nil)
    {
        // yes, so was there actually a plActivateMsg?
        plNotifyMsg* pNtfyMsg = plNotifyMsg::ConvertNoRef(msg);
        if (pNtfyMsg)
        {
            // remember if this was a Local Broad cast or not
            fLocalNotify = (pNtfyMsg->HasBCastFlag(plMessage::kNetNonLocal)) ? false : true;

            // create a list for the event records
            PyObject* levents = PyList_New(0); // start with a list of no elements
            // loop thought the event records to get the data and transform into python objects
            int32_t num_records = pNtfyMsg->GetEventCount();
            int j;
            for ( j=0; j<num_records; j++ )
            {
                // get an event record
                proEventData* pED = pNtfyMsg->GetEventRecord(j);
                switch ( pED->fEventType )
                {

                    case proEventData::kCollision:
                        {
                            proCollisionEventData *eventData = (proCollisionEventData *)pED;
                            // get data from the collision
                            // create list
                            PyObject* event = PyList_New(4);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kCollision));
                            PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEnter ? 1 : 0));
                            PyList_SetItem(event, 2, pySceneObject::New(eventData->fHitter, fSelfKey));
                            PyList_SetItem(event, 3, pySceneObject::New(eventData->fHittee, fSelfKey));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;
                        
                    case proEventData::kSpawned:
                        {
                            proSpawnedEventData *eventData = (proSpawnedEventData *)pED;
                            PyObject* event = PyList_New(3);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kSpawned));
                            PyList_SetItem(event, 1, pySceneObject::New(eventData->fSpawner, fSelfKey));
                            PyList_SetItem(event, 2, pySceneObject::New(eventData->fSpawnee, fSelfKey));
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kPicked:
                        {
                            // get data from the picked event
                            proPickedEventData *eventData = (proPickedEventData *)pED;
                            // create list
                            PyObject* event = PyList_New(6);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kPicked));
                            PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEnabled ? 1 : 0));
                            PyList_SetItem(event, 2, pySceneObject::New(eventData->fPicker, fSelfKey));
                            PyList_SetItem(event, 3, pySceneObject::New(eventData->fPicked, fSelfKey));
                            PyList_SetItem(event, 4, pyPoint3::New(eventData->fHitPoint));

                            // make it in the local space
                            hsPoint3 tolocal(0,0,0);
                            if(eventData->fPicked)
                            {
                                plSceneObject* obj = plSceneObject::ConvertNoRef(eventData->fPicked->ObjectIsLoaded());
                                if ( obj )
                                {
                                    const plCoordinateInterface* ci = obj->GetCoordinateInterface();
                                    if ( ci )
                                        tolocal = (hsMatrix44)ci->GetWorldToLocal() * eventData->fHitPoint;
                                }
                            }
                            PyList_SetItem(event, 5, pyPoint3::New(tolocal));

                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kControlKey:
                        {
                            proControlKeyEventData *eventData = (proControlKeyEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(3);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kControlKey));
                            PyList_SetItem(event, 1, PyLong_FromLong(eventData->fControlKey));
                            PyList_SetItem(event, 2, PyInt_FromLong(eventData->fDown ? 1 : 0));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kVariable:
                        {
                            proVariableEventData *eventData = (proVariableEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(4);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kVariable));
                            PyList_SetItem(event, 1, PyString_FromString(eventData->fName));
                            PyList_SetItem(event, 2, PyLong_FromLong(eventData->fDataType));
                            
                            // depending on the data type create the data
                            switch ( eventData->fDataType )
                            {
                                case proEventData::kNumber:
                                    PyList_SetItem(event, 3, PyFloat_FromDouble(eventData->fNumber));
                                    break;
                                case proEventData::kKey:
                                    PyList_SetItem(event, 3, pyKey::New(eventData->fKey));
                                    break;
                            }
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kFacing:
                        {
                            proFacingEventData *eventData = (proFacingEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(5);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kFacing));
                            PyList_SetItem(event, 1, PyInt_FromLong(eventData->enabled ? 1 : 0));
                            PyList_SetItem(event, 2, pySceneObject::New(eventData->fFacer, fSelfKey));
                            PyList_SetItem(event, 3, pySceneObject::New(eventData->fFacee, fSelfKey));
                            PyList_SetItem(event, 4, PyFloat_FromDouble(eventData->dot));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kContained:
                        {
                            proContainedEventData *eventData = (proContainedEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(4);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kContained));
                            PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEntering ? 1 : 0));
                            PyList_SetItem(event, 2, pySceneObject::New(eventData->fContained, fSelfKey));
                            PyList_SetItem(event, 3, pySceneObject::New(eventData->fContainer, fSelfKey));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kActivate:
                        {
                            proActivateEventData *eventData = (proActivateEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(3);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kActivate));
                            PyList_SetItem(event, 1, PyInt_FromLong(eventData->fActive ? 1 : 0));
                            PyList_SetItem(event, 2, PyInt_FromLong(eventData->fActivate ? 1 : 0));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kCallback:
                        {
                            proCallbackEventData *eventData = (proCallbackEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(2);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kCallback));
                            PyList_SetItem(event, 1, PyLong_FromLong(eventData->fEventType));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kResponderState:
                        {
                            proResponderStateEventData *eventData = (proResponderStateEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(2);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kResponderState));
                            PyList_SetItem(event, 1, PyLong_FromLong(eventData->fState));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;

                    case proEventData::kMultiStage:
                        {
                            proMultiStageEventData *eventData = (proMultiStageEventData *)pED;
                            // create event list
                            PyObject* event = PyList_New(4);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kMultiStage));
                            PyList_SetItem(event, 1, PyLong_FromLong(eventData->fStage));
                            PyList_SetItem(event, 2, PyLong_FromLong(eventData->fEvent));
                            PyList_SetItem(event, 3, pySceneObject::New(eventData->fAvatar, fSelfKey));
                            // add this event record to the main event list (lists within a list)
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;
                    case proEventData::kOfferLinkingBook:
                        {
                            proOfferLinkingBookEventData* eventData = (proOfferLinkingBookEventData*)pED;
                            // create event list
                            PyObject* event = PyList_New(4);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kOfferLinkingBook));
                            PyList_SetItem(event, 1, pySceneObject::New(eventData->offerer, fSelfKey));
                            PyList_SetItem(event, 2, PyInt_FromLong(eventData->targetAge));
                            PyList_SetItem(event, 3, PyInt_FromLong(eventData->offeree));
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;
                    case proEventData::kBook:
                        {
                            proBookEventData* eventData = (proBookEventData*)pED;
                            // create event list
                            PyObject* event = PyList_New(3);
                            PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kBook));
                            PyList_SetItem(event, 1, PyLong_FromUnsignedLong(eventData->fEvent));
                            PyList_SetItem(event, 2, PyLong_FromUnsignedLong(eventData->fLinkID));
                            PyList_Append(levents, event);
                            Py_DECREF(event);
                        }
                        break;
                }
            }

            // Need to determine which of the Activators sent this plNotifyMsg
            // and set the ID appropriately
            int32_t id = -1;  // assume that none was found
            if ( pNtfyMsg->GetSender() != nil )
            {
                // loop throught the parameters and set them by id
                // (will need to create the appropiate Python object for each type)
                int npm;
                for ( npm=0; npm<GetParameterListCount() ; npm++ )
                {
                    plPythonParameter parameter = GetParameterItem(npm);
                    // is it something that could produce a plNotifiyMsg?
                    if ( parameter.fValueType == plPythonParameter::kActivatorList
                        || parameter.fValueType == plPythonParameter::kBehavior 
                        || parameter.fValueType == plPythonParameter::kResponderList )
                    {
                        // is there an actual ObjectKey to look at?
                        if (parameter.fObjectKey != nil )
                        {
                            // is it the same as the sender of the notify message?
                            if ( pNtfyMsg->GetSender()->GetUoid() == parameter.fObjectKey->GetUoid() )
                            {
                                // match! Then return that as the ID
                                id = parameter.fID;
                            }
                        }
                    }
                }
            }



            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_Notify],
                    (char*)fFunctionNames[kfunc_Notify],
                    "flO", pNtfyMsg->fState, id, levents);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_Notify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(levents);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for a key event message?
    if (fPyFunctionInstances[kfunc_OnKeyEvent] != nil)
    {
        // we are looking for collision messages, is it one?
        plControlEventMsg* pEMsg = plControlEventMsg::ConvertNoRef(msg);
        if (pEMsg)
        {
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnKeyEvent],
                    (char*)fFunctionNames[kfunc_OnKeyEvent],
                    "ll", pEMsg->GetControlCode(),
                    pEMsg->ControlActivated());
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnKeyEvent] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }

    }

    // are they looking for an Timer message?
    if (fPyFunctionInstances[kfunc_AtTimer])
    {
        // yes, so was there actually a plActivateMsg?
        plTimerCallbackMsg* pTimerMsg = plTimerCallbackMsg::ConvertNoRef(msg);
        if (pTimerMsg)
        {
            // yes...
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_AtTimer],
                    (char*)fFunctionNames[kfunc_AtTimer],
                    "l", pTimerMsg->fID);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_AtTimer] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for an GUINotify message?
    if (fPyFunctionInstances[kfunc_GUINotify])
    {
        // yes, so was there actually a plActivateMsg?
        pfGUINotifyMsg* pGUIMsg = pfGUINotifyMsg::ConvertNoRef(msg);
        if (pGUIMsg)
        {
            // yes...
            // call it ... but first create the control that started this mess
            // create the key
            PyObject* pyControl = nil;
            if ( pGUIMsg->GetControlKey() )     // make sure there is a control key
            {
                // now create the control... but first we need to find out what it is
                PyObject* pyCtrlKey = pyKey::New(pGUIMsg->GetControlKey());
                uint32_t control_type = pyGUIDialog::WhatControlType(*(pyKey::ConvertFrom(pyCtrlKey)));
                Py_DECREF(pyCtrlKey);

                switch (control_type)
                {
                    case pyGUIDialog::kDialog:
                        pyControl = pyGUIDialog::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kButton:
                        pyControl = pyGUIControlButton::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kListBox:
                        pyControl = pyGUIControlListBox::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kTextBox:
                        pyControl = pyGUIControlTextBox::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kEditBox:
                        pyControl = pyGUIControlEditBox::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kUpDownPair:
                    case pyGUIDialog::kKnob:
                        pyControl = pyGUIControlValue::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kCheckBox:
                        pyControl = pyGUIControlCheckBox::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kRadioGroup:
                        pyControl = pyGUIControlRadioGroup::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kDynamicText:
                        pyControl = pyGUIControlDynamicText::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kMultiLineEdit:
                        pyControl = pyGUIControlMultiLineEdit::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kPopUpMenu:
                        pyControl = pyGUIPopUpMenu::New(pGUIMsg->GetControlKey());
                        break;

                    case pyGUIDialog::kClickMap:
                        pyControl = pyGUIControlClickMap::New(pGUIMsg->GetControlKey());
                        break;

                    default:
                        // we don't know what it is... just send 'em the pyKey
                        pyControl = pyKey::New(pGUIMsg->GetControlKey());
                        break;

                }
            }
            // Need to determine which of the GUIDialogs sent this plGUINotifyMsg
            // and set the ID appropriately
            int32_t id = -1;  // assume that none was found
            if ( pGUIMsg->GetSender() != nil )
            {
                // loop throught the parameters and set them by id
                // (will need to create the appropiate Python object for each type)
                int npm;
                for ( npm=0; npm<GetParameterListCount() ; npm++ )
                {
                    plPythonParameter parameter = GetParameterItem(npm);
                    // is it something that could produce a plNotifiyMsg?
                    if ( parameter.fValueType == plPythonParameter::kGUIDialog || parameter.fValueType == plPythonParameter::kGUIPopUpMenu )
                    {
                        // is there an actual ObjectKey to look at?
                        if (parameter.fObjectKey != nil )
                        {
                            // is it the same of the sender of the notify message?
                            if ( pGUIMsg->GetSender()->GetUoid() == parameter.fObjectKey->GetUoid() )
                            {
                                // match! then set the ID to what the parameter is, so the python programmer can find it
                                id = parameter.fID;
                            }
                        }
                    }
                }
            }

            // make sure that we found a control to go with this
            if ( pyControl == nil )
            {
                // if none then return a Python None object
                Py_INCREF(Py_None);
                pyControl = Py_None;
            }

            // call their OnGUINotify method
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_GUINotify],
                    (char*)fFunctionNames[kfunc_GUINotify],
                    "lOl", id, pyControl, pGUIMsg->GetEvent());
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_GUINotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(pyControl);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for an RoomLoadNotify message?
    if (fPyFunctionInstances[kfunc_PageLoad])
    {
        // yes, so was there actually a plRoomLoadNotifyMsg?
        plRoomLoadNotifyMsg* pRLNMsg = plRoomLoadNotifyMsg::ConvertNoRef(msg);
        if (pRLNMsg)
        {
            // yes...
            // call it
            const char* roomname = "";
            if ( pRLNMsg->GetRoom() != nil )
                roomname = pRLNMsg->GetRoom()->GetName().c_str();

            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_PageLoad],
                    (char*)fFunctionNames[kfunc_PageLoad],
                    "ls", pRLNMsg->GetWhatHappen(), roomname);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_PageLoad] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }


    // are they looking for an ClothingUpdate message?
    if (fPyFunctionInstances[kfunc_ClothingUpdate])
    {
        // yes, so was there actually a plClothingUpdateBCMsg?
        plClothingUpdateBCMsg* pCUMsg = plClothingUpdateBCMsg::ConvertNoRef(msg);
        if (pCUMsg)
        {
            // yes...
            // call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_ClothingUpdate],
                    (char*)fFunctionNames[kfunc_ClothingUpdate], nil);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_ClothingUpdate] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for an KIMsg message?
    if (fPyFunctionInstances[kfunc_KIMsg])
    {
        // yes, so was there actually a pfKIMsg?
        pfKIMsg* pkimsg = pfKIMsg::ConvertNoRef(msg);
        if (pkimsg && pkimsg->GetCommand() != pfKIMsg::kHACKChatMsg)
        {
            // yes...
            // find the value that would go with a command
            PyObject* value;
            std::wstring str;
            switch (pkimsg->GetCommand())
            {
                case pfKIMsg::kSetChatFadeDelay:
                    value = PyFloat_FromDouble(pkimsg->GetDelay());
                    break;
                case pfKIMsg::kSetTextChatAdminMode:
                    value = PyLong_FromLong(pkimsg->GetFlags()&pfKIMsg::kAdminMsg ? 1 : 0 );
                    break;
                case pfKIMsg::kYesNoDialog:
                    value = PyTuple_New(2);
                    str = pkimsg->GetStringU();
                    PyTuple_SetItem(value, 0, PyUnicode_FromWideChar(str.c_str(), str.length()));
                    PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender()));
                    break;
                case pfKIMsg::kGZInRange:
                    value = PyTuple_New(2);
                    PyTuple_SetItem(value, 0, PyLong_FromLong(pkimsg->GetIntValue()));
                    PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender()));
                    break;
                case pfKIMsg::kRateIt:
                    value = PyTuple_New(3);
                    str = pkimsg->GetStringU();
                    PyTuple_SetItem(value,0,PyString_FromString(pkimsg->GetUser()));
                    PyTuple_SetItem(value,1,PyUnicode_FromWideChar(str.c_str(), str.length()));
                    PyTuple_SetItem(value,2,PyLong_FromLong(pkimsg->GetIntValue()));
                    break;
                case pfKIMsg::kRegisterImager:
                    value = PyTuple_New(2);
                    str = pkimsg->GetStringU();
                    PyTuple_SetItem(value, 0, PyUnicode_FromWideChar(str.c_str(), str.length()));
                    PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender()));
                    break;
                case pfKIMsg::kAddPlayerDevice:
                case pfKIMsg::kRemovePlayerDevice:
                    {
                        str = pkimsg->GetStringU();
                        if ( str.length() > 0 )
                            value = PyUnicode_FromWideChar(str.c_str(), str.length());
                        else
                        {
                            Py_INCREF(Py_None);
                            value = Py_None;
                        }
                    }
                    break;
                case pfKIMsg::kKIChatStatusMsg:
                case pfKIMsg::kKILocalChatStatusMsg:
                case pfKIMsg::kKILocalChatErrorMsg:
                case pfKIMsg::kKIOKDialog:
                case pfKIMsg::kKIOKDialogNoQuit:
                case pfKIMsg::kGZFlashUpdate:
                case pfKIMsg::kKICreateMarkerNode:
                    str = pkimsg->GetStringU();
                    value = PyUnicode_FromWideChar(str.c_str(), str.length());
                    break;
                case pfKIMsg::kMGStartCGZGame:
                case pfKIMsg::kMGStopCGZGame:
                case pfKIMsg::kFriendInviteSent:
                default:
                    value = PyLong_FromLong(pkimsg->GetIntValue());
                    break;
            }

            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_KIMsg],
                    (char*)fFunctionNames[kfunc_KIMsg],
                    "lO", pkimsg->GetCommand(), value);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_KIMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(value);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for an MemberUpdate message?
    if (fPyFunctionInstances[kfunc_MemberUpdate])
    {
        // yes, so was there actually a plMemberUpdateMsg?
        plMemberUpdateMsg* pmumsg = plMemberUpdateMsg::ConvertNoRef(msg);
        if (pmumsg)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_MemberUpdate],
                    (char*)fFunctionNames[kfunc_MemberUpdate], nil);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_MemberUpdate] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for a RemoteAvatar Info message?
    if (fPyFunctionInstances[kfunc_RemoteAvatarInfo])
    {
        // yes, so was there actually a plActivateMsg?
        plRemoteAvatarInfoMsg* pramsg = plRemoteAvatarInfoMsg::ConvertNoRef(msg);
        if (pramsg)
        {
            // yes...
            PyObject* player;
            // if there was no avatar key in the message
            if ( pramsg->GetAvatarKey() == nil )
            {
                // then just return a None... same thing as nil.. which I guess means a non-avatar is selected
                player = PyInt_FromLong(0);
            }
            else
            {
                // try to create the pyPlayer for where this message came from
                int mbrIndex = plNetClientMgr::GetInstance()->TransportMgr().FindMember(pramsg->GetAvatarKey());
                if ( mbrIndex != -1 )
                {
                    plNetTransportMember *mbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember( mbrIndex );
                    player = pyPlayer::New(mbr->GetAvatarKey(), mbr->GetPlayerName().c_str(), mbr->GetPlayerID(), mbr->GetDistSq());
                }
                else
                {
                    // else if we could not find the player in our list, then no avatar selected
                    player = PyInt_FromLong(0);
                }
            }

            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_RemoteAvatarInfo],
                    (char*)fFunctionNames[kfunc_RemoteAvatarInfo],
                    "O", player);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_RemoteAvatarInfo] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(player);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }


    // are they looking for a CCR communication message?
    if (fPyFunctionInstances[kfunc_OnCCRMsg])
    {
        // yes, so was there actually a plActivateMsg?
        plCCRCommunicationMsg* ccrmsg = plCCRCommunicationMsg::ConvertNoRef(msg);
        if (ccrmsg)
        {
            const char* textmessage = ccrmsg->GetMessage();
            if ( textmessage == nil)
                textmessage = "";
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnCCRMsg],
                    (char*)fFunctionNames[kfunc_OnCCRMsg],
                    "lsl", ccrmsg->GetType(), textmessage,
                    ccrmsg->GetCCRPlayerID());
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnCCRMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }


    // are they looking for a VaultNotify message?
    if (fPyFunctionInstances[kfunc_OnVaultNotify])
    {
        // yes, so was there actually a plVaultNotifyMsg?
        if (plVaultNotifyMsg * vaultNotifyMsg = plVaultNotifyMsg::ConvertNoRef(msg))
        {
            if ( hsSucceeded( vaultNotifyMsg->GetResultCode() ) )
            {
                // Create a tuple for second argument according to msg type.
                // Default to an empty tuple.
                PyObject* ptuple = PyTuple_New(0);
                switch ( vaultNotifyMsg->GetType() )
                {
                    case plVaultNotifyMsg::kRegisteredOwnedAge:
                    case plVaultNotifyMsg::kRegisteredVisitAge:
                    case plVaultNotifyMsg::kUnRegisteredOwnedAge:
                    case plVaultNotifyMsg::kUnRegisteredVisitAge: {
                        if (RelVaultNode * rvn = VaultGetNodeIncRef(vaultNotifyMsg->GetArgs()->GetInt(plNetCommon::VaultTaskArgs::kAgeLinkNode))) {
                            Py_DECREF(ptuple);
                            ptuple = PyTuple_New(1);
                            PyTuple_SetItem(ptuple, 0, pyVaultAgeLinkNode::New(rvn));
                            rvn->DecRef();
                        }
                    }
                    break;
                    
                    case plVaultNotifyMsg::kPublicAgeCreated:
                    case plVaultNotifyMsg::kPublicAgeRemoved: {
                        if (const char * ageName = vaultNotifyMsg->GetArgs()->GetString(plNetCommon::VaultTaskArgs::kAgeFilename)) {
                            Py_DECREF(ptuple);
                            ptuple = PyTuple_New(1);
                            PyTuple_SetItem(ptuple, 0, PyString_FromString(ageName));
                        }
                    }
                    break;
                }

                plProfile_BeginTiming(PythonUpdate);
                PyObject* retVal = PyObject_CallMethod(
                        fPyFunctionInstances[kfunc_OnVaultNotify],
                        (char*)fFunctionNames[kfunc_OnVaultNotify],
                        "lO", vaultNotifyMsg->GetType(), ptuple);
                if ( retVal == nil )
                {
#ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_OnVaultNotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                Py_DECREF(ptuple);
                plProfile_EndTiming(PythonUpdate);
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
                // we handled this message (I think)
            }
            return true;
        }
    }

    
    // are they looking for a RealTimeChat message?
    if (fPyFunctionInstances[kfunc_RTChat])
    {
        // yes, so was there actually a pfKIMsg?
        pfKIMsg* pkimsg = pfKIMsg::ConvertNoRef(msg);
        if (pkimsg && pkimsg->GetCommand() == pfKIMsg::kHACKChatMsg)
        {
            // yes...
            // filter ignored player
            if ( !VaultAmIgnoringPlayer( pkimsg->GetPlayerID() ) )
            {
                // create the pyPlayer for where this message came from
                PyObject* player;
                PyObject* ptPlayerClass = PythonInterface::GetPlasmaItem("ptPlayer");
                hsAssert(ptPlayerClass,"Could not create a ptPlayer");
                int mbrIndex = plNetClientMgr::GetInstance()->TransportMgr().FindMember(pkimsg->GetPlayerID());
                if ( mbrIndex != -1 )
                {
                    plNetTransportMember *mbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember( mbrIndex );
                    player = pyPlayer::New(mbr->GetAvatarKey(), pkimsg->GetUser(), mbr->GetPlayerID(), mbr->GetDistSq());
                }
                else
                {
                    // else if we could not find the player in our list, then just return a string of the user's name
                    const char * fromName = pkimsg->GetUser();
                    if (!fromName)
                        fromName = "Anonymous Coward";
                    player = pyPlayer::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), fromName, pkimsg->GetPlayerID(), 0.0);
                }

                plProfile_BeginTiming(PythonUpdate);
                PyObject* retVal = PyObject_CallMethod(
                        fPyFunctionInstances[kfunc_RTChat],
                        (char*)fFunctionNames[kfunc_RTChat],
                        "Osl", player, pkimsg->GetString().c_str(),
                        pkimsg->GetFlags());
                if ( retVal == nil )
                {
#ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_RTChat] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                Py_DECREF(player);
                plProfile_EndTiming(PythonUpdate);
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
                // we handled this message (I think)
                return true;
            }
        }
    }
    if (plPlayerPageMsg::ConvertNoRef(msg))
    {
        if (fPyFunctionInstances[kfunc_AvatarPage])
        {
            // yes, so was there actually a player page msg
            plPlayerPageMsg* ppMsg = plPlayerPageMsg::ConvertNoRef(msg);
            if (ppMsg)
            {
                PyObject* pSobj = pySceneObject::New(ppMsg->fPlayer, fSelfKey);
                plProfile_BeginTiming(PythonUpdate);
                plSynchEnabler ps(true);    // enable dirty state tracking during shutdown  
    
                PyObject* retVal = PyObject_CallMethod(
                        fPyFunctionInstances[kfunc_AvatarPage],
                        (char*)fFunctionNames[kfunc_AvatarPage],
                        "Oli", pSobj, !ppMsg->fUnload, ppMsg->fLastOut);
                if ( retVal == nil )
                {
    #ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_AvatarPage] = nil;
    #endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                Py_DECREF(pSobj);
                plProfile_EndTiming(PythonUpdate);
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
                // we handled this message (I think)
                return true;
            }
        }
    }
    if (plAgeBeginLoadingMsg::ConvertNoRef(msg))
    {
        if (fPyFunctionInstances[kfunc_OnBeginAgeLoad])
        {
            // yes, so was there actually a player page msg
            plAgeBeginLoadingMsg* ppMsg = plAgeBeginLoadingMsg::ConvertNoRef(msg);
            if (ppMsg)
            {
                PyObject* pSobj = pySceneObject::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), fSelfKey);
                plProfile_BeginTiming(PythonUpdate);
                plSynchEnabler ps(true);    // enable dirty state tracking during shutdown  
    
                PyObject* retVal = PyObject_CallMethod(
                        fPyFunctionInstances[kfunc_OnBeginAgeLoad],
                        (char*)fFunctionNames[kfunc_OnBeginAgeLoad],
                        "O", pSobj);
                if ( retVal == nil )
                {
    #ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_OnBeginAgeLoad] = nil;
    #endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
                }
                Py_XDECREF(retVal);
                Py_DECREF(pSobj);
                plProfile_EndTiming(PythonUpdate);
                // display any output (NOTE: this would be disabled in production)
                DisplayPythonOutput();
                // we handled this message (I think)
                return true;
            }
        }
    }
    if (plInitialAgeStateLoadedMsg::ConvertNoRef(msg))// initial server update complete message
    {
        // make sure there is a valid python instance
        if ( fInstance )
        {
            // set the isInitialStateLoaded to that it is loaded
            PyObject* pInitialState = PyInt_FromLong(1);
            PyObject_SetAttrString(fInstance, "isInitialStateLoaded", pInitialState);
            Py_DECREF(pInitialState);
        }
        if (fPyFunctionInstances[kfunc_OnServerInitComplete])
        {
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnServerInitComplete],
                    (char*)fFunctionNames[kfunc_OnServerInitComplete],
                    nil);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                    // for some reason this function didn't, remember that and not call it again
                    fPyFunctionInstances[kfunc_OnServerInitComplete] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                    // if there was an error make sure that the stderr gets flushed so it can be seen
                    ReportError();
            }
            Py_XDECREF(retVal);

            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }
    // are they looking for an plSDLNotificationMsg message?
    if (fPyFunctionInstances[kfunc_SDLNotify])
    {
        // yes, so was there actually a plSDLNotificationMsg?
        plSDLNotificationMsg* sn = plSDLNotificationMsg::ConvertNoRef(msg);
        if (sn)
        {
            const char* tag = sn->fHintString.c_str();
            if (tag == nil)
                tag = "";
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_SDLNotify],
                    (char*)fFunctionNames[kfunc_SDLNotify],
                    "ssls", sn->fVar->GetName(), sn->fSDLName.c_str(),
                    sn->fPlayerID, tag);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_SDLNotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }
    // are they looking for an plNetOwnershipMsg message?
    if (fPyFunctionInstances[kfunc_OwnershipNotify])
    {
        // yes, so was there actually a plNetOwnershipMsg?
        plNetOwnershipMsg* nom = plNetOwnershipMsg::ConvertNoRef(msg);
        if (nom)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OwnershipNotify],
                    (char*)fFunctionNames[kfunc_OwnershipNotify],
                    nil);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OwnershipNotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }
    // are they looking for an pfMarkerMsg message?
    if (fPyFunctionInstances[kfunc_OnMarkerMsg])
    {
        pfMarkerMsg* markermsg = pfMarkerMsg::ConvertNoRef(msg);
        if (markermsg)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            // Default to an empty tuple.
            PyObject* ptuple = PyTuple_New(0);
            switch ( markermsg->fType )
            {
                case pfMarkerMsg::kMarkerCaptured:
                    // Sent when we collide with a marker
                    Py_DECREF(ptuple);
                    ptuple = PyTuple_New(1);
                    PyTuple_SetItem(ptuple, 0, PyLong_FromUnsignedLong((long)markermsg->fMarkerID));
                    break;
            }

            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnMarkerMsg],
                    (char*)fFunctionNames[kfunc_OnMarkerMsg],
                    "lO", (uint32_t)markermsg->fType, ptuple);
            if (retVal == nil)
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnMarkerMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(ptuple);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

#ifndef PLASMA_EXTERNAL_RELEASE
    // are they looking for an pfDebugTriggerMsg message?
    if (fPyFunctionInstances[kfunc_OnBackdoorMsg])
    {
        // yes, so was there actually a plNetOwnershipMsg?
        pfBackdoorMsg* dt = pfBackdoorMsg::ConvertNoRef(msg);
        if (dt)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnBackdoorMsg],
                    (char*)fFunctionNames[kfunc_OnBackdoorMsg],
                    "ss", dt->GetTarget(), dt->GetString());
            if ( retVal == nil )
            {
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }
#endif  //PLASMA_EXTERNAL_RELEASE

    // are they looking for a plLOSHitMsg message?
    if (fPyFunctionInstances[kfunc_OnLOSNotify])
    {
        // yes, so was there actually a plLOSHitMsg?
        plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg );
        if (pLOSMsg)
        {
            // yes... then call it (self,ID,noHitFlag,sceneobject,hitPoint,distance)
            plProfile_BeginTiming(PythonUpdate);
            PyObject* scobj;
            PyObject* hitpoint;
            if ( pLOSMsg->fObj && plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded()) )
            {
                scobj = pySceneObject::New(pLOSMsg->fObj);
                hitpoint = pyPoint3::New(pLOSMsg->fHitPoint);
            }
            else
            {
                // otherwise return a None object for the avatarKey
                Py_INCREF(Py_None);
                scobj = Py_None;
                Py_INCREF(Py_None);
                hitpoint = Py_None;
            }
                    
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnLOSNotify],
                    (char*)fFunctionNames[kfunc_OnLOSNotify],
                    "llOOf", pLOSMsg->fRequestID, pLOSMsg->fNoHit,
                    scobj, hitpoint, pLOSMsg->fDistance);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnLOSNotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(scobj);
            Py_DECREF(hitpoint);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for a plAvatarBehaviorNotifyMsg message?
    if (fPyFunctionInstances[kfunc_OnBehaviorNotify])
    {
        // yes, so was there actually a plAvatarBehaviorNotifyMsg?
        plAvatarBehaviorNotifyMsg *behNotifymsg = plAvatarBehaviorNotifyMsg::ConvertNoRef(msg);
        if (behNotifymsg)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            // the parent of the sender should be the avatar that did the behavior
            PyObject* pSobj;
                
            plModifier* avmod = plModifier::ConvertNoRef(behNotifymsg->GetSender()->ObjectIsLoaded());
            if ( avmod && avmod->GetNumTargets() > 0 )
            {
                pSobj = pySceneObject::New(avmod->GetTarget(0)->GetKey(), fSelfKey);
            }
            else
            {
                // otherwise return a None object for the avatarKey
                Py_INCREF(Py_None);
                pSobj = Py_None;
            }
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnBehaviorNotify],
                    (char*)fFunctionNames[kfunc_OnBehaviorNotify],
                    "lOl", behNotifymsg->fType, pSobj,
                    behNotifymsg->state);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnBehaviorNotify] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(pSobj);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for a pfMovieEventMsg message?
    if (fPyFunctionInstances[kfunc_OnMovieEvent])
    {
        // yes, so was there actually a pfMovieEventMsg?
        pfMovieEventMsg *moviemsg = pfMovieEventMsg::ConvertNoRef(msg);
        if (moviemsg)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnMovieEvent],
                    (char*)fFunctionNames[kfunc_OnMovieEvent],
                    "si", moviemsg->fMovieName, (uint32_t)moviemsg->fReason);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnMovieEvent] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    // are they looking for a plCaptureRenderMsg message?
    if (fPyFunctionInstances[kfunc_OnScreenCaptureDone])
    {
        // yes, so was there actually a pfMovieEventMsg?
        plCaptureRenderMsg *capturemsg = plCaptureRenderMsg::ConvertNoRef(msg);
        if (capturemsg)
        {
            // yes... then call it
            plProfile_BeginTiming(PythonUpdate);
            PyObject* pSobj;
                
            if ( capturemsg->GetMipmap() )
            {
                pSobj = pyImage::New(capturemsg->GetMipmap());
            }
            else
            {
                // otherwise return a None object for the avatarKey
                Py_INCREF(Py_None);
                pSobj = Py_None;
            }
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnScreenCaptureDone],
                    (char*)fFunctionNames[kfunc_OnScreenCaptureDone],
                    "O", pSobj);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnScreenCaptureDone] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(pSobj);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();
            // we handled this message (I think)
            return true;
        }
    }

    if (fPyFunctionInstances[kfunc_OnClimbBlockerEvent])
    {
        plClimbEventMsg* pEvent = plClimbEventMsg::ConvertNoRef(msg);
        if (pEvent)
        {
            PyObject* pSobj = pySceneObject::New(pEvent->GetSender(), fSelfKey);
            
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnClimbBlockerEvent],
                    (char*)fFunctionNames[kfunc_OnClimbBlockerEvent],
                    "O", pSobj);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnClimbBlockerEvent] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            Py_DECREF(pSobj);
            return true;
        }
    }
    if (fPyFunctionInstances[kfunc_OnAvatarSpawn])
    {
        plAvatarSpawnNotifyMsg* pSpawn = plAvatarSpawnNotifyMsg::ConvertNoRef(msg);
        if (pSpawn)
        {
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnAvatarSpawn],
                    (char*)fFunctionNames[kfunc_OnAvatarSpawn],
                    "l", 1);
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnAvatarSpawn] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            return true;
        }
    }
    
    if (fPyFunctionInstances[kfunc_OnAccountUpdate])
    {
        plAccountUpdateMsg* pUpdateMsg = plAccountUpdateMsg::ConvertNoRef(msg);
        if (pUpdateMsg)
        {
            plProfile_BeginTiming(PythonUpdate);
            PyObject* retVal = PyObject_CallMethod(
                    fPyFunctionInstances[kfunc_OnAccountUpdate],
                    (char*)fFunctionNames[kfunc_OnAccountUpdate],
                    "iii", (int)pUpdateMsg->GetUpdateType(), 
                    (int)pUpdateMsg->GetResult(),
                    (int)pUpdateMsg->GetPlayerInt()
            );
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnAccountUpdate] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();

            return true;
        }
    }

    if (fPyFunctionInstances[kfunc_gotPublicAgeList])
    {
        plNetCommPublicAgeListMsg * pPubAgeMsg = plNetCommPublicAgeListMsg::ConvertNoRef(msg);
        if (pPubAgeMsg)
        {
            plProfile_BeginTiming(PythonUpdate);
            PyObject* pyEL = PyList_New(pPubAgeMsg->ages.Count());
            for (unsigned i = 0; i<pPubAgeMsg->ages.Count(); ++i) {
                plAgeInfoStruct ageInfo;
                ageInfo.CopyFrom(pPubAgeMsg->ages[i]);
                unsigned nPlayers = pPubAgeMsg->ages[i].currentPopulation;
                unsigned nOwners = pPubAgeMsg->ages[i].population;
                
                PyObject* t = PyTuple_New(3);
                PyTuple_SetItem(t, 0, pyAgeInfoStruct::New(&ageInfo));
                PyTuple_SetItem(t, 1, PyLong_FromUnsignedLong(nPlayers));
                PyTuple_SetItem(t, 2, PyLong_FromUnsignedLong(nOwners));
                PyList_SetItem(pyEL, i, t); // steals the ref
            }
            
            PyObject* retVal = PyObject_CallMethod(
                fPyFunctionInstances[kfunc_gotPublicAgeList],
                (char*)fFunctionNames[kfunc_gotPublicAgeList],
                "O",
                pyEL
            );
            if ( retVal == nil )
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_gotPublicAgeList] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output (NOTE: this would be disabled in production)
            DisplayPythonOutput();

            return true;
        }
    }

    if (fPyFunctionInstances[kfunc_OnGameMgrMsg])
    {
        pfGameMgrMsg* gameMgrMsg = pfGameMgrMsg::ConvertNoRef(msg);
        if (gameMgrMsg)
        {
            plProfile_BeginTiming(PythonUpdate);
            PyObject* pythonMsg = pyGameMgrMsg::New(gameMgrMsg);
            PyObject* retVal = PyObject_CallMethod(
                fPyFunctionInstances[kfunc_OnGameMgrMsg],
                (char*)fFunctionNames[kfunc_OnGameMgrMsg],
                "O",
                pythonMsg
            );
            Py_DECREF(pythonMsg);
            if (retVal == nil)
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnGameMgrMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output
            DisplayPythonOutput();

            return true;
        }
    }

    if (fPyFunctionInstances[kfunc_OnGameCliMsg])
    {
        pfGameCliMsg* gameMgrMsg = pfGameCliMsg::ConvertNoRef(msg);
        if (gameMgrMsg)
        {
            plProfile_BeginTiming(PythonUpdate);
            PyObject* pythonMsg = pyGameCliMsg::New(gameMgrMsg);
            PyObject* retVal = PyObject_CallMethod(
                fPyFunctionInstances[kfunc_OnGameCliMsg],
                (char*)fFunctionNames[kfunc_OnGameCliMsg],
                "O",
                pythonMsg
            );
            Py_DECREF(pythonMsg);
            if (retVal == nil)
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnGameCliMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);
            plProfile_EndTiming(PythonUpdate);
            // display any output
            DisplayPythonOutput();

            return true;
        }
    }

    if (fPyFunctionInstances[kfunc_OnAIMsg])
    {
        plAIMsg* aiMsg = plAIMsg::ConvertNoRef(msg);
        if (aiMsg)
        {
            plProfile_BeginTiming(PythonUpdate);

            // grab the sender (the armature mod that has our brain)
            plArmatureMod* armMod = plArmatureMod::ConvertNoRef(aiMsg->GetSender()->ObjectIsLoaded());
            PyObject* brainObj = NULL;
            if (armMod)
            {
                plArmatureBrain* brain = armMod->FindBrainByClass(plAvBrainCritter::Index());
                plAvBrainCritter* critterBrain = plAvBrainCritter::ConvertNoRef(brain);
                if (critterBrain)
                    brainObj = pyCritterBrain::New(critterBrain);
            }
            if (!brainObj)
            {
                Py_INCREF(Py_None);
                brainObj = Py_None;
            }

            // set up the msg type and any args, based on the message we got
            int msgType = plAIMsg::kAIMsg_Unknown;
            PyObject* args = NULL;
            plAIBrainCreatedMsg* brainCreatedMsg = plAIBrainCreatedMsg::ConvertNoRef(aiMsg);
            if (brainCreatedMsg)
                msgType = plAIMsg::kAIMsg_BrainCreated;

            plAIArrivedAtGoalMsg* arrivedMsg = plAIArrivedAtGoalMsg::ConvertNoRef(aiMsg);
            if (arrivedMsg)
            {
                msgType = plAIMsg::kAIMsg_ArrivedAtGoal;
                args = PyTuple_New(1);
                PyTuple_SetItem(args, 0, pyPoint3::New(arrivedMsg->Goal()));
            }

            // if no args were set, simply set to none
            if (!args)
            {
                Py_INCREF(Py_None);
                args = Py_None;
            }

            // call the function with the above arguments
            PyObject* retVal = PyObject_CallMethod(
                fPyFunctionInstances[kfunc_OnAIMsg],
                (char*)fFunctionNames[kfunc_OnAIMsg],
                "OisO",
                brainObj, msgType, aiMsg->BrainUserString().c_str(), args
            );
            Py_DECREF(brainObj);
            Py_DECREF(args);
            if (retVal == nil)
            {
#ifndef PLASMA_EXTERNAL_RELEASE
                // for some reason this function didn't, remember that and not call it again
                fPyFunctionInstances[kfunc_OnAIMsg] = nil;
#endif  //PLASMA_EXTERNAL_RELEASE
                // if there was an error make sure that the stderr gets flushed so it can be seen
                ReportError();
            }
            Py_XDECREF(retVal);

            plProfile_EndTiming(PythonUpdate);
            // display any output
            DisplayPythonOutput();

            return true;
        }
    }

    return plModifier::MsgReceive(msg);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ReportError
//  PARAMETERS : 
//
//  PURPOSE    : Report error to somewhere
//
void plPythonFileMod::ReportError()
{
    plString objectName = this->GetKeyName();
    objectName += _TEMP_CONVERT_FROM_LITERAL(" - ");

    PythonInterface::WriteToStdErr(objectName.c_str());

    PyErr_Print();      // make sure the error is printed
    PyErr_Clear();      // clear the error
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DisplayPythonOutput
//  PARAMETERS : 
//
//  PURPOSE    : display any Python stdout or stderr to file and to screen(later)
//
void plPythonFileMod::DisplayPythonOutput()
{
    // get the messages
    PythonInterface::getOutputAndReset();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : SetSourceFile
//  PARAMETERS : code      - text source code
//             : filename  - where the source code came from (just say the object name)
//
//  PURPOSE    : Sets the source code for this modifier.
//             : Compile it into a Python code object
//             : (This is usually called by the component)
//
void plPythonFileMod::SetSourceFile(const char* filename)
{
    delete [] fPythonFile;
    fPythonFile = hsStrcpy(filename);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : getPythonOutput
//  PARAMETERS : none
//
//  PURPOSE    : get the Output to the error file to be displayed
//
int  plPythonFileMod::getPythonOutput(std::string* line)
{
     return PythonInterface::getOutputAndReset(line);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : EnableControlKeys
//  PARAMETERS : none
//
//  PURPOSE    : get the Output to the error file to be displayed
//
void plPythonFileMod::EnableControlKeyEvents()
{
    // register for keyboard events if needed
    if ( fPyFunctionInstances[kfunc_OnKeyEvent] != nil )
    {
        // register for key events
        plCmdIfaceModMsg* pModMsg = new plCmdIfaceModMsg;
        pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
        pModMsg->SetSender(GetKey());
        pModMsg->SetCmd(plCmdIfaceModMsg::kAdd);
        plgDispatch::MsgSend(pModMsg);
    }
}

    
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DisableControlKeys
//  PARAMETERS : none
//
//  PURPOSE    : get the Output to the error file to be displayed
//
void plPythonFileMod::DisableControlKeyEvents()
{
    // unregister for key events
    plCmdIfaceModMsg* pModMsg = new plCmdIfaceModMsg;
    pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
    pModMsg->SetSender(GetKey());
    pModMsg->SetCmd(plCmdIfaceModMsg::kRemove);
    plgDispatch::MsgSend(pModMsg);
}

void plPythonFileMod::Read(hsStream* stream, hsResMgr* mgr)
{
    plMultiModifier::Read(stream, mgr);

    // read in the compile python code (pyc)
    if ( fPythonFile )
    {
        // if we already have some code, get rid of it!
        delete [] fPythonFile;
        fPythonFile = nil;
    }
    fPythonFile = stream->ReadSafeString();

    // then read in the list of receivers that want to be notified
    int nRcvs = stream->ReadLE32();
    fReceivers.Reset();
    int m;
    for( m=0; m<nRcvs; m++ )
    {   
        fReceivers.Append(mgr->ReadKey(stream));
    }

    // then read in the list of parameters
    int nParms = stream->ReadLE32();
    fParameters.SetCountAndZero(nParms);
    int i;
    for( i=0; i<nParms; i++ )
    {
        plPythonParameter parm;
        parm.Read(stream,mgr);
        fParameters[i] = parm;
    }   
}

void plPythonFileMod::Write(hsStream* stream, hsResMgr* mgr)
{
    plMultiModifier::Write(stream, mgr);

    stream->WriteSafeString(fPythonFile);

    // then write out the list of receivers that want to be notified
    stream->WriteLE32(fReceivers.GetCount());
    int m;
    for( m=0; m<fReceivers.GetCount(); m++ )
        mgr->WriteKey(stream, fReceivers[m]);

    // then write out the list of parameters
    stream->WriteLE32(fParameters.GetCount());
    int i;
    for( i=0; i<fParameters.GetCount(); i++ )
        fParameters[i].Write(stream,mgr);
}

//// kGlobalNameKonstant /////////////////////////////////////////////////
//  My continued attempt to spread the CORRECT way to spell konstant. -mcn

plString plPythonFileMod::kGlobalNameKonstant = _TEMP_CONVERT_FROM_LITERAL("VeryVerySpecialPythonFileMod");
