HUD Tracking Game State Data

  • Thread starter Thread starter Sarron
  • Start date Start date
S

Sarron

Guest
Hi,

The issue I'm dealing with here is as follows:

I'm trying to develop a game wherein the world around you has a certain -- let's say "economic" -- state and I want to represent that on the player's HUD. The state involves many things including the number of various entities in a given area, etc.

So what I need is for my HUD elements to know about lots of different bits of info about the game world. However, looking at all the HUD elements in the SDK, it seems that the HUD usually just reads data about the player character (from a C_BasePlayer -derived entity/object).

So my question is this: Should I be making my own derivation of C_BasePlayer (or perhaps some subclass down the line from it) and having that class collect all the server-side data needed?

Then, when the HUD wants to know about the world state it simply queries the player character's object for the relevant data since I collected it all there on the server side.

The alternative seems to be some complicated (and large) set of messages that would have to be sent from the server to the client every time a resource change was recorded. This seems very inefficient...

Am I missing something about the architecture of Source here? Is there an easier way for client-side elements like HUD elements to query custom elements of the game state that I create and keep track of on the server side?

Thanks for the advice. =)
 
Oh, I know the answer to this one, and it doesn't involve using C_BasePlayer.

I can't really answer it fully just yet, as I've gotta switch over to my SDK computer, but it involves having an entity that broadcasts a load of data to the client every think cycle, and having the players pick it up, and interpret it.

I'll go more into depth later on, when I have the chance. And a mod checking "econimic" state? I'm very much intrigued.

-Angry Lawyer
 
Thanks for the reply, even in part. =)

I've been looking at the concept of an entity that is shared between the client and the server. The trouble is that I don't understand:

a.) When and where I should actually create the entity (assuming I'd use something like CreateEntityByName("myentity"))

b.) How I would then access that entity's data from other objects, such as my HUD element or other entities (such as those counting things that relate to resources and setting the values the HUD wants)


As an aside, does anyone know of a good/decent/any resource that goes over the basic architecture of Source and how a game runs in general? I realize it's a big picture, but many of my confusions, such as the one I'm having now, seem to come from not "getting" the mindset that Source requires.
 
Bwuh, I didn't realise how much of that code I'd written while under the infuence of beer.

Anyways, what I did:

Spawn the entity on map start-up (look for the same bit that loads the skill.cfg and stuff).

Have it, on every think, look for whatever you're checking - and now for the magic. This is a direct copy-paste out of my code.

First, you need to hook to the players. This might look odd, but I'm passing in a variable that checks against a variable stored on each player. You seem a smart guy, you should be able to work out what does what.

Code:
	CPASFilter filter;

	// Send a message to all clients
	CBasePlayer *pPlayer;
	CBaseEntity *pPing = NULL;

	while((pPing = gEntList.FindEntityByClassname(pPing, "player")) != NULL) //While we have a player
	{
		pPlayer = ((CBasePlayer*)pPing);

		if (pPlayer->GetConqTeam() == team)
		{
			filter.AddRecipient(pPlayer); //Lawyer:  If on our team, message him/her!
		}
	}

And then, you need to broadcast it. "Resourcechange" needs to be defined in your HUD element. iResources is the variable that's getting sent.
Code:
	UserMessageBegin(filter, "ResourceChange"); //Begin the broadcast!
	WRITE_LONG(iResources);
	MessageEnd();

After this, it gets really confusing. You'll have to find a way to hook into the thing using a new HUD element - use the SDK_HUD_Health as a template, and change the hooks to whatever you've called your thingie you've sent. HOOK_HUD_MESSAGE and SetDisplayValue are your key functions, search for them. And in Usermessages, you'll need to define the new data type.

I'm just as confused as to what I wrote as you are, trust me.

-Angry Lawyer
 
Thanks!

Thank you very much, my law-centric friend. =)

You code helped me a lot, though I had to do a LOT of poking around to figure the whole paradigm out (or at least enough to get things going). I'm considering posting what I've figured out so far, but it's been too long of a day for that right now.

Ultimately, I'd like to do a tutorial or something for this, because it seems to be an important task for many interesting mods.
 
You mean, you actually followed what I posted? Wow, you've got some interpretation skills.

I considered doing a tutorial, until I looked at how ugly my code was...

Oh, and, you're welcome :)

-Angry Lawyer
 
How I Got Updates to the Player GUI

Okay, while I'm by no means an expert at this, I found NO other references on how to do something like this end-to-end, so rather than worrying about finding time to do a formal tutorial, I'll at least go over how I got this done.

I will try to generalize it, though, as much as possible. Bear in mind that I did all this in a project starting from the base SDK (a.k.a. the base for a multiplayer mod).

The first thing to do is to register your new user message in sdk_usermessages.cpp. Just below

Code:
// Used to send a sample HUD message
usermessages->Register( "GameMessage", -1 );

add the registration of your new message by copy/pasting the above code and replacing "GameMessage" with the name of your new message (again in quotes, as before). For example:

Code:
// Sarron's new message
usermessages->Register( "SarronNewMsg", 1 );


The second thing to do is create and register a method that receives a message of that type. For this example, we'll work with a HUD element, whose class is called "CHudSarronElement". Let's say this class is defined in hud_sarronelement.h and hud_sarronelement.cpp.

In hud_sarronelement.h (or wherever you're declaring your CHudSarronElement class) add a declaration for a public method returning void named "MsgFunc_YOURNAME( bf_read &msg)" where YOURNAME is the exact name you gave your message above (in my example case, SarronNewMsg). For example:

Code:
public void MsgFunc_SarronNewMsg( bf_read &msg );

Now define the method you just declared in the corresponding .cpp file (hud_sarronelement.cpp for the example). At the very least this method must have the signature "void CHudMonitor::MsgFunc_SarronNewMsg( bf_read &msg )" to match the declaration. Here's an example of what your method may look like in whole (though bear in mind this is a boring example):

Code:
void CHudMonitor::MsgFunc_SarronNewMsg( bf_read &msg )
{
	// Reads a received SarronNewMsg message
	//  and stores its first integer value

	// Store the first byte in the message to
	//  an integer member variable (declared above)
	m_nValueInNewSarronMessage = msg.ReadByte();

}

Thirdly, you must register the message as a message your HUD receives.

Somewhere free in your .cpp file (not as a part of any other code block), add:

Code:
DECLARE_HUD_MESSAGE( CHudMonitor, SarronNewMsg );

Next in the Init() method of your HUD element class add:

Code:
HOOK_HUD_MESSAGE( CHudSarronElement, SarronNewMsg );

where CHudSarronElement is the name of your HUD class. For example, your init may look like:

Code:
void CHudSarronElement::Init()
{
	HOOK_HUD_MESSAGE( CHudSarronElement, SarronNewMsg );
	SetActive( false );
}

Now you just need something to send a message of your new SarronNewMsg message type!

So finally, you find whatever place in the server code the message should be send and add something like:

Code:
// Create a message filter to select just the player
//  that just touched this item
CSingleUserRecipientFilter user( pPlayer );
user.MakeReliable();

// Send out a message of type SarronNewMsg,
//  which contains the integer "4",
//  to the player who touched this item
UserMessageBegin( user, "SarronNewMsg" );
	WRITE_BYTE( 4 );
MessageEnd();

(the above code snippet updated at 2:27am Eastern on April 2nd, 2005)

When this code is executed, a message will be sent out, to only those players specified in your filter, via Source's messaging system. Since you registered your HUD element as receiving messages of that type, that "MsgFunc_SarronNewMsg( bf_read &msg )" method your declared above in your HUD class will be executed. You can now read the message and do whatever you'd like with the information therein.

(For more information on reading and writing messages, see Valve's SDK Docs: http://www.valve-erc.com/srcsdk/ -- esp. "Networking Events & Messages" and "HUD Elements")

Again, I apologize for not having the time to make a formal tutorial. I certainly haven't explained how to make a HUD element class, but suffice it to say that Valve's SDK docs, online tutorials, and copying code from existing HUD elements is the way to go.

I hope this may help someone out there.
If this does help you, or if you find something plain-out wrong or missing from this discription, please reply here as such. Thanks. =)
 
Yeah, that's pretty much how I did it, although I used the filter thing to make sure that only players on the correct team recieve the data, and so that the number sent was a variable stored in an entity.
You've written it up well.

-Angry Lawyer
 
Important Correction/Addition Made; Feedback on Tutorial?

Darn -- I knew I forgot something! You're right that the filter is important. I have updated the corresponding code snippet to include an example of creating a code snippet (in the above case, by using the pointer to whoever touched the item that's sending the message).

It occurs to me that the above "tutorial" is longer than most actual claimed tutorials out there. What, if anything, do you think I'd need to do to make it a tutorial proper?

I might see if I can state more explicitly any other assumptions I made for my examples or whatnot...

Thanks for the feedback at any rate. =)
 
You need to add filenames, I think, and go a little more in-depth as to what each bit does. Otherwise, it's really good.

-Angry Lawyer
 
Okay, I'm not 100% sure what you mean when you say I need to add filenames, but I have an idea.

It seems that to really solidify this as a step-by-step tutorial (with good descriptions of what each step is doing and why it works, which most tutorial seem to miss) I need to walk readers through the whole process, starting with the creation of the HUD element, giving the exact names and locations of files to edit, etc. The reason I didn't do that before, is that it seems there's plent of information on how to do that already.

That said, the task of creating a HUD element that is updated when server-side events happen is VERY important. So perhaps it does deserve a full end-to-end treatment. I really want to see people do great stuff and try out experimental ideas with the concept of the client GUI as a visual representation (non-numberic when possible, please!) of what's going on in the world and portal to the elements therein...

At any rate,am I on the right track for what you're talking about to improve the tutorial?

Also, which parts do you think could use more explanation in specific? (I ask these questions to anyone reading this, not just Lawyer).

Thanks everyone. =)
 
Filenames: Which .cpp files you're editing.

*Gives a thumbs up*

You could go into depth as to what server to client messages are actually used for. It's not all that obvious for the new coder.

-Angry Lawyer
 
Back
Top