Forum

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

Discuss CSQC related programming.

Moderator: InsideQC Admins

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

Postby toneddu2000 » Sun Sep 14, 2014 9:22 pm

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.
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby Spike » Sun Sep 14, 2014 11:06 pm

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.
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Mon Sep 15, 2014 3:18 pm

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
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Tue Sep 16, 2014 11:32 am

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
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby Spike » Tue Sep 16, 2014 2:45 pm

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.
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Tue Sep 16, 2014 3:53 pm

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:
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby Spike » Tue Sep 16, 2014 5:01 pm

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).
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Tue Sep 16, 2014 9:47 pm

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
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby Spike » Tue Sep 16, 2014 10:50 pm

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.
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Tue Sep 16, 2014 11:02 pm

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.
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby Spike » Wed Sep 17, 2014 12:33 am

you missed ClientConnect and ClientDisconnect too.
Spike
 
Posts: 2878
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Wed Sep 17, 2014 1:15 am

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
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Mon Sep 22, 2014 6:38 pm

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
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby toneddu2000 » Wed Sep 24, 2014 10:09 am

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:
toneddu2000
 
Posts: 1301
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Why sending entity with FTE is so hard?

Postby ceriux » Wed Sep 24, 2014 9:09 pm

are you sure you're spawning your player at the proper entities ?
User avatar
ceriux
 
Posts: 2223
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Next

Return to CSQC Programming

Who is online

Users browsing this forum: No registered users and 1 guest