文章目录
Part 0. 声明
本小游戏是基于 WinSocket 开发的,类似的代码如 C++ Chat。如果有严重影响游戏体验的问题(包括但不限于如无法匹配等),欢迎私信指出。
由于本程序使用多线程,故代码中有一些等待其他程序或线程的代码、标记等。
游戏中,空心方块为空,实心方块为机身,三角实心块为机头。
炸飞机网址
Update on 2024/9/22 : 解决了在 Win10 中无法正常运行的问题;优化了连接和消息接收等策略;支持自动检测和申请防火墙规则(会自动申请管理员权限,望周知);修复了一些已知问题。
Part 1. 功能介绍
- 本小游戏支持(包括但不限于)以下功能:
- 支持联机游戏,可在同局域网(机房)内进行双人对局;
- 有老板键(Alt+Q);
- 模拟炸飞机网页游戏,布置地图时支持放置、移动、旋转、一键放置未放置的飞机;
- 对局时,可以同时看到自己有没有炸到对手和对手有没有炸到自己;
- 可以在服务端代码主函数中
注释掉隐藏窗口的代码然后作弊(逃)
Part 2. 操作简介
- 用键盘上的
WASD
移动; - 按
ALT+Q
(灵敏度可在代码中调整)隐藏窗口,再按一次显示; - 在放置界面:
- 按
V
放置飞机,在放置点再按一下V
删除飞机; - 按空格选择,选择后可移动飞机;再按一次空格取消选择;若碰到障碍物无法移动则自动取消选择;
- 在放置点按
R
顺时针旋转飞机; - 按
Q
一键放置剩余未放置的飞机; - 按回车键确定放置完毕。
- 按
- 在对局界面,按空格键炸标记点。
Part 3. 如何使用
将文末的两份代码保存在同个文件夹内并分别编译(编译指令加上 -lws2_32
),随后运行 client.exe 即可。
Part 4. 游戏截图
- 开始界面:
- 放置界面:
- 对局界面:
Part 5. 注意事项与提示
- 需要在
windows
下运行; - 不要开
O2
!(它会吃掉你的多线程) - 如果在放置界面操作失败,则需要考虑操作后飞机是否重叠或出界;放完 3 3 3 架飞机后才可继续;
- 设置的名字长度不超过
10
10
10(中文算
2
2
2 个字符),首字母不得为
O
; - 若出现乱码现象,可在默认值中更改字符集为
gbk
(win10 可使用旧版控制台); - 不要和你的伙伴取相同的名字。
- 此代码需要您提供管理员权限来更改防火墙规则,否则程序无法正常联机执行。您也可以手动添加防火墙入站/出站规则或直接关闭防火墙。
放置界面中飞机的中心(唯一可以代表一架飞机的地方,图中用绿色方块表示):
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;
}
感谢阅读,祝您游玩愉快!