PDA

View Full Version : [Tutorial] settext() without precaching



IzNoGoD
29th December 2014, 12:19
Heres a small tutorial for you all.

It will allow you to settext() to any clienthudelem any string you desire.

Known bugs: does not work properly with killcam and .archived = true (so set .archived to false if you use killcam, or just dont use it all together :) )

Background:

Configstrings:
CoD2 precaching fills an array of configstrings with information. Precachetext(&"Hello world") will write "Hello world" to a semi-random configstring number.
When a client connects to a server, all configstrings are synced to this client.
CoD2 does not allow for precaching after frame 1, but we CAN change the value the CLIENT has in his configstring by using sendgameservercommand(entnum, "d [number] [newvalue]")
This will overwrite the "Hello world" clientside with another string, which will propagate to any hud that was called with settext(&"Hello world") immediately.
So all we need to do is precache a bunch of strings, find their configstring number and when we wanna settext() the hud, instead of settext()-ing it, we just simply send a gameservercommand to change the client's configstring.


Ok, on to the code:



init()
{
level.inf_hud_str = [];
for(i = 0; i < 32; i++)
{
level.inf_hud_str[i] = spawnstruct();
level.inf_hud_str[i].locstr = findlocstr(i);
level.inf_hud_str[i].str = findstr(i);
precachestring(level.inf_hud_str[i].locstr);
level.inf_hud_str[i].index = G_FindConfigstringIndex(level.inf_hud_str[i].str, 1310, 256);
}
level thread waitforconnect();
}

createinfhud(num)
{
hud = newclienthudelem(self);
hud.infhudindex = num;
hud.owner = self;
self.infhudlist[num] = hud;
hud settext(level.inf_hud_str[num].locstr);
hud.alpha = 0;
hud.txt = "";
return hud;
}

setinftext(txt)
{
player = self.owner;
index = level.inf_hud_str[self.infhudindex].index;
sendgameservercommand(player getentitynumber(), "d " + index + " " + txt);
if(!isdefined(self.archived) || self.archived)
{
specs = player getspectators();
for(i = 0; i < specs.size; i++)
sendgameservercommand(specs[i] getentitynumber(), "d " + index + " " + txt);
}
self.txt = txt;
}

waitforconnect()
{
while(true)
{
level waittill("connecting", player);
player playerinit();
}
}

waitforspec()
{
self endon("disconnect");
while(true)
{
self waittill("spawned");
if(self.pers["team"] == "spectator")
self thread onspec();
}
}

playerinit()
{
self.infhudlist = [];
self thread waitforspec();
}

findstr(i)
{
return "JumpersHeavenHudValue" + i;
}

findlocstr(i)
{
switch(i)
{
case 0: return &"JumpersHeavenHudValue0";
case 1: return &"JumpersHeavenHudValue1";
case 2: return &"JumpersHeavenHudValue2";
case 3: return &"JumpersHeavenHudValue3";
case 4: return &"JumpersHeavenHudValue4";
case 5: return &"JumpersHeavenHudValue5";
case 6: return &"JumpersHeavenHudValue6";
case 7: return &"JumpersHeavenHudValue7";
case 8: return &"JumpersHeavenHudValue8";
case 9: return &"JumpersHeavenHudValue9";
case 10: return &"JumpersHeavenHudValue10";
case 11: return &"JumpersHeavenHudValue11";
case 12: return &"JumpersHeavenHudValue12";
case 13: return &"JumpersHeavenHudValue13";
case 14: return &"JumpersHeavenHudValue14";
case 15: return &"JumpersHeavenHudValue15";
case 16: return &"JumpersHeavenHudValue16";
case 17: return &"JumpersHeavenHudValue17";
case 18: return &"JumpersHeavenHudValue18";
case 19: return &"JumpersHeavenHudValue19";
case 20: return &"JumpersHeavenHudValue20";
case 21: return &"JumpersHeavenHudValue21";
case 22: return &"JumpersHeavenHudValue22";
case 23: return &"JumpersHeavenHudValue23";
case 24: return &"JumpersHeavenHudValue24";
case 25: return &"JumpersHeavenHudValue25";
case 26: return &"JumpersHeavenHudValue26";
case 27: return &"JumpersHeavenHudValue27";
case 28: return &"JumpersHeavenHudValue28";
case 29: return &"JumpersHeavenHudValue29";
case 30: return &"JumpersHeavenHudValue30";
case 31: return &"JumpersHeavenHudValue31";
}
}

onspec()
{
if(isdefined(self.spectator_handling_running) && self.spectator_handling_running)
return;
self.spectator_handling_running = true;

old_spectator_client = self;
while(isdefined(self) && (!isdefined(self.sessionstate) || self.sessionstate == "spectator"))
{
spectator_client = self getSpectatorClient();
if(old_spectator_client != spectator_client)
{
if(isdefined(spectator_client) && spectator_client != self)
{
for(i = 0; i < 32; i++)
{
if(isdefined(spectator_client.infhudlist[i]) && (!isdefined(spectator_client.infhudlist[i].archived) || spectator_client.infhudlist[i].archived))
sendgameservercommand(self getentitynumber(), "d " + level.inf_hud_str[spectator_client.infhudlist[i].infhudindex].index + " " + spectator_client.infhudlist[i].txt);
}
}
old_spectator_client = spectator_client;
}
wait 0.05;
}
if(isdefined(self))
self.spectator_handling_running = false;
}

getspectators()
{
spectators = [];
players = getentarray("player", "classname");
for(i = 0; i < players.size; i++)
{
if(isdefined(players[i].pers["team"]) && players[i].pers["team"] == "spectator")
{
speccing = players[i] getspectatorclient();
if(isdefined(speccing) && speccing == self)
spectators[spectators.size] = players[i];
}
}
return spectators;
}


Looks like an aweful lot of code, but it isnt.

All you need to know is:


newclienthudelem() is replaced (and wrapped) by createinfhud(num)
settext() is replaced (but not wrapped) by setinftext(txt)
init() needs to be called ongamestart
You need a libcod version that supports G_FindConfigstringIndex()

So, you manually need to find a free index for the createinfhud. I suggest you do this statically so all clients have the same hud associated with the same configstring (eg self.statshud = createinfhud(13) for all clients) This will then return a hud element which can be used with hud setinftext("any string that isnt localized and is under 256 characters long");

Thats all
Have fun with it :)

kung foo man
29th December 2014, 14:05
Nice wrapper :D

Uploaded the new binaries: http://killtube.org/downloads/libcod/2014_12_29/ (last upload was 7 months ago, lol)

Yesterday I've finally merged Mitch's repo with like 70 new commits (basically just copied it, since Mitch merged my last 2 commits from some months ago).

Now my repo is 5 commits ahead with:

814

The dummy-replacement-function for the "does the mapname exist-function" in setmapnamestring() was the first try to get this done, but it didn't work out well, because the 2nd hud would overwrite the value of the first one.

Now we added G_FindConfigstringIndex() and G_FindConfigstringIndexOriginal() for all three CoD2 versions.

G_FindConfigstringIndex(): does not crash, but can't create new configstrings also and can be used for non-precached shaders (ask IzNoGod, just played with strings yet)
G_FindConfigstringIndexOriginal(): can crash, but might become useful for somebody, as it is the original function

Regarding forum: I've added tag

IzNoGoD
29th December 2014, 20:07
Heres a libcod build with the G_FindConfigstringIndex(), but without G_FindConfigstringIndexOriginal()

https://www.dropbox.com/s/kf36aak3qj10qrg/libcod_2014_12_29.zip?dl=0

(you can use this version with the infinite-settext)