v5.0.0 Changes
Written on March 27, 2023
I think it's time to start explaining all of the changes that have happened in V5, and how those changes effect Athena. There is a lot to cover and I actuall may even miss a few changes because there are just that many changes.
I will be going through the CHANGELOG.md
file and trying to explain each section and what the reason was behind it.
The first commit for Version 5 was on December 12, 2022
, this entire change took almost 3 months to complete.
Breaking Changes
Keep Reading
At first this is going to look like the framework is going backwards, but these major breaking changes tell a larger story.
- Removed Inventory
- Removed Equipment
- Removed Toolbar
- Removed Storage System
- Removed Vehicle Keys
- Removed Holograms
- Removed Camera Target
- Removed Item Interfaces
All V4 plugins are not working in V5.
They will likely not be updated, as I have no interest in maintaining 30 plugins anymore. I will focus solely on the framework.
API Imports Changed
This effects all plugins, and all code.
import * as Athena from '@AthenaServer/api';
import * as AthenaClient from '@AthenaClient/api';
Production Upgrade Pathway
WARNING
This only applies to you if you want to maintain player data
If you are not in production I highly recommend just wiping the database and going for the full upgrade to V5.
However, if you are in production and still want to find an upgrade pathway... it will be difficult but doable.
Create a list of all player owned vehicles. Store it somewhere.
Create a script that reads the list.
Use the
Athena.vehicle.add
pathway to re-create vehicles.
Create a basic list of the items that were stored in each players inventory. You want
dbName
andquantity
.Wipe the
items
collectionWipe the
inventory
,equipment
, andtoolbar
of all players.Convert all items to BaseItems in the
Athena.systems.inventory.factory
functions.Turn off weight checks in
Athena.systems.inventory.config
Use the
Athena.player.inventory.add
function to add by dbName, and quantity.
Storages
Anything that has a
storage
should be given to the original owner. Create a list of who owns what, and add it to the user.Wipe the entire
storages
collection.
permissionLevel
Permission Level is no longer a thing. We now use an array of
permissions
which contains a list ofstring
values.The two permissions that come with Athena are
admin
andmoderator
. Almost everything requiresadmin
rights by default.
Ares Service
- The original Ares service is dead. The Discord plugin was updated to already use the new system.
Interactions
All interactions now use SHIFT + E
by default.
This is to prevent collisions with default in-game controls.
Default Systems
Default Systems are a new section of the systems
folder under src/core/systems
.
Inside you will find a few general systems that can easily be disabled by following this API Pathway: Athena.systems.defaults.X
.
You should disable these default systems by creating your own plugin, and invoking the disable function.
Here are a few default systems that come with Athena now.
ammo
- An item called 'ammo-box' is added.
- The ammo box can be used on a weapon to add ammo.
- Weapons track some ammo; albeit not a complete system yet.
- Can be disabled
clothingCrafting
- Take two clothing items and combine them.
- Then take the single clothing item and combine it with more items.
- The items that are combined are essentially 'sewn' together.
- Can be disabled
death
- A very simple death system.
- It just respawns a player after 5 seconds.
- Can be disabled
displayId
- Shows your ID in the top-right of the screen
- Can be disabled
hospitalBlips
- Adds all hopsital locations to the map
- Can be disabled
inventorySync
- Whenever inventory or equipment is updated, it synchronizes it to the client.
- Can be disabled
time
- Time progresses based on server time.
- Whatever time it is where the server is located is what time it is in-game.
- Can be disabled
toolbar
- Handles invoking item functionality when press 1-4
- Can be disabled
vehiclesDespawnOnDestroy
- When a vehicle is destroyed, it is removed from the world after 60s
- Can be disabled
vehiclesDespawnOnLeave
- When a character leaves the server, so does all their vehicles.
- Can be disabled
vehiclesSpawnOnJoin
- When a character joins the server, so does all their vehicles.
- Can be disabled
weaponItems
- All weapons have a name and item created to represent them.
- They can be spawned through the add item functionality.
- These items are automatically added to the database.
- Can be disabled
weather
- Weather rotates throug a specific weather pattern for the whole map.
- The weather pattern can be changed through a function.
- Can be disabled
Inventory Rewrite
Why?
The old inventory was one of the more legacy things that existed in Athena. It had been around forever, and it was impossible to change without changing the core functionality. Explaining how it all worked was a mystery in itself.
How?
The new inventory system has two levels of API. One is the newbie programmer API which is very easy to use, it's meant for adding / removing items and maybe sometimes checking if the user has an item.
Key Notes
Athena.player.inventory
functions all save automatically.- Items should not be modified directly.
- New lower level inventory functions are input and output based. Meaning if you feed it an array, you will get an array back. Nothing is changed for a player until it is set through the
Athena.document.character
API. - Always do your inventory updates all at once, never delay inventory changes.
Player API Examples
const didAdd = await Athena.player.inventory.add(somePlayer, { dbName: 'burger', quantity: 1, data: {} });
const didSubtract = await Athena.player.inventory.sub(somePlayer, { dbName: 'burger', quantity: 1 });
const didRemove = await Athena.player.inventory.remove(somePlayer, someSlot);
const itemClone = await Athena.player.inventory.getAt(somePlayer, someSlot);
const hasEnough = Athena.player.inventory.has(somePlayer, 'burger', 5);
Item Differences
StoredItem vs BaseItem vs Item
BaseItem
- A single instance of general item information.
- Usually contains generic information to establish what the item does.
- This can include item effects.
- The behavior of the item can be generated here as well.
StoredItem
- The smallest version of an inventory item.
- Often stored in the player document, or other storage interfaces.
- This is the type you will come across frequently.
- Can contain unique data specific to that item instance only.
Item
- Distributed to the client at runtime with all item information when they open their inventory.
- You will almost never create this one.
- This is a combination of both BaseItem and StoredItem
- The 'data' from the BaseItem is assigned to the StoredItem
tldr - Base Item is the factory template. Stored item is the minted item.
You can also use custom data on all of these interfaces
const item = StoredItemEx<{ valueInData: string }> = {
...
data: {
valueInData: 'hi!'
}
}
const item = BaseItemEx<{ valueInData: string }> = {
...
data: {
valueInData: 'hi!'
}
}
State Rewrite
Why?
If you used Athena.systems.state
before it was the way to update player data. It was clunky and sometimes a really dumb solution to a harder problem I was dealing with which is Prototyping.
You can read more about the why in this other blog post.
How?
The important thing about this API replacement is that it can now be found under Athena.document.character
and it is named in this way because we are modifying MongoDB documents that belong to a specific character
, vehicle
, or account
.
WARNING
If you modify the data from Athena.document.x.get
it will not be synchronized. It will not update. You MUST use the set
or setBulk
functionality to push the update to the player.
Key Notes
- Player Joins
- They Authenticate
- They are assigned an Account, and the Account Data is cached in memory for this session.
- They select a Character
- The are assigned a Character, and the Character Data is cached in memory for this session.
- They vehicles are created
- The vehicles are assigned an OwnedVehicle document, and the Vehicle Data is cached in memory.
All of this data is now easily accessible, and we use a write cache first, and output database changes after method.
This means that when we update character
, vehicle
, or account
data it is pushed to the in-memory cache, and the output to the Database as soon as possible. This keeps everything fluid while the rest of the server moves along.
Examples
await Athena.document.character.set(somePlayer, 'cash', 25);
await Athena.document.character.setBulk(somePlayer, { cash: 25, bank: 100 });
// Custom Interface Support
interface CharacterExtension {
newValue: string
}
let data = Athena.document.character.get<CharacterExtension>(somePlayer);
if (!data.newValue) {
await Athena.document.character.set<CharacterExtension>(somePlayer, 'newValue', 25);
data = Athena.document.character.get<CharacterExtension>(somePlayer);
}
Attachables Update
Attachables use the new alt.Object API which fixed some issues with removing the objects.
Object Controller Update
The Object API was rewritten to use the new alt.Object API which fixed issues with removing objects.
Also performance should be much better.
Currency
Currencies now support custom types, so you can create a new currency automatically by using the given functionality.
Athena.player.currency.add<'bitcoin' | 'ethereum'>(somePlayer, 'ethereum', 5)
Chat / Messenger System
Chat was replaced with a new messaging system.
The messaging system allows you to easily intercept all messages from a player when they are chatting.
The original chat input was replaced with RMLUI
which makes chat input very fast.
All of this is done through the core-chat
plugin.
If your language is more complex (I have no idea how to word this, Chinese, Japenese, etc.) then the input though RMLUI may not support your typing, consider replacing the RMLUI chat input with a CEF based one to fix the input.
It still supports these other languages, but you'll have to write your own chat input. The rest can be recycled though.
Here's an example on server-side
on how to re-route a message from a player.
import * as alt from 'alt-server';
import * as Athena from '@AthenaServer/api';
function handleMessage(player: alt.Player, msg: string) {
const data = Athena.document.character.get(player);
if (typeof data === 'undefined') {
return;
}
const closestPlayers = Athena.getters.players.inRange(player.pos, CHAT_CONFIG.settings.range);
Athena.systems.messenger.messaging.sendToPlayers(closestPlayers, `${data.name}: ${msg}`);
}
export function init() {
Athena.systems.messenger.messaging.addCallback(handleMessage);
}
Here's an example on client-side
on how to send a message or command.
import * as AthenaClient from '@AthenaClient/api';
// Goes up to the server as if the client were the one to type it.
AthenaClient.systems.messenger.send('Hello World!');
Permission System
Permission Level is no longer a thing. We now use an array of permissions
which contains a list of string
values.
The two permissions that come with Athena are admin
and moderator
. Almost everything requires admin
rights by default.
Permissions can be validated with a few different functions.
Athena.systems.permission.has<'custom-perm'>('character', somePlayer, 'custom-perm');
Athena.systems.permission.hasOne<'custom-perm1' | 'custom-perm2'>('account', somePlayer, ['admin', 'custom-perm1']);
Athena.systems.permission.hasAll('character', somePlayer, ['admin', 'moderator']);
Permissions can be added / removed easily as well.
type CustomPerms = 'perm1' | 'perm2' | 'perm3'
await Athena.systems.permission.add('character', somePlayer, 'admin')
await Athena.systems.permission.add<CustomPerms>('character', somePlayer, 'perm1');
await Athena.systems.permission.remove<CustomPerms>('character', somePlayer, 'perm2');
Athena.get
I had to replace this with getters
because of TypeScript limitations.
Anyway, you can find all the relevant APIs for getting various player info, ids, etc. under this pathway.
Vue Dev Menu
When you open http://localhost:3000
in the browser while using dev
mode you had to constantly close the panel to browse pages.
I've changed the dev menu to allow you to hide
the panel from showing, or simply Hide on Refresh
which lets you focus on what you are working on.
Additional instructions are there on how to bring the panel back before hiding it.
Inventory
The inventory UI was moved out of the core system.
It now exists as its own plugin which you can make a copy of, and make all of your appearance changes.
The thing is a monster, and it was really difficult to move it over to a plugin.
That being said, it's much more robust and offers tons of new functionality and even a context menu.
Item Factory
When you want to create an item, you create a base item.
Use the upsertAsync
function to pass that item to the database so 'copies' of that item can be made when specifying a matching dbName
and version
to a player to add that item.
When you want to add an item to a player, you use the manager or the Athena.player.inventory
API.
Item Manager
The entire item system was reworked to work in a very specific way.
This item manager is considered the advanced
portion of inventory management. If you want an easy to use function use Athena.player.inventory.x
or Athena.player.toolbar.x
.
Fundamentals
- The item manager saves nothing.
- The item manager does not auto-synchronize anything.
- The item manager does update data automatically for a player.
- Almost everything can return
undefined
that means that the data could not be set, or modified. When the data is returned correctly you get the modified data.
You still need to set that data through Athena.document.x.set
or Athena.document.x.setBulk
.
Here's an example of actually adding an item to a user through the advanced
API that is this manager.
Code Example
async function add(player: alt.Player, dbName: string, quantity: number): Promise<boolean> {
const data = document.character.get(player);
if (typeof data === 'undefined') {
return false;
}
const baseItem = Athena.systems.inventory.factory.getBaseItem(dbName);
if (typeof baseItem === 'undefined') {
return false;
}
if (typeof data.inventory === 'undefined') {
data.inventory = [];
}
const newInventoryData = Athena.systems.inventory.manager.add({ dbName, quantity, data: {} }, data.inventory, 'inventory');
if (typeof newInventoryData === 'undefined') {
return false;
}
if (Athena.systems.inventory.weight.isWeightExceeded([newInventoryData, data.toolbar])) {
return false;
}
await document.character.set(player, 'inventory', newInventoryData);
return true;
}
// This needs to be wrapped in an async function
const didAdd = await add(somePlayer, 'burger', 5);
Item Effects
Item effects are now handled a bit differently.
Items are not automatically removed when an effect is invoked.
Instead it's up to you to decide what to do with an item after it is used.
Code Example
Athena.systems.inventory.effects.add(
'edible',
async (player: alt.Player, slot: number, type: 'inventory' | 'toolbar') => {
const propertyName = String(type);
const data = Athena.document.character.get(player);
if (typeof data === 'undefined' || typeof data[propertyName] === 'undefined') {
return;
}
// This is an item reference; do not modify it directly.
const item = Athena.systems.inventory.slot.getAt<{ health: number }>(slot, data[propertyName]);
if (typeof item === 'undefined') {
return;
}
const index = data[propertyName].findIndex((x) => x.slot === slot);
if (index <= -1) {
// Item was not found with matching slot
return;
}
// Instead you can directly apply data changes...
Athena.systems.inventory.manager.setData<{ health: number }>(item, { health: item.data.health + 5 });
const didRemove = await Athena.player.inventory.sub(player, { dbName: item.dbName, quantity: 1 });
if (!didRemove) {
Athena.player.emit.notification(player, `Could not eat ${item.dbName}`);
return;
}
Athena.player.safe.addHealth(player, item.data.health);
Athena.player.emit.notification(player, `You ate 1 ${item.dbName}`);
},
);
Item Crafting
Item crafting is something new but it allows you to simply combine two items by left-clicking one item in the inventory, and trying to combine it with the other.
If a recipe exists, it will combine it.
Adding new recipes is even easier.
Athena.systems.inventory.crafting.addRecipe({
combo: ['burger', 'tomato'],
quantities: [1, 1],
uid: 'burger-with-tomato',
result: { dbName: 'burger-with-tomato', quantity: 1 },
});
Item Weapons
Weapons have their given data; but you can also modify a weapon to add weapon component information to it.
It must be stored under the data
section of the item though.
const someWeaponInSlot = 5;
Athena.systems.inventory.weapon.addComponent(somePlayer, 'inventory', someWeaponInSlot, 'COMPONENT_AT_PI_FLSH');
Clothing
We used to have an array called equipment
but this is completely gone now. Instead you can now equip items directly inside of your inventory.
This means that when you right-click and select use
in the context menu clothing is given an equipped
status.
This equipped
status is also available for other items as well.
By default the order in which your clothing is applied is from top to bottom, meaning that if something is in the very last slot of your inventory it is equipped last
.
I've also created various interfaces / functions to help build clothing items quickly.
Here are some of those functions...
Athena.systems.inventory.clothing.outfitFromDlc
// Use this one for clothing systems
// You just apply all the relative values on a player...
// Then you get the constructed StoredItem back.
const storeableItem = Athena.systems.inventory.clothing.outfitFromPlayer
Uniforms
These override clothes. They are applied over clothing.
They function like normal clothing, but are a uniform.
Athena.systems.inventory.clothing.setUniform
Athena.systems.inventory.clothing.clearUniform
Skins
Default Ped Skins, Animals, etc. are available through these APIs now as well.
Athena.systems.inventory.clothing.setSkin
Athena.systems.inventory.clothing.clearSkin
Hotkey Registry
All hotkey systems were removed and moved into a single standard
hotkey registration systep. This hotkey registration allows for rebinding hotkeys, as long as you register it through this system.
There is a ton of functionality built into this hotkey system now.
- Do something while pressed
- Do something on press down
- Do something on press up
- Bypass Menu Restrictions
- Disable One
- Enable One
- Spam Prevention
- Delay Key Down Function (Hold for X ms)
- Allow for specific page only
Here's a simple hotkey example for client-side.
const THE_LETTER_C = 67;
AthenaClient.systems.hotkeys.add({
key: THE_LETTER_C,
description: 'Do Something',
identifier: 'my-unique-key-identifier',
keyDown: () => {
console.log('Did it!')
},
keyUp: () => {
console.log('Did it!')
},
whilePressed: () => {
console.log('This is going to log A LOT')
},
modifier: 'shift'
});
Admin Commands
Added a lot of new commands, but these are the admin ones.
account or character
is the type
you are adding a permission to.
/addperm [account or character] [ingame-id] [permission] - Add permission to an account
/removeperm [account or character] [ingame-id] [permission] - Remove permission from an account
/getperms [account or character] [ingame-id] - Returns the current list of permissions for given type
Player API Changes
Athena.player.x
got a lot of changes in general. Lots of new pathways and such but here are a few that are worth mentioning.
Athena.player.appearance.setHair
Athena.player.appearance.setFacialHair
Athena.player.appearance.setEyebrows
Athena.player.appearance.setEyeColor
Athena.player.appearance.setModel
Notification System
Much like the messenger system, you can intercept the notifications on client-side and write overrides for it. This means you can write your own notification system instead of using the default GTA:V one.
Is that everything?
I do not know, but the CHANGELOG tells a larger story of this update.
There are tons of new functions, and useful things that were added to this changelog but still plenty missing from this post. This really just highlights some more significant changes to the Framework.
https://github.com/Stuyk/altv-athena/blob/alpha/5.0.0/CHANGELOG.md