1 /*
2 NETWORK.C
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 Monday, June 20, 1994 12:22:03 PM
22 Wednesday, June 29, 1994 9:14:21 PM
23 	made ddp ring work with more than 2 players (upring and downring were confused)
24 Saturday, July 2, 1994 3:54:12 PM
25 	simple distribution of map
26 Friday, July 15, 1994 10:51:38 AM
27 	gracefully handling players dropping from the game. don't allow quiting from the game while
28 	we have the ring packet. changed distribution of the map now that we transfer a level at a time.
29 Sunday, July 17, 1994 4:01:18 PM
30 	multiple updates per packet
31 Monday, July 18, 1994 11:51:51 AM
32 	transfering map in chunks now, since ADSP can only write 64K at a time.
33 Tuesday, July 19, 1994 7:14:30 PM
34 	fixed one player ring bug yesterday.
35 Wednesday, July 20, 1994 12:34:06 AM
36 	variable number of updates per packet. (can only be adjusted upward, not downward).
37 Monday, July 25, 1994 9:04:24 PM
38 	Jason's new algorithm. dropping players and slowing down the ring doesn't work now.
39 	but performance is much smoother, and better understood, to boot.
40 Sunday, August 21, 1994 3:58:23 PM
41 	about a week ago, added stuff to use the ring to distribute other information, like
42 	sound or text for the game.
43 
44 Jan 30, 2000 (Loren Petrich):
45 	Added some typecasts
46 
47 Feb. 4, 2000 (Loren Petrich):
48 	Changed halt() to assert(false) for better debugging
49 
50 Sept-Oct 2001 (Woody Zenfell): (roughly in order)
51         Plugged in netcpy/_NET stuff and a couple #ifdef SDL byte-swappers for portable data formats.
52         Changed a couple memcpy() calls to memmove() calls as they have overlapping source and dest.
53         Allowed the use of the MyTM* functions, which now have SDL_thread-based implementations.
54         Added optional NETWORK_FAUX_QUEUE mechanism, should work on either platform.
55         It was a good idea, I think, but ultimately fairly pointless.  NETWORK_ADAPTIVE_LATENCY should be better.
56         Added optional NETWORK_ADAPTIVE_LATENCY mechanism, should work on either platform.
57         Changed some #ifdef mac conditionals to #ifndef NETWORK_IP to better convey what we're worried about.
58         Added NETWORK_USE_RECENT_FLAGS option to discard excess flags from the head, rather than tail, of the queue.
59         Added... how to say... "copious" comments (ZZZ) at various times as I browsed the source and made changes.
60         Found that a basic assumption I was using in my optimizations (i.e. that the game processed action_flags
61         at a constant rate) was wrong, which made the Bungie way make a lot more sense.  I now recommend using *none*
62         of the three NETWORK_* options (do use NETWORK_IP though of course if appropriate).
63 
64 Nov 13, 2001 (Woody Zenfell):
65         Although things were basically OK under favorable conditions, they were IMO too "fragile" - sensitive
66         to latency and jitter.  I couldn't help but try again... so NETWORK_ADAPTIVE_LATENCY_2 has been added.
67         Also put in NETWORK_SMARTER_FLAG_DITCHING mechanism.
68 
69 Feb 27, 2002 (Br'fin (Jeremy Parsons)):
70 	Rewired things to more generally key off of HAVE_SDL_NET than SDL (The Carbon build has SDL_NET, but
71 		understandably lacks SDL)
72 	Uses #if HAVE_SDL_NET in place of calls to #ifndef mac to allow SDL networking under Carbon
73 
74 Mar 3-8, 2002 (Woody Zenfell):
75     Changed net distribution stuff to use an STL map to associate distribution types with
76     {lossy, handling procedure}.  Now different endstations can have different distribution
77     types installed for handling (previously they had to all install the same handlers in the
78     same order to get the same distribution type ID's).
79 
80 Feb 5, 2003 (Woody Zenfell):
81         Preliminary support for resuming saved-games networked.
82 
83 Feb 13, 2003 (Woody Zenfell):
84         Resuming saved-games as network games works.
85 
86 May 24, 2003 (Woody Zenfell):
87 	Split out ring-protocol-specific stuff from here to RingGameProtocol.cpp.
88 	This is multiple-game-protocol-savvy now.
89 	Support for graceful handling of unknown streaming-data packet types.
90 
91 July 03, 2003 (jkvw):
92         Added network lua scripts.
93 
94 September 17, 2004 (jkvw):
95 	NAT-friendly networking.  That is, joiners behind firewalls should be able to play.
96 	Also moved to TCPMess for TCP communications.
97 */
98 
99 #if defined(DISABLE_NETWORKING)
100 
101 #include "network_dummy.cpp"
102 
103 #else
104 
105 /*
106 I would really like to be able to let the Unysnc packet go around the loop, but it is difficult
107 	because all the code is currently setup to handle only one packet at a time.
108 Currently 1 player games (when others are dropped) always have lots of late packets (never get
109 	acknowledged properly, since they are the only player.)
110 Note that the unregister isn't fast enough, and that the registration code is stupid.  Also should
111 	setup the dialog such that it doesn't allow for network play when a player is added.
112 */
113 
114 /*
115 NetADSPRead() should time out by calling PBControl(dspStatus, ...) to see if there are enough bytes
116 clearly this is all broken until we have packet types
117 */
118 
119 
120 #include "cseries.h"
121 #include "map.h"       // for TICKS_PER_SECOND and "struct entry_point"
122 #include "interface.h" // for transfering map
123 #include "mytm.h"	// ZZZ: both versions use mytm now
124 #include "preferences.h" // for network_preferences and environment_preferences
125 
126 #include "sdl_network.h"
127 #include "network_lookup_sdl.h"
128 #include "SDL_thread.h"
129 
130 #include "game_errors.h"
131 #include "CommunicationsChannel.h"
132 #include "Console.h"
133 #include "MessageDispatcher.h"
134 #include "MessageInflater.h"
135 #include "MessageHandler.h"
136 #include "progress.h"
137 #include "extensions.h"
138 
139 #include <stdlib.h>
140 #include <string.h>
141 
142 #include <map>
143 #include <vector>
144 #include "Logging.h"
145 
146 // ZZZ: moved many struct definitions, constant #defines, etc. to header for (limited) sharing
147 #include "network_private.h"
148 
149 // ZZZ: since network data format is now distinct from in-memory format.
150 // (quite similar, admittedly, in this first effort... ;) )
151 #include "network_data_formats.h"
152 
153 #include "network_messages.h"
154 
155 #include "NetworkGameProtocol.h"
156 
157 #include "RingGameProtocol.h"
158 #include "StarGameProtocol.h"
159 
160 #include "lua_script.h"
161 
162 #include "libnat.h"
163 
164 #include <boost/bind.hpp>
165 
166 #include "network_metaserver.h"
167 
168 #include "network_sound.h"
169 
170 #include "ConnectPool.h"
171 
172 /* ---------- globals */
173 
174 static short ddpSocket; /* our ddp socket number */
175 
176 static short localPlayerIndex;
177 static short localPlayerIdentifier;
178 static std::string gameSessionIdentifier;
179 static NetTopologyPtr topology;
180 static short sServerPlayerIndex;
181 static bool sOldSelfSendStatus;
182 static RingGameProtocol sRingGameProtocol;
183 static StarGameProtocol sStarGameProtocol;
184 static NetworkGameProtocol* sCurrentGameProtocol = NULL;
185 
186 static byte *deferred_script_data = NULL;
187 static size_t deferred_script_length = 0;
188 static bool do_netscript;
189 
190 static CommunicationsChannelFactory *server = NULL;
191 
192 typedef std::map<int, Client *> client_map_t;
193 static client_map_t connections_to_clients;
194 typedef std::map<int, ClientChatInfo *> client_chat_info_map_t;
195 static client_chat_info_map_t client_chat_info;
196 static CommunicationsChannel *connection_to_server = NULL;
197 static NonblockingConnect *server_nbc = 0;
198 static bool nbc_is_resolving = false;
199 static int next_stream_id = 1; // 0 is local player
200 static IPaddress host_address;
201 static bool host_address_specified = false;
202 static MessageInflater *inflater = NULL;
203 static MessageDispatcher *joinDispatcher = NULL;
204 static uint32 next_join_attempt;
205 static Capabilities my_capabilities;
206 
207 static GatherCallbacks *gatherCallbacks = NULL;
208 static ChatCallbacks *chatCallbacks = NULL;
209 
210 static UpnpController *controller = NULL;
211 extern MetaserverClient* gMetaserverClient;
212 
213 static std::vector<NetworkStats> sNetworkStats;
214 const static NetworkStats sInvalidStats = {
215 	NetworkStats::invalid,
216 	NetworkStats::invalid,
217 	0
218 };
219 uint32 last_network_stats_send = 0;
220 const static int network_stats_send_period = MACHINE_TICKS_PER_SECOND;
221 
222 // ignore list
223 static std::set<int> sIgnoredPlayers;
224 
player_is_ignored(int player_index)225 bool player_is_ignored(int player_index)
226 {
227 	return (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end());
228 }
229 
230 struct ignore_player {
operator ()ignore_player231 	void operator()(const std::string& s) const {
232 		int player_index = atoi(s.c_str());
233 		if (player_index == localPlayerIndex)
234 		{
235 			screen_printf("you can't ignore yourself");
236 		}
237 		else if (player_index >= 0 && player_index < topology->player_count)
238 		{
239 			if (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end())
240 			{
241 				screen_printf("removing player %i from the ignore list", player_index);
242 				sIgnoredPlayers.erase(player_index);
243 			}
244 			else
245 			{
246 				screen_printf("adding player %i to the ignore list", player_index);
247 				sIgnoredPlayers.insert(player_index);
248 			}
249 		}
250 		else
251 		{
252 			screen_printf("invalid player %i", player_index);
253 		}
254 	}
255 };
256 
257 struct ignore_lua
258 {
operator ()ignore_lua259 	void operator()(const std::string&) const {
260 		ToggleLuaMute();
261 	}
262 };
263 
264 struct ignore_mic
265 {
operator ()ignore_mic266 	void operator()(const std::string& s) const {
267 		int player_index = atoi(s.c_str());
268 		if (player_index == localPlayerIndex)
269 		{
270 			screen_printf("you can't ignore your own mic");
271 		}
272 		else if (player_index >= 0 && player_index < topology->player_count)
273 		{
274 			mute_player_mic(player_index);
275 		}
276 		else
277 		{
278 			screen_printf("invalid player %i", player_index);
279 		}
280 	}
281 };
282 
283 // ZZZ note: very few folks touch the streaming data, so the data-format issues outlined above with
284 // datagrams (the data from which are passed around, interpreted, and touched by many functions)
285 // don't matter as much.  Do observe, though, that users of the "distribution" mechanism will have
286 // to pack and unpack their own distribution data - we can't be expected to know what they're doing.
287 
288 // ZZZ note: read this externally with the NetState() function.
289 static short netState= netUninitialized;
290 
291 // ZZZ change: now using an STL 'map' to, well, _map_ distribution types to info records.
292 typedef std::map<int16, NetDistributionInfo> distribution_info_map_t;
293 static  distribution_info_map_t distribution_info_map;
294 
295 
296 #ifdef NETWORK_CHAT
297 static	NetChatMessage	incoming_chat_message_buffer;
298 static bool		new_incoming_chat_message = false;
299 #endif
300 
301 
302 // ZZZ: are we trying to start a new game or resume a saved-game?
303 // This is only valid on the gatherer after NetGather() is called;
304 // only valid on a joiner once he receives the final topology (tagRESUME_GAME)
305 // Used, at least, on the gatherer to determine whether or not to resort players by address
306 static bool resuming_saved_game = false;
307 
308 
309 /* ---------- private prototypes */
310 void NetPrintInfo(void);
311 void NetInitializeSessionIdentifier(void);
312 
313 // ZZZ: cmon, we're not fooling anyone... game_data is a game_info*; player_data is a player_info*
314 // Originally I guess the plan was to have a strong separation between Marathon game code and the networking code,
315 // such that they could be compiled independently and only know about each other at link-time, but I don't see any
316 // reason to try to keep that... and I suspect Jason abandoned this separation long ago anyway.
317 // For now, the only effect I see is a reduction in type-safety.  :)
318 static void NetInitializeTopology(void *game_data, short game_data_size, void *player_data, short player_data_size);
319 static void NetLocalAddrBlock(NetAddrBlock *address, short socketNumber);
320 
321 static int net_compare(void const *p1, void const *p2);
322 
323 static void NetUpdateTopology(void);
324 static void NetDistributeTopology(short tag);
325 
326 static bool NetSetSelfSend(bool on);
327 
328 static void NetDDPPacketHandler(DDPPacketBufferPtr inPacket);
329 
getStreamIdFromChannel(CommunicationsChannel * channel)330 int getStreamIdFromChannel(CommunicationsChannel *channel) {
331   client_map_t::iterator it;
332   for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
333     if (it->second->channel == channel) {
334       return it->first;
335     }
336   }
337   return -1;
338 }
339 
340 //-----------------------------------------------------------------------------
341 // Message handlers
342 //-----------------------------------------------------------------------------
343 
344 // Gatherer
~Client()345 Client::~Client() {
346   delete channel;
347 }
348 
349 CheckPlayerProcPtr Client::check_player = 0;
350 
Client(CommunicationsChannel * inChannel)351 Client::Client(CommunicationsChannel *inChannel) : channel(inChannel), state(_connecting), network_version(0), mDispatcher(new MessageDispatcher())
352 {
353 	std::fill_n(name, MAX_NET_PLAYER_NAME_LENGTH, '\0');
354 	mJoinerInfoMessageHandler.reset(newMessageHandlerMethod(this, &Client::handleJoinerInfoMessage));
355 	mCapabilitiesMessageHandler.reset(newMessageHandlerMethod(this, &Client::handleCapabilitiesMessage));
356 	mAcceptJoinMessageHandler.reset(newMessageHandlerMethod(this, &Client::handleAcceptJoinMessage));
357 	mChatMessageHandler.reset(newMessageHandlerMethod(this, &Client::handleChatMessage));
358 	mChangeColorsMessageHandler.reset(newMessageHandlerMethod(this, &Client::handleChangeColorsMessage));
359 	mUnexpectedMessageHandler.reset(newMessageHandlerMethod(this, &Client::unexpectedMessageHandler));
360 	mDispatcher->setDefaultHandler(mUnexpectedMessageHandler.get());
361 	mDispatcher->setHandlerForType(mJoinerInfoMessageHandler.get(), JoinerInfoMessage::kType);
362 	mDispatcher->setHandlerForType(mCapabilitiesMessageHandler.get(), CapabilitiesMessage::kType);
363 	mDispatcher->setHandlerForType(mAcceptJoinMessageHandler.get(), AcceptJoinMessage::kType);
364 	mDispatcher->setHandlerForType(mChatMessageHandler.get(), NetworkChatMessage::kType);
365 	mDispatcher->setHandlerForType(mChangeColorsMessageHandler.get(), ChangeColorsMessage::kType);
366 	channel->setMessageHandler(mDispatcher.get());
367 }
368 
drop()369 void Client::drop()
370 {
371 	int stream_id = getStreamIdFromChannel(channel);
372 	if (client_chat_info[stream_id]) {
373 		ClientInfoMessage clientInfoMessage(stream_id, client_chat_info[stream_id], (int16) ClientInfoMessage::kRemove);
374 		client_map_t::iterator it;
375 		for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
376 			if (it->second->can_pregame_chat()) {
377 				it->second->channel->enqueueOutgoingMessage(clientInfoMessage);
378 			}
379 		}
380 
381 		delete client_chat_info[stream_id];
382 		client_chat_info.erase(stream_id);
383 	}
384 
385 	if (state == _connected) { // (remove from the list of joinable players)
386 		if (gatherCallbacks) {
387 			prospective_joiner_info player;
388 			player.stream_id = getStreamIdFromChannel(channel);
389 			gatherCallbacks->JoiningPlayerDropped(&player);
390 		}
391 	} else if (state == _awaiting_map) { // need to remove from topo
392 		uint16 stream_id = getStreamIdFromChannel(channel);
393 		int i;
394 		for (i = 1; i < topology->player_count; i++) {
395 			if (topology->players[i].stream_id == stream_id) {
396 				break;
397 			}
398 		}
399 		if (i != topology->player_count) {
400 			for (; i < topology->player_count - 1; i++) {
401 				topology->players[i] = topology->players[i + 1];
402 			}
403 			topology->player_count--;
404 			NetUpdateTopology();
405 
406 			if (gatherCallbacks) {
407 				prospective_joiner_info player;
408 				player.stream_id = getStreamIdFromChannel(channel);
409 				gatherCallbacks->JoinedPlayerDropped(&player);
410 			}
411 
412 			NetDistributeTopology(tagDROPPED_PLAYER);
413 		} else {
414 			logAnomaly("a client in state _awaiting_map dropped, but was not found in the topology");
415 		}
416 	}
417 }
418 
419 // This serves as a generic M1 check. It doesn't guarantee
420 // map or physics are M1, but for now it suffices.
421 extern bool shapes_file_is_m1();
422 
capabilities_indicate_player_is_gatherable(bool warn_joiner)423 bool Client::capabilities_indicate_player_is_gatherable(bool warn_joiner)
424 {
425 	// ghs: perhaps someday there will be an elegant, extensible way to do this
426 	//      but for now, this is what you get
427 
428 	char s[256];
429 
430 	// As this is the first version, I do not check version numbers for
431 	// compatibility! Because I don't know if higher numbers necessarily mean
432 	// incompatibility. In the future, gatherer and joiner should advertise what
433 	// they have, and the higher one must contain the backward compatibility
434 	// logic if any exists
435 
436 	// joiners can disable themselves from being gathered by replying with
437 	// capabilities where the "Gatherable" capability is set to 0...this will
438 	// not trigger a warning message from the gatherer
439 
440 	// gatherer disables gathering by returning false from this function, and
441 	// sending a descriptive error message to the joiner saying he won't show up
442 
443 	if (capabilities[Capabilities::kGatherable] == 0) {
444 		// no warning, the joiner already knows he is incompatible
445 		return false;
446 	}
447 
448 	if (capabilities[Capabilities::kGameworld] < Capabilities::kGameworldVersion || (shapes_file_is_m1() && capabilities[Capabilities::kGameworldM1] < Capabilities::kGameworldM1Version))
449 	{
450 		if (warn_joiner)
451 		{
452 			ServerWarningMessage serverWarningMessage(expand_app_variables("The gatherer is using a new version of $appName$. You will not appear in the list of available players."), ServerWarningMessage::kJoinerUngatherable);
453 			channel->enqueueOutgoingMessage(serverWarningMessage);
454 		}
455 		return false;
456 	}
457 
458 	if (network_preferences->game_protocol == _network_game_protocol_star) {
459 		if (capabilities[Capabilities::kStar] == 0) {
460 			if (warn_joiner) {
461 				ServerWarningMessage serverWarningMessage(getcstr(s, strNETWORK_ERRORS, netWarnJoinerHasNoStar), ServerWarningMessage::kJoinerUngatherable);
462 				channel->enqueueOutgoingMessage(serverWarningMessage);
463 			}
464 			return false;
465 		} else if (capabilities[Capabilities::kStar] < Capabilities::kStarVersion) {
466 			if (warn_joiner) {
467 				ServerWarningMessage serverWarningMessage(expand_app_variables("The gatherer is using a newer version of $appName$. You will not appear in the list of available players."), ServerWarningMessage::kJoinerUngatherable);
468 				channel->enqueueOutgoingMessage(serverWarningMessage);
469 			}
470 			return false;
471 		}
472 	} else {
473 		if (capabilities[Capabilities::kRing] == 0) {
474 			if (warn_joiner) {
475 				ServerWarningMessage serverWarningMessage(getcstr(s, strNETWORK_ERRORS, netWarnJoinerHasNoRing), ServerWarningMessage::kJoinerUngatherable);
476 				channel->enqueueOutgoingMessage(serverWarningMessage);
477 			}
478 			return false;
479 		}
480 	}
481 
482 	if (do_netscript)
483 	{
484 		if (capabilities[Capabilities::kLua] == 0) {
485 			if (warn_joiner) {
486 				char s[256];
487 				ServerWarningMessage serverWarningMessage(getcstr(s, strNETWORK_ERRORS, netWarnJoinerNoLua), ServerWarningMessage::kJoinerUngatherable);
488 				channel->enqueueOutgoingMessage(serverWarningMessage);
489 			}
490 			return false;
491 		} else if (capabilities[Capabilities::kLua] < Capabilities::kLuaVersion)
492 		{
493 			if (warn_joiner)
494 			{
495 				ServerWarningMessage serverWarningMessage("The gatherer is using a newer version of Lua (net script) that you do not have. You will not appear in the list of available players.", ServerWarningMessage::kJoinerUngatherable);
496 				channel->enqueueOutgoingMessage(serverWarningMessage);
497 			}
498 		return false;
499 		}
500 	}
501 
502 	if (topology->game_data.net_game_type == _game_of_rugby)
503 	{
504 		if (capabilities[Capabilities::kRugby] == 0)
505 		{
506 			if (warn_joiner)
507 			{
508 				ServerWarningMessage serverWarningMessage(expand_app_variables("The gatherer is using a newer version of $appName$ with different rugby scoring. You will not appear in the list of available players."), ServerWarningMessage::kJoinerUngatherable);
509 				channel->enqueueOutgoingMessage(serverWarningMessage);
510 			}
511 			return false;
512 		}
513 	}
514 
515 	return true;
516 }
517 
518 
handleJoinerInfoMessage(JoinerInfoMessage * joinerInfoMessage,CommunicationsChannel *)519 void Client::handleJoinerInfoMessage(JoinerInfoMessage* joinerInfoMessage, CommunicationsChannel *)
520 {
521   if (netState == netGathering) {
522     if (joinerInfoMessage->version() == kNetworkSetupProtocolID) {
523       strncpy(name, joinerInfoMessage->info()->name, MAX_NET_PLAYER_NAME_LENGTH);
524 
525       int16 stream_id = getStreamIdFromChannel(channel);
526       client_chat_info[stream_id] = new ClientChatInfo;
527       client_chat_info[stream_id]->name = joinerInfoMessage->info()->name;
528       client_chat_info[stream_id]->color = joinerInfoMessage->info()->color;
529       client_chat_info[stream_id]->team = joinerInfoMessage->info()->team;
530 
531       ClientInfoMessage clientInfoMessage(stream_id, client_chat_info[stream_id], (int16) ClientInfoMessage::kAdd);
532       client_map_t::iterator it;
533       for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
534 	      if (it->second->can_pregame_chat()) {
535 		      it->second->channel->enqueueOutgoingMessage(clientInfoMessage);
536 	      }
537       }
538 
539       // send gatherer capabilities
540       CapabilitiesMessage capabilitiesMessage(my_capabilities);
541       channel->enqueueOutgoingMessage(capabilitiesMessage);
542       state = Client::_awaiting_capabilities;
543     } else {
544       // strange, joiner should have realized he couldn't join
545       // ok, disconnect him
546       state = Client::_disconnect;
547     }
548   } else {
549     logAnomaly("unexpected joiner info message received (netState is %i)", netState);
550   }
551 }
552 
handleCapabilitiesMessage(CapabilitiesMessage * capabilitiesMessage,CommunicationsChannel *)553 void Client::handleCapabilitiesMessage(CapabilitiesMessage* capabilitiesMessage, CommunicationsChannel *)
554 {
555 	if (state == _awaiting_capabilities) {
556 		capabilities = *capabilitiesMessage->capabilities();
557 
558 		if (capabilities_indicate_player_is_gatherable(_warn_joiner)) {
559 			state = Client::_connected_but_not_yet_shown;
560 		} else {
561 			state = Client::_ungatherable;
562 		}
563 
564 		client_chat_info_map_t::iterator it;
565 		for (it = client_chat_info.begin(); it != client_chat_info.end(); it++) {
566 			if (it->second)
567 			{
568 				ClientInfoMessage clientInfoMessage(it->first, it->second, (int16) ClientInfoMessage::kAdd);
569 				channel->enqueueOutgoingMessage(clientInfoMessage);
570 			}
571 		}
572 	} else {
573 		logAnomaly("unexpected capabilities message received (state is %i)", state);
574 	}
575 }
576 
577 /*
578 void Client::handleScriptMessage(ScriptMessage* scriptMessage, CommunicationsChannel *)
579 {
580   if (state == _awaiting_script_message) {
581     if (do_netscript &&
582 	scriptMessage->value() != _netscript_yes_script_message) {
583       alert_user(infoError, strNETWORK_ERRORS, netErrUngatheredPlayerUnacceptable, 0);
584       state = _ungatherable;
585     } else {
586       JoinPlayerMessage joinPlayerMessage(topology->nextIdentifier);
587       channel->enqueueOutgoingMessage(joinPlayerMessage);
588       state = _awaiting_accept_join;
589     }
590   } else {
591     logAnomaly1("unexpected script message received (state is %i)", state);
592   }
593 }
594 */
595 
handleAcceptJoinMessage(AcceptJoinMessage * acceptJoinMessage,CommunicationsChannel *)596 void Client::handleAcceptJoinMessage(AcceptJoinMessage* acceptJoinMessage,
597 				     CommunicationsChannel *)
598 {
599   if (state == _awaiting_accept_join) {
600     if (acceptJoinMessage->accepted()) {
601       topology->players[topology->player_count] = *acceptJoinMessage->player();
602       topology->players[topology->player_count].stream_id = getStreamIdFromChannel(channel);
603 	  topology->players[topology->player_count].net_dead = false;
604       prospective_joiner_info player;
605       player.stream_id = topology->players[topology->player_count].stream_id;
606       topology->players[topology->player_count].dspAddress = channel->peerAddress();
607       topology->players[topology->player_count].ddpAddress.host = channel->peerAddress().host;
608 
609       topology->player_count += 1;
610       check_player(topology->player_count - 1, topology->player_count);
611       NetUpdateTopology();
612 
613       GameSessionMessage gameSessionMessage(reinterpret_cast<const uint8*>(gameSessionIdentifier.c_str()), gameSessionIdentifier.size());
614       CommunicationsChannel *channel = connections_to_clients[player.stream_id]->channel;
615       channel->enqueueOutgoingMessage(gameSessionMessage);
616 
617       NetDistributeTopology(tagNEW_PLAYER);
618       state = _awaiting_map;
619       if (gatherCallbacks) gatherCallbacks->JoinSucceeded(&player);
620     } else {
621       // joiner didn't accept!?
622       alert_user(infoError, strNETWORK_ERRORS, netErrCantAddPlayer, 0);
623       state = _ungatherable;
624     }
625   } else {
626     logAnomaly("unexpected accept join message received (state is %i)", state);
627   }
628 }
629 
handleChangeColorsMessage(ChangeColorsMessage * changeColorsMessage,CommunicationsChannel * channel)630 void Client::handleChangeColorsMessage(ChangeColorsMessage *changeColorsMessage,
631 				       CommunicationsChannel *channel)
632 {
633 	if (can_pregame_chat()) {
634 		int stream_id = getStreamIdFromChannel(channel);
635 		if (client_chat_info[stream_id]) {
636 			client_chat_info[stream_id]->color = changeColorsMessage->color();
637 			client_chat_info[stream_id]->team = changeColorsMessage->team();
638 
639 			ClientInfoMessage clientInfoMessage(stream_id, client_chat_info[stream_id], (int16) ClientInfoMessage::kUpdate);
640 			client_map_t::iterator it;
641 			for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
642 				if (it->second->can_pregame_chat()) {
643 					it->second->channel->enqueueOutgoingMessage(clientInfoMessage);
644 				}
645 			}
646 		} else {
647 			logAnomaly("change colors message received, but client chat info does not exist for %i", stream_id);
648 		}
649 	}
650 	if (state == _awaiting_map) {
651 		uint16 stream_id = getStreamIdFromChannel(channel);
652 		int i;
653 		for (i = 1; i < topology->player_count; i++) {
654 			if (topology->players[i].stream_id == stream_id) {
655 				break;
656 			}
657 		}
658 
659 		if (i != topology->player_count) {
660 			player_info *player = &topology->players[i].player_data;
661 			if (player->desired_color != changeColorsMessage->color() ||
662 			    player->team != changeColorsMessage->team()) {
663 
664 				player->desired_color = changeColorsMessage->color();
665 				player->team = changeColorsMessage->team();
666 
667 				check_player(i, topology->player_count);
668 				NetUpdateTopology();
669 
670 				NetDistributeTopology(tagCHANGED_PLAYER);
671 				if (gatherCallbacks) {
672 					prospective_joiner_info player_to_change;
673 					player_to_change.stream_id = stream_id;
674 					gatherCallbacks->JoinedPlayerChanged(&player_to_change);
675 				}
676 			}
677 		} else {
678 			logAnomaly("a client in state _awaiting_map requested a color change, but was not found in the topology");
679 		}
680 	} else if (!can_pregame_chat()) {
681 		logAnomaly("unexpected change colors message received (state is %i)", state);
682 	}
683 }
684 
handleChatMessage(NetworkChatMessage * netChatMessage,CommunicationsChannel *)685 void Client::handleChatMessage(NetworkChatMessage* netChatMessage,
686 			       CommunicationsChannel *)
687 {
688 	// relay this to all clients
689 	if (state == _ingame) {
690 		assert(netState == netActive);
691 		if (netChatMessage->target() == NetworkChatMessage::kTargetPlayers) {
692 			NetworkChatMessage chatMessage(netChatMessage->chatText(), getStreamIdFromChannel(channel), NetworkChatMessage::kTargetPlayers);
693 			client_map_t::iterator it;
694 			for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
695 				if (it->second->state == _ingame) {
696 					it->second->channel->enqueueOutgoingMessage(chatMessage);
697 				}
698 			}
699 
700 			// display it locally
701 			if (chatCallbacks) {
702 				for (int playerIndex = 0; playerIndex < topology->player_count; playerIndex++) {
703 					if (topology->players[playerIndex].stream_id == getStreamIdFromChannel(channel)) {
704 						if (player_is_ignored(playerIndex)) return;
705 						chatCallbacks->ReceivedMessageFromPlayer(topology->players[playerIndex].player_data.name, netChatMessage->chatText());
706 						return;
707 					}
708 				}
709 			}
710 		} else {
711 			logNote("in-game chat message currently only supports sending messages to all players; not relaying");
712 		}
713 	} else if (can_pregame_chat()) {
714 		if (netChatMessage->target() == NetworkChatMessage::kTargetClients) {
715 			NetworkChatMessage chatMessage(netChatMessage->chatText(), getStreamIdFromChannel(channel), NetworkChatMessage::kTargetClients);
716 			client_map_t::iterator it;
717 			for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
718 				if (it->second->can_pregame_chat()) {
719 					it->second->channel->enqueueOutgoingMessage(chatMessage);
720 				}
721 			}
722 			if (chatCallbacks) {
723 				int stream_id = getStreamIdFromChannel(channel);
724 				if (client_chat_info[stream_id]) {
725 					chatCallbacks->ReceivedMessageFromPlayer(client_chat_info[stream_id]->name.c_str(), netChatMessage->chatText());
726 				} else {
727 					logAnomaly("chat message from %i, player not found", stream_id);
728 				}
729 			}
730 		} else {
731 			logNote("pre-game chat currently only supports sending messages to all players; not relaying");
732 		}
733 	} else {
734 		logNote("non in-game/pre-game chat message received; ignoring");
735 	}
736 }
737 
unexpectedMessageHandler(Message * message,CommunicationsChannel *)738 void Client::unexpectedMessageHandler(Message *message, CommunicationsChannel *) {
739 	logAnomaly("unexpected message type %i received (net state)", message->type(), netState);
740 }
741 
742 
743 
744 static short handlerState;
745 
handleHelloMessage(HelloMessage * helloMessage,CommunicationsChannel *)746 static void handleHelloMessage(HelloMessage* helloMessage, CommunicationsChannel*)
747 {
748 	if (handlerState == netAwaitingHello) {
749 		// if the network versions match, reply with my join info
750 		if (helloMessage->version() == kNetworkSetupProtocolID) {
751 			prospective_joiner_info my_info = {};
752 
753 			strncpy(my_info.name, player_preferences->name, sizeof(my_info.name) - 1);
754 			my_info.color = player_preferences->color;
755 			my_info.team = player_preferences->team;
756 
757 			JoinerInfoMessage joinerInfoMessage(&my_info, kNetworkSetupProtocolID);
758 			connection_to_server->enqueueOutgoingMessage(joinerInfoMessage);
759 			handlerState = netJoining;
760 		} else {
761 			alert_user(infoError, strNETWORK_ERRORS, netErrIncompatibleVersion, 0);
762 			handlerState = netJoinErrorOccurred;
763 		}
764 	} else {
765 		logAnomaly("unexpected hello message received (netState is %i)", netState);
766 	}
767 }
768 
handleCapabilitiesMessage(CapabilitiesMessage * capabilitiesMessage,CommunicationsChannel *)769 static void handleCapabilitiesMessage(CapabilitiesMessage* capabilitiesMessage,
770 				      CommunicationsChannel *)
771 {
772 	if (handlerState == netJoining) {
773 		Capabilities capabilities = *capabilitiesMessage->capabilities();
774 		if (capabilities[Capabilities::kGameworld] < Capabilities::kGameworldVersion || (shapes_file_is_m1() && capabilities[Capabilities::kGameworldM1] < Capabilities::kGameworldM1Version) || (network_preferences->game_protocol == _network_game_protocol_star && capabilities[Capabilities::kStar] < Capabilities::kStarVersion))
775 		{
776 			// I'm not gatherable
777 			my_capabilities[Capabilities::kGatherable] = 0;
778 			CapabilitiesMessage capabilitiesMessageReply(my_capabilities);
779 			connection_to_server->enqueueOutgoingMessage(capabilitiesMessageReply);
780 			my_capabilities[Capabilities::kGatherable] = Capabilities::kGatherableVersion;
781 
782 			alert_user(expand_app_variables("The gatherer is using an old version of $appName$. You will not appear in the list of available players.").c_str());
783 		} else {
784 			// everything else is version 1
785 			CapabilitiesMessage capabilitiesMessageReply(my_capabilities);
786 			connection_to_server->enqueueOutgoingMessage(capabilitiesMessageReply);
787 		}
788 
789 	} else {
790 		logAnomaly("unexpected capabilities message received (netState is %i)", netState);
791 	}
792 }
793 
handleClientInfoMessage(ClientInfoMessage * clientInfoMessage,CommunicationsChannel *)794 static void handleClientInfoMessage(ClientInfoMessage* clientInfoMessage, CommunicationsChannel *) {
795 	if (netState == netJoining || netState == netWaiting || netState == netStartingUp || netState == netActive) {
796 		int16 id = clientInfoMessage->stream_id();
797 		if (clientInfoMessage->action() == ClientInfoMessage::kAdd) {
798 			if (client_chat_info[id]) {
799 				logAnomaly("add message for client that already exists (%i)", id);
800 				delete client_chat_info[id];
801 				client_chat_info.erase(id);
802 			}
803 			client_chat_info[id] = new ClientChatInfo;
804 			*client_chat_info[id] = *clientInfoMessage->info();
805 		} else if (clientInfoMessage->action() == ClientInfoMessage::kRemove) {
806 			delete client_chat_info[id];
807 			client_chat_info.erase(id);
808 		} else if (clientInfoMessage->action() == ClientInfoMessage::kUpdate) {
809 			*client_chat_info[id] = *clientInfoMessage->info();
810 		} else {
811 			logAnomaly("unknown client info message action %i", clientInfoMessage->action());
812 		}
813 	} else {
814 		logAnomaly("unexpected client info message received (netState is %i)", netState);
815 	}
816 }
817 
handleJoinPlayerMessage(JoinPlayerMessage * joinPlayerMessage,CommunicationsChannel *)818 static void handleJoinPlayerMessage(JoinPlayerMessage* joinPlayerMessage, CommunicationsChannel*) {
819   if (handlerState == netJoining) {
820     /* Note that we could set accepted to false if we wanted to for some */
821     /*  reason- such as bad serial numbers.... */
822 
823     SetNetscriptStatus (false); // Unless told otherwise, we don't expect a netscript
824 
825     /* Note that we share the buffers.. */
826     localPlayerIdentifier= joinPlayerMessage->value();
827     topology->players[localPlayerIndex].identifier= localPlayerIdentifier;
828     topology->players[localPlayerIndex].net_dead= false;
829 
830     /* Confirm. */
831     AcceptJoinMessage acceptJoinMessage(true, &topology->players[localPlayerIndex]);
832     connection_to_server->enqueueOutgoingMessage(acceptJoinMessage);
833 
834     if (acceptJoinMessage.accepted()) {
835       handlerState = netWaiting;
836     } else {
837       handlerState = netJoinErrorOccurred;
838     }
839   } else {
840     logAnomaly("unexpected join player message received (netState is %i)", netState);
841   }
842 }
843 
844 static byte *handlerLuaBuffer = NULL;
845 static size_t handlerLuaLength = 0;
846 
handleLuaMessage(BigChunkOfDataMessage * luaMessage,CommunicationsChannel *)847 static void handleLuaMessage(BigChunkOfDataMessage *luaMessage, CommunicationsChannel *) {
848   if (netState == netStartingUp || netState == netDown) {
849     if (handlerLuaBuffer) {
850       delete[] handlerLuaBuffer;
851       handlerLuaBuffer = NULL;
852     }
853     handlerLuaLength = luaMessage->length();
854     if (handlerLuaLength > 0) {
855       handlerLuaBuffer = new byte[handlerLuaLength];
856       memcpy(handlerLuaBuffer, luaMessage->buffer(), handlerLuaLength);
857     }
858   } else {
859     logAnomaly("unexpected lua message received (netState is %i)", netState);
860   }
861 }
862 
863 static byte *handlerMapBuffer = NULL;
864 static size_t handlerMapLength = 0;
865 
handleMapMessage(BigChunkOfDataMessage * mapMessage,CommunicationsChannel *)866 static void handleMapMessage(BigChunkOfDataMessage *mapMessage, CommunicationsChannel *) {
867 	if (netState == netStartingUp || netState == netDown) {
868 		if (handlerMapBuffer) { // assume the last map the server sent is right
869 			free(handlerMapBuffer);
870 			handlerMapBuffer = NULL;
871 		}
872 		handlerMapLength = mapMessage->length();
873 		if (handlerMapLength > 0) {
874 			handlerMapBuffer = reinterpret_cast<byte*>(malloc(handlerMapLength));
875 			memcpy(handlerMapBuffer, mapMessage->buffer(), handlerMapLength);
876 		}
877 	} else {
878 		logAnomaly("unexpected map message received (netState is %i)", netState);
879 	}
880 }
881 
handleNetworkChatMessage(NetworkChatMessage * chatMessage,CommunicationsChannel *)882 static void handleNetworkChatMessage(NetworkChatMessage *chatMessage, CommunicationsChannel *) {
883 	if (chatCallbacks) {
884 		if (netState == netActive) {
885 			for (int playerIndex = 0; playerIndex < topology->player_count; playerIndex++) {
886 				if (topology->players[playerIndex].stream_id == chatMessage->senderID()) {
887 					if (player_is_ignored(playerIndex)) return;
888 					chatCallbacks->ReceivedMessageFromPlayer(topology->players[playerIndex].player_data.name, chatMessage->chatText());
889 					return;
890 				}
891 			}
892 			logAnomaly("chat message from %i, player not found", chatMessage->senderID());
893 		} else if (netState == netJoining || netState == netWaiting) {
894 			if (client_chat_info[chatMessage->senderID()]) {
895 				chatCallbacks->ReceivedMessageFromPlayer(client_chat_info[chatMessage->senderID()]->name.c_str(), chatMessage->chatText());
896 			} else {
897 				logAnomaly("chat message from %i, player not found", chatMessage->senderID());
898 			}
899 			return;
900 		} else {
901 			// not enough smarts to correctly redistrbute these, so just ignore them
902 			logNote("non in-game chat message received; ignoring");
903 		}
904 	}
905 }
906 
handleNetworkStatsMessage(NetworkStatsMessage * statsMessage,CommunicationsChannel *)907 static void handleNetworkStatsMessage(NetworkStatsMessage *statsMessage, CommunicationsChannel *)
908 {
909 	if (netState == netActive)
910 	{
911 		if (sNetworkStats.empty() || statsMessage->mStats.size() == sNetworkStats.size())
912 		{
913 			sNetworkStats = statsMessage->mStats;
914 		}
915 		else
916 		{
917 			logWarning("network stats message is wrong size; lost stats");
918 			std::fill(sNetworkStats.begin(), sNetworkStats.end(), sInvalidStats);
919 		}
920 	} else {
921 		logAnomaly("unexpected network stats message received (netState is %i", netState);
922 	}
923 }
924 
925 static byte *handlerPhysicsBuffer = NULL;
926 static size_t handlerPhysicsLength = 0;
927 
handlePhysicsMessage(BigChunkOfDataMessage * physicsMessage,CommunicationsChannel *)928 static void handlePhysicsMessage(BigChunkOfDataMessage *physicsMessage, CommunicationsChannel *) {
929 	if (netState == netStartingUp || netState == netDown) {
930 		if (handlerPhysicsBuffer) {
931 			free(handlerPhysicsBuffer);
932 			handlerPhysicsBuffer = NULL;
933 		}
934 		handlerPhysicsLength = physicsMessage->length();
935 		if (handlerPhysicsLength > 0) {
936 			handlerPhysicsBuffer = reinterpret_cast<byte*>(malloc(handlerPhysicsLength));
937 			memcpy(handlerPhysicsBuffer, physicsMessage->buffer(), handlerPhysicsLength);
938 		}
939 	} else {
940 		logAnomaly("unexpected physics message received (netState is %i)", netState);
941 	}
942 }
943 
944 /*
945 static void handleScriptMessage(ScriptMessage* scriptMessage, CommunicationsChannel*) {
946   if (netState == netJoining) {
947     ScriptMessage replyToScriptMessage;
948     if (scriptMessage->value() == _netscript_query_message) {
949 #ifdef HAVE_LUA
950       replyToScriptMessage.setValue(_netscript_yes_script_message);
951 #else
952       replyToScriptMessage.setValue(_netscript_no_script_message);
953 #endif
954     } else {
955       replyToScriptMessage.setValue(_netscript_no_script_message);
956     }
957     connection_to_server->enqueueOutgoingMessage(replyToScriptMessage);
958   } else {
959     logAnomaly1("unexpected script message received (netState is %i)", netState);
960   }
961 }
962 */
963 
handleServerWarningMessage(ServerWarningMessage * serverWarningMessage,CommunicationsChannel *)964 static void handleServerWarningMessage(ServerWarningMessage *serverWarningMessage, CommunicationsChannel *) {
965   char *s = strdup(serverWarningMessage->string()->c_str());
966   alert_user(s);
967   free(s);
968 }
969 
970 
handleTopologyMessage(TopologyMessage * topologyMessage,CommunicationsChannel *)971 static void handleTopologyMessage(TopologyMessage* topologyMessage, CommunicationsChannel *) {
972   if (netState == netWaiting) {
973     *topology = *(topologyMessage->topology());
974 
975     NetAddrBlock address;
976 
977       address = connection_to_server->peerAddress();
978 
979       // ZZZ: the code below used to assume the server was _index_ 0; now, we merely
980       // assume the server has _identifier_ 0.
981       int theServerIndex;
982       for(theServerIndex = 0; theServerIndex < topology->player_count; theServerIndex++)
983 	{
984 	  if(topology->players[theServerIndex].identifier == 0) break;
985 	}
986 
987       assert(theServerIndex != topology->player_count);
988 
989       topology->players[theServerIndex].dspAddress= address;
990       topology->players[theServerIndex].ddpAddress.host = address.host;
991 
992       NetUpdateTopology();
993 
994     switch (topology->tag)
995       {
996       case tagNEW_PLAYER:
997 	handlerState = netPlayerAdded;
998 	break;
999       case tagDROPPED_PLAYER:
1000 	handlerState = netPlayerDropped;
1001 	break;
1002       case tagCHANGED_PLAYER:
1003 	handlerState = netPlayerChanged;
1004 	break;
1005 
1006       case tagCANCEL_GAME:
1007 	handlerState= netCancelled;
1008 	alert_user(infoError, strNETWORK_ERRORS, netErrServerCanceled, 0);
1009 	break;
1010 
1011       case tagSTART_GAME:
1012 	handlerState = netStartingUp;
1013 	resuming_saved_game = false;
1014 	break;
1015 
1016 	// ZZZ addition
1017       case tagRESUME_GAME:
1018 	handlerState = netStartingResumeGame;
1019 	resuming_saved_game = true;
1020 	break;
1021 
1022       default:
1023 	logAnomaly("topology message received with unknown tag %i; ignoring", topology->tag);
1024 	break;
1025       }
1026   } else {
1027     logWarning("unexpected topology message received -- gatherer and joiner could disagree on topology! (netState is %i)", netState);
1028   }
1029 }
1030 
handleGameSessionMessage(GameSessionMessage * gameSessionMessage,CommunicationsChannel *)1031 static void handleGameSessionMessage(GameSessionMessage* gameSessionMessage, CommunicationsChannel*) {
1032 	if (handlerState == netWaiting) {
1033 		gameSessionIdentifier.assign(gameSessionMessage->buffer(), gameSessionMessage->buffer() + gameSessionMessage->length());
1034 	} else {
1035 		logAnomaly("unexpected game session message received (netState is %i)", netState);
1036 	}
1037 }
1038 
handleUnexpectedMessage(Message * inMessage,CommunicationsChannel *)1039 static void handleUnexpectedMessage(Message *inMessage, CommunicationsChannel *) {
1040   if (handlerState == netAwaitingHello) {
1041     // an unexpected message before hello usually means we couldn't parse
1042     // hello; which means it's likely we're not compatible
1043     alert_user(infoError, strNETWORK_ERRORS, netErrIncompatibleVersion, 0);
1044     handlerState = netJoinErrorOccurred;
1045   }
1046   logAnomaly("unexpected message ID %i received", inMessage->type());
1047 }
1048 
1049 static TypedMessageHandlerFunction<HelloMessage> helloMessageHandler(&handleHelloMessage);
1050 static TypedMessageHandlerFunction<JoinPlayerMessage> joinPlayerMessageHandler(&handleJoinPlayerMessage);
1051 static TypedMessageHandlerFunction<BigChunkOfDataMessage> luaMessageHandler(&handleLuaMessage);
1052 static TypedMessageHandlerFunction<BigChunkOfDataMessage> mapMessageHandler(&handleMapMessage);
1053 static TypedMessageHandlerFunction<NetworkChatMessage> networkChatMessageHandler(&handleNetworkChatMessage);
1054 static TypedMessageHandlerFunction<BigChunkOfDataMessage> physicsMessageHandler(&handlePhysicsMessage);
1055  static TypedMessageHandlerFunction<CapabilitiesMessage> capabilitiesMessageHandler(&handleCapabilitiesMessage);
1056 static TypedMessageHandlerFunction<TopologyMessage> topologyMessageHandler(&handleTopologyMessage);
1057 static TypedMessageHandlerFunction<ServerWarningMessage> serverWarningMessageHandler(&handleServerWarningMessage);
1058 static TypedMessageHandlerFunction<ClientInfoMessage> clientInfoMessageHandler(&handleClientInfoMessage);
1059 static TypedMessageHandlerFunction<NetworkStatsMessage> networkStatsMessageHandler(&handleNetworkStatsMessage);
1060 static TypedMessageHandlerFunction<GameSessionMessage> gameSessionMessageHandler(&handleGameSessionMessage);
1061 static TypedMessageHandlerFunction<Message> unexpectedMessageHandler(&handleUnexpectedMessage);
1062 
NetSetGatherCallbacks(GatherCallbacks * gc)1063 void NetSetGatherCallbacks(GatherCallbacks *gc) {
1064   gatherCallbacks = gc;
1065 }
1066 
NetSetChatCallbacks(ChatCallbacks * cc)1067 void NetSetChatCallbacks(ChatCallbacks *cc) {
1068   chatCallbacks = cc;
1069 }
1070 
SendChatMessage(const std::string & message)1071 void ChatCallbacks::SendChatMessage(const std::string& message)
1072 {
1073 	if (message == "") return;
1074 	if (netState == netActive) {
1075 		if (connection_to_server) {
1076 			NetworkChatMessage chatMessage(message.c_str(), 0, NetworkChatMessage::kTargetPlayers); // gatherer will replace senderID with my ID
1077 			connection_to_server->enqueueOutgoingMessage(chatMessage);
1078 		} else {
1079 			NetworkChatMessage chatMessage(message.c_str(), 0, NetworkChatMessage::kTargetPlayers); // gatherer stream ID is always 0
1080 			client_map_t::iterator it;
1081 			for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
1082 				if (it->second->state == Client::_ingame) {
1083 					it->second->channel->enqueueOutgoingMessage(chatMessage);
1084 				}
1085 			}
1086 			if (chatCallbacks) {
1087 				for (int playerIndex = 0; playerIndex < topology->player_count; playerIndex++) {
1088 					if (playerIndex == localPlayerIndex) {
1089 						chatCallbacks->ReceivedMessageFromPlayer(topology->players[playerIndex].player_data.name, message.c_str());
1090 					}
1091 				}
1092 			}
1093 		}
1094 	} else if (netState == netGathering) {
1095 		NetworkChatMessage chatMessage(message.c_str(), 0, NetworkChatMessage::kTargetClients); // gatherer stream ID is always 0
1096 		client_map_t::iterator it;
1097 		for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
1098 			if (it->second->can_pregame_chat()) {
1099 				it->second->channel->enqueueOutgoingMessage(chatMessage);
1100 			}
1101 		}
1102 		if (chatCallbacks) {
1103 			chatCallbacks->ReceivedMessageFromPlayer(client_chat_info[0]->name.c_str(), message.c_str());
1104 		}
1105 	} else if (netState == netJoining || netState == netWaiting) {
1106 		assert(connection_to_server);
1107 		NetworkChatMessage chatMessage(message.c_str(), 0, NetworkChatMessage::kTargetClients); // gatherer will replace senderID with my ID
1108 		connection_to_server->enqueueOutgoingMessage(chatMessage);
1109 	} else {
1110 		logNote("SendChatMessage called but non in-game/pre-game chat messages are not yet implemented");
1111 	}
1112 }
1113 
1114 
instance()1115 InGameChatCallbacks *InGameChatCallbacks::instance() {
1116 	static InGameChatCallbacks *m_instance = nullptr;
1117   if (!m_instance) {
1118     m_instance = new InGameChatCallbacks();
1119   }
1120   return m_instance;
1121 }
1122 
prompt()1123 std::string InGameChatCallbacks::prompt() {
1124   return (std::string(player_preferences->name) + ":");
1125 }
1126 
ReceivedMessageFromPlayer(const char * player_name,const char * message)1127 void InGameChatCallbacks::ReceivedMessageFromPlayer(const char *player_name, const char *message) {
1128   screen_printf("%s: %s", player_name, message);
1129 }
1130 
NetEnter(void)1131 bool NetEnter(void)
1132 {
1133 	OSErr error;
1134 
1135 	assert(netState==netUninitialized);
1136 
1137 	{
1138 		static bool added_exit_procedure= false;
1139 
1140 		if (!added_exit_procedure) atexit(NetExit);
1141 		added_exit_procedure= true;
1142 	}
1143 
1144 	sCurrentGameProtocol = (network_preferences->game_protocol == _network_game_protocol_star) ?
1145 		static_cast<NetworkGameProtocol*>(&sStarGameProtocol) :
1146 		static_cast<NetworkGameProtocol*>(&sRingGameProtocol);
1147 
1148 	error= NetDDPOpen();
1149 	if (!error) {
1150 		topology = (NetTopologyPtr)malloc(sizeof(NetTopology));
1151 		assert(topology);
1152 		memset(topology, 0, sizeof(NetTopology));
1153 
1154 		NetSetServerIdentifier(0);
1155 
1156 		// ZZZ: Sorry, if this swapping is not supported on all current A1
1157 		// platforms, feel free to rewrite it in a way that is.
1158 		ddpSocket= SDL_SwapBE16(GAME_PORT);
1159 		error= NetDDPOpenSocket(&ddpSocket, NetDDPPacketHandler);
1160 		if (!error) {
1161 			sOldSelfSendStatus= NetSetSelfSend(true);
1162 			sServerPlayerIndex= 0;
1163 
1164 			sCurrentGameProtocol->Enter(&netState);
1165 
1166 			netState= netDown;
1167 			handlerState = netDown;
1168 		} else {
1169 			logError("unable to open socket");
1170 		}
1171 	}
1172 
1173 	if (!inflater) {
1174 		inflater = new MessageInflater ();
1175 		for (int i = 0; i < NUMBER_OF_STREAM_PACKET_TYPES; i++) {
1176 			BigChunkOfDataMessage *prototype = new BigChunkOfDataMessage (i);
1177 			inflater->learnPrototypeForType (i, *prototype);
1178 			delete prototype;
1179 		}
1180 
1181 		inflater->learnPrototype(AcceptJoinMessage());
1182 		inflater->learnPrototype(EndGameDataMessage());
1183 		inflater->learnPrototype(HelloMessage());
1184 		inflater->learnPrototype(JoinerInfoMessage());
1185 		inflater->learnPrototype(JoinPlayerMessage());
1186 		inflater->learnPrototype(LuaMessage());
1187 		inflater->learnPrototype(ZippedLuaMessage());
1188 		inflater->learnPrototype(MapMessage());
1189 		inflater->learnPrototype(ZippedMapMessage());
1190 		inflater->learnPrototype(NetworkChatMessage());
1191 		inflater->learnPrototype(PhysicsMessage());
1192 		inflater->learnPrototype(ZippedPhysicsMessage());
1193 		inflater->learnPrototype(CapabilitiesMessage());
1194 		inflater->learnPrototype(TopologyMessage());
1195 		inflater->learnPrototype(ChangeColorsMessage());
1196 		inflater->learnPrototype(ServerWarningMessage());
1197 		inflater->learnPrototype(ClientInfoMessage());
1198 		inflater->learnPrototype(NetworkStatsMessage());
1199 		inflater->learnPrototype(GameSessionMessage());
1200 	}
1201 
1202 	if (!joinDispatcher) {
1203 		joinDispatcher = new MessageDispatcher();
1204 
1205 		joinDispatcher->setDefaultHandler(&unexpectedMessageHandler);
1206 		joinDispatcher->setHandlerForType(&helloMessageHandler, HelloMessage::kType);
1207 		joinDispatcher->setHandlerForType(&joinPlayerMessageHandler, JoinPlayerMessage::kType);
1208 		joinDispatcher->setHandlerForType(&luaMessageHandler, LuaMessage::kType);
1209 		joinDispatcher->setHandlerForType(&luaMessageHandler, ZippedLuaMessage::kType);
1210 		joinDispatcher->setHandlerForType(&mapMessageHandler, MapMessage::kType);
1211 		joinDispatcher->setHandlerForType(&mapMessageHandler, ZippedMapMessage::kType);
1212 		joinDispatcher->setHandlerForType(&networkChatMessageHandler, NetworkChatMessage::kType);
1213 		joinDispatcher->setHandlerForType(&physicsMessageHandler, PhysicsMessage::kType);
1214 		joinDispatcher->setHandlerForType(&physicsMessageHandler, ZippedPhysicsMessage::kType);
1215 		joinDispatcher->setHandlerForType(&capabilitiesMessageHandler, CapabilitiesMessage::kType);
1216 		joinDispatcher->setHandlerForType(&serverWarningMessageHandler, ServerWarningMessage::kType);
1217 		joinDispatcher->setHandlerForType(&clientInfoMessageHandler, ClientInfoMessage::kType);
1218 		joinDispatcher->setHandlerForType(&topologyMessageHandler, TopologyMessage::kType);
1219 		joinDispatcher->setHandlerForType(&networkStatsMessageHandler, NetworkStatsMessage::kType);
1220 		joinDispatcher->setHandlerForType(&gameSessionMessageHandler, GameSessionMessage::kType);
1221 	}
1222 
1223 	my_capabilities.clear();
1224 	my_capabilities[Capabilities::kGameworld] = Capabilities::kGameworldVersion;
1225 	my_capabilities[Capabilities::kGameworldM1] = Capabilities::kGameworldM1Version;
1226 	my_capabilities[Capabilities::kSpeex] = Capabilities::kSpeexVersion;
1227 	if (network_preferences->game_protocol == _network_game_protocol_star) {
1228 		my_capabilities[Capabilities::kStar] = Capabilities::kStarVersion;
1229 	} else {
1230 		my_capabilities[Capabilities::kRing] = Capabilities::kRingVersion;
1231 	}
1232 #ifdef HAVE_LUA
1233 	my_capabilities[Capabilities::kLua] = Capabilities::kLuaVersion;
1234 #endif
1235 	my_capabilities[Capabilities::kGatherable] = Capabilities::kGatherableVersion;
1236 	my_capabilities[Capabilities::kZippedData] = Capabilities::kZippedDataVersion;
1237 	my_capabilities[Capabilities::kNetworkStats] = Capabilities::kNetworkStatsVersion;
1238 	my_capabilities[Capabilities::kRugby] = Capabilities::kRugbyVersion;
1239 
1240 	// net commands!
1241 	sIgnoredPlayers.clear();
1242 	CommandParser IgnoreParser;
1243 	IgnoreParser.register_command("player", ignore_player());
1244 
1245 	clear_player_mic_mutes();
1246 	IgnoreParser.register_command("mic", ignore_mic());
1247 
1248 	ResetLuaMute();
1249 	IgnoreParser.register_command("lua", ignore_lua());
1250 
1251 	Console::instance()->register_command("ignore", IgnoreParser);
1252 
1253 	next_join_attempt = last_network_stats_send = machine_tick_count();
1254 
1255 	if (error) {
1256 		alert_user(infoError, strNETWORK_ERRORS, netErrCantContinue, error);
1257 		NetExit();
1258 		return false;
1259 	} else {
1260 		return true;
1261 	}
1262 }
1263 
NetDoneGathering(void)1264 void NetDoneGathering(void)
1265 {
1266 	if (server) {
1267 		delete server;
1268 		server = NULL;
1269 	}
1270 }
1271 
NetExit(void)1272 void NetExit(
1273 	void)
1274 {
1275 	OSErr error = noErr;
1276 
1277 	sCurrentGameProtocol->Exit1();
1278 
1279 	// ZZZ: clean up SDL Time Manager emulation.
1280 	// true says wait for any late finishers to finish
1281 	// (but does NOT say to kill anyone not already removed.)
1282 	myTMCleanup(true);
1283 
1284 	if (netState!=netUninitialized) {
1285 		error= NetDDPCloseSocket(ddpSocket);
1286 		if (!error) {
1287 			NetSetSelfSend(sOldSelfSendStatus);
1288 
1289 			free(topology);
1290 			topology= NULL;
1291 
1292 			sCurrentGameProtocol->Exit2();
1293 
1294 			netState= netUninitialized;
1295 		} else {
1296 			logAnomaly("NetDDPCloseSocket returned %i", error);
1297 		}
1298 	}
1299 
1300 	if (connection_to_server) {
1301 		delete connection_to_server;
1302 		connection_to_server = NULL;
1303 	}
1304 
1305 	if (server_nbc) {
1306 		ConnectPool::instance()->abandon(server_nbc);
1307 		server_nbc = 0;
1308 	}
1309 
1310 	{
1311 		client_map_t::iterator it;
1312 		for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++)
1313 			delete(it->second);
1314 		connections_to_clients.clear();
1315 	}
1316 
1317 	{
1318 		client_chat_info_map_t::iterator it;
1319 		for (it = client_chat_info.begin(); it != client_chat_info.end(); it++)
1320 			delete(it->second);
1321 		client_chat_info.clear();
1322 	}
1323 
1324 	sNetworkStats.clear();
1325 
1326 	if (server) {
1327 		delete server;
1328 		server = NULL;
1329 	}
1330 
1331 	delete gMetaserverClient;
1332 	gMetaserverClient = new MetaserverClient();
1333 
1334 	if (controller)
1335 	{
1336 		open_progress_dialog(_closing_router_ports);
1337 		LNat_Upnp_Remove_Port_Mapping(controller, GAME_PORT, "TCP");
1338 		LNat_Upnp_Remove_Port_Mapping(controller, GAME_PORT, "UDP");
1339 		LNat_Upnp_Controller_Free(&controller);
1340 		controller = NULL;
1341 		close_progress_dialog();
1342 	}
1343 
1344 	Console::instance()->unregister_command("ignore");
1345 
1346 	NetDDPClose();
1347 
1348 }
1349 
1350 bool
NetSync()1351 NetSync()
1352 {
1353 	return sCurrentGameProtocol->Sync(topology, dynamic_world->tick_count, localPlayerIndex, sServerPlayerIndex);
1354 }
1355 
1356 
1357 
1358 bool
NetUnSync()1359 NetUnSync()
1360 {
1361 	return sCurrentGameProtocol->UnSync(true, dynamic_world->tick_count);
1362 }
1363 
1364 
1365 
1366 /* Add a function for a distribution type. returns the type, or NONE if it can't be
1367  * installed. It's safe to call this function multiple times for the same proc. */
1368 // ZZZ: changed to take in the desired type, so a given machine can handle perhaps only some
1369 // of the distribution types.
1370 void
NetAddDistributionFunction(int16 inDataTypeID,NetDistributionProc inProc,bool inLossy)1371 NetAddDistributionFunction(int16 inDataTypeID, NetDistributionProc inProc, bool inLossy) {
1372     // We don't support lossless distribution yet.
1373     assert(inLossy);
1374 
1375     // Prepare a NetDistributionInfo with the desired data.
1376     NetDistributionInfo theInfo;
1377     theInfo.lossy               = inLossy;
1378     theInfo.distribution_proc   = inProc;
1379 
1380     // Insert or update a map entry
1381     distribution_info_map[inDataTypeID] = theInfo;
1382 }
1383 
1384 
1385 /* Remove a distribution proc that has been installed. */
1386 void
NetRemoveDistributionFunction(int16 inDataTypeID)1387 NetRemoveDistributionFunction(int16 inDataTypeID) {
1388     distribution_info_map.erase(inDataTypeID);
1389 }
1390 
1391 
1392 const NetDistributionInfo*
NetGetDistributionInfoForType(int16 inType)1393 NetGetDistributionInfoForType(int16 inType)
1394 {
1395 	distribution_info_map_t::const_iterator    theEntry = distribution_info_map.find(inType);
1396 	if(theEntry != distribution_info_map.end())
1397 		return &(theEntry->second);
1398 	else
1399 		return NULL;
1400 }
1401 
1402 
1403 
1404 
NetDistributeInformation(short type,void * buffer,short buffer_size,bool send_to_self,bool only_send_to_team)1405 void NetDistributeInformation(
1406                               short type,
1407                               void *buffer,
1408                               short buffer_size,
1409                               bool send_to_self,
1410 	bool only_send_to_team)
1411 {
1412 	sCurrentGameProtocol->DistributeInformation(type, buffer, buffer_size, send_to_self, only_send_to_team);
1413 }
1414 
1415 
NetState(void)1416 short NetState(
1417 	       void)
1418 {
1419 	return netState;
1420 }
1421 
1422 // Game session identifiers allow the metaserver to
1423 // identify which players join a particular game.
1424 
NetSessionIdentifier(void)1425 std::string NetSessionIdentifier(void)
1426 {
1427 	return gameSessionIdentifier;
1428 }
1429 
NetInitializeSessionIdentifier(void)1430 void NetInitializeSessionIdentifier(void)
1431 {
1432 	// A robust GUID would be even better here, but
1433 	// all we really need is an ID unlikely to be
1434 	// chosen by two gatherers at about the same time.
1435 	gameSessionIdentifier.clear();
1436 	for (int i = 0; i < 16; i++)
1437 	{
1438 		gameSessionIdentifier += static_cast<char>(rand() % 256);
1439 	}
1440 
1441 }
1442 
1443 
1444 /*
1445 ---------
1446 NetGather
1447 ---------
1448 
1449 	---> game data (pointer to typeless data no bigger than MAXIMUM_GAME_DATA_SIZE)
1450 	---> size of game data
1451 	---> player data of gathering player (no bigger than MAXIMUM_PLAYER_DATA_SIZE)
1452 	---> size of player data
1453 	---> lookupUpdateProc (we call NetLookupOpen() and NetLookupClose())
1454 
1455 	<--- error
1456 
1457 start gathering players.
1458 
1459 ---------------
1460 NetGatherPlayer
1461 ---------------
1462 
1463 	---> player index (into the array of looked up names)
1464 
1465 	<--- error
1466 
1467 bring the given player into our game.
1468 
1469 ---------------
1470 NetCancelGather
1471 ---------------
1472 
1473 	<--- error
1474 
1475 tells all players in the game that the game has been cancelled.
1476 
1477 --------
1478 NetStart
1479 --------
1480 
1481 	<--- error
1482 
1483 start the game with the existing topology (which all players should have)
1484 */
1485 
NetGather(void * game_data,short game_data_size,void * player_data,short player_data_size,bool resuming_game)1486 bool NetGather(
1487 	void *game_data,
1488 	short game_data_size,
1489 	void *player_data,
1490 	short player_data_size,
1491         bool resuming_game)
1492 {
1493         resuming_saved_game = resuming_game;
1494 
1495 	NetInitializeTopology(game_data, game_data_size, player_data, player_data_size);
1496 	NetInitializeSessionIdentifier();
1497 
1498 	if (network_preferences->attempt_upnp)
1499 	{
1500 		// open the port!
1501 		open_progress_dialog(_opening_router_ports);
1502 		char public_ip[32];
1503 		int ret = 0;
1504 		if ((ret = LNat_Upnp_Discover(&controller)) != 0)
1505 			logWarning("LibNAT: Failed to discover UPnP controller");
1506 		if (ret == 0)
1507 			if ((ret = LNat_Upnp_Get_Public_Ip(controller, public_ip, 32)) != 0)
1508 				logWarning("LibNAT: Failed to acquire public IP");
1509 		if (ret == 0)
1510 			if ((ret = LNat_Upnp_Set_Port_Mapping(controller, NULL, GAME_PORT, "TCP")) != 0)
1511 				logWarning("LibNAT: Failed to map port %d (TCP)", GAME_PORT);
1512 		if (ret == 0)
1513 			if ((ret = LNat_Upnp_Set_Port_Mapping(controller, NULL, GAME_PORT, "UDP")) != 0)
1514 				logWarning("LibNAT: Failed to map port %d (UDP)", GAME_PORT);
1515 		close_progress_dialog();
1516 
1517 		if (ret != 0)
1518 		{
1519 			controller = 0;
1520 			alert_user(infoError, strNETWORK_ERRORS, netWarnUPnPConfigureFailed, ret);
1521 		}
1522 	}
1523 
1524 	// Start listening for joiners
1525 	server = new CommunicationsChannelFactory(GAME_PORT);
1526 
1527 	netState= netGathering;
1528 
1529 	client_chat_info[0] = new ClientChatInfo;
1530 	client_chat_info[0]->name = player_preferences->name;
1531 	client_chat_info[0]->color = player_preferences->color;
1532 	client_chat_info[0]->team = player_preferences->team;
1533 
1534 	return true;
1535 }
1536 
NetCancelGather(void)1537 void NetCancelGather(
1538 	void)
1539 {
1540 	assert(netState==netGathering);
1541 
1542 	NetDistributeTopology(tagCANCEL_GAME);
1543 }
1544 
NetStart(void)1545 bool NetStart(
1546 	void)
1547 {
1548 	assert(netState==netGathering);
1549 
1550 	// how about we sort the players before we pass them out to everyone?
1551 	// This is an attempt to have a slightly more efficent ring in a multi-zone network.
1552 	// we should really do some sort of pinging to determine an optimal order (or perhaps
1553 	// sort on hop counts) but we'll just order them by their network numbers.
1554 	// however, we need to leave the server player index 0 because we assume that the person
1555 	// that starts out at index 0 is the server.
1556 
1557         // ZZZ: do that sorting in a new game only - in a resume game, the ordering is significant
1558         // (it's how netplayers will line up with existing saved-game players).
1559 
1560         if(!resuming_saved_game)
1561         {
1562                 if (topology->player_count > 2)
1563                 {
1564                         qsort(topology->players+1, topology->player_count-1, sizeof(struct NetPlayer), net_compare);
1565                 }
1566 
1567                 NetUpdateTopology();
1568         }
1569 
1570 	NetDistributeTopology(resuming_saved_game ? tagRESUME_GAME : tagSTART_GAME);
1571 
1572 	return true;
1573 }
1574 
net_compare(void const * p1,void const * p2)1575 static int net_compare(
1576 	void const *p1,
1577 	void const *p2)
1578 {
1579 	uint32 p1_host = SDL_SwapBE32(((const NetPlayer *)p1)->ddpAddress.host);
1580 	uint32 p2_host = SDL_SwapBE32(((const NetPlayer *)p2)->ddpAddress.host);
1581 	return p2_host - p1_host;
1582 }
1583 
1584 /*
1585 -----------
1586 NetGameJoin
1587 -----------
1588 
1589 	---> player name (to register)
1590 	---> player type (to register)
1591 	---> player data (no larger than MAXIMUM_PLAYER_DATA_SIZE)
1592 	---> size of player data
1593 	---> version number of network protocol (used with player type to construct entity name)
1594 	---> host address
1595 
1596 	<--- error
1597 
1598 ------------------
1599 NetUpdateJoinState
1600 ------------------
1601 
1602 	<--- new state (==netJoined,netWaiting,netStartingUp)
1603 
1604 -------------
1605 NetCancelJoin
1606 -------------
1607 
1608 	<--- error
1609 
1610 can�t be called after the player has been gathered
1611 */
1612 
NetGameJoin(void * player_data,short player_data_size,const char * host_addr_string)1613 bool NetGameJoin(
1614 	void *player_data,
1615 	short player_data_size,
1616 	const char* host_addr_string
1617 	)
1618 {
1619   /* Attempt a connection to host */
1620 
1621   if (server_nbc)
1622   {
1623 	  ConnectPool::instance()->abandon(server_nbc);
1624 	  server_nbc = 0;
1625   }
1626 
1627   host_address_specified = (host_addr_string != NULL);
1628   if (host_address_specified)
1629     {
1630 	    uint16 port = DEFAULT_GAME_PORT;
1631 	    std::string host_str = host_addr_string;
1632 	    std::string::size_type pos = host_str.rfind(':');
1633 	    if (pos != std::string::npos)
1634 	    {
1635 			port = atoi(host_str.substr(pos + 1).c_str());
1636 			host_str = host_str.substr(0, pos);
1637 	    }
1638 
1639 	    nbc_is_resolving = true;
1640 	    server_nbc = ConnectPool::instance()->connect(host_str.c_str(), port);
1641     }
1642 
1643     netState = netConnecting;
1644 
1645     NetInitializeTopology((void *) NULL, 0, player_data, player_data_size);
1646     return true;
1647 }
1648 
NetRetargetJoinAttempts(const IPaddress * inAddress)1649 void NetRetargetJoinAttempts(const IPaddress* inAddress)
1650 {
1651 	host_address_specified = (inAddress != NULL);
1652 	if(host_address_specified)
1653 	{
1654 		host_address = *inAddress;
1655 		// Aleph One 1.1 and earlier didn't send the gather port
1656 		if(host_address.port == 0)
1657 		{
1658 			host_address.port = SDL_SwapBE16(DEFAULT_GAME_PORT);
1659 		}
1660 	}
1661 }
1662 
NetCancelJoin(void)1663 void NetCancelJoin(
1664 	void)
1665 {
1666 	assert(netState==netConnecting||netState==netJoining||netState==netWaiting||netState==netCancelled||netState==netJoinErrorOccurred);
1667 }
1668 
NetChangeColors(int16 color,int16 team)1669 void NetChangeColors(int16 color, int16 team) {
1670   assert(netState == netWaiting || netState == netGathering);
1671 
1672   if (netState == netWaiting) {
1673     ChangeColorsMessage changeColorsMessage(color, team);
1674     connection_to_server->enqueueOutgoingMessage(changeColorsMessage);
1675   } else if (netState == netGathering) {
1676     player_info *player = &topology->players[localPlayerIndex].player_data;
1677     if (player->desired_color != color ||
1678 	player->team != team) {
1679       player->desired_color = color;
1680       player->team = team;
1681 
1682       Client::check_player(localPlayerIndex, topology->player_count);
1683       NetUpdateTopology();
1684 
1685       NetDistributeTopology(tagCHANGED_PLAYER);
1686     }
1687   }
1688 }
1689 
1690 /*
1691 	Externally, this is only called before a new game.  It removes the reliance that
1692 	localPlayerIndex of zero is the server, which is not necessarily true if we are
1693 	resyncing for another cooperative level.
1694 */
NetSetServerIdentifier(short identifier)1695 void NetSetServerIdentifier(
1696 	short identifier)
1697 {
1698 	sServerPlayerIndex= identifier;
1699 }
1700 
1701 
1702 /*
1703 net accessor functions
1704 */
1705 
NetGetLocalPlayerIndex(void)1706 short NetGetLocalPlayerIndex(
1707 	void)
1708 {
1709 	assert(netState!=netUninitialized&&netState!=netDown&&netState!=netJoining);
1710 
1711 	return localPlayerIndex;
1712 }
1713 
NetGetPlayerIdentifier(short player_index)1714 short NetGetPlayerIdentifier(
1715 	short player_index)
1716 {
1717 	assert(netState!=netUninitialized&&netState!=netDown&&netState!=netJoining);
1718 	assert(player_index>=0&&player_index<topology->player_count);
1719 
1720 	return topology->players[player_index].identifier;
1721 }
1722 
NetNumberOfPlayerIsValid(void)1723 bool NetNumberOfPlayerIsValid(
1724 	void)
1725 {
1726 	bool valid;
1727 
1728 	switch(netState)
1729 	{
1730 		case netUninitialized:
1731 		case netConnecting:
1732 		case netJoining:
1733 		case netJoinErrorOccurred:
1734 			valid= false;
1735 			break;
1736 		default:
1737 			valid= true;
1738 			break;
1739 	}
1740 
1741 	return valid;
1742 }
1743 
NetGetNumberOfPlayers(void)1744 short NetGetNumberOfPlayers(
1745 	void)
1746 {
1747 	assert(netState!=netUninitialized /* &&netState!=netDown*/ &&netState!=netJoining);
1748 
1749 	return topology->player_count;
1750 }
1751 
NetGetPlayerData(short player_index)1752 void *NetGetPlayerData(
1753 	short player_index)
1754 {
1755 	assert(netState!=netUninitialized/* && netState!=netDown */ &&netState!=netJoining);
1756 	assert(player_index>=0&&player_index<topology->player_count);
1757 
1758 	return (void *) &topology->players[player_index].player_data;
1759 }
1760 
NetGetGameData(void)1761 void *NetGetGameData(
1762 	void)
1763 {
1764 	assert(netState!=netUninitialized && netState!=netJoining);
1765 
1766 	return &topology->game_data;
1767 }
1768 
1769 /* ZZZ addition:
1770 --------------------------
1771 NetSetupTopologyFromStarts
1772 --------------------------
1773 
1774         ---> array of starts
1775         ---> count of starts
1776 
1777         This should be called once (at most) per game, after deciding who's going to take over which player,
1778         before calling NetStart().
1779 */
1780 
NetSetupTopologyFromStarts(const player_start_data * inStartArray,short inStartCount)1781 void NetSetupTopologyFromStarts(const player_start_data* inStartArray, short inStartCount)
1782 {
1783 	NetPlayer thePlayers[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS];
1784         memcpy(thePlayers, topology->players, sizeof(thePlayers));
1785         for(int s = 0; s < inStartCount; s++)
1786         {
1787                 if(inStartArray[s].identifier == NONE)
1788                 {
1789                         // Is this really all I have to do here?
1790                         // NO, I need to set up the player name, color, team, etc. for transmission to others.
1791                         // That requires knowledge of the player_info or player_data (whichever one the net system
1792                         // uses for such transmission.)
1793                         topology->players[s].identifier = NONE;
1794                         // XXX ZZZ violation of separation of church and state - oops I mean net code and game code
1795                         player_info* thePlayerInfo = (player_info*) &topology->players[s].player_data;
1796                         strncpy(thePlayerInfo->name, inStartArray[s].name, MAX_NET_PLAYER_NAME_LENGTH);
1797                         thePlayerInfo->name[MAX_NET_PLAYER_NAME_LENGTH] = '\0';
1798                         thePlayerInfo->desired_color = 0; // currently unused
1799                         thePlayerInfo->team = inStartArray[s].team;
1800                         thePlayerInfo->color = inStartArray[s].color;
1801                         memset(thePlayerInfo->long_serial_number, 0, LONG_SERIAL_NUMBER_LENGTH);
1802                 }
1803                 else
1804                 {
1805                         int p;
1806                         for(p = 0; p < topology->player_count; p++)
1807                         {
1808                                 if(thePlayers[p].identifier == inStartArray[s].identifier)
1809                                         break;
1810                         }
1811 
1812                         assert(p != topology->player_count);
1813 
1814                         topology->players[s] = thePlayers[p];
1815                 }
1816         }
1817 
1818         topology->player_count = inStartCount;
1819 
1820         NetUpdateTopology();
1821 }
1822 
1823 /*
1824 ------------------
1825 NetEntityNotInGame
1826 ------------------
1827 
1828 	---> entity
1829 	---> address
1830 
1831 	<--- true if the entity is not in the game, false otherwise
1832 
1833 used to filter entities which have been added to a game out of the lookup list
1834 */
1835 
1836 /* if the given address is already added to our game, filter it out of the gather dialog */
NetEntityNotInGame(NetEntityName * entity,NetAddrBlock * address)1837 bool NetEntityNotInGame(
1838 	NetEntityName *entity,
1839 	NetAddrBlock *address)
1840 {
1841 	short player_index;
1842 	bool valid= true;
1843 
1844 	(void) (entity);
1845 
1846 	for (player_index=0;player_index<topology->player_count;++player_index)
1847 	{
1848 		NetAddrBlock *player_address= &topology->players[player_index].dspAddress;
1849 
1850 		if (address->host == player_address->host && address->port == player_address->port)
1851 		{
1852 			valid= false;
1853 			break;
1854 		}
1855 	}
1856 
1857 	return valid;
1858 }
1859 
1860 
1861 
1862 
1863 /* ---------- private code */
1864 
1865 void
NetDDPPacketHandler(DDPPacketBufferPtr packet)1866 NetDDPPacketHandler(DDPPacketBufferPtr packet)
1867 {
1868 	sCurrentGameProtocol->PacketHandler(packet);
1869 }
1870 
1871 
1872 
1873 /*
1874 local player initializers
1875 */
1876 
NetInitializeTopology(void * game_data,short game_data_size,void * player_data,short player_data_size)1877 static void NetInitializeTopology(
1878 	void *game_data,
1879 	short game_data_size,
1880 	void *player_data,
1881 	short player_data_size)
1882 {
1883 	NetPlayerPtr local_player;
1884 
1885 	assert(player_data_size>=0&&player_data_size<MAXIMUM_PLAYER_DATA_SIZE);
1886 	assert(game_data_size>=0&&game_data_size<MAXIMUM_GAME_DATA_SIZE);
1887 
1888 	/* initialize the local player (assume we�re index zero, identifier zero) */
1889 	localPlayerIndex= localPlayerIdentifier= 0;
1890 	local_player= topology->players + localPlayerIndex;
1891 	local_player->identifier= localPlayerIdentifier;
1892 	local_player->net_dead= false;
1893 	local_player->stream_id = 0;
1894 
1895 	NetLocalAddrBlock(&local_player->dspAddress, GAME_PORT);
1896 	NetLocalAddrBlock(&local_player->ddpAddress, ddpSocket);
1897 	if (player_data_size > 0)
1898 		memcpy(&local_player->player_data, player_data, player_data_size);
1899 
1900 	/* initialize the network topology (assume we�re the only player) */
1901 	topology->player_count= 1;
1902 	topology->nextIdentifier= 1;
1903 	if (game_data_size > 0)
1904 		memcpy(&topology->game_data, game_data, game_data_size);
1905 	gameSessionIdentifier.clear();
1906 }
1907 
NetLocalAddrBlock(NetAddrBlock * address,short socketNumber)1908 static void NetLocalAddrBlock(
1909 	NetAddrBlock *address,
1910 	short socketNumber)
1911 {
1912 
1913 	address->host = 0x7f000001;	//!! XXX (ZZZ) yeah, that's really bad.
1914 	address->port = socketNumber;	// OTOH, I guess others are set up to "stuff" the address they actually saw for us instead of
1915 					// this, anyway... right??  So maybe it's not that big a deal.......
1916 }
1917 
1918 
1919 
NetUpdateTopology(void)1920 static void NetUpdateTopology(
1921 	void)
1922 {
1923 	/* recalculate localPlayerIndex */
1924 	for (localPlayerIndex=0;localPlayerIndex<topology->player_count;++localPlayerIndex)
1925 	{
1926 		if (topology->players[localPlayerIndex].identifier==localPlayerIdentifier) break;
1927 	}
1928 #ifdef DEBUG
1929 	if (localPlayerIndex==topology->player_count) fdprintf("couldn't find my identifier: %p", (void*)topology);
1930 #endif
1931 }
1932 
1933 
1934 
NetSetSelfSend(bool on)1935 static bool NetSetSelfSend(
1936 	bool on)
1937 {
1938 	return false;
1939 }
1940 
1941 
1942 
1943 /* ------ this needs to let the gatherer keep going if there was an error.. */
1944 /* ����Marathon Specific Code ��� */
1945 /* Returns error code.. */
1946 // ZZZ annotation: this function doesn't seem to belong here - maybe more like interface.cpp?
NetChangeMap(struct entry_point * entry)1947 bool NetChangeMap(
1948 	struct entry_point *entry)
1949 {
1950 	byte   *wad= NULL;
1951 	int32   length;
1952 	bool success= true;
1953 
1954 	/* If the guy that was the server died, and we are trying to change levels, we lose */
1955         // ZZZ: if we used the parent_wad_checksum stuff to locate the containing Map file,
1956         // this would be the case somewhat less frequently, probably...
1957 	if(localPlayerIndex==sServerPlayerIndex && localPlayerIndex != 0) {
1958 	  logError("server died while trying to get another level");
1959 	  success= false;
1960 	} else {
1961 	  // being the server, we must send out the map to everyone.
1962 	  if(localPlayerIndex==sServerPlayerIndex) {
1963 	    wad = (unsigned char *)get_map_for_net_transfer(entry);
1964 	    assert(wad);
1965 
1966 	    length= get_net_map_data_length(wad);
1967 	    NetDistributeGameDataToAllPlayers(wad, length, true);
1968 	  } else { // wait for de damn map.
1969 	      wad = NetReceiveGameData(true);
1970 	      if(!wad) {
1971 		alert_user(infoError, strNETWORK_ERRORS, netErrCouldntReceiveMap, 0);
1972 		success= false;
1973 
1974 	      }
1975 	  }
1976 
1977 	  /* Now load the level.. */
1978 	  if (wad)
1979 	    {
1980 	      /* Note that this frees the wad as well!! */
1981 	      process_net_map_data(wad);
1982 	    }
1983 	}
1984 
1985 	return success;
1986 }
1987 
DeferredScriptSend(byte * data,size_t length)1988 void DeferredScriptSend (byte* data, size_t length)
1989 {
1990         if (deferred_script_data != NULL) {
1991             delete [] deferred_script_data;
1992             deferred_script_data = NULL;
1993         }
1994         deferred_script_data = data;
1995         deferred_script_length = length;
1996 }
1997 
SetNetscriptStatus(bool status)1998 void SetNetscriptStatus (bool status)
1999 {
2000         do_netscript = status;
2001 }
2002 
2003 // ZZZ this "ought" to distribute to all players simultaneously (by interleaving send calls)
2004 // in case the server bandwidth is much greater than the others' bandwidths.  But that would
2005 // take a fair amount of reworking of the streaming system, which only groks talking with one
2006 // machine at a time.
NetDistributeGameDataToAllPlayers(byte * wad_buffer,int32 wad_length,bool do_physics)2007 OSErr NetDistributeGameDataToAllPlayers(byte *wad_buffer,
2008 					int32 wad_length,
2009 					bool do_physics)
2010 {
2011 	short playerIndex, message_id;
2012 	OSErr error= noErr;
2013 	int32 total_length;
2014 	uint32 initial_ticks= machine_tick_count();
2015 	short physics_message_id;
2016 	byte *physics_buffer = NULL;
2017 	int32 physics_length;
2018 
2019 	message_id= (topology->player_count==2) ? (_distribute_map_single) : (_distribute_map_multiple);
2020 	physics_message_id= (topology->player_count==2) ? (_distribute_physics_single) : (_distribute_physics_multiple);
2021 	open_progress_dialog(physics_message_id);
2022 
2023 	/* For updating our progress bar.. */
2024 	total_length= (topology->player_count-1)*wad_length;
2025 
2026 	/* Get the physics */
2027 	if(do_physics)
2028 		physics_buffer= (unsigned char *)get_network_physics_buffer(&physics_length);
2029 
2030 	// build a list of players to send to
2031 	std::vector<CommunicationsChannel *> channels;
2032 
2033 	// also a list of who and who can not take compressed data
2034 	std::vector<CommunicationsChannel *> zipCapableChannels;
2035 	std::vector<CommunicationsChannel *> zipIncapableChannels;
2036 	for (playerIndex = 0; playerIndex < topology->player_count; playerIndex++)
2037 	{
2038 		NetPlayer player = topology->players[playerIndex];
2039 
2040 		/* If the player is not net dead. */
2041 		// ZZZ: and is not going to be a zombie and is not us
2042 
2043 		if (!player.net_dead && player.identifier != NONE && playerIndex != localPlayerIndex)
2044 		{
2045 			Client *client = connections_to_clients[player.stream_id];
2046 			channels.push_back(client->channel);
2047 			if (client->capabilities[Capabilities::kZippedData] >= my_capabilities[Capabilities::kZippedData])
2048 			{
2049 				zipCapableChannels.push_back(client->channel);
2050 			}
2051 			else
2052 			{
2053 				zipIncapableChannels.push_back(client->channel);
2054 			}
2055 
2056 		}
2057 
2058 	}
2059 
2060 	set_progress_dialog_message(message_id);
2061 	reset_progress_bar();
2062 
2063 	if (physics_buffer)
2064 	{
2065 		if (zipCapableChannels.size())
2066 		{
2067 			ZippedPhysicsMessage zippedPhysicsMessage(physics_buffer, physics_length);
2068 			std::unique_ptr<UninflatedMessage> uninflatedMessage(zippedPhysicsMessage.deflate());
2069 			std::for_each(zipCapableChannels.begin(), zipCapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, *uninflatedMessage));
2070 		}
2071 
2072 		if (zipIncapableChannels.size())
2073 		{
2074 			PhysicsMessage physicsMessage(physics_buffer, physics_length);
2075 			std::for_each(zipIncapableChannels.begin(), zipIncapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, physicsMessage));
2076 		}
2077 	}
2078 
2079 	{
2080 		// send zipped map to anyone who can accept it
2081 		if (zipCapableChannels.size())
2082 		{
2083 			ZippedMapMessage zippedMapMessage(wad_buffer, wad_length);
2084 			// zipped messages are compressed when deflated
2085 			// since we may have to send this to multiple joiners,
2086 			// deflate it now so that compression only happens once
2087 			std::unique_ptr<UninflatedMessage> uninflatedMessage(zippedMapMessage.deflate());
2088 			std::for_each(zipCapableChannels.begin(), zipCapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, *uninflatedMessage));
2089 		}
2090 
2091 		if (zipIncapableChannels.size())
2092 		{
2093 			MapMessage mapMessage(wad_buffer, wad_length);
2094 			std::for_each(zipIncapableChannels.begin(), zipIncapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, mapMessage));
2095 		}
2096 	}
2097 
2098 	if (do_netscript)
2099 	{
2100 		if (zipCapableChannels.size())
2101 		{
2102 			ZippedLuaMessage zippedLuaMessage(deferred_script_data, deferred_script_length);
2103 			std::unique_ptr<UninflatedMessage> uninflatedMessage(zippedLuaMessage.deflate());
2104 			std::for_each(zipCapableChannels.begin(), zipCapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, *uninflatedMessage));
2105 		}
2106 
2107 		if (zipIncapableChannels.size())
2108 		{
2109 			LuaMessage luaMessage(deferred_script_data, deferred_script_length);
2110 			std::for_each(zipIncapableChannels.begin(), zipIncapableChannels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, luaMessage));
2111 		}
2112 	}
2113 
2114 	{
2115 		EndGameDataMessage endGameDataMessage;
2116 		std::for_each(channels.begin(), channels.end(), boost::bind(&CommunicationsChannel::enqueueOutgoingMessage, _1, endGameDataMessage));
2117 	}
2118 
2119 	CommunicationsChannel::multipleFlushOutgoingMessages(channels, false, 30000, 30000);
2120 
2121 	for (playerIndex = 0; playerIndex < topology->player_count; playerIndex++) {
2122 		if (playerIndex != localPlayerIndex) {
2123 			connections_to_clients[topology->players[playerIndex].stream_id]->state = Client::_ingame;
2124 		}
2125 	}
2126 
2127 
2128 
2129 	if (error) { // ghs: nothing above returns an error at the moment,
2130 		// but I'll leave so you know what error could be displayed
2131 		alert_user(infoError, strNETWORK_ERRORS, netErrCouldntDistribute, error);
2132 	} else if  (machine_tick_count()-initial_ticks>uint32(topology->player_count*MAP_TRANSFER_TIME_OUT)) {
2133 		alert_user(infoError, strNETWORK_ERRORS, netErrWaitedTooLongForMap, error);
2134 		error= 1;
2135 	}
2136 
2137 	if (!error) {
2138 		/* Process the physics file & frees it!.. */
2139 		if (physics_buffer)
2140 			process_network_physics_model(physics_buffer);
2141 
2142 		draw_progress_bar(total_length, total_length);
2143 
2144 #ifdef HAVE_LUA
2145 		if (do_netscript) {
2146 			LoadLuaScript ((char*)deferred_script_data, deferred_script_length, _lua_netscript);
2147 		}
2148 #endif
2149 	}
2150 
2151 	close_progress_dialog();
2152 
2153 	return error;
2154 }
2155 
NetReceiveGameData(bool do_physics)2156 byte *NetReceiveGameData(bool do_physics)
2157 {
2158   byte *map_buffer= NULL;
2159 
2160   open_progress_dialog(_awaiting_map);
2161 
2162   // handlers will take care of all messages, and when they're done
2163   // the server will send us this:
2164   std::unique_ptr<EndGameDataMessage> endGameDataMessage(connection_to_server->receiveSpecificMessage<EndGameDataMessage>((Uint32) 60000, (Uint32) 30000));
2165   if (endGameDataMessage.get()) {
2166     // game data was received OK
2167 	  if (do_physics) {
2168       process_network_physics_model(handlerPhysicsBuffer);
2169       handlerPhysicsLength = 0;
2170       handlerPhysicsBuffer = NULL;
2171     }
2172 
2173     if (handlerMapLength > 0) {
2174       map_buffer = handlerMapBuffer;
2175       handlerMapBuffer = NULL;
2176       handlerMapLength = 0;
2177     }
2178 
2179     if (handlerLuaLength > 0) {
2180       do_netscript = true;
2181 #ifdef HAVE_LUA
2182       LoadLuaScript((char *) handlerLuaBuffer, handlerLuaLength, _lua_netscript);
2183 #endif
2184       handlerLuaBuffer = NULL;
2185       handlerLuaLength = 0;
2186     } else {
2187       do_netscript = false;
2188     }
2189 
2190     draw_progress_bar(10, 10);
2191     close_progress_dialog();
2192   } else {
2193     draw_progress_bar(10, 10);
2194     close_progress_dialog();
2195 
2196     if (handlerPhysicsLength > 0) {
2197       delete[] handlerPhysicsBuffer;
2198       handlerPhysicsBuffer = NULL;
2199       handlerPhysicsLength = 0;
2200     }
2201     if (handlerMapLength > 0) {
2202       delete[] handlerMapBuffer;
2203       handlerMapBuffer = NULL;
2204       handlerMapLength = 0;
2205     }
2206     if (handlerLuaLength > 0) {
2207       delete[] handlerLuaBuffer;
2208       handlerLuaBuffer = NULL;
2209       handlerLuaLength = 0;
2210     }
2211 
2212     alert_user(infoError, strNETWORK_ERRORS, netErrMapDistribFailed, 1);
2213   }
2214 
2215   return map_buffer;
2216 }
2217 
NetSetInitialParameters(short updates_per_packet,short update_latency)2218 void NetSetInitialParameters(
2219 	short updates_per_packet,
2220 	short update_latency)
2221 {
2222 //	initial_updates_per_packet= updates_per_packet;
2223 //	initial_update_latency= update_latency;
2224 }
2225 
2226 int32
NetGetNetTime(void)2227 NetGetNetTime(void)
2228 {
2229         return sCurrentGameProtocol->GetNetTime();
2230 }
2231 
2232 extern const NetworkStats& hub_stats(int player_index);
2233 
NetProcessMessagesInGame()2234 void NetProcessMessagesInGame() {
2235 	if (connection_to_server) {
2236 		connection_to_server->pump();
2237 		connection_to_server->dispatchIncomingMessages();
2238 	} else {
2239 		// update stats
2240 		if (sCurrentGameProtocol == static_cast<NetworkGameProtocol*>(&sStarGameProtocol) && last_network_stats_send + network_stats_send_period < machine_tick_count())
2241 		{
2242 			std::vector<NetworkStats> stats(topology->player_count);
2243 			for (int playerIndex = 0; playerIndex < topology->player_count; ++playerIndex)
2244 			{
2245 				stats[playerIndex] = hub_stats(playerIndex);
2246 			}
2247 
2248 			NetworkStatsMessage statsMessage(stats);
2249 			for (int playerIndex = 0; playerIndex < topology->player_count; ++playerIndex)
2250 			{
2251 				NetPlayer player = topology->players[playerIndex];
2252 				if (!player.net_dead && player.identifier != NONE && playerIndex != localPlayerIndex)
2253 				{
2254 					Client *client = connections_to_clients[player.stream_id];
2255 					if (client->capabilities[Capabilities::kNetworkStats] >= Capabilities::kNetworkStatsVersion)
2256 					{
2257 						client->channel->enqueueOutgoingMessage(statsMessage);
2258 					}
2259 				}
2260 			}
2261 
2262 			last_network_stats_send = machine_tick_count();
2263 		}
2264 
2265 		// pump chat messages
2266 		client_map_t::iterator it;
2267 		for (it = connections_to_clients.begin(); it != connections_to_clients.end(); it++) {
2268 			it->second->channel->pump();
2269 			it->second->channel->dispatchIncomingMessages();
2270 		}
2271 	}
2272 
2273 	if (gMetaserverClient && gMetaserverClient->isConnected())
2274 		gMetaserverClient->pump();
2275 }
2276 
2277 // If a potential joiner has connected to us, handle em
NetCheckForNewJoiner(prospective_joiner_info & info)2278 bool NetCheckForNewJoiner (prospective_joiner_info &info)
2279 {
2280 	CommunicationsChannel *new_joiner = server->newIncomingConnection();
2281 
2282 	if (new_joiner) {
2283 
2284 		new_joiner->setMessageInflater(inflater);
2285 		Client *client = new Client(new_joiner);
2286 		connections_to_clients[next_stream_id] = client;
2287 		next_stream_id++;
2288 
2289 		HelloMessage helloMessage(kNetworkSetupProtocolID);
2290 		new_joiner->enqueueOutgoingMessage(helloMessage);
2291 	}
2292 
2293 	{
2294 		client_map_t::iterator it = connections_to_clients.begin();
2295 		while (it != connections_to_clients.end()) {
2296 			if (it->second->channel->isConnected()) {
2297 				it->second->channel->pump();
2298 				it->second->channel->dispatchIncomingMessages();
2299 				++it;
2300 			} else {
2301 				it->second->drop();
2302 				delete connections_to_clients[it->first];
2303 				connections_to_clients.erase(it++);
2304 			}
2305 		}
2306 	}
2307 
2308 	// now check to see if any one has actually connected
2309 	client_map_t::iterator it = connections_to_clients.begin();
2310 	while (it != connections_to_clients.end()) {
2311 		if (it->second->state == Client::_connected_but_not_yet_shown) {
2312 			info.stream_id = it->first;
2313 			strncpy(info.name, it->second->name, MAX_NET_PLAYER_NAME_LENGTH);
2314 			it->second->state = Client::_connected;
2315 			info.gathering = false;
2316 			return true;
2317 		} else if (it->second->state == Client::_disconnect) {
2318 			connections_to_clients[it->first]->drop();
2319 			delete connections_to_clients[it->first];
2320 			connections_to_clients.erase(it++);
2321 		}
2322 		else
2323 		{
2324 			++it;
2325 		}
2326 	}
2327 
2328 	return false;
2329 }
2330 
2331 /* check for messages from gather nodes; returns new state */
NetUpdateJoinState(void)2332 short NetUpdateJoinState(
2333 			 void)
2334 {
2335   logContext("updating network join status");
2336 
2337   short newState= netState;
2338 
2339   switch (netState)
2340     {
2341     case netConnecting:
2342       // trying to connect to gatherer
2343       // jkvw: Here's what's up - We want to be able to click join before gatherer
2344       //	 has started gathering.  So after a join click we attempt a connection
2345       //	 every five seconds.  This is fine so long as remote computer quickly
2346       //	 refuses connection each time we poll; the dialog will remain responsive.
2347       //	 It's possible that a connection attempt will take a long time, however,
2348       //	 so we look for that and abort dialog when it happens.
2349 
2350 	    // ghs: it's possible the guy hasn't opened his firewall yet, so always retry
2351 	    if (machine_tick_count() >= next_join_attempt) {
2352 		    if(host_address_specified)
2353 		    {
2354 			    if (!server_nbc)
2355 			    {
2356 				    server_nbc = ConnectPool::instance()->connect(host_address);
2357 			    }
2358 		    }
2359 
2360 		    if (server_nbc)
2361 		    {
2362 			    if (server_nbc->status() == NonblockingConnect::Connected)
2363 			    {
2364 				    newState = netJoining;
2365 				    handlerState = netAwaitingHello;
2366 				    connection_to_server = server_nbc->release();
2367 				    ConnectPool::instance()->abandon(server_nbc);
2368 				    server_nbc = 0;
2369 				    if (!connection_to_server->isConnected())
2370 				    {
2371 					    newState= netJoinErrorOccurred;
2372 					    alert_user(infoError, strNETWORK_ERRORS, netErrCouldntJoin, 3);
2373 				    }
2374 				    else
2375 				    {
2376 					    connection_to_server->setMessageInflater(inflater);
2377 					    connection_to_server->setMessageHandler(joinDispatcher);
2378 				    }
2379 			    }
2380 			    else if (server_nbc->status() == NonblockingConnect::ResolutionFailed)
2381 			    {
2382 				    // name resolution error isn't recoverable
2383 				    ConnectPool::instance()->abandon(server_nbc);
2384 				    server_nbc = 0;
2385 
2386 				    newState = netJoinErrorOccurred;
2387 				    alert_user(infoError, strNETWORK_ERRORS, netErrCouldntResolve, 0);
2388 			    }
2389 			    else if (server_nbc->status() == NonblockingConnect::ConnectFailed)
2390 			    {
2391 				    assert(host_address_specified);
2392 				    if (nbc_is_resolving)
2393 					    host_address = server_nbc->address();
2394 				    nbc_is_resolving = false;
2395 				    ConnectPool::instance()->abandon(server_nbc);
2396 				    server_nbc = ConnectPool::instance()->connect(host_address);
2397 			    }
2398 		    }
2399 
2400 		    next_join_attempt = machine_tick_count() + 5*MACHINE_TICKS_PER_SECOND;
2401 	    }
2402 	    break;
2403 
2404     case netJoining:	// waiting to be gathered
2405       if (!connection_to_server->isConnected ()) {
2406 	newState = netJoinErrorOccurred;
2407 	alert_user(infoError, strNETWORK_ERRORS, netErrLostConnection, 0);
2408       } else {
2409 	connection_to_server->pump();
2410 	connection_to_server->dispatchIncomingMessages();
2411 	if (handlerState == netWaiting) {
2412 	  newState= netWaiting;
2413 	} else if (handlerState == netJoinErrorOccurred) {
2414 	  newState= netJoinErrorOccurred;
2415 	}
2416       }
2417       break;
2418       // netJoining
2419 
2420     case netWaiting:	// have been gathered, waiting for other players / game start
2421       if (!connection_to_server->isConnected ()) {
2422 	newState = netJoinErrorOccurred;
2423 	alert_user(infoError, strNETWORK_ERRORS, netErrLostConnection, 0);
2424       } else {
2425 	handlerState = netWaiting;
2426 	connection_to_server->pump();
2427 	connection_to_server->dispatchOneIncomingMessage();
2428 	if (handlerState != netWaiting) {
2429 	  newState = handlerState;
2430 	}
2431       }
2432       break;
2433       // netWaiting
2434 
2435     default:
2436       newState= NONE;
2437       break;
2438     }
2439 
2440   /* return netPlayerAdded to tell the caller to refresh his topology, but don�t change netState to that */
2441   // ZZZ: similar behavior for netChatMessageReceived and netStartingResumeGame
2442   if (newState!=netPlayerAdded && newState!=netPlayerDropped && newState!=netPlayerChanged && newState != netChatMessageReceived && newState != netStartingResumeGame && newState != NONE)
2443     netState= newState;
2444 
2445   // ZZZ: netStartingResumeGame is used as a return value only; the corresponding netState is netStartingUp.
2446   if(newState == netStartingResumeGame)
2447     netState = netStartingUp;
2448 
2449   return newState;
2450 }
2451 
NetGatherPlayer(const prospective_joiner_info & player,CheckPlayerProcPtr check_player)2452 int NetGatherPlayer(const prospective_joiner_info &player,
2453   CheckPlayerProcPtr check_player)
2454 {
2455   assert(netState == netGathering);
2456   assert(topology->player_count < MAXIMUM_NUMBER_OF_NETWORK_PLAYERS);
2457 
2458   Client::check_player = check_player;
2459 
2460   JoinPlayerMessage joinPlayerMessage(topology->nextIdentifier++);
2461   connections_to_clients[player.stream_id]->channel->enqueueOutgoingMessage(joinPlayerMessage);
2462   connections_to_clients[player.stream_id]->state = Client::_awaiting_accept_join;
2463 
2464   /*
2465   // reject a player if he can't handle our script demands
2466   ScriptMessage scriptMessage(_netscript_query_message);
2467   connections_to_clients[player.stream_id]->channel->enqueueOutgoingMessage(scriptMessage);
2468   connections_to_clients[player.stream_id]->state = Client::_awaiting_script_message;
2469   */
2470 
2471   // lie to the network code and say we gathered successfully
2472   return kGatherPlayerSuccessful;
2473 }
2474 
NetHandleUngatheredPlayer(prospective_joiner_info ungathered_player)2475 void NetHandleUngatheredPlayer (prospective_joiner_info ungathered_player)
2476 {
2477   // Drop connection of ungathered player
2478   delete connections_to_clients[ungathered_player.stream_id];
2479   connections_to_clients.erase(ungathered_player.stream_id);
2480 }
2481 
2482 /*
2483 ---------------------
2484 NetDistributeTopology
2485 ---------------------
2486 
2487 	<--- error
2488 
2489 connect to everyone�s dspAddress and give them the latest copy of the network topology.  this
2490 used to be NetStart() and it used to connect all upring and downring ADSP connections.
2491 */
NetDistributeTopology(short tag)2492 static void NetDistributeTopology(
2493 	short tag)
2494 {
2495 	short playerIndex;
2496 
2497 	assert(netState==netGathering);
2498 
2499 	topology->tag= tag;
2500 
2501 	TopologyMessage topologyMessage(topology);
2502 
2503 	for (playerIndex=0; playerIndex<topology->player_count; ++playerIndex)
2504 	  {
2505 	    // ZZZ: skip players with identifier NONE - they don't really exist... also skip ourselves.
2506 	    if(topology->players[playerIndex].identifier != NONE && playerIndex != localPlayerIndex)
2507 	      {
2508 		CommunicationsChannel *channel = connections_to_clients[topology->players[playerIndex].stream_id]->channel;
2509 		channel->enqueueOutgoingMessage(topologyMessage);
2510 	      }
2511 	  }
2512 }
2513 
NetGetPlayerADSPAddress(short player_index)2514 NetAddrBlock *NetGetPlayerADSPAddress(
2515 	short player_index)
2516 {
2517 	return &topology->players[player_index].dspAddress;
2518 }
2519 
NetAllowCrosshair()2520 bool NetAllowCrosshair() {
2521   return (dynamic_world->player_count == 1 ||
2522 	  (dynamic_world->game_information.cheat_flags & _allow_crosshair));
2523 }
2524 
NetAllowTunnelVision()2525 bool NetAllowTunnelVision() {
2526   return (dynamic_world->player_count == 1 ||
2527 	  dynamic_world->game_information.cheat_flags & _allow_tunnel_vision);
2528 }
2529 
NetAllowBehindview()2530 bool NetAllowBehindview() {
2531   return (dynamic_world->player_count == 1 ||
2532 	  dynamic_world->game_information.cheat_flags & _allow_behindview);
2533 }
2534 
NetAllowCarnageMessages()2535 bool NetAllowCarnageMessages() {
2536 	return (dynamic_world->player_count == 1 ||
2537 		!(dynamic_world->game_information.cheat_flags & _disable_carnage_messages));
2538 }
2539 
NetAllowSavingLevel()2540 bool NetAllowSavingLevel() {
2541 	return (dynamic_world->player_count == 1 ||
2542 		localPlayerIndex == sServerPlayerIndex ||
2543 		!(dynamic_world->game_information.cheat_flags & _disable_saving_level));
2544 
2545 }
2546 
NetAllowOverlayMap()2547 bool NetAllowOverlayMap() {
2548 	return (dynamic_world->player_count == 1 ||
2549 			(dynamic_world->game_information.cheat_flags & _allow_overlay_map));
2550 }
2551 
2552 
2553 extern int32 spoke_latency();
2554 
NetGetLatency()2555 int32 NetGetLatency() {
2556 	if (sCurrentGameProtocol == static_cast<NetworkGameProtocol*>(&sStarGameProtocol) && connection_to_server) {
2557 		return spoke_latency();
2558 	} else {
2559 		return NetworkStats::invalid;
2560 	}
2561 }
2562 
NetGetStats(int player_index)2563 const NetworkStats& NetGetStats(int player_index)
2564 {
2565 	if (sCurrentGameProtocol == static_cast<NetworkGameProtocol*>(&sStarGameProtocol))
2566 	{
2567 		if (connection_to_server)
2568 		{
2569 			if (player_index < sNetworkStats.size())
2570 			{
2571 				return sNetworkStats[player_index];
2572 			}
2573 			else
2574 			{
2575 				return sInvalidStats;
2576 			}
2577 		}
2578 		else
2579 		{
2580 			return hub_stats(player_index);
2581 		}
2582 	}
2583 	else
2584 	{
2585 		return sInvalidStats;
2586 	}
2587 }
2588 
NetGetUnconfirmedActionFlagsCount()2589 int32 NetGetUnconfirmedActionFlagsCount()
2590 {
2591 	assert (sCurrentGameProtocol);
2592 	return sCurrentGameProtocol->GetUnconfirmedActionFlagsCount();
2593 }
2594 
NetGetUnconfirmedActionFlag(int32 offset)2595 uint32 NetGetUnconfirmedActionFlag(int32 offset)
2596 {
2597 	assert (sCurrentGameProtocol);
2598 	return sCurrentGameProtocol->PeekUnconfirmedActionFlag(offset);
2599 }
2600 
NetUpdateUnconfirmedActionFlags()2601 void NetUpdateUnconfirmedActionFlags()
2602 {
2603 	assert (sCurrentGameProtocol);
2604 	return sCurrentGameProtocol->UpdateUnconfirmedActionFlags();
2605 }
2606 
2607 #endif // !defined(DISABLE_NETWORKING)
2608 
2609