Game Development Community

PyTGE (also PyTSE) - Python Bindings

by Prairie Games · in Torque Game Engine · 07/12/2006 (2:38 am) · 101 replies

I've had a number of requests for this so here it is sooner rather than later... Python bindings for TGE/TSE and probably TGB:

http://www.prairiegames.com/pytse10a.zip

It's called PyTSE, though there isn't any TSE specific source in it. So, it should build with no modifications with TGE 1.4

This hasn't been field tested, though it should be more or less bug free. Here's the text from the readme.txt, it's sparse I know. Time is a very limited commodity :|

Quote:This is a new TGE/TSE (and probably TGB) Python binding I have been working on. You can do with it what you like.

I don't have any time to document it. Though, the source file is only 16k, so it should be pretty clear.

I've also included a diff of source changes. They should be pretty close to the current HEAD revision.

You need to change your build target to a shared library (a dll on windows, also change to multithreaded dll code generation for this platform)

Here are some minimal docs in the forum of example usage, replace TSE with TGE if that is your desire:

[b]#--- TSE Python Module Example ---[/b]

[b]#TSE as a standard Python extension (no longer a executable)[/b]
import pytse

[b]#initialize pytse, this also executes main.cs and the .cs packages[/b]
pytse.initialize()

[b]#example of executing a script file[/b]
f = file("myscript.cs","rb")
script = f.read()
f.close()
pytse.evaluate(script)

[b]#or, just generate the cs code right inside Python![/b] 
pytse.evaluate("""
new GuiBitmapButtonCtrl(MyButton) {
 profile = "GuiButtonProfile";
 horizSizing = "right";
 vertSizing = "bottom";
 position = "404 361";
 extent = "285 85";
 minExtent = "8 2";
 visible = "1";
 text = "Button";
 groupNum = "-1";
 buttonType = "PushButton";
 bitmap = "./button";
 helpTag = "0";
};""")

[b]#it's easy to grab a reference to the button we created[/b]
button = TSEObject("MyButton")

[b]#buttons are kind of worthless without commands.  Let's make one:[/b]
def OnMyButton(value):
    print "Button pushed with value",value
    
[b]#export the function to the console system in much the same way the C++ system does...
#we also support optional namespaces, usage documentation, and min/max args[/b]
pytse.export(OnMyButton,"MyButton","OnButton","Example button command",1,1)

[b]#we can get and set fields (including dynamic fields).  We'll set our button's command:[/b]
button.command = "MyButton::OnButton(42);"

[b]#we can call console methods on our TSEObjects... So, let's simulate a button click.  
#the OnMyButton function will be called with the value 42 :)[/b]
button.performClick()

[i]#note that getting an object reference to the button and setting the command like this is 
purely for illustration. You can also: command = "MyButton::OnButton(42);" in the evaluated code.[/i]

[b]#moving on, we can get and set global variables[/b]
pytse.setglobal("$MyVariable",42)
print pytse.getglobal("$MyVariable")
pytse.evaluate('echo ("*** Here is your variable:" @ $MyVariable);')

[b]#the main loop is broken out and can be combined with other frameworks rather easily[/b]
while pytse.tick():
    pass

[b]#cleanup pytse.. goodbye![/b]
pytse.shutdown()


-Josh Ritter
Prairie Games, Inc
#21
07/19/2006 (10:32 pm)
All : Do you know how to request to GG to integrate the missing file core/tDictionary.h into TGE ?

Josh : could you tell us how to replace core/tDictionary.h in ptytse.ccp ? Could you give us some clues ?



I have a look on the tagDictionary.h content : 67 lines !

I can believe that the core/tDictionary.h could not be provided : I can garantee to GG that nobody is able to hack TSE with this file ... :-)

I can unterstand EULA but some time there is the "common sens". I am a linux user and TSE does not work on linux and bougth TSE for only one file with may be 67 lines of code ...
#22
07/19/2006 (11:03 pm)
Hey guys. I'm not usually this deep in the tech stuff, but I always read Josh Ritter's PyT** threads. Anyway, I'm talking to Josh Williams, and he says it is A-OK to share the tDictionary.h files between the engines, even for non-owners, so you can get PyTSE running.

-Jeff Tunnell, GG
#23
07/19/2006 (11:20 pm)
Hey, that's great Jeff. Thanks.

I updated the download link to include the elusive tDictionary sources for TGE/TGB with GG's permission.

-JR
#24
07/19/2006 (11:41 pm)
Jeff Tunnell:
That's good news. Then I can stop my effort in rolling my own Hash Table :-)

Any chanse tDictionary would make it to TGE CVS?
It's a useful "standard" class, in the absense of STL.
#25
07/20/2006 (12:01 am)
Thanks Jeff Tunnel and Josh Williams for allowing those files to be included, you guys are the best.

Thanks again to Josh Ritter for extending Torque into the Python world, this is absolutely fantastic.
#26
07/20/2006 (3:38 am)
Lol, this probably sounds noobish, but I only just got around to testing it out and was stuck at the part in the readme with "You need to change your build target to a shared library (a dll on windows, also change to multithreaded dll code generation for this platform)".
I have no idea what it's talking about here, I've changed the project to compile as a Shared DLL, but have no idea why. I'd really appreciate somebdy giving me a bit of an explaination here ;)
#27
07/20/2006 (5:08 am)
@Mincetro: The idea is that you load the entire game engine through Python as a Python module, as opposed to loading Python in the game engine like you would TorqueScript (and subsequently Josh's first version of TgePython). With that in mind, Python modules are built as Shared Libraries, so you will want to build the engine as a library instead of an executable. At that point, you can happily load PyTGE as a Python module!
#28
07/20/2006 (5:26 am)
@Mincetro: Also you need to call the shared library pytse.pyd (at least I needed too on Windows :-) and have it in the Python path. Well starting python in the example directory (where standard TGE/TSE puts the binary and the main.cs lives) will work quite nice (working directory is in teh python path, but you probably know all this :-)
#29
07/20/2006 (5:58 am)
Anyone have any suggestions as to a smooth way to build this under Xcode as a library?

Edit: Ok, a few hours later I learned more about Xcode than I cared to. Now I am having problems loading the module itself. Onwards...

Anyone know how to link the Framework stuff correctly into a Dynamic Libary under Xcode? Unfortunately I am getting an Import: dlopen() error in regards to expected Framework paths which can't logically exist.
#30
07/20/2006 (11:27 am)
@Brian: I wish I knew more about Mac platform coding, but I'll be here cheering for you. :) If (when!) you get it working, could you please write up how you did it?

For PC, it's simpler than I expected. You just change your project file to output a shared library/dll, then integrate the changes from the diff file into your source files, and recompile. You should probably also rename the linker output file to something like "pytse.dll". Then just drop that dll into your directory (with python24.dll if you don't have Python in your system path). Away you go.

I'm still having trouble remembering that I start TGE from Python instead of clicking on an .exe file. Very nice!
#31
07/20/2006 (11:37 am)
When you go to make a redistributable here are a couple of nice packages:

Windows
py2exe

OSX
py2app

-JR
#32
07/20/2006 (12:15 pm)
Good news :-)

Thanks

EDIT : ok i need definitevely binocular ....
#33
07/20/2006 (12:50 pm)
W00-h00! I was looking for an excuse to learn Python, this is perfect!

Thanks a *ton* for making this available, and thanks to everyone involved for letting tDictionary out!

:-)
#34
07/20/2006 (4:46 pm)
I'm having a problem with tagged/dynamic fields. For example:
def onCollision( datablock, object, collider, pos1, pos2 ):
    print "onCollision", datablock, object, collider, pos1, pos2 # these print correctly
    col = pytse.TSEObject( collider )
    obj = pytse.TSEObject( object )
    if ( col.getClassName() == "Player" ):
        client = col.client
        obj.delete()
This is part of the TGE 1.4 "Getting Started" tutorial, converted to Python. I've exported this call and it gets invoked. But it throws an exception on the line where "client = col.client" is. It complains that the "TSEObject of class Player has no field or function client".

The "demo" from the pytse zipfile works fine, so I am not sure why this won't work. What am I doing wrong?

Oh, and if I use the TGE console to .dump() the object, the "client" field is there and has a value.
#35
07/20/2006 (9:44 pm)
Try adding this for dynamic field support:

static PyObject* PyTSEObject_getattr(PyTSEObject *self, char *attr)
{
   StringTableEntry fieldName = StringTable->insert(attr);

   const AbstractClassRep::Field* field = self->simObject->getClassRep()->findField(fieldName);

   if (field)
   {
      //XXX: Handle Arrays
      return PyString_FromString(self->simObject->getDataField(fieldName,NULL));
   }

+   const char* dfieldval;
+   if (self->simObject->getFieldDictionary() && (dfieldval=self->simObject->getFieldDictionary()->getFieldValue(fieldName)))
+	   return PyString_FromString(dfieldval);
#36
07/21/2006 (12:42 am)
That fixed it. :) Thanks very much!
#37
07/21/2006 (1:46 am)
Hey, it's me again. Surprised? :)

How can I increment a dynamic field value? The trouble is that TS doesn't care about types, but Python needs to know what type it's dealing with. So say you set a field on a GameConnection object called %client:
%client.score = 22;
Now you want to increment this field from Python
client.score += 1
but Python says you can't concatenate strings and ints. Ok, so you try
print "score", client.score
client.score = int(client.score) + 1
print "score now", client.score
but Python says throws an exception that "TSEObject of class GameConnection has no field score". And both print statements say that score is 22.

Any suggestions?

Edit: Corrected the exception message.

Edit2: Looks like the issue might be in PyTSEObject_setattr() since that matches the exception message.
#38
07/21/2006 (2:13 am)
I've never run into an instance where I had to set a dynamic field from Python. It could be added, though the idea with this isn't to program in constant mix of TorqueScript and Python... It's to program your game logic in Python.

-JR
#39
07/21/2006 (2:20 am)
I definitely want to do as much Python as possible, but might there be times when some things are defined in TS that you may need to modify from Python? My example above is arbitrary to show the nature of the issue, but isn't really based on much experience of using pytse in a full game situation. Since you obviously have gobs of experience using your code, I'll trust your judgement that I shouldn't need to modify anything that TS sets up. :) Thanks for the reply.
#40
07/21/2006 (2:27 am)
You can call any console function or instance method, get/set global and instance variables, and read dynamic instance fields... for completeness, I should probably add setting dynamic fields. Though, I've never run into a case where I needed to do this... for reference, I wrote about 200 lines total of TorqueScript for Minions of Mirth. The rest is Python... however, there *are* places in the code where generating and evaluating TS code is very useful... dynamically generating datablocks for instance...

-JR