Jump to content

Client (Python) check variable declared by Quest


Go to solution Solved by OtherChoice,

Recommended Posts

Hello everyone. 

I'm currently facing an issue where I want to make a certain system / icon visible only if a variable (which was declared and changed via quest) has a certain value. I want the client to check the value of the variable for the quest, and the client to update automatically as the variable is set to a certain value. 

Can this be done? I have looked into an older topic related to this issue: [HowTo|Old]Quest-Client communication(for any version) , but it didn't help really much. I'm still not sure how to check this.

Can someone help me please? I'm pretty dumb when comes to client communication 

Link to comment
Share on other sites

each quest function, including questflags and globalflags, its called by the quest itself(lua) but is handled on game server and its declaration its c++ code. What you could do is adding a new function in client binary to pythonnetworkmodule so your py files can call this function which sends a request packet (HEADER_CG) to game server which performs any check you want on said flags and sends the response back to client (HEADER_GC), then you could have a BINARY_CALLBACK (game.py function accessible by client binary) which performs your desired python function. Hope it was clear, if not just tell me which part isn't clear and i'll make some example code as i get back home.

  • Love 2
Link to comment
Share on other sites

Acum 5 ore, OtherChoice a spus:

each quest function, including questflags and globalflags, its called by the quest itself(lua) but is handled on game server and its declaration its c++ code. What you could do is adding a new function in client binary to pythonnetworkmodule so your py files can call this function which sends a request packet (HEADER_CG) to game server which performs any check you want on said flags and sends the response back to client (HEADER_GC), then you could have a BINARY_CALLBACK (game.py function accessible by client binary) which performs your desired python function. Hope it was clear, if not just tell me which part isn't clear and i'll make some example code as i get back home.

Thank you for your answer, it made me understand a little how this works. 

What I want to do is only quest - client related. For example I want an UI button to be displayed only if the variable declared by quest has a certain value. Do you think this is possible only with python(client) and quest(lua) ? 

I would really appreciate an example if you have one. Something like, if you could make a present game button (e.g.:  Inventory Button from the bottom bar) be displayed only when quest variable is 1 that would be the easiest i think. I'm sorry for asking for this, I'm not good at it.

Link to comment
Share on other sites

22 hours ago, HisaoShou said:

Thank you for your answer, it made me understand a little how this works. 

What I want to do is only quest - client related. For example I want an UI button to be displayed only if the variable declared by quest has a certain value. Do you think this is possible only with python(client) and quest(lua) ? 

I would really appreciate an example if you have one. Something like, if you could make a present game button (e.g.:  Inventory Button from the bottom bar) be displayed only when quest variable is 1 that would be the easiest i think. I'm sorry for asking for this, I'm not good at it.

It's not possible to have only python and quest in this system since python will access game variables only through python modules inside client binary, i'll send you an example in a few minutes

Link to comment
Share on other sites

  • Solution

Packet.h (both client and server source)

add where HEADER(s) are declared
HEADER_GC_FLAG_RESPONSE					= 156, //any number still not used
HEADER_CG_FLAG_REQUEST					= 157, // same here

add where struct(s) are declared
typedef struct packet_flag_response
{
	BYTE	header;
	char*	FlagName;
} TPacketCGFlagRequest;

typedef struct packet_flag_response
{
	BYTE	header;
	DWORD	dwFlag;
} TPacketGCFlagResponse;

PythonNetworkStreamPhaseGame.cpp (client binary)

In function GamePhase() add below other cases:
case HEADER_GC_FLAG_RESPONSE:
				ret = RecvFlagResponsePacket();
				break;
Then add 2 new bools:
bool CPythonNetworkStream::SendFlagRequestPacket(char* flag)
{
	TPacketCGFlagRequest pPack;
	pPack.header = HEADER_CG_FLAG_REQUEST;
	pPack.FlagName = flag;

	if (!Send(sizeof(ItemInfoLoadPacket), &ItemInfoLoadPacket))
		return false;

	return SendSequence();
}


bool CPythonNetworkStream::RecvFlagResponsePacket(){
	TPacketGCFlagResponse pPack;
	if (!Recv(sizeof(TPacketGCFLagResponse), &pPack))
		return false;
	DWORD flag = pPack.dwFlag;
	CInstanceBase * pInstPlayer = CPythonCharacterManager::Instance().GetMainInstancePtr();
	if (pInstPlayer)
	{
		PyCallClassMemberFunc(m_apoPhaseWnd[PHASE_WINDOW_GAME], "BINARY_AddFlagValue", Py_BuildValue("(i)", flag));
	}
	return true;
}

In PythonNetworkStream.h add the definition of the previous function (client binary)

after
bool RecvPartyParameter();
add
bool RecvFlagResponsePacket();

In PythonNetworkStreamModule.cpp add:

PyObject* netFlagRequest(PyObject* poSelf, PyObject* poArgs)
{
	char* flag;

	if (!PyTuple_GetString(poArgs, 0, &flag))
	{
		return Py_BuildException();
	}

	CPythonNetworkStream& rns = CPythonNetworkStream::Instance();
	rns.SendFlagRequestPacket(flag);

	return Py_BuildNone();
}

then in void initnet() add
{ "SendFlagRequest",						netSendFlagRequest,						METH_VARARGS },

that's client binary example now we move on game server

Packet.h edited as above

input_main.cpp

case HEADER_CG_FLAG_REQUEST:
			{
				FlagRequest(ch, c_pData);
			}
			break;
then add a new void (and of course its definition in input_main.h)
void CInputMain::FlagRequest(LPCHARACTER ch, const char* c_pData)
{
	TPacketCGFlagRequest pRequest = (TPacketCGFlagRequest*)c_pData;
	TPacketGCFLagResponse pResponse;
	pResponse.header = HEADER_GC_FLAG_RESPONSE;
	pResponse.flag = ch->GetQuestFlag(pRequest.FlagName);
	ch->GetDesc()->Packet(&pResponse, sizeof(TPacketGCFlagResponse));
}

packet_info.cpp

in CPacketInfoCG::CPacketInfoCG() add
Set(HEADER_CG_ITEM_INFO_LOAD, sizeof(TPacketCGItemInfoLoad), "ItemInfoLoad", true);

and we're done server side, now there's only python part left and since i do not know your code i'll make a very simple example:

in game.py add
def BINARY_AddFlagValue(self, flag):
  if flag == YOUR_VALUE:
    #do something like self.YourButton.Show() if self.YourButton is part of game.py else if your element is part of another python file we call uiexample.py which gets initialized in game.py like self.myexample = uiexample.ExampleClass() do self.myexample.YourButton.Show() or self.myexample.MyCustomFunction() which handles visibility of the button
#INVENTORY BUTTON EXAMPLE:
   	self.interface.ShowInventoryButton()
    
#then in interfacemodule.py in class interface add
	def ShowInvetoryButton(self):
    	self.wndTaskBar.GetChild("InventoryButton").Show()
Spoiler

now to have the whole process start we need to call net.SendFlagRequest("flagname") but with the current example we need to constatly know when the flag changes so the best way to avoid a whole bunch of server traffic is to have the server tell the cilent when to call net.SendFlagRequest("flagname") and we just need to add a check inside CHARACTER::SetQuestFlag(const std::string& flag, int value) (game/char.cpp) and when our check flag == interestedflag is true as we did above we add a new packet struct, header, and client handle (we only need game to client so just HEADER_GC) which gets sent. When Client recives this packet it will call our function SendFlagRequestPacket(char* flag) and the process will start. Notice that with the example of interface button and new packet (game -> to -> client only) there were no need to call net.SendFlagRequest("flagname") from python but directly from binary SendFlagRequestPacket(char* flag), THIS IS NOT ALWAYS TRUE, you might find yourself using both calls upon different situation. MOREOVER the whole example purpose is not to have a quick and clean code but to understand how things work so yeah, we could have skipped a whole part and have it straight handled by game source (eg. in SetQuestFlag instead of calling our new packet that will make the whole process start, we simply have our FLAG == YOUR_VALUE check here and on successful check we send our response packet back to client and it will call game.py BINARY_SetFlagValue which handles the visibility of the button. As I said its way shorter and better but these way you have access to a better code blueprint in my opinion)

It should be complete, I could have missed a declaration or something since i coded it without source by hand so if any problem just show your errors. Hope I helped!

P.S. As you might have noticed i totally skipped lua part since its function pc.getqf is c++ code and its definition is CHARACTER::GetQuestFlag so no need to change anything on lua, just do your quest, use pc.setqf and you will be able to access just calling in game source ch->GetQuestFlag(flagname)

  • Love 1
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

Announcements



×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.