Talk:Fast Commands

From SA-MP Wiki

Jump to: navigation, search

Contents

New page version

As sscanf 2.0 and zcmd are both about now this page needs updating to use these newer methods. The idea of this page is that it represents the best way of writing commands, so older methods should be removed entirely or moved to an alternate page. I'll start writing a new version of the page here and update the main one as appropriate. --Y_Less 16:19, 11 January 2010 (UTC)

New version code

Introduction

Commands represent a major form of input to gamemodes, so doing them well is very important. A large number of gamemodes suffer from holes introduced by simply trusting what users enter without checking for things like overflows and code injections. Unfortunately a lot of the time this is simply because writing secure code is such an advanced topic that many new coders have no idea that they can even write insecure code, let alone know how to not do so. The other issue is that of speed - the early modes which came with the server, and modes based on them, used very basic command processing methods. These were fine at the time, as there was nothing else, but made the code awkward and difficult to scale up.

Compare an old implementation of the "givecash" command (slightly modified from the original LVDM version by Jax) with a modern implementation. Do not worry if you do not understand either code - you simply need to be able to see that one is far simpler than the other. Note that to make the comparison as fair as possible, all code has been expanded to maximum brace sizes.

LVDM givecash

public OnPlayerCommandText(playerid, cmdtext[])
{
	new
		idx,
		cmd[32];
	cmd = strtok(cmdtext, idx);
	if(strcmp(cmd, "/givecash", true) == 0)
	{
		new
			giveplayerid,
			playermoney,
			moneys,
			giveplayer[MAX_PLAYER_NAME],
			sendername[MAX_PLAYER_NAME],
			tmp[32];
		tmp = strtok(cmdtext, idx);
		if(!strlen(tmp))
		{
			SendClientMessage(playerid, COLOR_WHITE, "USAGE: /givecash [playerid] [amount]");
			return 1;
		}
		giveplayerid = strval(tmp);
		tmp = strtok(cmdtext, idx);
		if(!strlen(tmp))
		{
			SendClientMessage(playerid, COLOR_WHITE, "USAGE: /givecash [playerid] [amount]");
			return 1;
		}
		moneys = strval(tmp);
		if (IsPlayerConnected(giveplayerid))
		{
			GetPlayerName(giveplayerid, giveplayer, sizeof(giveplayer));
			GetPlayerName(playerid, sendername, sizeof(sendername));
			playermoney = GetPlayerMoney(playerid);
			if (moneys > 0 && playermoney >= moneys)
			{
				GivePlayerMoney(playerid, (0 - moneys));
				GivePlayerMoney(giveplayerid, moneys);
				format(string, sizeof(string), "You have sent %s(player: %d), $%d.", giveplayer,giveplayerid, moneys);
				SendClientMessage(playerid, COLOR_YELLOW, string);
				format(string, sizeof(string), "You have recieved $%d from %s(player: %d).", moneys, sendername, playerid);
				SendClientMessage(giveplayerid, COLOR_YELLOW, string);
			}
			else
			{
				SendClientMessage(playerid, COLOR_YELLOW, "Invalid transaction amount.");
			}
		}
		else
		{
			format(string, sizeof(string), "%d is not an active player.", giveplayerid);
			SendClientMessage(playerid, COLOR_YELLOW, string);
		}
		return 1;
	}
	return 0;
}

ZCMD and sscanf givecash

CMD:givecash(playerid, params[])
{
	new
		giveplayerid,
		moneys;
	if (sscanf(params, "ui", giveplayerid, moneys))
	{
		SendClientMessage(playerid, COLOR_WHITE, "USAGE: /givecash [playerid] [amount]");
	}
	else if (giveplayerid == INVALID_PLAYER_ID)
	{
		// This line has been changed slightly as "u" means player name or player id.
		SendClientMessage(playerid, COLOR_YELLOW, "Not an active player.");
	}
	else
	{
		new
			playermoney = GetPlayerMoney(playerid),
			playername[MAX_PLAYER_NAME];
		if (moneys > 0 && playermoney >= moneys)
		{
			// Transfer the money
			GivePlayerMoney(playerid, (0 - moneys));
			GivePlayerMoney(giveplayerid, moneys);
			// Send the messages
			GetPlayerName(giveplayerid, playername, sizeof (playername));
			format(string, sizeof(string), "You have sent %s(player: %d), $%d.", playername, giveplayerid, moneys);
			SendClientMessage(playerid, COLOR_YELLOW, string);
			GetPlayerName(playerid, playername, sizeof (playername));
			format(string, sizeof(string), "You have recieved $%d from %s(player: %d).", moneys, playername, playerid);
			SendClientMessage(giveplayerid, COLOR_YELLOW, string);
		}
		else
		{
			SendClientMessage(playerid, COLOR_YELLOW, "Invalid transaction amount.");
		}
	}
	return 1;
}

The second version is much more compact and, although it's may not be obvious yet, much more flexible and secure - there are a number of bugs in the first version with long numbers. The second version also allows you to enter a player's name instead of their ID, and expanding to more parameters takes a couple of simple characters, instead of at least 5 lines of new code.

Personal tools
Navigation
Toolbox