Identifying a player in a command
#1

I'm trying to improve the ease at which a player's name can be used in a command (for example /wave TestDucati). The problem is that the method I'm currently using is not very efficient, currently the server will only correctly identify the player I'm aiming the command toward if I include the first part of their name, if that makes any sense.

For example:
/wave TestDucati = works
/wave TestDu = works (if nobody else online has a name starting with TestDu)
/wave T = works (if nobody else online has a name starting with T)

/wave Ducati = Doesn't work (returns invalid command) as the identifier doesn't have the beginning of the username

This might not sound like much of a problem but with a growing server I am beginning to get players who share similar (or the same) words at the start of their names.

For example two players named TestJon and TestMike.
The command "/wave Test" returns an Invalid command as there are multiple usernames starting with the word Test. This means that to /wave one of them I need to type either the entire name or the name up until the unique character (/wave TestJ) which is very annoying due to /wave Jon or /wave Mike not correctly identifying the player I am trying to wave at

How easy would it be to adjust my commands to identify the player more efficiently?
(I'm aware of not needing to type an ID in the "wave" example, due to it finding the nearest player, but this is mainly a problem with commands like /info and /stats)

An example of one of the commands which I need to improve:
Код:
COMMAND:wave(playerid, params[])
{
	if (PlayerInfo[playerid][pSpawned] == 1)
	{
		new idx,TargetID,string[256],tmp[256];

		if(PlayerInfo[playerid][pWaveTime] > 0)
		{
		SendClientMessage(playerid,COLOR_ERROR,"Please Wait Before Using This Command Again.");
		return 1;
		}

		tmp = strtok(params, idx);


		if (!strlen(tmp))
		{
		TargetID = GetClosestPlayer(playerid);

		if (TargetID == INVALID_PLAYER_ID)
		{
		SendClientMessage(playerid, COLOR_ERROR, "No Players Close Enough To Wave At.");
		return 1;
		}
		}else{

		if (!isNumeric(tmp))
		{
		TargetID = ReturnUser(tmp, playerid);
		if (TargetID == INVALID_PLAYER_ID)
		{
		return 1;
		}
     	}
		else
		{
		TargetID = strval(tmp);

		if (!IsPlayerConnected(TargetID))
		{
		format(string, sizeof(string), "%d Is Not A Valid ID.", TargetID);
    	SendClientMessage(playerid, COLOR_ERROR, string);
		return 1;
		}
		}
		}

		if (TargetID == playerid)
		{
			SendClientMessage(playerid, COLOR_ERROR, "You Cannot Wave At Yourself.");
			return 1;
		}

		if (GetDistanceBetweenPlayers(playerid,TargetID) > 9)
		{
			format(string, sizeof(string), "%s (%d) Is Not Close Enough.  You Cannot Wave At %s.",PlayerInfo[TargetID][pName],TargetID, ObjectGenderPronouns[PlayerInfo[TargetID][pGender]]);
			SendClientMessage(playerid, COLOR_ERROR, string);
			return 1;
		}

		if (GetPlayerVirtualWorld(playerid) != GetPlayerVirtualWorld(TargetID))
		{
			format(string, sizeof(string), "%s (%d) Is Not Close Enough.  You Cannot Wave At %s.",PlayerInfo[TargetID][pName],TargetID, ObjectGenderPronouns[PlayerInfo[TargetID][pGender]]);
			SendClientMessage(playerid, COLOR_ERROR, string);
			return 1;
		}

		if (GetPlayerState(TargetID) == PLAYER_STATE_WASTED)
		{
        format(string, sizeof(string), "%s (%d) Waves at %s (%d)'s Corpse.",PlayerInfo[playerid][pName],playerid,PlayerInfo[TargetID][pName],TargetID);
        SendClientMessageToAll(GetPlayerColor(playerid), string);
        PlayerInfo[playerid][pWaveTime] = 10;
		}else{
		format(string, sizeof(string), "%s (%d) Waves at %s (%d).",PlayerInfo[playerid][pName],playerid,PlayerInfo[TargetID][pName],TargetID);
		SendClientMessageToAll(GetPlayerColor(playerid), string);
		PlayerInfo[playerid][pWaveTime] = 10;
		}
		if (!IsPlayerInAnyVehicle(playerid))
		{
		ApplyAnimation(playerid, "ON_LOOKERS", "wave_loop", 3.0, 0, 0, 0, 0, 0, 1);
		}
	}
	else
	{
		SendClientMessage(playerid, COLOR_ERROR, "You Cannot Use This Command When You're Dead.");
	}

	return 1;
}
Reply
#2

Typically most people use sscanf() keep in mind I am pretty sure it will still return a result even if there is more than one result.
Reply
#3

Your first option would probably be to use sscanf.

PHP код:
CMD:waveplayeridcmdtext[] ) {
    
    new 
        
a_szTmpDelim128 ],
        
iIndex,
        
iGivePlayerID
    
;
    
a_szTmpDelim strtokcmdtextiIndex );
    if( 
isnulla_szTmpDelim ) )
    {
        
SendClientMessageplayerid, -1"/wave [playerid/PoN]" );
        return 
1;
    }
    
sscanfa_szTmpDelim"u"iGivePlayerID );
    if( !
IsPlayerConnectediGivePlayerID ) )
    {
        
SendClientMessageplayeridCOLOR_RED"Playerid specified is invalid." );
        return 
1;
    }
    return 
1;

Or create a function to loop through players and compare names, etc.
Reply
#4

Yes using sscanf is correct but there's already a built-in function for that, there's no need for looping and such.


PHP код:
if(sscanf(params"?<CELLMIN_ON_MATCHES=1>u"target)) return SendClientMessage(playerid, -1"Usage: /wave");
if(
target == cellmin) return SendClientMessage(playerid, -1"[ERROR]: Multiple users found."); 
Reply
#5

Using sscanf (u param) it will most likely to get the result which is in lower IDs, lets say you are searching 'Ada` and there are two players with name starting from Ada (likely Adax(1) and Aday(11), then it will return the name with lower id or the name which comes first in the loop search, i.e. Adax(1)
Reply
#6

Thanks all for the help, I will have a go at replacing with sscanf in the morning.

I dont have much (any) experience with sscanf yet, will using sscanf/u enable me to identify a player by other parts of the name other than the beginning?

For example will it allow me to use /wave John if a player's name is TestJohn
Reply
#7

Quote:
Originally Posted by Ducati
Посмотреть сообщение
Thanks all for the help, I will have a go at replacing with sscanf in the morning.

I dont have much (any) experience with sscanf yet, will using sscanf/u enable me to identify a player by other parts of the name other than the beginning?

For example will it allow me to use /wave John if a player's name is TestJohn
No it will not, It would have if the player name would have been JohnTest. sscanf dosent search in between name, it starts from the beginning of the name.
Reply
#8

using strtok in 2018 smh
Reply
#9

Quote:
Originally Posted by Zeth
Посмотреть сообщение
Using sscanf (u param) it will most likely to get the result which is in lower IDs, lets say you are searching 'Ada` and there are two players with name starting from Ada (likely Adax(1) and Aday(11), then it will return the name with lower id or the name which comes first in the loop search, i.e. Adax(1)
Quote:
Originally Posted by Zeth
Посмотреть сообщение
No it will not, It would have if the player name would have been JohnTest. sscanf dosent search in between name, it starts from the beginning of the name.
No, if you use the array plus the MATCH_NAME_PARTIAL flag

Quote:
Originally Posted by Emmet_
Посмотреть сообщение
Users can now optionally return an ARRAY of users instead of just one. This array is just a list of matched IDs, followed by "INVALID_PLAYER_ID". Given the following players:

Код:
0) ******
1) [CLAN]******
2) Jake
3) Alex
4) Hass
This code:

pawn Код:
new ids[3], i;
if (sscanf("Le", "?<MATCH_NAME_PARTIAL=1>u[3]", ids)) printf("Error in input");
for (i = 0; ids[i] != INVALID_PLAYER_ID; ++i)
{
    if (ids[i] == cellmin)
    {
        printf("Too many matches");
        break;
    }
    printf("id = %d", ids[i]);
}
if (i == 0) printf("No matching players found.");
Will output:

Код:
id = 0
id = 1
Too many matches
Searching "Les" instead will give:

Код:
id = 0
id = 1
And searching without "MATCH_NAME_PARTIAL" will give:

Код:
No matching players found.
Basically, if an array of size "N" is passed, this code will return the first N-1 results. If there are less than "N" players whose name matches the given name then that many players will be returned and the next slot will be "INVALID_PLAYER_ID" to indicate the end of the list. On the other hand if there are MORE than "N - 1" players whose name matches the given pattern, then the last slot will be "cellmin" to indicate this fact.

When combined with "U" and returning the default, the first slot is always exactly the default value (even if that's not a valid connected player) and the next slot is always "INVALID_PLAYER_ID".

Note also that user arrays can't be combined with normal arrays or enums, but normal single-return user specifiers still can be.

  • MATCH_NAME_PARTIAL:
Currently sscanf will search for players by name, and will ALWAYS search for player whose name STARTS with the specified string. If you have, say "[CLAN]******" connected and someone types "******", sscanf will not find "[CLAN]******" because there name doesn't start with the specified name. This option, when set to 1, will search ANYWHERE in the player's name for the given string.
  • CELLMIN_ON_MATCHES:
Whatever the value of "MATCH_NAME_PARTIAL", the first found player will always be returned, so if you do a search for "_" on an RP server, you could get almost anyone. To detect this case, if more than one player will match the specified string then sscanf will return an ID of "cellmin" instead. This can be combined with "U" for a lot more power:

pawn Код:
sscanf(params, "?<CELLMIN_ON_MATCHES=1>U(-1)", id);
    if (id == -1)
    {
        // No player was entered.
    }
    else if (id == cellmin)
    {
        // Multiple matches found
    }
    else if (id == INVALID_PLAYER_ID)
    {
        // Entered player is not connected.
    }
    else
    {
        // Found just one player.
    }
And for multiple match, since this is a /wave command, it would be best to pick the nearest player to the target, regardless their order or id.


EDIT: the smh reply made me to answer this 8 months old thread, didn't notice
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)