[request/help] Correct way of sending EntityMove/Jump packets?

09/02/2013 22:56 dospy#1
this have been bothered me for a while and i know that i am not the only one who struggles to break this tough nut. The methodology of sending the Jump packets to clients is a pain in the ass as there are many things to be considered in order to implement a bug-free method of doing it.
Let's say that player X jumps(distance between them > max_view_distance before jump) near player Y(distance between them <= max_view_distance after jump), both packets for entity information are sent(
Code:
X->Send((EntityPacket)Y); Y->Send((EntityPacket)X);
), however, it happens sometimes that either player X does not see player Y or viceversa/
Any thoughts how to fix this?
the method i am using right now is the one from the HybridCo source for client 5017:
Code:
void PlayerJump(CGameClient *Client, DataPacket *Ptr)
{
	WORD X = LOWORD(Ptr->dwParam);
	WORD Y = HIWORD(Ptr->dwParam);

	if (Distance(X, Y, Client->Entity->X, Client->Entity->Y) > MAX_SCREEN_WIDTH)
	{
		Client->Socket->Disconnect();
		return;
	}

	Client->Entity->X = X;
	Client->Entity->Y = Y;

	SendRangePacket(Ptr, Client->Entity, true);

	LoadScreen(Client, NULL);
}
void SendRangePacket(void* Packet, IMapObject* mapObj, bool SendSelf, StdConquerCallback Callback, bool Delete)
{
	CGameClient* Client;
	CDatabaseRoot::Core->Clients->ObtainSyncHandle();
	for (int i = 0; i < CDatabaseRoot::Core->Clients->Count; i++)
	{
		Client = CDatabaseRoot::Core->Clients->Elements[i];
		if (Client->Entity->Map == mapObj->Map)
		{
			if (Distance(Client->Entity->X, Client->Entity->Y, mapObj->X, mapObj->Y) <= MAX_VIEW_DISTANCE)
			{
				if (Client->ID == mapObj->UID)
				{
					if (SendSelf)
					{
						Client->Send(Packet);
						if (Callback != NULL)
							Callback(mapObj, Client->Entity, NULL);
					}
				}
				else
				{
					Client->Send(Packet);
					if (Callback != NULL)
						Callback(mapObj, Client->Entity, NULL);
				}
			}
		}
	}
	CDatabaseRoot::Core->Clients->FreeSyncHandle();
	if (Delete)
	{
		delete[] Packet;
	}
}
void LoadScreen(CGameClient *Client, DataPacket *Ptr)
{
	if (Ptr != NULL) // LoadScreen() Standerd
	{
		Client->Screen->Wipe();

		Ptr->ID = DATA_ID_SET_MAP_COLOR;
		Ptr->dwParam = 0xFFFFFFFF;
		Client->Send(Ptr);
		// <todo = weather>
		// </todo>
	}
	else
	{
		Client->Screen->Cleanup();
	}

	// <Clients>
	CDatabaseRoot::Core->Clients->ObtainSyncHandle();
	for (int i = 0; i < CDatabaseRoot::Core->Clients->Count; i++)
	{
		CGameClient* iClient = CDatabaseRoot::Core->Clients->Elements[i];
		if (iClient->Entity->Map == Client->Entity->Map &&
			iClient->ID != Client->ID)
		{
			if (Distance(Client->Entity->X, Client->Entity->Y,
					iClient->Entity->X, iClient->Entity->Y) <= MAX_SCREEN_WIDTH)
			{
				if (Client->Screen->Add(iClient->Entity))
				{
					Client->Send(iClient->Entity->Data);
					if (Ptr != NULL) // LoadScreen() Standerd
					{
						iClient->Screen->Add(Client->Entity);
						iClient->Send(Client->Entity->Data);
					}
				}
			}
		}
	}
	CDatabaseRoot::Core->Clients->FreeSyncHandle();
	// </Clients>
}
09/02/2013 23:00 Super Aids#2
however, it happens sometimes that either player X does not see player Y or viceversa/
Any thoughts how to fix this?


Screen problem, not the way jump is handled.

Also you shouldn't clear the whole screen for every jump (Ex. reloading the whole screen.)
You should first check existing entities in the screen whether they're outside of the screen and then check for new entities that has to be added to it.
09/02/2013 23:49 dospy#3
Quote:
Originally Posted by Super Aids View Post
[I]
Also you shouldn't clear the whole screen for every jump (Ex. reloading the whole screen.)
You should first check existing entities in the screen whether they're outside of the screen and then check for new entities that has to be added to it.
yes i know, this is the original HybridCO code(i've done lots of changed to it), but i posted this code for showing the method i use.

i really doubt that the problem is not in the code tho as this happens way too often to be a client bug or whatever and i don't recall this happening on the TQ servers either. Anyway, i know a friend who is having the same problem on a 5165 client although he reduced the distance from which he sends the spawn entity packet(by using the mathematical formula for distance between 2 points rather than max(x1-x2, y1-y2)). it might be that the server computes the coordinates instantly when the jump request is send ignoring the mid-air time and this might differ from the client?
thx for the fast response.

PS: if anyone got a way around this problem i would greatly appreciate your insight. Thx in advance
EDIT: or by screen problem you mean the way UserScreen is updated and not a client bug?
09/04/2013 00:35 dospy#4
found the problem; it figures that you have to send the jump packet to the client that request the Jump FIRST so he can acknowledge the coordinates before trying to spawn entities in his screen(otherwise the client computes the distance between you and the entity requested and sees it as being to far away);

EDIT: #can be closed

Will post here for anybody who is wondering/struggling with this too
Code:
void PlayerJump(CGameClient *Client, DataPacket *Ptr)
{
	WORD X = LOWORD(Ptr->dwParam);
	WORD Y = HIWORD(Ptr->dwParam);
	
	if(Distance(X, Y, Client->X, Client->Y) > MAX_SCREEN_WIDTH)
	{
		Client->Socket->Disconnect( );
		return;
	}

	Client->SetCoords(X, Y);
	Client->Send(Ptr);	// VERY IMPORTANT! SEND THE PACKET TO THE REQUESTING PLAYER FIRST
	// So he can acknowledge the new location before trying to spawn chars into he's screen

	CDatabaseRoot::Core->Clients->ObtainSyncHandle( );
	for(int i = 0; i < CDatabaseRoot::Core->Clients->Count; i++)
	{
		CGameClient *C = CDatabaseRoot::Core->Clients->Elements[i];
		
		if(C->Map != Client->Map || C->ID == Client->ID)
			continue;

		if(Distance(Client->PrevX, Client->PrevY, C->X, C->Y) <= MAX_VIEW_DISTANCE)
		{
			// distance dintre between characters BEFORE jump <= MAX_VIEW_DISTANCE

			C->Send(Ptr);

			if(Distance(Client->X, Client->Y, C->X, C->Y) > MAX_VIEW_DISTANCE)
			{
				// distance between characters AFTER jump > MAX_VIEW_DISTANCE
				// we DO NOT need to take care of sending EntityRemovePacket here 
				// as the client automatically removes the entity if it goes out of range
				
				Client->ScreenObjects->Remove(C);
				C->ScreenObjects->Remove(Client);
			}
		}
		else
		{
			// distance between the characters BEFORE jump > MAX_VIEW_DISTANCE
			// TODO: spawn at the T point and then send the jump packet so the char appears
			// to be jumping into the screen rather than magically spawning

			if(Distance(Client->X, Client->Y, C->X, C->Y) <= MAX_VIEW_DISTANCE)
			{
				// distance between characters AFTER jump <= MAX_VIEW_DISTANCE

				Client->ScreenObjects->Add(C);
				Client->Send(C->Data);
				
				C->ScreenObjects->Add(Client);
				C->Send(Client->Data);
			}
		}
	}
	CDatabaseRoot::Core->Clients->FreeSyncHandle( );

	// TODO: monsters, items, NPCs and other Objects needed to be handled
}