To create API bindings for the module there are two different ways to do it, in this article both will be explained. But as a requisite, the different types of available bindings will be explained.
There are 3 different types of "objects" that bindings can be written for, these would be:
Modules are as the name would suggest the same as JavaScript modules, they can be imported and then be used by scripts,
for example the shared module that can be found in shared/modules/SharedModule.cpp
is the base module for the @altv/shared
import (which is inherited by @altv/server
and @altv/client
).
These modules implement global functions like alt.log
which are not associated to any object.
They are created by specifying a new js::Module
instance globally, which needs a name and callback passed.
The name passed there is only for internal use and not the actual name of the module when imported.
The callback is called when getting the template for the module, which specifies which functions / properties it has.
TODO
TODO
The different types of binding types also have different functions available for specifying the API in their template, these different functions will be explained here.
All specific templates inherit from the base Template
class.
This sets a static property on the template, for classes this would be e.g. Player.getByID
so any function
that is related to the class, but not to any specific instance of it.
For modules and namespaces, this is what is used to specify normal properties on them like alt.log
.
Automatic bindings are not available for static properties, so they always need to be manually binded.
These can also just be a simple value, instead of a getter / setter pair.
The getter can be omitted, which makes the property read-only.
Usage:
static void GetterCallback(js::PropertyContext& ctx) {}
static void SetterCallback(js::PropertyContext& ctx) {}
tpl.StaticProperty("propertyName", GetterCallback, SetterCallback); // Getter / Setter
tpl.StaticProperty("propertyName2", GetterCallback); // Getter (Read-only)
tpl.StaticProperty("propertyName3", 3452); // Static value
myTemplate.propertyName = 23; // Calls setter with value `23`
alt.log(myTemplate.propertyName2); // Calls getter and logs return value
alt.log(myTemplate.propertyName3); // Logs the value `3452`
TODO
This sets a lazy static property on the template, these are most the same as StaticProperty
with the only exception that this function only accepts a getter, and that this getter is only called once.
On the first access of this property the specified getter is called, and the return value of it is stored on the object. Every subsequent access just returns that value and does not call the getter again.
So this class is useful for any values that never change and should be preferred for those, as getting that "cached" value is a lot faster than calling the getter again.
Usage:
static void GetterCallback(js::LazyPropertyContext& ctx) {}
tpl.StaticLazyProperty("propertyName", GetterCallback);
alt.log(myTemplate.propertyName); // Calls getter and logs return value
alt.log(myTemplate.propertyName); // Does not call the getter again and logs the value of the previous return value
This sets a static function on the template, it is similiar to StaticProperty
as it is not
related to any specific instance of a class.
The only difference that instead of a property which calls a getter / setter, it is a normal function which can also be called with multiple arguments.
Usage:
static void FunctionCallback(js::FunctionContext& ctx) {}
tpl.StaticFunction("functionName", FunctionCallback);
myTemplate.functionName("arg1", 23, []); // Calls the function callback with specified arguments
The template used for namespaces, this template has (at this point) no functions
other than the inherited functions from the Template
base class.
The template used for modules, this template provides functions for adding namespaces and binding exports to the module.
This sets a namespace on the template, which acts like an object which can be accessed on the module
often used to provide functions for specific systems like Timers
or Events
.
It has multiple overloads for different purposes.
Usage:
static js::Namespace myNamespace("MyNamespace", [](js::NamespaceTemplate& tpl) {
tpl.StaticProperty("myProperty", "test");
});
tpl.Namespace(myNamespace); // Sets a namespace on the module from a previously specified namespace template
tpl.Namespace("MyCustomNamespaceName", myNamespace); // Does the same as above, but uses the name passed instead of the namespace name
tpl.Namespace("MyOtherNamespace"); // Just creates an empty namespace on the module, this is used for namespaces which are only extended from JS bindings
alt.log(myTemplate.MyNamespace.myProperty); // Logs `test`
This sets a value on the template, which is retreived from a binding export specified in the JS bindings.
This can be of any type like a property, class or function, but the binding export name passed here has to be registered in a JS binding, otherwise this value will always be undefined.
Usage:
tpl.StaticBindingExport("myBinding", "myBindingExport:myValue"); // Sets the value of the `myBindingExport:myValue` binding export
TODO
When appropriate, bindings can be automatically generated from the C++ method itself.
These automatic bindings can only be generated for classes from the alt:V C++ SDK and their respective JS classes.
To add such a method or property it looks like this: (Example taken from the Player
class in shared)
tpl.Property<&alt::IPlayer::GetHealth>("health");
To add this kind of binding, there is no other code needed than that single line, because the C++ function will automatically be wrapped and the arguments will be converted from C++ to JS when calling it and back to JS when returning a value.
For special cases, like overloads for example it is still needed to write manual bindings, but as this is only rarely the case
most methods and properties can be added to the API via these automatic bindings. The implementation of these automatic bindings
can be found in shared/helpers/CallContext.h
.
Very simply put, the argument and return types are extracted from the function pointer passed as a template and will be converted to
their C++ / JS counterpart when called and call the respective method on the this
object of the object it is called on.
TODO
TODO
TODO
TODO
TODO
TODO
To deprecate a function, property or any other API, use the ctx.Deprecate(...)
function.
Inside the message explain why it is deprecated and/or which API should be used instead.
Example:
static void MyFunction(js::FunctionContext& ctx)
{
ctx.Deprecate("MyFunction is deprecated due to being unsafe. Consider using MyOtherFunction.");
if(!ctx.CheckArgCount(1)) return;
// ... and so on
}
Which will result with this warning when calling the function:
This API is deprecated: MyFunction is deprecated due to being unsafe. Consider using MyOtherFunction.