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 "server_interface.h"
13 
14 #include <cassert>
15 #include <stdexcept>
16 
17 #include "platform_util.h"
18 #include "conversion.h"
19 #include "config.h"
20 #include "lang.h"
21 
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 ServerInterface
32 // =====================================================
33 
ServerInterface()34 ServerInterface::ServerInterface(){
35 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
36 		slots[i]= NULL;
37 	}
38 	serverSocket.setBlock(false);
39 	serverSocket.bind(GameConstants::serverPort);
40 }
41 
~ServerInterface()42 ServerInterface::~ServerInterface(){
43 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
44 		delete slots[i];
45 	}
46 }
47 
addSlot(int playerIndex)48 void ServerInterface::addSlot(int playerIndex){
49 	assert(playerIndex>=0 && playerIndex<GameConstants::maxPlayers);
50 
51 	delete slots[playerIndex];
52 	slots[playerIndex]= new ConnectionSlot(this, playerIndex);
53 	updateListen();
54 }
55 
removeSlot(int playerIndex)56 void ServerInterface::removeSlot(int playerIndex){
57 	delete slots[playerIndex];
58 	slots[playerIndex]= NULL;
59 	updateListen();
60 }
61 
getSlot(int playerIndex)62 ConnectionSlot* ServerInterface::getSlot(int playerIndex){
63 	return slots[playerIndex];
64 }
65 
getConnectedSlotCount()66 int ServerInterface::getConnectedSlotCount(){
67 	int connectedSlotCount= 0;
68 
69 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
70 		if(slots[i]!= NULL){
71 			++connectedSlotCount;
72 		}
73 	}
74 	return connectedSlotCount;
75 }
76 
update()77 void ServerInterface::update(){
78 
79 	//update all slots
80 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
81 		if(slots[i]!= NULL){
82 			slots[i]->update();
83 		}
84 	}
85 
86 	//process text messages
87 	chatText.clear();
88 	chatSender.clear();
89 	chatTeamIndex= -1;
90 
91 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
92 		ConnectionSlot* connectionSlot= slots[i];
93 
94 		if(connectionSlot!= NULL){
95 			if(connectionSlot->isConnected()){
96 				if(connectionSlot->getNextMessageType()==nmtText){
97 					NetworkMessageText networkMessageText;
98 					if(connectionSlot->receiveMessage(&networkMessageText)){
99 						broadcastMessage(&networkMessageText, i);
100 						chatText= networkMessageText.getText();
101 						chatSender= networkMessageText.getSender();
102 						chatTeamIndex= networkMessageText.getTeamIndex();
103 						break;
104 					}
105 				}
106 			}
107 		}
108 	}
109 }
110 
updateKeyframe(int frameCount)111 void ServerInterface::updateKeyframe(int frameCount){
112 
113 	NetworkMessageCommandList networkMessageCommandList(frameCount);
114 
115 	//build command list, remove commands from requested and add to pending
116 	while(!requestedCommands.empty()){
117 		if(networkMessageCommandList.addCommand(&requestedCommands.back())){
118 			pendingCommands.push_back(requestedCommands.back());
119 			requestedCommands.pop_back();
120 		}
121 		else{
122 			break;
123 		}
124 	}
125 
126 	//broadcast commands
127 	broadcastMessage(&networkMessageCommandList);
128 }
129 
waitUntilReady(Checksum * checksum)130 void ServerInterface::waitUntilReady(Checksum* checksum){
131 
132 	Chrono chrono;
133 	bool allReady= false;
134 
135 	chrono.start();
136 
137 	//wait until we get a ready message from all clients
138 	while(!allReady){
139 
140 		allReady= true;
141 		for(int i= 0; i<GameConstants::maxPlayers; ++i){
142 			ConnectionSlot* connectionSlot= slots[i];
143 
144 			if(connectionSlot!=NULL){
145 				if(!connectionSlot->isReady()){
146 					NetworkMessageType networkMessageType= connectionSlot->getNextMessageType();
147 					NetworkMessageReady networkMessageReady;
148 
149 					if(networkMessageType==nmtReady && connectionSlot->receiveMessage(&networkMessageReady)){
150 						connectionSlot->setReady();
151 					}
152 					else if(networkMessageType!=nmtInvalid){
153 						throw runtime_error("Unexpected network message: " + intToStr(networkMessageType));
154 					}
155 
156 					allReady= false;
157 				}
158 			}
159 		}
160 
161 		//check for timeout
162 		if(chrono.getMillis()>readyWaitTimeout){
163 			throw runtime_error("Timeout waiting for clients");
164 		}
165 	}
166 
167 	//send ready message after, so clients start delayed
168 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
169 		NetworkMessageReady networkMessageReady(checksum->getSum());
170 		ConnectionSlot* connectionSlot= slots[i];
171 
172 		if(connectionSlot!=NULL){
173 			connectionSlot->sendMessage(&networkMessageReady);
174 		}
175 	}
176 }
177 
sendTextMessage(const string & text,int teamIndex)178 void ServerInterface::sendTextMessage(const string &text, int teamIndex){
179 	NetworkMessageText networkMessageText(text, getHostName(), teamIndex);
180 	broadcastMessage(&networkMessageText);
181 }
182 
quitGame()183 void ServerInterface::quitGame(){
184 	NetworkMessageQuit networkMessageQuit;
185 	broadcastMessage(&networkMessageQuit);
186 }
187 
getNetworkStatus() const188 string ServerInterface::getNetworkStatus() const{
189 	Lang &lang= Lang::getInstance();
190 	string str;
191 
192 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
193 		ConnectionSlot* connectionSlot= slots[i];
194 
195 		str+= intToStr(i)+ ": ";
196 
197 		if(connectionSlot!= NULL){
198 			if(connectionSlot->isConnected()){
199 				str+= connectionSlot->getName();
200 			}
201 		}
202 		else
203 		{
204 			str+= lang.get("NotConnected");
205 		}
206 
207 		str+= '\n';
208 	}
209 	return str;
210 }
211 
launchGame(const GameSettings * gameSettings)212 void ServerInterface::launchGame(const GameSettings* gameSettings){
213 	NetworkMessageLaunch networkMessageLaunch(gameSettings);
214 	broadcastMessage(&networkMessageLaunch);
215 }
216 
broadcastMessage(const NetworkMessage * networkMessage,int excludeSlot)217 void ServerInterface::broadcastMessage(const NetworkMessage* networkMessage, int excludeSlot){
218 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
219 		ConnectionSlot* connectionSlot= slots[i];
220 
221 		if(i!= excludeSlot && connectionSlot!= NULL){
222 			if(connectionSlot->isConnected()){
223 				connectionSlot->sendMessage(networkMessage);
224 			}
225 			else{
226 				removeSlot(i);
227 			}
228 		}
229 	}
230 }
231 
updateListen()232 void ServerInterface::updateListen(){
233 	int openSlotCount= 0;
234 
235 	for(int i= 0; i<GameConstants::maxPlayers; ++i){
236 		if(slots[i]!= NULL && !slots[i]->isConnected()){
237 			++openSlotCount;
238 		}
239 	}
240 
241 	serverSocket.listen(openSlotCount);
242 }
243 
244 }}//end namespace
245