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
 

Leave a Reply

Your email address will not be published. Required fields are marked *