Post

TPS 개발 : 트러블슈팅 — 크래프팅 탄 스왑 시 MaxStack 붕괴

테이블 없는 크래프팅 탄이 스왑 시 잔탄을 가방에 넣는 과정에서 MaxStack을 잃어버리는 버그를 추적한 기록

TPS 개발 : 트러블슈팅 — 크래프팅 탄 스왑 시 MaxStack 붕괴

TPS 개발 - 트러블슈팅: 크래프팅 탄 스왑 버그

크래프팅으로 만든 특수탄은 일반 탄과 데이터 구조 자체가 다르다. 이 차이가 탄 스왑 로직에서 꽤 복잡한 버그로 터졌다.


배경 — 크래프팅 탄은 테이블이 없다

크래프팅한 탄은 데이터 테이블이 없다. MaxStack은 1000으로 고정(Fix)해두고, 그 위에서 Definition이 재료(Material)의 효과 개수만큼 추가되는 식으로 만들어졌다.

1
2
3
4
크래프팅 탄
  ├─ 데이터 테이블 X (일반 탄과 다름)
  ├─ MaxStack: 1000 (고정값)
  └─ Definition: 재료 효과 수만큼 동적으로 늘어남

일반 탄은 CSV 테이블을 기준으로 값을 읽어오는데, 크래프팅 탄은 그 자리에 참고할 테이블이 없다. 이 차이가 나중에 문제의 근원이 된다.


잔탄 반환 로직

탄을 스왑할 때, 무기에 남아있던 잔탄(잔여 탄약)은 가방(인벤토리)으로 들어가도록 개발되어 있었다.

1
2
탄 스왑
  └─ 무기에 장전된 잔탄 → 가방으로 반환

문제는 여기서 시작됐다. 가방에 해당 탄이 0발이고, 무기에 남은 잔탄에 특수탄이 남아있는 상태에서 잔탄을 가방에 넣으려 하니, MaxStack을 제대로 못 받아오는 이슈가 생겼다.


방어 코드 → 방어 코드 → 새 버그

여기서부터 방어 코드가 방어 코드를 낳는 연쇄가 시작됐다.

1차 — “MaxStack 최소값은 1이다” 방어 코드와 충돌

예전에 넣어둔 “MaxStack의 최소값은 1이다”라는 방어 코드가 있었다. 이게 위 이슈와 맞물리면서, 잔탄이 1발만 들어오는 현상이 생겼다.

2차 — 총알을 다 쓰고 스왑할 때도 1발이 낀다

거기다 총알을 다 쓰고 일반 탄으로 스왑하는 과정에서도 무조건 1발이 들어오는 이상한 이슈가 함께 발생했다.

3차 — 1발 방어 코드 제거, 0으로 픽스

그래서 “최소 1발” 방어 코드를 아예 없앴다. 대신 0발로 픽스를 박았다.

1
2
Before: MaxStack 최소값 = 1  (→ 잔탄 1발 오염)
After : MaxStack 픽스값 = 0

진짜 문제 — Origin Table에 크래프팅 탄이 없다

0으로 픽스한 뒤, 진짜 버그가 드러났다.

상황: 가방 탄 0발, 총에 붙은 스페셜 아모(크래프팅 탄) 180발. 이 상태에서 다른 탄으로 스왑을 시도했다.

1
2
3
4
5
스왑 시도
  └─ 가방에 해당 탄 Definition이 없음
       └─ Origin Table을 참고해서 체크
            └─ Origin Table에도 크래프팅 탄 정의가 없음
                 └─ "최소 스택 0" 방어 코드 발동

결과가 꽤 복잡했다.

  • 탄 레이아웃(무기 쪽 표시) 에는 분명 탄이 있다고 뜬다.
  • 가방 에는 탄이 비었다고 뜬다.

같은 탄에 대해 두 곳의 상태가 서로 다르게 보이는, 겉보기엔 사소해도 원인 추적은 까다로운 버그였다. [배경]에서 언급했듯 크래프팅 탄은 애초에 데이터 테이블이 없는데, 인벤토리 로직이 정보가 없을 때 참고하는 곳이 하필 Origin Table이었던 게 근본 원인이었다.


해결 — 참고 대상을 Origin Table에서 탄 레이아웃 Definition으로

인벤토리가 정보를 잃었을 때 엉뚱한 곳(Origin Table) 을 참고하던 걸 바로잡았다.

  • 탄 레이아웃은 기존처럼 인벤에서 정보를 제공받는다. (역할 유지)
  • 인벤은 탄 레이아웃의 탄 정보가 없을 때, Origin Table이 아니라 탄 레이아웃 Definition을 직접 참고해서 인스턴스를 다시 관리할 수 있도록 변경했다.
1
2
3
4
5
Before
인벤 정보 없음 → Origin Table 참고 (크래프팅 탄 없음 → 붕괴)

After
인벤 정보 없음 → 탄 레이아웃 Definition 참고 → 인스턴스 재구성

크래프팅 탄은 테이블 기반이 아니라 런타임에 조립된 Definition이 유일한 진실이므로, 정보가 끊겼을 때 되돌아갈 곳도 그 Definition이어야 맞았다.


오늘 정리

  • 크래프팅 탄은 데이터 테이블이 없고 MaxStack 1000 고정 + Definition에 재료 효과 누적
  • 잔탄 반환 시 MaxStack을 못 가져오는 문제 → “최소 1발” 방어 코드와 얽혀 1발 오염 버그
  • 방어 코드 제거하고 0으로 픽스했더니, Origin Table에 크래프팅 탄이 없어 레이아웃/가방 상태 불일치 버그로 재발
  • 근본 해결: 정보 유실 시 참고 대상을 Origin Table → 탄 레이아웃 Definition으로 변경

방어 코드를 하나 걷어낼 때마다 다른 방어 코드와의 상호작용이 드러나는, 전형적인 “고칠수록 다른 데서 터지는” 버그였다.

This post is licensed under CC BY 4.0 by the author.