Один из способов работы с UI клиента, с помощью CEF(HTML), на примере модального окна со списком игроков(ник, ip, ping)
Дабы не использовать хостинг, развернем веб-сервер прямо в игровом сервере. Начнём. Создаем в каталоге 'packages/keker' папку 'ui' (в ней будут лежать клиентские файлы) и файл 'web.js', и объявляем его в 'packages/keker/index.js' добавив туда строку:
require('./web');
В 'web.js' вставляем этот код:
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const mimeType = { // mime типы по расширениям файлов
'.ico': 'image/x-icon',
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json',
'.css': 'text/css',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.eot': 'appliaction/vnd.ms-fontobject',
'.ttf': 'aplication/font-sfnt'
};
http.createServer(function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*'); // разрешаем кросс-деменые запросы
let parsedUrl = url.parse(req.url); // отсекаем от url все лишнее
let filePath = __dirname+'/ui' + parsedUrl.pathname; // Парсим url в путь к файлу
let ext = path.extname(filePath); // получаем расширение файла
if(req.url=="/api/players_list.json"){ // отдельная ссылка для генерации JSON списка игроков
let pl = {
online: mp.players.length,
slots: mp.players.size,
players: []
}
mp.players.forEach(player => {
pl.players.push({ name: player.name, ip: player.ip, ping: player.ping });
});
res.writeHead(200, { 'Content-Type': mimeType['.json'] });
res.end(JSON.stringify(pl), 'utf-8');
} else {
fs.readFile(filePath, function(error, content) {
if (error) {
if(error.code == 'ENOENT'){ // если файл отсутсвует
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('404 Not Found');
}
else { // другие ошибки
res.writeHead(500);
res.end('Error: '+error.code+' ..\n');
}
}
else {
res.writeHead(200, { 'Content-Type': mimeType[ext] || 'text/plain' });
res.end(content, 'utf-8');
}
});
}
}).listen(8080); // вешаем наш веб сервер на свободный порт, у меня это 8080
Он создает веб-сервер для выдачи статики из нашей папки 'ui'.
В папку 'ui' добавляем наш HTML и CSS и JS файлы, HTML и CSS комментировать не буду, тут у всех на свой вкус
players_list.html:
<div class="p-list">
<p>Игроков онлайн: 0/100</p>
<table id="pl-table" border="0" cellpadding="5">
<tr>
<th>Имя:</th>
<th>IP:</th>
<th>Ping:</th>
</tr>
<tr><td>Nick</td><td>0.0.0.0</td><td>0</td></tr>
</table>
</div>
main.css:
.p-list {
cursor: none;
position: absolute;
top: 20%;
left: 20%;
width: 60%;
height: 60%;
background: rgba(96,125,139,0.8);
text-align: center;
align-content: center;
color: #eee;
font-size: 18px;
overflow-y: scroll;
border: 1px solid #eee;
border-radius: 5px;
display: none;
}
.p-list p {
padding: 8px 0px;
}
.p-list table {
width: 100%;
color: #eee;
}
.p-list tr {
text-align: center;
}
.p-list td {
border-top: 1px solid #eee;
width: 30%;
}
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #37474F;
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
}
Туда же добавим и JS файл который будет отвечать за функционал. JQuery у нас уже подключен, его можно спокойно использовать.
main.js:
const addr = "http://127.0.0.1:8080/"; // Здесь обязательно задаем адресс вашего сервера и порт на котором висит веб
var pl_enable = false; // флаг проверки открыто/скрыто окно со списком игроков
var css_el = document.createElement("link"); // создаем элемент подключающий css
css_el.rel = "stylesheet";
css_el.href = addr+"main.css";
$("head").append(css_el);// подключаем его в head
$.get( addr+"players_list.html", function( data ) { // загружаем HTML нашего модального окна
$("body").append( data ); // вставляем его в body
});
$("body").keydown(function( e ) { // событие нажания на кнопку
if(e.which == 9){ // 9 KeyCode кнопки TAB
if(pl_enable){ // если модальное окно открыто скрываем его
pl_enable = false;
$('.p-list').hide();
} else {
pl_enable = true;
$.getJSON( addr+"api/players_list.json", function( data ) { // получаем список игроков
$(".p-list p").html("<b>Игроков онлайн: "+data.online+"/"+data.slots+"</b>"); // кол-во онлайна
let str = "<tr><th>Ник:</th><th>IP:</th><th>Ping:</th></tr>";
for(let i = 0; i < data.players.length; i++){
str += "<tr><td>"+data.players[i].name+"</td><td>"+data.players[i].ip+"</td><td>"+data.players[i].ping+"</td>";
}
$("#pl-table").html(str); // запихиваем список в модальное окно
});
$('.p-list').show(); // делаем его видимым
}
mp.invoke("focus", pl_enable); // отключаем игровой процесс делаем видимым курсор (он лагучий и находится под меню)
//курсор можно накатать свой но нам это пока не надо
}
});
Теперь нужно отослать наш скрипт на исполнение игроку, я это сделал в 'common.js' на событие о подключении игрока:
player.outputChatBox("<script src='http://127.0.0.1:8080/main.js'></script>"); // указываем ваш адрес сервера и веб порт
/*кавычки только так, снаружи двойные внутри одинарные, по другому не сработало,
если у вас обьем кода больше не забывайте экранировать кавычки */
Ну вот и все, теперь при нажатии на TAB в игре, должно открыться модальное окно со списком игроков.
Так же в папку 'ui' можно полностью перенести стандартное меню и изменять уже его, переход на него будет выполнятся так:
player.outputChatBox("<script>document.location.href = 'http://127.0.0.1:8080/index.html';</script>"); // не забываем подставить свой адрес и порт
Так же в клиентских скриптах можно использовать этот метод для отправки команд на сервер:
mp.invoke("command", "команда агрумент1 агрумент2 ...");
Веб-сервер выдает большинство mime типов так что можно подключать всякие фреймворки типа Bootstrap, Angular, React и прочие плюшки.