ScratchCSQC?

Discuss CSQC related programming.
JasonX
Posts: 422
Joined: Tue Apr 21, 2009 2:08 pm

ScratchCSQC?

Post by JasonX »

I was wondering how nice would it be to have a project like ScratchQC, but aimed at CSQC features and modern engines. Specially when dealing with MP stuff, like vweaps. Any guru up to it? :D
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: ScratchCSQC?

Post by Spike »

hey, don't look at me. I'm too lazy to faff around re-writing code, and too lazy to add lots of animations to models too.
besides, most csqc is from scratch anyway. :P
but yeah, a modern full from-scratch mod is kinda needed. the issue, however, is exactly how modern it should be - that stuff can be really fiddly.

I believe shpuld's work-in-progress nba2 mod is from scratch, but its not exactly regular deathmatch. its been a while since he mentioned it too.
Shpuld
Posts: 106
Joined: Sat Feb 13, 2010 1:48 pm

Re: ScratchCSQC?

Post by Shpuld »

As Spike mentioned, I have a FTEQW only full scratch base (both server and client), that I've used for mobster massacre, new nba and also this bullet hell game I'm working on. I have it on github https://github.com/shpuld/CleanQC4FTE but it's still a "work in progress" version, buggy code, a lot of missing but planned features, not completely clean either. Still I'd say it's useful especially if you're a CSQC newbie and don't know how to do it yourself.

Please also note the description, I haven't put any of the temprary art assets I have up, so you'll need to replace "player.iqm", "pistol.iqm", etc... or rip a lot of the stuff out if you want.

EDIT: also worth mentioning, since it's from scratch, the GPL doesn't apply to it, so I decided to put it under MIT, which should be much more attractive to people who might want to work on more commercial projects and open QC isn't a good option.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: ScratchCSQC?

Post by toneddu2000 »

EDIT: also worth mentioning, since it's from scratch, the GPL doesn't apply to it, so I decided to put it under MIT, which should be much more attractive to people who might want to work on more commercial projects and open QC isn't a good option.
Great guy, Shpuld! :D
On my own, I'm creating a fteqw project aimed only to discover and (as much as possible) document skeletal animations. It will be released soon as CC0 license
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: ScratchCSQC?

Post by Seven »

Hello,

I have a question regarding csqc and didnt want to start a new thread, so I picked this one. I hope you do not mind.

My question is:
In ssqc you can check which .worldtype the currently played map is via:
if (world.worldtype == 0)
{
do something... this is a medieval map!
}



But how would you do this in csqc ?
I added this into csqc´s defs.qc:
entity world;
.float worldtype;


But when I use something like I would use in ssqc (shown above), it doesnt work.
I do not know what "world" is called in csqc and I do not know how .worldtype can be checked in csqc.
Can someone give me the necesarry code for this check ?

Thank you very much for your help in advance.

0: medieval
1: metal
2: base
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: ScratchCSQC?

Post by toneddu2000 »

Hi Seven!

from csqc_for_idiots.txt by spike
The main way to add a stat is with this builtin:
void(float num, float type, .__variant fld) clientstat = #232;
Which is typically invoked from worldspawn using something like:
clientstat(STAT_MYSUPERSTAT, EV_FLOAT, mysuperstat);
where mysuperstat was defined in defs.qc or whereever as:
.float mysuperstat;
And, as noted above, is read with getstatf(STAT_MYSUPERSTAT)
You'll want to share the STAT_FOO defines between ssqc+csqc.
But I guess that .worldtype, being related to world entity, instead of self.entity, it won't work.
Maybe using a globalstat:

Code: Select all

void(float num, float type, string name) globalstat = #233; /*
		Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass "foo"). */
But never used.

--------

Maybe I would do something like this - this will work ONLY in FTEQW - in Darkplaces you have to invent something else
1)Understood WHEN I want to know which worldtype is now (I don't know, when mouse is triggered, for example)
2)SSQC - in PlayerPostThink() (this is just an example)
write

Code: Select all

if(self.mouse1){
SendWorldType();
}
Then, above that, write

Code: Select all

void SendWorldType()
{
	msg_entity = self;
	WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
	WriteByte(MSG_MULTICAST, PE_WORLDTYPE);
	WriteByte(MSG_MULTICAST, world.worldtype);
	multicast(self.origin, MULTICAST_ALL);//Or MULTICAST_ONE. That depends to how many audience the messange needs to reach
}
3.In csqc add

Code: Select all

noref void() CSQC_Parse_Event =
{
local float rb = readbyte();   
if(rb == PE_WORLDTYPE){
             yourCSQCWorldTypeVar = readbute();//et voila'!Worldtype is saved in your yourCSQCWorldTypeVar 
	}
}
Than, wherever you need you can do

Code: Select all

if (yourCSQCWorldTypeVar == 0){
cprint("You're in medieval environment\n");
}
else if(yourCSQCWorldTypeVar == 1){
cprint("You're in metalenvironment\n");
}
//and so on..
Don't forget to declare your float yourCSQCWorldTypeVar in your cs defs and declare

Code: Select all

float PE_WORLDTYPE=1;
in your commondefs.
This is the only example that comes to my mind, sorry. You could try with stats but never tried. Let us know if it helps.


If it's a single player game you can just create a self .float field, copy worldtype into that and then send it via SendEntity - in this case - worldtype will be alwas update every frame or so
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: ScratchCSQC?

Post by Spike »

in CSQC_WorldLoaded, you can call getentitytoken a few times to parse the client's version of the entity lump / .ent file. this can be done without any ssqc changes. however, I do not believe this works in DP.
alternatively you can do this in ssqc: localcmd(sprintf("serverinfo wt %g\n", world.worldtype)); inside your worldspawn function, and worldtype = stof(serverkey("wt")); in your csqc. again, this will not work in DP.
alternatively you can use writebytes as toneddu2000, in order for it to work in DP, you will need to use MSG_ONE, and to hack at svc_tempentity. this will not work in saved games unless you use nosave hacks to detect when the game was saved etc (in fte, you would send from a sv_parseclientcommand 'enablecsqc' handler, to determine whenever the client's csqc has [re]started, while in dp you do not known when the client itself is up and running).
alternatively you can do a stuffcmd(self, sprintf("//worldtype %g\n", world.worldtype)); and scan for that prefix inside CSQC_Parse_StuffCmd. again, this will not normally work in saved games without extra work as described above.
alternatively you could use globalstat from worldspawn, along with a global that contains your value. however, globalstat does not exist in dp, but the server does at least have a chance of restoring the value automatically in saved games.
the penultimate option is clientstat as toneddu2000 described. logically this is the simplest method I can think of that will work in dp and should also persist in saved games - assuming the server remembers the mappings on load.
the final option is to use SendEntity. the server will automatically resend the entity when the game is loaded. you can add all sorts of other fields too, you can send strings without worrying about length truncations (unlike dp's stats). however, to resend the entity the entity must be within the client's pvs, and dp does not provide you with the ability to ensure this is always the case, unless you reuse the player's entity for this purpose.
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: ScratchCSQC?

Post by Seven »

Hello,

Just when you think you understood how Quake gamecode works and you slowly get an adult user,
you have to start all over again in csqc and become a young child again :)
But hey, that keeps us old farts in shape, no ? :P

First of all, Thank you very much for your answers and time writing and patience with me.
Now, that I have read a lot that DP cannot do, my options are getting really limited here on this topic.
But fortunately usually I do not use/need csqc, so this is a real seldom case.

Sorry, I didn’t mention what I need this for:
Yes, it is a single player mod. Some call it smc. It is developed for DP. Switching to FTE is unfortunately no option anymore,
as the effort to do so would be too much.
Intention is to have a separate key icon for the HUD (vanilla Quake has only 1 HUD icon set for all 3 key type sets, while each .worldtype uses a different key model as you know).
Furthermore to be able to use 2 different armor model sets incl. HUD icons (1 for base maps -> .worldtype = 2 and 1 for the other map types -> .worldtype = 0 and 1)

Here is what I understood so far from your explanations and which *should* work for dp:
Please tell me where my main mistakes are:



In ssqc´s defs.qc I add:

Code: Select all

.float this_is_the_worldtype_value;
In ssqc´s PutClientInServer I add:

Code: Select all

self.this_is_the_worldtype_value = world.worldtype;
and then this inside ssqc´s PlayerPreThink, so it gets send every frame (or would be once only in PutClientInServer enough ??):

Code: Select all

self.SendEntity = send_value_to_csqc;
with the function:

Code: Select all

float()send_value_to_csqc =
{
WriteCoord(MSG_ENTITY, self.this_is_the_worldtype_value);
return TRUE;
};

THEN in csqc I add this to defs.qc:

Code: Select all

float worldtype;
and can then simply add in my Sbar_Draw function (which gets updated every frame) ??

Code: Select all

worldtype = self.this_is_the_worldtype_value; 
I can now use the float “worldtype” to make my checks ??

Code: Select all

If (worldtype == 2)
              … use this icon
else if (worldtype == 1)
              … use that icon

Did I forget some functions ?
I am still not sure if I need a function in csqc to read the self.this_is_the_worldtype_value, which I sent via self.SendEntity = send_value_to_csqc; and the Writecoord part.
Or if self.this_is_the_worldtype_value is magically available as it is (without needing a special csqc read function) ?

Thank you for your corrections.


PS: fteqccgui.exe gives me this errors when compiling ssqc
in function send_value_to_csqc (line 1096),
client.qc:1098: error: Unknown value "MSG_ENTITY".


client.qc:1513: error: Unknown field "SendEntity" in class "entity"
client.qc:1513: warning F307: type mismatch: float() to __variant entity.SendEntity



What entries are missing ?
Is MSG_ENTITY are simple entity which I can add in defs.qc ?
Is .SendEntity a simple .float which I can add in defs.qc ?
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: ScratchCSQC?

Post by Spike »

stop stop stop!

you're going the overcomplicated way!

ssqc:

Code: Select all

#define EV_FLOAT 2
void(float num, float type, .__variant fld) clientstat = #232;
void() worldspawn = {
...
clientstat(32, EV_FLOAT, worldtype);  //tell the server that stat[32] is a float, and should be sourced from player.worldtype
};
void() PutClientInServer = {
...
self.worldtype = world.worldtype;  //make sure that player.worldtype is actually correct.
};
csqc:

Code: Select all

void(float w,float h, float inmenu) CSQC_UpdateView=
{
float theworldtype = getstatf(32);
if (theworldtype == 2)
              … use this icon
else if (theworldtype == 1)
              … use that icon
};

stat numbers 32-127 are the stats that are exclusively reserved for mod-specific/qc usage. lower values are used for health etc. beware that vectors take 3 consecutive stats. strings in dp take 4 consecutive stats (and have a 15-char limit last I checked, which was ages ago now tbh). dp truncates floats to integers over the wire, so look out for that too (fte does not).

stats are basically built into an array. if one changes, an updated value is sent to the client. use getstatf to read from the client's local copy of the array. it may be sent over the reliable channel so may update at a different rate from entities, or may be sent using a similar method to entities and update independantly from both ents AND reliables... so try not to make too many assumptions about multiple stats updating at the same time.
welcome to networking, and yes, the cat really is both alive and dead at the same time.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: ScratchCSQC?

Post by Spike »

if you do want to go with entities:

both:

Code: Select all

enum
{
et_foo,
et_bar
};
ssqc:

Code: Select all

//system stuff
.float(entity playerent, float changedflags) SendEntity;
.float SendFlags;

float(entity playerent, float changedflags) mysend =
{
writebyte(MSG_ENTITY, et_foo); //lead byte so the csqc can distinguish ent types
writebyte(MSG_ENTITY, changedflags&255); //flags value, so the csqc knows what changed.
if (changedflags & 1)
{
writecoord(MSG_ENTITY, self.origin_x);
writecoord(MSG_ENTITY, self.origin_y);
writecoord(MSG_ENTITY, self.origin_z);
}
if (changedflags & 2)
writebyte(MSG_ENTITY, world.worldtype);
return TRUE;
};
void() somethink =
{
self.nextthink=time+0.1;
setorigin(self, self.origin + '0 0 1'); //this is just an example of an entity moving around.
self.SendFlags |= 1; //flag the origin field as having changed
};
entity(vector pos) spawnsomething =
{
entity e = spawn();
setmodel(e,  "progs/player.mdl"); //noone will see it, but dp mandates that a model is set.
e.think = somethink;
self.nextthink=time+0.1;
e.SendEntity = mysend;
e.SendFlags = ~0; //mark all flags as dirty. note that this is implied per-player when an entity first becomes visible to said player.
setorigin(e, pos);
return e;
};
csqc:

Code: Select all

float theworldtype; //ho hum
void() CSQC_Ent_Remove =
{
//clean up, because the engine won't (this allows the csqc to keep things flying across the room despite being removed from the server - think gib spawn events).
remove(self);
};
void(float isnew) CSQC_Ent_Update =
{
float lead = readbyte();
switch(lead)
{
case et_foo:
if (isnew) //this is the first time we've seen it. note that this MUST NOT affect the reads.
{
setmodel(self, "progs/somethingscary.mdl"); //you precached this, yes?
self.drawmask = 1; //make sure addentities picks us up.
//self.predraw = foo; //put your interpolation in here.
}
float flags = readbyte();
if (flags & 1)
{
self.origin_x = readcoord();
self.origin_y = readcoord();
self.origin_z = readcoord();
setorigin(self, self.origin); //make sure we're relinked, because just writing to origin is bad practise.
}
if (flags & 2)
theworldtype = readbyte(); //oh look. the worldtype changed... more likely it just spawned.
break;
case et_bar:
someothertypethatImincludingasanexamplethatyouwillneedtostripoutorsomething();
break;
default:
error(sprintf("unknown lead byte in entity: %g\n", leadbyte));
break;
}
};

Now, this example was noticably more complex than the previous one, even over-complicated in relation to it on account of faffing around with origins.
my justification for this is that DP does not provide the pvsflags field that FTE does, and thus has no sane way of disabling pvs checks and thus the entity MUST be within the player's pvs in order for it to be visible to the csqc. Ergo, an entity with an invalid origin is pointless. Ergo, I might as well document origins as a way to pad out the example into something useful that can be used as a base for other things (seriously, go with stats).

beginnery uses for 'shared' entities like the above are for gibs. instead of spawning 3 gibs that all move around on their own, you could instead spawn a 'gib event' entity that sends the various info to the csqc, and have the csqc spawn some extra gib entities and run the effect itself. dp doesn't support movetypes in csqc however, so you would need to implement the gibs using tracelines. this significantly complicates things. so okay, maybe not beginner uses then if you want dp compat. blurgh.
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: ScratchCSQC?

Post by Seven »

Hello Spike,

I followed your simple example no1. with clientstat.
fteqccgui.exe doesnt seem to like the ssqc:

Code: Select all

compiling world.qc
in function worldspawn (line 175),
world.qc:652: error: type mismatch on parm 3 - (.float should be .__variant)
defs.qc:671:    clientstat  is defined here

line 652 of world.qc is the line I added this line at the end of worldspawn():
clientstat(32, EV_FLOAT, worldtype); //tell the server that stat[32] is a float, and should be sourced from player.worldtype

line 671 of defs.qc I added this line:
void(float num, float type, .__variant fld) clientstat = #232;


Maybe because .worldtype is already declared as a simple .float ?
And therefore cannot be redeclared to .__variant fld in clientstat() again ?
Should I use another word, like "worldtypee" instead of "worldtype" for the clientstat thing ?
I will try.
EDITED:
No, didnt work. Error persists
/EDITED



Unfortunately I am forced to use your older version of fteqcc from 2012 (maybe that is the culprit ?),
because the 2015 version gives an error after compiling 90% (independent to the current csqc trial):
************ ERROR ************
Too many types
:?
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: ScratchCSQC?

Post by Spike »

so use this then:
void(float num, float type, .float fld) clientstatf = #232;
Seven
Posts: 301
Joined: Sat Oct 06, 2007 8:49 pm
Location: Germany

Re: ScratchCSQC?

Post by Seven »

Hello Spike,

ssqc is compiling fine now. Thank you.

But csqc is telling me:
client/sbar.qc:550: error: wrong immediate type for theworldtype

line 550 is the line where I added this into the existing Sbar_Draw():

Code: Select all

float theworldtype = getstatf(32);

When I add it like this:

Code: Select all

float theworldtype;
theworldtype = getstatf(32);
It compiles, but theworldtype is always "0". No matter what map type I play.
I used this to check:

Code: Select all

print(strcat(ftos(theworldtype),"\n"));
(and it flooded my game with "0.000000"´s in every map type)


I am stuck.
But I want to thank you and toneddu2000 that you tried to help me.

I guess csqc is simply not for me :|

Best wishes,
Seven
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: ScratchCSQC?

Post by toneddu2000 »

On my fte installation it works

Introduction -- I don't know what worldtype is and how to use it.
On my csdefs generated by fte it's not there. Anyway, to set it, I just opened NetRadiant, selected worldspawn and add key worldType and value 2


commondefs

Code: Select all

float	PE_WORLDTYPE = 1;//if you have more of just one parsed events, change 1 with the next free number after the last one - for example, if your last parsed event is PE_WEAPONRELOAD = 5, set this to 6
ssdefs

Code: Select all

.float 	worldType;
float SECONDSYOUWANT = 5;//it works with 2 sec too on my installation, but, if you want to be super sure...
ssmain

Code: Select all

void SendWorldType()
{
	local float wt;
	switch(world.worldType){
		case 1:
			wt = 1;
		break;
		case 2:
			wt = 2;
		break;
		case 3:
			wt = 3;
		break;
		default:
			wt = 0;//that means error
		break;
	}
	msg_entity = self;
	WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
	WriteByte(MSG_MULTICAST, PE_WORLDTYPE);
	WriteByte(MSG_MULTICAST, world.worldType);
	multicast(world.origin, MULTICAST_ALL);
}

//entities find in editor
void worldspawn()
{
	//first leaf of every map

	self.think=SendWorldType;
	self.nextthink = time + SECONDSYOUWANT;//launch SendWorldType after X sec, so we're sure you'll send it
}
csdefs

Code: Select all

float wType;//no need to be a field. Printing a world.field will make fte craaaaaaaaash!!:)
csmain

Code: Select all

noref void() CSQC_Parse_Event =
{
local float rb = readbyte();
if(rb == PE_WORLDTYPE){
	local float w = readbyte();//I always found that FTE is happier if you store your readvalues in a tempvar first
	wType = w;
}
}

void CSQC_UpdateView(float vwidth, float vheight, float notmenu)
{
// a lot of stuff here to render screen bla bla
cprint(ftos(wType));
}
On my fte installation, after SECONDSYOUWANT elapsed, it prints the value I put on worldspawn in NetRadiant.
Note that you need the timer in worldspawn or it won't print it.
I guess csqc is simply not for me :|
Believe me, CSQC is not simply for anyone, except for Spike, of course! :lol: You need to test,retest,retest, and again and again, and after that, when you think to give up, some little result will come up to make you happy!
Unfortunately I am forced to use your older version of fteqcc from 2012 (maybe that is the culprit ?),
because the 2015 version gives an error after compiling 90% (independent to the current csqc trial):
Yeah, Spike, I forgot to tell you that last version of win64-fteqccgui.exe on http://triptohell.info/moodles/ gives a strange error

Code: Select all

************ ERROR ************
Compile hunk was filled
Old win64-fteqccgui.exe and latest win32-fteqccgui.exe, instead, give no error at all! I never asked you, but, what's the difference between 32 and 64 bit versions? Do they compile qc bytecode in 32 or 64 bit? Naah, I don't believe that! :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: ScratchCSQC?

Post by Spike »

@seven:
fteqcc should be able to compile smc again.
you will need to remove the non-breaking-spaces that your code seems to have accumulated from somewhere, as non-ascii chars are no longer ignored.

make sure that your csqc's getstatf builtin is #331, and not #330 (because EV_FLOAT+getstati is a _serious_ wtf found in a certain csqc mod targetted at DP).

@toneddu2000:
there is no practical difference between 32bit and 64bit qccs - it refers to the instruction set the compiler executes, not what it generates.
when it comes to windows, I tend not to bother with 64bit on account of both microsoft and third parties not supporting it so well.
Post Reply