Gamemode by include
#21

Quote:
Originally Posted by Gammix
View Post
There is a difference in "modular scripting" and "object oriented scripting".
The word module is self explanatory. It means spliting your script, either by hooking way(which i recommend) or simple splitting.

Also another way out is using filterscripts and (S)PVars fpr script integration. The only reason you do modular type of scripting is for:
- Script integration (like you want to get player admin level in more than one module, like you have login dialog in login.pwn and want to access that variable in say houses.pwn)

- Making a part more readable and editable, like you know in future you want to change textdraws, you can make a function "CreateSelectionTextDraws" in a seperate module so it will be easy for modifying the tds)

- Plug'n'play features, they are same as filterscripts (like maps or objects; a custom vending machine script etc)

I remade serversided.inc using modular scripting technique, if you want a refrence here it is: https://github.com/Agneese-Saini/SA-...de/serversided
Actually, only important files that connect stuff like MySQL connection info or SQLite details should be in modules, and then included to the gamemode so it can be used in other scripts without rewriting it, but adding callbacks for an event in a module? how does that even make sense?

Modular design examples:
- create "houses.pwn/p/inc" module which contains the functions that adds houses, removes houses or edits houses.
- create "database" module that connects to database, returns connection handle, closes connection, etc.
- create "accounts" module that adds accounts, removes accounts, renames accounts, etc.

So it can be used in both gamemodes and other scripts.

The modular designs which I call shit:

- create "callbacks.pwn headers.pwn commands.pwn" for a single script, definitely not going to be used in other scripts so it's really useless to do that.

Anyways, it's possible to achieve some sort of basic/simple modular design in PAWN. Unlike other high level languages such as C++ or C#.
Reply
#22

Well.. I'm trying some stuff. Is it the good way?
From MySQLConnection.inc
PHP Code:
/*
    -
*/
#if defined ji@core_MySQLConnection
    #endinput
#endif
#define ji@core_MySQLConnection
#if !defined OnTestServer
    #error "OnTestServer n'est pas dйfinie!"
#endif
#if OnTestServer
    #define    MYSQL_HOST        "localhost" 
    #define    MYSQL_USER        "root" 
    #define    MYSQL_DATABASE    "myserver" 
    #define    MYSQL_PASSWORD    ""
#else
    #if !defined MYSQL_HOST
        #define    MYSQL_HOST        "127.0.0.1" 
        #define    MYSQL_USER        "-" 
        #define    MYSQL_DATABASE    "-" 
        #define    MYSQL_PASSWORD    "-"
    #endif
#endif
new MySQL:MySQL;
stock ConnectToMYSQLDataBase()
{
    new 
MySQLOpt:option_id mysql_init_options();
    
mysql_set_option(option_idAUTO_RECONNECTtrue);
    
MySQL mysql_connect(MYSQL_HOSTMYSQL_USERMYSQL_PASSWORDMYSQL_DATABASEoption_id);
    
    if(
MySQL == MYSQL_INVALID_HANDLE || mysql_errno(MySQL) != 0)
    {
        print(
"[SERVER] Connexion MySQL ratйe. Fermeture du serveur...");
        
SendRconCommand("exit");
        return 
1;
    }
    print(
"[SERVER] Connexion MySQL rйussie.");
    
mysql_log(ALL);
    return 
1;

From my gamemode:
PHP Code:
#include "/newGM/core/MySQLConnection.inc"
...
public 
OnGameModeInit() 
{
    print(
"\n\n[SERVER] Initialisation du gamemode...");
    
#if OnTestServer
        
print("[SERVER] Bienvenue sur le serveur de test!");
    
#endif
    #if DebugMode
        
print("[SERVER] MODE DEBUG ACTIVЙ !");
    
#endif
    
ConnectToMYSQLDataBase();
... 
And..
I still don't understand this:
Quote:
Originally Posted by patrickgtr
View Post
Code:
/* - 
	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.
*/
Reply
#23

Quote:
Originally Posted by Dayrion
View Post
Hello. I'm wondering if it's not better to make a gameode with only include.
Example:
- MainFolder
-- Players
--- VariablesSaveLoading
--- Other
-- Vehicle
...
And all of them included in one gamemode.
Here is an example of how I lay things out

Code:
- {Name of Project} (Folder)
    - Config ( Core )
        - base.pwn ( Global Definitions, MySQL, Server Settings )
        - macros.pwn ( Global Macros for shortcuts like FUNCTION: and PUBLIC: )
        - plugins.pwn ( Global Plugin Definitions )
    - Player ( Module ) // The Prefix will be PD for PLAYER_DATA the name of the ENUM ( E_PLAYER_DATA )
        - init.pwn ( Includes, Initialize and Deinitialize functions )
        - config.pwn ( Enums, Definitions, Variables )
        - functions.pwn ( IsPlayerLoggedIn, etc )
        - database.pwn ( DB Functions / Threaded Query )
        - callbacks.pwn ( Prefixed Names, PD_OnPlayerConnect )
        - dialogs.pwn ( Functions / Responses )
        - textdraws.pwn ( Functions / Callbacks )
        - commands.pwn ( Player / Admin Commands )
        // You can just add a file for each section, pickups / objects, etc
 
     bootstrap.pwn // includes all modules and core files, so main gamemode only needs to include this file.
I hope this helps, I find this method to work the best for me, since I know whats in the file just by the name of it.

As for your mysql idea I find this to be a better way.

Code:
#define 	ENVIORNMENT 		        ( 0 ) 		// 0 - Production, 1 - Local Development, 2 - Remote Development

#define 	MYSQL_REMOTE_HOST 		"xxx.xxx.xxx.xxxx"
#define    	MYSQL_REMOTE_USER 		"server_name"
#define 	MYSQL_REMOTE_PASS 		"server_password"
#define 	MYSQL_REMOTE_TABLE 		"server_table"

#define 	MYSQL_LOCAL_HOST 		"xxx.xxx.xxx.xxxx"
#define    	MYSQL_LOCAL_USER 		"server_name"
#define 	MYSQL_LOCAL_PASS 		"server_password"
#define 	MYSQL_LOCAL_TABLE 		"server_table"

#define 	MYSQL_PRODUCTION_HOST 	"xxx.xxx.xxx.xxxx"
#define    	MYSQL_PRODUCTION_USER 	"server_name"
#define 	MYSQL_PRODUCTION_PASS 	"server_password"
#define 	MYSQL_PRODUCTION_TABLE 	"server_table"


// Now depending on how much you care, not that it makes much difference that you should, you can either use #if and #endif
// or you can a switch statment, I prefer swtich as it looks cleaner, and I love clean code.

new 
	database_connection
;

stock mysql_connect_ex()
{
	switch( ENVIORNMENT )
	{
		case 0: database_connection = mysql_connect( MYSQL_PRODUCTION_HOST, MYSQL_PRODUCTION_USER, MYSQL_PRODUCTION_PASS, MYSQL_PRODUCTION_TABLE );	
		case 1: database_connection = mysql_connect( MYSQL_LOCAL_HOST, MYSQL_LOCAL_USER, MYSQL_LOCAL_PASS, MYSQL_LOCAL_TABLE );
		case 2: database_connection = mysql_connect( MYSQL_REMOTE_HOST, MYSQL_REMOTE_USER, MYSQL_REMOTE_PASS, MYSQL_REMOTE_TABLE );
	}

	return database_connection;
}
Reply
#24

Quote:
Originally Posted by azzerking
View Post
Here is an example of how I lay things out

Code:
- {Name of Project} (Folder)
    - Config ( Core )
        - base.pwn ( Global Definitions, MySQL, Server Settings )
        - macros.pwn ( Global Macros for shortcuts like FUNCTION: and PUBLIC: )
        - plugins.pwn ( Global Plugin Definitions )
    - Player ( Module ) // The Prefix will be PD for PLAYER_DATA the name of the ENUM ( E_PLAYER_DATA )
        - init.pwn ( Includes, Initialize and Deinitialize functions )
        - config.pwn ( Enums, Definitions, Variables )
        - functions.pwn ( IsPlayerLoggedIn, etc )
        - database.pwn ( DB Functions / Threaded Query )
        - callbacks.pwn ( Prefixed Names, PD_OnPlayerConnect )
        - dialogs.pwn ( Functions / Responses )
        - textdraws.pwn ( Functions / Callbacks )
        - commands.pwn ( Player / Admin Commands )
        // You can just add a file for each section, pickups / objects, etc
 
     bootstrap.pwn // includes all modules and core files, so main gamemode only needs to include this file.
I hope this helps, I find this method to work the best for me, since I know whats in the file just by the name of it.

As for your mysql idea I find this to be a better way.

Code:
#define 	ENVIORNMENT 		        ( 0 ) 		// 0 - Production, 1 - Local Development, 2 - Remote Development

#define 	MYSQL_REMOTE_HOST 		"xxx.xxx.xxx.xxxx"
#define    	MYSQL_REMOTE_USER 		"server_name"
#define 	MYSQL_REMOTE_PASS 		"server_password"
#define 	MYSQL_REMOTE_TABLE 		"server_table"

#define 	MYSQL_LOCAL_HOST 		"xxx.xxx.xxx.xxxx"
#define    	MYSQL_LOCAL_USER 		"server_name"
#define 	MYSQL_LOCAL_PASS 		"server_password"
#define 	MYSQL_LOCAL_TABLE 		"server_table"

#define 	MYSQL_PRODUCTION_HOST 	"xxx.xxx.xxx.xxxx"
#define    	MYSQL_PRODUCTION_USER 	"server_name"
#define 	MYSQL_PRODUCTION_PASS 	"server_password"
#define 	MYSQL_PRODUCTION_TABLE 	"server_table"


// Now depending on how much you care, not that it makes much difference that you should, you can either use #if and #endif
// or you can a switch statment, I prefer swtich as it looks cleaner, and I love clean code.

new 
	database_connection
;

stock mysql_connect_ex()
{
	switch( ENVIORNMENT )
	{
		case 0: database_connection = mysql_connect( MYSQL_PRODUCTION_HOST, MYSQL_PRODUCTION_USER, MYSQL_PRODUCTION_PASS, MYSQL_PRODUCTION_TABLE );	
		case 1: database_connection = mysql_connect( MYSQL_LOCAL_HOST, MYSQL_LOCAL_USER, MYSQL_LOCAL_PASS, MYSQL_LOCAL_TABLE );
		case 2: database_connection = mysql_connect( MYSQL_REMOTE_HOST, MYSQL_REMOTE_USER, MYSQL_REMOTE_PASS, MYSQL_REMOTE_TABLE );
	}

	return database_connection;
}
I've 2 connection's type : when i'm on local (OnTestServer) and when I'm not. I'm using OnTestServer in differents commands like /ban, etc..
For this:
Code:
    - Config ( Core )
        - base.pwn ( Global Definitions, MySQL, Server Settings )
        - macros.pwn ( Global Macros for shortcuts like FUNCTION: and PUBLIC: )
        - plugins.pwn ( Global Plugin Definitions )
I'm doing like this:
"../core/configuration.inc" (GM's inititialization)
"../core/MySQLConnections.inc" (Creating table, connection to mysql, etc...)
"../core/base.inc" (Macros and constants)
That's good?

Annnd..
I'm trying a new thing. It's for creating logs (from configuration.inc):
PHP Code:
new 
    
Log_Level:Log_Server,
    ...;
enum Log_Level:LogType
{
    
NOTHING,
    
COMMANDS,
    
TCHAT,
    
FULL_LOG NOTHING COMMANDS TCHAT
};
stock CreateServerLog(Log_Level:Log_Serv Log_Level:FULL_LOG)
{
    
Log_Server Log_Serv;
    if(
_:Log_Serv == NOTHING)
        return print(
"[SERVER] Aucun log crйй."), 1;
    static
        
string[120],
        
hour,
        
minu,
        
sec,
        
day,
        
month,
        
year;
    
Get@SetWorldTime(hourminusec);
    
getdate(yearmonthday);
    
format(stringsizeof(string), "-------- Dйbut -------- [%02i/%02i/%02i] - %02ih %02imin %02is."daymonthyearhourminusec);
    
WriteLogs("AllCommands.txt"string);
    print(
"[SERVER] Logs configurйs.")
    return 
1;
}
stock Get@SetWorldTime(&hour = -1, &minutes = -1, &seconds = -1)
{
    
gettime(hourminusec);
    
SetWorldTime(hour);

Log_Server is gonna be used when writing logs.

EDIT: Write logs function
PHP Code:
WriteLogs(file[70], const reason[], playerid INVALID_PLAYER_ID)
{
    if(!
_:Log_Server)
        return 
1;
    static     
day,
            
month,
            
hour,
            
minute,
            
seconde,
            
year;
    static    
stringW[190];
    if(
strfind(file".txt"false0) == -1format(filesizeof(file), "%s.txt"file);
    new 
File:pos=fopen(fileio_append);
    if(!
pos)
    {
        
printf("[Server] Write Logs Error | Reason : pos=fopen Error | File : '%s' | Reason: '%s'"filereason);
        
SendMessageToAdmins(RED"[Server] "SAUMON_U" Une erreur est survenue dans les logs. Merci de vйrifier ceux-ci!");
        
SendMessageToAdmins(RED"[Server] "SAUMON_U"Fichier : '%s' | Contenus : '%s'"filereason);
        return 
1;
    } 
    
    
gettime(hourminuteseconde);
    
getdate(yearmonthday);
    if(!
strcmp(file"AllCommands.txt") && _:Log_Server == L_COMMANDS)
        
format(stringW,sizeof(stringW),"\r\n[%i] [%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"CmdID++, daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    else if(!
strcmp(file"AllTChat.txt") && _:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s (Player #%i) » '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"p_NameOff{playerid}, reason);
    else if(
_:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    
fwrite(pos,stringW);
    
fclose(pos);
    return 
1;

Reply
#25

Quote:
Originally Posted by Dayrion
View Post
I've 2 connection's type : when i'm on local (OnTestServer) and when I'm not. I'm using OnTestServer in differents commands like /ban, etc..
For this:
Code:
    - Config ( Core )
        - base.pwn ( Global Definitions, MySQL, Server Settings )
        - macros.pwn ( Global Macros for shortcuts like FUNCTION: and PUBLIC: )
        - plugins.pwn ( Global Plugin Definitions )
I'm doing like this:
"../core/configuration.inc" (GM's inititialization)
"../core/MySQLConnections.inc" (Creating table, connection to mysql, etc...)
"../core/base.inc" (Macros and constants)
That's good?

Annnd..
I'm trying a new thing. It's for creating logs (from configuration.inc):
PHP Code:
new 
    
Log_Level:Log_Server,
    ...;
enum Log_Level:LogType
{
    
NOTHING,
    
COMMANDS,
    
TCHAT,
    
FULL_LOG NOTHING COMMANDS TCHAT
};
stock CreateServerLog(Log_Level:Log_Serv Log_Level:FULL_LOG)
{
    
Log_Server Log_Serv;
    if(
_:Log_Serv == NOTHING)
        return print(
"[SERVER] Aucun log crйй."), 1;
    static
        
string[120],
        
hour,
        
minu,
        
sec,
        
day,
        
month,
        
year;
    
Get@SetWorldTime(hourminusec);
    
getdate(yearmonthday);
    
format(stringsizeof(string), "-------- Dйbut -------- [%02i/%02i/%02i] - %02ih %02imin %02is."daymonthyearhourminusec);
    
WriteLogs("AllCommands.txt"string);
    print(
"[SERVER] Logs configurйs.")
    return 
1;
}
stock Get@SetWorldTime(&hour = -1, &minutes = -1, &seconds = -1)
{
    
gettime(hourminusec);
    
SetWorldTime(hour);

Log_Server is gonna be used when writing logs.

EDIT: Write logs function
PHP Code:
WriteLogs(file[70], const reason[], playerid INVALID_PLAYER_ID)
{
    if(!
_:Log_Server)
        return 
1;
    static     
day,
            
month,
            
hour,
            
minute,
            
seconde,
            
year;
    static    
stringW[190];
    if(
strfind(file".txt"false0) == -1format(filesizeof(file), "%s.txt"file);
    new 
File:pos=fopen(fileio_append);
    if(!
pos)
    {
        
printf("[Server] Write Logs Error | Reason : pos=fopen Error | File : '%s' | Reason: '%s'"filereason);
        
SendMessageToAdmins(RED"[Server] "SAUMON_U" Une erreur est survenue dans les logs. Merci de vйrifier ceux-ci!");
        
SendMessageToAdmins(RED"[Server] "SAUMON_U"Fichier : '%s' | Contenus : '%s'"filereason);
        return 
1;
    } 
    
    
gettime(hourminuteseconde);
    
getdate(yearmonthday);
    if(!
strcmp(file"AllCommands.txt") && _:Log_Server == L_COMMANDS)
        
format(stringW,sizeof(stringW),"\r\n[%i] [%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"CmdID++, daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    else if(!
strcmp(file"AllTChat.txt") && _:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s (Player #%i) » '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"p_NameOff{playerid}, reason);
    else if(
_:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    
fwrite(pos,stringW);
    
fclose(pos);
    return 
1;

I would keep your structure either camel case, or lowercase as mixing the two is a bad idea ( in your case anyway ).

I prefer lowercase since it just easier to read ( for me anyway ).

As for what you said, If you are able to understand it easily then you can continue the way your doing it, but for the best practices you will need to pretend your not you and you've been given this script by someone else. Then ask yourself questions like does it make sense? is it easy to follow? is it easy to add onto or modifiy?

If I was you I would follow my way, since there literally nothing to be confused about, since every is labeled based on what the file will contain.

I also see, you do not comment your code, which is poor practice, you must always comment your code, even if your the only one modifying it.

If you are having issue finding functions, variables, enums or callbacks. Then your structure is wrong, it must follow a path that is easy to understand.

Also I would add a file in core called database.pwn ( containing the stock functions for connecting to the db ), keep the definitions in base.pwn. Make another file for macros.pwn since these can get messy to look at and also can be long as well as you having many of them. So rename MysqlConnections.pwn (it looks ugly) to database.pwn.
Reply
#26

Quote:
Originally Posted by Dayrion
View Post
I've 2 connection's type : when i'm on local (OnTestServer) and when I'm not. I'm using OnTestServer in differents commands like /ban, etc..
For this:
Code:
    - Config ( Core )
        - base.pwn ( Global Definitions, MySQL, Server Settings )
        - macros.pwn ( Global Macros for shortcuts like FUNCTION: and PUBLIC: )
        - plugins.pwn ( Global Plugin Definitions )
I'm doing like this:
"../core/configuration.inc" (GM's inititialization)
"../core/MySQLConnections.inc" (Creating table, connection to mysql, etc...)
"../core/base.inc" (Macros and constants)
That's good?

Annnd..
I'm trying a new thing. It's for creating logs (from configuration.inc):
PHP Code:
new 
    
Log_Level:Log_Server,
    ...;
enum Log_Level:LogType
{
    
NOTHING,
    
COMMANDS,
    
TCHAT,
    
FULL_LOG NOTHING COMMANDS TCHAT
};
stock CreateServerLog(Log_Level:Log_Serv Log_Level:FULL_LOG)
{
    
Log_Server Log_Serv;
    if(
_:Log_Serv == NOTHING)
        return print(
"[SERVER] Aucun log crйй."), 1;
    static
        
string[120],
        
hour,
        
minu,
        
sec,
        
day,
        
month,
        
year;
    
Get@SetWorldTime(hourminusec);
    
getdate(yearmonthday);
    
format(stringsizeof(string), "-------- Dйbut -------- [%02i/%02i/%02i] - %02ih %02imin %02is."daymonthyearhourminusec);
    
WriteLogs("AllCommands.txt"string);
    print(
"[SERVER] Logs configurйs.")
    return 
1;
}
stock Get@SetWorldTime(&hour = -1, &minutes = -1, &seconds = -1)
{
    
gettime(hourminusec);
    
SetWorldTime(hour);

Log_Server is gonna be used when writing logs.

EDIT: Write logs function
PHP Code:
WriteLogs(file[70], const reason[], playerid INVALID_PLAYER_ID)
{
    if(!
_:Log_Server)
        return 
1;
    static     
day,
            
month,
            
hour,
            
minute,
            
seconde,
            
year;
    static    
stringW[190];
    if(
strfind(file".txt"false0) == -1format(filesizeof(file), "%s.txt"file);
    new 
File:pos=fopen(fileio_append);
    if(!
pos)
    {
        
printf("[Server] Write Logs Error | Reason : pos=fopen Error | File : '%s' | Reason: '%s'"filereason);
        
SendMessageToAdmins(RED"[Server] "SAUMON_U" Une erreur est survenue dans les logs. Merci de vйrifier ceux-ci!");
        
SendMessageToAdmins(RED"[Server] "SAUMON_U"Fichier : '%s' | Contenus : '%s'"filereason);
        return 
1;
    } 
    
    
gettime(hourminuteseconde);
    
getdate(yearmonthday);
    if(!
strcmp(file"AllCommands.txt") && _:Log_Server == L_COMMANDS)
        
format(stringW,sizeof(stringW),"\r\n[%i] [%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"CmdID++, daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    else if(!
strcmp(file"AllTChat.txt") && _:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s (Player #%i) » '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"p_NameOff{playerid}, reason);
    else if(
_:Log_Server == L_TCHAT)
        
format(stringW,sizeof(stringW),"\r\n[%02i/%02i/%02i] %02i:%02i:%02i %s | '%s'"daymonthyearhourminutesecondeplayerid != INVALID_PLAYER_ID GetName(playeridtrue) : "ALL"reason);
    
fwrite(pos,stringW);
    
fclose(pos);
    return 
1;

Don't do logs in that way, just... don't...
Maddinat0r made an excepcional plugin for logging: https://sampforum.blast.hk/showthread.php?tid=603175
Reply
#27

Quote:
Originally Posted by renatog
View Post
Don't do logs in that way, just... don't...
Maddinat0r made an excepcional plugin for logging: https://sampforum.blast.hk/showthread.php?tid=603175
Eh, what is wrong with my way to write logs?

I've take azzerking's advices in considerations.
I've removed log_level everywhere.
PHP Code:
stock CreateServerLog(LogType:Log_Serv FULL_LOG)
{
    
Log_Server _:Log_Serv;
    if(
Log_Serv == NOTHING)
        return print(
"[SERVER] Aucune logs crййes."), 1;
    static
        
string[120],
        
hour,
        
minu,
        
sec,
        
day,
        
month,
        
year;
    
Get@SetWorldTime(hourminusec);
    
getdate(yearmonthday);
    
format(stringsizeof(string), "-------- Dйbut -------- [%02i/%02i/%02i] - %02ih %02imin %02is."daymonthyearhourminusec);
    
WriteLogs("AllCommands.txt"string);
    print(
"[SERVER] Logs configurйs.");
    return 
1;

Name looked horrible (like you said), so I changed most of them. Is better now?

And what I should comment ? When I include something?
Reply
#28

Quote:
Originally Posted by Dayrion
View Post
Eh, what is wrong with my way to write logs?
Because it's not friendly, you have to pass the file name to your function. You have to manually create, open, write and close the files. And maddinat0r's plugin is threaded. Don't get me wrong, it's just an advice, you'll see that your way is annoying.
Reply
#29

Quote:
Originally Posted by Dayrion
View Post
Name looked horrible (like you said), so I changed most of them. Is better now?

And what I should comment ? When I include something?
Using different folders to organize the files is good, but dont use the same filenames multiple times (e.g. Ji_core.inc), this will cause nothing but trouble.
Splitting up the stuff into one file for functions, one for commands, etc also isnt the best way to go. Better use files to separate semantic stuff: one file for stuff according to vehicles attachments, one files for vehicle ownership, one for database saving, and each with the variables, commands and functions that are related to it.
Why? There are dozens of reasons, but mainly its easier to work with files like that. Usually youll work on one certain "system" of the GM (e.g. house ownership) Its much, much easier if you got everything related to that in one file, instead of browsing through 3 or 4 different files at the same time.

Theres a bunch of threads here about how to organize a gamemode, Id suggest to take a look at them to get a better idea, no need to repeat everything here in my post.
Reply
#30

Quote:
Originally Posted by PawnHunter
View Post
Because PAWN is not object oriented, PAWN is simple, PAWN is outdated (the version SAMP uses is probably 10 years old), and more ..



I didn't "fail", I've been testing PAWN limits for months now, and I'm saying there is no way to process with a REAL modular design, yeah you could shit yourself and make some files called "callbacks, commands, header" separate then include to main file, but this isn't modular. I'm not just a beginner scripter, it's just the language that lacks the required features to make it modular. Adding some hooks and functions in different files, isn't what Modular design is. Anyways, that's my last reply here, I've said what I wanted to.
No, having hooks and functions in separate files is not modular design at all, if you have a file called "callbacks" anywhere in your hierarchy you are probably doing it wrong, no wonder you gave up - that is not "real" at all. However, if you do it properly it is very easy to get good modular design, even in PAWN (and I've been testing its limits for years now).

Hooks, functions, macros, variables etc. should be kept together in one file when they are for the same logical part of code (semantics - meaning, instead of syntax - appearance). If you have a variable storing the owner of a vehicle, and a function to sell a vehicle - put them in the same file, named something like "VehicleOwnership.pwn". If you have another variable storing the last person to kill someone, that has probably got nothing at all to do with vehicle ownership and should go in a totally separate file. Yes, they are both variables. Yes, they might look very similar, so you can do:

PHP Code:
new gVehicleOwner;
new 
gLastKillerID
But they are totally unrelated and things should not be split up by what looks nice (though once split correctly, appearance does help).

The best way I have found to keep things modular is static variables. Make all the globals in one file "static" and only provide access through a function API:

PHP Code:
static
    
gVehicleOwner[MAX_VEHICLES] = { INVALID_PLAYER_ID, ... };
bool:VO_SetOwner(vehicleidplayerid)
{
    
// Already owned.
    
if (gVehicleOwner[vehicleid] != INVALID_PLAYER_ID)
        return 
false;
    
gVehicleOwner[vehicleid] = playerid;
    return 
true;
}
VO_GetOwner(vehicleid)
{
    return 
gVehicleOwner[vehicleid];

NEVER let another file see the "gVehicleOwner" variable. This way you can keep all the restrictions on it in one place. If you decide to use a linked list of vehicles people own instead, you can just change this one file and not break any other code anywhere:

PHP Code:
static
    
Iterator:gOwnedVehicles<MAX_PLAYERSMAX_VEHICLES>;
bool:VO_SetOwner(vehicleidplayerid)
{
    
// Already owned.
    
if (Iter_Contains(gOwnedVehicles<>, vehicleid))
        return 
false;
    
Iter_Add(gOwnedVehicles<playerid>, vehicleid);
    return 
true;
}
VO_GetOwner(vehicleid)
{
    new
        
ret Iter_GetMulti(gOwnedVehicles<>, vehicleid);
    return (
ret == -1) ? INVALID_PLAYER_ID ret;

Totally different internal implementation, no different external API - still just "VO_SetOwner" and "VO_GetOwner"; and because the variables involved were all static you know that no other code in the mode can access them directly or will need updating in any way.

Some good examples of modular modes and includes:

YSI (obviously). Look through that and take a guess at what "y_inline.inc", "y_foreach.inc", and "y_commands.inc" do. They do often have a second file of (say) "y_commands/impl.inc", but it is always just "impl.inc" (short for "implementation") and never split up further in to variables and functions in separate files, because then you can't use statics, and then you can't KNOW that no-one else is directly accessing your private internal state. Even the tests don't (usually) have access to internal state, because they test the external API ("VO_SetOwner" and "VO_GetOwner" above), not the implementation (again, usually).

https://github.com/Zeex/amx_assembly Designed very purely, so actually has very little global state (variables) at all anywhere.

https://github.com/Southclaws/ScavengeSurvive Already mentioned, but worth repeating. Again, see how all the files are named for the gamemode system they implement, not what the code in them looks like. A file for macros, another file for functions, that's just totally useless - like classifying vehicles by their colour because a red van and a red car have a superficially similar appearance (as functions do to other functions), instead of by use.
Reply
#31

Quote:
Originally Posted by Y_Less
View Post
No, having hooks and functions in separate files is not modular design at all, if you have a file called "callbacks" anywhere in your hierarchy you are probably doing it wrong, no wonder you gave up - that is not "real" at all. However, if you do it properly it is very easy to get good modular design, even in PAWN (and I've been testing its limits for years now).

Hooks, functions, macros, variables etc. should be kept together in one file when they are for the same logical part of code (semantics - meaning, instead of syntax - appearance). If you have a variable storing the owner of a vehicle, and a function to sell a vehicle - put them in the same file, named something like "VehicleOwnership.pwn". If you have another variable storing the last person to kill someone, that has probably got nothing at all to do with vehicle ownership and should go in a totally separate file. Yes, they are both variables. Yes, they might look very similar, so you can do:

PHP Code:
new gVehicleOwner;
new 
gLastKillerID
But they are totally unrelated and things should not be split up by what looks nice (though once split correctly, appearance does help).

The best way I have found to keep things modular is static variables. Make all the globals in one file "static" and only provide access through a function API:

PHP Code:
static
    
gVehicleOwner[MAX_VEHICLES] = { INVALID_PLAYER_ID, ... };
bool:VO_SetOwner(vehicleidplayerid)
{
    
// Already owned.
    
if (gVehicleOwner[vehicleid] != INVALID_PLAYER_ID)
        return 
false;
    
gVehicleOwner[vehicleid] = playerid;
    return 
true;
}
VO_GetOwner(vehicleid)
{
    return 
gVehicleOwner[vehicleid];

NEVER let another file see the "gVehicleOwner" variable. This way you can keep all the restrictions on it in one place. If you decide to use a linked list of vehicles people own instead, you can just change this one file and not break any other code anywhere:

PHP Code:
static
    
Iterator:gOwnedVehicles<MAX_PLAYERSMAX_VEHICLES>;
bool:VO_SetOwner(vehicleidplayerid)
{
    
// Already owned.
    
if (Iter_Contains(gOwnedVehicles<>, vehicleid))
        return 
false;
    
Iter_Add(gOwnedVehicles<playerid>, vehicleid);
    return 
true;
}
VO_GetOwner(vehicleid)
{
    new
        
ret Iter_GetMulti(gOwnedVehicles<>, vehicleid);
    return (
ret == -1) ? INVALID_PLAYER_ID ret;

Totally different internal implementation, no different external API - still just "VO_SetOwner" and "VO_GetOwner"; and because the variables involved were all static you know that no other code in the mode can access them directly or will need updating in any way.

Some good examples of modular modes and includes:

YSI (obviously). Look through that and take a guess at what "y_inline.inc", "y_foreach.inc", and "y_commands.inc" do. They do often have a second file of (say) "y_commands/impl.inc", but it is always just "impl.inc" (short for "implementation") and never split up further in to variables and functions in separate files, because then you can't use statics, and then you can't KNOW that no-one else is directly accessing your private internal state. Even the tests don't (usually) have access to internal state, because they test the external API ("VO_SetOwner" and "VO_GetOwner" above), not the implementation (again, usually).

https://github.com/Zeex/amx_assembly Designed very purely, so actually has very little global state (variables) at all anywhere.

https://github.com/Southclaws/ScavengeSurvive Already mentioned, but worth repeating. Again, see how all the files are named for the gamemode system they implement, not what the code in them looks like. A file for macros, another file for functions, that's just totally useless - like classifying vehicles by their colour because a red van and a red car have a superficially similar appearance (as functions do to other functions), instead of by use.
OH SHIT Y_LESS HI <3
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)