/*==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],
(char*)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);
}