网络游戏编程-联机版植物大战虫子

本文档详细介绍了多人在线游戏《植物大战虫子》的服务器与客户端之间的网络消息处理,包括游戏房间的创建、加入、退出、更新等操作。还涉及到消息封包、解包、多线程安全、网络IO模型等方面,展示了游戏服务器如何处理玩家的交互请求,并通过消息队列来同步游戏状态。此外,还展示了部分游戏协议,如玩家准备、开始游戏、结束游戏等命令。
摘要由CSDN通过智能技术生成

这是多年前写的小游戏初版,游戏逻辑部分后来已经重构,变成了支持通用塔防游戏的结构,重构版源码后续整理好再介绍,这里先介绍下网络消息、创建房间部分逻辑。 下面是之前打包的初步版本。

联机版植物大战虫子游戏下载地址icon-default.png?t=M4ADhttp://yun.baidu.com/share/link?shareid=2456982233&uk=3442849180

游戏方法:

      1,双击开启服务器 EngineDemoServer_Release.exe。(服务器ip和端口配置在data\Sys\server.ini)

      2,双击开启客户端 EngineDemoClient_Release。 一般开启两个客户端创建房间进行游戏。也可以开启一个客户端然后创建ai玩家进行游戏。

      测试用登录id   test** (注意重复登录会被踢),测试用登录密码 1111

      EngineDemoRobotClient_Release.exe是测试用机器人客户端,可选择性开启

截图

 游戏网络编程 - twopointfive - twopointfive的博客

 游戏网络编程 - twopointfive - twopointfive的博客
 游戏网络编程 - twopointfive - twopointfive的博客

 

服务器

多线程安全问题。接收网络包为了提高吞吐量一般放在子线程中,此时若直接处理消息包,如果需要调用标准的windows控件来显示是不起作用的,如果调用引擎的其它接口比如执行脚本甚至会引起崩溃。所以在子线程收到消息包时将其保存到对列,主线程循环中再从队列弹出并处理,注意对列操作前要加锁。

 另一篇文章单独介绍了消息封包和IOCP,这里不再重复。

客户端网络部分        

//========================================================
//  @Date:     2016.05
//  @File:     Include/Net/NetClient.h
//  @Brief:     NetClient
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#pragma once

#ifndef  __NetClient__H__
#define  __NetClient__H__

#include "General/singleton.h"

#define NET_CLIENT_PORT_MIN		10001
#define NET_CLIENT_PORT_MAX		20001

#define G_Client      NetClient::GetSingleton()

class PacketQueue;
class PacketBase;
class TcpStream;

typedef void*  PSocket;
#include "General/Thread.h"


typedef void (*OutPutFun)(const char *msg);
//?? 可能多个实体 连接多个服务器game chat? 代理模式?
class NetClient:public MemberSingleton<NetClient>
{
public:
	NetClient();
	~NetClient();			

	//!子线程可调用,控制台可以立即输出,但ui无法立即刷新,需要主线程异步处理
	void LogSub(const char* lpszFormat, ...);
	void SetLogFun(OutPutFun fun);

	bool InitNetwork(const char *serv_addr, unsigned int serv_port);
	bool Close();

	//!向服务器发消息
	bool SendMsg(int packetID, char* buffer, int bufferSize);
	bool SendPacketToServer( PacketBase* packet);


	//for inner server
	int  InnerRecvPacket( PacketBase* packet );

	int  RecvStream( PSocket socket, char* buffer, int bufferSize );

	void SetSpecialPacketID(int login,int disconnect);

#ifdef WIN32APP
	//!线程回调
	int RecvThreadProc( void* threadParam );
	void* m_receiveThread;
#endif

#ifdef ANDROIDAPP
	//!网络环境在android中用java开启
	//!网络开启是否成功
	void SetRunning(bool running);
	//!处理tcp流
	int AndroidTcpStream(int streamSize,const char* stream);
#endif

	
	//!处理消息队列
	void MainThreadProcessPacketPool();
	static const char* classname(){return "NetClient";}


	PacketQueue*		m_packetQueue;

	PSocket				m_socket;	
	char                m_serverAddress[256];
	unsigned int		m_serverPort;	
	unsigned int		m_port;	

	//1: connected to net server
	//0: connected to inner server
	bool        m_bConnecting;

	//断开协议id
	int         m_disconnectPacketID;

	OutPutFun   m_logFun;
	char        m_logBuff[1024*10];
	int         m_logSize;
	CriticalSection m_logCriticalSection;

	//!64k合设置的系统缓存一样大,可以大于(浪费),可以小于(低效)
	char*		m_receiveBuffer;

	TcpStream*    m_tcpStream;

	//有些消息阻塞后续消息的发送,比如登录
	bool m_waitingPacket;

	//
#define MaxPingCount 8
	int   m_iPingCount;
	float m_fPingLatency[MaxPingCount];
	float m_fLastPingTime;
	float m_fPingLatencyAvg;
};


#endif

//========================================================
//  @Date:     2016.05
//  @File:     Include/Net/NetClient.h
//  @Brief:     NetClient
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#pragma once

#ifndef  __NetRobotClient__H__
#define  __NetRobotClient__H__

#include "General/singleton.h"

#define NET_CLIENT_PORT_MIN		10001
#define NET_CLIENT_PORT_MAX		20001

class PacketQueue;
class PacketBase;
class TcpStream;

//
typedef void* PSocket;

#include "General/Thread.h"

typedef void (*OutPutFun)(const char *msg);

#define NET_ROBOTCLIENT_PORT_MIN		6000
#define NET_ROBOTCLIENT_PORT_MAX		9000
//模拟客户端的机器人
class NetRobot
{
public:
	friend class NetRobotsClient;	
	NetRobot();
	virtual ~NetRobot();

	virtual void Update(){};
	virtual void* RelocateSyncGameInfo(){return NULL;};

	bool InitNetwork(const char *serv_addr, unsigned int serv_port);
	bool Close();
	//!向服务器发消息
	bool SendMsg(int packetID, char* buffer, int bufferSize);
	void SendPacketToServer( PacketBase* packet);
	int  RecvStream( PSocket socket, char* buffer, int bufferSize );
	void Receive();
	
//protected:
	PSocket				m_socket;	
	//!自己的座位号
	int                 m_playerID;
	unsigned int		m_port;
	int                 m_index;
	char                m_name[128];
	bool                m_bConnecting;

	//!每个socket配备独立缓冲区,减小缓冲可以使机器人占用内存减少
	//!64k合设置的系统缓存一样大,可以大于(浪费),可以小于(低效)
	char*		m_receiveBuffer;

	TcpStream*    m_tcpStream;
	bool   m_waitingPacket;//某些消息在没收到反馈前不能发第二次
};

//若干机器人共用一个线程,否则线程开多了会卡(一个线程至少1M的栈),因此不能在进程中直接开多个客户端。
//模拟多个机器人的客户端
class NetRobotsClient:public MemberSingleton<NetRobotsClient>
{
public:
	NetRobotsClient();
	PacketQueue*			m_packetPool;

	char                m_serverAddress[256];
	unsigned int		m_serverPort;	
				
	//!子线程可调用,控制台可以立即输出,但ui无法立即刷新,需要主线程异步处理
	void LogSub(const char* lpszFormat, ...);
	//!或者使用virtual fun
	void SetLogFun(OutPutFun fun);
	virtual const char* GetPacketLogStr(PacketBase* packet);

	template <class Robot>
	/*virtual*/bool InitRobot(int robotNum);
	NetRobot*  GetRobot(const char* name);
	NetRobot*  GetRobot(int playerID);
	//NetRobot*  GetRobot(int index);
	bool InitNetwork(const char *serv_addr, unsigned int serv_port);
	bool Close();
	static const char* classname(){return "NetRobotsClient";}
	//!线程回调
	typedef struct ThreadParm 
	{
		HANDLE  pThread; 
		int     index;
		int     startRobotIndex;
		int     endRobotIndex;
	} ;
	int MsgReceiverProc( void* threadParam );
	HANDLE*   m_receiveThread;
	int     m_receiveThreadNum;

	//!处理消息队列
	void MainThreadProcessPacketPool();

	NetRobot**  m_robots;
	int         m_robotNum;

	bool m_bRunning;

	OutPutFun   m_logFun;
	char        m_logBuff[1024*10];
	int         m_logSize;
	CriticalSection m_logCriticalSection;
};

template <class Robot>
bool NetRobotsClient::InitRobot(int robotNum)
{
	m_robotNum = robotNum;
	m_robots = new NetRobot*[m_robotNum];
	for (int i=0;i<m_robotNum;i++)
	{
		m_robots[i] = new Robot;
		m_robots[i]->m_index = i;
		sprintf(m_robots[i]->m_name,"robot%04d",i);
	}
	return true;
}

#endif

//========================================================
//  @Date:     2016.05
//  @File:     Include/Net/NetUdp.h
//  @Brief:     NetUdp
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#pragma once

#ifndef  __NetUdp__H__
#define  __NetUdp__H__

#include "General/singleton.h"

#define NET_CLIENT_PORT_MIN		10001
#define NET_CLIENT_PORT_MAX		20001

#define G_UdpPoint      NetPointUdp::GetSingleton()

class PacketQueue;
class PacketBase;

typedef void*  PSocket;
#include "General/Thread.h"

//UDP不存在粘包问题 send、recv个数是一致的
typedef void (*OutPutFun)(const char *msg);

class NetPointUdp:public MemberSingleton<NetPointUdp>
{
public:
	NetPointUdp();
	~NetPointUdp();			

	//!子线程可调用,控制台可以立即输出,但ui无法立即刷新,需要主线程异步处理
	void LogSub(const char* lpszFormat, ...);
	void SetLogFun(OutPutFun fun);
	virtual const char* GetPacketLogStr(PacketBase* packet);

	//无需指定端口
	bool InitNetwork(int broadcastPort);
	bool Close();

	//!向服务器发udp 暂未实现 server必须在外网或同一nat,否则发不过去。在不同nat时服务器必须立刻返回消息,否则链路很短的时间内(200ms)将断开。
	//!向某地址发udp 只能是同一nat 
	bool SendMsgToAdress(char* buffer, int bufferSize,const char* address,int port);
	bool SendPacketToAdress( PacketBase* packet,const char* address,int port);

	//!局域网udp广播 暂时只实现此处
	bool SendPacketBroadcast( PacketBase* packet,int port);

	int  RecvBuffer( PSocket socket, char* buffer, int bufferSize );

#ifdef WIN32APP
	//!线程回调
	int RecvThreadProc( void* threadParam );
	void* m_receiveThread;
#endif

	
	//!处理消息队列
	void MainThreadProcessPacketPool();
	static const char* classname(){return "NetPointUdp";}


	bool                m_bRunning;
	PSocket		        m_broadcastSocket;
	PacketQueue*		m_packetQueue;
	char                m_myAddress[256];
	char                m_hostName[256];

	//广播到的端口,即对方侦听接收的端口,而自己发送的端口是自动分派的。
	int  BroadcastPort; 

	OutPutFun   m_logFun;
	char        m_logBuff[1024*10];
	int         m_logSize;
	CriticalSection m_logCriticalSection;
};


#endif

消息包处理

主循环消息包分发技巧。一种方法是在界面更新函数中处理消息队列,这样可以将一系列消息处理内聚到一起。可能存在的问题是大量不同的界面会无差别的处理相同的消息,以致很多消息要放到基类而形成一个超级类。所以此处将消息包处理通过文件来聚合,同时不必放在界面处理中,通过一个小的设计模式还可以避免一个很大的switch语句的使用,避免需要同时包含所有的消息。每个消息对应一个类,消息解析统一由服务器端编写,客户端负责检查并实现该类的处理函数。

游戏网络编程 - twopointfive - twopointfive的博客

 游戏消息协议
游戏的初步构思是若干开放式小游戏在mmorpg的世界中进行,经过的玩家都可以观战到。
//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoServer/Packet/Protocol.h
//  @Brief:     Protocol
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#pragma once

#ifndef  __Protocol__H__
#define  __Protocol__H__

#define PlayerNameLenMax  32
#define RoomNameLenMax    32
#define LobbyNameLenMax   32
#define IPAddressMax      16

//即时刷新过滤,商城道具列表基本不变无需刷新,俱乐部成员列表点击刷新,房间列表在打开列表界面时即时刷新
enum PacketUpdateFilter
{
    PF_RoomListUpdate = 1,
    PF_PlayerListUpdate = 1<<2,
};
//传输大的列表时将消息分包
enum PacketListFlag
{ 
    List_Begin = 1, 
    List_Data = 2,
    List_End = 4,
};    

//一个ID将一系列功能相关的消息封装起来处理。
enum Protocol
{
    //<<基本
    BEGIN_NORMAL                      = 10000,
    C2S_DisConnection                 ,//断开连接
    S2C_ConnectionShutdown            ,//被踢
    //>>

    //<<登录相关 
    C2S_Verificate                    ,//验证客户端合法性
    S2C_Verificate                    ,
    C2S_Regist                        ,//注册
    S2C_Regist                        ,
    C2S_Login                         ,//登录
    S2C_Login                         ,
    C2S_EnterLobby                    ,//进入大厅
    S2C_EnterLobby                    ,//
    C2S_ExitLobby                     ,//退出大厅
    S2C_ExitLobby                     ,//
    //>>

    //<<聊天相关
    C2S_Chat                          ,//聊天
    S2C_Chat                          ,//
    C2S_ChatPic                       ,//图片聊天,大消息
    S2C_ChatPic                       ,//
    C2S_ChatOption                    ,//聊天设置
    S2C_ChatOption                    ,//
    C2S_MsgList                       ,//消息列表
    S2C_MsgList                       ,//
    C2S_MsgListUpdate                 ,//移除  等
    S2C_MsgListUpdate                 ,//添加 移除  变更等
    //>>

    //<<邮件相关
    C2S_MailList                      ,//邮件列表
    S2C_MailList                      ,//
    //C2S_MailListUpdate                ,//移除  等
    S2C_MailListUpdate                ,//增加 移除  接收 变更等
    C2S_MailOP                        ,//移除  等
    //S2C_MailOP                        ,//添加 移除  变更等
    //>>

    //<<玩家、好友相关
    C2S_PlayerInfo                    ,//玩家信息
    S2C_PlayerInfo                    ,//
    C2S_PlayerList                    ,//玩家列表
    S2C_PlayerList                    ,//
    S2C_PlayerListUpdate              ,//添加 移除 名字 位置变更等
    C2S_FriendList                    ,//好友列表
    S2C_FriendList                    ,//
    C2S_FriendListUpdate              ,//添加 移除  位置变更等
    S2C_FriendListUpdate              ,//
    //>>

    //<<RPG游戏内相关
    S2C_PlayerBaseInfo                ,//玩家基本信息
    S2C_PlayerVisible                 ,//玩家进入退出视野 monster  npc?
    C2S_PlayerMove                    ,//玩家移动,只给在其视野内的玩家发送
    S2C_PlayerMove                    ,
    C2S_PlayerPoint                   ,//玩家点数
    S2C_PlayerPoint                   ,
    C2S_PlayerAnim                    ,//玩家动画
    S2C_PlayerAnim                    ,
    C2S_PlayerState                   ,//玩家状态
    S2C_PlayerState                   ,
    
    S2C_NpcVisible                    ,//npc 单向消息
    S2C_NpcMove                       ,
    S2C_NpcAnim,
    S2C_NpcState,

    S2C_MonsterVisible                ,//monster 单向消息
    S2C_MonsterMove                   ,
    C2S_MonsterHurt                   ,//monster 点数
    S2C_MonsterHurt                   ,
    S2C_MonsterAnim,
    S2C_MonsterState,

    C2S_UsingList                     ,
    S2C_UsingList                     ,
    C2S_Using                         ,//物品操作,具体操作见操作码  :捡取、npc处购买出售、摆摊购买出售、使用、other使用(比如给其他玩家加血)
    S2C_Using                         ,
    C2S_WeaponList                    ,
    S2C_WeaponList                    ,
    C2S_WearList                      ,
    S2C_WearList                      ,
    C2S_Weapon                        ,//装备操作
    S2C_Weapon                        ,
    S2C_WeaponOther                   ,//其它玩家穿脱装备
    C2S_MissionList                   ,
    S2C_MissionList                   ,
    C2S_Mission                       ,//任务操作
    S2C_Mission                       ,
    C2S_SkillList                     ,//已学技能列表
    S2C_SkillList                     ,
    C2S_Skill                         ,//技能操作
    S2C_Skill                         ,
    S2C_OtherSkill                    ,//其它玩家、宠物释放、打断技能
    C2S_ShortcutList                  ,//快捷槽列表
    S2C_ShortcutList                  ,
    C2S_Shortcut                      ,//快捷槽操作
    S2C_Shortcut                      ,
    C2S_Bag                           ,//背包操作
    C2S_Fight                         ,//战斗操作
    //>>

    //<<小游戏房间相关
    C2S_RoomList                      ,//房间列表
    S2C_RoomList                      ,//
    S2C_RoomListUpdate                ,//列表跟新:添加 移除 名字 状态 成员变更等。 只给打开了房间列表界面的玩家发送。房间列表更新频度,房间个数r,玩家个数n,平均视野内玩家个数v,前者复杂度rXn,后者nXv(?一帧内变化的房间一起发)
    C2S_CreateRoom                    ,//创建房间
    S2C_CreateRoom                    ,//自己创建
    C2S_EnterRoom                     ,//进入房间
    S2C_EnterRoom                     ,//创建后进入或选择进入
    S2C_OtherEnterRoom                ,
    C2S_CreateAiRoomPlayer            ,//主机创建ai玩家并进入房间
    S2C_CreateAiRoomPlayer            ,
    S2C_AiEnterRoom                   ,
    C2S_ExitRoom                      ,//退出房间
    S2C_ExitRoom                      ,
    C2S_KickOtherRoomPlayer           ,//房主踢人 或踢ai玩家
    S2C_OtherExitRoom                 ,//别人退出房间   ?? 利用S2C_RoomPlayerUpdate
    C2S_RoomUpdate                    ,//房主改变,房间类型,名字等改变
    S2C_RoomUpdate                    ,//
    C2S_RoomPlayerUpdate              ,//玩家avatar改变,房主,队伍位置改变等
    S2C_RoomPlayerUpdate              ,//
    //S2C_RoomPlayerListUpdate          ,//不添加该消息因为仅可以代替S2C_OtherEnterRoom S2C_OtherExitRoom
    //>>

    //<<小游戏相关
    //一个玩家不可以多视口同时进行两个小游戏,因为只能处在一个房间中
    //小游戏内部消息添加命令类型
    C2S_MiniPlayerReady               ,//玩家准备好
    S2C_MiniPlayerReady               ,
    S2C_MiniGameReady                 ,//游戏准备好
    C2S_MiniGameStart                 ,//房主点击游戏开始
    S2C_MiniGameStart                 ,//
    C2S_MiniGameStartRes              ,//某客户端开启失败
    C2S_MiniGameEnd                   ,//房主端判断游戏结束
    S2C_MiniGameEnd                   ,//
    C2S_MiniGameCmd                   ,//游戏内命令,客户端发给服务器要求转发房间内其它人
    C2S_MiniGameCmdAll                ,//游戏内命令,客户端发给服务器要求转发房间内所有人
    S2C_MiniGameCmd                   ,

    //<<不需要以下消息
    //C2S_MiniGameCmd_2H                ,//客户端发给服务器要求转发主机
    //C2S_MiniGameCmd_2Other            ,//客户端发给服务器要求转发房间内其它所有人
    //S2C_MiniGameCmd_H2                ,//主机发给服务器要求转发房间内其它所有人
    //S2C_MiniGameCmd_C2                ,//客户端发给服务器要求转发房间内其它所有人
    //C2S_MiniGameAICmd                 ,//主机客户端上的ai玩家发给服务器要求转发,和主机真人具有相同socket。 直接使用C2S_MiniGameCmd即可
    //S2C_MiniGameAICmd                 , 
    //主机ai发addplant给server,server要转发屏蔽主机是可以的,主机上ai玩家和主机使用的是同一个游戏环境,此时已经是新的环境了
    //其它玩家发addplant给server,server只要转发一份给主机,即使主机有ai
    //命令可以带有发送者字段,发送者客户端添加
    //>>

    //<<小游戏观战相关
    //观战玩家可以看到minigame中的人在玩vr游戏? 给这些人带上vr头盔标志?
    //一个玩家可以同时看到多个minigame?(最多可以设置上限,服务器是否限制某片区域内minigame数量?比如若干房间都想在一个地点进行赛车游戏)
    //C2S_PlayerReady                   ,//玩家准备好
    //S2C_PlayerReady                   ,
    //S2C_GameReady                     ,//游戏准备好
    C2S_MiniGameOtherOption           ,//设置不观战黑名单的游戏
    S2C_MiniGameOtherVisible          ,//
    //S2C_MiniGameOtherEnd              ,//use visible
    C2S_MiniGameOtherCmd              ,//观战游戏内命令
    S2C_MiniGameOtherCmd              ,
    //>>

    //<<活动相关
    C2S_EventList                     ,//活动列表
    S2C_EventList                     ,//
    C2S_EventPlayerInfo               ,//单个活动及玩家相关信息
    S2C_EventPlayerInfo               ,//
    C2S_EventPlayerOp                 ,//活动中玩家操作
    S2C_EventPlayerOp                 ,//
    //>>

    C2S_PacketUpdateFilter            ,//即时过滤开关

    UDP_S2L_LanServerCreate           ,//创建局域网服务器时发送局域网广播,以便同局域网的客户端可以发现并联机
    UDP_S2L_LanServerClose            ,
    UDP_C2L_LanClientSearchServer     ,//客户端启动或刷新lanserver列表时请求,收到的此消息的lanserver回复UDP_LanServerCreate


    END_MAX,

};

//通用错误码
enum 
{
    Err_WrongMoney = -11,        //金币钱不足
    Err_UnKnow = -999,           //未知错误
};

#endif    // _PROTOCOL_PACKETID_H

植物大战虫子小游戏内部命令

enum MiniPVBCmd
{
CMD_PlantAdd, //种植植物
CMD_PlantDead, //植物死亡
CMD_BugAdd, //产生虫子
CMD_BugDead, //虫子死亡
CMD_BugMove, //虫子移动

CMD_Anim, //播放动画
};

后续对界面做了改版,ui是自己p的图,改来改去就是不好看,将就着用了。

rpg玩家可以参与小游戏内使用技能杀虫子的构思被砍掉了,可能会破坏塔防类游戏的玩法。

  

服务器端房间相关消息处理:写的不好,仅供参考。游戏核心逻辑还没动头呢就要先写一堆基础代码,不是一班的烦。

//========================================================
//  @Date:     2016.05
//  @File:     SourceDemoServer/Packet/PacketRoomServer.cpp
//  @Brief:     PacketRoomServer
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#include "General/Pch.h"
#include "Net/NetServerIocp.h"
#include "Game/NetPlayer.h"
#include "Game/NetRoom.h"
#include "Game/NetLobby.h"
#include "Packet/Protocol.h"
#include "Packet/PacketRoom.h"
#include "Packet/PacketPlayerFriend.h"
#include "Rpg/MiniGameStyle.h"
#include "Game/NetWeapon.h"
#include "General/Pce.h"


//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketRoomList::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);

	NetRoomList& sendList = G_Lobby->m_roomList;
	NetRoomList::iterator it = sendList.begin();
	int flag = 0;
	S2CPacketRoomList packet;
	int accumNum = 0;
	int curNum = 0;
	for ( ; it!=sendList.end();it++ ) 
	{
		//if ( (*it)->m_playerLocation != PL_OFFLINE) 
		{
			if (accumNum==0)
			{
				flag |= List_Begin;
			}
			if (accumNum==sendList.size()-1)
			{
				flag |= List_End;
			}
			//
			LobbyRoomInfo& roominfo = packet.roomInfos[curNum];
			strcpy(roominfo.roomName,(*it)->m_roomName);
			roominfo.roomID = (*it)->m_roomID;
			roominfo.ownerID = (*it)->m_chiefPlayerID;
			roominfo.roomType = (*it)->m_roomType;
			roominfo.roomState = (*it)->m_roomState;
			//
			accumNum++;
			curNum++;
			if (curNum==MaxRoomList
				|| flag&List_End
				)
			{
				packet.infoNum = curNum;
				G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
				flag = 0;
				curNum = 0;
			}
		}
	}

	return true;
}
//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketCreateRoom::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
	{
		G_Server->LogSub("[%d] C2SPacketCreateRoom玩家不存在。\n",m_fromSocket);
		return false;
	}

	if(fromPlayer->m_roomID!=-1) 
	{
		S2CPacketCreateRoom packet;
		packet.m_res = S2CPacketCreateRoom::Err_InRoom;
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketCreateRoom已经在房间内。\n",roomType);
		return false;
	}


	MiniGameStyle* miniGameStyle = G_StyleMgr->GetStyle<MiniGameStyle>(roomType);
	if (miniGameStyle==NULL)
	{
		S2CPacketCreateRoom packet;
		packet.m_res = S2CPacketCreateRoom::Err_NoRoomType;
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketCreateRoom房间类型不存在。\n",roomType);
		return false;
	}

	//int roomID = fromPlayer->m_playerKey;    //bug 房主退出后新建房间id重复
	//int roomID = G_Lobby->m_roomList.size(); //bug 0 1 2; 1room解散后 新建的2重复
	//ASSERT G_Lobby->GetRoom(roomID)==NULL;
	int roomID = G_Lobby->NextValidRoomID(); 

	NetRoom* room = new NetRoom;
	room->m_roomID = roomID;
	room->m_chiefPlayerID = fromPlayer->ObjID();
	room->m_roomType = (MiniGameRoomType)roomType;
	room->m_maxPlayerNum = miniGameStyle->maxPlayerNum;
	room->m_sideNum = miniGameStyle->sideNum;
	if (room->m_maxPlayerNum<=0)
	{
		//至少1个人
		room->m_maxPlayerNum = 1;
	}
	strcpy(room->m_roomName,roomName);
	G_Lobby->AddRoom(room);

	{
		//
		S2CPacketCreateRoom packet;
		packet.roomID = roomID;
		packet.roomType = roomType;
		strcpy(packet.roomName,roomName);
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
	}

	{
		//创建后进入
		room->AddPlayer(fromPlayer);
		fromPlayer->m_bAi = false;
		S2CPacketEnterRoom packet;
		packet.roomID = roomID;
		packet.roomType = (MiniGameRoomType)roomType;
		packet.chiefPlayerID = fromPlayer->ObjID();
		packet.roomSlot = fromPlayer->m_roomSlot;
		strcpy(packet.roomName,roomName);

		//此时应该只有一个人
		MyRoomInfo* info = G_SyncMyRoomInfo;
		info->m_roomPlayers.clear();
		MyRoomPlayerInfo playerinfo;
		NetPlayer* roomPlayer;
		for (int i=0;i<MaxRoomPlayer;++i)
		{
			if (room->m_players[i])
			{
				roomPlayer = room->m_players[i];
				playerinfo.roomSlot = roomPlayer->m_roomSlot;
				strcpy(playerinfo.playerName,roomPlayer->m_playerName);
				playerinfo.playerID = roomPlayer->ObjID();
				playerinfo.level = roomPlayer->m_level;
				playerinfo.isAI = roomPlayer->m_bAi;
				strcpy(playerinfo.icon,roomPlayer->m_headIcon);

				//my avatar没必要发
				playerinfo.style = roomPlayer->GetStyle()->ID;
				WeaponPart* weaponPart = roomPlayer->GetPart<WeaponPart>();
				playerinfo.weaponNum = weaponPart->m_weaponWearList.size();
				int i = 0;
				for (NetWeaponPtrList::iterator it=weaponPart->m_weaponWearList.begin();it!=weaponPart->m_weaponWearList.end();++it)
				{
					playerinfo.weaponStyle[i] = (*it)->StyleID();
					playerinfo.weaponID[i] = (*it)->ObjID();
					i++;
				}
				info->m_roomPlayers.push_back(playerinfo);
			}
		}

		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
	}

	{
		//房间列表改变
		S2CPacketRoomListUpdate packet;
		packet.updateType = S2CPacketRoomListUpdate::Room_Add;
		packet.roominfo.roomID = roomID;
		packet.roominfo.roomType = roomType;
		packet.roominfo.ownerID = room->m_chiefPlayerID;
		strcpy(packet.roominfo.roomName,roomName);
		G_Lobby->SendPacketToLobbyOther(&packet,fromPlayer);
	}
	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketEnterRoom::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
	{
		G_Server->LogSub("[%d] C2SPacketEnterRoom玩家不存在。\n",m_fromSocket);
		return false;
	}
	NetRoom* room = G_Lobby->GetRoom(roomID);
	if(room==NULL) 
	{
		S2CPacketEnterRoom packet;
		packet.roomID = roomID;
		packet.m_res = S2CPacketEnterRoom::Err_NoRoom;//房间不存在
		//packet.roomType = 0;
		//strcpy(packet.roomName,roomName);
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketEnterRoom房间不存在。\n",roomID);
		return false;
	}

	if (fromPlayer->m_roomID==roomID)
	{
		S2CPacketEnterRoom packet;
		packet.roomID = roomID;
		packet.m_res = S2CPacketEnterRoom::Err_AlreadyInRoom;//已经在房间内
		//packet.roomType = 0;
		//strcpy(packet.roomName,roomName);
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketEnterRoom已经在房间内。\n",roomID);
		return false;
	}

	if (room->m_roomState==RS_Gaming)
	{
		S2CPacketEnterRoom packet;
		packet.roomID = roomID;
		packet.m_res = S2CPacketEnterRoom::Err_Gaming;//游戏中
		//packet.roomType = 0;
		//strcpy(packet.roomName,roomName);
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketEnterRoom游戏中。\n",roomID);
		return false;

		//如果类似rpg,游戏进行中玩家也可以随意进入退出游戏,房间就变成了‘任务房间’、‘副本房间’。
		//去掉return,给当事人发送进入房间消息后,再发送游戏开始消息。
	}

	if (room->IsFull())
	{
		S2CPacketEnterRoom packet;
		packet.roomID = roomID;
		packet.m_res = S2CPacketEnterRoom::Err_FullRoom;//满员
		//packet.roomType = 0;
		//strcpy(packet.roomName,roomName);
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketEnterRoom房间满员。\n",roomID);
		return false;
	}

	int res = room->AddPlayer(fromPlayer);
	if (res!=0)
	{
		return false;
	}
	else
	{
		fromPlayer->m_bAi = false;
		{  
			//通知当事人
			S2CPacketEnterRoom packet;
			packet.m_res = 0;
			packet.roomID = roomID;
			packet.roomType = room->m_roomType;
			strcpy(packet.roomName,room->m_roomName);
			packet.chiefPlayerID = room->m_chiefPlayerID;
			packet.roomSlot = fromPlayer->m_roomSlot;

			MyRoomInfo* info = G_SyncMyRoomInfo;
			info->m_roomPlayers.clear();
			MyRoomPlayerInfo playerinfo;
			NetPlayer* roomPlayer;
			for (int i=0;i<MaxRoomPlayer;++i)
			{
				if (room->m_players[i])
				{
					roomPlayer = room->m_players[i];
					playerinfo.roomSlot = roomPlayer->m_roomSlot;
					strcpy(playerinfo.playerName,roomPlayer->m_playerName);
					playerinfo.playerID = roomPlayer->ObjID();
					playerinfo.level = roomPlayer->m_level;
					strcpy(playerinfo.icon,roomPlayer->m_headIcon);
					playerinfo.isAI = roomPlayer->m_bAi;

					//avatar
					if (roomPlayer->m_bAi)
					{
						playerinfo.style = dynamic_cast<MiniAiNetPlayer*>(roomPlayer)->m_npcStyle;
					}
					else
					{
						playerinfo.style = roomPlayer->GetStyle()->ID;
					}
					
					WeaponPart* weaponPart = roomPlayer->GetPart<WeaponPart>();
					playerinfo.weaponNum = weaponPart->m_weaponWearList.size();
					int i = 0;
					for (NetWeaponPtrList::iterator it=weaponPart->m_weaponWearList.begin();it!=weaponPart->m_weaponWearList.end();++it)
					{
						playerinfo.weaponStyle[i] = (*it)->StyleID();
						playerinfo.weaponID[i] = (*it)->ObjID();
						i++;
					}

					info->m_roomPlayers.push_back(playerinfo);
				}
			}
			G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		}

		{
			//通知房间内其它玩家
			S2CPacketOtherEnterRoom packet;
			packet.m_res = 0;
			packet.roomID = roomID;
			packet.playerID = fromPlayer->ObjID();
			packet.roomSlot = fromPlayer->m_roomSlot;
			packet.level = fromPlayer->m_level;
			strcpy(packet.playerName,fromPlayer->m_playerName);

			//avatar
			packet.style = fromPlayer->GetStyle()->ID;
			WeaponPart* weaponPart = fromPlayer->GetPart<WeaponPart>();
			packet.weaponNum = weaponPart->m_weaponWearList.size();
			int i = 0;
			for (NetWeaponPtrList::iterator it=weaponPart->m_weaponWearList.begin();it!=weaponPart->m_weaponWearList.end();++it)
			{
				packet.weaponStyle[i] = (*it)->StyleID();
				packet.weaponID[i] = (*it)->ObjID();
				i++;
			}
	
			room->SendPacketToRoomOther(&packet,fromPlayer);
		}

		{
			//通知大厅内其它玩家房间列表改变
			S2CPacketRoomListUpdate packet;
			packet.updateType = S2CPacketRoomListUpdate::Room_UpdateMember;
			packet.roominfo.roomID = roomID;
			G_Lobby->SendPacketToLobbyAll(&packet);
		}

		fromPlayer->m_playerLocation = PL_INROOM;

		{
			//玩家列表改变
			S2CPacketPlayerListUpdate packet;
			packet.updateType = S2CPacketPlayerListUpdate::Player_UpdateLocation;
			packet.playerinfo.playerID = fromPlayer->ObjID();
			packet.playerinfo.playerLocation = fromPlayer->m_playerLocation;
			G_Lobby->SendPacketToLobbyAll(&packet);
		}
	}
	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketCreateAiRoomPlayer::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
		return false;
	NetRoom* room = G_Lobby->GetRoom(roomID);
	if(room==NULL) 
	{
		S2CPacketCreateAiRoomPlayer packet;
		packet.m_res = S2CPacketCreateAiRoomPlayer::Err_NoRoom;//房间不存在
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketCreateAiRoomPlayer房间不存在。\n",roomID);
		return false;
	}

	if(room->m_chiefPlayerID != fromPlayer->ObjID())
	{
		S2CPacketCreateAiRoomPlayer packet;
		packet.m_res = S2CPacketCreateAiRoomPlayer::Err_NotChief;//不是房主
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketCreateAiRoomPlayer不是房主。\n",roomID);
		return false;
	}

	//ai玩家允许重名
	//NetPlayer* aiplayer = room->GetPlayerByName(playerName);
	//if(aiplayer!=NULL) 
	//{
	//	S2CPacketCreateAiRoomPlayer packet;
	//	packet.m_res = S2CPacketCreateAiRoomPlayer::Err_AlreadyName;//已经存在同名玩家
	//	G_Server->SendPacketToPlayer(&packet,m_fromSocket);
	//	return false;
	//}

	if (room->IsFull())
	{
		S2CPacketCreateAiRoomPlayer packet;
		packet.m_res = S2CPacketCreateAiRoomPlayer::Err_FullRoom;//满员
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketCreateAiRoomPlayer房间满员。\n",roomID);
		return false;
	}

	MiniAiNetPlayer* aiplayer = new MiniAiNetPlayer;
	int res = room->AddPlayer(aiplayer);
	if (res!=0)
	{
		delete aiplayer;
	}
	else
	{
		aiplayer->m_bAi = true;
		aiplayer->m_npcStyle = npcStyle;
		aiplayer->SetID(G_Lobby->NextValidMonsterID());
		Ptr2Socket(aiplayer->m_socket) = Ptr2Socket(m_fromSocket);
		sprintf(aiplayer->m_playerName,playerName);
		static int test = 0;
		//从数据库读取一系列数据
		aiplayer->m_level = level;
		sprintf(aiplayer->m_headIcon,"default%d",test%4);
		test++;

		//aiplayer->npcStyle = npcStyle;

		{
			//通知房间内所有玩家
			S2CPacketAiEnterRoom packet;
			packet.m_res = 0;
			packet.roomID = roomID;
			packet.playerID = aiplayer->ObjID();
			packet.roomSlot = aiplayer->m_roomSlot;
			packet.level = aiplayer->m_level;
			strcpy(packet.playerName,aiplayer->m_playerName);
			packet.style = npcStyle;
			room->SendPacketToRoomAll(&packet,false);
		}

		{
			//通知大厅内其它玩家房间列表改变
			S2CPacketRoomListUpdate packet;
			packet.updateType = S2CPacketRoomListUpdate::Room_UpdateMember;
			packet.roominfo.roomID = roomID;
			G_Lobby->SendPacketToLobbyAll(&packet);
		}
	}
	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketExitRoom::Process() 
{
	//真人, ai退出会发C2SPacketKickOtherRoomPlayer
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
	{
		//S2CPacketExitRoom packet;
		//packet.m_res = S2CPacketExitRoom::Err_NotInRoom;//玩家不存在
		//G_Server->SendPacketToPlayer(&packet,m_fromSocket);
		//G_Server->LogSub("[%d] C2SPacketExitRoom玩家不存在。\n",playerID);
		return false;
	}
	
	//assert fromPlayer->m_roomID == roomID
	roomID = fromPlayer->m_roomID;

	//如果游戏正在进行中,不会发生exit消息,而是直接发送gameend消息。

	NetRoom* room = G_Lobby->GetRoom(roomID);
	if(room==NULL) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_NoRoom;//房间不存在
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom房间不存在。\n",roomID);
		return false;
	}

	if(room->m_roomState == RS_Gaming) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_Gaming;//游戏进行中不能退出
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom游戏进行中不能退出房间。\n",roomID);
		return false;
	}

	int playerID = fromPlayer->ObjID();
	int res = room->RemovePlayer(playerID);
	if (res!=0)
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_NotInRoom;//不在房间内
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom不在房间内。\n",playerID);
		return false;
	}
	else
	{
		if(room->GetRealPlayerNum()==0)
		{
			//最后一个退出,解散房间
			G_Lobby->RemoveRoom(roomID);

			{  
				//通知当事人
				S2CPacketExitRoom packet;
				packet.m_res = 0;
				G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			}

			{
				//通知大厅内其它玩家房间列表改变
				S2CPacketRoomListUpdate packet;
				packet.updateType = S2CPacketRoomListUpdate::Room_Remove;
				packet.roominfo.roomID = roomID;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}

			{
				fromPlayer->m_roomID = -1;
				fromPlayer->m_playerLocation = PL_INLOBBY;

				//玩家列表改变
				S2CPacketPlayerListUpdate packet;
				packet.updateType = S2CPacketPlayerListUpdate::Player_UpdateLocation;
				packet.playerinfo.playerID = playerID;
				packet.playerinfo.playerLocation = fromPlayer->m_playerLocation;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
		}
		else
		{
			//不是最后一个退出
			if (room->m_chiefPlayerID == playerID)
			{
				//房主退出,变更房主
				room->AutoChief();

				S2CPacketRoomUpdate packet;
				packet.roomID = room->m_roomID;
				packet.chiefID = room->m_chiefPlayerID;
				packet.roomType = room->m_roomType;
				strcpy(packet.roomName,room->m_roomName);
				room->SendPacketToRoomAll(&packet);
			}

			{  
				//通知当事人
				S2CPacketExitRoom packet;
				packet.m_res = 0;
				G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			}

			{
				//通知房间内其它玩家
				S2CPacketOtherExitRoom packet;
				packet.m_res    = 0;
				packet.roomID   = roomID;
				packet.playerID = playerID;
				//strcpy(packet.roomName,roomName);
				room->SendPacketToRoomOther(&packet,fromPlayer);
			}

			{
				//通知大厅内其它玩家房间列表改变
				S2CPacketRoomListUpdate packet;
				packet.updateType = S2CPacketRoomListUpdate::Room_UpdateMember;
				packet.roominfo.roomID = roomID;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}

			{
				fromPlayer->m_roomID = -1;
				fromPlayer->m_playerLocation = PL_INLOBBY;

				//玩家列表改变
				S2CPacketPlayerListUpdate packet;
				packet.updateType = S2CPacketPlayerListUpdate::Player_UpdateLocation;
				packet.playerinfo.playerID = playerID;
				packet.playerinfo.playerLocation = fromPlayer->m_playerLocation;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
		}
	}
	return true;
}
//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketKickOtherRoomPlayer::Process() 
{
	可能是真人或ai, ai只存在于房间列表
	//assert player->m_roomID == roomID
	NetRoom* room = G_Lobby->GetRoom(roomID);
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(room==NULL) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_NoRoom;//房间不存在
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom房间不存在。\n",roomID);
		return false;
	}

	//不是房主没有权限
	if(room->m_chiefPlayerID!=fromPlayer->ObjID())
	{
	//	S2CPacketKickOtherRoomPlayer packet;
	//	packet.m_res = S2CPacketKickOtherRoomPlayer::Err_NotChief;
	//	G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketKickOtherRoomPlayer不是房主没有权限。\n",roomID);
		return false;
	}

	//真人或ai
	NetPlayer* beKickPlayer = room->GetPlayerByID(playerID);
	if(beKickPlayer==NULL) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_NotInRoom;//不在房间内
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom玩家不存在。\n",playerID);
		return false;
	}

	bool bAi = beKickPlayer->m_bAi;
	int res = room->RemovePlayer(playerID);
	if (res!=0)
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketExitRoom::Err_NotInRoom;//不在房间内
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketExitRoom不在房间内。\n",playerID);
		return false;
	}
	else
	{
		//房主最后踢了房主
		if(room->GetRealPlayerNum()==0)
		{
			//最后一个退出,解散房间
			G_Lobby->RemoveRoom(roomID);

			{
				//通知被踢玩家
				S2CPacketExitRoom packet;
				packet.m_res = 0;
				//strcpy(packet.roomName,roomName);
				G_Server->SendPacketToSocket(&packet,beKickPlayer->m_socket);
			}

			{
				//通知大厅内其它玩家房间列表改变
				S2CPacketRoomListUpdate packet;
				packet.updateType = S2CPacketRoomListUpdate::Room_Remove;
				packet.roominfo.roomID = roomID;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
			
			{
				beKickPlayer->m_roomID = -1;
				beKickPlayer->m_playerLocation = PL_INLOBBY;

				//玩家列表改变
				S2CPacketPlayerListUpdate packet;
				packet.updateType = S2CPacketPlayerListUpdate::Player_UpdateLocation;
				packet.playerinfo.playerID = playerID;
				packet.playerinfo.playerLocation = beKickPlayer->m_playerLocation;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
		}
		else
		{
			//房主踢了房主
			if (room->m_chiefPlayerID == playerID)
			{
				//房主退出,变更房主
				room->AutoChief();

				S2CPacketRoomUpdate packet;
				packet.roomID   = room->m_roomID;
				packet.chiefID  = room->m_chiefPlayerID;
				packet.roomType = room->m_roomType;
				strcpy(packet.roomName,room->m_roomName);
				room->SendPacketToRoomAll(&packet);
			}

			if(bAi)
			{
				//通知房间内所有玩家
				S2CPacketOtherExitRoom packet;
				packet.m_res    = 0;
				packet.roomID   = roomID;
				packet.playerID = playerID;
				//strcpy(packet.roomName,roomName);
				room->SendPacketToRoomAll(&packet);
			}
			else
			{
				{
					//通知被踢玩家
					S2CPacketExitRoom packet;
					packet.m_res = 0;
					//strcpy(packet.roomName,roomName);
					G_Server->SendPacketToSocket(&packet,beKickPlayer->m_socket);
				}
				{
					//通知被踢除外的玩家
					S2CPacketOtherExitRoom packet;
					packet.m_res  = 0;
					packet.roomID = roomID;
					packet.playerID = beKickPlayer->ObjID();
					//strcpy(packet.roomName,roomName);
					room->SendPacketToRoomOther(&packet,beKickPlayer);
				}
			}

			{
				//通知大厅内其它玩家房间列表改变
				S2CPacketRoomListUpdate packet;
				packet.updateType = S2CPacketRoomListUpdate::Room_UpdateMember;
				packet.roominfo.roomID = roomID;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}


			if(bAi==false)
			{
				beKickPlayer->m_roomID = -1;
				beKickPlayer->m_playerLocation = PL_INLOBBY;

				//玩家列表改变
				S2CPacketPlayerListUpdate packet;
				packet.updateType = S2CPacketPlayerListUpdate::Player_UpdateLocation;
				packet.playerinfo.playerID = playerID;
				packet.playerinfo.playerLocation = beKickPlayer->m_playerLocation;
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
		}
	}
	return true;
}

bool C2SPacketRoomUpdate::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
		return false;
	NetRoom* room = G_Lobby->GetRoom(roomID);
	if(room==NULL) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketEnterRoom::Err_NoRoom;//房间不存在
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
		G_Server->LogSub("[%d] C2SPacketRoomUpdate房间不存在。\n",roomID);
		return false;
	}

	if(room->m_chiefPlayerID!=fromPlayer->ObjID())
	{
		//不是房主 没有权限
		S2CPacketRoomUpdate packet;
		packet.m_res = -1;
		room->SendPacketToRoomAll(&packet);

	}
	else
	{
		//房主退出,变更房主
		room->m_chiefPlayerID = chiefID;
		room->m_roomType = (MiniGameRoomType)roomType;
		strcpy(room->m_roomName,roomName);

		S2CPacketRoomUpdate packet;
		packet.roomID = room->m_roomID;
		packet.chiefID = room->m_chiefPlayerID;
		packet.roomType = room->m_roomType;
		strcpy(packet.roomName,room->m_roomName);
		room->SendPacketToRoomAll(&packet);

		{
			//通知大厅内其它玩家房间列表改变
			S2CPacketRoomListUpdate packet;
			packet.updateType = S2CPacketRoomListUpdate::Room_Update;
			packet.roominfo.roomID = roomID;
			packet.roominfo.roomType = room->m_roomType;
			packet.roominfo.ownerID = room->m_chiefPlayerID;
			strcpy(packet.roominfo.roomName,room->m_roomName);
			G_Lobby->SendPacketToLobbyAll(&packet);
		}
	}
	return true;
}

//==================^_^==================^_^==================^_^==================^_^
bool C2SPacketRoomPlayerUpdate::Process() 
{
	NetPlayer* fromPlayer = G_Lobby->GetPlayerBySocket(m_fromSocket);
	if(fromPlayer==NULL) 
		return false;
	int roomID = fromPlayer->m_roomID;
	NetRoom* room = G_Lobby->GetRoom(roomID);
	if(room==NULL) 
	{
		S2CPacketExitRoom packet;
		packet.m_res = S2CPacketEnterRoom::Err_NoRoom;//房间不存在
		G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
//		G_Server->LogSub("[%d] C2SPacketRoomUpdate房间不存在。\n",roomID);
		return false;
	}

	if (updateType == RoomPlayer_AvatarChange)
	{
		//不会收到此类型,会收到weapon update
	}
	else if (updateType == RoomPlayer_SlotChange)
	{
		S2CPacketRoomPlayerUpdate packet;
		packet.updateType = updateType;
		//房主可以变更其它人的位置
		NetPlayer* changePlayer   = G_Lobby->GetPlayerByID(playerID);
		NetPlayer* beChangePlayer = room->GetPlayerBySlot(toRoomSlot);
		if(changePlayer==NULL) 
		{
//			packet.m_res = S2CPacketRoomPlayerUpdate::Err_NoPL;
			G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			return false;
		}
		else if (room->m_chiefPlayerID!=fromPlayer->ObjID()
			&&(changePlayer!=fromPlayer || beChangePlayer!=NULL)
			)
		{
			//不是房主 没有权限
			packet.m_res = -1;
			G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			return false;
		}
		else
		{
			//成功换队
			if (beChangePlayer)
			{
				Swap(changePlayer->m_roomSlot,beChangePlayer->m_roomSlot);
				packet.playerID   = beChangePlayer->ObjID();
				packet.toRoomSlot = beChangePlayer->m_roomSlot;
				room->SendPacketToRoomAll(&packet);
			}
			else
			{
				changePlayer->m_roomSlot = toRoomSlot;
			}
		
			packet.m_res = 0;
			packet.playerID   = changePlayer->ObjID();
			packet.toRoomSlot = changePlayer->m_roomSlot;
			room->SendPacketToRoomAll(&packet);


			{
				//通知大厅内其它玩家房间列表改变
				S2CPacketRoomListUpdate packet;
				packet.updateType = S2CPacketRoomListUpdate::Room_Update;
				packet.roominfo.roomID   = roomID;
				packet.roominfo.roomType = room->m_roomType;
				packet.roominfo.ownerID  = room->m_chiefPlayerID;
				strcpy(packet.roominfo.roomName,room->m_roomName);
				G_Lobby->SendPacketToLobbyAll(&packet);
			}
		}
	}
	else if (updateType == RoomPlayer_StyleChange)
	{
		S2CPacketRoomPlayerUpdate packet;
		packet.updateType = updateType;
		NetPlayer* changePlayer   = G_Lobby->GetPlayerByID(playerID);
		if(changePlayer==NULL) 
		{
			//			packet.m_res = S2CPacketRoomPlayerUpdate::Err_NoPL;
			G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			return false;
		}
		else 
		{
			packet.miniPlayerStyle = miniPlayerStyle;
			G_Lobby->SendPacketToPlayer(&packet,fromPlayer);
			return false;
		}
	}
	return true;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值