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