# 派对

**URL:** https://heroiclabs.com/docs/zh/nakama/concepts/parties/
**Summary:** 让用户组织派对，与成员交流。任何用户均可创建派对，并将担任首任派对长（派对主持人）。用户可以邀请其他玩家加入，也可以将该派对标记为私密，让其他玩家必须请求才能加入。所有玩家退出后，派对消失。

---


# 实时派对

实时派对是将团队游戏添加到游戏中的一种好方法，让用户可以组建派对并与派对成员交流。

派对是聚在一起参加某种游戏或社交聚会的一群用户。任何用户均可创建派对，并将担任首任派对长（派对主持人）。用户可以邀请其他玩家加入，也可以将该派对标记为私密，让其他玩家必须请求才能加入。必须一直有一个派对长，当前派对长退出时，需要从当前连接的玩家中选择一个派对长。所有玩家退出后，派对消失。

Nakama 支持的一个非常常见的派对用例是一起匹配到群组中。这适合玩家在团队中协作或玩家在团队战斗中竞争的游戏。可以向[匹配程序](../multiplayer/matchmaker/)传递一个派对 ID，该 ID 指示匹配逻辑，确保预留足够的容量来让派对全部加入比赛。

派对与[群组](../groups/)功能不同，因为它们不会在用户离线时也在游戏会话之间存续。派对只要还有最后一个用户存在就一直存在。如果您想创建公会系统或其他游戏，这些游戏应作为一个持久实体存在于您的游戏或应用程序中，您应该使用群组。

## 创建派对

任何用户均可创建派对。您可以对派对中允许的最大用户数（最多 256 个）以及成员是否必须等待派对长批准才能加入作出限制。可以创建有状态的派对，派对长可以修改此状态。

{{< code type="client" >}}
```csharp
// Create an open party (i.e. no approval needed to join) with 10 max users
// This maximum includes the party leader
var party = await socket.CreatePartyAsync(open: true, hidden: false, maxSize: 10);
System.Console.WriteLine("New Party: {0}", party);

// Create a closed party (i.e. approval needed to join) with 5 max users
// This maximum includes the party leader
var party = await socket.CreatePartyAsync(open: false, hidden: false, maxSize: 5);
System.Console.WriteLine("New Party: {0}", party);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
# Create an open party (i.e. no approval needed to join) with 10 max users
# This maximum includes the party leader
var open = true
var max_size = 10
var party = yield(socket.create_party_async(open, max_size), "completed")
print("New party: %s" % party)

# Create a closed party (i.e. approval needed to join) with 5 max users
# This maximum includes the party leader
var open = false
var max_size = 5
var party = yield(socket.create_party_async(open, max_size), "completed")
print("New party: %s" % party)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
// Create an open party (i.e. no approval needed to join) with 10 max users
// This maximum includes the party leader
const open = true;
const maxSize = 10;
const party = await socket.createParty(open, maxSize);
console.info("New Party: ", party);

// Create a closed party (i.e. approval needed to join) with 5 max users
// This maximum includes the party leader
const open = false;
const maxSize = 5;
const party = await socket.createParty(open, maxSize);
console.info("New Party: ", party);
```
{{< / code>}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

您可以使用运行时代码创建规则，指定哪些用户可以用 [before 挂钩](../../server-framework/introduction/hooks/#before-hooks)在游戏中创建派对。

## 查找派对

创建新派对后，有多种办法与其他用户共享派对 ID，让他们能够查找和加入：

- [状态事件](../status/)以更新形式发布 ID，让玩家的订户可以看到其在某个派对中处于活动状态。
- [应用程序内通知](../notifications/)发送到用户的[好友](../friends/)列表
- [公会](../groups/)，与所有成员共享 ID。

将服务器的这些功能结合起来，为游戏设计提供动力，可以创建奇妙的社交机制，将用户聚集在一起，创建一个有意义的铁杆玩家社区。

{{< code type="client" >}}
```csharp
string partyId = "<partyid>";

// Set a status update with your party ID
await socket.UpdateStatusAsync("Join my party: ", partyId);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var party_id = "<party_id>"

# Set a status update with your party ID
var update : NakamaAsyncResult = yield(socket.update_status_async(JSON.print({"Join my party: %s" % party_id}), "completed")
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const partyId = "<partyId>";

// Set a status update with your party ID
socket.updateStatus("Join my party: ", partyId);
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

有了派对 ID，然后用户就可以[加入派对](#join-party)。

## 加入派对

用户只有加入派对后才能发送消息，看到成员列表，或邀请其他用户。

{{< code type="client" >}}
```csharp
string partyId = "<partyid>";
await socket.JoinPartyAsync(partyId);

socket.ReceivedParty += party =>
{
    System.Console.WriteLine("Joined party: " + party);
};
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var party_id = "<party_id>"

var join: NakamaAsyncResult = yield(socket.join_party_async(party_id), "completed")

func _on_party_presence(p_presence : NakamaRTAPI.PartyPresenceEvent):
    print("Joined party: %s" % [p_presence.party_id])
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const partyId = "<partyId>";
await socket.joinParty(partyId);

socket.onpartypresence = (presence) => {
    console.info("Joined party: ", partyId);
};
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

如派对为私密，用户可以请求加入，派对长可以接受或拒绝。

{{< code type="client" >}}
```csharp
string partyId = "<partyid>";
// List all existing join requests
var requests = await socket.ListPartyJoinRequestsAsync(partyId);

// Accept a join request
await socket.AcceptPartyMemberAsync(partyId, <userPresence>);

// Reject a join request
await socket.RemovePartyMemberAsync(partyId, <userPresence>);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var party_id = "<party_id>"

# List all existing join requests
var requests: NakamaAsyncResult = yield(socket.list_party_join_requests_async(party_id), "completed")

# Accept a join request
var request: NakamaAsyncResult = yield(socket.accept_party_member_async(party_id, <user_presence>), "completed")

# Reject a join request
var request: NakamaAsyncResult = yield(socket.remove_party_member_async(party_id, <user_presence>), "completed")
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const partyId = "<partyid>";

// List all existing join requests
const requests = await socket.listPartyJoinRequests(partyId);

// Accept a join request
await socket.acceptPartyMember(partyId, <userPresence>);

// Reject a join request
await socket.removePartyMember(partyId, <userPresence>);
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

## 退出派对

用户可随时退出派对如果用户断开连接，且在允许的暂时断开连接超时时间内未重新连接并重新加入该派对，将自动从该派对中删除此用户。
可以将暂时断开连接超时事件[配置](../../getting-started/configuration/)为服务器的启动选项。

{{< code type="client" >}}
```csharp
string partyId = "<partyid>";
var party = await socket.LeavePartyAsync(partyId);
System.Console.WriteLine("Left party: " + party);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var party_id = "<party_id>"
var party: NakamaAsyncResult = yield(socket.leave_party_async(party_id), "completed")
print("Left party: %s" % party)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const partyId = "<partyid>";
const party = await socket.leaveParty(partyId);
console.info("Left party: ", partyId);
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

### 派对长轮换

当有用户退出时，服务器将检查他们是否是当前的派对长，如果是，则发送一条消息，指出派对长已经离开，以及哪个成员被提升为新的派对长。

当发生自动提升时，所选的派对长将始终是自创建派对以来参与时间最长的用户。这是一个决定性的过程，无法更改。如果您的游戏需要为提升新派对长设置不同的条件，请参阅[人工提升派对长](#manual-leader-promotion)。

## 发送消息

作为派对成员的任何用户都可以向派对发送包含文本、表情或游戏特定动作的消息。

{{< code type="client" >}}
```csharp
// Sending a message
socket.SendPartyDataAsync(partyId: "<partyid>", opCode: 1, data: System.Text.Encoding.UTF8.GetBytes("{<message>}"));

// Receiving the message
socket.ReceivedPartyData += data =>
{
    System.Console.WriteLine("Received: " + System.Text.Encoding.UTF8.GetString(data));
};
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
# Sending the message
var party_id = "<party_id>"
var op_code = 1
var data = "<message>".to_utf8()
var party_message: NakamaAsyncResult =  yield(socket.send_party_data_async(party_id, op_code, data), "completed")

# Receiving the message
var message: NakamaAsyncResult = yield(socket.received_party_data(data), "completed)
print("Received: %s" % message)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
// Sending the message
const partyId = "<partyId>";
const opCode = 1;
const data = {"hello": "world"};
socket.sendPartyData(partyId, opCode, data);

// Receiving the message
socket.onpartydata = (data) => {
    console.info("Received: ", data)
};
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

### 人工提升派对长

除了常规信息外，派对长还可以发送一条特别提升消息，宣布他们已不再担任现任派对长，以及谁被提升为新派对长。

{{< code type="client" >}}
```csharp
socket.ReceivedPartyLeader += newLeader =>
{
    System.Console.WriteLine("new party leader " + newLeader);
};

await socket.PromotePartyMemberAsync("<partyid>", partyMember);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var new_leader = "<user_id>"
var party_id = "<party_id>"
var leader: NakamaAsyncResult = yield(socket.received_party_leader(party_id, new_leader), "completed)
print("New party leader: %s" % new_leader)
```
{{< / code >}}

{{< code type="client" >}}
```javascript
socket.onpartyleader += (newLeader) =>
{
    console.info("New party leader: ", newLeader);
};

await socket.promotePartyMember("<partyid>", partyMember);
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

## 关闭派对

除派对长以外的任何成员均可关闭派对。派对长也可以驱逐所有成员，这将关闭该派对并在服务器上清除其状态。从游戏设计角度看，在大多数情况下，允许派对长退出更有好处。

{{< code type="client" >}}
```csharp
string partyId = "<partyid>";
await socket.ClosePartyAsync(partyId);
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var party_id = "<party_id>"
var party: NakamaAsyncResult = yield(socket.close_party_async(party_id), "completed")
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const partyId = "<partyid>";
await socket.closeParty(partyId);
```
{{< / code >}}

{{< missing type="client" lang="cpp" />}}
{{< missing type="client" lang="java" />}}
{{< missing type="client" lang="lua" framework="defold" />}}
{{< missing type="client" lang="shell" />}}
{{< missing type="client" lang="bash" />}}

## 最佳做法

我们建议您不要使用派对来实现多人游戏逻辑，也不要将其用于向派对成员发送消息来推动游戏进程的情景。在这些情况，最好在游戏服务器中使用[权威多人游戏引擎](../multiplayer/authoritative/)。这样，您可以完全控制多人游戏网络代码以及与用户行为相关的逻辑。
