Async, Eval and Stuff


#1

Heho!
So i have events that get handled in script. Now i need to execute some actions later in time - for that i have the events spawn new events with a future timestamp. I am unsure on how to attach custom functionality to those future events though. For now i just abuse a string-member of the events to store a bit of script and eval it later when the event’s timestamp is reached using a handler for ‘later’-events. That’s kind of ugly, but i don’t think defining a function in script, having it go out of scope and then calling it later from a different handler would work.
So i do like this to spawn a new event for later use (prepare for the uglyness!):

def later(when,func) {
	print("will call at "+when.to_string());
	var ev = Event();
	ev.when = when;
	ev.type = 5;
	ev.who = 0;
	ev.what = 0;
	ev.id = func;
    	queueEvent(ev);
	print("script queued event");
}

later(ev.when + 15,"
	print(\"BOOST "+ev.when.to_string()+" "+ev.who.to_string()+"\");    
	var & player = create_entity(\""+ev.who.to_string()+"\");
	var & spc = player.getPlayerComponent();
	var cs = ClientState();
	print(\"BOOST1 "+(ev.when+15).to_string()+"\");
	spc.getInputAtTime("+(ev.when+15).to_string()+",cs);
	var dir = b2Vec2(cs.axes[3],cs.axes[4]);
	dir.Normalize();
	print(\"BOOST2\");
	var & pos = player.getPositionComponent();
	print(\"BOOST3\");
	pos.vx += dir.x * 10;
	pos.vy += dir.y * 10;
");

this is used to give a player a delayed speed-boost. player presses button, the scripted action does stuff and then executes the call to later() as above. when the new event gets handled later this is used:

def handleFunc(Event ev) {
	print("handle FUNC at "+ev.when.to_string());
	eval(ev.id);
	return true;
}
registerEventHandler("func",5,handleFunc);

first, maybe someone knows a better way than this. i want the action to be executed later on be defined in script, and i need to pass it some info from the first event, like the players id and the time.
second, can i somehow pass on Event ev in handleFunc to the evaled string?


#2

You would definitely be better off creating a function object and passing that around. As long as someone keeps a handle / copy of the function object (and the ChaiScript interpreter is still alive) it will be fine.

Here is hopefully a good example from my own demo ChaiScript based game engine. We create a function object and pass that to a C++ function for later callback.

And the C++ function is defined here:

Which maps into this object:

Which is then called (when a collision actually occurs) via this method:

This method of passing function objects around is a very easy method of managing (and abstracting) script / C++ interaction and performs very well.


#3

Hmk, so i derived ScriptEvent from my Event class in c++ and added a std::function to it.
i changed all the related handlers ,replaced bindings and added one for the std::function.
now i can assign chai-funcs to it and then call them no problem, even pass in the event the function belongs to as a parameter as i wanted.
now simple tests work, but actually using it for player’s events introduced a problem i do not quite understand.
i push events from players’ queues to a vector with events for the scriptsystem, like so:

events.push_back(static_cast<ScriptEvent&>(ev));

players have Events, ScriptSystem has std::vector of ScriptEvent. this results in segfault as soon as ScriptEvent has a std::function member:

(gdb) bt
#0  0x00007fff00000000 in ?? ()
#1  0x00000000007bf574 in std::function<bool (ScriptEvent const&)>::function(std::function<bool (ScriptEvent const&)> const&) () at /usr/include/c++/4.9/functional:2410
#2  0x00000000007a2a02 in ScriptSystem::update(int const&, int const&) () at scriptsystem.hpp:6

which leads me to believe that the casting somehow calls function’s copy-constructor in a bad way, maybe trying to copy func from Event - which does not have such member. i’m not sure, probably i messed up somewhere because i would expect this to work. “Solved” it by adding a

     void ScriptEvent::fromEvent(const Event& ev);

and use that to explicitly copy data from Events to ScriptEvents, as i copy them anyhow.

Works so far, thx.


#4

The question is: what’s the type of events.

If it’s

std::vector<EventBaseClass> events;
events.push_back(static_cast<ScriptEvent &>(ev));

Then you are doing something called “slicing” where you are calling the copy constructor of the base class with an object of the derived class. This loses all of the information that was in the derived type.

If, however, you are doing:

std::vector<std::reference_wrapper<EventBaseClass>> events;
events.push_back(static_cast<ScriptEvent &>(ev));

Then theoretically this would work - assuming that ev object still has a place to live and is not being popped from the stack by C++.

What catches my attention and makes me concerned is the need for the static_cast at all, which is why I’m suspecting there’s something else going on in your code.

If neither of those is the problem then I might have a bug in ChaiScript.


#5

Had nothing to do with chaiscript - was a mess of bugs that may or may not have been related.
After some rewriting and good measure of valgrind now my delayed events fire off nicely, and even fire nicely when doing prediction rollbacks to get corrections from server’s updates. And all that with sweet chaiscripted functions attached to them.
*slurps chaitea-o-victory


#6

Excellent. Let me know if there’s anything else I can help with.