Python API

Agree it was just easier to me, but i guess there will be many callback objects accessed differently.

Can you tell me how to acces that input?

Input on client - CInGameConsole class.
Sending via PlayerMessage netpack
Processing on server (including cheats) - CGameHandler::playerMessage()

What do you think about unhardcoding some objects behaviour? For example hillfort,tavern, ad hill fort have Class CGObjectInstance with switch in onEnter:

{
	switch(ID)
	{
	case Obj::HILL_FORT:
		{
			openWindow(OpenWindow::HILL_FORT_WINDOW,id.getNum(),h->id.getNum());
		}
		break;
	case Obj::SANCTUARY:
		{
			//You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders.  You feel safe here.
			showInfoDialog(h,114,soundBase::GETPROTECTION);
		}
		break;
	case Obj::TAVERN:
		{
			openWindow(OpenWindow::TAVERN_WINDOW,h->id.getNum(),id.getNum());
		}
		break;
	}

My idea is to move objects classes to a file config/scripts/objects.py and have them accessible via vcmi.objects.class. Much work but, should be easy for simple objects, and occasion to test how python engine is doing.

Short version: yes.

Long version:
Specifically these 3 classes may be not that easy to move - Sanctuary has check somewhere near pathfinder to block this tile when there is a hero in it, hill fort has checks for its presence in creature upgrade fort, tavern is likely to have such checks in hero recruitment.

What about these objects:

  • 3 objects you’ve mentioned IF you can remove hardcoded mechanics. Run search for constants, e.g. Obj::Tavern to check where they’re used.
  • Stables (movement bonus + Cavalier upgrade)
  • Sirens ( -30% of army for XP )

I dont have code right now but if logic os based on object id it is accesed the same way, and i will not override it. Will see this evening. More od my concern is serialization. We must have one serialiser for CGPythonObject which will persist python defined fields. Already gór that. But where is it deserialaized? Can we specify deserialization template?

Serializer template is used both to serialize and deserialize object. Some classes (CGHeroInstance for eexample) may have additional custom behavior.

Detailed description of seriallizer is here wiki.vcmi.eu/index.php?title=Serialization

Thanks, but this can be not enough from my understanding it goes like this

CGPyObjectInstance * instance=new CGPyObjectInstance();
instance->serialize(handler,version)(

But i don`t want to call constructor CGPyObjectInstance(), it is meant to never be used directly. I want to call python class constructor. Like in ObjectHandler->create(). I want to do something like this

string className=somehow read python className.
CGPyObjectInstance * instance=CScriptManager::createObject("className");
instance->serialize(handler,version);

So the python object will be serialized to two fields: first className as string, second actualClassData.

Edit:
This works actually in game because only Object id are sent so if the objects are the same on client and server there are no issues, but this can be handy when saving/loading game state.

I don’t think that it can work as of now - construction is always done via serializer. You’ll need to somehow override this behavior.

Try looking into ClassObjectCreator - perhaps you can use template specialization to properly load your object. But it will still fail on loading because serializer will have no knowledge of python types. Not sure if there is an easy way to change that.

I think, CGPyObjectInstance should be registered in serializer as all other objects. All python-related stuff goes into CGPyObjectInstance::serialize()

Thanks i will look at this. Seems we can change runtime class of python object at runtime (cool) like this.

object.__class__ = type("className"))

So the deserialization of PyObjectInstance will look like this

	void serialize(Handler & h, const int version)
	{
                std:: string className;
                h & className;
                switchRuntimeClass(boost::python::ptr<CGPyObjectInstance>(this),className);//this function will be written in python.
		h & more serialization.

	}
def switchRuntimeClass(object,className):
    object.__class__=type(className)

Looks like magic, but we can give it a shot if there is any other possibility.

I think this point should describe the scope of scripts - what they can do and they can’t.

Things I would like to have in adventure map objects are:

  • Access to common methods typical to objects, like wasVisited and setPropertyDer (possibly wrapped in some interface function with sane name).
  • Access to callback functions
  • Posibility to override object methods, or even better, add new functionality to them (triggers). It’s not wise to override important initializers or complex algorithms essential for game performance.

As described above, these are all server-side scripts that extend functionalities of objects. No object (netpack) creation, no access to internal guts, no client-side scripts. Just an arbitrary set of idiot-proof functions.

Once this works in some resonable way, we cna think about extending it with more features.

This is possible, and working without any complex issues.
Can you explain how do you see triggers mechanism, from upstream perspective?

So i understand callback method sendAndApply will not be accessible from scripts. It makes work easier, cause it would require exporting existing netpacks.

Out of curiosity - how do you want to implement it? I know that Python has some sandboxing setup, but I never played with it. Is it secure enough?
Let me try and put a stick in to test the idea:

  1. Disabled Import.
    I hope you implement allowed modules via something smarter than “if mymodule in allowed_modnames: imp(mymodule)”. This can be trivially worked around by creating mymodule.py in the game directory.
  2. Standard libraries.
    If game is run with PYTHONPATH=/home/user/mylibraries, then standard library modules are under user control, meaning mods can do anything again. I’m not sure if you can get rid of this environment variable or if it’s a good idea.

I think this is an outright bad idea. Did you take into account how users/package maintainers compile vcmi themselves? If their Python version differs from the one where checksums were generated, they have to recalculate checksums. Some distributions don’t package .pyo and .pyc files and only generate them during package installation. Some might not want them at all.
Development is also going to be annoying when you have to remember to regenerate checksums after every touch of the script.

I’m not sure what the checksumming is supposed to prevent that file permissions don’t prevent already. If an attacker can rewrite .pyc files, then they could probably rewrite the binary together with checksums and the user is screwed anyway.

BUMP!

Nice you have working PY© interpreter, In my project I couldn’t do it at all… It seem the script interpreter even does something… Would it take long to have fulll list of triggers(events) and receivers(game interface)?

These futures requires nearly complete engine redesign

why? isn’t it ready to do changes about script interfaces? I don’t fully insist to do ALL triggers/receivers from WoG, just complete enviroment so we can script more than hello world. Are you sure it needs such drastics steps? (Thankfully, they had network model from begginning so they don’t have to do rewrite for multiplayer). if such drastic steps are needed why they don’t make these project asssumptions from beggining?

Scripting was kind of supported from early on, but design is bad. (BTW subject of this particular thread no longer exist - it was not fully implemented and was even wiped from github). However scripting (ERM at first) is next feature to implement (in my todo list)

would you implement DLL-interfacing? (mod could contain DLL/so to your interfaces instead a script - sometimes)

This feature will not be implemented.

Is this idea dead or is it somewhere?