forked from bartvdbraak/blender
BGE Python sys.path for the blenderplayer and blender
sys.path is the search path for python modules. This is useful so people making games can put all their scripts in a folder and be sure they will always load into the BGE. for each blend file a scripts directory is added to the path /home/me/foo.blend will look for modules in... /home/me/scripts/*.py It could also default to look for modules in the same directory as the blend file but I think this is messy. Added a note in the tooltip about //scripts so its not such a hidden feature. This works by storing the original sys.path, then adding the paths for the blendfile and all its libs, when a new blendfile is loaded, the original sys.path is restored before adding the blendfiles paths again so the sys.path wont get junk in it. One problem with this - when using linked libs the module names must be unique else it will load the wrong module for one of the controllers. also fixed 2 bugs - sys.path in the blenderplayer was growing by 1 for every file load in blenderplayer - the relative path (gp_GamePythonPath), wasnt being set when loading files in the blenderlayer (as I wrongly said in the last commit).
This commit is contained in:
parent
939290b59c
commit
b5b24ee521
@ -1580,12 +1580,12 @@ static short draw_controllerbuttons(bController *cont, uiBlock *block, short xco
|
||||
|
||||
|
||||
uiBlockBeginAlign(block);
|
||||
uiDefButI(block, MENU, B_REDR, "Execution Method%t|Script%x0|Module%x1", xco+24,yco-24, 66, 19, &pc->mode, 0, 0, 0, 0, "Python script type (textblock or module)");
|
||||
uiDefButI(block, MENU, B_REDR, "Execution Method%t|Script%x0|Module%x1", xco+24,yco-24, 66, 19, &pc->mode, 0, 0, 0, 0, "Python script type (textblock or module - faster)");
|
||||
if(pc->mode==0)
|
||||
uiDefIDPoinBut(block, test_scriptpoin_but, ID_SCRIPT, 1, "", xco+90,yco-24,width-90, 19, &pc->text, "Blender textblock to run as a script");
|
||||
else {
|
||||
uiDefBut(block, TEX, 1, "", xco+90,yco-24,(width-90)-25, 19, pc->module, 0, 63, 0, 0, "Module name and function to run eg \"someModule.main\"");
|
||||
uiDefButBitI(block, TOG, CONT_PY_DEBUG, B_REDR, "D", (xco+width)-25, yco-24, 19, 19, &pc->flag, 0, 0, 0, 0, "Continuously reload the module from disk for editing external modules without restrting, (slow)");
|
||||
uiDefBut(block, TEX, 1, "", xco+90,yco-24,(width-90)-25, 19, pc->module, 0, 63, 0, 0, "Module name and function to run eg \"someModule.main\", internal texts and //scripts/*.py on the filesystem can be used");
|
||||
uiDefButBitI(block, TOG, CONT_PY_DEBUG, B_REDR, "D", (xco+width)-25, yco-24, 19, 19, &pc->flag, 0, 0, 0, 0, "Continuously reload the module from disk for editing external modules without restrting");
|
||||
}
|
||||
uiBlockEndAlign(block);
|
||||
|
||||
|
@ -704,10 +704,10 @@ int main(int argc, char** argv)
|
||||
|
||||
BLI_strncpy(pathname, maggie->name, sizeof(pathname));
|
||||
BLI_strncpy(G.sce, maggie->name, sizeof(G.sce));
|
||||
setGamePythonPath(G.sce);
|
||||
|
||||
if (firstTimeRunning)
|
||||
{
|
||||
setGamePythonPath(G.sce);
|
||||
firstTimeRunning = false;
|
||||
|
||||
if (fullScreen)
|
||||
|
@ -80,6 +80,10 @@
|
||||
|
||||
#include "KX_PythonInitTypes.h"
|
||||
|
||||
/* we only need this to get a list of libraries from the main struct */
|
||||
#include "DNA_ID.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
extern "C" {
|
||||
#include "Mathutils.h" // Blender.Mathutils module copied here so the blenderlayer can use.
|
||||
#include "bpy_internal_import.h" /* from the blender python api, but we want to import text too! */
|
||||
@ -110,6 +114,7 @@ static KX_Scene* gp_KetsjiScene = NULL;
|
||||
static KX_KetsjiEngine* gp_KetsjiEngine = NULL;
|
||||
static RAS_IRasterizer* gp_Rasterizer = NULL;
|
||||
static char gp_GamePythonPath[FILE_MAXDIR + FILE_MAXFILE] = "";
|
||||
static PyObject *gp_OrigPythonSysPath= NULL;
|
||||
|
||||
void KX_RasterizerDrawDebugLine(const MT_Vector3& from,const MT_Vector3& to,const MT_Vector3& color)
|
||||
{
|
||||
@ -1522,11 +1527,111 @@ void setSandbox(TPythonSecurityLevel level)
|
||||
}
|
||||
}
|
||||
|
||||
/* Explanation of
|
||||
*
|
||||
* - backupPySysPath() : stores sys.path in gp_OrigPythonSysPath
|
||||
* - initPySysPath(main) : initializes the blendfile and library paths
|
||||
* - restorePySysPath() : restores sys.path from gp_OrigPythonSysPath
|
||||
*
|
||||
* These exist so the //scripts folder can always be used to import modules from.
|
||||
* the reason we need a few functions for this is that python is not only used by the game engine
|
||||
* so we cant just add to sys.path all the time, it would leave pythons state in a mess.
|
||||
* It would also be incorrect since loading blend files for new levels etc would alwasy add to sys.path
|
||||
*
|
||||
* To play nice with blenders python, the sys.path is backed up and the current blendfile along
|
||||
* with all its lib paths are added to the sys path.
|
||||
* When loading a new blendfile, the original sys.path is restored and the new paths are added over the top.
|
||||
*/
|
||||
|
||||
/**
|
||||
* So we can have external modules mixed with our blend files.
|
||||
*/
|
||||
static void backupPySysPath(void)
|
||||
{
|
||||
PyObject *sys_path= PySys_GetObject("path"); /* should never fail */
|
||||
|
||||
/* just incase its set */
|
||||
Py_XDECREF(gp_OrigPythonSysPath);
|
||||
gp_OrigPythonSysPath= NULL;
|
||||
|
||||
gp_OrigPythonSysPath = PyList_GetSlice(sys_path, 0, INT_MAX); /* copy the list */
|
||||
}
|
||||
|
||||
/* for initPySysPath only,
|
||||
* takes a blend path and adds a scripts dir from it
|
||||
*
|
||||
* "/home/me/foo.blend" -> "/home/me/scripts"
|
||||
*/
|
||||
static void initPySysPath__append(PyObject *sys_path, char *filename)
|
||||
{
|
||||
PyObject *item;
|
||||
char expanded[FILE_MAXDIR + FILE_MAXFILE] = "//";
|
||||
|
||||
BLI_convertstringcode(expanded, filename);
|
||||
BLI_join_dirfile(expanded, expanded, "scripts"); /* double checked and using the dir twice is safe */
|
||||
|
||||
item= PyString_FromString(expanded);
|
||||
|
||||
if(PySequence_Index(sys_path, item) == -1) {
|
||||
PyList_Insert(sys_path, 0, item);
|
||||
}
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
static void initPySysPath(Main *maggie)
|
||||
{
|
||||
PyObject *sys_path= PySys_GetObject("path"); /* should never fail */
|
||||
|
||||
if (gp_OrigPythonSysPath==NULL) {
|
||||
/* backup */
|
||||
backupPySysPath();
|
||||
}
|
||||
else {
|
||||
/* get the original sys path when the BGE started */
|
||||
PyList_SetSlice(sys_path, 0, INT_MAX, gp_OrigPythonSysPath);
|
||||
}
|
||||
|
||||
Library *lib= (Library *)maggie->library.first;
|
||||
|
||||
while(lib) {
|
||||
initPySysPath__append(sys_path, lib->name);
|
||||
lib= (Library *)lib->id.next;
|
||||
}
|
||||
|
||||
initPySysPath__append(sys_path, gp_GamePythonPath);
|
||||
|
||||
// fprintf(stderr, "\nNew Path: %d ", PyList_Size(sys_path));
|
||||
// PyObject_Print(sys_path, stderr, 0);
|
||||
}
|
||||
|
||||
static void restorePySysPath(void)
|
||||
{
|
||||
if (gp_OrigPythonSysPath==NULL)
|
||||
return;
|
||||
|
||||
PyObject *sys_path= PySys_GetObject("path"); /* should never fail */
|
||||
|
||||
PyList_SetSlice(sys_path, 0, INT_MAX, gp_OrigPythonSysPath);
|
||||
Py_DECREF(gp_OrigPythonSysPath);
|
||||
gp_OrigPythonSysPath= NULL;
|
||||
|
||||
// fprintf(stderr, "\nRestore Path: %d ", PyList_Size(sys_path));
|
||||
// PyObject_Print(sys_path, stderr, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Python is not initialised.
|
||||
*/
|
||||
PyObject* initGamePlayerPythonScripting(const STR_String& progname, TPythonSecurityLevel level, Main *maggie, int argc, char** argv)
|
||||
{
|
||||
/* Yet another gotcha in the py api
|
||||
* Cant run PySys_SetArgv more then once because this adds the
|
||||
* binary dir to the sys.path each time.
|
||||
* Id have thaught python being totally restarted would make this ok but
|
||||
* somehow it remembers the sys.path - Campbell
|
||||
*/
|
||||
static bool first_time = true;
|
||||
|
||||
#if (PY_VERSION_HEX < 0x03000000)
|
||||
STR_String pname = progname;
|
||||
Py_SetProgramName(pname.Ptr());
|
||||
@ -1536,7 +1641,7 @@ PyObject* initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur
|
||||
Py_Initialize();
|
||||
|
||||
#if (PY_VERSION_HEX < 0x03000000)
|
||||
if(argv) /* browser plugins dont currently set this */
|
||||
if(argv && first_time) /* browser plugins dont currently set this */
|
||||
PySys_SetArgv(argc, argv);
|
||||
#endif
|
||||
//importBlenderModules()
|
||||
@ -1546,6 +1651,10 @@ PyObject* initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur
|
||||
|
||||
bpy_import_main_set(maggie);
|
||||
|
||||
initPySysPath(maggie);
|
||||
|
||||
first_time = false;
|
||||
|
||||
PyObject* moduleobj = PyImport_AddModule("__main__");
|
||||
return PyModule_GetDict(moduleobj);
|
||||
}
|
||||
@ -1553,10 +1662,16 @@ PyObject* initGamePlayerPythonScripting(const STR_String& progname, TPythonSecur
|
||||
void exitGamePlayerPythonScripting()
|
||||
{
|
||||
//clearGameModules(); // were closing python anyway
|
||||
|
||||
/* since python restarts we cant let the python backup of the sys.path hang around in a global pointer */
|
||||
restorePySysPath(); /* get back the original sys.path and clear the backup */
|
||||
|
||||
Py_Finalize();
|
||||
bpy_import_main_set(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Python is already initialized.
|
||||
*/
|
||||
@ -1574,6 +1689,8 @@ PyObject* initGamePythonScripting(const STR_String& progname, TPythonSecurityLev
|
||||
|
||||
bpy_import_main_set(maggie);
|
||||
|
||||
initPySysPath(maggie);
|
||||
|
||||
/* clear user defined modules that may contain data from the last run */
|
||||
clearGameModules();
|
||||
|
||||
@ -1619,6 +1736,7 @@ static void clearGameModules()
|
||||
void exitGamePythonScripting()
|
||||
{
|
||||
clearGameModules();
|
||||
restorePySysPath(); /* get back the original sys.path and clear the backup */
|
||||
bpy_import_main_set(NULL);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user