Skip to content

How to Contribute

Phabbits edited this page Feb 14, 2022 · 1 revision

Contents

New to Github

Coding Standards

Style Guide

Issues

Pull Requests

Code Reviews

New to Github

  1. To begin contributing, download the github desktop app here
  2. Clone the desired project: File>Clone Repositiory>Enter the URL of the repository page
  3. Create a branch as your name
  4. Hit publish branch
  5. Hit open this repository in explorer in the middle of the app
  6. Open the project in your favorite text editor
  7. Make your edits and save the file
  8. On the GitHub desktop app, commit the changes in the bottom left, adding a title and description of your changes
  9. Click Push Origin at the top right
  10. Go to GitHub online and create a pull request under the Pull requests tab.
  11. Wait for your request to be reviewed

Coding Standards

This project is coded in GML. When programming, it is important you follow these standards to increase readability and avoid errors.

Variable Naming

There are many types of variables and separating which type is which will greatly increase readability. In general variable names should be short and concise. However, if a slightly longer name will increase readability, typing long variable names will not be the death of us. Follow these guidelines:

'Normal' Variables

The typical variable you define, such as player_health or room_width should be written in all snake_case, with words separated by capitalizing the first letter of the next word. Numeric suffixes can be added directly to the end such as block3. Note: variable names can never start with number. Not 2player but player2.

Constants

Constants are definitions that do not change, ever. For example pi or player_speed (If the player always travels at the same speed). Think, "Am I ever going to change this variable? Ever?" If the answer is no, it's a constant. Constants are written in all caps, e.g. PI or PLAYER_SPEED.

Counters

Counters are variables that get incremented or decremented in a for or while loop. If the variable is being used specifically for counting in that loop, i j and k should be used. For example:

int i;
int j;
for (i=0; i<10; i+=1){
  for (j=0; j<10; j+=1){
    // Code
  }
}

Function Declaration

All functions will be declared after main, with a function prototype copied to a designated // Function prototypes section before main. See Function Prototypes on how to style the prototype statement.

Printing Output

The defacto standard console width is 80 characters, so no single line output should be more than this.

Newline characters \n should be implemented at the end of a print statement because of line-buffering which may cause a line to not appear until a newline character. Source

Style Guide

The style guide is standard and following it will make your code easier to read, especially for reviewers. The guide may seem nitpicky but getting into the habit of good coding will make things easier for you and others.

Indentation

Indentation is not required in C but is highly recommended. Every level or loop of code that is nested should be indented one further than the level right above it. Here's an example:

switch (state){
  case 0:
    // Do first thing
  
  case 1:
    // Do second thing
}

Note how each block of nested code is indented one more level than the last. It's easy to see where all the cases are for the switch statement because they're all at the same indentation or column.

New Lines (Vertical Spacing)

Also note the usage of new lines in the above example, one after each block of code. In general, add a newline after each loop or statement that doesn't contain the "final level" of code or the code that actually does something: action code, if you will. The exception is that there is no new line needed when the next line is indented further than the previous line. That is, a new line of code should not be both indented and have a blank line. There is no line after the switch statement because the case statement is already indented. There is no line after the case 0: or case 1: because they contain the action code. However! There is a line between the case statements because they are not action code. Here's another example:

// Other code, unrelated to the for loop

int i;
for (i=0; i<10; i+=1){
  // Do something each time
  
  if i < 5{
    // Do something else when i<5
  }
}

Basically code is broken into blocks by newlines. The declaration int i; is related to for loop so there is no blank line after it. The "Other code" however is unrelated and therefore there is a blank space after it. The action code is directly under the for loop and if statement. The if statement is not, itself, action code though, so it has a line separating it from the action code above it since it is on the same indentation as it. Don't be stingy with space! Each statement deserves it's own line. Don't use x = 1; y= 1;, instead do:

x = 1;
y = 1;

C also allows for control statements to all be on one line if there is only one line of code to execute. For readability, it is preferred this is avoided unless a long list of control statements all execute one line of code each.

// Bad
if lives < 1 destroy_instance();
// Good
if lives < 1{
  destroy_instance();
}
// Also good
if condition_1 action_1;
if condition_2 action_2;
if condition_3 action_3;
if condition_4 action_4;

There's endless amounts of space and since C is not a whitespace language, it just ignores all the unnecessary lines anyway. It's just for readability!

Spaces (Horizontal spacing)

Space can convey meaning and make what is happening in the code much clearer if we use it consistently. I'll try to list as many examples as I can to show you when a space is appropriate and when it is not.

Situation Needs spaces? Example(s)
Variable Assignment Yes speed = 5
Operators Yes speed += 5 height * room_height speed < 5
Parameter Assignment No function(param=1)
After keyword1 Yes while (x > 2 && y < 3){
for loop assignments No for i=10 etc.
for loop sections Yes for (i=10; i>0; i++) (After semicolons)
After commas Yes fuction(param1, param2, param3)
Around parentheses No grid = (room_height / 64) * 5
After statements, before braces No if x == true{

1This differentiates them from function calls where there is no space. Source

Braces

Include immediately after the statement, regardless of its size. End them on a different line, aligned with the beginning of the statement they refer to. Most text editors take care of this automatically.

if statement{
  if statement{
    // Do stuff
  }
}

Don't add an extra line between braces, just keep the all on different lines.

Parentheses

Use parentheses when necessary or when they improve readability, try not to overuse them. C follows order of operations so 2 + 2 * 2 = 6 not 8, no parentheses required.

In regard to control statements, they are not required! However, do use them if the statement is lengthy or has multiple conditions.

Good:

if speed < 5{
  speed += 1;
}

Also good:

if (speed < 5 and energy > 5) or health < 10{
  speed += 1;
}

Logical operators

Parentheses may be needed with these if you're applying them to multiple terms.

if lives > 0 && health <= 0{
  // Restart
}

No parenthesis needed

or:

if !(speed == 5){
  speed = 5
}

Note: The parentheses here are needed or the ! will only negate the speed and not the comparison speed == 5. No parenthesis are needed for negating boolean values, e.g. if ! is_alive{ would work if is_alive is a boolean (true or false).

Pointers

Pointer declarations will have the indirection operator attached to the variable name, not type. For example int *some_ptr; not int* some_ptr;. This more closely corresponds to C's language grammar and avoids confusion in the case of int *some_ptr, i;, where i is just an int.

Per the naming conventions all pointers must end with _ptr.

Function prototypes

Function prototypes will be declared before main and will include the parameter names from the function. This will allow for a simple copy and paste of the function declaration statement to the top when it is done.

// Function prototypes
void display_results(struct Player *player_ptr);

Commenting

Comments are critical for code that will be reviewed, shared, and edited. As is our quest in coding, knowing when and how much to comment will improve readability and manageability. There are a few different types of comments.

Function Documentation

These comments take place at the beginning of a function. The Documentation should include the function's purpose, parameters, and what it returns. For documentation a multiline comment is used with the beginning and ending line taking up all 80 characters of a standard console display.

/******************************************************************************
* Function:    display_results
* Description: Print list of results
* Parameters:  playerPtr, Player *, pointer to the player
* Return:      void
******************************************************************************/
void display_results(struct Player *player_ptr){
  // Actual Code

In-line comments

Finally in-line comments can help supplement code. In general, if the style guide and standards are followed, code should be fairly readable on its own. However, if further explanation is needed besides what is obvious, in-line comments are required.

In-line comments use the // at the beginning of the line and should always take place before the code they are referring to on a separate line, (except in the case of a long list in which each line needs a comment, then they can go on the same line).

All comments should start with one space as per suggested in Python's style guide PEP 8, and discussed on this forum. This will allow for easy identification of comments vs commented-out code.

// Comment about code
// Code itself

In-line comments should say why a certain thing is being done instead of simply restating what is happening in the code. For example:

// Check to see if the speed is less than 5, then set it to 5
if speed < 5{
  speed = 5
}

This is bad. The comment states the obvious. So what do you comment here? Nothing. Comments are for explaining the ambiguous, if they don't make something clear that wasn't clear before they shouldn't be included. Here's a good comment to reduce ambiguity:

// If the player is below the kill line, kill him
if obj_player.y < ((room_height / 64) + 8) * level{
  instance_destroy(obj_player)
}

The random coordinates may not be obvious to someone looking at this code for the first time. The comment explains why the coordinate is used. Of course, even better might be to use a variable: kill_line = (room_height / 64 + 8) * level since the line if obj_player.y < kill_line would be self explanatory. Anyway, use in-line comments sparingly, always asking yourself if you could still explain this code in a month, or year. If not, add a comment.

A short inline comment should be included before a section of code (more than 1 or 2 lines) to identify the purpose of the code, even if the code is self-explanatory. This is to allow programmers to quicky break down what's going on in an event without reading all the code.

// Release object
with instance_destroy(other){
  inventory -= 1;
  sprite_index -= 1;
}

Issues

Creating Issues

You see a problem with the game. Or maybe something you think should be added? Time to create an issue.

This is done in the issues tab by hitting new issue. Several templates will appear to help you construct a well formatted issue. If you have a bug, fill out the bug template. If you don't have a specific idea but want to discuss something or brainstorm ideas, fill out the discussion thread template (don't forget to label this issue as discussion).

After you fill out the issue form, label it with the correct issues, add it to related projects, milestones, etc. Well categorized issues are important if someone is trying to find issues related to a specific problem.

We use Markdown for all our issue formatting. For a very succinct lesson in Markdown formatting visit this site.

Closing an Issue

Generally leave closing an issue to the person who opened it or the code reviewer who pulled the solution to the issue. If an issue has had no attention and seems it will not be dealt with, submit a comment mentioning the issue creator (@USERNAME using their username) asking if it can be closed.

If an issue is tagged bug and you follow the reproduce steps but can't reproduce the bugs do not close it. Instead label it with could not reproduce and wait for another user to confirm it.

If you're searching for an issue and can't find it, don't forget to search the closed issues as well.

Labels

See the labels page for a list of labels.

Pull Requests

A pull request is how code is added from your branch to the main (or master branch). You request that the code be reviewed, and once it is approved, you can merge it into the master's branch. It is important you read and follow the steps listed below as they help protect the master branch from bugs and glitches, as well as give order to the whole process.

Submitting a Pull Request

So how do you submit a pull request? Well, once you have completed an issue:

  1. Carefully test your code. It should compile without errors, perform the intended action, and not alter unrelated sections of the game
  2. Commit the changes to a branch named after you (if you don't have one go to Branch>New Branch on the GitHub Desktop)
  3. Submit a pull request on the pull request tab
  4. Type in relevant information including issue number into the prompt
  5. Go to your issue and label it with the green pull request submitted label so others know you have completed that issue and are just waiting on a review
  6. Don't close the issue. The reviewer will close it when he/she has ensured your code can be safely added to the master branch. In the meantime others will want to know that your code has not yet been added

Making Required Changes

Your pull request will be reviewed and the reviewer will almost always find something that needs to be changed, whether it be a small bug or something larger. Once you receive change requests and make the changes, they will be automatically added to your pull request as you commit them to your branch.

Be sure to update the Changes Requested category if a change alters the content of your pull request. If you feel a reviewer has requested a change that is unnecessary or incorrect, politely point this out in a comment on the pull request. If the the reviewer still denies your change and you still believe it to be a good one, you can request an additional review. If your reviewer(s) do not approve of the change, don’t lose heart! Not every change suggested will get into the main game. Move on to another change.

Note: If you do have to remove part of a pull request, don't delete it! You can just make the change on your branch and make another commit to override the previous one.

Code Reviews

Reviewing submitted pull requests is a necessary step to building a successful project. It's not always fun, but its necessary. If you want to review code, there are some requirements you should meet:

  1. You should be familiar with C in general
  2. You should be aware of what makes code efficient or inefficient so you can make suggestions for submitted code
  3. You should be familiar with the project in general to prevent subtle bugs from occuring in other project sections due to code that has unintended consequences, such as an effect on networking.

Types of Reviews

Reviews can take 3 forms:

  1. Approve - Submit the review without requiring changes to the code
  2. Request Changes - Submit the review requiring changes to the code
  3. Comment - Make a suggestion without approving or rejecting the code

The comment is just a note for the reviewee, and may be submitted by anyone as it does not give or reject approval.

How to Write a Review

Reviews should be clear and concise to save time for both the reviewer and the reviewee. When writing a review you can type suggestions directly attached to lines of code by going to the files changed and clicking on the blue comment below the line. You can also suggest a correction here using ctrl-g that the reviewee can use to automatically change the code without submitting another commit. In-line comments should be used when possible for smaller errors, one-time mistakes, and typos. If a larger issue is present such as:

  • Code that raises numerous errors
  • Code that effects unrelated portions of the project
  • Code that is generally inefficient

A general comment can be submitted at the end of the review. Reviews should always be kind and respectful. Suggest rather than demand changes and be open to different ways of doing things, even if they aren't how you pictured the solution to an issue.

What to Correct

Reviews must be thorough, even if just to educate the reviewee. Every pull request is a learning experience. Here's what to point out to reviewees (based on guidelines written by ryanmcdermott here)

  • Styling issues - improper indentation, new lines, spaces, variable capitalization, etc. Just mention it and/or propose the proper style, don't dwell on it. If someone consistently has improper style, politely direct them to the Style Guide in the general review
  • Ambiguous function names - If a function name is very confusing, mention it, suggest a better one if you can.
  • Long functions - If a function is very lengthy, make sure the function is only completing it's specified task. For example if the function is scr_deal_damage, make sure the function only deals damage, and doesn't also check if the player is dead. That should use a different function, even if the scr_check_if_dead function is called during scr_deal_damage
  • Lacking comments - If code is complex and you have to spend a lot of time interpreting it, it should be commented. Fuctions should all have proper documentation. If a strange approach is taken, a comment should explain that approach to avoid confusion later. Follow the style guide. Don't worry too much about unneeded comments unless they are excessive.
  • Side effects - If code is going to cause obvious complications or is needlessly limiting, a suggestion should be made for more flexible code
  • Error catching - Code should catch exceptions to prevent fatal errors. What if the modified object is destroyed? What if a different room is entered?
  • User input - Along the same lines, errors regarding unexpected user input should be handled. What if the user enters a string where you expeceted an int? What if the user inputs an unexpected control or escape key?
  • Inefficient Code - Correct code that puts unecessary strain on the processor or server. Consider networking in all codes application.
  • Document TODOs - If code includes TODO comments, ensure those comments are documented either in new issues or somewhere else, don't allow the code to be too cluttered with TODO comments.
  • Avoid submitting general game ideas or possible enhancements - Did the code submitted fulfill the issue it was connected to? Then your ideas for improvements belong in a new enhancement issue.