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