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 bool connectedlocally = false;
9
multiplayer(bool msg)10 bool multiplayer(bool msg)
11 {
12 // check if we're playing alone
13 int n = client::otherclients();
14 if (n && msg) conoutft(CON_MESG, "\froperation not available with other clients");
15 return n > 0;
16 }
17
setrate(int rate)18 void setrate(int rate)
19 {
20 if(!curpeer) return;
21 enet_host_bandwidth_limit(clienthost, rate, rate);
22 }
23
24 VARF(rate, 0, 0, 25000, setrate(rate));
25
26 void throttle();
27
28 VARF(throttle_interval, 0, 5, 30, throttle());
29 VARF(throttle_accel, 0, 2, 32, throttle());
30 VARF(throttle_decel, 0, 2, 32, throttle());
31
throttle()32 void throttle()
33 {
34 if(!curpeer) return;
35 ASSERT(ENET_PEER_PACKET_THROTTLE_SCALE==32);
36 enet_peer_throttle_configure(curpeer, throttle_interval*1000, throttle_accel, throttle_decel);
37 }
38
connected(bool attempt)39 bool connected(bool attempt)
40 {
41 return curpeer || (attempt && connpeer) || connectedlocally;
42 }
43
abortconnect(bool msg)44 void abortconnect(bool msg)
45 {
46 if(!connpeer) return;
47 client::connectfail();
48 if(msg) conoutft(CON_MESG, "\faaborting connection attempt");
49 if(connpeer->state!=ENET_PEER_STATE_DISCONNECTED) enet_peer_reset(connpeer);
50 connpeer = NULL;
51 if(curpeer) return;
52 enet_host_destroy(clienthost);
53 clienthost = NULL;
54 }
55
connectfail()56 void connectfail()
57 {
58 abortconnect(false);
59 localconnect(false);
60 }
61
trydisconnect()62 void trydisconnect()
63 {
64 if(connpeer) abortconnect();
65 else if(curpeer || connectedlocally)
66 {
67 if(verbose) conoutft(CON_MESG, "\faattempting to disconnect...");
68 disconnect(0, !discmillis);
69 }
70 else conoutft(CON_MESG, "\frnot connected");
71 }
72
73 char *lastaddress = NULL;
connectserv(const char * name,int port,int qport,const char * password)74 void connectserv(const char *name, int port, int qport, const char *password)
75 {
76 abortconnect();
77
78 if(!port) port = ENG_SERVER_PORT;
79 if(!qport) qport = ENG_QUERY_PORT;
80
81 ENetAddress address;
82 address.port = port;
83
84 if(lastaddress) delete[] lastaddress;
85 lastaddress = NULL;
86
87 if(name && *name)
88 {
89 addserver(name, port, qport);
90 conoutft(CON_MESG, "\faattempting to connect to %s:[%d]", name, port);
91 if(!resolverwait(name, port, &address))
92 {
93 conoutft(CON_MESG, "\frcould not resolve host %s", name);
94 connectfail();
95 return;
96 }
97 lastaddress = newstring(name);
98 }
99 else
100 {
101 conoutft(CON_MESG, "\faattempting to connect to a local server");
102 address.host = ENET_HOST_BROADCAST;
103 }
104
105 if(!clienthost) clienthost = enet_host_create(NULL, 2, rate, rate);
106
107 if(clienthost)
108 {
109 connpeer = enet_host_connect(clienthost, &address, client::numchannels());
110 enet_host_flush(clienthost);
111 connmillis = totalmillis;
112 connattempts = 0;
113 client::connectattempt(name ? name : "", port, qport, password ? password : "", address);
114 conoutft(CON_MESG, "\fgconnecting to %s:[%d]", name != NULL ? name : "local server", port);
115 }
116 else
117 {
118 conoutft(CON_MESG, "\frfailed creating client socket");
119 connectfail();
120 }
121 }
122
disconnect(int onlyclean,int async)123 void disconnect(int onlyclean, int async)
124 {
125 bool cleanup = onlyclean!=0;
126 if(curpeer || connectedlocally)
127 {
128 if(curpeer)
129 {
130 if(!discmillis)
131 {
132 enet_peer_disconnect(curpeer, DISC_NONE);
133 if(clienthost) enet_host_flush(clienthost);
134 discmillis = totalmillis;
135 }
136 if(curpeer->state!=ENET_PEER_STATE_DISCONNECTED)
137 {
138 if(async) return;
139 enet_peer_reset(curpeer);
140 }
141 curpeer = NULL;
142 }
143 discmillis = 0;
144 conoutft(CON_MESG, "\frdisconnected");
145 cleanup = true;
146 }
147 if(!connpeer && clienthost)
148 {
149 enet_host_destroy(clienthost);
150 clienthost = NULL;
151 }
152 if(cleanup)
153 {
154 client::gamedisconnect(onlyclean);
155 localdisconnect();
156 }
157 if(!onlyclean) localconnect(false);
158 }
159
160 ICOMMAND(connect, "siis", (char *n, int *a, int *b, char *pwd), connectserv(n && *n ? n : servermaster, a ? *a : serverport, b ? *b : serverqueryport, pwd));
161 COMMANDN(disconnect, trydisconnect, "");
162
163 ICOMMAND(lanconnect, "", (), connectserv());
164 ICOMMAND(localconnect, "i", (int *n), localconnect(*n ? false : true));
165
reconnect()166 void reconnect()
167 {
168 disconnect(1);
169 connectserv(lastaddress && *lastaddress ? lastaddress : NULL);
170 }
171 COMMAND(reconnect, "");
172
173 int lastupdate = -1000;
174
sendclientpacket(ENetPacket * packet,int chan)175 void sendclientpacket(ENetPacket *packet, int chan)
176 {
177 if(curpeer) enet_peer_send(curpeer, chan, packet);
178 else localclienttoserver(chan, packet);
179 }
180
flushclient()181 void flushclient()
182 {
183 if(clienthost) enet_host_flush(clienthost);
184 }
185
neterr(const char * s)186 void neterr(const char *s)
187 {
188 conoutft(CON_MESG, "\frillegal network message (%s)", s);
189 disconnect();
190 }
191
localservertoclient(int chan,ENetPacket * packet)192 void localservertoclient(int chan, ENetPacket *packet) // processes any updates from the server
193 {
194 packetbuf p(packet);
195 client::parsepacketclient(chan, p);
196 }
197
clientkeepalive()198 void clientkeepalive()
199 {
200 if (clienthost) enet_host_service(clienthost, NULL, 0);
201 if (serverhost) enet_host_service(serverhost, NULL, 0);
202
203 }
204
gets2c()205 void gets2c() // get updates from the server
206 {
207 ENetEvent event;
208 if(!clienthost) return;
209 if(connpeer && totalmillis/3000 > connmillis/3000)
210 {
211 connmillis = totalmillis;
212 ++connattempts;
213 if(connattempts > 3)
214 {
215 conoutft(CON_MESG, "\frcould not connect to server");
216 connectfail();
217 return;
218 }
219 else conoutft(CON_MESG, "\faconnection attempt %d", connattempts);
220 }
221 while(clienthost && enet_host_service(clienthost, &event, 0)>0)
222 switch(event.type)
223 {
224 case ENET_EVENT_TYPE_CONNECT:
225 disconnect(1);
226 curpeer = connpeer;
227 connpeer = NULL;
228 conoutft(CON_MESG, "\fgconnected to server");
229 throttle();
230 if(rate) setrate(rate);
231 client::gameconnect(true);
232 break;
233
234 case ENET_EVENT_TYPE_RECEIVE:
235 if(discmillis) conoutft(CON_MESG, "\faattempting to disconnect...");
236 else localservertoclient(event.channelID, event.packet);
237 enet_packet_destroy(event.packet);
238 break;
239
240 case ENET_EVENT_TYPE_DISCONNECT:
241 extern const char *disc_reasons[];
242 if(event.data>=DISC_NUM) event.data = DISC_NONE;
243 if(event.peer==connpeer)
244 {
245 conoutft(CON_MESG, "\frcould not connect to server");
246 connectfail();
247 }
248 else
249 {
250 if(!discmillis || event.data) conoutft(CON_MESG, "\frserver network error, disconnecting (%s) ...", disc_reasons[event.data]);
251 disconnect();
252 }
253 return;
254
255 default:
256 break;
257 }
258 }
259
260