【C++小游戏】炸飞机代码(联机版)

Part 0. 声明

本小游戏是基于 WinSocket 开发的,类似的代码如 C++ Chat。如果有严重影响游戏体验的问题(包括但不限于如无法匹配等),欢迎私信指出。
由于本程序使用多线程,故代码中有一些等待其他程序或线程的代码、标记等。
游戏中,空心方块为空,实心方块为机身,三角实心块为机头。
炸飞机网址

Update on 2024/9/22 : 解决了在 Win10 中无法正常运行的问题;优化了连接和消息接收等策略;支持自动检测和申请防火墙规则(会自动申请管理员权限,望周知);修复了一些已知问题。

Part 1. 功能介绍

  • 本小游戏支持(包括但不限于)以下功能:
    • 支持联机游戏,可在同局域网(机房)内进行双人对局;
    • 有老板键(Alt+Q);
    • 模拟炸飞机网页游戏,布置地图时支持放置、移动、旋转、一键放置未放置的飞机;
    • 对局时,可以同时看到自己有没有炸到对手和对手有没有炸到自己;
    • 可以在服务端代码主函数中注释掉隐藏窗口的代码然后作弊(逃)

Part 2. 操作简介

  • 用键盘上的 WASD 移动;
  • ALT+Q(灵敏度可在代码中调整)隐藏窗口,再按一次显示;
  • 在放置界面:
    1. V 放置飞机,在放置点再按一下 V 删除飞机;
    2. 按空格选择,选择后可移动飞机;再按一次空格取消选择;若碰到障碍物无法移动则自动取消选择;
    3. 在放置点按 R 顺时针旋转飞机;
    4. Q 一键放置剩余未放置的飞机;
    5. 按回车键确定放置完毕。
  • 在对局界面,按空格键炸标记点。

Part 3. 如何使用

将文末的两份代码保存在同个文件夹内并分别编译(编译指令加上 -lws2_32),随后运行 client.exe 即可。

Part 4. 游戏截图

  • 开始界面:
    图片02
  • 放置界面:
    图片03
  • 对局界面:
    图片04

Part 5. 注意事项与提示

  1. 需要在 windows 下运行;
  2. 不要开 O2(它会吃掉你的多线程)
  3. 如果在放置界面操作失败,则需要考虑操作后飞机是否重叠或出界;放完 3 3 3 架飞机后才可继续;
  4. 设置的名字长度不超过 10 10 10(中文算 2 2 2 个字符),首字母不得为 O
  5. 若出现乱码现象,可在默认值中更改字符集为 gbk(win10 可使用旧版控制台);
  6. 不要和你的伙伴取相同的名字。
  7. 此代码需要您提供管理员权限来更改防火墙规则,否则程序无法正常联机执行。您也可以手动添加防火墙入站/出站规则或直接关闭防火墙。

放置界面中飞机的中心(唯一可以代表一架飞机的地方,图中用绿色方块表示):
图片01

Part 6. 代码

Version:2.1.0

client.cpp

#include<bits/stdc++.h>
#include<windows.h>
#include<conio.h>
#pragma comment (lib,"ws2_32")
#define port 1337//端口号
#define sz 6400//缓冲区长度
#define debug cout<<"**debug**\n";
#define pii pair<int,int>
#define endl '\n'
#define For(i,l,r) for(int i=l,i##end=r;i<=i##end;++i)
#define Rof(i,l,r) for(int i=l,i##end=r;i>=i##end;--i)
using namespace std;
int grid[25][25],vis[25][25]; // 炸到标记
int Map[25][25]; // 我方地图
int Hi=0; // 是否隐藏窗口
string name;
#define kd(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000)?1:0)
void gotoxy(int x,int y){COORD pos={(short)x,(short)y};HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleCursorPosition(hOut,pos);return;}
void noedit(){HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);DWORD mode;GetConsoleMode(hStdin,&mode);mode&=~ENABLE_QUICK_EDIT_MODE;mode&=~ENABLE_INSERT_MODE;mode&=~ENABLE_MOUSE_INPUT;SetConsoleMode(hStdin,mode);}
void hide(){CONSOLE_CURSOR_INFO cur={1,0};SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cur);} // 隐藏光标
void show(){CONSOLE_CURSOR_INFO cur={1,1};SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cur);} // 显示光标
void noEdit(){ // 取消快速编辑模式
	HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);
	DWORD mode;
	GetConsoleMode(hStdin,&mode);
	mode&=~ENABLE_QUICK_EDIT_MODE;
	mode&=~ENABLE_INSERT_MODE;
	mode&=~ENABLE_MOUSE_INPUT;
	SetConsoleMode(hStdin,mode);
}
bool rulestatus;
void ChkRule(){ // 检查防火墙规则
	rulestatus=!system("netsh advfirewall firewall show rule name=\"Flight\"");
	system("cls");
}
void RunAsAdmin(){ // 申请管理员权限
    BOOL isElevated = FALSE;
    HANDLE hToken = NULL;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
        TOKEN_ELEVATION elevation;
        DWORD dwSize;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) {
            isElevated = elevation.TokenIsElevated;
        }
    }
    if (hToken) {
        CloseHandle(hToken);
    }
    if (!isElevated) {
        // 如果不是管理员,以管理员权限重新启动自己
        SHELLEXECUTEINFO sei = { sizeof(sei) };
        sei.lpVerb = TEXT("runas");
        sei.lpFile = TEXT(_pgmptr);
        sei.hwnd = NULL;
        sei.nShow = SW_NORMAL;
        if (!ShellExecuteEx(&sei)) {
            DWORD dwError = GetLastError();
            if (dwError == ERROR_CANCELLED)
                MessageBox(NULL, "This program requires administrator privileges to modify firewall rules. Please be aware of this.", "Elevation Canceled", MB_OK);
        }
        exit(0);
    }
}
void ApplyRule(){ // 申请防火墙入站和出站规则
	RunAsAdmin();
	system("netsh advfirewall firewall add rule name=Flight dir=in action=allow protocol=tcp localport=1337");
	system("netsh advfirewall firewall add rule name=Flight dir=out action=allow protocol=tcp localport=1337");
	ChkRule();
}
string GetIP(){ // 获取IP地址
    WSADATA WSAData;
    char hostName[256];
    if (!WSAStartup(MAKEWORD(2, 0),&WSAData)){
        if(!gethostname(hostName,sizeof(hostName))){
            hostent *host=gethostbyname(hostName);
            if(host!=NULL){
                return inet_ntoa(*(struct in_addr*)*host->h_addr_list);
            }
        }
    }
    return ".-1";
}
DWORD WINAPI whide(LPVOID param){ //隐藏窗口
	HWND hWnd=GetConsoleWindow();
	while(1){
		if(kd(18)){ // Alt
			int t=clock();
			while (clock()<=t+300){
				if (kd('Q')){
					if(Hi) Hi=0;
					else Hi=1;
					break;
				}
			}
		}
    	if(Hi) ShowWindow(hWnd,SW_HIDE); // 隐藏窗口
    	else ShowWindow(hWnd,SW_SHOW); // 显示窗口
		Sleep(150);
	}
}
bool modify=0,mod=0;
const int N=12,M=19,P=26;
SOCKET server;
string a;
struct player{int x,y;void check(){x=max(1,x),x=min(M,x),y=max(3,y),y=min(N,y);}}p[P]; // 准心
string yts(){ // 地图转换
	string ret="";
	For(i, 1, 10){
		For(j, 1, 10){
			ret.push_back(Map[i][j]+'0');
		}
	}
	return ret;
}
char rc[100000];
bool closed=0;
int toint(string st){ // 字符串转自然数
	int ret=0;
	for (char it:st){
		ret=ret*10+(it-'0');
	}
	return ret;
}
int step1, step2; // step1: 别人炸我,step2:我炸别人
bool died1,died2;
bool flagg;
DWORD WINAPI receive(LPVOID param){ // 接收来自服务器的消息
	while (1){
		memset(rc,0,sizeof(rc));
		int rag=recv(server,rc,sz,0);
		if(rag==-1){system("cls"),puts("Err: Room closed!\n");closed=1;exit(0);}
		string st=rc;
		if ((*st.rbegin())=='\n') st.pop_back();
		if (st.substr(0,1)=="O"){ // OUTxxx
			string str="";int poss=3;
			while (poss<(int)st.size())str.push_back(st[poss]),++poss;
			if (str==a||died1){
				died2=1;
				modify=1;
			}else{
				died1=1;
				modify=1;
			}
		}else if (st.size()>=a.size()&&st.substr(0,a.size())==a){ // 别人炸我
			string str=st.substr(a.size(),(int)st.size()-(int)a.size());
			string xx="",yy="",status="";
			int poss=1;
			while (str[poss]!='|') xx.push_back(str[poss]),++poss;++poss;
			while (str[poss]!='|') yy.push_back(str[poss]),++poss;++poss;
			while (poss<(int)str.size()) status.push_back(str[poss]),++poss;
			vis[toint(xx)][toint(yy)]=toint(status); // 更新自己地图是否被炸过
			++step1;
			modify=1;
			flagg=1;
		}else{ // 我炸别人
			string str=st;
			string xx="",yy="",status="";
			int poss=0;name="";
			while (str[poss]!='|') name.push_back(str[poss]),++poss;++poss; // 截取对手名字并更新
			while (str[poss]!='|') xx.push_back(str[poss]),++poss;++poss;
			while (str[poss]!='|') yy.push_back(str[poss]),++poss;++poss;
			while (poss<(int)str.size()) status.push_back(str[poss]),++poss;
			grid[toint(xx)][toint(yy)]=toint(status); // 从服务器获取对手地图的这一格
			++step2;
			modify=1;
			mod=1;
		}
	}
}
HANDLE hThread1,hThread2;
bool ended=0;
void Client(){ // 占位主线程
	memset(rc,0,sizeof(rc));
	while(1){
		if (closed) exit(0);
		if (ended) return;
		Sleep(10);
	}
}
DWORD WINAPI c_sends(LPVOID param){ // 处理操作并发送消息
	bool ms=0;
	p[1].x=1,p[1].y=3;
	while (modify);
	gotoxy(p[1].x,p[1].y),printf("+");
	while (1){
		while (modify);
		Sleep(150);
		if(ms==1) ms=0;
		if (flagg) ms=1;
		//操作
		p[0].x=p[1].x,p[0].y=p[1].y;
		if(kd('W')){
			p[1].check();
			p[1].y--,ms=1;
		}
		if(kd('S')){
			p[1].check();
			p[1].y++,ms=1;
		}
		if(kd('A')){
			p[1].check();
			p[1].x-=2,ms=1;
		}
		if(kd('D')){
			p[1].check();
			p[1].x+=2,ms=1;
		}
		p[1].check();
		bool fire=0;
		if (kd(VK_SPACE)){
			string ccc=to_string(p[1].y-2)+"|"+to_string(p[1].x/2+1);
			ccc+='\n';
			send(server,ccc.c_str(),ccc.size(),0); // 发送炸操作的信息
			fire=1;
		}
		if(ms) {
			while (modify);
			if(p[0].x!=0) {
				int Mapp=grid[p[0].y-2][p[0].x/2+1];
				string stt=(Mapp==1? "□":Mapp==2?"■":Mapp==3?"▲":"  ");
				gotoxy(p[0].x,p[0].y);cout<<stt;
			}
			gotoxy(p[1].x,p[1].y),printf("+");
			if (mod) {
				mod=0;
				if (fire)modify=1;
			}
		}
		if(closed||ended) break;
	}
	return 0;
}
DWORD WINAPI sent(LPVOID param){ // 处理界面
	while (1){
		while (1){
			if (modify){ // 有修改就重新加载
				break;
			}
		}
		system("cls");
		cout<<"Bombing Airplanes!\n";
		cout<<"Your partner's("<<name<<"'s):\n ⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽\n";
		For(i, 1, 10){
			cout<<(char)('A'+i-1);
			For(j, 1, 10){
				if (grid[i][j]==1) cout<<"□";
				else if (grid[i][j]==2) cout<<"■";
				else if (grid[i][j]==3) cout<<"▲";
				else cout<<"  ";
			}
			cout<<endl;
		}
		cout<<"---------------------\n";
		cout<<"Yours("<<a<<"'s):\n ⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽\n";
		For(i, 1, 10){
			cout<<(char)('A'+i-1);
			For(j, 1, 10){
				if (vis[i][j]==1) cout<<"□";
				else if (vis[i][j]==2) cout<<"■";
				else if (vis[i][j]==3) cout<<"▲";
				else cout<<"  ";
			}
			cout<<endl;
		}
		if (!died1) cout<<"Shoot by SPACE.\n";
		if (died1){
			cout<<"Your partner has been defeated by you! Step: "<<step2<<endl;
		}
		if (died2){
			cout<<"You were defeated by your partner! Step: "<<step1<<endl;
		}
		modify=0;
		if (died1&&died2) {
			ended=1;
			Sleep(200); // 等待其他线程结束
			gotoxy(0,31);
			cout<<"Game ended!\n";
			if (step2>step1) cout<<"Your partner wins!\n";
			else if (step2==step1)cout<<"Draw.\n";
			else if (step2<step1) cout<<"You win!\n";
			Sleep(800);
			system("pause");
			ended=0;
			break;
		}
	}
}
int isplane[25][25]; // 这一格内有没有飞机的中心位置
char msg[1005];
int cnt=3;
int plane[25][25]; // 机头朝向
int zhu[5][5][5]={{
	{0,0,0,0,-1},
	{0,0,0,0,-1},
	{0,0,0,0,-1},
	{0,0,0,0,-1},
	{0,0,0,0,-1}
},{
	{0,1,0,0,-1},
	{0,1,0,1,-1},
	{2,1,1,1,-1},
	{0,1,0,1,-1},
	{0,1,0,0,-1}
},{
	{0,0,2,0,0},
	{1,1,1,1,1},
	{0,0,1,0,0},
	{0,1,1,1,0},
	{-1,-1,-1,-1,-1}
},{
	{0,0,1,0,-1},
	{1,0,1,0,-1},
	{1,1,1,2,-1},
	{1,0,1,0,-1},
	{0,0,1,0,-1}
},{
	{0,1,1,1,0},
	{0,0,1,0,0},
	{1,1,1,1,1},
	{0,0,2,0,0},
	{-1,-1,-1,-1,-1}
}};
pair<int, int> zhuyi[5]={{0,0},{5,4},{4,5},{5,4},{4,5}}; // 机头朝向对应的地图大小
pair<int, int> zhy[5]={{0,0},{2,2},{2,2},{2,1},{1,2}}; // 飞机中心位置
void Del(int x, int y){ // 删除飞机
	int pos=plane[x][y];
	int tx=x-zhy[pos].first,ty=y-zhy[pos].second;
	For(i, tx, tx+zhuyi[pos].first-1){
		For(j, ty, ty+zhuyi[pos].second-1){
			if (Map[i][j]==zhu[pos][i-tx][j-ty]){
				Map[i][j]=0;
			}
		}
	}
	isplane[x][y]=0;
	++cnt;
}
bool tryins(int x, int y, int pos){ // 尝试放置飞机
	if (!cnt) return 1;
	int tx=x-zhy[pos].first,ty=y-zhy[pos].second;
	if (tx<=0||ty<=0||tx+zhuyi[pos].first>11||ty+zhuyi[pos].second>11) return 1;
	For(i, tx, tx+zhuyi[pos].first-1){
		For(j, ty, ty+zhuyi[pos].second-1){
			if (zhu[pos][i-tx][j-ty]&&Map[i][j]){
				return 1;
			}
		}
	}
	For(i, tx, tx+zhuyi[pos].first-1){
		For(j, ty, ty+zhuyi[pos].second-1){
			if (!Map[i][j]) Map[i][j]=zhu[pos][i-tx][j-ty];
		}
	}
	isplane[x][y]=1;
	plane[x][y]=pos;
	--cnt;
	return 0;
}
void tryPlace(int x, int y){ // 尝试加入/删除飞机
	if (isplane[x][y]){
		Del(x,y);
	}else{
		tryins(x,y,2);
	}
}
void tryRotate(int x, int y){ // 尝试旋转当前飞机
	if (isplane[x][y]){
		Del(x, y);
		if (tryins(x, y, plane[x][y]%4+1)){
			tryins(x, y, plane[x][y]);
		}
	}
}
bool tryMove(int xx1,int yy1,int xx2, int yy2){ // 尝试移动当前飞机
	if (!isplane[xx1][yy1]) return 1;
	Del(xx1, yy1);
	if (tryins(xx2,yy2,plane[xx1][yy1])){
		tryins(xx1,yy1,plane[xx1][yy1]);
		return 1;
	}else{
		isplane[xx2][yy2]=1;
		plane[xx2][yy2]=plane[xx1][yy1];
		isplane[xx1][yy1]=0;
	}
	return 0;
}
void SetMap(){ // 设置地图
	srand(time(0));
	bool ms=0;
	bool selected=0;
	p[1].x=1,p[1].y=3;
	gotoxy(p[1].x,p[1].y),printf("+");
	while (1){
		system("cls");
		gotoxy(0,0);
		cout<<"Please set up your map.Place(or delete) by 'V'.\n\n";
		cout<<" ⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽\n";
		For(i, 1, 10){
			cout<<(char)('A'+i-1);
			For(j, 1, 10){
				if (Map[i][j]==0) cout<<"□";
				else if (Map[i][j]==1) cout<<"■";
				else if (Map[i][j]==2) cout<<"▲";
			}
			cout<<endl;
		}
		gotoxy(p[1].x,p[1].y),printf("+");
		gotoxy(0,14);
		cout<<"Rotate by 'R'. Randomly place the remaining planes according to 'Q'. Press Enter to finish. Select by SPACE.\n";
		cout<<"Remain: "<<cnt<<" plane(s)\n";
		if (selected) cout<<"*SELECTED*\n";
		while (1){
			pair<int, int> pp={p[1].y-2,p[1].x/2+1};
			Sleep(130);
			if (kd('V')){
				tryPlace(p[1].y-2,p[1].x/2+1);
				break;
			}else if (kd('R')){
				tryRotate(p[1].y-2,p[1].x/2+1);
				break;
			}
			if(ms==1) ms=0;
			//操作
			if(kd('W')) { // 上
				p[1].check();
				if(p[1].x!=0) gotoxy(p[1].x,p[1].y),printf(" ");
				p[1].y--,ms=1;
			}
			if(kd('S')) { // 下
				p[1].check();
				if(p[1].x!=0) gotoxy(p[1].x,p[1].y),printf(" ");
				p[1].y++,ms=1;
			}
			if(kd('A')) { // 左
				p[1].check();
				if(p[1].x!=0) gotoxy(p[1].x,p[1].y),printf(" ");
				p[1].x-=2,ms=1;
			}
			if(kd('D')) { // 右
				p[1].check();
				if(p[1].x!=0) gotoxy(p[1].x,p[1].y),printf(" ");
				p[1].x+=2,ms=1;
			}
			if(kd(VK_SPACE)){ // 选择
				selected^=1;
			}
			if(kd(13)){ // 确认
				if (cnt==0) return;
			}
			if (kd('Q')) { // 一键放置未放置的飞机
				while (cnt){
					int xx=rand()%10+1,yy=rand()%10+1,dir=rand()%4+1;
					if (!isplane[xx][yy]) tryins(xx,yy,dir);
				}
				break;
			}
			if (kd('H')) { // 重新随机放置所有飞机
				For(i, 1, 10){
					For(j, 1, 10){
						if (isplane[i][j]) Del(i,j);
					}
				}
				For(i, 1, 3) {
					int xx=rand()%10+1,yy=rand()%10+1,dir=rand()%4+1;
					if (!isplane[xx][yy]) tryins(xx,yy,dir);
				}
				cnt=0;
			}
			p[1].check();
			if(ms) { // 有移动操作
				if(selected) selected=!(tryMove(pp.first,pp.second,p[1].y-2,p[1].x/2+1));
				break;
			}
		}
	}
}
void mkmatch(){ // 匹配
	cout<<"Match making...\n";
	while (1){
 		recv(server,msg,sz,0);
		string str=msg;
		if (*str.rbegin()=='\n') str.pop_back();
		if (str.size()){ // 收到来自服务器匹配成功的消息(三个感叹号)
			cout<<"Match found!\n";
			break;
		}
	}
	Sleep(2000);
	system("cls");
}
void init(){
	memset(Map,0,sizeof (Map));
	memset(vis,0,sizeof (vis));
	memset(grid,0,sizeof (grid));
	memset(isplane,0,sizeof (isplane));
	memset(plane,0,sizeof (plane));
	memset(msg,0,sizeof (msg));
	memset(rc,0,sizeof (rc));
	name="";
	ended=0;modify=0;flagg=0;closed=0;
	step1=step2=0;
	died1=died2=0;
	cnt=3;
}
ifstream in;
void join(){
	system("cls");
	WSADATA wsaData;
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) exit(0);
	server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(server==(SOCKET)(~0)) WSACleanup(),exit(0);
	string ss=GetIP();
	bool flaggg=0;
	if (a.size()){
		in>>ss;
		flaggg=1;
	}else{
		set_name:
		show();
		printf("Your name:");
		cin>>a;
		if(a.size()>10||!a.size()||a[0]=='O'){
			printf("Username illegal. Try again.\n");
			system("pause");
			system("cls");
			goto set_name;
			exit(0);
		}
	}
	SetIPv4:
	if (!flaggg){
		printf("Room number:");
		string st;cin>>st;
		string sss="";
		while (ss[ss.size()-1]!='.') sss.push_back(*ss.rbegin()),ss.pop_back();
		ss+=st;
		if (sss==st) ss="127.0.0.1"; // 本地直接连
	}
	sockaddr_in conser;
	conser.sin_family=AF_INET;
	conser.sin_port=htons(port);
	conser.sin_addr.S_un.S_addr=inet_addr(ss.c_str());
	For(i, 1, 5){
		if(connect(server,(LPSOCKADDR)&conser,sizeof(conser))==(SOCKET)(~0)){
			if(i==5){
				puts("Error: Failed to join room! Please check the room number and IP address.\n");
				system("del trans.txt");
				flaggg=0;
				goto SetIPv4;
				exit(0);
			}
		}
		else break;
	}
	hide();
	send(server,(a+"\n").c_str(),(int)a.size()+1,0);
	system("cls");
	char re[1000];
	recv(server,re,sz,0);
	cout<<(string)re<<endl;
	if ((string)re!="CHECK"){ // 事先通信一遍检查是否正常,防止游戏过程中通信紊乱
		cout<<"Unknown Error, please try again.\n";
		system("pause");
	    closesocket(server);
	    WSACleanup();
	    flaggg=1;
	    goto SetIPv4;
		return;
	}
	SetMap();
	cout<<"Successfully set up map.\n";
	system("pause");
	system("cls");
	string ccc;ccc=yts()+'\n';
	send(server,ccc.c_str(),ccc.size(),0);
	mkmatch();
	modify=1;
	hThread1=CreateThread(NULL,0,c_sends,NULL,0,NULL); // 开线程
	hThread2=CreateThread(NULL,0,receive,NULL,0,NULL);
	HANDLE hThread3=CreateThread(NULL,0,sent,NULL,0,NULL);
	Client();
    ::CloseHandle(hThread1); // 关线程
    ::CloseHandle(hThread2);
    ::CloseHandle(hThread3);
    while (ended);
    system("cls");
    cout<<"Play again? (Y/N)\n";
    while (1){
    	if (kd('Y')){
			ofstream out;out.open("trans.txt");
			out<<a<<endl<<ss;
			Sleep(300);
			system("start client.exe");
			exit(0);
		}
		if (kd('N')){
			break;
		}
	}
    closesocket(server);
    WSACleanup();
}
int main(){
	ChkRule();
	if (!rulestatus) ApplyRule(); // 申请防火墙规则
	system("mode con cols=70 lines=35"); // 设置窗口大小
	hide();
	noEdit();
	CreateThread(NULL,0,whide,NULL,0,NULL); //开老板键线程
	puts("Press Alt+Q once to hide, press the second time to display.");
	noedit();
	in.open("trans.txt");
	in>>a;
	if (a.size()) {
		join();
		return 0;
	}
	while (1){
		puts("Select an operation:");
		puts("1. Create a room;");
		puts("2. Join a room;");
		puts("3. View game introduction.");
		while (1){
			if (kbhit()){
				char ch=getch();
				if (ch=='1'){
					system("cls");
					system("start server.exe"); // 打开server.exe
					string ss=GetIP();
					int i=ss.size()-1;while (ss[i]!='.')--i;
					ss=ss.substr(i+1,ss.size()-i-1);
					cout<<"Room has been successfully created. Room number: "<<ss<<endl;
					system("pause");
					break;
				}else if (ch=='2'){
					join();
					break;
				}else if (ch=='3'){
					system("cls");
					cout<<"Game objective: Blow up the nose of all the opponent's planes with as few steps as possible, and set up a map to make the opponent take as many steps as possible.\n\n";
					cout<<"Game rules: You need to place 3 planes on a 10 by 10 map. One step can only blow up one grid of the opponent's map. Your own map needs to be kept secret from your opponents.\n\n";
					cout<<"Victory condition: The side with fewer steps wins.\n\n";
					system("pause");
					system("cls");
					break;
				}
			}
		}
		system("cls");
	}
	return 0;
}

server.cpp

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
#pragma comment (lib,"ws2_32")
#define port 1337//端口号
#define sz 6400//缓冲区长度
#define people 2//限制人数
#define kd(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000)?1:0)
#define debug cout<<"**debug**\n";
#define pii pair<int,int>
#define endl '\n'
bool rulestatus;
void ChkRule(){
	rulestatus=!system("netsh advfirewall firewall show rule name=\"Flight\"");
	system("cls");
}
void RunAsAdmin(){
    BOOL isElevated = FALSE;
    HANDLE hToken = NULL;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
        TOKEN_ELEVATION elevation;
        DWORD dwSize;
        if (GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) {
            isElevated = elevation.TokenIsElevated;
        }
    }
    if (hToken) {
        CloseHandle(hToken);
    }
    if (!isElevated) {
        // 如果不是管理员,以管理员权限重新启动自己
        SHELLEXECUTEINFO sei = { sizeof(sei) };
        sei.lpVerb = TEXT("runas");
        sei.lpFile = TEXT(_pgmptr);
        sei.hwnd = NULL;
        sei.nShow = SW_NORMAL;
        if (!ShellExecuteEx(&sei)) {
            DWORD dwError = GetLastError();
            if (dwError == ERROR_CANCELLED)
                MessageBox(NULL, "This program requires administrator privileges to modify firewall rules. Please be aware of this.", "Elevation Canceled", MB_OK);
        }
        exit(0);
    }
}
void ApplyRule(){
	RunAsAdmin();
	system("netsh advfirewall firewall add rule name=Flight dir=in action=allow protocol=tcp localport=1337");
	system("netsh advfirewall firewall add rule name=Flight dir=out action=allow protocol=tcp localport=1337");
	ChkRule();
}
void gotoxy(int x,int y) {
	COORD pos= {(short)x,(short)y};
	HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hOut,pos);
	return;
}
void noedit() {
	HANDLE hStdin=GetStdHandle(STD_INPUT_HANDLE);
	DWORD mode;
	GetConsoleMode(hStdin,&mode);
	mode&=~ENABLE_QUICK_EDIT_MODE;
	mode&=~ENABLE_INSERT_MODE;
	mode&=~ENABLE_MOUSE_INPUT;
	SetConsoleMode(hStdin,mode);
}
void hide() {
	CONSOLE_CURSOR_INFO cur= {1,0};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cur);
}
void show() {
	CONSOLE_CURSOR_INFO cur= {1,1};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cur);
}
map<string, map<int, map<int, int> > > Map, status;
bool Hi=0;
const int N=1e5+10;
deque<string> sent;//发送信息缓冲
string a[N];//存储昵称
SOCKET server,lt[N];//接受 SOCKET用  最多可承载次数
int t_lt[N],t,online;//存储承载下标 ( 用来删除下线状态 )  承载过的次数  在线人数
bool flagg;//延时判断
set<string> ready;
map<string, int> mp1;
map<int, string> mp2;
int toint(string st) {
	int ret=0;
	for (char it:st) {
		ret=ret*10+(it-'0');
	}
	return ret;
}
bool check(string name) {
	for (int i = 1; i <= 10; ++i) {
		for (int j = 1; j <= 10; ++j) {
			if (Map[name][i][j]==2&&!status[name][i][j]) {
				return 0;
			}
		}
	}
	return 1;
}
bool ended=0;
set<string> se;
void init(){
	Map.clear();
	ready.clear();
	mp1.clear();
	mp2.clear();
	status.clear();
	se.clear();
	ended=0;
}
DWORD WINAPI recvs(LPVOID param) { //接收信息
	SOCKET sclient=lt[t];
	int reg;
	char rc[10000];
	memset(rc,0,sizeof(rc));
	while(1) {
		memset(rc,0,sizeof(rc));
		reg=recv(sclient,rc,sz,0);//尝试接受信息
		if(reg==-1) break;//对方离开了
		else {
			string rcc=rc;
			if (*(rcc.rbegin())=='\n') rcc.pop_back();
			string Sent=a[sclient];Sent+=':'+rcc;
			cout<<"RECEIVE: "<<Sent<<endl;
			string st=rcc;
			if (st.size()>=50) { // 地图
				ready.insert((string)a[sclient]);
				cout<<a[sclient]<<endl;
				mp1[a[sclient]]=(int)ready.size()-1;
				mp2[(int)ready.size()-1]=a[sclient];
				int poss=0;
				for (int i = 1; i <= 10; ++i) {
					for (int j = 1; j <= 10; ++j) {
						Map[a[sclient]][i][j]=st[poss++]-'0';
					}
				}
				cout<<" ⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽\n";
				for(int i = 1; i <= 10; ++i){
					cout<<(char)('A'+i-1);
					for (int j = 1; j <= 10; ++j){
						if (Map[a[sclient]][i][j]==0) cout<<"□";
						else if (Map[a[sclient]][i][j]==1) cout<<"■";
						else if (Map[a[sclient]][i][j]==2) cout<<"▲";
					}
					cout<<endl;
				}
				cout<<ready.size()<<" player(s) ready."<<endl;
				if (ready.size()==2) {
					cout<<"MATCH START!!!!!\n";
					flagg=0;
					Sleep(50);
					sent.push_back("!!!");
					for(string it:ready) {
						Sleep(20);
						cout<<sent.size()<<' ';
						cout<<"NAME: "<<sent.back()<<"\n";
					}
					cout<<"\n";
					flagg=1;
				}
			} else if (st!="REPLY") {
				string xx, yy;
				xx=yy="";
				int poss=0;
				while (st[poss]!='|') xx.push_back(st[poss]),++poss;
				++poss;
				while (poss<(int)st.size()) yy.push_back(st[poss]),++poss;
				int xxx=toint(xx),yyy=toint(yy);
				string flag=mp2[mp1[a[sclient]]^1];
				cout<<"to "<<flag<<endl;
				if (!status[flag][xxx][yyy]&&!se.count(flag)) { // 炸到了?
					status[flag][xxx][yyy]=1;
					string ccc=flag+"|"+to_string(xxx)+"|"+to_string(yyy)+"|"+to_string(Map[flag][xxx][yyy]+1);
					flagg=0;
					Sleep(80);
					if (ended) {
						flagg=1;continue;
					}
					sent.push_back(ccc);
					cout<<sent.size()<<' ';
					cout<<"SEND: "<<ccc<<endl;;
					if (check(flag)) { // 玩家被淘汰
						ccc="OUT"+flag;
						sent.push_back((char*)ccc.c_str());
						cout<<"SEND: "<<(string)sent.back()<<endl;;
						se.insert(flag);
						if (se.size()==2) {
							flagg=1;ended=1; // 打标记
							while (sent.size());
							Sleep(800);
							init();
						}
					}
					flagg=1;
				}
			}
		}
	}
	ready.erase(a[sclient]);
	a[sclient]+=" leaves!";
	cout<<a[sclient]<<endl;
	lt[t_lt[sclient]]=-1;//下标改为-1
	online--;//在线人数-1
	closesocket(sclient);//释放
	if(online==0) exit(0);//没有在线的人了,这个服务器就没必要存在了,直接关掉
	return 0;
}
bool vis[N][15];
DWORD WINAPI sends(LPVOID param) { // 发送队列里的消息
	while(1){
		if(!sent.empty()&&flagg) {
			cout<<sent.size()<<' ';
			cout<<"*NOW SENT: "<<(string)sent.front()<<"*\n";
			for(int i=1; i<=t; i++) {
				send(lt[i],(sent.front()+'\n').c_str(),sent.front().size(),0);
				cout<<"SENT to "<<(string)a[lt[i]]<<" : "<<(string)sent.front()<<endl;
			}
			sent.pop_front(); //切下一条消息
		}
	}
}
void find_socket() {
	sockaddr_in client;
	int client_len=sizeof(client);
	CreateThread(NULL,0,sends,NULL,0,NULL); //开发送消息的线程
	while(1) {
		if(t>N-10) {
			t=0; //信息达到上限,初始化掉它
			memset(lt,0,sizeof lt);
		}
		lt[++t]=accept(server,(sockaddr FAR*)&client,&client_len); //来新人了
		if(lt[t]==(SOCKET)(~0)) {
			puts("Err: connect failed!\n");
			t--;
			continue;
		}
		t_lt[lt[t]]=t; //记录下标
		a[lt[t]]=to_string(t);
		cout<<t<<' '<<lt[t]<<endl;
		char phj[1000];
		memset(phj,0,sizeof(phj));
		recv(lt[t],phj,sz,0);
		a[lt[t]]=(string)phj;
		if (*a[lt[t]].rbegin()=='\n') a[lt[t]].pop_back();
		online++; //在线人数增加
		CreateThread(NULL,0,recvs,NULL,0,NULL); //为它建立接受消息的线程
		printf("[%s] join!\n",inet_ntoa(client.sin_addr)); //广播消息
		send(lt[t],"CHECK\n",5,0); // 发送确认消息
	}
}
bool open_socket() { //尝试打开服务器
	WSADATA wsaData; //WSA
	if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) puts("Err1 - Winsock open failed!"),exit(0);//打开WSA
	server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//初始化协议
	if(server==(SOCKET)(~0)) { //协议初始化失败
		puts("Err2 - socket init failed!");
		WSACleanup(); //释放WSA
		exit(0);
	}
	sockaddr_in bindser; //绑定
	bindser.sin_family=AF_INET; //一个一个设置过去
	bindser.sin_port=htons(port); //指向端口号
	bindser.sin_addr.s_addr=htonl(INADDR_ANY); //指向全部人可连接
	if(bind(server,(const struct sockaddr*)&bindser,sizeof(bindser))==(SOCKET)(~0)) { //尝试绑定
		puts("Err3 - already has a server!");
		WSACleanup(); //释放WSA
		closesocket(server); //释放socket
		return 1;
	}
	if(listen(server,people)==(SOCKET)(~0)) { //尝试监听
		puts("Err4 - Monitoring failed!");
		WSACleanup();//释放
		closesocket(server);//释放
		exit(0);
	}
	return 0;
}
int main() {
	ChkRule();
	if (!rulestatus) ApplyRule();
	bool f;
	HWND hWnd=GetConsoleWindow();
	ShowWindow(hWnd,SW_HIDE);
	cout<<"Server is running! Port: "<<port<<endl;
	f=open_socket();
	if(!f)find_socket();
	return 0;
}

感谢阅读,祝您游玩愉快!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值