Jump to content
Sign in to follow this  
Jotunson

[Solved] Problem sending custom data from Pyrogenesis to Petra modification

Recommended Posts

Hi, I am currently finishing up a project on implementing some emotions in the Petrabot to test a library I've written in C++. However I have  run into a problem regarding the data transfer between the Pyrogenesis C++ layer and the mod javascript layer. I apologize for the amount of code in advance, but I am completely stumped as to where the error might be happening. 

In short, I am trying to convert data contained in a data wrapper class I've made to transfer data from the library. As far as feedback is concerned the data seems to be sent properly without any hiccups as the Javascript end seems to recognize the emotions vector as an object. However, the problem arises when I try to reference the "who" integer as it returns as undefined (this may very well be the case with more of the members of the Emotions wrapper, however I have no evidence that this is the case as of yet).

class EmoState
{
public:
	class Emotions
	{
	public:
		int who;
		int strongest;
		std::vector<const Emote> emotes;
		Emotions::Emotions();
		Emotions::~Emotions();
		void SetWho(int s);
		void SetStrongest(int s);
		void AddEmotion(const Emote& e);
	};
	int strAt;
	std::vector<Emotions> emotions;
	EmoState::EmoState();
	EmoState::~EmoState();
	void SetStrAt(int s);
	void AddEmotions(Emotions& e);
};

The when the bot requests the data through GetEmoState (below), the C++ architecture fetches the current EmoState (which I've confirmed functions correctly through multiple tests) and tries to convert it for use with the bot. (Though I doubt this script contains the error, I included it to give a complete view of what's happening),

static JS::Value GetEmoState(ScriptInterface::CxPrivate* pCxPrivate, int playerid)
{
	ENSURE(pCxPrivate->pCBData);
	CAIWorker* self = static_cast<CAIWorker*> (pCxPrivate->pCBData);
	JSContext* cx(self->m_ScriptInterface->GetContext());
		
	JS::RootedValue emoState(cx);
	const EmoState emoStates = self->ValTest.EmotionalState(playerid);
	self->m_ScriptInterface->ToJSVal<EmoState>(cx, &emoState, emoStates);
	return emoState;		
}

The data conversion I wrote to handle the container is as seen below, I suspect that the error lies somewhere in this block of code, however I am not sure what I might have written wrong.

template<> void ScriptInterface::ToJSVal<Emote>(JSContext* cx, JS::MutableHandleValue ret, const Emote& val)
{
	JSAutoRequest rq(cx);

	JS::RootedValue who(cx);
	JS::RootedValue name(cx);
	JS::RootedValue intensity(cx);

	JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
	if (!obj)
	{
		ret.setUndefined();
		LOGERROR("Failed to create emote object");
		return;
	}

	ToJSVal<int>(cx, &who, *val.directedAt);
	ToJSVal<int>(cx, &name, *val.name);
	ToJSVal<double>(cx, &intensity, *val.intensity);

	JS_SetProperty(cx, obj, "directedAt", who);
	JS_SetProperty(cx, obj, "name", name);
	JS_SetProperty(cx, obj, "intensity", intensity);

	ret.setObject(*obj);
}
template<> void ScriptInterface::ToJSVal<EmoState::Emotions>(JSContext* cx, JS::MutableHandleValue ret, const EmoState::Emotions& val)
{
	JSAutoRequest rq(cx);

	JS::RootedValue who(cx);
	JS::RootedValue strongest(cx);

	JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
	if (!obj)
	{
		ret.setUndefined();
		LOGERROR("Failed to create emotions object");
		return;
	}

	JS::RootedObject em(cx, JS_NewArrayObject(cx, 0));
	if (!em)
	{
		ret.setUndefined();
		LOGERROR("Failed to create emotions object");
		return;
	}

	for (size_t i = 0; i < val.emotes.size(); ++i)
	{
		JS::RootedValue el(cx);
		ScriptInterface::ToJSVal<Emote>(cx, &el, val.emotes[i]);
		JS_SetElement(cx, em, i, el);
	}

	JS::RootedValue em2(cx);
	em2.setObject(*em);

	ToJSVal<int>(cx, &who, val.who);
	ToJSVal<int>(cx, &strongest, val.strongest);

	JS_SetProperty(cx, obj, "who", who);
	JS_SetProperty(cx, obj, "strongest", strongest);
	JS_SetProperty(cx, obj, "emotes", em2);

	ret.setObject(*obj);
}
template<> void ScriptInterface::ToJSVal<EmoState>(JSContext* cx, JS::MutableHandleValue ret, const EmoState& val)
{
	JSAutoRequest rq(cx);

	JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
	if (!obj)
	{
		ret.setUndefined();
		LOGERROR("Failed to create EmoState object");
		return;
	}

	JS::RootedValue strAt(cx);
	ScriptInterface::ToJSVal(cx, &strAt, val.strAt);

	JS::RootedObject em(cx, JS_NewArrayObject(cx, 0));
	if (!em)
	{
		ret.setUndefined();
		LOGERROR("Failed to create EmoState object");
		return;
	}

	for (size_t i = 0; i < val.emotions.size(); ++i)
	{
		JS::RootedValue el(cx);
		ScriptInterface::ToJSVal<EmoState::Emotions>(cx, &el, val.emotions[i]);
		JS_SetElement(cx, em, i, el);
	}

	JS::RootedValue em2(cx);
	em2.setObject(*em);

	JS_SetProperty(cx, obj, "strAt", strAt);
	JS_SetProperty(cx, obj, "emotions", em2);

	ret.setObject(*obj);
}

As mentioned before, the javascript code (as seen below) requests the data, which is then applied to their relevant variables on the javascript end. The specific problem variable in the script is the "eS.who" variable which returns undefined and as such the if statement defaults to else. 

let emoState = Engine.GetEmoState(PlayerID);
		
let pHF = this.PersonalEmoState.HopeFear;
let pJD = this.PersonalEmoState.JoyDistress;
let pPS = this.PersonalEmoState.PrideShame;
for(let eS in emoState.emotions)
{
	if(eS.who === PlayerID)
	{
		this.PersonalEmoState.Strongest = eS.strongest; this.PersonalEmoState.PrideShame = eS.emotes[0].intensity; this.PersonalEmoState.JoyDistress = eS.emotes[1].intensity; this.PersonalEmoState.HopeFear = eS.emotes[2].intensity; this.PersonalEmoState.SatisfactionFearsconfirmed = eS.emotes[3].intensity; this.PersonalEmoState.ReliefDisappointment = eS.emotes[4].intensity; this.PersonalEmoState.GratificationRemorse = eS.emotes[5].intensity;
	}
	else
	{
		let pGA = m.EmotionHandler.SocialEmoState[eS.who].GratitudeAnger;
		m.EmotionHandler.SocialEmoState[eS.who].Strongest = eS.strongest; m.EmotionHandler.SocialEmoState[eS.who].AdmirationReproach = eS.emotes[0].intensity; m.EmotionHandler.SocialEmoState[eS.who].HappyforResentment = eS.emotes[1].intensity; m.EmotionHandler.SocialEmoState[eS.who].GloatingPity = eS.emotes[2].intensity; m.EmotionHandler.SocialEmoState[eS.who].GratitudeAnger = eS.emotes[3].intensity;
		if(gameState.isPlayerAlly(eS.who))
		{
			pJD = (pJD === this.PersonalEmoState.JoyDistress ? 0 : this.PersonalEmoState.JoyDistress);
			pPS = (pPS === this.PersonalEmoState.PrideShame ? 0 : this.PersonalEmoState.PrideShame);
			if(pGA !== m.EmotionHandler.SocialEmoState[eS.who].GratitudeAnger)
			{
				pGA = m.EmotionHandler.SocialEmoState[eS.who].GratitudeAnger ? 0 : this.PersonalEmoState.PrideShame;
				this.Config.personality.cooperative = Math.max(Math.min(1, this.Config.personality.cooperative + ((pGA + pPS + pJD)/3)), 0);
			}
		}
	}
}
this.StrongestAt = emoState.strAt;

Once again, I am very sorry for the long post and mass of code. I hope someone will be able to help me as I believe I can make this a cool update. If my tests of the system are successful I am planning on developing a fully Javascript version of my library, which I'll submit as a fork to the Petrabot for people to play with as they want.

Edited by Jotunson

Share this post


Link to post
Share on other sites

Can you test if the other variables come through correctly first (or log the entire object)?

I have a suspicion you're naming the attribute "directedAt":

JS_SetProperty(cx, obj, "directedAt", who);

 

Share this post


Link to post
Share on other sites

Yeah I would try dumping it out to the game console, something I would use:

warn(JSON.stringify(emoState));

Do you have a repo for this fork yet?

Share this post


Link to post
Share on other sites

I don't have a repo fork yet as I don't want to implement the C++ version into the game as I feel it might be better to add it fully as JS. I will definitely post updates as I go along. Thanks for this help, I will get back to you once I've attempted it.

Share this post


Link to post
Share on other sites

So it turns out that all the integer values are mutated somewhere in being called in GetEmoState or being converted to JS, all int values are mutated to something completely different. Usually a really high or low number. The odd thing is that this mutation only happens to integers, while my doubles all stay intact. I've so far changed pretty much all values in the EmoState to const, however, after outputing all integers from the EmoState they're all correct.

Edited by Jotunson

Share this post


Link to post
Share on other sites

It looks like this would cast improperly (char/string) ... I'm not 100% familiar w/ Spidermonkey yet but have you tried JS::Int32Value and passing that?

ToJSVal<int>(cx, &name, *val.name);

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...