The high-level structure of HL2's code

Grab your favourite IDE and tinker with the innards of game engines

The high-level structure of HL2's code

Postby Sapphon on Mon Oct 04, 2010 4:06 pm

Hi all,

I'm "new" at this but am not quite making a "newbie" post - I've got source control working, can compile and run my mod, understand compiler directives in Visual Studio, and understand C++. I "get" template classes and can generally make sense of any particular piece of the HL2 code that I care to read.

The problem is, how do I know what I ought to read?

One thing I don't see a lot of (on this or other sites) is a breakdown of the enormous number of files in the solution and how they divide up into game responsibilities. Heck, even something as essential as which functions comprise the main loop is buried inside of another article on the Wiki. As a beginning modder, my challenge has not been "how do I get what I want out of Source and C++", but rather "How do I avoid spending hours hoping I'm looking at the right files?"

Related questions would be, "How do I know when I should look for shared (ie duplicated =() work in both the client and server projects?" or "What are the general responsibilities of client and server projects?" or "What does the naming system that seems to have been used on some of the files (c_XXX for example) mean?"

Thanks in advance for any and all help.
Sapphon
Dumpling
Dumpling
 
Joined: Mon Oct 04, 2010 3:57 pm

Re: The high-level structure of HL2's code

Postby zombie@computer on Mon Oct 04, 2010 5:21 pm

The Source code is divided up in client and server.dll

A 'game' in source consists of a server (server.dll) and any number of clients (up to 128 iirc) connected to it. Note that for singleplayer this means a server and one client are running on the same computer!

So, in essence, the server has all the code that 'applies to all clients', while the client contains the code that applies to 'just that client'. Simpler said the server does the game mechanics, the client does the rendering and the interface.

A short list of examples: (may not be 100% accurate)

client:
-rendering (closed source)
-hud
-controls (via keybindings)
-clientside physics (eg prop_physics_multiplayer)


server:
game mechanics (game rules, eg checking if client x can fire weapon y)
Interactions (eg bullets, explosions, calculation of health)
entity i/o's
physics
AI

Client and server interact with each other. This interaction goes really fast when both are running on the same computer, but can be a lot slower when your client is connected to a server on the internet.
They interact using data packets. Basically, the server summarises some variables of entities and sends them to the client (about 20 times per second). These variables need to be selected by the programmers. The client uses these packets to synchronize its local variables. This is best explained using an example.

For starters, a class is needed to contain these variables. (usually called C_someclass clientside, CSomeclass serverside) These classes are usually entities themselves (meaning, derived from cbaseentity and c_baseentity). We call these networked entities. You can have up to 2048 of these, each entity can have 1024 variables synchronised between server and client.

In programming, you select the variables (serverside these are templated using CNetworkVar) and put them in a NETWORK_TABLE. This groups all variables to be sent (using SendPropInt serverside, RecvPropInt clientside). The server then synchronises these variables to all the clients.

Pure serverside logic has no need for clientside classes. Logics like a logic_trigger has no place in a client, as the server executes the logic, then only needs to tell the clients what the outcomes are. Since physics are calculated serverside, you'd think physic entities (eg prop_physics) are serverside only as well, but this is wrong. The clients need to know what the state of the prop is (eg rotation, location) to render it correctly. That is why the prop_physics is a shared class. The clientside can then be used for rendering and other clientstuff.

Pure clientside logic are those of keybindings, and the HUD.

For optimization purposes, clients do a bit more of calculation than mentioned above. The client for example is more than capable of determining if a player's move is blocked by another entity. These exceptions are rare though, and mostly you never going to see them.

For starters, take a look at the following classes:

pure client

hud_*.cpp

pure server
logicrelay.cpp

shared
most weapons (note: some are just 'stubbed' clientside, as their clientside code is limited mostly to showing and animating the weapon model)

player:
CHL2_player.cpp
C_HL2_player.cpp


To make the code a bit less easy to read, theres quite a number of classes derived from other classes, like the player class mentioned above. (which is derived from cbaseplayer, which is derived from cbasecombatcharacter, which is derived from cbaseanimating (iirc), which is derived from... which is derived from cbaseentity. Some classes for client and server are coded in the same files (xxx_shared.cpp) in which clientside and serverside code is divided using preprocessor commands. Note that usually a #define cmyclass c_myclass line divides the server and client class names.

umm, was that at all understandable?
When you are up to your neck in shit, keep your head up high
zombie@computer
Forum Goer Elite™
Forum Goer Elite™
 
Joined: Fri Dec 31, 2004 5:58 pm
Location: Lent, Netherlands

Re: The high-level structure of HL2's code

Postby Sapphon on Mon Oct 04, 2010 5:49 pm

Very understandable, actually. Quite a useful explanation of HL2's client-server interaction. Also you are right, they really did go crazy with the chains of class derivation. Maybe that's what's giving me trouble.

Basically I set out with a goal (As an example, say, cause a certain keyboard input to spawn a given prop_phys with given properties) and can break that goal into parts (make a new keybinding, link the keybinding to a command intelligible to the I/O system, and code the behavior for the command once it's been received). The hardest part is not knowing how to achieve each of the individual parts of the task; the hardest part is knowing what file to start in! Once you find the right file, adding a keybinding is cake. Once you find the right files, adding IN_spawnphys and a corresponding kbutton_t and up/down handler functions aren't hard. But looking for where you ought to be doing these things can take forever!

So I guess if I could summarize my problem it would be that I have a poor idea of the original design intent RE: code structure and division into files, with my only real insight into what I'll find where being the file names.
Sapphon
Dumpling
Dumpling
 
Joined: Mon Oct 04, 2010 3:57 pm

Re: The high-level structure of HL2's code

Postby zombie@computer on Mon Oct 04, 2010 7:25 pm

Um, yes, i understand your problem. Even though ive also got extensive knowledge of coding in general, ive only recently started working on source. The preprocessor templates are a bitch, as sometimes intellisence doesnt know how to handle them (esp when switching between server and client). I have the same problems as you, even now, and the only good solution i have so far is the search option, which is excellent in visual studio.
When you are up to your neck in shit, keep your head up high
zombie@computer
Forum Goer Elite™
Forum Goer Elite™
 
Joined: Fri Dec 31, 2004 5:58 pm
Location: Lent, Netherlands

Return to Programming

Who is online

Users browsing this forum: No registered users

cron