Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
fd4e49f
add project overview
SoulBrotherDev Jul 31, 2026
3eca2be
add txAdmin deployment recipe
SoulBrotherDev Jul 31, 2026
a302852
add modular server configuration
SoulBrotherDev Jul 31, 2026
edd3754
add core convars
SoulBrotherDev Jul 31, 2026
5ce18f3
configure ESX for PT and Steam identifiers
SoulBrotherDev Jul 31, 2026
fb87330
add network security convars
SoulBrotherDev Jul 31, 2026
23cfe55
add voice configuration
SoulBrotherDev Jul 31, 2026
3c14d10
add Soul Project ACE hierarchy
SoulBrotherDev Jul 31, 2026
1bcd681
add deterministic resource order
SoulBrotherDev Jul 31, 2026
bc89eb2
add safe secrets template
SoulBrotherDev Jul 31, 2026
459172f
add migration registry
SoulBrotherDev Jul 31, 2026
cde8509
add installation guide
SoulBrotherDev Jul 31, 2026
cb541a7
document resource architecture
SoulBrotherDev Jul 31, 2026
c5d9d19
add repository validator
SoulBrotherDev Jul 31, 2026
9c244ff
add GitHub validation workflow
SoulBrotherDev Jul 31, 2026
67aad3d
add sp_bootstrap manifest
SoulBrotherDev Jul 31, 2026
4f5f59b
configure sp_bootstrap
SoulBrotherDev Jul 31, 2026
5a1f189
add shared bootstrap logger
SoulBrotherDev Jul 31, 2026
add6c25
implement Steam gate and health checks
SoulBrotherDev Jul 31, 2026
fefca2c
signal client readiness
SoulBrotherDev Jul 31, 2026
13b3c20
document core resource group
SoulBrotherDev Jul 31, 2026
3c995a9
prepare UI resource group
SoulBrotherDev Jul 31, 2026
f03d5bb
prepare jobs resource group
SoulBrotherDev Jul 31, 2026
096237b
prepare illegal resource group
SoulBrotherDev Jul 31, 2026
1b55469
prepare maps resource group
SoulBrotherDev Jul 31, 2026
510fd69
prepare vehicles resource group
SoulBrotherDev Jul 31, 2026
a37cff7
prepare custom resource group
SoulBrotherDev Jul 31, 2026
c0a8675
fix validator self-match
SoulBrotherDev Jul 31, 2026
88072cf
Configure voice radio and calls
SoulBrotherDev Jul 31, 2026
80c6558
Add essential item migration
SoulBrotherDev Jul 31, 2026
1a11244
Add phone database migration
SoulBrotherDev Jul 31, 2026
9f08a84
Add starter pack migration
SoulBrotherDev Jul 31, 2026
4e977e4
Add Portuguese job labels migration
SoulBrotherDev Jul 31, 2026
3954813
Add radio manifest
SoulBrotherDev Jul 31, 2026
d62f21d
Configure radio channels
SoulBrotherDev Jul 31, 2026
fa0519d
Implement secure radio server
SoulBrotherDev Jul 31, 2026
f7313f2
Implement radio client
SoulBrotherDev Jul 31, 2026
b82d6d9
Add starter pack manifest
SoulBrotherDev Jul 31, 2026
2678146
Configure starter pack
SoulBrotherDev Jul 31, 2026
169940b
Implement starter pack grant
SoulBrotherDev Jul 31, 2026
7b79367
Add phone manifest
SoulBrotherDev Jul 31, 2026
758f5d6
Configure phone system
SoulBrotherDev Jul 31, 2026
6ab4594
Implement phone server
SoulBrotherDev Jul 31, 2026
badf83b
Implement phone client bridge
SoulBrotherDev Jul 31, 2026
6d330bd
Add phone interface markup
SoulBrotherDev Jul 31, 2026
3fbbf6b
Style phone interface
SoulBrotherDev Jul 31, 2026
4e270cb
Implement phone interface logic
SoulBrotherDev Jul 31, 2026
3c5af63
Expand txAdmin recipe with essential systems
SoulBrotherDev Jul 31, 2026
cb5ef81
Install garage database schema
SoulBrotherDev Jul 31, 2026
bd99f31
Validate essential ESX systems
SoulBrotherDev Jul 31, 2026
1e65a5a
Add Lua and JavaScript validation
SoulBrotherDev Jul 31, 2026
e39da21
Document essential ESX systems
SoulBrotherDev Jul 31, 2026
d2fb0ca
Grant moderation voice permissions
SoulBrotherDev Jul 31, 2026
7c1872a
Add essential systems guide
SoulBrotherDev Jul 31, 2026
388e485
Expand staging installation guide
SoulBrotherDev Jul 31, 2026
1591034
Monitor essential Soul resources
SoulBrotherDev Jul 31, 2026
6c61799
Delay complete base health check
SoulBrotherDev Jul 31, 2026
467218b
Harden phone item validation
SoulBrotherDev Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Validate ESX base

on:
push:
pull_request:

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install Lua parser
run: |
sudo apt-get update
sudo apt-get install -y lua5.4

- name: Validate repository
run: python tools/validate.py

- name: Validate Lua syntax
shell: bash
run: |
while IFS= read -r -d '' file; do
echo "Checking $file"
luac5.4 -p "$file"
done < <(find resources -type f -name '*.lua' -print0)

- name: Validate JavaScript syntax
run: node --check 'resources/[sp_ui]/sp_phone/html/app.js'

- name: Check whitespace
run: git diff --check
117 changes: 117 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Soul Project ESX Legacy

Base modular do **Soul Project** para servidores FiveM assentes em **ESX Legacy**. A recipe txAdmin instala as dependências externas e mantém todo o código próprio `sp_*` separado, para permitir atualizações sem transformar o servidor num puzzle de recursos copiados à mão.

## Sistemas incluídos

### Fundação

- ESX Legacy Core e ESX Legacy Addons oficiais;
- `oxmysql`, `pma-voice`, `bob74_ipl` e recursos base Cfx.re;
- configuração PT-PT, OneSync e política Steam-only;
- permissões ACE para `owner`, `admin`, `dev`, `mod` e `support`;
- `sp_bootstrap` com gate Steam, health check e state bag `spReady`;
- migrações SQL cumulativas registadas em `sp_schema_migrations`.

### Comunicação

- `sp_radio` sobre `pma-voice`;
- frequências públicas e frequências reservadas por profissão;
- item `radio`, comandos `/radio`, `/radiooff` e `/radiovol`;
- revalidação server-side após mudança de emprego ou perda do item;
- `sp_phone` com número persistente por personagem;
- contactos, SMS persistentes, histórico de chamadas e chamadas de voz;
- pedidos com localização para Polícia, Emergência Médica, Mecânico e Táxi;
- item `phone`, comando `/phone` e tecla `F1`.

### Economia e roleplay

A instalação importa o SQL necessário para:

- contas partilhadas, inventários partilhados e datastores;
- sociedades e gestão de empresas;
- faturação, licenças e banking;
- lojas e necessidades incluídas nos addons ESX;
- concessionário, veículos próprios e garagem;
- propriedades, personalização, HUD e restantes módulos do pack oficial.

### Empregos essenciais

- Polícia;
- Emergência Médica;
- Mecânico;
- Táxi;
- Concessionário;
- Imobiliária;
- empregos civis do `esx_jobs`.

Os nomes principais são ajustados para PT-PT pela migração `020_sp_jobs_pt.sql`.

### Primeiro personagem

O `sp_starterpack` entrega apenas uma vez:

- 1 telemóvel;
- 1 rádio;
- 2 águas;
- 2 pães.

A entrega é idempotente e fica registada em `sp_starter_claims`.

## Instalação pelo txAdmin

Usa esta recipe personalizada no Server Deployer:

```text
https://raw.githubusercontent.com/SoulBrotherDev/ESX-LEGACY/main/recipe.yaml
```

Depois da instalação, abre `config/secrets.cfg` e coloca a Steam Web API Key. Chaves, tokens e palavras-passe reais nunca devem ser enviados para o Git.

Consulta o guia completo em [`docs/INSTALL.md`](docs/INSTALL.md) e o catálogo funcional em [`docs/ESSENTIALS.md`](docs/ESSENTIALS.md).

## Arranque e diagnóstico

A ordem dos recursos está em `config/resources.cfg`. Após arrancar, executa na consola:

```text
sp_health
```

O comando apresenta o estado do núcleo ESX, base de dados e módulos Soul Project essenciais.

## Estrutura

```text
config/ Configuração dividida por domínio
database/ Migrações próprias do Soul Project
docs/ Instalação, arquitetura e catálogo funcional
resources/[sp_core]/ Bootstrap, rádio e kit inicial
resources/[sp_ui]/ Telefone, interfaces e HUD
resources/[sp_jobs]/ Trabalhos legais próprios
resources/[sp_illegal]/ Sistemas criminais
resources/[sp_maps]/ IPLs, MLOs e mapas próprios
resources/[sp_vehicles]/ Veículos, handling e ecossistema automóvel
resources/[sp_custom]/ Integrações transversais
```

## Qualidade

O GitHub Actions verifica automaticamente:

- ficheiros e dependências obrigatórias;
- SQL próprio incluído na recipe;
- ordem de arranque;
- referências dos manifests;
- sintaxe Lua;
- sintaxe JavaScript;
- whitespace e deteção básica de segredos.

## Regras da base

1. Não versionar chaves, tokens, palavras-passe ou connection strings reais.
2. Recursos de terceiros permanecem fora do Git e são instalados pela recipe.
3. Recursos próprios usam prefixo `sp_` e `fxmanifest.lua`.
4. Alterações SQL próprias são cumulativas e registadas em `sp_schema_migrations`.
5. Autoridade financeira, inventário, rádio, telefone e empregos permanece no servidor.
6. Atualizações do ESX são testadas em staging antes de chegar a produção.
17 changes: 17 additions & 0 deletions config/core.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Identidade pública
sets locale "pt-PT"
sets tags "pt-PT, roleplay, esx, esx-legacy, soul-project, steam-only"

# Cfx.re
set resources_useSystemChat true
sv_scriptHookAllowed 0
sv_endpointPrivacy true

# Soul Project
setr sp:server_name "Soul Project"
setr sp:locale "pt-PT"
setr sp:environment "development"
setr sp:steam_only 1

# Ativar apenas depois de validar mapas, veículos e artefactos DLC.
# sv_enforceGameBuild 3258
5 changes: 5 additions & 0 deletions config/esx.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ESX Legacy
setr esx:locale "pt"
setr esx:identifier "steam"

# A identificação Steam exige uma Steam Web API Key válida em config/secrets.cfg.
30 changes: 30 additions & 0 deletions config/permissions.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Hierarquia Soul Project
add_principal group.owner group.admin
add_principal group.admin group.mod
add_principal group.mod group.support
add_principal group.admin group.user
add_principal group.dev group.user

# Administração geral
add_ace group.owner command allow
add_ace group.admin command allow
add_ace group.admin command.quit deny

# Desenvolvimento controlado
add_ace group.dev command.refresh allow
add_ace group.dev command.start allow
add_ace group.dev command.stop allow
add_ace group.dev command.restart allow
add_ace group.dev command.ensure allow
add_ace group.dev command.sp_health allow

# Moderação, voz e suporte
add_ace group.mod command.muteply allow
add_ace group.mod command.sp_health allow
add_ace group.support command.sp_health allow

# Permissões necessárias ao es_extended.
add_ace resource.es_extended command.add_ace allow
add_ace resource.es_extended command.add_principal allow
add_ace resource.es_extended command.remove_principal allow
add_ace resource.es_extended command.stop allow
23 changes: 23 additions & 0 deletions config/resources.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Cfx.re / sistema
ensure chat
ensure hardcap

# Persistência e voz
ensure oxmysql
ensure pma-voice
ensure bob74_ipl

# ESX Legacy
ensure esx_lib
ensure es_extended
ensure [core]
ensure [esx_addons]

# Soul Project
ensure [sp_core]
ensure [sp_ui]
ensure [sp_jobs]
ensure [sp_illegal]
ensure [sp_maps]
ensure [sp_vehicles]
ensure [sp_custom]
8 changes: 8 additions & 0 deletions config/secrets.example.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copiar para config/secrets.cfg e nunca versionar o ficheiro real.

# Obrigatório para a política Steam-only.
set steam_webApiKey "COLOCAR_STEAM_WEB_API_KEY"

# Exemplos de segredos futuros, manter comentados até serem usados.
# setr sp:discord_bot_token "COLOCAR_TOKEN"
# setr sp:discord_application_id "COLOCAR_APPLICATION_ID"
6 changes: 6 additions & 0 deletions config/security.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Convars de segurança para reduzir abuso de eventos de rede.
sv_enableNetworkedSounds false
sv_enableNetworkedScriptEntityStates false
sv_enableNetworkedPhoneExplosions false
sv_filterRequestControl 2
sv_endpointPrivacy true
18 changes: 18 additions & 0 deletions config/voice.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pma-voice: proximidade, rádio e chamadas
setr voice_enableUi 1
setr voice_enableProximityCycle 1
setr voice_defaultCycle "F11"
setr voice_defaultVoiceMode 2
setr voice_useNativeAudio true
setr voice_useSendingRangeOnly true
setr voice_enableRadios 1
setr voice_enableCalls 1
setr voice_enableSubmix 1
setr voice_enableRadioAnim 1
setr voice_defaultRadio "LMENU"
setr voice_defaultRadioVolume 35
setr voice_defaultCallVolume 60
setr voice_onClickVolume 10
setr voice_offClickVolume 3
setr voice_hideEndpoints 1
setr voice_debugMode 0
10 changes: 10 additions & 0 deletions database/000_sp_base.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS `sp_schema_migrations` (
`id` varchar(100) NOT NULL,
`description` varchar(255) NOT NULL,
`checksum` char(64) DEFAULT NULL,
`applied_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT IGNORE INTO `sp_schema_migrations` (`id`, `description`)
VALUES ('000_sp_base', 'Inicialização da base Soul Project ESX Legacy');
12 changes: 12 additions & 0 deletions database/005_sp_essential_items.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- Soul Project ESX: itens essenciais reutilizáveis.
INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES
('phone', 'Telemóvel', 1, 0, 1),
('radio', 'Rádio', 1, 0, 1)
ON DUPLICATE KEY UPDATE
`label` = VALUES(`label`),
`weight` = VALUES(`weight`),
`rare` = VALUES(`rare`),
`can_remove` = VALUES(`can_remove`);

INSERT IGNORE INTO `sp_schema_migrations` (`version`, `description`)
VALUES ('005', 'Itens essenciais: telemóvel e rádio');
60 changes: 60 additions & 0 deletions database/010_sp_phone.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- Soul Project ESX: telefone, contactos, mensagens, chamadas e pedidos de serviço.
CREATE TABLE IF NOT EXISTS `sp_phone_numbers` (
`identifier` VARCHAR(96) NOT NULL,
`phone_number` VARCHAR(20) NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`identifier`),
UNIQUE KEY `uq_sp_phone_number` (`phone_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `sp_phone_contacts` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`owner_identifier` VARCHAR(96) NOT NULL,
`display_name` VARCHAR(64) NOT NULL,
`phone_number` VARCHAR(20) NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_sp_phone_contact` (`owner_identifier`, `phone_number`),
KEY `idx_sp_phone_contacts_owner` (`owner_identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `sp_phone_messages` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`sender_number` VARCHAR(20) NOT NULL,
`receiver_number` VARCHAR(20) NOT NULL,
`body` VARCHAR(500) NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`read_at` TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_sp_phone_messages_sender` (`sender_number`, `created_at`),
KEY `idx_sp_phone_messages_receiver` (`receiver_number`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `sp_phone_calls` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`caller_number` VARCHAR(20) NOT NULL,
`receiver_number` VARCHAR(20) NOT NULL,
`status` ENUM('missed', 'declined', 'completed', 'failed') NOT NULL,
`duration_seconds` INT UNSIGNED NOT NULL DEFAULT 0,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_sp_phone_calls_caller` (`caller_number`, `created_at`),
KEY `idx_sp_phone_calls_receiver` (`receiver_number`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `sp_phone_service_messages` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`service` VARCHAR(32) NOT NULL,
`sender_identifier` VARCHAR(96) NOT NULL,
`sender_number` VARCHAR(20) NOT NULL,
`message` VARCHAR(500) NOT NULL,
`coords` LONGTEXT NULL,
`status` ENUM('open', 'accepted', 'closed') NOT NULL DEFAULT 'open',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_sp_phone_service_status` (`service`, `status`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT IGNORE INTO `sp_schema_migrations` (`version`, `description`)
VALUES ('010', 'Telefone Soul Project: números, contactos, SMS, chamadas e serviços');
10 changes: 10 additions & 0 deletions database/015_sp_starterpack.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Soul Project ESX: controlo idempotente do kit inicial.
CREATE TABLE IF NOT EXISTS `sp_starter_claims` (
`identifier` VARCHAR(96) NOT NULL,
`claimed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`payload` LONGTEXT NULL,
PRIMARY KEY (`identifier`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT IGNORE INTO `sp_schema_migrations` (`version`, `description`)
VALUES ('015', 'Registo idempotente do kit inicial');
22 changes: 22 additions & 0 deletions database/020_sp_jobs_pt.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Soul Project ESX: nomenclatura PT-PT para empregos essenciais.
UPDATE `jobs` SET `label` = 'Desempregado' WHERE `name` = 'unemployed';
UPDATE `jobs` SET `label` = 'Polícia' WHERE `name` = 'police';
UPDATE `jobs` SET `label` = 'Emergência Médica' WHERE `name` = 'ambulance';
UPDATE `jobs` SET `label` = 'Mecânico' WHERE `name` = 'mechanic';
UPDATE `jobs` SET `label` = 'Táxi' WHERE `name` = 'taxi';
UPDATE `jobs` SET `label` = 'Concessionário' WHERE `name` = 'cardealer';
UPDATE `jobs` SET `label` = 'Imobiliária' WHERE `name` = 'realestateagent';

UPDATE `job_grades` SET `label` = 'Recruta' WHERE `job_name` = 'police' AND `grade` = 0;
UPDATE `job_grades` SET `label` = 'Agente' WHERE `job_name` = 'police' AND `grade` = 1;
UPDATE `job_grades` SET `label` = 'Sargento' WHERE `job_name` = 'police' AND `grade` = 2;
UPDATE `job_grades` SET `label` = 'Tenente' WHERE `job_name` = 'police' AND `grade` = 3;
UPDATE `job_grades` SET `label` = 'Comandante' WHERE `job_name` = 'police' AND `grade` >= 4;

UPDATE `job_grades` SET `label` = 'Estagiário' WHERE `job_name` = 'ambulance' AND `grade` = 0;
UPDATE `job_grades` SET `label` = 'Paramédico' WHERE `job_name` = 'ambulance' AND `grade` = 1;
UPDATE `job_grades` SET `label` = 'Médico' WHERE `job_name` = 'ambulance' AND `grade` = 2;
UPDATE `job_grades` SET `label` = 'Diretor Clínico' WHERE `job_name` = 'ambulance' AND `grade` >= 3;

INSERT IGNORE INTO `sp_schema_migrations` (`version`, `description`)
VALUES ('020', 'Nomenclatura PT-PT dos empregos ESX essenciais');
Loading
Loading