Game Development Community

dev|Pro Game Development Curriculum

Efficient Coding

by Gonzo T. Clown · 05/02/2003 (1:10 pm) · 13 comments

//=============================================================================
// Admin Discipline commands
//=============================================================================

Lets take a look at how to code effciently for maximum precesion usage CPU times. We'll do it by examining how to condense a function to make it far more efficient.

Started out with this cool admin command to handle all the admin functions Note the progression of the names as the code was being written. Nothing namewise changes at all, the code was formatted so well, it collapsed upon itself. You'll understand what I mean by the end of this

First incarnation:
function serverCmdAdminClient(%admin, %client, %action)
{
	if(%admin.isAdmin $= true)
	{
		switch$(%action)
		{
			case "KICK":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				kick(%client);

			case "BAN":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				ban(%client);

			case "PERMABAN":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				permaban(%client);

			case "KILL":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "TIMEOUT":

				if(isObject(%client.player))
					%client.player.kill("Suicide");

				centerprint(%client, "You have been placed in timeout by an Admin.", 4, 1);
				timeout(%client);

			case "LOCK":

				if(%client.teamLocked $= true)
				{
					centerprint(%client, "Your ability to change teams \nis  RESTORED  by Admin.", 4, 2);
					%client.teamLocked = "";
				}
				else
				{
					centerprint(%client, "Your ability to change teams \nis  REVOKED  by Admin.", 4, 2);
					%client.teamLocked = true;
				}

			case "SWAP":

				if(%client.team $= Team1)
				{
					if(isObject(%client.player))
						%client.player.kill("Suicide");
		
					%tr = (%client.team @ "Roster");
					%tr.remove(%client.idHolder);
					%client.team = Team2;
					%newtr = (%client.team @ "Roster");
					%newtr.add(%client.idHolder);
					centerprint(%client, "You have been team changed by an Admin.", 4, 2);
					MessageAll('MsgClientTeamChanged', "", %client, %client.name, %client.getPing(), %client.score, 						%client.caps, %client.kills, %client.deaths, %client.totalScore, %client.team);
				}
				else if(%client.team $= Team2)
				{
					if(isObject(%client.player))
						%client.player.kill("Suicide");
		
					%tr = (%client.team @ "Roster");
					%tr.remove(%client.idHolder);
					%client.team = Team1;
					%newtr = (%client.team @ "Roster");
					%newtr.add(%client.idHolder);
					centerprint(%client, "You have been team changed by an Admin.", 4, 2);
					MessageAll('MsgClientTeamChanged', "", %client, %client.name, %client.getPing(), 					%client.score, %client.caps, %client.kills, %client.deaths, %client.totalScore, %client.team);
				}
				else
				{
				}

			case "MUTE":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "UNMUTE":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "LEADER":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "REMOVELEADER":

				if(isObject(%client.player))

					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "NOBAN":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

			case "REMOVENOBAN":

				if(isObject(%client.player))
					%client.player.kill("Suicide");
				centerprint(%client, "You have been terminated by an Admin.", 4, 1);

		}	
	}
}

Not a bad function, but because I was unsure about how switch/case really loaded a server and it's overall efficiency when it's large like that, I decided to shift the load to smaller easier to customize functions, and I didn't even bother to finish the above function.

second round:
function serverCmdAdminClient(%admin, %client, %action)
{
	if(%admin.isAdmin $= true)
	{
		switch$(%action)
		{
			case "KILL":

				AdminKill(%client);

			case "KICK":

				AdminKick(%client);

			case "BAN":

				AdminBan(%client);

			case "PERMABAN":

				AdminPermaBan(%client);

			case "UNBAN":

				AdminRemoveBan(%client);

			case "TIMEOUT":

				AdminTimeout(%client);

			case "SWAP":

				AdminSwapTeam(%client);

			case "LOCK":

				AdminLockTeam(%client);

			case "MUTE":

				AdminMute(%client);

			case "LEADER":

				AdminLeader(%client);

			case "NOBAN":

				AdminNoBan(%client);

		}	
	}
}

Now as you can see, that function there would be the equivilent of a switchboard that checks the client credentials and relays the signal. Very efficient, very neat. This is whatI strive for in my code. Fast, effecient processing that does a big job quickly.
Now, are you ready for the punchline? After a few minutes of basking in my content My brain clicked in, and I realized that I had been trying to hard the second time, lol scroll down to see the final version of this function. It is by far the best, lol.

Replace all that with this......

function serverCmdAdminClient(%admin, %client, %action)
{
	if(%admin.isAdmin $= true)
		schedule(0, 0, "Admin" @ %action, %client);
}

Now that you see it, it's obvious isn't it, lol. This is what is possible with a good naming convention to your variables. By naming them all alike out of habit and practice I turned 52 lines of code into 5. And thats a fast 5 at that lol. This is actually saving me more
because I will apply the same formulas to at least another half dozen menu and admin processes that are being developed, and all that are going to be developed. When you study in reverse where this function evolved from, you can see it's one little function that packs quite a wallop. :-)

#1
05/02/2003 (1:08 pm)
Actually... you can do one better by using eval() instead of the schedule function :)
#2
05/02/2003 (1:12 pm)
Hmm, well... eval may not actually be much faster, but it does avoid creating an event :)
#3
05/03/2003 (11:02 pm)
Thanks for the advice Tim, I'll definately consider that modification in the future.
#4
05/04/2003 (12:15 am)
Tim, I replaced the schedule with an eval as suggested, but ran into a few snags.


function serverCmdAdminClient(%admin, %client, %action, %arg)
{
if(%admin.isAdmin $= true)
eval("Admin" @ %action @ "(" @ %client @ "," @ %admin @"," @ %arg @ ");");
// schedule(0, 0, "Admin" @ %action, %client, %admin, %arg);
}


As you can see, I have since added another argument to the function for more flexibility and
options. Adding the %arg to the schedule was a piece of cake and would execute properly even if there was no %arg available. This was good because not all of the admin functions use the %arg variable. But the "eval" line will fail completely with a syntax error if the %arg variable is not present. This means I would have to go back and adjust every function involved to at least send some value to the %arg to prevent a failure. Before I do that, I would just like to get your opinion on the "eval".

1. Is there a better way to write that eval?

2. If not, would that eval take more CPU cycles to interpret than the schedule did?


I know this seems nit-picky, but I personally have proven that little losses here, and little losses there start adding up into big losses overall. So I try to eliminate any inefficiency I can in every function. I also find that clean, fault tolerant code (like the above schedule function) tends to lead to a more stable application less prone to crashing.

Thanks for any advice you can give on this.
#5
05/04/2003 (8:43 am)
Actually, the schedule may be the better performer which is why I posted my second comment :) Building the eval string is time consuming, and eval must also go through the parser.

The two approaches have different execution behavior though. The eval will execute immediatly, whereas the schedule will execute on the next run through the simulation's event loop. Which could be after a lot of other stuff has been run. Which approach you use may depend more on what kind of behavior you need.
#6
05/05/2003 (5:08 pm)
I thought you could do:
eval( "Admin" @ %action, %arg1, %arg2, %arg3, %arg4 );
I'd imagine that's what you'd want to do. It also executes immediately, so that you know the function won't return until what you wanted to occur actually does.
#7
05/05/2003 (5:32 pm)
Thank you for your insights Tim. In this particular case, I would consider these admin functions non-critical. Meaning, if they went off two seconds later(very unlikely) the effect would be the same. It is not imperitive for them to return any critical data or effect. So in this case, I will go with the schedule.



Bryan,

Thats a negatory my man. If you try that you'll get an error message like this....


main/server/scripts/game/Commands.cs (81): (null)::eval - wrong number of arguments.
main/server/scripts/game/Commands.cs (81): usage: eval(consoleString)

You have to form the evals parameters in the form of a full function(or variable I guess, but why you would want to do that I'm not sure) with no syntax errors. Your eval has no real structure and the engine sees it as a bunch of arguments instead of a function.
#8
05/06/2003 (4:32 pm)
If you were going to do this you should at least put a comment in explaining what the possible values are for %action. Imagine coming back to this six months later...no idea what it does.
#9
05/07/2003 (11:59 pm)
James, had you read the whole thing, you would know exactly what all the %action args are.
#10
05/13/2003 (9:57 pm)
call("Admin"@%action, %client);

:p
#11
05/14/2003 (8:44 am)
Doh! didn't even know that function was there :) Definitely the way to go.
#12
05/15/2003 (7:59 pm)
Double Doh!! Neither did I. Thanks Chris.


Me thinks I have not fully master the mighty power of Torque, lol
#13
05/15/2003 (8:25 pm)
Nor has the grasshopper MASTERED the grammer engine, lol