Just a little something I've been tinkering with and finally got working the way I want it to. It'll be used in an upcoming update to my old Lo-Fi Longwave Beacon Deluxe plugin but I realize this could be very useful for people wanting an easy way to allow players to choose their own RGB/RGBA values for things like FuncommandsX's colorizing (or in my case, my fully separate player/weapon/cosmetic/building colorizing commands).
I've left some comment notes in there regarding some stuff. Got suggestions for improvement of this code? Send 'em my way!
Update: Added new version 2 of the commands. Main difference is version 1 uses GetCmdArgString and regex checks the entire string using a huge regex pattern. Version 2 uses individual GetCmdArg's and a significantly smaller regex pattern to check each arg individually.
public Action Command_EnterRGB(int iClient, int iArgs) { if (iArgs < 3) { ReplyToCommand(iClient, "[SM] Usage: sm_rgb <0-255> <0-255> <0-255>"); return Plugin_Handled; } if (IsClientInGame(iClient)) { char strCmdArg[13]; GetCmdArgString(strCmdArg, sizeof(strCmdArg)); TrimString(strCmdArg);
char strError[128]; char strPattern[] = "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"; RegexError iReturn; Regex hRGB = new Regex(strPattern, _, strError, sizeof(strError), iReturn); hRGB.Match(strCmdArg, iReturn); /* * I'm not sure what I'm doing here with this if statement. * The regex never seemed to produce a proper error code if it detected something invalid. * I'd always end up getting iReturn = 0 and an empty strError string. * The documentation on the regex error codes for the return is sparse. */ if (iReturn == REGEX_ERROR_NONE) { char strRed[4], strGreen[4], strBlue[4]; hRGB.GetSubString(1, strRed, sizeof(strRed)); hRGB.GetSubString(2, strGreen, sizeof(strGreen)); hRGB.GetSubString(3, strBlue, sizeof(strBlue));
/* * !strlen + regex = easy detection of invalid values. * The above regex pattern returns empty substrings if something doesn't match it. * For example, things like negative integers or A-F for hex numbers aren't passed through. * So doing silly stuff like "-255 0 0" or "25A 0 0" won't work. */ if (!strlen(strRed) || !strlen(strGreen) || !strlen(strBlue)) { ReplyToCommand(iClient, "[SM] Invalid value detected"); return Plugin_Handled; }
/* * Do what you want here. * For example, here's a snippet from my Lo-Fi Longwave Beacon plugin * * Create integers to store the substring values. * int iRed, iGreen, iBlue; * * Convert them to integers. * StringToIntEx(strRed, iRed, 10); * StringToIntEx(strGreen, iGreen, 10); * StringToIntEx(strBlue, iBlue, 10); * * I modified the global int array that held the RGBA values of the beacon * and changed it to g_iBeaconRGB2[MAXPLAYERS + 1][4] for per-player colors. * g_iBeaconRGB2[iClient][0] = iRed; * g_iBeaconRGB2[iClient][1] = iGreen; * g_iBeaconRGB2[iClient][2] = iBlue;
* ReplyToCommand(iClient, "[SM] Your beacon color is now %i %i %i", iRed, iGreen, iBlue); * return Plugin_Handled; */ } } return Plugin_Handled; }
public Action Command_EnterRGBA(int iClient, int iArgs) { if (iArgs < 4) { ReplyToCommand(iClient, "[SM] Usage: sm_rgba <0-255> <0-255> <0-255> <0-255>"); return Plugin_Handled; } if (IsClientInGame(iClient)) { char strCmdArg[16]; GetCmdArgString(strCmdArg, sizeof(strCmdArg)); TrimString(strCmdArg);
char strRed[4], strGreen[4] strBlue[4]; hRGB.Match(strCmdArg1, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strRed, sizeof(strRed)); if (!strlen(strRed)) { ReplyToCommand("[SM] Value %s is outside the valid range of 0-255.", strRed); return Plugin_Handled; } }
hRGB.Match(strCmdArg2, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strGreen, sizeof(strGreen)); if (!strlen(strGreen)) { ReplyToCommand("[SM] Value %s is outside the valid range of 0-255.", strGreen); return Plugin_Handled; } }
hRGB.Match(strCmdArg3, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strBlue, sizeof(strBlue)); if (!strlen(strBlue)) { ReplyToCommand("[SM] Value %s is outside the valid range of 0-255.", strBlue); return Plugin_Handled; } } } return Plugin_Handled; }
char strRed[4], strGreen[4] strBlue[4], strAlpha[4]; hRGB.Match(strCmdArg1, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strRed, sizeof(strRed)); if (!strlen(strRed)) { ReplyToCommand("[SM] Red value %s is outside the valid range of 0-255.", strRed); return Plugin_Handled; } }
hRGB.Match(strCmdArg2, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strGreen, sizeof(strGreen)); if (!strlen(strGreen)) { ReplyToCommand("[SM] Blue value %s is outside the valid range of 0-255.", strGreen); return Plugin_Handled; } }
hRGB.Match(strCmdArg3, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strBlue, sizeof(strBlue)); if (!strlen(strBlue)) { ReplyToCommand("[SM] Green value %s is outside the valid range of 0-255.", strBlue); return Plugin_Handled; } }
hRGB.Match(strCmdArg4, iReturn); if (iReturn == REGEX_ERROR_NONE) { hRGB.GetSubString(1, strAlpha, sizeof(strAlpha)); if (!strlen(strAlpha)) { ReplyToCommand("[SM] Alpha value %s is outside the valid range of 0-255.", strAlpha); return Plugin_Handled; } } } return Plugin_Handled; }
Last edited by 404UserNotFound; 02-25-2018 at 02:07.
public Action Command_EnterRGBA(int client, int args) { if (args != 4) { ReplyToCommand(client, "[SM] Usage: sm_rgba <0-255> <0-255> <0-255> <0-255>"); return Plugin_Handled; } if (client == 0 || !IsClientInGame(client)) { ReplyToCommand(client, "[SM] You must be in-game to use this command!"); return Plugin_Handled; }
int r = StringToInt(str1); if (!(0 <= r <= 255)) { ReplyToCommand(client, "[SM] Red value is outside the valid range of 0-255."); return Plugin_Handled; }
int g = StringToInt(str2); if (!(0 <= g <= 255)) { ReplyToCommand(client, "[SM] Green value is outside the valid range of 0-255."); return Plugin_Handled; }
int b = StringToInt(str3); if (!(0 <= b <= 255)) { ReplyToCommand(client, "[SM] Blue value is outside the valid range of 0-255."); return Plugin_Handled; }
int a = StringToInt(str4); if (!(0 <= a <= 255)) { ReplyToCommand(client, "[SM] Alpha value is outside the valid range of 0-255."); return Plugin_Handled; }
public Action Command_EnterRGBA(int client, int args) { if (args != 4) { ReplyToCommand(client, "[SM] Usage: sm_rgba <0-255> <0-255> <0-255> <0-255>"); return Plugin_Handled; } if (client == 0 || !IsClientInGame(client)) { ReplyToCommand(client, "[SM] You must be in-game to use this command!"); return Plugin_Handled; }
int r = StringToInt(str1); if (!(0 <= r <= 255)) { ReplyToCommand(client, "[SM] Red value is outside the valid range of 0-255."); return Plugin_Handled; }
int g = StringToInt(str2); if (!(0 <= g <= 255)) { ReplyToCommand(client, "[SM] Green value is outside the valid range of 0-255."); return Plugin_Handled; }
int b = StringToInt(str3); if (!(0 <= b <= 255)) { ReplyToCommand(client, "[SM] Blue value is outside the valid range of 0-255."); return Plugin_Handled; }
int a = StringToInt(str4); if (!(0 <= a <= 255)) { ReplyToCommand(client, "[SM] Alpha value is outside the valid range of 0-255."); return Plugin_Handled; }
// use RGBA how you want return Plugin_Handled; }
Even this is a little excessive; it's not necessary to hold the user's hand for the range validation, just as it's not necessary to use regex to check for non-numeric characters. Even if they omit an argument, I'd just go with it.
PHP Code:
public Action Command_EnterRGBA(int client, int args) {
if (client == 0 || !IsClientInGame(client)) { ReplyToCommand(client, "[SM] You must be in-game to use this command!"); return Plugin_Handled; }
char chRGBA[4][4]; int iRGBA[4]; for (int i; i < sizeof(chRGBA) && i < args; i++) { GetCmdArg(i+1, chRGBA[i], sizeof(chRGBA[i])); iRGBA[i] = StringToInt(chRGBA[i]); if (!(0 <= iRGBA[i] <= 255)) { iRGBA[i] = 0; ReplyToCommand(client, "RGBA values must be between 0 and 255."); } }
Even this is a little excessive; it's not necessary to hold the user's hand for the range validation, just as it's not necessary to use regex to check for non-numeric characters. Even if they omit an argument, I'd just go with it.
PHP Code:
public Action Command_EnterRGBA(int client, int args) {
if (client == 0 || !IsClientInGame(client)) {
ReplyToCommand(client, "[SM] You must be in-game to use this command!");
return Plugin_Handled;
}
char chRGBA[4][4];
int iRGBA[4];
for (int i; i < sizeof(chRGBA) && i < args; i++) {
GetCmdArg(i+1, chRGBA[i], sizeof(chRGBA[i]));
iRGBA[i] = StringToInt(chRGBA[i]);
if (!(0 <= iRGBA[i] <= 255)) {
iRGBA[i] = 0;
ReplyToCommand(client, "RGBA values must be between 0 and 255.");
}
}
// use RGBA how you want
return Plugin_Handled;
}
Every code post turns into a pissing contest. It just depends on if you want to coddle the user, and how simple you’d want the code to be. While it could be done with iteration as you’ve shown, it’s a bit much for someone who doesn’t yet grasp the usage of regex. The approach I posted is nearly equivalent to yours, but I tend to avoid assuming user inputs if they’re invalid. Garbage data in -> garbage data out.
I prefer to be a thorough programmer, and to think 10 steps ahead and get in the mindset of the end user who will use my plugin. And I think that's the reason we have if/else if/else statements in the first place, for checking values and ensuring bugs don't arise due to whatever circumstances should cause them, right?
And honestly, who does it hurt if I use Regex And I'm interested in regex, not obsessed with it. I'm in a computer programming course to learn programming, so maybe I should also learn other things related, y'know?.
Quote:
Originally Posted by ddhoward
Even this is a little excessive; it's not necessary to hold the user's hand for the range validation, just as it's not necessary to use regex to check for non-numeric characters. Even if they omit an argument, I'd just go with it.
Holy crap, that looks good. And understandably so, given the huge difference in our skill levels (yours being much higher than mine). But I disagree with it being excessive. It's simple, it does its job. The only real check is the strlen check, and the regex was done because I figured it'd be simpler and it gave me a finer level of control and also prevented me from releasing something like this:
PHP Code:
if (iRed < 0) iRed = 0; else if (iRed > 255) iRed = 255; else if (iGreen < 0) iGreen = 0; else if (iGreen > 255) iGreen = 255; else if (iBlue < 0) iBlue = 0; else if (iBlue > 255) iBlue = 255; else if (iAlpha < 0) iAlpha = 0; else if (iAlpha > 255) iAlpha = 255;
My next train of thought on the above code would have been "gee, I wonder if I could compress this into "if (iRed < 0 || iGreen < 0 || iBlue < 0 || iAlpha < 0)" and then set up some quick and dirty thing to change the value of the specific integer the check was being done on." You see how quickly this becomes convoluted.
Last edited by 404UserNotFound; 02-25-2018 at 06:19.
It is unfortunate that you interpreted this conversation in this way. I was merely joining you in demonstrating to our OP colleague that there are simpler ways to perform the stated task. Your code is more readable, mine is compacter and more easily editable (for example, if a "target" argument were to be prepended to the list of arguments.)
Quote:
Originally Posted by 404UNF
I prefer to be a thorough programmer, and to think 10 steps ahead and get in the mindset of the end user who will use my plugin. And I think that's the reason we have if/else if/else statements in the first place, for checking values and ensuring bugs don't arise due to whatever circumstances should cause them, right?
This is absolutely correct. If you look at some of my earlier work, especially with Friendly Mode, I went absolutely insane with validating user input. However, this isn't always necessary. In the stated task, there are simpler ways to accomplish this. Validating input isn't that important here (in my opinion!) since the input boils down to a few numerical values. This is why I just implemented a simple 0 <= i <= 255 check. In this particular scenario, I don't think anything else is really necessary.
Quote:
And honestly, who does it hurt if I use Regex And I'm interested in regex, not obsessed with it. I'm in a computer programming course to learn programming, so maybe I should also learn other things related, such as regex.
Ah, but the question is whether or not that particular solution is suitable. There are several things to consider:
Is the code readable, so you can go back and update it later without trying to re-learn something, or remembering your motivations for given bits of the code?
Is the solution expensive? In the magical world of Sourcepawn, this generally isn't something we need to concern ourselves to much with, but it's always something to keep in mind!
Will the code I write be understandable to others who wish to edit my work? Will I end up causing irritating people to add me on Steam, or Discord, or spam me with PMs asking me to modify the plugin because they don't understand the code themselves?
Do I intend on posting the solution as an example of a good solution on how to perform the given task?
It's great that you're learning regex. The question is whether or not this particular plugin was the best medium in which to do that. Me, I'm still looking for an excuse to learn regex. And git. And SQL. Or literally any other language outside of Pawn. (Every time I try to just read about one of these subjects without a given project in mind, my eyes just gloss over and I lose any interest I ever had...)
Quote:
given the huge difference in our skill levels (yours being much higher than mine)
I disagree. My knowledge is very general with only a few specializations. You put a plugin in front of me that involves gamedata, or particle effects, or databases, or viewmodels, or regex, or anything cool... And I'll stand there with my thumb in my mouth. You're got at least regex on me, now, and probably lots more.
Quote:
text which was edited out
Oh, your code is fine. If it works, it works. Your method choice, however, is questionable, in the opinion of probably most people here, especially when posted to the public forum as an example of how to efficiently preform the task.
You're doing fine. Please don't interpret this criticism as non-constructive.
I don't know why I took things negatively initially, especially given me saying to leave suggestions for improvement. I think I got too into my own mind with the obsessed with regex post, wondering if it was meant negatively and ended up making myself believe it was and I got defensive.
I do thank you both for the help. It's greatly appreciated, especially as I find I learn better from seeing completed examples of something so I can wrap my head around the "proper" way to do things. It's how I got my start here, picking apart plugins, seeing how they worked, and making them work the way I wanted them to.
Last edited by 404UserNotFound; 02-25-2018 at 09:17.
In SM 1.10 and 1.9 soon(tm) You can use MatchAll to make the regex even easier. Although, using args is much better. But, since i decided to test with this type of an example... hf
int CheckRGBA(const char [] szRGB, int values[4], bool hasAlpha) { Regex re = new Regex("\\b([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\b"); int ret = -1; int matches = re.MatchAll(szRGB);