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