Why Create Overrides?
Written on March 11, 2023
Over the course of several years while writing the Athena Framework there was something developers were doing that was against the fundamentals of this framework.
That was to not change the core code base when using it.
When a developer changes the core code and they are not familiar with merge conflicts, it can become difficult to upgrade to newer versions of Athena which may include better performance, cleaner code, or simply put a better API to work with.
What did I try, and why it didn't work
Let's talk about what I tried to do first while writing this framework.
In V4
I had introduced a concept called Injections
.
Injections were meant to be a way for developers to add small pieces of data into the core functions. The injection code itself would be added to individual functions based on a developer saying hey can I get an injection added here. Now you can understand how quickly the amount of injections being added would spiral out of control and become incredibly messy.
Injections were a pathway to spaghetti code, and awful understanding of how they work.
They were also quite limiting when I am trying to decrease limitations while using this framework.
Why are they awful?
The injections themselves were described as follows:
- Add functionality to the beginning of a function
- Add functionality to the end of a function
However, this limiting functionality would also prevent you from making any additional changes to anything that happened in between.
You were effectively writing code that would work backwards to potentially remove whatever was happening inside of that function.
Where are we going?
Injections are removed in V5
and have now be replaced with Overrides
instead.
Overrides allow you to take the original function and essentially fork it in your own plugin.
This allows you to fully change the function WITHOUT EVER CHANGING CORE.
This is important because now you can write a plugin that overrides a specific function to extend it even further beyond its current capabilities.
How does it work?
In the Athena API pathway you will find a function called override
in almost every file. This one function will let you override any other function that is inside of that specific file.
Let's take for example the following function from src/core/server/vehicle/spawn.ts
.
This function creates a temporary vehicle
that can be despawned on leave if desired.
This vehicle can be accessed by anyone, and remains in the server until restart or despawn.
export function temporary(vehicleInfo: VehicleSpawnInfo, deleteOnLeave = false): alt.Vehicle {
if (Overrides.temporary) {
return Overrides.temporary(vehicleInfo, deleteOnLeave);
}
const vehicle = new alt.Vehicle(vehicleInfo.model, vehicleInfo.pos, vehicleInfo.rot);
vehicle.manualEngineControl = true;
Athena.vehicle.tempVehicles.add(vehicle, { deleteOnLeave });
if (vehicleInfo.data) {
Athena.vehicle.tuning.applyState(vehicle, vehicleInfo.data);
}
Athena.vehicle.events.trigger('vehicle-spawned', vehicle);
return vehicle;
}
Now let's say that I want to modify this function and log all temporary vehicles that are created.
I can do this by using an override
on the function itself.
Athena.vehicle.spawn.override('temporary', (vehicleInfo, deleteOnLeave) => {
})
After introducing this override, I can now add the additional logging information by modifying the original function.
I am essentially performing a copy
and paste
of the original logic inside of this function and then changing it.
This is done entirely through a Plugin
at this point.
Athena.vehicle.spawn.override('temporary', (vehicleInfo, deleteOnLeave) => {
// ALWAYS REMOVE THE OVERRIDE AT THE TOP
const vehicle = new alt.Vehicle(vehicleInfo.model, vehicleInfo.pos, vehicleInfo.rot);
vehicle.manualEngineControl = true;
Athena.vehicle.tempVehicles.add(vehicle, { deleteOnLeave });
if (vehicleInfo.data) {
Athena.vehicle.tuning.applyState(vehicle, vehicleInfo.data);
}
// This is what I wanted to add
alt.log(`A temporary vehicle with ${vehicleInfo.model} was created!`);
Athena.vehicle.events.trigger('vehicle-spawned', vehicle);
return vehicle;
})
That's it! That's how I solved it.
Now when you run the main function Athena.vehicle.spawn.temporary
it will call the Override
function instead of the main function.
You can now override almost any function in the framework in V5
and this lowers the time necessary to upgrade to the next version because as long as you follow the one core rule which is; do not modify core and make a plugin instead.
Then your upgrade time will be very quick.