> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Schedule

이벤트 스케줄 기능




게임 내 이벤트 등의 스케줄을 관리하는 기능을 제공합니다.

GS2-Schedule 은 단독으로는 이벤트의 기간 정보만을 관리하는 기능을 가지고 있으며, 보상 지급이나 구매 제한과 같은 처리는 다른 마이크로서비스와 조합하여 실현합니다.
GS2-Showcase / GS2-Exchange / GS2-LoginReward / GS2-Mission 등 거의 모든 게임 요소 마이크로서비스가 이벤트 기간과의 연계를 전제로 설계되어 있기 때문에, 기간 한정 운영 시책을 실시할 때 핵심이 되는 마이크로서비스입니다.

## 이벤트 개최 기간

이벤트 개최 기간에는 두 가지 종류가 있습니다.
첫 번째는 모든 플레이어가 동일한 기간을 공유하는 "절대 기간", 두 번째는 플레이어마다 기간이 다른 "상대 기간"입니다.

```mermaid
graph TD
  Event["이벤트"]
  Event --> Absolute["절대 기간<br/>(scheduleType = absolute)"]
  Event --> Relative["상대 기간<br/>(scheduleType = relative)"]
  Absolute --> AbsoluteSample["예: 1월 1일~1월 3일의<br/>뉴이어 이벤트"]
  Relative --> RelativeSample["예: 첫 플레이로부터 7일간<br/>첫 보스 격파로부터 24시간"]
```

### 절대 기간

"1월 1일~1월 3일에 뉴이어 이벤트를 개최한다"와 같은 경우에 사용하는 기간 유형입니다.
모든 플레이어에게 동일한 개최 기간이 공유되므로, `absoluteBegin` / `absoluteEnd` 를 마스터 데이터에서 지정합니다.

### 상대 기간

"게임 시작으로부터 1주일" 이나 "처음 보스를 쓰러뜨린 후 24시간"처럼, 플레이어에 따라 이벤트 기간이 달라지는 경우에 사용하는 기간 유형입니다.

상대 기간을 실현하기 위한 "트리거"라는 구조가 있습니다.
트리거를 실행한 후, 지정된 기간(ttl)이 이벤트의 개최 기간으로 취급됩니다.

> [Gs2Schedule:TriggerByUserId 액션]()

#### 트리거를 당기는 방식의 종류

트리거를 당길 때, 이미 트리거가 당겨져 있는 경우 이벤트 기간을 지정하는 방식에는 여러 가지가 있습니다.

- 트리거를 재기동한다(renew)
- 트리거를 연장한다(extend)
- 아무것도 하지 않는다(drop)
- 이벤트의 반복 종료 일시에 유효 기간이 만료된다(repeatCycleEnd)
- 다음 반복 시작 일시에 유효 기간이 만료된다(repeatCycleNextStart)
- 절대 기간 이벤트의 종료 일시에 유효 기간이 만료된다(absoluteEnd)

repeatCycleEnd / repeatCycleNextStart / absoluteEnd 는 반복 설정이나 절대 기간을 가진 이벤트에 맞춰 유효 기간을 자동으로 조정하는 방식입니다.

2020년 1월 1일 00:00 에 트리거를 당겨 7일간의 상대 기간 이벤트가 시작된 경우, 2020년 1월 3일 00:00 에 트리거를 다시 당겼을 때의 각 동작은 다음과 같습니다.

| 방식 | 이벤트 종료 일시          |                                         |
| ---- |-------------------|-----------------------------------------|
| renew | 2020년 1월 10일 00:00  | 트리거를 기동한 시점인 2020년 1월 3일 00:00 에 7일간을 추가        |
| extend | 2020년 01월 14일 00:00 | 이미 존재하는 트리거의 유효 기간인 2020년 1월 7일 00:00 에 7일간을 추가 |
| drop | 2020년 01월 7일 00:00  | 이미 존재하는 트리거를 그대로 유지                       |

### 반복 설정

절대 기간·상대 기간 모두, `RepeatSetting` 을 조합함으로써 "특정 요일이나 시간대에만 유효", "며칠 주기로 유효·무효를 전환"과 같은 반복 스케줄을 표현할 수 있습니다.

설정 가능한 반복 유형은 다음과 같습니다.

| repeatType | 설명 |
| --- | --- |
| `always` | 기간 중 항상 유효 |
| `daily` | 매일 지정 시각~지정 시각 사이에만 유효(`beginHour` / `endHour`) |
| `weekly` | 매주 지정 요일~지정 요일 사이에만 유효(`beginDayOfWeek` / `endDayOfWeek`) |
| `monthly` | 매월 지정일~지정일 사이에만 유효(`beginDayOfMonth` / `endDayOfMonth`) |
| `custom` | 임의의 주기 일수로 유효·무효를 반복(`anchorTimestamp` / `activeDays` / `inactiveDays`) |

`custom` 유형은 "앵커 일시로부터 3일간 유효·4일간 무효"와 같은 임의의 반복 주기를 표현할 수 있어, 변칙적인 상시 이벤트 설계에 유용합니다.

#### 반복 스케줄 상태 취득

반복 설정을 가진 이벤트에 대해서는 현재 및 직전 반복 주기의 시작 일시·종료 일시를 `RepeatSchedule` 로 취득할 수 있습니다.
이를 통해 현재 주기 내에서 보상을 몇 번 수령했는지에 대한 집계나, 다음 주기까지의 남은 시간 표시가 가능합니다.

## 트랜잭션 액션

GS2-Schedule 에서는 다음의 트랜잭션 액션을 제공합니다.

- 검증 액션: 이벤트 개최 기간 중 검증, 이벤트 기간 외 검증, 트리거가 당겨져 있는지 검증, 트리거가 당겨져 있지 않은지 검증, 트리거를 당긴 후 경과 시간 검증
- 소비 액션: 트리거 삭제
- 입수 액션: 트리거 실행, 트리거 기간 연장

"이벤트 개최 기간 검증"을 검증 액션으로 이용함으로써, 특정 이벤트 기간 중에만 구매 가능한 상품이나 기간 한정 교환소와 같은 제한을 트랜잭션 내에서 안전하게 마련할 수 있습니다. 또한 "트리거 실행"을 입수 액션으로 이용함으로써, 퀘스트 클리어 시나 아이템 입수 시에 해당 플레이어만의 기간 한정 이벤트(상대 기간)를 시작시키는 등, 동적인 게임 경험 제어가 쉬워집니다.

## 마스터 데이터 관리

마스터 데이터를 등록함으로써 마이크로서비스에서 사용 가능한 데이터나 동작을 설정할 수 있습니다.

마스터 데이터의 종류에는 다음이 있습니다.

- `EventMaster`: 이벤트 기간과 반복 설정

다음은 마스터 데이터의 JSON 예시입니다.

```json
{
  "version": "2019-03-31",
  "events": [
    {
      "name": "newyear-2026",
      "metadata": "신년 이벤트",
      "scheduleType": "absolute",
      "absoluteBegin": 1735660800000,
      "absoluteEnd": 1735920000000,
      "repeatType": "always"
    },
    {
      "name": "tutorial-bonus",
      "metadata": "최초 플레이로부터 7일간 한정",
      "scheduleType": "relative",
      "relativeTriggerName": "tutorial-clear",
      "repeatType": "always"
    }
  ]
}
```

마스터 데이터의 등록은 관리 콘솔에서 등록하는 것 외에도, GitHub에서 데이터를 반영하거나, GS2-Deploy를 사용하여 CI에서 등록하는 워크플로우를 구성할 수도 있습니다.

## 구현 예제

### 트리거 당기기

트리거를 당기는 처리는 게임 엔진용 SDK 에서는 처리할 수 없습니다.

GS2-Account 의 계정 생성 시 스크립트 내에서, 또는 GS2-Quest 의 클리어 보상으로 트리거를 당기는 방법 등으로 구현하십시오.

### 개최 중인 이벤트 목록 취득

현재 플레이어 기준으로 개최 중인 이벤트만 반환됩니다.
절대 기간으로 개최 중이거나, 상대 기간으로 트리거가 당겨져 유효 기간 내에 있는 이벤트가 해당합니다.



**Unity**
```csharp

    var items = await gs2.Schedule.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).EventsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Schedule->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto It = Domain->Events(
    );
    TArray<Gs2::UE5::Schedule::Model::FEzEventPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.schedule.namespace_(
        "namespace-0001"
    ).me(
        game_session
    ).events(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 에러를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


### 특정 이벤트의 정보 취득

특정 이벤트 이름을 지정하여, 해당 이벤트의 개최 기간이나 반복 설정을 취득합니다.
`isInSchedule` 을 `true` 로 하면 현재 개최 중인 이벤트만, `false` 로 하면 마스터 데이터에 등록된 모든 이벤트를 참조할 수 있습니다.



**Unity**
```csharp

    var item = await gs2.Schedule.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Event(
        eventName: "event-0001"
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Schedule->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Event(
        "event-0001" // eventName
    );
    const auto Item = Domain->Model();
```
**Godot**
```gdscript

var domain = ez.schedule.namespace_(
        "namespace-0001"
    ).me(game_session).event(
        "event-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 트리거 목록 취득

플레이어에게 당겨져 있는 상대 기간 트리거의 목록과 유효 기간을 취득할 수 있습니다.



**Unity**
```csharp

    var items = await gs2.Schedule.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).TriggersAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Schedule->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto It = Domain->Triggers(
    );
    TArray<Gs2::UE5::Schedule::Model::FEzTriggerPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.schedule.namespace_(
        "namespace-0001"
    ).me(
        game_session
    ).triggers(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 에러를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


### 반복 스케줄 상태 취득

반복 설정을 가진 이벤트의 현재 반복 주기 정보를 취득합니다.
반복 횟수나, 현재 주기의 시작 일시·종료 일시, 직전 주기의 종료 일시를 확인할 수 있습니다.



**Unity**
```csharp

    var item = await gs2.Schedule.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Event(
        eventName: "event-0001"
    ).RepeatSchedule(
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Schedule->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Event(
        "event-0001" // eventName
    )->RepeatSchedule(
    );
    const auto Item = Domain->Model();
```
**Godot**
```gdscript

var domain = ez.schedule.namespace_(
    "namespace-0001"
).me(game_session).event(
    "event-0001"
).repeat_schedule()
var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return
var item = async_result.result

```


## 상세 레퍼런스

[GS2-Schedule API 레퍼런스](../../api_reference/schedule)



