Skip to content

Server Systems

Artmines edited this page Nov 3, 2025 · 1 revision

Server Systems

Complete technical reference for all server-side systems: database operations, event handling, medical profiles, commands, and caching.


Table of Contents

  1. Database System
  2. Medical Events System
  3. Medical Profile System
  4. Commands & Items
  5. Server-Side Caching

Database System

File: server/database.lua (713 lines)

What It Does

  • Handles all SQL operations
  • Manages data persistence for 5 tables
  • Logs medical history events
  • Provides stored procedures for optimization

Tables Managed

  1. player_wounds - Active wounds and scars
  2. medical_treatments - Current active treatments
  3. player_infections - Active infections
  4. player_fractures - Bone fractures/breaks
  5. medical_history - Complete audit trail

See Database Schema for complete table structure.

Key Functions

-- Save wound data (UPSERT query)
exports['QC-AdvancedMedic']:SaveWoundData(citizenid, woundData)

-- Load complete medical profile
local profile = exports['QC-AdvancedMedic']:GetCompleteMedicalProfile(citizenid)
-- Returns: {wounds, treatments, infections, fractures, scars, history}

-- Log medical event
exports['QC-AdvancedMedic']:LogMedicalEvent(citizenid, eventType, bodyPart, details, performedBy)

Event Types Logged

  • wound_created - New injury occurred
  • wound_scarred - Wound healed to scar
  • treatment_applied - Bandage/medicine applied
  • treatment_removed - Treatment expired
  • infection_started - Infection began
  • infection_cured - Infection cured
  • medical_inspection - Medic examined player
  • admin_clear_wounds - Admin cleared all wounds

Note

Medical history is automatically cleaned up after 30 days to prevent database bloat. Use the CleanupExpiredMedicalData() stored procedure.


Medical Events System

File: server/medical_events.lua (684 lines)

What It Does

  • Handles 40+ network events between client/server
  • Validates medic permissions
  • Syncs medical data in real-time
  • Broadcasts updates to nearby players

Key Event Categories

Data Synchronization

  • UpdateWoundData - Client sends wound changes
  • UpdateTreatmentData - Client sends treatment changes
  • UpdateInfectionData - Client sends infection changes
  • LoadMedicalData - Client requests data on spawn

Medical Actions

  • ApplyTreatment - Medic treats patient
  • TreatInfection - Medic applies cure
  • StartMedicalInspection - Medic inspects patient
  • CompleteMedicalInspection - Medic finishes inspection

Broadcasting to Nearby Medics

When wounds change, nearby medics (within 10m) are notified automatically:

-- Find nearby medics
for _, player in pairs(RSGCore.Functions.GetPlayers()) do
    local targetCoords = GetEntityCoords(GetPlayerPed(player))
    if #(coords - targetCoords) < 10.0 then
        if IsMedicJob(RSGCore.Functions.GetPlayer(player).PlayerData.job.name) then
            TriggerClientEvent('QC-AdvancedMedic:client:UpdateNearbyPlayerWounds', player, source, woundData)
        end
    end
end

Important

All treatment applications require job validation via IsMedicJob() to prevent non-medics from treating others.


Medical Profile System

File: server/medical_server.lua (929 lines)

What It Does

  • Maintains server-side cache of player medical data
  • Handles /inspect command for medics
  • Manages automatic wound progression
  • Processes natural pain healing

Server-Side Cache

PlayerMedicalData[playerId] = {
    citizenid = "ABC12345",
    wounds = {...},
    treatments = {...},
    infections = {...},
    bandages = {...},
    lastSync = os.time()  -- Last database sync time
}

Cache Management:

  • Initialized on player connect
  • Updated on every medical event
  • Synced to database every 5 minutes
  • Saved on player disconnect

The /inspect Command

Command: /inspect [player_id] Permission: Medic jobs only (validated via IsMedicJob()) Distance Limit: 10 meters

Flow:

  1. Medic executes /inspect 5
  2. Server validates:
    • Medic has valid job
    • Target player exists
    • Distance is within 10m
  3. Server fetches medical profile from cache (or database if cache empty)
  4. Server sends data to medic client
  5. NUI opens showing complete medical status
  6. Inspection is logged to medical_history table

Data Sent to Medic:

{
    playerName = "John Doe",
    playerId = "ABC12345",
    playerSource = 5,
    wounds = {
        ["HEAD"] = {painLevel = 5, bleedingLevel = 3, metadata = {...}},
        ["LARM"] = {painLevel = 2, bleedingLevel = 1, metadata = {...}}
    },
    treatments = {
        ["HEAD"] = {treatmentType = "bandage", itemType = "cotton", appliedTime = 1234567890}
    },
    infections = {
        ["LARM"] = {stage = 2, startTime = 1234567000}
    },
    inspectedBy = "XYZ67890",
    inspectionTime = 1700000000
}

Tip

Medics can see ALL medical data including hidden infections and exact wound severity, not just visible symptoms.

Server-Side Wound Progression

Bleeding Progression (every 1 minute):

  • Untreated wounds: 15% chance to increase bleeding by 1 level
  • Only affects wounds without active bandage
  • Caps at bleeding level 10 (max)

Natural Pain Healing (every 5 minutes):

  • All wounds: Pain decreases by 1 level
  • Simulates natural recovery over time
  • Only affects pain, not bleeding

Note

Server-side progression is SEPARATE from client-side progression. Both run in parallel for redundancy.


Commands & Items

File: server/server.lua (549 lines)

What It Does

  • Registers all admin/medic commands
  • Creates useable items dynamically from config
  • Manages duty pay system (10-minute intervals)
  • Handles pharmaceutical shop purchases

Admin Commands

Command Permission Description
/revive [id] admin Revive player (self if no ID)
/heal [id] admin Heal player to full HP
/kill [id] admin Kill target player
/clearwounds [id] admin Clear all wounds, treatments, infections, fractures
/medwounds [id] admin View player medical data count

Medic Commands

Command Permission Description
/inspect [id] Medic jobs Inspect patient medical condition

Dynamic Item Registration

Items are created automatically from Config:

-- Bandages (from Config.BandageTypes)
for bandageType, config in pairs(Config.BandageTypes) do
    RSGCore.Functions.CreateUseableItem(config.itemName, function(source, item)
        TriggerClientEvent('QC-AdvancedMedic:client:usebandage', source, bandageType)
    end)
end

Items Created:

  • Bandages: cloth_band, cotton_band, linen_band, sterile_band
  • Cure Items: antibiotics, alcohol, cocaine (from InfectionSystem.cureItems)
  • Medicines: medicine_laudanum, medicine_morphine, medicine_whiskey, medicine_quinine
  • Medical Bag: medicalbag

Duty Pay System

How It Works:

  1. Medic goes on duty
  2. Client triggers StartDutyPayTimer event
  3. Server pays every 10 minutes
  4. On duty end, partial pay calculated for remaining minutes

Pay Rates by Grade:

Grade Title Pay Rate
0 Recruit $1/hour
1 Trainee $2/hour
2 Pharmacist $3/hour
3 Doctor $4/hour
4 Surgeon $6/hour
5 Manager/Boss $8/hour

Payment Calculation:

-- Every 10 minutes
local payAmount = math.floor((hourlyRate * 10) / 60)

-- Partial pay (< 10 minutes)
if remainingMinutes >= 5 then
    local partialPay = math.floor((hourlyRate * remainingMinutes) / 60)
end

Note

Duty pay is automatically stopped on disconnect. Timers are cleaned up to prevent memory leaks.


Server-Side Caching

Purpose

Reduce database queries by ~90% through in-memory caching.

Cache Structure

PlayerMedicalData = {
    [playerId] = {
        citizenid = "ABC12345",
        wounds = PlayerWounds,         -- Copy of client wounds
        treatments = ActiveTreatments, -- Copy of client treatments
        infections = PlayerInfections, -- Copy of client infections
        bandages = BandageTracker,     -- Legacy bandage tracking
        lastSync = os.time()           -- Last database sync time
    }
}

Cache Lifecycle

  1. Initialization: When player connects

    RegisterNetEvent('RSGCore:Server:OnPlayerLoaded', function()
        local src = source
        LoadPlayerMedicalDataToCache(src)
    end)
  2. Updates: On every medical event

    RegisterNetEvent('QC-AdvancedMedic:server:UpdateWoundData', function(woundData)
        PlayerMedicalData[source].wounds = woundData
        PlayerMedicalData[source].lastSync = os.time()
    end)
  3. Periodic Sync: Every 5 minutes to database

    CreateThread(function()
        while true do
            Wait(300000) -- 5 minutes
            SyncAllPlayerDataToDatabase()
        end
    end)
  4. Cleanup: On player disconnect

    RegisterNetEvent('RSGCore:Server:OnPlayerUnload', function()
        local src = source
        SavePlayerMedicalData(src) -- Final save
        PlayerMedicalData[src] = nil -- Clear cache
    end)

Performance Impact

Without Cache:

  • Every wound update = 1-2 database queries
  • Every treatment = 1-2 database queries
  • Every inspection = 5-10 database queries
  • ~50 queries/minute per active player

With Cache:

  • Most operations = 0 database queries (cached)
  • Periodic sync = 1 batch query per player
  • ~5 queries/minute per active player

Result: 90% reduction in database load


Job Validation

IsMedicJob() Function

Used throughout the system to validate medic permissions:

function IsMedicJob(jobName)
    local medicJobs = {
        'doctor',
        'medic',
        'surgeon',
        -- Add your medic jobs here
    }
    for _, job in ipairs(medicJobs) do
        if job == jobName then
            return true
        end
    end
    return false
end

Used For:

  • /inspect command permission
  • Treatment application validation
  • Nearby medic broadcasting
  • Duty pay eligibility

← Client Systems | Next: Database Schema →

📖 QC-AdvancedMedic

🏠 Home


📚 Documentation

  1. Architecture
  2. Client Systems
  3. Server Systems
  4. Database Schema

⚙️ Configuration

  1. Configuration
  2. Translation System
  3. API Reference

🛠️ Development

  1. Extending the System
  2. Performance Optimization

⚠️ Support

  1. Known Issues

🔗 Links


v0.3.1-alpha

Clone this wiki locally