Skip to content

Advanced (Developers & Admins)

Alex Smith edited this page May 27, 2024 · 2 revisions

This page is intended for addon and server developers that need detailed information on Photon 2's internal operations for compatibility or troubleshooting.

For assistance in troubleshooting compatibility issues or to request a specific API feature (hooks, easier access to variables, etc.) please join the Photon Community Discord Server and create a support thread in the #v2-support channel.

Detecting Photon 2

Photon 2's primary codebase resides in a global table aptly named Photon2. Detecting if Photon 2 exists is as straightforward as:

hook.Add( "Initialize", "MyAddon.CheckForPhoton2", function()
   if ( Photon2 ) then 
       -- Photon 2 is installed
   else
       -- Photon 2 is not installed
   end
end)

CAMI Admin Permissions

Photon 2 uses the "Common Admin Mod Interface" (CAMI) for permissions checks when setting Photon 2 console variables.

As of writing, server settings are defined with the following:

CAMI.RegisterPrivilege({
	Name = "Photon2.ServerSettings",
	MinAccess = "superadmin"
})

If you do not use a CAMI-compliant admin mod, only super admins are able to adjust server settings.

Detecting a Photon 2 Vehicle

The most reliable way to detect if a spawned vehicle/entity is bridged to Photon 2 is to check it for an attached photon_controller entity. Photon 2 adds a few shared (client and server) entity meta-table functions called ENT:GetPhotonController() and ENT:GetPhotonControllerFromAncestor() for this purpose.

Unlike Photon LE, Photon 2 uses dedicated entities to mange each discrete instance of Photon (i.e. each vehicle).

ENT:GetPhotonController()

Returns an entity's directly-attached photon_controller if it exists.

if ( ent.GetPhotonController and IsValid( ent:GetPhotonController() ) ) then
    -- entity is bridged to Photon 2
end

ENT:GetPhotonControllerFromAncestor()

Returns a photon_controller from an entity's parent or any of its ancestors (i.e. parent of a parent, etc.) This is specifically necessary for custom vehicle platforms where players are in seats and the corresponding controller is attached to a shared parent entity.

if ( ent.GetPhotonControllerFromAncestor and IsValid( ent:GetPhotonControllerFromAncestor() ) ) then
    -- entity is directly attached or has a parent bridged to Photon 2
end

Spawning a Photon 2 Vehicle

Photon 2 uses a very similar approach to Photon LE for detecting vehicles to initialize. As long as a vehicle's .VehicleTable is appended on spawn, Photon 2 will detect an apply the correct vehicle configuration. This is done by the default gm_spawnvehicle command in Sandbox, and I strongly recommend looking at the implementation of that function.

Why is the .VehicleTable Necessary?

While many addons consider a vehicle's model (e.g. models/car.mdl) to be a reliable identifier, Photon allows for one vehicle model to be used for an unlimited number of other vehicles, each with their own equipment and skin configurations. For this reason, Photon is not able to determine what vehicle configuration should be applied to a vehicle strictly based on its model.

MetaTable Overrides

The overwhelming majority of Photon 2's in-game operations occur within Photon-specific entities or in isolated wrapper tables. Unlike Photon LE, vehicle functions and datatable slots are not relied upon, greatly reducing inherent conflicts.

However, a few isolated hacks or workarounds were required in order to account for minor Garry's Mod bugs or insufficient API support.

Vehicle.SetSubMaterial()

Photon overrides the Vehicle meta-table's :SetSubMaterial( id, material ) function in order to intercept server-side changes (this function is not overridden on the client). This is necessary because 1) Photon's equipment system is handled almost exclusively client-side, and 2) Photon utilizes dynamically-generated materials for a variety of reasons.

The drawback in this approach, however, is that any manipulation of an entity's sub-materials on the server will reset sub-materials on the client. As it is currently implemented in Garry's Mod, there is no reliable way to detect when this happens. So to account for this, Photon overrides the function on the Vehicle meta-table as a means of notification, then resumes the function call to the original function. Clients within the entity's PVS are sent a very brief network message and instructed to reapply their sub-materials on the client.

The override should be designed in a manner that other overrides of the same function are not canceled out or interfered with. Photon simply performs a one-time check to create a reference to the current SetSubMaterial function (whatever it is) and calls it within the override. As long as all overrides are structured this way, there shouldn't be an issue.

Hooks

These are hooks that can be used to detect internal Photon 2 events.

Important

Not seeing a hook you need? Please create a thread in #v2-support on the Photon Discord server.

Photon2:EntityCreated

Called whenever a client-side photon_entity is created.

hook.Add( "Photon2:EntityCreated", "MyHook", function( ent, controller )
-- ent is the created photon_entity
-- controller is photon_controller entity managing it (networked)
end )

Lua File Limit

Garry's Mod has a hard limit on the maximum number of Lua files it will load. What the number actually is remains unclear, but it's somewhere between 8,192 and 32,000. As Photon 2 is an unusually large addon already, it's recommended to favor using a combined file for Photon 2 content (e.g. sirens) when reasonable.

There are more variables that play into this -- but, again -- Facepunch has not been especially forthcoming on these limits for reasons I do not understand.