Part 1: registration, login and character creation written from scratch
Всё, что здесь лежит, написал ИИ-агент на живом сервере под запись — без ESX, без QBCore, без скачанных с форума скриптов. - resources/justrp — свой ресурс на Lua: регистрация, вход, создание персонажа, спавн - resources/justrp/html — экраны NUI: кинематографичная загрузка, вход, редактор персонажа - api/api.py — внутренний HTTP-API к MariaDB, пароли через scrypt - server.cfg.example — пример конфига Секреты (пароль БД, внутренний ключ API) вынесены в переменные окружения и convar. Ключ Cfx.re в репозиторий не попадает. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
968d047f87
commit
0144e83f05
@@ -0,0 +1,210 @@
|
||||
-- JustRP — Server-side core
|
||||
-- All DB operations go through the internal Python API on :8787
|
||||
|
||||
local API_BASE = "http://127.0.0.1:8787"
|
||||
-- Должен совпадать с JUSTRP_API_SECRET у Python-API. Задаётся в server.cfg:
|
||||
-- set justrp_api_key "ваш-ключ"
|
||||
local API_KEY = GetConvar("justrp_api_key", "change-me")
|
||||
|
||||
-- Per-player session state
|
||||
local Sessions = {}
|
||||
|
||||
-- ─── HTTP helper ────────────────────────────────────────────────────────────
|
||||
|
||||
local function apiRequest(method, path, data, cb)
|
||||
PerformHttpRequest(
|
||||
API_BASE .. path,
|
||||
function(status, body, _headers)
|
||||
local ok, result = pcall(json.decode, body or "{}")
|
||||
if ok then
|
||||
cb(status, result)
|
||||
else
|
||||
cb(status, { success = false, error = "parse error" })
|
||||
end
|
||||
end,
|
||||
method,
|
||||
data and json.encode(data) or "",
|
||||
{
|
||||
["Content-Type"] = "application/json",
|
||||
["X-API-Key"] = API_KEY,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
-- ─── Player connecting ───────────────────────────────────────────────────────
|
||||
|
||||
AddEventHandler("playerConnecting", function(name, setKickReason, deferrals)
|
||||
local src = source
|
||||
local license = GetPlayerIdentifierByType(src, "license") or
|
||||
GetPlayerIdentifierByType(src, "license2") or
|
||||
"license:" .. tostring(src)
|
||||
deferrals.defer()
|
||||
Wait(0)
|
||||
deferrals.update("Checking account…")
|
||||
|
||||
apiRequest("GET", "/account?license=" .. license, nil, function(status, res)
|
||||
if status == 200 and res.success then
|
||||
if res.account.banned == 1 then
|
||||
deferrals.done("You are banned from JustRP.\nReason: " .. (res.account.ban_reason or "No reason given"))
|
||||
else
|
||||
deferrals.done()
|
||||
end
|
||||
else
|
||||
-- No account yet — let them through, they'll register in-game
|
||||
deferrals.done()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- ─── Client ready → start session ───────────────────────────────────────────
|
||||
|
||||
RegisterNetEvent("justrp:clientReady")
|
||||
AddEventHandler("justrp:clientReady", function()
|
||||
local src = source
|
||||
local license = GetPlayerIdentifierByType(src, "license") or
|
||||
GetPlayerIdentifierByType(src, "license2") or
|
||||
"license:" .. tostring(src)
|
||||
Sessions[src] = { license = license, accountId = nil, charId = nil }
|
||||
TriggerClientEvent("justrp:startSession", src)
|
||||
end)
|
||||
|
||||
-- ─── Auth events ────────────────────────────────────────────────────────────
|
||||
|
||||
RegisterNetEvent("justrp:auth:register")
|
||||
AddEventHandler("justrp:auth:register", function(username, password)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses then return end
|
||||
|
||||
apiRequest("POST", "/auth/register", {
|
||||
license = ses.license,
|
||||
username = username,
|
||||
password = password,
|
||||
}, function(status, res)
|
||||
if res.success then
|
||||
ses.accountId = res.account_id
|
||||
TriggerClientEvent("justrp:auth:ok", src, res.account_id, res.username)
|
||||
else
|
||||
TriggerClientEvent("justrp:auth:fail", src, res.error or "Unknown error")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("justrp:auth:login")
|
||||
AddEventHandler("justrp:auth:login", function(username, password)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses then return end
|
||||
|
||||
apiRequest("POST", "/auth/login", {
|
||||
license = ses.license,
|
||||
username = username,
|
||||
password = password,
|
||||
}, function(status, res)
|
||||
if res.success then
|
||||
ses.accountId = res.account_id
|
||||
TriggerClientEvent("justrp:auth:ok", src, res.account_id, res.username)
|
||||
else
|
||||
TriggerClientEvent("justrp:auth:fail", src, res.error or "Unknown error")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- ─── Character events ────────────────────────────────────────────────────────
|
||||
|
||||
RegisterNetEvent("justrp:chars:load")
|
||||
AddEventHandler("justrp:chars:load", function()
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses or not ses.accountId then
|
||||
TriggerClientEvent("justrp:chars:list", src, {})
|
||||
return
|
||||
end
|
||||
|
||||
apiRequest("GET", "/characters?account_id=" .. ses.accountId, nil, function(status, res)
|
||||
if res.success then
|
||||
TriggerClientEvent("justrp:chars:list", src, res.characters or {})
|
||||
else
|
||||
TriggerClientEvent("justrp:chars:list", src, {})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("justrp:char:create")
|
||||
AddEventHandler("justrp:char:create", function(data)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses or not ses.accountId then return end
|
||||
|
||||
data.account_id = ses.accountId
|
||||
apiRequest("POST", "/character/create", data, function(status, res)
|
||||
if res.success then
|
||||
ses.charId = res.character_id
|
||||
TriggerClientEvent("justrp:char:created", src, res.character_id)
|
||||
else
|
||||
TriggerClientEvent("justrp:char:createFail", src, res.error or "Unknown error")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("justrp:char:select")
|
||||
AddEventHandler("justrp:char:select", function(charId)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses or not ses.accountId then return end
|
||||
|
||||
apiRequest("GET", "/character?id=" .. tostring(charId), nil, function(status, res)
|
||||
if res.success and res.character then
|
||||
local ch = res.character
|
||||
if ch.account_id ~= ses.accountId then
|
||||
return -- character doesn't belong to this account
|
||||
end
|
||||
ses.charId = charId
|
||||
TriggerClientEvent("justrp:char:loaded", src, ch)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- ─── Spawn ───────────────────────────────────────────────────────────────────
|
||||
|
||||
RegisterNetEvent("justrp:spawn:request")
|
||||
AddEventHandler("justrp:spawn:request", function(spawnKey)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses or not ses.charId then return end
|
||||
|
||||
local spawns = {
|
||||
legion = { x = -166.7, y = -928.9, z = 31.4, h = 120.0 },
|
||||
vespucci = { x = -1380.0, y = -1520.0, z = 4.9, h = 75.0 },
|
||||
sandy = { x = 1843.4, y = 3683.0, z = 34.3, h = 200.0 },
|
||||
paleto = { x = -165.8, y = 6330.0, z = 31.5, h = 270.0 },
|
||||
airport = { x = -1037.0, y = -2738.0, z = 20.0, h = 330.0 },
|
||||
}
|
||||
local pos = spawns[spawnKey] or spawns["legion"]
|
||||
TriggerClientEvent("justrp:spawn:go", src, pos)
|
||||
end)
|
||||
|
||||
-- ─── Auto-save position every 60s ────────────────────────────────────────────
|
||||
|
||||
RegisterNetEvent("justrp:savePosition")
|
||||
AddEventHandler("justrp:savePosition", function(x, y, z, h)
|
||||
local src = source
|
||||
local ses = Sessions[src]
|
||||
if not ses or not ses.charId then return end
|
||||
apiRequest("POST", "/character/save", {
|
||||
character_id = ses.charId,
|
||||
last_x = x,
|
||||
last_y = y,
|
||||
last_z = z,
|
||||
last_heading = h,
|
||||
}, function() end)
|
||||
end)
|
||||
|
||||
-- ─── Disconnect ──────────────────────────────────────────────────────────────
|
||||
|
||||
AddEventHandler("playerDropped", function()
|
||||
local src = source
|
||||
Sessions[src] = nil
|
||||
end)
|
||||
|
||||
print("[JustRP] Server core loaded.")
|
||||
Reference in New Issue
Block a user