변화하는 플레이어 데이터와 함께 변화하는 디자이너 데이터를 관리하는 방법


14

플레이어가 어떤 방식 으로든 세상을 형성 할 수있는 온라인 게임이 있습니다 (예 : Ultima Online의 주택으로, 세계지도의 특정 부분에 직접 주택을 건설 할 수 있습니다. 이러한 변화는 지속적 세계의 일부로 시간이 지남에 따라 지속되어야하는 변화입니다.

동시에 디자인 팀은 새로운 플레이어를 위해 게임을 개선하고 확장하기 위해 새로운 콘텐츠를 추가하고 오래된 콘텐츠를 수정하고 있습니다. 테스트하는 동안 개발 서버에서 먼저이 작업을 수행 한 다음 작업을 라이브 서버의 플레이어 "작업"과 병합해야합니다.

게임 디자인 문제를 해결한다고 가정합니다 (예 : 플레이어는 지정된 영역에서만 빌드 할 수 있으므로 디자이너 편집과 지리적으로 충돌하지 않습니다. 새로운 디자이너 데이터가 새로운 플레이어 데이터와 병합 될 때 충돌을 피하기 위해 데이터를 처리하거나 데이터 구조를 배열하는 좋은 방법은 무엇입니까?

예 1 : 플레이어가 새로운 유형의 아이템을 제작하고 게임에 ID 123456이 할당됩니다. 해당 항목의 인스턴스는 모두 123456으로 다시 나타납니다. 이제 게임 디자이너가 비슷한 시스템을 가지고 있고 디자이너가 123456이라는 새 항목을 생성한다고 상상해보십시오. 어떻게 피할 수 있습니까?

예 2 : 누군가가 모든 드래곤에게 프랑스 악센트를주는 인기있는 모드를 만듭니다. 여기에는 assignFrenchAccent각 용 객체에 새 음성 자산을 할당하는 데 사용되는 새 객체가 포함 된 스크립트가 포함되어 있습니다 . 그러나 같은 이름의 개체를 가진 "Napoleon vs Smaug"DLC를 배포하려고합니다. 고객 서비스 문제없이이 작업을 어떻게 수행 할 수 있습니까?

나는 다음과 같은 전략을 생각했습니다.

  • 2 개의 개별 파일 / 디렉토리 / 데이터베이스를 사용할 수 있지만 읽기 작업이 상당히 복잡합니다. "모든 항목 표시"는 디자이너 DB에서 하나의 읽기와 플레이어 DB에서 하나의 읽기를 수행해야합니다 (여전히 2를 구분해야합니다).
  • 예를 들어, 한 상점에서 2 개의 서로 다른 네임 스페이스를 사용할 수 있습니다. 문자열을 기본 키로 사용하고 "DESIGN :"또는 "PLAYER :"로 접두사를 붙이지 만 네임 스페이스를 만드는 것은 쉽지 않으며 종속성이 명확하지 않습니다. (예 : RDBMS에서는 문자열을 기본 키로 효율적으로 사용하지 못할 수 있습니다. 정수를 사용하고 특정 숫자 (예 : 1 백만) 아래의 모든 기본 키를 디자이너 데이터로 할당 할 수 있으며 그 위의 모든 것은 그러나이 정보는 RDBMS에 보이지 않으며 외래 키 링크는 '분할'을 가로 지르므로 모든 툴링 및 스크립트가 명시 적으로 해결해야합니다.)
  • 항상 동일한 공유 데이터베이스에서 실시간으로 작업 할 수 있지만 성능이 저하되고 플레이어 데이터의 손상 위험이 향상 될 수 있습니다. 또한 세계 데이터가 다른 두 대 이상의 서버에서 실행되는 게임으로 확장되지 않습니다.
  • ... 다른 아이디어?

온라인 게임에서는 이것이 주로 문제이지만 개념이 모딩에도 적용될 수 있습니다. 커뮤니티에서 개발자가 게임을 패치하는 동시에 모드를 만듭니다. 새로운 패치가 나올 때 모드가 깨질 가능성을 줄이기 위해 여기에 어떤 전략이 사용됩니까?

또한 한 수준에서 병합이 필요한 데이터 개발의 두 가지 분기이기 때문에이 버전을 "버전 제어"로 태그 지정했습니다. 아마도 그 통찰력이 그 방향에서 나올 수도 있습니다.

편집-문제를 명확히하기 위해 위에 추가 된 몇 가지 예. 문제가 실제로 이름 공간화 중 하나라고 생각하기 시작했습니다. 이는 복합 키를 통해 상점에서 구현할 수 있습니다. 그것은 적어도 병합 전략을 단순화시킵니다. 그러나 내가 보지 못하는 대안이있을 수 있습니다.


1
이에 대한 대답은 디자이너가 추가하는 데이터 유형과 플레이어가 추가하는 데이터에 적어도 부분적으로 달려 있다고 생각합니다.
lathomas64

그것은 가능하지만 단일 데이터 저장소에 기여하는 두 당사자에 대한 일반적인 솔루션에 더 관심이 있습니다.
Kylotan

1
많은 사람들이이 질문의 요점을 놓치고 있습니다. 충돌하는 플레이어의 변경은 아닙니다. 오히려 컨텐츠 업데이트 등 기존 레이아웃을 깨는 것입니다. @ 킬로 탄 내가 맞아?
Jonathan Dickinson

개발자 콘텐츠 업데이트가 플레이어 콘텐츠 업데이트와 충돌 할 수있는 곳입니다. 저는 게임 디자인 해결 방법 (예 : 특정 장소에서만 플레이어를 구축하도록 허용)에 관심이 없지만 데이터 구조 해결 방법 (예 : 플레이어가 ID가 백만 이상인 물건을 생성하도록 허용)에 관심이 있습니다.
Kylotan

2
지정하지 않았습니다. 실시간으로 실시간 업데이트를 기대하십니까? 참고 사항 : 단일 데이터 저장소에 기여하는 2 명의 당사자는 데이터베이스입니다. 즉, 데이터베이스가하는 일이므로 그 사실을 해결할 수 없으며 공유 데이터의 문제를 피하는 방법에 대한 수십 년의 지식을 무시하는 것은 어리석은 일입니다.
Patrick Hughes

답변:


2

DB 솔루션을 제안하는 답변이 문제를 이해하지 않고 특정 구현으로 뛰어 들었다고 생각합니다. 데이터베이스는 쉽게 병합 할 수 없으며 데이터를 저장할 수있는 프레임 워크 만 제공합니다. DB에 있더라도 충돌은 여전히 ​​충돌입니다. 그리고 체크 아웃은 문제에 대한 가난한 사람의 해결책입니다. 작동하지만 유용성에 치명적인 비용이 듭니다.

여기서 말하는 것은 문제의 분산 개발 모델에 해당합니다. 내가 믿는 첫 번째 단계는 플레이어와 디자이너를 별도의 유형의 콘텐츠 제작자로 생각하지 않는 것입니다. 솔루션에 영향을 미치지 않는 문제의 인공적인 차원을 제거합니다.

사실상 개발자가 승인 한 정식 버전 인 기본이 있습니다. 아마도 다른 브랜치가있을 수도 있습니다. 라이브 서버는 사람들이 적극적으로 모드를 개발하고 공유하고있었습니다. 모든 지점에 컨텐츠를 추가 할 수 있습니다. 결정적으로, 디자이너는 여기서 특별한 것이 없습니다. 디자이너는 사내에서 사는 콘텐츠 제작자 일뿐입니다.

그런 다음 사용자 생성 콘텐츠를 수락하는 것이 표준 병합 문제입니다. 변경 사항을 메인 라인으로 다시 가져 와서 병합 한 다음 다시 밀어 내거나 메인 라인 변경 사항을 브랜치로 끌어와 병합해야합니다 (사용자 생성 항목의 메인 라인 '깨끗한 상태'). 평소와 같이 지점을 당기고 수정하는 것은 다른 사람들에게 변경 사항을 가져 와서 끝까지 원격으로 수정하는 것보다 친절합니다.

이러한 종류의 모델로 작업하면 병합 충돌 방지에 대한 모든 일반적인 프로세스가 적용됩니다. 더 명백한 것 중 일부 :

  • 네임 스페이스를 자유롭게 사용하여 특정 저자 / 모드 / 팀의 컨텐츠를 울타리로 묶으십시오.
  • 컨텐츠가 상호 작용해야하는 경우 명확한 호출 / 사용 규칙, 이름 지정 규칙 및 개발을 안내하는 느슨한 '규칙'을 설정하여 쉽게 병합 할 수 있습니다. 컨텐츠 제작자가 규칙을 따르고 있는지를 컨텐츠 제작 자체에 이상적으로 통합 할 수있는 도구를 제공하십시오.
  • 보고 / 분석 도구를 제공하여 가능한 병합 실패가 발생하기 전에 파악하십시오. 병합 후 수정하는 것은 아마도 매우 고통 스럽습니다. 특정 비트의 내용을 확인하고 병합 준비가 된 상태로 명확하게 표시 할 수 있도록하여 병합에 어려움이 없도록하십시오.
  • 통합 / 통합 강화 쉽게 롤백 할 수 있습니다. 병합 된 컨텐츠에 대한 엄격한 테스트를 수행하십시오. 테스트에 실패하면 병합하지 마십시오! 병합이 깨끗하게 진행될 때까지 컨텐츠 또는 사용자 컨텐츠를 반복하십시오.
  • 증분 정수 ID를 사용하지 마십시오 (생성자에게 신뢰할 수있는 방법은 없습니다). DB 자체는 정식 ID 공급자이므로 중복되지 않기 때문에 DB에서만 작동합니다. 그러나 시스템에 단일 장애 지점 /로드가 발생합니다.
  • 대신 GUID를 사용하십시오. 저장 비용이 많이 들지만 기계마다 다르므로 충돌이 발생하지 않습니다. 또는 문자열 식별자를 사용하면 디버깅 / 해결이 훨씬 쉽지만 저장 및 비교에 더 비쌉니다.

슬프게도이 중 일부는 내 문제에 도움이되지 않습니다 (예 : 플레이어가 특정 규칙을 따르도록하는 것 (이 모든 것이 서버 측에서 자동으로 수행되어야 함)). 병합 관리 및 트랜잭션 정도를 지원하는 것이 실용적이지 않다고 생각합니다 언급 한 의미론이지만 보장 된 고유 ID (아마도 GUID)를 할당하는 일반적인 접근 방식은 아마도 내가 함께 할 것에 가장 가깝습니다.
Kylotan 2012

아, 알겠습니다 건물 도구를 제어하기 때문에 병합에 적합한 접근 방식 (네임 스페이스 등)을 적용하는 것은 플레이어의 동의없이 할 수있는 일입니다.
MrCranky

두 명의 플레이어가 중복 콘텐츠를 만들면 어떻게합니까? 아니면 게임 월드의 개별 인스턴스가 고유 한 것으로 취급됩니까? 이 경우 코어 / 메인 라인 지점에 대해 알고있는 모든 고유 한 인스턴스를 해당 변경 사항을 인스턴스로 푸시 할 때 발생할 수있는 충돌을 자동으로 확인하는 것이 유용한 방법 일 수 있습니다. 플레이어를 제어 할 수없는 경우 최소한 개발 초기에 내부 팀이 수행중인 작업이 세계의 인스턴스 X와 충돌한다고 경고 할 수 있습니다.
MrCranky

네임 스페이스의 개념은 그다지 문제가되지 않습니다. 가능한 모든 네임 스페이스의 네임 스페이스에서 적절한 네임 스페이스를 선택하는 것이 중요합니다! :) 그리고 나에게 콘텐츠를 복제하는 것은 문제가되지 않습니다-동등한 2 개의 인스턴스 일뿐입니다. 중요한 것은 손상된 병합이나 덮어 쓰기가 발생하지 않는다는 것입니다. 자동 충돌 검사의 경우 쓰기로 인한 손상을 중지하지만 원래의 이름 지정 문제는 해결하지 못합니다. (교차 참조로 인해 충돌을 피하기 위해 이름을 바꾸는 것은 사소하지 않을 수 있습니다.)
Kylotan

아 네, 지금은 이름 선택만큼 네임 스페이스 자체가 아닙니다. 이 경우 GUID는 아마도 다시 답이 될 것입니다. 컨텐트는 자체 작은 영역에 효과적으로 유지됩니다. 장식적인 이름을 지정할 수 있지만 게임은 GUID를 사용합니다.
MrCranky

1

마운트 포인트를 사용하여 모든 것을 속성 (또는 데코레이터)으로 저장하십시오. 플레이어가 디자인 한 집을 예로 들어 보자.

o House: { Type = 105 } // Simple square cottage.
 o Mount point: South Wall:
  o Doodad: Chair { Displacement = 10cm }
   o Mount point: Seat:
    o Doodad: Pot Plant { Displacement = 0cm, Flower = Posies } // Work with me here :)
 o Mount point: North Wall:
  o Doodad: Table { Displacement = 1m }
    o Mount point: Left Edge:
     o Doodad: Food Bowl { Displacement = 20cm, Food = Meatballs}

따라서 각 엔티티는 하나 이상의 마운트 포인트를 가질 수 있습니다. 각 마운트 포인트는 0 개 이상의 다른 구성 요소를 수용 할 수 있습니다. 이 데이터는 저장됩니다 버전과 이 (예 : 변위 등 내 예에서와 같이)를 모든 관련 속성과 함께,에 저장되었다 - NoSQL의 가능성 (키 = 엔티티 ID, 값 = 직렬화 된 바이너리 여기 정말 좋은 적합 할 것 데이터).

그런 다음 각 구성 요소는 이전 버전에서 이전 데이터를 '업그레이드'할 수 있어야합니다 (직렬화 된 데이터에서 필드를 제거하지 말고 그냥 '널'로 설정하십시오). 사용 가능한 최신 버전). 우리 집의 크기가 바뀌 었다고 가정 해 봅시다. 업그레이드 코드는 상대적으로 북쪽과 남쪽 벽 사이의 거리를 계산하고 포함 된 모든 엔티티의 변위를 비례 적으로 변경합니다. 다른 예로 고기 그릇에 '식품'필드를 제거하고 대신 'Variety'(고기)와 'Recipe'(공)을 얻을 수 있습니다. 업그레이드 스크립트는 'Meat Balls'를 'Meat', 'Balls'로 바꿉니다. 각 구성 요소는 또한 마운트 포인트 변경을 처리하는 방법을 알아야합니다 (예 :

이것은 모두 정확히 하나의 문제를 열어 둡니다. 두 객체가 서로 충돌하면 어떻게됩니까 (컨테이너가 아닌 마운트 지점이 당신을 보호합니다)? 업그레이드 후 충돌을 확인하고 충돌을 해결하려고 시도해야합니다 (SAT와 같이 조금씩 움직여서). 충돌을 해결하는 방법을 알 수 없다면 물건 중 하나를 제거하고 숨겨 놓은 물건을 제거하십시오. 플레이어에게 업그레이드가 레이아웃의 일부를 깨 뜨렸을 가능성이 있음을 분명히 알리십시오. 문제를 볼 수 있도록 '확대'기능이 있습니다.

어떤 알고리즘도 미학을 설명 할 수 없기 때문에 궁극적으로 플레이어의 손에 복잡한 변화 (빠른 실패)를 남겨 두어야합니다. 아이템이 어디에 있었는지 플레이어에게 컨텍스트를 제공 할 수 있어야합니다. 이 물건들을 모두 숨겨두고 어디에 있었는지 모릅니다).


이것은 객체 포지셔닝에 너무 좁게 초점을 맞추고 있는데, 실제로 해결하려는 주요 문제는 아닙니다. 동시 데이터 세트에 고유 식별자가 있고 충돌 가능성이없는 식별자를 병합 할 수 있어야합니다. 다른 날 내 게시물에 2 가지 예를 추가하여 조금 더 설명하고 설명했습니다.
Kylotan 2019

1

나는 이것을 내가 이해하는 것과 연관 시키려고 노력하고 있으므로 지금 Minecraft와 관련하여 생각하고 있습니다. 개발자가 새로운 콘텐츠를 수정 / 작성하는 테스트 서버에서 실행하는 동안 플레이어가 실시간으로 변경하는 라이브 서버를 상상하고 있습니다.

귀하의 질문은 거의 두 가지 독특한 질문처럼 보입니다.

  1. 객체 ID가 고유한지 확인하는 방법
  2. 스크립트 네임 스페이스가 충돌하지 않도록하는 방법

임시 참조 시스템을 통해 # 1을 해결하려고합니다. 예를 들어, 누군가가 새 개체를 만들면 일시적 또는 일시적으로 표시 될 수 있습니다. 테스트 서버에서 생성 된 모든 새로운 내용은 일시적으로 표시됩니다 (비 휘발성 내용을 참조 할 수도 있음).

새 컨텐츠를 라이브 서버로 가져올 준비가되면 가져 오기 프로세스에서 휘발성 오브젝트를 찾아서 돌로 설정된 라이브 서버 오브젝트 ID를 지정합니다. 기존의 비 휘발성 개체를 수정하거나 업데이트해야하는 경우이를 참조 할 수 있어야하기 때문에이 방법은 직접 가져 오기 / 병합과 다릅니다.

# 2의 경우 함수 이름을 고유 네임 스페이스로 해시 할 수있는 중간 수준의 스크립트 변환이 필요합니다. 즉

assignFrenchAccent

된다

_Z11assignFrenchAccent

0

데이터 파일이 바이너리가 아닌 텍스트이고 디자이너와 플레이어가 다른 영역을 수정하는 경우 SVN 병합을 시도 할 수 있습니다.


0

'체크 아웃'절차를 사용하여 여러 환경에 복제 된 데이터베이스 / 파일 시스템이 가장 좋습니다.

따라서 디자이너가 세상을 약간 수정하려고 할 때마다 데이터베이스의 모든 복사본 (개발 및 프로덕션)에서 생성 / 수정하려는 모든 자산을 체크 아웃 / 잠금하여 다른 플레이어 나 디자이너는 수정할 수 없습니다 . 그런 다음 새 디자인이 완료 될 때까지 개발 데이터베이스에서 작업 한 다음 변경 사항이 프로덕션 데이터베이스와 병합되고 모든 환경에서 해당 자산이 체크인 / 잠금 해제됩니다.

플레이어 편집은 데이터베이스 / 파일 시스템 역할이 역전된다는 점을 제외하고는 거의 같은 방식으로 작동합니다. 프로덕션 데이터베이스에서 작동하고 모든 업데이트가 완료되면 dev에 업로드됩니다.

자산 잠금은 충돌을 보장하지 않으려는 속성으로 제한 될 수 있습니다. 예 1에서는 ID 123456플레이어가 제작을 시작하자마자 잠금 되므로 개발자에게 해당 ID가 할당되지 않습니다. 예제 2에서 개발자는 assignFrenchAccent개발하는 동안 스크립트 이름을 잠 갔을 것이므로 플레이어는 수정을 개발할 때 다른 이름을 선택해야합니다 (이름 공간에 의해 작은 성가심을 줄일 수는 있지만 각각을 제공하지 않으면 자체적으로 충돌을 피할 수는 없습니다) 사용자 / 개발자에게 특정 네임 스페이스를 사용하면 네임 스페이스 관리와 동일한 문제가 발생합니다). 즉, 모든 개발은 단일 온라인 데이터베이스에서 읽어야하지만이 예제에서 해당 데이터베이스에서 필요한 것은 개체 이름이므로 성능에는 문제가되지 않습니다.

구현 측면에서 모든 키와 자산 상태 (사용 가능, 개발 잠금, 제품 잠금)가 단일 테이블에 있으면 환경 전체에서 실시간으로 동기화 / 액세스 할 수 있어야합니다. 보다 복잡한 솔루션은 완전한 버전 제어 시스템을 구현합니다. 자산이 모두 파일 시스템에있는 경우 CVS 또는 SVN과 같은 기존 시스템을 사용할 수 있습니다.


일반적으로 데이터를 전 세계적으로 잠그는 것은 실용적이지 않습니다. 플레이어는 정상적인 플레이를 통해서만 세상을 편집 할 수 있으며 디자이너가 작업을 수행하지 못하게하기를 원치 않을 수 있습니다. 전역 잠금을 허용하면 병합 작업은 기본적으로 덮어 쓰기 작업입니다. 그러나 전역 잠금이없는 경우 어떻게해야합니까?
Kylotan

lathomas64가 언급했듯이 대답은 어떤 종류의 데이터에 대해 말하고 있는지에 달려 있습니다. 글로벌 잠금 장치가 없으면 충돌을 해결하기 위해 버전 관리 시스템과 일련의 규칙이 있어야한다고 생각합니다.이 규칙은 데이터 및 게임 플레이 요구 사항에 따라 다릅니다. 일단 당신이 그것들을 가지고 있다면, 모든 병합은 간단한 덮어 쓰기 작업으로 줄어 듭니다.
SkimFlux

0

여기에서 요점은 당신의 책임을 깨끗하게 받아들이는 것입니다. 1) 서버는 현재 허용 가능한 것과 액세스 할 API를 말합니다. 특정 규칙에 따라 데이터베이스가 수정되고 있습니다. 2) 제작자는 콘텐츠를 만들 수 있지만 업데이트 후에는 재생할 수 있어야합니다. 이는 전적으로 귀하의 책임입니다. 모든 업데이트는 가능한 한 깨끗하고 쉬운 이전 데이터 구조를 구문 분석 할 수 있어야합니다.

마운트 포인트 아이디어는 가단 구조 내에서 고유 한 항목과 위치를 추적하는 데 관심이 있다면, 특히 플레이어의 전체 '홈'구조가 극적인 변화를 겪고 있다는 점을 인정할 때 장점이 있습니다. 각각의 사물함에 작은 데코 물건.

매우 복잡한 문제입니다. 행운을 빌어 요! 답이 하나도 없을 것입니다.


0

나는 당신이 그것을 만드는 동안 큰 문제가 있다고 생각하지 않습니다.

"Mod X가이 버전에서 제대로 작동하지 않을 수 있습니다"라는 경고와 함께 사용자가 만든 모드를 덮어 쓰고 작업을 변경하려면 모드 작성자에게 맡기십시오. 나는 이것이 업데이트가 특정 모드를 비활성화 할 수 있다는 비현실적인 기대라고 생각하지 않습니다.

사용자가 만든 컨텐츠와 동일하며 백업 만하고 덮어 씁니다.

나는 이것에 대한 실제 경험이 없으며 제안을합니다.


나는 그것이 사용자가 제공 한 개조를위한 것이라고 생각한다면, 당신이 옳을 것입니다. 그러나 일부 게임은 사용자가 만든 콘텐츠에 관한 것이므로이를 파괴 할 수는 없습니다.
Kylotan 2019

그런 다음 나중에 추가 할 수있는 내용을 시스템에 남겨 두십시오. ID 번호를 사용하는 경우 1-1000을 예약하십시오. 또는 사용자가 자산 이름을 지정할 수있는 경우 사용자가 "FINAL-"또는 다른 이름으로 이름을 시작하지 못하게하십시오 (자신의 자산을 위해 예약). 편집 : 또는 더 나은 아직, 반대로 사용자 콘텐츠를 범위 또는 접두사로 강요
Woody Zantzinger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.