Welcome, Guest! Login | Register

Hints on how to program a Half-Life mod [Print this Article]
Posted by: Persuter
Date posted: Dec 19 2003
User Rating: 5 out of 5.0
Number of views: 11683
Number of comments: 5
Description: This article tries to give hints to would-be HL mod authors in many different aspects of the programming experience.
Introduction

The purpose of this article is to give those of you out there who are new to the HL SDK a bit of a boost. Not only will I explain how to work with the SDK, I'll also explain how to work with MSVC and, hopefully, how to be a better programmer. Now, before we go any further, if you don't know C++ to at least some degree, turn around right now. C++ is not a complex language but you must know a fair amount before you can really be expected to work well with the HL SDK. Make no mistake about it, it is a major piece of software, and the IDE which you need to use, MSVC, is a powerful package. You don't have to know every obscure thing about C++, but if you don't know what a pointer is, or what inheritance is, you probably need to spend more time in the books. Take the time to learn C++ well - it is applicable in many areas of endeavour, and indeed, in life as well. Always nice to know how to program a computer.

That having been said, let's start right in. I'll begin with a few general comments on programming, then move on to a few points about MSVC, then finally a bit about the HL SDK itself, and then end with a couple of examples.

Programming

The first and most important thing is to realize and respect what programming is. All too many people seem to come to the forums imagining that HL coding is akin to finding as many tutorials as you can and snapping them together to form a game. Nothing could be further from the truth. To program a game, you have to envision a goal and work towards it, using tutorials and the SDK as a source of good examples. Coding is a creative art just like architecture or writing. Just as you couldn't copy and paste passages out of various books to become a well-known author, you're going to have to be able to create code for yourself before you can be any good. Start off by thinking of yourself as a programmer and you just might become one. It is a serious art that many forumgoers practice for a living.

Spend time familiarizing yourself with the SDK

Second, be curious about the HL SDK. When you encounter a type that you haven't seen, try checking out its definition. Read the header files of major classes like CBaseEntity and CGameRules. The HL SDK is great because it provides a lot of code examples to the beginning SDK coder. Take advantage of this bounty. Time spent reading code is time well spent. In fact, it may sound silly, but seriously think about just setting aside some time to clamber around in the HL code and look at functions, code. Get down into the functions one or two below where you normally code - that is, if you're coding Think functions, why don't you see what calls Think? Or if you're doing weapons, take a look at what's calling PrimaryAttack. I would say that it would be hard to overstate the importance of this. If you do not try to familiarize yourself with how HL works inside, you will not gain enough understanding to really get it as a whole.

Using your memory wisely

Third, as a coder, your most precious resource is your own memory. Coding doesn't really require complex thinking in the same way that, say, writing a philosophy paper does. Instead it requires lots of memory. To that end, if you can substitute disk storage for your own memory, you should do so - the more memory you have free, the better you program. When you figure out how to do something, write it down. Don't strain your brain trying to remember what header files you need to include in that file, check what files are included in similar files. Edsger Dijkstra, one of the most famous computer scientists of all time, said, "The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility..." Programming is a very difficult thing to do well - you should give your brain as much extra room as you can.

Finally, start small and build up. If you start out trying to do too much, you won't learn anything, you'll just get frustrated and confused. Start off with some small, reasonable goals, and code them. Think about it like this. Trust me when I say that you are going to look back on the first 50 things you code and laugh your ass off. Why make the first thing something REALLY hard when it and the next forty-nine are going to be terrible?

Microsoft Visual C++

(Note that this is entirely based on MSVC 6.0, which is what I have. If you have .NET, this is your opportunity to try to figure out the similarities.)

There are three essential tools in MSVC that you need to learn about. The first is creating browse info. Go into MSVC, right-click on any variable name, and select "Go To Definition Of...". Then say yes when it asks you if you want to rebuild. From now on, you can right click on any variable name, type name, macro, etc., and be instantly taken to where it is defined. I cannot tell you how important this tool is in terms of finding things quickly (this again comes back to using the computer rather than your own memory - if you don't remember what functions a class has, check out the definition quickly and easily). The second essential tool is Find In Files. Go ahead, open up the Edit window, say hello, and get friendly. Find In Files will check every file in your project directory for a search string, and return a list of each usage. You can double-click on these and be instantly taken to the usage in question. These two tools will help you navigate through the project.

Debugging, an important requirement

The third is how to use debugging mode. So many new HL coders don't know about this, which is sad, because it is hands down the best debugging tool you have at your immediate disposal. If you put your cursor anywhere in the code and press CTRL-F10, the game will start and run until it hits where your cursor is in the code, at which point it will stop (this is only if you have your debugging info set up correctly). You can watch it run line by line, see what it does to your variables, you can put your cursor somewhere else and make it run to there, etc. etc. Another great help is in debugging access violations, which happen a lot. If your game is crashing, put your cursor somewhere you know won't get called, and press CTRL-F10. When it crashes, MSVC will stop the program and point directly to the line that is causing the access violation. I would say this will solve 95% of your crashing problems outright, because you'll see that you were calling a NULL pointer or something. Again, if you are not using this tool, you are putting yourself at a great disadvantage. (Also, in your debugging info, if you are working on a multiplayer mod, put "+maxplayers 2 +map somemap" in the program arguments. This allows the game to immediately load a map when you run the game, thus saving you some hassle.)

Errors and how you should take care of them

MSVC's error and warning messages can be dense. But don't be scared of them, because they do explain the problem. We get too many posts on the Wavelength forums asking us what "unreferenced local variable" or "unresolved external symbol". If you don't know what they mean, Google the warning number or look it up in the MSDN library. (Speaking of which, I know what it's like to have a warezed copy of MSVC so I know that most of the people I'm talking to in this article don't have the MSDN library. But if you have it, that's another great tool that you should be using. And for the rest of you, you ought to buy it. :)) Never ask the Wavelength crew what an error message means. We aren't a manual. Try to understand the error message. Chances are, if you understand the error message, you will understand why you're getting it. Similarly, before asking what a function does, see if it's commented anywhere in the SDK. (Of course, if you have an error message and really understand what it means but can't figure out what part of your code is causing it, then you can ask. But it is very unusual to run into truly baffling output from the compiler, so take your time on this. Time spent understanding your IDE is time well spent.

Organization

Also, do not have two different workspaces, one for the client, one for the server. The best way to do it is to have one single workspace with two projects, as detailed in my article on setting up Visual C++ 6.0. You should only have to have one copy of MSVC open at a time. Also, go to Project -> Dependencies, set the "Select project..." combo box to hl, and click cl_dll. This way, if you have the hl workspace selected and you change code that exists in both the server side and client side, both dlls are compiled. This is really helpful when you're editing physics or weapon code, because oftentimes you forget to compile one dll and then you have weird errors. This goes back to freeing your memory up. If the computer will do it for you, let it.

To sum up, MSVC is a really good tool. It's a professional-level developing interface, and it is my personal favourite. The debugging tool is extremely powerful and yet easy-to-use. So don't be afraid of this big boy, saddle on up! :D

Hints in HL Coding

A first big hint in HL coding is to find the CGameRules class, take a look at it and all of its functions, and make creating that object, or at least a skeleton of it, the first thing you do when you start coding. There are a lot of functions, but if you take them one at a time and really take a look at how they're used, you'll find that you will really be able to go a long way towards the creation of your mod with this one object. This is where round play, team play, classes, all that stuff originates. It's what really makes your mod unique from the others. Take your time and try to do this right. Really try to think about how you want to achieve a certain effect. For example, take adding classes. What is a class? Well, you need to assign the player a number that says what class he is. That means you need to add an integer to the CBasePlayer definition (actually, pev already has an int called playerclass, so you could of course use that). Then, in PlayerSpawn in the game rules, you give them whatever weapons they're supposed to get when they spawn. You'll find that a lot of the infrastructure for the stuff you want is already inherent in the CGameRules class.

The "Shared" concept

It's also important to learn the concept of "shared" code in the HL SDK. Some files are compiled into both the server and client. The client then uses this code to simulate what it thinks the server will do. The weapons code is shared like this. For example, if you fire a gun, both the server and the client run through the appropriate weapon's PrimaryAttack function. Only the client is used to display graphics, to make sounds, and so forth. The physics code is also shared like this. Physics code is in pm_shared.c, pm_math.c, and pm_debug.c. Again, this is valuable code to look through, although it's much denser than most of the logic code. This is where you come if you want to implement different movement in the game, such as forcing a player to face a ladder he's climbing, or adding walljumps. (Remember, by the way, because of this, every time you change something in physics or weapons, you should recompile BOTH dlls. If you already have both projects in the workspace and made a dependency between them, it will automatically do this.)

Understanding client-server communication

Get familiar with the server-client messaging system. So many people come into the forums asking silly questions about the messaging system. However, perhaps part of the problem is that no one has ever really written down, step-by-step, how to create a server-client message. Remember again, though, these are tools that you use. If you need to send a value to the client-side, this is the method. Similarly, get used to the ClientCommand functions on the server side (there's at least two important ones), and the input functions on the client side. When you need to add some kind of command to the game, this is where you want to go. Again, it's hard to understand this stuff when you're used to thinking only in terms of connecting tutorials together. Use these as tools to create your own mod. Mods require a surprisingly small amount of code when done right. However, they are almost never done right the first time. :D

Using the SDK to your advantage

This has already been said but cannot be too often repeated - use the HL SDK to your advantage. There are tens of thousands of lines of example code in the SDK. Most of what you will want to do will have at least some sort of analogue in the HL SDK, or can be made up of different parts of the SDK. Become familiar with it. If you're not interested enough to read the HL code just because, you probably aren't interested enough to really make a good mod. The art of coding is figuring out what you want to do, and then figuring out how to do it. If you don't know the SDK very well, how to do it will be very hard. Indeed, it will be so hard that your decisions on what to do will be made by whether you can do them at all, i.e., you only do things that are in tutorials because you can't do anything else.

Also, don't get discouraged when you don't do something right the first time. In all human endeavour, you have to fail a bunch of times trying to get something right before you get it right. No amount of studying and learning will allow you to do a problem right the first time. That simply isn't the way human thinking works. Some people call problems like these "wicked problems", defined as "problems that must be solved partially before any progress can be made in solving it at all". :D Again, you'll throw away the first 50 things you write because they're terrible. In every project I've ever worked on, I've started over from scratch at least three or four times. Practice is the only way to get good at coding in general and your mod in particular, so code as much as you possibly can.

Test Problems

Next, I'm going to take you through a couple of "test problems", where I'll discuss where I might begin solving such a problem. These aren't really to be used as direct guidelines to solving these problems, but simply general examples of problem-solving. First, let's assume you have a crashing bug somewhere in your code. You start up the game, it begins loading a map, and then HL crashes to desktop with an OK/Debug window. First, as you would learn through long experience if you weren't reading this tutorial, hard crashes like that, nine times out of ten or more, is caused by accessing an uninitialized or NULL pointer. You should put your cursor in some unused code and hit CTRL-F10 on whichever side you suspect of trouble (we'll assume this is clear, though it isn't always). When the program crashes, the IDE will show you exactly where you crashed and what the pointer was that crashed. Quite often the problem is that you hadn't considered what would happen if a certain pointer was NULL, and a simple if check will solve this. A real-life example: a week ago, a friend of mine asked me to take a look at a particle system he had, which crashed every time it started up. I hit CTRL-F10, and found that the program crashed on the line which loaded the particle's texture into memory, and found that it was because the texture was NULL. After a bit of testing, I saw that the reason was that the file that it was supposed to be loading for the texture was missing. I replaced the texture and it worked beautifully.

Example

Let's now assume that you want to implement walljumping into your mod. That is, I want people to be able to run up to a wall, jump, and then while they are in the air against the wall, jump up and away from the wall. This is a physics problem, so we want to open up pm_shared.c. A simple search for "jump" ges me right to:

 CODE (C++) 
if ( pmove->cmd.buttons & IN_JUMP )
        {
            if ( !pLadder )
            {
                PM_Jump ();
            }
        }
        else
        {
            pmove->oldbuttons &= ~IN_JUMP;
        }


OK, to keep things simple I could put this right below PM_Jump, checking if onground is false. If so, then maybe we can do the wall jump. Send a trace line ahead of you to see if a wall is close enough. If so, then give the person velocity up and away from the wall. Now, I want to be clear on something here: if you plan to do something with physics, you HAVE to learn vector math. It is very simple. Just learn how to add and subtract vectors, and what that means in a 3D system (e.g., to find the vector pointing from one vector to another, you need to subtract the first from the second). You also need to know that the dot product of two vectors returns the product of those two vectors' lengths, multiplied by the cosine of the angle between the two vectors. This is something that's hard to understand until you've taken a class in it, but do try.

Conclusion

That's the article. If you have any questions, or suggestions for things I should put in here, email me at persuter@planethalflife.com. If you're just starting to learn C++ for programming the HL SDK, I hope you won't get scared away from it if the SDK proves too difficult for you. C++ is an extremely important language today, and it is a great thing to be able to put on your resume. So don't get discouraged if it takes a while: this is a skill that takes years to develop. Try to practice good coding habits - these include things like properly-named variables and elegant code. Don't be afraid to ask questions. A lot of new coders ask questions and get yelled at because the questions aren't well-posed. Programmers love answering good questions. If you say what you want to do, what's not working, how it's not working, what you've tried to fix it, and what that produced, you will get answers. If you ask how to do something with no preface, you will get no answers. People will assume you haven't studied the problem, regardless of whether you have. So now, armed with your hints about C++, MSVC, the HL SDK, and programming in general, go out there and try to surprise the world! :)

Rate This Article
This article is currently rated: 5 out of 5.0 (2 Votes)

You have to register to rate this article.
User Comments Showing comments 1-5

Posted By: harSens on Dec 20 2003 at 00:38:44
Nice article, I actually learned something about setting up MSVC.
Just a few picky side notes:
Creating walljump might not be the best example for an inexperienced SDK coder

 QUOTE  

What is a class? Well, you need to assign the player a number that says what class he is.

You didn't give an answer to the "what is a class"-question. For me, a class represents certain abilities that are given to the players of that class. You can represent a class by defining it as an integer in CBaseClass (why would you do that if you have pev->class?). There are other ways to represent a class, but this is not the place to go into the class vs integer class based discussion.

Posted By: Persuter on Dec 22 2003 at 08:49:48
I was just trying to show a simple way in which the gamerules object would come into play in a real-life situation. I did not intend to explain everything about implementing a class.

However, you reminded me that what I really should have used there is team play, because I need to tell them not to use the teamplay functions in there. :)

As for the walljump example, I'm not giving these examples as homework problems to try. However, I would disagree with you. Walljumping, if you know vector math, is QUITE easy. Send a traceline out, if it hits a wall, push them up and back.

(Oooh, an interesting variant would be to push them up and wherever they were looking. So they jump towards the wall, spin around so their back faces it, and then hit jump again and launch up and in whatever direction they're facing. It would feel like Spiderman, probably. :D)

Posted By: harSens on Dec 22 2003 at 18:26:28
Well... What I tried to point out, is that you start with some requirements:
- I need a class system
- What is my class system for?
wich is good. Then you don't finish those requirements, and then you go to an unfinished implementation:
- I'm going to implement my class system as an integer in the CBasePlayer
This implementation decision is completely unclear for an unexperienced coder. If you do this, then at least show one of the case statements to use the integer in. Showing an alternative implementation might be good too. That is, if you wanted to use this as an example of the requirements->design->implementation process.

As for the walljumping, it requires vector math, shared code and coding in C. There must be something easier to code that's still somewhat educational (although I can't think of anything right now).Edited by harSens on Dec 22 2003, 18:33:44

Posted By: BlackPanther on Dec 27 2003 at 19:47:59
Great stuff Percy! :D

Posted By: Zipster on Jan 02 2004 at 03:58:51
Releasing the MOD in a timely manner is something that should be thrown in there too :) But a great article I hope people follow.


You must register to post a comment. If you have already registered, you must login.

Latest Articles
3rd person View in Multiplayer
Half-Life 2 | Coding | Client Side Tutorials
How to enable it in HL2DM

By: cct | Nov 13 2006

Making a Camera
Half-Life 2 | Level Design
This camera is good for when you join a map, it gives you a view of the map before you join a team

By: slackiller | Mar 05 2006

Making a camera , Part 2
Half-Life 2 | Level Design
these cameras are working monitors that turn on when a button is pushed.

By: slackiller | Mar 04 2006

Storing weapons on ladder
Half-Life 2 | Coding | Snippets
like Raven Sheild or BF2

By: British_Bomber | Dec 24 2005

Implementation of a string lookup table
Half-Life 2 | Coding | Snippets
A string lookup table is a set of functions that is used to convert strings to pre-defined values

By: deathz0rz | Nov 13 2005


Latest Comments
knock knock
General | News
By: MIFUNE | Dec 31 2017
 
knock knock
General | News
By: omega | Dec 22 2016
 
knock knock
General | News
By: MIFUNE | Oct 10 2015
 
New HL HUD Message System
Half-Life | Coding | Shared Tutorials
By: chbrules | Dec 31 2011
 
knock knock
General | News
By: Whistler | Nov 05 2011
 
Particle Engine tutorial part 4
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 18 2010
 
Particle Engine tutorial part 2
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Particle Engine tutorial part 3
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Game Movement Series #2: Analog Jumping and Floating
Half-Life 2 | Coding | Shared Tutorials
By: mars3554 | Oct 26 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Deadpool | Aug 02 2009
 

Site Info
297 Approved Articless
8 Pending Articles
3940 Registered Members
0 People Online (4 guests)
About - Credits - Contact Us

Wavelength version: 3.0.0.9
Valid XHTML 1.0! Valid CSS!