alt:V Development Gotcha's
If you aren't aware. alt:V is a multiplayer client for GTA:V that allows up to 4,500+ players in a single game world to interact and play with each other. alt:V allows users to easily create custom game modes such as capture the flag, death match, racing, role-play, etc. You can check that out over at https://altv.mp/.
Well. I'm coming close to my first year developing on the alt:V platform. Let's talk about some the gotcha's and caveats that come with working on this truly ground breaking client in no specific order.
Webviews, CEF, etc.
If you aren't aware alt:V allows you to write in-game menus through an IPC process that has a built in CEF client that talks from the CEF client to the game itself. This can be done through alt.emit
and alt.on
events as long as alt
is present inside of CEF and the WebView
class is initiated and active on client-side.
Sending Data Too Early
Here's what most newbies to alt:V will do when they're working with CEF.
const myView = new alt.WebView('/client/index.html');
myView.emit('read:Data', 'Hello World');
Do you see the issue with this? If not then you are probably a victim of a gotcha. WebViews are not fully ready right away. Instead you need to wait for the DOM to be ready and send an event from CEF to the Client letting the client know the View is ready to receive some data.
Here's a generic solution.
app.js (Your CEF JavaScript)
document.addEventListener('DOMContentLoaded', (event) => {
if ('alt' in window) {
alt.emit('ready');
}
});
client.js (Your Clientside JavaScript)
const myData = 'Hello World'
const myView = new alt.WebView('/client/index.html');
myView.on('ready', () => {
myView.emit('read:Data', myData');
});
Loading Times
Here's another fun one for CEF. If you're using the WebView.destroy() event you may have noticed that each time you request to re-create that WebView it will take a little while to load.
Here's what you can do to increase your loading times for your views.
Create a singleton WebView wrapper that creates a single WebView instance. This instance will then be re-routed with the .url
property. Where you can assign a new page based on this property.
ie. webview.url = '/client/otherPage.html'
This will allow your CEF to quickly switch between pages without issue. Others have also recommended creating a Single Page Application (SPA). If you want to keep your loading times short and quick.
However, this solution allows me to keep everything separated without issue.
Sending Too Much Data Over IPC
This one is really hard to come by but it can happen depending on the size of the data you are attempting to send from client to view.
If you have a large JSON string of data such as a vehicle list with prices. You may want to reconsider sending that all over the IPC in a single shot. Instead of doing an event like setVehicleData
try parsing your JSON before sending it over then appending individual vehicles to that list in a handful of vents.
For example you could parse the JSON. Loop through all entries in the Array or Object. And do individual events such as appendVehicleData
which simply pushes a smaller amount of data through.
Other options include some form of data buffer that waits for a specific size to be reached before it is marked as complete.
Server Side
I'm a huge fan of what we can do with the things on server side but there's a few annoyances that come to mind that will actually catch most developers off guard. Especially with how alt:V is implemented. Let's talk about them.
Timeout or Interval Crashing
This one is fun to think about. I had a friend of mine write some code that used a Timeout. After he completed his code without even looking at it I told him to do exactly two things.
- Start the Timeout.
- Disconnect From the Server
- Crash the Server
Yup. That's exactly how you crash the server. Here's what you can do to get around this.
Instead of relying on the code inside of the Timeout or Interval. Create an alt.emit
event inside of it that calls a function. Here's an example.
function somethingThatHasAnEntity(player) {
setTimeout(() => {
alt.emit('do:Something', player);
}, 1000);
}
alt.on('do:Something', (player) => {
if (!player || !player.valid) {
return;
}
// do other stuff
});
The above code handles the issue with the entity potentially being invalid and forwards it through an event. If that event just so happens to not catch the player being valid it will no longer crash your server.
Import Statements Not Working
If you have never come across NodeJS before and this is your first time developing with it. You may not be aware that NodeJS 13+ supports import
rather than require
if you specify the type of module
inside of your package.json
.
package.json...
"type": "module",
Which is a bit of a caveat if you're not aware of NodeJS changes.
Note: The module type is a direct replacement for .mjs
files.
Note2: If you use VSCode this also fixes auto-completion and auto-import for files.
Socket Overflowing
Did you know that you can send too much data from Server Side to client side? Well we covered it in the CEF section. However, that still applies here. Make sure you're not sending massive amounts of data over the socket. There's a size limitation and larger data sets should be send in portions or through some form of a buffer function that waits for all data to be sent.
Client Side
Client side is where a lot of magic happens and allows for a ton of functionality to be seen.
setTimeout and setInterval
Did you know that alt has its own implementation of setInterval and setTimeout? I didn't. You should though because it's an important part of the development process for client side coding.
Normally you'd just run a normal timeout or interval.
setTimeout(() => {}, 1000);
setInterval(() => {}, 1000);
These DO NOT WORK on client side. You need to use the alt implementation of this.
alt.setTimeout(() => {}, 1000});
alt.setInterval(() => {}, 1000});
They function exactly the same and even have a returnable number so you can clear that timeout or interval. For example...
let count = 0;
const interval = alt.setInterval(() => {
count += 1;
if (count >= 10) {
alt.clearInterval(interval);
}
}, 1000);
After it counts to 10. It'll remove the interval and stop it from running. I highly recommend that anyone who is using intervals, timeouts, etc. make sure they are not running if they are not necessary.
An alternative to the above would be using Date.now()
variables with MS offsets and using alt.everyTick()
to determine how often you want to update data. Which is also another decent solution.
Using Non-Existent Functions
Did you know that using a non-existant function on client-side in alt.on
will immediately crash your client? Me neither. I found this out recently. If you're trying to figure out what is crashing your client. Take a look at your client.log
after crashing and you'll probably find a broken function.
Using Non-Existent Files
Did you know that importing a non-existent file on client-side will crash your client? Me neither. I found this out by deleting a couple of files and all of a sudden my game started crashing.
This can be resolved by looking at your client.log
to see what files it is trying to import and then removing the imports for those non-existent files.