1 // ==============================================================
2 //	This file is part of Glest (www.glest.org)
3 //
4 //	Copyright (C) 2001-2008 Marti�o Figueroa
5 //
6 //	You can redistribute this code and/or modify it under
7 //	the terms of the GNU General Public License as published
8 //	by the Free Software Foundation; either version 2 of the
9 //	License, or (at your option) any later version
10 // ==============================================================
11 
12 #include "client_interface.h"
13 
14 #include <stdexcept>
15 #include <cassert>
16 
17 #include "platform_util.h"
18 #include "game_util.h"
19 #include "conversion.h"
20 #include "config.h"
21 #include "lang.h"
22 #include "leak_dumper.h"
23 
24 using namespace std;
25 using namespace Shared::Platform;
26 using namespace Shared::Util;
27 
28 namespace Glest{ namespace Game{
29 
30 // =====================================================
31 //	class ClientInterface
32 // =====================================================
33 
34 const int ClientInterface::messageWaitTimeout= 10000;	//10 seconds
35 const int ClientInterface::waitSleepTime= 50;
36 
ClientInterface()37 ClientInterface::ClientInterface(){
38 	clientSocket= NULL;
39 	launchGame= false;
40 	introDone= false;
41 	playerIndex= -1;
42 }
43 
~ClientInterface()44 ClientInterface::~ClientInterface(){
45 	delete clientSocket;
46 }
47 
connect(const Ip & ip,int port)48 void ClientInterface::connect(const Ip &ip, int port){
49 	delete clientSocket;
50 	clientSocket= new ClientSocket();
51 	clientSocket->setBlock(false);
52 	clientSocket->connect(ip, port);
53 }
54 
reset()55 void ClientInterface::reset(){
56 	delete clientSocket;
57 	clientSocket= NULL;
58 }
59 
update()60 void ClientInterface::update(){
61 	NetworkMessageCommandList networkMessageCommandList;
62 
63 	//send as many commands as we can
64 	while(!requestedCommands.empty()){
65 		if(networkMessageCommandList.addCommand(&requestedCommands.back())){
66 			requestedCommands.pop_back();
67 		}
68 		else{
69 			break;
70 		}
71 	}
72 	if(networkMessageCommandList.getCommandCount()>0){
73 		sendMessage(&networkMessageCommandList);
74 	}
75 
76 	//clear chat variables
77 	chatText.clear();
78 	chatSender.clear();
79 	chatTeamIndex= -1;
80 }
81 
updateLobby()82 void ClientInterface::updateLobby(){
83 	NetworkMessageType networkMessageType= getNextMessageType();
84 
85 	switch(networkMessageType){
86 		case nmtInvalid:
87 			break;
88 
89 		case nmtIntro:{
90 			NetworkMessageIntro networkMessageIntro;
91 
92 			if(receiveMessage(&networkMessageIntro)){
93 
94 				//check consistency
95 				if(Config::getInstance().getBool("NetworkConsistencyChecks")){
96 					if(networkMessageIntro.getVersionString()!=getNetworkVersionString()){
97 						throw runtime_error("Server and client versions do not match (" + networkMessageIntro.getVersionString() + "). You have to use the same binaries.");
98 					}
99 				}
100 
101 				//send intro message
102 				NetworkMessageIntro sendNetworkMessageIntro(getNetworkVersionString(), getHostName(), -1);
103 
104 				playerIndex= networkMessageIntro.getPlayerIndex();
105 				serverName= networkMessageIntro.getName();
106 				sendMessage(&sendNetworkMessageIntro);
107 
108 				assert(playerIndex>=0 && playerIndex<GameConstants::maxPlayers);
109 				introDone= true;
110 			}
111 		}
112 		break;
113 
114 		case nmtLaunch:{
115 			NetworkMessageLaunch networkMessageLaunch;
116 
117 			if(receiveMessage(&networkMessageLaunch)){
118 				networkMessageLaunch.buildGameSettings(&gameSettings);
119 
120 				//replace server player by network
121 				for(int i= 0; i<gameSettings.getFactionCount(); ++i){
122 
123 					//replace by network
124 					if(gameSettings.getFactionControl(i)==ctHuman){
125 						gameSettings.setFactionControl(i, ctNetwork);
126 					}
127 
128 					//set the faction index
129 					if(gameSettings.getStartLocationIndex(i)==playerIndex){
130 						gameSettings.setThisFactionIndex(i);
131 					}
132 				}
133 				launchGame= true;
134 			}
135 		}
136 		break;
137 
138 		default:
139 			throw runtime_error("Unexpected network message: " + intToStr(networkMessageType));
140 	}
141 }
142 
updateKeyframe(int frameCount)143 void ClientInterface::updateKeyframe(int frameCount){
144 
145 	bool done= false;
146 
147 	while(!done){
148 		//wait for the next message
149 		waitForMessage();
150 
151 		//check we have an expected message
152 		NetworkMessageType networkMessageType= getNextMessageType();
153 
154 		switch(networkMessageType){
155 			case nmtCommandList:{
156 				//make sure we read the message
157 				NetworkMessageCommandList networkMessageCommandList;
158 				while(!receiveMessage(&networkMessageCommandList)){
159 					sleep(waitSleepTime);
160 				}
161 
162 				//check that we are in the right frame
163 				if(networkMessageCommandList.getFrameCount()!=frameCount){
164 					throw runtime_error("Network synchronization error, frame counts do not match");
165 				}
166 
167 				// give all commands
168 				for(int i= 0; i<networkMessageCommandList.getCommandCount(); ++i){
169 					pendingCommands.push_back(*networkMessageCommandList.getCommand(i));
170 				}
171 
172 				done= true;
173 			}
174 			break;
175 
176 			case nmtQuit:{
177 				NetworkMessageQuit networkMessageQuit;
178 				if(receiveMessage(&networkMessageQuit)){
179 					quit= true;
180 				}
181 				done= true;
182 			}
183 			break;
184 
185 			case nmtText:{
186 				NetworkMessageText networkMessageText;
187 				if(receiveMessage(&networkMessageText)){
188 					chatText= networkMessageText.getText();
189 					chatSender= networkMessageText.getSender();
190 					chatTeamIndex= networkMessageText.getTeamIndex();
191 				}
192 			}
193 			break;
194 
195 			default:
196 				throw runtime_error("Unexpected message in client interface: " + intToStr(networkMessageType));
197 		}
198 	}
199 }
200 
waitUntilReady(Checksum * checksum)201 void ClientInterface::waitUntilReady(Checksum* checksum){
202 
203 	NetworkMessageReady networkMessageReady;
204 	Chrono chrono;
205 
206 	chrono.start();
207 
208 	//send ready message
209 	sendMessage(&networkMessageReady);
210 
211 	//wait until we get a ready message from the server
212 	while(true){
213 
214 		NetworkMessageType networkMessageType= getNextMessageType();
215 
216 		if(networkMessageType==nmtReady){
217 			if(receiveMessage(&networkMessageReady)){
218 				break;
219 			}
220 		}
221 		else if(networkMessageType==nmtInvalid){
222 			if(chrono.getMillis()>readyWaitTimeout){
223 				throw runtime_error("Timeout waiting for server");
224 			}
225 		}
226 		else{
227 			throw runtime_error("Unexpected network message: " + intToStr(networkMessageType) );
228 		}
229 
230 		// sleep a bit
231 		sleep(waitSleepTime);
232 	}
233 
234 	//check checksum
235 	if(Config::getInstance().getBool("NetworkConsistencyChecks")){
236 		if(networkMessageReady.getChecksum()!=checksum->getSum()){
237 			throw runtime_error("Checksum error, you don't have the same data as the server");
238 		}
239 	}
240 
241 	//delay the start a bit, so clients have nore room to get messages
242 	sleep(GameConstants::networkExtraLatency);
243 }
244 
sendTextMessage(const string & text,int teamIndex)245 void ClientInterface::sendTextMessage(const string &text, int teamIndex){
246 	NetworkMessageText networkMessageText(text, getHostName(), teamIndex);
247 	sendMessage(&networkMessageText);
248 }
249 
getNetworkStatus() const250 string ClientInterface::getNetworkStatus() const{
251 	return Lang::getInstance().get("Server") + ": " + serverName;
252 }
253 
waitForMessage()254 void ClientInterface::waitForMessage(){
255 	Chrono chrono;
256 
257 	chrono.start();
258 
259 	while(getNextMessageType()==nmtInvalid){
260 
261 		if(!isConnected()){
262 			throw runtime_error("Disconnected");
263 		}
264 
265 		if(chrono.getMillis()>messageWaitTimeout){
266 			throw runtime_error("Timeout waiting for message");
267 		}
268 
269 		sleep(waitSleepTime);
270 	}
271 }
272 
273 }}//end namespace
274