Ahorra hoy mismo en 20%. Usa el código WELCOME al finalizar la compra. WELCOME

How To Write FiveM Scripts Using AI: Complete Developer&#…

¿Estás probando un script gratuito?

Los scripts gratuitos son útiles para comprobaciones rápidas. Para servidores de producción, compare los paquetes completos para servidores o los scripts de pago con mantenimiento, teniendo en cuenta el framework y el caso de uso.

Tradicionalmente, escribir scripts de FiveM requería un profundo conocimiento de Lua, JavaScript y la API de FiveM. Hoy en día, herramientas de IA como Claude Code, GitHub Copilot y ChatGPT están revolucionando la forma en que los desarrolladores crean todo, desde... Scripts de ESX demasiado complejo sistemas independientesEsta guía completa le muestra exactamente cómo aprovechar la IA para el desarrollo de FiveM, con ejemplos concretos y flujos de trabajo probados.

Por qué el desarrollo FiveM asistido por IA lo cambia todo

El desarrollo tradicional de scripts de FiveM requiere dominar varias tecnologías simultáneamente: Lua para la lógica del lado del servidor, JavaScript para interfaces NUI, SQL para operaciones de base de datos y la extensa Biblioteca de funciones nativas de FiveMLas herramientas de IA comprimen meses de aprendizaje en horas de codificación productiva.

Impacto real para los propietarios de servidores:

  • Reducir el tiempo de desarrollo de scripts personalizados en un 60-80%
  • Genere código repetitivo al instante para patrones comunes
  • Depurar problemas de sincronización complejos entre el cliente y el servidor
  • Convierte ideas directamente en prototipos funcionales

Herramientas esenciales de IA para el desarrollo de FiveM

Código Claude (Antrópico)

Claude Code destaca en el desarrollo de FiveM porque comprende el contexto de múltiples archivos y puede generar estructuras de recursos completas. Instalación mediante línea de comandos:

pip install claude-code claude-code init --proyecto fivem-resource

Ventajas específicas de FiveM:

  • Genera configuraciones completas de fxmanifest.lua
  • Comprende los marcos ESX, QBCore y VRP
  • Crea automáticamente controladores de eventos cliente/servidor coincidentes

Copiloto GitHub

Se integra directamente con VS Code y ofrece sugerencias en tiempo real mientras escribes. Especialmente eficaz para:

  • Completar llamadas a funciones nativas
  • Generación de patrones de controladores de eventos
  • Consultas de base de datos con autocompletado

ChatGPT con instrucciones personalizadas

Configure ChatGPT específicamente para FiveM configurando instrucciones personalizadas:

Eres desarrollador de scripts de FiveM. Usa siempre: - Sintaxis Lua 5.4 para scripts de servidor - JavaScript moderno para scripts de cliente - Nativos de FiveM de la última versión del juego - Seguridad de eventos adecuada con validación del lado del servidor

Paso a paso: Crea tu primer script generado por IA

Construyamos un script de taller de vehículos completo usando Claude Code, que demuestre todo el flujo de trabajo desde el concepto hasta la implementación.

Paso 1: Definir requisitos claros

Crear una requisitos.md file:

## Requisitos del script del taller de vehículos - Marco: ESX Legacy 1.9.0+ - Base de datos: MySQL con oxmysql - Características: - Explorar vehículos por categoría - Sistema de prueba de manejo con límite de tiempo - Opciones de financiación con pagos semanales - Comandos de administrador para agregar vehículos - UI: NUI moderna con React

Paso 2: Generar la estructura de recursos

Comando a Claude Code:

Claude-code genera "Crear un recurso completo para el taller de vehículos FiveM según los requisitos.md"

Esto genera:

vehicle_shop/ ├── fxmanifest.lua ├── config.lua ├── server/ │ ├── main.lua │ ├── database.lua │ └── commands.lua ├── client/ │ ├── main.lua │ ├── showroom.lua │ └── testdrive.lua ├── ui/ │ ├── index.html │ ├── style.css │ └── script.js └── sql/ └── tienda_de_vehículos.sql

Paso 3: fxmanifest.lua generado por IA

fx_version 'cerulean' juego 'gta5' autor 'YourName' descripción 'Tienda de vehículos generada por IA' versión '1.0.0' shared_scripts { '@es_extended/imports.lua', '@ox_lib/init.lua', 'config.lua' } client_scripts { 'client/*.lua' } server_scripts { '@oxmysql/lib/MySQL.lua', 'server/*.lua' } ui_page 'ui/index.html' archivos { 'ui/index.html', 'ui/style.css', 'ui/script.js' } dependencias { 'es_extended', 'oxmysql', 'ox_lib' }

Paso 4: Implementar la funcionalidad principal con IA

Solicitud de carga del vehículo del lado del servidor:

-- AI-Generated: server/database.lua
local vehicles = {}

CreateThread(function()
    local result = MySQL.query.await('SELECT * FROM vehicle_shop WHERE stock > 0')
    
    for i = 1, #result do
        local vehicle = result[i]
        vehicles[vehicle.model] = {
            model = vehicle.model,
            name = vehicle.name,
            price = vehicle.price,
            category = vehicle.category,
            stock = vehicle.stock
        }
    end
    
    print(('[^2INFO^7] Loaded %d vehicles from database'):format(#result))
end)

-- Secure server event for purchasing
RegisterNetEvent('vehicle_shop:server:purchaseVehicle', function(vehicleModel, paymentMethod)
    local src = source
    local xPlayer = ESX.GetPlayerFromId(src)
    
    -- Security check
    if not vehicles[vehicleModel] then
        return TriggerClientEvent('ox_lib:notify', src, {
            title = 'Error',
            description = 'Invalid vehicle model',
            type = 'error'
        })
    end
    
    local vehicleData = vehicles[vehicleModel]
    
    -- Payment processing
    if paymentMethod == 'cash' then
        if xPlayer.getMoney() >= vehicleData.price then
            xPlayer.removeMoney(vehicleData.price)
            -- Generate plate and spawn vehicle
            local plate = GeneratePlate()
            
            MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {
                xPlayer.identifier,
                plate,
                json.encode({model = vehicleModel, plate = plate})
            })
            
            TriggerClientEvent('vehicle_shop:client:spawnVehicle', src, vehicleModel, plate)
        else
            TriggerClientEvent('ox_lib:notify', src, {
                title = 'Insufficient Funds',
                description = ('You need $%d'):format(vehicleData.price),
                type = 'error'
            })
        end
    end
end)

Paso 5: Implementación del lado del cliente

Aviso de IA: Cree una sala de exposición del lado del cliente con controles de cámara y vista previa del vehículo.

-- Generado por IA: cliente/showroom.lua local currentVehicle = nil local cam = nil local inShowroom = false local showroomCoords = vector3(-42.34, -1097.35, 26.42) local vehicleSpawnCoords = vector4(-47.52, -1097.29, 26.42, 340.0) function OpenShowroom(category) inShowroom = true DoScreenFadeOut(500) Wait(500) -- Configurar cámara cam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true) SetCamCoord(cam, -54.31, -1097.29, 28.42) PointCamAtCoord(cam, vehicleSpawnCoords.x, vehicleSpawnCoords.y, vehicleSpawnCoords.z) SetCamActive(cam, true) RenderScriptCams(true, false, 0, true, false) - Solicitar vehículos del servidor ESX.TriggerServerCallback('vehicle_shop:getVehicles', function(vehicles) SendNUIMessage({ action = 'showVehicles', vehicle = vehicle, category = category }) SetNuiFocus(true, true) end, category) DoScreenFadeIn(500) end RegisterNUICallback('previewVehicle', function(data, cb) if currentVehicle then DeleteEntity(currentVehicle) end local model = GetHashKey(data.model) RequestModel(model) while not HasModelLoaded(model) do Wait(10) end currentVehicle = CreateVehicle(model, vehicleSpawnCoords.x, vehicleSpawnCoords.y, vehicleSpawnCoords.z, vehicleSpawnCoords.w, false, false) SetEntityAsMissionEntity(vehículoactual, verdadero, verdadero) SetVehículoEnSueloCorrectamente(vehículoactual) SetVehículoPuertasBloqueadas(vehículoactual, 2) cb('ok') fin)

Técnicas avanzadas de IA para sistemas complejos

Gestión de contexto de múltiples archivos

Al desarrollar sistemas interconectados como scripts de teléfono, proporcionar a la IA un contexto completo:

# Alimentar varios archivos a Claude Code claude-code analyze --files "server/*.lua,client/*.lua,config.lua" \ --prompt "Agregar un sistema de garaje que se integre con el taller de vehículos existente"

Incitación específica del marco

Distintos marcos requieren enfoques diferentes. A continuación, se explica cómo optimizar las indicaciones de IA para cada uno:

Para ESX:

Generar un script de trabajo ESX con: - Sistema de calificaciones/rangos usando ESX.Jobs - Pago de salarios mediante ESX.GetJobs() - Menú del jefe con fondos de la sociedad - Usar patrones esx_society

Para QBCore:

Crea un sistema de pandillas QBCore con: - Territorios de pandillas usando PolyZone - Sistema de reputación con QB.Shared.Gangs - Almacenamiento usando qb-inventory - Administración de pandillas a través de patrones qb-gangmenu

Para Scripts VRP:

Construya un sistema comercial VRP usando: - vRP.getUserId para identificación del jugador - sistema de permisos vRP - métodos de dinero vRP (giveMoney, tryPayment) - túnel vRPclient para devoluciones de llamadas del cliente

Generación de esquemas de base de datos

La IA se destaca en la creación de estructuras de bases de datos optimizadas:

-- Aviso de IA: "Crear esquema MySQL para tienda de vehículos avanzada con financiamiento" -- Resultado generado: CREATE TABLE IF NOT EXISTS `vehicle_shop_catalog` ( `id` int(11) NOT NULL AUTO_INCREMENT, `model` varchar(60) NOT NULL, `name` varchar(60) NOT NULL, `manufacturer` varchar(60) DEFAULT NULL, `category` varchar(60) DEFAULT NULL, `price` int(11) NOT NULL, `stock` int(11) DEFAULT 10, `max_speed` int(11) DEFAULT NULL, `acceleration` float DEFAULT NULL, `braking` float DEFAULT NULL, `handling` float DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `model` (`model`), KEY `idx_category` (`categoría`), CLAVE `idx_price` (`precio`) ) MOTOR=InnoDB CONJUNTO DE CARACTERES PREDETERMINADO=utf8mb4; CREAR TABLA SI NO EXISTE `financiamiento_vehiculo` ( `id` int(11) NO NULO AUTO_INCREMENT, `identificador` varchar(60) NO NULO, `id_vehiculo` int(11) NO NULO, `pago_inicial` int(11) NO NULO, `pago_semanal` int(11) NO NULO, `semanas_restantes` int(11) NO NULO, `ultimo_pago` marca_de_tiempo PREDETERMINADA MARCA_DE_TIEMPO_ACTUAL, `advertencia_repositorio_sentido` booleano PREDETERMINADO FALSO, CLAVE PRINCIPAL (`id`), CLAVE `idx_identificador` (`identificador`), CLAVE EXTERNA (`id_vehiculo`) REFERENCIAS `vehiculos_propiedad`(`id`) AL ELIMINAR EN CASCADA ) MOTOR=InnoDB JUEGO DE CARACTERES PREDETERMINADO=utf8mb4;

Depuración y optimización con IA

Indicaciones de creación de perfiles de rendimiento

-- Prompt: "Add performance monitoring to this resource"
-- AI generates:

local performanceStats = {
    events = {},
    queries = {}
}

local function profileEvent(eventName, func)
    return function(...)
        local startTime = GetGameTimer()
        local result = {func(...)}
        local executionTime = GetGameTimer() - startTime
        
        performanceStats.events[eventName] = performanceStats.events[eventName] or {}
        table.insert(performanceStats.events[eventName], executionTime)
        
        if executionTime > 50 then
            print(('[^3WARNING^7] Event %s took %dms'):format(eventName, executionTime))
        end
        
        return table.unpack(result)
    end
end

-- Wrap existing events
RegisterNetEvent('vehicle_shop:server:purchaseVehicle', 
                profileEvent('purchaseVehicle', purchaseVehicleHandler))

Validación de seguridad

La IA puede identificar y solucionar vulnerabilidades de seguridad:

-- Mensaje: "Añadir validación de seguridad a este evento de cliente a servidor" -- Antes (vulnerable): RegisterNetEvent('giveWeapon', function(weapon) local src = source GiveWeaponToPed(GetPlayerPed(src), weapon, 250, false, true) end) -- Después (protegido por IA): local allowedWeapons = { ['WEAPON_PISTOL'] = true, ['WEAPON_COMBATPISTOL'] = true } RegisterNetEvent('giveWeapon', function(weapon) local src = source local xPlayer = ESX.GetPlayerFromId(src) -- Varias capas de validación si no es xPlayer, entonces retorna fin si no es allowedWeapons[weapon] entonces retorna DropPlayer(src, 'Se intentó generar un arma no válida') fin si no es xPlayer.hasPermission('weapons.spawn') entonces retorna TriggerClientEvent('ox_lib:notify', src, { title = 'Acceso denegado', type = 'error' }) end - Registrar la acción print(('[^3WEAPON^7] %s generó %s'):format(xPlayer.getName(), weapon)) xPlayer.addWeapon(weapon, 250) end)

Desarrollo de NUI con IA

Crear interfaces modernas para FiveM requiere experiencia en HTML/CSS/JavaScript. La IA agiliza este proceso:

Generación de componentes de React

// Mensaje: "Crear una interfaz de usuario de tienda de vehículos React con filtrado de categorías" // La IA genera: import React, { useState, useEffect } from 'react'; import './VehicleShop.css'; const VehicleShop = () => { const [vehicles, setVehicles] = useState([]); const [selectedCategory, setSelectedCategory] = useState('all'); const [selectedVehicle, setSelectedVehicle] = useState(null); const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); }, []); const handleMessage = (evento) => { const { acción, datos } = evento.datos; switch(acción) { caso 'mostrarVehículos': setVehicles(datos.vehículos); break; caso 'actualizarStock': updateVehicleStock(datos.modelo, datos.stock); break; } }; const filteredVehicles = vehículos.filter(vehículo => { const matchesCategory = selectedCategory === 'todos' || vehículo.categoría === selectedCategory; const matchesSearch = vehículo.nombre.toLowerCase() .includes(searchTerm.toLowerCase()); return matchesCategory && matchesSearch; }); const purchaseVehicle = (vehículo, métodoDePago) => { fetch(`https://${GetParentResourceName()}/purchaseVehicle`, { método: 'POST', encabezados: { 'TipoDeContenido': 'application/json' }, cuerpo: JSON.stringify({ modelo: vehículo.modelo, métodoDePago: métodoDePago }) }); }; return (
        <div classname="vehicle-shop-container">
            <div classname="shop-header">
                <h1>Deportes de motor de lujo premium</h1>
                <input 
                    type="text"
                    placeholder="Buscar vehículos..."
                    value="{searchTerm}"
                    onchange="{(e)" > setSearchTerm(e.target.value)} className=&quot;entrada de búsqueda&quot; /&gt;
            </div>

            <div classname="category-filters">
                {[&#039;todos&#039;, &#039;deportivos&#039;, &#039;super&#039;, &#039;sedán&#039;, &#039;suv&#039;, &#039;motocicleta&#039;].map(cat =&gt; (
                    <button
                        key="{cat}"
                        classname="{`category-btn" ${selectedcategory ="==" cat ? 'active' : ''}`}
 onclick="{()" > setSelectedCategory(cat)} &gt; {cat.toUpperCase()}
                    </button>
                ))}
            </div>

            <div classname="vehicles-grid">
                {filteredVehicles.map(vehículo =&gt; (
                    <vehiclecard 
                        key="{vehicle.model}"
                        vehicle="{vehicle}"
                        onselect="{()" > setSelectedVehicle(vehículo)} /&gt; ))}
            </div>

            {Vehículo seleccionado &amp;&amp; (
                <vehicledetails 
                    vehicle="{selectedVehicle}"
                    onpurchase="{purchaseVehicle}"
                    onclose="{()" > setSelectedVehicle(null)} /&gt; )}
        </div>
    );
};

Errores comunes de la IA y sus soluciones

Problema 1: Funciones nativas obsoletas

Asunto: La IA sugiere nativos de FiveM obsoletos Solución: Especifique siempre la compilación del juego en el mensaje:

Utilice solo nativos de FiveM compatibles con la compilación del juego 3095+. Reemplace los nativos obsoletos como GetPlayerPed(-1) con PlayerPedId()

Problema 2: Fugas de memoria en bucles

Asunto: La IA crea bucles infinitos sin una limpieza adecuada Solución: Solicitar patrones de recolección de basura:

-- Versión mejorada con IA con limpieza local activeThreads = {} función local createManagedThread(id, func) si activeThreads[id] entonces -- Eliminar hilo existente activeThreads[id] = falso fin activeThreads[id] = verdadero CreateThread(función() mientras activeThreads[id] do func() Wait(0) fin fin) fin -- Limpieza al detener el recurso AddEventHandler('onResourceStop', función(resourceName) si GetCurrentResourceName() == resourceName entonces para id, _ en pares(activeThreads) do activeThreads[id] = falso fin fin fin)

Problema 3: Problemas de sincronización

Asunto: Desajustes de estado entre cliente y servidor Solución: Utilice IA para generar gestión de estados:

-- Mensaje: "Crear un administrador de estados sincronizado para datos cliente-servidor" -- La IA genera: -- Administrador de estados del lado del servidor local StateManager = {} StateManager.states = {} function StateManager:set(key, value) self.states[key] = value TriggerClientEvent('stateManager:sync', -1, key, value) end function StateManager:get(key) return self.states[key] end RegisterNetEvent('stateManager:requestSync', function() local src = source TriggerClientEvent('stateManager:fullSync', src, StateManager.states) end) -- Administrador de estados del lado del cliente local ClientState = {} RegisterNetEvent('stateManager:sync', function(key, value) ClientState[key] = value end) RegisterNetEvent('stateManager:fullSync', function(states) ClientState = states end) -- Solicitar sincronización inicial CreateThread(function() Wait(1000) TriggerServerEvent('stateManager:requestSync') fin)

Estrategias de prueba e implementación

Pruebas automatizadas con IA

Genere conjuntos de pruebas completos:

-- Prompt: "Create unit tests for the vehicle shop purchase function"
-- AI generates:

local function runTests()
    local testResults = {
        passed = 0,
        failed = 0,
        tests = {}
    }

    -- Test 1: Valid purchase with sufficient funds
    local function testValidPurchase()
        local mockPlayer = {
            identifier = 'test_player_1',
            money = 50000,
            getMoney = function() return 50000 end,
            removeMoney = function(amount) 
                mockPlayer.money = mockPlayer.money - amount
                return true
            end
        }
        
        local result = attemptPurchase(mockPlayer, 'adder', 45000)
        assert(result.success == true, 'Valid purchase should succeed')
        assert(mockPlayer.money == 5000, 'Money should be deducted correctly')
        
        return true
    end

    -- Test 2: Purchase with insufficient funds
    local function testInsufficientFunds()
        local mockPlayer = {
            identifier = 'test_player_2',
            money = 1000,
            getMoney = function() return 1000 end
        }
        
        local result = attemptPurchase(mockPlayer, 'adder', 45000)
        assert(result.success == false, 'Purchase should fail with insufficient funds')
        assert(result.error == 'insufficient_funds', 'Should return correct error code')
        
        return true
    end

    -- Run all tests
    local tests = {
        {name = 'Valid Purchase', func = testValidPurchase},
        {name = 'Insufficient Funds', func = testInsufficientFunds}
    }

    for _, test in ipairs(tests) do
        local success, error = pcall(test.func)
        if success then
            testResults.passed = testResults.passed + 1
            print(('[^2✓^7] %s passed'):format(test.name))
        else
            testResults.failed = testResults.failed + 1
            print(('[^1✗^7] %s failed: %s'):format(test.name, error))
        end
        
        table.insert(testResults.tests, {
            name = test.name,
            passed = success,
            error = error
        })
    end

    return testResults
end

-- Run tests on resource start in development mode
if Config.Debug then
    CreateThread(function()
        Wait(1000)
        local results = runTests()
        print(('Tests completed: %d passed, %d failed'):format(
            results.passed, results.failed
        ))
    end)
end

Monitoreo del rendimiento

-- Prompt: "Add performance monitoring for production deployment"
-- AI generates:

local Monitor = {
    metrics = {
        eventCalls = {},
        dbQueries = {},
        memoryUsage = {}
    }
}

function Monitor:trackEvent(eventName)
    self.metrics.eventCalls[eventName] = (self.metrics.eventCalls[eventName] or 0) + 1
end

function Monitor:trackQuery(queryType, duration)
    table.insert(self.metrics.dbQueries, {
        type = queryType,
        duration = duration,
        timestamp = os.time()
    })
end

function Monitor:getReport()
    local report = {
        uptime = GetGameTimer() / 1000,
        totalEvents = 0,
        averageQueryTime = 0,
        memoryUsage = collectgarbage('count')
    }
    
    for _, count in pairs(self.metrics.eventCalls) do
        report.totalEvents = report.totalEvents + count
    end
    
    if #self.metrics.dbQueries > 0 then
        local totalTime = 0
        for _, query in ipairs(self.metrics.dbQueries) do
            totalTime = totalTime + query.duration
        end
        report.averageQueryTime = totalTime / #self.metrics.dbQueries
    end
    
    return report
end

-- Export metrics endpoint
RegisterCommand('metrics', function(source)
    if source == 0 or IsPlayerAceAllowed(source, 'admin.metrics') then
        print(json.encode(Monitor:getReport(), {indent = true}))
    end
end, true)

Integración con recursos existentes

Al agregar scripts generados por IA a servidores existentes con Scripts de ESX o scripts independientes, siga estos patrones de integración:

Dependencias de recursos

-- config.lua - AI-generated configuration for compatibility
Config = {}

-- Framework detection
Config.Framework = nil

CreateThread(function()
    if GetResourceState('es_extended') == 'started' then
        Config.Framework = 'esx'
        ESX = exports['es_extended']:getSharedObject()
    elseif GetResourceState('qb-core') == 'started' then
        Config.Framework = 'qbcore'
        QBCore = exports['qb-core']:GetCoreObject()
    else
        Config.Framework = 'standalone'
    end
    
    print(('[^2INFO^7] Detected framework: %s'):format(Config.Framework))
end)

-- Framework-agnostic money functions
function GetPlayerMoney(source)
    if Config.Framework == 'esx' then
        local xPlayer = ESX.GetPlayerFromId(source)
        return xPlayer.getMoney()
    elseif Config.Framework == 'qbcore' then
        local Player = QBCore.Functions.GetPlayer(source)
        return Player.PlayerData.money.cash
    else
        -- Standalone implementation
        return exports['your_economy']:GetMoney(source)
    end
end

Lista de verificación de mejores prácticas

Antes de implementar scripts generados por IA:

  • [ ] Validación de seguridad:Todos los eventos de cliente a servidor validados
  • [ ] Pruebas de rendimiento:No hay bucles sin Wait()
  • [ ] Gestión de la memoria: Limpieza adecuada al detener el recurso
  • [ ] Índices de bases de datos: Índices en columnas consultadas con frecuencia
  • [ ] Error ManejoBloques try-catch alrededor de operaciones críticas
  • [ ] Explotación florestal:Registro estructurado para depuración
  • [ ] Configuración: Valores de configuración externalizados
  • [ ] Documentación: README con los pasos de instalación
  • [ ] Control de versiones: Versiones semánticas en fxmanifest
  • [ ] Licencia:Se incluye el archivo de licencia correspondiente

Aviso optimizado para un agente de desarrollo de FiveM

Eres un desarrollador experto de FiveM con más de 5 años de experiencia en producción gestionando servidores de alta densidad. Tu experiencia abarca los principales frameworks y priorizas el código seguro y de alto rendimiento.

Conclusión

Las herramientas de inteligencia artificial como Claude Code transforman el desarrollo de FiveM de un proceso de aprendizaje de meses de duración a una creación de scripts productivos en cuestión de horas, lo que permite la creación rápida de prototipos y el desarrollo de sistemas complejos al tiempo que mantiene los estándares de seguridad y rendimiento.


¿Listo para mejorar tu servidor FiveM? Explora nuestra selección de guiones premium o sumergirse en soluciones independientes que complementan sus recursos generados por IA.

Lucas
Lucas

Soy Luke, gamer y me encanta escribir sobre FiveM, GTA y juegos de rol. Dirijo una comunidad de juegos de rol y tengo unos 10 años de experiencia administrando servidores.

Artículos: 436