Revision: 19999
          
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=19999
Author:   campbellbarton
Date:     2009-04-30 10:01:31 +0200 (Thu, 30 Apr 2009)

Log Message:
-----------
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).

Modified Paths:
--------------
    trunk/blender/source/blender/src/buttons_logic.c
    trunk/blender/source/gameengine/GamePlayer/ActiveX/BlenderPlayerCtl.cpp
    trunk/blender/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
    trunk/blender/source/gameengine/Ketsji/KX_PythonInit.cpp

Modified: trunk/blender/source/blender/src/buttons_logic.c
===================================================================
--- trunk/blender/source/blender/src/buttons_logic.c    2009-04-30 03:46:31 UTC 
(rev 19998)
+++ trunk/blender/source/blender/src/buttons_logic.c    2009-04-30 08:01:31 UTC 
(rev 19999)
@@ -1580,12 +1580,12 @@
 
        
                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);
                

Modified: 
trunk/blender/source/gameengine/GamePlayer/ActiveX/BlenderPlayerCtl.cpp
===================================================================
--- trunk/blender/source/gameengine/GamePlayer/ActiveX/BlenderPlayerCtl.cpp     
2009-04-30 03:46:31 UTC (rev 19998)
+++ trunk/blender/source/gameengine/GamePlayer/ActiveX/BlenderPlayerCtl.cpp     
2009-04-30 08:01:31 UTC (rev 19999)
@@ -662,9 +662,6 @@
                                m_gamedata->curscene);
                        
                        PyObject* m_dictionaryobject = 
initGamePlayerPythonScripting("Ketsji", psl_Highest, m_gamedata->main, 0, NULL);
-                       //PyObject* m_dictionaryobject = 
initGamePlayerPythonScripting("Ketsji", psl_Lowest, m_gamedata->main, 0, NULL);
-
-                       ///python scripting doesn't work
                        m_ketsjiengine->SetPythonDictionary(m_dictionaryobject);
 
                        initRasterizer(m_rasterizer, m_canvas);                 

Modified: trunk/blender/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
===================================================================
--- trunk/blender/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp      
2009-04-30 03:46:31 UTC (rev 19998)
+++ trunk/blender/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp      
2009-04-30 08:01:31 UTC (rev 19999)
@@ -704,10 +704,10 @@
                                                
                                                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)

Modified: trunk/blender/source/gameengine/Ketsji/KX_PythonInit.cpp
===================================================================
--- trunk/blender/source/gameengine/Ketsji/KX_PythonInit.cpp    2009-04-30 
03:46:31 UTC (rev 19998)
+++ trunk/blender/source/gameengine/Ketsji/KX_PythonInit.cpp    2009-04-30 
08:01:31 UTC (rev 19999)
@@ -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_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 @@
        }
 }
 
+/* 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 @@
        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 @@
        
        bpy_import_main_set(maggie);
        
+       initPySysPath(maggie);
+       
+       first_time = false;
+       
        PyObject* moduleobj = PyImport_AddModule("__main__");
        return PyModule_GetDict(moduleobj);
 }
@@ -1553,10 +1662,16 @@
 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 @@
        
        bpy_import_main_set(maggie);
        
+       initPySysPath(maggie);
+       
        /* clear user defined modules that may contain data from the last run */
        clearGameModules();
 
@@ -1619,6 +1736,7 @@
 void exitGamePythonScripting()
 {
        clearGameModules();
+       restorePySysPath(); /* get back the original sys.path and clear the 
backup */
        bpy_import_main_set(NULL);
 }
 


_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to