GTA San Andreas 온라인 서버 구축 가이드: open.mp 환경과 Pawn 언어 핵심 기능

open.mp 플랫폼 개관 및 개발 환경 설정

open.mp 는 Grand Theft Auto: San Andreas 멀티플레이어 환경을 위한 현대적인 서버 솔루션입니다. 기존 SA-MP 와 완벽하게 하위 호환되면서, 더 안정적이고 확장 가능한 아키텍처를 제공합니다. 서버 로직 구현에는 C 형 문법을 가진 경량 스크립팅 언어인 Pawn 이 사용됩니다.

이 가이드는 복잡한 프로그래밍 이론보다는 실제 게임 내 상호작용을 구현하는 데 필요한 실용적인 기능을 중점적으로 다룹니다.

서버 시작 전 준비 사항

1. 오픈소스 패키지 다운로드

공식 웹사이트에서 최신 빌드판을 취득하여 압축을 해제합니다. 주요 디렉터리 구조는 다음과 같습니다:

components     // 서버 코어 라이브러리
gamemodes      // 메인 모드 (.amx) 파일 저장 위치
filterscripts  // 보조 필터스크립트
plugins        // 추가 기능 플러그인
qawno          // 공식 소스 에디터 및 컴파일러
config.json    // 서버 기본 설정 파일
omp-server.exe // 서버 실행 파일

2. 설정 파일 구성

config.json 파일을 수정하여 서버 정보를 정의합니다. 특히 주 스크립트 경로와 네트워크 포트를 명시해야 합니다.

{
  "name": "개발 테스트 서버",
  "network": {
    "port": 7777
  },
  "max_players": 32,
  "pawn": {
    "main_scripts": ["gm_test"]
  },
  "rcon": {
    "password": "secure_rcon_pass"
  }
}

대부분의 옵션은 기본값으로 유지하되, 포트 충돌이 발생하지 않도록 확인해야 합니다.

3. 코드 작성 도구 선택

기본 제공되는 Qawno 를 사용하거나, 고급 자동완성 기능이 필요한 경우 Visual Studio Code 를Pawn 플러그인과 함께 활용할 수 있습니다. 초보자 환경 구축에는 Qawno 가 충분합니다.


Pawn 기초 문법 및 데이터 관리

최초의 행 파일 생성

에디터에서 새 프로젝트를 생성하고, 필수 헤더 파일을 포함시킨 후 메인 진입점을 정의합니다.

#include <A_samp>

public main()
{
    print("서버가 초기화되었습니다.");
}

// 서버 모드 초기화 이벤트
public OnGameModeInit()
{
    SetGameModeText("첫 번째 서버 프로젝트");
    // 플레이어 클래스 정의: SkinID, 좌표 X,Y,Z, 방향도, 총기 등
    AddPlayerClass(1, 1800.5, -1400.2, 15.0, 0.0, WEAPON_PISTOL, 10);
    
    // 정적 차량 배치
    AddStaticVehicle(411, 1805.0, -1395.0, 15.0, 90.0, -1, -1);
    
    return 1;
}

이제 이를 gamemodes/gm_test.pwn 로 저장하고 F5 를 눌러 컴파일이 잘 되는지 확인합니다. 성공하면 .amx 파일이 생성되며, 이를 서버 폴더에 배치하고 실행 프로그램을 띄웁니다.

주석 처리 및 변수 선언

코드 가독성을 위해 주석을 남기는 습관을 들이세요. 컴파일 시 주석은 무시됩니다.

  • //: 단행 주석
  • /* ... */: 범주석

변수는 메모리 영역에 데이터를 보관하는 공간입니다.

new n_LocalVar;           // 기본 정수형 변수
new Float:f_Speed = 0.0;  // 부동 소수점 타입 지정 필요
new g_GlobalScore[32];    // 전역 배열 (함수 외부에서 선언)

변명 규칙으로는 숫자로 시작하지 않으며, 전역 변수 앞에 g_, 지역 변수 앞에 일반 이름을 사용하는 것이 관례입니다.

상수 매핑 (Define)

코드 전반에 걸쳐 특정 값이 반복될 때는 상수로 정의하여 유지보수성을 높입니다. 이는 컴파일 단계에서 문자열 치환 형태로 동작합니다.

#define SERVER_MAX_LEVEL  50
#define PLAYER_START_FUND 1000

public OnPlayerConnect(playerid)
{
    g_PlayerCash[playerid] = PLAYER_START_FUND;
    return 1;
}

문자열 및 서식 처리

Pawn 에서 문자열은 배열로 관리되며, 끝에 널 문자가 필요합니다.

new szBuffer[128];
new szName[MAX_PLAYER_NAME];
GetPlayerName(playerid, szName, sizeof(szName));

// 변수 삽입을 위한 format 사용
format(szBuffer, sizeof(szBuffer), "환영합니다, %s 님!", szName);
SendClientMessage(playerid, 0xFFFFFFFF, szBuffer);

%s 는 문자열, %d 는 정수, %f 는 실수를 표현할 때 사용합니다.


제어 흐름 및 알고리즘 구조

조건 분기 (If / Else)

논리 연산자를 사용하여 상황별로 다른 동작을 수행하게 할 수 있습니다.

if (g_Level[playerid] > 10 && g_IsAdmin[playerid])
{
    SendClientMessage(playerid, 0xFF0000FF, "관리자 권한이 적용됨");
}
else if (g_Level[playerid] >= 5)
{
    SendClientMessage(playerid, 0x00FF00FF, "일반 멤버입니다");
}
else
{
    SendClientMessage(playerid, 0xFFFFFF00, "신규 사용자");
}

비교 시 = 가 아닌 == 를 반드시 사용해야 합니다.

다중 선택 (Switch)

한 가지 변수의 다양한 값에 따른 분기를 명확히 하기 위해 switch 문을 사용합니다. 대화창 응답 처리 등에 유용합니다.

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if (dialogid != DIALOG_STORE_ITEMS) return 1;
    if (!response) return 1; // 취소 버튼 클릭

    switch(listitem)
    {
        case 0: 
            GivePlayerWeapon(playerid, WEAPON_RIFLE, -1);
            SendClientMessage(playerid, COLOR_YELLOW, "총기 구매 완료");
        case 1:
            GivePlayerWeapon(playerid, WEAPON_SNIPERRIFLE, -1);
            break;
        default:
            Print("[ERROR] Unknown item selected");
    }
    return 1;
}

반복문 (Loops)

모든 접속된 플레이어를 순회하거나 조건 충족 때까지 대기하는 로직에 사용됩니다.

// 모든 플레이어 대상 메시지 발송 (foreach 대신 기본 예시)
for(new i = 0; i < MAX_PLAYERS; i++)
{
    if(IsPlayerConnected(i))
    {
        SendClientMessage(i, COLOR_WHITE, "[알림] 시스템 점검 예정입니다.");
    }
}

// 상태 변경까지 대기
new nCounter = 0;
while(nCounter < 10)
{
    Sleep(1000);
    nCounter++;
}

함수 모듈화

코드 재사용을 위해 논리를 별도 함수로 분리합니다.

// 보너스 지급 함수
stock void:GrantBonus(playerid, amount)
{
    g_Cash[playerid] += amount;
    ShowMessageToPlayer(playerid, "보너스를 받았습니다.");
}

// 호출
public OnPlayerSpawn(playerid)
{
    GrantBonus(playerid, 500);
    return 1;
}

게임 인터랙션 요소 구현

텍스트 UI 출력

채팅 창 전송

SendClientMessage 는 색상 코드를 지원하며, RGBA 형태의 16 진수 (0xRRGGBBAA) 를 사용합니다.

SendClientMessage(playerid, 0xFF0000FF, "경고"); // 빨간색
SendClientMessage(playerid, -1, "메세지");       // -1 은 흰색의 줄임말

화면 텍스트 (GameText)

화면 중앙 상단에 표시되며 일정 시간 후 사라집니다. 한글 지원되지 않습니다.

GameTextForPlayer(playerid, "~y~Mission Start!", 3000, 3);

스타일 코드는 1 부터 다양하게 존재하며, 색상 태그(~w~, ~r~, ~g~) 를 조합할 수 있습니다.

고정 텍스트 (TextDraw)

HUD 와 같이 화면에 영구적으로 고정되어 있는 요소를 만듭니다.

new Text:g_HUD_Cash;

public OnGameModeInit()
{
    g_HUD_Cash = TextDrawCreate(580.0, 50.0, "$0");
    TextDrawLetterSize(g_HUD_Cash, 0.300000, 1.000000);
    TextDrawColor(g_HUD_Cash, -1);
    return 1;
}

public OnPlayerSpawn(playerid)
{
    TextDrawShowForPlayer(playerid, g_HUD_Cash);
    return 1;
}

대화 상자 (Dialog)

게임 내 입력창이나 메뉴를 팝업합니다. 고유 ID 를 통해 응답을 구분합니다.

// 정의
enum { DLG_LOGIN, DLG_SHOP };

// 표시
ShowPlayerDialog(playerid, DLG_LOGIN, DIALOG_STYLE_INPUT, "접속", 
                 "비밀번호를 입력하세요:", "확인", "취소");

// 응답 처리
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    if(dialogid == DLG_LOGIN && response)
    {
        // 인증 로직 처리...
    }
    return 1;
}

환경 객체 조작

픽업 아이템 (Pickup)

플레이어가 닿으면 이벤트를 발생시키는 물체입니다.

public OnGameModeInit()
{
    CreatePickup(1240, 2, 1234.5, -567.8, 10.2, -1); // 모델 1240(심장 모양)
    return 1;
}

public OnPlayerPickUpPickup(playerid, pickupid)
{
    // 피킹 업 핸들링
    return 1;
}

체크포인트 (Checkpoint)

플레이어가 이동해야 도달하는 지점입니다. 붉은 빛 원으로 표시됩니다.

public OnEnterMission(playerid)
{
    SetPlayerCheckpoint(playerid, 1000.0, 2000.0, 10.0, 5.0);
    return 1;
}

public OnPlayerEnterCheckpoint(playerid)
{
    DisablePlayerCheckpoint(playerid);
    SendClientMessage(playerid, COLOR_GREEN, "도착했습니다!");
    return 1;
}

차량 (Vehicle)

운행 가능한 자동차나 항공기를 배치합니다.

new vCar;
public OnGameModeInit()
{
    vCar = CreateVehicle(411, 0.0, 0.0, 1.0, 0.0, 1, 0, -1); // 411=Infernus
    return 1;
}

public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
    if(ispassenger == 0)
        SendClientMessage(playerid, COLOR_BLUE, "운전석이었습니다.");
    return 1;
}

NPC 액터 (Actor)

세계 내에서 정적인 캐릭터 역할을 수행합니다.

new actorSmith;
public OnGameModeInit()
{
    actorSmith = CreateActor(179, 100.0, 100.0, 10.0, 0.0); // Skin 179
    ApplyActorAnimation(actorSmith, "DEALER", "shop_pay", 4.0, false, false, false, false, 0);
    return 1;
}

시간 제어 및 입력 감지

타이머 시스템

특정 시간 간격으로 혹은 지연하여 함수를 호출합니다.

public OnGameModeInit()
{
    // 10 초마다 반복 실행
    SetTimer("RoutineCheck", 10000, true);
    return 1;
}

forward RoutineCheck();
public RoutineCheck()
{
    SendClientMessageToAll(COLOR_WHITE, "[시스템] 정기 점검 완료");
    return 1;
}

// 파라미터 전달 가능 타이머
SetTimerEx("NotifyUser", 5000, 0, "i", playerid);
forward NotifyUser(p);
public NotifyUser(p) { /* 로직 */ }

키보드 입력 감지

플레이어의 키 상태 변화를 통해 인터랙션을 유도합니다.

public OnPlayerKeyStateChange(playerid, keys, oldkeys)
{
    // Y 건 (YES) 또는 E 건 감지
    if((keys & (1<<20)) && !(oldkeys & (1<<20))) // 예제용 비트 연산
    {
        SendClientMessage(playerid, COLOR_WHITE, "인터랙션 키 활성화");
        
        // 인근 NPC 가 있으면 대꾸하기
        if(DistanceToNPC(playerid) < 3.0)
        {
            ShowDialog(playerid, Dlg_NpcTalk);
        }
    }
    return 1;
}

태그: open.mp Pawn gta-san-andreas multiplayer-server samp-scripting

7월 27일 16:23에 게시됨