Спавна игрока не происходит до того момента, как вы сами его не заспавните.
Когда игрок подключается на сервер он появляется в дефолтной точке у гаража в городе и запускается ивент PlayerJoin.
Объясню как это работает у меня. (объясняю учитывая то, что ты уже знаешь как работает клиент-сервер и CEF)
При подключении игрока на сервер начинают свою работу 2 "главных" файла - index.js сервера и index.js клиента.
1. В клиенте я сразу замораживаю игрока, отключаю HUD, вызываю функцию браузера, которая отобразит форму авторизации, (заранее созданную(HTML+CSS+JS))
menu.execute(`showLoginForm('${mp.players.local.name}');`);
mp.players.local.freezePosition(true);
mp.game.ui.displayCash(false);
mp.game.ui.displayHud(false);
mp.game.ui.displayRadar(false);
mp.gui.chat.activate(false);
mp.gui.chat.show(false);
2. Игрок вводит свои данные в поля и нажимает кнопку авторизации.
3. После проверок на заполнение полей и прочего, клиент отправляет на сервер запрос в виде объекта типа
var data = {
type: 'auth',
login: login,
password: password
}
mp.events.callRemote('clientData', JSON.stringify(data));
4. Сервер, получив запрос с данными игрока проверяет, есть ли такой игрок в базе, верен ли пароль и в случае успешной проверки отправляет запрос клиенту (у меня опять же в виде объекта)
var data = { // в случае успешной авторизации
type: 'success'
}
player.call("serverData", JSON.stringify(data));
player.spawn(...);
// или
var data = { // в случае неверно введенного пароля
type: 'error',
errorType: 'wrongPass'
}
player.call("serverData", JSON.stringify(data));
При получении ответа от сервера на клиенте ты либо закрываешь форму авторизации либо даешь уведомление о неверно введенных данных.
В случае успешной авторизации размораживаешь игрока, включаешь HUD и спавнишь а где посчитаешь нужным.
P.S. Объясняю я наверное не лучшим образом, но объяснил чисто последовательность действий. Если что-то не ясно - задавай.