1 // client.cpp, mostly network related client game code
2 
3 #include "engine.h"
4 
5 ENetHost *clienthost = NULL;
6 ENetPeer *curpeer = NULL, *connpeer = NULL;
7 int connmillis = 0, connattempts = 0, discmillis = 0;
8 
multiplayer(bool msg)9 bool multiplayer(bool msg)
10 {
11     bool val = curpeer || hasnonlocalclients();
12     if(val && msg) conoutf(CON_ERROR, "operation not available in multiplayer");
13     return val;
14 }
15 
setrate(int rate)16 void setrate(int rate)
17 {
18    if(!curpeer) return;
19    enet_host_bandwidth_limit(clienthost, rate*1024, rate*1024);
20 }
21 
22 VARF(rate, 0, 0, 1024, setrate(rate));
23 
24 void throttle();
25 
26 VARF(throttle_interval, 0, 5, 30, throttle());
27 VARF(throttle_accel,    0, 2, 32, throttle());
28 VARF(throttle_decel,    0, 2, 32, throttle());
29 
throttle()30 void throttle()
31 {
32     if(!curpeer) return;
33     ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
34     enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
35 }
36 
isconnected(bool attempt,bool local)37 bool isconnected(bool attempt, bool local)
38 {
39     return curpeer || (attempt && connpeer) || (local && haslocalclients());
40 }
41 
42 ICOMMAND(isconnected, "bb", (int *attempt, int *local), intret(isconnected(*attempt > 0, *local != 0) ? 1 : 0));
43 
connectedpeer()44 const ENetAddress *connectedpeer()
45 {
46     return curpeer ? &curpeer->address : NULL;
47 }
48 
49 ICOMMAND(connectedip, "", (),
50 {
51     const ENetAddress *address = connectedpeer();
52     string hostname;
53     result(address && enet_address_get_host_ip(address, hostname, sizeof(hostname)) >= 0 ? hostname : "");
54 });
55 
56 ICOMMAND(connectedport, "", (),
57 {
58     const ENetAddress *address = connectedpeer();
59     intret(address ? address->port : -1);
60 });
61 
abortconnect()62 void abortconnect()
63 {
64     if(!connpeer) return;
65     game::connectfail();
66     if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
67     connpeer = NULL;
68     if(curpeer) return;
69     enet_host_destroy(clienthost);
70     clienthost = NULL;
71 }
72 
73 SVARP(connectname, "");
74 VARP(connectport, 0, 0, 0xFFFF);
75 
connectserv(const char * servername,int serverport,const char * serverpassword)76 void connectserv(const char *servername, int serverport, const char *serverpassword)
77 {
78     if(connpeer)
79     {
80         conoutf("aborting connection attempt");
81         abortconnect();
82     }
83 
84     if(serverport <= 0) serverport = server::serverport();
85 
86     ENetAddress address;
87     address.port = serverport;
88 
89     if(servername)
90     {
91         if(strcmp(servername, connectname)) setsvar("connectname", servername);
92         if(serverport != connectport) setvar("connectport", serverport);
93         addserver(servername, serverport, serverpassword && serverpassword[0] ? serverpassword : NULL);
94         conoutf("attempting to connect to %s:%d", servername, serverport);
95         if(!resolverwait(servername, &address))
96         {
97             conoutf("\f3could not resolve server %s", servername);
98             return;
99         }
100     }
101     else
102     {
103         setsvar("connectname", "");
104         setvar("connectport", 0);
105         conoutf("attempting to connect over LAN");
106         address.host = ENET_HOST_BROADCAST;
107     }
108 
109     if(!clienthost)
110         clienthost = enet_host_create(NULL, 2, server::numchannels(), rate*1024, rate*1024);
111 
112     if(clienthost)
113     {
114         connpeer = enet_host_connect(clienthost, &address, server::numchannels(), 0);
115         enet_host_flush(clienthost);
116         connmillis = totalmillis;
117         connattempts = 0;
118 
119         game::connectattempt(servername ? servername : "", serverpassword ? serverpassword : "", address);
120     }
121     else conoutf("\f3could not connect to server");
122 }
123 
reconnect(const char * serverpassword)124 void reconnect(const char *serverpassword)
125 {
126     if(!connectname[0] || connectport <= 0)
127     {
128         conoutf(CON_ERROR, "no previous connection");
129         return;
130     }
131 
132     connectserv(connectname, connectport, serverpassword);
133 }
134 
disconnect(bool async,bool cleanup)135 void disconnect(bool async, bool cleanup)
136 {
137     if(curpeer)
138     {
139         if(!discmillis)
140         {
141             enet_peer_disconnect(curpeer, DISC_NONE);
142             enet_host_flush(clienthost);
143             discmillis = totalmillis;
144         }
145         if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
146         {
147             if(async) return;
148             enet_peer_reset(curpeer);
149         }
150         curpeer = NULL;
151         discmillis = 0;
152         conoutf("disconnected");
153         game::gamedisconnect(cleanup);
154         mainmenu = 1;
155     }
156     if(!connpeer && clienthost)
157     {
158         enet_host_destroy(clienthost);
159         clienthost = NULL;
160     }
161 }
162 
trydisconnect(bool local)163 void trydisconnect(bool local)
164 {
165     if(connpeer)
166     {
167         conoutf("aborting connection attempt");
168         abortconnect();
169     }
170     else if(curpeer)
171     {
172         conoutf("attempting to disconnect...");
173         disconnect(!discmillis);
174     }
175     else if(local && haslocalclients()) localdisconnect();
176     else conoutf("not connected");
177 }
178 
179 ICOMMAND(connect, "sis", (char *name, int *port, char *pw), connectserv(name, *port, pw));
180 ICOMMAND(lanconnect, "is", (int *port, char *pw), connectserv(NULL, *port, pw));
181 COMMAND(reconnect, "s");
182 ICOMMAND(disconnect, "b", (int *local), trydisconnect(*local != 0));
183 ICOMMAND(localconnect, "", (), { if(!isconnected()) localconnect(); });
184 ICOMMAND(localdisconnect, "", (), { if(haslocalclients()) localdisconnect(); });
185 
sendclientpacket(ENetPacket * packet,int chan)186 void sendclientpacket(ENetPacket *packet, int chan)
187 {
188     if(curpeer) enet_peer_send(curpeer, chan, packet);
189     else localclienttoserver(chan, packet);
190 }
191 
flushclient()192 void flushclient()
193 {
194     if(clienthost) enet_host_flush(clienthost);
195 }
196 
neterr(const char * s,bool disc)197 void neterr(const char *s, bool disc)
198 {
199     conoutf(CON_ERROR, "\f3illegal network message (%s)", s);
200     if(disc) disconnect();
201 }
202 
localservertoclient(int chan,ENetPacket * packet)203 void localservertoclient(int chan, ENetPacket *packet)   // processes any updates from the server
204 {
205     packetbuf p(packet);
206     game::parsepacketclient(chan, p);
207 }
208 
clientkeepalive()209 void clientkeepalive() { if(clienthost) enet_host_service(clienthost, NULL, 0); }
210 
gets2c()211 void gets2c()           // get updates from the server
212 {
213     ENetEvent event;
214     if(!clienthost) return;
215     if(connpeer && totalmillis/3000 > connmillis/3000)
216     {
217         conoutf("attempting to connect...");
218         connmillis = totalmillis;
219         ++connattempts;
220         if(connattempts > 3)
221         {
222             conoutf("\f3could not connect to server");
223             abortconnect();
224             return;
225         }
226     }
227     while(clienthost && enet_host_service(clienthost, &event, 0)>0)
228     switch(event.type)
229     {
230         case ENET_EVENT_TYPE_CONNECT:
231             disconnect(false, false);
232             localdisconnect(false);
233             curpeer = connpeer;
234             connpeer = NULL;
235             conoutf("connected to server");
236             throttle();
237             if(rate) setrate(rate);
238             game::gameconnect(true);
239             break;
240 
241         case ENET_EVENT_TYPE_RECEIVE:
242             if(discmillis) conoutf("attempting to disconnect...");
243             else localservertoclient(event.channelID, event.packet);
244             enet_packet_destroy(event.packet);
245             break;
246 
247         case ENET_EVENT_TYPE_DISCONNECT:
248             if(event.data>=DISC_NUM) event.data = DISC_NONE;
249             if(event.peer==connpeer)
250             {
251                 conoutf("\f3could not connect to server");
252                 abortconnect();
253             }
254             else
255             {
256                 if(!discmillis || event.data)
257                 {
258                     const char *msg = disconnectreason(event.data);
259                     if(msg) conoutf("\f3server network error, disconnecting (%s) ...", msg);
260                     else conoutf("\f3server network error, disconnecting...");
261                 }
262                 disconnect();
263             }
264             return;
265 
266         default:
267             break;
268     }
269 }
270 
271