Diving In Head First

C++ sucks… Well, learning C++ in the context of Valve’s source engine sucks.

My mentality when it comes to learning a new language is to just dive into the environment. By the end – and 500 google searches later (not kidding) – I try to have a fundamental understanding of the language. The only problem is that this approach is not working with me in C++.

Thankfully, I’ve resided in a core member of the sourcemod dev team and they’ve helped me out tons in regards to my futile attempt of figuring out my life. After discussing his history and stuff I told him my concerns about learning C++ and he gave me an issue to fix. If you’re too lazy to read it yourself (I don’t blame you), or are unfamiliar with sourcemod, the tl;dr is that sourcemod has it’s own system for language translations, and sourcemod plugins couldn’t (at the time) check the existence of a certain translation. This was my job.

I was to create two natives:

bool TranslationPhraseExists(char[] phrase);
bool IsTranslatedForLanguage(char[] phrase, int language);

After digging through the sourcemod repo I found smn_lang.cpp which contains the natives for the language functions so I knew I’d eventually be adding my native here, but in regards to creating my two natives I didn’t really see any clues until I looked at the following function.

static cell_t sm_LoadTranslations(IPluginContext *pCtx, const cell_t *params)
{
    char *filename, *ext;
    char buffer[PLATFORM_MAX_PATH];
    IPlugin *pl = pluginsys->FindPluginByContext(pCtx->GetContext());

    pCtx->LocalToString(params[1], &filename);
    Me::SafeSprintf(buffer, sizeof(buffer), "%s", filename);

    /* Make sure there is no extension */
    if ((ext = strstr(buffer, ".txt")) != NULL || (ext = strstr(buffer, ".cfg")) != NULL)
    {
        /* Simple heuristic -- just see if it's at the end and terminate if so */
        if ((unsigned)(ext - buffer) == strlen(buffer) - 4)
        {
            *ext = '\0';
        }
    }

    pl->GetPhrases()->AddPhraseFile(buffer);

    return 1;
}

Or more specifically, this line

pl->GetPhrases()->AddPhraseFile(buffer);

I knew that each plugin has their own translation cache, but didn’t realize that IPlugin had a method to grab collection of phrases (IPhraseCollection). So now I’ve recognised that the phrase collection interface contains these phrases, I needed to add a method to search for the phrase, so I added the following to the interface.

virtual bool TranslationPhraseExists(const char *phrase) =0;

Inside of this definition of CPhraseCollection I needed another TranslationPhraseExists, but this time it needed to be a member of the CPhraseFile class. So I added it to the IPhraseFile interface & class, filled in the definition with a single use of the retrieve method, and then I was basically done!

So the process would look something like:

  1. Call TranslationPhraseExists on cached collection
  2. Call TranslationPhraseExists on phrase files in a loop until the translation file is found, or it’ll return false after no matches were found.

All I needed to do now was create the native!

static cell_t sm_TranslationPhraseExists(IPluginContext *pCtx, const cell_t *params)
{
    IPlugin *pl = pluginsys->FindPluginByContext(pCtx->GetContext());
    IPhraseCollection* collection = pl->GetPhrases();

    char *phrase;

    pCtx->LocalToString(params[1], &phrase);

    return collection->TranslationPhraseExists(phrase);
}

Easy peasy. Now the other native was much easier, so I won’t go into it in depth, but I noticed a method called FindTranslation, so I just used it!

static cell_t sm_IsTranslatedForLanguage(IPluginContext *pCtx, const cell_t *params)
{
    IPlugin *pl = pluginsys->FindPluginByContext(pCtx->GetContext());
    IPhraseCollection* collection = pl->GetPhrases();
   
    char *phrase;
    pCtx->LocalToString(params[1], &phrase);
   
    int langid = params[2];
 
     Translation trans;
     if (collection->FindTranslation(phrase, langid, &trans) == Trans_Okay)
     {
         return true;
     }
 
     return false;
 }

Anyway, here is the full breakdown on the commit.
https://github.com/alliedmodders/sourcemod/pull/669/files
 

Creating Software For Myself Or Others

My reason of starting this entire not-so-experienced programming blog is not to delve into technical detail and share my solutions to problems, but rather enable me to look back onto what I was thinking at the time and how I have grown. So basically, it’s not for you.

Wait, you’re still here?

Anyway. I feel like this is a good opener because its something I need to tackle head on. When a business or individual creates a piece of software, who do they make it for? Their customers? Themselves? Now obviously this depends on the type of person. An entrepreneur or business would probably make software for their customers, for the money.

However, who do I make software for?

Well, I guess the answer is that I make software for myself.

Everything I make is a solution to a problem I have, which gets posted publicly on my GitHub if it’s possible that someone else would find it useful, but I really haven’t ever made anything for someone else with the exception of a small TTS thingy. So maybe that’s my problem. I need to make more programs to solve bigger problems that will help others, as well as myself.