[Tutorial] Modular Programming
#1

What is modular programming?
Modular programming is a process of breaking down a code depending on its category to make the code reusable for future projects (avoid re-inventing the wheel) as well as making the code easier to read and track down mistakes. Modular programming comes handy if you are writing big projects, there's no point of modularizing your code if it doesn't consists of 5,000 lines as this can be done in one file but once again I am not your boss therefore this could be done on short codes but I see no point of doing so (personal opinion).

Advantages
  • Code reusability - This makes your life easier, if you're creating a new project that has similar needs your module can come handy and be re-used in this situation and saves you time! (wasting time on creating the code again from scratch).
  • Code readability - Modular programming can make your code much easier to read with few lines in each modules!
  • Ease of debugging and modifying - A modularized code are generally easier to debug than a code that has been done in ONE file. Why is it easier to debug? - Codes are isolated in a module and once a faulty code has been identified, fixing can be very easy. Modifying can come handy if a module has to be used once again! simple modification can be done and the module can be re-used again!
  • Multiple developers - If you are working as a team, each developer can work on a different module and this leads to faster development!
    • Each developer can use their expertise to work on a module! Maths, GUI, commands, arrays, checkpoints, etc...
Disadvantages
  • Variable names - each variable name should have a unique name or else it can cause warning 219: local variable "variable_name" shadows a variable at a preceding level
    • A fix for this is use static to declare the variable as a local variable yet you still need to use a non-generic variable name to avoid conflicts with other modules and released includes.
    • Another fix if a variable is declared locally, you can create a function if it needs to be used throughout the code.
  • Proper documentation - If you are working as a team, modules should be documented thoroughly for other developers to understand the code as well. (This also goes to codes that are going to be released in forums)
  • Hooking a function that is required to be re-used - This can be repetitive and hook should have a unique prefix. Writing the directives can be repetitive but this is why COPY and PASTE exists. Other solution is using y_hooks and including it every module. For y_hooks example see first 2 examples.
Examples Editor that supports multi-tasking
Notepad++ for PAWN - Slice
Sublime Text for PAWN - Southclaw
Atom for PAWN - Kaperstone

NOTE
You have to plan how you want to lay your code out or else it will be a disaster and you will be lost.


Layout
pawn Код:
- gamemodes
    - main.pwn
        - utils
            - colour.pwn
            - math.pwn
            - string.pwn
        - core
            - player
                - core.pwn
                - account.pwn
            - server
                - core.pwn
                - vehicles.pwn
pawn Код:
#include <a_samp>

//Modules

//Utils
#include "utils/colour.pwn"
#include "utils/math.pwn"
#include "utils/string.pwn"

//Player
#include "core/player/core.pwn"
#include "core/player/account.pwn"

//Server
#include "core/server/core.pwn"
#include "core/server/vehicles.pwn"
OR

pawn Код:
- includes
    - main.pwn
        - utils
            - colour.pwn
            - math.inc
            - string.inc
        - core
            - player
                - core.inc
                - account.inc
            - server
                - core.inc
                - vehicles.inc
pawn Код:
#include <a_samp>

//Utils
#include <utils/color>
#include <utils/math>
#include <utils/string>

//Player
#include <core/player/core>
#include <core/player/account>

//Server
#include <core/server/core>
#include <core/server/vehicles>

How does it work together?

Include is a directive that binds the contents of the module/second file together and insert it into the original file although the IDE doesn't read it this way but the compiler reads it like the code is in one-file.

enums.pwn
pawn Код:
enum E_PLAYER_DATA {
    plyUsername[MAX_PLAYER_NAME],
    plyPassword[65],
    plyKills,
    plyDeaths
}

new plyData[MAX_PLAYERS][E_PLAYER_DATA];
main.pwn
pawn Код:
#include <a_samp>

#include "core/player/enums.pwn"

new
    resetPData[E_PLAYER_DATA];

public OnPlayerConnect(playerid) {
    plyData[playerid] = resetPData; //sets all the value to 0.

    //Load everything here.

    return 1;
}

public OnPlayerDisconnect(playerid, reason) {
   
    //save everything here.

    plyData[playerid] = resetPData; //sets all the value to 0.
    return 1;
}
What actually happens
pawn Код:
#include <a_samp>

//this is enums.pwn
enum E_PLAYER_DATA {
    plyUsername[MAX_PLAYER_NAME],
    plyPassword[65],
    plyKills,
    plyDeaths
}

new plyData[MAX_PLAYERS][E_PLAYER_DATA];

new
    resetPData[E_PLAYER_DATA];

public OnPlayerConnect(playerid) {
    plyData[playerid] = resetPData; //sets all the value to 0.

    //Load everything here.

    return 1;
}

public OnPlayerDisconnect(playerid, reason) {
   
    //save everything here.

    plyData[playerid] = resetPData; //sets all the value to 0.
    return 1;
}
How does hooking work?
The modules should be done in a sequence in order for it to function properly. For example you are trying to open a database in main.pwn and you want to create a table of accounts in accounts.pwn to be able to do that. You
have to hook OnGameModeInit before every module and y_hooks (if you're using y_hooks). Example shown below.

main.pwn
pawn Код:
#include <a_samp>

/* -
    How it happens behind the console
    --------------------------------------
    * OnGameModeInitEx - first
        * hooks of OnGameModeInit - second
    --------------------------------------
    "OnGameModeInitEx" will be called first to avoid crashes and confusion within the hooks.
    Everything that you have to do regarding OnGameModeInit
    You have to place them in "OnGameModeInitEx" to make sure other hooks will not mess up on start up.
*/


public OnGameModeInit()
{

    OnGameModeInitEx();

    #if defined main_OnGameModeInit
        return main_OnGameModeInit();
    #else
        return 1;
    #endif
}
#if defined _ALS_OnGameModeInit
    #undef OnGameModeInit
#else
    #define _ALS_OnGameModeInit
#endif
 
#define OnGameModeInit main_OnGameModeInit
#if defined main_OnGameModeInit
    forward main_OnGameModeInit();
#endif

#include <YSI\y_hooks> //The y_hooks hook of OnGameModeInit will be called after the hook in main.pwn is called.
 
OnGameModeInitEx()
{
    db_open("server.db");
}

//We don't need to hook this because OnGameModeExit() hooks will be called first therefore the main OnGameModeExit() will be called last and this is what we need.
public OnGameModeExit()
{
    db_close(DB:0);
    return 1;
}

What happens behind?
pawn Код:
- hook of OnGameModeInit() calling OnGameModeInitEx() in main.pwn - This will be called first all the time!
- hook of OnGameModeInit() in different modules.
    - The hooks are called depending on the suquence of the module.

    For example
    - main.pwn
    - players.pwn
    - servers.pwn
    - checkpoints.pwn

    - OnGameModeInit() hook will be called first in main.pwn
    - OnGameModeInit() hook will be called second in players after main.pwn
    - OnGameModeInit() hook will be called third in servers after players.pwn
    - OnGameModeInit() hook will be called fourth in checkpoints after servers.pwn
Reply
#2

This is good. It would suit anyone who is considering the programming industry.
Reply
#3

Good tutorial
It would be a nice thing if people started to modular program, there are lots of good sа-mp scripters who still don't understand the advantage of dividing their project into modules, аnd thry publish huge gamemodes with 20000 lines of code
Reply
#4

This only explains the concept not the implementation 1-star from me because people will get to the end then ask "okay what do I do now?" it's like you left out 75 percent of the tutorial. It's a great start nice and neat but needs some effort to be useful.
Reply
#5

PBP compiler by Slice, is a very good tool for this.
Reply
#6

Good tutorial +rep
Reply
#7

Updated, explained how the modules binds together and how the hooking works in-order depending on module order.
Reply
#8

This is great. I've been trying to understand how to break down a gamemode into several filterscripts instead of one large, 20k+ lines script. Thank you very much.
Reply
#9

Okay this was actually really great, but I tried it and well ... obviously failed xD
I got an error not recognizing my main enum (PlayerI), If I move my enums to an include file it will be solved right ?
Oh and another question, is it better if I break down my enums into different modules or should I just put them all in one big file called header ? (because either way I should include all headers first right ?)
Reply
#10

Quote:
Originally Posted by PrO.GameR
Посмотреть сообщение
Okay this was actually really great, but I tried it and well ... obviously failed xD
I got an error not recognizing my main enum (PlayerI), If I move my enums to an include file it will be solved right ?
Oh and another question, is it better if I break down my enums into different modules or should I just put them all in one big file called header ? (because either way I should include all headers first right ?)
As I said your modules should be in order and its your choice, your the coder and you choose the layout that's more comfortable to you.

For example

pawn Код:
- declarations
- macros
- functions

- server

- player
Reply
#11

I'm relatively new to modular programming. Sometimes an include is not being included for some reason.

Example:
PHP Code:
#include "./AntiCheat/ac_money.pwn"
#include "./AntiCheat/ac_health.pwn"

// Inside ac_money
#if defined _ac_money_included
    #endinput
#endif
#define _ac_money_included

// Inside ac_health
#if defined _ac_health_included
    #endinput
#endif
#define _ac_health_included

// Under the #include directives in the main file
#if !defined _ac_health_included
    #error Anti health hack is not included
#endif

#if !defined _ac_money_included
    #error Anti money hack not included
#endif 
'ac_money' is included first in the example above and that will show the error: 'Anti health hack not included'.

Changing their position:
PHP Code:
#include "./AntiCheat/ac_health.pwn"
#include "./AntiCheat/ac_money.pwn" 
Now it will pop the error: "Anti money hack not included'.

Putting them together in one file works, but I don't want that obviously. Any suggestion on why that could happen? The rest of my modules get included perfectly.
Reply
#12

Quote:
Originally Posted by AndySedeyn
View Post
I'm relatively new to modular programming. Sometimes an include is not being included for some reason.

Example:
PHP Code:
#include "./AntiCheat/ac_money.pwn"
#include "./AntiCheat/ac_health.pwn"
// Inside ac_money
#if defined _ac_money_included
    #endinput
#endif
#define _ac_money_included
// Inside ac_health
#if defined _ac_health_included
    #endinput
#endif
#define _ac_health_included
// Under the #include directives in the main file
#if !defined _ac_health_included
    #error Anti health hack is not included
#endif
#if !defined _ac_money_included
    #error Anti money hack not included
#endif 
'ac_money' is included first in the example above and that will show the error: 'Anti health hack not included'.

Changing their position:
PHP Code:
#include "./AntiCheat/ac_health.pwn"
#include "./AntiCheat/ac_money.pwn" 
Now it will pop the error: "Anti money hack not included'.

Putting them together in one file works, but I don't want that obviously. Any suggestion on why that could happen? The rest of my modules get included perfectly.
Is the folder inside gamemodes? If yes include them this way #include "folder/pwnfile.pwn". Also make sure you put the anti-cheat first before other modules just to make sure these hooked function functions properly. also its unnecessary to have. if !defined directive.
Reply
#13

It was basically to check whether the include was included or not. They are in my gamemodes folder, yes. Including them like you advised me to gives me the same problem.
Reply
#14

Quote:
Originally Posted by AndySedeyn
View Post
It was basically to check whether the include was included or not. They are in my gamemodes folder, yes. Including them like you advised me to gives me the same problem.
I've tried your code and everything seems to be working fine... I am not sure where you made your mistake but maybe taking a look at my code and you'll realize your own mistake.

ac_health.pwn
pawn Code:
#if defined _ac_health_included
    #endinput
#endif
#define _ac_health_included
ac_money.pwn
pawn Code:
#if defined _ac_money_included
    #endinput
#endif
#define _ac_money_included
main.pwn
pawn Code:
#include <a_samp>

#include "AntiCheat/ac_money.pwn"
#include "AntiCheat/ac_health.pwn"

#if !defined _ac_money_included
    #error Anti money hack not included
#endif

#if !defined _ac_health_included
    #error Anti health hack is not included
#endif
Reply
#15

Will look into it. Thank you.
Reply
#16

If i include y_hooks in main do i still need to include it in every other module?
Reply
#17

No you don't need to, systems are still part of the same giant structure which y_hooks is already part of it.
Reply
#18

Quote:
Originally Posted by Kimble
View Post
If i include y_hooks in main do i still need to include it in every other module?
No, as long as you include it before the other includes.

Includes are copied and pasted into the gamemode.
Reply
#19

Yes it has many advantages as ive seen in many big gamemodes which used this method especially to protect their gamemode to fully be leaked.
Reply
#20

Erm, as for y_hooks, if you are hooking callbacks in every include, then you need to include y_hooks too (in reality this will only load y_unique to make sure your callbacks are, well, unique)
Reply


Forum Jump:


Users browsing this thread: 4 Guest(s)