PDA

View Full Version : Gamemode Script Layout


MP2
04/07/2012, 08:36 PM
Hei.

I've recently started development of a new gamemode/server and while originally the layout was okay, as it's building up more and more code it seems to be working less.

I have this issue where when I want to script something new, for example let's say I want to script some random weapon pickups - I need three things. The array of data, the creation and the OnPlayerPickUpPickup. First thing I do is the array - but I start to go 'WHERE DO I PUT THE ARRAY DO I PUT IT ABOVE ALL THE OTHERS OR AT THE BOTTOM OR A NEW SECTION FOR WEAPON PICKUPS OR WHAT' and yeah.

This is the current roughly layout of my gamemode (FROM MEMORY):

Includes

Defines

Enums/Arrays

Variables

Callbacks

Custom Functions

mySQL query callbacks

Timers

and I guess that's okay, but I seem to be losing track of where everything is.

Also I split my callbacks up in to sections, so like in OnGameModeInit I would do something like

Initial setup (DisableEnterExits and that sort of thing)
mySQL connection
Objects
Pickups
etc.

So yeah, let's discuss layout options. What do you do? What could I do to improve?
I know some people put different systems in different files and #include them, but I think that'd be a bit of a ballache. Instead of saying 'oh I need to add a new weapon to my weapon pickups, I'll just Ctrl+F and find the array and add it in' you'd have to find out what file it's in, find that file in your /include folder then Ctrl+F through that.

I dunno, it would make it more organized I guess. I'm also developing this server with another person (blewert \o/) and we need a way to contribute to the script at the same time. I guess having systems in separate files then including them would make our lives easier.

KyleSmith
04/07/2012, 08:38 PM
For me, tis:

Includes
Defines
Arrays / Enums
Variables

Then after that anything really, if I had a group chat system I place the stocks near the commands where it's required etc.

Also pop on the IRC and query me please.

Vince
04/07/2012, 09:05 PM
I have kind of the same problem where all variable declarations are scrambled. I try to use static locals(*) where possible, but that doesn't always cut it. I also do include lots of stuff, and in fact I created a whole folder structure for it. I also always seem to code with the thought in mind that I might actually release the script one day and that it thus must be somewhat noob friendly.

At the top of my GM I define general settings like #undef MAX_PLAYERS, #define LOCALHOST and stuff like that, followed by my list of files;


// ================================================== ===========================
// Variable Declarations

#include "../src/def/colors.txt" // Color definitions
#include "../src/def/sound_otb.txt" // Various sound ids
#include "../src/def/forward.txt" // Forward declarations for public functions
#include "../src/def/trucking.txt" // Coordinates for trucking pickup and dropoff
#include "../src/def/bcases.txt" // Coordinates for briefcase locations
#include "../src/def/unsafepass.txt" // List of unsafe passwords (used during registration)
#include "../src/def/var/fishlist.txt" // List of fish and rarity

#include "../src/def/enum/player/flags.txt" // Player boolean values and stock functions
#include "../src/def/enum/player/timers.txt" // Variable to store player timers

#include "../src/def/enum/global/textdraw.txt" // Various general textdraws
#include "../src/def/enum/global/checkpoints.txt" // Checkpoint definitions
#include "../src/def/enum/global/dialogs.txt" // Dialog definitions
#include "../src/def/enum/global/skills.txt" // Skill definitions

#include "../src/func/pinfo.txt" // PlayerInfo enum
#include "../src/func/pstats.txt" // PlayerStats enum
#include "../src/func/hinfo.txt" // HouseInfo enum
#include "../src/func/insurances.txt" // Insurances enum and stock functions

// ================================================== ===========================


which in turn is followed by random variables.

I'll probably clean it up one day. Not now.


(*) Example of static locals:

public OnPlayerUpdate(playerid)
{
if(IsPlayerNPC(playerid)) return 1;

// ----- OnPlayerHealthChange -----
static
Float:oldHealth[MAX_PLAYERS];

GetPlayerHealth(playerid, PlayerStats[playerid][pHealth]);

if(PlayerStats[playerid][pHealth] != oldHealth[playerid])
{
OnPlayerHealthChange(playerid, PlayerStats[playerid][pHealth], oldHealth[playerid]);
oldHealth[playerid] = PlayerStats[playerid][pHealth];
return 1;
}
// -----
return 1;
}

Works absolutely fine.

ReneG
04/07/2012, 10:30 PM
Call me an idiot, but I go for the same layout as the Godfather.


Includes
Defines
Public function forwards
Enums/Arrays
Variables
All default SA-MP callbacks
All custom functions.
Timers
Commands

All SQL code goes into an include file. Weird, but I edit the SQL include with Notepad++, CTRL+S, and compile. Different than using Pawno, and having it all cluttered in your mode.

TheChaoz
05/07/2012, 02:23 AM
Normaly for small scritps I tend to do something like this:
Includes
Macros
Vars/Arrays
Callbacks
(Commands if using ZCMD)
Functions

But on my GM I use includes for each system, so I do it this way:
Includes (#Incldue ""../Data/Systems/System_Name/Header"
Callbacks


Each system include header looks like this:
#include "../Data/Systems/System_Name/Arrays"//includes macros, enums, arrays, variables
#include "../Data/Systems/System_Name/Functions"
#include "../Data/Systems/System_Name/Callbacks"
#include "../Data/Systems/System_Name/Commands"
#include "../Data/Systems/System_Name/Dialogs"

It's quite simple to find bugs or change things from a system.

Y_Less
05/07/2012, 02:41 AM
OK, I'm intrigued, those of you who arrange your code by declaration type instead of function, why do you think this is a good way of doing it? And this is an honest question not sarcasm! The fact that a vehicle enum looks a little bit like a player enum doesn't mean that they're related in any way, so why put them together? That seems to me like organising a library by the colour of the book covers instead of the book topics!

As for having separate directories for systems and separate files for the different declarations in those systems - that's getting closer to a good layout but still means you can't use "static" to restrict access to data. Think of "static" as data private to a system - you want all of the system to be able to see that data, but you don't want any other system to see that data because they could start adjusting it in bad way. Instead you just provide a clean API to interact with that data any everyone is happy.

I'm not sure how this is even someone any programmer should wonder:

Number of major open source projects who "organise" their code in one massive file sorted by keyword: 0
Number of major open source projects who organise their code by splitting code up in to separate chunks of related functionality: All of them!

That alone should tell you something. I really suggest you start reading outside of SA:MP - you might learn something! There is a free book out called "The architecture of open source projects volume 2" - look it up. Also, just any generic reading on any generic programming will VERY quickly get on to how to split up code by function.

Finally, why do you think there are separate includes for player functions and object functions? Do you think that maybe says anything about the internal structure of the server's code? And I'm not giving ANYTHING away by saying that because any programmer with more than 10 minutes experience would realise that a large program like the server would split those functions up in that way.

MP2
05/07/2012, 03:13 AM
The general reason I and others put arrays and enums etc. above callbacks is because (obviously) the callbacks can't use them if they're declared after them. Most of them get used in OnGameModeInit so it wouldn't make sense to have them at the bottom of the script.

For example my weapon pickup array - it needs to be above OnGameModeInit because they get created there.

I do agree splitting up systems is important - I'm just not totally sure how to do it. I'm sort of put off by the fact of having a million files when I could just have one.

Also, say you scripted something 2 years ago and it's in some file in some directory in some other directory - if the compiler gives an error it's going to say the error is in your GM, not which file. How are you meant to remember what file the script is in?

I should also point out I'm co-developing this with another person - which makes it even harder to organize stuff.

I am definately going to split many systems and include them, for example the housing system, ammunation, bank robberies and stuff like that.

[ABK]Antonio
05/07/2012, 03:51 AM
Meh, it doesn't really matter what anyone's opinion on this is aside from your own. Though I'm still going to throw mine out there :P

I organize like Y_Less said no one does. The reason for this...I find it much easier. If I forget the name of the function I just made...variable or anything else - I won't have to search everywhere for it (with "splitting code up in to separate chunks of related functionality" you can lose things very easily. even if they're related).

I do go like most of you...sort of


Includes
Macros
Enums
Arrays
Vars
Main
ongamemodeinit
onplayerconnect
..
onplayerdisconnect
ongamemodeexit
Commands
Custom functions (not publics (those are in a separate place))
Custom functions (publics (not just timers, things for cross server stuff go here too))
Save File stuff (I always put it at the bottom (aside from forwards), just for easier access)
Forwards

I also comment above the beginning of them - keywords. Though I'm not an open source kind of guy.

TheChaoz
05/07/2012, 04:36 AM
OK, I'm intrigued, those of you who arrange your code by declaration type instead of function, why do you think this is a good way of doing it? And this is an honest question not sarcasm! The fact that a vehicle enum looks a little bit like a player enum doesn't mean that they're related in any way, so why put them together? That seems to me like organising a library by the colour of the book covers instead of the book topics!

As for having separate directories for systems and separate files for the different declarations in those systems - that's getting closer to a good layout but still means you can't use "static" to restrict access to data. Think of "static" as data private to a system - you want all of the system to be able to see that data, but you don't want any other system to see that data because they could start adjusting it in bad way. Instead you just provide a clean API to interact with that data any everyone is happy.

I'm not sure how this is even someone any programmer should wonder:

Number of major open source projects who "organise" their code in one massive file sorted by keyword: 0
Number of major open source projects who organise their code by splitting code up in to separate chunks of related functionality: All of them!

That alone should tell you something. I really suggest you start reading outside of SA:MP - you might learn something! There is a free book out called "The architecture of open source projects volume 2" - look it up. Also, just any generic reading on any generic programming will VERY quickly get on to how to split up code by function.

Finally, why do you think there are separate includes for player functions and object functions? Do you think that maybe says anything about the internal structure of the server's code? And I'm not giving ANYTHING away by saying that because any programmer with more than 10 minutes experience would realise that a large program like the server would split those functions up in that way.

Well that's a nice question you got there, but I can only answer for myself and not for others, so here I go.

In small scripts I tend to organize by "types" and not by "topic" as it's much easyer for me to find things, Ex: I got a 2000k lines file, and I need to remember how I did name an enum object, as I have all of them togeter it's a way faster. May you're right, enums may be for players/vehicles/special types but it's like when you (or at least me) organize your books at university/school, I organize them for years, not by each subject (may when I finish it i'll organize them by subject, but not for now).

And about the systems, I do need them go be conected with each other, not all of them but most of them, that's why I do it this way.

Vince
05/07/2012, 07:53 AM
Also, say you scripted something 2 years ago and it's in some file in some directory in some other directory - if the compiler gives an error it's going to say the error is in your GM, not which file. How are you meant to remember what file the script is in?

The compiler actually does tell you what file the error is in.

MadeMan
05/07/2012, 04:58 PM
The layout I'm trying to keep in my scripts:

Includes
Defines
Macros
Variables/Enums
Stock functions
Regular functions
Public functions
Callbacks

Reason: before you can use a variable/function, you need to define it. I find it logical that definitions come before parts that use them. Callbacks use functions, functions use variables and variable definitions require defines (#define).

And it makes searching for definitions easier: go to top of the script (Ctrl + Home), use search (Ctrl + F) and the first result is the definition.

AndreT
07/07/2012, 03:25 PM
What a nice topic to discuss on!

Calling my server a large project would be an exaggeration, but I do follow the same fashion of having separate files. To be more precise, 15 of them. I started doing this in 2010 and I also converted an old mode of mine to use several files.

To whoever this might look difficult to handle... stop using Notepad to script! (or Pawno, quite the equivalent). Use Notepad++ instead, it offers a very convenient way of handling files. And I believe that at least a few of the many plugins assist programmers in creating directory structures even more. Following Slice's tutorial, Notepad++ can also instruct the compiler to compile your code.

The benefits from doing this are immense. A good naming convention for your gamemode files will instantly make you aware of what is where and even with quite a lot of files containing PAWN code, I can still remember the location of certain functions quite well, or at least I have an idea of whether they're positioned in the lower/upper part of the SFSE_*.pwn file. Libraries and methods introduced by Zeex (zcmd) and whoever thought of handling dialogs in a similar way have made things even more convenient, as commands, MySQL result callbacks and much more can be shared between files, so the gamemode is categorized nicely. Writing new code for your mode is easy as well, as the more code unrelated code is in your currently operated file, the more distracted you will most likely get.

And for the record, there's not a problem with getting the location of an error unlike someone pointed out.

So multiple files... good idea!

Finn
07/07/2012, 04:46 PM
I must disagree with you guys about spreading the gamemode into separate files.

I am using 20 includes + couple of filterscripts, so splitting up the code even more would make the gamemode look like it was blown up. I don't have any opinion regarding color defines and shit like that placed in their own files, because I don't really have too many of them laying around.

I like the idea of having "visual code" (aka the gamemode) in 1 single file and the systems which 'run' the gamemode and offer handy functions in their own include files. Also I'm grouping up the code so the related functions, timers, commands and such are in the same section of the gamemode making it very easy to edit the system which is under development or being fixed.

Lorenc_
07/07/2012, 05:49 PM
I just add my core gamemode functions inside its own include.

includes
defines
enums
variables
forwards
main(){}
publics
commands (replaced OnPlayerCommandText with OnPlayerCommandPerformed, so...)
other publics
functions
sql dump at the bottom

ReneG
07/07/2012, 06:01 PM
What a nice topic to discuss on!

Calling my server a large project would be an exaggeration, but I do follow the same fashion of having separate files. To be more precise, 15 of them. I started doing this in 2010 and I also converted an old mode of mine to use several files.

To whoever this might look difficult to handle... stop using Notepad to script! (or Pawno, quite the equivalent). Use Notepad++ instead, it offers a very convenient way of handling files. And I believe that at least a few of the many plugins assist programmers in creating directory structures even more. Following Slice's tutorial, Notepad++ can also instruct the compiler to compile your code.

The benefits from doing this are immense. A good naming convention for your gamemode files will instantly make you aware of what is where and even with quite a lot of files containing PAWN code, I can still remember the location of certain functions quite well, or at least I have an idea of whether they're positioned in the lower/upper part of the SFSE_*.pwn file. Libraries and methods introduced by Zeex (zcmd) and whoever thought of handling dialogs in a similar way have made things even more convenient, as commands, MySQL result callbacks and much more can be shared between files, so the gamemode is categorized nicely. Writing new code for your mode is easy as well, as the more code unrelated code is in your currently operated file, the more distracted you will most likely get.

And for the record, there's not a problem with getting the location of an error unlike someone pointed out.

So multiple files... good idea!
It gets glitchy when you have multiple files open I guess

I was working in an include using notepad++ and had a couple of errors when I compiled. I fixed them and hit ctrl+s to save. Then when I went to Pawno to compile, and it wouldn't recognize that I've updated the include.

Y_Less
07/07/2012, 11:19 PM
It gets glitchy when you have multiple files open I guess

I was working in an include using notepad++ and had a couple of errors when I compiled. I fixed them and hit ctrl+s to save. Then when I went to Pawno to compile, and it wouldn't recognize that I've updated the include.

That has never happened to me ever, in MANY years of coding!

DeathTone
07/07/2012, 11:22 PM
Includes
Defines
Variables
Enums
Callbacks
Public timers
Custom Callbacks
ZCMD Commands
Mysql functions
Stock functions

but it gets EXTREAMLY messy sometimes, too much clutter, so I think i'll just add all my important defines and stocks, central stuff (such as small but important player variables), into one include to make it much easier to read and edit stuff.

MP2
08/07/2012, 01:37 AM
Well I've decided to do a bit of both. I'm still going to have a lot of stuff in the gamemode but some 'systems' will be in files such as the ammunation script. I don't like the idea of having a million files. To me it's like taking 5 shopping lists in to a supermarket - each one has a different 'category' of food or something. It's simpler to just have 1.

JoBullet
08/07/2012, 02:42 AM
Look up PAWN-Boilerplate project by Slice for solid script layout.

MP2
08/07/2012, 02:53 AM
Yeah I know about that.

Simon
08/07/2012, 06:23 AM
I tend to layout gamemode projects by seperating the files into folders. The folders symbolize a library or a 'class' of related methods. The rough idea is as follows:


<mode>.pwn (Link all the modules together, delegate responsibilities to <module>.pwn)
<mode>.h (Define your global constants, enumerations and forward functions. Variables)
version.in (Optional. SVN repository input file, allows for revision numbers to be generated to version.h which <mode>.h can include)
compile.bat (Optional. Runs some extra compiling procedures such as getting SVN revision number and authors)
modules\

modules.def (Optional. Define your scoping definitions here if you like using them, Module1::, Module2:: etc)
<module>\

<module>.h (Define your module constants, enumerations and forward functions. Variables)
<module>.pwn (Your module core logic, written as if it were a library for use by your handler. Variables)
events.pwn (Custom events/callbacks defined by your module. Code is very specific to the gamemode (messages etc.), generally not able to be re-used.)
handlers.pwn (Handle SA:MP and other 'external' callbacks using your module)


scriptfiles\ (List of scriptfiles ready to be dumped on the server)

something.cfg
users.db



The .h files are included before all .pwn files in <mode>.h, <mode>.pwn (your compile file) includes <mode>.h. This resolves any undefined variable issues. You should be very careful with manipulating variables directly and try abstract the problem to a method. Methods do not need to be pre-declared unless they have tags.

Where variables are declared varies on your laziness. Ideally I believe the variables should be static and declared in their <module>.pwn file (to allow more general variable names without conflicts, and to discourage manipulating a variable outside of it's "module logical domain"). Use of static can cause undeclared variable compile errors due to file-level symbol hiding. This can be resolved by using getter/setter type methods (which are better theoretically)

A core module can be created for stuff that is unique to the gamemode or cannot be classified into a general purpose library.

iggy1
08/07/2012, 09:35 AM
I'm writing a mini-games mode this is the approach i have taken.


Defines.
Samp and third party includes (max players in between, redefined directly below a_samp).
Enums
Global variables.
Gamemode custom includes (so they can use global variables very easily)
Main gamemode code


Even some of my callbacks are in separate files and i have not bothered hooking them. Callbacks like OnQueryFinish and OnDialogResponse that take up a lot of room i tend to put in their own file.

Until about a year ago i just used 3rd party includes and had all of my code in one file. I wrote a fairly large gamemode. When i had a break from scripting (about 3 months only) i came back to do some updates and found the code was just too unmanageable. It looked like obfuscated code, even though i never intended it to be, it was ok for me to update before i had the break. This is the reason i started keeping things in different files. So far so good. If i want to add something to a game, i just open that games file and add it. Without having to endlessly search a large gamemode. Which still takes a long time using Ctrl+F or Ctrl+G. Time which could be better spent adding new features.

eesh
09/07/2012, 05:47 AM
i do it like this

1.defines
2.forwards
2.includes
3.callbacks

thats all

i have an include called vars.inc where i put all the variable declarations, functions.inc where i put all the stocks, timers where i put all the timer publics, commands.inc where i put all my commands :D so if i have to show the login screen on player connect i just do ShowLogin which is a stock found in my functions.inc and LoginPlayer() under OnDialogResponse to login the player (also found in the functions include).

Apparently my includes are bigger than my pwn xD

I try putting most of the long code away from my pwn like i have a stock called ServerInit which loads all the OnGameModeInit() stuff too. feel its more organized

DiDok
10/07/2012, 11:01 AM
I use one .pwn with includes, simple definitions and callbacks only, rest, including all globals, is in 17 includes grouped by their job (one for timers, one for internal database arrays, one for admin script, one for commands, and every major feature like housing, police, trucking, companies, objects in their own includes)

Seriously, stop putting everything into one big file, this is one of the "C programmer's rules" [aka noob's]:
[translation, old post from newsgroups]
1. Use a lot of global variables
2. Give them mysterious names, like X27, a_gcl or Horacy
3. Write everything in one big .h file
4. Implement whole program at once
5. Use macrodefinitions to emulate Pascal
6. Imply that compiler will take care of all the details you don't exactly understand
It makes the file totally cluttered, unreadable and hard to maintain, I did the same mistake before and after rescripting I no longer have troubles with finding anything important

Also take care about forwards, do you really need all your functions to be public? [needed for timers and remote calling only], then put forwards BEFORE the function, not at top of script because it has no use there and you don't need to jump through file to delete function + forward

Also, stop using some weirdass .map readers unless you dynamically load maps, convert and put your maps directly into script (new include of course), if you want to laugh at that then fuck you and remember what happened to New Dawn and all their fabulous maps

Universal
15/07/2012, 03:14 PM
- Includes
- Defines
- Enums
- Variables
- Objects & RemoveBuildingForPlayer
- SA-MP callbacks
- Custom functions
- ZCMD commands

Nicholas.
08/08/2012, 10:39 PM
A friend and I are working on a RP gamemode. How should we properly layout our gamemode that it wouldn't get messy? Should we use separate includes for stocks, defines, variables. etc?

Patrik356b
09/08/2012, 06:36 AM
I like to sort things a bit randomly, separated by categories, like:
General includes
General data
SQL Stuff
GameModeInit/Exit
Commands
Dialogs
Others

At each category I declare only what's relevant.

I have 2 includes for common functions.

[HLF]Southclaw
10/08/2012, 09:28 PM
My gamemode increased massively recently, I added minigames and things.
I'd been playing about with Java and liked the whole 'class' thing (Later discovered to be OOP)

I've had separate files for a while now, I started to fix up my variable names when I had free time watching TV etc, loosely following Hungarian Notation, adding a "context prefix" followed by the standard variable name in H.N.:

races.pwn:
rc_giVehicleID
rc_giPlayerCount
rc_gtick_Start
rc_gflag_State
deathmatch.pwn:
dm_bSettings
dm_iCountdownValue
dm_Map
dm_Mode
I added a few prefixes like:
'tick' a tick counter, always used with GetTickCount for stopwatches etc
'flag' a flag with multiple values, such as the state the race can be in (Counting in, started, stopped, inactive, waiting for players etc)

'b' bit array, I used to use it for booleans but I don't use them anymore, it's all in bit arrays.
'g' global, as standard
'p' player related
'v' vehicle related
'o' object related

's' string (I ignore that 'sz' zero terminated thing, that doesn't apply in Pawn right?)
'i' integer
'f' float
'a' array followed by one of the above 3

As for structure, all things are declared/defined in their respective files unless used in multiple files, in which case they are declared in a 'public' area in the main script.

The (bad) structure:


Comments and credits in case I ever release my script (technically it is, I just never update the repo)
Main inckudes from "pawno/include"
Definitions/macros and shit, really unorganised section...
server related things like notification messages, admin-rank-names, bit settings, weather enum/variable, all that jazz...
'public' declarations, for stuff used in multiple scripts (even though it aint all here)
player related things like main data enumerator, bit array
vehicle things (not many of them)
the other includes from "../scripts" (included after the declarations and not with the other includes because of global stuff)
main()
massive clusterfuck of callbacks in no particular order (connect, class things, login, disconnect, logout, player input)


I can't believe I spend this long, I must have a lot of free time...

Anyway, it's certainly not a perfect system and needs a lot of refining! I have my old self to blame, stupid variable names etc but we live and learn! :p

Dan..
11/08/2012, 06:28 PM
Hello!

I've been scripting a game mode in Pawno for about 2 weeks, but most of the time I spent on the design of the server.

My first try was to write a very-very-long script in only one file. I reached almost 2000 lines, includes, variables, methods, commands all mixed in there so I tried something else. It was to messy for my taste.

At my second try I tried writing a "merger" that would automatically merge multiple files, declare function prototypes and arrange them in a such order so the output could be compiled by pawncc. I found that way cleaner as I could arrange my functions and engines in separate folders and files. However, that isn't pleasent enough as there are still some stuff mixed.

The third way, I was planning to use hooks and different files (something like some mini-gamemodes) that would merge togheter and make the final gamemode. However, this way seemed a bit unstable and not very efficient.

Currently, I'm looking for a fourth way to make it even more readable: each "engine" in a filterscript, but I still have to work a way to make variables global (so they can be accessed by all filterscripts). There would be lots of advantages, one of the most important being that you can reload parts of your server without any troubl and lots more.

Do you have any ideas how can I achieve that? (I guess I'll have to work on a plugin or use AMX properties - a way I don't really like.)

How do you manage your server?

I'm looking forward to hear your great ides.

Bakr
12/08/2012, 05:02 AM
Currently, I'm looking for a fourth way to make it even more readable: each "engine" in a filterscript, but I still have to work a way to make variables global (so they can be accessed by all filterscripts). There would be lots of advantages, one of the most important being that you can reload parts of your server without any troubl and lots more.

Do you have any ideas how can I achieve that? (I guess I'll have to work on a plugin or use AMX properties - a way I don't really like.)

Using filterscripts to reload certain parts of your script is a very nice idea. As for your question, you can use the PVar functions (example: http://wiki.sa-mp.com/wiki/SetPVarInt) and property functions (example: http://wiki.sa-mp.com/wiki/setproperty) to communicate between filterscripts and the main gamemode script.

After reading through the posts in this thread, I really like Simon's approach. Separating core concepts of the mode into different files is a nice idea while having a main core to tie everything together. I will definitely be using this approach in my next project.

Dan..
12/08/2012, 07:41 AM
Using filterscripts to reload certain parts of your script is a very nice idea. As for your question, you can use the PVar functions (example: http://wiki.sa-mp.com/wiki/SetPVarInt) and property functions (example: http://wiki.sa-mp.com/wiki/setproperty) to communicate between filterscripts and the main gamemode script.

After reading through the posts in this thread, I really like Simon's approach. Separating core concepts of the mode into different files is a nice idea while having a main core to tie everything together. I will definitely be using this approach in my next project.

Simon's approach is the equivalent of a mixture of my second and third approach and it isn't such a good one as it looks like.

Each "package" is in fact a "mini-gamemode". Every callback defined is actually a hook and the number of these hooks would be big. Let's say we have 20 jobs with almost 3 events each (there will be more for sure). You'll have to use 60 hooks, which in my opinion isn't that good. Also, the variables would still be mixed.

In my opinion, the filterscripts-approach is the best, but we need to find a way to declare "global" variables and functions. I don't think is a good idea using AMX properties. They aren't fast enough.

L.E. This is my layout at this moment: https://dl.dropbox.com/u/8087080/ShareX/2012-08/2012-08-12_11-20-20.png

Y_Less
12/08/2012, 08:58 AM
Dan: look up y_master, it does multi-script functions very smoothly. Not variables though, but frankly globals are bad, you should just be using functions to access different script modules, variables hold internal data which can change without warning.

What you describe with different engines in different scrips is exactly what y_master was designed for. However, I'd like to know why you think hooks are bad - they are actually now very fast and easy to use.

Dan..
12/08/2012, 06:03 PM
Dan: look up y_master, it does multi-script functions very smoothly. Not variables though, but frankly globals are bad, you should just be using functions to access different script modules, variables hold internal data which can change without warning.

What you describe with different engines in different scrips is exactly what y_master was designed for. However, I'd like to know why you think hooks are bad - they are actually now very fast and easy to use.

I've checked up y_hooks. I thought they were slow just because of the complexity, but it seems you have optimised those hooks a lot. They are very fast and I think I'm going to work with them in my future projects. About y_master, well.. as far as I've got the hooks-method I won't need it. Also, I already said that global variables would be a pain in the ass and also would be slow, unless you come with a nice idea.

Y_Less
12/08/2012, 06:59 PM
I do have an idea for true globals as it is possible to read arbitrary memory addresses. IF you can find the address of some variable in another script then you can use reduced code to read it (but still more than just reading one script local global). That I think will be the fastest way, but I'm still working on how to make it happen.

Dan..
12/08/2012, 08:25 PM
I do have an idea for true globals as it is possible to read arbitrary memory addresses. IF you can find the address of some variable in another script then you can use reduced code to read it (but still more than just reading one script local global). That I think will be the fastest way, but I'm still working on how to make it happen.

That's what I was thinking too, but you'll still have to find those addresses of variables in each script loaded.

Y_Less
12/08/2012, 09:59 PM
Yes, but that's actually the easy part!

Dan..
13/08/2012, 06:18 AM
Yes, but that's actually the easy part!

If I'm not mistaken y_amx does that for you, right?

Y_Less
13/08/2012, 07:27 AM
Yeah, or there is functionality that you can use in there.

im
13/08/2012, 09:45 AM
I use one big .pwn file with not-so-organized code and 20k+ lines of code.. I have to CTRL+F when I'm looking for something anyway, so it doesn't really matter if I have the functions organized by type or just put in a random order..

I tried using multiple files for my gamemode, but that didn't work for me. It's kinda hard to use multiple files and there are not enough examples on the forum on how to do that. All the gamemodes in the gamemodes section use just one big .pwn file.

Westingham
13/08/2012, 12:54 PM
If I were trying to streamline my gamemode, would it better to put some common code that I'm going to use over and over into an include or into a filterscript?

Related: what is the difference between an include and a filterscript and are there advantages to using one over the other?

Y_Less
13/08/2012, 12:58 PM
I use one big .pwn file with not-so-organized code and 20k+ lines of code.. I have to CTRL+F when I'm looking for something anyway, so it doesn't really matter if I have the functions organized by type or just put in a random order..

Have you considered that if it was ordered you wouldn't need to search as much as all the functions related to the one you were currently working on would be within scroll distance?

[HLF]Southclaw
13/08/2012, 01:48 PM
It's kinda hard to use multiple files and there are not enough examples on the forum on how to do that. All the gamemodes in the gamemodes section use just one big .pwn file.

It's exactly the same as how you use #include <scriptname> that basically just takes all the code from that file, and inserts it at the point you write the include line.

using <scriptname> will automatically read from the "pawno/includes" folder, but you can use "scriptname" to read straight from the same folder as the gamemode script (I think, correct me of I'm wrong!)

You can also use "../" to go 'up' a folder, so if I wrote this:

#include "../scripts/MyScript.pwn"

it would go up from the folder the gamemodes is in (In my case, my main SA:MP server folder) then go into a folder in there called 'scripts' and open "MyScript.pwn".



There are some more interesting things about #include I think, I can't remember though!




If I were trying to streamline my gamemode, would it better to put some common code that I'm going to use over and over into an include or into a filterscript?

Related: what is the difference between an include and a filterscript and are there advantages to using one over the other?


Filterscripts are loaded separately, includes are basically just bits of the main gamemode in separate files.

For instance, I could have an OnPlayerUpdate in a filterscript and one in a gamemode, they would be called separately, you wouldn't have any problem.

But if I had OnPlayerUpdate| in a gamemode/filterscript and in a file I was including into that gamemode/filterscript, I would get a "Symbol already defined error" because it's the same as defining OnPlayerUpdate twice in one script.

Gryphus One
13/08/2012, 02:33 PM
Dan: look up y_master, it does multi-script functions very smoothly. Not variables though, but frankly globals are bad, you should just be using functions to access different script modules, variables hold internal data which can change without warning.

What you describe with different engines in different scrips is exactly what y_master was designed for. However, I'd like to know why you think hooks are bad - they are actually now very fast and easy to use.

Maybe it's because I'm a scripting beginner, but I don't understand the part that I highlighted in bold: how can variables and arrays change themselves?
This worries me because I'm trying to create my first gamemode by leaving the .pwn file almost empty and writing everything in different includes with callback hooks, and as some of the custom functions that I have created and that are in different includes are called in the same callbacks and would use the same native functions (for example, I have several custom functions that are called in OnPlayerUpdate and would use GetPlayerPos), what I'm doing is first creating an "information" include with global arrays (for example, globalx[MAX_PLAYERS], globaly[MAX_PLAYERS] and globalz[MAX_PLAYERS]), so I use GetPlayerPos in OnPlayerUpdate only in that include to write in those arrays, and in the rest of my functions that are in my other includes I just read those arrays to avoid redundant calls to GetPlayerPos over and over again. And same with several other functions for other kinds of data, like facing angle, velocity, armed weapon, player state, player name, health and armour, vehicle health, etc.
Is this bad?

Y_Less
13/08/2012, 02:47 PM
OK, there are multiple questions here:

Maybe it's because I'm a scripting beginner, but I don't understand the part that I highlighted in bold: how can variables and arrays change themselves?

My post was poorly worded sorry. I meant that if you have an include with a variable called "gSomeData" and it stores some data then you can use it in your script. However, if the person who made the include decides to change how the include works, they may get rid of the "gSomeData" variable and replace it with another one - breaking your code as well. However, if they have written a function like "GetSomeData", then they may change how the function works, but the function itself should remain the same so you can still call it. This is even the case with your own code - you may decide to rewrite one file and as a result have to update every other file when you change the internal structure. If, however, you have a fixed API to access that file (i.e. a fixed list of functions) then it doesn't matter how the implementation changes, it won't change the rest of your code.

This worries me because I'm trying to create my first gamemode by leaving the .pwn file almost empty and writing everything in different includes with callback hooks, and as some of the custom functions that I have created and that are in different includes are called in the same callbacks and would use the same native functions (for example, I have several custom functions that are called in OnPlayerUpdate and would use GetPlayerPos), what I'm doing is first creating an "information" include with global arrays (for example, globalx[MAX_PLAYERS], globaly[MAX_PLAYERS] and globalz[MAX_PLAYERS]), so I use GetPlayerPos in OnPlayerUpdate only in that include to write in those arrays, and in the rest of my functions that are in my other includes I just read those arrays to avoid redundant calls to GetPlayerPos over and over again. And same with several other functions for other kinds of data, like facing angle, velocity, armed weapon, player state, player name, health and armour, vehicle health, etc.
Is this bad?

Yes, that's IMHO a terrible way of doing things, but not because the variables might change. For one, you don't need to store every player's positions all the time, secondly you could just use this and avoid globals:


public OnPlayerUpdate(playerid)
{
new Float:x, Float:y, Float:z;
GetPlayerPos(playerid, x, y, z);
OnPlayerUpdate_1(playerid, x, y, z);
OnPlayerUpdate_2(playerid, x, y, z);
OnPlayerUpdate_3(playerid, x, y, z);
}


Or you could just accept that calling a single native two or three times isn't actually that bad!

Gryphus One
13/08/2012, 03:05 PM
public OnPlayerUpdate(playerid)
{
new Float:x, Float:y, Float:z;
GetPlayerPos(playerid, x, y, z);
OnPlayerUpdate_1(playerid, x, y, z);
OnPlayerUpdate_2(playerid, x, y, z);
OnPlayerUpdate_3(playerid, x, y, z);
}


And using this, would I have to create as many custom OnPlayerUpdate_ callbacks as custom functions I have that are called there?

Y_Less
13/08/2012, 03:06 PM
Well you said you were already hooking them so I'm assuming you already have multiple custom OnPlayerUpdate functions.

Gryphus One
13/08/2012, 03:45 PM
Hmm I'm getting lost. Those OnPlayerUpdate_1(playerid, x, y, z), OnPlayerUpdate_2(playerid, x, y, z) and OnPlayerUpdate_3(playerid, x, y, z) are hooked callbacks, or what are they?

In any case, what's so bad about my method of using global arrays to store all the information of the player? at first glance it might look quite memory consuming, however I use the following reasoning: imagine that instead of using multiple includes, I were writing all my script in the .pwn file, so there would be no callback hooks. Now imagine that, like before, I have several custom functions that check the position of the player and are called in OnPlayerUpdate. In such a case, I would be using local variables inside OnPlayerUpdate to pass that information to those functions. If my server were having a good number of players, all of them sending updates at the same time, the number of those local variables would be proportional to the number of players, so wouldn't all those local variables take up almost the same amount of memory as my global arrays? Just a question, not trying to defend my model at all costs.

EDIT: ups, now that I realize it, even if several players send an update at the same time, the server will process them one after another and not at the same time, and those local variables are destroyed when a callback is finished, and are created again when the callback is called again, so there will be always only one local variable of each type at the same time, and therefore those locals won't take up as much memory, right?

Y_Less
13/08/2012, 05:05 PM
You are right about single-threaded processing, yes.

Gryphus One
13/08/2012, 05:24 PM
Ok thanks for all that information, you really saved my script from being a true memory guzzler.

mastermax7777
25/02/2013, 06:10 AM
i just define everything before each function :D so... no layout its the best ..
im too lazy to sort everything out, takes too much time.. ull get it when u can script like baws like me lolz

im
30/04/2013, 06:34 AM
So, if you use multiple files, some callbacks will be used by more than one module, so with a single files you would have something like this:


public OnPlayerDeath(playerid, killerid, reason){
if(IsACop(killerid)){
SendToJail(playerid);
}
else if(IsAHitman(killerid)){
GiveMoney(killerid, 3000);
}
else if(IsAdmin(playerid)){
BanEx(playerid, "you're not allowed to kill admins");
}
}


In multiple files, you would have to do something like this:

OnPlayerDeath(playerid, killerid, reason){
CopSystemOnPlayerDeath(playerid, killerid, reason);
HitmanSystemOnPlayerDeath(playerid, killerid, reason);
AdminSystemOnPlayerDeath(playerid, killerid, reason);
}


And then in /inc/cop.inc

stock CopSystemOnPlayerDeath(playerid, killerid, reason){
if(IsACop(killerid)){
SendToJail(playerid);
}
}


Isn't it faster to use a single file?

Pottus
30/04/2013, 06:38 AM
Using a single file gets really messy I don't know about you but when a callback extends for thousands of lines with a dozen different systems integrated it's such a pain in the ass to work with it's not worth doing it that way.

MP2
30/04/2013, 08:23 AM
I'm using a proper file system now, but it's fucking annoying that I can't Ctrl+F for stuff, and Windows 7 is fabulous and doesn't search my file contents (even though in search settings it's set to..).

cessil
30/04/2013, 08:56 AM
In multiple files, you would have to do something like this:

OnPlayerDeath(playerid, killerid, reason){
CopSystemOnPlayerDeath(playerid, killerid, reason);
HitmanSystemOnPlayerDeath(playerid, killerid, reason);
AdminSystemOnPlayerDeath(playerid, killerid, reason);
}


And then in /inc/cop.inc

stock CopSystemOnPlayerDeath(playerid, killerid, reason){
if(IsACop(killerid)){
SendToJail(playerid);
}
}


Isn't it faster to use a single file?

There's many different ways to go about it, if you think about creating a small script per include, something that allows you to become a cop in one, and also include a way to detect if someone was a cop.


stock isACop(playerid)
{
return g_isACop[playerid];
}

cmd:makemecop(playerid,params)
{
g_isACop[playerid] = true;
}

now in your main script you could just use the stocks that you created in the cop include



stock OnPlayerDeath(playerid, killerid, reason){
if(IsACop(killerid)){
SendToJail(playerid);
}
}


so instead of processing everything through all the includes you could just do it in the main pwn file, and use the includes for adding everything needed to be a cop or to be a hitman

MP2
30/04/2013, 08:57 AM
Or hook.

RajatPawar
30/04/2013, 09:00 AM
For me, code optimisation is important, I don't care about the layout honestly, even though it could be bad practice, . I group variable declarations together and that's it. Everything else is messed up, I put new code where I see blank space!

Dan..
30/04/2013, 11:08 AM
I put new code where I see blank space!

That's a good example of bad organization.

In my opinion, the speed and the design of the code should be balanced.

im
02/05/2013, 07:38 PM
I just thought about something. If I were to use hook OnPlayerDeath in 20 include files would that affect the performance in any way? Using hooks would make everyting a lot more organised and readable.

Y_Less
02/05/2013, 07:47 PM
Not really, in fact the next version of y_hooks is improved to the point of being faster to use hooks than not to use hooks!

Dan..
02/05/2013, 08:14 PM
Not really, in fact the next version of y_hooks is improved to the point of being faster to use hooks than not to use hooks!

How can be that even possible? :o

Y_Less
02/05/2013, 08:21 PM
I removed the code that pushes and removes arguments from the stack, so they're pushed once - multiple hooks are called, then all removed at once too. I've written a very detailed post about it here:

http://forum.sa-mp.com/showpost.php?p=2490142

DeathKing
30/09/2013, 11:29 AM
I have a question... Does having more includes defined make the script compilation slower? If i make the script longer but lesser includes what will it have the same effects? Any suggestions to keep my laptop speed faster in compiling? Thanks in Advance



NOTE: i found this topic area the most relevant to post this thread to..

playbox12
30/09/2013, 04:15 PM
I don't have the exact numbers, but no.

To make your laptop faster in compilation, writting better, more optimised code would help, though this is only part of the story.

maddinat0r
30/09/2013, 04:53 PM
The #include directive takes the code from a *.inc file and pastes it into the script you are currently compiling.
more includes -> more code -> longer compile time
I also don't think that writing efficient code has a direct impact on the compilation time. You can write code which is longer (takes longer to compile) and more efficient than code that is short but not very efficient (for example an own 'format' function which only parses a %d specifier).

Y_Less
30/09/2013, 05:00 PM
A better question is why are you trying to optimise compile times? Long ones take maybe 30 seconds, and you're unlikely to be able to go much beyond what it takes. Includes are just another place to put the code. If you want to do something you need the code to do it - if that is in another file or the same file makes basically no difference. so yes, you can reduce the number of includes you have, but you will have to reduce the number of things that your mode does to improve compile times by any significant amount.

Pottus
30/09/2013, 06:41 PM
Y_Less put it into perfect words but just wanted to add a couple of ideas for you when your doing development work it may be desirable to only compile a development version this might exclude certain modules in your gamemode to speed up compile time. I certainly have to do this with the DayZ gamemode a development compile takes about 30 seconds to a minute where a production compile often takes over 5 minutes.

Deji
30/09/2013, 06:59 PM
Allow me to explain what Y_Less did in 1 paragraph using over an hours worth of painfully written detail (in my defense I stopped halfway for coffee)...

Compilers are extremely fast at what they do (though a quick look through the source code of PAWN might make most programmers want to cut themselves). The time it takes to open an include file and include the code from it? You might as well not even ask, because in order to efficiently speed test it, you would have to iterate that same include a few hundred thousand times (and it would still probably be pretty quick). And guess how you make the preprocessor repeat a process? You would have to manually repeat the code. The preprocessor has no loops or anything, it literally goes from start to finish processing every useful symbol that exists.

With SCRambl, my compiler for SCR (the language used to write much of GTA's content), this is done in several steps. Since SCR is a basic language with no stack, it's done line-by-line. It would be more efficient to read the whole file, close the file, then work from memory. It then scans the string for unimportant characters and removes them, skips comments etc. Any label is stored internally, any declared var is stored internally. Information is collected on those labels, vars, constants, you name it, throughout the script. Also, for every token which needs compiling to something, a structure is created detailing what type of token it is, how large it is, etc.

That was just the first step, really. Parsing. This is where parser part of the 'compiler' itself starts to understand the code in small chunks and mainly collects data that the actual compiler needs. The actual compiler simply reads each token and generates some code for it.

SCRambl's compilation processs looks like this: 'parser', compiler, 'builder'
More advanced compilers work a bit differently: preprocessor, compiler, assembler, linker

The preprocessor is what reads macro's, includes files, etc. and code for it's use is marked in SCRambl, C++, PAWN, etc. by the line beginning with: #

The 'parser' in SCRambl converts the input code into somewhat of it's own language for the compiler.
In SCRambl, the compiler compiles directly to it's output format, however it's a multiscript compiler so it keeps each script compiled in different places.
More advanced languages differ by converting the code to a lower level language which already exists (in most cases assembly) and then passing the code on.
The assembler is sort of like SCRambl's compiler, but it works with assembly language and not pure data.
The 'builder' in SCRambl combines all the files together
Similarly, the 'linker' in other languages is used to pull together external library objects with the main code

Why am I saying all this? I've just basically highlighted the differences in two compilers, one of which hasn't even been released. But the point I'm trying to make comes from the perspective of someone who knows how a compiler might work internally (which I only learnt by making my own).

SCRambl would be more efficient if it didn't read code line-by-line from the file and I might still change that. However, it's important to note that this way, when an include directive is met, all the script has to do is:
1) open include file
2) do the same line-by-line processing we're already doing

All that happens is SCRambl stores the current file it's processing, sets the file to process to the new file, and continues exactly as if the code was actually in-file. Now I can't make any definite claims towards other compilers as they are sore to have 'real' pre-processors whereas SCRambl is still line-by-line. SCRambl actually does pre-processor stuff during parsing. Due to SCR allowing, say, a label after a jump to it:
GOTO label

label:
SCRambl is already programmed to treat unknown identifiers as potential identifiers. It sees the first line as "command [this_should_be_a_label_pointer]" but since it doesn't need the label pointer value until compile time, by which time it already knows where the label is.

OK I SEE Y_LESS ALREADY NINJA'D ME, BUT BEAR WITH ME WHILE I TRY TO REGAIN MY TRAIN OF THOUGHT...

I'm running the most horrible computer in the world at the moment. It's not so much that it's low-spec as it is old and misused, but I digress... SCRambl performs admirably no matter how many includes I add. And it was written all from scratch (even replacements for C++ STL are used) by me, as my first ever project of such magnitude. Considering anyone with past experience doing this could avoid all my mistakes and would probably know a tonne more ways to optimise, I would bet PAWN compiles almost as fast as C itself and would probably take the place of C, in relation to SCRambl.

However, I have discovered, since starting SCRambl, that optimisation is not even an important process. C/C++ can compile every individual process to something that is instantaneous on it's own and since it's the language most compilers are written in, processing one line takes less than a millisecond. Get 100,000 actual lines (not including stuff that's mostly skipped or ignored) and you might see a bump up to a second or two.

I challenge anyone to write so much code that their compiler is slow at processing it :p You should worry more about resolving errors, warnings, bad syntax and bad code layout, because that is going to cost you the most time. The actual writing of the code is the most important place to save time in any coding project.



OR y dnt u start teh samp straite after u hir compile?

I should be banned from writing posts when I'm bored.

Deji
30/09/2013, 07:10 PM
I hate myself. That post is horrible. I lost my point completely halfway through :p All that writing, and I never thought to simply post a link and leave whoever to do whatever research they want:
https://code.google.com/p/project-scrambl/source/browse/Project%20SCRamble/CParser.cpp

I would have more examples non-SCRambl related but it's kind of hard to explain this stuff in the right context. For example, most talk about optimisation online is based on optimisation of code execution.

Y_Less
30/09/2013, 09:46 PM
Just remember: you are there to make the compiler's job easier, not the other way around. This means:

No defines - they have to be parsed multiple times.

No includes - that requires a file open.

No global arrays - memory allocation is (relatively) slow.

No descriptive variable or function names - anything more than 1 character requires extra reading.

Or rather, the exact opposite of what I just said!

Deji: That post was awesome, I may add something when I'm not on my phone as I've written several compilers too, but everything you had was fine. However, I have slowed the PAWN compiler to a crawl in the past with horrible masses of macros calling more and more macros.

Deji
01/10/2013, 12:34 AM
SCRambl literally doesn't do any processing to lines of code beginning with '#' and instead uses basic strncmp to match each directive. In fact, this may be the only thing I ever have to try to optimise in SCRambl in the future. Again, using SCRambl as my only inside knowledge. A binary tree would save some time, I even have my own class for them... but these are a few built-in preprocessor commands (which are called directives, for those who don't know and are still reading... hello you, I hope you're paying attention). No more than 20. And on the other hand it has a binary tree containing two and a half thousand commands to sort through (probably slower than the preprocessor). It has a haystack of other identifiers which grow infinitely with the script, in which it has to constantly find a needle. There may be cases where a label identifier is first checked against being a command, global variable, local variable, constant, macro, directive etc. before the parser figures out it's actually been a label the whole time.

Who knows, maybe someone like Y_Less will use SCRambl's preprocessor to make more horrible masses of macros calling more and more macros (though I should point out the reason I'm giving SCRambl this power in the first place is to heavily abuse it myself). That's when I might see what I can do to improve things, but aiming for optimisation from the start isn't a great way to get things done.

AHA! That was kind of the essence of the point from my first post! I found it! And as much as you might want your chosen compiler to hurry the hell up, the writer of that compiler wanted it's compiler to just hurry up, too. The moral of the story is, the writer of your codes compiler does not care about your code, he mostly cares that his does... No, wait.. I lost it again :p

No, the real point of this particular rant is that the preprocessor be slow, yo! It's also what separates man from primitive apes or perhaps it's the inner primitive ape which makes marvellous things using the most inefficient of tools. Either way, I can rock a metaphor.


EDIT

On a different note, if I still have anybody's attention. Yes, you, the ones who haven't yet already quite managed to get out of this topic yet... How often do you compile?

Let me say this... It takes 1 minute to compile SCRambl on this machine. But I haven't compiled since August.

Actually, that was a lie, but the point is truthful. I have been weeks without compiling or even attempting to compile my program. This isn't something I could always do and it does put a lot of pressure on your mind, so it's not something I would encourage less 'hardcore programmer'-type people to attempt to achieve straight away, but don't think for a second I would ever complain that it takes too long to compile my program... Hell, it doesn't take long enough. How am I going to make another cup of coffee in order to continue my month of solid code writing in that time?!

Okay, the minute thing is a slight misrepresentation too. It only takes a minute after 1 month of coding. Due to the C++ compiler using things like precompiled headers and such, if I were to only change 1 thing in the code, it would take a few seconds to compile. So that minute (or two) only sneaks up on me when I've not let it compile in a while.

A major difference, though, is that I have an IDE capable of telling me when a line of code is wrong without compiling. However, I don't have a computer capable of that any more apparently, so I just code for a few hours, then tackle the few hundred mistypes, outdated names and silly little coding errors that may occur. You get to know them after a while, which makes fixing them all easy. This is why I picked C++ over PAWN, by the way, as strong as I'm sure it can be.

MP2
23/01/2014, 11:56 PM
I've always scripted my gamemodes (the VERY few I have made) in one single .pwn file, but about a year and a half ago realised how bad code practise that was, and started using files (http://forum.sa-mp.com/showthread.php?t=356764). Now however, I think I am doing it too much - making files for the smallest things. Surely it can't be efficient to have 300 hooked callbacks in a chain [y_hooks] (which is what could potentially happen if I carry on when the server script is much bigger and more complex)?

For example, I'm making a CnR gamemode, and I have a script that makes it so players' blips have a longer draw distance to cops depending on their wanted level. I've put this in its own file, which is ~150 lines long. I feel this is something that should be in the main gamemode .pwn file.

The issue is, in this file I have variables, macros, functions, hooked callbacks and more, and they're all together in one place. If I put the code in the main pwn file, it's going to be scattered all over hundreds of thousands of lines.

What should I do?

Ballu Miaa
24/01/2014, 03:30 AM
Even i had this question some days ago. A game mode is much more organized and optimized if you do it in separate files. It is also a pain in the ass as you need to browse to each file and open them to make any changes every time you need to. It can be annoying at times. Well the choice is yours MP2!

Maybe some one who is more experienced then us can answer it well if i did not but it was my own point of view.

DiDok
24/01/2014, 08:31 AM
From extreme to extreme, eh?

I do it this way:
main .pwn with basic definitions (colours dialogs etc) includes and all callbacks (all checking done in pwn and everything handled with functions, hooking sux)
main "database" include with all player/vehicle/whatever arrays, global strings, spawn coords, speedtraps, announcements, zones, garages etc.
another "database" with hauls arrays
generic functions include
generic commands include (feature-specific commands go to their own include)
objects include
global timers include
single include for any MAJOR feature

Everything looks like this (split due to horizontal size):
http://i.imgur.com/vq6FpAZ.png
and i never had any issues or problems with finding anything

Do it in similar way?

Y_Less
24/01/2014, 01:02 PM
Disclaimer: Every file in YSI has a generic header that's about 100 lines of comments with API and license listed - I'm going to ignore this header for the sake of this post because it isn't functional code.

I find the usefulness of files seriously depends on how you split the code up, but that regardless of the split more files are still better than one. What I mean by "how" you split is that there are two main ways of doing this:



By code type: With this method you put variables in one file, defines in another, hooks in another, dialogs in another, etc... This is completely useless split as it means the code to do one thing is still split between multiple files. You also loose any benefits of separation you could get by using static functions and variables.


By feature: With this method you have your streamer in one file (hooks, variables, API etc), your vehicles in another, your police job in another, your admin system in another, etc... This is called "separation of concerns" and is programming basics!


NO other software projects ever debate wether they should use one or many files because that's ridiculous question - a 10,000 line file is horrific to search through, maintain, debug, and just to look at! If you have a distinct feature, put it in a separate file so that when you come to modify it later you know exactly where ALL the code relating to it is - it doesn't matter if that's only a few lines, but if you start getting in to 4 figures of lines, it's time to start re-evaluating if this is not two systems combined to be reduced again.

Some short files in YSI:


y_writemem - 150 lines
y_stripnumbers - 16 lines
y_shortfunc - 150 lines
y_renative - 4 lines
y_globaltags - 10 lines
y_natives - 50 lines (5 NOT comments)
y_distribute - 160 lines
y_compilerpass - 10 lines
y_cgen - 114 lines
y_jaggedarray - 260 lines
y_lock - 70 lines


I can tell you exactly what each of those files do without even looking in them, and I know where they are should I ever need to edit the functionality that they provide. I am ashamed to say that there are also some monster files in YSI (but about 1/3 of each of these are comments):


y_areas - 2700 lines
y_commands/impl - 1500 lines
y_text/impl - 2300 lines
y_text/load - 1280 lines
y_text/render - 1650 lines


As you can see, the y_text library is vast even after splitting it up further. All of those, and others, need reducing more - 1000/2000 lines is just too many. There are also the y_colour library files, one at nearly 4000 lines, but they are largely automatically generated and repetetive, so count less.

Having said that, I do have a new method of dealing with larger files:

http://www.y-less.com/YSI/map.png

I use ASCII art with Notepad++'s "Document Map" feature, so that with a glance to the right I can figure out where I am, and can use that map to very quickly scroll through the document.

http://www.y-less.com/YSI/map2.png

At my standard resolution (which neither of these images are) I have a rule that I can always see at least one section name in the map, if one chunk of related functions is too big, they get subdivided further. Again there are exceptions I'm not happy about - y_inline/impl has a function so long it takes up more space than one section should (long functions are of course poor practice in themselves - if they don't fit on one screen of text, they're too long).

I also use ctrl-F2 / F2 / ctrl-f / F3 to navigate a lot within one file. Fortunately splitting files by feature not code type means that I very rarely need to switch between many files to write something.

Edit: Merged multiple topics - in future search before making a new one!

MP2
24/01/2014, 01:09 PM
Edit: Merged multiple topics - in future search before making a new one!
I knew the topic existed - it's mine and I even linked to it in the new topic. I just never thought about posting a follow-up in THAT topic. Oops :P

Anyway, thanks for your reply, it clears a lot of stuff up, and that ASCII thing is very neat and I will definitely be using that, but I still have (unnecessary?) concerns about hooking. Will 300 OnPlayerUpdate hooks cause any noticeable performance issues?

Y_Less
24/01/2014, 01:20 PM
If you are using 3.1 300 may be an issue - for one thing you can only have 256 files that hook things, and even if every one of them hooked OPU that's still not 300. Also, the hook method isn't the greatest. However, 4.0 upped the limit to 343 (more isn't hard, just a little awkward and bloated), and re-wrote the whole hook method in such a way that its now actually faster than calling normal functions!

And I didn't notice both topics were by you, oops - sorry.

MP2
24/01/2014, 01:36 PM
If you are using 3.1 300 may be an issue - for one thing you can only have 256 files that hook things, and even if every one of them hooked OPU that's still not 300. Also, the hook method isn't the greatest. However, 4.0 upped the limit to 343 (more isn't hard, just a little awkward and bloated), and re-wrote the whole hook method in such a way that its now actually faster than calling normal functions!

And I didn't notice both topics were by you, oops - sorry.
I'm using YSI 4, and I'm actually the one who asked you to increase the limit, because of this topic.

So yeah, eventually when my script is massive, surely I'll have more hooks than y_unique can handle?

Y_Less
24/01/2014, 02:17 PM
I can probably up it again. In fact, I might re-write it so adding new values is simpler and so it uses a better search method based on files instead of just a huge single file. It is currently at 343 because that is 7*7*7, and I used a sort-of n-ary search. Would 1000 make you happy? I really can't go beyond that without seriously redesigning the system because the function names all use 3 digit numbers, so 000-999.

MP2
24/01/2014, 02:27 PM
I can probably up it again. In fact, I might re-write it so adding new values is simpler and so it uses a better search method based on files instead of just a huge single file. It is currently at 343 because that is 7*7*7, and I used a sort-of n-ary search. Would 1000 make you happy? I really can't go beyond that without seriously redesigning the system because the function names all use 3 digit numbers, so 000-999.
I think 1000 should be enough :p Thanks.

Y_Less
24/01/2014, 02:51 PM
Try my new commit.

KyleSmith
24/01/2014, 03:02 PM
I believe larger gamemodes should be sectioned, but to a certain extent I wouldn't go overboard with it.

I'd just stick stocks and functions in one pwn, vehicles in another. But when you have pwn files for silly small things, then that is a bit extreme.

Although it would take a lot longer to split up a pre-done script. Rather than getting used to spitting from the start.

Try my new commit.

Come on the IRC. :(

MP2
24/01/2014, 03:07 PM
I can't seem to find the 'document map' D:

This is my toolbar:
http://puu.sh/6woHj.png

Can't find it in any of the dropdowns either (File, Edit, View etc.).

Nero_3D
24/01/2014, 03:57 PM
I can't seem to find the 'document map' D:

This is my toolbar:
http://puu.sh/6woHj.png

Can't find it in any of the dropdowns either (File, Edit, View etc.).

Check for updates (? -> Check for updates), the current one is v6.5.2 but I guess you already tried that ?

MP2
24/01/2014, 04:01 PM
Check for updates (? -> Check for updates), the current one is v6.5.2 but I guess you already tried that ?
Hmm, I'm sure I updated it only a couple of months ago. Guess it was added in that time.. But yeah, found it now, thanks.

EDIT: Seems it was added on 26 March, 2012.. Guess I haven't updated in a while then..

Shetch
24/01/2014, 10:08 PM
The ASCII method looks amazing. I use Sublime Text 3 which has the minimap on the right side aswell.
Thanks Y_Less.

EDIT: Here's a great letters to ASCII art generator
http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20

I also found a pretty good "font"
http://patorjk.com/software/taag/#p=display&h=0&v=0&f=Banner3&t=Players

[HLF]Southclaw
24/01/2014, 11:54 PM
I split things in a hierarchy like this:

Core - Scripts that do stuff to data, frameworks etc.
Data - Arrays or definitions of data that scripts do stuff to (vehicle fuel/names, weapon names, loot indexes)
World - Still technically data, but environment based. Vehicle placements, puzzles, loot placements, etc.

In "Core" it's separated into Player, Server, Items, Vehicles etc. Basically each level just gets more specific.

This makes working so much faster than what I used to have (Script for ALL player stuff, script for ALL vehicle stuff, scripts were still huge)

Programie
03/03/2014, 02:50 PM
I split my gamemode in the following style:


SAMP standard includes (a_samp)
Custom includes (Plugins, etc.)
Constants (database configuration, constants like MAX_for arrays, macros)
Structures/Enum (One file per enum)
Global variables
Custom functions (One file per function)
Callbacks (One file per callback)
Dialogs (Yes, I have one file per dialog)
Timers (One file per timer)
Commands (One file per command)

Misiur
03/03/2014, 03:04 PM
I tried a few diffrent layouts (https://github.com/Misiur/NSSGM) (I abandoned that one). Currently I go for full modularity, so each system is separated into filterscript (so easy to enable/disable at runtime, faster compile times, etc.). I don't have to worry about scope leaking, so I can chunk it into even smaller pieces. Separating enums from variable declarations and functions isn't too smart, as you lose the possiblity of further encapsulation (static keyword is not available for you).

Also: abstraction from certain implementations is a good thing to do (easy swapping between files, sqlite and mysql).

AlonzoTorres
03/03/2014, 04:40 PM
OK, I'm intrigued, those of you who arrange your code by declaration type instead of function, why do you think this is a good way of doing it? And this is an honest question not sarcasm! The fact that a vehicle enum looks a little bit like a player enum doesn't mean that they're related in any way, so why put them together? That seems to me like organising a library by the colour of the book covers instead of the book topics!

As for having separate directories for systems and separate files for the different declarations in those systems - that's getting closer to a good layout but still means you can't use "static" to restrict access to data. Think of "static" as data private to a system - you want all of the system to be able to see that data, but you don't want any other system to see that data because they could start adjusting it in bad way. Instead you just provide a clean API to interact with that data any everyone is happy.

I'm not sure how this is even someone any programmer should wonder:

Number of major open source projects who "organise" their code in one massive file sorted by keyword: 0
Number of major open source projects who organise their code by splitting code up in to separate chunks of related functionality: All of them!

That alone should tell you something. I really suggest you start reading outside of SA:MP - you might learn something! There is a free book out called "The architecture of open source projects volume 2" - look it up. Also, just any generic reading on any generic programming will VERY quickly get on to how to split up code by function.

Finally, why do you think there are separate includes for player functions and object functions? Do you think that maybe says anything about the internal structure of the server's code? And I'm not giving ANYTHING away by saying that because any programmer with more than 10 minutes experience would realise that a large program like the server would split those functions up in that way.


I get a negative feeling about your post and it's kind of boring to read to be honest. We wanted to know what layout different users have but you just ramble on about how you're intrigued and that we should read more books and stuff. Why not relax with your academic chit-chat and just tell us how your layout is. The only thing I got from your text is that one should not arrange the code by declaration type instead one should arrange by function. You have to understand who your audience is and adapt your language and the information given in that way.

ColeMiner
03/03/2014, 07:30 PM
I get a negative feeling about your post and it's kind of boring to read to be honest. We wanted to know what layout different users have but you just ramble on about how you're intrigued and that we should read more books and stuff. Why not relax with your academic chit-chat and just tell us how your layout is. The only thing I got from your text is that one should not arrange the code by declaration type instead one should arrange by function. You have to understand who your audience is and adapt your language and the information given in that way.

You SHOULD read more - everyone should! Anyway, clearly you got the main points of my post despite it being "boring" and "academic" (programming is a very mental subject btw, so it IS academic). You got what you wanted and someone else may have found the additional information useful, so I don't see the problem. If I dumb my posts down for people that can't be bothered to learn, then those that can will miss out - it is much better to have too much information than not enough.

If all you want is a one word answer, then "functionality". My code is split in to multiple files, each one dealing with a separate part of the mode's operation. But I could have told you that in 2 seconds in a simple poll, which really isn't the point of the "discussion" forum. We created this place explicitly for more in-depth (academic) discussions, away from the "how do I make a gate" topics of scripting help. I do know the audience of my posts - the audience is people reading the scripting "discussion" forum, maybe you should be more aware of where you have stumbled in to and not expect everything to be spoon-fed to you here.

Gryphus One
04/03/2014, 01:31 AM
Currently I go for full modularity, so each system is separated into filterscript (so easy to enable/disable at runtime, faster compile times, etc.).

The main problem I see with this is there's a limit in the number of filterscripts you can run at the same time.

By the way, Y_Less, there's something I want to know your opinion on: I too split my code into different includes, hooking the callbacks and leaving only a few lines in the .pwn file. And generally I'm happy with this way of scripting, however there's something that clearly is less optimized than if I were writing all my code in the .pwn file: loops. Imagine there are several includes in which I have a for loop in the same callback, for example I loop through all vehicles in OnVehicleSpawn. If I hook that callback in those includes and put a loop inside, that loop will be repeated for that callback as many times as includes have that code, however if all that code were in the .pwn file I could loop only once and put all the code from all those includes into that single loop, right?

Ada32
04/03/2014, 02:19 AM
I get a negative feeling about your post and it's kind of boring to read to be honest. We wanted to know what layout different users have but you just ramble on about how you're intrigued and that we should read more books and stuff. Why not relax with your academic chit-chat and just tell us how your layout is. The only thing I got from your text is that one should not arrange the code by declaration type instead one should arrange by function. You have to understand who your audience is and adapt your language and the information given in that way.

i find the quality of his posts helpful (i enjoy reading so). you really don't learn anything by simple responses (just like the til thread..) - yes it's what you want, but sometimes you just need to soak some sun to have some fun!

anyways, ot - i write in one mode. i'm the only dev and i get things done much quicker. fine me later..(i do have one semi-abandoned project done with some y_master help and separated files (so i gave that a try, but it's a tdm i'll finish that later)

AlonzoTorres
04/03/2014, 09:08 AM
You SHOULD read more - everyone should! Anyway, clearly you got the main points of my post despite it being "boring" and "academic" (programming is a very mental subject btw, so it IS academic). You got what you wanted and someone else may have found the additional information useful, so I don't see the problem. If I dumb my posts down for people that can't be bothered to learn, then those that can will miss out - it is much better to have too much information than not enough.

If all you want is a one word answer, then "functionality". My code is split in to multiple files, each one dealing with a separate part of the mode's operation. But I could have told you that in 2 seconds in a simple poll, which really isn't the point of the "discussion" forum. We created this place explicitly for more in-depth (academic) discussions, away from the "how do I make a gate" topics of scripting help. I do know the audience of my posts - the audience is people reading the scripting "discussion" forum, maybe you should be more aware of where you have stumbled in to and not expect everything to be spoon-fed to you here.

Thank you for the answer. I never said that I wanted to be spoon-fed nor did I say that I only wanted one word for an answer. I simply said that you could be more specific at times and make it easier for those who wants a quick answer. You could do this by segmenting the information with as you describe it, "dumb" or as I would say, clear statements at first followed by a more in-depth discussion. No need to feel insulted and no need for insulting me with silly statements such as "and not expect everything to be spoon-fed to you here"... Only trying to help out you know!

Misiur
04/03/2014, 09:26 AM
@Gryphus One: Yup I'm aware of that. After completing fully working fs you can merge it into final gamemode (there was something about that in y_master/y_lock thread). Also I'm 99% sure there was somewhere plugin available allowing unlimited filterscipts. Still, while on dev/testing stage fs's form gives really big advantage in speed.

[HLF]Southclaw
04/03/2014, 09:49 AM
I second that! I recently wrote a new module for my gamemode entirely in filterscript form. Fast compilation and testing since I can just reload it instead of rebooting the entire server.

Another handy thing is, since all scripts are only accessed via interface functions, I created a "dummy" script of all the dependencies that just had blank copies of the interface functions to avoid undefined symbols. Since interface functions are all 'stock' prefixed, generating that file was a piece of cake with regex.

ColeMiner
04/03/2014, 11:05 AM
Thank you for the answer. I never said that I wanted to be spoon-fed nor did I say that I only wanted one word for an answer. I simply said that you could be more specific at times and make it easier for those who wants a quick answer. You could do this by segmenting the information with as you describe it, "dumb" or as I would say, clear statements at first followed by a more in-depth discussion. No need to feel insulted and no need for insulting me with silly statements such as "and not expect everything to be spoon-fed to you here"... Only trying to help out you know!

Fair enough. Sorry.

The main problem I see with this is there's a limit in the number of filterscripts you can run at the same time.

By the way, Y_Less, there's something I want to know your opinion on: I too split my code into different includes, hooking the callbacks and leaving only a few lines in the .pwn file. And generally I'm happy with this way of scripting, however there's something that clearly is less optimized than if I were writing all my code in the .pwn file: loops. Imagine there are several includes in which I have a for loop in the same callback, for example I loop through all vehicles in OnVehicleSpawn. If I hook that callback in those includes and put a loop inside, that loop will be repeated for that callback as many times as includes have that code, however if all that code were in the .pwn file I could loop only once and put all the code from all those includes into that single loop, right?

Short answer: I don't know.

That is a VERY good question, and now you've got me thinking about possible solutions - I don't have one at this time sadly. Something like "ptask", which is explicitly designed for per-player timer would be good; or "PSF:" functions, which are a bit of a hidden feature in YSI but again abstract per-player functions. As for a more general solution for your question, I just don't know unfortunately.

However, you would have to look at how much overhead the actual loop introduces. Most of the time spent on loops is spent doing the code inside the loop, not spent on doing the looping itself. The time taken to run code within the loop will be the same whether you have it in 1 loop or 10 loops (more or less, excluding code that needs to be replicated). If you can use "foreach" for things as well you may even get close to having trivial additional overhead for the extra loops. Of course, it will never be as good as only having one loop.

I was trying to imagine a solution in which hooks could be made aware of inner loops and handle those too, but I don't think that is at all possible. For one thing you would need to handle code run before and after the loop to set things up and destroy them. For another thing, you would have to call each hook for every loop iteration. Both of those problems can be solved, but I think the code required to solve them would be greater and slower than the code used by extra loops, so you are better off sticking to them.

The final question is: Is it even a problem? Yes it is "slower", but is it actually "slow"? If the slower version still runs fast enough to run the server, it isn't something that needs worrying about. If this changes, THEN you can start trying to optimise - bear in mind that readable code is much more useful in the long term than optimised code.

[HLF]Southclaw
04/03/2014, 11:39 AM
I've been eliminating unnecessary loops recently partly due to this problem. One method is to link up entities by just using more memory. And the other is to do the loop once then call a callback that is hooked in other modules instead of doing the loop in each module.

Eliminating unnecessary loops
Take for instance a GetVehiclePlayerID(vehicleid) function (opposite of GetPlayerVehicleID), in it's most basic implementation you'd loop through online players and return who is in the inputted vehicleid with IsPlayerInVehicle(i, vehicleid). (note that this example ignores the fact that vehicles can have multiple players within them). But why do that when you can just store what player is in a vehicle with an array. Assigning and removing from each vehicle's list with the enter/exit callbacks.

I couldn't think of a decent example without referencing my own libraries but you can apply this concept to many things to remove the need for loops to "search" for associations between entities.

Binding streamer entities
Another thing relating to the above: if you use the streamer plugin then there's a really useful "empty" slot for storing an array of any size with any streamer item. Use the array data manipulation functions with E_STREAMER_EXTRA_ID. I use the first cell as a "type" and the second slot as an actual ID (type can be SIF button, vehicle, player, etc and the ID slot is the actual buttonid/vehicleid/playerid etc)

Doing the work once and calling a custom callback
Finally, (this really depends on what your code does) you can do the loop once and create a completely separate callback for the other modules. For instance, there are a bunch of modules in my mode that run code when the player presses a key while standing at the front/read/sides of vehicles. I used to do the angle math under OnPlayerKeyStateChange in each module but now I do it once and call a new callback called OnPlayerInteractWithVehicle. (Again, it was the only example I could think of!)

ColeMiner
04/03/2014, 04:11 PM
The thing I would still be worried about with that solution is that the overheads of calling the custom callbacks in multiple modules would be slower than the overheads of having a separate loop in every file.

[HLF]Southclaw
04/03/2014, 05:43 PM
I guess ALS hooking doesn't help since they are custom callbacks (not updated to YSI 4.0 yet). It's in use in 3 modules at the moment and puts some distance checks and angle checks into one place instead of 3. I haven't tested timings but it's still a bit easier/quicker to use the custom callback instead of doing all the checks again (I suppose that would come under readability over optimisation point you made above)

I never took into account the callback hook vs loops overhead thing until you mentioned it though!

MP2
04/03/2014, 06:21 PM
Southclaw;2939613']I guess ALS hooking doesn't help since they are custom callbacks (not updated to YSI 4.0 yet).
ALS works with any callback. It's y_hooks that doesn't (in < 3.0).

[HLF]Southclaw
04/03/2014, 08:54 PM
That's why I said I haven't updated to y_hooks 4.0; as far as I know, y_hooks is faster than ALS, hence why using ALS it doesn't help the situation much.

Gryphus One
05/03/2014, 02:05 AM
@Gryphus One: Yup I'm aware of that. After completing fully working fs you can merge it into final gamemode (there was something about that in y_master/y_lock thread). Also I'm 99% sure there was somewhere plugin available allowing unlimited filterscipts. Still, while on dev/testing stage fs's form gives really big advantage in speed.
Southclaw;2939048']I second that! I recently wrote a new module for my gamemode entirely in filterscript form. Fast compilation and testing since I can just reload it instead of rebooting the entire server.

Well even if we assume there's no limit in the number of simultaneous filterscripts, still one thing is testing and another thing is adding new features permanently: it's true that filterscripts have the advantage of you being able to load, unload and reload them while the server is running, but I don't find them practical for permanent pieces of script for several reasons: if you want to share functions or global variables between different scripts you have to create public functions and use CallRemoteFunction (unless you use PVars, which is not the best idea), and also it's more messy when you have to deal with several .amx files at the same time (specially since sa-mp server has no function to tell what filterscripts are running at the moment). Also if you load a fs with loadfs (instead of adding it in server.cfg) and later your server restarts (for example if it crashes), that fs won't be loaded until you do it again.

Southclaw;2939048']Another handy thing is, since all scripts are only accessed via interface functions, I created a "dummy" script of all the dependencies that just had blank copies of the interface functions to avoid undefined symbols. Since interface functions are all 'stock' prefixed, generating that file was a piece of cake with regex.

This sounds like Chinese to me :confused:

Short answer: I don't know.

That is a VERY good question, and now you've got me thinking about possible solutions - I don't have one at this time sadly. Something like "ptask", which is explicitly designed for per-player timer would be good; or "PSF:" functions, which are a bit of a hidden feature in YSI but again abstract per-player functions. As for a more general solution for your question, I just don't know unfortunately.

However, you would have to look at how much overhead the actual loop introduces. Most of the time spent on loops is spent doing the code inside the loop, not spent on doing the looping itself. The time taken to run code within the loop will be the same whether you have it in 1 loop or 10 loops (more or less, excluding code that needs to be replicated). If you can use "foreach" for things as well you may even get close to having trivial additional overhead for the extra loops. Of course, it will never be as good as only having one loop.

I was trying to imagine a solution in which hooks could be made aware of inner loops and handle those too, but I don't think that is at all possible. For one thing you would need to handle code run before and after the loop to set things up and destroy them. For another thing, you would have to call each hook for every loop iteration. Both of those problems can be solved, but I think the code required to solve them would be greater and slower than the code used by extra loops, so you are better off sticking to them.

The final question is: Is it even a problem? Yes it is "slower", but is it actually "slow"? If the slower version still runs fast enough to run the server, it isn't something that needs worrying about. If this changes, THEN you can start trying to optimise - bear in mind that readable code is much more useful in the long term than optimised code.

I thought of a possible solution which maybe is one of those Southclaw talked about: creating a custom callback for the loop (I haven't done it though). If we take my previous example of looping through all vehicles in OnVehicleSpawn, it could look like this:


forward OnVehicleSpawnLoop(vehicleid);
// This is the new custom callback.

static g_example_HasOVS;

public OnGameModeInit()
{
g_example_HasOVS = funcidx("example_OnVehicleSpawn") != -1;
if (funcidx("example_OnGameModeInit") != -1)
{
return CallLocalFunction("example_OnGameModeInit", "");
}
return 1;
}

#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit example_OnGameModeInit
forward example_OnGameModeInit();

public OnVehicleSpawn(vehicleid)
{
// Other code...

for(new i = 1; i <= MAX_VEHICLES; i++)
// Since vehicle ids start from 1 and not from 0, variable
// 'i' too must start from 1 and be equal or less (and not
// only less) than MAX_VEHICLES.
{
if( GetVehicleModel(i) )
// This is so we don't loop through inexisting vehicle ids.
{
CallLocalFunction("OnVehicleSpawnLoop", "i", i);
}
}

// Other code...

if (g_example_HasOVS)
{
return CallLocalFunction("example_OnVehicleSpawn", "i",vehicleid);
}
return 1;
}

#if defined _ALS_OnVehicleSpawn
#undef OnVehicleSpawn
#else
#define _ALS_OnVehicleSpawn
#endif
#define OnVehicleSpawn example_OnVehicleSpawn
forward example_OnVehicleSpawn(vehicleid);


So in my other includes I only would have to hook the callback OnVehicleSpawnLoop. This way I could be hooking this callback in as many includes as I wanted, yet the loop would be executed only once.

MP2
05/03/2014, 03:52 AM
Well even if we assume there's no limit in the number of simultaneous filterscripts, still one thing is testing and another thing is adding new features permanently: it's true that filterscripts have the advantage of you being able to load, unload and reload them while the server is running, but I don't find them practical for permanent pieces of script for several reasons: if you want to share functions or global variables between different scripts you have to create public functions and use CallRemoteFunction (unless you use PVars, which is not the best idea), and also it's more messy when you have to deal with several .amx files at the same time (specially since sa-mp server has no function to tell what filterscripts are running at the moment). Also if you load a fs with loadfs (instead of adding it in server.cfg) and later your server restarts (for example if it crashes), that fs won't be loaded until you do it again.



This sounds like Chinese to me :confused:



I thought of a possible solution which maybe is one of those Southclaw talked about: creating a custom callback for the loop (I haven't done it though). If we take my previous example of looping through all vehicles in OnVehicleSpawn, it could look like this:


forward OnVehicleSpawnLoop(vehicleid);
// This is the new custom callback.

static g_example_HasOVS;

public OnGameModeInit()
{
g_example_HasOVS = funcidx("example_OnVehicleSpawn") != -1;
if (funcidx("example_OnGameModeInit") != -1)
{
return CallLocalFunction("example_OnGameModeInit", "");
}
return 1;
}

#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit example_OnGameModeInit
forward example_OnGameModeInit();

public OnVehicleSpawn(vehicleid)
{
// Other code...

for(new i = 1; i <= MAX_VEHICLES; i++)
// Since vehicle ids start from 1 and not from 0, variable
// 'i' too must start from 1 and be equal or less (and not
// only less) than MAX_VEHICLES.
{
if( GetVehicleModel(i) )
// This is so we don't loop through inexisting vehicle ids.
{
CallLocalFunction("OnVehicleSpawnLoop", "i", i);
}
}

// Other code...

if (g_example_HasOVS)
{
return CallLocalFunction("example_OnVehicleSpawn", "i",vehicleid);
}
return 1;
}

#if defined _ALS_OnVehicleSpawn
#undef OnVehicleSpawn
#else
#define _ALS_OnVehicleSpawn
#endif
#define OnVehicleSpawn example_OnVehicleSpawn
forward example_OnVehicleSpawn(vehicleid);


So in my other includes I only would have to hook the callback OnVehicleSpawnLoop. This way I could be hooking this callback in as many includes as I wanted, yet the loop would be executed only once.

But surely calling a local function 2000 times is going to be slower than a 2000 loop..

[HLF]Southclaw
05/03/2014, 08:21 AM
Well even if we assume there's no limit in the number of simultaneous filterscripts, still one thing is testing and another thing is adding new features permanently: it's true that filterscripts have the advantage of you being able to load, unload and reload them while the server is running, but I don't find them practical for permanent pieces of script for several reasons: if you want to share functions or global variables between different scripts you have to create public functions and use CallRemoteFunction (unless you use PVars, which is not the best idea), and also it's more messy when you have to deal with several .amx files at the same time (specially since sa-mp server has no function to tell what filterscripts are running at the moment). Also if you load a fs with loadfs (instead of adding it in server.cfg) and later your server restarts (for example if it crashes), that fs won't be loaded until you do it again.


I use filterscripts for development only, all modules are compiled into a single gamemode script for "production".


This sounds like Chinese to me :confused:


Check out this: http://www.codeproject.com/Articles/22769/Introduction-to-Object-Oriented-Programming-Concep#Interface
It means that my modules generally only interact using functions. There's no global "Player Info" variable for instance, admin level is stored in the admin module, password is stored in the accounts module, etc.


I thought of a possible solution which maybe is one of those Southclaw talked about: creating a custom callback for the loop (I haven't done it though). If we take my previous example of looping through all vehicles in OnVehicleSpawn, it could look like this:


So in my other includes I only would have to hook the callback OnVehicleSpawnLoop. This way I could be hooking this callback in as many includes as I wanted, yet the loop would be executed only once.

I think Y_Less is right about the overhead, definitely in this sort of example. (your code would call the custom callback 2000 times for each vehicle). My implementation of this idea only picks out some vehicles under a conditional statement that does a distance and angle check to the vehicle.

But I've found the best way is to just avoid large loops altogether! The thing I haven't got around to implementing yet is just a simple list of vehicles assigned to each player and a dynamic area on each vehicle. When the player enters the zone of a vehicle, it's added to his list and when he leaves, it's removed. This way, when the player tries to interact with a vehicle, the code only loops the vehicles in their list.

I've already implemented this idea here: https://github.com/Southclaw/SIF/blob/master/Button.pwn#L790-L806 and it's far better than looping through 32,000+ buttons!

Gryphus One
06/03/2014, 01:18 AM
But surely calling a local function 2000 times is going to be slower than a 2000 loop..
Southclaw;2940351']I think Y_Less is right about the overhead, definitely in this sort of example. (your code would call the custom callback 2000 times for each vehicle). My implementation of this idea only picks out some vehicles under a conditional statement that does a distance and angle check to the vehicle.

Actually my custom function 'OnVehicleSpawnLoop' won't be called 2k times but as many times as vehicles there are in the server, because the loop has in first place an 'if' conditional in which GetVehicleModel is called to tell if each id corresponds to an existing vehicle.
However this was only an example of a possibility I had thought of, and I haven't been doing that in my own scripts.

Southclaw;2940351']But I've found the best way is to just avoid large loops altogether! The thing I haven't got around to implementing yet is just a simple list of vehicles assigned to each player and a dynamic area on each vehicle. When the player enters the zone of a vehicle, it's added to his list and when he leaves, it's removed. This way, when the player tries to interact with a vehicle, the code only loops the vehicles in their list.

Yeah it's true that sometimes you can avoid loops by storing some information in variables. In that case I guess one has to choose between using more CPU or more memory.

MP2
06/03/2014, 03:15 AM
I just had a thought about vehicle loops, specifically only looping through vehicles which are near-by for a player. Couldn't you simply have a per-player iteration for 'nearby vehicles' based on OnVehicleStreamIn/Out? I guess that would be more efficient (processing wise) than a 2000 loop checking distances.

In regards to gamemode script layouts, I have run in to a bit of a problem, which is native function hooking. For this example, I have a file for my damage system, in which I hook SetPlayerHealth so that it can't be used to kill players if their health gets set to 0 (for reasons that are irrelevant). The problem is that SetPlayerHealth might be used in included files before the damage system is included, so won't be hooked. You might say 'well include it first', but I can't include EVERYTHING first.

Say I have 1.pwn that hooks SetPlayerHealth, and 2.pwn which hooks SetPlayerArmour. What if they both use both functions? Only one of the functions will be hooked in each..

The only solution I can think of is to have a file that's only purpose is to hook native SA-MP functions, which is included first (before scripts that will use the functions), but then there's the problem that if a variable needs to be used (in a file that needs to alter a native function like the damage sytstem), it can't be, you'd have to include the damage system first. I guess then I'd have to have two files for the damage system - one that declares vars/enums etc. for it, then the native function hooks, then the damage system file itself.

But then it seems like the 'modularisation' is getting a bit messy, with parts of the script in the hooks file.

I'm sorry, this is horribly worded, it's quite late. I hope it's clear enough.

Emmet_
06/03/2014, 03:23 AM
I just had a thought about vehicle loops, specifically only looping through vehicles which are near-by for a player. Couldn't you simply have a per-player iteration for 'nearby vehicles' based on OnVehicleStreamIn/Out? I guess that would be more efficient (processing wise) than a 2000 loop checking distances.

Nice, I haven't thought about it that way!

I was thinking that a custom iterator can be used (using foreach) to improve performance:


new Iterator:Vehicle[MAX_PLAYERS]<MAX_VEHICLES>;

public OnGameModeInit()
{
Iter_Init(Vehicle);
return 1;
}


And then, when a vehicle streams in and out:


public OnVehicleStreamIn(vehicleid, forplayerid)
{
Iter_Add(Vehicle[playerid], vehicleid);
return 1;
}

public OnVehicleStreamOut(vehicleid, forplayerid)
{
Iter_Remove(Vehicle[playerid], vehicleid);
return 1;
}


And finally:


foreach (new i : Vehicle[playerid])
{
// ...
}

MP2
06/03/2014, 03:24 AM
I was thinking that a custom iterator can be used (using foreach) to improve performance:


Yeah, that's what I meant.

Gryphus One
06/03/2014, 03:36 AM
In regards to gamemode script layouts, I have run in to a bit of a problem, which is native function hooking. For this example, I have a file for my damage system, in which I hook SetPlayerHealth so that it can't be used to kill players if their health gets set to 0 (for reasons that are irrelevant). The problem is that SetPlayerHealth might be used in included files before the damage system is included, so won't be hooked. You might say 'well include it first', but I can't include EVERYTHING first.

Say I have 1.pwn that hooks SetPlayerHealth, and 2.pwn which hooks SetPlayerArmour. What if they both use both functions? Only one of the functions will be hooked in each..

The only solution I can think of is to have a file that's only purpose is to hook native SA-MP functions, which is included first (before scripts that will use the functions), but then there's the problem that if a variable needs to be used (in a file that needs to alter a native function like the damage sytstem), it can't be, you'd have to include the damage system first. I guess then I'd have to have two files for the damage system - one that declares vars/enums etc. for it, then the native function hooks, then the damage system file itself.

But then it seems like the 'modularisation' is getting a bit messy, with parts of the script in the hooks file.

I'm sorry, this is horribly worded, it's quite late. I hope it's clear enough.

Maybe a solution could be to use custom functions to return those variables. Unlike variables, defines and enums, functions CAN be called before they are declared.

And I guess you meant 1.inc and 2.inc instead of 1.pwn and 2.pwn didn't you?

MP2
06/03/2014, 03:46 AM
And I guess you meant 1.inc and 2.inc instead of 1.pwn and 2.pwn didn't you?
No, you can include any file type. I personally use .inc for libraries and .pwn for scripts (modules). I use other files types too (e.g. .def for defines, .map for objects/maps etc.).

Anyway, I don't think you understood the problem. It's not about the variables, it's about being able to ensure native functions get hooked first time. It's not possible to do in each file, as I'll try and explain even clearer:

1.pwn:
- Hooks SetPlayerHealth.
- Uses SetPlayerHealth. Hooked.
- Uses SetPlayerArmour. Not hooked yet because 2.pwn isn't included..

2.pwn:
- Hooks SetPlayerArmour.
- Uses SetPlayerHealth. Hooked (by 1.pwn).
- Uses SetPlayerArmour. Hooked.

You see the problem? SetPlayerArmour is hooked by 2.pwn, but it's used in 1.pwn, and it won't be hooked if used there.

I'm also skeptical about how it would play along with fixes.inc (which is included directly after a_samp).

ColeMiner
06/03/2014, 04:42 AM
Have the hooks at the top of the file, include the other file in the middle of the file, then do the remainder of the code at the end of the file.

MP2
06/03/2014, 04:46 AM
Have the hooks at the top of the file, include the other file in the middle of the file, then do the remainder of the code at the end of the file.
By 'file' (first one) you mean the main gamemode file? So basically..

main.pwn

#include <a_samp>

#include <samphooks>

#include <module1>
#include <module2>
#include <module3>
#include <module4>

// Code

?

But I may need variables from 'module1' in samphooks, so I guess I'd have to have another file for variables..

ColeMiner
06/03/2014, 05:03 AM
No, by "file" I meant the first include that needs hooked functions from the second include - I'm just assuming that they include each other instead of needing a centralised file including everything.

Anyway, you shouldn't need variables from one file in another file if you code your module APIs right.

MP2
06/03/2014, 05:11 AM
I don't think you understand the problem. I need to ensure that before a native function is used by a module, it's hooked, so hooked functions have to come first, before any modules - not inside the modules themselves, otherwise earlier modules wouldn't be hooked.

Sometimes I may need variables from the modules in the hooking. For a terrible example, say I make a function SetPlayerInvincible, and what this does is sets a var to true, and when SetPlayerHealth is used on the player, if it gets set to 0, it voids it. I would need access to the variable. It [the hook] can't be in the module itself though, as like I've said it wouldn't be hooked in earlier modules.

ColeMiner
06/03/2014, 05:29 AM
That still sounds like the problem I gave you the solution for:

"module1.inc":


static gLastSet;

nhook My_SetPlayerPos(playerid, Float:x, Float:y, Float:z) // Imagine that works.
{
printf("%d %f %f %f", playerid, x, y, z);
SetPlayerPos(playerid, x, y, z);
gLastSet = playerid;
}

#include "module2"

hook OnPlayerSpawn(playerid)
{
SetPlayerHealth(playerid, 50.0);
}


"module2.inc":


static Float:gHealth = 10.0;

nhook My_SetPlayerHealth(playerid, Float:h) // Imagine that works.
{
SetPlayerHealth(playerid, h + gHealth);
}

#include "module1"

hook OnPlayerSpawn(playerid)
{
SetPlayerPos(playerid, 2.2, 3.3, 4.4);
}

MP2
06/03/2014, 05:30 AM
So you're saying include every module inside every other module? That's going to be very tricky with hundreds of modules.. Every module I add I'm going to have to add it to 99 other files...

ColeMiner
06/03/2014, 05:32 AM
If you want every module to have an ordered dependency on every other module, then you need to code that ordered dependency. The SLIGHTLY simpler way is to have a chain instead - module1 includes module2, module2 includes module3, module3 loops and includes module1. Then whichever one you include you still get them all and they will all still get each other's hooks. Adding a new item into the chain also won't be that hard.

MP2
06/03/2014, 06:20 AM
Wouldn't the simplest way be to declare variables first, hook next, then the main modules next? What would be the problem with that?

ColeMiner
06/03/2014, 10:54 AM
Mainly just scope and separation - your modules would be defined in multiple files, and depend on being included in exactly the correct order.

Gryphus One
07/03/2014, 01:33 AM
Anyway, I don't think you understood the problem. It's not about the variables, it's about being able to ensure native functions get hooked first time. It's not possible to do in each file, as I'll try and explain even clearer:

1.pwn:
- Hooks SetPlayerHealth.
- Uses SetPlayerHealth. Hooked.
- Uses SetPlayerArmour. Not hooked yet because 2.pwn isn't included..

2.pwn:
- Hooks SetPlayerArmour.
- Uses SetPlayerHealth. Hooked (by 1.pwn).
- Uses SetPlayerArmour. Hooked.

You see the problem? SetPlayerArmour is hooked by 2.pwn, but it's used in 1.pwn, and it won't be hooked if used there.

Try something like this:


#if defined _1pwn_included
#endinput
#endif
#define _1pwn_included

stock SPHHook(playerid, health) // Hooking SetPlayerHealth
{
// Additional code you want to hook...

return SetPlayerHealth(playerid, health);
}
#if defined _ALS_SetPlayerHealth
#undef SetPlayerHealth
#else
#define _ALS_SetPlayerHealth
#endif
#define SetPlayerHealth SPHHook

#include <2.pwn> // So SetPlayerArmour too will be hooked now.

// Rest of 1.pwn here, including calls to SetPlayerHealth and SetPlayerArmour
// which are now both hooked.



#if defined _2pwn_included
#endinput
#endif
#define _2pwn_included

stock SPAHook(playerid, Float:armour) // Hooking SetPlayerArmour
{
// Additional code you want to hook...

return SetPlayerArmour(playerid, armour);
}
#if defined _ALS_SetPlayerArmour
#undef SetPlayerArmour
#else
#define _ALS_SetPlayerArmour
#endif
#define SetPlayerArmour SPAHook

// Rest of 2.pwn here, including calls to SetPlayerHealth and SetPlayerArmour
// which are now both hooked.


By 'file' (first one) you mean the main gamemode file? So basically..

main.pwn

#include <a_samp>

#include <samphooks>

#include <module1>
#include <module2>
#include <module3>
#include <module4>

// Code

?

But I may need variables from 'module1' in samphooks, so I guess I'd have to have another file for variables..

Ok lets imagine that currently, your includes are like these:


#if defined _samphooks_included
#endinput
#endif
#define _samphooks_included

stock SPHHook(playerid, health) // Hooking SetPlayerHealth
{
if(Variable[playerid])
{
// Some code...
return 1; // Making SetPlayerHealth have no effect.
}
return SetPlayerHealth(playerid, health);
}
#if defined _ALS_SetPlayerHealth
#undef SetPlayerHealth
#else
#define _ALS_SetPlayerHealth
#endif
#define SetPlayerHealth SPHHook



#if defined _module1_included
#endinput
#endif
#define _module1_included

new Variable[MAX_PLAYERS];

// Some other code where you need SetPlayerHealth to be hooked...


In this case, you can include samphooks just below your Variable declaration in module1, or instead you can create a function to return the value of Variable to be used in samphooks which comes first, because as I said yesterday, you can call functions before they are declared:


#if defined _samphooks_included
#endinput
#endif
#define _samphooks_included

stock SPHHook(playerid, health) // Hooking SetPlayerHealth
{
if( GetPlayerVariable(playerid) ) // Instead of directly checking the variable,
// we just call a function that returns its value.
{
// Some code...
return 1; // Making SetPlayerHealth have no effect.
}
return SetPlayerHealth(playerid, health);
}
#if defined _ALS_SetPlayerHealth
#undef SetPlayerHealth
#else
#define _ALS_SetPlayerHealth
#endif
#define SetPlayerHealth SPHHook



#if defined _module1_included
#endinput
#endif
#define _module1_included

new Variable[MAX_PLAYERS];

stock GetPlayerVariable(playerid)
{
return Variable[playerid];
}

// Some other code where you need SetPlayerHealth to be hooked...


With the new function GetPlayerVariable, you will be able to include samphooks before module1 (so module1 will have its functions hooked), yet samphooks will still have access to Variable which is declared in module1.

Ceathor
11/03/2014, 09:39 AM
What I like to do, or rather, what I try to do, is to make different files for different parts of the gamemode.

As Y_Less said, not sorting the books by color, but by topic.

Some of the files that I include back into my main gamemode are:
- Initiation
- User initiation
- User damage things
- Vehicle purchase things
- Vehicle damage things

Well, basically, new file for a new feature. If I for instance decide to create a new feature that allows users to purchase and place furniture, I would make a brand new file for this feature on its own. That way everything is easy to edit, and the file for a quote on quote _bugless_ feature that I do not want to alter does not have to be opened at all.

Of course I sort things into different folders and such, but that's the main point of it all anyway.

[HLF]Southclaw
11/03/2014, 09:48 AM
I read some really neat articles on "Leaky Abstraction" check them out:

http://www.joelonsoftware.com/articles/LeakyAbstractions.html

http://www.claire-blackshaw.com/blog/2012/07/leaky-abstractions/

I think they will tie in pretty well with this thread.

tl;dr?

The concept discussed in the articles is basically about abstracting too far or unnecessarily from the fundamentals and the complex problems "leaking through" to the simple layer on top and causing more problems that the developer using the simple method has no idea how to address.

Here's a SA:MP example where IsPlayerInAnyVehicle is just a glorified vehicle ID validation check (I don't actually know the internals of this function, this is hypothetical). You run a loop and check IsPlayerInAnyVehicle then if they are, GetPlayerVehicleID and do something. But you may as well just get the vehicle ID on every iteration and just check if it's valid thus only calling one function instead of two that pretty much do the same thing only one returns a normalised result.

Another example of the second point: I am using YSI to develop a higher level player-interaction library, SIF. If I upgrade to YSI 4.0 because I need a feature that is only in YSI 4.0 and y_hooks breaks while Y_Less is on holiday, I am completely stuck as I have no idea how y_hooks works or how to fix it.

ColeMiner
11/03/2014, 10:35 AM
What's the problem again sorry?

Gryphus One
12/03/2014, 02:53 AM
What I like to do, or rather, what I try to do, is to make different files for different parts of the gamemode.

As Y_Less said, not sorting the books by color, but by topic.

Some of the files that I include back into my main gamemode are:
- Initiation
- User initiation
- User damage things
- Vehicle purchase things
- Vehicle damage things

Well, basically, new file for a new feature. If I for instance decide to create a new feature that allows users to purchase and place furniture, I would make a brand new file for this feature on its own. That way everything is easy to edit, and the file for a quote on quote _bugless_ feature that I do not want to alter does not have to be opened at all.

Of course I sort things into different folders and such, but that's the main point of it all anyway.

Except folders, that's what I too do.

Southclaw;2948990']I read some really neat articles on "Leaky Abstraction" check them out:

http://www.joelonsoftware.com/articles/LeakyAbstractions.html

http://www.claire-blackshaw.com/blog/2012/07/leaky-abstractions/

I think they will tie in pretty well with this thread.

tl;dr?

The concept discussed in the articles is basically about abstracting too far or unnecessarily from the fundamentals and the complex problems "leaking through" to the simple layer on top and causing more problems that the developer using the simple method has no idea how to address.

Here's a SA:MP example where IsPlayerInAnyVehicle is just a glorified vehicle ID validation check (I don't actually know the internals of this function, this is hypothetical). You run a loop and check IsPlayerInAnyVehicle then if they are, GetPlayerVehicleID and do something. But you may as well just get the vehicle ID on every iteration and just check if it's valid thus only calling one function instead of two that pretty much do the same thing only one returns a normalised result.

Another example of the second point: I am using YSI to develop a higher level player-interaction library, SIF. If I upgrade to YSI 4.0 because I need a feature that is only in YSI 4.0 and y_hooks breaks while Y_Less is on holiday, I am completely stuck as I have no idea how y_hooks works or how to fix it.

I have read the first article: well, it's obvious that abstractions can simplify things but cannot do miracles (like the examples of TCP when a snake chews your network cable or having to drive more slowly when it's raining). Apart from that, the article tells us that we must learn how abstractions work and the things they abstract away, but with that reasoning we sa-mp scripters would have to learn binary code just in case abstraction layers like Pawn start leaking.

I have also read the second article and well, I understand its point in the example of calculating the position of a point from a plane and using two functions when only one was enough, but then, must we know exactly all the internals of every single function from every single abstraction layer we use? progress in hardware, with more and more CPU, memory, graphics and storage resources available, improved energetic efficiency, longer lasting batteries and decreasing prices, should all compensate by far that loss of code optimization brought by the increased number of layers. In fact in that article there's a comment which shows what Y_Less said in this thread: sometimes readable code being better than fully optimized but harder to understand code.
Here in sa-mp scripting we have a clear example with file scripts: Dini is an abstraction layer for sa-mp's raw file functions and it's slower, but much more usable than those. Y_ini is another abstraction layer, and while more optimized than Dini, it's also harder for the scripter.

I think your example about IsPlayerInAnyVehicle and GetPlayerVehicleID, although somewhat similar to that of the article about the point and the plane, has more to do with the scripter not thinking well than with the abstraction layers themselves, because in this case you can easily know that GetPlayerVehicleID itself is enough to tell if a player is in a vehicle.

Your example about upgrading to YSI 4.0 and something breaking, reminds me of a thread I read about sa-mp not upgrading to a newer version of Pawn so some "hacky" things won't stop working.

ColeMiner
12/03/2014, 05:35 PM
SA:MP hasn't upgraded because doing so would break almost every script ever, not just the "hacky" ones. Yes, we have pushed the limits of the current version and upgrading would require some major overhauls in some areas, but when EVERYTHING needs to be changed for a new version it just isn't going to happen and I'm quite glad it isn't!

Gryphus One
13/03/2014, 12:19 AM
SA:MP hasn't upgraded because doing so would break almost every script ever, not just the "hacky" ones. Yes, we have pushed the limits of the current version and upgrading would require some major overhauls in some areas, but when EVERYTHING needs to be changed for a new version it just isn't going to happen and I'm quite glad it isn't!

I'm afraid my comment sounded as if I were asking for a Pawn upgrade when actually that wasn't my intention, I'm fine with our current Pawn version.

Bertie
29/07/2014, 07:49 PM
I am writing a new gamemode. The gamemode will be MySQL based and I want to separate each 'system' into files. The reason for this is because more developers will be working on the gamemode in the future. I want it so that each developer can work on a particular system at the same time and to also prevent leakage of the gamemode.

Any suggestions?

Y_Less
29/07/2014, 08:50 PM
All the suggestions are generally, do what you already plan to do. Make sure your interfaces are clean too - keep all variables "static" from the start and let different files interact with each other only through functions, that way you will find that modifying how things work becomes much simpler.

Also, look up y_master if you are concerned about code theft. That allows you to split your separate files up and compile them as filterscripts, so your developers don't have all your source code; but allows you to compile everything in to one single mode with no modifications.

Bertie
29/07/2014, 09:59 PM
All the suggestions are generally, do what you already plan to do. Make sure your interfaces are clean too - keep all variables "static" from the start and let different files interact with each other only through functions, that way you will find that modifying how things work becomes much simpler.

Also, look up y_master if you are concerned about code theft. That allows you to split your separate files up and compile them as filterscripts, so your developers don't have all your source code; but allows you to compile everything in to one single mode with no modifications.

Appreciated. I will look up y_master, thanks.

Gryphus One
29/07/2014, 11:45 PM
keep all variables "static" from the start

By the way, is it possible for a variable to be both "static" and "stock" at the same time?

Kar
30/07/2014, 12:43 AM
By the way, is it possible for a variable to be both "static" and "stock" at the same time?

Yeah

Y_Less
30/07/2014, 04:45 AM
By the way, is it possible for a variable to be both "static" and "stock" at the same time?

Yes, but if you are writing a mode, don't use "stock" - that's only useful for libraries.

Bertie
30/07/2014, 05:13 PM
I am going with this layout for my gamemode.

main .pwn file:


// Include by SA-MP Team
#include <a_samp>

// Include by BlueG
#include <a_mysql>

// Include by Y_Less
#include <sscanf2>

// Include by Y_Less
#include <YSI\y_commands>

// Include by Y_Less
#include <YSI\y_hooks>

#include "../include/systems/player.inc" // consist of all my mysql saving, login and register dialogs etc.
#include "../include/commands/playercmds.inc" // consist of all my player/general commands



Any opinions?

[HLF]Southclaw
30/07/2014, 05:27 PM
Here's my input regarding modular code.

Building on what Y_Less said about interfacing, you might want to build your database stuff separate from your dialogs and stuff which are separate from the login code. That way, if you want to log in via the text box, that's very easy to do. If you want to change the database code to SQLite or files or something else, you change the implementation and the function calls remain the same.

So, this file (https://github.com/Southclaw/ScavengeSurvive/blob/master/gamemodes/SS/Core/Player/Accounts.pwn) contains all my database related code with interface functions such as GetAccountPassword and GetAccountRegistrationDate. If I wanted to change my database code from SQLite to MySQL, I'd rewrite all the internal code but keep those Get/Set functions at the end of the file. Now, my other scripts are still using the GetAccount/SetAccount functions but with a brand new database engine.

On a final note, a personal opinion about naming schemes: I see "includes" as libraries, as in external libraries/dependencies. I tend to name gamemode modules .pwn and place them within the "/gamemodes/gamemodename/" directory (example (https://github.com/Southclaw/ScavengeSurvive/tree/master/gamemodes)). Poke through that linked gamemode source by the way, it's mostly modular aside from a few small oddities and old code.