Job Finding As A Computer Engineering Student

Well, here’s to the first post of 2018. This year is starting off quite strong; people are eating tide pods, Donald Trump said in reference to African countries, they’re “shithole countries,” and the spring semester is starting in a few days and I’ll be taking three mathematics classes. Besides the unfortunate reality that my social life will soon be non-existent, I’m also searching for a job. Not your average college-student-mindless-brain-melting job, but one which is actually engaging and (here’s the kicker) in industry.

Finding jobs in software engineering is quite easy, when you’re a college graduate. As a student, your only options for part-time jobs in industry end up being (hopefully paid) internships, if you can find them in the first place. As someone who has no connection to software companies outside of those involved in the SourceMod project, putting your foot in the door is difficult, especially when the only companies hiring in my area are looking for CS/CSE graduates (or equivalent) with full-time hours, an impossible task for students unless you’re a super-human and involved in the medical field, where there always an abundance of people working full-time jobs while being full-time students.

At the end of the day, all I can keep doing is to look for opportunities in my area, constantly check job websites, consistently put myself out there to companies as an employable intern with an advanced skill set considering my degree progress and hope for the best.

Feature Implementation In Large Unfamiliar Codebases

I feel like every contribution I make to the SourceMod project leaves me on a wild goose-chase following class hierarchies and I always end up with about 10-20 tabs open of different files in Notepad++ before I have a clue about what I need to do. Luckily this time, I decided to write down some of this goose-chase. Here goes.

In SourceMod errors are loud and halt execution. This is by design, as any error that may occur during plugin execution is nearly always the programmer’s fault (even if it really isn’t, the author should be doing proper checking). So if you wanted a stack trace and to halt a function’s execution you’d use the native SetFailState¬†with some useful error message.

However, in SourceMod (pre this pull request) there’s no way of grabbing a stack trace from a point in time without halting function execution. This would be useful for reporting errors or debugging, but you still want the rest of your function call to be completed.

In an effort to stay organized I drafted what I’d like to see from an input -> output point of view and here’s what I came up with:

The following code would output two stack traces. One will be from the native I intend to make RequestStackTrace() & the already existing SetFailState().

#include <sourcemod>

public void OnPluginStart()
{
    SomeFunction();
}

void SomeFunction()
{
    SomeFunction2();
}

void SomeFunction2()
{
    RequestStackTrace("RequestStackTrace %i %.2f", 4, 10.0);
    SetFailState("SetFailState %i %.2f", 4, 10.0);
}

Which would output the following into the error logs

L 09/17/2017 - 15:07:55: [SM] Stack trace requested: RequestStackTrace 4 10.00
L 09/17/2017 - 15:07:55: [SM] Call stack trace:
L 09/17/2017 - 15:07:55: [SM] [0] RequestStackTrace
L 09/17/2017 - 15:07:55: [SM] [1] Line 15, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::SomeFunction2
L 09/17/2017 - 15:07:55: [SM] [2] Line 10, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::SomeFunction
L 09/17/2017 - 15:07:55: [SM] [3] Line 5, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::OnPluginStart
L 09/17/2017 - 15:07:55: [SM] Exception reported: SetFailState 4 10.00
L 09/17/2017 - 15:07:55: [SM] Blaming: test.smx
L 09/17/2017 - 15:07:55: [SM] Call stack trace:
L 09/17/2017 - 15:07:55: [SM] [0] SetFailState
L 09/17/2017 - 15:07:55: [SM] [1] Line 16, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::SomeFunction2
L 09/17/2017 - 15:07:55: [SM] [2] Line 10, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::SomeFunction
L 09/17/2017 - 15:07:55: [SM] [3] Line 5, C:\Users\Micha\Desktop\sourcemod\sourcemod\build\package\addons\sourcemod\Test.sp::OnPluginStart

It’s beautiful isn’t it? Well, now that we have expected usage and output, we can actually implement this. First, do you remember earlier I said that I decided to write down my goose-chase? Well, here is my step by step mental process, before I hit a huge roadblock.

Trying to solve https://bugs.alliedmods.net/show_bug.cgi?id=4317 (Dump call stack)

Found DebugReporter.h in core/logic/ and found IFrameIterator ; https://github.com/alliedmodders/sourcemod/blob/master/core/logic/DebugReporter.h#L45

Looked at the definition for DebugReport::ReportError ; https://github.com/alliedmodders/sourcemod/blob/master/core/logic/DebugReporter.cpp#L158

Looking to either create 'DebugReport::GetTrace(IFrameIterator)', but the problem is that the trace is logged step by step, and not cached at all.
This means that DebugReport::GetTrace would have to return a 2d char array. One column per line of the stack trace. Then I'd have to change DebugReport::ReportError
and have it just loop that array and throw it's contents to logs.

This adds an extra step for DebugReport::ReportError, but also allows us to make stack traces more dynamically. In fact, the presence of a bool 'isIntentional' could
allow us to determine if "Stack trace requested: %s" should be used instead of "Exception reported: %s"

 

This is all fine and dandy, most of what I said ended up being what I used in my actual implementation. The problem, however, is that in order for the stack trace to be built we need a class from the interface sp::IFrameIterator, but as you can see if you have a keen eye IFrameIterator is a part of sourcepawn, and there is no actual implementation IFrameIterator anywhere inside of SourceMod, nor are there any interfaces to GET a plugin’s FrameIterator at runtime. Fuck.

I had a discussion with VoiDeD, a software developer who is quite keen on the SourceMod project. If you’d like to read the entire conversation, I’m not going to stop you, but here’s the tldr: I originally wanted to copy the definition of FrameIterator to SourceMod core, but was told that the better option is to expose/define CreateFrameIterator & DestoryFrameIterator methods inside of sourcepawn, and call those to grab the FrameIterator I need inside of SourceMod.

So, I did and the rest was just a matter of programming it. If you’d like to see the pull requests for sourcemod and sourcepawn they’re here and here, respectively. Otherwise that’s about it. I’m positive there will be more wild goose-chases in my future, but I just hope to god I end up gaining some familiarity. Even if it’s one pr at a time…

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-&gt;GetContext());

    pCtx->LocalToString(params[1], &amp;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-&gt;GetContext());
    IPhraseCollection* collection = pl->GetPhrases();

    char *phrase;

    pCtx->LocalToString(params[1], &amp;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.