• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Продолжение написания крипто игры на ue5 (Работа с запросами и шифрование AES)

OverlordGameDev

RAM
Пользователь
Регистрация
14.07.2024
Сообщения
106
Реакции
131

Предисловие:​

Данная статья продолжает пошаговую инструкцию по разработке онлайн крипто-игры в стилистике MMORPG. В этой части будет показано, как реализовать сервер регистрации на Python с шифрованием паролей в базе данных. Приведённый ниже код отлично подойдёт для других проектов, таких как сайты и приложения а также использование игрового движка как инсталлер ваших файлов.

Кроме того, в статье будет объяснено, как работать с JSON: создавать JSON-объекты и читать их. Также будет рассмотрен процесс отправки запросов с клиента на сервер и обратно — это решение также можно использовать в других проектах, не связанных с геймдевом.

Помимо этого, в статье будет описана реализация онлайн-составляющей, включая настройку выделенного сервера (dedicated server), синхронизацию действий разных клиентов на одном сервере.

Прежде чем приступить к работе с мультиплеерной составляющей, необходимо выбрать, как будет организован мультиплеер. Существует два основных подхода:

  1. Первый вариант — это когда один из клиентов одновременно выступает и как клиент, и как сервер, а остальные устройства подключаются к нему как клиенты. Основной недостаток этого метода заключается в том, что если клиент, выполняющий роль сервера, выйдет из игры, то все остальные клиенты также потеряют соединение. Этот подход лучше всего подходит для кооперативных игр с друзьями в лобби.
  2. Второй вариант — это использование выделенного сервера (dedicated server), когда сервер запускается как отдельная программа, а все клиенты подключаются к нему. Этот метод более надежен и лучше подходит для проектов, рассчитанных на многопользовательский режим, таких как наш проект.
Для текущего проекта единственным подходящим способом является использование выделенного сервера (dedicated server).

Основы работы с dedicated server​

Для тестов внутри движка пока что не потребуется скомпилировать отдельный сервер, и его можно сразу запустить через Unreal Engine. Для этого нужно перейти на игровую сцену, выбрать сетевой режим и указать количество запущенных клиентов.
1725194203080.png

Если на данном этапе запустить игру, то откроются два окна с клиентами, и передвижение, а также анимации передвижения будут реплицированы по умолчанию, так как передвижение осуществляется с использованием функции Add Movement, которая уже реплицирована.

Что такое репликация​

Если говорить простыми словами, репликация — это процесс, при котором действие, выполненное на одной системе, повторяется на другой системе. Таким образом достигается синхронизация между сервером и клиентами. Клиент выполняет действие и получает его результаты, сервер повторяет это действие и получает те же результаты, а затем сервер вызывает мультикаст для распространения действия на остальных клиентов, чтобы у них были те же результаты.

Что такое мультикаст​

Это один из видов репликации, позволяющий вызвать функцию на всех подключенных клиентах. Данный тип репликации может быть вызван только сервером.

В данный момент практически всё управление персонажем реплицировано по умолчанию, остаётся лишь реализовать репликацию поворота на месте и наклона спины за камерой вверх и вниз. Для начала можно рассмотреть простой график, который объяснит, как будет подключаться репликация:
1725194233396.png

Синхронизация действий клиентов​

Теперь можно приступить к практическому применению полученных знаний. Для начала нужно перейти в BluePrint персонажа, в котором выстроена основная логика перемещения. Далее нужно найти блок, где реализована логика поворота мышью. Рядом с ним нужно вызвать кастомные события (Custom Event); их потребуется два, и назвать их можно как SERVER и MULTICAST.
1725194255850.png

В событии сервера нужно установить свойство копирования события с клиента на сервер. Так как событие поворота также передаёт переменную типа float, в кастомный ивент нужно добавить слот для хранения этой переменной.
1725194276986.png

1725194299485.png


В событии мультикаста нужно установить свойство вещания, то есть мультикаст. Также нужно добавить слот под float переменную, значения которой поступают из ивента InputAxis LookRL. Эти значения будут передаваться от клиента к серверу, а затем от сервера к мультикасту.
1725194325130.png

Теперь нужно подключить ивент поворота к SERVER, затем SERVER к MULTICAST, а на мультикасте выполнить основную логику.
1725194342938.png


По аналогии нужно реализовать репликацию и для поворота на месте.
1725194364447.png

Сервер регистрации на Python​

Теперь можно начать этап регистрации игроков и авторизации в игре. Первым делом будет реализована регистрация игрока; сервер регистрации будет написан на Python Flask. На данный момент сервер регистрации будет представлять собой простую страницу в браузере для ввода логина и пароля, а также для записи этих данных в базу данных SQLite.

Подготовка проекта​

Для начала нужно подготовить проект сервера регистрации. Для этого потребуется основная папка, в которой будет находиться файл main.py. В этой папке также должна быть папка templates, а в папке templates — два HTML-файла: registration.html и profile.html.

Так как проект будет использовать веб-фреймворк Flask, нужно установить Flask командой pip install flask. Кроме того, сервер будет работать с базой данных SQLite, и для работы с ней можно установить дополнительный модуль Flask под названием flask-sqlalchemy.
Python:
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy

Теперь нужно инициализировать Flask и SQLAlchemy, указать путь к базе данных, а также задать ключ для шифрования куки-сессий Flask, в которых хранятся данные, такие как информация об авторизации.
Python:
app = Flask(__name__)
app.secret_key = 'supersecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

Теперь нужно определить структуру таблиц в базе данных (в скобках указаны свойства, такие как ограничения количества символов и тип записываемых данных).
Python:
class User(db.Model):
   id = db.Column(db.Integer, primary_key=True)
   username = db.Column(db.String(80), unique=True, nullable=False)
   password = db.Column(db.String(500), nullable=False)
P.S. Максимальное количество символов в пароле такое большое для того, чтобы в дальнейшем хранить зашифрованные пароли, так как зашифрованные пароли гораздо длиннее обычных.

Также нужно определить маршруты для страницы авторизации и регистрации, а также для страницы профиля.
Python:
@app.route('/', methods=['GET'])
def registration():
   return render_template('registration.html')


@app.route('/profile')
def profile():
   return render_template('profile.html')

И так как данные будут шифроваться, а именно на текущий момент будет шифроваться только пароль, понадобится функция для шифрования и расшифровки.

Работа с шифрованием и расшифровкой​

Для шифрования будет использоваться метод AES-128.

Как работает шифрование AES​

Данный метод очень популярен и используется во многих программах; например, в браузерах данные шифруются именно таким образом. Этапов шифрования данным методом достаточно немного. Сначала берутся исходные данные, затем выбирается режим шифрования данных (в данном случае будет использован режим CBC), после этого выбирается ключ для шифрования. Далее берутся данные для шифрования, и на выходе получаются два объекта: зашифрованные данные и вектор инициализации (IV).

Что такое CBC​

Это один из режимов шифрования, который позволяет шифровать одинаковые блоки данных, получая при этом разные вариации зашифрованных данных. Это улучшает защиту, так как невозможно расшифровать данные методом перебора. При использовании данного метода результатом работы будут не только зашифрованные данные, но и вектор инициализации (IV).

Что такое IV​

IV — это набор случайных данных, которые добавляются к чистым незашифрованным данным, чтобы каждый раз результат шифрования был разным.

Написание логики сервера регистрации​

Для начала нужно установить и импортировать необходимые библиотеки. Для этого будет использоваться библиотека pycryptodome.

P.S. Существует также библиотека pycryptodomex, которая по сути является той же библиотекой, но по неизвестным причинам она не подходит. Поэтому важно не перепутать и установить правильную библиотеку. Если будут установлены обе, они могут конфликтовать, и ничего работать не будет, что потребует удаления лишней библиотеки.
Python:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

После импортирования библиотек нужно создать переменную, в которой будет храниться ключ для шифрования пароля (в дальнейшем шифроваться также будут криптокошельки, но для них будет использоваться отдельный ключ для большей безопасности).

P.S. Ключ для шифрования должен иметь размер 16, 24 или 32 байта. Для AES-128 используется 16-байтовый ключ.
Python:
KEY_PASSWORD = b'1234567887654321'

Функция шифрования​

Теперь нужно написать функцию для шифрования данных, которая будет принимать один аргумент, а именно данные для шифрования.
Python:
def encrypt(data):
cipher = AES.new(KEY_PASSWORD, AES.MODE_CBC)
ct_bytes = cipher.encrypt(pad(data.encode(), AES.block_size))
iv = base64.b64encode(cipher.iv).decode('utf-8')
ct = base64.b64encode(ct_bytes).decode('utf-8')
return iv, ct
1 строка: KEY_PASSWORD — это ключ для шифрования и расшифровки, AES.MODE_CBC — это режим шифрования.

2 строка: cipher.encrypt — это вызов объекта с параметрами для шифрования и само шифрование. pad — это добавление байтов к нешифрованному паролю, чтобы он состоял из блоков данных, каждый из которых равен 16 байтам; это можно назвать выравниванием пароля. data.encode() — это преобразование полученных данных из переменной в байты. AES.block_size обеспечивает правильное дополнение нешифрованного пароля, чтобы все блоки были по 16 байт.

3 строка: base64.b64encode — это конвертация в формат base64. cipher.iv — это вектор инициализации, который будет конвертирован. decode('utf-8') конвертирует байтовую строку в обычную.

4 строка: Аналогично третьей строке, в этой строке происходит конвертация уже самих зашифрованных данных.

Функция расшифровки​

Теперь потребуется функция для расшифровки, которая будет принимать два аргумента: IV и зашифрованные данные.
Python:
def decrypt(iv, encrypted_data):
   iv = base64.b64decode(iv)
   ct = base64.b64decode(encrypted_data)
   cipher = AES.new(KEY_PASSWORD, AES.MODE_CBC, iv)
   pt = unpad(cipher.decrypt(ct), AES.block_size)
   return pt.decode()
1 и 2 строка: IV и зашифрованные данные декодируются из base64 обратно в их исходный вид.

3 строка: Создаётся объект шифрования с ключом расшифровки (KEY_PASSWORD), режимом шифрования (AES.MODE_CBC) и IV.

4 строка: Расшифровка ct (зашифрованные данные). unpad(..., AES.block_size) означает удаление лишних байтов, которые были добавлены для выравнивания блоков при шифровании данных, чтобы каждый блок был ровно по 16 байтов.

5 строка: Возвращает расшифрованные данные и переводит их из байтов в строку.

Написание веб части​

Теперь можно приступить к созданию страницы регистрации и авторизации. HTML-файл был подготовлен ранее и называется registration.html. В нём нужно будет создать форму с полями ввода и кнопкой для отправки данных. Так как при создании HTML-файла через PyCharm заготовка страницы создаётся автоматически, в файле сразу будет этот код:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Registration</title>
</head>
<body>


</body>
</html>

Первым делом будет реализована форма для регистрации, использующая метод POST. Это означает, что данные из формы будут отправляться POST-запросом в функцию, которая будет обрабатывать полученные данные. В данном случае такой функцией является register_user.

P.S. При нажатии на кнопку с типом submit данные из формы отправляются на адрес register_user.
HTML:
<form method="POST" action="{{ url_for('register_user') }}">
   <label>Логин:</label>
   <input type="text" name="username" required>
   <br>
   <label>Пароль:</label>
   <input type="password" name="password" required>
   <br>
   <input type="submit" value="Зарегистрироваться">
</form>

Точно так же нужно реализовать отправку данных для авторизации. Для этого нужно создать форму с полями ввода, но отправка будет происходить в функцию login_user.
HTML:
<form method="POST" action="{{ url_for('login_user') }}">
   <label>Логин:</label>
   <input type="text" name="username" required>
   <br>
   <label>Пароль:</label>
   <input type="password" name="password" required>
   <br>
   <input type="submit" value="Войти">
</form>

Также на страницу будут выводиться сообщения с Python-части сервера об удачных и неудачных действиях, таких как успешная расшифровка пароля, удачная регистрация и т.д.
Python:
{% with messages = get_flashed_messages() %}
   {% if messages %}
       <ul>
           {% for message in messages %}
               <li>{{ message }}</li>
           {% endfor %}
       </ul>
   {% endif %}
{% endwith %}
В данном коде вызывается функция get_flashed_messages() для получения flash-сообщений, хранящихся в хранилище сообщений, и результат вызова записывается в переменную messages. После этого идёт условие if, которое срабатывает, если в переменной messages есть сообщения, и выводит их на странице. Внутри if вызывается цикл for, в котором перебираются все сообщения из переменной messages и вставляются в переменную message. Затем выводится значение из переменной message прямо на страницу.

Также нужно подготовить страницу профиля. В данный момент она будет представлять собой пустую страницу с выводом приветственного текста. Файл данной страницы был также ранее заготовлен и называется profile.html.
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Profile</title>
</head>
<body>
   <h2>Добро пожаловать на страницу профиля!</h2>
</body>
</html>

Python функции​

Теперь нужно реализовать две функции. Одна будет предназначена для обработки данных из формы регистрации, а вторая — для обработки данных из формы авторизации.

Регистрация​

Первой на очереди будет функция регистрации. Первое, что нужно в ней сделать, это принять данные из формы и назначить полученные данные переменным.
Python:
@app.route('/register_user', methods=['POST'])
def register_user():
   username = request.form['username']
   password = request.form['password']

Далее нужно проверить полученный логин на совпадение в базе данных. Если такой логин уже присутствует, необходимо вывести уведомление об этом на веб-странице (ранее в файле registration.html был написан код для отображения подобных уведомлений). После вывода уведомления происходит редирект на страницу регистрации.
Python:
existing_user = User.query.filter_by(username=username).first()
if existing_user:
   flash('Аккаунт с таким логином уже существует. Попробуйте другой логин.')
   return redirect(url_for('registration'))
P.S. flash — это функция во Flask, которая сохраняет сообщение в специальном хранилище для сообщений внутри сессии. После отображения сообщения оно удаляется из хранилища.

Далее, если логин в базе данных не обнаружен, следует использовать этот код:
Python:
iv, encrypted_password = encrypt(password)
new_user = User(username=username, password=f'{iv}:{encrypted_password}')
db.session.add(new_user)
db.session.commit()
return redirect(url_for('profile'))
1 строка: Функция encrypt принимает пароль, полученный с веб-страницы, и возвращает значения iv и зашифрованный пароль. Эти значения сохраняются в переменные iv и encrypted_password.

2 и 3 строка: Добавление в базу данных полученного с веб-страницы логина и запись в столбец password значения iv и зашифрованного пароля, разделённых двоеточием, полученных из функции шифрования.

4 строка: Сохранение всех изменений в базе данных.

5 строка: После сохранения данных в базе данных происходит редирект на страницу profile.

Авторизация​

Теперь будет показана реализация функции авторизации. Первое, что нужно сделать, это также добавить переменные, в которые будут записываться логин и пароль из формы веб-страницы, а также провести проверку на наличие введённого логина в базе данных.
Python:
@app.route('/login_user', methods=['POST'])
def login_user():
   username = request.form['username']
   password = request.form['password']


   user = User.query.filter_by(username=username).first()

Далее идет условие if: если логин найден, происходит расшифровка пароля из базы данных.

P.S. Возможно, у кого-то возникнет вопрос, почему бы не зашифровать полученный пароль и сравнить их в зашифрованном виде. Это невозможно, потому что при написании функции шифрования был использован метод, который выдаёт каждый раз разный результат при шифровании для большей защиты данных.

Python:
if user:
   iv, encrypted_password = user.password.split(':', 1)
   try:
       decrypted_password = decrypt(iv, encrypted_password)
   except (ValueError, KeyError) as e:
       flash('Ошибка при проверке пароля. Попробуйте снова.')
       return redirect(url_for('registration'))
2 строка: Разделение зашифрованного пароля на IV и зашифрованный пароль. ':' означает, что строка будет разделена по этому символу, 1 означает, что строка будет разделена один раз. Это гарантирует, что из строки получится два объекта: iv и пароль.

4 строка: Вызывается функция decrypt, в которую передаются два значения: iv и зашифрованный пароль. Функция расшифровки возвращает расшифрованный пароль, который записывается в переменную decrypted_password.

6 строка: Вывод сообщения на веб-страницу с использованием flash, если пароль не удалось расшифровать.

7 строка: Если пароль не подошел, происходит редирект на страницу регистрации.

Далее идет код с условием: если расшифрованный пароль из базы данных совпадает с паролем, полученным с веб-страницы, то происходит редирект на страницу профиля. На этом функция авторизации завершена.
Python:
 if decrypted_password == password:
       return redirect(url_for('profile'))
   else:
       flash('Неверный пароль. Попробуйте снова.')
else:
   flash('Пользователь с таким логином не найден.')

Теперь можно указать, что будет запускаться при запуске программы, а именно: создание базы данных в контексте приложения во Flask (app — это инициализированный объект Flask, app.app_context() — это контекст приложения). Также в самом конце запускается сам Flask с флагом debug, благодаря которому как минимум выводятся подробные ошибки.
Python:
if __name__ == '__main__':
   with app.app_context():
       db.create_all()
   app.run(debug=True)

Ограничение входа на страницу профиля без аккаунта​

Теперь регистрация и авторизация готовы. Однако от страницы профиля мало толку, так как перейти на неё можно без прохождения регистрации или авторизации. Чтобы это исправить, нужно найти функцию регистрации и в ней найти строку:
Python:
db.session.commit()

После этой строки нужно добавить маркер того, что авторизация прошла успешно (маркер сохраняется в сессии Flask. В куки браузера записывается идентификатор сессии, который связывается с сервером. Сервер использует этот идентификатор для получения данных из сессии, таких как этот маркер авторизации).
Python:
session['logged_in'] = True

Далее нужно найти функцию авторизации и в ней проверить совпадение пароля из базы данных с паролем из формы веб-страницы.
Python:
if decrypted_password == password:

После этой строки нужно также установить маркер авторизации.
Python:
session['logged_in'] = True

Теперь нужно найти функцию, которая определяет маршрут на страницу профиля, и добавить в неё условие проверки: если маркер отсутствует или равен False, перенаправлять на страницу авторизации.
Python:
@app.route('/profile')
def profile():
   if not session.get('logged_in'):
       return redirect(url_for('registration'))
   return render_template('profile.html')
P.S. В условии не нужно указывать что-то типа == False, так как если ключ отсутствует или его значение равно False, значение маркера в любом случае будет отрицательным. Поэтому not подходит под оба варианта. После этих изменений на страницу профиля можно попасть только если пользователь успешно прошел регистрацию или авторизацию.

На данный момент всё закончено, и можно перейти к проекту игры на UE.

Создание меню авторизации в игровом движке​

1725195135983.png

Показывать как создать верстку интерфейса я не буду и сразу покажу как настроить отправку запросов на Python сервер.

Логика взаимодействия игры с Python сервером​

Теперь нужно начать разрабатывать логику подключения к серверу регистрации. Для этого нужно скачать плагин VaRest в магазине Epic Games. Затем включите его в игровом проекте.
1725195167143.png

1725195183497.png


Теперь нужно открыть виджет с интерфейсом и установить у всех кнопок и полей параметр, что объект является переменной.
1725195209650.png


Далее нужно выбрать кнопку авторизации и создать событие клика.
1725195235885.png


После этого нужно перейти в Event Graph виджета и там вызвать VaRest Subsystem. Затем вызвать Construct JSON Object и подключить его к On Clicked кнопки авторизации.
1725195257826.png


Затем нужно вызвать две функции Set String Field, в которых указать имена login и password.
P.S. Функция Set String Field создаёт JSON-объект с названием, указанным в Field Name.
1725195282768.png


После этого нужно получить значения из полей ввода, используя Get Text (Text Box). Затем подключите каждый Get Text (Text Box) к соответствующему Set String Field.
1725195303130.png


При желании можно добавить вывод получившегося JSON в игре.
1725195324112.png

1725195336894.png

Получается, что теперь считываются данные из полей ввода, и при нажатии на кнопку авторизации создаётся JSON с двумя объектами. Этот JSON выводится в игре в виде текста.

Принятие запросов на Python сервере​

Теперь можно начать делать отправку и обработку JSON на сервере. Для этого нужно перейти в Python-сервер и создать новую функцию, которая будет принимать JSON, извлекать данные из объекта login и password, а затем сверять пароль с паролем из базы данных, аналогично функции, написанной для авторизации.
Python:
@app.route('/api/login', methods=['POST'])
def api_login():
   data = request.get_json()
   username = data.get('login')
   password = data.get('password')


   user = User.query.filter_by(username=username).first()


   if user:
       iv, encrypted_password = user.password.split(':', 1)
       try:
           decrypted_password = decrypt(iv, encrypted_password)
           if decrypted_password == password:
               return jsonify({"status": 0}), 200  # Пароль расшифрован и он сходится с паролем из базы данных
           else:
               return jsonify({"status": 1}), 401  # Неверный пароль
       except (ValueError, KeyError):
           return jsonify({"status": 2}), 401  # Не удалось расшифровать пароль
   else:
       return jsonify({"status": 3}), 404  # Пользователь не найден

При обработке пароля сервер будет возвращать игре JSON с объектом status, содержащим цифровое значение. Это необходимо для того, чтобы игра могла интерпретировать различные статусы и понимать, что произошло, так как использование true/false не подходит из-за множественной вариации ответов. Это последнее изменение на Python-сервере.

Отправка запросов на Python сервер и обработка ответа​

Теперь нужно зайти в движок UE5 и перейти в файл с виджетом меню авторизации в Event Graph, а затем вызвать функцию отправки запросов.
1725195402289.png

В URL нужно указать ссылку на сервер и /api/login (именно туда, потому что такой путь был указан в самом верху функции). Далее нужно из callback создать новый кастомный ивент с названием CALLBACK, из него вытянуть Get Response Object, который будет брать данные из JSON, и из этого объекта вытащить Get Field, чтобы извлечь из JSON объект с названием status.
1725195420755.png


Затем нужно вытянуть As Number из Get Field, чтобы конвертировать значение из объекта status в float. После этого float значение нужно перевести в целое число, используя Truncate.
1725195439873.png


Теперь из Truncate нужно вытянуть Switch on Int и добавить в нём контакты.
1725195466997.png

Получается так, что если полученное значение из объекта status равно 0, то выполняется определённое действие; если 1, то выполняется другое действие и т.д.

Теперь к контактам нужно подключить то, что будет выполняться при определённом значении из объекта status. Для этого можно создать 4 кастомных ивента для 4 статусов. Статус 0 — это успешная авторизация, остальные статусы сообщают об ошибках.
1725195487113.png

1725195507336.png


К ивентам для статусов с ошибками были добавлены простые выводы Print String для отображения ошибок.
1725195536352.png


Для ивента со статусом 0 был добавлен не только вывод, но и Open Level, который будет открывать игровой сервер с картой.
1725195557130.png

Так как игровой сервер сейчас находится на локальной машине, его адрес — это 127.0.0.1, а порт можно изменить в настройках проекта, но это будет позже.

Для начала нужно все эти ивенты вызвать в Switch on Int.
1725195580191.png

Таким образом, при получении конкретного числа из объекта status в JSON будет вызываться конкретный ивент. Если это ивент, сообщающий об удаче, игрок будет переходить на игровой сервер.

Настройка dedicated server​

Теперь нужно рассмотреть настройки выделенного сервера. Для этого нужно выйти из виджета и зайти в дополнительные настройки на сцене, чтобы установить порт для сервера.
1725195602787.png

1725195618057.png


Далее нужно выбрать карту, которая будет запускаться при запуске сервера и при запуске клиента.
1725195639951.png

1725195653984.png

В данном случае для запуска клиента используется карта с меню, а для сервера используется карта TestMap, то есть игровая карта, где находятся спавны игроков и т.д. В дальнейшем на её месте должна быть карта, которая будет основным полем для игры.

Теперь нужно всё это проверить, но через игровой движок запустить всё не получится, поэтому нужно запускать по-другому. Для этого нужно перейти в папку с игровым проектом и создать 2 .bat файла.
1725195680448.png

В .bat файле start_game нужно указать такие параметры: "Полный путь до UnrealEditor.exe" "Полный путь до .uproject файла игры" -game.

В .bat файле start_server нужно указать такие параметры: "Полный путь до UnrealEditor.exe" "Полный путь до .uproject файла игры" -server -log -port=1488.

После этого можно запустить сервер и две копии игры, и авторизоваться, введя логины и пароли, зарегистрированные на Python-сервере. Игроков успешно отправит на игровой сервер.

Вывод​

В данной статье есть упущенные моменты при разработке в движке UE5, но это связано с тем, что админ форума попросил делать статью более ориентированной на форум и давать те знания, которые могут пригодиться основному контингенту форума. Поэтому, если нужна более подробная статья, она доступна по данной ссылке:
Если людям, читающим статью, действительно интересно, как сделать игру с блокчейном, можете отписать в комментариях. Если это будет интересно, я создам канал в Telegram, в котором буду выкладывать более подробные гайды, даже когда не смогу их показывать на форуме.


Сделано OverlordGameDev специально для форума xss.pro
 
Код:
if user:

       iv, encrypted_password = user.password.split(':', 1)

       try:

           decrypted_password = decrypt(iv, encrypted_password)

           if decrypted_password == password:

               return jsonify({"status": 0}), 200  # Пароль расшифрован и он сходится с паролем из базы данных

           else:

               return jsonify({"status": 1}), 401  # Неверный пароль

       except (ValueError, KeyError):

           return jsonify({"status": 2}), 401  # Не удалось расшифровать пароль

   else:

       return jsonify({"status": 3}), 404  # Пользователь не найден

это что за бред, зачем это, пароль просто хешируется и в базе хранится исключительно хеш, при вводе хешируется и сравнивается с хешем в базе
ты намудрил непонятно что, и по факту хранишь открытый пароль в базе
 
Код:
if user:

       iv, encrypted_password = user.password.split(':', 1)

       try:

           decrypted_password = decrypt(iv, encrypted_password)

           if decrypted_password == password:

               return jsonify({"status": 0}), 200  # Пароль расшифрован и он сходится с паролем из базы данных

           else:

               return jsonify({"status": 1}), 401  # Неверный пароль

       except (ValueError, KeyError):

           return jsonify({"status": 2}), 401  # Не удалось расшифровать пароль

   else:

       return jsonify({"status": 3}), 404  # Пользователь не найден

это что за бред, зачем это, пароль просто хешируется и в базе хранится исключительно хеш, при вводе хешируется и сравнивается с хешем в базе
ты намудрил непонятно что, и по факту хранишь открытый пароль в базе
Мне кажется ты ошибаешься, если бы не то что я сделал в коде, пароль был бы в открытом виде. Что бы он хранился в виде хэша нужно заюзать flask_bcrypt (bcrypt.generate_password_hash), в таком случае в бд и правда будет пароль в виде хэша. На момент написания статьи я об этом не знал поэтому делал шифрование в aes
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх