[SOLVED]Why sending entity with FTE is so hard?

Discuss CSQC related programming.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

[SOLVED]Why sending entity with FTE is so hard?

Post by toneddu2000 »

Ola guys!
I'm here to bother you again, CSQC mentors! :D

CASE:
simple mixture of SSQC + CSQC to spawn a player on a map. Nothing else. Let's start simple.

DP
on the client I create the SendEntity function

Code: Select all

float Player_SendEntityToCSQC()
{
	WriteByte(MSG_ENTITY, ENT_CSPLAYER);
	WriteByte(MSG_ENTITY, player.frame);	//FIXME NEED COMPRESSION
	WriteByte(MSG_ENTITY, player.modelindex);
	WriteByte(MSG_ENTITY, player.PersID);
	WriteShort(MSG_ENTITY, player.angles_x);
	WriteShort(MSG_ENTITY, player.angles_y);
	WriteShort(MSG_ENTITY, player.angles_z);
	WriteCoord(MSG_ENTITY, player.origin_x);
	WriteCoord(MSG_ENTITY, player.origin_y);
	WriteCoord(MSG_ENTITY, player.origin_z);
	WriteShort(MSG_ENTITY, player.velocity_x);
	WriteShort(MSG_ENTITY, player.velocity_y);
	WriteShort(MSG_ENTITY, player.velocity_z);
	return TRUE;
}

void PutClientInServer()
{
	local				entity	playerspwn;
	
	playerspwn = 		Player_FindStartPoint();
	player = self;		//now use player as global player entity
	player.classname = "player";
	player.health = 100;
	player.takedamage = DAMAGE_AIM;
	player.solid = SOLID_SLIDEBOX;
	player.movetype = MOVETYPE_WALK;
	player.origin = playerspwn.origin;
	player.angles = playerspwn.angles;
	player.fixangle = TRUE;		// turn this way immediately
	precache_model(PLAYER_MODEL);
	setmodel (player, PLAYER_MODEL); 
	setsize (player, VEC_HULL_MIN, VEC_HULL_MAX);
	player.flags = FL_CLIENT;
	player.SendEntity = Player_SendEntityToCSQC;//VITAL!
	self.Version = self.Version + 1;//Is this needed?
}
On CSQC I read these values in the CSQC_Ent_Update() function and launch the "check player" func if player is new

Code: Select all

void Player_Startup(float newent)
{
	if (newent)
	{
		player.classname = "player";
		setmodelindex(self, player.modelindex); 
		self.drawmask = MASK_NORMAL; // makes the entity visible
	}
}

void CSQC_Ent_Update (float isnew)
{
	self.enttype = ReadByte();
	switch(self.enttype)
	{
		//receiving Player
		case 	ENT_CSPLAYER:
			player = self;
			//read here all send values from client.c in ssqc	
			player.animation = ReadByte();
			player.modelindex = ReadByte();
			player.PersID = ReadByte();
			player.angles_x = ReadShort();
			player.angles_y = ReadShort();
			player.angles_z = ReadShort();
			player.origin_x = ReadCoord();
			player.origin_y = ReadCoord();
			player.origin_z = ReadCoord();
			player.velocity_x = ReadShort();
			player.velocity_y = ReadShort();
			player.velocity_z = ReadShort();

			Player_Startup(isnew); // IT WILL HAPPEN JUST ONCE - in cs/player/startup.c	
		break;
	}

}
DONE.
I can see my shiny Quake3 map and that's it

FTE
...now here come the troubles!!
If you do something similar above, the map won't load: black screen, no errors, no warnings (and that's a bit annoying, I must say)
I want to clarify that I completely replaced CSQC DP defs with the command "pr_dumpplatform -FFTE -TCS -O csplat".

Then I replaced the CSQC part with cleancsqc.
This time map loads but only if I comment the SSQC and CSQC part of read/write values.

I studied A LOT csqctest and it uses a pretty obscure (at least for me) construction to send values from SSQC to SSQC.
Both SSQC AND CSQC share a classes.qc file

Code: Select all

#ifndef _CLASSES_QC_
	#define _CLASSES_QC_
#define eclasses	eclass(CLASS_ROCKET, ParseRocketClass)	eclass(CLASS_GIB, ParseGibbing)	eclass(CLASS_PLAYER, ParsePlayer) eclass(CLASS_EXPLOSION, ParseExplosion) eclass(CLASS_NAIL, ParseNailClass)
#ifdef CSQC
#define eclass(eid,func) void(float isnew) func;
		eclasses
#undef eclass
#endif
	enum {
#define eclass(eid,func) eid,
		eclasses CLASS_MAX
#undef eclass
		};

	enum {
		GIB_BAD,
		GIB_PLAYER,
		GIB_SOLDIER,
		GIB_ZOMBIE,
		GIB_DEMON,
		GIB_OGRE,
		GIB_DOG,
		GIB_WIZARD,
		GIB_SHAMBLER,
		GIB_KNIGHT,
		GIB_HELLKNIGHT,
		GIB_FISH,
		GIB_ENFORCER,
		GIB_SHALRATH,
		GIB_MAX
	};
#else
	#ifdef CSQC
		nonstatic var void(float isnew) ParseEntityClass[ ] = {
#define eclass(eid,func) func ,
		eclasses
#undef eclass
__NULL__
		};
	#endif
#endif
Interested line is #define eclasses eclass(CLASS_ROCKET, ParseRocketClass) eclass(CLASS_GIB, ParseGibbing) eclass(CLASS_PLAYER, ParsePlayer) eclass(CLASS_EXPLOSION, ParseExplosion) eclass(CLASS_NAIL, ParseNailClass)

Now, my mind completely refuses to understand the #DEFINE directives, ok, but... what's the difference with the code I used?

CLASS_PLAYER is used in SSQC/client.qc

Code: Select all

WriteByte(MSG_ENTITY, CLASS_PLAYER);
and ParsePlayer in CSQC/player.qc

Code: Select all

/*
this file handles the local player, and marshalling between the different sorts of player models.
RefreshPlayer: Called from the engine each time a player (ent with player.mdl) is about to be drawn
ParsePlayer: Called from CSQC_Ent_Parse for csqc protocol ents
*/
nonstatic void(float isnew) ParsePlayer =
{
	local float f;

	f = readbyte();

	if (f != self.frame || isnew)
	{
		self.frame2 = self.frame;
		self.lerptime = time;
		self.frame = f;
	}

	self.angles_x = readbyte()*(360/256);
	self.angles_y = readbyte()*(360/256);
	self.origin_x = readcoord();
	self.origin_y = readcoord();
	self.origin_z = readcoord();
	self.velocity_x = readshort();
	self.velocity_y = readshort();
	self.velocity_z = readshort();
	self.colormap = self.entnum;

	self.sveffects = readbyte();

	RefreshPlayer(isnew);
};
First, I can't understand where ParsePlayer is called (not in CSQC_Ent_Update for sure), second: again.. what's the difference with the code I used? :D

Last funny thing: If I compile csqctest on svn FTE, quake(1)maps work great, instead my custom Quake 3 maps are not visible again!! :shock:

Here a little test I made with DP and FTE. Very basic pack: a map, an exe(only Win version, sorry), simple code and a bunch of silly assets!
In the FTE zip you find two code folders, one similar to DP's one (code_simple), with the only difference in FTE functions names and defs files, the other (code_cleancsqc) with the csqc part completely taken from cleancsqc
Please let me know if someone had same problems with FTE!
Thanks in advance
Last edited by toneddu2000 on Thu Sep 25, 2014 12:07 am, edited 1 time in total.
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Why sending entity with FTE is so hard?

Post by Spike »

ignore .Version in both engines. its deprecated and may end up getting removed some time.
use SendFlags instead. set some flag, any flag, each time the entity is updated just like you currently do with Version. For players, that would generally be in PlayerPostThink. you're meant to be able to set different flags to ensure your SendEntity function gets passed a copy of only the flags that were updated, so you can be more network efficient.

the #define magic pulls out different args for the different times that the eclasses define is evalulated, allowing the list of classes to be defined in only one place instead of having to shove info about each and every new class in 5+ different QC files/locations.
its me overcomplicating things by trying to be clean by being terse.

its been a while since I actually tried a mod in which ownership of players is given purely to qc. I guess its time I re-tested that.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

ignore .Version in both engines. its deprecated and may end up getting removed some time.
Ok, removed
use SendFlags instead. set some flag, any flag, each time the entity is updated just like you currently do with Version. For players, that would generally be in PlayerPostThink. you're meant to be able to set different flags to ensure your SendEntity function gets passed a copy of only the flags that were updated, so you can be more network efficient.
Could you be more specific, maybe with an example? In csqctest the only SendFlags I found is:
self.SendFlags = FULLSEND;
after
self.SendEntity = SendPlayer;
in PutClientInServer

Same structure in other files like weapons, player and ai.

thanks again Spike
Meadow Fun!! - my first commercial game, made with FTEQW game engine
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

No, it doesn't work.
I added

Code: Select all

self.SendFlags = FULLSEND;
In PutClientInServer(), after

Code: Select all

player.SendEntity = Player_SendEntityToCSQC;
and in

Code: Select all

void PlayerPostThink()
{
	self.SendFlags = FULLSEND;
}
and I defined:

Code: Select all

 #define FULLSEND 0xffffff
In CSQC, btw, I didn't add anything. There's something should I have added in CSQC?
Help! :D
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Why sending entity with FTE is so hard?

Post by Spike »

works for me.

ssqc:

Code: Select all

float() playersend =
{
	WriteCoord(MSG_ENTITY, self.origin_x);
	WriteCoord(MSG_ENTITY, self.origin_y);
	WriteCoord(MSG_ENTITY, self.origin_z);
	return TRUE;
};

inside PutClientInServer:
	self.SendEntity = playersend;
csqc:

Code: Select all

#pragma PROGS_DAT "csprogs.dat"
#define CSQC
#include "fteextensions.qc"

entity somerandomplayer;
void(float isnew) CSQC_Ent_Update =
{
	setmodel(self, "progs/player.mdl");
	self.renderflags = RF_EXTERNALMODEL;
	self.drawmask = 1;
	self.origin_x = readcoord();
	self.origin_y = readcoord();
	self.origin_z = readcoord();

	somerandomplayer = self;
};

void(float vwidth, float vheight, float notmenu) CSQC_UpdateView =
{
	clearscene();
	setviewprop(VF_ORIGIN, somerandomplayer.origin);
	setviewprop(VF_ANGLES, view_angles);
	addentities(1|2);
	renderscene();
};
of course, playersend will only be called when you update the player's SendFlags field, and thus the view won't move from the initial spawn position until you do
give self.SendFlags=1
can do that, if you have cheats on. which allows playersend to be called again, thus ultimately updating the VF_ORIGIN call in csqc.
obviously it doesn't know which player the local player is, etc, as it doesn't bother with player_localentnum. this is just quick test code.

works just fine for me.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

thanks Spike for your code but I'm starting to think that my pc is possessed! :D

progs.src

Code: Select all

#pragma sourcefile ss/ssprogs.src
#pragma sourcefile cs/csprogs.src
ss/ssprogs.src

Code: Select all

#pragma PROGS_DAT "../progs.dat"
#define SSQC
#include "../common/fteextensions.qc"
#include "../common/commondefs.qc"

void main(){}

void SetNewParms(){}

void SetChangeParms(){}

void Precache(){}

void worldspawn(){}

void StartFrame(){}

float playersend()
{
   WriteCoord(MSG_ENTITY, self.origin_x);
   WriteCoord(MSG_ENTITY, self.origin_y);
   WriteCoord(MSG_ENTITY, self.origin_z);
   return TRUE;
}

void PutClientInServer()
{
	setmodel (self, PLAYER_MODEL); 
	setsize (self, '128 128 128', '-128 -128 -128');
	self.SendEntity = playersend;
}

cs/csprogs.src

Code: Select all

#pragma PROGS_DAT "../csprogs.dat"
#define CSQC
#include "../common/fteextensions.qc"
#include "../common/commondefs.qc"

void CSQC_Ent_Update(float isnew)
{
   setmodel(self, PLAYER_MODEL);
   self.renderflags = RF_EXTERNALMODEL;
   self.drawmask = 1;
   self.origin_x = readcoord();
   self.origin_y = readcoord();
   self.origin_z = readcoord();
   somerandomplayer = self;
}

void CSQC_UpdateView(float vwidth, float vheight, float notmenu)
{
   clearscene();
   setviewprop(VF_ORIGIN, somerandomplayer.origin);
   setviewprop(VF_ANGLES, view_angles);
   addentities(1|2);
   renderscene();
}
common/commondefs.qc

Code: Select all

string PLAYER_MODEL = "models/red.iqm";
entity somerandomplayer;
RESULT: unending list of:

Code: Select all

PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 007126A1)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 007126A1)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 007126A1)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 00711C44)
PR_ExecuteProgram: NULL function from exe (address 007126A1)
:shock:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Why sending entity with FTE is so hard?

Post by Spike »

you have no playerprethink nor playerpostthink (and thus also still lacks any automatic SendFlags updates).
your ssqc is not setting the player's origin (spawning at '0 0 0').
your player is left with movetype_none (completely unable to move even if you use the give command to poke stuff).
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

Thanks Spike for the help, really appreciated!
you have no playerprethink nor playerpostthink (and thus also still lacks any automatic SendFlags updates).
yeah, right! Sorry, I typed all the code a little bit hasty
your ssqc is not setting the player's origin (spawning at '0 0 0').
ok, done
your player is left with movetype_none (completely unable to move even if you use the give command to poke stuff).
done now

ssprogs.src

Code: Select all

#pragma PROGS_DAT "../progs.dat"
#define SSQC
#include "../common/fteextensions.qc"
#include "../common/commondefs.qc"
#include "../common/quakedefs.qc"

//needed functions, placeholders just for now
void main(){}
void SetNewParms(){}
void SetChangeParms(){}
void Precache(){}
void worldspawn(){}
void StartFrame(){}

entity Player_FindStartPoint()
{
	local entity spot;
	
	spot = find(world,classname,"info_player_start");
	if(!spot){
		error ("PutClientInServer: no starting point on level");
	}
	return spot;
}

float playersend()
{
   WriteCoord(MSG_ENTITY, self.origin_x);
   WriteCoord(MSG_ENTITY, self.origin_y);
   WriteCoord(MSG_ENTITY, self.origin_z);
   return TRUE;
}

void PlayerPreThink(){}

void PlayerPostThink()
{
	self.SendFlags = FULLSEND;
}

void PutClientInServer()
{
	local				entity	playerspwn;
	
	playerspwn = 		Player_FindStartPoint();
	self.origin = playerspwn.origin;
	self.movetype = MOVETYPE_WALK;
	precache_model(PLAYER_MODEL);
	setmodel (self, PLAYER_MODEL); 
	setsize (self, '128 128 128', '-128 -128 -128');
	self.SendEntity = playersend;
	self.SendFlags = FULLSEND;
}

void info_player_start()
{
}

csprogs.src

Code: Select all

#pragma PROGS_DAT "../csprogs.dat"
#define CSQC
#include "../common/fteextensions.qc"
#include "../common/commondefs.qc"

//needed functions, placeholders just for now
void CSQC_Init(float apilevel, string enginename, float engineversion){}
void CSQC_Shutdown(void){}
float CSQC_ConsoleCommand(string strMessage){return 0;}
float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent ={return FALSE;};

void CSQC_Ent_Update(float isnew)
{
   self.origin_x = readcoord();
   self.origin_y = readcoord();
   self.origin_z = readcoord();
   somerandomplayer = self;
}

void CSQC_UpdateView(float vwidth, float vheight, float notmenu)
{
   clearscene();
   setviewprop(VF_ORIGIN, somerandomplayer.origin);
   setviewprop(VF_ANGLES, view_angles);
   addentities(1|2);
   renderscene();
}
Result (just one line, now):

Code: Select all

PR_ExecuteProgram: NULL function from exe (address 007129B7)
Did I miss something?
On DP, same code, works

Thanks again
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Why sending entity with FTE is so hard?

Post by Spike »

your size is inside out, and your player is overweight.
(I fixed a bug some time around the classic preset that was snapping the player to '0 0 0' if they spawned stuck, and your messed up size will generally trigger that)
setsize (self, '-16 -16 -24', '16 16 32');
use instead or something

also, your player has health=0, which generally results in movetype_walk not being allowed to accelerate the player, meaning you can't move. give him some health, make him not dead.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

nope, adjusted size and added health but still

Code: Select all

PR_ExecuteProgram: NULL function from exe (address 007129B7)
The problem is not that I got stuck, the problem is that map doesn't load at all. Only black screen with above line.
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Why sending entity with FTE is so hard?

Post by Spike »

you missed ClientConnect and ClientDisconnect too.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

Thanks Spike, at least that error message disappeared! :D
Still black screen, though.
Can you please post the code you said it worked on your pc? Because, if neither that doesn't work on my pc, well, I'll see later what to do! :D
Thanks again
Meadow Fun!! - my first commercial game, made with FTEQW game engine
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

ok, today I had some spare time and I tried another way. I compiled csqctest and I opened start.bsp: no problem (except all the original id models it couldn't find).
Then I opened my simple.bsp map. Lots of errors about id models not found. Added progs folder. Re-run:

Code: Select all

Warning: Runtime-linked function MenuEventFunc could not be found (loading csprogs.dat)
huh? Well, delete MenuEventFunc function from cs/defs.qc/menu.qc and tetris.qc, delete in entrypoints.qc all the cases regarding tetris/skinchooser and randomskin.
re run simple.bsp map

Code: Select all

map simple
Loaded progs progs.dat

Changing map...


-------------------


Player entered the game
Black screen and nothing else. If I understood correctly, "Changing map..." after map loading is not good, because engine is skipping current map. Am I wrong?
So.. it's quake3 map the problem? No, because, if I delete csprogs.dat and csprogs.lno (by the way, what the **** is that file? :D ) map is loaded beautyfully with player running happy along it!

I already asked gb (who wrote a beautiful introduction to FTEQW engine) about this funny map problem once and he told he had no problem with q3maps and fte, but I forgot to ask him if he tested csqctest AND q3maps...
Anyone has experienced same problem with Client Side quakec and quake3 maps?
Thanks guys, I'd really like to understand this issue
Meadow Fun!! - my first commercial game, made with FTEQW game engine
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Post by toneddu2000 »

come on guys, really NO ONE used csqctest on FTE and tried a normal quake3 map? Well that's impossible! :D If no one can help I'll be forced to use Darkplaces, but I'm pretty disappointed I couldn't figure out this basic issue. Well, thinking about it, maybe it's better fail at start than build a completely project and then realize it's impossible to go further with the development! :lol:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: Why sending entity with FTE is so hard?

Post by ceriux »

are you sure you're spawning your player at the proper entities ?
Post Reply