/*==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 . 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) { 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; } 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); }