Development Guidelines

Please review the following guidelines for developers regarding the products you plan to sell on the marketplace. These guidelines and practices will help ensure that your script is not denied during curation or disabled.


1. Malicious Code

Development -> Safe for Customers


Malicious code is unacceptable in any manner, no matter the purpose. We also include backdoors in this definition. In our terms, backdoors are features or loopholes in code that allow unfair advantages to you (the developer) or to other players.

Having such code will result in your script being immediately denied and may result in your submission privileges being revoked.

Debugging utilities may be provided, determined on a case-by-case basis. Utilities which are restricted by default (both to support, and by server owners), provide no in-game benefit and are safe from exploitation are very likely to be approved, but unrestricted tools which are exploitable are not.


2.  Obfuscation / DRM

General -> Curation Facilitation


Obfuscation is the process of making code obscure or unintelligible to the reader, typically deliberately, creating source code that is difficult for humans to understand when viewing.

If you plan to obfuscate any code within the addon you wish to sell; the initial product submission must contain the unobfuscated code for our submission team to review. This can be added as a private version, limited to curators and moderators.

Digital rights management (DRM) is a systematic approach to copyright protection for digital media. Your addon is allowed to utilise DRM protection, however, the original submission must contain ALL code that the addon will utilise locally to function properly. This can be added as a private version, with DRM added to the stable branch.

Be aware that DRM, external services and Software as a Service products must conform to our sunsetting policy when you wish to end support.


3. Product Interoperability

Development -> Interoperability


Ensure your products work well with others as within Garry’s Mod, your products do not run on their own islands, and should not behave as though they do. When Garry’s Mod starts, all addon folders are mounted together in a virtual file system, material paths are merged, and code runs together in the same Lua interpreter.


3a. File Structure

Having something named lua/autorun/init.lua will instantly get your script denied.

Here is an example standard structure which would be acceptable.

📁 addons

📁 product_name

📁 lua

📁 product_unique_name

📄 cl_init.lua

📄 sh_init.lua

📄 sv_init.lua

📁 autorun

📄 product_unique_name_autorun.lua

📁 materials

📁 resource

📁 sound


It is also common to create sub-folders within lua/product_unique_name/ such as client, server and shared or otherwise, defining your product’s structure.

Your product should only have a single autorun file, which is limited to loading your other files (with include and AddCSLuaFile).

Please remember that if you do not have anything to add to folders such as materials, resource, sound, then you should exclude adding those folders to your addon structure. The point of the above example is to show that addons should rest within their own uniquely named parent folder.

If you integrate with third party addons, this rule also applies. For example, the filename gmodadminsuite/modules/logging/modules/logs.lua is disallowed, but  gmodadminsuite/modules/logging/modules/myaddon_logs.lua is allowed.


3b. Hooks

Ensure the following:

  • Your hooks should not prevent other hooks from running without a reason.
  • This includes detouring functions without calling the "old function"
  • Whenever a hook returns a value, other hooks may not be called.
  • Be considerate of other addons that a customer may have installed, make sure your script works with any other addons installed
  • Instead of returning a value if something doesn't meet your conditions, return nothing!
  • Example of what NOT to do:
-- THIS IS VERY BAD
hook.Add( "PlayerUse", "some_unique_name", function( ply, ent )
    if ply:IsAdmin() then
        return true
    else
        return false
    end
end )
  • Alternately, this example is better as it is restricted to only your entity, so won’t block other entities from being used.
hook.Add( "PlayerUse", "some_unique_name", function( ply, ent )
    if ent:GetClass() == “my_class” then
      if ply:IsAdmin() then
          return true
      else
          return false
      end
    end
end )


3c. Optimisation

Having a poorly performing product can have a major impact on highly loaded servers, so you should avoid costly operations:

  • Do not loop through functions such as player.GetAll() or player.GetHumans() / long tables in think hooks.
  • Looping through the players or entities table should instead use the player / entity iterator functions, so long as the table is not modified in the loop.
  • Alternately you should cache your required players / entities outside of the think hook.
  • net.WriteType (and by extension, net.WriteTable) is a very expensive function, and should be avoided.
  • Sending from client to server is not recommended and should only be used when absolutely necessary.
  • Sending from server to client is allowed but should be avoided if possible.
  • Try to send static structures instead of using WriteTable.


i. Object Caching

You should cache objects, including but not limited to; Color, Material, Vector, Angle, Matrix. Where these objects are used multiple times, there is little reason to not re-use them, especially when being used in functions called every frame.

DrawHUD and the likes are called every time the game renders a frame, by not caching Color and Material you create new objects every frame, which fills up memory, reduces framerate, and hurts the garbage collector's performance.

Example of bad code:

function SWEP:DrawHUD()
	surface.SetDrawColor(Color(255,0,0)) -- Creating a colour every frame.
	surface.DrawRect(0,0,ScrW(),ScrH()) -- Calling ScrW and ScrH every frame.

	surface.SetMaterial(Material("phoenix_storms/stripes")) -- Creating a material.
	surface.DrawTexturedRect(0,0,ScrW(),ScrH()) -- Double called functions.
end

Example of better code:

local col_red = Color(255,0,0) -- All of these are only created once.
local xd_mat = Material("phoenix_storms/stripes")
Local scr_h = ScrH() -- This is an issue if the user resizes.
Local scr_w = ScrW() -- Resolving that is left as an exercise for the reader.

function SWEP:DrawHUD()
	surface.SetDrawColor(col_red)
	surface.DrawRect(0,0,scr_w,scr_h)

	surface.SetMaterial(xd_mat)
	surface.DrawTexturedRect(0,0,scr_w,scr_h)
end


ii. 2d3d Distance Checks

You must implement distance or visibility checks when using 2d3d to draw in the world (such as adding overhead text), otherwise the drawing code will run constantly, reducing performance.

Here is a code example of what you could implement to stop the 3d2d from drawing if the player is far away.

if LocalPlayer():GetPos():DistToSqr( self:GetPos() ) >= 50000 then
     return
end

You can also use Distance instead of DistToSqr depending on your use case.


iii. Excessive Memory Use

You must not create structures which use excessive memory. For example, tables which store unbounded arbitrary data should have a limit, either implicit (such as number of players on a server) or explicit.

As an example, a server’s logs are unlimited, so a product should not load all logs into a table, instead should limit which logs are retrieved (such as via pagination).


4. Globals (_G)

Development -> Interoperability


The table _G is used internally by Lua, with any global variable access (variables other than locals and function upvalues) being redirected to this table.

Your functions, data and enumerations should be placed within their own table, with that table having a unique name (such as product_name, or author_product_name).

For advice on how you could structure your table, see the development guidance.


5. Deprecated Functions

Development -> Maintainability


Over time some code becomes deprecated and should no longer be used in your addons as it may be removed without warning. Here's some of the examples worth mentioning; you should never use any of these in your addons:


6. Networking

Development -> Interoperability


Under no circumstances should you do any of the following:

  • Setting DataTable variables or sending net messages too often (for example, in a think or move hook)
  • Using tostring on numbers and sending the string instead of simply sending a number unless there is a specific reason for it.
  • Setting DataTable variables that are never used on the client
  • Sending data to the client which is never used.
  • net.WriteEntity(LocalPlayer()) should never be done.
  • Use the second argument of net.Recieve’s callback (ply) to check the sender.
  • All important checks and calculations should be done server-side, not client.
  • The client is not a safe area, clients can be used to run code that is not yours.
  • DarkRP used to allow client side lua by default.
  • SendLua and BroadcastLua are not allowed under any circumstances.
  • Use the net library instead.


Please always do the following:

  • Debounce / have a cooldown on net messages for players, if they run expensive code or may throw an error.
  • Verify incoming data types to prevent errors from occuring.


Using NWVars may be allowed if absolutely required or you can argue that it's a better method than networking it with the net library. NW2 vars are NOT allowed until this and this has been fixed and implemented. For standard networking, you should use the net library and for custom entities you should use NetworkVars for most cases.

More information about networking can be found in development guidance.


7. Code Cleanliness

General -> Curation Facilitation

Development -> Maintainability


Make a good attempt at keeping your codebase simple, clean and easy to read:

  • If you are having to copy/paste the same code repeatedly, then this is a sign that you should be using a function or a loop.
  • Code must have consistency. This means that you should not be using numerous different practices in one script. For example, using two or more of the same type of casing for variable/function names such as: camelCase, PascalCase, snake_case
  • Properly use indentation and spacing to make your files more readable.
  • Avoid having massive files which are hard to read and understand.


8. Code Safety

Development -> Safety


  • Expect adverse situations and validate that data is in a correct state, don't assume a value will always be what you think it should be. Make your code know what to do when unexpected situations arise.
  • Validate user permissions for risky operations.
  • Ensure safety when changing collision rules.
  • Failure to do so prompts the error “CHANGING COLLISION RULES IN A CALLBACK IS LIKELY TO CAUSE CRASHES!”, and is also likely to cause crashes.
  • Do not:
  • Remove an entity in a touch or collision hook.
  • You must use SafeRemoveEntityDelayed in these use-cases to remove entities.
  • Change PhysObject properties in a touch or collision hook.


9. Licenses / Copyrighted Works

General -> Rules


Do not use work that you do not have permission to use. Review licences that come with any online resource before you integrate them into your script, including sounds, models, images or code.

If the resources you are using require acknowledgement to the developer/creator, then provide it in your product description.

Review any licences that come with resources; the licence file should state what you are allowed / not allowed to do with the resources. You can view https://choosealicense.com/licenses/ for a detailed outline of the most popular licences, though this does not constitute legal advice.

9a. Dependent / DLC Products

Products which are dependent on, or are DLC for other products must have permission from the base product's team to be sold. Products which integrate with other products (such as by adding commands, adding pages to UI) are recommended to reach out for permission, but it is not required.

If the information / APIs used to integrate are publically documented, we typically see this as an implicit permission to integrate.


10. UI Scaling

Development -> Operation


If your script includes a GUI (graphical user interface) for players to view and interact with; it must be structured to work with ANY monitor resolution. You should not be using static width/height values for major components. People on smaller resolutions may see parts of your interface going off-screen, behind panels, or missing entirely due to your code not knowing how to handle the discrepancies in resolution sizes.

This includes fonts and materials as well.

Your code should handle screen size changes.

Can't find what you're looking for?

Contact our support

Search products...