30. 콘텐츠 지원 Part 5: 3D 월드

월드 기능은 MMORPG 서버를 구현할 때 반드시 필요한 3D 공간에서 위치를 갖는 오브젝트를 다루는 기능을 제공합니다. 예를 들면 어떤 플레이어가 위치 A 에서 위치 B 로 이동하였을 때 이 정보를 전파하기 위해 특정 거리 이내의 플레이어들을 찾아내는 것 등이 될 수 있습니다.

월드 기능은 공간을 표현하는 월드와 플레이어나 몬스터 등을 표현할 수 있는 월드 오브젝트로 구성되어 있습니다.

30.1. 월드 생성

월드는 API 함수를 이용하여 런타임에 직접 생성하거나, MANIFEST 를 이용하여 서버가 구동될 때 자동으로 생성되게 할 수 있습니다.

월드를 생성하기 위해서는 아래 값들이 필요합니다.

Name

월드 객체를 가져오거나 검색할 때 사용할 이름입니다.

Index Block Length

성능을 좌우하는 값으로 월드 오브젝트 검색 시 가장 널리쓰이는 길이 값의 두배를 권장합니다. 예를 들면 플레이어 가시거리의 두 배 정도가 될 수 있습니다.

Zone List

월드 내의 일정 지역을 다른 지역과 구분하여 오브젝트가 들어오고 나갈 때 콜백 함수로 통지받을 수 있습니다. 자세한 설명은 Zone 를 참고 하시기 바랍니다.

Channel Number

같은 Name 의 월드가 2 개 이상일 때 구분하기 위한 번호입니다.

기본적으로 월드 콤포넌트의 모든 API 함수들은 내부에서 잠금(Lock) 을 통해 Thread-Safe 하게 구현되어 있습니다. 만일, 단일 쓰레드 또는 단일 이벤트 태그에서만((Example) Serializing using event tags) 접근하도록 구현할 경우 World::SetSingleThreadMode() 를 호출하여 내부 잠금(Lock) 을 하지 않도록 할 수 있습니다.

30.1.1. 방법1 - API 함수

WorldManager 클래스의 WorldManager::Create()WorldManager::CreateLocal() 함수로 월드를 생성할 수 있습니다. WorldManager::Create()WorldManager::CreateLocal() 보다 느리지만 이 함수로 만들어진 월드는 다른 서버에서 이름으로 검색할 수 있습니다. 두 함수 모두 같은 이름, 같은 채널의 월드가 이미 존재하면 생성에 실패합니다.

Create() 함수 예제

// Name: "earth"
// Zone: Empty
// Index Block Length: 1000
// Channel Number: 1 (Default)
WorldManager::Create("earth", WorldManager::ZoneVector(), 1000, OnWorldCreated);

void OnWorldCreated(const Ptr<World> &world) {
  if (world) {
    ...
  } else {
    LOG(ERROR) << "Failed to create the world.";
  }
}

CreateLocal() 함수 예제

// Name: "moon"
// Zone: Empty
// Index Block Length: 1000
// Channel Number: 2
Ptr<World> world = WorldManager::CreateLocal("moon", WorldManager::ZoneVector(), 1000, 2);

if (world) {
  ...
} else {
    LOG(ERROR) << "Failed to create the world.";
}

추후 지원됩니다.

30.1.2. 방법2 - MANIFEST 설정

API 함수 호출없이 MANIFEST 에 월드 정보를 기술하여 서버가 구동될 때 자동으로 생성되도록 할 수 있습니다.

MANIFEST 에 WorldManager 컴포넌트를 추가하여 아래와 같이 월드를 기술합니다. 서버가 구동되면 방법1 - API 함수 와 동일하게 월드(“earth”), 로컬 월드(“moon”) 가 자동으로 생성됩니다.

...
"WorldManager": {
  "world_index_block_length": 1000,
  "tag_rpc_with_world_name": true,
  "worlds": {
    "earth": {
      "channel": 0,
      "local": false,
      "zones": []
    },
    "moon": {
      "channel": 2,
      "local": true,
      "zones": []
    }
  },
...

30.1.3. Zone

Zone 기능을 활용하면 월드 내의 특정 지역에 들어오고 나갈 때 콜백 함수로 통지받을 수 있습니다. Zone 정보는 월드를 생성할 때 입력받습니다.

Zone 을 생성하기 위해서는 아래 값들이 필요합니다.

Name

Zone 을 구분하는 이름이며 Zone Handler 함수로 전달됩니다.

Type

Zone 의 종류를 구분하는 문자열 값이며 Zone Handler 함수로 전달됩니다.

Sphere

Zone 의 위치와 크기를 나타냅니다.

Argument

Zone Handler 로 함께 전달되는 JSON 값입니다.

30.1.3.1. Zone 생성

방법1 - API 함수 를 사용할 경우 아래와 같이 Zone 을 구성할 수 있습니다. struct WorldManager::Zone 을 이용하여 하나의 Zone 을 표현합니다. struct WorldManager::Zonenametype 값은 아래에서 설명하고 있는 Zone Handler 로 함께 전달되며 임의로 지정합니다.

WorldManager::Zone zone1;
zone1.name = "KennedySpaceCenter";
zone1.type = "portal";
zone1.sphere.coordinates.x = 1000;
zone1.sphere.coordinates.y = 1000;
zone1.sphere.coordinates.z = 1000;
zone1.sphere.radius = 30;
// zone1.argument = ...;

WorldManager::ZoneVector zones;
zones.push_back(zone1);

Ptr<World> world = WorldManager::CreateLocal("earth", zones, 1000, 2);

방법2 - MANIFEST 설정 를 사용할 경우 아래와 같이 Zone 을 구성할 수 있습니다.

...
"WorldManager": {
  "world_index_block_length": 1000,
  "tag_rpc_with_world_name": true,
  "worlds": {
    "earth": {
      "channel": 2,
      "local": true,
      "zones": [
        {"name": "KennedySpaceCenter",
         "type": "portal",
         "sphere": {"x": 1000, "y": 1000, "z": 10, "r": 30},
         "arguments": {
           "destination_world": "moon",
           "coordinates": { "x": 0, "y": 0, "z": 0 }
         }
        }
      ]
    }
  },
...

추후 지원됩니다.

30.1.3.2. Zone 핸들러

Zone 에 오브젝트가 들어가고 나갈 때 통지 받을 콜백함수를 WorldManager::RegisterZoneHandler() 함수를 이용해 등록합니다.

WorldManager::RegisterZoneHandler(MyCallback);

void MyCallback(const string &world_name, const string &zone_name,
                const string &type, const Json &argument, bool enter,
                const Ptr<World::Object> &object) {
  if (world_name == "earth" && zone_name == "KennedySpaceCenter" &&
      type == "portal") {
    if (enter) {
      // entered
      ...
    } else {
      // left
      ...
    }
  } else {
    // ...
  }
}

추후 지원됩니다.

30.2. 월드 찾기

월드 찾기 기능은 서버 내에 존재하는 월드 외에 다른 서버에 존재하는 월드까지 찾을 수 있습니다.

30.2.1. 같은 서버 내

WorldManager::Get() 함수를 이용하여 월드 이름과 채널 번호로 월드를 찾을 수 있습니다. 이 함수는 서버 내에 존재하는 월드만 찾을 수 있습니다.

{
  // earth#0
  Ptr<World> world = WorldManager::Get("earth");
  if (not world) {
    LOG(ERROR) << "No world.";
  }
}

{
  // earth#1
  Ptr<World> world = WorldManager::Get("earth", 1);
  if (not world) {
    LOG(ERROR) << "No world.";
  }
}

추후 지원됩니다.

30.2.2. 다른 서버

WorldManager::FindServer() 함수로 월드 이름으로 해당 월드가 위치한 서버를 찾을 수 있으며, Transferring clients to peers 에 설명된 Redirect 기능으로 클라이언트를 월드가 존재하는 서버로 이동시킬 수 있습니다.

Ptr<World> world = WorldManager::Get("earth");

if (world) {
  ...
} else {
  WorldManager::FindServerCallback cb = [=](const Rpc::PeerId &peer) {
    // AccountManager::Redirect(session, peer_id, extra_data);
  };
  WorldManager::FindServer("earth", cb);
}

추후 지원됩니다.

30.3. 월드 삭제

인스턴스 던전 구현 등 필요시 WorldManager::Delete()WorldManager::DeleteLocal() 함수를 이용하여 월드를 삭제할 수 있습니다. World::IsDeleted() 함수로 삭제된 월드를 구분할 수 있으며 삭제된 월드는 월드의 모든 기능이 작동하지 않습니다.

Ptr<World> earth = WorldManager::Get("earth");

WorldManager::DeleteLocal("earth");

BOOST_ASSERT(earth->IsDeleted());

추후 지원됩니다.

30.4. 월드 오브젝트 생성

World::Object 또는 해당 오브젝트를 상속 받은 객체는 월드 오브젝트라 부르며 월드상에 존재하거나 월드에 입장할 때 사용되는 오브젝트입니다. 월드에 속한 모든 오브젝트는 위치와 크기를 가지고 있으며 월드 내에서 유일한 ID 를 할당받습니다. 모든 오브젝트는 ID 를 사용하여 언제든지 접근할 수 있습니다.

30.4.1. 생성

월드 오브젝트는 아래와 같이 생성할 수 있습니다. ID 는 월드에 입장할 때 자동할당되며, 크기는 0 이 됩니다.

Ptr<World::Object> object(new World::Object());

BOOST_ASSERT(object->GetId() == World::kInvalidId);

추후 지원됩니다.

또는 ID 를 직접 부여하거나 반지름으로 표현되는 크기를 지정해줄 수 있습니다. ID 는 반드시 중복되지 않게 할당해야합니다.

int64_t id = 1234;
float radius = 5;

Ptr<World::Object> object1(new World::Object(id));
BOOST_ASSERT(object1->GetId() == id);

Ptr<World::Object> object2(new World::Object(radius));
BOOST_ASSERT(object2->GetId() == World::kInvalidId);

Ptr<World::Object> object3(new World::Object(id, radius));
BOOST_ASSERT(object3->GetId() == id);

추후 지원됩니다.

아래와 같이 상속한 클래스를 이용할 수 있습니다.

class MyObject : public World::Object {
  ...
};

Ptr<MyObject> object(new MyObject());

추후 지원됩니다.

30.4.2. 이름 붙이기

아래와 같이 자유롭게 이름을 붙일 수 있습니다.

Ptr<World::Object> object(new World::Object());

object->SetName("Example");

string name = object->GetName();

추후 지원됩니다.

30.4.3. 세션 붙이기

아래와 같이 Session 을 할당할 수 있으며 필요시 Session 을 가져와 메시지 전송 등을 할 수 있습니다. 그리고 World::Broadcast() 함수를 이용하여 메시지를 보낼 때 이용됩니다.

Ptr<Session> session = ...;

Ptr<World::Object> object(new World::Object());

object->SetSession(session);

Ptr<Session> session2 = object->GetSession();

추후 지원됩니다.

30.4.4. Type 지정

모든 오브젝트에는 Type 을 지정할 수 있으며 World::FindObject(), World::FindSession(), World::Broadcast() 에서 특정 Type 만 처리하게 할 수 있습니다. Type 은 반드시 2의 승수로 표현해야 하며 월드에 입장한 이후로는 변경할 수 없습니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...
};

Ptr<World::Object> object(new World::Object());

object->SetType(kPlayer | kClassWarrior);

int64_t type = object->GetType();

추후 지원됩니다.

30.4.5. 데이터 저장

월드 오브젝트는 간단한 Key/Value 형태로 데이터를 저장할 수 있는 기능이 있으며 사용 방법은 다음과 같습니다.

Ptr<World::Object> object(new World::Object());

object->SetKeyValue("ExampleKey", 1234);

int64_t value = object->GetValueForKey("ExampleKey");

추후 지원됩니다.

30.5. 월드 오브젝트 입장

월드 오브젝트는 World::InsertObject() 함수를 이용하여 월드에 입장할 수 있습니다. 오브젝트 생성 시 ID 를 할당하지 않았다면 자동으로 ID 가 할당됩니다. World::InsertObject() 함수는 할당된 ID 를 반환하며 ID 를 생성자로 할당했다면 ID 중복 등으로 입장에 실패하면 World::kInvalidId 값을 반환합니다.

Ptr<World> world = WorldManager::Get("earth");
BOOST_ASSERT(world);

Ptr<World::Object> object(new World::Object());

int64_t id = world->InsertObject(World::Point(0, 0, 0), object);
if (id == World::kInvalidId) {
  LOG(ERROR) << "Failed to insert object.";
  return;
}

BOOST_ASSERT(id == object->GetId());

추후 지원됩니다.

플레이어가 월드에 입장한다면 아래와 같은 형태가 될 수 있습니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...
};

Ptr<World> the_world;

void OnLoginComplete(const string &char_name, const Ptr<Session> &session) {
  Ptr<World::Object> object(new World::Object());
  object->SetName(char_name);
  object->SetSession(session);
  object->SetType(kPlayer | kClassWarrior);

  int64_t id = the_world->InsertObject(World::Point(0, 0, 0), object);
  if (id == World::kInvalidId) {
    LOG(ERROR) << "Failed to insert object.";
    return;
  }

  BOOST_ASSERT(id == object->GetId());

  session->AddToContext("world_object_id", id);
}

void OnMove(const Ptr<Session> &session, ...) {
  int64_t id;
  if (not session->GetFromContext("world_object_id", &id)) {
    LOG(ERROR) << "Not in the world.";
    return;
  }

  Ptr<World::Object> object = the_world->GetObject(id);
  ...;
}

추후 지원됩니다.

30.6. 월드 오브젝트 퇴장

입장한 월드 오브젝트는 World::EraseObject() 함수로 월드에서 나올 수 있습니다. 이 함수는 퇴장한 오브젝트를 반환하며 오브젝트가 존재하지 않을 경우 NULL 을 반환합니다.

int64_t world_object_id = ...;

Ptr<World> world = WorldManager::Get("earth");

Ptr<World::Object> object = world->EraseObject(world_object_id);

if (object) {
  ...
}

추후 지원됩니다.

30.7. 월드 오브젝트 이동

월드에 입장한 오브젝트는 World::MoveObject()World::MoveObjectTo() 함수로 월드 내에서 위치를 이동할 수 있습니다. 전자는 변화량을 후자는 절대값을 이용하여 오브젝트를 이동시킬 수 있습니다.

아래는 오브젝트를 현재 위치에서 x 를 100 만큼 이동시킵니다.

int64_t world_object_id = ...;

Ptr<World> world = WorldManager::Get("earth");

world->MoveObject(world_object_id, World::Point(100, 0, 0));

// World::Point new_coordinates;
// world->MoveObject(world_object_id, World::Point(100, 0, 0),
//                   &new_coordinates);

추후 지원됩니다.

아래는 오브젝트를 x=500, y=500, z=0 위치로 이동시킵니다.

int64_t world_object_id = ...;

Ptr<World> world = WorldManager::Get("earth");

world->MoveObjectTo(world_object_id, World::Point(500, 500, 0));

추후 지원됩니다.

Tip

새롭게 시야에 들어온 오브젝트 찾기 의 설명을 참고하면 손쉽게 새로 시야에 들어온 오브젝트들을 찾을 수 있습니다.

30.8. 월드 오브젝트 검색

아래는 월드 내 오브젝트들을 다양한 조건으로 검색하는 방법을 설명합니다.

30.8.1. 오브젝트 ID 로 찾기

World::GetObject() 함수는 월드 오브젝트 ID 를 이용하여 월드내에 속한 오브젝트를 가져오는 기능입니다. 오브젝트 ID 는 월드에 입장할 때 자동으로 할당되거나 오브젝트를 생성할 때 직접 부여할 수 있습니다.

Ptr<World> world = WorldManager::Get("earth");

int64_t world_object_id;
{
  Ptr<World::Object> object(new World::Object());
  world_object_id = world->InsertObject(World::Point(0, 0, 0), object);
}

if (world_object_id != World::kInvalidId) {
  Ptr<World::Object> object = world->GetObject(world_object_id);
  BOOST_ASSERT(world_object_id == object->GetId());
}

추후 지원됩니다.

30.8.2. 가까운 오브젝트들 찾기

World::FindObject() 함수는 어떠한 월드 오브젝트나 위치를 기준으로 일정 거리 이내에 있는 월드 오브젝트들을 찾을 수 있습니다.

30.8.2.1. 위치 기준으로 찾기

아래는 x=1000, y=1000, z=0 위치에서 거리 500 이내에 있는 오브젝트를 가져옵니다. 마지막 인자에 World::DistanceVector 를 넘기면 거리도 함께 가져옵니다.

Ptr<World> world = WorldManager::Get("earth");

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0), 500, &objects);

// World::DistanceVector distances;
// world->FindObject(World::Point(1000, 1000, 0),
//                   500,
//                   World::kDefaultOption,
//                   World::kNullFilter,
//                   &objects,
//                   &distances);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.2. 오브젝트 기준으로 찾기

아래는 특정 오브젝트의 위치에서 거리 500 이내에 있는 오브젝트를 가져옵니다. 이어지는 설명들에서는 모두 위치 기준으로 설명하지만 모두 동일하게 오브젝트 기준으로도 가능합니다.

Ptr<World> world = WorldManager::Get("earth");

int64_t world_object_id = ...;

World::ObjectVector objects;
world->FindObject(world_object_id, 500, &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.3. 가까운 순으로 정렬하기

World::FindOption 을 이용하여 반환되는 오브젝트들을 거리순으로 정렬할 수 있습니다.

Ptr<World> world = WorldManager::Get("earth");

World::FindOption option;
option.use_ascending_order = true;

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  option,
                  World::kNullFilter,
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.4. 가까운 순으로 N 번째 오브젝트 찾기

World::FindOption 을 이용하여 N 번째 오브젝트를 찾을 수 있습니다. 아래는 x=1000, y=1000, z=0 기준으로 거리 500 이내에 있는 오브젝트 중 3 번째로 가까운 오브젝트를 가져옵니다.

Ptr<World> world = WorldManager::Get("earth");

World::FindOption option;
option.use_ascending_order = true;
option.boundary_begin = 2;
option.boundary_count = 1;

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  option,
                  World::kNullFilter,
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.5. 새롭게 시야에 들어온 오브젝트 찾기

World::MakeMovingSightFilter() 를 이용하여 이동하는 캐릭터 등의 시야를 처리할 수 있습니다. 이 필터는 오브젝트가 위치를 이동하고 나서 새로 시야 범위에 들어온 오브젝트를 가져옵니다. 이미, 시야에 존재하던 오브젝트는 포함되지 않습니다.

Ptr<World> world = WorldManager::Get("earth");

int64_t world_object_id = ...;
Ptr<World::Object> object = world->GetObject(world_object_id);

World::Point prev_pos = object->GetCoordinates();

world->MoveObject(object, World::Point(100, 0, 0));

World::ObjectVector objects;
world->FindObject(object,
                  500,
                  World::kDefaultOption,
                  World::MakeMovingSightFilter(prev_pos),
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.6. 특정 Type 의 오브젝트 찾기

World::MakeFilter() 를 이용하여 특정 Type 의 오브젝트만 찾을 수 있습니다.

아래는 전사와 마법사 플레이어 오브젝트만 가져오는 예입니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...
};

Ptr<World> world = WorldManager::Get("earth");

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  World::kDefaultOption,
                  World::MakeFilter(kClassWarrior | kClassWizard),
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.7. 사각형에 포함된 오브젝트 찾기

World::MakeRectangleFilter() 를 이용하여 사각형 범위에 있는 오브젝트만 가져올 수 있습니다. 이 함수는 기준 위치에서의 사각형이 만들어질 거리와 방향, 사각형의 크기 정보를 입력받으며 Type 또한 지정할 수 있습니다.

아래는 x=1000, y=1000, z=0 기준위치로 하여 거리 500 이내의 오브젝트를 찾고 기준위치에서 x=1, y=0.5 방향으로 30 만큼 떨어진 위치에 길이 50 의 정사각형 안에 포함되는 플레이어 오브젝트만 가져옵니다.

높이 값이 음수이면 무한대로 처리됩니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...
};

Ptr<World> world = WorldManager::Get("earth");

World::FindFilter filter = World::MakeRectangleFilter(
    30, World::Vector2(1, 0.5), 50, 50, 50, kPlayer);

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  World::kDefaultOption,
                  filter,
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.8. 부채꼴에 포함된 오브젝트 찾기

World::MakeCircularSectorFilter() 를 이용하여 부채꼴 범위에 있는 오브젝트만 가져올 수 있습니다. 이 함수는 기준 위치에서의 부채꼴이 만들어질 거리와 방향, 부채꼴의 반지름 길이, 좌우 각도 정보를 입력받으며 Type 또한 지정할 수 있습니다.

아래는 x=1000, y=1000, z=0 기준위치로 하여 거리 500 이내의 오브젝트를 찾고 기준위치에서 x=1, y=1 방향으로 50 만큼 떨어진 위치에 반지름 100, 좌 45 도, 우 45 도, 높이 70 의 부채꼴에 포함되는 NPC 오브젝트만 가져옵니다.

높이 값이 음수이면 무한대로 처리됩니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...
};

Ptr<World> world = WorldManager::Get("earth");

World::FindFilter filter = World::MakeCircularSectorFilter(
    50, World::Vector2(1, 1), 100, -45, 45, 70, kNpc);

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  World::kDefaultOption,
                  filter,
                  &objects);

for (size_t i = 0; i < objects.size(); ++i) {
  Ptr<World::Object> object = objects[i];
  ...
}

추후 지원됩니다.

30.8.2.9. 사용자 정의 필터

World::MakeFilter(), World::MakeRectangleFilter(), World::MakeCircularSectorFilter() 모두 원하는 필터가 아닐 경우 World::FindFilter 형식에 맞는 함수를 만들어 원하는 필터를 만들 수 있습니다. World::FindFilter 의 첫 번째와 두 번째 인자는 World::FindObject() 에 전달된 위치와 거리입니다. 세 번째 인자에 해당하는 오브젝트가 결과에 포함돼야하면 true 를 아니면 false 를 반환합니다.

아래는 세션이 있는 오브젝트만 찾는 필터를 만드고, 사용하는 예입니다.

bool MySessionFilter(const World::Point &, float,
                     const Ptr<Object> &object) {
  if (object->GetSession()) {
    return true;
  } else {
    return false;
  }
}

Ptr<World> world = WorldManager::Get("earth");

World::ObjectVector objects;
world->FindObject(World::Point(1000, 1000, 0),
                  500,
                  World::kDefaultOption,
                  MySessionFilter,
                  &objects);

추후 지원됩니다.

30.8.3. 가까운 플레이어들에게 메시지 보내기

아래에서 설명하는 기능은 반드시 세션 붙이기 와 함께 사용되어야 합니다.

30.8.3.1. 가까운 플레이어들의 세션 찾기

World::FindSession()World::FindObject() 에서 World::ObjectVector 대신 World::SessionVector 를 쓰는 것 외에 모두 동일합니다. 이 함수를 이용하면 월드 오브젝트 대신 월드 오브젝트에 할당된 Session 을 가져올 수 있습니다.

30.8.3.2. 가까운 플레이어들에게 메시지 보내기

World::Broadcast() 를 이용하면 가까운 플레이어들에게 메시지를 보낼 수 있습니다.

아래는 x=1000, y=1000, z=0 기준 거리 500 이내의 플레이어들에게 이동 메시지를 전송합니다.(이동 메시지 표현은 생략)

Ptr<World> world = WorldManager::Get("earth");

string message_type = "move";
Ptr<FunMessage> message = ...; // or Json message;

world->Broadcast(World::Point(1000, 1000, 0),
                 500,
                 message_type,
                 message,
                 kDefaultEncryption,
                 kTcp);

추후 지원됩니다.

30.9. 정적 월드 오브젝트

마을, 부활 위치 등 위치 이동이 없으며 거리 제약 없이 검색되어야 하는 오브젝트는 정적 오브젝트로 등록하여 다루는 것이 편리합니다. 정적 오브젝트는 비정적 오브젝트와 분리되어 처리됩니다. World::FindObject() 등으로 검색되지 않으며 아래에 설명된 함수들로만 다룰 수 있습니다.

30.9.1. 등록

World::InsertStaticObject() 함수로 오브젝트를 정적 오브젝트로 등록할 수 있습니다.

아래는 “MyTownA” 마을을 x=300, y=300, z=0 위치에 정적 오브젝트로 등록합니다.

enum WorldObjectType {
  kPlayer = 1,
  kClassWarrior = 2,
  kClassArcher = 4,
  kClassWizard = 8,
  kNpc = 16,
  kNpcMonster = 32,
  ...,
  kTown = 1024
};

Ptr<World> world = WorldManager::Get("earth");
BOOST_ASSERT(world);

Ptr<World::Object> object(new World::Object());
object->SetName("MyTownA");
object->SetType(kTown);

world->InsertStaticObject(World::Point(300, 300, 0), object);

추후 지원됩니다.

30.9.2. 검색

World::FindStaticObject() 함수로 정적 오브젝트를 검색할 수 있습니다.

아래는 x=700, y=700, z=0 위치에서 가장 가까운 마을을 찾는 예입니다.

Ptr<World> world = WorldManager::Get("earth");
BOOST_ASSERT(world);

World::FindOption option;
option.use_ascending_order = true;
option.boundary_begin = 0;
option.boundary_count = 1;

World::ObjectVector objects;
world->FindStaticObject(World::Point(700, 700, 0), option,
                        World::MakeFilter(kTown), &objects);

if (not objects.empty()) {
  const Ptr<World::Object> &town_object = objects[i];

  World::Point rebirth_point = town_object->GetCoordinates();

  ...
}

추후 지원됩니다.

World::FindObject() 와 마찬가지로 기준 위치는 위치(World::Point) 대신 월드 오브젝트 아이디로 대체 가능합니다.

30.10. 월드 기타 기능

30.10.1. 오브젝트 존재 유무 검사

World::MonitorTypeExistence() 함수를 이용하면 월드에 특정 Type 의 월드 오브젝트가 존재하는지 알 수 있습니다. 이 함수는 월드 생성 직후 어떠한 오브젝트도 월드에 입장하지 않은 상태에서만 사용해야합니다.

아래는 월드에 유저가 한 명도 없을 경우 몬스터 AI 처리를 중지시키는 예입니다.

bool Install(...) {
  Ptr<World> w = WorldManager::Create(...);
  w->MonitorTypeExistence(kPlayer, MyTypeExistenceCallback);
}

void MyTypeExistenceCallback(int64_t type, bool exist) {
  if (type == kPlayer) {
    if (exist) {
      // start monster AI
    } else {
      // stop monster AI
    }
  }
}

추후 지원됩니다.

30.10.2. 모든 오브젝트 가져오기

World::GetObjectCount() 함수로 월드 내의 오브젝트 개수를 가져올 수 있으며, World::GetAllObject() 함수로 월드 내의 모든 오브젝트를 가져올 수 있습니다.

30.10.3. 모든 세션에게 메시지 보내기

World::Broadcast() 함수로 월드 내 모든 세션에게 메시지를 보낼 수 있습니다.

아래는 “earth” 월드 내에 모든 세션에게 “earth_notice” 메시지를 전송하는 예제입니다.

Ptr<World> world = WorldManager::Get("earth");

string message_type = "earth_notice";
Ptr<FunMessage> message = ...; // or Json message;

world->Broadcast(message_type,
                 message,
                 kDefaultEncryption,
                 kTcp);

추후 지원됩니다.