PDA

View Full Version : How to successfully precache files


GXLZPGX
08-28-2010, 21:43
Precaching Files, Yay!

I've seen a lot of topics in the Scripting Help section where the creator talks about having problems with precaching. So here's a quicky.

First we want to create a constant to hold the place of the file.

Model:
new const gszModel[] = "models/model.mdl";

Use this for sprites as well! But for sprites, you may want to define the sprites folder of course:
new const gszSprite[] = "sprites/sprite.spr";

Note: If you want to set a PLAYER model, so that a player looks like that specific model, you have to create a subfolder with the name of the model, such as:
new const gszModel[] = "models/player/model/model.mdl";

Sound:
new const gszSound[] = "sound.wav";

And no, I don't know why you have to specify the models folder when creating a constant for a model, and you don't for sound.

Multiple files:
new const g_Models[][] =
{
"models/model1.mdl",
"models/model2.mdl",
"models/model3.mdl"
}

Generic folders:
new const gszSprite[] = "folder"

Next, we want to actually precache the file so that the players can successfully download it.

Model:
public plugin_precache()
{
precache_model( gszModel )
}

Sound:
public plugin_precache()
{
precache_sound( gszSound )
}

Multiple files:
public plugin_precache()
{
for ( new i = 0 ; i < sizeof( g_Models ) ; i++ )
precache_model( g_Models[ i ] );
}

Generic folders:
public plugin_precache()
{
precache_generic( gszFolder )
}

Examples

Models
There are many ways you can set a player model, but here's a quicky so you shut up.

Using CSTRIKE! (YAY)
cs_set_user_model( id, "model" )

If you want to remove that players model, you'll need to reset it.
cs_reset_user_model(id)

Sounds

Playing sounds is fairly simple. You can use client_cmd and spk.

Such as:
client_cmd( id, "spk sound.wav" )

If you have a subfolder for your sound, like "sounds/subfolder/sound.wav", you just use:
client_cmd( id, "spk subfolder/sound.wav" )

Yes, there are other ways to play sounds, I am too lazy to give examples.

If you have any suggestions just tell me. I know this isn't a very complete tutorial but w/e

drekes
08-28-2010, 21:45
Note: If you want to set a PLAYER model, so that a player looks like that specific model, you have to create a subfolder with the name of the model, such as:
new const gszModel[] = "models/model/model.mdl";

new const gszModel[] = "models/player/model/model.mdl";

GXLZPGX
08-28-2010, 21:46
new const gszModel[] = "models/player/model/model.mdl";

Thanks, fixed.

fezh
08-28-2010, 22:28
I wonder why people spent their time creating the funcwiki

GXLZPGX
08-28-2010, 22:41
I wonder why people spent their time creating the funcwiki

There are so many people that don't read the funcwiki it's not even funny.

platzpatrone
08-28-2010, 22:54
There are so many people that don't read the funcwiki it's not even funny.

and there are not many examples. so this tutorial will be helpful too.
but they should take a look on the funcwiki too.

fysiks
08-28-2010, 22:58
Some of these tutorials need to be put on the wiki and the wiki should then be promoted more.

platzpatrone
08-28-2010, 23:14
this is exactly what im thinking about :up:

Hunter-Digital
08-29-2010, 01:31
And no, I don't know why you have to specify the models folder when creating a constant for a model, and you don't for sound.
Because you precache models AND sprites with precache_model().

You should cover sprites too and also, cover model and sprite indexes... by saving the value returned by precache_*()

meTaLiCroSS
08-29-2010, 12:18
Add one thing: Explain why isn't needed to add "sound/" on a sound path while precaching.

albert123
08-29-2010, 13:25
Hey,GLX..you should add how to precache sprites too :)

NiHiLaNTh
08-29-2010, 13:35
and dont forget about precache_generic( ), too...

Bugsy
08-29-2010, 13:49
Though this is not necessarily part of the precache process, it can be helpful for those that need to precache a list of items.

new const g_Models[][] =
{
"models/model1.mdl",
"models/model2.mdl",
"models/model3.mdl"
}

public plugin_precache()
{
for ( new i = 0 ; i < sizeof( g_Models ) ; i++ )
precache_model( g_Models[ i ] );
}

GXLZPGX
08-29-2010, 13:57
Though this is not necessarily part of the precache process, it can be helpful for those that need to precache a list of items.

new const g_Models[][] =
{
"models/model1.mdl",
"models/model2.mdl",
"models/model3.mdl"
}

public plugin_precache()
{
for ( new i = 0 ; i < sizeof( g_Models ) ; i++ )
precache_model( g_Models[ i ] );
}

Added, thanks :)

GXLZPGX
08-29-2010, 13:58
Add one thing: Explain why isn't needed to add "sound/" on a sound path while precaching.

Let me refer you to the first post.

And no, I don't know why you have to specify the models folder when creating a constant for a model, and you don't for sound.

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

Add one thing: Explain why isn't needed to add "sound/" on a sound path while precaching.

Let me refer you to the first post.

And no, I don't know why you have to specify the models folder when creating a constant for a model, and you don't for sound.

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

Hey,GLX..you should add how to precache sprites too :)

Added, it's under the first Models text in bold.

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

and dont forget about precache_generic( ), too...

Added.

NiHiLaNTh
08-29-2010, 14:42
Add a notice that with precache_sound you can precache only .wav files.To precache mp3 files use precache_generic( )

YamiKaitou
08-29-2010, 14:44
Add one thing: Explain why isn't needed to add "sound/" on a sound path while precaching.

Let me refer you to the first post.

And no, I don't know why you have to specify the models folder when creating a constant for a model, and you don't for sound.

Seeing as the SDK also doesn't include the sound folder, the only logical conclusion is that it assumes the sounds folder when using PRECACHE_SOUND

Devil259
08-29-2010, 15:11
There isn't another (better) way to set a player model ?

Arkshine
08-29-2010, 15:13
Devil259, learn to search before posting... Already so much asked. Anyway, see the connor's plugins, he uses the right method.

Devil259
08-29-2010, 15:18
Ok ok sorry, I will see PlayerModels.

platzpatrone
08-29-2010, 20:57
Anyway, see the connor's plugins, he uses the right method.

just to mention: without any SVC_BAD ! :) and the API is really helpful
for own Plugins

Kreation
08-29-2010, 22:46
If you're making a variable to precache it, why not use that variable when you client_cmd() or cs_set_user_model()?

Like:
client_cmd( iIndex, "spk %s", g_szSound );

Or:
cs_set_user_model( iIndex, g_szModel );

YamiKaitou
08-29-2010, 22:51
Or:
cs_set_user_model( iIndex, g_szModel );

Fixed

Kreation
08-29-2010, 23:06
Fixed

Yeah, I knew that looked wrong.

fysiks
08-29-2010, 23:13
If you're making a variable to precache it, why not use that variable when you client_cmd() or cs_set_user_model()?

Like:
client_cmd( iIndex, "spk %s", g_szSound );

Or:
cs_set_user_model( iIndex, "%s", g_szModel );

You can't always do that when using cs_set_user_model(). If the variable g_szModel = "models/player/modelname/modelname.mdl" then it won't work because it can only be used with "modelname".

HOWEVER, this is 100% dependent on how you define g_szModel and how you use it in precache_model().

Basically, you can't do precache_model(g_szModel) AND cs_set_user_model(id, g_szModel) if g_szModel remains unchange between these two functions.

r14170
09-02-2010, 15:46
thx for the TUT!

Drak
09-03-2010, 01:48
For player models, don't use precache_model() as they don't need to be loaded into the engine. Use precache_generic().
Infact, you use precache_generic() for ANYTHING that doesn't need to be loaded (IE: Setting a model onto a created entity / emitting a sound - Needs to be loaded precache_sound/model())

If you just want a player to download a given file (like a player model, that they can be set too) precache_generic() should be used.
Same goes for sounds, if you use your method the "spk" command. You're loading the sound into the engine using "precache_sound()" you don't want that. There's no need.

Bad practice everywhere!

fysiks
09-03-2010, 02:32
For player models, don't use precache_model() as they don't need to be loaded into the engine. Use precache_generic().
Infact, you use precache_generic() for ANYTHING that doesn't need to be loaded (IE: Setting a model onto a created entity / emitting a sound - Needs to be loaded precache_sound/model())

If you just want a player to download a given file (like a player model, that they can be set too) precache_generic() should be used.
Same goes for sounds, if you use your method the "spk" command. You're loading the sound into the engine using "precache_sound()" you don't want that. There's no need.

Bad practice everywhere!

What do you mean by "loaded into the engine"?

Arkshine
09-06-2010, 06:19
I would like to hear the answer too.

ot_207
09-06-2010, 09:32
What do you mean by "loaded into the engine"?

I think that when you precache a sound with precache_sound the sound is loaded into a list, that does not allow more than 255 sounds. These sounds are the ones that are truly needed for running the server, and are used in emit_sound, emit_ambientsound, pm_playsound. Generic loads files in a separate list a second one that can support other files and more as number, and these sounds aren't used in emit_sound, just in spk or others.

Arkshine
09-06-2010, 09:56
Looking at the engine dll, the array size for the sounds are not 255. There are 3 arrays.

- PF_precache_sound_I -> 512
- PF_precache_model_I -> 1024 but the check is set to 512 (Something which could be changed)
- PF_precache_generic_I -> 527

ot_207
09-06-2010, 10:12
Looking at the engine dll, the array size for the sounds are not 255. There are 3 arrays.

- PF_precache_sound_I -> 512
- PF_precache_model_I -> 1024 but the check is set to 512 (Something which could be changed)
- PF_precache_generic_I -> 527

You forgot that the sound index that can be transmitted via SVC_SOUND does not go further than 255. That is one problem.

Arkshine
09-06-2010, 12:12
I can't agree with you. :p

To find where SVC_SOUND is used : PF_sound_I -> SV_StartSound -> SV_BuildSoundMsg

Based on Quak1SDK, SV_BuildSoundMsg() looks like :


int SV_BuildSoundMsg (edict_t *entity, int channel, char *sample, int volume, float attenuation, int flags, int pitch, origin?, datagram? )
{
int sound_num;
int field_mask;
int ent;

if ( volume < 0 || volume > 255 )
Sys_Error( "SV_StartSound: volume = %i", volume );

if ( attenuation < 0 || attenuation > 4 )
Sys_Error( "SV_StartSound: attenuation = %f", attenuation);

if ( channel < 0 || channel > 7 )
Sys_Error( "SV_StartSound: channel = %i", channel );

if ( pitch < 0 || pitch > 255 )
Sys_Error( "SV_StartSound: pitch = %i", pitch );

if ( sample )
{
if ( sample[0] == '!' || sample[0] == '#' )
{
field_mask = flags | SND_SENTENCE(?); // 1 << 4

if ( Q_atoi( sample + 1 ) > 1536 )
{
Con_Printf( "invalid sentence number: %s", sample + 1 );
return 0;
}
}
}

sound_num = SV_LookupSoundIndex( sample )

if ( !sound_num || !sv.sound_precache[sound_num] )
{
Con_Printf( "SV_StartSound: %s not precacheed\n", sample );
return 0;
}

ent = NUM_FOR_EDICT( entity );

if ( volume != 255 )
field_mask |= SND_VOLUME; // 1 << 0

if ( attenuation != 1.0 )
field_mask |= SND_ATTENUATION; // 1 << 1

if ( pitch != 100 )
field_mask |= SND_PITCH; // 1 << 3

if ( sound_num > 255 )
field_mask |= SND_?; // 1 << 2

MSG_WriteByte( datagram, SVC_SOUND );
MSG_StartBitWriting( datagram );
MSG_WriteBits( field_mask, 9 );
if ( field_mask & SND_VOLUME )
MSG_WriteBits( volume );
if ( field_mask & SND_ATTENUATION )
MSG_WriteBits( attenuation * 64, 8 );
MSG_WriteBits( channel, 3 );
MSG_WriteBits( ent, 11 );
MSG_WriteBits( sound_num, sound_num <= 255 ? 8 : 16 );
MSG_WriteBitVec3Coord( origin );
if ( field_mask & SND_PITCH )
MSG_WriteBits( pitch, 8 );
MSG_EndBitWriting( &sv.datagram );

reutrn 1;
}


And looking at SV_LookupSoundIndex(), basically check the "sample" in the array and returns its index if found.

So, there is not a problem like you say, there is probably some bits manipulation like you can see in the code. If I have well understood. :p

ot_207
09-06-2010, 17:22
Oh, I thought it was the same as quake. :D

Arkshine
09-06-2010, 17:28
Just based on it with a lot of changes. But It can help to understand some things. :)

Drak
09-09-2010, 20:12
What I was refering to, is what "ot_207" said. But, not just the precache list/limit/whatever. Like Arkshine was talking about, open the quake source, and look through "zone.c" it's all the memory allocating stuff. This file, is exactly the same ( or super close - as a decompiled showed) to the HL engine.

When you use precache_sound/model() - it add's to all kinds of internal limits (that have been discussed millions of times). But behind all the HL related stuff, memory wise it loads it into. When you use precache_generic() it loads into a "dynamic" hunk. This data will be whiped/re-loaded upon, a map change. When it's anything else, it permanently keeps that data (just the string to the file name) within the engine. Now, this data does get re-loaded, but it doesn't get "pushed" around. You can set model onto and ent, display it in-game, etc. When it's precached. This also can cause problems. (Cache_TryAlloc() and Hunk_Alloc() errors)

OVERALL. DON'T PRECACHE MODEL/SOUNDS - IF YOU JUST WANT A PLAYER TO DOWNLOAD THEM.

ConnorMcLeod
09-11-2010, 07:11
I was thinking that this tut would be more about a snippet like this :

- Make sure the file exists before trying to precache it (prevent servers crashes).
- Ability to format the file path.

PrecacheFile(fmt[], any:...)
{
new szFile[256], n
n = vformat(szFile, charsmax(szFile), fmt, 2)

if( n > 4 )
{
if( equali(szFile[n-4], ".mdl") || equali(szFile[n-4], ".spr") )
{
if( !file_exists(szFile) )
{
new szError[512]
formatex(szError, charsmax(szError), "File %s doesn't exists", szFile)
set_fail_state(szError)
return 0
}
return precache_model(szFile)
}
else if( equali(szFile[n-4], ".wav") )
{
new szFullPathFile[262] = "sound/"
add(szFullPathFile, charsmax(szFullPathFile), szFile)
if( !file_exists(szFullPathFile) )
{
new szError[512]
formatex(szError, charsmax(szError), "File %s doesn't exists", szFullPathFile)
set_fail_state(szError)
return 0
}
return precache_sound(szFile)
}
else // .mp3 .tga
{
if( !file_exists(szFile) )
{
new szError[512]
formatex(szError, charsmax(szError), "File %s doesn't exists", szFile)
set_fail_state(szError)
return 0
}
return precache_generic(szFile)
}
}
return 0
}

Lawer
09-11-2010, 09:44
client_cmd( id, "spk subfolder/sound.wav" )

You can use EmitSound or no?

GXLZPGX
09-11-2010, 12:18
client_cmd( id, "spk subfolder/sound.wav" )

You can use EmitSound or no?

Yes