Photon Server基础应用教程与实例

Photon Server基础应用教程与实例

本文还有配套的精品资源,点击获取

简介:Photon Server是一款实时网络通信引擎,常用于多人在线游戏和实时协作应用。本文将通过一个简单示例来介绍如何在项目中基本操作Photon Server,包括了解核心概念、客户端与服务器代码的关键部分、以及如何在Unity中进行快速配置。本示例分为客户端代码和服务器DLL代码两个主要部分,旨在帮助开发者掌握Photon Server的初始化连接、房间管理、事件监听和数据同步等核心功能。

1. Photon Server概念介绍

Photon Server是一款由Exit Games公司开发的高性能的网络中间件,广泛应用于多人在线游戏以及实时网络应用开发中。它的设计目标是提供一个稳定、可扩展和高性能的平台,使得开发者可以专注于游戏逻辑和用户体验的开发,而不必担心底层网络通信的复杂性。

Photon Server支持C/S架构,也就是客户端-服务器模式,这种模式的优点在于可以对游戏逻辑和游戏世界进行集中管理,确保所有客户端看到的游戏状态是同步的,并且能够进行快速的数据同步,提升游戏的实时性和互动性。

在Photon Server的架构中,主要包含以下几个关键组件:

Load Balancing Master Server(负载均衡主服务器) :负责对客户端的请求进行分发,实现客户端与游戏服务器的匹配,并且管理整个游戏服务器集群。 Game Server(游戏服务器) :负责执行游戏逻辑,处理玩家的行动,并维护游戏状态。 Web Services(网络服务) :提供游戏以外的网络功能,例如排行榜、社交和游戏内置商城等。

接下来,我们将深入探讨Photon Server的网络协议基础和其特有的协议支持特性,以及如何在实际项目中使用Photon Server的SDK,优化网络连接和数据同步。

2. 网络协议基础及Photon Server支持

2.1 网络协议的基本原理

网络协议是网络通信的基础,它定义了数据传输的规则和格式。理解网络协议的基本原理对于开发稳定且高效的网络应用程序至关重要。

2.1.1 理解UDP和TCP协议的区别

UDP(User Datagram Protocol)和TCP(Transmission Control Protocol)是两种最常用的传输层协议。

UDP 是一种无连接的协议,数据包的发送不需要建立连接,因此它提供的是不可靠的、无序的、面向消息的服务。它的优点是开销小,传输速度快,适用于实时性强但对数据准确度要求不高的应用,例如视频流或在线游戏。 TCP 是一种面向连接的协议,提供可靠的、有序的、面向字节流的服务。它在发送数据之前需要先建立连接,通过确认机制和重传机制保证数据包的完整性和顺序。TCP适用于文件传输、电子邮件等对数据准确度要求高的场景。

2.1.2 协议在游戏服务器中的作用

在游戏服务器中,协议的选择对游戏的性能和用户体验有着直接的影响。例如,对于需要快速响应的实时竞技游戏,开发者可能会倾向于使用UDP协议来减少延迟。然而,由于UDP不保证数据的可靠性,因此需要在应用层实现额外的确认和重传机制。而TCP由于其固有的连接管理和数据保证机制,适用于那些对网络延迟容忍度较高,但对数据完整性要求更高的游戏。

2.2 Photon Server的协议支持详解

Photon Server为开发者提供了灵活的网络协议支持,既能使用TCP也能使用UDP协议。

2.2.1 Photon Server的UDP支持特性

Photon Server的UDP支持特别适合需要低延迟的实时应用,例如在线游戏。UDP协议的优点在于它的速度和效率,它避免了TCP协议中建立连接和维持连接的开销,这对于需要快速响应的游戏场景至关重要。Photon Server的UDP实现在网络层提供了优化,例如它能够通过自定义的数据通道和负载平衡来提高吞吐量和减少延迟。

2.2.2 Photon Server的TCP支持特性

对于需要数据可靠性的应用,Photon Server提供了对TCP的支持。使用TCP协议时,Photon Server保证数据的完整性和顺序性。这在那些对数据准确度要求极高的游戏中尤为重要,例如需要准确同步玩家位置和状态的策略游戏。Photon Server通过TCP提供了一个稳定的平台,以确保游戏中的所有操作都能准确无误地传递给其他玩家。

Photon Server的TCP实现在确保可靠性的同时,也进行了优化以减少可能带来的网络延迟。例如,通过合理的数据包压缩和批处理,来平衡网络传输的效率和可靠性。这使得开发者能够在不同网络条件下,根据游戏的具体需求选择最合适的协议。

至此,我们已初步探讨了网络协议的基本原理,并深入分析了Photon Server对这些协议的支持特性。在接下来的章节中,我们将进一步探讨Photon Server如何在具体的应用实践中发挥作用,以及如何通过优化策略进一步提升性能。

3. PUN和C# SDK的使用方法

3.1 Photon Unity Networking(PUN)简介

3.1.1 PUN的主要特点和优势

Photon Unity Networking(PUN)是专为Unity游戏引擎设计的一款网络解决方案,它简化了多人网络游戏的开发过程,通过提供高水准的抽象层来隐藏底层网络通信的复杂性。PUN的主要特点包括:

易于使用 :提供了简单的API,允许开发者在无需深入了解网络编程的情况下就能创建多人在线游戏。 高度可扩展 :PUN支持自定义消息类型,这意味着开发者可以为特定的游戏逻辑编写并发送自己的消息,从而实现更复杂的功能。 高可靠性 :确保数据包的传输尽可能地减少丢失或延迟。 跨平台支持 :PUN可以在多种设备和操作系统上运行,包括Windows, macOS, iOS, Android等,使游戏能够触达更广泛的玩家群体。

3.1.2 PUN在Unity游戏开发中的应用

PUN在Unity游戏开发中的应用非常广泛,适用于需要多人在线互动的各种类型的游戏。以下是PUN应用的一些常见场景:

实时多人竞技游戏 :例如多人射击游戏、战斗竞技场等,玩家需要实时对战。 合作型游戏 :如多人冒险游戏,玩家需要协作解决谜题或完成任务。 实时策略游戏 :玩家进行实时策略决策以对抗其他玩家或AI对手。 社交游戏 :提供在线互动的休闲游戏,如卡牌游戏、棋盘游戏等。

PUN的使用简化了这些类型游戏的网络编程需求,使得开发者能够专注于游戏逻辑和用户体验的开发。

3.2 C# SDK的基本使用流程

3.2.1 C# SDK的安装与配置步骤

Photon C# SDK(Software Development Kit)是PUN的基础,它通过提供易于使用的API接口,让开发者能够轻松将多人网络游戏功能集成到他们的Unity项目中。安装和配置C# SDK的基本步骤如下:

下载并安装Photon Unity Networking :访问Photon官网,下载适合您Unity版本的PUN包。 在Unity中导入PUN包 :打开Unity,选择“Assets”> “Import Package”> “Custom Package”,然后选择下载的PUN包文件导入。 创建PhotonServerSettings文件 :Unity会提示您创建这个文件,它用于存储关于连接到Photon服务器的配置信息。 登录到Photon Dashboard :使用您的Photon账户登录到dashboard.photonengine.com,创建一个新应用并获取您的AppId。 配置PhotonServerSettings :在Unity编辑器中打开PhotonServerSettings文件,并填入从Photon Dashboard获得的AppId和任何其他需要的设置。 加入服务器 :通过编写代码或使用Photon提供的UI工具,将您的Unity项目连接到Photon服务器。

3.2.2 C# SDK在游戏开发中的实际操作

使用C# SDK在Unity项目中实现多人网络功能涉及编写脚本,用于处理连接到Photon服务器、创建和加入房间、同步游戏数据等核心功能。以下是C# SDK实际操作中的关键步骤:

连接到Photon服务器 :创建一个用于管理连接的类,在其中编写连接逻辑,并在适当的时候(例如游戏开始时)调用连接方法。 房间管理 :在游戏场景中编写代码以创建和加入房间。加入房间通常是玩家进入游戏的入口。 状态同步 :为了确保所有玩家看到的游戏状态一致,需要同步玩家的动作和游戏环境的改变。 网络消息处理 :实现回调函数来响应网络事件,比如玩家加入或离开房间,以及游戏状态更新。

示例代码:连接到Photon服务器

以下是一个基本的C#脚本示例,演示如何使用Photon Unity Networking API连接到服务器:

using Photon.Pun;

using Photon.Realtime;

public class PUNConnector : MonoBehaviourPunCallbacks

{

public void ConnectToPhotonServer()

{

PhotonNetwork.ConnectUsingSettings();

PhotonNetwork.AutomaticallySyncScene = true;

}

public override void OnConnectedToMaster()

{

base.OnConnectedToMaster();

Debug.Log("Connected to Photon and ready to join/leave rooms");

}

public override void OnJoinRandomFailed(short returnCode, string message)

{

base.OnJoinRandomFailed(returnCode, message);

Debug.Log("Failed to join a random room. Creating a new one");

PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = 8 });

}

}

在这个示例中, ConnectToPhotonServer 方法用于发起连接到Photon服务器, OnConnectedToMaster 回调表示连接成功,而 OnJoinRandomFailed 方法处理随机加入房间失败的情况,提示创建一个新房间。注意,实际应用中还需要在适当的时机调用这些方法,并处理可能发生的其他网络事件。

以上章节内容仅为示例,后续文章将深入探讨如何使用Photon Unity Networking和C# SDK开发完整的游戏网络功能,包括状态同步、房间管理、消息处理等高级话题。

4. 客户端与服务器的代码要点分析

4.1 客户端代码的编写要点

4.1.1 客户端与服务器交互的核心代码解析

在Photon Server环境下,客户端与服务器的交互主要通过RPC(Remote Procedure Call)和自定义网络事件实现。RPC允许客户端调用服务器端的方法,而自定义网络事件则用于发送不直接对应方法调用的消息。

//RPC调用示例代码

public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)

{

if (stream.IsWriting)

{

// 我们发送玩家位置

stream.SendNext(this.transform.position);

}

else

{

// 我们接收其他玩家位置

this.transform.position = (Vector3)stream.ReceiveNext();

}

}

在上面的示例中, OnPhotonSerializeView 方法中的 IsWriting 判断当前节点是否为发送者,根据该标志执行数据的发送或接收操作。发送者将位置信息编码到网络流中,而接收者则从网络流中解码位置信息并更新玩家位置。

4.1.2 客户端逻辑处理的优化技巧

为了优化客户端逻辑处理,需要确保数据处理效率最大化,减少不必要的网络延迟,并对可能的异常情况进行处理。以下是一些优化技巧:

避免在主线程中进行耗时的网络操作。考虑使用异步方法或子线程处理网络请求和数据解析。 对于连续的数据请求,使用批处理来减少单独请求的次数。 实现数据缓存机制,对于不经常变化的数据(如玩家属性),在本地进行缓存,减少网络请求。

// 使用异步方法减少主线程阻塞

private async void SendDataAsync()

{

await Task.Run(() =>

{

// 执行耗时的网络请求或数据处理

});

}

通过使用异步方法 SendDataAsync ,我们可以将耗时的数据处理操作放在后台线程中执行,从而避免阻塞主线程,提高客户端的响应性能。

4.2 服务器端代码的编写要点

4.2.1 服务器逻辑结构设计

服务器端的逻辑结构设计应遵循高内聚低耦合的原则,保持代码的清晰和可维护性。通常,服务器逻辑会被分解为若干个离散的组件,如房间管理器、玩家管理器等,每个组件负责处理特定的逻辑。

public class GameRoom : IIn房间管理器

{

private List players = new List();

public void AddPlayer(Player player)

{

players.Add(player);

}

public void RemovePlayer(Player player)

{

players.Remove(player);

}

}

上述代码展示了一个简单的房间管理器组件,其中包含了添加和移除玩家的方法。通过将逻辑封装在这样的组件中,可以使得整个服务器的代码结构更加清晰。

4.2.2 服务器性能优化与异常处理

服务器的性能优化重点在于减少不必要的计算,以及优化数据存储和检索效率。同时,应当合理地分配资源,比如使用线程池来处理并发请求。

异常处理同样关键,确保服务器端能够稳定运行并及时响应异常情况。这可能涉及到捕获并记录异常、重试机制、以及优雅的错误恢复策略。

// 使用线程池处理并发请求

public class ThreadPool

{

private readonly Queue workQueue = new Queue();

private readonly int threadCount;

private readonly List threads = new List();

public ThreadPool(int threadCount)

{

this.threadCount = threadCount;

for (int i = 0; i < threadCount; i++)

{

Thread thread = new Thread(Run);

thread.Start();

threads.Add(thread);

}

}

private void Run()

{

while (true)

{

Action workItem;

lock (workQueue)

{

if (workQueue.Count == 0) continue;

workItem = workQueue.Dequeue();

}

workItem();

}

}

public void QueueUserWorkItem(Action workItem)

{

lock (workQueue)

{

workQueue.Enqueue(workItem);

}

}

}

该线程池类 ThreadPool 定义了一个线程队列和一个工作队列。通过在不同的线程中处理队列中的工作项,能够有效地处理并发请求,提高服务器的性能。同时,通过合理地管理线程资源,可以避免资源耗尽和性能下降。

5. 服务器DLL部署与 Photon Server配置

5.1 服务器DLL的打包与部署步骤

5.1.1 DLL的构建与打包指南

在开发基于Photon Server的应用程序时,DLL(动态链接库)文件的打包与部署是将应用程序推向生产环境的重要步骤。构建DLL文件通常需要遵循以下步骤:

项目配置 - 确保你的项目配置正确,包含了所有必要的Photon Server插件和引用。 编译设置 - 在Visual Studio中设置正确的编译配置,通常是Release模式以获取优化的性能。 清理与构建 - 清理任何旧的构建文件,并重新编译项目。确保没有任何编译错误。 打包插件 - Photon Server的插件通常被打包成DLL文件,这些文件需要被放置到Photon Server安装目录下的相应插件文件夹中。

代码示例(Visual Studio 项目文件):

bin\Release\

TRACE

以上XML配置指示Visual Studio在Release模式下,将输出文件放置到 bin\Release\ 目录,并定义了编译时的符号常量。

5.1.2 DLL部署到服务器的详细流程

部署DLL文件到服务器的流程可以通过以下步骤详细说明:

传输文件 - 将DLL文件通过FTP或者其他文件传输方式传输到Photon Server的 binPlugins 目录。 重启服务 - 文件传输完成后,需要重启Photon Server服务以使新的DLL生效。 验证部署 - 登录到Photon Server的管理控制台进行验证,确保新的DLL已被加载且运行正常。

代码示例(FTP命令行):

ftp

Connected to .

220 Microsoft FTP Service

Name ():

331 Password required for .

Password:

230 User logged in.

ftp> cd binPlugins

250 CWD command successful.

ftp> put .dll

200 PORT command successful.

125 Data connection already open; Transfer starting.

226 Transfer complete.

ftp> quit

221 Goodbye.

这段命令行展示了通过FTP将文件上传到服务器的过程。

5.2 Photon Server的安装与配置要点

5.2.1 Photon Server的安装步骤和环境配置

Photon Server的安装是一个简单的步骤,但需要正确配置环境以确保其高效运行:

下载Photon Server - 访问Photon Server官方网站,下载对应版本的Photon Server安装文件。 运行安装向导 - 运行安装程序,并按照向导指示完成安装。 配置环境变量 - 根据需要配置环境变量,以确保Photon Server命令行工具可以在任何目录下运行。 安装示例应用 - Photon Server安装完成后,你可以安装并测试示例应用程序来验证安装是否成功。

5.2.2 高级配置选项解析和性能调整

Photon Server提供了一个灵活的配置系统,允许你根据需要调整性能:

负载均衡配置 - 如果你有一个多节点的Photon Server集群,你需要配置负载均衡器来分配客户端到合适的节点。 持久化存储设置 - 根据应用需求,你可以配置使用SQL Server或MongoDB作为持久化存储选项。 最大用户数限制 - 你可以设置服务器的最大用户连接数,以确保性能不会因超出负载而下降。

代码示例(配置文件):

100

此XML配置片段定义了Photon Server的用户连接限制。

接下来,我们将探讨客户端连接初始化的详细流程。

6. 客户端连接初始化与房间管理实践

6.1 客户端连接初始化的详细流程

6.1.1 连接服务器的代码示例与分析

在Photon Server中,客户端的连接初始化是一个涉及多个步骤的过程。以下是一个基本的连接服务器的代码示例,使用了PUN(Photon Unity Networking):

using Photon.Pun;

using Photon.Realtime;

public class Player : MonoBehaviourPunCallbacks, IInRoomCallbacks

{

private void Start()

{

PhotonNetwork.ConnectUsingSettings();

PhotonNetwork.AutomaticallySyncScene = true;

}

}

在上面的代码中, PhotonNetwork.ConnectUsingSettings() 方法用于启动客户端的连接过程。这个方法接受一个可选的参数,用于指定连接设置。参数通常可以留空,除非你想要设置特定的区域或者连接类型。

PhotonNetwork.AutomaticallySyncScene 属性被设置为 true ,这意味着当场景被加载时,所有客户端会自动同步到新的场景。

分析: - 当调用 ConnectUsingSettings 方法后,客户端会尝试与服务器建立连接。 - Photon会处理底层的网络通信,使用UDP或TCP协议来传输数据。 - 一旦连接成功,服务器会将客户端加入到一个默认的”Master”房间中。

6.1.2 连接过程中的常见问题与解决方法

在连接过程中可能会遇到各种问题,比如网络延迟、连接失败等。以下是一些常见问题及其解决方法:

问题:连接超时 如果客户端在指定时间内未能成功连接到服务器,会出现连接超时错误。

解决方法: - 确认服务器端口是否开放,以及是否有正确的网络路由。 - 检查客户端到服务器的网络连通性。 - 调整连接超时时间设置。

问题:认证失败 Photon Cloud需要一个有效的AppId才能连接,错误的AppId会导致认证失败。

解决方法: - 核对AppId是否在Photon Cloud控制台中正确设置。 - 确认AppId的类型(如:原生、Unity、WebGL等)与使用的客户端SDK相匹配。

代码中的连接方法可以进一步扩展以处理连接的回调事件,提供更丰富的用户体验和错误处理:

public override void OnConnectedToMaster()

{

Debug.Log("Connected to Master Server");

PhotonNetwork.JoinLobby(TypedLobby.Default); // 加入默认的 lobby

}

public override void OnJoinLobbyFailed(short returnCode, string message)

{

Debug.LogError("Failed to join lobby");

// 可以在这里提供一个错误处理机制

}

public override void OnDisconnected(DisconnectCause cause)

{

Debug.LogError("Disconnected from Photon Server with reason: " + cause);

// 可以在这里提供重连逻辑

}

6.2 游戏房间的创建与管理

6.2.1 房间创建的代码实现和逻辑设计

游戏房间是多人在线游戏中玩家进行交互的基础结构。以下是一个简单的房间创建和加入的代码示例:

public void CreateRoom(string roomName)

{

RoomOptions roomOptions = new RoomOptions();

roomOptions.MaxPlayers = 4; // 设置最大玩家数量

PhotonNetwork.CreateRoom(roomName, roomOptions);

}

public void JoinRoom(string roomName)

{

PhotonNetwork.JoinRoom(roomName);

}

逻辑设计: - CreateRoom 方法允许创建一个新的房间,并且可以设置房间的一些属性,例如最大玩家数量。 - JoinRoom 方法用于加入一个已存在的房间。 - PhotonNetwork创建或加入房间时,会调用回调事件来通知客户端房间状态的变化。

6.2.2 房间管理中事件处理和状态同步

事件处理是管理房间状态的关键。以下是一些重要事件及其处理方法:

public override void OnJoinedRoom()

{

Debug.Log("Joined the room successfully");

// 在这里可以加载游戏场景或执行其他加入房间后的逻辑

}

public override void OnCreateRoomFailed(short returnCode, string message)

{

Debug.LogError("Failed to create room");

// 在这里可以处理创建房间失败的逻辑,例如重试或者显示错误信息

}

状态同步: - Photon通过内置的网络事件来同步所有房间内的状态变化。 - 开发者可以通过实现 IInRoomCallbacks 接口中的回调方法来处理这些事件,如房间成员加入、离开、房间属性改变等。 - 为了确保所有客户端看到相同的游戏状态,需要仔细管理这些事件的处理逻辑,确保它们及时且正确地被处理。

通过这些代码示例和逻辑设计,玩家可以通过客户端软件连接到服务器,并参与或创建游戏房间进行游戏。在实际开发中,还需要考虑安全性、数据同步、异常处理等多方面的因素。

7. 网络事件监听与数据同步机制

网络游戏和其他实时应用的体验质量很大程度上取决于网络事件的处理效率和数据同步的实时性。事件监听和数据同步是Photon Server强大功能的核心部分。本章将深入探讨如何有效地实现和优化这些机制。

7.1 网络事件监听的重要性与实现

事件监听是客户端与服务器间通信的基础,它允许系统在特定事件发生时触发相应的响应。在 Photon Server 中,事件监听提供了实时响应玩家操作或游戏状态改变的能力。

7.1.1 事件监听的原理与代码实现

Photon Server 使用事件回调机制来监听网络事件。客户端和服务器都可通过实现特定的接口或继承基类,来处理诸如玩家加入、离开游戏、玩家移动等事件。

public class GameEventHandler : IPhotonPeerListener

{

public void DebugReturn(DebugLevel level, string message)

{

// 处理调试信息

}

public void OnOperationResponse(OperationResponse operationResponse)

{

// 处理操作响应事件

}

// 其他事件处理方法...

}

在这个示例中, GameEventHandler 类实现 IPhotonPeerListener 接口, Photon Server 将调用适当的方法以通知事件。

7.1.2 高效事件处理的设计模式和实践技巧

事件处理时,可能会存在大量并发事件。因此,采用合适的架构模式是至关重要的。一个普遍推荐的做法是使用事件队列和异步处理模式,以避免在主线程中进行长时间的事件处理。

// 事件处理队列

private Queue eventQueue = new Queue();

// 异步事件处理方法

private void ProcessEvents()

{

while(eventQueue.Count > 0)

{

var action = eventQueue.Dequeue();

action.Invoke();

}

}

// 在事件监听回调中添加事件到队列

public void OnEvent(EventData eventData)

{

eventQueue.Enqueue(() =>

{

// 处理具体事件

});

}

此代码片段展示了事件处理队列和异步处理的基本逻辑。

7.2 数据同步的策略与优化

数据同步对于实时游戏至关重要,不恰当的数据同步策略会导致延迟和卡顿,影响玩家体验。

7.2.1 数据同步的基本原理和类型

数据同步主要分为客户端-服务器(C/S)模式和对等网络模式。在C/S模式中,服务器是数据一致性的权威,客户端接收服务器状态的更新。 Photon Server 主要支持C/S模式,并提供状态更新机制来同步数据。

7.2.2 同步优化策略和减少延迟的方法

为了优化同步,开发者可以减少不必要的数据传输,并只同步变化的部分。 Photon Server 提供了各种同步控制选项,如房间属性、玩家属性的设置,以及事件和命令的过滤。

// 设置房间属性,只同步改变的部分

Room room = PhotonNetwork.JoinRoom("MyRoom");

room.SetCustomProperties(new ExitGames.Client.Photon.Hashtable()

{

{ "key", "value" }

});

// 使用同步过滤器

ExitGames.Client.Photon.Hashtable filters = new ExitGames.Client.Photon.Hashtable();

filters.Add(" интересующий ключ", true);

room.SetInterestGroupProperties(0, filters);

以上代码展示了如何只同步特定属性的改变,以及如何设置同步过滤器,从而有效减少带宽使用和延迟。

在本章中,我们探索了 Photon Server 中的网络事件监听和数据同步机制的实现方法和优化策略。合理的事件监听和数据同步机制对于构建稳定、响应迅速的网络游戏至关重要。在下一章中,我们将详细讨论服务器逻辑处理与事件处理机制,这是进一步提升游戏性能和体验的关键步骤。

本文还有配套的精品资源,点击获取

简介:Photon Server是一款实时网络通信引擎,常用于多人在线游戏和实时协作应用。本文将通过一个简单示例来介绍如何在项目中基本操作Photon Server,包括了解核心概念、客户端与服务器代码的关键部分、以及如何在Unity中进行快速配置。本示例分为客户端代码和服务器DLL代码两个主要部分,旨在帮助开发者掌握Photon Server的初始化连接、房间管理、事件监听和数据同步等核心功能。

本文还有配套的精品资源,点击获取

🎯 相关推荐

电视机顶盒智能卡在哪个位置安装好?
365bet进入官网

电视机顶盒智能卡在哪个位置安装好?

📅 10-26 👁️ 891
和平精英怎么改性别(和平精英男女角色怎么改)
365bet足彩网上投注

和平精英怎么改性别(和平精英男女角色怎么改)

📅 07-14 👁️ 4115
男士护肤必备:欧莱雅劲能醒肤爽肤水测评,控油提亮效果惊人!
如何说再见
365bet进入官网

如何说再见

📅 08-03 👁️ 1356
【科普一下】从地质学角度看,哪吒的老家陈塘关在哪里?
Baldr Force Exe 哈啦板
365bet足彩网上投注

Baldr Force Exe 哈啦板

📅 09-12 👁️ 8130