Gamemode Script Layout
#1

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.
Reply
#2

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.
Reply
#3

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;

pawn Код:
// =============================================================================
// 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:
pawn Код:
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.
Reply
#4

Call me an idiot, but I go for the same layout as the Godfather.
  1. Includes
  2. Defines
  3. Public function forwards
  4. Enums/Arrays
  5. Variables
  6. All default SA-MP callbacks
  7. All custom functions.
  8. Timers
  9. 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.
Reply
#5

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:
pawn Код:
Includes (#Incldue ""../Data/Systems/System_Name/Header"
Callbacks
Each system include header looks like this:
pawn Код:
#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.
Reply
#6

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.
Reply
#7

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 ****** 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.
Reply
#8

Quote:
Originally Posted by ******
Посмотреть сообщение
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.
Reply
#9

Quote:
Originally Posted by MP2
Посмотреть сообщение
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.
Reply
#10

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.
Reply
#11

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!
Reply
#12

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.
Reply
#13

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
Reply
#14

Quote:
Originally Posted by AndreT
View Post
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.
Reply
#15

Quote:
Originally Posted by VincentDunn
View Post
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!
Reply
#16

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.
Reply
#17

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.
Reply
#18

Look up PAWN-Boilerplate project by Slice for solid script layout.
Reply
#19

Yeah I know about that.
Reply
#20

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.
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)