/*==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 . 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==*/ #include "hsConfig.h" #include "plPythonSDLModifier.h" #include "cyPythonInterface.h" #include "plPythonFileMod.h" #include "pyKey.h" #include "cyMisc.h" #include "../pnSceneObject/plSceneObject.h" #include "../plResMgr/plKeyFinder.h" #include "../plAgeDescription/plAgeDescription.h" #include "../plSDL/plSDL.h" #include "../pnNetCommon/plNetApp.h" #include "../plNetClient/plNetClientMgr.h" plStateDataRecord * GetAgeSDL() { const plPythonSDLModifier * mod = plPythonSDLModifier::FindAgeSDL(); if ( mod ) return mod->GetStateCache(); return nil; } #define PyLoggedAssert(cond, text) \ if (!cond) PythonInterface::WriteToLog(text); \ hsAssert(cond, text); plPythonSDLModifier::plPythonSDLModifier(plPythonFileMod* owner) : fOwner(owner) { plStateDescriptor* desc = plSDLMgr::GetInstance()->FindDescriptor(fOwner->fPythonFile, plSDL::kLatestVersion); if (desc) { // Create a Python SDL record with no values set int numVars = desc->GetNumVars(); for (int i = 0; i < numVars; i++) { plVarDescriptor* var = desc->GetVar(i); const char* name = var->GetName(); int count = var->GetCount(); fMap[name] = SDLObj(nil, count, false); } } } plPythonSDLModifier::~plPythonSDLModifier() { SDLMap::iterator it; for (it = fMap.begin(); it != fMap.end(); it++) { PyObject* obj = it->second.obj; Py_XDECREF(obj); } } PyObject* plPythonSDLModifier::GetItem(const char* key) { SDLMap::iterator it = fMap.find(key); if (it == fMap.end()) { char errmsg[256]; sprintf(errmsg,"SDL key %s not found",key); PyErr_SetString(PyExc_KeyError, errmsg); PYTHON_RETURN_ERROR; } PyObject* val = it->second.obj; if (!val) val = PyTuple_New(0); Py_INCREF(val); return val; } void plPythonSDLModifier::ISetItem(const char* key, PyObject* value) { if (!value || !PyTuple_Check(value)) { PyLoggedAssert(0, "[SDL] Trying to set a tuple value to something that isn't a tuple"); return; } SDLMap::iterator it = fMap.find(key); if (it == fMap.end()) { PyLoggedAssert(0, "[SDL] Tried to set a nonexistent SDL value"); return; } SDLObj& oldObj = it->second; if (oldObj.size != 0 && PyTuple_Size(value) != oldObj.size) { PyLoggedAssert(0, "[SDL] Wrong size for a fixed size SDL value"); return; } Py_XDECREF(oldObj.obj); Py_XINCREF(value); oldObj.obj = value; } void plPythonSDLModifier::SendToClients(const char* key) { SDLMap::iterator it = fMap.find(key); if (it != fMap.end()) it->second.sendToClients = true; } void plPythonSDLModifier::SetNotify(pyKey& selfkey, const char* key, float tolerance) { AddNotifyForVar(selfkey.getKey(), key, tolerance); } void plPythonSDLModifier::SetItem(const char* key, PyObject* value) { ISetItem(key, value); IDirtySynchState(key); } void plPythonSDLModifier::SetItemFromSDLVar(plSimpleStateVariable* var) { const char* name = var->GetName(); // Get the SDL value in Python format PyObject* pyVar = ISDLVarToPython(var); ISetItem(name, pyVar); Py_XDECREF(pyVar); // let the sender do the dirty sync state stuff } void plPythonSDLModifier::SetDefault(const char* key, PyObject* value) { ISetItem(key, value); } void plPythonSDLModifier::SetItemIdx(const char* key, int idx, PyObject* value, hsBool sendImmediate) { if (!value) { PyLoggedAssert(0, "[SDL] Trying to set a value to nil"); return; } SDLMap::iterator it = fMap.find(key); if (it == fMap.end()) { PyLoggedAssert(0, "[SDL] Tried to set a nonexistent SDL value"); return; } PyObject* pyTuple = it->second.obj; int size = it->second.size; if (size != 0 && idx >= size) { PyLoggedAssert(0, "[SDL] Trying to resize a SDL value that can't be"); return; } if (pyTuple && pyTuple->ob_refcnt != 1) { // others already have references to the tuple and expect it to be immutable, must make a copy int n = PyTuple_Size(pyTuple); PyObject* newTuple = PyTuple_New(n); for (int j = 0; j < n; j++) { PyObject* item = PyTuple_GetItem(pyTuple, j); Py_INCREF(item); PyTuple_SetItem(newTuple, j, item); } Py_DECREF(pyTuple); pyTuple = newTuple; it->second.obj = newTuple; } if (pyTuple) { if (PyTuple_Size(pyTuple) <= idx) { int oldsize = PyTuple_Size(pyTuple); _PyTuple_Resize(&pyTuple, idx+1); // initialize the tuple elements to None, because Python don't like NULLs int j; for ( j=oldsize; jsecond.obj = pyTuple; } } else { int newSize = (size == 0) ? idx+1 : size; pyTuple = PyTuple_New(newSize); // initialize the tuple elements to None, because Python don't like NULLs int j; for ( j=0; jsecond.obj = pyTuple; } Py_XINCREF(value); // PyTuple_SetItem doesn't increment the ref count PyTuple_SetItem(pyTuple, idx, value); IDirtySynchState(key, sendImmediate); } const char* plPythonSDLModifier::GetSDLName() const { return fOwner->fPythonFile; } void plPythonSDLModifier::SetFlags(const char* name, bool sendImmediate, bool skipOwnershipCheck) { SDLMap::iterator it = fMap.find(name); if (it != fMap.end()) { it->second.sendImmediate = sendImmediate; it->second.skipLocalCheck = skipOwnershipCheck; } } void plPythonSDLModifier::SetTagString(const char* name, const char* tag) { SDLMap::iterator it = fMap.find(name); if (it != fMap.end()) { it->second.hintString = tag; } } void plPythonSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) { plStateDataRecord::SimpleVarsList vars; int num = srcState->GetUsedVars(&vars); for (int i = 0; i < num; i++) { plSimpleStateVariable* var = vars[i]; const char* name = var->GetName(); // Get the SDL value in Python format PyObject* pyVar = ISDLVarToPython(var); SetItem(name, pyVar); Py_XDECREF(pyVar); } // Notify the Python code that we updated the SDL record if (fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load] != nil) { PyObject* retVal = PyObject_CallMethod(fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load], fOwner->fFunctionNames[plPythonFileMod::kfunc_Load], nil); if (retVal == nil) { #ifndef PLASMA_EXTERNAL_RELEASE // for some reason this function didn't, remember that and not call it again fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load] = nil; #endif //PLASMA_EXTERNAL_RELEASE // if there was an error make sure that the stderr gets flushed so it can be seen fOwner->ReportError(); } Py_XDECREF(retVal); } // display any output fOwner->DisplayPythonOutput(); } void plPythonSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) { SDLMap::iterator it = fMap.begin(); for (; it != fMap.end(); it++) { IPythonVarToSDL(dstState, it->first.c_str()); } } void plPythonSDLModifier::IDirtySynchState(const char* name, hsBool sendImmediate) { SDLMap::iterator it = fMap.find(name); if (it != fMap.end()) { UInt32 flags = 0; if (it->second.sendToClients) flags |= kBCastToClients; if (it->second.sendImmediate) flags |= kSendImmediately; if (it->second.skipLocalCheck) flags |= kSkipLocalOwnershipCheck; if (sendImmediate) flags |= kSendImmediately; GetTarget()->DirtySynchState(fOwner->fPythonFile, flags); } } bool plPythonSDLModifier::IPythonVarIdxToSDL(plSimpleStateVariable* var, int varIdx, int type, PyObject* pyVar, const char* hintstring) { switch (type) { case plVarDescriptor::kShort: case plVarDescriptor::kByte: case plVarDescriptor::kBool: case plVarDescriptor::kInt: if (PyInt_Check(pyVar)) { int v = PyInt_AsLong(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } else if (PyLong_Check(pyVar)) { int v = (int)PyLong_AsLong(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } else if (PyFloat_Check(pyVar)) { int v = (int)PyFloat_AsDouble(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } break; case plVarDescriptor::kFloat: if (PyFloat_Check(pyVar)) { float v = (float)PyFloat_AsDouble(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } // does python think that its an integer? too bad, we'll make it a float anyhow! else if (PyInt_Check(pyVar)) { float v = (float)PyInt_AsLong(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } break; case plVarDescriptor::kString32: if (PyString_Check(pyVar)) { char* v = PyString_AsString(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); } break; case plVarDescriptor::kKey: { pyKey* key = PythonInterface::GetpyKeyFromPython(pyVar); if ( key ) var->Set(key->getKey(),varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); } break; case plVarDescriptor::kDouble: if (PyFloat_Check(pyVar)) { double v = PyFloat_AsDouble(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } else if (PyInt_Check(pyVar)) { double v = (double)PyInt_AsLong(pyVar); var->Set(v, varIdx); if (hintstring) var->GetNotificationInfo().SetHintString(hintstring); return true; } break; case plVarDescriptor::kAgeTimeOfDay: break; default: hsAssert(0, "Not supported yet"); } return false; } void plPythonSDLModifier::IPythonVarToSDL(plStateDataRecord* state, const char* name) { plSimpleStateVariable* var = state->FindVar(name); PyObject* pyVar = nil; SDLMap::iterator it = fMap.find(name); if (it != fMap.end()) pyVar = it->second.obj; if (!var || !pyVar) return; if (PyTuple_Check(pyVar)) { int count = PyTuple_Size(pyVar); plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType(); for (int i = 0; i < count; i++) { PyObject* pyVarItem = PyTuple_GetItem(pyVar, i); if (pyVarItem) IPythonVarIdxToSDL(var, i, type, pyVarItem,it->second.hintString.c_str()); } } } PyObject* plPythonSDLModifier::ISDLVarIdxToPython(plSimpleStateVariable* var, int type, int idx) { switch (type) { case plVarDescriptor::kShort: case plVarDescriptor::kByte: case plVarDescriptor::kInt: { int v; var->Get(&v, idx); return PyInt_FromLong(v); } case plVarDescriptor::kFloat: case plVarDescriptor::kAgeTimeOfDay: { float v; var->Get(&v, idx); return PyFloat_FromDouble(v); } case plVarDescriptor::kBool: { bool v; var->Get(&v, idx); return PyLong_FromLong(v); } case plVarDescriptor::kString32: { char v[256]; var->Get(v, idx); return PyString_FromString(v); } case plVarDescriptor::kKey: { plKey v; var->Get(&v, idx); PyObject* keyObj = pyKey::New(v); return keyObj; } case plVarDescriptor::kDouble: { double v; var->Get(&v, idx); return PyFloat_FromDouble(v); } // case plVarDescriptor::kStateDescriptor: // case plVarDescriptor::kCreatable: // case plVarDescriptor::kVector3: // case plVarDescriptor::kPoint3: // case plVarDescriptor::kRGB: // case plVarDescriptor::kRGBA: // case plVarDescriptor::kQuaternion: default: hsAssert(0, "Not supported yet"); } PYTHON_RETURN_NONE; } PyObject* plPythonSDLModifier::ISDLVarToPython(plSimpleStateVariable* var) { plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType(); int count = var->GetCount(); PyObject* pyTuple = PyTuple_New(count); for (int i = 0; i < count; i++) { PyObject* varVal = ISDLVarIdxToPython(var, type, i); PyTuple_SetItem(pyTuple, i, varVal); } return pyTuple; } bool plPythonSDLModifier::HasSDL(const char* pythonFile) { return (plSDLMgr::GetInstance()->FindDescriptor(pythonFile, plSDL::kLatestVersion) != nil); } const plSDLModifier *ExternFindAgeSDL() { return plPythonSDLModifier::FindAgeSDL(); } const plPythonSDLModifier *ExternFindAgePySDL() { return plPythonSDLModifier::FindAgeSDL(); } const plPythonSDLModifier* plPythonSDLModifier::FindAgeSDL() { const char* ageName = cyMisc::GetAgeName(); if (strcmp(ageName, "") == 0) return nil; // don't have an age, probably because we're running in max? // find the Age Global object plLocation loc = plKeyFinder::Instance().FindLocation(ageName,plAgeDescription::GetCommonPage(plAgeDescription::kGlobal)); if ( loc.IsValid() ) { plUoid oid(loc,plPythonFileMod::Index(), plPythonFileMod::kGlobalNameKonstant); if ( oid.IsValid() ) { plKey key = hsgResMgr::ResMgr()->FindKey(oid); plPythonFileMod *pfmod = plPythonFileMod::ConvertNoRef(key ? key->ObjectIsLoaded() : nil); if ( pfmod ) { plPythonSDLModifier * sdlMod = pfmod->GetSDLMod(); if(sdlMod) // we found it! return sdlMod; plNetClientApp::StaticErrorMsg("pfmod %s has a nil python SDL modifier for age sdl %s", pfmod->GetKeyName() ? pfmod->GetKeyName() : "?", ageName); } else { char str[256]; if (!key) plNetClientApp::StaticErrorMsg("nil key %s for age sdl %s", ageName, oid.StringIze(str)); else if (!key->ObjectIsLoaded()) plNetClientApp::StaticErrorMsg("key %s not loaded for age sdl %s", key->GetName() ? key->GetName() : "?", ageName); else if (!plPythonFileMod::ConvertNoRef(key->ObjectIsLoaded())) plNetClientApp::StaticErrorMsg("key %s is not a python file mod for age sdl %s", key->GetName() ? key->GetName() : "?", ageName); } } else plNetClientApp::StaticErrorMsg("Invalid plUoid for age sdl %s", ageName); } else plNetClientApp::StaticErrorMsg("Invalid plLocation for age sdl %s", ageName); // couldn't find one (maybe because we didn't look) return nil; } plKey ExternFindAgeSDLTarget() { return plPythonSDLModifier::FindAgeSDLTarget(); } plKey plPythonSDLModifier::FindAgeSDLTarget() { // find the Age Global object plLocation loc = plKeyFinder::Instance().FindLocation(cyMisc::GetAgeName(),plAgeDescription::GetCommonPage(plAgeDescription::kGlobal)); if ( loc.IsValid() ) { plUoid oid(loc,plPythonFileMod::Index(), plPythonFileMod::kGlobalNameKonstant); if ( oid.IsValid() ) { plKey key = hsgResMgr::ResMgr()->FindKey(oid); plPythonFileMod *pfmod = plPythonFileMod::ConvertNoRef(key ? key->GetObjectPtr() : nil); if ( pfmod ) { if (pfmod->GetTarget(0)) return pfmod->GetTarget(0)->GetKey(); } } } // couldn't find one (maybe because we didn't look) return nil; } ///////////////////////////////////////////// pySDLModifier::pySDLModifier(plPythonSDLModifier* sdlMod) { fRecord = sdlMod; } PyObject* pySDLModifier::GetAgeSDL() { const char* ageName = cyMisc::GetAgeName(); if (strcmp(ageName, "") == 0) PYTHON_RETURN_NONE; // just return none if the age is blank (running in max?) const plPythonSDLModifier* ageSDL = plPythonSDLModifier::FindAgeSDL(); if ( ageSDL ) { return pySDLModifier::New((plPythonSDLModifier*)ageSDL); } // didn't find one, throw an exception for the python programmer to chew on char errmsg[256]; sprintf(errmsg,"Age Global SDL for %s does not exist!",ageName); plNetClientApp::StaticErrorMsg(errmsg); PyErr_SetString(PyExc_KeyError, errmsg); PYTHON_RETURN_ERROR; } void pySDLModifier::SetDefault(pySDLModifier& self, std::string key, PyObject* value) { self.fRecord->SetDefault(key.c_str(), value); } void pySDLModifier::SendToClients(pySDLModifier& self, std::string key) { self.fRecord->SendToClients(key.c_str()); } void pySDLModifier::SetNotify(pySDLModifier& self, pyKey& selfkey, std::string key, float tolerance) { self.fRecord->SetNotify(selfkey, key.c_str(), tolerance); } PyObject* pySDLModifier::GetItem(pySDLModifier& self, std::string key) { return self.fRecord->GetItem(key.c_str()); } void pySDLModifier::SetItem(pySDLModifier& self, std::string key, PyObject* value) { self.fRecord->SetItem(key.c_str(), value); } void pySDLModifier::SetItemIdx(pySDLModifier& self, std::string key, int idx, PyObject* value) { self.fRecord->SetItemIdx(key.c_str(), idx, value); } void pySDLModifier::SetItemIdxImmediate(pySDLModifier& self, std::string key, int idx, PyObject* value) { self.fRecord->SetItemIdx(key.c_str(), idx, value, true); } void pySDLModifier::SetFlags(pySDLModifier& self, const char* name, bool sendImmediate, bool skipOwnershipCheck) { self.fRecord->SetFlags(name,sendImmediate,skipOwnershipCheck); } void pySDLModifier::SetTagString(pySDLModifier& self, const char* name, const char* tag) { self.fRecord->SetTagString(name,tag); }