welcome to my lianliankan game on line
project name: lianliankan online
project role: leader and main developer
project technique: vs 2005 ,c/c++,c/s communication,MFC
project introduction: this is a whole game platform and game ,you can register and login into the game hall ,there are different areas according to your skill . you can sit on a table and wait for players to come to play . It can also detect the players off line .
project core code:
/////socket.h
#include<WinSock2.h>
#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
#include"GameDataManager.h"
#include"playerInfo.h"
#pragma comment(lib,"DataBaseManagerDll.lib")
#pragma comment(lib,"WS2_32.lib")
#define MAX_PLAYERS_IN_ROOM 20//每个房间做多20人
#define MAXTABLENUMBER 12//每个房间12张桌子
#define MAXROOMNUMBER 18//先设置18的房间
#define MAX_PLAYERS_IN_HALL 100000//大厅最多支持100000人同时在线
class CInitSock///////////初始化socket的代码
{
public:
CInitSock(BYTE minorVer=2,BYTE majorVer=2)
{
WSADATA wsaData;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if (::WSAStartup(sockVersion,&wsaData)!=0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
//////////////////
///////////////////////////////////////////////
////////// 下面全是与网络通信的结构与函数
typedef struct _playerServerInfo//玩家在服务器上的信息
{
string userName;//玩家的用户名,也是玩家的唯一标示符,不同的玩家的用户名是不可以相同的
string level;//玩家的级别
int lMarks;//玩家的积分
int nStatus;//玩家的状态,分为OUTROOM(在房间外),INROOM(在房间里),ONTABLE(在桌子上), READY(准备好了),PLAYING(正在玩游戏)
int nRoomNumber;//玩家玩游戏的房间-1表示玩家没有选择房间
int nTableNumber;//玩家玩游戏的桌子-1表示玩家没有选择桌子
string competitorName;//表示对家的用户名,其实也是可以找到对家的唯一的密码
int nOnlineStatus;//ONLINE表示在线,WAITCONFIRMED表示等待确认
}PLAYERSERVERINFO,*LPPLAYERSERVERINFO;
typedef struct _SOCKET_OBJ//服务器端一个玩家的通信对象
{
bool bIsValid;//表示这个通信对象是不是与具体的玩家有关联,当玩家退出游戏时就把它设置为false
SOCKET s;//玩家通信的套接字
WSAEVENT hEvent;//与玩家通信的套接字关联的事件,标示玩家收到客户端的信息
sockaddr_in addrRemote;//玩家客户端的地址信息
int nAddMode;//REPLACING 表示是替换原来的已经退出的玩家 NEWBIRTH表示新生的
PLAYERSERVERINFO playerInfo;//玩家的信息
}SOCKET_OBJ,*PSOCKET_OBJ;
//游戏大厅的数据结构
typedef struct _ROOM_INFO//描述的是一个房间内的情况
{
vector<PSOCKET_OBJ> vecRoomPlayersSocketObi;//存的是此时这个房间内的所有的玩家
PSOCKET_OBJ desksSocketObjArr[MAXTABLENUMBER];//存的是此时这个房间内的4个桌子上的玩家
int nTotalPlayers;
}ROOM_INFO,*PROOM_INFO;
//下面是当玩家从游戏大厅内的一个房间到另一个房间的时候得把这个玩家从这个房间删除
typedef struct _HALL_MANAGER//大厅的管理
{
vector<PSOCKET_OBJ> vecTotalPlayersSocketObj;//进入游戏内的所有的玩家
vector<PSOCKET_OBJ> vecNetBrokenPlayersSocketObj;//表示断线的玩家
map<int ,int >netBroken_onlineMap;//这两个int分别是玩家在vecTotalPlayersSocketObj里面的位置和vecNetBrokenPlayersSocketObj里面的位置的对应关系
ROOM_INFO totalRooms[MAXROOMNUMBER];//暂时设置18个房间
int nTotalPlayers;
}HALL_MANAGER,*PHALL_MANAGER;
typedef struct _THREAD_OBJ//线程数据对象
{
bool bIsFull;//标示这个线程是否已经玩家数量达到最大值了
WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];//记录当前线程等待的事件句柄
int nSocketCount;//记录当前套接字的数量<=WSA_MAXIMUM_WAIT_EVENTS
vector<PSOCKET_OBJ> vecSocket;//记录当前线程中所有的通信套接字
CRITICAL_SECTION cs;//关键码,为的是同步对本结构的访问
}THREAD_OBJ,*PTHREAD_OBJ;
//
///全局变量
//vector<PTHREAD_OBJ> vecTotalThread;
//CRITICAL_SECTION g_cs;//同步对这个对象的访问
typedef struct _TOTALTHREAD//存放所有线程对象的全部对象结构
{
vector<PTHREAD_OBJ> vecTotalThread;
map<string,PSOCKET_OBJ> userName_userSocketMap;//把玩家的唯一标示符用户名与玩家的通信信息关联起来
//关联事件发生在玩家登陆时或者是注册时
//这样做的目的是当玩家和另一个玩家玩的时候,玩家只要知道对方的用户名,就可以和对方进行通信了
HALL_MANAGER myHallOfGame;//我的游戏大厅的数据
CGameDataManager *pMyGameDataManager;//管理数据库的对象
CRITICAL_SECTION g_cs;
}TOTALTHREAD,*PTOTALTHREAD;
typedef struct _THREAD_PARAM//传递给线程函数的参数
{
PTHREAD_OBJ pThread;
PTOTALTHREAD pTotalThread;
}THREAD_PARAM,*PTHREAD_PARAM;
typedef struct _competitorThreadParam
{
PSOCKET_OBJ pSocket;
PTOTALTHREAD pTotalThread;
}COMPETITORTHREADPARAM,*LPCOMPETITORTHREADPARAM;
///////////////////下面是各种消息的数据结构
//消息的结构统一设置成如下格式
typedef struct _netMsg
{
UINT nFlags;//消息标示符,代表消息的种类
UINT nSize;//表示消息的正文大小
char buffer[256];//消息正文的缓冲区
}NETMSG,*LPNETMSG;
///下面是各种消息的标示符
enum{REGMSGIID,REGSUCCESSMSGIID,REGERRORMSGIID,LOGINMSGIID,LOGINERRORMSGIID,LOGINSUCCESSMSGIID,
ENTERROOMMSGIID,ROOMFULLMSGIID,PLAYERINTHISROOMMSGIID,PLAYERINFOMSGIID,TABLEPLAYERINFOMSGIID,
LEAVEROOMMSGIID,
ENTERROOMPLAYERINFOMSGIID//当玩家进入房间时,服务器会向其他的玩家发送这个消息
,LEAVEROOMPLAYERINFOMSGIID//当玩家离开房间时服务器会向其他的玩家发送这个消息
,SITTABLEMSGIID,LEAVETABLEMSGIID,STARTGAMEMSGIID,CHATMSGIID,READYMSGIID,ONEHITMSGIID,GAMEOVERMSGIID,
CONFIRMONLINEMSGIID,NETBROKENMSGIID,ONLINECONFIRMMSGIID,NETBACKMSGIID,CHECKUSERNAMEMSGIID,CHECKUSERNAMERESMSGIID,QUITMSGIID
};
//下面是程序的常量
enum{CONGRATULATIONS=100,HASEXEISTED,OUTROOM,INROOM,ONTABLE,READY,PLAYING,NOUSERNAME,CODEERROR,ROOMFULL,BEUSED
,EMPTY,ENTER,LEAVE,SITTABLE,LEAVETABLE,START,ONEHITADDED,NORMAL,QUIT,BADEXIT,ONLINE,WAITCONFIRMED,NEWBIRTH,REPLACING,USERNAMEVALID,USERNAMEINVALID};
//检测用户名是否无效
typedef struct _checkUserNameMsg
{
string userName;
}CHECKUSERNAMEMSG,*LPCHECKUSERNAMEMSG;
//下面是回复检测用户名是否有效的消息
typedef struct _checkUserNameResMsg
{
int nFlags;//USRNAMEVALID 表示用户名有效 USERNAMEINVALID 表示用户名无效
}CHECKUSERNAMERESMSG,*LPCHECKUSERNAMERESMSG;
//注册消息
typedef struct _regMsg
{
string userName;//用户名
string userCode;//密码
}REGMSG,*LPREGMSG;
//注册成功消息格式如下
typedef struct _regSuccessMsg
{
int nSuccessFlag;//为CONGRATULATIONS时表示注册成功
long lMarks;
string level;
}REGSUCCESSMSG,*LPREGSUCCESSMSG;
//注册不成功的消息格式如下
typedef struct _regErrorMsg
{
int nErrorFlag;//错误代码为HASEXEISTED表示用户名存在
}REGERRORMSG,*LPREGERRORMSG;
//
//登陆消息格式如下
typedef struct _loginMsg
{
string userName;
string userCode;
}LOGINMSG,*LPLOGINMSG;
//登陆成功消息格式如下
typedef struct _loginSuccessMsg
{
int nSuccessFlags;
long lMarks;//返回积分信息
string level;
}LOGINSUCCESSMSG,*LPLOGINSUCCESSMSG;
//登陆失败消息格式如下
typedef struct _loginErrorMsg
{
int nErrorFlags;//NOUSERNAME表示用户不存在,CODEERROR表示密码不正确
}LOGINERRORMSG,*LPLOGINERRORMSG;
//进入房间消息格式如下
typedef struct _enterRoomMsg
{
int nRoomNumber;
}ENTERROOMMSG,*LPENTERROOMMSG;
//房间满了的消息格式如下
typedef struct _roomFullMsg
{
int nFlags;//ROOMFULL表示房间满了
}ROOMFULLMSG,*LPROOMFULLMSG;
///通知客户端这个房间内有多少玩家的消息格式如下
typedef struct _playerInThisRoomMsg
{
int nRoomNumber;
int nTotalPlayersNumber;
}PLAYERINTHISROOMMSG,*LPPLAYERINTHISROOMMSG;
//传递玩家信息的消息格式如下
typedef struct _playerInfoMsg
{
string name;
long lMarks;
string level;
}PLAYERINFOMSG,*LPPLAYERINFOMSG;
//下面是座位上的玩家信息
typedef struct _tablePlayerInfoMsg
{
int nTableNumber;//代表桌子号
int nStatus;//状态表示座位上有没有玩家,BEUSED,EMPTY
string name;
long lMarks;
string level;
int nOnlineStatus;///表示表示是否在线
}TABLEPLAYERINFOMSG,*LPTABLEPLAYERINFOMSG;
//下面是玩家离开房间时发送的消息
typedef struct _leaveRoomMsg
{
int nRoomNumber;//房间号
}LEAVEROOMMSG,*LPLEAVEROOMMSG;
//下面是玩家选择桌子时发送的消息
typedef struct _sitTableMsg
{
int nTableNumber;//桌子的号码0-7
string name;
long lMarks;
string level;//这里也记录这坐在这张桌子上的玩家信息
}SITTABLEMSG,*LPSITTABLEMSG;
//下面是玩家离开桌子,也就是在游戏窗口中点了退出游戏
typedef struct _leaveTableMsg
{
int nTableNumber;
}LEAVETABLEMSG,*LPLEAVETABLEMSG;
//玩家点了开始游戏的消息
typedef struct _readyMsg
{
int nFlags;//READY表示开始
}READYMSG,*LPREADYMSG;
//游戏开始的消息通知
typedef struct _startGameMsg
{
int nStatus;//START表示游戏开始
}STARTGAMEMSG,*LPSTARTGAMEMSG;
///聊天 消息
typedef struct _chatMsg
{
char words[128];
}CHATMSG,*LPCHATMSG;
//
//单击消除一对方块通知
typedef struct _oneHitMsg
{
int nFlags;//ONEHITADDED表示成功的消除一对
}ONEHITMSG,*LPONEHITMSG;
//游戏结束上传游戏结果 消息
typedef struct _gameOverMsg
{
int nFlags;//NORMAL表示正常结束,BADEXIT表示强退,QUIT表示放弃
long lMarksGet;//表示游戏的得分
}GAMEOVERMSG,*LPGAMEOVERMSG;
//确认玩家是否在线消息
typedef struct _confirmOnlineMsg
{
int nFlags;
}CONFIRMONLINEMSG,*LPCONFIRONLINEMSG;
//下面是断线玩家的信息
typedef struct _netBrokenMsg
{
int nTableNumber;///表示哪个桌子上的玩家断线了
}NETBROKENMSG,*LPNETBROKENMSG;
//下面是玩家上线消息
typedef struct _netBackMsg
{
int nTableNumber;
}NETBACKMSG,*LPNETBACKMSG;
//下面是玩家发过来的确认在线消息
typedef struct _onlineConfirmMsg
{
int nFlags;//
}ONLINECONFIRMMSG,*LPONLINECONFIRMMSG;
typedef struct _quitMsg
{
int nStatus;//QUIT表示放弃
}QUITMSG,*LPQUITMSG;
//把一个套接字分配给一个已创建的套接字线程处理
PSOCKET_OBJ AssignToExistThread(SOCKET s,PTHREAD_OBJ &pThread);
//当有新的连接到达时,不是急着新分配新的socket_obj对象,
///而是先查找之前的线程对象看看有没有空闲的位置可以放置,如果没有才会分配新的对象
int FindInThreadObj(PTHREAD_OBJ &pThread,SOCKET s);
//释放一个套接字通信对象
void CloseSocketObj(PSOCKET_OBJ &pSocket);
//当一个线程已经到达能够处理的最大数量的玩家对象时,就需要申请新的线程对象
PTHREAD_OBJ &GetThreadObj(TOTALTHREAD &gTotalThread);
///处理网络事件的函数
bool HandleIO(PTHREAD_PARAM pTotalThreadParam,PSOCKET_OBJ pSocket,int i);
///线程函数
DWORD WINAPI ServerThread(LPVOID lparam);
//将一个套接字安排给一个新创建的线程处理
void AssignToNewThread(SOCKET s,TOTALTHREAD &gTotalThread);
///下面是接受到一个套接字的时候要把这个套接字分配让线程去管理
void ManagerNewSocket(SOCKET s,TOTALTHREAD &gTotalThread);
//初始化房间
void InitialRoom(ROOM_INFO &myRoom);
//初始化大厅
void InitialHall(HALL_MANAGER &myHall);
//进房间函数
bool EnterRoom(ROOM_INFO &myRoom,PSOCKET_OBJ pSocket);
//下面是更新玩家的位置(包括房间和桌子)
bool ChangePlayerPosition(int nFlags,HALL_MANAGER &myHall,PSOCKET_OBJ pSocket);
//把玩家从房间里删除
bool RemovePlayerFromRoom(ROOM_INFO &roomInfo,PSOCKET_OBJ pSocket);
//把房间内的所有玩家信息发送给客户端玩家
void SendAllPlayersToClient(PTOTALTHREAD &pTotalThread,PSOCKET_OBJ &pSocket);
//下面是把这个房间内的8个桌子上的玩家信息发送给客户端
void SendTotalPlayersOnTablesToClient(PTOTALTHREAD &pTotalThread,PSOCKET_OBJ &pSocket);
//这个函数是把刚刚进入房间的玩家信息发送给所有已经在这个房间内的玩家
//或者是刚刚离开房间的玩家信息发送给所有这个房间内的玩家
//根据nFlags而定当为ENTER时为第一个,当为LEAVE时为第二个
void SendChangedRoomPlayerInfoToAllPlayers(int nFlags,PTOTALTHREAD &pTotalThread,PSOCKET_OBJ &pSocket,int nRoomNumber);
//把坐上桌子的玩家或者是离开桌子的玩家信息发送给所有其他的玩家更新客户显示
void SendChangedTablePlayerMsgToALlPlayers(int nFlags,PTOTALTHREAD &pTotalThread,PSOCKET_OBJ &pSocket,int nTableNumber);
//由玩家的PSOCKET_OBJ 得到对方的PSOCKET_OBJ ,这个时候对方如果没有玩家的话就为NULL
PSOCKET_OBJ GetCompetitorSocketObj(PTOTALTHREAD pTotalThread,PSOCKET_OBJ pSocket);
//此线程是用来检测对方的状态的,当对方有玩家的时候就会
//设置对方的name可以和对方聊天了
//如果对方开始了,玩家也开始了,那么游戏就开始了
DWORD WINAPI CheckGameCompetitorThread(LPVOID lParam);
//根据相应的分数来更改相应的级别
void UpdateLevelAccordingMarks(string &s,long marks);
//下面是把玩家从断线玩家列表中删除并且把玩家加入到在线玩家列表中,或者是从在线玩家列表中删除,把玩家加入到断线玩家列表中
int ChangePlayerPosition(HALL_MANAGER &myHallOfGame,PSOCKET_OBJ &pSocket);
//查找玩家是否在断线列表中
int FindPlayersINNetBrokenPlayers(vector<PSOCKET_OBJ> &playersVec,PSOCKET_OBJ &pSocket);