改行コード修正
@@ -1,531 +1,531 @@ | ||
1 | -/* | |
2 | - * network_dialogs.h | |
3 | - * | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * | |
23 | - * Sept 19, 2001 (Woody Zenfell): split this file away from network_dialogs.cpp for sharing | |
24 | - * Also made whatever simple changes were needed for it to compile/work. | |
25 | - * | |
26 | - * Sept-Nov 2001 (Woody Zenfell): added some identifiers and prototypes for carnage report | |
27 | - * and for better code sharing between the Mac and SDL versions. | |
28 | - | |
29 | -Feb 27, 2002 (Br'fin (Jeremy Parsons)): | |
30 | - Moved shared SDL hint address info here from network_dialogs_sdl.cpp | |
31 | - Added dialog item definitions for a Join by Host in the join dialog | |
32 | - | |
33 | -Mar 1, 2002 (Woody Zenfell): | |
34 | - SDL dialog uses new level-selection scheme; new interface based on level number, not menu index. | |
35 | - */ | |
36 | - | |
37 | -#ifndef NETWORK_DIALOGS_H | |
38 | -#define NETWORK_DIALOGS_H | |
39 | - | |
40 | -#include "player.h" // for MAXIMUM_NUMBER_OF_PLAYERS | |
41 | -#include "network.h" | |
42 | -#include "network_private.h" // for JoinerSeekingGathererAnnouncer | |
43 | -#include "FileHandler.h" | |
44 | -#include "network_metaserver.h" | |
45 | -#include "metaserver_dialogs.h" | |
46 | - | |
47 | -#include "shared_widgets.h" | |
48 | - | |
49 | -#include <string> | |
50 | - | |
51 | -#include <map> | |
52 | -#include <set> | |
53 | - | |
54 | -#ifdef USES_NIBS | |
55 | -const CFStringRef Window_Network_Distribute = CFSTR("Network_Distribute"); | |
56 | -#endif | |
57 | - | |
58 | -// ZZZ: Moved here so constants can be shared by Mac and SDL dialog code. | |
59 | -/* ------------------ enums */ | |
60 | -enum { | |
61 | - strNET_STATS_STRINGS= 153, | |
62 | - strKILLS_STRING= 0, | |
63 | - strDEATHS_STRING, | |
64 | - strSUICIDES_STRING, | |
65 | - strTOTALS_STRING, | |
66 | - strMONSTERS_STRING, | |
67 | - strTOTAL_KILLS_STRING, | |
68 | - strTOTAL_DEATHS_STRING, | |
69 | - strINCLUDING_SUICIDES_STRING, | |
70 | - strTEAM_TOTALS_STRING, | |
71 | - strFRIENDLY_FIRE_STRING, | |
72 | - strTOTAL_SCORES, | |
73 | - strTOTAL_TEAM_SCORES, | |
74 | -// ZZZ: added the following to support my postgame report | |
75 | - strTEAM_CARNAGE_STRING, | |
76 | - strKILLS_LEGEND, | |
77 | - strDEATHS_LEGEND, | |
78 | - strSUICIDES_LEGEND, | |
79 | - strFRIENDLY_FIRE_LEGEND | |
80 | -}; | |
81 | - | |
82 | -enum { | |
83 | - kNetworkGameTypesStringSetID = 146, | |
84 | - kEndConditionTypeStringSetID = 147, | |
85 | - kScoreLimitTypeStringSetID = 148, | |
86 | - kSingleOrNetworkStringSetID = 149 | |
87 | -}; | |
88 | - | |
89 | - | |
90 | -#ifdef USES_NIBS | |
91 | - | |
92 | -enum { | |
93 | - dlogNET_GAME_STATS= 5000, | |
94 | - // iGRAPH_POPUP moved from #2 because that is the "Cancel" value | |
95 | - iDAMAGE_STATS = 3, | |
96 | - iTOTAL_KILLS, | |
97 | - iTOTAL_DEATHS, | |
98 | - iGRAPH_POPUP | |
99 | -}; | |
100 | - | |
101 | -#else | |
102 | - | |
103 | -enum { | |
104 | - dlogNET_GAME_STATS= 5000, | |
105 | - iGRAPH_POPUP= 2, | |
106 | - iDAMAGE_STATS, | |
107 | - iTOTAL_KILLS, | |
108 | - iTOTAL_DEATHS | |
109 | -}; | |
110 | - | |
111 | -#endif | |
112 | - | |
113 | -enum /* All the different graph types */ | |
114 | -{ | |
115 | - _player_graph, | |
116 | - _total_carnage_graph, | |
117 | - _total_scores_graph, | |
118 | - _total_team_carnage_graph, | |
119 | - _total_team_scores_graph | |
120 | -}; | |
121 | - | |
122 | -enum { | |
123 | - _suicide_color, | |
124 | - _kill_color, | |
125 | - _death_color, | |
126 | - _score_color, | |
127 | - NUMBER_OF_NET_COLORS | |
128 | -}; | |
129 | - | |
130 | -/* SDL/TCP hinting info. JTP: moved here from network_dialogs_sdl.cpp */ | |
131 | -enum { | |
132 | - kJoinHintingAddressLength = 64, | |
133 | -}; | |
134 | - | |
135 | -#define strJOIN_DIALOG_MESSAGES 136 | |
136 | -enum /* join dialog string numbers */ | |
137 | -{ | |
138 | - _join_dialog_welcome_string, | |
139 | - _join_dialog_waiting_string, | |
140 | - _join_dialog_accepted_string | |
141 | -}; | |
142 | - | |
143 | -enum { | |
144 | - strSETUP_NET_GAME_MESSAGES= 141, | |
145 | - killLimitString= 0, | |
146 | - killsString, | |
147 | - flagPullsString, | |
148 | - flagsString, | |
149 | - pointLimitString, | |
150 | - pointsString, | |
151 | - // START Benad | |
152 | - timeOnBaseString, | |
153 | - minutesString | |
154 | - // END Benad | |
155 | -}; | |
156 | - | |
157 | -enum { | |
158 | - dlogGATHER= 10000, | |
159 | - iPLAYER_DISPLAY_AREA= 3, | |
160 | - iADD, | |
161 | - iNETWORK_LIST_BOX, | |
162 | - iPLAYER_LIST_TEXT =9, | |
163 | - iAUTO_GATHER = 19 | |
164 | -}; | |
165 | - | |
166 | -enum { | |
167 | - dlogJOIN= 10001, | |
168 | -#ifndef USES_NIBS | |
169 | - iJOIN= 1, | |
170 | -#else | |
171 | - iJOIN= 101, | |
172 | -#endif | |
173 | - // iPLAYER_DISPLAY_AREA = 3, | |
174 | - iJOIN_NAME= 4, | |
175 | - iJOIN_TEAM, | |
176 | - iJOIN_COLOR, | |
177 | - iJOIN_MESSAGES, | |
178 | - // Group line = 12 | |
179 | - // iJOIN_NETWORK_TYPE= 13, | |
180 | - iJOIN_BY_HOST = 14, | |
181 | - iJOIN_BY_HOST_LABEL, | |
182 | - iJOIN_BY_HOST_ADDRESS, | |
183 | - iJOIN_CHAT_ENTRY, | |
184 | - iJOIN_CHAT_CHOICE, | |
185 | - iJOIN_BY_METASERVER = 20 | |
186 | -}; | |
187 | - | |
188 | -enum { | |
189 | - dlogGAME_SETUP= 3000, | |
190 | - iNETWORK_SPEED= 3, | |
191 | - iENTRY_MENU, | |
192 | - iDIFFICULTY_MENU, | |
193 | - iMOTION_SENSOR_DISABLED, | |
194 | - iDYING_PUNISHED, | |
195 | - iBURN_ITEMS_ON_DEATH, | |
196 | - iREAL_TIME_SOUND, | |
197 | - iUNLIMITED_MONSTERS, | |
198 | - iFORCE_UNIQUE_TEAMS, | |
199 | - iRADIO_NO_TIME_LIMIT, | |
200 | - iRADIO_TIME_LIMIT, | |
201 | - iRADIO_KILL_LIMIT, | |
202 | - iTIME_LIMIT, | |
203 | - iKILL_LIMIT, | |
204 | - iREALTIME_NET_STATS, | |
205 | - iTEXT_KILL_LIMIT, | |
206 | - iGATHER_NAME= 21, | |
207 | - iGATHER_TEAM, | |
208 | - iSUICIDE_PUNISHED= 24, | |
209 | - iGAME_TYPE, | |
210 | - iGATHER_COLOR, | |
211 | - iUSE_SCRIPT= 28, | |
212 | - iCHOOSE_SCRIPT, | |
213 | - iTEXT_TIME_LIMIT= 35, | |
214 | - iMICROPHONE_TYPE, | |
215 | - iTEXT_SCRIPT_NAME, | |
216 | - iADVERTISE_GAME_ON_METASERVER = 39, | |
217 | - iCHOOSE_MAP, | |
218 | - iTEXT_MAP_NAME, | |
219 | - iALLOW_ZOOM, | |
220 | - iALLOW_CROSSHAIRS, | |
221 | - iALLOW_LARA_CROFT, | |
222 | - iGATHER_CHAT_ENTRY, | |
223 | - iGATHER_CHAT_CHOICE, | |
224 | - iUSE_UPNP, | |
225 | - iLATENCY_TOLERANCE, | |
226 | - iSNG_TABS = 400, | |
227 | - iSNG_GENERAL_TAB, | |
228 | - iSNG_STUFF_TAB | |
229 | -}; | |
230 | - | |
231 | -enum { | |
232 | - duration_no_time_limit = 0, | |
233 | - duration_time_limit, | |
234 | - duration_kill_limit | |
235 | -}; | |
236 | - | |
237 | -#ifdef USES_NIBS | |
238 | - | |
239 | -// Because otherwise it would be interpreted as a regular "OK" | |
240 | -const int iOK_SPECIAL = 101; | |
241 | - | |
242 | -// For player-display Data Browser control: | |
243 | -const OSType PlayerDisplay_Name = 'name'; | |
244 | - | |
245 | -// Signature of player-select buttons in player dialog: | |
246 | -const OSType StatsDisplay_Player = 'plyr'; | |
247 | - | |
248 | -#endif | |
249 | - | |
250 | - | |
251 | -/* ------------------ structures */ | |
252 | -struct net_rank | |
253 | -{ | |
254 | - short kills, deaths; | |
255 | - int32 ranking; | |
256 | - int32 game_ranking; | |
257 | - | |
258 | - short player_index; | |
259 | - short color; // only valid if player_index== NONE! | |
260 | - short friendly_fire_kills; | |
261 | -}; | |
262 | - | |
263 | -struct player_info; | |
264 | -struct game_info; | |
265 | - | |
266 | -#ifndef USES_NIBS | |
267 | -typedef DialogPtr NetgameOutcomeData; | |
268 | -#endif | |
269 | - | |
270 | -#ifdef USES_NIBS | |
271 | - | |
272 | -struct NetgameOutcomeData | |
273 | -{ | |
274 | - ControlRef SelectCtrl; | |
275 | - ControlRef DisplayCtrl; | |
276 | - | |
277 | - // Invisible, but hittable controls; | |
278 | - // their drawing is done by the drawing callback for DisplayCtrl | |
279 | - ControlRef PlayerButtonCtrls[MAXIMUM_NUMBER_OF_PLAYERS]; | |
280 | - | |
281 | - ControlRef KillsTextCtrl; | |
282 | - ControlRef DeathsTextCtrl; | |
283 | -}; | |
284 | - | |
285 | -#endif | |
286 | - | |
287 | -/* ---------------------- globals */ | |
288 | -extern struct net_rank rankings[MAXIMUM_NUMBER_OF_PLAYERS]; | |
289 | - | |
290 | - | |
291 | -//class MetaserverClient; | |
292 | -//class GlobalMetaserverChatNotificationAdapter; | |
293 | -class GatherDialog : public GatherCallbacks, public ChatCallbacks, public GlobalMetaserverChatNotificationAdapter | |
294 | -{ | |
295 | -public: | |
296 | -// Abstract factory; concrete type determined at link-time | |
297 | - static std::auto_ptr<GatherDialog> Create(); | |
298 | - | |
299 | - bool GatherNetworkGameByRunning (); | |
300 | - | |
301 | - virtual ~GatherDialog (); | |
302 | - | |
303 | - // Callbacks for network code; final methods | |
304 | - virtual void JoinSucceeded(const prospective_joiner_info* player); | |
305 | - virtual void JoiningPlayerDropped(const prospective_joiner_info* player); | |
306 | - virtual void JoinedPlayerDropped(const prospective_joiner_info* player); | |
307 | - virtual void JoinedPlayerChanged(const prospective_joiner_info* player); | |
308 | - | |
309 | - virtual void ReceivedMessageFromPlayer( | |
310 | - const char *player_name, | |
311 | - const char *message); | |
312 | - | |
313 | -protected: | |
314 | - GatherDialog(); | |
315 | - | |
316 | - virtual bool Run() = 0; | |
317 | - virtual void Stop(bool result) = 0; | |
318 | - | |
319 | - void idle (); | |
320 | - | |
321 | - void StartGameHit (); | |
322 | - | |
323 | - void update_ungathered_widget (); | |
324 | - | |
325 | - bool player_search (prospective_joiner_info& player); | |
326 | - bool gathered_player (const prospective_joiner_info& player); | |
327 | - | |
328 | - void sendChat (); | |
329 | - void chatTextEntered (char character); | |
330 | - void chatChoiceHit (); | |
331 | - | |
332 | - map<int, prospective_joiner_info> m_ungathered_players; | |
333 | - | |
334 | - ButtonWidget* m_cancelWidget; | |
335 | - ButtonWidget* m_startWidget; | |
336 | - | |
337 | - ToggleWidget* m_autogatherWidget; | |
338 | - | |
339 | - JoiningPlayerListWidget* m_ungatheredWidget; | |
340 | - PlayersInGameWidget* m_pigWidget; | |
341 | - | |
342 | - EditTextWidget* m_chatEntryWidget; | |
343 | - SelectorWidget* m_chatChoiceWidget; | |
344 | - ColorfulChatWidget* m_chatWidget; | |
345 | - | |
346 | - enum { kPregameChat = 0, kMetaserverChat }; | |
347 | -}; | |
348 | - | |
349 | - | |
350 | -class JoinDialog : public GlobalMetaserverChatNotificationAdapter, public ChatCallbacks | |
351 | -{ | |
352 | -public: | |
353 | - // Abstract factory; concrete type determined at link-time | |
354 | - static std::auto_ptr<JoinDialog> Create(); | |
355 | - | |
356 | - const int JoinNetworkGameByRunning(); | |
357 | - | |
358 | - virtual ~JoinDialog (); | |
359 | - | |
360 | -protected: | |
361 | - JoinDialog(); | |
362 | - | |
363 | - virtual void Run() = 0; | |
364 | - virtual void Stop() = 0; | |
365 | - | |
366 | - virtual void respondToJoinHit (); | |
367 | - | |
368 | - void gathererSearch (); | |
369 | - void attemptJoin (); | |
370 | - void changeColours (); | |
371 | - void getJoinAddressFromMetaserver (); | |
372 | - | |
373 | - // ChatCallbacks | |
374 | - virtual void ReceivedMessageFromPlayer(const char *player_name, const char *message); | |
375 | - | |
376 | - void sendChat (); | |
377 | - void chatTextEntered (char character); | |
378 | - void chatChoiceHit (); | |
379 | - | |
380 | - ButtonWidget* m_cancelWidget; | |
381 | - ButtonWidget* m_joinWidget; | |
382 | - | |
383 | - ButtonWidget* m_joinMetaserverWidget; | |
384 | - EditTextWidget* m_joinAddressWidget; | |
385 | - ToggleWidget* m_joinByAddressWidget; | |
386 | - | |
387 | - EditTextWidget* m_nameWidget; | |
388 | - SelectorWidget* m_colourWidget; | |
389 | - SelectorWidget* m_teamWidget; | |
390 | - | |
391 | - StaticTextWidget* m_messagesWidget; | |
392 | - | |
393 | - PlayersInGameWidget* m_pigWidget; | |
394 | - | |
395 | - EditTextWidget* m_chatEntryWidget; | |
396 | - SelectorWidget* m_chatChoiceWidget; | |
397 | - ColorfulChatWidget* m_chatWidget; | |
398 | - | |
399 | - BinderSet binders; | |
400 | - | |
401 | - enum { kPregameChat = 0, kMetaserverChat }; | |
402 | - | |
403 | - std::auto_ptr<JoinerSeekingGathererAnnouncer> join_announcer; | |
404 | - int join_result; | |
405 | - bool got_gathered; | |
406 | - | |
407 | - bool skipToMetaserver; | |
408 | -}; | |
409 | - | |
410 | - | |
411 | -bool network_game_setup(player_info *player_information, game_info *game_information, bool inResumingGame, bool& outAdvertiseGameOnMetaserver); | |
412 | - | |
413 | -class SetupNetgameDialog | |
414 | -{ | |
415 | -public: | |
416 | - // Abstract factory; concrete type determined at link-time | |
417 | - static std::auto_ptr<SetupNetgameDialog> Create(); | |
418 | - | |
419 | - bool SetupNetworkGameByRunning ( | |
420 | - player_info *player_information, | |
421 | - game_info *game_information, | |
422 | - bool ResumingGame, | |
423 | - bool& outAdvertiseGameOnMetaserver); | |
424 | - | |
425 | - virtual ~SetupNetgameDialog (); | |
426 | - | |
427 | -protected: | |
428 | - SetupNetgameDialog(); | |
429 | - | |
430 | - virtual bool Run () = 0; | |
431 | - virtual void Stop (bool result) = 0; | |
432 | - | |
433 | - virtual bool allLevelsAllowed () = 0; | |
434 | - bool m_allow_all_levels; | |
435 | - int m_old_game_type; | |
436 | - | |
437 | - void setupForUntimedGame (); | |
438 | - void setupForTimedGame (); | |
439 | - void setupForScoreGame (); | |
440 | - void limitTypeHit (); | |
441 | - void teamsHit (); | |
442 | - void setupForGameType (); | |
443 | - void gameTypeHit (); | |
444 | - void chooseMapHit (); | |
445 | - bool informationIsAcceptable (); | |
446 | - void okHit (); | |
447 | - | |
448 | - virtual void unacceptableInfo () = 0; | |
449 | - | |
450 | - ButtonWidget* m_cancelWidget; | |
451 | - ButtonWidget* m_okWidget; | |
452 | - | |
453 | - EditTextWidget* m_nameWidget; | |
454 | - SelectorWidget* m_colourWidget; | |
455 | - SelectorWidget* m_teamWidget; | |
456 | - | |
457 | - FileChooserWidget* m_mapWidget; | |
458 | - SelectorWidget* m_levelWidget; | |
459 | - SelectorWidget* m_gameTypeWidget; | |
460 | - SelectorWidget* m_difficultyWidget; | |
461 | - | |
462 | - SelectorWidget* m_limitTypeWidget; | |
463 | - EditNumberWidget* m_timeLimitWidget; | |
464 | - EditNumberWidget* m_scoreLimitWidget; | |
465 | - | |
466 | - ToggleWidget* m_aliensWidget; | |
467 | - ToggleWidget* m_allowTeamsWidget; | |
468 | - ToggleWidget* m_deadPlayersDropItemsWidget; | |
469 | - ToggleWidget* m_penalizeDeathWidget; | |
470 | - ToggleWidget* m_penalizeSuicideWidget; | |
471 | - | |
472 | - ToggleWidget* m_useMetaserverWidget; | |
473 | - | |
474 | - ToggleWidget* m_useScriptWidget; | |
475 | - FileChooserWidget* m_scriptWidget; | |
476 | - | |
477 | - ToggleWidget* m_allowMicWidget; | |
478 | - | |
479 | - ToggleWidget* m_liveCarnageWidget; | |
480 | - ToggleWidget* m_motionSensorWidget; | |
481 | - | |
482 | - ToggleWidget* m_zoomWidget; | |
483 | - ToggleWidget* m_crosshairWidget; | |
484 | - ToggleWidget* m_overlayWidget; | |
485 | - ToggleWidget* m_laraCroftWidget; | |
486 | - ToggleWidget* m_carnageMessagesWidget; | |
487 | - ToggleWidget* m_savingLevelWidget; | |
488 | - | |
489 | - ToggleWidget* m_useUpnpWidget; | |
490 | - SelectorWidget* m_latencyToleranceWidget; | |
491 | -}; | |
492 | - | |
493 | - | |
494 | - | |
495 | -extern void reassign_player_colors(short player_index, short num_players); | |
496 | - | |
497 | - | |
498 | - | |
499 | -// (Postgame Carnage Report routines) | |
500 | -extern short find_graph_mode(NetgameOutcomeData &outcome, short *index); | |
501 | -extern void draw_new_graph(NetgameOutcomeData &outcome); | |
502 | - | |
503 | -extern void draw_player_graph(NetgameOutcomeData &outcome, short index); | |
504 | -extern void get_net_color(short index, RGBColor *color); | |
505 | - | |
506 | -extern short calculate_max_kills(size_t num_players); | |
507 | -extern void draw_totals_graph(NetgameOutcomeData &outcome); | |
508 | -extern void calculate_rankings(struct net_rank *ranks, short num_players); | |
509 | -extern int rank_compare(void const *rank1, void const *rank2); | |
510 | -extern int team_rank_compare(void const *rank1, void const *ranks2); | |
511 | -extern int score_rank_compare(void const *rank1, void const *ranks2); | |
512 | -extern void draw_team_totals_graph(NetgameOutcomeData &outcome); | |
513 | -extern void draw_total_scores_graph(NetgameOutcomeData &outcome); | |
514 | -extern void draw_team_total_scores_graph(NetgameOutcomeData &outcome); | |
515 | -extern void update_carnage_summary(NetgameOutcomeData &outcome, struct net_rank *ranks, | |
516 | - short num_players, short suicide_index, bool do_totals, bool friendly_fire); | |
517 | - | |
518 | -// Routines | |
519 | -extern void menu_index_to_level_entry(short index, int32 entry_flags, struct entry_point *entry); | |
520 | -extern int menu_index_to_level_index (int menu_index, int32 entry_flags); | |
521 | -extern int level_index_to_menu_index(int level_index, int32 entry_flags); | |
522 | - | |
523 | -// (Postgame carnage report) | |
524 | -extern void draw_names(NetgameOutcomeData &outcome, struct net_rank *ranks, | |
525 | - short number_of_bars, short which_player); | |
526 | -extern void draw_kill_bars(NetgameOutcomeData &outcome, struct net_rank *ranks, short num_players, | |
527 | - short suicide_index, bool do_totals, bool friendly_fire); | |
528 | -extern void draw_score_bars(NetgameOutcomeData &outcome, struct net_rank *ranks, short bar_count); | |
529 | - | |
530 | - | |
531 | -#endif//NETWORK_DIALOGS_H | |
1 | +/* | |
2 | + * network_dialogs.h | |
3 | + * | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * | |
23 | + * Sept 19, 2001 (Woody Zenfell): split this file away from network_dialogs.cpp for sharing | |
24 | + * Also made whatever simple changes were needed for it to compile/work. | |
25 | + * | |
26 | + * Sept-Nov 2001 (Woody Zenfell): added some identifiers and prototypes for carnage report | |
27 | + * and for better code sharing between the Mac and SDL versions. | |
28 | + | |
29 | +Feb 27, 2002 (Br'fin (Jeremy Parsons)): | |
30 | + Moved shared SDL hint address info here from network_dialogs_sdl.cpp | |
31 | + Added dialog item definitions for a Join by Host in the join dialog | |
32 | + | |
33 | +Mar 1, 2002 (Woody Zenfell): | |
34 | + SDL dialog uses new level-selection scheme; new interface based on level number, not menu index. | |
35 | + */ | |
36 | + | |
37 | +#ifndef NETWORK_DIALOGS_H | |
38 | +#define NETWORK_DIALOGS_H | |
39 | + | |
40 | +#include "player.h" // for MAXIMUM_NUMBER_OF_PLAYERS | |
41 | +#include "network.h" | |
42 | +#include "network_private.h" // for JoinerSeekingGathererAnnouncer | |
43 | +#include "FileHandler.h" | |
44 | +#include "network_metaserver.h" | |
45 | +#include "metaserver_dialogs.h" | |
46 | + | |
47 | +#include "shared_widgets.h" | |
48 | + | |
49 | +#include <string> | |
50 | + | |
51 | +#include <map> | |
52 | +#include <set> | |
53 | + | |
54 | +#ifdef USES_NIBS | |
55 | +const CFStringRef Window_Network_Distribute = CFSTR("Network_Distribute"); | |
56 | +#endif | |
57 | + | |
58 | +// ZZZ: Moved here so constants can be shared by Mac and SDL dialog code. | |
59 | +/* ------------------ enums */ | |
60 | +enum { | |
61 | + strNET_STATS_STRINGS= 153, | |
62 | + strKILLS_STRING= 0, | |
63 | + strDEATHS_STRING, | |
64 | + strSUICIDES_STRING, | |
65 | + strTOTALS_STRING, | |
66 | + strMONSTERS_STRING, | |
67 | + strTOTAL_KILLS_STRING, | |
68 | + strTOTAL_DEATHS_STRING, | |
69 | + strINCLUDING_SUICIDES_STRING, | |
70 | + strTEAM_TOTALS_STRING, | |
71 | + strFRIENDLY_FIRE_STRING, | |
72 | + strTOTAL_SCORES, | |
73 | + strTOTAL_TEAM_SCORES, | |
74 | +// ZZZ: added the following to support my postgame report | |
75 | + strTEAM_CARNAGE_STRING, | |
76 | + strKILLS_LEGEND, | |
77 | + strDEATHS_LEGEND, | |
78 | + strSUICIDES_LEGEND, | |
79 | + strFRIENDLY_FIRE_LEGEND | |
80 | +}; | |
81 | + | |
82 | +enum { | |
83 | + kNetworkGameTypesStringSetID = 146, | |
84 | + kEndConditionTypeStringSetID = 147, | |
85 | + kScoreLimitTypeStringSetID = 148, | |
86 | + kSingleOrNetworkStringSetID = 149 | |
87 | +}; | |
88 | + | |
89 | + | |
90 | +#ifdef USES_NIBS | |
91 | + | |
92 | +enum { | |
93 | + dlogNET_GAME_STATS= 5000, | |
94 | + // iGRAPH_POPUP moved from #2 because that is the "Cancel" value | |
95 | + iDAMAGE_STATS = 3, | |
96 | + iTOTAL_KILLS, | |
97 | + iTOTAL_DEATHS, | |
98 | + iGRAPH_POPUP | |
99 | +}; | |
100 | + | |
101 | +#else | |
102 | + | |
103 | +enum { | |
104 | + dlogNET_GAME_STATS= 5000, | |
105 | + iGRAPH_POPUP= 2, | |
106 | + iDAMAGE_STATS, | |
107 | + iTOTAL_KILLS, | |
108 | + iTOTAL_DEATHS | |
109 | +}; | |
110 | + | |
111 | +#endif | |
112 | + | |
113 | +enum /* All the different graph types */ | |
114 | +{ | |
115 | + _player_graph, | |
116 | + _total_carnage_graph, | |
117 | + _total_scores_graph, | |
118 | + _total_team_carnage_graph, | |
119 | + _total_team_scores_graph | |
120 | +}; | |
121 | + | |
122 | +enum { | |
123 | + _suicide_color, | |
124 | + _kill_color, | |
125 | + _death_color, | |
126 | + _score_color, | |
127 | + NUMBER_OF_NET_COLORS | |
128 | +}; | |
129 | + | |
130 | +/* SDL/TCP hinting info. JTP: moved here from network_dialogs_sdl.cpp */ | |
131 | +enum { | |
132 | + kJoinHintingAddressLength = 64, | |
133 | +}; | |
134 | + | |
135 | +#define strJOIN_DIALOG_MESSAGES 136 | |
136 | +enum /* join dialog string numbers */ | |
137 | +{ | |
138 | + _join_dialog_welcome_string, | |
139 | + _join_dialog_waiting_string, | |
140 | + _join_dialog_accepted_string | |
141 | +}; | |
142 | + | |
143 | +enum { | |
144 | + strSETUP_NET_GAME_MESSAGES= 141, | |
145 | + killLimitString= 0, | |
146 | + killsString, | |
147 | + flagPullsString, | |
148 | + flagsString, | |
149 | + pointLimitString, | |
150 | + pointsString, | |
151 | + // START Benad | |
152 | + timeOnBaseString, | |
153 | + minutesString | |
154 | + // END Benad | |
155 | +}; | |
156 | + | |
157 | +enum { | |
158 | + dlogGATHER= 10000, | |
159 | + iPLAYER_DISPLAY_AREA= 3, | |
160 | + iADD, | |
161 | + iNETWORK_LIST_BOX, | |
162 | + iPLAYER_LIST_TEXT =9, | |
163 | + iAUTO_GATHER = 19 | |
164 | +}; | |
165 | + | |
166 | +enum { | |
167 | + dlogJOIN= 10001, | |
168 | +#ifndef USES_NIBS | |
169 | + iJOIN= 1, | |
170 | +#else | |
171 | + iJOIN= 101, | |
172 | +#endif | |
173 | + // iPLAYER_DISPLAY_AREA = 3, | |
174 | + iJOIN_NAME= 4, | |
175 | + iJOIN_TEAM, | |
176 | + iJOIN_COLOR, | |
177 | + iJOIN_MESSAGES, | |
178 | + // Group line = 12 | |
179 | + // iJOIN_NETWORK_TYPE= 13, | |
180 | + iJOIN_BY_HOST = 14, | |
181 | + iJOIN_BY_HOST_LABEL, | |
182 | + iJOIN_BY_HOST_ADDRESS, | |
183 | + iJOIN_CHAT_ENTRY, | |
184 | + iJOIN_CHAT_CHOICE, | |
185 | + iJOIN_BY_METASERVER = 20 | |
186 | +}; | |
187 | + | |
188 | +enum { | |
189 | + dlogGAME_SETUP= 3000, | |
190 | + iNETWORK_SPEED= 3, | |
191 | + iENTRY_MENU, | |
192 | + iDIFFICULTY_MENU, | |
193 | + iMOTION_SENSOR_DISABLED, | |
194 | + iDYING_PUNISHED, | |
195 | + iBURN_ITEMS_ON_DEATH, | |
196 | + iREAL_TIME_SOUND, | |
197 | + iUNLIMITED_MONSTERS, | |
198 | + iFORCE_UNIQUE_TEAMS, | |
199 | + iRADIO_NO_TIME_LIMIT, | |
200 | + iRADIO_TIME_LIMIT, | |
201 | + iRADIO_KILL_LIMIT, | |
202 | + iTIME_LIMIT, | |
203 | + iKILL_LIMIT, | |
204 | + iREALTIME_NET_STATS, | |
205 | + iTEXT_KILL_LIMIT, | |
206 | + iGATHER_NAME= 21, | |
207 | + iGATHER_TEAM, | |
208 | + iSUICIDE_PUNISHED= 24, | |
209 | + iGAME_TYPE, | |
210 | + iGATHER_COLOR, | |
211 | + iUSE_SCRIPT= 28, | |
212 | + iCHOOSE_SCRIPT, | |
213 | + iTEXT_TIME_LIMIT= 35, | |
214 | + iMICROPHONE_TYPE, | |
215 | + iTEXT_SCRIPT_NAME, | |
216 | + iADVERTISE_GAME_ON_METASERVER = 39, | |
217 | + iCHOOSE_MAP, | |
218 | + iTEXT_MAP_NAME, | |
219 | + iALLOW_ZOOM, | |
220 | + iALLOW_CROSSHAIRS, | |
221 | + iALLOW_LARA_CROFT, | |
222 | + iGATHER_CHAT_ENTRY, | |
223 | + iGATHER_CHAT_CHOICE, | |
224 | + iUSE_UPNP, | |
225 | + iLATENCY_TOLERANCE, | |
226 | + iSNG_TABS = 400, | |
227 | + iSNG_GENERAL_TAB, | |
228 | + iSNG_STUFF_TAB | |
229 | +}; | |
230 | + | |
231 | +enum { | |
232 | + duration_no_time_limit = 0, | |
233 | + duration_time_limit, | |
234 | + duration_kill_limit | |
235 | +}; | |
236 | + | |
237 | +#ifdef USES_NIBS | |
238 | + | |
239 | +// Because otherwise it would be interpreted as a regular "OK" | |
240 | +const int iOK_SPECIAL = 101; | |
241 | + | |
242 | +// For player-display Data Browser control: | |
243 | +const OSType PlayerDisplay_Name = 'name'; | |
244 | + | |
245 | +// Signature of player-select buttons in player dialog: | |
246 | +const OSType StatsDisplay_Player = 'plyr'; | |
247 | + | |
248 | +#endif | |
249 | + | |
250 | + | |
251 | +/* ------------------ structures */ | |
252 | +struct net_rank | |
253 | +{ | |
254 | + short kills, deaths; | |
255 | + int32 ranking; | |
256 | + int32 game_ranking; | |
257 | + | |
258 | + short player_index; | |
259 | + short color; // only valid if player_index== NONE! | |
260 | + short friendly_fire_kills; | |
261 | +}; | |
262 | + | |
263 | +struct player_info; | |
264 | +struct game_info; | |
265 | + | |
266 | +#ifndef USES_NIBS | |
267 | +typedef DialogPtr NetgameOutcomeData; | |
268 | +#endif | |
269 | + | |
270 | +#ifdef USES_NIBS | |
271 | + | |
272 | +struct NetgameOutcomeData | |
273 | +{ | |
274 | + ControlRef SelectCtrl; | |
275 | + ControlRef DisplayCtrl; | |
276 | + | |
277 | + // Invisible, but hittable controls; | |
278 | + // their drawing is done by the drawing callback for DisplayCtrl | |
279 | + ControlRef PlayerButtonCtrls[MAXIMUM_NUMBER_OF_PLAYERS]; | |
280 | + | |
281 | + ControlRef KillsTextCtrl; | |
282 | + ControlRef DeathsTextCtrl; | |
283 | +}; | |
284 | + | |
285 | +#endif | |
286 | + | |
287 | +/* ---------------------- globals */ | |
288 | +extern struct net_rank rankings[MAXIMUM_NUMBER_OF_PLAYERS]; | |
289 | + | |
290 | + | |
291 | +//class MetaserverClient; | |
292 | +//class GlobalMetaserverChatNotificationAdapter; | |
293 | +class GatherDialog : public GatherCallbacks, public ChatCallbacks, public GlobalMetaserverChatNotificationAdapter | |
294 | +{ | |
295 | +public: | |
296 | +// Abstract factory; concrete type determined at link-time | |
297 | + static std::auto_ptr<GatherDialog> Create(); | |
298 | + | |
299 | + bool GatherNetworkGameByRunning (); | |
300 | + | |
301 | + virtual ~GatherDialog (); | |
302 | + | |
303 | + // Callbacks for network code; final methods | |
304 | + virtual void JoinSucceeded(const prospective_joiner_info* player); | |
305 | + virtual void JoiningPlayerDropped(const prospective_joiner_info* player); | |
306 | + virtual void JoinedPlayerDropped(const prospective_joiner_info* player); | |
307 | + virtual void JoinedPlayerChanged(const prospective_joiner_info* player); | |
308 | + | |
309 | + virtual void ReceivedMessageFromPlayer( | |
310 | + const char *player_name, | |
311 | + const char *message); | |
312 | + | |
313 | +protected: | |
314 | + GatherDialog(); | |
315 | + | |
316 | + virtual bool Run() = 0; | |
317 | + virtual void Stop(bool result) = 0; | |
318 | + | |
319 | + void idle (); | |
320 | + | |
321 | + void StartGameHit (); | |
322 | + | |
323 | + void update_ungathered_widget (); | |
324 | + | |
325 | + bool player_search (prospective_joiner_info& player); | |
326 | + bool gathered_player (const prospective_joiner_info& player); | |
327 | + | |
328 | + void sendChat (); | |
329 | + void chatTextEntered (char character); | |
330 | + void chatChoiceHit (); | |
331 | + | |
332 | + map<int, prospective_joiner_info> m_ungathered_players; | |
333 | + | |
334 | + ButtonWidget* m_cancelWidget; | |
335 | + ButtonWidget* m_startWidget; | |
336 | + | |
337 | + ToggleWidget* m_autogatherWidget; | |
338 | + | |
339 | + JoiningPlayerListWidget* m_ungatheredWidget; | |
340 | + PlayersInGameWidget* m_pigWidget; | |
341 | + | |
342 | + EditTextWidget* m_chatEntryWidget; | |
343 | + SelectorWidget* m_chatChoiceWidget; | |
344 | + ColorfulChatWidget* m_chatWidget; | |
345 | + | |
346 | + enum { kPregameChat = 0, kMetaserverChat }; | |
347 | +}; | |
348 | + | |
349 | + | |
350 | +class JoinDialog : public GlobalMetaserverChatNotificationAdapter, public ChatCallbacks | |
351 | +{ | |
352 | +public: | |
353 | + // Abstract factory; concrete type determined at link-time | |
354 | + static std::auto_ptr<JoinDialog> Create(); | |
355 | + | |
356 | + const int JoinNetworkGameByRunning(); | |
357 | + | |
358 | + virtual ~JoinDialog (); | |
359 | + | |
360 | +protected: | |
361 | + JoinDialog(); | |
362 | + | |
363 | + virtual void Run() = 0; | |
364 | + virtual void Stop() = 0; | |
365 | + | |
366 | + virtual void respondToJoinHit (); | |
367 | + | |
368 | + void gathererSearch (); | |
369 | + void attemptJoin (); | |
370 | + void changeColours (); | |
371 | + void getJoinAddressFromMetaserver (); | |
372 | + | |
373 | + // ChatCallbacks | |
374 | + virtual void ReceivedMessageFromPlayer(const char *player_name, const char *message); | |
375 | + | |
376 | + void sendChat (); | |
377 | + void chatTextEntered (char character); | |
378 | + void chatChoiceHit (); | |
379 | + | |
380 | + ButtonWidget* m_cancelWidget; | |
381 | + ButtonWidget* m_joinWidget; | |
382 | + | |
383 | + ButtonWidget* m_joinMetaserverWidget; | |
384 | + EditTextWidget* m_joinAddressWidget; | |
385 | + ToggleWidget* m_joinByAddressWidget; | |
386 | + | |
387 | + EditTextWidget* m_nameWidget; | |
388 | + SelectorWidget* m_colourWidget; | |
389 | + SelectorWidget* m_teamWidget; | |
390 | + | |
391 | + StaticTextWidget* m_messagesWidget; | |
392 | + | |
393 | + PlayersInGameWidget* m_pigWidget; | |
394 | + | |
395 | + EditTextWidget* m_chatEntryWidget; | |
396 | + SelectorWidget* m_chatChoiceWidget; | |
397 | + ColorfulChatWidget* m_chatWidget; | |
398 | + | |
399 | + BinderSet binders; | |
400 | + | |
401 | + enum { kPregameChat = 0, kMetaserverChat }; | |
402 | + | |
403 | + std::auto_ptr<JoinerSeekingGathererAnnouncer> join_announcer; | |
404 | + int join_result; | |
405 | + bool got_gathered; | |
406 | + | |
407 | + bool skipToMetaserver; | |
408 | +}; | |
409 | + | |
410 | + | |
411 | +bool network_game_setup(player_info *player_information, game_info *game_information, bool inResumingGame, bool& outAdvertiseGameOnMetaserver); | |
412 | + | |
413 | +class SetupNetgameDialog | |
414 | +{ | |
415 | +public: | |
416 | + // Abstract factory; concrete type determined at link-time | |
417 | + static std::auto_ptr<SetupNetgameDialog> Create(); | |
418 | + | |
419 | + bool SetupNetworkGameByRunning ( | |
420 | + player_info *player_information, | |
421 | + game_info *game_information, | |
422 | + bool ResumingGame, | |
423 | + bool& outAdvertiseGameOnMetaserver); | |
424 | + | |
425 | + virtual ~SetupNetgameDialog (); | |
426 | + | |
427 | +protected: | |
428 | + SetupNetgameDialog(); | |
429 | + | |
430 | + virtual bool Run () = 0; | |
431 | + virtual void Stop (bool result) = 0; | |
432 | + | |
433 | + virtual bool allLevelsAllowed () = 0; | |
434 | + bool m_allow_all_levels; | |
435 | + int m_old_game_type; | |
436 | + | |
437 | + void setupForUntimedGame (); | |
438 | + void setupForTimedGame (); | |
439 | + void setupForScoreGame (); | |
440 | + void limitTypeHit (); | |
441 | + void teamsHit (); | |
442 | + void setupForGameType (); | |
443 | + void gameTypeHit (); | |
444 | + void chooseMapHit (); | |
445 | + bool informationIsAcceptable (); | |
446 | + void okHit (); | |
447 | + | |
448 | + virtual void unacceptableInfo () = 0; | |
449 | + | |
450 | + ButtonWidget* m_cancelWidget; | |
451 | + ButtonWidget* m_okWidget; | |
452 | + | |
453 | + EditTextWidget* m_nameWidget; | |
454 | + SelectorWidget* m_colourWidget; | |
455 | + SelectorWidget* m_teamWidget; | |
456 | + | |
457 | + FileChooserWidget* m_mapWidget; | |
458 | + SelectorWidget* m_levelWidget; | |
459 | + SelectorWidget* m_gameTypeWidget; | |
460 | + SelectorWidget* m_difficultyWidget; | |
461 | + | |
462 | + SelectorWidget* m_limitTypeWidget; | |
463 | + EditNumberWidget* m_timeLimitWidget; | |
464 | + EditNumberWidget* m_scoreLimitWidget; | |
465 | + | |
466 | + ToggleWidget* m_aliensWidget; | |
467 | + ToggleWidget* m_allowTeamsWidget; | |
468 | + ToggleWidget* m_deadPlayersDropItemsWidget; | |
469 | + ToggleWidget* m_penalizeDeathWidget; | |
470 | + ToggleWidget* m_penalizeSuicideWidget; | |
471 | + | |
472 | + ToggleWidget* m_useMetaserverWidget; | |
473 | + | |
474 | + ToggleWidget* m_useScriptWidget; | |
475 | + FileChooserWidget* m_scriptWidget; | |
476 | + | |
477 | + ToggleWidget* m_allowMicWidget; | |
478 | + | |
479 | + ToggleWidget* m_liveCarnageWidget; | |
480 | + ToggleWidget* m_motionSensorWidget; | |
481 | + | |
482 | + ToggleWidget* m_zoomWidget; | |
483 | + ToggleWidget* m_crosshairWidget; | |
484 | + ToggleWidget* m_overlayWidget; | |
485 | + ToggleWidget* m_laraCroftWidget; | |
486 | + ToggleWidget* m_carnageMessagesWidget; | |
487 | + ToggleWidget* m_savingLevelWidget; | |
488 | + | |
489 | + ToggleWidget* m_useUpnpWidget; | |
490 | + SelectorWidget* m_latencyToleranceWidget; | |
491 | +}; | |
492 | + | |
493 | + | |
494 | + | |
495 | +extern void reassign_player_colors(short player_index, short num_players); | |
496 | + | |
497 | + | |
498 | + | |
499 | +// (Postgame Carnage Report routines) | |
500 | +extern short find_graph_mode(NetgameOutcomeData &outcome, short *index); | |
501 | +extern void draw_new_graph(NetgameOutcomeData &outcome); | |
502 | + | |
503 | +extern void draw_player_graph(NetgameOutcomeData &outcome, short index); | |
504 | +extern void get_net_color(short index, RGBColor *color); | |
505 | + | |
506 | +extern short calculate_max_kills(size_t num_players); | |
507 | +extern void draw_totals_graph(NetgameOutcomeData &outcome); | |
508 | +extern void calculate_rankings(struct net_rank *ranks, short num_players); | |
509 | +extern int rank_compare(void const *rank1, void const *rank2); | |
510 | +extern int team_rank_compare(void const *rank1, void const *ranks2); | |
511 | +extern int score_rank_compare(void const *rank1, void const *ranks2); | |
512 | +extern void draw_team_totals_graph(NetgameOutcomeData &outcome); | |
513 | +extern void draw_total_scores_graph(NetgameOutcomeData &outcome); | |
514 | +extern void draw_team_total_scores_graph(NetgameOutcomeData &outcome); | |
515 | +extern void update_carnage_summary(NetgameOutcomeData &outcome, struct net_rank *ranks, | |
516 | + short num_players, short suicide_index, bool do_totals, bool friendly_fire); | |
517 | + | |
518 | +// Routines | |
519 | +extern void menu_index_to_level_entry(short index, int32 entry_flags, struct entry_point *entry); | |
520 | +extern int menu_index_to_level_index (int menu_index, int32 entry_flags); | |
521 | +extern int level_index_to_menu_index(int level_index, int32 entry_flags); | |
522 | + | |
523 | +// (Postgame carnage report) | |
524 | +extern void draw_names(NetgameOutcomeData &outcome, struct net_rank *ranks, | |
525 | + short number_of_bars, short which_player); | |
526 | +extern void draw_kill_bars(NetgameOutcomeData &outcome, struct net_rank *ranks, short num_players, | |
527 | + short suicide_index, bool do_totals, bool friendly_fire); | |
528 | +extern void draw_score_bars(NetgameOutcomeData &outcome, struct net_rank *ranks, short bar_count); | |
529 | + | |
530 | + | |
531 | +#endif//NETWORK_DIALOGS_H |
@@ -1,1111 +1,1111 @@ | ||
1 | -/* | |
2 | - * network_dialog_widgets_sdl.cpp | |
3 | - | |
4 | - Copyright (C) 2001 and beyond by Woody Zenfell, III | |
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 | - * Implementation of network-dialog-specific widgets in the SDL dialog system. | |
22 | - * | |
23 | - * Created by Woody Zenfell, III on Fri Sep 28 2001. | |
24 | - * | |
25 | - * Mar 1, 2002 (Woody Zenfell): Added new w_entry_point_selector widget. | |
26 | - */ | |
27 | - | |
28 | -#if !defined(DISABLE_NETWORKING) | |
29 | - | |
30 | -#include "network_dialog_widgets_sdl.h" | |
31 | - | |
32 | -#include "screen_drawing.h" | |
33 | -#include "sdl_fonts.h" | |
34 | -#include "interface.h" | |
35 | -#include "network.h" | |
36 | - | |
37 | -// these next are for playing with shape-drawing | |
38 | -#include "player.h" | |
39 | -#include "HUDRenderer.h" | |
40 | -#include "shell.h" | |
41 | -#include "collection_definition.h" | |
42 | - | |
43 | -// here are some for w_entry_point_selector | |
44 | -#include "preferences.h" | |
45 | -#include "screen.h" | |
46 | - | |
47 | -// for TS_GetCString, get shared ref rather than copying string. | |
48 | -#include "TextStrings.h" | |
49 | - | |
50 | -#include "TextLayoutHelper.h" | |
51 | - | |
52 | -#include <string> | |
53 | - | |
54 | - | |
55 | -// jkvw: I'm putting this here because we only really want it for find_item_index_in_vecotr, | |
56 | -// and of course we shouldn't be doing that anyway :). | |
57 | -bool operator==(const prospective_joiner_info &left, const prospective_joiner_info &right) | |
58 | -{ return left.stream_id == right.stream_id; } | |
59 | - | |
60 | - | |
61 | -////// helper functions ////// | |
62 | -// Actually, as it turns out, there should be a generic STL algorithm that does this, I think. | |
63 | -// Well, w_found_players ought to be using a set<> or similar anyway, much more natural. | |
64 | -// Shrug, this was what I came up with before I knew anything about STL, and I'm too lazy to change it. | |
65 | -template<class T> | |
66 | -static const size_t | |
67 | -find_item_index_in_vector(const T& inItem, const vector<T>& inVector) { | |
68 | - typename vector<T>::const_iterator i = inVector.begin(); | |
69 | - typename vector<T>::const_iterator end = inVector.end(); | |
70 | - size_t index = 0; | |
71 | - | |
72 | - while(i != end) { | |
73 | - if(*i == inItem) | |
74 | - return index; | |
75 | - | |
76 | - index++; | |
77 | - i++; | |
78 | - } | |
79 | - | |
80 | - // Didn't find it | |
81 | - return -1; | |
82 | -} | |
83 | - | |
84 | - | |
85 | - | |
86 | -////// w_found_players ////// | |
87 | -void | |
88 | -w_found_players::found_player(prospective_joiner_info &player) { | |
89 | - | |
90 | - // Found one | |
91 | - found_players.push_back(player); | |
92 | - | |
93 | - // List it | |
94 | - list_player(player); | |
95 | -} | |
96 | - | |
97 | - | |
98 | -void | |
99 | -w_found_players::hide_player(const prospective_joiner_info &player) { | |
100 | - found_players.push_back(player); | |
101 | - | |
102 | - unlist_player(player); | |
103 | -} | |
104 | - | |
105 | - | |
106 | -void | |
107 | -w_found_players::list_player(prospective_joiner_info &player) { | |
108 | - listed_players.push_back(player); | |
109 | - num_items = listed_players.size(); | |
110 | - new_items(); | |
111 | -} | |
112 | - | |
113 | -void w_found_players::update_player(prospective_joiner_info &player) { | |
114 | - unlist_player(player); | |
115 | - list_player(player); | |
116 | -} | |
117 | - | |
118 | - | |
119 | -void | |
120 | -w_found_players::unlist_player(const prospective_joiner_info &player) { | |
121 | - size_t theIndex = find_item_index_in_vector(player, listed_players); | |
122 | - if(theIndex == -1) | |
123 | - return; | |
124 | - | |
125 | - listed_players.erase(listed_players.begin() + theIndex); | |
126 | - | |
127 | - size_t old_top_item = top_item; | |
128 | - | |
129 | - num_items = listed_players.size(); | |
130 | - new_items(); | |
131 | - | |
132 | - // If the element deleted was the top item or before the top item, shift view up an item to compensate (if there is anything "up"). | |
133 | - if(theIndex <= old_top_item && old_top_item > 0) | |
134 | - old_top_item--; | |
135 | - | |
136 | - // Reconcile overhang, if needed. | |
137 | - if(old_top_item + shown_items > num_items && num_items >= shown_items) | |
138 | - set_top_item(num_items - shown_items); | |
139 | - else | |
140 | - set_top_item(old_top_item); | |
141 | -} | |
142 | - | |
143 | - | |
144 | -void | |
145 | -w_found_players::item_selected() { | |
146 | - if(player_selected_callback != NULL) | |
147 | - player_selected_callback(this, listed_players[get_selection()]); | |
148 | -} | |
149 | - | |
150 | - | |
151 | -// ZZZ: this is pretty ugly, it assumes that the callback will remove players from the widget. | |
152 | -// Fortunately, that's the case currently. :) | |
153 | -void | |
154 | -w_found_players::callback_on_all_items() { | |
155 | - if(player_selected_callback != NULL) { | |
156 | - for (vector<prospective_joiner_info>::iterator it = listed_players.begin(); it != listed_players.end(); it++) { | |
157 | - player_selected_callback(this, *it); | |
158 | - } | |
159 | - } | |
160 | -} | |
161 | - | |
162 | -void | |
163 | -w_found_players::draw_item(vector<prospective_joiner_info>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const { | |
164 | - char theNameBuffer[SSLP_MAX_NAME_LENGTH + 12]; | |
165 | - | |
166 | - pstrncpy((unsigned char*)theNameBuffer, (unsigned char*)(*i).name, SSLP_MAX_NAME_LENGTH - 1); | |
167 | - a1_p2cstr((unsigned char *) theNameBuffer); | |
168 | - if ((*i).gathering) { | |
169 | - strcat(theNameBuffer, " (gathering)"); | |
170 | - } | |
171 | - | |
172 | - int computed_x = x + (width - text_width(theNameBuffer, font, style)) / 2; | |
173 | - int computed_y = y + font->get_ascent(); | |
174 | - | |
175 | - //unsigned char text_length = (*i)->sslps_name[0]; | |
176 | - | |
177 | - //if(text_length > SSLP_MAX_NAME_LENGTH - 1) | |
178 | - // text_length = SSLP_MAX_NAME_LENGTH - 1; | |
179 | - if ((*i).gathering) { | |
180 | - draw_text(s, theNameBuffer, computed_x, computed_y, get_theme_color(ITEM_WIDGET, DISABLED_STATE), font, style); | |
181 | - } else { | |
182 | - draw_text(s, /*&((*i)->sslps_name[1]), text_length,*/ theNameBuffer, computed_x, computed_y, | |
183 | - selected ? get_theme_color(ITEM_WIDGET, ACTIVE_STATE) : get_theme_color(ITEM_WIDGET, DEFAULT_STATE), font, style); | |
184 | - } | |
185 | -} | |
186 | - | |
187 | -////// w_players_in_game2 ////// | |
188 | - | |
189 | -// I guess these should be computed more dynamically, but it wasn't obvious the best way to do that. | |
190 | -// These values work well for the standard player shapes, anyway. | |
191 | -enum { | |
192 | - kWPIG2Width = 600, // widget width | |
193 | - kWPIG2Height = 142, // widget height (actual height will differ if postgame_layout) | |
194 | - kMaxHeadroom = 53, // height above player origin (approx. navel) of tallest player shape | |
195 | - kNameOffset = 80, // how far below player origin baseline of player's name should appear | |
196 | - kNumNameOffsets = MAXIMUM_NUMBER_OF_PLAYERS, // used to resolve overlapping names | |
197 | - kNameMargin = 6, // names overlap if their edges are fewer than this many pixels apart | |
198 | - kNormalPlayerOffset = kMaxHeadroom, | |
199 | - kNormalNameTotalOffset = kNormalPlayerOffset + kNameOffset, | |
200 | - | |
201 | -// kPostgameTopMargin = 70, // how much extra space is at the top of widget in postgame layout | |
202 | - kPostgameTopMargin = 190, // For postgame layout without chat window, we can use a lot more space. (use 70 to coexist with full chat UI) | |
203 | - kPostgameBottomMargin = 6, // how much extra space is at the bottom of widget in postgame layout | |
204 | - kBarBottomOffset = 80, // how far below player origin score/kill bars should start | |
205 | - kBarWidth = 10, // how wide a kill/score bar should be | |
206 | - kBarOffsetX = 20, // how much to offset a bar so it won't draw directly on a player | |
207 | - kBevelSize = 2, // how much "depth effect" (in pixels around the border) bars have | |
208 | - kUseLegendThreshhold = 5, // with this many players or more, use legend for kills/deaths rather than print at bar labels | |
209 | - kPostgamePlayerOffset = kPostgameTopMargin + kMaxHeadroom, | |
210 | - kPostgameNameTotalOffset = kPostgamePlayerOffset + kNameOffset, | |
211 | - kBarBottomTotalOffset = kPostgamePlayerOffset + kBarBottomOffset, | |
212 | - kPostgameHeight = kPostgameTopMargin + kWPIG2Height + kPostgameBottomMargin | |
213 | -}; | |
214 | - | |
215 | -/* | |
216 | -// These can't see postgame_layout. Duh. And the idea here was to avoid having the constants above | |
217 | -// in a header file (as would be needed for making inline methods) where they would force extra | |
218 | -// recompilation... burrito. Macros it is. | |
219 | -static inline int | |
220 | -get_player_y_offset() { return postgame_layout ? kPostgamePlayerOffset : kNormalPlayerOffset; } | |
221 | - | |
222 | -static inline int | |
223 | -get_name_y_offset() { return postgame_layout ? kPostgameNameTotalOffset : kNormalNameTotalOffset; } | |
224 | -*/ | |
225 | - | |
226 | -#define get_player_y_offset() (postgame_layout ? kPostgamePlayerOffset : kNormalPlayerOffset) | |
227 | -#define get_name_y_offset() (postgame_layout ? kPostgameNameTotalOffset : kNormalNameTotalOffset) | |
228 | - | |
229 | -// Here I divide each piece of space into N pieces (where N is the number of things to draw) | |
230 | -// each item is drawn in the center of its space. This pitches them a little more widely than | |
231 | -// is used in the separately-drawn strategy. | |
232 | -// The computation used is (I from 0 to N-1, W is width) for the center: | |
233 | -// ((I + .5) / N) * W | |
234 | -// == WI + .5W / N | |
235 | -// == W*(2I + 1) / 2N | |
236 | -static inline int | |
237 | -get_wide_spaced_center_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
238 | - return left_x + (((2 * (int)index + 1) * available_width) / (2 * (int)num_items)); | |
239 | -} | |
240 | - | |
241 | -// for the left: | |
242 | -// I/N * W | |
243 | -// == WI/N | |
244 | -static inline int | |
245 | -get_wide_spaced_left_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
246 | - return left_x + (((int)index * available_width) / (int)num_items); | |
247 | -} | |
248 | - | |
249 | -// width is easy... | |
250 | -// note though that the actual distances between left_offsets may vary slightly from this width due to rounding. | |
251 | -static inline int | |
252 | -get_wide_spaced_width(int available_width, size_t num_items) { | |
253 | - return available_width / (int)num_items; | |
254 | -} | |
255 | - | |
256 | - | |
257 | -// Horizontal layout centers single player at 1/2 the width; two players at 1/3 and 2/3; three at 1/4, 2/4, 3/4.... | |
258 | -// Doing (I * W) / N rather than the more natural (I/N) * W may give more accurate results with integer math. | |
259 | -static inline int | |
260 | -get_close_spaced_center_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
261 | - return left_x + ((((int)index + 1) * available_width) / ((int)num_items + 1)); | |
262 | -} | |
263 | - | |
264 | -static inline int | |
265 | -get_close_spaced_width(int available_width, size_t num_items) { | |
266 | - return available_width / ((int)num_items + 1); | |
267 | -} | |
268 | - | |
269 | - | |
270 | -w_players_in_game2::w_players_in_game2(bool inPostgameLayout) : | |
271 | - widget(MESSAGE_WIDGET), displaying_actual_information(false), postgame_layout(inPostgameLayout), | |
272 | - draw_carnage_graph(false), num_valid_net_rankings(0), selected_player(NONE), | |
273 | - clump_players_by_team(false), draw_scores_not_carnage(false) | |
274 | -{ | |
275 | - rect.w = kWPIG2Width; | |
276 | - rect.h = postgame_layout ? kPostgameHeight : kWPIG2Height; | |
277 | - | |
278 | - saved_min_width = rect.w; | |
279 | - saved_min_height = rect.h; | |
280 | -} | |
281 | - | |
282 | - | |
283 | -w_players_in_game2::~w_players_in_game2() { | |
284 | - clear_vector(); | |
285 | -} | |
286 | - | |
287 | - | |
288 | -void | |
289 | -w_players_in_game2::update_display(bool inFromDynamicWorld /* default=false */) { | |
290 | - // Start over - wipe out our local player-storage | |
291 | - clear_vector(); | |
292 | - | |
293 | - // Wipe out references to players through teams | |
294 | - for(int i = 0; i < NUMBER_OF_TEAM_COLORS; i++) | |
295 | - players_on_team[i].clear(); | |
296 | - | |
297 | - // Find the number of players | |
298 | - int num_players; | |
299 | - if(inFromDynamicWorld) | |
300 | - num_players = dynamic_world->player_count; | |
301 | - else | |
302 | - num_players = displaying_actual_information ? NetGetNumberOfPlayers() : 0; | |
303 | - | |
304 | - // Fill in the entries | |
305 | - for(int i = 0; i < num_players; i++) { | |
306 | - player_entry2 thePlayerEntry; | |
307 | - | |
308 | - int thePlayerTeam; | |
309 | - int thePlayerColor; | |
310 | - | |
311 | - if(inFromDynamicWorld) { | |
312 | - // Get player information from dynamic_world | |
313 | - player_data* thePlayerData = get_player_data(i); | |
314 | - | |
315 | - // Copy the player name. We will store it as a cstring... | |
316 | - strncpy(thePlayerEntry.player_name, thePlayerData->name, MAXIMUM_PLAYER_NAME_LENGTH + 1); | |
317 | - | |
318 | - // Look up colors | |
319 | - thePlayerTeam = thePlayerData->team; | |
320 | - thePlayerColor = thePlayerData->color; | |
321 | - } | |
322 | - else { | |
323 | - // Get player information from topology | |
324 | - player_info* thePlayerInfo = (player_info*)NetGetPlayerData(i); | |
325 | - | |
326 | - // Alias the player entry's name field as a pstring | |
327 | - unsigned char* thePlayerEntryNameP = (unsigned char*) thePlayerEntry.player_name; | |
328 | - | |
329 | - // Copy the player name. We will store it as a cstring... | |
330 | - pstrncpy(thePlayerEntryNameP, thePlayerInfo->name, MAXIMUM_PLAYER_NAME_LENGTH + 1); | |
331 | - | |
332 | - // In-place conversion. | |
333 | - a1_p2cstr(thePlayerEntryNameP); | |
334 | - | |
335 | - // Look up colors | |
336 | - thePlayerTeam = thePlayerInfo->team; | |
337 | - thePlayerColor = thePlayerInfo->color; | |
338 | - } | |
339 | - | |
340 | - // Set the size of the text | |
341 | - thePlayerEntry.name_width = text_width(thePlayerEntry.player_name, font, style | styleShadow); | |
342 | - | |
343 | - // Get the pixel-color for the player's team (for drawing the name) | |
344 | - thePlayerEntry.name_pixel_color = get_dialog_player_color(thePlayerTeam); | |
345 | - | |
346 | - // Set up a player image for the player (funfun) | |
347 | - thePlayerEntry.player_image = new PlayerImage; | |
348 | - thePlayerEntry.player_image->setRandomFlatteringView(); | |
349 | - thePlayerEntry.player_image->setPlayerColor(thePlayerColor); | |
350 | - thePlayerEntry.player_image->setTeamColor(thePlayerTeam); | |
351 | - | |
352 | - // Add the player to our local storage area | |
353 | - player_entries.push_back(thePlayerEntry); | |
354 | - | |
355 | - // Add a reference to the player through his team color | |
356 | - players_on_team[thePlayerTeam].push_back(i); | |
357 | - } | |
358 | - | |
359 | - dirty = true; | |
360 | -} | |
361 | - | |
362 | - | |
363 | -#if 0 | |
364 | -// this is for testing | |
365 | -static const char* sTestingNames[] = { | |
366 | - "Doctor Burrito", | |
367 | - "Carnage Asada", | |
368 | - "Bongo Bob", | |
369 | - "The Napalm Man", | |
370 | - "The Big Lebowski", | |
371 | - "lala", | |
372 | - "Prof. Windsurf", | |
373 | - "<<<-ZED-<<<" | |
374 | -}; | |
375 | - | |
376 | -void | |
377 | -w_players_in_game2::click(int, int) { | |
378 | - player_entry2 thePlayerEntry; | |
379 | - | |
380 | - // make up a name | |
381 | -/* int theNameLength = (local_random() % MAXIMUM_PLAYER_NAME_LENGTH) + 1; | |
382 | - for(int i = 0; i < theNameLength; i++) | |
383 | - thePlayerEntry.player_name[i] = 'a' + (local_random() % ('z' - 'a')); | |
384 | - thePlayerEntry.player_name[theNameLength] = '\0'; | |
385 | -// strcpy(thePlayerEntry.player_name, "The Big Lebowski"); | |
386 | -*/ | |
387 | - strcpy(thePlayerEntry.player_name, sTestingNames[local_random() % 8]); | |
388 | - | |
389 | - // Set the size of the text | |
390 | - thePlayerEntry.name_width = text_width(thePlayerEntry.player_name, font, style); | |
391 | - | |
392 | - // Make up a team-color | |
393 | - int theTeamColor = local_random() % 8; | |
394 | - | |
395 | - // Get the pixel-color for the player's team (for drawing the name) | |
396 | - thePlayerEntry.name_pixel_color = get_dialog_player_color(theTeamColor); | |
397 | - | |
398 | - // Set up a player image for the player (funfun) | |
399 | - thePlayerEntry.player_image = new PlayerImage; | |
400 | - thePlayerEntry.player_image->setRandomFlatteringView(); | |
401 | - thePlayerEntry.player_image->setTeamColor(theTeamColor); | |
402 | - | |
403 | - player_entries.push_back(thePlayerEntry); | |
404 | - | |
405 | - dirty = true; | |
406 | -} | |
407 | -#else // NOT 0 | |
408 | -void | |
409 | -w_players_in_game2::click(int x, int) { | |
410 | - if(draw_carnage_graph) { | |
411 | - | |
412 | - if(clump_players_by_team) { | |
413 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
414 | - if(ABS(x - get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings)) | |
415 | - < (get_wide_spaced_width(rect.w, num_valid_net_rankings) / 2)) | |
416 | - { | |
417 | - if(element_clicked_callback != NULL) | |
418 | - element_clicked_callback(this, clump_players_by_team, draw_carnage_graph, draw_scores_not_carnage, | |
419 | - i, net_rankings[i].color); | |
420 | - | |
421 | - break; | |
422 | - } | |
423 | - } | |
424 | - } | |
425 | - | |
426 | - else { | |
427 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
428 | - if(ABS(x - get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings)) | |
429 | - < (get_close_spaced_width(rect.w, num_valid_net_rankings) / 2)) | |
430 | - { | |
431 | - if(element_clicked_callback != NULL) | |
432 | - element_clicked_callback(this, clump_players_by_team, draw_carnage_graph, draw_scores_not_carnage, | |
433 | - i, net_rankings[i].player_index); | |
434 | - | |
435 | - break; | |
436 | - } | |
437 | - } | |
438 | - } | |
439 | - | |
440 | - } // draw_carnage_graph | |
441 | -} | |
442 | -#endif // NOT 0 | |
443 | - | |
444 | -// enable carnage reporting mode and set the data needed to draw a graph. | |
445 | -void | |
446 | -w_players_in_game2::set_graph_data(const net_rank* inRankings, int inNumRankings, int inSelectedPlayer, | |
447 | - bool inClumpPlayersByTeam, bool inDrawScoresNotCarnage) | |
448 | -{ | |
449 | - draw_carnage_graph = true; | |
450 | - num_valid_net_rankings = inNumRankings; | |
451 | - selected_player = inSelectedPlayer; | |
452 | - clump_players_by_team = inClumpPlayersByTeam; | |
453 | - draw_scores_not_carnage = inDrawScoresNotCarnage; | |
454 | - memcpy(net_rankings, inRankings, inNumRankings * sizeof(net_rank)); | |
455 | - | |
456 | - dirty = true; | |
457 | -} | |
458 | - | |
459 | - | |
460 | -void | |
461 | -w_players_in_game2::draw_player_icon(SDL_Surface* s, size_t rank_index, int center_x) const { | |
462 | - // Note, player images will not be re-fetched unless the brightness has *changed* since last draw. | |
463 | - PlayerImage* theImage = player_entries[net_rankings[rank_index].player_index].player_image; | |
464 | - if(selected_player != NONE && selected_player != rank_index) | |
465 | - theImage->setBrightness(.4f); | |
466 | - else | |
467 | - theImage->setBrightness(1.0f); | |
468 | - | |
469 | - theImage->drawAt(s, center_x, rect.y + get_player_y_offset()); | |
470 | -} | |
471 | - | |
472 | - | |
473 | -void | |
474 | -w_players_in_game2::draw_player_icons_separately(SDL_Surface* s) const { | |
475 | - if(draw_carnage_graph) { | |
476 | - // Draw in sorted order (according to net_rankings) | |
477 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
478 | - int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
479 | - | |
480 | - draw_player_icon(s, i, center_x); | |
481 | - } | |
482 | - } | |
483 | - else { | |
484 | - // Draw in "natural order" (according to topology) | |
485 | - size_t theNumPlayers = player_entries.size(); | |
486 | - for(size_t i = 0; i < theNumPlayers; i++) { | |
487 | - int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, theNumPlayers); | |
488 | - player_entries[i].player_image->drawAt(s, center_x, rect.y + get_player_y_offset()); | |
489 | - } | |
490 | - } | |
491 | -} // draw_player_icons_separately | |
492 | - | |
493 | - | |
494 | -void | |
495 | -w_players_in_game2::draw_player_icons_clumped(SDL_Surface* s) const { | |
496 | - assert(draw_carnage_graph); | |
497 | - | |
498 | - int width_per_team = get_wide_spaced_width(rect.w, num_valid_net_rankings); | |
499 | - | |
500 | - // Walk through teams, drawing each batch. | |
501 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
502 | - int team_left_x = get_wide_spaced_left_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
503 | - | |
504 | - size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
505 | - | |
506 | - assert(theNumberOfPlayersOnThisTeam > 0); | |
507 | - | |
508 | - // Walk through players on a team to draw a batch. | |
509 | - for(size_t j = 0; j < theNumberOfPlayersOnThisTeam; j++) { | |
510 | - int player_center_x = get_close_spaced_center_offset(team_left_x, width_per_team, j, theNumberOfPlayersOnThisTeam); | |
511 | - | |
512 | - // Note, player images will not be re-fetched unless the brightness has *changed* since last draw. | |
513 | - // Though Marathon does not let one view team vs team carnage (just total team carnage), I'm leaving | |
514 | - // the highlighting stuff here in case team view is later added. | |
515 | - PlayerImage* theImage = player_entries[players_on_team[net_rankings[i].color][j]].player_image; | |
516 | - if(selected_player != NONE && selected_player != i) | |
517 | - theImage->setBrightness(.4f); | |
518 | - else | |
519 | - theImage->setBrightness(1.0f); | |
520 | - | |
521 | - theImage->drawAt(s, player_center_x, rect.y + get_player_y_offset()); | |
522 | - } // players | |
523 | - } // teams | |
524 | -} // draw_player_icons_clumped | |
525 | - | |
526 | - | |
527 | -void | |
528 | -w_players_in_game2::draw_player_names_separately(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const { | |
529 | - // Now let's draw the names. Let's take care to offset names vertically if they would | |
530 | - // overlap (or come too close as defined by kNameMargin), so it's more readable. | |
531 | - | |
532 | - size_t theNumPlayers = draw_carnage_graph ? num_valid_net_rankings : player_entries.size(); | |
533 | - | |
534 | - for(size_t i = 0; i < theNumPlayers; i++) { | |
535 | - int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, theNumPlayers); | |
536 | - const player_entry2* theEntry = draw_carnage_graph ? &player_entries[net_rankings[i].player_index] : &player_entries[i]; | |
537 | - int name_x = center_x - (theEntry->name_width / 2); | |
538 | - int name_y = rect.y + get_name_y_offset(); | |
539 | - | |
540 | - // Find a suitable vertical offset | |
541 | - name_y = ioTextLayoutHelper.reserveSpaceFor(name_x - kNameMargin / 2, theEntry->name_width + kNameMargin, name_y, font->get_line_height()); | |
542 | - | |
543 | - draw_text(s, theEntry->player_name, name_x, name_y, | |
544 | - theEntry->name_pixel_color, font, style | styleShadow); | |
545 | - } | |
546 | -} | |
547 | - | |
548 | - | |
549 | -void | |
550 | -w_players_in_game2::draw_player_names_clumped(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const { | |
551 | - // Now let's draw the names. Let's take care to offset names vertically if they would | |
552 | - // overlap (or come too close as defined by kNameMargin), so it's more readable. | |
553 | - | |
554 | - // Walk through teams, drawing each batch. | |
555 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
556 | - int team_center_x = get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
557 | - | |
558 | - size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
559 | - | |
560 | - assert(theNumberOfPlayersOnThisTeam > 0); | |
561 | - | |
562 | - // Walk through players on a team to draw a batch. | |
563 | - for(size_t j = 0; j < theNumberOfPlayersOnThisTeam; j++) { | |
564 | - | |
565 | - const player_entry2* theEntry = &(player_entries[players_on_team[net_rankings[i].color][j]]); | |
566 | - int name_x = team_center_x - (theEntry->name_width / 2); | |
567 | - int name_y = rect.y + get_name_y_offset(); | |
568 | - | |
569 | - // Find a suitable vertical offset | |
570 | - name_y = ioTextLayoutHelper.reserveSpaceFor(name_x - kNameMargin/2, theEntry->name_width + kNameMargin, | |
571 | - name_y, font->get_line_height()); | |
572 | - | |
573 | - draw_text(s, theEntry->player_name, name_x, name_y, | |
574 | - theEntry->name_pixel_color, font, style | styleShadow); | |
575 | - } | |
576 | - } | |
577 | -} | |
578 | - | |
579 | - | |
580 | -int | |
581 | -w_players_in_game2::find_maximum_bar_value() const { | |
582 | - int theMaxValue = INT_MIN; | |
583 | - | |
584 | - // We track min also to handle games with negative scores. | |
585 | - int theMinValue = INT_MAX; | |
586 | - | |
587 | - if(selected_player != NONE) | |
588 | - // This way, all player vs player graphs use the same scale. | |
589 | - theMaxValue = calculate_max_kills(num_valid_net_rankings); | |
590 | - else { | |
591 | - // Note this does the right thing for suicide bars as well. | |
592 | - if(draw_scores_not_carnage) { | |
593 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
594 | - if(net_rankings[i].game_ranking > theMaxValue) | |
595 | - theMaxValue = net_rankings[i].game_ranking; | |
596 | - if(net_rankings[i].game_ranking < theMinValue) | |
597 | - theMinValue = net_rankings[i].game_ranking; | |
598 | - } | |
599 | - } else { | |
600 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
601 | - if(net_rankings[i].kills > theMaxValue) | |
602 | - theMaxValue = net_rankings[i].kills; | |
603 | - if(net_rankings[i].deaths > theMaxValue) | |
604 | - theMaxValue = net_rankings[i].deaths; | |
605 | - } | |
606 | - } | |
607 | - } | |
608 | - | |
609 | - // If all values were nonpositive, and we had at least one negative, we | |
610 | - // return the (negative) value furthest from 0. | |
611 | - // The Mac version seems to do nothing of the sort - how can it possibly | |
612 | - // display correct bars for games with negative scores like "Tag"?? | |
613 | - if(theMaxValue <= 0 && theMinValue < 0) | |
614 | - theMaxValue = theMinValue; | |
615 | - | |
616 | - return theMaxValue; | |
617 | -} | |
618 | - | |
619 | -struct bar_info { | |
620 | - int center_x; | |
621 | - int top_y; | |
622 | - uint32 pixel_color; | |
623 | - string label_text; | |
624 | -}; | |
625 | - | |
626 | -void | |
627 | -w_players_in_game2::draw_bar_or_bars(SDL_Surface* s, size_t rank_index, int center_x, int maximum_value, vector<bar_info>& outBarInfos) const { | |
628 | - // Draw score bar | |
629 | - if(draw_scores_not_carnage) { | |
630 | - bar_info theBarInfo; | |
631 | - int theScore = net_rankings[rank_index].game_ranking; | |
632 | - | |
633 | - calculate_ranking_text_for_post_game(temporary, theScore); | |
634 | - theBarInfo.label_text = temporary; // this makes a copy | |
635 | - | |
636 | - draw_bar(s, center_x, _score_color, theScore, maximum_value, theBarInfo); | |
637 | - | |
638 | - // Don't draw a "0" score label | |
639 | - if(theScore != 0) | |
640 | - outBarInfos.push_back(theBarInfo); | |
641 | - } | |
642 | - else { | |
643 | - // Draw carnage bar(s) | |
644 | - if(rank_index == selected_player) { | |
645 | - // Draw suicides/friendly-fires | |
646 | - bar_info theBarInfo; | |
647 | - | |
648 | - char* theSuicidesFormat = TS_GetCString(strNET_STATS_STRINGS, strSUICIDES_STRING); | |
649 | - int theNumberOfSuicides = net_rankings[rank_index].kills; | |
650 | - sprintf(temporary, theSuicidesFormat, theNumberOfSuicides); | |
651 | - theBarInfo.label_text = temporary; // this makes a copy | |
652 | - | |
653 | - draw_bar(s, center_x, _suicide_color, theNumberOfSuicides, maximum_value, theBarInfo); | |
654 | - | |
655 | - // Don't push a "0" label. | |
656 | - if(theNumberOfSuicides > 0) | |
657 | - outBarInfos.push_back(theBarInfo); | |
658 | - } | |
659 | - else { | |
660 | - // Draw kills and deaths | |
661 | - int theNumKills = net_rankings[rank_index].kills; | |
662 | - int theNumDeaths = net_rankings[rank_index].deaths; | |
663 | - | |
664 | - // Get strings for labelling | |
665 | - const char* theKillsFormat; | |
666 | - const char* theDeathsFormat; | |
667 | - char theKillsString[32]; | |
668 | - char theDeathsString[32]; | |
669 | - | |
670 | - // If more than threshhold bar-pairs to draw, use short form with legend rather than normal (long) form. | |
671 | - theKillsFormat = num_valid_net_rankings >= kUseLegendThreshhold ? "%d" : TS_GetCString(strNET_STATS_STRINGS, strKILLS_STRING); | |
672 | - theDeathsFormat = num_valid_net_rankings >= kUseLegendThreshhold ? "%d" : TS_GetCString(strNET_STATS_STRINGS, strDEATHS_STRING); | |
673 | - | |
674 | - // Construct labels | |
675 | - sprintf(theKillsString, theKillsFormat, theNumKills); | |
676 | - sprintf(theDeathsString, theDeathsFormat, theNumDeaths); | |
677 | - | |
678 | - // Set up bar_infos | |
679 | - bar_info theKillsBarInfo; | |
680 | - bar_info theDeathsBarInfo; | |
681 | - | |
682 | - // Copy strings into bar_infos | |
683 | - theKillsBarInfo.label_text = theKillsString; | |
684 | - theDeathsBarInfo.label_text = theDeathsString; | |
685 | - | |
686 | - // Draw shorter bar in front - looks nicer | |
687 | - // If equal, draw kills in front | |
688 | - // Put shorter bar_info in vector first so its label doesn't "leapfrog" the taller bar label in case of conflict. | |
689 | - // Don't put "0"s into the vector. | |
690 | - if(theNumKills > theNumDeaths) { | |
691 | - // Deaths bar is shorter - draw it last | |
692 | - draw_bar(s, center_x - kBarWidth / 3, _kill_color, theNumKills, maximum_value, theKillsBarInfo); | |
693 | - draw_bar(s, center_x + kBarWidth / 3, _death_color, theNumDeaths, maximum_value, theDeathsBarInfo); | |
694 | - | |
695 | - if(theNumDeaths > 0) | |
696 | - outBarInfos.push_back(theDeathsBarInfo); | |
697 | - if(theNumKills > 0) | |
698 | - outBarInfos.push_back(theKillsBarInfo); | |
699 | - } | |
700 | - else { | |
701 | - // Kills bar is shorter or equal - draw it last | |
702 | - draw_bar(s, center_x + kBarWidth / 3, _death_color, theNumDeaths, maximum_value, theDeathsBarInfo); | |
703 | - draw_bar(s, center_x - kBarWidth / 3, _kill_color, theNumKills, maximum_value, theKillsBarInfo); | |
704 | - | |
705 | - if(theNumKills > 0) | |
706 | - outBarInfos.push_back(theKillsBarInfo); | |
707 | - if(theNumDeaths > 0) | |
708 | - outBarInfos.push_back(theDeathsBarInfo); | |
709 | - } // kills and deaths (not suicides) | |
710 | - } // carnage bars | |
711 | - } // !draw_scores_not_carnage (i.e. draw carnage) | |
712 | -} // draw_bar_or_bars | |
713 | - | |
714 | - | |
715 | -void | |
716 | -w_players_in_game2::draw_bars_separately(SDL_Surface* s, vector<bar_info>& outBarInfos) const { | |
717 | - // Find the largest value we'll be drawing, so we know how to scale our bars. | |
718 | - int theMaxValue = find_maximum_bar_value(); | |
719 | - | |
720 | - // Draw the bars. | |
721 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
722 | - int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
723 | - | |
724 | - draw_bar_or_bars(s, i, center_x + kBarOffsetX, theMaxValue, outBarInfos); | |
725 | - } // walk through rankings | |
726 | -} // draw_bars_separately | |
727 | - | |
728 | - | |
729 | -void | |
730 | -w_players_in_game2::draw_bars_clumped(SDL_Surface* s, vector<bar_info>& outBarInfos) const { | |
731 | - // Find the largest value we'll be drawing, so we know how to scale our bars. | |
732 | - int theMaxValue = find_maximum_bar_value(); | |
733 | - | |
734 | - // Walk through teams, drawing each batch. | |
735 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
736 | - int team_center_x = (int)(rect.x + ((2*i + 1) * rect.w) / (2 * num_valid_net_rankings)); | |
737 | - | |
738 | - size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
739 | - | |
740 | - assert(theNumberOfPlayersOnThisTeam > 0); | |
741 | - | |
742 | - // We will offset if we would draw on top of a player (i.e. if num players is odd), else | |
743 | - // we will draw right smack in the middle. | |
744 | - int center_x = team_center_x; | |
745 | - | |
746 | - if(theNumberOfPlayersOnThisTeam % 2 == 1) | |
747 | - center_x += kBarOffsetX; | |
748 | - | |
749 | - draw_bar_or_bars(s, i, center_x, theMaxValue, outBarInfos); | |
750 | - } // walk through rankings | |
751 | -} // draw_bars_clumped | |
752 | - | |
753 | - | |
754 | -void | |
755 | -w_players_in_game2::draw_carnage_totals(SDL_Surface* s) const { | |
756 | - for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
757 | - int center_x; | |
758 | - if(clump_players_by_team) | |
759 | - center_x = get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
760 | - else | |
761 | - center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
762 | - | |
763 | - // Draw carnage score for player/team (list -N for N suicides) | |
764 | - int thePlayerCarnageScore = (selected_player == i) ? -net_rankings[i].kills : net_rankings[i].kills - net_rankings[i].deaths; | |
765 | - if(thePlayerCarnageScore == 0) | |
766 | - strcpy(temporary, "0"); | |
767 | - else | |
768 | - sprintf(temporary, "%+d", thePlayerCarnageScore); | |
769 | - | |
770 | - uint16 theBiggerFontStyle = 0; | |
771 | - font_info* theBiggerFont = get_theme_font(LABEL_WIDGET, theBiggerFontStyle); | |
772 | - | |
773 | - int theStringCenter = center_x - (text_width(temporary, theBiggerFont, theBiggerFontStyle | styleShadow) / 2); | |
774 | - | |
775 | - draw_text(s, temporary, theStringCenter, rect.y + rect.h - 1, SDL_MapRGB(s->format, 0xff, 0xff, 0xff), | |
776 | - theBiggerFont, theBiggerFontStyle | styleShadow); | |
777 | - } // walk through rankings | |
778 | -} // draw_carnage_totals | |
779 | - | |
780 | - | |
781 | -void | |
782 | -w_players_in_game2::draw_carnage_legend(SDL_Surface* s) const { | |
783 | - RGBColor theBrightestColor; | |
784 | - get_net_color(_kill_color, &theBrightestColor); | |
785 | - | |
786 | - RGBColor theMiddleColor; | |
787 | - theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
788 | - theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
789 | - theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
790 | - | |
791 | - uint32 thePixelColor = SDL_MapRGB(s->format, theMiddleColor.red >> 8, theMiddleColor.green >> 8, theMiddleColor.blue >> 8); | |
792 | - | |
793 | - draw_text(s, TS_GetCString(strNET_STATS_STRINGS, strKILLS_LEGEND), rect.x, rect.y + font->get_line_height(), | |
794 | - thePixelColor, font, style); | |
795 | - | |
796 | - get_net_color(_death_color, &theBrightestColor); | |
797 | - | |
798 | - theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
799 | - theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
800 | - theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
801 | - | |
802 | - thePixelColor = SDL_MapRGB(s->format, theMiddleColor.red >> 8, theMiddleColor.green >> 8, theMiddleColor.blue >> 8); | |
803 | - | |
804 | - draw_text(s, TS_GetCString(strNET_STATS_STRINGS, strDEATHS_LEGEND), rect.x, rect.y + 2 * font->get_line_height(), | |
805 | - thePixelColor, font, style); | |
806 | -} | |
807 | - | |
808 | - | |
809 | -void | |
810 | -w_players_in_game2::draw_bar_labels(SDL_Surface* s, const vector<bar_info>& inBarInfos, TextLayoutHelper& ioTextLayoutHelper) const { | |
811 | - size_t theNumberOfLabels = inBarInfos.size(); | |
812 | - | |
813 | - for(size_t i = 0; i < theNumberOfLabels; i++) { | |
814 | - const bar_info& theBarInfo = inBarInfos[i]; | |
815 | - | |
816 | - int theStringWidth = text_width(theBarInfo.label_text.c_str(), font, style | styleShadow); | |
817 | - int theTextX = theBarInfo.center_x - theStringWidth / 2; | |
818 | - int theBestY = ioTextLayoutHelper.reserveSpaceFor(theTextX - kNameMargin/2, | |
819 | - theStringWidth + kNameMargin, theBarInfo.top_y - 1, font->get_line_height()); | |
820 | - | |
821 | - draw_text(s, theBarInfo.label_text.c_str(), theTextX, theBestY, theBarInfo.pixel_color, font, style | styleShadow); | |
822 | - } | |
823 | -} // draw_bar_labels | |
824 | - | |
825 | - | |
826 | -void | |
827 | -w_players_in_game2::draw(SDL_Surface* s) const { | |
828 | -// printf("widget top is %d, bottom is %d\n", rect.y, rect.y + rect.h); | |
829 | - | |
830 | - // Set clip rectangle so we don't color outside the lines | |
831 | - set_drawing_clip_rectangle(rect.y, rect.x, rect.y + rect.h, rect.x + rect.w); | |
832 | - | |
833 | -// Did some tests here - it seems that text drawing is clipped by this rectangle, but rect-filling | |
834 | -// and blitting are not. (at least, on the Mac OS X version of SDL. actually, in Win 98 too.) | |
835 | -// This means that little tiny chunks of pistol fire, feet, etc. can stick around if they are drawn | |
836 | -// outside the widget (because they won't be cleared away when the widget is redrawn). I'm surrounding | |
837 | -// that with a "Somebody Else's Problem" field for the time being. | |
838 | -// set_drawing_clip_rectangle(100, 300, 200, 400); | |
839 | -// printf("clipped at <%d %d %d %d>\n", rect.y, rect.x, rect.y + rect.h, rect.x + rect.w); | |
840 | - | |
841 | - // theTextLayoutHelper exists for the duration of the draw operation | |
842 | - // helps us draw bits of text that do not overlap one another. | |
843 | - TextLayoutHelper theTextLayoutHelper; | |
844 | - | |
845 | - // theBarInfos exists for the duration of the draw operation | |
846 | - // helps us plan our bar label placement early (at draw_bar time) | |
847 | - // but draw them late (at draw_bar_labels time). | |
848 | - vector<bar_info> theBarInfos; | |
849 | - | |
850 | - // We draw in this order: | |
851 | - // Player icons | |
852 | - // Graph bars | |
853 | - // Player names | |
854 | - // Carnage totals (nobody should overlap, so timing is arbitrary) | |
855 | - // Carnage legend | |
856 | - // Bar labels | |
857 | - | |
858 | - // This order is largely for back-to-front ordering. Bar labels and player names, since | |
859 | - // they are placed using a text layout helper, will not overlap... but we draw bar labels | |
860 | - // after player names anyway (which takes considerable extra effort, note) so that the names | |
861 | - // have "first dibs" on the coveted low-in-the-widget screen area. Bar labels that want space | |
862 | - // occupied by player names will have to "float up"... which looks nicer than making the names | |
863 | - // float up to give the bar labels space. | |
864 | - | |
865 | - // Draw actual content | |
866 | - if(clump_players_by_team) { | |
867 | - // draw player icons in clumps | |
868 | - draw_player_icons_clumped(s); | |
869 | - | |
870 | - if(draw_carnage_graph) | |
871 | - draw_bars_clumped(s, theBarInfos); | |
872 | - | |
873 | - draw_player_names_clumped(s, theTextLayoutHelper); | |
874 | - } | |
875 | - else { | |
876 | - // Draw all the player icons first, so icons don't obscure names | |
877 | - draw_player_icons_separately(s); | |
878 | - | |
879 | - if(draw_carnage_graph) | |
880 | - draw_bars_separately(s, theBarInfos); | |
881 | - | |
882 | - draw_player_names_separately(s, theTextLayoutHelper); | |
883 | - } | |
884 | - | |
885 | - if(draw_carnage_graph && !draw_scores_not_carnage) { | |
886 | - draw_carnage_totals(s); | |
887 | - if(num_valid_net_rankings >= kUseLegendThreshhold) | |
888 | - draw_carnage_legend(s); | |
889 | - } | |
890 | - | |
891 | - if(draw_carnage_graph) | |
892 | - draw_bar_labels(s, theBarInfos, theTextLayoutHelper); | |
893 | - | |
894 | - // Reset clipping rectangle | |
895 | - set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX); | |
896 | -} | |
897 | - | |
898 | - | |
899 | -void | |
900 | -w_players_in_game2::clear_vector() { | |
901 | - vector<player_entry2>::const_iterator i = player_entries.begin(); | |
902 | - vector<player_entry2>::const_iterator end = player_entries.end(); | |
903 | - | |
904 | - while(i != end) { | |
905 | - // Free the name buffers associated with the elements. | |
906 | - // I don't do this in a player_entry destructor because I'm afraid of freeing the name twice | |
907 | - // (once here, when the vector's entry is destroyed, and another time when thePlayerEntry | |
908 | - // above goes out of scope). | |
909 | - if(i->player_image != NULL) | |
910 | - delete i->player_image; | |
911 | - | |
912 | - i++; | |
913 | - } | |
914 | - | |
915 | - player_entries.clear(); | |
916 | -} | |
917 | - | |
918 | - | |
919 | -void | |
920 | -w_players_in_game2::draw_bar(SDL_Surface* s, int inCenterX, int inBarColorIndex, int inBarValue, int inMaxValue, bar_info& outBarInfo) const { | |
921 | - if(inBarValue != 0) { | |
922 | - // Check that we'll draw a positive bar - value and max are either both positive or both negative. | |
923 | - if(inBarValue > 0) | |
924 | - assert(inMaxValue > 0); | |
925 | - | |
926 | - if(inBarValue < 0) | |
927 | - assert(inMaxValue < 0); | |
928 | - | |
929 | - // "- 1" leaves room for shadow style. Leave two line-heights so a kills and deaths at the top of widget resolve | |
930 | - // (thanks to TextLayoutHelper) and still have space to live. | |
931 | - int theMaximumBarHeight = kBarBottomTotalOffset - font->get_line_height() * 2 - 1; | |
932 | - int theBarHeight = (theMaximumBarHeight * inBarValue) / inMaxValue; | |
933 | - | |
934 | - SDL_Rect theBarRect; | |
935 | - | |
936 | - theBarRect.y = rect.y + kBarBottomTotalOffset - theBarHeight; | |
937 | - theBarRect.h = theBarHeight; | |
938 | - theBarRect.w = kBarWidth; | |
939 | - theBarRect.x = inCenterX - kBarWidth / 2; | |
940 | - | |
941 | - RGBColor theBrightestColor; | |
942 | - get_net_color(inBarColorIndex, &theBrightestColor); | |
943 | - | |
944 | - RGBColor theMiddleColor; | |
945 | - theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
946 | - theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
947 | - theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
948 | - | |
949 | - RGBColor theDarkestColor; | |
950 | - theDarkestColor.red = (theBrightestColor.red * 2) / 10; | |
951 | - theDarkestColor.blue = (theBrightestColor.blue * 2) / 10; | |
952 | - theDarkestColor.green = (theBrightestColor.green * 2) / 10; | |
953 | - | |
954 | - RGBColor* theRGBColor; | |
955 | - uint32 thePixelColor; | |
956 | - | |
957 | - // Draw the lightest part | |
958 | - theRGBColor = &theBrightestColor; | |
959 | - thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
960 | - | |
961 | - SDL_FillRect(s, &theBarRect, thePixelColor); | |
962 | - | |
963 | - // Draw the dark part | |
964 | - theRGBColor = &theDarkestColor; | |
965 | - thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
966 | - | |
967 | - SDL_Rect theDarkRect; | |
968 | - theDarkRect.x = theBarRect.x + theBarRect.w - kBevelSize; | |
969 | - theDarkRect.w = kBevelSize; | |
970 | - theDarkRect.y = theBarRect.y + kBevelSize; | |
971 | - theDarkRect.h = theBarRect.h - kBevelSize; | |
972 | - | |
973 | - if(theBarRect.h > kBevelSize) | |
974 | - SDL_FillRect(s, &theDarkRect, thePixelColor); | |
975 | - | |
976 | - // Draw the middle part | |
977 | - theRGBColor = &theMiddleColor; | |
978 | - thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
979 | - | |
980 | - SDL_Rect theMiddleRect; | |
981 | - theMiddleRect.x = theBarRect.x + kBevelSize; | |
982 | - theMiddleRect.w = theBarRect.w - 2 * kBevelSize; | |
983 | - theMiddleRect.y = theBarRect.y + kBevelSize; | |
984 | - theMiddleRect.h = theBarRect.h - kBevelSize; | |
985 | - | |
986 | - if(theBarRect.h > kBevelSize) | |
987 | - SDL_FillRect(s, &theMiddleRect, thePixelColor); | |
988 | - | |
989 | - // Capture bar information | |
990 | - outBarInfo.center_x = inCenterX; | |
991 | - outBarInfo.top_y = theBarRect.y; | |
992 | - outBarInfo.pixel_color = thePixelColor; | |
993 | - } // if(inBarValue > 0) | |
994 | -} // draw_bar | |
995 | - | |
996 | - | |
997 | - | |
998 | - | |
999 | - | |
1000 | -////// w_entry_point_selector ////// | |
1001 | - | |
1002 | -void | |
1003 | -w_entry_point_selector::validateEntryPoint() { | |
1004 | - // Get the entry-point flags from the game type. | |
1005 | - int theAppropriateLevelTypeFlags = get_entry_point_flags_for_game_type(mGameType); | |
1006 | - | |
1007 | - mEntryPoints.clear(); | |
1008 | - | |
1009 | - // OK, get the vector of entry points. | |
1010 | - get_entry_points(mEntryPoints, theAppropriateLevelTypeFlags); | |
1011 | - | |
1012 | - if(mEntryPoints.size() <= 0) { | |
1013 | - mEntryPoint.level_number = NONE; | |
1014 | - strcpy(mEntryPoint.level_name, "(no valid options)"); | |
1015 | - mCurrentIndex = NONE; | |
1016 | - } | |
1017 | - else { | |
1018 | - unsigned i; | |
1019 | - | |
1020 | - for(i = 0; i < mEntryPoints.size(); i++) { | |
1021 | - if(mEntryPoints[i].level_number == mEntryPoint.level_number) | |
1022 | - break; | |
1023 | - } | |
1024 | - | |
1025 | - if(i == mEntryPoints.size()) { | |
1026 | - mEntryPoint = mEntryPoints[0]; | |
1027 | - mCurrentIndex = 0; | |
1028 | - } | |
1029 | - else { | |
1030 | - mEntryPoint = mEntryPoints[i]; | |
1031 | - mCurrentIndex = i; | |
1032 | - } | |
1033 | - } | |
1034 | - | |
1035 | - dirty = true; | |
1036 | -} | |
1037 | - | |
1038 | -void | |
1039 | -w_entry_point_selector::gotSelectedCallback(void* arg) { | |
1040 | - ((w_entry_point_selector*) arg)->gotSelected(); | |
1041 | -} | |
1042 | - | |
1043 | -void | |
1044 | -w_entry_point_selector::gotSelected() { | |
1045 | - if(mEntryPoints.size() > 1) { | |
1046 | - dialog theDialog; | |
1047 | - | |
1048 | - vertical_placer *placer = new vertical_placer; | |
1049 | - placer->dual_add(new w_title("SELECT LEVEL"), theDialog); | |
1050 | - | |
1051 | - placer->add(new w_spacer(), true); | |
1052 | - | |
1053 | - FileSpecifier theFile(environment_preferences->map_file); | |
1054 | - char theName[256]; | |
1055 | - theFile.GetName(theName); | |
1056 | - sprintf(temporary, "%s", theName); | |
1057 | - placer->dual_add(new w_static_text(temporary), theDialog); | |
1058 | - | |
1059 | - placer->add(new w_spacer(), true); | |
1060 | - | |
1061 | - w_levels* levels_w = new w_levels(mEntryPoints, &theDialog, 480, 16, mCurrentIndex, false); | |
1062 | - placer->dual_add(levels_w, theDialog); | |
1063 | - | |
1064 | - placer->add(new w_spacer(), true); | |
1065 | - | |
1066 | - sprintf(temporary, "%lu %s levels available", | |
1067 | - mEntryPoints.size(), | |
1068 | - TS_GetCString(kNetworkGameTypesStringSetID, mGameType) | |
1069 | - ); | |
1070 | - placer->dual_add(new w_static_text(temporary), theDialog); | |
1071 | - | |
1072 | - placer->add(new w_spacer(), true); | |
1073 | - | |
1074 | - placer->dual_add(new w_button("CANCEL", dialog_cancel, &theDialog), theDialog); | |
1075 | - | |
1076 | - theDialog.set_widget_placer(placer); | |
1077 | - | |
1078 | - clear_screen(); | |
1079 | - | |
1080 | - if(theDialog.run() == 0) { | |
1081 | - mCurrentIndex = levels_w->get_selection(); | |
1082 | - mEntryPoint = mEntryPoints[mCurrentIndex]; | |
1083 | - dirty = true; | |
1084 | - } | |
1085 | - } | |
1086 | -} | |
1087 | - | |
1088 | -void | |
1089 | -w_entry_point_selector::event(SDL_Event &e) { | |
1090 | - if (e.type == SDL_KEYDOWN) { | |
1091 | - if (e.key.keysym.sym == SDLK_LEFT || e.key.keysym.sym == SDLK_RIGHT) { | |
1092 | - size_t theNumberOfEntryPoints = mEntryPoints.size(); | |
1093 | - | |
1094 | - if(theNumberOfEntryPoints > 1) { | |
1095 | - int theDesiredOffset = (e.key.keysym.sym == SDLK_LEFT) ? -1 : 1; | |
1096 | - | |
1097 | - mCurrentIndex = (mCurrentIndex + theNumberOfEntryPoints + theDesiredOffset) | |
1098 | - % theNumberOfEntryPoints; | |
1099 | - | |
1100 | - mEntryPoint = mEntryPoints[mCurrentIndex]; | |
1101 | - | |
1102 | - dirty = true; | |
1103 | - play_dialog_sound(DIALOG_CLICK_SOUND); | |
1104 | - } | |
1105 | - | |
1106 | - e.type = SDL_NOEVENT; // Swallow event | |
1107 | - } | |
1108 | - } | |
1109 | -} | |
1110 | - | |
1111 | -#endif // !defined(DISABLE_NETWORKING) | |
1 | +/* | |
2 | + * network_dialog_widgets_sdl.cpp | |
3 | + | |
4 | + Copyright (C) 2001 and beyond by Woody Zenfell, III | |
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 | + * Implementation of network-dialog-specific widgets in the SDL dialog system. | |
22 | + * | |
23 | + * Created by Woody Zenfell, III on Fri Sep 28 2001. | |
24 | + * | |
25 | + * Mar 1, 2002 (Woody Zenfell): Added new w_entry_point_selector widget. | |
26 | + */ | |
27 | + | |
28 | +#if !defined(DISABLE_NETWORKING) | |
29 | + | |
30 | +#include "network_dialog_widgets_sdl.h" | |
31 | + | |
32 | +#include "screen_drawing.h" | |
33 | +#include "sdl_fonts.h" | |
34 | +#include "interface.h" | |
35 | +#include "network.h" | |
36 | + | |
37 | +// these next are for playing with shape-drawing | |
38 | +#include "player.h" | |
39 | +#include "HUDRenderer.h" | |
40 | +#include "shell.h" | |
41 | +#include "collection_definition.h" | |
42 | + | |
43 | +// here are some for w_entry_point_selector | |
44 | +#include "preferences.h" | |
45 | +#include "screen.h" | |
46 | + | |
47 | +// for TS_GetCString, get shared ref rather than copying string. | |
48 | +#include "TextStrings.h" | |
49 | + | |
50 | +#include "TextLayoutHelper.h" | |
51 | + | |
52 | +#include <string> | |
53 | + | |
54 | + | |
55 | +// jkvw: I'm putting this here because we only really want it for find_item_index_in_vecotr, | |
56 | +// and of course we shouldn't be doing that anyway :). | |
57 | +bool operator==(const prospective_joiner_info &left, const prospective_joiner_info &right) | |
58 | +{ return left.stream_id == right.stream_id; } | |
59 | + | |
60 | + | |
61 | +////// helper functions ////// | |
62 | +// Actually, as it turns out, there should be a generic STL algorithm that does this, I think. | |
63 | +// Well, w_found_players ought to be using a set<> or similar anyway, much more natural. | |
64 | +// Shrug, this was what I came up with before I knew anything about STL, and I'm too lazy to change it. | |
65 | +template<class T> | |
66 | +static const size_t | |
67 | +find_item_index_in_vector(const T& inItem, const vector<T>& inVector) { | |
68 | + typename vector<T>::const_iterator i = inVector.begin(); | |
69 | + typename vector<T>::const_iterator end = inVector.end(); | |
70 | + size_t index = 0; | |
71 | + | |
72 | + while(i != end) { | |
73 | + if(*i == inItem) | |
74 | + return index; | |
75 | + | |
76 | + index++; | |
77 | + i++; | |
78 | + } | |
79 | + | |
80 | + // Didn't find it | |
81 | + return -1; | |
82 | +} | |
83 | + | |
84 | + | |
85 | + | |
86 | +////// w_found_players ////// | |
87 | +void | |
88 | +w_found_players::found_player(prospective_joiner_info &player) { | |
89 | + | |
90 | + // Found one | |
91 | + found_players.push_back(player); | |
92 | + | |
93 | + // List it | |
94 | + list_player(player); | |
95 | +} | |
96 | + | |
97 | + | |
98 | +void | |
99 | +w_found_players::hide_player(const prospective_joiner_info &player) { | |
100 | + found_players.push_back(player); | |
101 | + | |
102 | + unlist_player(player); | |
103 | +} | |
104 | + | |
105 | + | |
106 | +void | |
107 | +w_found_players::list_player(prospective_joiner_info &player) { | |
108 | + listed_players.push_back(player); | |
109 | + num_items = listed_players.size(); | |
110 | + new_items(); | |
111 | +} | |
112 | + | |
113 | +void w_found_players::update_player(prospective_joiner_info &player) { | |
114 | + unlist_player(player); | |
115 | + list_player(player); | |
116 | +} | |
117 | + | |
118 | + | |
119 | +void | |
120 | +w_found_players::unlist_player(const prospective_joiner_info &player) { | |
121 | + size_t theIndex = find_item_index_in_vector(player, listed_players); | |
122 | + if(theIndex == -1) | |
123 | + return; | |
124 | + | |
125 | + listed_players.erase(listed_players.begin() + theIndex); | |
126 | + | |
127 | + size_t old_top_item = top_item; | |
128 | + | |
129 | + num_items = listed_players.size(); | |
130 | + new_items(); | |
131 | + | |
132 | + // If the element deleted was the top item or before the top item, shift view up an item to compensate (if there is anything "up"). | |
133 | + if(theIndex <= old_top_item && old_top_item > 0) | |
134 | + old_top_item--; | |
135 | + | |
136 | + // Reconcile overhang, if needed. | |
137 | + if(old_top_item + shown_items > num_items && num_items >= shown_items) | |
138 | + set_top_item(num_items - shown_items); | |
139 | + else | |
140 | + set_top_item(old_top_item); | |
141 | +} | |
142 | + | |
143 | + | |
144 | +void | |
145 | +w_found_players::item_selected() { | |
146 | + if(player_selected_callback != NULL) | |
147 | + player_selected_callback(this, listed_players[get_selection()]); | |
148 | +} | |
149 | + | |
150 | + | |
151 | +// ZZZ: this is pretty ugly, it assumes that the callback will remove players from the widget. | |
152 | +// Fortunately, that's the case currently. :) | |
153 | +void | |
154 | +w_found_players::callback_on_all_items() { | |
155 | + if(player_selected_callback != NULL) { | |
156 | + for (vector<prospective_joiner_info>::iterator it = listed_players.begin(); it != listed_players.end(); it++) { | |
157 | + player_selected_callback(this, *it); | |
158 | + } | |
159 | + } | |
160 | +} | |
161 | + | |
162 | +void | |
163 | +w_found_players::draw_item(vector<prospective_joiner_info>::const_iterator i, SDL_Surface *s, int16 x, int16 y, uint16 width, bool selected) const { | |
164 | + char theNameBuffer[SSLP_MAX_NAME_LENGTH + 12]; | |
165 | + | |
166 | + pstrncpy((unsigned char*)theNameBuffer, (unsigned char*)(*i).name, SSLP_MAX_NAME_LENGTH - 1); | |
167 | + a1_p2cstr((unsigned char *) theNameBuffer); | |
168 | + if ((*i).gathering) { | |
169 | + strcat(theNameBuffer, " (gathering)"); | |
170 | + } | |
171 | + | |
172 | + int computed_x = x + (width - text_width(theNameBuffer, font, style)) / 2; | |
173 | + int computed_y = y + font->get_ascent(); | |
174 | + | |
175 | + //unsigned char text_length = (*i)->sslps_name[0]; | |
176 | + | |
177 | + //if(text_length > SSLP_MAX_NAME_LENGTH - 1) | |
178 | + // text_length = SSLP_MAX_NAME_LENGTH - 1; | |
179 | + if ((*i).gathering) { | |
180 | + draw_text(s, theNameBuffer, computed_x, computed_y, get_theme_color(ITEM_WIDGET, DISABLED_STATE), font, style); | |
181 | + } else { | |
182 | + draw_text(s, /*&((*i)->sslps_name[1]), text_length,*/ theNameBuffer, computed_x, computed_y, | |
183 | + selected ? get_theme_color(ITEM_WIDGET, ACTIVE_STATE) : get_theme_color(ITEM_WIDGET, DEFAULT_STATE), font, style); | |
184 | + } | |
185 | +} | |
186 | + | |
187 | +////// w_players_in_game2 ////// | |
188 | + | |
189 | +// I guess these should be computed more dynamically, but it wasn't obvious the best way to do that. | |
190 | +// These values work well for the standard player shapes, anyway. | |
191 | +enum { | |
192 | + kWPIG2Width = 600, // widget width | |
193 | + kWPIG2Height = 142, // widget height (actual height will differ if postgame_layout) | |
194 | + kMaxHeadroom = 53, // height above player origin (approx. navel) of tallest player shape | |
195 | + kNameOffset = 80, // how far below player origin baseline of player's name should appear | |
196 | + kNumNameOffsets = MAXIMUM_NUMBER_OF_PLAYERS, // used to resolve overlapping names | |
197 | + kNameMargin = 6, // names overlap if their edges are fewer than this many pixels apart | |
198 | + kNormalPlayerOffset = kMaxHeadroom, | |
199 | + kNormalNameTotalOffset = kNormalPlayerOffset + kNameOffset, | |
200 | + | |
201 | +// kPostgameTopMargin = 70, // how much extra space is at the top of widget in postgame layout | |
202 | + kPostgameTopMargin = 190, // For postgame layout without chat window, we can use a lot more space. (use 70 to coexist with full chat UI) | |
203 | + kPostgameBottomMargin = 6, // how much extra space is at the bottom of widget in postgame layout | |
204 | + kBarBottomOffset = 80, // how far below player origin score/kill bars should start | |
205 | + kBarWidth = 10, // how wide a kill/score bar should be | |
206 | + kBarOffsetX = 20, // how much to offset a bar so it won't draw directly on a player | |
207 | + kBevelSize = 2, // how much "depth effect" (in pixels around the border) bars have | |
208 | + kUseLegendThreshhold = 5, // with this many players or more, use legend for kills/deaths rather than print at bar labels | |
209 | + kPostgamePlayerOffset = kPostgameTopMargin + kMaxHeadroom, | |
210 | + kPostgameNameTotalOffset = kPostgamePlayerOffset + kNameOffset, | |
211 | + kBarBottomTotalOffset = kPostgamePlayerOffset + kBarBottomOffset, | |
212 | + kPostgameHeight = kPostgameTopMargin + kWPIG2Height + kPostgameBottomMargin | |
213 | +}; | |
214 | + | |
215 | +/* | |
216 | +// These can't see postgame_layout. Duh. And the idea here was to avoid having the constants above | |
217 | +// in a header file (as would be needed for making inline methods) where they would force extra | |
218 | +// recompilation... burrito. Macros it is. | |
219 | +static inline int | |
220 | +get_player_y_offset() { return postgame_layout ? kPostgamePlayerOffset : kNormalPlayerOffset; } | |
221 | + | |
222 | +static inline int | |
223 | +get_name_y_offset() { return postgame_layout ? kPostgameNameTotalOffset : kNormalNameTotalOffset; } | |
224 | +*/ | |
225 | + | |
226 | +#define get_player_y_offset() (postgame_layout ? kPostgamePlayerOffset : kNormalPlayerOffset) | |
227 | +#define get_name_y_offset() (postgame_layout ? kPostgameNameTotalOffset : kNormalNameTotalOffset) | |
228 | + | |
229 | +// Here I divide each piece of space into N pieces (where N is the number of things to draw) | |
230 | +// each item is drawn in the center of its space. This pitches them a little more widely than | |
231 | +// is used in the separately-drawn strategy. | |
232 | +// The computation used is (I from 0 to N-1, W is width) for the center: | |
233 | +// ((I + .5) / N) * W | |
234 | +// == WI + .5W / N | |
235 | +// == W*(2I + 1) / 2N | |
236 | +static inline int | |
237 | +get_wide_spaced_center_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
238 | + return left_x + (((2 * (int)index + 1) * available_width) / (2 * (int)num_items)); | |
239 | +} | |
240 | + | |
241 | +// for the left: | |
242 | +// I/N * W | |
243 | +// == WI/N | |
244 | +static inline int | |
245 | +get_wide_spaced_left_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
246 | + return left_x + (((int)index * available_width) / (int)num_items); | |
247 | +} | |
248 | + | |
249 | +// width is easy... | |
250 | +// note though that the actual distances between left_offsets may vary slightly from this width due to rounding. | |
251 | +static inline int | |
252 | +get_wide_spaced_width(int available_width, size_t num_items) { | |
253 | + return available_width / (int)num_items; | |
254 | +} | |
255 | + | |
256 | + | |
257 | +// Horizontal layout centers single player at 1/2 the width; two players at 1/3 and 2/3; three at 1/4, 2/4, 3/4.... | |
258 | +// Doing (I * W) / N rather than the more natural (I/N) * W may give more accurate results with integer math. | |
259 | +static inline int | |
260 | +get_close_spaced_center_offset(int left_x, int available_width, size_t index, size_t num_items) { | |
261 | + return left_x + ((((int)index + 1) * available_width) / ((int)num_items + 1)); | |
262 | +} | |
263 | + | |
264 | +static inline int | |
265 | +get_close_spaced_width(int available_width, size_t num_items) { | |
266 | + return available_width / ((int)num_items + 1); | |
267 | +} | |
268 | + | |
269 | + | |
270 | +w_players_in_game2::w_players_in_game2(bool inPostgameLayout) : | |
271 | + widget(MESSAGE_WIDGET), displaying_actual_information(false), postgame_layout(inPostgameLayout), | |
272 | + draw_carnage_graph(false), num_valid_net_rankings(0), selected_player(NONE), | |
273 | + clump_players_by_team(false), draw_scores_not_carnage(false) | |
274 | +{ | |
275 | + rect.w = kWPIG2Width; | |
276 | + rect.h = postgame_layout ? kPostgameHeight : kWPIG2Height; | |
277 | + | |
278 | + saved_min_width = rect.w; | |
279 | + saved_min_height = rect.h; | |
280 | +} | |
281 | + | |
282 | + | |
283 | +w_players_in_game2::~w_players_in_game2() { | |
284 | + clear_vector(); | |
285 | +} | |
286 | + | |
287 | + | |
288 | +void | |
289 | +w_players_in_game2::update_display(bool inFromDynamicWorld /* default=false */) { | |
290 | + // Start over - wipe out our local player-storage | |
291 | + clear_vector(); | |
292 | + | |
293 | + // Wipe out references to players through teams | |
294 | + for(int i = 0; i < NUMBER_OF_TEAM_COLORS; i++) | |
295 | + players_on_team[i].clear(); | |
296 | + | |
297 | + // Find the number of players | |
298 | + int num_players; | |
299 | + if(inFromDynamicWorld) | |
300 | + num_players = dynamic_world->player_count; | |
301 | + else | |
302 | + num_players = displaying_actual_information ? NetGetNumberOfPlayers() : 0; | |
303 | + | |
304 | + // Fill in the entries | |
305 | + for(int i = 0; i < num_players; i++) { | |
306 | + player_entry2 thePlayerEntry; | |
307 | + | |
308 | + int thePlayerTeam; | |
309 | + int thePlayerColor; | |
310 | + | |
311 | + if(inFromDynamicWorld) { | |
312 | + // Get player information from dynamic_world | |
313 | + player_data* thePlayerData = get_player_data(i); | |
314 | + | |
315 | + // Copy the player name. We will store it as a cstring... | |
316 | + strncpy(thePlayerEntry.player_name, thePlayerData->name, MAXIMUM_PLAYER_NAME_LENGTH + 1); | |
317 | + | |
318 | + // Look up colors | |
319 | + thePlayerTeam = thePlayerData->team; | |
320 | + thePlayerColor = thePlayerData->color; | |
321 | + } | |
322 | + else { | |
323 | + // Get player information from topology | |
324 | + player_info* thePlayerInfo = (player_info*)NetGetPlayerData(i); | |
325 | + | |
326 | + // Alias the player entry's name field as a pstring | |
327 | + unsigned char* thePlayerEntryNameP = (unsigned char*) thePlayerEntry.player_name; | |
328 | + | |
329 | + // Copy the player name. We will store it as a cstring... | |
330 | + pstrncpy(thePlayerEntryNameP, thePlayerInfo->name, MAXIMUM_PLAYER_NAME_LENGTH + 1); | |
331 | + | |
332 | + // In-place conversion. | |
333 | + a1_p2cstr(thePlayerEntryNameP); | |
334 | + | |
335 | + // Look up colors | |
336 | + thePlayerTeam = thePlayerInfo->team; | |
337 | + thePlayerColor = thePlayerInfo->color; | |
338 | + } | |
339 | + | |
340 | + // Set the size of the text | |
341 | + thePlayerEntry.name_width = text_width(thePlayerEntry.player_name, font, style | styleShadow); | |
342 | + | |
343 | + // Get the pixel-color for the player's team (for drawing the name) | |
344 | + thePlayerEntry.name_pixel_color = get_dialog_player_color(thePlayerTeam); | |
345 | + | |
346 | + // Set up a player image for the player (funfun) | |
347 | + thePlayerEntry.player_image = new PlayerImage; | |
348 | + thePlayerEntry.player_image->setRandomFlatteringView(); | |
349 | + thePlayerEntry.player_image->setPlayerColor(thePlayerColor); | |
350 | + thePlayerEntry.player_image->setTeamColor(thePlayerTeam); | |
351 | + | |
352 | + // Add the player to our local storage area | |
353 | + player_entries.push_back(thePlayerEntry); | |
354 | + | |
355 | + // Add a reference to the player through his team color | |
356 | + players_on_team[thePlayerTeam].push_back(i); | |
357 | + } | |
358 | + | |
359 | + dirty = true; | |
360 | +} | |
361 | + | |
362 | + | |
363 | +#if 0 | |
364 | +// this is for testing | |
365 | +static const char* sTestingNames[] = { | |
366 | + "Doctor Burrito", | |
367 | + "Carnage Asada", | |
368 | + "Bongo Bob", | |
369 | + "The Napalm Man", | |
370 | + "The Big Lebowski", | |
371 | + "lala", | |
372 | + "Prof. Windsurf", | |
373 | + "<<<-ZED-<<<" | |
374 | +}; | |
375 | + | |
376 | +void | |
377 | +w_players_in_game2::click(int, int) { | |
378 | + player_entry2 thePlayerEntry; | |
379 | + | |
380 | + // make up a name | |
381 | +/* int theNameLength = (local_random() % MAXIMUM_PLAYER_NAME_LENGTH) + 1; | |
382 | + for(int i = 0; i < theNameLength; i++) | |
383 | + thePlayerEntry.player_name[i] = 'a' + (local_random() % ('z' - 'a')); | |
384 | + thePlayerEntry.player_name[theNameLength] = '\0'; | |
385 | +// strcpy(thePlayerEntry.player_name, "The Big Lebowski"); | |
386 | +*/ | |
387 | + strcpy(thePlayerEntry.player_name, sTestingNames[local_random() % 8]); | |
388 | + | |
389 | + // Set the size of the text | |
390 | + thePlayerEntry.name_width = text_width(thePlayerEntry.player_name, font, style); | |
391 | + | |
392 | + // Make up a team-color | |
393 | + int theTeamColor = local_random() % 8; | |
394 | + | |
395 | + // Get the pixel-color for the player's team (for drawing the name) | |
396 | + thePlayerEntry.name_pixel_color = get_dialog_player_color(theTeamColor); | |
397 | + | |
398 | + // Set up a player image for the player (funfun) | |
399 | + thePlayerEntry.player_image = new PlayerImage; | |
400 | + thePlayerEntry.player_image->setRandomFlatteringView(); | |
401 | + thePlayerEntry.player_image->setTeamColor(theTeamColor); | |
402 | + | |
403 | + player_entries.push_back(thePlayerEntry); | |
404 | + | |
405 | + dirty = true; | |
406 | +} | |
407 | +#else // NOT 0 | |
408 | +void | |
409 | +w_players_in_game2::click(int x, int) { | |
410 | + if(draw_carnage_graph) { | |
411 | + | |
412 | + if(clump_players_by_team) { | |
413 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
414 | + if(ABS(x - get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings)) | |
415 | + < (get_wide_spaced_width(rect.w, num_valid_net_rankings) / 2)) | |
416 | + { | |
417 | + if(element_clicked_callback != NULL) | |
418 | + element_clicked_callback(this, clump_players_by_team, draw_carnage_graph, draw_scores_not_carnage, | |
419 | + i, net_rankings[i].color); | |
420 | + | |
421 | + break; | |
422 | + } | |
423 | + } | |
424 | + } | |
425 | + | |
426 | + else { | |
427 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
428 | + if(ABS(x - get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings)) | |
429 | + < (get_close_spaced_width(rect.w, num_valid_net_rankings) / 2)) | |
430 | + { | |
431 | + if(element_clicked_callback != NULL) | |
432 | + element_clicked_callback(this, clump_players_by_team, draw_carnage_graph, draw_scores_not_carnage, | |
433 | + i, net_rankings[i].player_index); | |
434 | + | |
435 | + break; | |
436 | + } | |
437 | + } | |
438 | + } | |
439 | + | |
440 | + } // draw_carnage_graph | |
441 | +} | |
442 | +#endif // NOT 0 | |
443 | + | |
444 | +// enable carnage reporting mode and set the data needed to draw a graph. | |
445 | +void | |
446 | +w_players_in_game2::set_graph_data(const net_rank* inRankings, int inNumRankings, int inSelectedPlayer, | |
447 | + bool inClumpPlayersByTeam, bool inDrawScoresNotCarnage) | |
448 | +{ | |
449 | + draw_carnage_graph = true; | |
450 | + num_valid_net_rankings = inNumRankings; | |
451 | + selected_player = inSelectedPlayer; | |
452 | + clump_players_by_team = inClumpPlayersByTeam; | |
453 | + draw_scores_not_carnage = inDrawScoresNotCarnage; | |
454 | + memcpy(net_rankings, inRankings, inNumRankings * sizeof(net_rank)); | |
455 | + | |
456 | + dirty = true; | |
457 | +} | |
458 | + | |
459 | + | |
460 | +void | |
461 | +w_players_in_game2::draw_player_icon(SDL_Surface* s, size_t rank_index, int center_x) const { | |
462 | + // Note, player images will not be re-fetched unless the brightness has *changed* since last draw. | |
463 | + PlayerImage* theImage = player_entries[net_rankings[rank_index].player_index].player_image; | |
464 | + if(selected_player != NONE && selected_player != rank_index) | |
465 | + theImage->setBrightness(.4f); | |
466 | + else | |
467 | + theImage->setBrightness(1.0f); | |
468 | + | |
469 | + theImage->drawAt(s, center_x, rect.y + get_player_y_offset()); | |
470 | +} | |
471 | + | |
472 | + | |
473 | +void | |
474 | +w_players_in_game2::draw_player_icons_separately(SDL_Surface* s) const { | |
475 | + if(draw_carnage_graph) { | |
476 | + // Draw in sorted order (according to net_rankings) | |
477 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
478 | + int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
479 | + | |
480 | + draw_player_icon(s, i, center_x); | |
481 | + } | |
482 | + } | |
483 | + else { | |
484 | + // Draw in "natural order" (according to topology) | |
485 | + size_t theNumPlayers = player_entries.size(); | |
486 | + for(size_t i = 0; i < theNumPlayers; i++) { | |
487 | + int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, theNumPlayers); | |
488 | + player_entries[i].player_image->drawAt(s, center_x, rect.y + get_player_y_offset()); | |
489 | + } | |
490 | + } | |
491 | +} // draw_player_icons_separately | |
492 | + | |
493 | + | |
494 | +void | |
495 | +w_players_in_game2::draw_player_icons_clumped(SDL_Surface* s) const { | |
496 | + assert(draw_carnage_graph); | |
497 | + | |
498 | + int width_per_team = get_wide_spaced_width(rect.w, num_valid_net_rankings); | |
499 | + | |
500 | + // Walk through teams, drawing each batch. | |
501 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
502 | + int team_left_x = get_wide_spaced_left_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
503 | + | |
504 | + size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
505 | + | |
506 | + assert(theNumberOfPlayersOnThisTeam > 0); | |
507 | + | |
508 | + // Walk through players on a team to draw a batch. | |
509 | + for(size_t j = 0; j < theNumberOfPlayersOnThisTeam; j++) { | |
510 | + int player_center_x = get_close_spaced_center_offset(team_left_x, width_per_team, j, theNumberOfPlayersOnThisTeam); | |
511 | + | |
512 | + // Note, player images will not be re-fetched unless the brightness has *changed* since last draw. | |
513 | + // Though Marathon does not let one view team vs team carnage (just total team carnage), I'm leaving | |
514 | + // the highlighting stuff here in case team view is later added. | |
515 | + PlayerImage* theImage = player_entries[players_on_team[net_rankings[i].color][j]].player_image; | |
516 | + if(selected_player != NONE && selected_player != i) | |
517 | + theImage->setBrightness(.4f); | |
518 | + else | |
519 | + theImage->setBrightness(1.0f); | |
520 | + | |
521 | + theImage->drawAt(s, player_center_x, rect.y + get_player_y_offset()); | |
522 | + } // players | |
523 | + } // teams | |
524 | +} // draw_player_icons_clumped | |
525 | + | |
526 | + | |
527 | +void | |
528 | +w_players_in_game2::draw_player_names_separately(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const { | |
529 | + // Now let's draw the names. Let's take care to offset names vertically if they would | |
530 | + // overlap (or come too close as defined by kNameMargin), so it's more readable. | |
531 | + | |
532 | + size_t theNumPlayers = draw_carnage_graph ? num_valid_net_rankings : player_entries.size(); | |
533 | + | |
534 | + for(size_t i = 0; i < theNumPlayers; i++) { | |
535 | + int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, theNumPlayers); | |
536 | + const player_entry2* theEntry = draw_carnage_graph ? &player_entries[net_rankings[i].player_index] : &player_entries[i]; | |
537 | + int name_x = center_x - (theEntry->name_width / 2); | |
538 | + int name_y = rect.y + get_name_y_offset(); | |
539 | + | |
540 | + // Find a suitable vertical offset | |
541 | + name_y = ioTextLayoutHelper.reserveSpaceFor(name_x - kNameMargin / 2, theEntry->name_width + kNameMargin, name_y, font->get_line_height()); | |
542 | + | |
543 | + draw_text(s, theEntry->player_name, name_x, name_y, | |
544 | + theEntry->name_pixel_color, font, style | styleShadow); | |
545 | + } | |
546 | +} | |
547 | + | |
548 | + | |
549 | +void | |
550 | +w_players_in_game2::draw_player_names_clumped(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const { | |
551 | + // Now let's draw the names. Let's take care to offset names vertically if they would | |
552 | + // overlap (or come too close as defined by kNameMargin), so it's more readable. | |
553 | + | |
554 | + // Walk through teams, drawing each batch. | |
555 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
556 | + int team_center_x = get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
557 | + | |
558 | + size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
559 | + | |
560 | + assert(theNumberOfPlayersOnThisTeam > 0); | |
561 | + | |
562 | + // Walk through players on a team to draw a batch. | |
563 | + for(size_t j = 0; j < theNumberOfPlayersOnThisTeam; j++) { | |
564 | + | |
565 | + const player_entry2* theEntry = &(player_entries[players_on_team[net_rankings[i].color][j]]); | |
566 | + int name_x = team_center_x - (theEntry->name_width / 2); | |
567 | + int name_y = rect.y + get_name_y_offset(); | |
568 | + | |
569 | + // Find a suitable vertical offset | |
570 | + name_y = ioTextLayoutHelper.reserveSpaceFor(name_x - kNameMargin/2, theEntry->name_width + kNameMargin, | |
571 | + name_y, font->get_line_height()); | |
572 | + | |
573 | + draw_text(s, theEntry->player_name, name_x, name_y, | |
574 | + theEntry->name_pixel_color, font, style | styleShadow); | |
575 | + } | |
576 | + } | |
577 | +} | |
578 | + | |
579 | + | |
580 | +int | |
581 | +w_players_in_game2::find_maximum_bar_value() const { | |
582 | + int theMaxValue = INT_MIN; | |
583 | + | |
584 | + // We track min also to handle games with negative scores. | |
585 | + int theMinValue = INT_MAX; | |
586 | + | |
587 | + if(selected_player != NONE) | |
588 | + // This way, all player vs player graphs use the same scale. | |
589 | + theMaxValue = calculate_max_kills(num_valid_net_rankings); | |
590 | + else { | |
591 | + // Note this does the right thing for suicide bars as well. | |
592 | + if(draw_scores_not_carnage) { | |
593 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
594 | + if(net_rankings[i].game_ranking > theMaxValue) | |
595 | + theMaxValue = net_rankings[i].game_ranking; | |
596 | + if(net_rankings[i].game_ranking < theMinValue) | |
597 | + theMinValue = net_rankings[i].game_ranking; | |
598 | + } | |
599 | + } else { | |
600 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
601 | + if(net_rankings[i].kills > theMaxValue) | |
602 | + theMaxValue = net_rankings[i].kills; | |
603 | + if(net_rankings[i].deaths > theMaxValue) | |
604 | + theMaxValue = net_rankings[i].deaths; | |
605 | + } | |
606 | + } | |
607 | + } | |
608 | + | |
609 | + // If all values were nonpositive, and we had at least one negative, we | |
610 | + // return the (negative) value furthest from 0. | |
611 | + // The Mac version seems to do nothing of the sort - how can it possibly | |
612 | + // display correct bars for games with negative scores like "Tag"?? | |
613 | + if(theMaxValue <= 0 && theMinValue < 0) | |
614 | + theMaxValue = theMinValue; | |
615 | + | |
616 | + return theMaxValue; | |
617 | +} | |
618 | + | |
619 | +struct bar_info { | |
620 | + int center_x; | |
621 | + int top_y; | |
622 | + uint32 pixel_color; | |
623 | + string label_text; | |
624 | +}; | |
625 | + | |
626 | +void | |
627 | +w_players_in_game2::draw_bar_or_bars(SDL_Surface* s, size_t rank_index, int center_x, int maximum_value, vector<bar_info>& outBarInfos) const { | |
628 | + // Draw score bar | |
629 | + if(draw_scores_not_carnage) { | |
630 | + bar_info theBarInfo; | |
631 | + int theScore = net_rankings[rank_index].game_ranking; | |
632 | + | |
633 | + calculate_ranking_text_for_post_game(temporary, theScore); | |
634 | + theBarInfo.label_text = temporary; // this makes a copy | |
635 | + | |
636 | + draw_bar(s, center_x, _score_color, theScore, maximum_value, theBarInfo); | |
637 | + | |
638 | + // Don't draw a "0" score label | |
639 | + if(theScore != 0) | |
640 | + outBarInfos.push_back(theBarInfo); | |
641 | + } | |
642 | + else { | |
643 | + // Draw carnage bar(s) | |
644 | + if(rank_index == selected_player) { | |
645 | + // Draw suicides/friendly-fires | |
646 | + bar_info theBarInfo; | |
647 | + | |
648 | + char* theSuicidesFormat = TS_GetCString(strNET_STATS_STRINGS, strSUICIDES_STRING); | |
649 | + int theNumberOfSuicides = net_rankings[rank_index].kills; | |
650 | + sprintf(temporary, theSuicidesFormat, theNumberOfSuicides); | |
651 | + theBarInfo.label_text = temporary; // this makes a copy | |
652 | + | |
653 | + draw_bar(s, center_x, _suicide_color, theNumberOfSuicides, maximum_value, theBarInfo); | |
654 | + | |
655 | + // Don't push a "0" label. | |
656 | + if(theNumberOfSuicides > 0) | |
657 | + outBarInfos.push_back(theBarInfo); | |
658 | + } | |
659 | + else { | |
660 | + // Draw kills and deaths | |
661 | + int theNumKills = net_rankings[rank_index].kills; | |
662 | + int theNumDeaths = net_rankings[rank_index].deaths; | |
663 | + | |
664 | + // Get strings for labelling | |
665 | + const char* theKillsFormat; | |
666 | + const char* theDeathsFormat; | |
667 | + char theKillsString[32]; | |
668 | + char theDeathsString[32]; | |
669 | + | |
670 | + // If more than threshhold bar-pairs to draw, use short form with legend rather than normal (long) form. | |
671 | + theKillsFormat = num_valid_net_rankings >= kUseLegendThreshhold ? "%d" : TS_GetCString(strNET_STATS_STRINGS, strKILLS_STRING); | |
672 | + theDeathsFormat = num_valid_net_rankings >= kUseLegendThreshhold ? "%d" : TS_GetCString(strNET_STATS_STRINGS, strDEATHS_STRING); | |
673 | + | |
674 | + // Construct labels | |
675 | + sprintf(theKillsString, theKillsFormat, theNumKills); | |
676 | + sprintf(theDeathsString, theDeathsFormat, theNumDeaths); | |
677 | + | |
678 | + // Set up bar_infos | |
679 | + bar_info theKillsBarInfo; | |
680 | + bar_info theDeathsBarInfo; | |
681 | + | |
682 | + // Copy strings into bar_infos | |
683 | + theKillsBarInfo.label_text = theKillsString; | |
684 | + theDeathsBarInfo.label_text = theDeathsString; | |
685 | + | |
686 | + // Draw shorter bar in front - looks nicer | |
687 | + // If equal, draw kills in front | |
688 | + // Put shorter bar_info in vector first so its label doesn't "leapfrog" the taller bar label in case of conflict. | |
689 | + // Don't put "0"s into the vector. | |
690 | + if(theNumKills > theNumDeaths) { | |
691 | + // Deaths bar is shorter - draw it last | |
692 | + draw_bar(s, center_x - kBarWidth / 3, _kill_color, theNumKills, maximum_value, theKillsBarInfo); | |
693 | + draw_bar(s, center_x + kBarWidth / 3, _death_color, theNumDeaths, maximum_value, theDeathsBarInfo); | |
694 | + | |
695 | + if(theNumDeaths > 0) | |
696 | + outBarInfos.push_back(theDeathsBarInfo); | |
697 | + if(theNumKills > 0) | |
698 | + outBarInfos.push_back(theKillsBarInfo); | |
699 | + } | |
700 | + else { | |
701 | + // Kills bar is shorter or equal - draw it last | |
702 | + draw_bar(s, center_x + kBarWidth / 3, _death_color, theNumDeaths, maximum_value, theDeathsBarInfo); | |
703 | + draw_bar(s, center_x - kBarWidth / 3, _kill_color, theNumKills, maximum_value, theKillsBarInfo); | |
704 | + | |
705 | + if(theNumKills > 0) | |
706 | + outBarInfos.push_back(theKillsBarInfo); | |
707 | + if(theNumDeaths > 0) | |
708 | + outBarInfos.push_back(theDeathsBarInfo); | |
709 | + } // kills and deaths (not suicides) | |
710 | + } // carnage bars | |
711 | + } // !draw_scores_not_carnage (i.e. draw carnage) | |
712 | +} // draw_bar_or_bars | |
713 | + | |
714 | + | |
715 | +void | |
716 | +w_players_in_game2::draw_bars_separately(SDL_Surface* s, vector<bar_info>& outBarInfos) const { | |
717 | + // Find the largest value we'll be drawing, so we know how to scale our bars. | |
718 | + int theMaxValue = find_maximum_bar_value(); | |
719 | + | |
720 | + // Draw the bars. | |
721 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
722 | + int center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
723 | + | |
724 | + draw_bar_or_bars(s, i, center_x + kBarOffsetX, theMaxValue, outBarInfos); | |
725 | + } // walk through rankings | |
726 | +} // draw_bars_separately | |
727 | + | |
728 | + | |
729 | +void | |
730 | +w_players_in_game2::draw_bars_clumped(SDL_Surface* s, vector<bar_info>& outBarInfos) const { | |
731 | + // Find the largest value we'll be drawing, so we know how to scale our bars. | |
732 | + int theMaxValue = find_maximum_bar_value(); | |
733 | + | |
734 | + // Walk through teams, drawing each batch. | |
735 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
736 | + int team_center_x = (int)(rect.x + ((2*i + 1) * rect.w) / (2 * num_valid_net_rankings)); | |
737 | + | |
738 | + size_t theNumberOfPlayersOnThisTeam = players_on_team[net_rankings[i].color].size(); | |
739 | + | |
740 | + assert(theNumberOfPlayersOnThisTeam > 0); | |
741 | + | |
742 | + // We will offset if we would draw on top of a player (i.e. if num players is odd), else | |
743 | + // we will draw right smack in the middle. | |
744 | + int center_x = team_center_x; | |
745 | + | |
746 | + if(theNumberOfPlayersOnThisTeam % 2 == 1) | |
747 | + center_x += kBarOffsetX; | |
748 | + | |
749 | + draw_bar_or_bars(s, i, center_x, theMaxValue, outBarInfos); | |
750 | + } // walk through rankings | |
751 | +} // draw_bars_clumped | |
752 | + | |
753 | + | |
754 | +void | |
755 | +w_players_in_game2::draw_carnage_totals(SDL_Surface* s) const { | |
756 | + for(size_t i = 0; i < num_valid_net_rankings; i++) { | |
757 | + int center_x; | |
758 | + if(clump_players_by_team) | |
759 | + center_x = get_wide_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
760 | + else | |
761 | + center_x = get_close_spaced_center_offset(rect.x, rect.w, i, num_valid_net_rankings); | |
762 | + | |
763 | + // Draw carnage score for player/team (list -N for N suicides) | |
764 | + int thePlayerCarnageScore = (selected_player == i) ? -net_rankings[i].kills : net_rankings[i].kills - net_rankings[i].deaths; | |
765 | + if(thePlayerCarnageScore == 0) | |
766 | + strcpy(temporary, "0"); | |
767 | + else | |
768 | + sprintf(temporary, "%+d", thePlayerCarnageScore); | |
769 | + | |
770 | + uint16 theBiggerFontStyle = 0; | |
771 | + font_info* theBiggerFont = get_theme_font(LABEL_WIDGET, theBiggerFontStyle); | |
772 | + | |
773 | + int theStringCenter = center_x - (text_width(temporary, theBiggerFont, theBiggerFontStyle | styleShadow) / 2); | |
774 | + | |
775 | + draw_text(s, temporary, theStringCenter, rect.y + rect.h - 1, SDL_MapRGB(s->format, 0xff, 0xff, 0xff), | |
776 | + theBiggerFont, theBiggerFontStyle | styleShadow); | |
777 | + } // walk through rankings | |
778 | +} // draw_carnage_totals | |
779 | + | |
780 | + | |
781 | +void | |
782 | +w_players_in_game2::draw_carnage_legend(SDL_Surface* s) const { | |
783 | + RGBColor theBrightestColor; | |
784 | + get_net_color(_kill_color, &theBrightestColor); | |
785 | + | |
786 | + RGBColor theMiddleColor; | |
787 | + theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
788 | + theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
789 | + theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
790 | + | |
791 | + uint32 thePixelColor = SDL_MapRGB(s->format, theMiddleColor.red >> 8, theMiddleColor.green >> 8, theMiddleColor.blue >> 8); | |
792 | + | |
793 | + draw_text(s, TS_GetCString(strNET_STATS_STRINGS, strKILLS_LEGEND), rect.x, rect.y + font->get_line_height(), | |
794 | + thePixelColor, font, style); | |
795 | + | |
796 | + get_net_color(_death_color, &theBrightestColor); | |
797 | + | |
798 | + theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
799 | + theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
800 | + theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
801 | + | |
802 | + thePixelColor = SDL_MapRGB(s->format, theMiddleColor.red >> 8, theMiddleColor.green >> 8, theMiddleColor.blue >> 8); | |
803 | + | |
804 | + draw_text(s, TS_GetCString(strNET_STATS_STRINGS, strDEATHS_LEGEND), rect.x, rect.y + 2 * font->get_line_height(), | |
805 | + thePixelColor, font, style); | |
806 | +} | |
807 | + | |
808 | + | |
809 | +void | |
810 | +w_players_in_game2::draw_bar_labels(SDL_Surface* s, const vector<bar_info>& inBarInfos, TextLayoutHelper& ioTextLayoutHelper) const { | |
811 | + size_t theNumberOfLabels = inBarInfos.size(); | |
812 | + | |
813 | + for(size_t i = 0; i < theNumberOfLabels; i++) { | |
814 | + const bar_info& theBarInfo = inBarInfos[i]; | |
815 | + | |
816 | + int theStringWidth = text_width(theBarInfo.label_text.c_str(), font, style | styleShadow); | |
817 | + int theTextX = theBarInfo.center_x - theStringWidth / 2; | |
818 | + int theBestY = ioTextLayoutHelper.reserveSpaceFor(theTextX - kNameMargin/2, | |
819 | + theStringWidth + kNameMargin, theBarInfo.top_y - 1, font->get_line_height()); | |
820 | + | |
821 | + draw_text(s, theBarInfo.label_text.c_str(), theTextX, theBestY, theBarInfo.pixel_color, font, style | styleShadow); | |
822 | + } | |
823 | +} // draw_bar_labels | |
824 | + | |
825 | + | |
826 | +void | |
827 | +w_players_in_game2::draw(SDL_Surface* s) const { | |
828 | +// printf("widget top is %d, bottom is %d\n", rect.y, rect.y + rect.h); | |
829 | + | |
830 | + // Set clip rectangle so we don't color outside the lines | |
831 | + set_drawing_clip_rectangle(rect.y, rect.x, rect.y + rect.h, rect.x + rect.w); | |
832 | + | |
833 | +// Did some tests here - it seems that text drawing is clipped by this rectangle, but rect-filling | |
834 | +// and blitting are not. (at least, on the Mac OS X version of SDL. actually, in Win 98 too.) | |
835 | +// This means that little tiny chunks of pistol fire, feet, etc. can stick around if they are drawn | |
836 | +// outside the widget (because they won't be cleared away when the widget is redrawn). I'm surrounding | |
837 | +// that with a "Somebody Else's Problem" field for the time being. | |
838 | +// set_drawing_clip_rectangle(100, 300, 200, 400); | |
839 | +// printf("clipped at <%d %d %d %d>\n", rect.y, rect.x, rect.y + rect.h, rect.x + rect.w); | |
840 | + | |
841 | + // theTextLayoutHelper exists for the duration of the draw operation | |
842 | + // helps us draw bits of text that do not overlap one another. | |
843 | + TextLayoutHelper theTextLayoutHelper; | |
844 | + | |
845 | + // theBarInfos exists for the duration of the draw operation | |
846 | + // helps us plan our bar label placement early (at draw_bar time) | |
847 | + // but draw them late (at draw_bar_labels time). | |
848 | + vector<bar_info> theBarInfos; | |
849 | + | |
850 | + // We draw in this order: | |
851 | + // Player icons | |
852 | + // Graph bars | |
853 | + // Player names | |
854 | + // Carnage totals (nobody should overlap, so timing is arbitrary) | |
855 | + // Carnage legend | |
856 | + // Bar labels | |
857 | + | |
858 | + // This order is largely for back-to-front ordering. Bar labels and player names, since | |
859 | + // they are placed using a text layout helper, will not overlap... but we draw bar labels | |
860 | + // after player names anyway (which takes considerable extra effort, note) so that the names | |
861 | + // have "first dibs" on the coveted low-in-the-widget screen area. Bar labels that want space | |
862 | + // occupied by player names will have to "float up"... which looks nicer than making the names | |
863 | + // float up to give the bar labels space. | |
864 | + | |
865 | + // Draw actual content | |
866 | + if(clump_players_by_team) { | |
867 | + // draw player icons in clumps | |
868 | + draw_player_icons_clumped(s); | |
869 | + | |
870 | + if(draw_carnage_graph) | |
871 | + draw_bars_clumped(s, theBarInfos); | |
872 | + | |
873 | + draw_player_names_clumped(s, theTextLayoutHelper); | |
874 | + } | |
875 | + else { | |
876 | + // Draw all the player icons first, so icons don't obscure names | |
877 | + draw_player_icons_separately(s); | |
878 | + | |
879 | + if(draw_carnage_graph) | |
880 | + draw_bars_separately(s, theBarInfos); | |
881 | + | |
882 | + draw_player_names_separately(s, theTextLayoutHelper); | |
883 | + } | |
884 | + | |
885 | + if(draw_carnage_graph && !draw_scores_not_carnage) { | |
886 | + draw_carnage_totals(s); | |
887 | + if(num_valid_net_rankings >= kUseLegendThreshhold) | |
888 | + draw_carnage_legend(s); | |
889 | + } | |
890 | + | |
891 | + if(draw_carnage_graph) | |
892 | + draw_bar_labels(s, theBarInfos, theTextLayoutHelper); | |
893 | + | |
894 | + // Reset clipping rectangle | |
895 | + set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX); | |
896 | +} | |
897 | + | |
898 | + | |
899 | +void | |
900 | +w_players_in_game2::clear_vector() { | |
901 | + vector<player_entry2>::const_iterator i = player_entries.begin(); | |
902 | + vector<player_entry2>::const_iterator end = player_entries.end(); | |
903 | + | |
904 | + while(i != end) { | |
905 | + // Free the name buffers associated with the elements. | |
906 | + // I don't do this in a player_entry destructor because I'm afraid of freeing the name twice | |
907 | + // (once here, when the vector's entry is destroyed, and another time when thePlayerEntry | |
908 | + // above goes out of scope). | |
909 | + if(i->player_image != NULL) | |
910 | + delete i->player_image; | |
911 | + | |
912 | + i++; | |
913 | + } | |
914 | + | |
915 | + player_entries.clear(); | |
916 | +} | |
917 | + | |
918 | + | |
919 | +void | |
920 | +w_players_in_game2::draw_bar(SDL_Surface* s, int inCenterX, int inBarColorIndex, int inBarValue, int inMaxValue, bar_info& outBarInfo) const { | |
921 | + if(inBarValue != 0) { | |
922 | + // Check that we'll draw a positive bar - value and max are either both positive or both negative. | |
923 | + if(inBarValue > 0) | |
924 | + assert(inMaxValue > 0); | |
925 | + | |
926 | + if(inBarValue < 0) | |
927 | + assert(inMaxValue < 0); | |
928 | + | |
929 | + // "- 1" leaves room for shadow style. Leave two line-heights so a kills and deaths at the top of widget resolve | |
930 | + // (thanks to TextLayoutHelper) and still have space to live. | |
931 | + int theMaximumBarHeight = kBarBottomTotalOffset - font->get_line_height() * 2 - 1; | |
932 | + int theBarHeight = (theMaximumBarHeight * inBarValue) / inMaxValue; | |
933 | + | |
934 | + SDL_Rect theBarRect; | |
935 | + | |
936 | + theBarRect.y = rect.y + kBarBottomTotalOffset - theBarHeight; | |
937 | + theBarRect.h = theBarHeight; | |
938 | + theBarRect.w = kBarWidth; | |
939 | + theBarRect.x = inCenterX - kBarWidth / 2; | |
940 | + | |
941 | + RGBColor theBrightestColor; | |
942 | + get_net_color(inBarColorIndex, &theBrightestColor); | |
943 | + | |
944 | + RGBColor theMiddleColor; | |
945 | + theMiddleColor.red = (theBrightestColor.red * 7) / 10; | |
946 | + theMiddleColor.blue = (theBrightestColor.blue * 7) / 10; | |
947 | + theMiddleColor.green = (theBrightestColor.green * 7) / 10; | |
948 | + | |
949 | + RGBColor theDarkestColor; | |
950 | + theDarkestColor.red = (theBrightestColor.red * 2) / 10; | |
951 | + theDarkestColor.blue = (theBrightestColor.blue * 2) / 10; | |
952 | + theDarkestColor.green = (theBrightestColor.green * 2) / 10; | |
953 | + | |
954 | + RGBColor* theRGBColor; | |
955 | + uint32 thePixelColor; | |
956 | + | |
957 | + // Draw the lightest part | |
958 | + theRGBColor = &theBrightestColor; | |
959 | + thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
960 | + | |
961 | + SDL_FillRect(s, &theBarRect, thePixelColor); | |
962 | + | |
963 | + // Draw the dark part | |
964 | + theRGBColor = &theDarkestColor; | |
965 | + thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
966 | + | |
967 | + SDL_Rect theDarkRect; | |
968 | + theDarkRect.x = theBarRect.x + theBarRect.w - kBevelSize; | |
969 | + theDarkRect.w = kBevelSize; | |
970 | + theDarkRect.y = theBarRect.y + kBevelSize; | |
971 | + theDarkRect.h = theBarRect.h - kBevelSize; | |
972 | + | |
973 | + if(theBarRect.h > kBevelSize) | |
974 | + SDL_FillRect(s, &theDarkRect, thePixelColor); | |
975 | + | |
976 | + // Draw the middle part | |
977 | + theRGBColor = &theMiddleColor; | |
978 | + thePixelColor = SDL_MapRGB(s->format, theRGBColor->red >> 8, theRGBColor->green >> 8, theRGBColor->blue >> 8); | |
979 | + | |
980 | + SDL_Rect theMiddleRect; | |
981 | + theMiddleRect.x = theBarRect.x + kBevelSize; | |
982 | + theMiddleRect.w = theBarRect.w - 2 * kBevelSize; | |
983 | + theMiddleRect.y = theBarRect.y + kBevelSize; | |
984 | + theMiddleRect.h = theBarRect.h - kBevelSize; | |
985 | + | |
986 | + if(theBarRect.h > kBevelSize) | |
987 | + SDL_FillRect(s, &theMiddleRect, thePixelColor); | |
988 | + | |
989 | + // Capture bar information | |
990 | + outBarInfo.center_x = inCenterX; | |
991 | + outBarInfo.top_y = theBarRect.y; | |
992 | + outBarInfo.pixel_color = thePixelColor; | |
993 | + } // if(inBarValue > 0) | |
994 | +} // draw_bar | |
995 | + | |
996 | + | |
997 | + | |
998 | + | |
999 | + | |
1000 | +////// w_entry_point_selector ////// | |
1001 | + | |
1002 | +void | |
1003 | +w_entry_point_selector::validateEntryPoint() { | |
1004 | + // Get the entry-point flags from the game type. | |
1005 | + int theAppropriateLevelTypeFlags = get_entry_point_flags_for_game_type(mGameType); | |
1006 | + | |
1007 | + mEntryPoints.clear(); | |
1008 | + | |
1009 | + // OK, get the vector of entry points. | |
1010 | + get_entry_points(mEntryPoints, theAppropriateLevelTypeFlags); | |
1011 | + | |
1012 | + if(mEntryPoints.size() <= 0) { | |
1013 | + mEntryPoint.level_number = NONE; | |
1014 | + strcpy(mEntryPoint.level_name, "(no valid options)"); | |
1015 | + mCurrentIndex = NONE; | |
1016 | + } | |
1017 | + else { | |
1018 | + unsigned i; | |
1019 | + | |
1020 | + for(i = 0; i < mEntryPoints.size(); i++) { | |
1021 | + if(mEntryPoints[i].level_number == mEntryPoint.level_number) | |
1022 | + break; | |
1023 | + } | |
1024 | + | |
1025 | + if(i == mEntryPoints.size()) { | |
1026 | + mEntryPoint = mEntryPoints[0]; | |
1027 | + mCurrentIndex = 0; | |
1028 | + } | |
1029 | + else { | |
1030 | + mEntryPoint = mEntryPoints[i]; | |
1031 | + mCurrentIndex = i; | |
1032 | + } | |
1033 | + } | |
1034 | + | |
1035 | + dirty = true; | |
1036 | +} | |
1037 | + | |
1038 | +void | |
1039 | +w_entry_point_selector::gotSelectedCallback(void* arg) { | |
1040 | + ((w_entry_point_selector*) arg)->gotSelected(); | |
1041 | +} | |
1042 | + | |
1043 | +void | |
1044 | +w_entry_point_selector::gotSelected() { | |
1045 | + if(mEntryPoints.size() > 1) { | |
1046 | + dialog theDialog; | |
1047 | + | |
1048 | + vertical_placer *placer = new vertical_placer; | |
1049 | + placer->dual_add(new w_title("SELECT LEVEL"), theDialog); | |
1050 | + | |
1051 | + placer->add(new w_spacer(), true); | |
1052 | + | |
1053 | + FileSpecifier theFile(environment_preferences->map_file); | |
1054 | + char theName[256]; | |
1055 | + theFile.GetName(theName); | |
1056 | + sprintf(temporary, "%s", theName); | |
1057 | + placer->dual_add(new w_static_text(temporary), theDialog); | |
1058 | + | |
1059 | + placer->add(new w_spacer(), true); | |
1060 | + | |
1061 | + w_levels* levels_w = new w_levels(mEntryPoints, &theDialog, 480, 16, mCurrentIndex, false); | |
1062 | + placer->dual_add(levels_w, theDialog); | |
1063 | + | |
1064 | + placer->add(new w_spacer(), true); | |
1065 | + | |
1066 | + sprintf(temporary, "%lu %s levels available", | |
1067 | + mEntryPoints.size(), | |
1068 | + TS_GetCString(kNetworkGameTypesStringSetID, mGameType) | |
1069 | + ); | |
1070 | + placer->dual_add(new w_static_text(temporary), theDialog); | |
1071 | + | |
1072 | + placer->add(new w_spacer(), true); | |
1073 | + | |
1074 | + placer->dual_add(new w_button("CANCEL", dialog_cancel, &theDialog), theDialog); | |
1075 | + | |
1076 | + theDialog.set_widget_placer(placer); | |
1077 | + | |
1078 | + clear_screen(); | |
1079 | + | |
1080 | + if(theDialog.run() == 0) { | |
1081 | + mCurrentIndex = levels_w->get_selection(); | |
1082 | + mEntryPoint = mEntryPoints[mCurrentIndex]; | |
1083 | + dirty = true; | |
1084 | + } | |
1085 | + } | |
1086 | +} | |
1087 | + | |
1088 | +void | |
1089 | +w_entry_point_selector::event(SDL_Event &e) { | |
1090 | + if (e.type == SDL_KEYDOWN) { | |
1091 | + if (e.key.keysym.sym == SDLK_LEFT || e.key.keysym.sym == SDLK_RIGHT) { | |
1092 | + size_t theNumberOfEntryPoints = mEntryPoints.size(); | |
1093 | + | |
1094 | + if(theNumberOfEntryPoints > 1) { | |
1095 | + int theDesiredOffset = (e.key.keysym.sym == SDLK_LEFT) ? -1 : 1; | |
1096 | + | |
1097 | + mCurrentIndex = (mCurrentIndex + theNumberOfEntryPoints + theDesiredOffset) | |
1098 | + % theNumberOfEntryPoints; | |
1099 | + | |
1100 | + mEntryPoint = mEntryPoints[mCurrentIndex]; | |
1101 | + | |
1102 | + dirty = true; | |
1103 | + play_dialog_sound(DIALOG_CLICK_SOUND); | |
1104 | + } | |
1105 | + | |
1106 | + e.type = SDL_NOEVENT; // Swallow event | |
1107 | + } | |
1108 | + } | |
1109 | +} | |
1110 | + | |
1111 | +#endif // !defined(DISABLE_NETWORKING) |
@@ -1,34 +1,34 @@ | ||
1 | -/* network_capabilities.cpp | |
2 | - | |
3 | - Copyright (C) 2005 and beyond by Gregory Smith | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | -*/ | |
21 | - | |
22 | -#include "network_capabilities.h" | |
23 | - | |
24 | -const string Capabilities::kGameworld = "Gameworld"; | |
25 | -const string Capabilities::kStar = "Star"; | |
26 | -const string Capabilities::kRing = "Ring"; | |
27 | -const string Capabilities::kLua = "Lua"; | |
28 | -const string Capabilities::kSpeex = "Speex"; | |
29 | -const string Capabilities::kGatherable = "Gatherable"; | |
30 | -const string Capabilities::kZippedData = "ZippedData"; | |
31 | -const string Capabilities::kNetworkStats = "NetworkStats"; | |
32 | -const string Capabilities::kRugby = "Rugby"; | |
33 | - | |
34 | - | |
1 | +/* network_capabilities.cpp | |
2 | + | |
3 | + Copyright (C) 2005 and beyond by Gregory Smith | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | +*/ | |
21 | + | |
22 | +#include "network_capabilities.h" | |
23 | + | |
24 | +const string Capabilities::kGameworld = "Gameworld"; | |
25 | +const string Capabilities::kStar = "Star"; | |
26 | +const string Capabilities::kRing = "Ring"; | |
27 | +const string Capabilities::kLua = "Lua"; | |
28 | +const string Capabilities::kSpeex = "Speex"; | |
29 | +const string Capabilities::kGatherable = "Gatherable"; | |
30 | +const string Capabilities::kZippedData = "ZippedData"; | |
31 | +const string Capabilities::kNetworkStats = "NetworkStats"; | |
32 | +const string Capabilities::kRugby = "Rugby"; | |
33 | + | |
34 | + |
@@ -1,311 +1,311 @@ | ||
1 | -/* | |
2 | - * network_microphone_shared.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Utility/support routines for various platforms' network microphone implementations. | |
29 | - * | |
30 | - * Created by woody Jan 31, 2003, largely from code originally in network_microphone_sdl_win32.cpp. | |
31 | - * | |
32 | - * May 28, 2003 (Gregory Smith): | |
33 | - * Speex audio compression | |
34 | - */ | |
35 | - | |
36 | -#if !defined(DISABLE_NETWORKING) | |
37 | - | |
38 | -#include "cseries.h" | |
39 | -#include "network_speaker_sdl.h" | |
40 | -#include "network_data_formats.h" | |
41 | -#include "network_distribution_types.h" | |
42 | - | |
43 | -#include <algorithm> // for STL pair<> type | |
44 | - | |
45 | -#ifdef SPEEX | |
46 | -#include "speex/speex.h" | |
47 | -#include "preferences.h" | |
48 | -#include "network_speex.h" | |
49 | -#endif | |
50 | - | |
51 | -#include "map.h" // _force_unique_teams! | |
52 | - | |
53 | -using namespace std; | |
54 | - | |
55 | -#ifdef DEBUG | |
56 | -// For testing: don't send audio on the network - pass it directly to network_speaker. | |
57 | -//#define MICROPHONE_LOCAL_LOOPBACK | |
58 | -#endif | |
59 | - | |
60 | -enum { | |
61 | - kNetworkAudioSamplesPerPacket = 800, | |
62 | -}; | |
63 | - | |
64 | -static uint32 sSamplesPerSecond = 0; | |
65 | -static uint32 sStereo = false; | |
66 | -static uint32 s16Bit = false; | |
67 | -static int32 sCaptureBytesPerPacket = 0; | |
68 | -static uint32 sNumberOfBytesPerSample = 0; | |
69 | - | |
70 | -static _fixed rate = 0; | |
71 | -static _fixed counter = 0; | |
72 | - | |
73 | -bool announce_microphone_capture_format(uint32 inSamplesPerSecond, bool inStereo, bool in16Bit) | |
74 | -{ | |
75 | - sSamplesPerSecond = inSamplesPerSecond; | |
76 | - sStereo = inStereo; | |
77 | - s16Bit = in16Bit; | |
78 | - | |
79 | - rate = (_fixed) (FIXED_ONE * (float) inSamplesPerSecond / (float) kNetworkAudioSampleRate); | |
80 | - counter = 0; | |
81 | - | |
82 | - sNumberOfBytesPerSample = (inStereo ? 2 : 1) * (in16Bit ? 2 : 1); | |
83 | - sCaptureBytesPerPacket = (uint32) ((rate * kNetworkAudioSamplesPerPacket) >> 16) * sNumberOfBytesPerSample; | |
84 | - | |
85 | - return true; | |
86 | -}; | |
87 | - | |
88 | - | |
89 | -int32 | |
90 | -get_capture_byte_count_per_packet() { | |
91 | - // Catch folks who call us without specifying a rate first | |
92 | - assert(sSamplesPerSecond > 0); | |
93 | - | |
94 | - return sCaptureBytesPerPacket; | |
95 | -} | |
96 | - | |
97 | -template<bool stereo, bool sixteenBit> | |
98 | -inline int16 getSample(void *data) | |
99 | -{ | |
100 | - if (sixteenBit) | |
101 | - { | |
102 | - if (stereo) | |
103 | - { | |
104 | - return ((int16 *) data)[0] >> 1 + ((int16 *)data)[1] >> 1; | |
105 | - } | |
106 | - else | |
107 | - { | |
108 | - return ((int16 *) data)[0]; | |
109 | - } | |
110 | - } | |
111 | - else | |
112 | - { | |
113 | - if (stereo) | |
114 | - { | |
115 | - return ((((uint8 *) data)[0] / 2 + ((uint8 *) data)[1] / 2) - 128) << 8; | |
116 | - } | |
117 | - else | |
118 | - { | |
119 | - return (((uint8 *) data)[0] - 128) << 8; | |
120 | - } | |
121 | - } | |
122 | -} | |
123 | - | |
124 | -#ifdef SPEEX | |
125 | -template <bool stereo, bool sixteenBit> | |
126 | -int32 copy_and_speex_encode_template(uint8* outStorage, void* inStorage, int32 inCount) | |
127 | -{ | |
128 | - static int16 frame[160]; | |
129 | - static int storedSamples = 0; | |
130 | - int bytesWritten = 0; | |
131 | - | |
132 | - while (inCount > 0) | |
133 | - { | |
134 | - int16 left_sample = getSample<stereo, sixteenBit>(inStorage); | |
135 | - | |
136 | - if (inCount > sNumberOfBytesPerSample) | |
137 | - { | |
138 | - uint8* data = (uint8 *) inStorage + sNumberOfBytesPerSample; | |
139 | - int16 right_sample = getSample<stereo, sixteenBit>(data); | |
140 | - | |
141 | - int32 sample = left_sample + (((right_sample - left_sample) * (counter & 0xffff)) >> 16); | |
142 | - frame[storedSamples++] = (int16) sample; | |
143 | - } | |
144 | - else | |
145 | - { | |
146 | - frame[storedSamples++] = left_sample; | |
147 | - } | |
148 | - | |
149 | - // advance data | |
150 | - counter += rate; | |
151 | - if (counter >= 0x10000) | |
152 | - { | |
153 | - int count = counter >> 16; | |
154 | - counter &= 0xffff; | |
155 | - inStorage = (uint8 *) inStorage + sNumberOfBytesPerSample * count; | |
156 | - inCount -= sNumberOfBytesPerSample * count; | |
157 | - } | |
158 | - | |
159 | - if (storedSamples >= 160) | |
160 | - { | |
161 | - // encode the frame | |
162 | - speex_bits_reset(&gEncoderBits); | |
163 | - speex_encode_int(gEncoderState, frame, &gEncoderBits); | |
164 | - uint8 nbytes = speex_bits_write(&gEncoderBits, reinterpret_cast<char *>(outStorage) + 1, 200); | |
165 | - bytesWritten += nbytes + 1; | |
166 | - // first put the size of this frame in storage | |
167 | - *(outStorage) = nbytes; | |
168 | - outStorage += nbytes + 1; | |
169 | - | |
170 | - storedSamples = 0; | |
171 | - } | |
172 | - | |
173 | - } | |
174 | - | |
175 | - return bytesWritten; | |
176 | -} | |
177 | - | |
178 | -int32 copy_and_speex_encode(uint8* outStorage, void *inStorage, int32 inCount) | |
179 | -{ | |
180 | - if (sStereo) | |
181 | - { | |
182 | - if (s16Bit) | |
183 | - { | |
184 | - return copy_and_speex_encode_template<true, true>(outStorage, inStorage, inCount); | |
185 | - } | |
186 | - else | |
187 | - { | |
188 | - return copy_and_speex_encode_template<true, false>(outStorage, inStorage, inCount); | |
189 | - } | |
190 | - } | |
191 | - else | |
192 | - { | |
193 | - if (s16Bit) | |
194 | - { | |
195 | - return copy_and_speex_encode_template<false, true>(outStorage, inStorage, inCount); | |
196 | - } | |
197 | - else | |
198 | - { | |
199 | - return copy_and_speex_encode_template<false, false>(outStorage, inStorage, inCount); | |
200 | - } | |
201 | - } | |
202 | -} | |
203 | - | |
204 | -static void | |
205 | -send_audio_data(void* inData, short inSize) { | |
206 | -#ifdef MICROPHONE_LOCAL_LOOPBACK | |
207 | -#include "network_sound.h" | |
208 | - received_network_audio_proc(inData, inSize, 0); | |
209 | -#else | |
210 | - NetDistributeInformation(kNewNetworkAudioDistributionTypeID, inData, inSize, false, !(GET_GAME_OPTIONS() & _force_unique_teams)); | |
211 | -#endif | |
212 | -} | |
213 | - | |
214 | -#endif | |
215 | - | |
216 | -int32 | |
217 | -copy_and_send_audio_data(uint8* inFirstChunkReadPosition, int32 inFirstChunkBytesRemaining, | |
218 | - uint8* inSecondChunkReadPosition, int32 inSecondChunkBytesRemaining, | |
219 | - bool inForceSend) { | |
220 | - | |
221 | - // Make sure the capture format has been announced to us | |
222 | - assert(sSamplesPerSecond > 0); | |
223 | - | |
224 | - // caller better not be splitting chunks up mid-sample | |
225 | - if (inFirstChunkBytesRemaining % sNumberOfBytesPerSample) | |
226 | - { | |
227 | - inFirstChunkBytesRemaining -= (inFirstChunkBytesRemaining % sNumberOfBytesPerSample); | |
228 | - assert(inSecondChunkBytesRemaining == 0); | |
229 | - } | |
230 | - | |
231 | - if (inSecondChunkBytesRemaining % sNumberOfBytesPerSample) | |
232 | - { | |
233 | - inSecondChunkBytesRemaining -= (inSecondChunkBytesRemaining % sNumberOfBytesPerSample); | |
234 | - } | |
235 | - | |
236 | - // Let runtime system worry about allocating and freeing the buffer (and don't do it on the stack). | |
237 | - // assume Speex will not encode kNetworkAudioSamplesPerPacket samples to be larger than kNetworkAudioSamplesPerPacket * kNetworkAudioBytesPerFrame! | |
238 | - static uint8 sOutgoingPacketBuffer[kNetworkAudioSamplesPerPacket * kNetworkAudioBytesPerFrame + SIZEOF_network_audio_header]; | |
239 | - | |
240 | - network_audio_header theHeader; | |
241 | -#ifdef SPEEX | |
242 | - theHeader.mReserved = 1; | |
243 | - theHeader.mFlags = 0; | |
244 | - | |
245 | - network_audio_header_NET* theHeader_NET = (network_audio_header_NET*) sOutgoingPacketBuffer; | |
246 | - | |
247 | - netcpy(theHeader_NET, &theHeader); | |
248 | - | |
249 | - uint8* theOutgoingAudioData = &sOutgoingPacketBuffer[SIZEOF_network_audio_header]; | |
250 | - | |
251 | - // Do the copying and sending | |
252 | - pair<int32, int32> theBytesConsumed; | |
253 | - int32 theTotalCaptureBytesConsumed = 0; | |
254 | - | |
255 | - // Keep sending if we have data and either we're squeezing out the last drop or we have a packet's-worth. | |
256 | - while(inFirstChunkBytesRemaining >= static_cast<int32>(sNumberOfBytesPerSample) && | |
257 | - (inForceSend || inFirstChunkBytesRemaining + inSecondChunkBytesRemaining >= (int32)sCaptureBytesPerPacket)) { | |
258 | - | |
259 | - int captureBytesToCopy = std::min(inFirstChunkBytesRemaining, sCaptureBytesPerPacket); | |
260 | - int bytesCopied = copy_and_speex_encode(theOutgoingAudioData, inFirstChunkReadPosition, captureBytesToCopy); | |
261 | - | |
262 | - theTotalCaptureBytesConsumed += captureBytesToCopy; | |
263 | - | |
264 | - // If there's space left in the packet and we have a second chunk, start on it. | |
265 | - if(captureBytesToCopy < sCaptureBytesPerPacket && inSecondChunkBytesRemaining > 0) { | |
266 | - int secondCaptureBytesToCopy = std::min(sCaptureBytesPerPacket - captureBytesToCopy, inSecondChunkBytesRemaining); | |
267 | - | |
268 | - int secondBytesCopied = copy_and_speex_encode(&theOutgoingAudioData[bytesCopied], inSecondChunkReadPosition, secondCaptureBytesToCopy); | |
269 | - theTotalCaptureBytesConsumed += secondCaptureBytesToCopy; | |
270 | - | |
271 | - send_audio_data((void *) sOutgoingPacketBuffer, | |
272 | - SIZEOF_network_audio_header + bytesCopied + secondBytesCopied); | |
273 | - | |
274 | - // Update the second chunk position and length | |
275 | - inSecondChunkReadPosition += secondCaptureBytesToCopy; | |
276 | - inSecondChunkBytesRemaining -= secondCaptureBytesToCopy; | |
277 | - } | |
278 | - // Else, either we've filled up a packet or exhausted the buffer (or both). | |
279 | - else { | |
280 | - send_audio_data((void *) sOutgoingPacketBuffer, SIZEOF_network_audio_header + bytesCopied); | |
281 | - } | |
282 | - | |
283 | - // Update the first chunk position and length | |
284 | - inFirstChunkReadPosition += captureBytesToCopy; | |
285 | - inFirstChunkBytesRemaining -= captureBytesToCopy; | |
286 | - } | |
287 | - | |
288 | - // Now, the first chunk is exhausted. See if there's any left in the second chunk. Same rules apply. | |
289 | - while(inSecondChunkBytesRemaining >= static_cast<int32>(sNumberOfBytesPerSample) && | |
290 | - (inForceSend || inSecondChunkBytesRemaining >= (int32) sCaptureBytesPerPacket)) { | |
291 | - | |
292 | - int captureBytesToCopy = std::min(inSecondChunkBytesRemaining, sCaptureBytesPerPacket); | |
293 | - | |
294 | - int bytesCopied = copy_and_speex_encode(theOutgoingAudioData, inSecondChunkReadPosition, captureBytesToCopy); | |
295 | - | |
296 | - theTotalCaptureBytesConsumed += captureBytesToCopy; | |
297 | - | |
298 | - send_audio_data((void *) sOutgoingPacketBuffer, SIZEOF_network_audio_header + bytesCopied); | |
299 | - | |
300 | - inSecondChunkReadPosition += captureBytesToCopy; | |
301 | - inSecondChunkBytesRemaining -= captureBytesToCopy; | |
302 | - } | |
303 | - | |
304 | - return theTotalCaptureBytesConsumed; | |
305 | -#else | |
306 | - return inFirstChunkBytesRemaining + inSecondChunkBytesRemaining; // eat the entire thing, we only support speex | |
307 | -#endif | |
308 | -} | |
309 | - | |
310 | -#endif // !defined(DISABLE_NETWORKING) | |
311 | - | |
1 | +/* | |
2 | + * network_microphone_shared.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Utility/support routines for various platforms' network microphone implementations. | |
29 | + * | |
30 | + * Created by woody Jan 31, 2003, largely from code originally in network_microphone_sdl_win32.cpp. | |
31 | + * | |
32 | + * May 28, 2003 (Gregory Smith): | |
33 | + * Speex audio compression | |
34 | + */ | |
35 | + | |
36 | +#if !defined(DISABLE_NETWORKING) | |
37 | + | |
38 | +#include "cseries.h" | |
39 | +#include "network_speaker_sdl.h" | |
40 | +#include "network_data_formats.h" | |
41 | +#include "network_distribution_types.h" | |
42 | + | |
43 | +#include <algorithm> // for STL pair<> type | |
44 | + | |
45 | +#ifdef SPEEX | |
46 | +#include "speex/speex.h" | |
47 | +#include "preferences.h" | |
48 | +#include "network_speex.h" | |
49 | +#endif | |
50 | + | |
51 | +#include "map.h" // _force_unique_teams! | |
52 | + | |
53 | +using namespace std; | |
54 | + | |
55 | +#ifdef DEBUG | |
56 | +// For testing: don't send audio on the network - pass it directly to network_speaker. | |
57 | +//#define MICROPHONE_LOCAL_LOOPBACK | |
58 | +#endif | |
59 | + | |
60 | +enum { | |
61 | + kNetworkAudioSamplesPerPacket = 800, | |
62 | +}; | |
63 | + | |
64 | +static uint32 sSamplesPerSecond = 0; | |
65 | +static uint32 sStereo = false; | |
66 | +static uint32 s16Bit = false; | |
67 | +static int32 sCaptureBytesPerPacket = 0; | |
68 | +static uint32 sNumberOfBytesPerSample = 0; | |
69 | + | |
70 | +static _fixed rate = 0; | |
71 | +static _fixed counter = 0; | |
72 | + | |
73 | +bool announce_microphone_capture_format(uint32 inSamplesPerSecond, bool inStereo, bool in16Bit) | |
74 | +{ | |
75 | + sSamplesPerSecond = inSamplesPerSecond; | |
76 | + sStereo = inStereo; | |
77 | + s16Bit = in16Bit; | |
78 | + | |
79 | + rate = (_fixed) (FIXED_ONE * (float) inSamplesPerSecond / (float) kNetworkAudioSampleRate); | |
80 | + counter = 0; | |
81 | + | |
82 | + sNumberOfBytesPerSample = (inStereo ? 2 : 1) * (in16Bit ? 2 : 1); | |
83 | + sCaptureBytesPerPacket = (uint32) ((rate * kNetworkAudioSamplesPerPacket) >> 16) * sNumberOfBytesPerSample; | |
84 | + | |
85 | + return true; | |
86 | +}; | |
87 | + | |
88 | + | |
89 | +int32 | |
90 | +get_capture_byte_count_per_packet() { | |
91 | + // Catch folks who call us without specifying a rate first | |
92 | + assert(sSamplesPerSecond > 0); | |
93 | + | |
94 | + return sCaptureBytesPerPacket; | |
95 | +} | |
96 | + | |
97 | +template<bool stereo, bool sixteenBit> | |
98 | +inline int16 getSample(void *data) | |
99 | +{ | |
100 | + if (sixteenBit) | |
101 | + { | |
102 | + if (stereo) | |
103 | + { | |
104 | + return ((int16 *) data)[0] >> 1 + ((int16 *)data)[1] >> 1; | |
105 | + } | |
106 | + else | |
107 | + { | |
108 | + return ((int16 *) data)[0]; | |
109 | + } | |
110 | + } | |
111 | + else | |
112 | + { | |
113 | + if (stereo) | |
114 | + { | |
115 | + return ((((uint8 *) data)[0] / 2 + ((uint8 *) data)[1] / 2) - 128) << 8; | |
116 | + } | |
117 | + else | |
118 | + { | |
119 | + return (((uint8 *) data)[0] - 128) << 8; | |
120 | + } | |
121 | + } | |
122 | +} | |
123 | + | |
124 | +#ifdef SPEEX | |
125 | +template <bool stereo, bool sixteenBit> | |
126 | +int32 copy_and_speex_encode_template(uint8* outStorage, void* inStorage, int32 inCount) | |
127 | +{ | |
128 | + static int16 frame[160]; | |
129 | + static int storedSamples = 0; | |
130 | + int bytesWritten = 0; | |
131 | + | |
132 | + while (inCount > 0) | |
133 | + { | |
134 | + int16 left_sample = getSample<stereo, sixteenBit>(inStorage); | |
135 | + | |
136 | + if (inCount > sNumberOfBytesPerSample) | |
137 | + { | |
138 | + uint8* data = (uint8 *) inStorage + sNumberOfBytesPerSample; | |
139 | + int16 right_sample = getSample<stereo, sixteenBit>(data); | |
140 | + | |
141 | + int32 sample = left_sample + (((right_sample - left_sample) * (counter & 0xffff)) >> 16); | |
142 | + frame[storedSamples++] = (int16) sample; | |
143 | + } | |
144 | + else | |
145 | + { | |
146 | + frame[storedSamples++] = left_sample; | |
147 | + } | |
148 | + | |
149 | + // advance data | |
150 | + counter += rate; | |
151 | + if (counter >= 0x10000) | |
152 | + { | |
153 | + int count = counter >> 16; | |
154 | + counter &= 0xffff; | |
155 | + inStorage = (uint8 *) inStorage + sNumberOfBytesPerSample * count; | |
156 | + inCount -= sNumberOfBytesPerSample * count; | |
157 | + } | |
158 | + | |
159 | + if (storedSamples >= 160) | |
160 | + { | |
161 | + // encode the frame | |
162 | + speex_bits_reset(&gEncoderBits); | |
163 | + speex_encode_int(gEncoderState, frame, &gEncoderBits); | |
164 | + uint8 nbytes = speex_bits_write(&gEncoderBits, reinterpret_cast<char *>(outStorage) + 1, 200); | |
165 | + bytesWritten += nbytes + 1; | |
166 | + // first put the size of this frame in storage | |
167 | + *(outStorage) = nbytes; | |
168 | + outStorage += nbytes + 1; | |
169 | + | |
170 | + storedSamples = 0; | |
171 | + } | |
172 | + | |
173 | + } | |
174 | + | |
175 | + return bytesWritten; | |
176 | +} | |
177 | + | |
178 | +int32 copy_and_speex_encode(uint8* outStorage, void *inStorage, int32 inCount) | |
179 | +{ | |
180 | + if (sStereo) | |
181 | + { | |
182 | + if (s16Bit) | |
183 | + { | |
184 | + return copy_and_speex_encode_template<true, true>(outStorage, inStorage, inCount); | |
185 | + } | |
186 | + else | |
187 | + { | |
188 | + return copy_and_speex_encode_template<true, false>(outStorage, inStorage, inCount); | |
189 | + } | |
190 | + } | |
191 | + else | |
192 | + { | |
193 | + if (s16Bit) | |
194 | + { | |
195 | + return copy_and_speex_encode_template<false, true>(outStorage, inStorage, inCount); | |
196 | + } | |
197 | + else | |
198 | + { | |
199 | + return copy_and_speex_encode_template<false, false>(outStorage, inStorage, inCount); | |
200 | + } | |
201 | + } | |
202 | +} | |
203 | + | |
204 | +static void | |
205 | +send_audio_data(void* inData, short inSize) { | |
206 | +#ifdef MICROPHONE_LOCAL_LOOPBACK | |
207 | +#include "network_sound.h" | |
208 | + received_network_audio_proc(inData, inSize, 0); | |
209 | +#else | |
210 | + NetDistributeInformation(kNewNetworkAudioDistributionTypeID, inData, inSize, false, !(GET_GAME_OPTIONS() & _force_unique_teams)); | |
211 | +#endif | |
212 | +} | |
213 | + | |
214 | +#endif | |
215 | + | |
216 | +int32 | |
217 | +copy_and_send_audio_data(uint8* inFirstChunkReadPosition, int32 inFirstChunkBytesRemaining, | |
218 | + uint8* inSecondChunkReadPosition, int32 inSecondChunkBytesRemaining, | |
219 | + bool inForceSend) { | |
220 | + | |
221 | + // Make sure the capture format has been announced to us | |
222 | + assert(sSamplesPerSecond > 0); | |
223 | + | |
224 | + // caller better not be splitting chunks up mid-sample | |
225 | + if (inFirstChunkBytesRemaining % sNumberOfBytesPerSample) | |
226 | + { | |
227 | + inFirstChunkBytesRemaining -= (inFirstChunkBytesRemaining % sNumberOfBytesPerSample); | |
228 | + assert(inSecondChunkBytesRemaining == 0); | |
229 | + } | |
230 | + | |
231 | + if (inSecondChunkBytesRemaining % sNumberOfBytesPerSample) | |
232 | + { | |
233 | + inSecondChunkBytesRemaining -= (inSecondChunkBytesRemaining % sNumberOfBytesPerSample); | |
234 | + } | |
235 | + | |
236 | + // Let runtime system worry about allocating and freeing the buffer (and don't do it on the stack). | |
237 | + // assume Speex will not encode kNetworkAudioSamplesPerPacket samples to be larger than kNetworkAudioSamplesPerPacket * kNetworkAudioBytesPerFrame! | |
238 | + static uint8 sOutgoingPacketBuffer[kNetworkAudioSamplesPerPacket * kNetworkAudioBytesPerFrame + SIZEOF_network_audio_header]; | |
239 | + | |
240 | + network_audio_header theHeader; | |
241 | +#ifdef SPEEX | |
242 | + theHeader.mReserved = 1; | |
243 | + theHeader.mFlags = 0; | |
244 | + | |
245 | + network_audio_header_NET* theHeader_NET = (network_audio_header_NET*) sOutgoingPacketBuffer; | |
246 | + | |
247 | + netcpy(theHeader_NET, &theHeader); | |
248 | + | |
249 | + uint8* theOutgoingAudioData = &sOutgoingPacketBuffer[SIZEOF_network_audio_header]; | |
250 | + | |
251 | + // Do the copying and sending | |
252 | + pair<int32, int32> theBytesConsumed; | |
253 | + int32 theTotalCaptureBytesConsumed = 0; | |
254 | + | |
255 | + // Keep sending if we have data and either we're squeezing out the last drop or we have a packet's-worth. | |
256 | + while(inFirstChunkBytesRemaining >= static_cast<int32>(sNumberOfBytesPerSample) && | |
257 | + (inForceSend || inFirstChunkBytesRemaining + inSecondChunkBytesRemaining >= (int32)sCaptureBytesPerPacket)) { | |
258 | + | |
259 | + int captureBytesToCopy = std::min(inFirstChunkBytesRemaining, sCaptureBytesPerPacket); | |
260 | + int bytesCopied = copy_and_speex_encode(theOutgoingAudioData, inFirstChunkReadPosition, captureBytesToCopy); | |
261 | + | |
262 | + theTotalCaptureBytesConsumed += captureBytesToCopy; | |
263 | + | |
264 | + // If there's space left in the packet and we have a second chunk, start on it. | |
265 | + if(captureBytesToCopy < sCaptureBytesPerPacket && inSecondChunkBytesRemaining > 0) { | |
266 | + int secondCaptureBytesToCopy = std::min(sCaptureBytesPerPacket - captureBytesToCopy, inSecondChunkBytesRemaining); | |
267 | + | |
268 | + int secondBytesCopied = copy_and_speex_encode(&theOutgoingAudioData[bytesCopied], inSecondChunkReadPosition, secondCaptureBytesToCopy); | |
269 | + theTotalCaptureBytesConsumed += secondCaptureBytesToCopy; | |
270 | + | |
271 | + send_audio_data((void *) sOutgoingPacketBuffer, | |
272 | + SIZEOF_network_audio_header + bytesCopied + secondBytesCopied); | |
273 | + | |
274 | + // Update the second chunk position and length | |
275 | + inSecondChunkReadPosition += secondCaptureBytesToCopy; | |
276 | + inSecondChunkBytesRemaining -= secondCaptureBytesToCopy; | |
277 | + } | |
278 | + // Else, either we've filled up a packet or exhausted the buffer (or both). | |
279 | + else { | |
280 | + send_audio_data((void *) sOutgoingPacketBuffer, SIZEOF_network_audio_header + bytesCopied); | |
281 | + } | |
282 | + | |
283 | + // Update the first chunk position and length | |
284 | + inFirstChunkReadPosition += captureBytesToCopy; | |
285 | + inFirstChunkBytesRemaining -= captureBytesToCopy; | |
286 | + } | |
287 | + | |
288 | + // Now, the first chunk is exhausted. See if there's any left in the second chunk. Same rules apply. | |
289 | + while(inSecondChunkBytesRemaining >= static_cast<int32>(sNumberOfBytesPerSample) && | |
290 | + (inForceSend || inSecondChunkBytesRemaining >= (int32) sCaptureBytesPerPacket)) { | |
291 | + | |
292 | + int captureBytesToCopy = std::min(inSecondChunkBytesRemaining, sCaptureBytesPerPacket); | |
293 | + | |
294 | + int bytesCopied = copy_and_speex_encode(theOutgoingAudioData, inSecondChunkReadPosition, captureBytesToCopy); | |
295 | + | |
296 | + theTotalCaptureBytesConsumed += captureBytesToCopy; | |
297 | + | |
298 | + send_audio_data((void *) sOutgoingPacketBuffer, SIZEOF_network_audio_header + bytesCopied); | |
299 | + | |
300 | + inSecondChunkReadPosition += captureBytesToCopy; | |
301 | + inSecondChunkBytesRemaining -= captureBytesToCopy; | |
302 | + } | |
303 | + | |
304 | + return theTotalCaptureBytesConsumed; | |
305 | +#else | |
306 | + return inFirstChunkBytesRemaining + inSecondChunkBytesRemaining; // eat the entire thing, we only support speex | |
307 | +#endif | |
308 | +} | |
309 | + | |
310 | +#endif // !defined(DISABLE_NETWORKING) | |
311 | + |
@@ -1,62 +1,62 @@ | ||
1 | -/* | |
2 | - * network_audio_shared.h | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Stuff shared internally between the network microphone and network speaker code. | |
29 | - * | |
30 | - * Created by woody Feb 1, 2003, largely from stuff in network_speaker_sdl.h. | |
31 | - */ | |
32 | - | |
33 | -#ifndef NETWORK_AUDIO_SHARED_H | |
34 | -#define NETWORK_AUDIO_SHARED_H | |
35 | - | |
36 | -#include "cseries.h" | |
37 | - | |
38 | -// In-memory header for sound data | |
39 | -struct network_audio_header { | |
40 | - uint32 mReserved; // Should be 0 for now, later maybe use a FourCharCode for format? shrug | |
41 | - uint32 mFlags; | |
42 | -}; | |
43 | - | |
44 | -// For network_audio_header::mFlags | |
45 | -enum { | |
46 | - kNetworkAudioForTeammatesOnlyFlag = 0x01 | |
47 | -}; | |
48 | - | |
49 | - | |
50 | -// Useful information about the network audio | |
51 | -// Note: at present, the microphone routines ignore these settings and target | |
52 | -// 11025 unsigned 8-bit mono. If you want to change formats you'll need to edit those | |
53 | -// routines too (hopefully to make them more general ;) ). Also you'll want to somehow | |
54 | -// differentiate your format from this one (use a Flag, or value in Reserved, or an | |
55 | -// entirely new distribution type, or something). | |
56 | -const bool kNetworkAudioIsStereo = false; | |
57 | -const bool kNetworkAudioIs16Bit = true; | |
58 | -const bool kNetworkAudioIsSigned8Bit = false; | |
59 | -const int kNetworkAudioSampleRate = 8000; | |
60 | -const int kNetworkAudioBytesPerFrame = (kNetworkAudioIs16Bit ? 2 : 1) * (kNetworkAudioIsStereo ? 2 : 1); | |
61 | - | |
62 | -#endif // NETWORK_AUDIO_SHARED_H | |
1 | +/* | |
2 | + * network_audio_shared.h | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Stuff shared internally between the network microphone and network speaker code. | |
29 | + * | |
30 | + * Created by woody Feb 1, 2003, largely from stuff in network_speaker_sdl.h. | |
31 | + */ | |
32 | + | |
33 | +#ifndef NETWORK_AUDIO_SHARED_H | |
34 | +#define NETWORK_AUDIO_SHARED_H | |
35 | + | |
36 | +#include "cseries.h" | |
37 | + | |
38 | +// In-memory header for sound data | |
39 | +struct network_audio_header { | |
40 | + uint32 mReserved; // Should be 0 for now, later maybe use a FourCharCode for format? shrug | |
41 | + uint32 mFlags; | |
42 | +}; | |
43 | + | |
44 | +// For network_audio_header::mFlags | |
45 | +enum { | |
46 | + kNetworkAudioForTeammatesOnlyFlag = 0x01 | |
47 | +}; | |
48 | + | |
49 | + | |
50 | +// Useful information about the network audio | |
51 | +// Note: at present, the microphone routines ignore these settings and target | |
52 | +// 11025 unsigned 8-bit mono. If you want to change formats you'll need to edit those | |
53 | +// routines too (hopefully to make them more general ;) ). Also you'll want to somehow | |
54 | +// differentiate your format from this one (use a Flag, or value in Reserved, or an | |
55 | +// entirely new distribution type, or something). | |
56 | +const bool kNetworkAudioIsStereo = false; | |
57 | +const bool kNetworkAudioIs16Bit = true; | |
58 | +const bool kNetworkAudioIsSigned8Bit = false; | |
59 | +const int kNetworkAudioSampleRate = 8000; | |
60 | +const int kNetworkAudioBytesPerFrame = (kNetworkAudioIs16Bit ? 2 : 1) * (kNetworkAudioIsStereo ? 2 : 1); | |
61 | + | |
62 | +#endif // NETWORK_AUDIO_SHARED_H |
@@ -1,70 +1,70 @@ | ||
1 | -/* | |
2 | - * network_speaker_sdl.h | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Realtime audio (network microphone) playback support for SDL platforms. | |
29 | - * | |
30 | - * Created by woody Mar 3-8, 2002. | |
31 | - * | |
32 | - * Feb 1, 2003 (Woody Zenfell): | |
33 | - * This file now describes only the interface between the SDL network speaker receiving | |
34 | - * code and the SDL sound code. The main interface to the actual speaker stuff will be | |
35 | - * in network_audio.h. | |
36 | - */ | |
37 | - | |
38 | -#ifndef NETWORK_SPEAKER_SDL_H | |
39 | -#define NETWORK_SPEAKER_SDL_H | |
40 | - | |
41 | -#include "cseries.h" | |
42 | - | |
43 | -// Flags for NetworkSpeakerSoundBuffer::mFlags below | |
44 | -enum { | |
45 | - kSoundDataIsDisposable = 0x01 // dequeuer is expected to call release_network_speaker_buffer(mData) | |
46 | -}; | |
47 | - | |
48 | -// These are used to link the network_speaker routines to the sound_sdl routines. | |
49 | -struct NetworkSpeakerSoundBufferDescriptor { | |
50 | - byte* mData; | |
51 | - uint32 mLength; | |
52 | - uint32 mFlags; | |
53 | -}; | |
54 | - | |
55 | -// To insulate callers from details of flag storage | |
56 | -__inline__ bool | |
57 | -is_sound_data_disposable(NetworkSpeakerSoundBufferDescriptor* inBuffer) { | |
58 | - return (inBuffer->mFlags & kSoundDataIsDisposable) ? true : false; | |
59 | -} | |
60 | - | |
61 | - | |
62 | -// Called by sound playback routines to get incoming network audio | |
63 | -// (also called by main thread in close_network_speaker()) | |
64 | -// Calling this invalidates the pointer returned the previous call. | |
65 | -NetworkSpeakerSoundBufferDescriptor* dequeue_network_speaker_data(); | |
66 | - | |
67 | -// Called by sound playback routines to return storage-buffers to the freequeue | |
68 | -void release_network_speaker_buffer(byte* inBuffer); | |
69 | - | |
70 | -#endif // NETWORK_SPEAKER_SDL_H | |
1 | +/* | |
2 | + * network_speaker_sdl.h | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Realtime audio (network microphone) playback support for SDL platforms. | |
29 | + * | |
30 | + * Created by woody Mar 3-8, 2002. | |
31 | + * | |
32 | + * Feb 1, 2003 (Woody Zenfell): | |
33 | + * This file now describes only the interface between the SDL network speaker receiving | |
34 | + * code and the SDL sound code. The main interface to the actual speaker stuff will be | |
35 | + * in network_audio.h. | |
36 | + */ | |
37 | + | |
38 | +#ifndef NETWORK_SPEAKER_SDL_H | |
39 | +#define NETWORK_SPEAKER_SDL_H | |
40 | + | |
41 | +#include "cseries.h" | |
42 | + | |
43 | +// Flags for NetworkSpeakerSoundBuffer::mFlags below | |
44 | +enum { | |
45 | + kSoundDataIsDisposable = 0x01 // dequeuer is expected to call release_network_speaker_buffer(mData) | |
46 | +}; | |
47 | + | |
48 | +// These are used to link the network_speaker routines to the sound_sdl routines. | |
49 | +struct NetworkSpeakerSoundBufferDescriptor { | |
50 | + byte* mData; | |
51 | + uint32 mLength; | |
52 | + uint32 mFlags; | |
53 | +}; | |
54 | + | |
55 | +// To insulate callers from details of flag storage | |
56 | +__inline__ bool | |
57 | +is_sound_data_disposable(NetworkSpeakerSoundBufferDescriptor* inBuffer) { | |
58 | + return (inBuffer->mFlags & kSoundDataIsDisposable) ? true : false; | |
59 | +} | |
60 | + | |
61 | + | |
62 | +// Called by sound playback routines to get incoming network audio | |
63 | +// (also called by main thread in close_network_speaker()) | |
64 | +// Calling this invalidates the pointer returned the previous call. | |
65 | +NetworkSpeakerSoundBufferDescriptor* dequeue_network_speaker_data(); | |
66 | + | |
67 | +// Called by sound playback routines to return storage-buffers to the freequeue | |
68 | +void release_network_speaker_buffer(byte* inBuffer); | |
69 | + | |
70 | +#endif // NETWORK_SPEAKER_SDL_H |
@@ -1,157 +1,157 @@ | ||
1 | -/* | |
2 | -NETWORK_NAMES.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 | -Sunday, June 26, 1994 5:45:49 PM | |
22 | -Friday, July 15, 1994 11:03:22 AM | |
23 | - allocated name table entry in the system heap, to prevent problems if the name isn't | |
24 | - unregistered before the application exits. (ajr, suggested by jgj.) | |
25 | -*/ | |
26 | - | |
27 | -#if !defined(DISABLE_NETWORKING) | |
28 | - | |
29 | -#include "macintosh_cseries.h" | |
30 | -#include "macintosh_network.h" | |
31 | - | |
32 | -#ifdef env68k | |
33 | -#pragma segment network | |
34 | -#endif | |
35 | - | |
36 | -// #define MODEM_TEST | |
37 | - | |
38 | -/* ---------- constants */ | |
39 | - | |
40 | -#define strUSER_NAME -16096 | |
41 | - | |
42 | -/* ---------- types */ | |
43 | - | |
44 | -typedef NamesTableEntry *NamesTableEntryPtr; | |
45 | - | |
46 | -/* ---------- globals */ | |
47 | - | |
48 | -static NamesTableEntryPtr myNTEName= (NamesTableEntryPtr) NULL; | |
49 | - | |
50 | -/* ---------- code */ | |
51 | - | |
52 | -/* | |
53 | ---------------- | |
54 | -NetRegisterName | |
55 | ---------------- | |
56 | - | |
57 | -allocates and registers and entity name for the given socket; call NetUnRegisterName to unregister | |
58 | -the name. only one name can be registered at a time through NetRegisterName(). | |
59 | - | |
60 | - ---> name (pascal string, can be NULL and will be replaced with user name) | |
61 | - ---> type (pascal string) | |
62 | - ---> socket number | |
63 | - | |
64 | - <--- error | |
65 | -*/ | |
66 | - | |
67 | -OSErr NetRegisterName( | |
68 | - unsigned char *name, | |
69 | - unsigned char *type, | |
70 | - short version, | |
71 | - short socketNumber) | |
72 | -{ | |
73 | - MPPPBPtr myMPPPBPtr= (MPPPBPtr) NewPtrClear(sizeof(MPPParamBlock)); | |
74 | - Str255 adjusted_name; | |
75 | - Str255 adjusted_type; | |
76 | - OSErr error; | |
77 | - | |
78 | -#ifdef MODEM_TEST | |
79 | - error= ModemRegisterName(name, type, version, socketNumber); | |
80 | -#else | |
81 | - | |
82 | - assert(!myNTEName); | |
83 | - // we stick it in the system heap so that if the application crashes/quits while the name | |
84 | - // is registered, this pointer won't be trashed by another application (unless, of course, | |
85 | - // it trashes the system heap). | |
86 | - myNTEName= (NamesTableEntryPtr) NewPtrSysClear(sizeof(NamesTableEntry)); | |
87 | - assert(myNTEName); | |
88 | - | |
89 | - /* get user name if no object name was supplied*/ | |
90 | - pstrcpy(adjusted_name, name ? name : *GetString(strUSER_NAME)); | |
91 | - | |
92 | - /* Calculate the adjusted type */ | |
93 | - psprintf(adjusted_type, "%.*s%d", type[0], type+1, version); | |
94 | - | |
95 | - error= MemError(); | |
96 | - if (error==noErr) | |
97 | - { | |
98 | - NBPSetNTE((Ptr)myNTEName, adjusted_name, adjusted_type, "\p*", socketNumber); /* build names table entry */ | |
99 | - | |
100 | - myMPPPBPtr->NBP.nbpPtrs.ntQElPtr= (Ptr) myNTEName; | |
101 | - myMPPPBPtr->NBP.parm.verifyFlag= true; /* verify this name doesnユt already exist */ | |
102 | - myMPPPBPtr->NBP.interval= 2; /* retry every 2*8 == 16 ticks */ | |
103 | - myMPPPBPtr->NBP.count= 4; /* retry 4 times ( == 64 ticks) */ | |
104 | - | |
105 | - error= PRegisterName(myMPPPBPtr, false); | |
106 | - | |
107 | - DisposePtr((Ptr)myMPPPBPtr); | |
108 | - } | |
109 | -#endif | |
110 | - | |
111 | - return error; | |
112 | -} | |
113 | - | |
114 | -/* | |
115 | - | |
116 | ------------------ | |
117 | -NetUnRegisterName | |
118 | ------------------ | |
119 | - | |
120 | - (no parameters) | |
121 | - | |
122 | -deallocates and unregisters the entity name previously allocated with NetRegisterName(). | |
123 | -*/ | |
124 | - | |
125 | -OSErr NetUnRegisterName( | |
126 | - void) | |
127 | -{ | |
128 | - OSErr error= noErr; | |
129 | - | |
130 | -#ifdef MODEM_TEST | |
131 | - error= ModemUnRegisterName(); | |
132 | -#else | |
133 | - | |
134 | - if (myNTEName) | |
135 | - { | |
136 | - MPPPBPtr myMPPPBPtr= (MPPPBPtr) NewPtrClear(sizeof(MPPParamBlock)); | |
137 | - | |
138 | - error= MemError(); | |
139 | - if (error==noErr) | |
140 | - { | |
141 | - myMPPPBPtr->NBP.nbpPtrs.entityPtr= (Ptr) &myNTEName->nt.entityData; /* canユt just give back names table entry */ | |
142 | - | |
143 | - error= PRemoveName(myMPPPBPtr, false); | |
144 | - | |
145 | - DisposePtr((Ptr)myMPPPBPtr); | |
146 | - | |
147 | - DisposePtr((Ptr)myNTEName); | |
148 | - myNTEName= (NamesTableEntryPtr) NULL; | |
149 | - } | |
150 | - } | |
151 | -#endif | |
152 | - | |
153 | - return error; | |
154 | -} | |
155 | - | |
156 | -#endif // !defined(DISABLE_NETWORKING) | |
157 | - | |
1 | +/* | |
2 | +NETWORK_NAMES.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 | +Sunday, June 26, 1994 5:45:49 PM | |
22 | +Friday, July 15, 1994 11:03:22 AM | |
23 | + allocated name table entry in the system heap, to prevent problems if the name isn't | |
24 | + unregistered before the application exits. (ajr, suggested by jgj.) | |
25 | +*/ | |
26 | + | |
27 | +#if !defined(DISABLE_NETWORKING) | |
28 | + | |
29 | +#include "macintosh_cseries.h" | |
30 | +#include "macintosh_network.h" | |
31 | + | |
32 | +#ifdef env68k | |
33 | +#pragma segment network | |
34 | +#endif | |
35 | + | |
36 | +// #define MODEM_TEST | |
37 | + | |
38 | +/* ---------- constants */ | |
39 | + | |
40 | +#define strUSER_NAME -16096 | |
41 | + | |
42 | +/* ---------- types */ | |
43 | + | |
44 | +typedef NamesTableEntry *NamesTableEntryPtr; | |
45 | + | |
46 | +/* ---------- globals */ | |
47 | + | |
48 | +static NamesTableEntryPtr myNTEName= (NamesTableEntryPtr) NULL; | |
49 | + | |
50 | +/* ---------- code */ | |
51 | + | |
52 | +/* | |
53 | +--------------- | |
54 | +NetRegisterName | |
55 | +--------------- | |
56 | + | |
57 | +allocates and registers and entity name for the given socket; call NetUnRegisterName to unregister | |
58 | +the name. only one name can be registered at a time through NetRegisterName(). | |
59 | + | |
60 | + ---> name (pascal string, can be NULL and will be replaced with user name) | |
61 | + ---> type (pascal string) | |
62 | + ---> socket number | |
63 | + | |
64 | + <--- error | |
65 | +*/ | |
66 | + | |
67 | +OSErr NetRegisterName( | |
68 | + unsigned char *name, | |
69 | + unsigned char *type, | |
70 | + short version, | |
71 | + short socketNumber) | |
72 | +{ | |
73 | + MPPPBPtr myMPPPBPtr= (MPPPBPtr) NewPtrClear(sizeof(MPPParamBlock)); | |
74 | + Str255 adjusted_name; | |
75 | + Str255 adjusted_type; | |
76 | + OSErr error; | |
77 | + | |
78 | +#ifdef MODEM_TEST | |
79 | + error= ModemRegisterName(name, type, version, socketNumber); | |
80 | +#else | |
81 | + | |
82 | + assert(!myNTEName); | |
83 | + // we stick it in the system heap so that if the application crashes/quits while the name | |
84 | + // is registered, this pointer won't be trashed by another application (unless, of course, | |
85 | + // it trashes the system heap). | |
86 | + myNTEName= (NamesTableEntryPtr) NewPtrSysClear(sizeof(NamesTableEntry)); | |
87 | + assert(myNTEName); | |
88 | + | |
89 | + /* get user name if no object name was supplied*/ | |
90 | + pstrcpy(adjusted_name, name ? name : *GetString(strUSER_NAME)); | |
91 | + | |
92 | + /* Calculate the adjusted type */ | |
93 | + psprintf(adjusted_type, "%.*s%d", type[0], type+1, version); | |
94 | + | |
95 | + error= MemError(); | |
96 | + if (error==noErr) | |
97 | + { | |
98 | + NBPSetNTE((Ptr)myNTEName, adjusted_name, adjusted_type, "\p*", socketNumber); /* build names table entry */ | |
99 | + | |
100 | + myMPPPBPtr->NBP.nbpPtrs.ntQElPtr= (Ptr) myNTEName; | |
101 | + myMPPPBPtr->NBP.parm.verifyFlag= true; /* verify this name doesnユt already exist */ | |
102 | + myMPPPBPtr->NBP.interval= 2; /* retry every 2*8 == 16 ticks */ | |
103 | + myMPPPBPtr->NBP.count= 4; /* retry 4 times ( == 64 ticks) */ | |
104 | + | |
105 | + error= PRegisterName(myMPPPBPtr, false); | |
106 | + | |
107 | + DisposePtr((Ptr)myMPPPBPtr); | |
108 | + } | |
109 | +#endif | |
110 | + | |
111 | + return error; | |
112 | +} | |
113 | + | |
114 | +/* | |
115 | + | |
116 | +----------------- | |
117 | +NetUnRegisterName | |
118 | +----------------- | |
119 | + | |
120 | + (no parameters) | |
121 | + | |
122 | +deallocates and unregisters the entity name previously allocated with NetRegisterName(). | |
123 | +*/ | |
124 | + | |
125 | +OSErr NetUnRegisterName( | |
126 | + void) | |
127 | +{ | |
128 | + OSErr error= noErr; | |
129 | + | |
130 | +#ifdef MODEM_TEST | |
131 | + error= ModemUnRegisterName(); | |
132 | +#else | |
133 | + | |
134 | + if (myNTEName) | |
135 | + { | |
136 | + MPPPBPtr myMPPPBPtr= (MPPPBPtr) NewPtrClear(sizeof(MPPParamBlock)); | |
137 | + | |
138 | + error= MemError(); | |
139 | + if (error==noErr) | |
140 | + { | |
141 | + myMPPPBPtr->NBP.nbpPtrs.entityPtr= (Ptr) &myNTEName->nt.entityData; /* canユt just give back names table entry */ | |
142 | + | |
143 | + error= PRemoveName(myMPPPBPtr, false); | |
144 | + | |
145 | + DisposePtr((Ptr)myMPPPBPtr); | |
146 | + | |
147 | + DisposePtr((Ptr)myNTEName); | |
148 | + myNTEName= (NamesTableEntryPtr) NULL; | |
149 | + } | |
150 | + } | |
151 | +#endif | |
152 | + | |
153 | + return error; | |
154 | +} | |
155 | + | |
156 | +#endif // !defined(DISABLE_NETWORKING) | |
157 | + |
@@ -1,211 +1,211 @@ | ||
1 | -/* | |
2 | - * network_microphone_sdl_alsa.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2007 and beyond by Gregory Smith | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - */ | |
23 | - | |
24 | -#include "cseries.h" | |
25 | -#ifdef HAVE_ALSA | |
26 | -#include <alsa/asoundlib.h> | |
27 | - | |
28 | -#ifdef SPEEX | |
29 | -#include "network_speex.h" | |
30 | -#include "preferences.h" | |
31 | -#endif | |
32 | - | |
33 | -#include "network_microphone_shared.h" | |
34 | - | |
35 | -static snd_pcm_t *capture_handle = 0; | |
36 | -static snd_pcm_hw_params_t *hw_params; | |
37 | - | |
38 | -static bool initialized = false; | |
39 | -static bool active; | |
40 | - | |
41 | -static const int bytes_per_frame = 2; // 16-bit, mono | |
42 | -static snd_pcm_uframes_t frames = 0; // period | |
43 | - | |
44 | -OSErr | |
45 | -open_network_microphone() { | |
46 | - int err; | |
47 | - | |
48 | - if ((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) { | |
49 | - fprintf(stderr, "snd_pcm_open\n"); | |
50 | - return -1; | |
51 | - } | |
52 | - | |
53 | - if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { | |
54 | - fprintf(stderr, "snd_pcm_hw_params_malloc\n"); | |
55 | - return -1; | |
56 | - } | |
57 | - | |
58 | - if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) { | |
59 | - fprintf(stderr, "snd_pcm_hw_params_any\n"); | |
60 | - return -1; | |
61 | - } | |
62 | - | |
63 | - if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { | |
64 | - fprintf(stderr, "snd_pcm_hw_params_set_access\n"); | |
65 | - return -1; | |
66 | - } | |
67 | - | |
68 | - snd_pcm_format_t format; | |
69 | -#ifdef ALEPHONE_LITTLE_ENDIAN | |
70 | - format = SND_PCM_FORMAT_S16_LE; | |
71 | -#else | |
72 | - format = SND_PCM_FORMAT_S16_BE; | |
73 | -#endif | |
74 | - | |
75 | - if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0) { | |
76 | - fprintf(stderr, "snd_pcm_hw_params_set_format\n"); | |
77 | - return -1; | |
78 | - } | |
79 | - | |
80 | - unsigned int rate = 8000; | |
81 | - if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0) { | |
82 | - fprintf(stderr, "snd_pcm_hw_params_set_rate_near\n"); | |
83 | - return -1; | |
84 | - } | |
85 | - | |
86 | - if (!announce_microphone_capture_format(rate, false, true)) { | |
87 | - fprintf(stderr, "network microphone support code rejected audio format (rate=%i)\n", rate); | |
88 | - return -1; | |
89 | - } | |
90 | - | |
91 | - if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 1)) < 0) { | |
92 | - fprintf(stderr, "snd_pcm_hw_params_set_channels\n"); | |
93 | - return -1; | |
94 | - } | |
95 | - | |
96 | - frames = get_capture_byte_count_per_packet() / bytes_per_frame; | |
97 | - if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &frames, 0)) < 0) { | |
98 | - fprintf(stderr, "snd_pcm_hw_params_set_period_size_near\n"); | |
99 | - return -1; | |
100 | - } | |
101 | - | |
102 | - if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) { | |
103 | - fprintf(stderr, "snd_pcm_hw_params\n"); | |
104 | - return -1; | |
105 | - } | |
106 | - | |
107 | - snd_pcm_hw_params_free(hw_params); | |
108 | - | |
109 | - snd_pcm_sw_params_t *sw_params; | |
110 | - if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) { | |
111 | - fprintf(stderr, "snd_pcm_sw_params_malloc\n"); | |
112 | - return -1; | |
113 | - } | |
114 | - | |
115 | - if ((err = snd_pcm_sw_params_current(capture_handle, sw_params)) < 0) { | |
116 | - fprintf(stderr, "snc_pcm_sw_params_current\n"); | |
117 | - return -1; | |
118 | - } | |
119 | - | |
120 | - if ((err = snd_pcm_sw_params_set_avail_min(capture_handle, sw_params, frames)) < 0) | |
121 | - { | |
122 | - fprintf(stderr, "snd_pcm_params_set_avail_min\n"); | |
123 | - return -1; | |
124 | - } | |
125 | - | |
126 | - if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params, frames)) < 0) { | |
127 | - fprintf(stderr, "snd_pcm_params_set_start_threshold\n"); | |
128 | - return -1; | |
129 | - } | |
130 | - | |
131 | -#ifdef SPEEX | |
132 | - if (network_preferences->use_speex_encoder) { | |
133 | - init_speex_encoder(); | |
134 | - } | |
135 | -#endif | |
136 | - | |
137 | - initialized = true; | |
138 | - | |
139 | - return 0; | |
140 | -} | |
141 | - | |
142 | -void | |
143 | -close_network_microphone() { | |
144 | - initialized = false; | |
145 | - snd_pcm_close(capture_handle); | |
146 | - capture_handle = 0; | |
147 | - | |
148 | -#ifdef SPEEX | |
149 | - if (network_preferences->use_speex_encoder) { | |
150 | - destroy_speex_encoder(); | |
151 | - } | |
152 | -#endif | |
153 | -} | |
154 | - | |
155 | -void CaptureCallback(snd_async_handler_t *) | |
156 | -{ | |
157 | - snd_pcm_sframes_t avail = snd_pcm_avail_update(capture_handle); | |
158 | - while (avail >= frames) { | |
159 | - static uint8 buffer[16384]; | |
160 | - int frames_read = snd_pcm_readi(capture_handle, buffer, frames); | |
161 | - if (frames_read == -EPIPE) { | |
162 | - snd_pcm_prepare(capture_handle); | |
163 | - } else if (frames_read > 0) { | |
164 | - copy_and_send_audio_data(buffer, frames_read * bytes_per_frame, NULL, 0, true); | |
165 | - } | |
166 | - | |
167 | - avail = snd_pcm_avail_update(capture_handle); | |
168 | - } | |
169 | -} | |
170 | - | |
171 | -static bool mic_active = false; | |
172 | - | |
173 | -static snd_async_handler_t *pcm_callback; | |
174 | - | |
175 | -void | |
176 | -set_network_microphone_state(bool inActive) { | |
177 | - if (!initialized) return; | |
178 | - if (inActive && !mic_active) { | |
179 | - // prepare the pcm | |
180 | - if (snd_pcm_prepare(capture_handle) < 0) { | |
181 | - fprintf(stderr, "preparing stream failed\n"); | |
182 | - } | |
183 | - if (snd_async_add_pcm_handler(&pcm_callback, capture_handle, CaptureCallback, NULL) < 0) { | |
184 | - fprintf(stderr, "adding pcm handler failed\n"); | |
185 | - return; | |
186 | - } | |
187 | - if (snd_pcm_start(capture_handle) < 0) { | |
188 | - fprintf(stderr, "starting pcm failed\n"); | |
189 | - } | |
190 | - mic_active = true; | |
191 | - } else if (!inActive && mic_active) { | |
192 | - snd_async_del_handler(pcm_callback); | |
193 | - snd_pcm_drop(capture_handle); | |
194 | - mic_active = false; | |
195 | - } | |
196 | - | |
197 | -} | |
198 | - | |
199 | -bool | |
200 | -is_network_microphone_implemented() { | |
201 | - return true; | |
202 | -} | |
203 | - | |
204 | -void | |
205 | -network_microphone_idle_proc() { | |
206 | - // do nothing | |
207 | -} | |
208 | - | |
209 | -#else | |
210 | -#include "network_microphone_sdl_dummy.cpp" | |
211 | -#endif | |
1 | +/* | |
2 | + * network_microphone_sdl_alsa.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2007 and beyond by Gregory Smith | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + */ | |
23 | + | |
24 | +#include "cseries.h" | |
25 | +#ifdef HAVE_ALSA | |
26 | +#include <alsa/asoundlib.h> | |
27 | + | |
28 | +#ifdef SPEEX | |
29 | +#include "network_speex.h" | |
30 | +#include "preferences.h" | |
31 | +#endif | |
32 | + | |
33 | +#include "network_microphone_shared.h" | |
34 | + | |
35 | +static snd_pcm_t *capture_handle = 0; | |
36 | +static snd_pcm_hw_params_t *hw_params; | |
37 | + | |
38 | +static bool initialized = false; | |
39 | +static bool active; | |
40 | + | |
41 | +static const int bytes_per_frame = 2; // 16-bit, mono | |
42 | +static snd_pcm_uframes_t frames = 0; // period | |
43 | + | |
44 | +OSErr | |
45 | +open_network_microphone() { | |
46 | + int err; | |
47 | + | |
48 | + if ((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) { | |
49 | + fprintf(stderr, "snd_pcm_open\n"); | |
50 | + return -1; | |
51 | + } | |
52 | + | |
53 | + if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { | |
54 | + fprintf(stderr, "snd_pcm_hw_params_malloc\n"); | |
55 | + return -1; | |
56 | + } | |
57 | + | |
58 | + if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) { | |
59 | + fprintf(stderr, "snd_pcm_hw_params_any\n"); | |
60 | + return -1; | |
61 | + } | |
62 | + | |
63 | + if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { | |
64 | + fprintf(stderr, "snd_pcm_hw_params_set_access\n"); | |
65 | + return -1; | |
66 | + } | |
67 | + | |
68 | + snd_pcm_format_t format; | |
69 | +#ifdef ALEPHONE_LITTLE_ENDIAN | |
70 | + format = SND_PCM_FORMAT_S16_LE; | |
71 | +#else | |
72 | + format = SND_PCM_FORMAT_S16_BE; | |
73 | +#endif | |
74 | + | |
75 | + if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0) { | |
76 | + fprintf(stderr, "snd_pcm_hw_params_set_format\n"); | |
77 | + return -1; | |
78 | + } | |
79 | + | |
80 | + unsigned int rate = 8000; | |
81 | + if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &rate, 0)) < 0) { | |
82 | + fprintf(stderr, "snd_pcm_hw_params_set_rate_near\n"); | |
83 | + return -1; | |
84 | + } | |
85 | + | |
86 | + if (!announce_microphone_capture_format(rate, false, true)) { | |
87 | + fprintf(stderr, "network microphone support code rejected audio format (rate=%i)\n", rate); | |
88 | + return -1; | |
89 | + } | |
90 | + | |
91 | + if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 1)) < 0) { | |
92 | + fprintf(stderr, "snd_pcm_hw_params_set_channels\n"); | |
93 | + return -1; | |
94 | + } | |
95 | + | |
96 | + frames = get_capture_byte_count_per_packet() / bytes_per_frame; | |
97 | + if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &frames, 0)) < 0) { | |
98 | + fprintf(stderr, "snd_pcm_hw_params_set_period_size_near\n"); | |
99 | + return -1; | |
100 | + } | |
101 | + | |
102 | + if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) { | |
103 | + fprintf(stderr, "snd_pcm_hw_params\n"); | |
104 | + return -1; | |
105 | + } | |
106 | + | |
107 | + snd_pcm_hw_params_free(hw_params); | |
108 | + | |
109 | + snd_pcm_sw_params_t *sw_params; | |
110 | + if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) { | |
111 | + fprintf(stderr, "snd_pcm_sw_params_malloc\n"); | |
112 | + return -1; | |
113 | + } | |
114 | + | |
115 | + if ((err = snd_pcm_sw_params_current(capture_handle, sw_params)) < 0) { | |
116 | + fprintf(stderr, "snc_pcm_sw_params_current\n"); | |
117 | + return -1; | |
118 | + } | |
119 | + | |
120 | + if ((err = snd_pcm_sw_params_set_avail_min(capture_handle, sw_params, frames)) < 0) | |
121 | + { | |
122 | + fprintf(stderr, "snd_pcm_params_set_avail_min\n"); | |
123 | + return -1; | |
124 | + } | |
125 | + | |
126 | + if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params, frames)) < 0) { | |
127 | + fprintf(stderr, "snd_pcm_params_set_start_threshold\n"); | |
128 | + return -1; | |
129 | + } | |
130 | + | |
131 | +#ifdef SPEEX | |
132 | + if (network_preferences->use_speex_encoder) { | |
133 | + init_speex_encoder(); | |
134 | + } | |
135 | +#endif | |
136 | + | |
137 | + initialized = true; | |
138 | + | |
139 | + return 0; | |
140 | +} | |
141 | + | |
142 | +void | |
143 | +close_network_microphone() { | |
144 | + initialized = false; | |
145 | + snd_pcm_close(capture_handle); | |
146 | + capture_handle = 0; | |
147 | + | |
148 | +#ifdef SPEEX | |
149 | + if (network_preferences->use_speex_encoder) { | |
150 | + destroy_speex_encoder(); | |
151 | + } | |
152 | +#endif | |
153 | +} | |
154 | + | |
155 | +void CaptureCallback(snd_async_handler_t *) | |
156 | +{ | |
157 | + snd_pcm_sframes_t avail = snd_pcm_avail_update(capture_handle); | |
158 | + while (avail >= frames) { | |
159 | + static uint8 buffer[16384]; | |
160 | + int frames_read = snd_pcm_readi(capture_handle, buffer, frames); | |
161 | + if (frames_read == -EPIPE) { | |
162 | + snd_pcm_prepare(capture_handle); | |
163 | + } else if (frames_read > 0) { | |
164 | + copy_and_send_audio_data(buffer, frames_read * bytes_per_frame, NULL, 0, true); | |
165 | + } | |
166 | + | |
167 | + avail = snd_pcm_avail_update(capture_handle); | |
168 | + } | |
169 | +} | |
170 | + | |
171 | +static bool mic_active = false; | |
172 | + | |
173 | +static snd_async_handler_t *pcm_callback; | |
174 | + | |
175 | +void | |
176 | +set_network_microphone_state(bool inActive) { | |
177 | + if (!initialized) return; | |
178 | + if (inActive && !mic_active) { | |
179 | + // prepare the pcm | |
180 | + if (snd_pcm_prepare(capture_handle) < 0) { | |
181 | + fprintf(stderr, "preparing stream failed\n"); | |
182 | + } | |
183 | + if (snd_async_add_pcm_handler(&pcm_callback, capture_handle, CaptureCallback, NULL) < 0) { | |
184 | + fprintf(stderr, "adding pcm handler failed\n"); | |
185 | + return; | |
186 | + } | |
187 | + if (snd_pcm_start(capture_handle) < 0) { | |
188 | + fprintf(stderr, "starting pcm failed\n"); | |
189 | + } | |
190 | + mic_active = true; | |
191 | + } else if (!inActive && mic_active) { | |
192 | + snd_async_del_handler(pcm_callback); | |
193 | + snd_pcm_drop(capture_handle); | |
194 | + mic_active = false; | |
195 | + } | |
196 | + | |
197 | +} | |
198 | + | |
199 | +bool | |
200 | +is_network_microphone_implemented() { | |
201 | + return true; | |
202 | +} | |
203 | + | |
204 | +void | |
205 | +network_microphone_idle_proc() { | |
206 | + // do nothing | |
207 | +} | |
208 | + | |
209 | +#else | |
210 | +#include "network_microphone_sdl_dummy.cpp" | |
211 | +#endif |
@@ -1,57 +1,57 @@ | ||
1 | -/* | |
2 | - * network_ring.h | |
3 | - | |
4 | - Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | - * Created by Woody Zenfell, III on Sat May 17 2003. | |
22 | - * | |
23 | - * Interface between the old ring game protocol module and the rest of the game. | |
24 | - */ | |
25 | - | |
26 | -#ifndef NETWORK_RING | |
27 | -#define NETWORK_RING | |
28 | - | |
29 | -#include "NetworkGameProtocol.h" | |
30 | - | |
31 | -#include <stdio.h> | |
32 | - | |
33 | -class XML_ElementParser; | |
34 | - | |
35 | -class RingGameProtocol : public NetworkGameProtocol | |
36 | -{ | |
37 | -public: | |
38 | - bool Enter(short* inNetStatePtr); | |
39 | - void Exit1(); | |
40 | - void Exit2(); | |
41 | - void DistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool only_send_to_team); | |
42 | - bool Sync(NetTopology* inTopology, int32 inSmallestGameTick, size_t inLocalPlayerIndex, size_t inServerPlayerIndex); | |
43 | - bool UnSync(bool inGraceful, int32 inSmallestPostgameTick); | |
44 | - int32 GetNetTime(); | |
45 | - void PacketHandler(DDPPacketBuffer* inPacket); | |
46 | - | |
47 | - int32 GetUnconfirmedActionFlagsCount(); | |
48 | - uint32 PeekUnconfirmedActionFlag(int32 offset); | |
49 | - void UpdateUnconfirmedActionFlags(); | |
50 | - | |
51 | - static XML_ElementParser* GetParser(); | |
52 | -}; | |
53 | - | |
54 | -extern void DefaultRingPreferences(); | |
55 | -extern void WriteRingPreferences(FILE* F); | |
56 | - | |
57 | -#endif // NETWORK_RING | |
1 | +/* | |
2 | + * network_ring.h | |
3 | + | |
4 | + Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | + * Created by Woody Zenfell, III on Sat May 17 2003. | |
22 | + * | |
23 | + * Interface between the old ring game protocol module and the rest of the game. | |
24 | + */ | |
25 | + | |
26 | +#ifndef NETWORK_RING | |
27 | +#define NETWORK_RING | |
28 | + | |
29 | +#include "NetworkGameProtocol.h" | |
30 | + | |
31 | +#include <stdio.h> | |
32 | + | |
33 | +class XML_ElementParser; | |
34 | + | |
35 | +class RingGameProtocol : public NetworkGameProtocol | |
36 | +{ | |
37 | +public: | |
38 | + bool Enter(short* inNetStatePtr); | |
39 | + void Exit1(); | |
40 | + void Exit2(); | |
41 | + void DistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool only_send_to_team); | |
42 | + bool Sync(NetTopology* inTopology, int32 inSmallestGameTick, size_t inLocalPlayerIndex, size_t inServerPlayerIndex); | |
43 | + bool UnSync(bool inGraceful, int32 inSmallestPostgameTick); | |
44 | + int32 GetNetTime(); | |
45 | + void PacketHandler(DDPPacketBuffer* inPacket); | |
46 | + | |
47 | + int32 GetUnconfirmedActionFlagsCount(); | |
48 | + uint32 PeekUnconfirmedActionFlag(int32 offset); | |
49 | + void UpdateUnconfirmedActionFlags(); | |
50 | + | |
51 | + static XML_ElementParser* GetParser(); | |
52 | +}; | |
53 | + | |
54 | +extern void DefaultRingPreferences(); | |
55 | +extern void WriteRingPreferences(FILE* F); | |
56 | + | |
57 | +#endif // NETWORK_RING |
@@ -1,36 +1,36 @@ | ||
1 | -/* | |
2 | - * network_distribution_types.h | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * Centralized location for distribution types (for NetDistributeInformation, | |
23 | - * NetAddDistributionFunction, etc.) helps avoid potential conflicts. | |
24 | - * | |
25 | - * Created by woody Mar 3-8, 2002. | |
26 | - */ | |
27 | - | |
28 | -#ifndef NETWORK_DISTRIBUTION_TYPES_H | |
29 | -#define NETWORK_DISTRIBUTION_TYPES_H | |
30 | - | |
31 | -enum { | |
32 | - kOriginalNetworkAudioDistributionTypeID = 0, // for compatibility with older versions | |
33 | - kNewNetworkAudioDistributionTypeID = 1 // new-style realtime network audio data | |
34 | -}; | |
35 | - | |
36 | -#endif // NETWORK_DISTRIBUTION_TYPES_H | |
1 | +/* | |
2 | + * network_distribution_types.h | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * Centralized location for distribution types (for NetDistributeInformation, | |
23 | + * NetAddDistributionFunction, etc.) helps avoid potential conflicts. | |
24 | + * | |
25 | + * Created by woody Mar 3-8, 2002. | |
26 | + */ | |
27 | + | |
28 | +#ifndef NETWORK_DISTRIBUTION_TYPES_H | |
29 | +#define NETWORK_DISTRIBUTION_TYPES_H | |
30 | + | |
31 | +enum { | |
32 | + kOriginalNetworkAudioDistributionTypeID = 0, // for compatibility with older versions | |
33 | + kNewNetworkAudioDistributionTypeID = 1 // new-style realtime network audio data | |
34 | +}; | |
35 | + | |
36 | +#endif // NETWORK_DISTRIBUTION_TYPES_H |
@@ -1,127 +1,127 @@ | ||
1 | -/* | |
2 | - * network_data_formats.h | |
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 | - * The purpose of this file is to define structures that will be used with exactly the same | |
22 | - * padding, byte ordering, etc. on all platforms, and to declare handy functions to copy data | |
23 | - * to or from the corresponding "unpacked" structures already used by the code. This approach | |
24 | - * requires only minimal changes to the original code. | |
25 | - * | |
26 | - * Created by Woody Zenfell, III on Thu Oct 11 2001; structures copied from network_private.h. | |
27 | - | |
28 | - Jan 16, 2002 (Loren Petrich) Replaced compiler-specific packing code with generalized wrappers | |
29 | - | |
30 | - Mar 5, 2002 (Woody Zenfell) added network_audio_header | |
31 | - | |
32 | - Mar 9, 2002 (Woody Zenfell) changed some SIZEOF_'s to be more accurate/specific | |
33 | - */ | |
34 | - | |
35 | -#ifndef NETWORK_DATA_FORMATS_H | |
36 | -#define NETWORK_DATA_FORMATS_H | |
37 | - | |
38 | -#include "cseries.h" // Need ALEPHONE_LITTLE_ENDIAN, if appropriate. | |
39 | -#include "network.h" | |
40 | -#include "network_private.h" | |
41 | -#include "network_audio_shared.h" | |
42 | - | |
43 | - | |
44 | - | |
45 | -// Note: no further interpretation/manipulation of a packet is attempted here. That's up | |
46 | -// to whomever actually deals with receiving and interpreting the packet (though they will | |
47 | -// probably make use of the netcpy's for the next couple structures below). | |
48 | - | |
49 | -const int SIZEOF_NetPacketHeader = 6; | |
50 | - | |
51 | -struct NetPacketHeader_NET | |
52 | -{ | |
53 | - uint8 data[SIZEOF_NetPacketHeader]; | |
54 | -}; | |
55 | - | |
56 | -extern void netcpy(NetPacketHeader_NET* dest, const NetPacketHeader* src); | |
57 | -extern void netcpy(NetPacketHeader* dest, const NetPacketHeader_NET* src); | |
58 | - | |
59 | - | |
60 | - | |
61 | -// Note: we do not attempt any manipulations on the actual action_flags, as we do not claim | |
62 | -// to compute the number that would be there. (I suppose, knowing that the only stuff in the | |
63 | -// buffer will be action flags, that we could just walk through and swap all of it, but...) | |
64 | -// We'll leave it to whomever interprets or writes to the action_flags data segment to do the | |
65 | -// necessary manipulations. | |
66 | - | |
67 | -const int SIZEOF_NetPacket = 2*MAXIMUM_NUMBER_OF_NETWORK_PLAYERS + 8; | |
68 | - | |
69 | -struct NetPacket_NET | |
70 | -{ | |
71 | - uint8 data[SIZEOF_NetPacket]; | |
72 | -}; | |
73 | - | |
74 | -extern void netcpy(NetPacket_NET* dest, const NetPacket* src); | |
75 | -extern void netcpy(NetPacket* dest, const NetPacket_NET* src); | |
76 | - | |
77 | - | |
78 | -// For action flags - note length is in bytes, not number of flags. This is 'bidirectional', | |
79 | -// i.e. same function is used to copy from _NET to unpacked as the other way around. | |
80 | -// Note, since there is no packing to do - only byte swapping - we can pass along to memcpy if we're | |
81 | -// on a big-endian architecture. | |
82 | -#ifdef ALEPHONE_LITTLE_ENDIAN | |
83 | -extern void netcpy(uint32* dest, const uint32* src, size_t length); | |
84 | -#else | |
85 | -__inline__ void netcpy(uint32* dest, const uint32* src, size_t length) { memcpy(dest, src, length); } | |
86 | -#endif | |
87 | - | |
88 | - | |
89 | - | |
90 | -// Note: we do not attempt any sort of processing on the "data" segment here, | |
91 | -// since we may not understand its format. Whoever interprets it will have to | |
92 | -// do the necessary packing/unpacking, byte-swapping, etc. | |
93 | - | |
94 | -const int SIZEOF_NetDistributionPacket = 6; | |
95 | - | |
96 | -struct NetDistributionPacket_NET | |
97 | -{ | |
98 | - uint8 data[SIZEOF_NetDistributionPacket]; | |
99 | -}; | |
100 | - | |
101 | -extern void netcpy(NetDistributionPacket_NET* dest, const NetDistributionPacket* src); | |
102 | -extern void netcpy(NetDistributionPacket* dest, const NetDistributionPacket_NET* src); | |
103 | - | |
104 | - | |
105 | -// Note: unlike other _NET structures, neither 'host' nor 'port' here is byte-swapped | |
106 | -// in conversion to/from the non-_NET structures. | |
107 | -// It is believed that both should ALWAYS be dealt with in network byte order. | |
108 | - | |
109 | -const int SIZEOF_IPaddress = 6; | |
110 | - | |
111 | -struct IPaddress_NET { | |
112 | - uint8 data[SIZEOF_IPaddress]; | |
113 | -}; | |
114 | - | |
115 | -extern void netcpy(IPaddress_NET* dest, const IPaddress* src); | |
116 | -extern void netcpy(IPaddress* dest, const IPaddress_NET* src); | |
117 | - | |
118 | -const int SIZEOF_network_audio_header = 8; | |
119 | - | |
120 | -struct network_audio_header_NET { | |
121 | - uint8 data[SIZEOF_network_audio_header]; | |
122 | -}; | |
123 | - | |
124 | -extern void netcpy(network_audio_header_NET* dest, const network_audio_header* src); | |
125 | -extern void netcpy(network_audio_header* dest, const network_audio_header_NET* src); | |
126 | - | |
127 | -#endif//NETWORK_DATA_FORMATS_H | |
1 | +/* | |
2 | + * network_data_formats.h | |
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 | + * The purpose of this file is to define structures that will be used with exactly the same | |
22 | + * padding, byte ordering, etc. on all platforms, and to declare handy functions to copy data | |
23 | + * to or from the corresponding "unpacked" structures already used by the code. This approach | |
24 | + * requires only minimal changes to the original code. | |
25 | + * | |
26 | + * Created by Woody Zenfell, III on Thu Oct 11 2001; structures copied from network_private.h. | |
27 | + | |
28 | + Jan 16, 2002 (Loren Petrich) Replaced compiler-specific packing code with generalized wrappers | |
29 | + | |
30 | + Mar 5, 2002 (Woody Zenfell) added network_audio_header | |
31 | + | |
32 | + Mar 9, 2002 (Woody Zenfell) changed some SIZEOF_'s to be more accurate/specific | |
33 | + */ | |
34 | + | |
35 | +#ifndef NETWORK_DATA_FORMATS_H | |
36 | +#define NETWORK_DATA_FORMATS_H | |
37 | + | |
38 | +#include "cseries.h" // Need ALEPHONE_LITTLE_ENDIAN, if appropriate. | |
39 | +#include "network.h" | |
40 | +#include "network_private.h" | |
41 | +#include "network_audio_shared.h" | |
42 | + | |
43 | + | |
44 | + | |
45 | +// Note: no further interpretation/manipulation of a packet is attempted here. That's up | |
46 | +// to whomever actually deals with receiving and interpreting the packet (though they will | |
47 | +// probably make use of the netcpy's for the next couple structures below). | |
48 | + | |
49 | +const int SIZEOF_NetPacketHeader = 6; | |
50 | + | |
51 | +struct NetPacketHeader_NET | |
52 | +{ | |
53 | + uint8 data[SIZEOF_NetPacketHeader]; | |
54 | +}; | |
55 | + | |
56 | +extern void netcpy(NetPacketHeader_NET* dest, const NetPacketHeader* src); | |
57 | +extern void netcpy(NetPacketHeader* dest, const NetPacketHeader_NET* src); | |
58 | + | |
59 | + | |
60 | + | |
61 | +// Note: we do not attempt any manipulations on the actual action_flags, as we do not claim | |
62 | +// to compute the number that would be there. (I suppose, knowing that the only stuff in the | |
63 | +// buffer will be action flags, that we could just walk through and swap all of it, but...) | |
64 | +// We'll leave it to whomever interprets or writes to the action_flags data segment to do the | |
65 | +// necessary manipulations. | |
66 | + | |
67 | +const int SIZEOF_NetPacket = 2*MAXIMUM_NUMBER_OF_NETWORK_PLAYERS + 8; | |
68 | + | |
69 | +struct NetPacket_NET | |
70 | +{ | |
71 | + uint8 data[SIZEOF_NetPacket]; | |
72 | +}; | |
73 | + | |
74 | +extern void netcpy(NetPacket_NET* dest, const NetPacket* src); | |
75 | +extern void netcpy(NetPacket* dest, const NetPacket_NET* src); | |
76 | + | |
77 | + | |
78 | +// For action flags - note length is in bytes, not number of flags. This is 'bidirectional', | |
79 | +// i.e. same function is used to copy from _NET to unpacked as the other way around. | |
80 | +// Note, since there is no packing to do - only byte swapping - we can pass along to memcpy if we're | |
81 | +// on a big-endian architecture. | |
82 | +#ifdef ALEPHONE_LITTLE_ENDIAN | |
83 | +extern void netcpy(uint32* dest, const uint32* src, size_t length); | |
84 | +#else | |
85 | +__inline__ void netcpy(uint32* dest, const uint32* src, size_t length) { memcpy(dest, src, length); } | |
86 | +#endif | |
87 | + | |
88 | + | |
89 | + | |
90 | +// Note: we do not attempt any sort of processing on the "data" segment here, | |
91 | +// since we may not understand its format. Whoever interprets it will have to | |
92 | +// do the necessary packing/unpacking, byte-swapping, etc. | |
93 | + | |
94 | +const int SIZEOF_NetDistributionPacket = 6; | |
95 | + | |
96 | +struct NetDistributionPacket_NET | |
97 | +{ | |
98 | + uint8 data[SIZEOF_NetDistributionPacket]; | |
99 | +}; | |
100 | + | |
101 | +extern void netcpy(NetDistributionPacket_NET* dest, const NetDistributionPacket* src); | |
102 | +extern void netcpy(NetDistributionPacket* dest, const NetDistributionPacket_NET* src); | |
103 | + | |
104 | + | |
105 | +// Note: unlike other _NET structures, neither 'host' nor 'port' here is byte-swapped | |
106 | +// in conversion to/from the non-_NET structures. | |
107 | +// It is believed that both should ALWAYS be dealt with in network byte order. | |
108 | + | |
109 | +const int SIZEOF_IPaddress = 6; | |
110 | + | |
111 | +struct IPaddress_NET { | |
112 | + uint8 data[SIZEOF_IPaddress]; | |
113 | +}; | |
114 | + | |
115 | +extern void netcpy(IPaddress_NET* dest, const IPaddress* src); | |
116 | +extern void netcpy(IPaddress* dest, const IPaddress_NET* src); | |
117 | + | |
118 | +const int SIZEOF_network_audio_header = 8; | |
119 | + | |
120 | +struct network_audio_header_NET { | |
121 | + uint8 data[SIZEOF_network_audio_header]; | |
122 | +}; | |
123 | + | |
124 | +extern void netcpy(network_audio_header_NET* dest, const network_audio_header* src); | |
125 | +extern void netcpy(network_audio_header* dest, const network_audio_header_NET* src); | |
126 | + | |
127 | +#endif//NETWORK_DATA_FORMATS_H |
@@ -1,1288 +1,1288 @@ | ||
1 | -/* | |
2 | - * network_star_spoke.cpp | |
3 | - | |
4 | - Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | - * The portion of the star game protocol run on every player's machine. | |
22 | - * | |
23 | - * Created by Woody Zenfell, III on Fri May 02 2003. | |
24 | - * | |
25 | - * May 27, 2003 (Woody Zenfell): lossy byte-stream distribution. | |
26 | - * | |
27 | - * June 30, 2003 (Woody Zenfell): lossy byte-stream distribution more tolerant of scheduling jitter | |
28 | - * (i.e. will queue multiple chunks before send, instead of dropping all data but most recent) | |
29 | - * | |
30 | - * September 17, 2004 (jkvw): | |
31 | - * NAT-friendly networking - we no longer get spoke addresses form topology - | |
32 | - * instead spokes send identification packets to hub with player ID. | |
33 | - * Hub can then associate the ID in the identification packet with the paket's source address. | |
34 | - */ | |
35 | - | |
36 | -#if !defined(DISABLE_NETWORKING) | |
37 | - | |
38 | -#include "network_star.h" | |
39 | -#include "AStream.h" | |
40 | -#include "mytm.h" | |
41 | -#include "network_private.h" // kPROTOCOL_TYPE | |
42 | -#include "WindowedNthElementFinder.h" | |
43 | -#include "vbl.h" // parse_keymap | |
44 | -#include "CircularByteBuffer.h" | |
45 | -#include "Logging.h" | |
46 | -#include "crc.h" | |
47 | -#include "player.h" | |
48 | - | |
49 | -#include <map> | |
50 | - | |
51 | -extern void make_player_really_net_dead(size_t inPlayerIndex); | |
52 | -extern void call_distribution_response_function_if_available(byte* inBuffer, uint16 inBufferSize, int16 inDistributionType, uint8 inSendingPlayerIndex); | |
53 | - | |
54 | - | |
55 | -enum { | |
56 | - kDefaultPregameTicksBeforeNetDeath = 90 * TICKS_PER_SECOND, | |
57 | - kDefaultInGameTicksBeforeNetDeath = 5 * TICKS_PER_SECOND, | |
58 | - kDefaultOutgoingFlagsQueueSize = TICKS_PER_SECOND / 2, | |
59 | - kDefaultRecoverySendPeriod = TICKS_PER_SECOND / 2, | |
60 | - kDefaultTimingWindowSize = 3 * TICKS_PER_SECOND, | |
61 | - kDefaultTimingNthElement = kDefaultTimingWindowSize / 2, | |
62 | - kLossyByteStreamDataBufferSize = 1280, | |
63 | - kTypicalLossyByteStreamChunkSize = 56, | |
64 | - kLossyByteStreamDescriptorCount = kLossyByteStreamDataBufferSize / kTypicalLossyByteStreamChunkSize | |
65 | -}; | |
66 | - | |
67 | -struct SpokePreferences | |
68 | -{ | |
69 | - int32 mPregameTicksBeforeNetDeath; | |
70 | - int32 mInGameTicksBeforeNetDeath; | |
71 | -// int32 mOutgoingFlagsQueueSize; | |
72 | - int32 mRecoverySendPeriod; | |
73 | - int32 mTimingWindowSize; | |
74 | - int32 mTimingNthElement; | |
75 | - bool mAdjustTiming; | |
76 | -}; | |
77 | - | |
78 | -static SpokePreferences sSpokePreferences; | |
79 | - | |
80 | -static TickBasedActionQueue sOutgoingFlags(kDefaultOutgoingFlagsQueueSize); | |
81 | -static TickBasedActionQueue sUnconfirmedFlags(kDefaultOutgoingFlagsQueueSize); | |
82 | -static DuplicatingTickBasedCircularQueue<action_flags_t> sLocallyGeneratedFlags; | |
83 | -static int32 sSmallestRealGameTick; | |
84 | - | |
85 | -struct IncomingGameDataPacketProcessingContext { | |
86 | - bool mMessagesDone; | |
87 | - bool mGotTimingAdjustmentMessage; | |
88 | - | |
89 | - IncomingGameDataPacketProcessingContext() : mMessagesDone(false), mGotTimingAdjustmentMessage(false) {} | |
90 | -}; | |
91 | - | |
92 | -typedef void (*StarMessageHandler)(AIStream& s, IncomingGameDataPacketProcessingContext& c); | |
93 | -typedef std::map<uint16, StarMessageHandler> MessageTypeToMessageHandler; | |
94 | - | |
95 | -static MessageTypeToMessageHandler sMessageTypeToMessageHandler; | |
96 | - | |
97 | -static int8 sRequestedTimingAdjustment; | |
98 | -static int8 sOutstandingTimingAdjustment; | |
99 | - | |
100 | -struct NetworkPlayer_spoke { | |
101 | - bool mZombie; | |
102 | - bool mConnected; | |
103 | - int32 mNetDeadTick; | |
104 | - WritableTickBasedActionQueue* mQueue; | |
105 | -}; | |
106 | - | |
107 | -static vector<NetworkPlayer_spoke> sNetworkPlayers; | |
108 | -static int32 sNetworkTicker; | |
109 | -static int32 sLastNetworkTickHeard; | |
110 | -static int32 sLastNetworkTickSent; | |
111 | -static bool sConnected = false; | |
112 | -static bool sSpokeActive = false; | |
113 | -static myTMTaskPtr sSpokeTickTask = NULL; | |
114 | -static DDPFramePtr sOutgoingFrame = NULL; | |
115 | -static DDPPacketBuffer sLocalOutgoingBuffer; | |
116 | -static bool sNeedToSendLocalOutgoingBuffer = false; | |
117 | -static bool sHubIsLocal = false; | |
118 | -static NetAddrBlock sHubAddress; | |
119 | -static size_t sLocalPlayerIndex; | |
120 | -static int32 sSmallestUnreceivedTick; | |
121 | -static WindowedNthElementFinder<int32> sNthElementFinder(kDefaultTimingWindowSize); | |
122 | -static bool sTimingMeasurementValid; | |
123 | -static int32 sTimingMeasurement; | |
124 | -static bool sHeardFromHub = false; | |
125 | - | |
126 | -static vector<int32> sDisplayLatencyBuffer; // stores the last 30 latency calculations, in ticks | |
127 | -static uint32 sDisplayLatencyCount = 0; | |
128 | -static int32 sDisplayLatencyTicks = 0; // sum of the latency ticks from the last 30 seconds, using above two | |
129 | - | |
130 | -static int32 sSmallestUnconfirmedTick; | |
131 | - | |
132 | -struct SpokeLossyByteStreamChunkDescriptor | |
133 | -{ | |
134 | - uint16 mLength; | |
135 | - int16 mType; | |
136 | - uint32 mDestinations; | |
137 | -}; | |
138 | - | |
139 | -// This holds outgoing lossy byte stream data | |
140 | -static CircularByteBuffer sOutgoingLossyByteStreamData(kLossyByteStreamDataBufferSize); | |
141 | - | |
142 | -// This holds a descriptor for each chunk of lossy byte stream data held in the above buffer | |
143 | -static CircularQueue<SpokeLossyByteStreamChunkDescriptor> sOutgoingLossyByteStreamDescriptors(kLossyByteStreamDescriptorCount); | |
144 | - | |
145 | -// This is currently used only to hold incoming streaming data until it's passed to the upper-level code | |
146 | -static byte sScratchBuffer[kLossyByteStreamDataBufferSize]; | |
147 | - | |
148 | - | |
149 | -static void spoke_became_disconnected(); | |
150 | -static void spoke_received_game_data_packet_v1(AIStream& ps, bool reflected_flags); | |
151 | -static void process_messages(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
152 | -static void handle_end_of_messages_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
153 | -static void handle_player_net_dead_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
154 | -static void handle_timing_adjustment_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
155 | -static void handle_lossy_byte_stream_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
156 | -static void process_optional_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context, uint16 inMessageType); | |
157 | -static bool spoke_tick(); | |
158 | -static void send_packet(); | |
159 | -static void send_identification_packet(); | |
160 | - | |
161 | - | |
162 | -static inline NetworkPlayer_spoke& | |
163 | -getNetworkPlayer(size_t inIndex) | |
164 | -{ | |
165 | - assert(inIndex < sNetworkPlayers.size()); | |
166 | - return sNetworkPlayers[inIndex]; | |
167 | -} | |
168 | - | |
169 | - | |
170 | - | |
171 | -static inline bool | |
172 | -operator !=(const NetAddrBlock& a, const NetAddrBlock& b) | |
173 | -{ | |
174 | - return memcmp(&a, &b, sizeof(a)) != 0; | |
175 | -} | |
176 | - | |
177 | - | |
178 | - | |
179 | -static OSErr | |
180 | -send_frame_to_local_hub(DDPFramePtr frame, NetAddrBlock *address, short protocolType, short port) | |
181 | -{ | |
182 | - sLocalOutgoingBuffer.datagramSize = frame->data_size; | |
183 | - memcpy(sLocalOutgoingBuffer.datagramData, frame->data, frame->data_size); | |
184 | - sLocalOutgoingBuffer.protocolType = protocolType; | |
185 | - // An all-0 sourceAddress is the cue for "local spoke" currently. | |
186 | - obj_clear(sLocalOutgoingBuffer.sourceAddress); | |
187 | - sNeedToSendLocalOutgoingBuffer = true; | |
188 | - return noErr; | |
189 | -} | |
190 | - | |
191 | - | |
192 | - | |
193 | -static inline void | |
194 | -check_send_packet_to_hub() | |
195 | -{ | |
196 | - if(sNeedToSendLocalOutgoingBuffer) | |
197 | - { | |
198 | - logContextNMT("delivering stored packet to local hub"); | |
199 | - hub_received_network_packet(&sLocalOutgoingBuffer); | |
200 | - } | |
201 | - | |
202 | - sNeedToSendLocalOutgoingBuffer = false; | |
203 | -} | |
204 | - | |
205 | - | |
206 | - | |
207 | -void | |
208 | -spoke_initialize(const NetAddrBlock& inHubAddress, int32 inFirstTick, size_t inNumberOfPlayers, WritableTickBasedActionQueue* const inPlayerQueues[], bool inPlayerConnected[], size_t inLocalPlayerIndex, bool inHubIsLocal) | |
209 | -{ | |
210 | - assert(inNumberOfPlayers >= 1); | |
211 | - assert(inLocalPlayerIndex < inNumberOfPlayers); | |
212 | - assert(inPlayerQueues[inLocalPlayerIndex] != NULL); | |
213 | - assert(inPlayerConnected[inLocalPlayerIndex]); | |
214 | - | |
215 | - sHubIsLocal = inHubIsLocal; | |
216 | - sHubAddress = inHubAddress; | |
217 | - | |
218 | - sLocalPlayerIndex = inLocalPlayerIndex; | |
219 | - | |
220 | - sOutgoingFrame = NetDDPNewFrame(); | |
221 | - | |
222 | - sSmallestRealGameTick = inFirstTick; | |
223 | - int32 theFirstPregameTick = inFirstTick - kPregameTicks; | |
224 | - sOutgoingFlags.reset(theFirstPregameTick); | |
225 | - sUnconfirmedFlags.reset(sSmallestRealGameTick); | |
226 | - sSmallestUnconfirmedTick = sSmallestRealGameTick; | |
227 | - sSmallestUnreceivedTick = theFirstPregameTick; | |
228 | - | |
229 | - sNetworkPlayers.clear(); | |
230 | - sNetworkPlayers.resize(inNumberOfPlayers); | |
231 | - | |
232 | - sLocallyGeneratedFlags.children().clear(); | |
233 | - sLocallyGeneratedFlags.children().insert(&sOutgoingFlags); | |
234 | - sLocallyGeneratedFlags.children().insert(&sUnconfirmedFlags); | |
235 | - | |
236 | - for(size_t i = 0; i < inNumberOfPlayers; i++) | |
237 | - { | |
238 | - sNetworkPlayers[i].mZombie = (inPlayerQueues[i] == NULL); | |
239 | - sNetworkPlayers[i].mConnected = inPlayerConnected[i]; | |
240 | - sNetworkPlayers[i].mNetDeadTick = theFirstPregameTick - 1; | |
241 | - sNetworkPlayers[i].mQueue = inPlayerQueues[i]; | |
242 | - if(sNetworkPlayers[i].mConnected) | |
243 | - { | |
244 | - sNetworkPlayers[i].mQueue->reset(sSmallestRealGameTick); | |
245 | - } | |
246 | - } | |
247 | - | |
248 | - sRequestedTimingAdjustment = 0; | |
249 | - sOutstandingTimingAdjustment = 0; | |
250 | - | |
251 | - sNetworkTicker = 0; | |
252 | - sLastNetworkTickHeard = 0; | |
253 | - sLastNetworkTickSent = 0; | |
254 | - sConnected = true; | |
255 | - sNthElementFinder.reset(sSpokePreferences.mTimingWindowSize); | |
256 | - sTimingMeasurementValid = false; | |
257 | - | |
258 | - sOutgoingLossyByteStreamDescriptors.reset(); | |
259 | - sOutgoingLossyByteStreamData.reset(); | |
260 | - | |
261 | - sMessageTypeToMessageHandler.clear(); | |
262 | - sMessageTypeToMessageHandler[kEndOfMessagesMessageType] = handle_end_of_messages_message; | |
263 | - sMessageTypeToMessageHandler[kTimingAdjustmentMessageType] = handle_timing_adjustment_message; | |
264 | - sMessageTypeToMessageHandler[kPlayerNetDeadMessageType] = handle_player_net_dead_message; | |
265 | - sMessageTypeToMessageHandler[kHubToSpokeLossyByteStreamMessageType] = handle_lossy_byte_stream_message; | |
266 | - | |
267 | - sNeedToSendLocalOutgoingBuffer = false; | |
268 | - | |
269 | - sSpokeActive = true; | |
270 | - sSpokeTickTask = myXTMSetup(1000/TICKS_PER_SECOND, spoke_tick); | |
271 | - | |
272 | - sDisplayLatencyBuffer.resize(TICKS_PER_SECOND, 0); | |
273 | - sDisplayLatencyCount = 0; | |
274 | - sDisplayLatencyTicks = 0; | |
275 | - | |
276 | - sHeardFromHub = false; | |
277 | -} | |
278 | - | |
279 | - | |
280 | - | |
281 | -void | |
282 | -spoke_cleanup(bool inGraceful) | |
283 | -{ | |
284 | - // Stop processing incoming packets (packet processor won't start processing another packet | |
285 | - // due to sSpokeActive = false, and we know it's not in the middle of processing one because | |
286 | - // we take the mutex). | |
287 | - if(take_mytm_mutex()) | |
288 | - { | |
289 | - // Mark the tick task for cancellation (it won't start running again after this returns). | |
290 | - myTMRemove(sSpokeTickTask); | |
291 | - sSpokeTickTask = NULL; | |
292 | - | |
293 | - sSpokeActive = false; | |
294 | - | |
295 | - // We send one last packet here to try to not leave the hub hanging on our ACK. | |
296 | - send_packet(); | |
297 | - check_send_packet_to_hub(); | |
298 | - | |
299 | - release_mytm_mutex(); | |
300 | - } | |
301 | - | |
302 | - // This waits for the tick task to actually finish | |
303 | - myTMCleanup(true); | |
304 | - | |
305 | - sMessageTypeToMessageHandler.clear(); | |
306 | - sNetworkPlayers.clear(); | |
307 | - sLocallyGeneratedFlags.children().clear(); | |
308 | - sDisplayLatencyBuffer.clear(); | |
309 | - NetDDPDisposeFrame(sOutgoingFrame); | |
310 | - sOutgoingFrame = NULL; | |
311 | -} | |
312 | - | |
313 | - | |
314 | - | |
315 | -int32 | |
316 | -spoke_get_net_time() | |
317 | -{ | |
318 | - static int32 sPreviousDelay = -1; | |
319 | - | |
320 | - int32 theDelay = (sSpokePreferences.mAdjustTiming && sTimingMeasurementValid) ? sTimingMeasurement : 0; | |
321 | - | |
322 | - if(theDelay != sPreviousDelay) | |
323 | - { | |
324 | - logDump1("local delay is now %d", theDelay); | |
325 | - sPreviousDelay = theDelay; | |
326 | - } | |
327 | - | |
328 | - return (sConnected ? sOutgoingFlags.getWriteTick() - theDelay : getNetworkPlayer(sLocalPlayerIndex).mQueue->getWriteTick()); | |
329 | -} | |
330 | - | |
331 | - | |
332 | - | |
333 | -void | |
334 | -spoke_distribute_lossy_streaming_bytes_to_everyone(int16 inDistributionType, byte* inBytes, uint16 inLength, bool inExcludeLocalPlayer, bool onlySendToTeam) | |
335 | -{ | |
336 | - | |
337 | - int16 local_team; | |
338 | - if (onlySendToTeam) | |
339 | - { | |
340 | - player_info* player = (player_info *)NetGetPlayerData(sLocalPlayerIndex); | |
341 | - local_team = player->team; | |
342 | - } | |
343 | - | |
344 | - uint32 theDestinations = 0; | |
345 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
346 | - { | |
347 | - if((i != sLocalPlayerIndex || !inExcludeLocalPlayer) && !sNetworkPlayers[i].mZombie && sNetworkPlayers[i].mConnected) | |
348 | - { | |
349 | - if (onlySendToTeam) | |
350 | - { | |
351 | - player_info* player = (player_info *)NetGetPlayerData(i); | |
352 | - if (player->team == local_team) | |
353 | - theDestinations |= (((uint32)1) << i); | |
354 | - | |
355 | - } | |
356 | - else | |
357 | - { | |
358 | - theDestinations |= (((uint32)1) << i); | |
359 | - } | |
360 | - } | |
361 | - } | |
362 | - | |
363 | - spoke_distribute_lossy_streaming_bytes(inDistributionType, theDestinations, inBytes, inLength); | |
364 | -} | |
365 | - | |
366 | - | |
367 | - | |
368 | -void | |
369 | -spoke_distribute_lossy_streaming_bytes(int16 inDistributionType, uint32 inDestinationsBitmask, byte* inBytes, uint16 inLength) | |
370 | -{ | |
371 | - if(inLength > sOutgoingLossyByteStreamData.getRemainingSpace()) | |
372 | - { | |
373 | - logNoteNMT2("spoke has insufficient buffer space for %hu bytes of outgoing lossy streaming type %hd; discarded", inLength, inDistributionType); | |
374 | - return; | |
375 | - } | |
376 | - | |
377 | - if(sOutgoingLossyByteStreamDescriptors.getRemainingSpace() < 1) | |
378 | - { | |
379 | - logNoteNMT2("spoke has exhausted descriptor buffer space; discarding %hu bytes of outgoing lossy streaming type %hd", inLength, inDistributionType); | |
380 | - return; | |
381 | - } | |
382 | - | |
383 | - struct SpokeLossyByteStreamChunkDescriptor theDescriptor; | |
384 | - theDescriptor.mLength = inLength; | |
385 | - theDescriptor.mDestinations = inDestinationsBitmask; | |
386 | - theDescriptor.mType = inDistributionType; | |
387 | - | |
388 | - logDumpNMT3("spoke application decided to send %d bytes of lossy streaming type %d destined for players 0x%x", inLength, inDistributionType, inDestinationsBitmask); | |
389 | - | |
390 | - sOutgoingLossyByteStreamData.enqueueBytes(inBytes, inLength); | |
391 | - sOutgoingLossyByteStreamDescriptors.enqueue(theDescriptor); | |
392 | -} | |
393 | - | |
394 | - | |
395 | - | |
396 | -static void | |
397 | -spoke_became_disconnected() | |
398 | -{ | |
399 | - sConnected = false; | |
400 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
401 | - { | |
402 | - if(sNetworkPlayers[i].mConnected) | |
403 | - make_player_really_net_dead(i); | |
404 | - } | |
405 | -} | |
406 | - | |
407 | - | |
408 | - | |
409 | -void | |
410 | -spoke_received_network_packet(DDPPacketBufferPtr inPacket) | |
411 | -{ | |
412 | - logContextNMT("spoke processing a received packet"); | |
413 | - | |
414 | - // If we've already given up on the connection, ignore packets. | |
415 | - if(!sConnected || !sSpokeActive) | |
416 | - return; | |
417 | - | |
418 | - // Ignore packets not from our hub | |
419 | -// if(inPacket->sourceAddress != sHubAddress) | |
420 | -// return; | |
421 | - | |
422 | - try { | |
423 | - AIStreamBE ps(inPacket->datagramData, inPacket->datagramSize); | |
424 | - | |
425 | - uint16 thePacketMagic; | |
426 | - ps >> thePacketMagic; | |
427 | - | |
428 | - uint16 thePacketCRC; | |
429 | - ps >> thePacketCRC; | |
430 | - | |
431 | - // blank out the CRC field before calculating | |
432 | - inPacket->datagramData[2] = 0; | |
433 | - inPacket->datagramData[3] = 0; | |
434 | - | |
435 | - if (thePacketCRC != calculate_data_crc_ccitt(inPacket->datagramData, inPacket->datagramSize)) | |
436 | - { | |
437 | - logWarningNMT1("CRC failure; discarding packet type %i", thePacketMagic); | |
438 | - return; | |
439 | - } | |
440 | - | |
441 | - switch(thePacketMagic) | |
442 | - { | |
443 | - case kHubToSpokeGameDataPacketV1Magic: | |
444 | - spoke_received_game_data_packet_v1(ps, false); | |
445 | - break; | |
446 | - | |
447 | - case kHubToSpokeGameDataPacketWithSpokeFlagsV1Magic: | |
448 | - spoke_received_game_data_packet_v1(ps, true); | |
449 | - break; | |
450 | - | |
451 | - default: | |
452 | - // Ignore unknown packet types | |
453 | - logTraceNMT1("unknown packet type %i", thePacketMagic); | |
454 | - break; | |
455 | - } | |
456 | - } | |
457 | - catch(...) | |
458 | - { | |
459 | - // do nothing special, we just ignore the remainder of the packet. | |
460 | - } | |
461 | -} | |
462 | - | |
463 | - | |
464 | - | |
465 | -static void | |
466 | -spoke_received_game_data_packet_v1(AIStream& ps, bool reflected_flags) | |
467 | -{ | |
468 | - sHeardFromHub = true; | |
469 | - | |
470 | - IncomingGameDataPacketProcessingContext context; | |
471 | - | |
472 | - // Piggybacked ACK | |
473 | - int32 theSmallestUnacknowledgedTick; | |
474 | - ps >> theSmallestUnacknowledgedTick; | |
475 | - | |
476 | - // we can get an early ACK only if the server made up flags for us... | |
477 | - if (theSmallestUnacknowledgedTick > sOutgoingFlags.getWriteTick()) | |
478 | - { | |
479 | - if (reflected_flags) | |
480 | - { | |
481 | - theSmallestUnacknowledgedTick = sOutgoingFlags.getWriteTick(); | |
482 | - } | |
483 | - else | |
484 | - { | |
485 | - logTraceNMT2("early ack (%d > %d)", theSmallestUnacknowledgedTick, sOutgoingFlags.getWriteTick()); | |
486 | - return; | |
487 | - } | |
488 | - } | |
489 | - | |
490 | - | |
491 | - // Heard from hub | |
492 | - sLastNetworkTickHeard = sNetworkTicker; | |
493 | - | |
494 | - // Remove acknowledged elements from outgoing queue | |
495 | - for(int tick = sOutgoingFlags.getReadTick(); tick < theSmallestUnacknowledgedTick; tick++) | |
496 | - { | |
497 | - logTraceNMT1("dequeueing tick %d from sOutgoingFlags", tick); | |
498 | - sOutgoingFlags.dequeue(); | |
499 | - } | |
500 | - | |
501 | - // Process messages | |
502 | - process_messages(ps, context); | |
503 | - | |
504 | - if(!context.mGotTimingAdjustmentMessage) | |
505 | - { | |
506 | - if(sRequestedTimingAdjustment != 0) | |
507 | - logTraceNMT("timing adjustment no longer requested"); | |
508 | - | |
509 | - sRequestedTimingAdjustment = 0; | |
510 | - } | |
511 | - | |
512 | - // Action_flags!!! | |
513 | - // If there aren't any, we're done | |
514 | - if(ps.tellg() >= ps.maxg()) | |
515 | - { | |
516 | - // If we are the only connected player, well because of the way the loop below works, | |
517 | - // we have to enqueue net_dead flags for the other players now, up to match the most | |
518 | - // recent tick the hub has acknowledged. | |
519 | - // We can't assume we are the only connected player merely by virtue of there being no | |
520 | - // flags in the packet. The hub could be waiting on somebody else to send and is | |
521 | - // essentially sending us "keep-alive" packets (which could contain no new flags) in the meantime. | |
522 | - // In other words, (we are alone) implies (no flags in packet), but not the converse. | |
523 | - // Here "alone" means there are no other players who are connected or who will be netdead | |
524 | - // sometime in the future. (If their NetDeadTick is greater than the ACKed tick, we expect | |
525 | - // that the hub will be sending actual flags in the future to make up the difference.) | |
526 | - bool weAreAlone = true; | |
527 | - int32 theSmallestUnacknowledgedTick = sOutgoingFlags.getReadTick(); | |
528 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
529 | - { | |
530 | - if(i != sLocalPlayerIndex && (sNetworkPlayers[i].mConnected || sNetworkPlayers[i].mNetDeadTick > theSmallestUnacknowledgedTick)) | |
531 | - { | |
532 | - weAreAlone = false; | |
533 | - break; | |
534 | - } | |
535 | - } | |
536 | - | |
537 | - if(weAreAlone) | |
538 | - { | |
539 | - logContextNMT("handling special \"we are alone\" case"); | |
540 | - | |
541 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
542 | - { | |
543 | - NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
544 | - if (i == sLocalPlayerIndex) | |
545 | - { | |
546 | - while (sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()) | |
547 | - { | |
548 | - sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick++)); | |
549 | - } | |
550 | - } | |
551 | - else if (!thePlayer.mZombie) | |
552 | - { | |
553 | - while(thePlayer.mQueue->getWriteTick() < theSmallestUnacknowledgedTick) | |
554 | - { | |
555 | - logDumpNMT2("enqueued NET_DEAD_ACTION_FLAG for player %d tick %d", i, thePlayer.mQueue->getWriteTick()); | |
556 | - thePlayer.mQueue->enqueue(static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG)); | |
557 | - } | |
558 | - } | |
559 | - } | |
560 | - | |
561 | - sSmallestUnreceivedTick = theSmallestUnacknowledgedTick; | |
562 | - logDumpNMT1("sSmallestUnreceivedTick is now %d", sSmallestUnreceivedTick); | |
563 | - } | |
564 | - | |
565 | - return; | |
566 | - } // no data left in packet | |
567 | - | |
568 | - int32 theSmallestUnreadTick; | |
569 | - ps >> theSmallestUnreadTick; | |
570 | - | |
571 | - // Can't accept packets that skip ticks | |
572 | - if(theSmallestUnreadTick > sSmallestUnreceivedTick) | |
573 | - { | |
574 | - logTraceNMT2("early flags (%d > %d)", theSmallestUnreadTick, sSmallestUnreceivedTick); | |
575 | - return; | |
576 | - } | |
577 | - | |
578 | - // Figure out how many ticks of flags we can actually enqueue | |
579 | - // We want to stock all queues evenly, since we ACK everyone's flags for a tick together. | |
580 | - int theSmallestQueueSpace = INT_MAX; | |
581 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
582 | - { | |
583 | - // we'll never get flags for zombies, and we're not expected | |
584 | - // to enqueue flags for zombies | |
585 | - if(sNetworkPlayers[i].mZombie) | |
586 | - continue; | |
587 | - | |
588 | - int theQueueSpace = sNetworkPlayers[i].mQueue->availableCapacity(); | |
589 | - | |
590 | - /* | |
591 | - hmm, taking this exemption out, because we will start enqueueing PLAYER_NET_DEAD_FLAG onto the queue. | |
592 | - // If player is netdead or will become netdead before queue fills, | |
593 | - // player's queue space will not limit us | |
594 | - if(!sNetworkPlayers[i].mConnected) | |
595 | - { | |
596 | - int theRemainingLiveTicks = sNetworkPlayers[i].mNetDeadTick - sSmallestUnreceivedTick; | |
597 | - if(theRemainingLiveTicks < theQueueSpace) | |
598 | - continue; | |
599 | - } | |
600 | - */ | |
601 | - | |
602 | - if(theQueueSpace < theSmallestQueueSpace) | |
603 | - theSmallestQueueSpace = theQueueSpace; | |
604 | - } | |
605 | - | |
606 | - logDumpNMT1("%d queue space available", theSmallestQueueSpace); | |
607 | - | |
608 | - // Read and enqueue the actual action_flags from the packet | |
609 | - // The body of this loop is a bit more convoluted than you might | |
610 | - // expect, because the same loop is used to skip already-seen action_flags | |
611 | - // and to enqueue new ones. | |
612 | - while(ps.tellg() < ps.maxg()) | |
613 | - { | |
614 | - // If we've no room to enqueue stuff, no point in finishing reading the packet. | |
615 | - if(theSmallestQueueSpace <= 0) | |
616 | - break; | |
617 | - | |
618 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
619 | - { | |
620 | - | |
621 | - // We'll never get flags for zombies | |
622 | - if (sNetworkPlayers[i].mZombie) | |
623 | - continue; | |
624 | - | |
625 | - // if our own flags are not sent back to us, | |
626 | - // confirm the ones we have in our unconfirmed queue, | |
627 | - // and do not read any from the packet | |
628 | - if (i == sLocalPlayerIndex && !reflected_flags) | |
629 | - { | |
630 | - if (theSmallestUnreadTick == sSmallestUnreceivedTick && theSmallestUnreadTick >= sSmallestRealGameTick) | |
631 | - { | |
632 | - assert(sNetworkPlayers[i].mQueue->getWriteTick() == sSmallestUnconfirmedTick); | |
633 | - assert(sSmallestUnconfirmedTick >= sUnconfirmedFlags.getReadTick()); | |
634 | - assert(sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()); | |
635 | - // confirm this flag | |
636 | - sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick)); | |
637 | - sSmallestUnconfirmedTick++; | |
638 | - } | |
639 | - | |
640 | - continue; | |
641 | - } | |
642 | - | |
643 | - bool shouldEnqueueNetDeadFlags = false; | |
644 | - | |
645 | - // We won't get flags for netdead players | |
646 | - NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
647 | - if(!thePlayer.mConnected) | |
648 | - { | |
649 | - if(thePlayer.mNetDeadTick < theSmallestUnreadTick) | |
650 | - shouldEnqueueNetDeadFlags = true; | |
651 | - | |
652 | - if(thePlayer.mNetDeadTick == theSmallestUnreadTick) | |
653 | - { | |
654 | - // Only actually act if this tick is new to us | |
655 | - if(theSmallestUnreadTick == sSmallestUnreceivedTick) | |
656 | - make_player_really_net_dead(i); | |
657 | - shouldEnqueueNetDeadFlags = true; | |
658 | - } | |
659 | - } | |
660 | - | |
661 | - // Decide what flags are appropriate for this player this tick | |
662 | - action_flags_t theFlags; | |
663 | - if(shouldEnqueueNetDeadFlags) | |
664 | - // We effectively generate a tick's worth of flags in lieu of reading it from the packet. | |
665 | - theFlags = static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG); | |
666 | - else | |
667 | - { | |
668 | - // We should have a flag for this player for this tick! | |
669 | - try | |
670 | - { | |
671 | - ps >> theFlags; | |
672 | - } | |
673 | - catch (const AStream::failure& f) | |
674 | - { | |
675 | - logWarningNMT3("AStream exception (%s) for player %i at theSmallestUnreadTick %i! OOS is likely!\n", f.what(), i, theSmallestUnreadTick); | |
676 | - return; | |
677 | - } | |
678 | - } | |
679 | - | |
680 | - | |
681 | - // Now, we've gotten flags, probably from the packet... should we enqueue them? | |
682 | - if(theSmallestUnreadTick == sSmallestUnreceivedTick) | |
683 | - { | |
684 | - if(theSmallestUnreadTick >= sSmallestRealGameTick) | |
685 | - { | |
686 | - WritableTickBasedActionQueue& theQueue = *(sNetworkPlayers[i].mQueue); | |
687 | - assert(theQueue.getWriteTick() == sSmallestUnreceivedTick); | |
688 | - assert(theQueue.availableCapacity() > 0); | |
689 | - logTraceNMT3("enqueueing flags %x for player %d tick %d", theFlags, i, theQueue.getWriteTick()); | |
690 | - theQueue.enqueue(theFlags); | |
691 | - if (i == sLocalPlayerIndex) sSmallestUnconfirmedTick++; | |
692 | - } | |
693 | - } | |
694 | - | |
695 | - } // iterate over players | |
696 | - | |
697 | - theSmallestUnreadTick++; | |
698 | - if(sSmallestUnreceivedTick < theSmallestUnreadTick) | |
699 | - { | |
700 | - theSmallestQueueSpace--; | |
701 | - sSmallestUnreceivedTick = theSmallestUnreadTick; | |
702 | - | |
703 | - int32 theLatencyMeasurement = sOutgoingFlags.getWriteTick() - sSmallestUnreceivedTick; | |
704 | - logDumpNMT1("latency measurement: %d", theLatencyMeasurement); | |
705 | - | |
706 | -// We can't use NthElementFinder at interrupt time in Mac OS 9 since its std::set can [de]allocate memory | |
707 | -// We don't really use it for anything on the spoke-side now anyway, thanks to player motion prediction... | |
708 | -#if !(defined(mac) && !defined(__MACH__)) | |
709 | - sNthElementFinder.insert(theLatencyMeasurement); | |
710 | - // We capture these values here so we don't have to take a lock in GetNetTime. | |
711 | - sTimingMeasurementValid = sNthElementFinder.window_full(); | |
712 | - if(sTimingMeasurementValid) | |
713 | - sTimingMeasurement = sNthElementFinder.nth_largest_element(sSpokePreferences.mTimingNthElement); | |
714 | -#endif | |
715 | - | |
716 | - // update the latency display | |
717 | - sDisplayLatencyTicks -= sDisplayLatencyBuffer[sDisplayLatencyCount % sDisplayLatencyBuffer.size()]; | |
718 | - sDisplayLatencyBuffer[sDisplayLatencyCount++ % sDisplayLatencyBuffer.size()] = theLatencyMeasurement; | |
719 | - sDisplayLatencyTicks += theLatencyMeasurement; | |
720 | - } | |
721 | - | |
722 | - } // loop while there's packet data left | |
723 | - | |
724 | -} | |
725 | - | |
726 | - | |
727 | - | |
728 | -static void | |
729 | -process_messages(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
730 | -{ | |
731 | - while(!context.mMessagesDone) | |
732 | - { | |
733 | - uint16 theMessageType; | |
734 | - ps >> theMessageType; | |
735 | - | |
736 | - MessageTypeToMessageHandler::iterator i = sMessageTypeToMessageHandler.find(theMessageType); | |
737 | - | |
738 | - if(i == sMessageTypeToMessageHandler.end()) | |
739 | - process_optional_message(ps, context, theMessageType); | |
740 | - else | |
741 | - i->second(ps, context); | |
742 | - } | |
743 | -} | |
744 | - | |
745 | - | |
746 | - | |
747 | -static void | |
748 | -handle_end_of_messages_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
749 | -{ | |
750 | - context.mMessagesDone = true; | |
751 | -} | |
752 | - | |
753 | - | |
754 | - | |
755 | -static void | |
756 | -handle_player_net_dead_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
757 | -{ | |
758 | - uint8 thePlayerIndex; | |
759 | - int32 theTick; | |
760 | - | |
761 | - ps >> thePlayerIndex >> theTick; | |
762 | - | |
763 | - if(thePlayerIndex > sNetworkPlayers.size()) | |
764 | - return; | |
765 | - | |
766 | - sNetworkPlayers[thePlayerIndex].mConnected = false; | |
767 | - sNetworkPlayers[thePlayerIndex].mNetDeadTick = theTick; | |
768 | - | |
769 | - logDumpNMT2("netDead message: player %d in tick %d", thePlayerIndex, theTick); | |
770 | -} | |
771 | - | |
772 | - | |
773 | - | |
774 | -static void | |
775 | -handle_timing_adjustment_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
776 | -{ | |
777 | - int8 theAdjustment; | |
778 | - | |
779 | - ps >> theAdjustment; | |
780 | - | |
781 | - if(theAdjustment != sRequestedTimingAdjustment) | |
782 | - { | |
783 | - sOutstandingTimingAdjustment = theAdjustment; | |
784 | - sRequestedTimingAdjustment = theAdjustment; | |
785 | - logTraceNMT2("new timing adjustment message; requested: %d outstanding: %d", sRequestedTimingAdjustment, sOutstandingTimingAdjustment); | |
786 | - } | |
787 | - | |
788 | - context.mGotTimingAdjustmentMessage = true; | |
789 | -} | |
790 | - | |
791 | - | |
792 | - | |
793 | -static void | |
794 | -handle_lossy_byte_stream_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
795 | -{ | |
796 | - uint16 theMessageLength; | |
797 | - ps >> theMessageLength; | |
798 | - | |
799 | - size_t theStartOfMessage = ps.tellg(); | |
800 | - | |
801 | - int16 theDistributionType; | |
802 | - uint8 theSendingPlayer; | |
803 | - ps >> theDistributionType >> theSendingPlayer; | |
804 | - | |
805 | - uint16 theDataLength = theMessageLength - (ps.tellg() - theStartOfMessage); | |
806 | - uint16 theSpilloverDataLength = 0; | |
807 | - if(theDataLength > sizeof(sScratchBuffer)) | |
808 | - { | |
809 | - logNoteNMT3("received too many bytes (%d) of lossy streaming data type %d from player %d; truncating", theDataLength, theDistributionType, theSendingPlayer); | |
810 | - theSpilloverDataLength = theDataLength - sizeof(sScratchBuffer); | |
811 | - theDataLength = sizeof(sScratchBuffer); | |
812 | - } | |
813 | - ps.read(sScratchBuffer, theDataLength); | |
814 | - ps.ignore(theSpilloverDataLength); | |
815 | - | |
816 | - logDumpNMT3("received %d bytes of lossy streaming type %d data from player %d", theDataLength, theDistributionType, theSendingPlayer); | |
817 | - | |
818 | - call_distribution_response_function_if_available(sScratchBuffer, theDataLength, theDistributionType, theSendingPlayer); | |
819 | -} | |
820 | - | |
821 | - | |
822 | - | |
823 | -static void | |
824 | -process_optional_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context, uint16 inMessageType) | |
825 | -{ | |
826 | - // We don't know of any optional messages, so we just skip any we encounter. | |
827 | - // (All optional messages are required to encode their length (not including the | |
828 | - // space required for the message type or length) in the two bytes immediately | |
829 | - // following the message type.) | |
830 | - uint16 theLength; | |
831 | - ps >> theLength; | |
832 | - | |
833 | - ps.ignore(theLength); | |
834 | -} | |
835 | - | |
836 | - | |
837 | - | |
838 | -static bool | |
839 | -spoke_tick() | |
840 | -{ | |
841 | - logContextNMT1("processing spoke_tick %d", sNetworkTicker); | |
842 | - | |
843 | - sNetworkTicker++; | |
844 | - | |
845 | - if(sConnected) | |
846 | - { | |
847 | - int32 theSilentTicksBeforeNetDeath = (sOutgoingFlags.getReadTick() >= sSmallestRealGameTick) ? sSpokePreferences.mInGameTicksBeforeNetDeath : sSpokePreferences.mPregameTicksBeforeNetDeath; | |
848 | - | |
849 | - if(sNetworkTicker - sLastNetworkTickHeard > theSilentTicksBeforeNetDeath) | |
850 | - { | |
851 | - logTraceNMT("giving up on hub; disconnecting"); | |
852 | - spoke_became_disconnected(); | |
853 | - return true; | |
854 | - } | |
855 | - } | |
856 | - | |
857 | - bool shouldSend = false; | |
858 | - | |
859 | - // Negative timing adjustment means we need to provide extra ticks because we're late. | |
860 | - // We let this cover the normal timing adjustment = 0 case too. | |
861 | - if(sOutstandingTimingAdjustment <= 0) | |
862 | - { | |
863 | - int theNumberOfFlagsToProvide = -sOutstandingTimingAdjustment + 1; | |
864 | - | |
865 | - logDumpNMT1("want to provide %d flags", theNumberOfFlagsToProvide); | |
866 | - | |
867 | - while(theNumberOfFlagsToProvide > 0) | |
868 | - { | |
869 | - | |
870 | - // If we're not connected, write only to our local game's queue. | |
871 | - // If we are connected, | |
872 | - // if we're actually in the game, write to both local game and outbound flags queues | |
873 | - // else (if pregame), write only to the outbound flags queue. | |
874 | - | |
875 | - WritableTickBasedActionQueue& theTargetQueue = | |
876 | - sConnected ? | |
877 | - ((sOutgoingFlags.getWriteTick() >= sSmallestRealGameTick) ? | |
878 | - static_cast<WritableTickBasedActionQueue&>(sLocallyGeneratedFlags) | |
879 | - : static_cast<WritableTickBasedActionQueue&>(sOutgoingFlags)) | |
880 | - : *(sNetworkPlayers[sLocalPlayerIndex].mQueue); | |
881 | - | |
882 | - if(theTargetQueue.availableCapacity() <= 0) | |
883 | - break; | |
884 | - | |
885 | - logDumpNMT1("enqueueing flags for tick %d", theTargetQueue.getWriteTick()); | |
886 | - | |
887 | - theTargetQueue.enqueue(parse_keymap()); | |
888 | - shouldSend = true; | |
889 | - theNumberOfFlagsToProvide--; | |
890 | - } | |
891 | - | |
892 | - // Prevent creeping timing adjustment during "lulls"; OTOH remember to | |
893 | - // finish next time if we made progress but couldn't complete our obligation. | |
894 | - if(theNumberOfFlagsToProvide != -sOutstandingTimingAdjustment + 1) | |
895 | - sOutstandingTimingAdjustment = -theNumberOfFlagsToProvide; | |
896 | - } | |
897 | - // Positive timing adjustment means we should delay sending for a while, | |
898 | - // so we just throw away this local tick. | |
899 | - else | |
900 | - { | |
901 | - logDumpNMT("ignoring this tick for timing adjustment"); | |
902 | - sOutstandingTimingAdjustment--; | |
903 | - } | |
904 | - | |
905 | - logDumpNMT1("sOutstandingTimingAdjustment is now %d", sOutstandingTimingAdjustment); | |
906 | - | |
907 | - if(sOutgoingLossyByteStreamDescriptors.getCountOfElements() > 0) | |
908 | - shouldSend = true; | |
909 | - | |
910 | - // If we're connected and (we generated new data or if it's been long enough since we last sent), send. | |
911 | - if(sConnected) | |
912 | - { | |
913 | - if (sHeardFromHub) { | |
914 | - if(shouldSend || (sNetworkTicker - sLastNetworkTickSent) >= sSpokePreferences.mRecoverySendPeriod) | |
915 | - send_packet(); | |
916 | - } else { | |
917 | - if (!(sNetworkTicker % 30)) | |
918 | - send_identification_packet(); | |
919 | - } | |
920 | - } | |
921 | - else | |
922 | - { | |
923 | - int32 theLocalPlayerWriteTick = getNetworkPlayer(sLocalPlayerIndex).mQueue->getWriteTick(); | |
924 | - | |
925 | - // Since we're not connected, we won't be enqueueing flags for the other players in the packet handler. | |
926 | - // So, we do it here to keep the game moving. | |
927 | - for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
928 | - { | |
929 | - if(i == sLocalPlayerIndex) | |
930 | - { | |
931 | - // move our flags from sent queue to player queue | |
932 | - while (sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()) | |
933 | - { | |
934 | - sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick++)); | |
935 | - } | |
936 | - continue; | |
937 | - } | |
938 | - | |
939 | - NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
940 | - | |
941 | - if(!thePlayer.mZombie) | |
942 | - { | |
943 | - while(thePlayer.mQueue->getWriteTick() < theLocalPlayerWriteTick) | |
944 | - { | |
945 | - logDumpNMT2("enqueueing NET_DEAD_ACTION_FLAG for player %d tick %d", i, thePlayer.mQueue->getWriteTick()); | |
946 | - thePlayer.mQueue->enqueue(static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG)); | |
947 | - } | |
948 | - } | |
949 | - } | |
950 | - } | |
951 | - | |
952 | - check_send_packet_to_hub(); | |
953 | - | |
954 | - // We want to run again. | |
955 | - return true; | |
956 | -} | |
957 | - | |
958 | - | |
959 | - | |
960 | -static void | |
961 | -send_packet() | |
962 | -{ | |
963 | - try { | |
964 | - AOStreamBE hdr(sOutgoingFrame->data, kStarPacketHeaderSize); | |
965 | - AOStreamBE ps(sOutgoingFrame->data, ddpMaxData, kStarPacketHeaderSize); | |
966 | - | |
967 | - // Packet type | |
968 | - hdr << (uint16)kSpokeToHubGameDataPacketV1Magic; | |
969 | - | |
970 | - // Acknowledgement | |
971 | - ps << sSmallestUnreceivedTick; | |
972 | - | |
973 | - // Messages | |
974 | - // Outstanding lossy streaming bytes? | |
975 | - if(sOutgoingLossyByteStreamDescriptors.getCountOfElements() > 0) | |
976 | - { | |
977 | - // Note: we make a conscious decision here to dequeue these things before | |
978 | - // writing to ps, so that if the latter operation exhausts ps's buffer and | |
979 | - // throws, we have less data to mess with next time, and shouldn't end up | |
980 | - // throwing every time we try to send here. | |
981 | - // If we eventually got smarter about managing packet space, we could try | |
982 | - // harder to preserve and pace data - e.g. change the 'if' immediately before this | |
983 | - // comment to a 'while', only put in as much data as we think we can fit, etc. | |
984 | - SpokeLossyByteStreamChunkDescriptor theDescriptor = sOutgoingLossyByteStreamDescriptors.peek(); | |
985 | - sOutgoingLossyByteStreamDescriptors.dequeue(); | |
986 | - | |
987 | - uint16 theMessageLength = theDescriptor.mLength + sizeof(theDescriptor.mType) + sizeof(theDescriptor.mDestinations); | |
988 | - | |
989 | - ps << (uint16)kSpokeToHubLossyByteStreamMessageType | |
990 | - << theMessageLength | |
991 | - << theDescriptor.mType | |
992 | - << theDescriptor.mDestinations; | |
993 | - | |
994 | - // XXX unnecessary copy due to overly restrictive interfaces (retaining for clarity) | |
995 | - assert(theDescriptor.mLength <= sizeof(sScratchBuffer)); | |
996 | - sOutgoingLossyByteStreamData.peekBytes(sScratchBuffer, theDescriptor.mLength); | |
997 | - sOutgoingLossyByteStreamData.dequeue(theDescriptor.mLength); | |
998 | - | |
999 | - ps.write(sScratchBuffer, theDescriptor.mLength); | |
1000 | - } | |
1001 | - | |
1002 | - // No more messages | |
1003 | - ps << (uint16)kEndOfMessagesMessageType; | |
1004 | - | |
1005 | - // Action_flags!!! | |
1006 | - if(sOutgoingFlags.size() > 0) | |
1007 | - { | |
1008 | - ps << sOutgoingFlags.getReadTick(); | |
1009 | - for(int32 tick = sOutgoingFlags.getReadTick(); tick < sOutgoingFlags.getWriteTick(); tick++) | |
1010 | - ps << sOutgoingFlags.peek(tick); | |
1011 | - } | |
1012 | - | |
1013 | - logDumpNMT3("preparing to send packet: ACK %d, flags [%d,%d)", sSmallestUnreceivedTick, sOutgoingFlags.getReadTick(), sOutgoingFlags.getWriteTick()); | |
1014 | - | |
1015 | - // blank out the CRC before calculating it | |
1016 | - sOutgoingFrame->data[2] = 0; | |
1017 | - sOutgoingFrame->data[3] = 0; | |
1018 | - | |
1019 | - uint16 crc = calculate_data_crc_ccitt(sOutgoingFrame->data, ps.tellp()); | |
1020 | - hdr << crc; | |
1021 | - | |
1022 | - // Send the packet | |
1023 | - sOutgoingFrame->data_size = ps.tellp(); | |
1024 | - | |
1025 | - if(sHubIsLocal) | |
1026 | - send_frame_to_local_hub(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1027 | - else | |
1028 | - NetDDPSendFrame(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1029 | - | |
1030 | - sLastNetworkTickSent = sNetworkTicker; | |
1031 | - } | |
1032 | - catch (...) { | |
1033 | - } | |
1034 | -} | |
1035 | - | |
1036 | - | |
1037 | - | |
1038 | -static void | |
1039 | -send_identification_packet() | |
1040 | -{ | |
1041 | - try { | |
1042 | - AOStreamBE hdr(sOutgoingFrame->data, kStarPacketHeaderSize); | |
1043 | - AOStreamBE ps(sOutgoingFrame->data, ddpMaxData, kStarPacketHeaderSize); | |
1044 | - | |
1045 | - // Message type | |
1046 | - hdr << (uint16) kSpokeToHubIdentification; | |
1047 | - | |
1048 | - // ID | |
1049 | - ps << (uint16)sLocalPlayerIndex; | |
1050 | - | |
1051 | - // blank out the CRC field before calculating | |
1052 | - sOutgoingFrame->data[2] = 0; | |
1053 | - sOutgoingFrame->data[3] = 0; | |
1054 | - | |
1055 | - uint16 crc = calculate_data_crc_ccitt(sOutgoingFrame->data, ps.tellp()); | |
1056 | - hdr << crc; | |
1057 | - | |
1058 | - // Send the packet | |
1059 | - sOutgoingFrame->data_size = ps.tellp(); | |
1060 | - if(sHubIsLocal) | |
1061 | - send_frame_to_local_hub(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1062 | - else | |
1063 | - NetDDPSendFrame(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1064 | - } | |
1065 | - catch (...) { | |
1066 | - } | |
1067 | -} | |
1068 | - | |
1069 | -int32 spoke_latency() | |
1070 | -{ | |
1071 | - return (sDisplayLatencyCount >= TICKS_PER_SECOND) ? sDisplayLatencyTicks * 1000 / TICKS_PER_SECOND / sDisplayLatencyBuffer.size() : NetworkStats::invalid; | |
1072 | -} | |
1073 | - | |
1074 | -TickBasedActionQueue* spoke_get_unconfirmed_flags_queue() | |
1075 | -{ | |
1076 | - return &sUnconfirmedFlags; | |
1077 | -} | |
1078 | - | |
1079 | -int32 spoke_get_smallest_unconfirmed_tick() | |
1080 | -{ | |
1081 | - return sSmallestUnconfirmedTick; | |
1082 | -} | |
1083 | - | |
1084 | - | |
1085 | -static inline const char *BoolString(bool B) {return (B ? "true" : "false");} | |
1086 | - | |
1087 | -enum { | |
1088 | - kPregameTicksBeforeNetDeathAttribute, | |
1089 | - kInGameTicksBeforeNetDeathAttribute, | |
1090 | - // kOutgoingFlagsQueueSizeAttribute, | |
1091 | - kRecoverySendPeriodAttribute, | |
1092 | - kTimingWindowSizeAttribute, | |
1093 | - kTimingNthElementAttribute, | |
1094 | - kNumInt32Attributes, | |
1095 | - kAdjustTimingAttribute = kNumInt32Attributes, | |
1096 | - kNumAttributes | |
1097 | -}; | |
1098 | - | |
1099 | -static const char* sAttributeStrings[kNumInt32Attributes] = | |
1100 | -{ | |
1101 | - "pregame_ticks_before_net_death", | |
1102 | - "ingame_ticks_before_net_death", | |
1103 | -// "outgoing_flags_queue_size", | |
1104 | - "recovery_send_period", | |
1105 | - "timing_window_size", | |
1106 | - "timing_nth_element" | |
1107 | -}; | |
1108 | - | |
1109 | -static int32* sAttributeDestinations[kNumInt32Attributes] = | |
1110 | -{ | |
1111 | - &sSpokePreferences.mPregameTicksBeforeNetDeath, | |
1112 | - &sSpokePreferences.mInGameTicksBeforeNetDeath, | |
1113 | -// &sSpokePreferences.mOutgoingFlagsQueueSize, | |
1114 | - &sSpokePreferences.mRecoverySendPeriod, | |
1115 | - &sSpokePreferences.mTimingWindowSize, | |
1116 | - &sSpokePreferences.mTimingNthElement | |
1117 | -}; | |
1118 | - | |
1119 | -class XML_SpokeConfigurationParser: public XML_ElementParser | |
1120 | -{ | |
1121 | -public: | |
1122 | - bool Start(); | |
1123 | - bool HandleAttribute(const char *Tag, const char *Value); | |
1124 | - bool AttributesDone(); | |
1125 | - | |
1126 | - XML_SpokeConfigurationParser(): XML_ElementParser("spoke") {} | |
1127 | - | |
1128 | -protected: | |
1129 | - bool mAttributePresent[kNumAttributes]; | |
1130 | - int32 mAttribute[kNumInt32Attributes]; | |
1131 | - bool mAdjustTiming; | |
1132 | -}; | |
1133 | - | |
1134 | -bool XML_SpokeConfigurationParser::Start() | |
1135 | -{ | |
1136 | - for(int i = 0; i < kNumAttributes; i++) | |
1137 | - mAttributePresent[i] = false; | |
1138 | - | |
1139 | - return true; | |
1140 | -} | |
1141 | - | |
1142 | -static const char* sAttributeMultiplySpecifiedString = "attribute multiply specified"; | |
1143 | - | |
1144 | -bool XML_SpokeConfigurationParser::HandleAttribute(const char *Tag, const char *Value) | |
1145 | -{ | |
1146 | - if (StringsEqual(Tag,"adjust_timing")) | |
1147 | - { | |
1148 | - if(!mAttributePresent[kAdjustTimingAttribute]) { | |
1149 | - if(ReadBooleanValueAsBool(Value,mAdjustTiming)) { | |
1150 | - mAttributePresent[kAdjustTimingAttribute] = true; | |
1151 | - return true; | |
1152 | - } | |
1153 | - else | |
1154 | - return false; | |
1155 | - } | |
1156 | - else { | |
1157 | - ErrorString = sAttributeMultiplySpecifiedString; | |
1158 | - return false; | |
1159 | - } | |
1160 | - } | |
1161 | - | |
1162 | - else | |
1163 | - { | |
1164 | - for(size_t i = 0; i < kNumInt32Attributes; i++) | |
1165 | - { | |
1166 | - if(StringsEqual(Tag,sAttributeStrings[i])) | |
1167 | - { | |
1168 | - if(!mAttributePresent[i]) { | |
1169 | - if(ReadInt32Value(Value,mAttribute[i])) { | |
1170 | - mAttributePresent[i] = true; | |
1171 | - return true; | |
1172 | - } | |
1173 | - else | |
1174 | - return false; | |
1175 | - } | |
1176 | - else { | |
1177 | - ErrorString = sAttributeMultiplySpecifiedString; | |
1178 | - return false; | |
1179 | - } | |
1180 | - } | |
1181 | - } | |
1182 | - } | |
1183 | - | |
1184 | - UnrecognizedTag(); | |
1185 | - return false; | |
1186 | -} | |
1187 | - | |
1188 | -bool XML_SpokeConfigurationParser::AttributesDone() { | |
1189 | - // Ignore out-of-range values | |
1190 | - for(int i = 0; i < kNumAttributes; i++) | |
1191 | - { | |
1192 | - if(mAttributePresent[i]) | |
1193 | - { | |
1194 | - switch(i) | |
1195 | - { | |
1196 | - case kPregameTicksBeforeNetDeathAttribute: | |
1197 | - case kInGameTicksBeforeNetDeathAttribute: | |
1198 | - case kRecoverySendPeriodAttribute: | |
1199 | - case kTimingWindowSizeAttribute: | |
1200 | - if(mAttribute[i] < 1) | |
1201 | - { | |
1202 | - // I don't know whether this actually does anything if I don't return false, | |
1203 | - // but I'd like to honor the user's wishes as far as I can without just throwing | |
1204 | - // up my hands. | |
1205 | - BadNumericalValue(); | |
1206 | - logWarning3("improper value %d for attribute %s of <spoke>; must be at least 1. using default of %d", mAttribute[i], sAttributeStrings[i], *(sAttributeDestinations[i])); | |
1207 | - mAttributePresent[i] = false; | |
1208 | - } | |
1209 | - else | |
1210 | - { | |
1211 | - *(sAttributeDestinations[i]) = mAttribute[i]; | |
1212 | - } | |
1213 | - break; | |
1214 | - | |
1215 | - case kTimingNthElementAttribute: | |
1216 | - if(mAttribute[i] < 0 || mAttribute[i] >= *(sAttributeDestinations[kTimingWindowSizeAttribute])) | |
1217 | - { | |
1218 | - BadNumericalValue(); | |
1219 | - logWarning4("improper value %d for attribute %s of <spoke>; must be at least 0 but less than %s. using default of %d", mAttribute[i], sAttributeStrings[i], sAttributeStrings[kTimingWindowSizeAttribute], *(sAttributeDestinations[i])); | |
1220 | - mAttributePresent[i] = false; | |
1221 | - } | |
1222 | - else | |
1223 | - { | |
1224 | - *(sAttributeDestinations[i]) = mAttribute[i]; | |
1225 | - } | |
1226 | - break; | |
1227 | - | |
1228 | - case kAdjustTimingAttribute: | |
1229 | - sSpokePreferences.mAdjustTiming = mAdjustTiming; | |
1230 | - break; | |
1231 | - | |
1232 | - default: | |
1233 | - assert(false); | |
1234 | - break; | |
1235 | - } // switch(attribute) | |
1236 | - | |
1237 | - } // if attribute present | |
1238 | - | |
1239 | - } // loop over attributes | |
1240 | - | |
1241 | - // The checks above are not sufficient to catch all bad cases; if user specified a window size | |
1242 | - // smaller than default, this is our only chance to deal with it. | |
1243 | - if(sSpokePreferences.mTimingNthElement >= sSpokePreferences.mTimingWindowSize) | |
1244 | - { | |
1245 | - logWarning5("value for <spoke> attribute %s (%d) must be less than value for %s (%d). using %d", sAttributeStrings[kTimingNthElementAttribute], mAttribute[kTimingNthElementAttribute], sAttributeStrings[kTimingWindowSizeAttribute], mAttribute[kTimingWindowSizeAttribute], sSpokePreferences.mTimingWindowSize - 1); | |
1246 | - | |
1247 | - sSpokePreferences.mTimingNthElement = sSpokePreferences.mTimingWindowSize - 1; | |
1248 | - } | |
1249 | - | |
1250 | - return true; | |
1251 | -} | |
1252 | - | |
1253 | - | |
1254 | -static XML_SpokeConfigurationParser SpokeConfigurationParser; | |
1255 | - | |
1256 | - | |
1257 | -XML_ElementParser* | |
1258 | -Spoke_GetParser() { | |
1259 | - return &SpokeConfigurationParser; | |
1260 | -} | |
1261 | - | |
1262 | - | |
1263 | - | |
1264 | -void | |
1265 | -WriteSpokePreferences(FILE* F) | |
1266 | -{ | |
1267 | - fprintf(F, " <spoke\n"); | |
1268 | - for(size_t i = 0; i < kNumInt32Attributes; i++) | |
1269 | - fprintf(F, " %s=\"%d\"\n", sAttributeStrings[i], *(sAttributeDestinations[i])); | |
1270 | - fprintf(F, " adjust_timing=\"%s\"\n", BoolString(sSpokePreferences.mAdjustTiming)); | |
1271 | - fprintf(F, " />\n"); | |
1272 | -} | |
1273 | - | |
1274 | - | |
1275 | - | |
1276 | -void | |
1277 | -DefaultSpokePreferences() | |
1278 | -{ | |
1279 | - sSpokePreferences.mPregameTicksBeforeNetDeath = kDefaultPregameTicksBeforeNetDeath; | |
1280 | - sSpokePreferences.mInGameTicksBeforeNetDeath = kDefaultInGameTicksBeforeNetDeath; | |
1281 | - // sSpokePreferences.mOutgoingFlagsQueueSize = kDefaultOutgoingFlagsQueueSize; | |
1282 | - sSpokePreferences.mRecoverySendPeriod = kDefaultRecoverySendPeriod; | |
1283 | - sSpokePreferences.mTimingWindowSize = kDefaultTimingWindowSize; | |
1284 | - sSpokePreferences.mTimingNthElement = kDefaultTimingNthElement; | |
1285 | - sSpokePreferences.mAdjustTiming = true; | |
1286 | -} | |
1287 | - | |
1288 | -#endif // !defined(DISABLE_NETWORKING) | |
1 | +/* | |
2 | + * network_star_spoke.cpp | |
3 | + | |
4 | + Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | + * The portion of the star game protocol run on every player's machine. | |
22 | + * | |
23 | + * Created by Woody Zenfell, III on Fri May 02 2003. | |
24 | + * | |
25 | + * May 27, 2003 (Woody Zenfell): lossy byte-stream distribution. | |
26 | + * | |
27 | + * June 30, 2003 (Woody Zenfell): lossy byte-stream distribution more tolerant of scheduling jitter | |
28 | + * (i.e. will queue multiple chunks before send, instead of dropping all data but most recent) | |
29 | + * | |
30 | + * September 17, 2004 (jkvw): | |
31 | + * NAT-friendly networking - we no longer get spoke addresses form topology - | |
32 | + * instead spokes send identification packets to hub with player ID. | |
33 | + * Hub can then associate the ID in the identification packet with the paket's source address. | |
34 | + */ | |
35 | + | |
36 | +#if !defined(DISABLE_NETWORKING) | |
37 | + | |
38 | +#include "network_star.h" | |
39 | +#include "AStream.h" | |
40 | +#include "mytm.h" | |
41 | +#include "network_private.h" // kPROTOCOL_TYPE | |
42 | +#include "WindowedNthElementFinder.h" | |
43 | +#include "vbl.h" // parse_keymap | |
44 | +#include "CircularByteBuffer.h" | |
45 | +#include "Logging.h" | |
46 | +#include "crc.h" | |
47 | +#include "player.h" | |
48 | + | |
49 | +#include <map> | |
50 | + | |
51 | +extern void make_player_really_net_dead(size_t inPlayerIndex); | |
52 | +extern void call_distribution_response_function_if_available(byte* inBuffer, uint16 inBufferSize, int16 inDistributionType, uint8 inSendingPlayerIndex); | |
53 | + | |
54 | + | |
55 | +enum { | |
56 | + kDefaultPregameTicksBeforeNetDeath = 90 * TICKS_PER_SECOND, | |
57 | + kDefaultInGameTicksBeforeNetDeath = 5 * TICKS_PER_SECOND, | |
58 | + kDefaultOutgoingFlagsQueueSize = TICKS_PER_SECOND / 2, | |
59 | + kDefaultRecoverySendPeriod = TICKS_PER_SECOND / 2, | |
60 | + kDefaultTimingWindowSize = 3 * TICKS_PER_SECOND, | |
61 | + kDefaultTimingNthElement = kDefaultTimingWindowSize / 2, | |
62 | + kLossyByteStreamDataBufferSize = 1280, | |
63 | + kTypicalLossyByteStreamChunkSize = 56, | |
64 | + kLossyByteStreamDescriptorCount = kLossyByteStreamDataBufferSize / kTypicalLossyByteStreamChunkSize | |
65 | +}; | |
66 | + | |
67 | +struct SpokePreferences | |
68 | +{ | |
69 | + int32 mPregameTicksBeforeNetDeath; | |
70 | + int32 mInGameTicksBeforeNetDeath; | |
71 | +// int32 mOutgoingFlagsQueueSize; | |
72 | + int32 mRecoverySendPeriod; | |
73 | + int32 mTimingWindowSize; | |
74 | + int32 mTimingNthElement; | |
75 | + bool mAdjustTiming; | |
76 | +}; | |
77 | + | |
78 | +static SpokePreferences sSpokePreferences; | |
79 | + | |
80 | +static TickBasedActionQueue sOutgoingFlags(kDefaultOutgoingFlagsQueueSize); | |
81 | +static TickBasedActionQueue sUnconfirmedFlags(kDefaultOutgoingFlagsQueueSize); | |
82 | +static DuplicatingTickBasedCircularQueue<action_flags_t> sLocallyGeneratedFlags; | |
83 | +static int32 sSmallestRealGameTick; | |
84 | + | |
85 | +struct IncomingGameDataPacketProcessingContext { | |
86 | + bool mMessagesDone; | |
87 | + bool mGotTimingAdjustmentMessage; | |
88 | + | |
89 | + IncomingGameDataPacketProcessingContext() : mMessagesDone(false), mGotTimingAdjustmentMessage(false) {} | |
90 | +}; | |
91 | + | |
92 | +typedef void (*StarMessageHandler)(AIStream& s, IncomingGameDataPacketProcessingContext& c); | |
93 | +typedef std::map<uint16, StarMessageHandler> MessageTypeToMessageHandler; | |
94 | + | |
95 | +static MessageTypeToMessageHandler sMessageTypeToMessageHandler; | |
96 | + | |
97 | +static int8 sRequestedTimingAdjustment; | |
98 | +static int8 sOutstandingTimingAdjustment; | |
99 | + | |
100 | +struct NetworkPlayer_spoke { | |
101 | + bool mZombie; | |
102 | + bool mConnected; | |
103 | + int32 mNetDeadTick; | |
104 | + WritableTickBasedActionQueue* mQueue; | |
105 | +}; | |
106 | + | |
107 | +static vector<NetworkPlayer_spoke> sNetworkPlayers; | |
108 | +static int32 sNetworkTicker; | |
109 | +static int32 sLastNetworkTickHeard; | |
110 | +static int32 sLastNetworkTickSent; | |
111 | +static bool sConnected = false; | |
112 | +static bool sSpokeActive = false; | |
113 | +static myTMTaskPtr sSpokeTickTask = NULL; | |
114 | +static DDPFramePtr sOutgoingFrame = NULL; | |
115 | +static DDPPacketBuffer sLocalOutgoingBuffer; | |
116 | +static bool sNeedToSendLocalOutgoingBuffer = false; | |
117 | +static bool sHubIsLocal = false; | |
118 | +static NetAddrBlock sHubAddress; | |
119 | +static size_t sLocalPlayerIndex; | |
120 | +static int32 sSmallestUnreceivedTick; | |
121 | +static WindowedNthElementFinder<int32> sNthElementFinder(kDefaultTimingWindowSize); | |
122 | +static bool sTimingMeasurementValid; | |
123 | +static int32 sTimingMeasurement; | |
124 | +static bool sHeardFromHub = false; | |
125 | + | |
126 | +static vector<int32> sDisplayLatencyBuffer; // stores the last 30 latency calculations, in ticks | |
127 | +static uint32 sDisplayLatencyCount = 0; | |
128 | +static int32 sDisplayLatencyTicks = 0; // sum of the latency ticks from the last 30 seconds, using above two | |
129 | + | |
130 | +static int32 sSmallestUnconfirmedTick; | |
131 | + | |
132 | +struct SpokeLossyByteStreamChunkDescriptor | |
133 | +{ | |
134 | + uint16 mLength; | |
135 | + int16 mType; | |
136 | + uint32 mDestinations; | |
137 | +}; | |
138 | + | |
139 | +// This holds outgoing lossy byte stream data | |
140 | +static CircularByteBuffer sOutgoingLossyByteStreamData(kLossyByteStreamDataBufferSize); | |
141 | + | |
142 | +// This holds a descriptor for each chunk of lossy byte stream data held in the above buffer | |
143 | +static CircularQueue<SpokeLossyByteStreamChunkDescriptor> sOutgoingLossyByteStreamDescriptors(kLossyByteStreamDescriptorCount); | |
144 | + | |
145 | +// This is currently used only to hold incoming streaming data until it's passed to the upper-level code | |
146 | +static byte sScratchBuffer[kLossyByteStreamDataBufferSize]; | |
147 | + | |
148 | + | |
149 | +static void spoke_became_disconnected(); | |
150 | +static void spoke_received_game_data_packet_v1(AIStream& ps, bool reflected_flags); | |
151 | +static void process_messages(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
152 | +static void handle_end_of_messages_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
153 | +static void handle_player_net_dead_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
154 | +static void handle_timing_adjustment_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
155 | +static void handle_lossy_byte_stream_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context); | |
156 | +static void process_optional_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context, uint16 inMessageType); | |
157 | +static bool spoke_tick(); | |
158 | +static void send_packet(); | |
159 | +static void send_identification_packet(); | |
160 | + | |
161 | + | |
162 | +static inline NetworkPlayer_spoke& | |
163 | +getNetworkPlayer(size_t inIndex) | |
164 | +{ | |
165 | + assert(inIndex < sNetworkPlayers.size()); | |
166 | + return sNetworkPlayers[inIndex]; | |
167 | +} | |
168 | + | |
169 | + | |
170 | + | |
171 | +static inline bool | |
172 | +operator !=(const NetAddrBlock& a, const NetAddrBlock& b) | |
173 | +{ | |
174 | + return memcmp(&a, &b, sizeof(a)) != 0; | |
175 | +} | |
176 | + | |
177 | + | |
178 | + | |
179 | +static OSErr | |
180 | +send_frame_to_local_hub(DDPFramePtr frame, NetAddrBlock *address, short protocolType, short port) | |
181 | +{ | |
182 | + sLocalOutgoingBuffer.datagramSize = frame->data_size; | |
183 | + memcpy(sLocalOutgoingBuffer.datagramData, frame->data, frame->data_size); | |
184 | + sLocalOutgoingBuffer.protocolType = protocolType; | |
185 | + // An all-0 sourceAddress is the cue for "local spoke" currently. | |
186 | + obj_clear(sLocalOutgoingBuffer.sourceAddress); | |
187 | + sNeedToSendLocalOutgoingBuffer = true; | |
188 | + return noErr; | |
189 | +} | |
190 | + | |
191 | + | |
192 | + | |
193 | +static inline void | |
194 | +check_send_packet_to_hub() | |
195 | +{ | |
196 | + if(sNeedToSendLocalOutgoingBuffer) | |
197 | + { | |
198 | + logContextNMT("delivering stored packet to local hub"); | |
199 | + hub_received_network_packet(&sLocalOutgoingBuffer); | |
200 | + } | |
201 | + | |
202 | + sNeedToSendLocalOutgoingBuffer = false; | |
203 | +} | |
204 | + | |
205 | + | |
206 | + | |
207 | +void | |
208 | +spoke_initialize(const NetAddrBlock& inHubAddress, int32 inFirstTick, size_t inNumberOfPlayers, WritableTickBasedActionQueue* const inPlayerQueues[], bool inPlayerConnected[], size_t inLocalPlayerIndex, bool inHubIsLocal) | |
209 | +{ | |
210 | + assert(inNumberOfPlayers >= 1); | |
211 | + assert(inLocalPlayerIndex < inNumberOfPlayers); | |
212 | + assert(inPlayerQueues[inLocalPlayerIndex] != NULL); | |
213 | + assert(inPlayerConnected[inLocalPlayerIndex]); | |
214 | + | |
215 | + sHubIsLocal = inHubIsLocal; | |
216 | + sHubAddress = inHubAddress; | |
217 | + | |
218 | + sLocalPlayerIndex = inLocalPlayerIndex; | |
219 | + | |
220 | + sOutgoingFrame = NetDDPNewFrame(); | |
221 | + | |
222 | + sSmallestRealGameTick = inFirstTick; | |
223 | + int32 theFirstPregameTick = inFirstTick - kPregameTicks; | |
224 | + sOutgoingFlags.reset(theFirstPregameTick); | |
225 | + sUnconfirmedFlags.reset(sSmallestRealGameTick); | |
226 | + sSmallestUnconfirmedTick = sSmallestRealGameTick; | |
227 | + sSmallestUnreceivedTick = theFirstPregameTick; | |
228 | + | |
229 | + sNetworkPlayers.clear(); | |
230 | + sNetworkPlayers.resize(inNumberOfPlayers); | |
231 | + | |
232 | + sLocallyGeneratedFlags.children().clear(); | |
233 | + sLocallyGeneratedFlags.children().insert(&sOutgoingFlags); | |
234 | + sLocallyGeneratedFlags.children().insert(&sUnconfirmedFlags); | |
235 | + | |
236 | + for(size_t i = 0; i < inNumberOfPlayers; i++) | |
237 | + { | |
238 | + sNetworkPlayers[i].mZombie = (inPlayerQueues[i] == NULL); | |
239 | + sNetworkPlayers[i].mConnected = inPlayerConnected[i]; | |
240 | + sNetworkPlayers[i].mNetDeadTick = theFirstPregameTick - 1; | |
241 | + sNetworkPlayers[i].mQueue = inPlayerQueues[i]; | |
242 | + if(sNetworkPlayers[i].mConnected) | |
243 | + { | |
244 | + sNetworkPlayers[i].mQueue->reset(sSmallestRealGameTick); | |
245 | + } | |
246 | + } | |
247 | + | |
248 | + sRequestedTimingAdjustment = 0; | |
249 | + sOutstandingTimingAdjustment = 0; | |
250 | + | |
251 | + sNetworkTicker = 0; | |
252 | + sLastNetworkTickHeard = 0; | |
253 | + sLastNetworkTickSent = 0; | |
254 | + sConnected = true; | |
255 | + sNthElementFinder.reset(sSpokePreferences.mTimingWindowSize); | |
256 | + sTimingMeasurementValid = false; | |
257 | + | |
258 | + sOutgoingLossyByteStreamDescriptors.reset(); | |
259 | + sOutgoingLossyByteStreamData.reset(); | |
260 | + | |
261 | + sMessageTypeToMessageHandler.clear(); | |
262 | + sMessageTypeToMessageHandler[kEndOfMessagesMessageType] = handle_end_of_messages_message; | |
263 | + sMessageTypeToMessageHandler[kTimingAdjustmentMessageType] = handle_timing_adjustment_message; | |
264 | + sMessageTypeToMessageHandler[kPlayerNetDeadMessageType] = handle_player_net_dead_message; | |
265 | + sMessageTypeToMessageHandler[kHubToSpokeLossyByteStreamMessageType] = handle_lossy_byte_stream_message; | |
266 | + | |
267 | + sNeedToSendLocalOutgoingBuffer = false; | |
268 | + | |
269 | + sSpokeActive = true; | |
270 | + sSpokeTickTask = myXTMSetup(1000/TICKS_PER_SECOND, spoke_tick); | |
271 | + | |
272 | + sDisplayLatencyBuffer.resize(TICKS_PER_SECOND, 0); | |
273 | + sDisplayLatencyCount = 0; | |
274 | + sDisplayLatencyTicks = 0; | |
275 | + | |
276 | + sHeardFromHub = false; | |
277 | +} | |
278 | + | |
279 | + | |
280 | + | |
281 | +void | |
282 | +spoke_cleanup(bool inGraceful) | |
283 | +{ | |
284 | + // Stop processing incoming packets (packet processor won't start processing another packet | |
285 | + // due to sSpokeActive = false, and we know it's not in the middle of processing one because | |
286 | + // we take the mutex). | |
287 | + if(take_mytm_mutex()) | |
288 | + { | |
289 | + // Mark the tick task for cancellation (it won't start running again after this returns). | |
290 | + myTMRemove(sSpokeTickTask); | |
291 | + sSpokeTickTask = NULL; | |
292 | + | |
293 | + sSpokeActive = false; | |
294 | + | |
295 | + // We send one last packet here to try to not leave the hub hanging on our ACK. | |
296 | + send_packet(); | |
297 | + check_send_packet_to_hub(); | |
298 | + | |
299 | + release_mytm_mutex(); | |
300 | + } | |
301 | + | |
302 | + // This waits for the tick task to actually finish | |
303 | + myTMCleanup(true); | |
304 | + | |
305 | + sMessageTypeToMessageHandler.clear(); | |
306 | + sNetworkPlayers.clear(); | |
307 | + sLocallyGeneratedFlags.children().clear(); | |
308 | + sDisplayLatencyBuffer.clear(); | |
309 | + NetDDPDisposeFrame(sOutgoingFrame); | |
310 | + sOutgoingFrame = NULL; | |
311 | +} | |
312 | + | |
313 | + | |
314 | + | |
315 | +int32 | |
316 | +spoke_get_net_time() | |
317 | +{ | |
318 | + static int32 sPreviousDelay = -1; | |
319 | + | |
320 | + int32 theDelay = (sSpokePreferences.mAdjustTiming && sTimingMeasurementValid) ? sTimingMeasurement : 0; | |
321 | + | |
322 | + if(theDelay != sPreviousDelay) | |
323 | + { | |
324 | + logDump1("local delay is now %d", theDelay); | |
325 | + sPreviousDelay = theDelay; | |
326 | + } | |
327 | + | |
328 | + return (sConnected ? sOutgoingFlags.getWriteTick() - theDelay : getNetworkPlayer(sLocalPlayerIndex).mQueue->getWriteTick()); | |
329 | +} | |
330 | + | |
331 | + | |
332 | + | |
333 | +void | |
334 | +spoke_distribute_lossy_streaming_bytes_to_everyone(int16 inDistributionType, byte* inBytes, uint16 inLength, bool inExcludeLocalPlayer, bool onlySendToTeam) | |
335 | +{ | |
336 | + | |
337 | + int16 local_team; | |
338 | + if (onlySendToTeam) | |
339 | + { | |
340 | + player_info* player = (player_info *)NetGetPlayerData(sLocalPlayerIndex); | |
341 | + local_team = player->team; | |
342 | + } | |
343 | + | |
344 | + uint32 theDestinations = 0; | |
345 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
346 | + { | |
347 | + if((i != sLocalPlayerIndex || !inExcludeLocalPlayer) && !sNetworkPlayers[i].mZombie && sNetworkPlayers[i].mConnected) | |
348 | + { | |
349 | + if (onlySendToTeam) | |
350 | + { | |
351 | + player_info* player = (player_info *)NetGetPlayerData(i); | |
352 | + if (player->team == local_team) | |
353 | + theDestinations |= (((uint32)1) << i); | |
354 | + | |
355 | + } | |
356 | + else | |
357 | + { | |
358 | + theDestinations |= (((uint32)1) << i); | |
359 | + } | |
360 | + } | |
361 | + } | |
362 | + | |
363 | + spoke_distribute_lossy_streaming_bytes(inDistributionType, theDestinations, inBytes, inLength); | |
364 | +} | |
365 | + | |
366 | + | |
367 | + | |
368 | +void | |
369 | +spoke_distribute_lossy_streaming_bytes(int16 inDistributionType, uint32 inDestinationsBitmask, byte* inBytes, uint16 inLength) | |
370 | +{ | |
371 | + if(inLength > sOutgoingLossyByteStreamData.getRemainingSpace()) | |
372 | + { | |
373 | + logNoteNMT2("spoke has insufficient buffer space for %hu bytes of outgoing lossy streaming type %hd; discarded", inLength, inDistributionType); | |
374 | + return; | |
375 | + } | |
376 | + | |
377 | + if(sOutgoingLossyByteStreamDescriptors.getRemainingSpace() < 1) | |
378 | + { | |
379 | + logNoteNMT2("spoke has exhausted descriptor buffer space; discarding %hu bytes of outgoing lossy streaming type %hd", inLength, inDistributionType); | |
380 | + return; | |
381 | + } | |
382 | + | |
383 | + struct SpokeLossyByteStreamChunkDescriptor theDescriptor; | |
384 | + theDescriptor.mLength = inLength; | |
385 | + theDescriptor.mDestinations = inDestinationsBitmask; | |
386 | + theDescriptor.mType = inDistributionType; | |
387 | + | |
388 | + logDumpNMT3("spoke application decided to send %d bytes of lossy streaming type %d destined for players 0x%x", inLength, inDistributionType, inDestinationsBitmask); | |
389 | + | |
390 | + sOutgoingLossyByteStreamData.enqueueBytes(inBytes, inLength); | |
391 | + sOutgoingLossyByteStreamDescriptors.enqueue(theDescriptor); | |
392 | +} | |
393 | + | |
394 | + | |
395 | + | |
396 | +static void | |
397 | +spoke_became_disconnected() | |
398 | +{ | |
399 | + sConnected = false; | |
400 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
401 | + { | |
402 | + if(sNetworkPlayers[i].mConnected) | |
403 | + make_player_really_net_dead(i); | |
404 | + } | |
405 | +} | |
406 | + | |
407 | + | |
408 | + | |
409 | +void | |
410 | +spoke_received_network_packet(DDPPacketBufferPtr inPacket) | |
411 | +{ | |
412 | + logContextNMT("spoke processing a received packet"); | |
413 | + | |
414 | + // If we've already given up on the connection, ignore packets. | |
415 | + if(!sConnected || !sSpokeActive) | |
416 | + return; | |
417 | + | |
418 | + // Ignore packets not from our hub | |
419 | +// if(inPacket->sourceAddress != sHubAddress) | |
420 | +// return; | |
421 | + | |
422 | + try { | |
423 | + AIStreamBE ps(inPacket->datagramData, inPacket->datagramSize); | |
424 | + | |
425 | + uint16 thePacketMagic; | |
426 | + ps >> thePacketMagic; | |
427 | + | |
428 | + uint16 thePacketCRC; | |
429 | + ps >> thePacketCRC; | |
430 | + | |
431 | + // blank out the CRC field before calculating | |
432 | + inPacket->datagramData[2] = 0; | |
433 | + inPacket->datagramData[3] = 0; | |
434 | + | |
435 | + if (thePacketCRC != calculate_data_crc_ccitt(inPacket->datagramData, inPacket->datagramSize)) | |
436 | + { | |
437 | + logWarningNMT1("CRC failure; discarding packet type %i", thePacketMagic); | |
438 | + return; | |
439 | + } | |
440 | + | |
441 | + switch(thePacketMagic) | |
442 | + { | |
443 | + case kHubToSpokeGameDataPacketV1Magic: | |
444 | + spoke_received_game_data_packet_v1(ps, false); | |
445 | + break; | |
446 | + | |
447 | + case kHubToSpokeGameDataPacketWithSpokeFlagsV1Magic: | |
448 | + spoke_received_game_data_packet_v1(ps, true); | |
449 | + break; | |
450 | + | |
451 | + default: | |
452 | + // Ignore unknown packet types | |
453 | + logTraceNMT1("unknown packet type %i", thePacketMagic); | |
454 | + break; | |
455 | + } | |
456 | + } | |
457 | + catch(...) | |
458 | + { | |
459 | + // do nothing special, we just ignore the remainder of the packet. | |
460 | + } | |
461 | +} | |
462 | + | |
463 | + | |
464 | + | |
465 | +static void | |
466 | +spoke_received_game_data_packet_v1(AIStream& ps, bool reflected_flags) | |
467 | +{ | |
468 | + sHeardFromHub = true; | |
469 | + | |
470 | + IncomingGameDataPacketProcessingContext context; | |
471 | + | |
472 | + // Piggybacked ACK | |
473 | + int32 theSmallestUnacknowledgedTick; | |
474 | + ps >> theSmallestUnacknowledgedTick; | |
475 | + | |
476 | + // we can get an early ACK only if the server made up flags for us... | |
477 | + if (theSmallestUnacknowledgedTick > sOutgoingFlags.getWriteTick()) | |
478 | + { | |
479 | + if (reflected_flags) | |
480 | + { | |
481 | + theSmallestUnacknowledgedTick = sOutgoingFlags.getWriteTick(); | |
482 | + } | |
483 | + else | |
484 | + { | |
485 | + logTraceNMT2("early ack (%d > %d)", theSmallestUnacknowledgedTick, sOutgoingFlags.getWriteTick()); | |
486 | + return; | |
487 | + } | |
488 | + } | |
489 | + | |
490 | + | |
491 | + // Heard from hub | |
492 | + sLastNetworkTickHeard = sNetworkTicker; | |
493 | + | |
494 | + // Remove acknowledged elements from outgoing queue | |
495 | + for(int tick = sOutgoingFlags.getReadTick(); tick < theSmallestUnacknowledgedTick; tick++) | |
496 | + { | |
497 | + logTraceNMT1("dequeueing tick %d from sOutgoingFlags", tick); | |
498 | + sOutgoingFlags.dequeue(); | |
499 | + } | |
500 | + | |
501 | + // Process messages | |
502 | + process_messages(ps, context); | |
503 | + | |
504 | + if(!context.mGotTimingAdjustmentMessage) | |
505 | + { | |
506 | + if(sRequestedTimingAdjustment != 0) | |
507 | + logTraceNMT("timing adjustment no longer requested"); | |
508 | + | |
509 | + sRequestedTimingAdjustment = 0; | |
510 | + } | |
511 | + | |
512 | + // Action_flags!!! | |
513 | + // If there aren't any, we're done | |
514 | + if(ps.tellg() >= ps.maxg()) | |
515 | + { | |
516 | + // If we are the only connected player, well because of the way the loop below works, | |
517 | + // we have to enqueue net_dead flags for the other players now, up to match the most | |
518 | + // recent tick the hub has acknowledged. | |
519 | + // We can't assume we are the only connected player merely by virtue of there being no | |
520 | + // flags in the packet. The hub could be waiting on somebody else to send and is | |
521 | + // essentially sending us "keep-alive" packets (which could contain no new flags) in the meantime. | |
522 | + // In other words, (we are alone) implies (no flags in packet), but not the converse. | |
523 | + // Here "alone" means there are no other players who are connected or who will be netdead | |
524 | + // sometime in the future. (If their NetDeadTick is greater than the ACKed tick, we expect | |
525 | + // that the hub will be sending actual flags in the future to make up the difference.) | |
526 | + bool weAreAlone = true; | |
527 | + int32 theSmallestUnacknowledgedTick = sOutgoingFlags.getReadTick(); | |
528 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
529 | + { | |
530 | + if(i != sLocalPlayerIndex && (sNetworkPlayers[i].mConnected || sNetworkPlayers[i].mNetDeadTick > theSmallestUnacknowledgedTick)) | |
531 | + { | |
532 | + weAreAlone = false; | |
533 | + break; | |
534 | + } | |
535 | + } | |
536 | + | |
537 | + if(weAreAlone) | |
538 | + { | |
539 | + logContextNMT("handling special \"we are alone\" case"); | |
540 | + | |
541 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
542 | + { | |
543 | + NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
544 | + if (i == sLocalPlayerIndex) | |
545 | + { | |
546 | + while (sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()) | |
547 | + { | |
548 | + sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick++)); | |
549 | + } | |
550 | + } | |
551 | + else if (!thePlayer.mZombie) | |
552 | + { | |
553 | + while(thePlayer.mQueue->getWriteTick() < theSmallestUnacknowledgedTick) | |
554 | + { | |
555 | + logDumpNMT2("enqueued NET_DEAD_ACTION_FLAG for player %d tick %d", i, thePlayer.mQueue->getWriteTick()); | |
556 | + thePlayer.mQueue->enqueue(static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG)); | |
557 | + } | |
558 | + } | |
559 | + } | |
560 | + | |
561 | + sSmallestUnreceivedTick = theSmallestUnacknowledgedTick; | |
562 | + logDumpNMT1("sSmallestUnreceivedTick is now %d", sSmallestUnreceivedTick); | |
563 | + } | |
564 | + | |
565 | + return; | |
566 | + } // no data left in packet | |
567 | + | |
568 | + int32 theSmallestUnreadTick; | |
569 | + ps >> theSmallestUnreadTick; | |
570 | + | |
571 | + // Can't accept packets that skip ticks | |
572 | + if(theSmallestUnreadTick > sSmallestUnreceivedTick) | |
573 | + { | |
574 | + logTraceNMT2("early flags (%d > %d)", theSmallestUnreadTick, sSmallestUnreceivedTick); | |
575 | + return; | |
576 | + } | |
577 | + | |
578 | + // Figure out how many ticks of flags we can actually enqueue | |
579 | + // We want to stock all queues evenly, since we ACK everyone's flags for a tick together. | |
580 | + int theSmallestQueueSpace = INT_MAX; | |
581 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
582 | + { | |
583 | + // we'll never get flags for zombies, and we're not expected | |
584 | + // to enqueue flags for zombies | |
585 | + if(sNetworkPlayers[i].mZombie) | |
586 | + continue; | |
587 | + | |
588 | + int theQueueSpace = sNetworkPlayers[i].mQueue->availableCapacity(); | |
589 | + | |
590 | + /* | |
591 | + hmm, taking this exemption out, because we will start enqueueing PLAYER_NET_DEAD_FLAG onto the queue. | |
592 | + // If player is netdead or will become netdead before queue fills, | |
593 | + // player's queue space will not limit us | |
594 | + if(!sNetworkPlayers[i].mConnected) | |
595 | + { | |
596 | + int theRemainingLiveTicks = sNetworkPlayers[i].mNetDeadTick - sSmallestUnreceivedTick; | |
597 | + if(theRemainingLiveTicks < theQueueSpace) | |
598 | + continue; | |
599 | + } | |
600 | + */ | |
601 | + | |
602 | + if(theQueueSpace < theSmallestQueueSpace) | |
603 | + theSmallestQueueSpace = theQueueSpace; | |
604 | + } | |
605 | + | |
606 | + logDumpNMT1("%d queue space available", theSmallestQueueSpace); | |
607 | + | |
608 | + // Read and enqueue the actual action_flags from the packet | |
609 | + // The body of this loop is a bit more convoluted than you might | |
610 | + // expect, because the same loop is used to skip already-seen action_flags | |
611 | + // and to enqueue new ones. | |
612 | + while(ps.tellg() < ps.maxg()) | |
613 | + { | |
614 | + // If we've no room to enqueue stuff, no point in finishing reading the packet. | |
615 | + if(theSmallestQueueSpace <= 0) | |
616 | + break; | |
617 | + | |
618 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
619 | + { | |
620 | + | |
621 | + // We'll never get flags for zombies | |
622 | + if (sNetworkPlayers[i].mZombie) | |
623 | + continue; | |
624 | + | |
625 | + // if our own flags are not sent back to us, | |
626 | + // confirm the ones we have in our unconfirmed queue, | |
627 | + // and do not read any from the packet | |
628 | + if (i == sLocalPlayerIndex && !reflected_flags) | |
629 | + { | |
630 | + if (theSmallestUnreadTick == sSmallestUnreceivedTick && theSmallestUnreadTick >= sSmallestRealGameTick) | |
631 | + { | |
632 | + assert(sNetworkPlayers[i].mQueue->getWriteTick() == sSmallestUnconfirmedTick); | |
633 | + assert(sSmallestUnconfirmedTick >= sUnconfirmedFlags.getReadTick()); | |
634 | + assert(sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()); | |
635 | + // confirm this flag | |
636 | + sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick)); | |
637 | + sSmallestUnconfirmedTick++; | |
638 | + } | |
639 | + | |
640 | + continue; | |
641 | + } | |
642 | + | |
643 | + bool shouldEnqueueNetDeadFlags = false; | |
644 | + | |
645 | + // We won't get flags for netdead players | |
646 | + NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
647 | + if(!thePlayer.mConnected) | |
648 | + { | |
649 | + if(thePlayer.mNetDeadTick < theSmallestUnreadTick) | |
650 | + shouldEnqueueNetDeadFlags = true; | |
651 | + | |
652 | + if(thePlayer.mNetDeadTick == theSmallestUnreadTick) | |
653 | + { | |
654 | + // Only actually act if this tick is new to us | |
655 | + if(theSmallestUnreadTick == sSmallestUnreceivedTick) | |
656 | + make_player_really_net_dead(i); | |
657 | + shouldEnqueueNetDeadFlags = true; | |
658 | + } | |
659 | + } | |
660 | + | |
661 | + // Decide what flags are appropriate for this player this tick | |
662 | + action_flags_t theFlags; | |
663 | + if(shouldEnqueueNetDeadFlags) | |
664 | + // We effectively generate a tick's worth of flags in lieu of reading it from the packet. | |
665 | + theFlags = static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG); | |
666 | + else | |
667 | + { | |
668 | + // We should have a flag for this player for this tick! | |
669 | + try | |
670 | + { | |
671 | + ps >> theFlags; | |
672 | + } | |
673 | + catch (const AStream::failure& f) | |
674 | + { | |
675 | + logWarningNMT3("AStream exception (%s) for player %i at theSmallestUnreadTick %i! OOS is likely!\n", f.what(), i, theSmallestUnreadTick); | |
676 | + return; | |
677 | + } | |
678 | + } | |
679 | + | |
680 | + | |
681 | + // Now, we've gotten flags, probably from the packet... should we enqueue them? | |
682 | + if(theSmallestUnreadTick == sSmallestUnreceivedTick) | |
683 | + { | |
684 | + if(theSmallestUnreadTick >= sSmallestRealGameTick) | |
685 | + { | |
686 | + WritableTickBasedActionQueue& theQueue = *(sNetworkPlayers[i].mQueue); | |
687 | + assert(theQueue.getWriteTick() == sSmallestUnreceivedTick); | |
688 | + assert(theQueue.availableCapacity() > 0); | |
689 | + logTraceNMT3("enqueueing flags %x for player %d tick %d", theFlags, i, theQueue.getWriteTick()); | |
690 | + theQueue.enqueue(theFlags); | |
691 | + if (i == sLocalPlayerIndex) sSmallestUnconfirmedTick++; | |
692 | + } | |
693 | + } | |
694 | + | |
695 | + } // iterate over players | |
696 | + | |
697 | + theSmallestUnreadTick++; | |
698 | + if(sSmallestUnreceivedTick < theSmallestUnreadTick) | |
699 | + { | |
700 | + theSmallestQueueSpace--; | |
701 | + sSmallestUnreceivedTick = theSmallestUnreadTick; | |
702 | + | |
703 | + int32 theLatencyMeasurement = sOutgoingFlags.getWriteTick() - sSmallestUnreceivedTick; | |
704 | + logDumpNMT1("latency measurement: %d", theLatencyMeasurement); | |
705 | + | |
706 | +// We can't use NthElementFinder at interrupt time in Mac OS 9 since its std::set can [de]allocate memory | |
707 | +// We don't really use it for anything on the spoke-side now anyway, thanks to player motion prediction... | |
708 | +#if !(defined(mac) && !defined(__MACH__)) | |
709 | + sNthElementFinder.insert(theLatencyMeasurement); | |
710 | + // We capture these values here so we don't have to take a lock in GetNetTime. | |
711 | + sTimingMeasurementValid = sNthElementFinder.window_full(); | |
712 | + if(sTimingMeasurementValid) | |
713 | + sTimingMeasurement = sNthElementFinder.nth_largest_element(sSpokePreferences.mTimingNthElement); | |
714 | +#endif | |
715 | + | |
716 | + // update the latency display | |
717 | + sDisplayLatencyTicks -= sDisplayLatencyBuffer[sDisplayLatencyCount % sDisplayLatencyBuffer.size()]; | |
718 | + sDisplayLatencyBuffer[sDisplayLatencyCount++ % sDisplayLatencyBuffer.size()] = theLatencyMeasurement; | |
719 | + sDisplayLatencyTicks += theLatencyMeasurement; | |
720 | + } | |
721 | + | |
722 | + } // loop while there's packet data left | |
723 | + | |
724 | +} | |
725 | + | |
726 | + | |
727 | + | |
728 | +static void | |
729 | +process_messages(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
730 | +{ | |
731 | + while(!context.mMessagesDone) | |
732 | + { | |
733 | + uint16 theMessageType; | |
734 | + ps >> theMessageType; | |
735 | + | |
736 | + MessageTypeToMessageHandler::iterator i = sMessageTypeToMessageHandler.find(theMessageType); | |
737 | + | |
738 | + if(i == sMessageTypeToMessageHandler.end()) | |
739 | + process_optional_message(ps, context, theMessageType); | |
740 | + else | |
741 | + i->second(ps, context); | |
742 | + } | |
743 | +} | |
744 | + | |
745 | + | |
746 | + | |
747 | +static void | |
748 | +handle_end_of_messages_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
749 | +{ | |
750 | + context.mMessagesDone = true; | |
751 | +} | |
752 | + | |
753 | + | |
754 | + | |
755 | +static void | |
756 | +handle_player_net_dead_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
757 | +{ | |
758 | + uint8 thePlayerIndex; | |
759 | + int32 theTick; | |
760 | + | |
761 | + ps >> thePlayerIndex >> theTick; | |
762 | + | |
763 | + if(thePlayerIndex > sNetworkPlayers.size()) | |
764 | + return; | |
765 | + | |
766 | + sNetworkPlayers[thePlayerIndex].mConnected = false; | |
767 | + sNetworkPlayers[thePlayerIndex].mNetDeadTick = theTick; | |
768 | + | |
769 | + logDumpNMT2("netDead message: player %d in tick %d", thePlayerIndex, theTick); | |
770 | +} | |
771 | + | |
772 | + | |
773 | + | |
774 | +static void | |
775 | +handle_timing_adjustment_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
776 | +{ | |
777 | + int8 theAdjustment; | |
778 | + | |
779 | + ps >> theAdjustment; | |
780 | + | |
781 | + if(theAdjustment != sRequestedTimingAdjustment) | |
782 | + { | |
783 | + sOutstandingTimingAdjustment = theAdjustment; | |
784 | + sRequestedTimingAdjustment = theAdjustment; | |
785 | + logTraceNMT2("new timing adjustment message; requested: %d outstanding: %d", sRequestedTimingAdjustment, sOutstandingTimingAdjustment); | |
786 | + } | |
787 | + | |
788 | + context.mGotTimingAdjustmentMessage = true; | |
789 | +} | |
790 | + | |
791 | + | |
792 | + | |
793 | +static void | |
794 | +handle_lossy_byte_stream_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context) | |
795 | +{ | |
796 | + uint16 theMessageLength; | |
797 | + ps >> theMessageLength; | |
798 | + | |
799 | + size_t theStartOfMessage = ps.tellg(); | |
800 | + | |
801 | + int16 theDistributionType; | |
802 | + uint8 theSendingPlayer; | |
803 | + ps >> theDistributionType >> theSendingPlayer; | |
804 | + | |
805 | + uint16 theDataLength = theMessageLength - (ps.tellg() - theStartOfMessage); | |
806 | + uint16 theSpilloverDataLength = 0; | |
807 | + if(theDataLength > sizeof(sScratchBuffer)) | |
808 | + { | |
809 | + logNoteNMT3("received too many bytes (%d) of lossy streaming data type %d from player %d; truncating", theDataLength, theDistributionType, theSendingPlayer); | |
810 | + theSpilloverDataLength = theDataLength - sizeof(sScratchBuffer); | |
811 | + theDataLength = sizeof(sScratchBuffer); | |
812 | + } | |
813 | + ps.read(sScratchBuffer, theDataLength); | |
814 | + ps.ignore(theSpilloverDataLength); | |
815 | + | |
816 | + logDumpNMT3("received %d bytes of lossy streaming type %d data from player %d", theDataLength, theDistributionType, theSendingPlayer); | |
817 | + | |
818 | + call_distribution_response_function_if_available(sScratchBuffer, theDataLength, theDistributionType, theSendingPlayer); | |
819 | +} | |
820 | + | |
821 | + | |
822 | + | |
823 | +static void | |
824 | +process_optional_message(AIStream& ps, IncomingGameDataPacketProcessingContext& context, uint16 inMessageType) | |
825 | +{ | |
826 | + // We don't know of any optional messages, so we just skip any we encounter. | |
827 | + // (All optional messages are required to encode their length (not including the | |
828 | + // space required for the message type or length) in the two bytes immediately | |
829 | + // following the message type.) | |
830 | + uint16 theLength; | |
831 | + ps >> theLength; | |
832 | + | |
833 | + ps.ignore(theLength); | |
834 | +} | |
835 | + | |
836 | + | |
837 | + | |
838 | +static bool | |
839 | +spoke_tick() | |
840 | +{ | |
841 | + logContextNMT1("processing spoke_tick %d", sNetworkTicker); | |
842 | + | |
843 | + sNetworkTicker++; | |
844 | + | |
845 | + if(sConnected) | |
846 | + { | |
847 | + int32 theSilentTicksBeforeNetDeath = (sOutgoingFlags.getReadTick() >= sSmallestRealGameTick) ? sSpokePreferences.mInGameTicksBeforeNetDeath : sSpokePreferences.mPregameTicksBeforeNetDeath; | |
848 | + | |
849 | + if(sNetworkTicker - sLastNetworkTickHeard > theSilentTicksBeforeNetDeath) | |
850 | + { | |
851 | + logTraceNMT("giving up on hub; disconnecting"); | |
852 | + spoke_became_disconnected(); | |
853 | + return true; | |
854 | + } | |
855 | + } | |
856 | + | |
857 | + bool shouldSend = false; | |
858 | + | |
859 | + // Negative timing adjustment means we need to provide extra ticks because we're late. | |
860 | + // We let this cover the normal timing adjustment = 0 case too. | |
861 | + if(sOutstandingTimingAdjustment <= 0) | |
862 | + { | |
863 | + int theNumberOfFlagsToProvide = -sOutstandingTimingAdjustment + 1; | |
864 | + | |
865 | + logDumpNMT1("want to provide %d flags", theNumberOfFlagsToProvide); | |
866 | + | |
867 | + while(theNumberOfFlagsToProvide > 0) | |
868 | + { | |
869 | + | |
870 | + // If we're not connected, write only to our local game's queue. | |
871 | + // If we are connected, | |
872 | + // if we're actually in the game, write to both local game and outbound flags queues | |
873 | + // else (if pregame), write only to the outbound flags queue. | |
874 | + | |
875 | + WritableTickBasedActionQueue& theTargetQueue = | |
876 | + sConnected ? | |
877 | + ((sOutgoingFlags.getWriteTick() >= sSmallestRealGameTick) ? | |
878 | + static_cast<WritableTickBasedActionQueue&>(sLocallyGeneratedFlags) | |
879 | + : static_cast<WritableTickBasedActionQueue&>(sOutgoingFlags)) | |
880 | + : *(sNetworkPlayers[sLocalPlayerIndex].mQueue); | |
881 | + | |
882 | + if(theTargetQueue.availableCapacity() <= 0) | |
883 | + break; | |
884 | + | |
885 | + logDumpNMT1("enqueueing flags for tick %d", theTargetQueue.getWriteTick()); | |
886 | + | |
887 | + theTargetQueue.enqueue(parse_keymap()); | |
888 | + shouldSend = true; | |
889 | + theNumberOfFlagsToProvide--; | |
890 | + } | |
891 | + | |
892 | + // Prevent creeping timing adjustment during "lulls"; OTOH remember to | |
893 | + // finish next time if we made progress but couldn't complete our obligation. | |
894 | + if(theNumberOfFlagsToProvide != -sOutstandingTimingAdjustment + 1) | |
895 | + sOutstandingTimingAdjustment = -theNumberOfFlagsToProvide; | |
896 | + } | |
897 | + // Positive timing adjustment means we should delay sending for a while, | |
898 | + // so we just throw away this local tick. | |
899 | + else | |
900 | + { | |
901 | + logDumpNMT("ignoring this tick for timing adjustment"); | |
902 | + sOutstandingTimingAdjustment--; | |
903 | + } | |
904 | + | |
905 | + logDumpNMT1("sOutstandingTimingAdjustment is now %d", sOutstandingTimingAdjustment); | |
906 | + | |
907 | + if(sOutgoingLossyByteStreamDescriptors.getCountOfElements() > 0) | |
908 | + shouldSend = true; | |
909 | + | |
910 | + // If we're connected and (we generated new data or if it's been long enough since we last sent), send. | |
911 | + if(sConnected) | |
912 | + { | |
913 | + if (sHeardFromHub) { | |
914 | + if(shouldSend || (sNetworkTicker - sLastNetworkTickSent) >= sSpokePreferences.mRecoverySendPeriod) | |
915 | + send_packet(); | |
916 | + } else { | |
917 | + if (!(sNetworkTicker % 30)) | |
918 | + send_identification_packet(); | |
919 | + } | |
920 | + } | |
921 | + else | |
922 | + { | |
923 | + int32 theLocalPlayerWriteTick = getNetworkPlayer(sLocalPlayerIndex).mQueue->getWriteTick(); | |
924 | + | |
925 | + // Since we're not connected, we won't be enqueueing flags for the other players in the packet handler. | |
926 | + // So, we do it here to keep the game moving. | |
927 | + for(size_t i = 0; i < sNetworkPlayers.size(); i++) | |
928 | + { | |
929 | + if(i == sLocalPlayerIndex) | |
930 | + { | |
931 | + // move our flags from sent queue to player queue | |
932 | + while (sSmallestUnconfirmedTick < sUnconfirmedFlags.getWriteTick()) | |
933 | + { | |
934 | + sNetworkPlayers[i].mQueue->enqueue(sUnconfirmedFlags.peek(sSmallestUnconfirmedTick++)); | |
935 | + } | |
936 | + continue; | |
937 | + } | |
938 | + | |
939 | + NetworkPlayer_spoke& thePlayer = sNetworkPlayers[i]; | |
940 | + | |
941 | + if(!thePlayer.mZombie) | |
942 | + { | |
943 | + while(thePlayer.mQueue->getWriteTick() < theLocalPlayerWriteTick) | |
944 | + { | |
945 | + logDumpNMT2("enqueueing NET_DEAD_ACTION_FLAG for player %d tick %d", i, thePlayer.mQueue->getWriteTick()); | |
946 | + thePlayer.mQueue->enqueue(static_cast<action_flags_t>(NET_DEAD_ACTION_FLAG)); | |
947 | + } | |
948 | + } | |
949 | + } | |
950 | + } | |
951 | + | |
952 | + check_send_packet_to_hub(); | |
953 | + | |
954 | + // We want to run again. | |
955 | + return true; | |
956 | +} | |
957 | + | |
958 | + | |
959 | + | |
960 | +static void | |
961 | +send_packet() | |
962 | +{ | |
963 | + try { | |
964 | + AOStreamBE hdr(sOutgoingFrame->data, kStarPacketHeaderSize); | |
965 | + AOStreamBE ps(sOutgoingFrame->data, ddpMaxData, kStarPacketHeaderSize); | |
966 | + | |
967 | + // Packet type | |
968 | + hdr << (uint16)kSpokeToHubGameDataPacketV1Magic; | |
969 | + | |
970 | + // Acknowledgement | |
971 | + ps << sSmallestUnreceivedTick; | |
972 | + | |
973 | + // Messages | |
974 | + // Outstanding lossy streaming bytes? | |
975 | + if(sOutgoingLossyByteStreamDescriptors.getCountOfElements() > 0) | |
976 | + { | |
977 | + // Note: we make a conscious decision here to dequeue these things before | |
978 | + // writing to ps, so that if the latter operation exhausts ps's buffer and | |
979 | + // throws, we have less data to mess with next time, and shouldn't end up | |
980 | + // throwing every time we try to send here. | |
981 | + // If we eventually got smarter about managing packet space, we could try | |
982 | + // harder to preserve and pace data - e.g. change the 'if' immediately before this | |
983 | + // comment to a 'while', only put in as much data as we think we can fit, etc. | |
984 | + SpokeLossyByteStreamChunkDescriptor theDescriptor = sOutgoingLossyByteStreamDescriptors.peek(); | |
985 | + sOutgoingLossyByteStreamDescriptors.dequeue(); | |
986 | + | |
987 | + uint16 theMessageLength = theDescriptor.mLength + sizeof(theDescriptor.mType) + sizeof(theDescriptor.mDestinations); | |
988 | + | |
989 | + ps << (uint16)kSpokeToHubLossyByteStreamMessageType | |
990 | + << theMessageLength | |
991 | + << theDescriptor.mType | |
992 | + << theDescriptor.mDestinations; | |
993 | + | |
994 | + // XXX unnecessary copy due to overly restrictive interfaces (retaining for clarity) | |
995 | + assert(theDescriptor.mLength <= sizeof(sScratchBuffer)); | |
996 | + sOutgoingLossyByteStreamData.peekBytes(sScratchBuffer, theDescriptor.mLength); | |
997 | + sOutgoingLossyByteStreamData.dequeue(theDescriptor.mLength); | |
998 | + | |
999 | + ps.write(sScratchBuffer, theDescriptor.mLength); | |
1000 | + } | |
1001 | + | |
1002 | + // No more messages | |
1003 | + ps << (uint16)kEndOfMessagesMessageType; | |
1004 | + | |
1005 | + // Action_flags!!! | |
1006 | + if(sOutgoingFlags.size() > 0) | |
1007 | + { | |
1008 | + ps << sOutgoingFlags.getReadTick(); | |
1009 | + for(int32 tick = sOutgoingFlags.getReadTick(); tick < sOutgoingFlags.getWriteTick(); tick++) | |
1010 | + ps << sOutgoingFlags.peek(tick); | |
1011 | + } | |
1012 | + | |
1013 | + logDumpNMT3("preparing to send packet: ACK %d, flags [%d,%d)", sSmallestUnreceivedTick, sOutgoingFlags.getReadTick(), sOutgoingFlags.getWriteTick()); | |
1014 | + | |
1015 | + // blank out the CRC before calculating it | |
1016 | + sOutgoingFrame->data[2] = 0; | |
1017 | + sOutgoingFrame->data[3] = 0; | |
1018 | + | |
1019 | + uint16 crc = calculate_data_crc_ccitt(sOutgoingFrame->data, ps.tellp()); | |
1020 | + hdr << crc; | |
1021 | + | |
1022 | + // Send the packet | |
1023 | + sOutgoingFrame->data_size = ps.tellp(); | |
1024 | + | |
1025 | + if(sHubIsLocal) | |
1026 | + send_frame_to_local_hub(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1027 | + else | |
1028 | + NetDDPSendFrame(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1029 | + | |
1030 | + sLastNetworkTickSent = sNetworkTicker; | |
1031 | + } | |
1032 | + catch (...) { | |
1033 | + } | |
1034 | +} | |
1035 | + | |
1036 | + | |
1037 | + | |
1038 | +static void | |
1039 | +send_identification_packet() | |
1040 | +{ | |
1041 | + try { | |
1042 | + AOStreamBE hdr(sOutgoingFrame->data, kStarPacketHeaderSize); | |
1043 | + AOStreamBE ps(sOutgoingFrame->data, ddpMaxData, kStarPacketHeaderSize); | |
1044 | + | |
1045 | + // Message type | |
1046 | + hdr << (uint16) kSpokeToHubIdentification; | |
1047 | + | |
1048 | + // ID | |
1049 | + ps << (uint16)sLocalPlayerIndex; | |
1050 | + | |
1051 | + // blank out the CRC field before calculating | |
1052 | + sOutgoingFrame->data[2] = 0; | |
1053 | + sOutgoingFrame->data[3] = 0; | |
1054 | + | |
1055 | + uint16 crc = calculate_data_crc_ccitt(sOutgoingFrame->data, ps.tellp()); | |
1056 | + hdr << crc; | |
1057 | + | |
1058 | + // Send the packet | |
1059 | + sOutgoingFrame->data_size = ps.tellp(); | |
1060 | + if(sHubIsLocal) | |
1061 | + send_frame_to_local_hub(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1062 | + else | |
1063 | + NetDDPSendFrame(sOutgoingFrame, &sHubAddress, kPROTOCOL_TYPE, 0 /* ignored */); | |
1064 | + } | |
1065 | + catch (...) { | |
1066 | + } | |
1067 | +} | |
1068 | + | |
1069 | +int32 spoke_latency() | |
1070 | +{ | |
1071 | + return (sDisplayLatencyCount >= TICKS_PER_SECOND) ? sDisplayLatencyTicks * 1000 / TICKS_PER_SECOND / sDisplayLatencyBuffer.size() : NetworkStats::invalid; | |
1072 | +} | |
1073 | + | |
1074 | +TickBasedActionQueue* spoke_get_unconfirmed_flags_queue() | |
1075 | +{ | |
1076 | + return &sUnconfirmedFlags; | |
1077 | +} | |
1078 | + | |
1079 | +int32 spoke_get_smallest_unconfirmed_tick() | |
1080 | +{ | |
1081 | + return sSmallestUnconfirmedTick; | |
1082 | +} | |
1083 | + | |
1084 | + | |
1085 | +static inline const char *BoolString(bool B) {return (B ? "true" : "false");} | |
1086 | + | |
1087 | +enum { | |
1088 | + kPregameTicksBeforeNetDeathAttribute, | |
1089 | + kInGameTicksBeforeNetDeathAttribute, | |
1090 | + // kOutgoingFlagsQueueSizeAttribute, | |
1091 | + kRecoverySendPeriodAttribute, | |
1092 | + kTimingWindowSizeAttribute, | |
1093 | + kTimingNthElementAttribute, | |
1094 | + kNumInt32Attributes, | |
1095 | + kAdjustTimingAttribute = kNumInt32Attributes, | |
1096 | + kNumAttributes | |
1097 | +}; | |
1098 | + | |
1099 | +static const char* sAttributeStrings[kNumInt32Attributes] = | |
1100 | +{ | |
1101 | + "pregame_ticks_before_net_death", | |
1102 | + "ingame_ticks_before_net_death", | |
1103 | +// "outgoing_flags_queue_size", | |
1104 | + "recovery_send_period", | |
1105 | + "timing_window_size", | |
1106 | + "timing_nth_element" | |
1107 | +}; | |
1108 | + | |
1109 | +static int32* sAttributeDestinations[kNumInt32Attributes] = | |
1110 | +{ | |
1111 | + &sSpokePreferences.mPregameTicksBeforeNetDeath, | |
1112 | + &sSpokePreferences.mInGameTicksBeforeNetDeath, | |
1113 | +// &sSpokePreferences.mOutgoingFlagsQueueSize, | |
1114 | + &sSpokePreferences.mRecoverySendPeriod, | |
1115 | + &sSpokePreferences.mTimingWindowSize, | |
1116 | + &sSpokePreferences.mTimingNthElement | |
1117 | +}; | |
1118 | + | |
1119 | +class XML_SpokeConfigurationParser: public XML_ElementParser | |
1120 | +{ | |
1121 | +public: | |
1122 | + bool Start(); | |
1123 | + bool HandleAttribute(const char *Tag, const char *Value); | |
1124 | + bool AttributesDone(); | |
1125 | + | |
1126 | + XML_SpokeConfigurationParser(): XML_ElementParser("spoke") {} | |
1127 | + | |
1128 | +protected: | |
1129 | + bool mAttributePresent[kNumAttributes]; | |
1130 | + int32 mAttribute[kNumInt32Attributes]; | |
1131 | + bool mAdjustTiming; | |
1132 | +}; | |
1133 | + | |
1134 | +bool XML_SpokeConfigurationParser::Start() | |
1135 | +{ | |
1136 | + for(int i = 0; i < kNumAttributes; i++) | |
1137 | + mAttributePresent[i] = false; | |
1138 | + | |
1139 | + return true; | |
1140 | +} | |
1141 | + | |
1142 | +static const char* sAttributeMultiplySpecifiedString = "attribute multiply specified"; | |
1143 | + | |
1144 | +bool XML_SpokeConfigurationParser::HandleAttribute(const char *Tag, const char *Value) | |
1145 | +{ | |
1146 | + if (StringsEqual(Tag,"adjust_timing")) | |
1147 | + { | |
1148 | + if(!mAttributePresent[kAdjustTimingAttribute]) { | |
1149 | + if(ReadBooleanValueAsBool(Value,mAdjustTiming)) { | |
1150 | + mAttributePresent[kAdjustTimingAttribute] = true; | |
1151 | + return true; | |
1152 | + } | |
1153 | + else | |
1154 | + return false; | |
1155 | + } | |
1156 | + else { | |
1157 | + ErrorString = sAttributeMultiplySpecifiedString; | |
1158 | + return false; | |
1159 | + } | |
1160 | + } | |
1161 | + | |
1162 | + else | |
1163 | + { | |
1164 | + for(size_t i = 0; i < kNumInt32Attributes; i++) | |
1165 | + { | |
1166 | + if(StringsEqual(Tag,sAttributeStrings[i])) | |
1167 | + { | |
1168 | + if(!mAttributePresent[i]) { | |
1169 | + if(ReadInt32Value(Value,mAttribute[i])) { | |
1170 | + mAttributePresent[i] = true; | |
1171 | + return true; | |
1172 | + } | |
1173 | + else | |
1174 | + return false; | |
1175 | + } | |
1176 | + else { | |
1177 | + ErrorString = sAttributeMultiplySpecifiedString; | |
1178 | + return false; | |
1179 | + } | |
1180 | + } | |
1181 | + } | |
1182 | + } | |
1183 | + | |
1184 | + UnrecognizedTag(); | |
1185 | + return false; | |
1186 | +} | |
1187 | + | |
1188 | +bool XML_SpokeConfigurationParser::AttributesDone() { | |
1189 | + // Ignore out-of-range values | |
1190 | + for(int i = 0; i < kNumAttributes; i++) | |
1191 | + { | |
1192 | + if(mAttributePresent[i]) | |
1193 | + { | |
1194 | + switch(i) | |
1195 | + { | |
1196 | + case kPregameTicksBeforeNetDeathAttribute: | |
1197 | + case kInGameTicksBeforeNetDeathAttribute: | |
1198 | + case kRecoverySendPeriodAttribute: | |
1199 | + case kTimingWindowSizeAttribute: | |
1200 | + if(mAttribute[i] < 1) | |
1201 | + { | |
1202 | + // I don't know whether this actually does anything if I don't return false, | |
1203 | + // but I'd like to honor the user's wishes as far as I can without just throwing | |
1204 | + // up my hands. | |
1205 | + BadNumericalValue(); | |
1206 | + logWarning3("improper value %d for attribute %s of <spoke>; must be at least 1. using default of %d", mAttribute[i], sAttributeStrings[i], *(sAttributeDestinations[i])); | |
1207 | + mAttributePresent[i] = false; | |
1208 | + } | |
1209 | + else | |
1210 | + { | |
1211 | + *(sAttributeDestinations[i]) = mAttribute[i]; | |
1212 | + } | |
1213 | + break; | |
1214 | + | |
1215 | + case kTimingNthElementAttribute: | |
1216 | + if(mAttribute[i] < 0 || mAttribute[i] >= *(sAttributeDestinations[kTimingWindowSizeAttribute])) | |
1217 | + { | |
1218 | + BadNumericalValue(); | |
1219 | + logWarning4("improper value %d for attribute %s of <spoke>; must be at least 0 but less than %s. using default of %d", mAttribute[i], sAttributeStrings[i], sAttributeStrings[kTimingWindowSizeAttribute], *(sAttributeDestinations[i])); | |
1220 | + mAttributePresent[i] = false; | |
1221 | + } | |
1222 | + else | |
1223 | + { | |
1224 | + *(sAttributeDestinations[i]) = mAttribute[i]; | |
1225 | + } | |
1226 | + break; | |
1227 | + | |
1228 | + case kAdjustTimingAttribute: | |
1229 | + sSpokePreferences.mAdjustTiming = mAdjustTiming; | |
1230 | + break; | |
1231 | + | |
1232 | + default: | |
1233 | + assert(false); | |
1234 | + break; | |
1235 | + } // switch(attribute) | |
1236 | + | |
1237 | + } // if attribute present | |
1238 | + | |
1239 | + } // loop over attributes | |
1240 | + | |
1241 | + // The checks above are not sufficient to catch all bad cases; if user specified a window size | |
1242 | + // smaller than default, this is our only chance to deal with it. | |
1243 | + if(sSpokePreferences.mTimingNthElement >= sSpokePreferences.mTimingWindowSize) | |
1244 | + { | |
1245 | + logWarning5("value for <spoke> attribute %s (%d) must be less than value for %s (%d). using %d", sAttributeStrings[kTimingNthElementAttribute], mAttribute[kTimingNthElementAttribute], sAttributeStrings[kTimingWindowSizeAttribute], mAttribute[kTimingWindowSizeAttribute], sSpokePreferences.mTimingWindowSize - 1); | |
1246 | + | |
1247 | + sSpokePreferences.mTimingNthElement = sSpokePreferences.mTimingWindowSize - 1; | |
1248 | + } | |
1249 | + | |
1250 | + return true; | |
1251 | +} | |
1252 | + | |
1253 | + | |
1254 | +static XML_SpokeConfigurationParser SpokeConfigurationParser; | |
1255 | + | |
1256 | + | |
1257 | +XML_ElementParser* | |
1258 | +Spoke_GetParser() { | |
1259 | + return &SpokeConfigurationParser; | |
1260 | +} | |
1261 | + | |
1262 | + | |
1263 | + | |
1264 | +void | |
1265 | +WriteSpokePreferences(FILE* F) | |
1266 | +{ | |
1267 | + fprintf(F, " <spoke\n"); | |
1268 | + for(size_t i = 0; i < kNumInt32Attributes; i++) | |
1269 | + fprintf(F, " %s=\"%d\"\n", sAttributeStrings[i], *(sAttributeDestinations[i])); | |
1270 | + fprintf(F, " adjust_timing=\"%s\"\n", BoolString(sSpokePreferences.mAdjustTiming)); | |
1271 | + fprintf(F, " />\n"); | |
1272 | +} | |
1273 | + | |
1274 | + | |
1275 | + | |
1276 | +void | |
1277 | +DefaultSpokePreferences() | |
1278 | +{ | |
1279 | + sSpokePreferences.mPregameTicksBeforeNetDeath = kDefaultPregameTicksBeforeNetDeath; | |
1280 | + sSpokePreferences.mInGameTicksBeforeNetDeath = kDefaultInGameTicksBeforeNetDeath; | |
1281 | + // sSpokePreferences.mOutgoingFlagsQueueSize = kDefaultOutgoingFlagsQueueSize; | |
1282 | + sSpokePreferences.mRecoverySendPeriod = kDefaultRecoverySendPeriod; | |
1283 | + sSpokePreferences.mTimingWindowSize = kDefaultTimingWindowSize; | |
1284 | + sSpokePreferences.mTimingNthElement = kDefaultTimingNthElement; | |
1285 | + sSpokePreferences.mAdjustTiming = true; | |
1286 | +} | |
1287 | + | |
1288 | +#endif // !defined(DISABLE_NETWORKING) |
@@ -1,480 +1,480 @@ | ||
1 | -/* | |
2 | - * network_messages.h - TCPMess message types for network game setup | |
3 | - | |
4 | - Copyright (C) 2005 and beyond by Gregory Smith | |
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 | - */ | |
22 | - | |
23 | -#ifndef NETWORK_MESSAGES_H | |
24 | -#define NETWORK_MESSAGES_H | |
25 | - | |
26 | -#include "cseries.h" | |
27 | -#include "AStream.h" | |
28 | -#include "Message.h" | |
29 | - | |
30 | -#include "SDL_net.h" | |
31 | - | |
32 | -#include "network_capabilities.h" | |
33 | -#include "network_private.h" | |
34 | - | |
35 | -enum { | |
36 | - kHELLO_MESSAGE = 700, | |
37 | - kJOINER_INFO_MESSAGE, | |
38 | - kJOIN_PLAYER_MESSAGE, | |
39 | - kCAPABILITIES_MESSAGE, | |
40 | - kACCEPT_JOIN_MESSAGE, | |
41 | - kTOPOLOGY_MESSAGE, | |
42 | - kMAP_MESSAGE, | |
43 | - kPHYSICS_MESSAGE, | |
44 | - kLUA_MESSAGE, | |
45 | - kCHAT_MESSAGE, | |
46 | - kUNKNOWN_MESSAGE_MESSAGE, | |
47 | - kEND_GAME_DATA_MESSAGE, | |
48 | - kCHANGE_COLORS_MESSAGE, | |
49 | - kSERVER_WARNING_MESSAGE, | |
50 | - kCLIENT_INFO_MESSAGE, | |
51 | - kZIPPED_MAP_MESSAGE, | |
52 | - kZIPPED_PHYSICS_MESSAGE, | |
53 | - kZIPPED_LUA_MESSAGE, | |
54 | - kNETWORK_STATS_MESSAGE | |
55 | -}; | |
56 | - | |
57 | -template <MessageTypeID tMessageType, typename tValueType> | |
58 | -class TemplatizedSimpleMessage : public SimpleMessage<tValueType> | |
59 | -{ | |
60 | - public: | |
61 | - enum { kType = tMessageType }; | |
62 | - | |
63 | - TemplatizedSimpleMessage() : SimpleMessage<tValueType>(tMessageType) { } | |
64 | - | |
65 | - TemplatizedSimpleMessage(tValueType inValue) : SimpleMessage<tValueType>(tMessageType, inValue) { } | |
66 | - | |
67 | - TemplatizedSimpleMessage<tMessageType, tValueType>* clone () const { | |
68 | - return new TemplatizedSimpleMessage<tMessageType, tValueType>(*this); | |
69 | - } | |
70 | - | |
71 | - MessageTypeID type() const { return kType; } | |
72 | -}; | |
73 | - | |
74 | -class AcceptJoinMessage : public SmallMessageHelper | |
75 | -{ | |
76 | - public: | |
77 | - enum { kType = kACCEPT_JOIN_MESSAGE }; | |
78 | - | |
79 | - AcceptJoinMessage() : SmallMessageHelper() { } | |
80 | - | |
81 | - AcceptJoinMessage(bool accepted, NetPlayer *player) : SmallMessageHelper() { | |
82 | - mAccepted = accepted; | |
83 | - mPlayer = *player; | |
84 | - } | |
85 | - | |
86 | - AcceptJoinMessage *clone() const { | |
87 | - return new AcceptJoinMessage(*this); | |
88 | - } | |
89 | - | |
90 | - bool accepted() { return mAccepted; } | |
91 | - void accepted(bool isAccepted) { mAccepted = isAccepted; } | |
92 | - NetPlayer *player() { return &mPlayer; } | |
93 | - void player(const NetPlayer *thePlayer) { mPlayer = *thePlayer; } | |
94 | - | |
95 | - MessageTypeID type() const { return kType; } | |
96 | - | |
97 | - protected: | |
98 | - void reallyDeflateTo(AOStream& outputStream) const; | |
99 | - bool reallyInflateFrom(AIStream& inputStream); | |
100 | - | |
101 | - private: | |
102 | - | |
103 | - bool mAccepted; | |
104 | - NetPlayer mPlayer; | |
105 | -}; | |
106 | - | |
107 | -class CapabilitiesMessage : public SmallMessageHelper | |
108 | -{ | |
109 | - public: | |
110 | - enum { kType = kCAPABILITIES_MESSAGE }; | |
111 | - | |
112 | - CapabilitiesMessage() : SmallMessageHelper() { } | |
113 | - | |
114 | - CapabilitiesMessage(const Capabilities &capabilities) : SmallMessageHelper() { | |
115 | - mCapabilities = capabilities; | |
116 | - } | |
117 | - | |
118 | - CapabilitiesMessage *clone() const { | |
119 | - return new CapabilitiesMessage(*this); | |
120 | - } | |
121 | - | |
122 | - const Capabilities *capabilities() { return &mCapabilities; } | |
123 | - | |
124 | - MessageTypeID type() const { return kType; } | |
125 | - | |
126 | -protected: | |
127 | - void reallyDeflateTo(AOStream& outputStream) const; | |
128 | - bool reallyInflateFrom(AIStream& inputStream); | |
129 | - | |
130 | -private: | |
131 | - Capabilities mCapabilities; | |
132 | -}; | |
133 | - | |
134 | -class ChangeColorsMessage : public SmallMessageHelper | |
135 | -{ | |
136 | - public: | |
137 | - enum { kType = kCHANGE_COLORS_MESSAGE }; | |
138 | - | |
139 | - ChangeColorsMessage() : SmallMessageHelper() { } | |
140 | - | |
141 | - ChangeColorsMessage(int16 color, int16 team) : SmallMessageHelper() { | |
142 | - mColor = color; | |
143 | - mTeam = team; | |
144 | - } | |
145 | - | |
146 | - ChangeColorsMessage *clone() const { | |
147 | - return new ChangeColorsMessage(*this); | |
148 | - } | |
149 | - | |
150 | - int16 color() { return mColor; } | |
151 | - int16 team() { return mTeam; } | |
152 | - | |
153 | - MessageTypeID type() const { return kType; } | |
154 | - | |
155 | - protected: | |
156 | - void reallyDeflateTo(AOStream& outputStream) const; | |
157 | - bool reallyInflateFrom(AIStream& inputStream); | |
158 | - | |
159 | - private: | |
160 | - | |
161 | - int16 mColor; | |
162 | - int16 mTeam; | |
163 | -}; | |
164 | - | |
165 | -class ClientInfoMessage : public SmallMessageHelper | |
166 | -{ | |
167 | -public: | |
168 | - enum { kType = kCLIENT_INFO_MESSAGE }; | |
169 | - enum { kAdd, kUpdate, kRemove }; | |
170 | - | |
171 | - ClientInfoMessage() : SmallMessageHelper() { } | |
172 | - | |
173 | - ClientInfoMessage(int16 stream_id, const ClientChatInfo *clientChatInfo, int16 action ) : mStreamID(stream_id), mAction(action), mClientChatInfo(*clientChatInfo) { } | |
174 | - | |
175 | - ClientInfoMessage *clone() const { | |
176 | - return new ClientInfoMessage(*this); | |
177 | - } | |
178 | - | |
179 | - const ClientChatInfo *info() { return &mClientChatInfo; } | |
180 | - const int16 action() { return mAction; } | |
181 | - const int16 stream_id() { return mStreamID; } | |
182 | - | |
183 | - MessageTypeID type() const { return kType; } | |
184 | - | |
185 | -protected: | |
186 | - void reallyDeflateTo(AOStream& outputStream) const; | |
187 | - bool reallyInflateFrom(AIStream& inputStream); | |
188 | - | |
189 | -private: | |
190 | - ClientChatInfo mClientChatInfo; | |
191 | - int16 mAction; | |
192 | - int16 mStreamID; | |
193 | -}; | |
194 | - | |
195 | - | |
196 | -typedef DatalessMessage<kEND_GAME_DATA_MESSAGE> EndGameDataMessage; | |
197 | - | |
198 | -class HelloMessage : public SmallMessageHelper | |
199 | -{ | |
200 | -public: | |
201 | - enum { kType = kHELLO_MESSAGE }; | |
202 | - | |
203 | - HelloMessage() : SmallMessageHelper() { } | |
204 | - | |
205 | - HelloMessage(const std::string &version) : | |
206 | - SmallMessageHelper(), mVersion(version) { } | |
207 | - | |
208 | - HelloMessage *clone() const { | |
209 | - return new HelloMessage(*this); | |
210 | - } | |
211 | - | |
212 | - std::string version() { return mVersion; } | |
213 | - void version(const std::string &version) { mVersion = version; } | |
214 | - | |
215 | - MessageTypeID type() const { return kType; } | |
216 | - | |
217 | -protected: | |
218 | - void reallyDeflateTo(AOStream& outputStream) const; | |
219 | - bool reallyInflateFrom(AIStream& inputStream); | |
220 | - | |
221 | -private: | |
222 | - | |
223 | - std::string mVersion; | |
224 | -}; | |
225 | - | |
226 | -typedef TemplatizedSimpleMessage<kJOIN_PLAYER_MESSAGE, int16> JoinPlayerMessage; | |
227 | - | |
228 | -class JoinerInfoMessage : public SmallMessageHelper | |
229 | -{ | |
230 | -public: | |
231 | - enum { kType = kJOINER_INFO_MESSAGE }; | |
232 | - | |
233 | - JoinerInfoMessage() : SmallMessageHelper() { } | |
234 | - | |
235 | - JoinerInfoMessage(prospective_joiner_info *info, const std::string &version) : SmallMessageHelper() { | |
236 | - | |
237 | - mInfo = *info; | |
238 | - mVersion = version; | |
239 | - } | |
240 | - | |
241 | - JoinerInfoMessage *clone() const { | |
242 | - return new JoinerInfoMessage(*this); | |
243 | - } | |
244 | - | |
245 | - prospective_joiner_info *info() { return &mInfo; } | |
246 | - void info(const prospective_joiner_info *theInfo) { | |
247 | - mInfo = *theInfo; | |
248 | - } | |
249 | - | |
250 | - std::string version() { return mVersion; } | |
251 | - void version(const std::string version) { mVersion = version; } | |
252 | - | |
253 | - MessageTypeID type() const { return kType; } | |
254 | - | |
255 | -protected: | |
256 | - void reallyDeflateTo(AOStream& outputStream) const; | |
257 | - bool reallyInflateFrom(AIStream& inputStream); | |
258 | - | |
259 | -private: | |
260 | - prospective_joiner_info mInfo; | |
261 | - std::string mVersion; | |
262 | -}; | |
263 | - | |
264 | -class BigChunkOfZippedDataMessage : public BigChunkOfDataMessage | |
265 | -// zips on deflate, unzips on inflate | |
266 | -{ | |
267 | -public: | |
268 | - BigChunkOfZippedDataMessage(MessageTypeID inType, const Uint8* inBuffer = NULL, size_t inLength = 0) : BigChunkOfDataMessage(inType, inBuffer, inLength) { } | |
269 | - BigChunkOfZippedDataMessage(const BigChunkOfDataMessage& other) : BigChunkOfDataMessage(other) { } | |
270 | - | |
271 | - bool inflateFrom(const UninflatedMessage& inUninflated); | |
272 | - UninflatedMessage* deflate() const; | |
273 | -}; | |
274 | - | |
275 | -template<int messageType, class T> class TemplatizedDataMessage : public T | |
276 | -{ | |
277 | -public: | |
278 | - enum { | |
279 | - kType = messageType | |
280 | - }; | |
281 | - | |
282 | - TemplatizedDataMessage(const Uint8* inBuffer = NULL, size_t inLength = 0) : | |
283 | - T(kType, inBuffer, inLength) { }; | |
284 | - TemplatizedDataMessage(const TemplatizedDataMessage& other) : T(other) { } | |
285 | - COVARIANT_RETURN(Message *, TemplatizedDataMessage *) clone () const { | |
286 | - return new TemplatizedDataMessage(*this); | |
287 | - } | |
288 | -}; | |
289 | - | |
290 | -typedef TemplatizedDataMessage<kMAP_MESSAGE, BigChunkOfDataMessage> MapMessage; | |
291 | -typedef TemplatizedDataMessage<kZIPPED_MAP_MESSAGE, BigChunkOfZippedDataMessage> ZippedMapMessage; | |
292 | - | |
293 | -typedef TemplatizedDataMessage<kPHYSICS_MESSAGE, BigChunkOfDataMessage> PhysicsMessage; | |
294 | -typedef TemplatizedDataMessage<kZIPPED_PHYSICS_MESSAGE, BigChunkOfZippedDataMessage> ZippedPhysicsMessage; | |
295 | - | |
296 | -typedef TemplatizedDataMessage<kLUA_MESSAGE, BigChunkOfDataMessage> LuaMessage; | |
297 | -typedef TemplatizedDataMessage<kZIPPED_LUA_MESSAGE, BigChunkOfZippedDataMessage> ZippedLuaMessage; | |
298 | - | |
299 | - | |
300 | -class NetworkChatMessage : public SmallMessageHelper | |
301 | -{ | |
302 | - public: | |
303 | - enum { kType = kCHAT_MESSAGE }; | |
304 | - enum { CHAT_MESSAGE_SIZE = 1024 }; | |
305 | - | |
306 | - enum { kTargetPlayers = 0, | |
307 | - kTargetTeam = 1, | |
308 | - kTargetPlayer = 2, | |
309 | - kTargetClients = 3, | |
310 | - kTargetClient = 4 }; | |
311 | - | |
312 | - NetworkChatMessage(const char *chatText = NULL, int16 senderID = 0, | |
313 | - int16 target = 0, int16 targetID = -1) | |
314 | - : mSenderID(senderID), mTarget(target), mTargetID(targetID) | |
315 | - { | |
316 | - strncpy(mChatText, (chatText == NULL) ? "" : chatText, CHAT_MESSAGE_SIZE); | |
317 | - mChatText[CHAT_MESSAGE_SIZE - 1] = '\0'; | |
318 | - } | |
319 | - | |
320 | - NetworkChatMessage * clone() const { | |
321 | - return new NetworkChatMessage(*this); | |
322 | - } | |
323 | - | |
324 | - MessageTypeID type() const { return kType; } | |
325 | - const char *chatText() const { return mChatText; } | |
326 | - int16 senderID() const { return mSenderID; } | |
327 | - int16 target() const { return mTarget; } | |
328 | - int16 targetID() const { return mTargetID; } | |
329 | - | |
330 | - protected: | |
331 | - void reallyDeflateTo(AOStream& outputStream) const; | |
332 | - bool reallyInflateFrom(AIStream& includeStream); | |
333 | - | |
334 | - private: | |
335 | - char mChatText[CHAT_MESSAGE_SIZE]; | |
336 | - int16 mSenderID; | |
337 | - int16 mTarget; | |
338 | - int16 mTargetID; | |
339 | -}; | |
340 | - | |
341 | -class NetworkStatsMessage : public SmallMessageHelper | |
342 | -{ | |
343 | -public: | |
344 | - enum { kType = kNETWORK_STATS_MESSAGE }; | |
345 | - | |
346 | - NetworkStatsMessage() : SmallMessageHelper() { } | |
347 | - NetworkStatsMessage(const std::vector<NetworkStats>& stats) : SmallMessageHelper(), mStats(stats) { } | |
348 | - | |
349 | - NetworkStatsMessage* clone() const { | |
350 | - return new NetworkStatsMessage(*this); | |
351 | - } | |
352 | - | |
353 | - MessageTypeID type() const { return kType; } | |
354 | - | |
355 | - std::vector<NetworkStats> mStats; | |
356 | -protected: | |
357 | - void reallyDeflateTo(AOStream& outputStream) const; | |
358 | - bool reallyInflateFrom(AIStream& inputStream); | |
359 | -}; | |
360 | - | |
361 | -class ServerWarningMessage : public SmallMessageHelper | |
362 | -{ | |
363 | -public: | |
364 | - enum { kType = kSERVER_WARNING_MESSAGE }; | |
365 | - enum { kMaxStringSize = 1024 }; | |
366 | - | |
367 | - enum Reason { | |
368 | - kNoReason, | |
369 | - kJoinerUngatherable | |
370 | - } ; | |
371 | - | |
372 | - ServerWarningMessage() { } | |
373 | - | |
374 | - ServerWarningMessage(std::string s, Reason reason) : | |
375 | - mString(s), mReason(reason) { | |
376 | - assert(s.length() < kMaxStringSize); | |
377 | - } | |
378 | - | |
379 | - ServerWarningMessage *clone() const { | |
380 | - return new ServerWarningMessage(*this); | |
381 | - } | |
382 | - | |
383 | - MessageTypeID type() const { return kType; } | |
384 | - void string (const std::string s) { | |
385 | - assert(s.length() < kMaxStringSize); | |
386 | - mString = s; | |
387 | - } | |
388 | - const std::string *string() { return &mString; } | |
389 | - | |
390 | -protected: | |
391 | - void reallyDeflateTo(AOStream& outputStream) const; | |
392 | - bool reallyInflateFrom(AIStream& inputStream); | |
393 | - | |
394 | -private: | |
395 | - std::string mString; | |
396 | - Reason mReason; | |
397 | -}; | |
398 | - | |
399 | - | |
400 | -class TopologyMessage : public SmallMessageHelper | |
401 | -{ | |
402 | - public: | |
403 | - enum { kType = kTOPOLOGY_MESSAGE }; | |
404 | - | |
405 | - TopologyMessage() : SmallMessageHelper() { } | |
406 | - | |
407 | - TopologyMessage(NetTopology *topology) : SmallMessageHelper() { | |
408 | - mTopology = *topology; | |
409 | - } | |
410 | - | |
411 | - TopologyMessage *clone() const { | |
412 | - return new TopologyMessage(*this); | |
413 | - } | |
414 | - | |
415 | - NetTopology *topology() { return &mTopology; } | |
416 | - void topology(const NetTopology *theTopology) { mTopology = *theTopology; } | |
417 | - | |
418 | - MessageTypeID type() const { return kType; } | |
419 | - | |
420 | - protected: | |
421 | - void reallyDeflateTo(AOStream& outputStream) const; | |
422 | - bool reallyInflateFrom(AIStream& inputStream); | |
423 | - | |
424 | - private: | |
425 | - NetTopology mTopology; | |
426 | -}; | |
427 | - | |
428 | -struct Client { | |
429 | - Client(CommunicationsChannel *); | |
430 | - enum { | |
431 | - _connecting, | |
432 | - _connected_but_not_yet_shown, | |
433 | - _connected, | |
434 | - _awaiting_capabilities, | |
435 | - _ungatherable, | |
436 | - _joiner_didnt_accept, | |
437 | - _awaiting_accept_join, | |
438 | - _awaiting_map, | |
439 | - _ingame, | |
440 | - _disconnect | |
441 | - }; | |
442 | - | |
443 | - CommunicationsChannel *channel; | |
444 | - short state; | |
445 | - uint16 network_version; | |
446 | - Capabilities capabilities; | |
447 | - unsigned char name[MAX_NET_PLAYER_NAME_LENGTH]; | |
448 | - | |
449 | - static CheckPlayerProcPtr check_player; | |
450 | - | |
451 | - ~Client(); | |
452 | - | |
453 | - void drop(); | |
454 | - | |
455 | - enum { | |
456 | - _dont_warn_joiner = false, | |
457 | - _warn_joiner = true | |
458 | - }; | |
459 | - bool capabilities_indicate_player_is_gatherable(bool warn_joiner); | |
460 | - bool can_pregame_chat() { return ((state == _connected) || (state == _connected_but_not_yet_shown) || (state == _ungatherable) || (state == _joiner_didnt_accept) || (state == _awaiting_accept_join) || (state == _awaiting_map)); } | |
461 | - | |
462 | - void handleJoinerInfoMessage(JoinerInfoMessage*, CommunicationsChannel*); | |
463 | - void unexpectedMessageHandler(Message *, CommunicationsChannel*); | |
464 | - void handleCapabilitiesMessage(CapabilitiesMessage*,CommunicationsChannel*); | |
465 | - void handleAcceptJoinMessage(AcceptJoinMessage*, CommunicationsChannel*); | |
466 | - void handleChatMessage(NetworkChatMessage*, CommunicationsChannel*); | |
467 | - void handleChangeColorsMessage(ChangeColorsMessage*, CommunicationsChannel*); | |
468 | - | |
469 | - std::auto_ptr<MessageDispatcher> mDispatcher; | |
470 | - std::auto_ptr<MessageHandler> mJoinerInfoMessageHandler; | |
471 | - std::auto_ptr<MessageHandler> mUnexpectedMessageHandler; | |
472 | - std::auto_ptr<MessageHandler> mCapabilitiesMessageHandler; | |
473 | - std::auto_ptr<MessageHandler> mAcceptJoinMessageHandler; | |
474 | - std::auto_ptr<MessageHandler> mChatMessageHandler; | |
475 | - std::auto_ptr<MessageHandler> mChangeColorsMessageHandler; | |
476 | -}; | |
477 | - | |
478 | - | |
479 | - | |
480 | -#endif // NETWORK_MESSAGES_H | |
1 | +/* | |
2 | + * network_messages.h - TCPMess message types for network game setup | |
3 | + | |
4 | + Copyright (C) 2005 and beyond by Gregory Smith | |
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 | + */ | |
22 | + | |
23 | +#ifndef NETWORK_MESSAGES_H | |
24 | +#define NETWORK_MESSAGES_H | |
25 | + | |
26 | +#include "cseries.h" | |
27 | +#include "AStream.h" | |
28 | +#include "Message.h" | |
29 | + | |
30 | +#include "SDL_net.h" | |
31 | + | |
32 | +#include "network_capabilities.h" | |
33 | +#include "network_private.h" | |
34 | + | |
35 | +enum { | |
36 | + kHELLO_MESSAGE = 700, | |
37 | + kJOINER_INFO_MESSAGE, | |
38 | + kJOIN_PLAYER_MESSAGE, | |
39 | + kCAPABILITIES_MESSAGE, | |
40 | + kACCEPT_JOIN_MESSAGE, | |
41 | + kTOPOLOGY_MESSAGE, | |
42 | + kMAP_MESSAGE, | |
43 | + kPHYSICS_MESSAGE, | |
44 | + kLUA_MESSAGE, | |
45 | + kCHAT_MESSAGE, | |
46 | + kUNKNOWN_MESSAGE_MESSAGE, | |
47 | + kEND_GAME_DATA_MESSAGE, | |
48 | + kCHANGE_COLORS_MESSAGE, | |
49 | + kSERVER_WARNING_MESSAGE, | |
50 | + kCLIENT_INFO_MESSAGE, | |
51 | + kZIPPED_MAP_MESSAGE, | |
52 | + kZIPPED_PHYSICS_MESSAGE, | |
53 | + kZIPPED_LUA_MESSAGE, | |
54 | + kNETWORK_STATS_MESSAGE | |
55 | +}; | |
56 | + | |
57 | +template <MessageTypeID tMessageType, typename tValueType> | |
58 | +class TemplatizedSimpleMessage : public SimpleMessage<tValueType> | |
59 | +{ | |
60 | + public: | |
61 | + enum { kType = tMessageType }; | |
62 | + | |
63 | + TemplatizedSimpleMessage() : SimpleMessage<tValueType>(tMessageType) { } | |
64 | + | |
65 | + TemplatizedSimpleMessage(tValueType inValue) : SimpleMessage<tValueType>(tMessageType, inValue) { } | |
66 | + | |
67 | + TemplatizedSimpleMessage<tMessageType, tValueType>* clone () const { | |
68 | + return new TemplatizedSimpleMessage<tMessageType, tValueType>(*this); | |
69 | + } | |
70 | + | |
71 | + MessageTypeID type() const { return kType; } | |
72 | +}; | |
73 | + | |
74 | +class AcceptJoinMessage : public SmallMessageHelper | |
75 | +{ | |
76 | + public: | |
77 | + enum { kType = kACCEPT_JOIN_MESSAGE }; | |
78 | + | |
79 | + AcceptJoinMessage() : SmallMessageHelper() { } | |
80 | + | |
81 | + AcceptJoinMessage(bool accepted, NetPlayer *player) : SmallMessageHelper() { | |
82 | + mAccepted = accepted; | |
83 | + mPlayer = *player; | |
84 | + } | |
85 | + | |
86 | + AcceptJoinMessage *clone() const { | |
87 | + return new AcceptJoinMessage(*this); | |
88 | + } | |
89 | + | |
90 | + bool accepted() { return mAccepted; } | |
91 | + void accepted(bool isAccepted) { mAccepted = isAccepted; } | |
92 | + NetPlayer *player() { return &mPlayer; } | |
93 | + void player(const NetPlayer *thePlayer) { mPlayer = *thePlayer; } | |
94 | + | |
95 | + MessageTypeID type() const { return kType; } | |
96 | + | |
97 | + protected: | |
98 | + void reallyDeflateTo(AOStream& outputStream) const; | |
99 | + bool reallyInflateFrom(AIStream& inputStream); | |
100 | + | |
101 | + private: | |
102 | + | |
103 | + bool mAccepted; | |
104 | + NetPlayer mPlayer; | |
105 | +}; | |
106 | + | |
107 | +class CapabilitiesMessage : public SmallMessageHelper | |
108 | +{ | |
109 | + public: | |
110 | + enum { kType = kCAPABILITIES_MESSAGE }; | |
111 | + | |
112 | + CapabilitiesMessage() : SmallMessageHelper() { } | |
113 | + | |
114 | + CapabilitiesMessage(const Capabilities &capabilities) : SmallMessageHelper() { | |
115 | + mCapabilities = capabilities; | |
116 | + } | |
117 | + | |
118 | + CapabilitiesMessage *clone() const { | |
119 | + return new CapabilitiesMessage(*this); | |
120 | + } | |
121 | + | |
122 | + const Capabilities *capabilities() { return &mCapabilities; } | |
123 | + | |
124 | + MessageTypeID type() const { return kType; } | |
125 | + | |
126 | +protected: | |
127 | + void reallyDeflateTo(AOStream& outputStream) const; | |
128 | + bool reallyInflateFrom(AIStream& inputStream); | |
129 | + | |
130 | +private: | |
131 | + Capabilities mCapabilities; | |
132 | +}; | |
133 | + | |
134 | +class ChangeColorsMessage : public SmallMessageHelper | |
135 | +{ | |
136 | + public: | |
137 | + enum { kType = kCHANGE_COLORS_MESSAGE }; | |
138 | + | |
139 | + ChangeColorsMessage() : SmallMessageHelper() { } | |
140 | + | |
141 | + ChangeColorsMessage(int16 color, int16 team) : SmallMessageHelper() { | |
142 | + mColor = color; | |
143 | + mTeam = team; | |
144 | + } | |
145 | + | |
146 | + ChangeColorsMessage *clone() const { | |
147 | + return new ChangeColorsMessage(*this); | |
148 | + } | |
149 | + | |
150 | + int16 color() { return mColor; } | |
151 | + int16 team() { return mTeam; } | |
152 | + | |
153 | + MessageTypeID type() const { return kType; } | |
154 | + | |
155 | + protected: | |
156 | + void reallyDeflateTo(AOStream& outputStream) const; | |
157 | + bool reallyInflateFrom(AIStream& inputStream); | |
158 | + | |
159 | + private: | |
160 | + | |
161 | + int16 mColor; | |
162 | + int16 mTeam; | |
163 | +}; | |
164 | + | |
165 | +class ClientInfoMessage : public SmallMessageHelper | |
166 | +{ | |
167 | +public: | |
168 | + enum { kType = kCLIENT_INFO_MESSAGE }; | |
169 | + enum { kAdd, kUpdate, kRemove }; | |
170 | + | |
171 | + ClientInfoMessage() : SmallMessageHelper() { } | |
172 | + | |
173 | + ClientInfoMessage(int16 stream_id, const ClientChatInfo *clientChatInfo, int16 action ) : mStreamID(stream_id), mAction(action), mClientChatInfo(*clientChatInfo) { } | |
174 | + | |
175 | + ClientInfoMessage *clone() const { | |
176 | + return new ClientInfoMessage(*this); | |
177 | + } | |
178 | + | |
179 | + const ClientChatInfo *info() { return &mClientChatInfo; } | |
180 | + const int16 action() { return mAction; } | |
181 | + const int16 stream_id() { return mStreamID; } | |
182 | + | |
183 | + MessageTypeID type() const { return kType; } | |
184 | + | |
185 | +protected: | |
186 | + void reallyDeflateTo(AOStream& outputStream) const; | |
187 | + bool reallyInflateFrom(AIStream& inputStream); | |
188 | + | |
189 | +private: | |
190 | + ClientChatInfo mClientChatInfo; | |
191 | + int16 mAction; | |
192 | + int16 mStreamID; | |
193 | +}; | |
194 | + | |
195 | + | |
196 | +typedef DatalessMessage<kEND_GAME_DATA_MESSAGE> EndGameDataMessage; | |
197 | + | |
198 | +class HelloMessage : public SmallMessageHelper | |
199 | +{ | |
200 | +public: | |
201 | + enum { kType = kHELLO_MESSAGE }; | |
202 | + | |
203 | + HelloMessage() : SmallMessageHelper() { } | |
204 | + | |
205 | + HelloMessage(const std::string &version) : | |
206 | + SmallMessageHelper(), mVersion(version) { } | |
207 | + | |
208 | + HelloMessage *clone() const { | |
209 | + return new HelloMessage(*this); | |
210 | + } | |
211 | + | |
212 | + std::string version() { return mVersion; } | |
213 | + void version(const std::string &version) { mVersion = version; } | |
214 | + | |
215 | + MessageTypeID type() const { return kType; } | |
216 | + | |
217 | +protected: | |
218 | + void reallyDeflateTo(AOStream& outputStream) const; | |
219 | + bool reallyInflateFrom(AIStream& inputStream); | |
220 | + | |
221 | +private: | |
222 | + | |
223 | + std::string mVersion; | |
224 | +}; | |
225 | + | |
226 | +typedef TemplatizedSimpleMessage<kJOIN_PLAYER_MESSAGE, int16> JoinPlayerMessage; | |
227 | + | |
228 | +class JoinerInfoMessage : public SmallMessageHelper | |
229 | +{ | |
230 | +public: | |
231 | + enum { kType = kJOINER_INFO_MESSAGE }; | |
232 | + | |
233 | + JoinerInfoMessage() : SmallMessageHelper() { } | |
234 | + | |
235 | + JoinerInfoMessage(prospective_joiner_info *info, const std::string &version) : SmallMessageHelper() { | |
236 | + | |
237 | + mInfo = *info; | |
238 | + mVersion = version; | |
239 | + } | |
240 | + | |
241 | + JoinerInfoMessage *clone() const { | |
242 | + return new JoinerInfoMessage(*this); | |
243 | + } | |
244 | + | |
245 | + prospective_joiner_info *info() { return &mInfo; } | |
246 | + void info(const prospective_joiner_info *theInfo) { | |
247 | + mInfo = *theInfo; | |
248 | + } | |
249 | + | |
250 | + std::string version() { return mVersion; } | |
251 | + void version(const std::string version) { mVersion = version; } | |
252 | + | |
253 | + MessageTypeID type() const { return kType; } | |
254 | + | |
255 | +protected: | |
256 | + void reallyDeflateTo(AOStream& outputStream) const; | |
257 | + bool reallyInflateFrom(AIStream& inputStream); | |
258 | + | |
259 | +private: | |
260 | + prospective_joiner_info mInfo; | |
261 | + std::string mVersion; | |
262 | +}; | |
263 | + | |
264 | +class BigChunkOfZippedDataMessage : public BigChunkOfDataMessage | |
265 | +// zips on deflate, unzips on inflate | |
266 | +{ | |
267 | +public: | |
268 | + BigChunkOfZippedDataMessage(MessageTypeID inType, const Uint8* inBuffer = NULL, size_t inLength = 0) : BigChunkOfDataMessage(inType, inBuffer, inLength) { } | |
269 | + BigChunkOfZippedDataMessage(const BigChunkOfDataMessage& other) : BigChunkOfDataMessage(other) { } | |
270 | + | |
271 | + bool inflateFrom(const UninflatedMessage& inUninflated); | |
272 | + UninflatedMessage* deflate() const; | |
273 | +}; | |
274 | + | |
275 | +template<int messageType, class T> class TemplatizedDataMessage : public T | |
276 | +{ | |
277 | +public: | |
278 | + enum { | |
279 | + kType = messageType | |
280 | + }; | |
281 | + | |
282 | + TemplatizedDataMessage(const Uint8* inBuffer = NULL, size_t inLength = 0) : | |
283 | + T(kType, inBuffer, inLength) { }; | |
284 | + TemplatizedDataMessage(const TemplatizedDataMessage& other) : T(other) { } | |
285 | + COVARIANT_RETURN(Message *, TemplatizedDataMessage *) clone () const { | |
286 | + return new TemplatizedDataMessage(*this); | |
287 | + } | |
288 | +}; | |
289 | + | |
290 | +typedef TemplatizedDataMessage<kMAP_MESSAGE, BigChunkOfDataMessage> MapMessage; | |
291 | +typedef TemplatizedDataMessage<kZIPPED_MAP_MESSAGE, BigChunkOfZippedDataMessage> ZippedMapMessage; | |
292 | + | |
293 | +typedef TemplatizedDataMessage<kPHYSICS_MESSAGE, BigChunkOfDataMessage> PhysicsMessage; | |
294 | +typedef TemplatizedDataMessage<kZIPPED_PHYSICS_MESSAGE, BigChunkOfZippedDataMessage> ZippedPhysicsMessage; | |
295 | + | |
296 | +typedef TemplatizedDataMessage<kLUA_MESSAGE, BigChunkOfDataMessage> LuaMessage; | |
297 | +typedef TemplatizedDataMessage<kZIPPED_LUA_MESSAGE, BigChunkOfZippedDataMessage> ZippedLuaMessage; | |
298 | + | |
299 | + | |
300 | +class NetworkChatMessage : public SmallMessageHelper | |
301 | +{ | |
302 | + public: | |
303 | + enum { kType = kCHAT_MESSAGE }; | |
304 | + enum { CHAT_MESSAGE_SIZE = 1024 }; | |
305 | + | |
306 | + enum { kTargetPlayers = 0, | |
307 | + kTargetTeam = 1, | |
308 | + kTargetPlayer = 2, | |
309 | + kTargetClients = 3, | |
310 | + kTargetClient = 4 }; | |
311 | + | |
312 | + NetworkChatMessage(const char *chatText = NULL, int16 senderID = 0, | |
313 | + int16 target = 0, int16 targetID = -1) | |
314 | + : mSenderID(senderID), mTarget(target), mTargetID(targetID) | |
315 | + { | |
316 | + strncpy(mChatText, (chatText == NULL) ? "" : chatText, CHAT_MESSAGE_SIZE); | |
317 | + mChatText[CHAT_MESSAGE_SIZE - 1] = '\0'; | |
318 | + } | |
319 | + | |
320 | + NetworkChatMessage * clone() const { | |
321 | + return new NetworkChatMessage(*this); | |
322 | + } | |
323 | + | |
324 | + MessageTypeID type() const { return kType; } | |
325 | + const char *chatText() const { return mChatText; } | |
326 | + int16 senderID() const { return mSenderID; } | |
327 | + int16 target() const { return mTarget; } | |
328 | + int16 targetID() const { return mTargetID; } | |
329 | + | |
330 | + protected: | |
331 | + void reallyDeflateTo(AOStream& outputStream) const; | |
332 | + bool reallyInflateFrom(AIStream& includeStream); | |
333 | + | |
334 | + private: | |
335 | + char mChatText[CHAT_MESSAGE_SIZE]; | |
336 | + int16 mSenderID; | |
337 | + int16 mTarget; | |
338 | + int16 mTargetID; | |
339 | +}; | |
340 | + | |
341 | +class NetworkStatsMessage : public SmallMessageHelper | |
342 | +{ | |
343 | +public: | |
344 | + enum { kType = kNETWORK_STATS_MESSAGE }; | |
345 | + | |
346 | + NetworkStatsMessage() : SmallMessageHelper() { } | |
347 | + NetworkStatsMessage(const std::vector<NetworkStats>& stats) : SmallMessageHelper(), mStats(stats) { } | |
348 | + | |
349 | + NetworkStatsMessage* clone() const { | |
350 | + return new NetworkStatsMessage(*this); | |
351 | + } | |
352 | + | |
353 | + MessageTypeID type() const { return kType; } | |
354 | + | |
355 | + std::vector<NetworkStats> mStats; | |
356 | +protected: | |
357 | + void reallyDeflateTo(AOStream& outputStream) const; | |
358 | + bool reallyInflateFrom(AIStream& inputStream); | |
359 | +}; | |
360 | + | |
361 | +class ServerWarningMessage : public SmallMessageHelper | |
362 | +{ | |
363 | +public: | |
364 | + enum { kType = kSERVER_WARNING_MESSAGE }; | |
365 | + enum { kMaxStringSize = 1024 }; | |
366 | + | |
367 | + enum Reason { | |
368 | + kNoReason, | |
369 | + kJoinerUngatherable | |
370 | + } ; | |
371 | + | |
372 | + ServerWarningMessage() { } | |
373 | + | |
374 | + ServerWarningMessage(std::string s, Reason reason) : | |
375 | + mString(s), mReason(reason) { | |
376 | + assert(s.length() < kMaxStringSize); | |
377 | + } | |
378 | + | |
379 | + ServerWarningMessage *clone() const { | |
380 | + return new ServerWarningMessage(*this); | |
381 | + } | |
382 | + | |
383 | + MessageTypeID type() const { return kType; } | |
384 | + void string (const std::string s) { | |
385 | + assert(s.length() < kMaxStringSize); | |
386 | + mString = s; | |
387 | + } | |
388 | + const std::string *string() { return &mString; } | |
389 | + | |
390 | +protected: | |
391 | + void reallyDeflateTo(AOStream& outputStream) const; | |
392 | + bool reallyInflateFrom(AIStream& inputStream); | |
393 | + | |
394 | +private: | |
395 | + std::string mString; | |
396 | + Reason mReason; | |
397 | +}; | |
398 | + | |
399 | + | |
400 | +class TopologyMessage : public SmallMessageHelper | |
401 | +{ | |
402 | + public: | |
403 | + enum { kType = kTOPOLOGY_MESSAGE }; | |
404 | + | |
405 | + TopologyMessage() : SmallMessageHelper() { } | |
406 | + | |
407 | + TopologyMessage(NetTopology *topology) : SmallMessageHelper() { | |
408 | + mTopology = *topology; | |
409 | + } | |
410 | + | |
411 | + TopologyMessage *clone() const { | |
412 | + return new TopologyMessage(*this); | |
413 | + } | |
414 | + | |
415 | + NetTopology *topology() { return &mTopology; } | |
416 | + void topology(const NetTopology *theTopology) { mTopology = *theTopology; } | |
417 | + | |
418 | + MessageTypeID type() const { return kType; } | |
419 | + | |
420 | + protected: | |
421 | + void reallyDeflateTo(AOStream& outputStream) const; | |
422 | + bool reallyInflateFrom(AIStream& inputStream); | |
423 | + | |
424 | + private: | |
425 | + NetTopology mTopology; | |
426 | +}; | |
427 | + | |
428 | +struct Client { | |
429 | + Client(CommunicationsChannel *); | |
430 | + enum { | |
431 | + _connecting, | |
432 | + _connected_but_not_yet_shown, | |
433 | + _connected, | |
434 | + _awaiting_capabilities, | |
435 | + _ungatherable, | |
436 | + _joiner_didnt_accept, | |
437 | + _awaiting_accept_join, | |
438 | + _awaiting_map, | |
439 | + _ingame, | |
440 | + _disconnect | |
441 | + }; | |
442 | + | |
443 | + CommunicationsChannel *channel; | |
444 | + short state; | |
445 | + uint16 network_version; | |
446 | + Capabilities capabilities; | |
447 | + unsigned char name[MAX_NET_PLAYER_NAME_LENGTH]; | |
448 | + | |
449 | + static CheckPlayerProcPtr check_player; | |
450 | + | |
451 | + ~Client(); | |
452 | + | |
453 | + void drop(); | |
454 | + | |
455 | + enum { | |
456 | + _dont_warn_joiner = false, | |
457 | + _warn_joiner = true | |
458 | + }; | |
459 | + bool capabilities_indicate_player_is_gatherable(bool warn_joiner); | |
460 | + bool can_pregame_chat() { return ((state == _connected) || (state == _connected_but_not_yet_shown) || (state == _ungatherable) || (state == _joiner_didnt_accept) || (state == _awaiting_accept_join) || (state == _awaiting_map)); } | |
461 | + | |
462 | + void handleJoinerInfoMessage(JoinerInfoMessage*, CommunicationsChannel*); | |
463 | + void unexpectedMessageHandler(Message *, CommunicationsChannel*); | |
464 | + void handleCapabilitiesMessage(CapabilitiesMessage*,CommunicationsChannel*); | |
465 | + void handleAcceptJoinMessage(AcceptJoinMessage*, CommunicationsChannel*); | |
466 | + void handleChatMessage(NetworkChatMessage*, CommunicationsChannel*); | |
467 | + void handleChangeColorsMessage(ChangeColorsMessage*, CommunicationsChannel*); | |
468 | + | |
469 | + std::auto_ptr<MessageDispatcher> mDispatcher; | |
470 | + std::auto_ptr<MessageHandler> mJoinerInfoMessageHandler; | |
471 | + std::auto_ptr<MessageHandler> mUnexpectedMessageHandler; | |
472 | + std::auto_ptr<MessageHandler> mCapabilitiesMessageHandler; | |
473 | + std::auto_ptr<MessageHandler> mAcceptJoinMessageHandler; | |
474 | + std::auto_ptr<MessageHandler> mChatMessageHandler; | |
475 | + std::auto_ptr<MessageHandler> mChangeColorsMessageHandler; | |
476 | +}; | |
477 | + | |
478 | + | |
479 | + | |
480 | +#endif // NETWORK_MESSAGES_H |
@@ -1,63 +1,63 @@ | ||
1 | -#ifndef __UPDATE_H | |
2 | -#define __UPDATE_H | |
3 | - | |
4 | -/* | |
5 | - | |
6 | - Copyright (C) 2007 Gregory Smith. | |
7 | - and the "Aleph One" developers. | |
8 | - | |
9 | - This program is free software; you can redistribute it and/or modify | |
10 | - it under the terms of the GNU General Public License as published by | |
11 | - the Free Software Foundation; either version 3 of the License, or | |
12 | - (at your option) any later version. | |
13 | - | |
14 | - This program is distributed in the hope that it will be useful, | |
15 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | - GNU General Public License for more details. | |
18 | - | |
19 | - This license is contained in the file "COPYING", | |
20 | - which is included with this source code; it is available online at | |
21 | - http://www.gnu.org/licenses/gpl.html | |
22 | - | |
23 | - Checks for updates online | |
24 | - | |
25 | -*/ | |
26 | - | |
27 | -#include <string> | |
28 | -#include <SDL_thread.h> | |
29 | -#include "cseries.h" | |
30 | - | |
31 | -class Update | |
32 | -{ | |
33 | -public: | |
34 | - static Update *instance() { if (!m_instance) m_instance = new Update(); return m_instance; } | |
35 | - | |
36 | - enum Status | |
37 | - { | |
38 | - CheckingForUpdate, | |
39 | - UpdateCheckFailed, | |
40 | - UpdateAvailable, | |
41 | - NoUpdateAvailable | |
42 | - }; | |
43 | - | |
44 | - Status GetStatus() { return m_status; } | |
45 | - std::string NewDisplayVersion() { assert(m_status == UpdateAvailable); return m_new_display_version; } | |
46 | - | |
47 | -private: | |
48 | - static Update *m_instance; | |
49 | - Update(); | |
50 | - ~Update(); | |
51 | - | |
52 | - void StartUpdateCheck(); | |
53 | - static int update_thread(void *); | |
54 | - int Thread(); | |
55 | - | |
56 | - Status m_status; | |
57 | - std::string m_new_date_version; | |
58 | - std::string m_new_display_version; | |
59 | - SDL_Thread *m_thread; | |
60 | - | |
61 | -}; | |
62 | - | |
63 | -#endif | |
1 | +#ifndef __UPDATE_H | |
2 | +#define __UPDATE_H | |
3 | + | |
4 | +/* | |
5 | + | |
6 | + Copyright (C) 2007 Gregory Smith. | |
7 | + and the "Aleph One" developers. | |
8 | + | |
9 | + This program is free software; you can redistribute it and/or modify | |
10 | + it under the terms of the GNU General Public License as published by | |
11 | + the Free Software Foundation; either version 3 of the License, or | |
12 | + (at your option) any later version. | |
13 | + | |
14 | + This program is distributed in the hope that it will be useful, | |
15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + GNU General Public License for more details. | |
18 | + | |
19 | + This license is contained in the file "COPYING", | |
20 | + which is included with this source code; it is available online at | |
21 | + http://www.gnu.org/licenses/gpl.html | |
22 | + | |
23 | + Checks for updates online | |
24 | + | |
25 | +*/ | |
26 | + | |
27 | +#include <string> | |
28 | +#include <SDL_thread.h> | |
29 | +#include "cseries.h" | |
30 | + | |
31 | +class Update | |
32 | +{ | |
33 | +public: | |
34 | + static Update *instance() { if (!m_instance) m_instance = new Update(); return m_instance; } | |
35 | + | |
36 | + enum Status | |
37 | + { | |
38 | + CheckingForUpdate, | |
39 | + UpdateCheckFailed, | |
40 | + UpdateAvailable, | |
41 | + NoUpdateAvailable | |
42 | + }; | |
43 | + | |
44 | + Status GetStatus() { return m_status; } | |
45 | + std::string NewDisplayVersion() { assert(m_status == UpdateAvailable); return m_new_display_version; } | |
46 | + | |
47 | +private: | |
48 | + static Update *m_instance; | |
49 | + Update(); | |
50 | + ~Update(); | |
51 | + | |
52 | + void StartUpdateCheck(); | |
53 | + static int update_thread(void *); | |
54 | + int Thread(); | |
55 | + | |
56 | + Status m_status; | |
57 | + std::string m_new_date_version; | |
58 | + std::string m_new_display_version; | |
59 | + SDL_Thread *m_thread; | |
60 | + | |
61 | +}; | |
62 | + | |
63 | +#endif |
@@ -1,337 +1,337 @@ | ||
1 | -/* | |
2 | - * network_private.h | |
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 | - * This file holds stuff that various parts of the network subsystem want to see, but that portions | |
22 | - * of Aleph One outside of the networking code should not care about. | |
23 | - * | |
24 | - * Oct 11, 2001 (Woody Zenfell): split code away from network.cpp to create this file. | |
25 | - * Made any simple modifications needed to make things compile/work. | |
26 | - * | |
27 | - * Oct-Nov 2001 (Woody Zenfell): added code in support of text messages in stream packets. | |
28 | - * Added many comments. | |
29 | - | |
30 | -Feb 27, 2002 (Br'fin (Jeremy Parsons)): | |
31 | - Enabled SDL networking for Carbon without fully using SDL | |
32 | - | |
33 | - May 24, 2003 (Woody Zenfell): | |
34 | - Split ring-protocol-specific stuff out into RingGameProtocol.cpp; | |
35 | - "Unknown packet type response" streaming packet type; NetGetDistributionInfoForType() | |
36 | - */ | |
37 | - | |
38 | -#ifndef NETWORK_PRIVATE_H | |
39 | -#define NETWORK_PRIVATE_H | |
40 | - | |
41 | -#include "cstypes.h" | |
42 | - | |
43 | -#include "sdl_network.h" | |
44 | - | |
45 | -#include "network.h" | |
46 | - | |
47 | -// "network_dialogs_private.h" | |
48 | -#include "SSLP_API.h" | |
49 | - | |
50 | -#include <memory> | |
51 | - | |
52 | -#define GAME_PORT 4226 | |
53 | - | |
54 | -// (ZZZ:) Moved here from sdl_network.h and macintosh_network.h | |
55 | - | |
56 | -/* ---------- constants */ | |
57 | - | |
58 | -#define asyncUncompleted 1 /* ioResult value */ | |
59 | - | |
60 | -#define strNETWORK_ERRORS 132 | |
61 | -enum /* error string for user */ | |
62 | -{ | |
63 | - netErrCantAddPlayer, | |
64 | - netErrCouldntDistribute, | |
65 | - netErrCouldntJoin, | |
66 | - netErrServerCanceled, | |
67 | - netErrMapDistribFailed, | |
68 | - netErrWaitedTooLongForMap, | |
69 | - netErrSyncFailed, | |
70 | - netErrJoinFailed, | |
71 | - netErrCantContinue, | |
72 | - netErrIncompatibleVersion, | |
73 | - netErrGatheredPlayerUnacceptable, | |
74 | - netErrUngatheredPlayerUnacceptable, | |
75 | - netErrJoinerCantFindScenario, | |
76 | - netErrLostConnection, | |
77 | - netErrCouldntResolve, | |
78 | - netErrCouldntReceiveMap, | |
79 | - netWarnJoinerHasNoStar, | |
80 | - netWarnJoinerHasNoRing, | |
81 | - netWarnJoinerNoLua, | |
82 | - netErrMetaserverConnectionFailure, | |
83 | - netWarnCouldNotAdvertiseOnMetaserver, | |
84 | - netWarnUPnPConfigureFailed | |
85 | -}; | |
86 | - | |
87 | -// (ZZZ:) Moved here from network.cpp | |
88 | - | |
89 | -/* ---------- constants */ | |
90 | - | |
91 | -#define NET_DEAD_ACTION_FLAG_COUNT (-1) | |
92 | -#define NET_DEAD_ACTION_FLAG (NONE) | |
93 | - | |
94 | -#define MAXIMUM_GAME_DATA_SIZE 256 | |
95 | -#define MAXIMUM_PLAYER_DATA_SIZE 128 | |
96 | -#define MAXIMUM_UPDATES_PER_PACKET 16 // how many action flags per player can be sent in each ring packet | |
97 | -#define UPDATES_PER_PACKET 1 // defines action flags per packet and period of the ring | |
98 | -#define UPDATE_LATENCY 1 | |
99 | - | |
100 | -#define NET_QUEUE_SIZE (MAXIMUM_UPDATES_PER_PACKET+1) | |
101 | - | |
102 | -#define UNSYNC_TIMEOUT (3*MACHINE_TICKS_PER_SECOND) // 3 seconds | |
103 | - | |
104 | -#define STREAM_TRANSFER_CHUNK_SIZE (10000) | |
105 | -#define MAP_TRANSFER_TIME_OUT (MACHINE_TICKS_PER_SECOND*70) // 70 seconds to wait for map. | |
106 | -#define NET_SYNC_TIME_OUT (MACHINE_TICKS_PER_SECOND*50) // 50 seconds to time out of syncing. | |
107 | - | |
108 | -#define kACK_TIMEOUT 40 | |
109 | -#define kRETRIES 50 // how many timeouts allowed before dropping the next player | |
110 | - // kRETRIES * kACK_TIMEOUT / 1000 = timeout in seconds | |
111 | - | |
112 | -#define NUM_DISTRIBUTION_TYPES 3 | |
113 | - | |
114 | -// Altering constants below should make you alter get_network_version(). - Woody | |
115 | -#define kPROTOCOL_TYPE 69 | |
116 | - | |
117 | -enum /* tag */ | |
118 | -{ // ZZZ annotation: these (in NetPacketHeader) indicate the rest of the datagram is a NetPacket (i.e. a ring packet). | |
119 | - tagRING_PACKET, | |
120 | - tagACKNOWLEDGEMENT, | |
121 | - tagCHANGE_RING_PACKET, // to tell a player to change his downring address. also has action flags. | |
122 | - | |
123 | - // ZZZ annotation: these should only be found in streaming data (in a NetTopology). | |
124 | - tagNEW_PLAYER, | |
125 | - tagCANCEL_GAME, | |
126 | - tagSTART_GAME, | |
127 | - tagDROPPED_PLAYER, | |
128 | - tagCHANGED_PLAYER, | |
129 | - | |
130 | - // ZZZ annotation: these (in NetPacketHeader) indicate the rest of the datagram is a NetDistributionPacket. | |
131 | - tagLOSSY_DISTRIBUTION, // for transfer data other than action flags | |
132 | - tagLOSSLESS_DISTRIBUTION, // ditto, but currently unimplemented | |
133 | - | |
134 | - // ZZZ: more streaming data (topology) packet types | |
135 | - tagRESUME_GAME // ZZZ addition: trying to resume a saved-game rather than start a new netgame. | |
136 | -}; | |
137 | - | |
138 | -enum | |
139 | -{ | |
140 | - typeSYNC_RING_PACKET, // first packet of the game, gets everyone in the game | |
141 | - typeTIME_RING_PACKET, // second packet of the game, sets everyone's clock | |
142 | - typeNORMAL_RING_PACKET, // all the other packets of the game | |
143 | - | |
144 | - typeUNSYNC_RING_PACKET, // last packet of the game, get everyone unsynced properly. | |
145 | - typeDEAD_PACKET // This is simply a convenience for a switch. This packet never leaves the server. | |
146 | -}; | |
147 | - | |
148 | -/* ---------- structures */ | |
149 | - | |
150 | - | |
151 | -// (ZZZ:) Note ye well!!: if you alter these network-related structures, you are probably going to need to modify | |
152 | -// the corresponding _NET structures in network_data_formats.h AND *both* corresponding netcpy() functions in | |
153 | -// network_data_formats.cpp. AND, you'll probably need to alter get_network_version() while you're at it, | |
154 | -// since you've made an incompatible change to the network communication protocol. | |
155 | - | |
156 | -// (ZZZ:) Information passed in datagrams (note: the _NET version is ALWAYS the one sent/received on the wire. | |
157 | -// If not, it's a BUG. These are used to setup/extract data.) | |
158 | -struct NetPacketHeader | |
159 | -{ | |
160 | - int16 tag; | |
161 | - int32 sequence; | |
162 | - | |
163 | - /* data */ | |
164 | -}; | |
165 | -typedef struct NetPacketHeader NetPacketHeader, *NetPacketHeaderPtr; | |
166 | - | |
167 | -struct NetPacket | |
168 | -{ | |
169 | - uint8 ring_packet_type; // typeSYNC_RING_PACKET, etc... | |
170 | - uint8 server_player_index; | |
171 | - int32 server_net_time; | |
172 | - int16 required_action_flags; // handed down from on high (the server) | |
173 | - int16 action_flag_count[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS]; // how many each player actually has. | |
174 | - uint32 action_flags[1]; | |
175 | -}; | |
176 | -typedef struct NetPacket NetPacket, *NetPacketPtr; | |
177 | - | |
178 | -struct NetDistributionPacket | |
179 | -{ | |
180 | - int16 distribution_type; // type of information | |
181 | - int16 first_player_index; // who sent the information | |
182 | - int16 data_size; // how much they're sending. | |
183 | - uint8 data[2]; // the chunk ユo shit to send | |
184 | -}; | |
185 | -typedef struct NetDistributionPacket NetDistributionPacket, *NetDistributionPacketPtr; | |
186 | - | |
187 | -// Information passed in streams | |
188 | -struct NetPlayer | |
189 | -{ | |
190 | - NetAddrBlock dspAddress, ddpAddress; | |
191 | - | |
192 | - int16 identifier; | |
193 | - | |
194 | - int16 stream_id; // to remind gatherer how to contact joiners | |
195 | - | |
196 | - bool net_dead; // only valid if you are the server. | |
197 | - | |
198 | - //uint8 player_data[MAXIMUM_PLAYER_DATA_SIZE]; | |
199 | - player_info player_data; | |
200 | -}; | |
201 | -typedef struct NetPlayer NetPlayer, *NetPlayerPtr; | |
202 | - | |
203 | -struct NetTopology | |
204 | -{ | |
205 | - int16 tag; | |
206 | - int16 player_count; | |
207 | - | |
208 | - int16 nextIdentifier; | |
209 | - | |
210 | - //uint8 game_data[MAXIMUM_GAME_DATA_SIZE]; | |
211 | - game_info game_data; | |
212 | - | |
213 | - struct NetPlayer players[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS]; | |
214 | -}; | |
215 | -typedef struct NetTopology NetTopology, *NetTopologyPtr; | |
216 | - | |
217 | -#ifdef NETWORK_CHAT | |
218 | -// (ZZZ addition) | |
219 | -enum { CHAT_MESSAGE_TEXT_BUFFER_SIZE = 250 }; | |
220 | - | |
221 | -struct NetChatMessage { | |
222 | - int16 sender_identifier; | |
223 | - char text[CHAT_MESSAGE_TEXT_BUFFER_SIZE]; | |
224 | -}; | |
225 | -#endif | |
226 | - | |
227 | - | |
228 | -// ZZZ: same here (should be safe to alter) | |
229 | -struct NetDistributionInfo | |
230 | -{ | |
231 | - bool lossy; | |
232 | - NetDistributionProc distribution_proc; | |
233 | -}; | |
234 | - | |
235 | -typedef struct NetDistributionInfo NetDistributionInfo, *NetDistributionInfoPtr; | |
236 | - | |
237 | -#define errInvalidMapPacket (42) | |
238 | -// ZZZ: taking a cue... used when trying to gather a player whose A1 doesn't support all the features we need. | |
239 | -#define errPlayerTooNaive (43) | |
240 | - | |
241 | -/* ===== application specific data structures/enums */ | |
242 | - | |
243 | -// Information sent via streaming protocol - warning above still applies! | |
244 | -struct gather_player_data { | |
245 | - int16 new_local_player_identifier; | |
246 | -}; | |
247 | - | |
248 | -// used in accept_gather_data::accepted - this is a sneaky way of detecting whether | |
249 | -// we're playing with a resume netgame-capable player or not. (Old code always sent | |
250 | -// 1 on accept, never 2; old code interprets any nonzero 'accepted' as an accept.) | |
251 | -enum { | |
252 | - kNaiveJoinerAccepted = 1, | |
253 | - kResumeNetgameSavvyJoinerAccepted = 2, // build knows how to resume saved games as netgames | |
254 | - kFixedTagAndBallJoinerAccepted = 3, // build lacks multiple-ball-drop bug and tag-suicide bug | |
255 | - | |
256 | - // this should always be updated to match the current best (unless our build isn't up to spec) | |
257 | - kStateOfTheArtJoinerAccepted = kFixedTagAndBallJoinerAccepted | |
258 | -}; | |
259 | - | |
260 | -struct accept_gather_data { | |
261 | - uint8 accepted; | |
262 | - NetPlayer player; | |
263 | -}; | |
264 | - | |
265 | -enum { | |
266 | - _netscript_query_message, | |
267 | - _netscript_no_script_message, | |
268 | - _netscript_yes_script_message, | |
269 | - _netscript_script_intent_message | |
270 | -}; | |
271 | - | |
272 | -// Altering these constants requires changes to get_network_version(). - Woody | |
273 | -enum { | |
274 | - _hello_packet, | |
275 | - _joiner_info_packet, | |
276 | - _join_player_packet, | |
277 | - _accept_join_packet, | |
278 | - _topology_packet, | |
279 | - _stream_size_packet, | |
280 | - _stream_data_packet, | |
281 | - // ZZZ additions below | |
282 | - _chat_packet, | |
283 | - // The following should only be sent when get_network_version() >= kMinimumNetworkVersionForGracefulUnknownStreamPackets | |
284 | - _unknown_packet_type_response_packet, | |
285 | - _script_packet, | |
286 | - NUMBER_OF_BUFFERED_STREAM_PACKET_TYPES, | |
287 | - NUMBER_OF_STREAM_PACKET_TYPES= NUMBER_OF_BUFFERED_STREAM_PACKET_TYPES | |
288 | -}; | |
289 | - | |
290 | -/* ===== end of application specific data structures/enums */ | |
291 | - | |
292 | -class CommunicationsChannel; | |
293 | -class MessageDispatcher; | |
294 | -class MessageHandler; | |
295 | - | |
296 | -class Message; | |
297 | - | |
298 | - | |
299 | -const NetDistributionInfo* NetGetDistributionInfoForType(int16 inType); | |
300 | - | |
301 | -struct ClientChatInfo | |
302 | -{ | |
303 | - std::string name; | |
304 | - int16 color; | |
305 | - int16 team; | |
306 | -}; | |
307 | - | |
308 | -// "network_dialogs_private.h" follows | |
309 | - | |
310 | -class GathererAvailableAnnouncer | |
311 | -{ | |
312 | -public: | |
313 | - GathererAvailableAnnouncer(); | |
314 | - ~GathererAvailableAnnouncer(); | |
315 | - static void pump(); | |
316 | - | |
317 | -private: | |
318 | - SSLP_ServiceInstance mServiceInstance; | |
319 | -}; | |
320 | - | |
321 | - | |
322 | -class JoinerSeekingGathererAnnouncer | |
323 | -{ | |
324 | -public: | |
325 | - JoinerSeekingGathererAnnouncer(bool shouldSeek); | |
326 | - ~JoinerSeekingGathererAnnouncer(); | |
327 | - static void pump(); | |
328 | - | |
329 | -private: | |
330 | - static void found_gatherer_callback(const SSLP_ServiceInstance* instance); | |
331 | - static void lost_gatherer_callback(const SSLP_ServiceInstance* instance); | |
332 | - | |
333 | - bool mShouldSeek; | |
334 | -}; | |
335 | - | |
336 | - | |
337 | -#endif//NETWORK_PRIVATE_H | |
1 | +/* | |
2 | + * network_private.h | |
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 | + * This file holds stuff that various parts of the network subsystem want to see, but that portions | |
22 | + * of Aleph One outside of the networking code should not care about. | |
23 | + * | |
24 | + * Oct 11, 2001 (Woody Zenfell): split code away from network.cpp to create this file. | |
25 | + * Made any simple modifications needed to make things compile/work. | |
26 | + * | |
27 | + * Oct-Nov 2001 (Woody Zenfell): added code in support of text messages in stream packets. | |
28 | + * Added many comments. | |
29 | + | |
30 | +Feb 27, 2002 (Br'fin (Jeremy Parsons)): | |
31 | + Enabled SDL networking for Carbon without fully using SDL | |
32 | + | |
33 | + May 24, 2003 (Woody Zenfell): | |
34 | + Split ring-protocol-specific stuff out into RingGameProtocol.cpp; | |
35 | + "Unknown packet type response" streaming packet type; NetGetDistributionInfoForType() | |
36 | + */ | |
37 | + | |
38 | +#ifndef NETWORK_PRIVATE_H | |
39 | +#define NETWORK_PRIVATE_H | |
40 | + | |
41 | +#include "cstypes.h" | |
42 | + | |
43 | +#include "sdl_network.h" | |
44 | + | |
45 | +#include "network.h" | |
46 | + | |
47 | +// "network_dialogs_private.h" | |
48 | +#include "SSLP_API.h" | |
49 | + | |
50 | +#include <memory> | |
51 | + | |
52 | +#define GAME_PORT 4226 | |
53 | + | |
54 | +// (ZZZ:) Moved here from sdl_network.h and macintosh_network.h | |
55 | + | |
56 | +/* ---------- constants */ | |
57 | + | |
58 | +#define asyncUncompleted 1 /* ioResult value */ | |
59 | + | |
60 | +#define strNETWORK_ERRORS 132 | |
61 | +enum /* error string for user */ | |
62 | +{ | |
63 | + netErrCantAddPlayer, | |
64 | + netErrCouldntDistribute, | |
65 | + netErrCouldntJoin, | |
66 | + netErrServerCanceled, | |
67 | + netErrMapDistribFailed, | |
68 | + netErrWaitedTooLongForMap, | |
69 | + netErrSyncFailed, | |
70 | + netErrJoinFailed, | |
71 | + netErrCantContinue, | |
72 | + netErrIncompatibleVersion, | |
73 | + netErrGatheredPlayerUnacceptable, | |
74 | + netErrUngatheredPlayerUnacceptable, | |
75 | + netErrJoinerCantFindScenario, | |
76 | + netErrLostConnection, | |
77 | + netErrCouldntResolve, | |
78 | + netErrCouldntReceiveMap, | |
79 | + netWarnJoinerHasNoStar, | |
80 | + netWarnJoinerHasNoRing, | |
81 | + netWarnJoinerNoLua, | |
82 | + netErrMetaserverConnectionFailure, | |
83 | + netWarnCouldNotAdvertiseOnMetaserver, | |
84 | + netWarnUPnPConfigureFailed | |
85 | +}; | |
86 | + | |
87 | +// (ZZZ:) Moved here from network.cpp | |
88 | + | |
89 | +/* ---------- constants */ | |
90 | + | |
91 | +#define NET_DEAD_ACTION_FLAG_COUNT (-1) | |
92 | +#define NET_DEAD_ACTION_FLAG (NONE) | |
93 | + | |
94 | +#define MAXIMUM_GAME_DATA_SIZE 256 | |
95 | +#define MAXIMUM_PLAYER_DATA_SIZE 128 | |
96 | +#define MAXIMUM_UPDATES_PER_PACKET 16 // how many action flags per player can be sent in each ring packet | |
97 | +#define UPDATES_PER_PACKET 1 // defines action flags per packet and period of the ring | |
98 | +#define UPDATE_LATENCY 1 | |
99 | + | |
100 | +#define NET_QUEUE_SIZE (MAXIMUM_UPDATES_PER_PACKET+1) | |
101 | + | |
102 | +#define UNSYNC_TIMEOUT (3*MACHINE_TICKS_PER_SECOND) // 3 seconds | |
103 | + | |
104 | +#define STREAM_TRANSFER_CHUNK_SIZE (10000) | |
105 | +#define MAP_TRANSFER_TIME_OUT (MACHINE_TICKS_PER_SECOND*70) // 70 seconds to wait for map. | |
106 | +#define NET_SYNC_TIME_OUT (MACHINE_TICKS_PER_SECOND*50) // 50 seconds to time out of syncing. | |
107 | + | |
108 | +#define kACK_TIMEOUT 40 | |
109 | +#define kRETRIES 50 // how many timeouts allowed before dropping the next player | |
110 | + // kRETRIES * kACK_TIMEOUT / 1000 = timeout in seconds | |
111 | + | |
112 | +#define NUM_DISTRIBUTION_TYPES 3 | |
113 | + | |
114 | +// Altering constants below should make you alter get_network_version(). - Woody | |
115 | +#define kPROTOCOL_TYPE 69 | |
116 | + | |
117 | +enum /* tag */ | |
118 | +{ // ZZZ annotation: these (in NetPacketHeader) indicate the rest of the datagram is a NetPacket (i.e. a ring packet). | |
119 | + tagRING_PACKET, | |
120 | + tagACKNOWLEDGEMENT, | |
121 | + tagCHANGE_RING_PACKET, // to tell a player to change his downring address. also has action flags. | |
122 | + | |
123 | + // ZZZ annotation: these should only be found in streaming data (in a NetTopology). | |
124 | + tagNEW_PLAYER, | |
125 | + tagCANCEL_GAME, | |
126 | + tagSTART_GAME, | |
127 | + tagDROPPED_PLAYER, | |
128 | + tagCHANGED_PLAYER, | |
129 | + | |
130 | + // ZZZ annotation: these (in NetPacketHeader) indicate the rest of the datagram is a NetDistributionPacket. | |
131 | + tagLOSSY_DISTRIBUTION, // for transfer data other than action flags | |
132 | + tagLOSSLESS_DISTRIBUTION, // ditto, but currently unimplemented | |
133 | + | |
134 | + // ZZZ: more streaming data (topology) packet types | |
135 | + tagRESUME_GAME // ZZZ addition: trying to resume a saved-game rather than start a new netgame. | |
136 | +}; | |
137 | + | |
138 | +enum | |
139 | +{ | |
140 | + typeSYNC_RING_PACKET, // first packet of the game, gets everyone in the game | |
141 | + typeTIME_RING_PACKET, // second packet of the game, sets everyone's clock | |
142 | + typeNORMAL_RING_PACKET, // all the other packets of the game | |
143 | + | |
144 | + typeUNSYNC_RING_PACKET, // last packet of the game, get everyone unsynced properly. | |
145 | + typeDEAD_PACKET // This is simply a convenience for a switch. This packet never leaves the server. | |
146 | +}; | |
147 | + | |
148 | +/* ---------- structures */ | |
149 | + | |
150 | + | |
151 | +// (ZZZ:) Note ye well!!: if you alter these network-related structures, you are probably going to need to modify | |
152 | +// the corresponding _NET structures in network_data_formats.h AND *both* corresponding netcpy() functions in | |
153 | +// network_data_formats.cpp. AND, you'll probably need to alter get_network_version() while you're at it, | |
154 | +// since you've made an incompatible change to the network communication protocol. | |
155 | + | |
156 | +// (ZZZ:) Information passed in datagrams (note: the _NET version is ALWAYS the one sent/received on the wire. | |
157 | +// If not, it's a BUG. These are used to setup/extract data.) | |
158 | +struct NetPacketHeader | |
159 | +{ | |
160 | + int16 tag; | |
161 | + int32 sequence; | |
162 | + | |
163 | + /* data */ | |
164 | +}; | |
165 | +typedef struct NetPacketHeader NetPacketHeader, *NetPacketHeaderPtr; | |
166 | + | |
167 | +struct NetPacket | |
168 | +{ | |
169 | + uint8 ring_packet_type; // typeSYNC_RING_PACKET, etc... | |
170 | + uint8 server_player_index; | |
171 | + int32 server_net_time; | |
172 | + int16 required_action_flags; // handed down from on high (the server) | |
173 | + int16 action_flag_count[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS]; // how many each player actually has. | |
174 | + uint32 action_flags[1]; | |
175 | +}; | |
176 | +typedef struct NetPacket NetPacket, *NetPacketPtr; | |
177 | + | |
178 | +struct NetDistributionPacket | |
179 | +{ | |
180 | + int16 distribution_type; // type of information | |
181 | + int16 first_player_index; // who sent the information | |
182 | + int16 data_size; // how much they're sending. | |
183 | + uint8 data[2]; // the chunk ユo shit to send | |
184 | +}; | |
185 | +typedef struct NetDistributionPacket NetDistributionPacket, *NetDistributionPacketPtr; | |
186 | + | |
187 | +// Information passed in streams | |
188 | +struct NetPlayer | |
189 | +{ | |
190 | + NetAddrBlock dspAddress, ddpAddress; | |
191 | + | |
192 | + int16 identifier; | |
193 | + | |
194 | + int16 stream_id; // to remind gatherer how to contact joiners | |
195 | + | |
196 | + bool net_dead; // only valid if you are the server. | |
197 | + | |
198 | + //uint8 player_data[MAXIMUM_PLAYER_DATA_SIZE]; | |
199 | + player_info player_data; | |
200 | +}; | |
201 | +typedef struct NetPlayer NetPlayer, *NetPlayerPtr; | |
202 | + | |
203 | +struct NetTopology | |
204 | +{ | |
205 | + int16 tag; | |
206 | + int16 player_count; | |
207 | + | |
208 | + int16 nextIdentifier; | |
209 | + | |
210 | + //uint8 game_data[MAXIMUM_GAME_DATA_SIZE]; | |
211 | + game_info game_data; | |
212 | + | |
213 | + struct NetPlayer players[MAXIMUM_NUMBER_OF_NETWORK_PLAYERS]; | |
214 | +}; | |
215 | +typedef struct NetTopology NetTopology, *NetTopologyPtr; | |
216 | + | |
217 | +#ifdef NETWORK_CHAT | |
218 | +// (ZZZ addition) | |
219 | +enum { CHAT_MESSAGE_TEXT_BUFFER_SIZE = 250 }; | |
220 | + | |
221 | +struct NetChatMessage { | |
222 | + int16 sender_identifier; | |
223 | + char text[CHAT_MESSAGE_TEXT_BUFFER_SIZE]; | |
224 | +}; | |
225 | +#endif | |
226 | + | |
227 | + | |
228 | +// ZZZ: same here (should be safe to alter) | |
229 | +struct NetDistributionInfo | |
230 | +{ | |
231 | + bool lossy; | |
232 | + NetDistributionProc distribution_proc; | |
233 | +}; | |
234 | + | |
235 | +typedef struct NetDistributionInfo NetDistributionInfo, *NetDistributionInfoPtr; | |
236 | + | |
237 | +#define errInvalidMapPacket (42) | |
238 | +// ZZZ: taking a cue... used when trying to gather a player whose A1 doesn't support all the features we need. | |
239 | +#define errPlayerTooNaive (43) | |
240 | + | |
241 | +/* ===== application specific data structures/enums */ | |
242 | + | |
243 | +// Information sent via streaming protocol - warning above still applies! | |
244 | +struct gather_player_data { | |
245 | + int16 new_local_player_identifier; | |
246 | +}; | |
247 | + | |
248 | +// used in accept_gather_data::accepted - this is a sneaky way of detecting whether | |
249 | +// we're playing with a resume netgame-capable player or not. (Old code always sent | |
250 | +// 1 on accept, never 2; old code interprets any nonzero 'accepted' as an accept.) | |
251 | +enum { | |
252 | + kNaiveJoinerAccepted = 1, | |
253 | + kResumeNetgameSavvyJoinerAccepted = 2, // build knows how to resume saved games as netgames | |
254 | + kFixedTagAndBallJoinerAccepted = 3, // build lacks multiple-ball-drop bug and tag-suicide bug | |
255 | + | |
256 | + // this should always be updated to match the current best (unless our build isn't up to spec) | |
257 | + kStateOfTheArtJoinerAccepted = kFixedTagAndBallJoinerAccepted | |
258 | +}; | |
259 | + | |
260 | +struct accept_gather_data { | |
261 | + uint8 accepted; | |
262 | + NetPlayer player; | |
263 | +}; | |
264 | + | |
265 | +enum { | |
266 | + _netscript_query_message, | |
267 | + _netscript_no_script_message, | |
268 | + _netscript_yes_script_message, | |
269 | + _netscript_script_intent_message | |
270 | +}; | |
271 | + | |
272 | +// Altering these constants requires changes to get_network_version(). - Woody | |
273 | +enum { | |
274 | + _hello_packet, | |
275 | + _joiner_info_packet, | |
276 | + _join_player_packet, | |
277 | + _accept_join_packet, | |
278 | + _topology_packet, | |
279 | + _stream_size_packet, | |
280 | + _stream_data_packet, | |
281 | + // ZZZ additions below | |
282 | + _chat_packet, | |
283 | + // The following should only be sent when get_network_version() >= kMinimumNetworkVersionForGracefulUnknownStreamPackets | |
284 | + _unknown_packet_type_response_packet, | |
285 | + _script_packet, | |
286 | + NUMBER_OF_BUFFERED_STREAM_PACKET_TYPES, | |
287 | + NUMBER_OF_STREAM_PACKET_TYPES= NUMBER_OF_BUFFERED_STREAM_PACKET_TYPES | |
288 | +}; | |
289 | + | |
290 | +/* ===== end of application specific data structures/enums */ | |
291 | + | |
292 | +class CommunicationsChannel; | |
293 | +class MessageDispatcher; | |
294 | +class MessageHandler; | |
295 | + | |
296 | +class Message; | |
297 | + | |
298 | + | |
299 | +const NetDistributionInfo* NetGetDistributionInfoForType(int16 inType); | |
300 | + | |
301 | +struct ClientChatInfo | |
302 | +{ | |
303 | + std::string name; | |
304 | + int16 color; | |
305 | + int16 team; | |
306 | +}; | |
307 | + | |
308 | +// "network_dialogs_private.h" follows | |
309 | + | |
310 | +class GathererAvailableAnnouncer | |
311 | +{ | |
312 | +public: | |
313 | + GathererAvailableAnnouncer(); | |
314 | + ~GathererAvailableAnnouncer(); | |
315 | + static void pump(); | |
316 | + | |
317 | +private: | |
318 | + SSLP_ServiceInstance mServiceInstance; | |
319 | +}; | |
320 | + | |
321 | + | |
322 | +class JoinerSeekingGathererAnnouncer | |
323 | +{ | |
324 | +public: | |
325 | + JoinerSeekingGathererAnnouncer(bool shouldSeek); | |
326 | + ~JoinerSeekingGathererAnnouncer(); | |
327 | + static void pump(); | |
328 | + | |
329 | +private: | |
330 | + static void found_gatherer_callback(const SSLP_ServiceInstance* instance); | |
331 | + static void lost_gatherer_callback(const SSLP_ServiceInstance* instance); | |
332 | + | |
333 | + bool mShouldSeek; | |
334 | +}; | |
335 | + | |
336 | + | |
337 | +#endif//NETWORK_PRIVATE_H |
@@ -1,97 +1,97 @@ | ||
1 | -#ifndef __CONNECTPOOL_H | |
2 | -#define __CONNECTPOOL_H | |
3 | - | |
4 | -/* | |
5 | - | |
6 | - Copyright (C) 2007 Gregory Smith. | |
7 | - and the "Aleph One" developers. | |
8 | - | |
9 | - This program is free software; you can redistribute it and/or modify | |
10 | - it under the terms of the GNU General Public License as published by | |
11 | - the Free Software Foundation; either version 3 of the License, or | |
12 | - (at your option) any later version. | |
13 | - | |
14 | - This program is distributed in the hope that it will be useful, | |
15 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | - GNU General Public License for more details. | |
18 | - | |
19 | - This license is contained in the file "COPYING", | |
20 | - which is included with this source code; it is available online at | |
21 | - http://www.gnu.org/licenses/gpl.html | |
22 | - | |
23 | - A pool for non-blocking outbound TCP connections | |
24 | - | |
25 | -*/ | |
26 | - | |
27 | -#include "cseries.h" | |
28 | -#include "CommunicationsChannel.h" | |
29 | -#include <string> | |
30 | -#include <memory> | |
31 | -#include <SDL_thread.h> | |
32 | - | |
33 | -class NonblockingConnect | |
34 | -{ | |
35 | -public: | |
36 | - NonblockingConnect(const std::string& address, uint16 port); | |
37 | - NonblockingConnect(const IPaddress& ip); | |
38 | - ~NonblockingConnect(); | |
39 | - | |
40 | - enum Status | |
41 | - { | |
42 | - Connecting, | |
43 | - Connected, | |
44 | - ResolutionFailed, | |
45 | - ConnectFailed | |
46 | - }; | |
47 | - | |
48 | - Status status() { return m_status; } | |
49 | - bool done() { return m_status != Connecting; } | |
50 | - const IPaddress& address() { | |
51 | - assert(m_status != Connecting && m_status != ResolutionFailed); | |
52 | - return m_ip; | |
53 | - } | |
54 | - | |
55 | - CommunicationsChannel* release() { | |
56 | - assert(m_status == Connected); | |
57 | - return m_channel.release(); | |
58 | - } | |
59 | - | |
60 | - | |
61 | -private: | |
62 | - void connect(); | |
63 | - std::auto_ptr<CommunicationsChannel> m_channel; | |
64 | - Status m_status; | |
65 | - | |
66 | - std::string m_address; | |
67 | - uint16 m_port; | |
68 | - | |
69 | - bool m_ipSpecified; | |
70 | - IPaddress m_ip; | |
71 | - | |
72 | - int Thread(); | |
73 | - static int connect_thread(void *); | |
74 | - SDL_Thread *m_thread; | |
75 | -}; | |
76 | - | |
77 | - | |
78 | -class ConnectPool | |
79 | -{ | |
80 | -public: | |
81 | - static ConnectPool *instance() { if (!m_instance) m_instance = new ConnectPool(); return m_instance; } | |
82 | - NonblockingConnect* connect(const std::string& address, uint16 port); | |
83 | - NonblockingConnect* connect(const IPaddress& ip); | |
84 | - void abandon(NonblockingConnect*); | |
85 | - ~ConnectPool(); | |
86 | - | |
87 | -private: | |
88 | - ConnectPool(); | |
89 | - void fast_free(); | |
90 | - enum { kPoolSize = 20 }; | |
91 | - // second is false if we are in use! | |
92 | - std::pair<NonblockingConnect *, bool> m_pool[kPoolSize]; | |
93 | - | |
94 | - static ConnectPool* m_instance; | |
95 | -}; | |
96 | - | |
97 | -#endif | |
1 | +#ifndef __CONNECTPOOL_H | |
2 | +#define __CONNECTPOOL_H | |
3 | + | |
4 | +/* | |
5 | + | |
6 | + Copyright (C) 2007 Gregory Smith. | |
7 | + and the "Aleph One" developers. | |
8 | + | |
9 | + This program is free software; you can redistribute it and/or modify | |
10 | + it under the terms of the GNU General Public License as published by | |
11 | + the Free Software Foundation; either version 3 of the License, or | |
12 | + (at your option) any later version. | |
13 | + | |
14 | + This program is distributed in the hope that it will be useful, | |
15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + GNU General Public License for more details. | |
18 | + | |
19 | + This license is contained in the file "COPYING", | |
20 | + which is included with this source code; it is available online at | |
21 | + http://www.gnu.org/licenses/gpl.html | |
22 | + | |
23 | + A pool for non-blocking outbound TCP connections | |
24 | + | |
25 | +*/ | |
26 | + | |
27 | +#include "cseries.h" | |
28 | +#include "CommunicationsChannel.h" | |
29 | +#include <string> | |
30 | +#include <memory> | |
31 | +#include <SDL_thread.h> | |
32 | + | |
33 | +class NonblockingConnect | |
34 | +{ | |
35 | +public: | |
36 | + NonblockingConnect(const std::string& address, uint16 port); | |
37 | + NonblockingConnect(const IPaddress& ip); | |
38 | + ~NonblockingConnect(); | |
39 | + | |
40 | + enum Status | |
41 | + { | |
42 | + Connecting, | |
43 | + Connected, | |
44 | + ResolutionFailed, | |
45 | + ConnectFailed | |
46 | + }; | |
47 | + | |
48 | + Status status() { return m_status; } | |
49 | + bool done() { return m_status != Connecting; } | |
50 | + const IPaddress& address() { | |
51 | + assert(m_status != Connecting && m_status != ResolutionFailed); | |
52 | + return m_ip; | |
53 | + } | |
54 | + | |
55 | + CommunicationsChannel* release() { | |
56 | + assert(m_status == Connected); | |
57 | + return m_channel.release(); | |
58 | + } | |
59 | + | |
60 | + | |
61 | +private: | |
62 | + void connect(); | |
63 | + std::auto_ptr<CommunicationsChannel> m_channel; | |
64 | + Status m_status; | |
65 | + | |
66 | + std::string m_address; | |
67 | + uint16 m_port; | |
68 | + | |
69 | + bool m_ipSpecified; | |
70 | + IPaddress m_ip; | |
71 | + | |
72 | + int Thread(); | |
73 | + static int connect_thread(void *); | |
74 | + SDL_Thread *m_thread; | |
75 | +}; | |
76 | + | |
77 | + | |
78 | +class ConnectPool | |
79 | +{ | |
80 | +public: | |
81 | + static ConnectPool *instance() { if (!m_instance) m_instance = new ConnectPool(); return m_instance; } | |
82 | + NonblockingConnect* connect(const std::string& address, uint16 port); | |
83 | + NonblockingConnect* connect(const IPaddress& ip); | |
84 | + void abandon(NonblockingConnect*); | |
85 | + ~ConnectPool(); | |
86 | + | |
87 | +private: | |
88 | + ConnectPool(); | |
89 | + void fast_free(); | |
90 | + enum { kPoolSize = 20 }; | |
91 | + // second is false if we are in use! | |
92 | + std::pair<NonblockingConnect *, bool> m_pool[kPoolSize]; | |
93 | + | |
94 | + static ConnectPool* m_instance; | |
95 | +}; | |
96 | + | |
97 | +#endif |
@@ -1,119 +1,119 @@ | ||
1 | -/* | |
2 | - * network_speaker_shared.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Network speaker-related code usable by multiple platforms. | |
29 | - * | |
30 | - * Created by woody Feb 1, 2003, largely from stuff in network_speaker_sdl.cpp. | |
31 | - * | |
32 | - * May 28, 2003 (Gregory Smith): | |
33 | - * Speex audio decompression | |
34 | - */ | |
35 | - | |
36 | -#if !defined(DISABLE_NETWORKING) | |
37 | - | |
38 | -#include "cseries.h" | |
39 | -#include "network_sound.h" | |
40 | -#include "network_data_formats.h" | |
41 | -#include "network_audio_shared.h" | |
42 | -#include "player.h" | |
43 | -#include "shell.h" // screen_print | |
44 | - | |
45 | -#ifdef SPEEX | |
46 | -#include "speex/speex.h" | |
47 | -#include "network_speex.h" | |
48 | -#endif | |
49 | - | |
50 | -#include <set> | |
51 | - | |
52 | -static std::set<short> sIgnoredPlayers; | |
53 | - | |
54 | -// This is what the network distribution system calls when audio is received. | |
55 | -void | |
56 | -received_network_audio_proc(void *buffer, short buffer_size, short player_index) { | |
57 | - | |
58 | - if (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end()) return; | |
59 | - | |
60 | - network_audio_header_NET* theHeader_NET = (network_audio_header_NET*) buffer; | |
61 | - | |
62 | - network_audio_header theHeader; | |
63 | - | |
64 | - netcpy(&theHeader, theHeader_NET); | |
65 | - | |
66 | - byte* theSoundData = ((byte*)buffer) + sizeof(network_audio_header_NET); | |
67 | - | |
68 | - // 0 if using uncompressed audio, 1 if using speex | |
69 | - if(!(theHeader.mFlags & kNetworkAudioForTeammatesOnlyFlag) || (local_player->team == get_player_data(player_index)->team)) | |
70 | - { | |
71 | -#ifdef SPEEX | |
72 | - if (theHeader.mReserved == 1) | |
73 | - { | |
74 | - | |
75 | - // decode the data | |
76 | - const int max_frames = 2048 / 160; | |
77 | - static int16 frames[max_frames][160]; | |
78 | - int nbytes; | |
79 | - | |
80 | - int numFrames = 0; | |
81 | - while (theSoundData < static_cast<byte*>(buffer) + buffer_size && | |
82 | - numFrames < max_frames) | |
83 | - { | |
84 | - // decode a frame | |
85 | - nbytes = *theSoundData++; | |
86 | - | |
87 | - speex_bits_read_from(&gDecoderBits, (char *) theSoundData, nbytes); | |
88 | - speex_decode_int(gDecoderState, &gDecoderBits, frames[numFrames]); | |
89 | - | |
90 | - numFrames++; | |
91 | - theSoundData += nbytes; | |
92 | - } | |
93 | - | |
94 | - queue_network_speaker_data((byte *) frames[0], 160 * 2 * numFrames); | |
95 | - } | |
96 | -#endif | |
97 | - } | |
98 | -} | |
99 | - | |
100 | -void mute_player_mic(short player_index) | |
101 | -{ | |
102 | - if (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end()) | |
103 | - { | |
104 | - screen_printf("removing player %i's mic from the ignore list", player_index); | |
105 | - sIgnoredPlayers.erase(player_index); | |
106 | - } | |
107 | - else | |
108 | - { | |
109 | - screen_printf("adding player %i's mic to the ignore list", player_index); | |
110 | - sIgnoredPlayers.insert(player_index); | |
111 | - } | |
112 | -} | |
113 | - | |
114 | -void clear_player_mic_mutes() | |
115 | -{ | |
116 | - sIgnoredPlayers.clear(); | |
117 | -} | |
118 | - | |
119 | -#endif // !defined(DISABLE_NETWORKING) | |
1 | +/* | |
2 | + * network_speaker_shared.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Network speaker-related code usable by multiple platforms. | |
29 | + * | |
30 | + * Created by woody Feb 1, 2003, largely from stuff in network_speaker_sdl.cpp. | |
31 | + * | |
32 | + * May 28, 2003 (Gregory Smith): | |
33 | + * Speex audio decompression | |
34 | + */ | |
35 | + | |
36 | +#if !defined(DISABLE_NETWORKING) | |
37 | + | |
38 | +#include "cseries.h" | |
39 | +#include "network_sound.h" | |
40 | +#include "network_data_formats.h" | |
41 | +#include "network_audio_shared.h" | |
42 | +#include "player.h" | |
43 | +#include "shell.h" // screen_print | |
44 | + | |
45 | +#ifdef SPEEX | |
46 | +#include "speex/speex.h" | |
47 | +#include "network_speex.h" | |
48 | +#endif | |
49 | + | |
50 | +#include <set> | |
51 | + | |
52 | +static std::set<short> sIgnoredPlayers; | |
53 | + | |
54 | +// This is what the network distribution system calls when audio is received. | |
55 | +void | |
56 | +received_network_audio_proc(void *buffer, short buffer_size, short player_index) { | |
57 | + | |
58 | + if (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end()) return; | |
59 | + | |
60 | + network_audio_header_NET* theHeader_NET = (network_audio_header_NET*) buffer; | |
61 | + | |
62 | + network_audio_header theHeader; | |
63 | + | |
64 | + netcpy(&theHeader, theHeader_NET); | |
65 | + | |
66 | + byte* theSoundData = ((byte*)buffer) + sizeof(network_audio_header_NET); | |
67 | + | |
68 | + // 0 if using uncompressed audio, 1 if using speex | |
69 | + if(!(theHeader.mFlags & kNetworkAudioForTeammatesOnlyFlag) || (local_player->team == get_player_data(player_index)->team)) | |
70 | + { | |
71 | +#ifdef SPEEX | |
72 | + if (theHeader.mReserved == 1) | |
73 | + { | |
74 | + | |
75 | + // decode the data | |
76 | + const int max_frames = 2048 / 160; | |
77 | + static int16 frames[max_frames][160]; | |
78 | + int nbytes; | |
79 | + | |
80 | + int numFrames = 0; | |
81 | + while (theSoundData < static_cast<byte*>(buffer) + buffer_size && | |
82 | + numFrames < max_frames) | |
83 | + { | |
84 | + // decode a frame | |
85 | + nbytes = *theSoundData++; | |
86 | + | |
87 | + speex_bits_read_from(&gDecoderBits, (char *) theSoundData, nbytes); | |
88 | + speex_decode_int(gDecoderState, &gDecoderBits, frames[numFrames]); | |
89 | + | |
90 | + numFrames++; | |
91 | + theSoundData += nbytes; | |
92 | + } | |
93 | + | |
94 | + queue_network_speaker_data((byte *) frames[0], 160 * 2 * numFrames); | |
95 | + } | |
96 | +#endif | |
97 | + } | |
98 | +} | |
99 | + | |
100 | +void mute_player_mic(short player_index) | |
101 | +{ | |
102 | + if (sIgnoredPlayers.find(player_index) != sIgnoredPlayers.end()) | |
103 | + { | |
104 | + screen_printf("removing player %i's mic from the ignore list", player_index); | |
105 | + sIgnoredPlayers.erase(player_index); | |
106 | + } | |
107 | + else | |
108 | + { | |
109 | + screen_printf("adding player %i's mic to the ignore list", player_index); | |
110 | + sIgnoredPlayers.insert(player_index); | |
111 | + } | |
112 | +} | |
113 | + | |
114 | +void clear_player_mic_mutes() | |
115 | +{ | |
116 | + sIgnoredPlayers.clear(); | |
117 | +} | |
118 | + | |
119 | +#endif // !defined(DISABLE_NETWORKING) |
@@ -1,59 +1,59 @@ | ||
1 | -/* | |
2 | - * network_microphone_sdl_dummy.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Dummy implementation of SDL-style network microphone. | |
29 | - * Does nothing except satisfy the linker (and admit to anyone interested it's a poseur). | |
30 | - * | |
31 | - * Created by woody March 9, 2002. | |
32 | - */ | |
33 | - | |
34 | -void | |
35 | -open_network_microphone() { | |
36 | - // Nothing | |
37 | -} | |
38 | - | |
39 | -void | |
40 | -close_network_microphone() { | |
41 | - // Nothing | |
42 | -} | |
43 | - | |
44 | -void | |
45 | -set_network_microphone_state(bool inActive) { | |
46 | - // Nothing | |
47 | - (void) (inActive); | |
48 | -} | |
49 | - | |
50 | -bool | |
51 | -is_network_microphone_implemented() { | |
52 | - // No! You'll get nothing useful from us! | |
53 | - return false; | |
54 | -} | |
55 | - | |
56 | -void | |
57 | -network_microphone_idle_proc() { | |
58 | - // Nothing | |
59 | -} | |
1 | +/* | |
2 | + * network_microphone_sdl_dummy.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Dummy implementation of SDL-style network microphone. | |
29 | + * Does nothing except satisfy the linker (and admit to anyone interested it's a poseur). | |
30 | + * | |
31 | + * Created by woody March 9, 2002. | |
32 | + */ | |
33 | + | |
34 | +void | |
35 | +open_network_microphone() { | |
36 | + // Nothing | |
37 | +} | |
38 | + | |
39 | +void | |
40 | +close_network_microphone() { | |
41 | + // Nothing | |
42 | +} | |
43 | + | |
44 | +void | |
45 | +set_network_microphone_state(bool inActive) { | |
46 | + // Nothing | |
47 | + (void) (inActive); | |
48 | +} | |
49 | + | |
50 | +bool | |
51 | +is_network_microphone_implemented() { | |
52 | + // No! You'll get nothing useful from us! | |
53 | + return false; | |
54 | +} | |
55 | + | |
56 | +void | |
57 | +network_microphone_idle_proc() { | |
58 | + // Nothing | |
59 | +} |
@@ -1,61 +1,61 @@ | ||
1 | -/* | |
2 | - * network_lookup_sdl.h - SDL network lookup stuff | |
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 | - * Sept-Nov 2001 (Woody Zenfell): split some prototypes out of sdl_network.h to localize | |
22 | - * changes and reduce recompilation | |
23 | - * | |
24 | - * Sept-Nov 2001 (Woody Zenfell): retooled (weakened) this interface to expose more of the | |
25 | - * underlying SSLP implementation, to make things a little simpler | |
26 | - */ | |
27 | - | |
28 | -#ifndef NETWORK_LOOKUP_SDL_H | |
29 | -#define NETWORK_LOOKUP_SDL_H | |
30 | - | |
31 | -#include "SSLP_API.h" | |
32 | - | |
33 | -/* ---------- prototypes/NETWORK_NAMES.C */ | |
34 | - | |
35 | -// ZZZ: added support for SSLP hinting | |
36 | -OSErr NetRegisterName(const unsigned char *name, const unsigned char *type, | |
37 | - short version, short socketNumber, const char* hint_addr_string); | |
38 | -OSErr NetUnRegisterName(void); | |
39 | - | |
40 | -/* ---------- prototypes/NETWORK_LOOKUP.C */ | |
41 | - | |
42 | -// Now unused - as long as SSLP_Pump() is called, e.g. by the dialog, it's unnecessary. | |
43 | -//void NetLookupUpdate(void); | |
44 | - | |
45 | -void NetLookupClose(void); | |
46 | - | |
47 | -// ZZZ: changed this interface to be more SSLP- and SDL-dialog-friendly | |
48 | -// I have renamed it since its interface has changed quite a bit - if this is a bad idea, | |
49 | -// feel free to name it back. | |
50 | -// Note it still takes a Pstring for maximum compatibility with MacOS version. | |
51 | -OSErr NetLookupOpen_SSLP(const unsigned char *type, short version, | |
52 | - SSLP_Service_Instance_Status_Changed_Callback foundInstance, | |
53 | - SSLP_Service_Instance_Status_Changed_Callback lostInstance, | |
54 | - SSLP_Service_Instance_Status_Changed_Callback nameChanged | |
55 | -); | |
56 | - | |
57 | -// Now unused - functionality handled in w_found_players widget type | |
58 | -//void NetLookupRemove(short index); | |
59 | -//void NetLookupInformation(short index, NetAddrBlock *address, NetEntityName *entity); | |
60 | - | |
61 | -#endif//NETWORK_LOOKUP_SDL_H | |
1 | +/* | |
2 | + * network_lookup_sdl.h - SDL network lookup stuff | |
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 | + * Sept-Nov 2001 (Woody Zenfell): split some prototypes out of sdl_network.h to localize | |
22 | + * changes and reduce recompilation | |
23 | + * | |
24 | + * Sept-Nov 2001 (Woody Zenfell): retooled (weakened) this interface to expose more of the | |
25 | + * underlying SSLP implementation, to make things a little simpler | |
26 | + */ | |
27 | + | |
28 | +#ifndef NETWORK_LOOKUP_SDL_H | |
29 | +#define NETWORK_LOOKUP_SDL_H | |
30 | + | |
31 | +#include "SSLP_API.h" | |
32 | + | |
33 | +/* ---------- prototypes/NETWORK_NAMES.C */ | |
34 | + | |
35 | +// ZZZ: added support for SSLP hinting | |
36 | +OSErr NetRegisterName(const unsigned char *name, const unsigned char *type, | |
37 | + short version, short socketNumber, const char* hint_addr_string); | |
38 | +OSErr NetUnRegisterName(void); | |
39 | + | |
40 | +/* ---------- prototypes/NETWORK_LOOKUP.C */ | |
41 | + | |
42 | +// Now unused - as long as SSLP_Pump() is called, e.g. by the dialog, it's unnecessary. | |
43 | +//void NetLookupUpdate(void); | |
44 | + | |
45 | +void NetLookupClose(void); | |
46 | + | |
47 | +// ZZZ: changed this interface to be more SSLP- and SDL-dialog-friendly | |
48 | +// I have renamed it since its interface has changed quite a bit - if this is a bad idea, | |
49 | +// feel free to name it back. | |
50 | +// Note it still takes a Pstring for maximum compatibility with MacOS version. | |
51 | +OSErr NetLookupOpen_SSLP(const unsigned char *type, short version, | |
52 | + SSLP_Service_Instance_Status_Changed_Callback foundInstance, | |
53 | + SSLP_Service_Instance_Status_Changed_Callback lostInstance, | |
54 | + SSLP_Service_Instance_Status_Changed_Callback nameChanged | |
55 | +); | |
56 | + | |
57 | +// Now unused - functionality handled in w_found_players widget type | |
58 | +//void NetLookupRemove(short index); | |
59 | +//void NetLookupInformation(short index, NetAddrBlock *address, NetEntityName *entity); | |
60 | + | |
61 | +#endif//NETWORK_LOOKUP_SDL_H |
@@ -1,57 +1,57 @@ | ||
1 | -/* | |
2 | - * StarGameProtocol.h | |
3 | - | |
4 | - Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | - * Created by Woody Zenfell, III on Sat May 17 2003. | |
22 | - * | |
23 | - * Interface between the star-topology game protocol module and the rest of the code. | |
24 | - */ | |
25 | - | |
26 | -#ifndef STARGAMEPROTOCOL_H | |
27 | -#define STARGAMEPROTOCOL_H | |
28 | - | |
29 | -#include "NetworkGameProtocol.h" | |
30 | - | |
31 | -#include <stdio.h> | |
32 | - | |
33 | -class XML_ElementParser; | |
34 | - | |
35 | -class StarGameProtocol : public NetworkGameProtocol | |
36 | -{ | |
37 | -public: | |
38 | - bool Enter(short* inNetStatePtr); | |
39 | - void Exit1(); | |
40 | - void Exit2(); | |
41 | - void DistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool only_send_to_team); | |
42 | - bool Sync(NetTopology* inTopology, int32 inSmallestGameTick, size_t inLocalPlayerIndex, size_t inServerPlayerIndex); | |
43 | - bool UnSync(bool inGraceful, int32 inSmallestPostgameTick); | |
44 | - int32 GetNetTime(); | |
45 | - void PacketHandler(DDPPacketBuffer* inPacket); | |
46 | - | |
47 | - static XML_ElementParser* GetParser(); | |
48 | - | |
49 | - int32 GetUnconfirmedActionFlagsCount(); | |
50 | - uint32 PeekUnconfirmedActionFlag(int32 offset); | |
51 | - void UpdateUnconfirmedActionFlags(); | |
52 | -}; | |
53 | - | |
54 | -extern void DefaultStarPreferences(); | |
55 | -extern void WriteStarPreferences(FILE* F); | |
56 | - | |
57 | -#endif // STARGAMEPROTOCOL_H | |
1 | +/* | |
2 | + * StarGameProtocol.h | |
3 | + | |
4 | + Copyright (C) 2003 and beyond by Woody Zenfell, III | |
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 | + * Created by Woody Zenfell, III on Sat May 17 2003. | |
22 | + * | |
23 | + * Interface between the star-topology game protocol module and the rest of the code. | |
24 | + */ | |
25 | + | |
26 | +#ifndef STARGAMEPROTOCOL_H | |
27 | +#define STARGAMEPROTOCOL_H | |
28 | + | |
29 | +#include "NetworkGameProtocol.h" | |
30 | + | |
31 | +#include <stdio.h> | |
32 | + | |
33 | +class XML_ElementParser; | |
34 | + | |
35 | +class StarGameProtocol : public NetworkGameProtocol | |
36 | +{ | |
37 | +public: | |
38 | + bool Enter(short* inNetStatePtr); | |
39 | + void Exit1(); | |
40 | + void Exit2(); | |
41 | + void DistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool only_send_to_team); | |
42 | + bool Sync(NetTopology* inTopology, int32 inSmallestGameTick, size_t inLocalPlayerIndex, size_t inServerPlayerIndex); | |
43 | + bool UnSync(bool inGraceful, int32 inSmallestPostgameTick); | |
44 | + int32 GetNetTime(); | |
45 | + void PacketHandler(DDPPacketBuffer* inPacket); | |
46 | + | |
47 | + static XML_ElementParser* GetParser(); | |
48 | + | |
49 | + int32 GetUnconfirmedActionFlagsCount(); | |
50 | + uint32 PeekUnconfirmedActionFlag(int32 offset); | |
51 | + void UpdateUnconfirmedActionFlags(); | |
52 | +}; | |
53 | + | |
54 | +extern void DefaultStarPreferences(); | |
55 | +extern void WriteStarPreferences(FILE* F); | |
56 | + | |
57 | +#endif // STARGAMEPROTOCOL_H |
@@ -586,7 +586,7 @@ | ||
586 | 586 | m_chatChoiceWidget->set_callback(boost::bind(&JoinDialog::chatChoiceHit, this)); |
587 | 587 | m_chatEntryWidget->set_callback(boost::bind(&JoinDialog::chatTextEntered, this, _1)); |
588 | 588 | |
589 | - getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string); | |
589 | + getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string); | |
590 | 590 | m_messagesWidget->set_text(temporary); |
591 | 591 | |
592 | 592 | CStringPref joinAddressPref (network_preferences->join_address, 255); |
@@ -659,7 +659,7 @@ | ||
659 | 659 | m_joinWidget->deactivate (); |
660 | 660 | m_joinMetaserverWidget->deactivate (); |
661 | 661 | |
662 | - getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string); | |
662 | + getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string); | |
663 | 663 | m_messagesWidget->set_text(temporary); |
664 | 664 | |
665 | 665 | if (!m_joinByAddressWidget->get_value()) { |
@@ -1,308 +1,308 @@ | ||
1 | -#ifndef __NETWORK_H | |
2 | -#define __NETWORK_H | |
3 | - | |
4 | -/* | |
5 | -NETWORK.H | |
6 | - | |
7 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | - and the "Aleph One" developers. | |
9 | - | |
10 | - This program is free software; you can redistribute it and/or modify | |
11 | - it under the terms of the GNU General Public License as published by | |
12 | - the Free Software Foundation; either version 3 of the License, or | |
13 | - (at your option) any later version. | |
14 | - | |
15 | - This program is distributed in the hope that it will be useful, | |
16 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | - GNU General Public License for more details. | |
19 | - | |
20 | - This license is contained in the file "COPYING", | |
21 | - which is included with this source code; it is available online at | |
22 | - http://www.gnu.org/licenses/gpl.html | |
23 | - | |
24 | -Tuesday, June 21, 1994 3:26:46 PM | |
25 | - | |
26 | - May 24, 2003 (Woody Zenfell): | |
27 | - compile-time constant MARATHON_NETWORK_VERSION replaced with runtime get_network_version() | |
28 | -*/ | |
29 | - | |
30 | -#include "cseries.h" | |
31 | -#include "cstypes.h" | |
32 | - | |
33 | -// This file should be used only for stuff that folks outside the network subsystem care about | |
34 | -// (i.e. it's the interface to the subsystem) | |
35 | - | |
36 | -// I'm tempted to slice the routines the network dialogs deal with away from those that the | |
37 | -// rest of the game deals with, but will leave that for another day. | |
38 | - | |
39 | -// unfortunately, this requires map.h because it needs "struct entry_point" | |
40 | - | |
41 | -#ifdef DEMO | |
42 | -#define MAXIMUM_NUMBER_OF_NETWORK_PLAYERS 2 | |
43 | -#else | |
44 | -#define MAXIMUM_NUMBER_OF_NETWORK_PLAYERS 8 | |
45 | -#endif | |
46 | - | |
47 | -#define MAX_LEVEL_NAME_LENGTH 64 | |
48 | - | |
49 | -// change this if you make a major change to the way the setup messages work | |
50 | -#define kNetworkSetupProtocolID "Aleph One WonderNAT V1" | |
51 | - | |
52 | -// ZZZ: there probably should be a published max size somewhere, but this isn't used anywhere; better | |
53 | -// not to pretend it's real. | |
54 | -//#define MAX_NET_DISTRIBUTION_BUFFER_SIZE 512 | |
55 | - | |
56 | -enum // base network speeds | |
57 | -{ | |
58 | - _appletalk_remote, // ARA | |
59 | - _localtalk, | |
60 | - _tokentalk, | |
61 | - _ethernet, | |
62 | -#ifdef USE_MODEM | |
63 | - _modem, | |
64 | -#endif | |
65 | - NUMBER_OF_NETWORK_TYPES | |
66 | -}; | |
67 | - | |
68 | -typedef struct game_info | |
69 | -{ | |
70 | - uint16 initial_random_seed; | |
71 | - int16 net_game_type; | |
72 | - int32 time_limit; | |
73 | - int16 kill_limit; | |
74 | - int16 game_options; | |
75 | - int16 difficulty_level; | |
76 | - bool server_is_playing; // if false, then observing | |
77 | - bool allow_mic; | |
78 | - | |
79 | - int16 cheat_flags; | |
80 | - | |
81 | - // where the game takes place | |
82 | - int16 level_number; | |
83 | - char level_name[MAX_LEVEL_NAME_LENGTH+1]; | |
84 | - uint32 parent_checksum; | |
85 | - | |
86 | - // network parameters | |
87 | - int16 initial_updates_per_packet; | |
88 | - int16 initial_update_latency; | |
89 | -} game_info; | |
90 | - | |
91 | -#define MAX_NET_PLAYER_NAME_LENGTH 32 | |
92 | -#define LONG_SERIAL_NUMBER_LENGTH 10 | |
93 | - | |
94 | -typedef struct player_info | |
95 | -{ | |
96 | - unsigned char name[MAX_NET_PLAYER_NAME_LENGTH+1]; | |
97 | - int16 desired_color; | |
98 | - int16 team; // from player.h | |
99 | - int16 color; | |
100 | - byte long_serial_number[LONG_SERIAL_NUMBER_LENGTH]; | |
101 | -} player_info; | |
102 | - | |
103 | - | |
104 | -struct prospective_joiner_info { | |
105 | - uint16 stream_id; | |
106 | - unsigned char name[MAX_NET_PLAYER_NAME_LENGTH]; | |
107 | - int16 color; | |
108 | - int16 team; | |
109 | - bool gathering; | |
110 | -}; | |
111 | - | |
112 | - | |
113 | - | |
114 | -/* ---------------- functions from network.c */ | |
115 | -enum /* message types passed to the userユs names lookup update procedure */ | |
116 | -{ | |
117 | - removeEntity, | |
118 | - insertEntity | |
119 | -}; | |
120 | - | |
121 | -class GatherCallbacks | |
122 | -{ | |
123 | - public: | |
124 | - virtual ~GatherCallbacks() { }; | |
125 | - | |
126 | - virtual void JoinSucceeded(const prospective_joiner_info *player) = 0; | |
127 | - virtual void JoiningPlayerDropped(const prospective_joiner_info *player) = 0; | |
128 | - virtual void JoinedPlayerDropped(const prospective_joiner_info *player) = 0; | |
129 | - virtual void JoinedPlayerChanged(const prospective_joiner_info *player) { }; | |
130 | -}; | |
131 | - | |
132 | -class ChatCallbacks | |
133 | -{ | |
134 | - public: | |
135 | - virtual ~ChatCallbacks() { }; | |
136 | - static void SendChatMessage(const std::string& message); | |
137 | - virtual void ReceivedMessageFromPlayer(const char *player_name, | |
138 | - const char *message) = 0; | |
139 | -}; | |
140 | - | |
141 | -class InGameChatCallbacks : public ChatCallbacks | |
142 | -{ | |
143 | - public: | |
144 | - ~InGameChatCallbacks() { } | |
145 | - static InGameChatCallbacks *instance(); | |
146 | - void ReceivedMessageFromPlayer(const char *player_name, const char *message); | |
147 | - | |
148 | - static std::string prompt(); | |
149 | - | |
150 | - private: | |
151 | - InGameChatCallbacks() { } | |
152 | - static InGameChatCallbacks *m_instance; | |
153 | -}; | |
154 | - | |
155 | - | |
156 | - | |
157 | -// ZZZ note: netPlayerAdded, netChatMessageReceived, and netStartingResumeGame are 'pseudo-states'; | |
158 | -// they are returned from NetUpdateJoinState() but will never be assigned to the actual "NetState()". | |
159 | -// ghs: netAwaitingHello isn't ever returned or assigned to netState | |
160 | -enum /* states */ | |
161 | -{ | |
162 | - netUninitialized, /* NetEnter() has not been called */ | |
163 | - netGathering, /* looking for players */ | |
164 | - netConnecting, /* trying to establish connection to gatherer */ | |
165 | - netJoining, /* waiting to be gathered */ | |
166 | - netWaiting, /* have been gathered, waiting for start message */ | |
167 | - netStartingUp, /* waiting for everyone to report (via NetSync) and begin queueing commands */ | |
168 | - netActive, /* in game */ | |
169 | - netComingDown, /* Coming down... */ | |
170 | - netDown, /* game over, waiting for new gather or join call */ | |
171 | - netCancelled, /* the game was just cancelled */ | |
172 | - netPlayerAdded, /* a new player was just added to the topology (will return to netWaiting) */ | |
173 | - netPlayerDropped, /* like netPlayerAdded */ | |
174 | - netPlayerChanged, /* like netPlayerAdded */ | |
175 | - netJoinErrorOccurred, | |
176 | - netChatMessageReceived, // ZZZ addition | |
177 | - netStartingResumeGame, // ZZZ addition: like netStartingUp, but starting a resume-game instead of a new game | |
178 | - netAwaitingHello // only used for handler state | |
179 | -}; | |
180 | - | |
181 | -/* -------- typedefs */ | |
182 | -// player index is the index of the player that is sending the information | |
183 | -typedef void (*NetDistributionProc)(void *buffer, short buffer_size, short player_index); | |
184 | -typedef void (*CheckPlayerProcPtr)(short player_index, short num_players); | |
185 | - | |
186 | -/* --------- prototypes/NETWORK.C */ | |
187 | -void NetSetGatherCallbacks(GatherCallbacks *gc); | |
188 | -void NetSetChatCallbacks(ChatCallbacks *cc); | |
189 | -bool NetEnter(); | |
190 | -void NetDoneGathering (void); | |
191 | -void NetExit(void); | |
192 | - | |
193 | -bool NetGather(void *game_data, short game_data_size, void *player_data, | |
194 | - short player_data_size, bool resuming_game); | |
195 | - | |
196 | -struct SSLP_ServiceInstance; | |
197 | - | |
198 | -enum { // NetGatherPlayer results | |
199 | - kGatherPlayerFailed, // generic | |
200 | - kGatherPlayerSuccessful, // generic | |
201 | - kGatheredUnacceptablePlayer // we had already committed to gathering this jimmy, | |
202 | - // but we can't start a game with him - upper-level code needs to make sure gathering is cancelled. | |
203 | -}; | |
204 | - | |
205 | -int NetGatherPlayer( | |
206 | -// ZZZ: in my formulation, player info is all passed along in one structure from the dialog here. | |
207 | -const prospective_joiner_info &player, | |
208 | -CheckPlayerProcPtr check_player); | |
209 | - | |
210 | -void NetHandleUngatheredPlayer(prospective_joiner_info ungathered_player); | |
211 | - | |
212 | -// jkvw: replaced SSLP hinting address with host address | |
213 | -bool NetGameJoin(void *player_data, short player_data_size, const char* host_address_string); | |
214 | - | |
215 | -bool NetCheckForNewJoiner (prospective_joiner_info &info); | |
216 | -short NetUpdateJoinState(void); | |
217 | -void NetCancelJoin(void); | |
218 | - | |
219 | -// ask to change color and team; it's up to the gatherer to update the topo | |
220 | -// usually he'll change your team, if the color's free you'll get that too | |
221 | -void NetChangeColors(int16 color, int16 team); | |
222 | - | |
223 | -// ghs: these are obsolete, I'll get rid of them when I'm sure I won't want | |
224 | -// to refer back to them | |
225 | - | |
226 | -// ZZZ addition - pre-game/(eventually) postgame chat | |
227 | -// Returns true if there was a pending message. | |
228 | -// Returns pointer to chat text. | |
229 | -// Returns pointer to sending player's data (does not copy player data). | |
230 | -// Data returned in pointers is only good until the next call to NetUpdateJoinState or NetCheckForIncomingMessages. | |
231 | -bool NetGetMostRecentChatMessage(player_info** outSendingPlayerData, char** outMessage); | |
232 | - | |
233 | -// Gatherer should use this to send out his messages or to broadcast a message received from a joiner | |
234 | -OSErr NetDistributeChatMessage(short sender_identifier, const char* message); | |
235 | - | |
236 | -void NetProcessMessagesInGame(); | |
237 | - | |
238 | -short NetGetLocalPlayerIndex(void); | |
239 | -short NetGetPlayerIdentifier(short player_index); | |
240 | - | |
241 | -bool NetNumberOfPlayerIsValid(void); | |
242 | -short NetGetNumberOfPlayers(void); | |
243 | - | |
244 | -void *NetGetPlayerData(short player_index); | |
245 | -void *NetGetGameData(void); | |
246 | - | |
247 | -struct player_start_data; | |
248 | -// Gatherer may call this once after all players are gathered but before NetStart() | |
249 | -void NetSetupTopologyFromStarts(const player_start_data* inStartArray, short inStartCount); | |
250 | - | |
251 | -void NetSetInitialParameters(short updates_per_packet, short update_latency); | |
252 | - | |
253 | -bool NetSync(void); | |
254 | -bool NetUnSync(void); | |
255 | - | |
256 | -bool NetStart(void); | |
257 | -void NetCancelGather(void); | |
258 | - | |
259 | -int32 NetGetNetTime(void); | |
260 | - | |
261 | -bool NetChangeMap(struct entry_point *entry); | |
262 | -OSErr NetDistributeGameDataToAllPlayers(byte* wad_buffer, int32 wad_length, bool do_physics); | |
263 | -byte* NetReceiveGameData(bool do_physics); | |
264 | - | |
265 | -void DeferredScriptSend (byte* data, size_t length); | |
266 | -void SetNetscriptStatus (bool status); | |
267 | - | |
268 | -void display_net_game_stats(void); | |
269 | - | |
270 | -// ZZZ change: caller specifies int16 ID for distribution type. Unknown types (when received) are | |
271 | -// passed along but ignored. Uses an STL 'map' so ID's need not be consecutive or in any particular | |
272 | -// sub-range. | |
273 | -void NetAddDistributionFunction(int16 type, NetDistributionProc proc, bool lossy); | |
274 | -void NetDistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool send_only_to_team = false); | |
275 | -void NetRemoveDistributionFunction(short type); | |
276 | - | |
277 | -// disable "cheats" | |
278 | -bool NetAllowCrosshair(); | |
279 | -bool NetAllowOverlayMap(); | |
280 | -bool NetAllowTunnelVision(); | |
281 | -bool NetAllowBehindview(); | |
282 | -bool NetAllowCarnageMessages(); | |
283 | -bool NetAllowSavingLevel(); | |
284 | - | |
285 | -// spoke stuf | |
286 | - | |
287 | -int32 NetGetUnconfirmedActionFlagsCount(); // how many flags can we use for prediction? | |
288 | -uint32 NetGetUnconfirmedActionFlag(int32 offset); // offset < GetUnconfirmedActionFlagsCount | |
289 | -void NetUpdateUnconfirmedActionFlags(); | |
290 | - | |
291 | -struct NetworkStats | |
292 | -{ | |
293 | - enum { | |
294 | - invalid = -1, | |
295 | - disconnected = -2, | |
296 | - }; | |
297 | - | |
298 | - int16 latency; | |
299 | - int16 jitter; | |
300 | - uint16 errors; | |
301 | -}; | |
302 | - | |
303 | -// returns latency in ms, or kNetLatencyInvalid or kNetLatencyDisconnected | |
304 | -int32 NetGetLatency(); | |
305 | - | |
306 | -const NetworkStats& NetGetStats(int player_index); | |
307 | - | |
308 | -#endif | |
1 | +#ifndef __NETWORK_H | |
2 | +#define __NETWORK_H | |
3 | + | |
4 | +/* | |
5 | +NETWORK.H | |
6 | + | |
7 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | + and the "Aleph One" developers. | |
9 | + | |
10 | + This program is free software; you can redistribute it and/or modify | |
11 | + it under the terms of the GNU General Public License as published by | |
12 | + the Free Software Foundation; either version 3 of the License, or | |
13 | + (at your option) any later version. | |
14 | + | |
15 | + This program is distributed in the hope that it will be useful, | |
16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + GNU General Public License for more details. | |
19 | + | |
20 | + This license is contained in the file "COPYING", | |
21 | + which is included with this source code; it is available online at | |
22 | + http://www.gnu.org/licenses/gpl.html | |
23 | + | |
24 | +Tuesday, June 21, 1994 3:26:46 PM | |
25 | + | |
26 | + May 24, 2003 (Woody Zenfell): | |
27 | + compile-time constant MARATHON_NETWORK_VERSION replaced with runtime get_network_version() | |
28 | +*/ | |
29 | + | |
30 | +#include "cseries.h" | |
31 | +#include "cstypes.h" | |
32 | + | |
33 | +// This file should be used only for stuff that folks outside the network subsystem care about | |
34 | +// (i.e. it's the interface to the subsystem) | |
35 | + | |
36 | +// I'm tempted to slice the routines the network dialogs deal with away from those that the | |
37 | +// rest of the game deals with, but will leave that for another day. | |
38 | + | |
39 | +// unfortunately, this requires map.h because it needs "struct entry_point" | |
40 | + | |
41 | +#ifdef DEMO | |
42 | +#define MAXIMUM_NUMBER_OF_NETWORK_PLAYERS 2 | |
43 | +#else | |
44 | +#define MAXIMUM_NUMBER_OF_NETWORK_PLAYERS 8 | |
45 | +#endif | |
46 | + | |
47 | +#define MAX_LEVEL_NAME_LENGTH 64 | |
48 | + | |
49 | +// change this if you make a major change to the way the setup messages work | |
50 | +#define kNetworkSetupProtocolID "Aleph One WonderNAT V1" | |
51 | + | |
52 | +// ZZZ: there probably should be a published max size somewhere, but this isn't used anywhere; better | |
53 | +// not to pretend it's real. | |
54 | +//#define MAX_NET_DISTRIBUTION_BUFFER_SIZE 512 | |
55 | + | |
56 | +enum // base network speeds | |
57 | +{ | |
58 | + _appletalk_remote, // ARA | |
59 | + _localtalk, | |
60 | + _tokentalk, | |
61 | + _ethernet, | |
62 | +#ifdef USE_MODEM | |
63 | + _modem, | |
64 | +#endif | |
65 | + NUMBER_OF_NETWORK_TYPES | |
66 | +}; | |
67 | + | |
68 | +typedef struct game_info | |
69 | +{ | |
70 | + uint16 initial_random_seed; | |
71 | + int16 net_game_type; | |
72 | + int32 time_limit; | |
73 | + int16 kill_limit; | |
74 | + int16 game_options; | |
75 | + int16 difficulty_level; | |
76 | + bool server_is_playing; // if false, then observing | |
77 | + bool allow_mic; | |
78 | + | |
79 | + int16 cheat_flags; | |
80 | + | |
81 | + // where the game takes place | |
82 | + int16 level_number; | |
83 | + char level_name[MAX_LEVEL_NAME_LENGTH+1]; | |
84 | + uint32 parent_checksum; | |
85 | + | |
86 | + // network parameters | |
87 | + int16 initial_updates_per_packet; | |
88 | + int16 initial_update_latency; | |
89 | +} game_info; | |
90 | + | |
91 | +#define MAX_NET_PLAYER_NAME_LENGTH 32 | |
92 | +#define LONG_SERIAL_NUMBER_LENGTH 10 | |
93 | + | |
94 | +typedef struct player_info | |
95 | +{ | |
96 | + unsigned char name[MAX_NET_PLAYER_NAME_LENGTH+1]; | |
97 | + int16 desired_color; | |
98 | + int16 team; // from player.h | |
99 | + int16 color; | |
100 | + byte long_serial_number[LONG_SERIAL_NUMBER_LENGTH]; | |
101 | +} player_info; | |
102 | + | |
103 | + | |
104 | +struct prospective_joiner_info { | |
105 | + uint16 stream_id; | |
106 | + unsigned char name[MAX_NET_PLAYER_NAME_LENGTH]; | |
107 | + int16 color; | |
108 | + int16 team; | |
109 | + bool gathering; | |
110 | +}; | |
111 | + | |
112 | + | |
113 | + | |
114 | +/* ---------------- functions from network.c */ | |
115 | +enum /* message types passed to the userユs names lookup update procedure */ | |
116 | +{ | |
117 | + removeEntity, | |
118 | + insertEntity | |
119 | +}; | |
120 | + | |
121 | +class GatherCallbacks | |
122 | +{ | |
123 | + public: | |
124 | + virtual ~GatherCallbacks() { }; | |
125 | + | |
126 | + virtual void JoinSucceeded(const prospective_joiner_info *player) = 0; | |
127 | + virtual void JoiningPlayerDropped(const prospective_joiner_info *player) = 0; | |
128 | + virtual void JoinedPlayerDropped(const prospective_joiner_info *player) = 0; | |
129 | + virtual void JoinedPlayerChanged(const prospective_joiner_info *player) { }; | |
130 | +}; | |
131 | + | |
132 | +class ChatCallbacks | |
133 | +{ | |
134 | + public: | |
135 | + virtual ~ChatCallbacks() { }; | |
136 | + static void SendChatMessage(const std::string& message); | |
137 | + virtual void ReceivedMessageFromPlayer(const char *player_name, | |
138 | + const char *message) = 0; | |
139 | +}; | |
140 | + | |
141 | +class InGameChatCallbacks : public ChatCallbacks | |
142 | +{ | |
143 | + public: | |
144 | + ~InGameChatCallbacks() { } | |
145 | + static InGameChatCallbacks *instance(); | |
146 | + void ReceivedMessageFromPlayer(const char *player_name, const char *message); | |
147 | + | |
148 | + static std::string prompt(); | |
149 | + | |
150 | + private: | |
151 | + InGameChatCallbacks() { } | |
152 | + static InGameChatCallbacks *m_instance; | |
153 | +}; | |
154 | + | |
155 | + | |
156 | + | |
157 | +// ZZZ note: netPlayerAdded, netChatMessageReceived, and netStartingResumeGame are 'pseudo-states'; | |
158 | +// they are returned from NetUpdateJoinState() but will never be assigned to the actual "NetState()". | |
159 | +// ghs: netAwaitingHello isn't ever returned or assigned to netState | |
160 | +enum /* states */ | |
161 | +{ | |
162 | + netUninitialized, /* NetEnter() has not been called */ | |
163 | + netGathering, /* looking for players */ | |
164 | + netConnecting, /* trying to establish connection to gatherer */ | |
165 | + netJoining, /* waiting to be gathered */ | |
166 | + netWaiting, /* have been gathered, waiting for start message */ | |
167 | + netStartingUp, /* waiting for everyone to report (via NetSync) and begin queueing commands */ | |
168 | + netActive, /* in game */ | |
169 | + netComingDown, /* Coming down... */ | |
170 | + netDown, /* game over, waiting for new gather or join call */ | |
171 | + netCancelled, /* the game was just cancelled */ | |
172 | + netPlayerAdded, /* a new player was just added to the topology (will return to netWaiting) */ | |
173 | + netPlayerDropped, /* like netPlayerAdded */ | |
174 | + netPlayerChanged, /* like netPlayerAdded */ | |
175 | + netJoinErrorOccurred, | |
176 | + netChatMessageReceived, // ZZZ addition | |
177 | + netStartingResumeGame, // ZZZ addition: like netStartingUp, but starting a resume-game instead of a new game | |
178 | + netAwaitingHello // only used for handler state | |
179 | +}; | |
180 | + | |
181 | +/* -------- typedefs */ | |
182 | +// player index is the index of the player that is sending the information | |
183 | +typedef void (*NetDistributionProc)(void *buffer, short buffer_size, short player_index); | |
184 | +typedef void (*CheckPlayerProcPtr)(short player_index, short num_players); | |
185 | + | |
186 | +/* --------- prototypes/NETWORK.C */ | |
187 | +void NetSetGatherCallbacks(GatherCallbacks *gc); | |
188 | +void NetSetChatCallbacks(ChatCallbacks *cc); | |
189 | +bool NetEnter(); | |
190 | +void NetDoneGathering (void); | |
191 | +void NetExit(void); | |
192 | + | |
193 | +bool NetGather(void *game_data, short game_data_size, void *player_data, | |
194 | + short player_data_size, bool resuming_game); | |
195 | + | |
196 | +struct SSLP_ServiceInstance; | |
197 | + | |
198 | +enum { // NetGatherPlayer results | |
199 | + kGatherPlayerFailed, // generic | |
200 | + kGatherPlayerSuccessful, // generic | |
201 | + kGatheredUnacceptablePlayer // we had already committed to gathering this jimmy, | |
202 | + // but we can't start a game with him - upper-level code needs to make sure gathering is cancelled. | |
203 | +}; | |
204 | + | |
205 | +int NetGatherPlayer( | |
206 | +// ZZZ: in my formulation, player info is all passed along in one structure from the dialog here. | |
207 | +const prospective_joiner_info &player, | |
208 | +CheckPlayerProcPtr check_player); | |
209 | + | |
210 | +void NetHandleUngatheredPlayer(prospective_joiner_info ungathered_player); | |
211 | + | |
212 | +// jkvw: replaced SSLP hinting address with host address | |
213 | +bool NetGameJoin(void *player_data, short player_data_size, const char* host_address_string); | |
214 | + | |
215 | +bool NetCheckForNewJoiner (prospective_joiner_info &info); | |
216 | +short NetUpdateJoinState(void); | |
217 | +void NetCancelJoin(void); | |
218 | + | |
219 | +// ask to change color and team; it's up to the gatherer to update the topo | |
220 | +// usually he'll change your team, if the color's free you'll get that too | |
221 | +void NetChangeColors(int16 color, int16 team); | |
222 | + | |
223 | +// ghs: these are obsolete, I'll get rid of them when I'm sure I won't want | |
224 | +// to refer back to them | |
225 | + | |
226 | +// ZZZ addition - pre-game/(eventually) postgame chat | |
227 | +// Returns true if there was a pending message. | |
228 | +// Returns pointer to chat text. | |
229 | +// Returns pointer to sending player's data (does not copy player data). | |
230 | +// Data returned in pointers is only good until the next call to NetUpdateJoinState or NetCheckForIncomingMessages. | |
231 | +bool NetGetMostRecentChatMessage(player_info** outSendingPlayerData, char** outMessage); | |
232 | + | |
233 | +// Gatherer should use this to send out his messages or to broadcast a message received from a joiner | |
234 | +OSErr NetDistributeChatMessage(short sender_identifier, const char* message); | |
235 | + | |
236 | +void NetProcessMessagesInGame(); | |
237 | + | |
238 | +short NetGetLocalPlayerIndex(void); | |
239 | +short NetGetPlayerIdentifier(short player_index); | |
240 | + | |
241 | +bool NetNumberOfPlayerIsValid(void); | |
242 | +short NetGetNumberOfPlayers(void); | |
243 | + | |
244 | +void *NetGetPlayerData(short player_index); | |
245 | +void *NetGetGameData(void); | |
246 | + | |
247 | +struct player_start_data; | |
248 | +// Gatherer may call this once after all players are gathered but before NetStart() | |
249 | +void NetSetupTopologyFromStarts(const player_start_data* inStartArray, short inStartCount); | |
250 | + | |
251 | +void NetSetInitialParameters(short updates_per_packet, short update_latency); | |
252 | + | |
253 | +bool NetSync(void); | |
254 | +bool NetUnSync(void); | |
255 | + | |
256 | +bool NetStart(void); | |
257 | +void NetCancelGather(void); | |
258 | + | |
259 | +int32 NetGetNetTime(void); | |
260 | + | |
261 | +bool NetChangeMap(struct entry_point *entry); | |
262 | +OSErr NetDistributeGameDataToAllPlayers(byte* wad_buffer, int32 wad_length, bool do_physics); | |
263 | +byte* NetReceiveGameData(bool do_physics); | |
264 | + | |
265 | +void DeferredScriptSend (byte* data, size_t length); | |
266 | +void SetNetscriptStatus (bool status); | |
267 | + | |
268 | +void display_net_game_stats(void); | |
269 | + | |
270 | +// ZZZ change: caller specifies int16 ID for distribution type. Unknown types (when received) are | |
271 | +// passed along but ignored. Uses an STL 'map' so ID's need not be consecutive or in any particular | |
272 | +// sub-range. | |
273 | +void NetAddDistributionFunction(int16 type, NetDistributionProc proc, bool lossy); | |
274 | +void NetDistributeInformation(short type, void *buffer, short buffer_size, bool send_to_self, bool send_only_to_team = false); | |
275 | +void NetRemoveDistributionFunction(short type); | |
276 | + | |
277 | +// disable "cheats" | |
278 | +bool NetAllowCrosshair(); | |
279 | +bool NetAllowOverlayMap(); | |
280 | +bool NetAllowTunnelVision(); | |
281 | +bool NetAllowBehindview(); | |
282 | +bool NetAllowCarnageMessages(); | |
283 | +bool NetAllowSavingLevel(); | |
284 | + | |
285 | +// spoke stuf | |
286 | + | |
287 | +int32 NetGetUnconfirmedActionFlagsCount(); // how many flags can we use for prediction? | |
288 | +uint32 NetGetUnconfirmedActionFlag(int32 offset); // offset < GetUnconfirmedActionFlagsCount | |
289 | +void NetUpdateUnconfirmedActionFlags(); | |
290 | + | |
291 | +struct NetworkStats | |
292 | +{ | |
293 | + enum { | |
294 | + invalid = -1, | |
295 | + disconnected = -2, | |
296 | + }; | |
297 | + | |
298 | + int16 latency; | |
299 | + int16 jitter; | |
300 | + uint16 errors; | |
301 | +}; | |
302 | + | |
303 | +// returns latency in ms, or kNetLatencyInvalid or kNetLatencyDisconnected | |
304 | +int32 NetGetLatency(); | |
305 | + | |
306 | +const NetworkStats& NetGetStats(int player_index); | |
307 | + | |
308 | +#endif |
@@ -1,398 +1,398 @@ | ||
1 | -/* | |
2 | - network_udp_opentransport.cpp | |
3 | - | |
4 | - Attempting to bring IPring in-game ring protocol to Classic via OpenTransport | |
5 | - (will still use SDL_net/SSLP for service location and "streaming" data) | |
6 | - | |
7 | - Copyright (C) 2003 and beyond by Woody Zenfell, III | |
8 | - and the "Aleph One" developers. | |
9 | - | |
10 | - This program is free software; you can redistribute it and/or modify | |
11 | - it under the terms of the GNU General Public License as published by | |
12 | - the Free Software Foundation; either version 3 of the License, or | |
13 | - (at your option) any later version. | |
14 | - | |
15 | - This program is distributed in the hope that it will be useful, | |
16 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | - GNU General Public License for more details. | |
19 | - | |
20 | - This license is contained in the file "COPYING", | |
21 | - which is included with this source code; it is available online at | |
22 | - http://www.gnu.org/licenses/gpl.html | |
23 | - | |
24 | - Jan. 10, 2003 (Woody Zenfell): created | |
25 | - | |
26 | - May 18, 2003 (Woody Zenfell): now uses passed-in port number for local socket | |
27 | -*/ | |
28 | - | |
29 | - | |
30 | -#include "cseries.h" | |
31 | -#include "sdl_network.h" // trust me, you don't even want to ask... | |
32 | -#include "network_private.h" // for kPROTOCOL_TYPE | |
33 | -#include "Logging.h" | |
34 | - | |
35 | -// ZZZ: guarding with MACH since building this in the Carbon version on Mac OS X | |
36 | -// doesn't like these #includes | |
37 | -#ifndef __MACH__ | |
38 | -// LP: had to do these includes to satisfy CodeWarrior's Classic support | |
39 | -#include <Files.h> // ghs: for FSSpec in OpenTransportProviders.h?? | |
40 | -#include <OpenTransport.h> | |
41 | -#include <OpenTransportProviders.h> | |
42 | -#endif // __MACH__ | |
43 | - | |
44 | -// ZZZ: Can define this symbol in a pinch for some additional debug logging. | |
45 | -// Don't do it in most cases because Logging is not thread-safe yet. | |
46 | -//#define UNSAFE_OTUDP_LOGGING | |
47 | - | |
48 | -static const int kReserved = 0; | |
49 | -static const int kUnused = 0; | |
50 | - | |
51 | -static EndpointRef sEndpoint = kOTInvalidEndpointRef; | |
52 | -static InetAddress sBoundAddress; | |
53 | -static InetAddress sIncomingAddress; | |
54 | -static TUnitData sIncomingUnitData = { { sizeof(sIncomingAddress), sizeof(sIncomingAddress), (UInt8*)&sIncomingAddress }, // addr | |
55 | - { 0, 0, NULL }, // opt | |
56 | - { 0, 0, NULL } // data | |
57 | - }; | |
58 | -static PacketHandlerProcPtr sPacketHandler = NULL; | |
59 | -static DDPPacketBuffer sPacketBufferForPacketHandler; | |
60 | -static OTNotifyUPP sNotifierUPP; | |
61 | - | |
62 | -// Make sure OT is ready | |
63 | -OSErr | |
64 | -NetDDPOpen() { | |
65 | - OSStatus theResult; | |
66 | - | |
67 | - theResult = InitOpenTransport(); | |
68 | - if(theResult == kEINVALErr) // seems to return this if I init twice - not sure about in Mac OS 9 | |
69 | - theResult = noErr; | |
70 | -//#endif | |
71 | - | |
72 | - // According to Inside Mac: Networking with OT, this will automatically patch ExitToShell | |
73 | - // to make sure CloseOpenTransport() is called. | |
74 | - // Also, the docs state that if we're using the Apple Shared Library Manager (ASLM), we need | |
75 | - // to call InitLibraryManager() before calling InitOpenTransport(). | |
76 | - | |
77 | - // I'm guessing here. Indeed, it seems OT is probably already going to be initialized | |
78 | - // by the time we get here since SSLP and the streaming stuff use SDL_net which uses OT. | |
79 | - // The Apple docs make no statement as to whether multiple initializations are safe. | |
80 | - | |
81 | - if(theResult != noErr) | |
82 | - logError1("cannot initialize Open Transport: returned %d", theResult); | |
83 | - | |
84 | - return theResult; | |
85 | -} | |
86 | - | |
87 | - | |
88 | - | |
89 | -// Nothing special to do | |
90 | -OSErr | |
91 | -NetDDPClose() { | |
92 | - return noErr; | |
93 | -} | |
94 | - | |
95 | - | |
96 | - | |
97 | -static void | |
98 | -handleDataArrival() { | |
99 | - OTFlags theFlags; // currently ignored | |
100 | - | |
101 | - // We don't receive directly into the sPacketBufferForPacketHandler in case a spurious T_DATA encourages us to overwrite | |
102 | - // that data with garbage. (So we wait to see that it seems like real data before overwriting with copied data.) | |
103 | - OSStatus theResult = OTRcvUData(sEndpoint, &sIncomingUnitData, &theFlags); | |
104 | - while(theResult == noErr) { | |
105 | - if(sIncomingUnitData.udata.len > 0) { | |
106 | - assert(sIncomingUnitData.udata.len <= ddpMaxData); // not really safe to assert() here probably, but need to catch this somehow | |
107 | - memcpy(sPacketBufferForPacketHandler.datagramData, sIncomingUnitData.udata.buf, sIncomingUnitData.udata.len); | |
108 | - sPacketBufferForPacketHandler.datagramSize = sIncomingUnitData.udata.len; | |
109 | - sPacketBufferForPacketHandler.protocolType = kPROTOCOL_TYPE; | |
110 | - InetAddress* theSourceAddress = (InetAddress*)(sIncomingUnitData.addr.buf); | |
111 | - assert(theSourceAddress->fAddressType = AF_INET); | |
112 | - sPacketBufferForPacketHandler.sourceAddress.host = theSourceAddress->fHost; | |
113 | - sPacketBufferForPacketHandler.sourceAddress.port = theSourceAddress->fPort; | |
114 | - | |
115 | - if(sPacketHandler != NULL) | |
116 | - sPacketHandler(&sPacketBufferForPacketHandler); | |
117 | - } | |
118 | - theResult = OTRcvUData(sEndpoint, &sIncomingUnitData, &theFlags); | |
119 | - } | |
120 | -#ifdef UNSAFE_OTUDP_LOGGING | |
121 | - if(theResult != kOTNoDataErr) | |
122 | - logAnomaly1("OTRcvUData returned %d", theResult); | |
123 | -#endif | |
124 | -} | |
125 | - | |
126 | - | |
127 | - | |
128 | -// This code is called (potentially at "deferred task time") by OT whenever interesting stuff happens. | |
129 | -static pascal void | |
130 | -udpNotifier(void* inContext, OTEventCode inEventCode, OTResult inResult, void* cookie) { | |
131 | -#ifdef UNSAFE_OTUDP_LOGGING | |
132 | - // This is fairly unsafe (calling this routine from deferred task time), probably... | |
133 | - // but if it works even just for now, it can help us understand why networking isn't working right in Classic. | |
134 | - logTrace2("udpNotifier called with eventCode=0x%x result=0x%x", inEventCode, inResult); | |
135 | -#endif | |
136 | - | |
137 | - switch(inEventCode) { | |
138 | - case T_UDERR: | |
139 | - // Need to clear out error even if we don't care about it | |
140 | - OTRcvUDErr(sEndpoint, NULL); | |
141 | - break; | |
142 | - | |
143 | - case T_GODATA: | |
144 | - // flow-control has lifted, clear to send data | |
145 | - break; | |
146 | - | |
147 | - case T_DATA: | |
148 | - handleDataArrival(); | |
149 | - break; | |
150 | - } | |
151 | -} | |
152 | - | |
153 | -// we can't send at interrupt time, so make a small array of frames to send | |
154 | -// in the main event loop | |
155 | - | |
156 | -enum { | |
157 | - kDataSize = 1500, | |
158 | - kFramesToSendSize = 16 | |
159 | -}; | |
160 | - | |
161 | -struct DeferredSentFrame | |
162 | -{ | |
163 | - unsigned char data[kDataSize]; | |
164 | - uint16 data_size; | |
165 | - InetAddress address; | |
166 | - bool send; | |
167 | -}; | |
168 | - | |
169 | -static DeferredSentFrame framesToSend[kFramesToSendSize]; | |
170 | - | |
171 | - | |
172 | - | |
173 | -// Open the socket ("endpoint provider" in OTspeak) | |
174 | -OSErr | |
175 | -NetDDPOpenSocket(short* ioPortNumber, PacketHandlerProcPtr inPacketHandler) { | |
176 | - TEndpointInfo theEndpointInfo; | |
177 | - OSStatus theResult; | |
178 | - | |
179 | - for (int i = 0; i < kFramesToSendSize; i++) | |
180 | - framesToSend[i].send = false; | |
181 | - | |
182 | - // Synchronously create the endpoint | |
183 | - sEndpoint = OTOpenEndpoint(OTCreateConfiguration(kUDPName), kReserved, &theEndpointInfo, &theResult); | |
184 | - | |
185 | - if(theResult != noErr) { | |
186 | - logError1("NetDDPOpenSocket: OTOpenEndpoint error (%d)", theResult); | |
187 | - return theResult; | |
188 | - } | |
189 | - | |
190 | - // Endpoint now is synchronous, nonblocking, and does not acknowledge sends | |
191 | - // (i.e. it will copy sent data into its own buffer so we can reuse ours) | |
192 | - | |
193 | - // Allocate storage for packet | |
194 | - if(theEndpointInfo.tsdu <= 0) { | |
195 | - logError1("NetDDPOpenSocket: endpoint tsdu nonpositive (%d)", theEndpointInfo.tsdu); | |
196 | - theResult = -1; | |
197 | - goto close_and_return; | |
198 | - } | |
199 | - | |
200 | - if(sIncomingUnitData.udata.buf == NULL) { | |
201 | - sIncomingUnitData.udata.buf = new UInt8[theEndpointInfo.tsdu]; | |
202 | - sIncomingUnitData.udata.maxlen = theEndpointInfo.tsdu; | |
203 | - | |
204 | - if(sIncomingUnitData.udata.buf == NULL) { | |
205 | - logError1("NetDDPOpenSocket: could not allocate %d bytes for sPacketBuffer", theEndpointInfo.tsdu); | |
206 | - goto close_and_return; | |
207 | - } | |
208 | - } | |
209 | - else | |
210 | - logNote("NetDDPOpenSocket: packet buffer already allocated?"); | |
211 | - | |
212 | - // Bind the endpoint | |
213 | - InetAddress theDesiredAddress; | |
214 | - theDesiredAddress.fAddressType = AF_INET; | |
215 | - theDesiredAddress.fPort = *ioPortNumber; | |
216 | - theDesiredAddress.fHost = kOTAnyInetAddress; | |
217 | - obj_clear(theDesiredAddress.fUnused); | |
218 | - | |
219 | - TBind theDesiredAddressBind; | |
220 | - theDesiredAddressBind.addr.buf = (UInt8*)&theDesiredAddress; | |
221 | - theDesiredAddressBind.addr.len = sizeof(theDesiredAddress); | |
222 | - theDesiredAddressBind.addr.maxlen = sizeof(theDesiredAddress); | |
223 | - theDesiredAddressBind.qlen = kUnused; | |
224 | - | |
225 | - obj_clear(sBoundAddress); | |
226 | - | |
227 | - TBind theActualAddressBind; | |
228 | - theActualAddressBind.addr.buf = (UInt8*)&sBoundAddress; | |
229 | - theActualAddressBind.addr.len = sizeof(sBoundAddress); | |
230 | - theActualAddressBind.addr.maxlen = sizeof(sBoundAddress); | |
231 | - | |
232 | - theResult = OTBind(sEndpoint, &theDesiredAddressBind, &theActualAddressBind); | |
233 | - | |
234 | - if(theResult != noErr) { | |
235 | - logError1("NetDDPOpenSocket: OTBind error (%d)", theResult); | |
236 | - goto dealloc_close_and_return; | |
237 | - } | |
238 | - | |
239 | - // Switch to blocking mode | |
240 | - theResult = OTSetBlocking(sEndpoint); | |
241 | - | |
242 | - if(theResult != noErr) { | |
243 | - logError1("NetDDPOpenSocket: OTSetBlocking error (%d)", theResult); | |
244 | - goto unbind_dealloc_close_and_return; | |
245 | - } | |
246 | - | |
247 | - // Switch to asynchronous mode | |
248 | - theResult = OTSetAsynchronous(sEndpoint); | |
249 | - | |
250 | - if(theResult != noErr) { | |
251 | - logError1("NetDDPOpenSocket: OTSetAsynchronous error (%d)", theResult); | |
252 | - goto unbind_dealloc_close_and_return; | |
253 | - } | |
254 | - | |
255 | - // Keep reference to caller's desired packet-handler | |
256 | - sPacketHandler = inPacketHandler; | |
257 | - | |
258 | - // Install our notifier | |
259 | - sNotifierUPP = NewOTNotifyUPP(udpNotifier); | |
260 | - theResult = OTInstallNotifier(sEndpoint, sNotifierUPP, kUnused); | |
261 | - | |
262 | - if(theResult != noErr) { | |
263 | - logError1("NetDDPOpenSocket: OTInstallNotifier error (%d)", theResult); | |
264 | - goto unbind_dealloc_close_and_return; | |
265 | - } | |
266 | - | |
267 | - // XXX what if data arrived after we bound but before we installed the notifier? | |
268 | - // Will we still get a T_DATA message? Will we when the next packet arrives? | |
269 | - // Maybe we should call the notifier directly with T_DATA if we somehow detect that there | |
270 | - // is, in fact, data. | |
271 | - | |
272 | - // How about this? Is this reasonably safe? | |
273 | - OTEnterNotifier(sEndpoint); | |
274 | - handleDataArrival(); | |
275 | - OTLeaveNotifier(sEndpoint); | |
276 | - | |
277 | - // XXX how is tearing down the connection complicated by async blocking mode? | |
278 | - | |
279 | - // Return our port number to caller | |
280 | - *ioPortNumber = sBoundAddress.fPort; | |
281 | - | |
282 | - return theResult; | |
283 | - | |
284 | -unbind_dealloc_close_and_return: | |
285 | - OTUnbind(sEndpoint); | |
286 | - | |
287 | - // fall through | |
288 | - | |
289 | -dealloc_close_and_return: | |
290 | - delete [] sIncomingUnitData.udata.buf; | |
291 | - sIncomingUnitData.udata.buf = NULL; | |
292 | - | |
293 | - // fall through | |
294 | - | |
295 | -close_and_return: | |
296 | - OTCloseProvider(sEndpoint); | |
297 | - sEndpoint = kOTInvalidEndpointRef; | |
298 | - | |
299 | - return theResult; | |
300 | -} | |
301 | - | |
302 | -OSErr | |
303 | -NetDDPCloseSocket(short socketNumber) { | |
304 | - if(sEndpoint != kOTInvalidEndpointRef) { | |
305 | - // This is pretty rudimentary (optimistic) currently... | |
306 | - //assert(socketNumber == kSocketNum); | |
307 | - | |
308 | - OTRemoveNotifier(sEndpoint); | |
309 | - | |
310 | - DisposeOTNotifyUPP(sNotifierUPP); | |
311 | - | |
312 | - OTSetSynchronous(sEndpoint); | |
313 | - | |
314 | - OTSetNonBlocking(sEndpoint); | |
315 | - | |
316 | - OTUnbind(sEndpoint); | |
317 | - | |
318 | - delete [] sIncomingUnitData.udata.buf; | |
319 | - sIncomingUnitData.udata.buf = NULL; | |
320 | - | |
321 | - OTCloseProvider(sEndpoint); | |
322 | - sEndpoint = kOTInvalidEndpointRef; | |
323 | - } | |
324 | - | |
325 | - for (int i = 0; i < kFramesToSendSize; i++) | |
326 | - framesToSend[i].send = false; | |
327 | - | |
328 | - return noErr; | |
329 | -} | |
330 | - | |
331 | - | |
332 | - | |
333 | -DDPFramePtr | |
334 | -NetDDPNewFrame(void) { | |
335 | - DDPFramePtr theFrame = new DDPFrame; | |
336 | - | |
337 | - if(theFrame != NULL) { | |
338 | - obj_clear(*theFrame); | |
339 | - } | |
340 | - | |
341 | - return theFrame; | |
342 | -} | |
343 | - | |
344 | - | |
345 | - | |
346 | -void | |
347 | -NetDDPDisposeFrame(DDPFramePtr inFrame) { | |
348 | - delete inFrame; | |
349 | -} | |
350 | - | |
351 | -OSErr | |
352 | -NetDDPSendUnsentFrames() | |
353 | -{ | |
354 | - for (int i = 0; i < kFramesToSendSize; i++) | |
355 | - { | |
356 | - if (framesToSend[i].send) | |
357 | - { | |
358 | - TUnitData theOutgoingData; | |
359 | - theOutgoingData.addr.buf = (Uint8*)&framesToSend[i].address; | |
360 | - theOutgoingData.addr.len = sizeof(framesToSend[i].address); | |
361 | - theOutgoingData.opt.len = 0; | |
362 | - theOutgoingData.udata.buf = framesToSend[i].data; | |
363 | - theOutgoingData.udata.len = framesToSend[i].data_size; | |
364 | - | |
365 | - OSErr theResult = OTSndUData(sEndpoint, &theOutgoingData); | |
366 | - if (theResult != noErr) | |
367 | - logNote1("NetDDPSendUnsentFrames: error sending %d", theResult); | |
368 | - framesToSend[i].send = false; | |
369 | - } | |
370 | - } | |
371 | -} | |
372 | - | |
373 | - | |
374 | -OSErr | |
375 | -NetDDPSendFrame(DDPFramePtr inFrame, NetAddrBlock* inAddress, short inProtocolType, short inSocket) { | |
376 | -// assert(inSocket == kSocketNum); | |
377 | -// assert(inFrame->socket == kSocketNum); | |
378 | - assert(inProtocolType == kPROTOCOL_TYPE); | |
379 | - assert(inFrame->data_size <= ddpMaxData); | |
380 | - | |
381 | - // find a free frame | |
382 | - int i; | |
383 | - for (i = 0; i < kFramesToSendSize && framesToSend[i].send; i++); | |
384 | - if (!framesToSend[i].send) | |
385 | - { | |
386 | - framesToSend[i].address.fAddressType = AF_INET; | |
387 | - framesToSend[i].address.fPort = inAddress->port; | |
388 | - framesToSend[i].address.fHost = inAddress->host; | |
389 | - | |
390 | - memcpy(framesToSend[i].data, inFrame->data, inFrame->data_size); | |
391 | - framesToSend[i].data_size = inFrame->data_size; | |
392 | - | |
393 | - framesToSend[i].send = true; | |
394 | - } | |
395 | - // otherwise, there are no frames free? drop the packet | |
396 | - | |
397 | - return noErr; // the calling code doesn't look at this anyway *sigh* | |
398 | -} | |
1 | +/* | |
2 | + network_udp_opentransport.cpp | |
3 | + | |
4 | + Attempting to bring IPring in-game ring protocol to Classic via OpenTransport | |
5 | + (will still use SDL_net/SSLP for service location and "streaming" data) | |
6 | + | |
7 | + Copyright (C) 2003 and beyond by Woody Zenfell, III | |
8 | + and the "Aleph One" developers. | |
9 | + | |
10 | + This program is free software; you can redistribute it and/or modify | |
11 | + it under the terms of the GNU General Public License as published by | |
12 | + the Free Software Foundation; either version 3 of the License, or | |
13 | + (at your option) any later version. | |
14 | + | |
15 | + This program is distributed in the hope that it will be useful, | |
16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + GNU General Public License for more details. | |
19 | + | |
20 | + This license is contained in the file "COPYING", | |
21 | + which is included with this source code; it is available online at | |
22 | + http://www.gnu.org/licenses/gpl.html | |
23 | + | |
24 | + Jan. 10, 2003 (Woody Zenfell): created | |
25 | + | |
26 | + May 18, 2003 (Woody Zenfell): now uses passed-in port number for local socket | |
27 | +*/ | |
28 | + | |
29 | + | |
30 | +#include "cseries.h" | |
31 | +#include "sdl_network.h" // trust me, you don't even want to ask... | |
32 | +#include "network_private.h" // for kPROTOCOL_TYPE | |
33 | +#include "Logging.h" | |
34 | + | |
35 | +// ZZZ: guarding with MACH since building this in the Carbon version on Mac OS X | |
36 | +// doesn't like these #includes | |
37 | +#ifndef __MACH__ | |
38 | +// LP: had to do these includes to satisfy CodeWarrior's Classic support | |
39 | +#include <Files.h> // ghs: for FSSpec in OpenTransportProviders.h?? | |
40 | +#include <OpenTransport.h> | |
41 | +#include <OpenTransportProviders.h> | |
42 | +#endif // __MACH__ | |
43 | + | |
44 | +// ZZZ: Can define this symbol in a pinch for some additional debug logging. | |
45 | +// Don't do it in most cases because Logging is not thread-safe yet. | |
46 | +//#define UNSAFE_OTUDP_LOGGING | |
47 | + | |
48 | +static const int kReserved = 0; | |
49 | +static const int kUnused = 0; | |
50 | + | |
51 | +static EndpointRef sEndpoint = kOTInvalidEndpointRef; | |
52 | +static InetAddress sBoundAddress; | |
53 | +static InetAddress sIncomingAddress; | |
54 | +static TUnitData sIncomingUnitData = { { sizeof(sIncomingAddress), sizeof(sIncomingAddress), (UInt8*)&sIncomingAddress }, // addr | |
55 | + { 0, 0, NULL }, // opt | |
56 | + { 0, 0, NULL } // data | |
57 | + }; | |
58 | +static PacketHandlerProcPtr sPacketHandler = NULL; | |
59 | +static DDPPacketBuffer sPacketBufferForPacketHandler; | |
60 | +static OTNotifyUPP sNotifierUPP; | |
61 | + | |
62 | +// Make sure OT is ready | |
63 | +OSErr | |
64 | +NetDDPOpen() { | |
65 | + OSStatus theResult; | |
66 | + | |
67 | + theResult = InitOpenTransport(); | |
68 | + if(theResult == kEINVALErr) // seems to return this if I init twice - not sure about in Mac OS 9 | |
69 | + theResult = noErr; | |
70 | +//#endif | |
71 | + | |
72 | + // According to Inside Mac: Networking with OT, this will automatically patch ExitToShell | |
73 | + // to make sure CloseOpenTransport() is called. | |
74 | + // Also, the docs state that if we're using the Apple Shared Library Manager (ASLM), we need | |
75 | + // to call InitLibraryManager() before calling InitOpenTransport(). | |
76 | + | |
77 | + // I'm guessing here. Indeed, it seems OT is probably already going to be initialized | |
78 | + // by the time we get here since SSLP and the streaming stuff use SDL_net which uses OT. | |
79 | + // The Apple docs make no statement as to whether multiple initializations are safe. | |
80 | + | |
81 | + if(theResult != noErr) | |
82 | + logError1("cannot initialize Open Transport: returned %d", theResult); | |
83 | + | |
84 | + return theResult; | |
85 | +} | |
86 | + | |
87 | + | |
88 | + | |
89 | +// Nothing special to do | |
90 | +OSErr | |
91 | +NetDDPClose() { | |
92 | + return noErr; | |
93 | +} | |
94 | + | |
95 | + | |
96 | + | |
97 | +static void | |
98 | +handleDataArrival() { | |
99 | + OTFlags theFlags; // currently ignored | |
100 | + | |
101 | + // We don't receive directly into the sPacketBufferForPacketHandler in case a spurious T_DATA encourages us to overwrite | |
102 | + // that data with garbage. (So we wait to see that it seems like real data before overwriting with copied data.) | |
103 | + OSStatus theResult = OTRcvUData(sEndpoint, &sIncomingUnitData, &theFlags); | |
104 | + while(theResult == noErr) { | |
105 | + if(sIncomingUnitData.udata.len > 0) { | |
106 | + assert(sIncomingUnitData.udata.len <= ddpMaxData); // not really safe to assert() here probably, but need to catch this somehow | |
107 | + memcpy(sPacketBufferForPacketHandler.datagramData, sIncomingUnitData.udata.buf, sIncomingUnitData.udata.len); | |
108 | + sPacketBufferForPacketHandler.datagramSize = sIncomingUnitData.udata.len; | |
109 | + sPacketBufferForPacketHandler.protocolType = kPROTOCOL_TYPE; | |
110 | + InetAddress* theSourceAddress = (InetAddress*)(sIncomingUnitData.addr.buf); | |
111 | + assert(theSourceAddress->fAddressType = AF_INET); | |
112 | + sPacketBufferForPacketHandler.sourceAddress.host = theSourceAddress->fHost; | |
113 | + sPacketBufferForPacketHandler.sourceAddress.port = theSourceAddress->fPort; | |
114 | + | |
115 | + if(sPacketHandler != NULL) | |
116 | + sPacketHandler(&sPacketBufferForPacketHandler); | |
117 | + } | |
118 | + theResult = OTRcvUData(sEndpoint, &sIncomingUnitData, &theFlags); | |
119 | + } | |
120 | +#ifdef UNSAFE_OTUDP_LOGGING | |
121 | + if(theResult != kOTNoDataErr) | |
122 | + logAnomaly1("OTRcvUData returned %d", theResult); | |
123 | +#endif | |
124 | +} | |
125 | + | |
126 | + | |
127 | + | |
128 | +// This code is called (potentially at "deferred task time") by OT whenever interesting stuff happens. | |
129 | +static pascal void | |
130 | +udpNotifier(void* inContext, OTEventCode inEventCode, OTResult inResult, void* cookie) { | |
131 | +#ifdef UNSAFE_OTUDP_LOGGING | |
132 | + // This is fairly unsafe (calling this routine from deferred task time), probably... | |
133 | + // but if it works even just for now, it can help us understand why networking isn't working right in Classic. | |
134 | + logTrace2("udpNotifier called with eventCode=0x%x result=0x%x", inEventCode, inResult); | |
135 | +#endif | |
136 | + | |
137 | + switch(inEventCode) { | |
138 | + case T_UDERR: | |
139 | + // Need to clear out error even if we don't care about it | |
140 | + OTRcvUDErr(sEndpoint, NULL); | |
141 | + break; | |
142 | + | |
143 | + case T_GODATA: | |
144 | + // flow-control has lifted, clear to send data | |
145 | + break; | |
146 | + | |
147 | + case T_DATA: | |
148 | + handleDataArrival(); | |
149 | + break; | |
150 | + } | |
151 | +} | |
152 | + | |
153 | +// we can't send at interrupt time, so make a small array of frames to send | |
154 | +// in the main event loop | |
155 | + | |
156 | +enum { | |
157 | + kDataSize = 1500, | |
158 | + kFramesToSendSize = 16 | |
159 | +}; | |
160 | + | |
161 | +struct DeferredSentFrame | |
162 | +{ | |
163 | + unsigned char data[kDataSize]; | |
164 | + uint16 data_size; | |
165 | + InetAddress address; | |
166 | + bool send; | |
167 | +}; | |
168 | + | |
169 | +static DeferredSentFrame framesToSend[kFramesToSendSize]; | |
170 | + | |
171 | + | |
172 | + | |
173 | +// Open the socket ("endpoint provider" in OTspeak) | |
174 | +OSErr | |
175 | +NetDDPOpenSocket(short* ioPortNumber, PacketHandlerProcPtr inPacketHandler) { | |
176 | + TEndpointInfo theEndpointInfo; | |
177 | + OSStatus theResult; | |
178 | + | |
179 | + for (int i = 0; i < kFramesToSendSize; i++) | |
180 | + framesToSend[i].send = false; | |
181 | + | |
182 | + // Synchronously create the endpoint | |
183 | + sEndpoint = OTOpenEndpoint(OTCreateConfiguration(kUDPName), kReserved, &theEndpointInfo, &theResult); | |
184 | + | |
185 | + if(theResult != noErr) { | |
186 | + logError1("NetDDPOpenSocket: OTOpenEndpoint error (%d)", theResult); | |
187 | + return theResult; | |
188 | + } | |
189 | + | |
190 | + // Endpoint now is synchronous, nonblocking, and does not acknowledge sends | |
191 | + // (i.e. it will copy sent data into its own buffer so we can reuse ours) | |
192 | + | |
193 | + // Allocate storage for packet | |
194 | + if(theEndpointInfo.tsdu <= 0) { | |
195 | + logError1("NetDDPOpenSocket: endpoint tsdu nonpositive (%d)", theEndpointInfo.tsdu); | |
196 | + theResult = -1; | |
197 | + goto close_and_return; | |
198 | + } | |
199 | + | |
200 | + if(sIncomingUnitData.udata.buf == NULL) { | |
201 | + sIncomingUnitData.udata.buf = new UInt8[theEndpointInfo.tsdu]; | |
202 | + sIncomingUnitData.udata.maxlen = theEndpointInfo.tsdu; | |
203 | + | |
204 | + if(sIncomingUnitData.udata.buf == NULL) { | |
205 | + logError1("NetDDPOpenSocket: could not allocate %d bytes for sPacketBuffer", theEndpointInfo.tsdu); | |
206 | + goto close_and_return; | |
207 | + } | |
208 | + } | |
209 | + else | |
210 | + logNote("NetDDPOpenSocket: packet buffer already allocated?"); | |
211 | + | |
212 | + // Bind the endpoint | |
213 | + InetAddress theDesiredAddress; | |
214 | + theDesiredAddress.fAddressType = AF_INET; | |
215 | + theDesiredAddress.fPort = *ioPortNumber; | |
216 | + theDesiredAddress.fHost = kOTAnyInetAddress; | |
217 | + obj_clear(theDesiredAddress.fUnused); | |
218 | + | |
219 | + TBind theDesiredAddressBind; | |
220 | + theDesiredAddressBind.addr.buf = (UInt8*)&theDesiredAddress; | |
221 | + theDesiredAddressBind.addr.len = sizeof(theDesiredAddress); | |
222 | + theDesiredAddressBind.addr.maxlen = sizeof(theDesiredAddress); | |
223 | + theDesiredAddressBind.qlen = kUnused; | |
224 | + | |
225 | + obj_clear(sBoundAddress); | |
226 | + | |
227 | + TBind theActualAddressBind; | |
228 | + theActualAddressBind.addr.buf = (UInt8*)&sBoundAddress; | |
229 | + theActualAddressBind.addr.len = sizeof(sBoundAddress); | |
230 | + theActualAddressBind.addr.maxlen = sizeof(sBoundAddress); | |
231 | + | |
232 | + theResult = OTBind(sEndpoint, &theDesiredAddressBind, &theActualAddressBind); | |
233 | + | |
234 | + if(theResult != noErr) { | |
235 | + logError1("NetDDPOpenSocket: OTBind error (%d)", theResult); | |
236 | + goto dealloc_close_and_return; | |
237 | + } | |
238 | + | |
239 | + // Switch to blocking mode | |
240 | + theResult = OTSetBlocking(sEndpoint); | |
241 | + | |
242 | + if(theResult != noErr) { | |
243 | + logError1("NetDDPOpenSocket: OTSetBlocking error (%d)", theResult); | |
244 | + goto unbind_dealloc_close_and_return; | |
245 | + } | |
246 | + | |
247 | + // Switch to asynchronous mode | |
248 | + theResult = OTSetAsynchronous(sEndpoint); | |
249 | + | |
250 | + if(theResult != noErr) { | |
251 | + logError1("NetDDPOpenSocket: OTSetAsynchronous error (%d)", theResult); | |
252 | + goto unbind_dealloc_close_and_return; | |
253 | + } | |
254 | + | |
255 | + // Keep reference to caller's desired packet-handler | |
256 | + sPacketHandler = inPacketHandler; | |
257 | + | |
258 | + // Install our notifier | |
259 | + sNotifierUPP = NewOTNotifyUPP(udpNotifier); | |
260 | + theResult = OTInstallNotifier(sEndpoint, sNotifierUPP, kUnused); | |
261 | + | |
262 | + if(theResult != noErr) { | |
263 | + logError1("NetDDPOpenSocket: OTInstallNotifier error (%d)", theResult); | |
264 | + goto unbind_dealloc_close_and_return; | |
265 | + } | |
266 | + | |
267 | + // XXX what if data arrived after we bound but before we installed the notifier? | |
268 | + // Will we still get a T_DATA message? Will we when the next packet arrives? | |
269 | + // Maybe we should call the notifier directly with T_DATA if we somehow detect that there | |
270 | + // is, in fact, data. | |
271 | + | |
272 | + // How about this? Is this reasonably safe? | |
273 | + OTEnterNotifier(sEndpoint); | |
274 | + handleDataArrival(); | |
275 | + OTLeaveNotifier(sEndpoint); | |
276 | + | |
277 | + // XXX how is tearing down the connection complicated by async blocking mode? | |
278 | + | |
279 | + // Return our port number to caller | |
280 | + *ioPortNumber = sBoundAddress.fPort; | |
281 | + | |
282 | + return theResult; | |
283 | + | |
284 | +unbind_dealloc_close_and_return: | |
285 | + OTUnbind(sEndpoint); | |
286 | + | |
287 | + // fall through | |
288 | + | |
289 | +dealloc_close_and_return: | |
290 | + delete [] sIncomingUnitData.udata.buf; | |
291 | + sIncomingUnitData.udata.buf = NULL; | |
292 | + | |
293 | + // fall through | |
294 | + | |
295 | +close_and_return: | |
296 | + OTCloseProvider(sEndpoint); | |
297 | + sEndpoint = kOTInvalidEndpointRef; | |
298 | + | |
299 | + return theResult; | |
300 | +} | |
301 | + | |
302 | +OSErr | |
303 | +NetDDPCloseSocket(short socketNumber) { | |
304 | + if(sEndpoint != kOTInvalidEndpointRef) { | |
305 | + // This is pretty rudimentary (optimistic) currently... | |
306 | + //assert(socketNumber == kSocketNum); | |
307 | + | |
308 | + OTRemoveNotifier(sEndpoint); | |
309 | + | |
310 | + DisposeOTNotifyUPP(sNotifierUPP); | |
311 | + | |
312 | + OTSetSynchronous(sEndpoint); | |
313 | + | |
314 | + OTSetNonBlocking(sEndpoint); | |
315 | + | |
316 | + OTUnbind(sEndpoint); | |
317 | + | |
318 | + delete [] sIncomingUnitData.udata.buf; | |
319 | + sIncomingUnitData.udata.buf = NULL; | |
320 | + | |
321 | + OTCloseProvider(sEndpoint); | |
322 | + sEndpoint = kOTInvalidEndpointRef; | |
323 | + } | |
324 | + | |
325 | + for (int i = 0; i < kFramesToSendSize; i++) | |
326 | + framesToSend[i].send = false; | |
327 | + | |
328 | + return noErr; | |
329 | +} | |
330 | + | |
331 | + | |
332 | + | |
333 | +DDPFramePtr | |
334 | +NetDDPNewFrame(void) { | |
335 | + DDPFramePtr theFrame = new DDPFrame; | |
336 | + | |
337 | + if(theFrame != NULL) { | |
338 | + obj_clear(*theFrame); | |
339 | + } | |
340 | + | |
341 | + return theFrame; | |
342 | +} | |
343 | + | |
344 | + | |
345 | + | |
346 | +void | |
347 | +NetDDPDisposeFrame(DDPFramePtr inFrame) { | |
348 | + delete inFrame; | |
349 | +} | |
350 | + | |
351 | +OSErr | |
352 | +NetDDPSendUnsentFrames() | |
353 | +{ | |
354 | + for (int i = 0; i < kFramesToSendSize; i++) | |
355 | + { | |
356 | + if (framesToSend[i].send) | |
357 | + { | |
358 | + TUnitData theOutgoingData; | |
359 | + theOutgoingData.addr.buf = (Uint8*)&framesToSend[i].address; | |
360 | + theOutgoingData.addr.len = sizeof(framesToSend[i].address); | |
361 | + theOutgoingData.opt.len = 0; | |
362 | + theOutgoingData.udata.buf = framesToSend[i].data; | |
363 | + theOutgoingData.udata.len = framesToSend[i].data_size; | |
364 | + | |
365 | + OSErr theResult = OTSndUData(sEndpoint, &theOutgoingData); | |
366 | + if (theResult != noErr) | |
367 | + logNote1("NetDDPSendUnsentFrames: error sending %d", theResult); | |
368 | + framesToSend[i].send = false; | |
369 | + } | |
370 | + } | |
371 | +} | |
372 | + | |
373 | + | |
374 | +OSErr | |
375 | +NetDDPSendFrame(DDPFramePtr inFrame, NetAddrBlock* inAddress, short inProtocolType, short inSocket) { | |
376 | +// assert(inSocket == kSocketNum); | |
377 | +// assert(inFrame->socket == kSocketNum); | |
378 | + assert(inProtocolType == kPROTOCOL_TYPE); | |
379 | + assert(inFrame->data_size <= ddpMaxData); | |
380 | + | |
381 | + // find a free frame | |
382 | + int i; | |
383 | + for (i = 0; i < kFramesToSendSize && framesToSend[i].send; i++); | |
384 | + if (!framesToSend[i].send) | |
385 | + { | |
386 | + framesToSend[i].address.fAddressType = AF_INET; | |
387 | + framesToSend[i].address.fPort = inAddress->port; | |
388 | + framesToSend[i].address.fHost = inAddress->host; | |
389 | + | |
390 | + memcpy(framesToSend[i].data, inFrame->data, inFrame->data_size); | |
391 | + framesToSend[i].data_size = inFrame->data_size; | |
392 | + | |
393 | + framesToSend[i].send = true; | |
394 | + } | |
395 | + // otherwise, there are no frames free? drop the packet | |
396 | + | |
397 | + return noErr; // the calling code doesn't look at this anyway *sigh* | |
398 | +} |
@@ -1,304 +1,304 @@ | ||
1 | -/* | |
2 | - * network_microphone_sdl_win32.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * This is a DirectSoundCapture (Win32 DirectX)-based network microphone implementation. | |
29 | - * Other platforms will need their own implementations. A dummy implementation is | |
30 | - * provided in network_microphone_sdl_dummy.cpp for you to link against if you don't have | |
31 | - * a "real" implementation. | |
32 | - * | |
33 | - * Please factor out parts useful on other platforms rather than nearly-duplicating them. | |
34 | - * | |
35 | - * Created by woody Mar 3-8, 2002. | |
36 | - * | |
37 | - * Feb. 1, 2003 (Woody Zenfell): | |
38 | - * factored out parts useful on other platforms into network_microphone_shared.cpp | |
39 | - */ | |
40 | - | |
41 | -#if !defined(DISABLE_NETWORKING) | |
42 | - | |
43 | -#include "cseries.h" | |
44 | -#include "network_speaker_sdl.h" | |
45 | -#include "network_microphone_shared.h" | |
46 | -#include "Logging.h" | |
47 | -#include <dsound.h> | |
48 | - | |
49 | -#ifdef SPEEX | |
50 | -#include "preferences.h" | |
51 | -#include "network_speex.h" | |
52 | -#endif | |
53 | - | |
54 | - | |
55 | - | |
56 | -static LPDIRECTSOUNDCAPTURE sDirectSoundCapture = NULL; | |
57 | -static LPDIRECTSOUNDCAPTUREBUFFER sCaptureBuffer = NULL; | |
58 | -static int sCaptureBufferSize = 0; | |
59 | -static bool sCaptureSystemReady = false; | |
60 | -static int sNextReadStartPosition = 0; | |
61 | -static int sCaptureFormatIndex = 0; | |
62 | -static bool sTransmittingAudio = false; | |
63 | - | |
64 | - | |
65 | - | |
66 | -struct CaptureFormat { | |
67 | - uint32 mIdentifier; | |
68 | - uint32 mRate; | |
69 | - bool mStereo; | |
70 | - bool m16Bit; | |
71 | -}; | |
72 | - | |
73 | -// These are searched in the listed order for a format the hardware supports. | |
74 | -// List ONLY those with sample rates >= the network audio sample rate, because | |
75 | -// the format-translation routines can only downsample. | |
76 | -CaptureFormat sFormatPreferences[] = { | |
77 | - { WAVE_FORMAT_1M16, 11025, false, true }, | |
78 | - { WAVE_FORMAT_1S16, 11025, true, true }, | |
79 | - { WAVE_FORMAT_1M08, 11025, false, false }, | |
80 | - { WAVE_FORMAT_1S08, 11025, true, false }, | |
81 | - { WAVE_FORMAT_2M16, 22050, false, true }, | |
82 | - { WAVE_FORMAT_2S16, 22050, true, true }, | |
83 | - { WAVE_FORMAT_2M08, 22050, false, false }, | |
84 | - { WAVE_FORMAT_2S08, 22050, true, false }, | |
85 | - { WAVE_FORMAT_4M16, 44100, false, true }, | |
86 | - { WAVE_FORMAT_4S16, 44100, true, true }, | |
87 | - { WAVE_FORMAT_4M08, 44100, false, false }, | |
88 | - { WAVE_FORMAT_4S08, 44100, true, false } | |
89 | -}; | |
90 | - | |
91 | -const int NUM_CAPTURE_FORMAT_PREFERENCES = sizeof(sFormatPreferences) / sizeof(sFormatPreferences[0]); | |
92 | - | |
93 | - | |
94 | - | |
95 | -static void | |
96 | -transmit_captured_data(bool inForceSend) { | |
97 | - // Find out how much data we can read | |
98 | - unsigned long theReadPosition; | |
99 | - sCaptureBuffer->GetCurrentPosition(&theReadPosition, NULL); | |
100 | - | |
101 | - int32 theAmountOfDataAvailable = (sCaptureBufferSize + theReadPosition - sNextReadStartPosition) % sCaptureBufferSize; | |
102 | - | |
103 | - // If we don't have a full packet's worth yet, don't send unless we're squeezing out the end. | |
104 | - if(theAmountOfDataAvailable >= get_capture_byte_count_per_packet() || inForceSend) { | |
105 | - // Lock capture buffer so we can copy audio out | |
106 | - void* theFirstChunkStart; | |
107 | - unsigned long theFirstChunkSize; | |
108 | - void* theSecondChunkStart; | |
109 | - unsigned long theSecondChunkSize; | |
110 | - | |
111 | - | |
112 | - sCaptureBuffer->Lock(sNextReadStartPosition, theAmountOfDataAvailable, &theFirstChunkStart, &theFirstChunkSize, | |
113 | - &theSecondChunkStart, &theSecondChunkSize, 0); | |
114 | - | |
115 | - // This ought to be the same as theAmountOfDataAvailable, I'd think, but just in case... | |
116 | - int32 theAmountOfDataLocked = theFirstChunkSize + theSecondChunkSize; | |
117 | - | |
118 | - // We might not actually use all of it, since we try to send in whole-packet increments. | |
119 | - int32 theAmountOfDataConsumed = copy_and_send_audio_data((uint8*) theFirstChunkStart, theFirstChunkSize, | |
120 | - (uint8*) theSecondChunkStart, theSecondChunkSize, inForceSend); | |
121 | - | |
122 | - sNextReadStartPosition = (sNextReadStartPosition + theAmountOfDataConsumed) % sCaptureBufferSize; | |
123 | - | |
124 | - sCaptureBuffer->Unlock(theFirstChunkStart, theFirstChunkSize, theSecondChunkStart, theSecondChunkSize); | |
125 | - } | |
126 | -} | |
127 | - | |
128 | - | |
129 | - | |
130 | -OSErr | |
131 | -open_network_microphone() { | |
132 | - logContext("setting up microphone"); | |
133 | - | |
134 | - // We will probably do weird things if people open us twice without closing in between | |
135 | - assert(!sCaptureSystemReady); | |
136 | - assert(sDirectSoundCapture == NULL); | |
137 | - assert(sCaptureBuffer == NULL); | |
138 | - | |
139 | - HRESULT theResult = DirectSoundCaptureCreate(NULL, &sDirectSoundCapture, NULL); | |
140 | - | |
141 | - if(FAILED(theResult)) { | |
142 | - logAnomaly1("DirectSoundCaptureCreate failed: %d", theResult); | |
143 | - } | |
144 | - else { | |
145 | - // See what capture formats the device supports | |
146 | - DSCCAPS theCaptureCapabilities; | |
147 | - ZeroMemory(&theCaptureCapabilities, sizeof(theCaptureCapabilities)); | |
148 | - theCaptureCapabilities.dwSize = sizeof(theCaptureCapabilities); | |
149 | - sDirectSoundCapture->GetCaps(&theCaptureCapabilities); | |
150 | - | |
151 | - // Find the most-preferred (earliest-listed) format the device supports | |
152 | - sCaptureFormatIndex = NONE; | |
153 | - for(int i = 0; i < NUM_CAPTURE_FORMAT_PREFERENCES; i++) { | |
154 | - if(theCaptureCapabilities.dwFormats & sFormatPreferences[i].mIdentifier) { | |
155 | - // Found a format the capture system supports | |
156 | - sCaptureFormatIndex = i; | |
157 | - break; | |
158 | - } | |
159 | - } | |
160 | - | |
161 | - if(sCaptureFormatIndex == NONE) { | |
162 | - logAnomaly("No valid audio capture formats supported"); | |
163 | - } | |
164 | - else { | |
165 | - CaptureFormat& theChosenFormat = sFormatPreferences[sCaptureFormatIndex]; | |
166 | - | |
167 | - WAVEFORMATEX theWaveFormat; | |
168 | - theWaveFormat.wFormatTag = WAVE_FORMAT_PCM; | |
169 | - theWaveFormat.nChannels = theChosenFormat.mStereo ? 2 : 1; | |
170 | - theWaveFormat.nSamplesPerSec = theChosenFormat.mRate; | |
171 | - theWaveFormat.wBitsPerSample = theChosenFormat.m16Bit ? 16 : 8; | |
172 | - theWaveFormat.nBlockAlign = theWaveFormat.nChannels * theWaveFormat.wBitsPerSample / 8; | |
173 | - theWaveFormat.nAvgBytesPerSec = theWaveFormat.nBlockAlign * theWaveFormat.nSamplesPerSec; | |
174 | - theWaveFormat.cbSize = 0; | |
175 | - | |
176 | - // Let's try a half-second buffer. | |
177 | - sCaptureBufferSize = (theWaveFormat.nSamplesPerSec / 2) * theWaveFormat.nBlockAlign; | |
178 | - | |
179 | - DSCBUFFERDESC theRecordingBufferDescription; | |
180 | - ZeroMemory(&theRecordingBufferDescription, sizeof(theRecordingBufferDescription)); | |
181 | - theRecordingBufferDescription.dwSize = sizeof(theRecordingBufferDescription); | |
182 | - theRecordingBufferDescription.dwBufferBytes = sCaptureBufferSize; | |
183 | - theRecordingBufferDescription.lpwfxFormat = &theWaveFormat; | |
184 | - | |
185 | - theResult = sDirectSoundCapture->CreateCaptureBuffer(&theRecordingBufferDescription, &sCaptureBuffer, NULL); | |
186 | - | |
187 | - if(FAILED(theResult)) { | |
188 | - logAnomaly1("CreateCaptureBuffer failed: %d", theResult); | |
189 | - } | |
190 | - else { | |
191 | - if(!announce_microphone_capture_format(theChosenFormat.mRate, theChosenFormat.mStereo, theChosenFormat.m16Bit)) { | |
192 | - logAnomaly3("network microphone support code rejected audio format: %d samp/sec, %s, %d bits/samp", | |
193 | - theChosenFormat.mRate, theChosenFormat.mStereo ? "stereo" : "mono", theChosenFormat.m16Bit ? 16 : 8); | |
194 | - } | |
195 | - else { | |
196 | - // Initialization successful | |
197 | - sCaptureSystemReady = true; | |
198 | - } | |
199 | - } | |
200 | - } | |
201 | - } | |
202 | - | |
203 | -#ifdef SPEEX | |
204 | - if (network_preferences->use_speex_encoder) { | |
205 | - init_speex_encoder(); | |
206 | - } | |
207 | -#endif | |
208 | - | |
209 | - if(!sCaptureSystemReady) | |
210 | - logAnomaly("Could not set up DirectSoundCapture - network microphone disabled"); | |
211 | - | |
212 | - // Here we _lie_, for now, to conform to the same interface the Mac code uses. | |
213 | - return 0; | |
214 | -} | |
215 | - | |
216 | - | |
217 | - | |
218 | -static void | |
219 | -start_transmitting_audio() { | |
220 | - assert(sCaptureSystemReady); | |
221 | - assert(!sTransmittingAudio); | |
222 | - | |
223 | - HRESULT theResult = sCaptureBuffer->Start(DSCBSTART_LOOPING); | |
224 | - | |
225 | - sTransmittingAudio = (theResult == DS_OK); | |
226 | -} | |
227 | - | |
228 | - | |
229 | - | |
230 | -static void | |
231 | -stop_transmitting_audio() { | |
232 | - assert(sCaptureSystemReady); | |
233 | - assert(sTransmittingAudio); | |
234 | - | |
235 | - HRESULT theResult = sCaptureBuffer->Stop(); | |
236 | - | |
237 | - sTransmittingAudio = !(theResult == DS_OK); | |
238 | - | |
239 | - // Flush out the last chunk of stuff | |
240 | - transmit_captured_data(true); | |
241 | -} | |
242 | - | |
243 | - | |
244 | - | |
245 | -void | |
246 | -close_network_microphone() { | |
247 | - if(sCaptureSystemReady && sTransmittingAudio) | |
248 | - stop_transmitting_audio(); | |
249 | - | |
250 | - if(sCaptureBuffer != NULL) { | |
251 | - sCaptureBuffer->Release(); | |
252 | - sCaptureBuffer = NULL; | |
253 | - } | |
254 | - | |
255 | - if(sDirectSoundCapture != NULL) { | |
256 | - sDirectSoundCapture->Release(); | |
257 | - sDirectSoundCapture = NULL; | |
258 | - } | |
259 | - | |
260 | -#ifdef SPEEX | |
261 | - if (network_preferences->use_speex_encoder) { | |
262 | - destroy_speex_encoder(); | |
263 | - } | |
264 | -#endif | |
265 | - | |
266 | - sCaptureSystemReady = false; | |
267 | -} | |
268 | - | |
269 | - | |
270 | - | |
271 | -void | |
272 | -set_network_microphone_state(bool inActive) { | |
273 | - if(sCaptureSystemReady) { | |
274 | - if(inActive && !sTransmittingAudio) | |
275 | - start_transmitting_audio(); | |
276 | - else if(!inActive && sTransmittingAudio) | |
277 | - stop_transmitting_audio(); | |
278 | - } | |
279 | -} | |
280 | - | |
281 | - | |
282 | - | |
283 | -bool | |
284 | -is_network_microphone_implemented() { | |
285 | - return true; | |
286 | -} | |
287 | - | |
288 | - | |
289 | - | |
290 | -bool | |
291 | -has_sound_input_capability() { | |
292 | - // We really probably should actually _check_. | |
293 | - return true; | |
294 | -} | |
295 | - | |
296 | - | |
297 | - | |
298 | -void | |
299 | -network_microphone_idle_proc() { | |
300 | - if(sCaptureSystemReady && sTransmittingAudio) | |
301 | - transmit_captured_data(false); | |
302 | -} | |
303 | - | |
304 | -#endif // !defined(DISABLE_NETWORKING) | |
1 | +/* | |
2 | + * network_microphone_sdl_win32.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * This is a DirectSoundCapture (Win32 DirectX)-based network microphone implementation. | |
29 | + * Other platforms will need their own implementations. A dummy implementation is | |
30 | + * provided in network_microphone_sdl_dummy.cpp for you to link against if you don't have | |
31 | + * a "real" implementation. | |
32 | + * | |
33 | + * Please factor out parts useful on other platforms rather than nearly-duplicating them. | |
34 | + * | |
35 | + * Created by woody Mar 3-8, 2002. | |
36 | + * | |
37 | + * Feb. 1, 2003 (Woody Zenfell): | |
38 | + * factored out parts useful on other platforms into network_microphone_shared.cpp | |
39 | + */ | |
40 | + | |
41 | +#if !defined(DISABLE_NETWORKING) | |
42 | + | |
43 | +#include "cseries.h" | |
44 | +#include "network_speaker_sdl.h" | |
45 | +#include "network_microphone_shared.h" | |
46 | +#include "Logging.h" | |
47 | +#include <dsound.h> | |
48 | + | |
49 | +#ifdef SPEEX | |
50 | +#include "preferences.h" | |
51 | +#include "network_speex.h" | |
52 | +#endif | |
53 | + | |
54 | + | |
55 | + | |
56 | +static LPDIRECTSOUNDCAPTURE sDirectSoundCapture = NULL; | |
57 | +static LPDIRECTSOUNDCAPTUREBUFFER sCaptureBuffer = NULL; | |
58 | +static int sCaptureBufferSize = 0; | |
59 | +static bool sCaptureSystemReady = false; | |
60 | +static int sNextReadStartPosition = 0; | |
61 | +static int sCaptureFormatIndex = 0; | |
62 | +static bool sTransmittingAudio = false; | |
63 | + | |
64 | + | |
65 | + | |
66 | +struct CaptureFormat { | |
67 | + uint32 mIdentifier; | |
68 | + uint32 mRate; | |
69 | + bool mStereo; | |
70 | + bool m16Bit; | |
71 | +}; | |
72 | + | |
73 | +// These are searched in the listed order for a format the hardware supports. | |
74 | +// List ONLY those with sample rates >= the network audio sample rate, because | |
75 | +// the format-translation routines can only downsample. | |
76 | +CaptureFormat sFormatPreferences[] = { | |
77 | + { WAVE_FORMAT_1M16, 11025, false, true }, | |
78 | + { WAVE_FORMAT_1S16, 11025, true, true }, | |
79 | + { WAVE_FORMAT_1M08, 11025, false, false }, | |
80 | + { WAVE_FORMAT_1S08, 11025, true, false }, | |
81 | + { WAVE_FORMAT_2M16, 22050, false, true }, | |
82 | + { WAVE_FORMAT_2S16, 22050, true, true }, | |
83 | + { WAVE_FORMAT_2M08, 22050, false, false }, | |
84 | + { WAVE_FORMAT_2S08, 22050, true, false }, | |
85 | + { WAVE_FORMAT_4M16, 44100, false, true }, | |
86 | + { WAVE_FORMAT_4S16, 44100, true, true }, | |
87 | + { WAVE_FORMAT_4M08, 44100, false, false }, | |
88 | + { WAVE_FORMAT_4S08, 44100, true, false } | |
89 | +}; | |
90 | + | |
91 | +const int NUM_CAPTURE_FORMAT_PREFERENCES = sizeof(sFormatPreferences) / sizeof(sFormatPreferences[0]); | |
92 | + | |
93 | + | |
94 | + | |
95 | +static void | |
96 | +transmit_captured_data(bool inForceSend) { | |
97 | + // Find out how much data we can read | |
98 | + unsigned long theReadPosition; | |
99 | + sCaptureBuffer->GetCurrentPosition(&theReadPosition, NULL); | |
100 | + | |
101 | + int32 theAmountOfDataAvailable = (sCaptureBufferSize + theReadPosition - sNextReadStartPosition) % sCaptureBufferSize; | |
102 | + | |
103 | + // If we don't have a full packet's worth yet, don't send unless we're squeezing out the end. | |
104 | + if(theAmountOfDataAvailable >= get_capture_byte_count_per_packet() || inForceSend) { | |
105 | + // Lock capture buffer so we can copy audio out | |
106 | + void* theFirstChunkStart; | |
107 | + unsigned long theFirstChunkSize; | |
108 | + void* theSecondChunkStart; | |
109 | + unsigned long theSecondChunkSize; | |
110 | + | |
111 | + | |
112 | + sCaptureBuffer->Lock(sNextReadStartPosition, theAmountOfDataAvailable, &theFirstChunkStart, &theFirstChunkSize, | |
113 | + &theSecondChunkStart, &theSecondChunkSize, 0); | |
114 | + | |
115 | + // This ought to be the same as theAmountOfDataAvailable, I'd think, but just in case... | |
116 | + int32 theAmountOfDataLocked = theFirstChunkSize + theSecondChunkSize; | |
117 | + | |
118 | + // We might not actually use all of it, since we try to send in whole-packet increments. | |
119 | + int32 theAmountOfDataConsumed = copy_and_send_audio_data((uint8*) theFirstChunkStart, theFirstChunkSize, | |
120 | + (uint8*) theSecondChunkStart, theSecondChunkSize, inForceSend); | |
121 | + | |
122 | + sNextReadStartPosition = (sNextReadStartPosition + theAmountOfDataConsumed) % sCaptureBufferSize; | |
123 | + | |
124 | + sCaptureBuffer->Unlock(theFirstChunkStart, theFirstChunkSize, theSecondChunkStart, theSecondChunkSize); | |
125 | + } | |
126 | +} | |
127 | + | |
128 | + | |
129 | + | |
130 | +OSErr | |
131 | +open_network_microphone() { | |
132 | + logContext("setting up microphone"); | |
133 | + | |
134 | + // We will probably do weird things if people open us twice without closing in between | |
135 | + assert(!sCaptureSystemReady); | |
136 | + assert(sDirectSoundCapture == NULL); | |
137 | + assert(sCaptureBuffer == NULL); | |
138 | + | |
139 | + HRESULT theResult = DirectSoundCaptureCreate(NULL, &sDirectSoundCapture, NULL); | |
140 | + | |
141 | + if(FAILED(theResult)) { | |
142 | + logAnomaly1("DirectSoundCaptureCreate failed: %d", theResult); | |
143 | + } | |
144 | + else { | |
145 | + // See what capture formats the device supports | |
146 | + DSCCAPS theCaptureCapabilities; | |
147 | + ZeroMemory(&theCaptureCapabilities, sizeof(theCaptureCapabilities)); | |
148 | + theCaptureCapabilities.dwSize = sizeof(theCaptureCapabilities); | |
149 | + sDirectSoundCapture->GetCaps(&theCaptureCapabilities); | |
150 | + | |
151 | + // Find the most-preferred (earliest-listed) format the device supports | |
152 | + sCaptureFormatIndex = NONE; | |
153 | + for(int i = 0; i < NUM_CAPTURE_FORMAT_PREFERENCES; i++) { | |
154 | + if(theCaptureCapabilities.dwFormats & sFormatPreferences[i].mIdentifier) { | |
155 | + // Found a format the capture system supports | |
156 | + sCaptureFormatIndex = i; | |
157 | + break; | |
158 | + } | |
159 | + } | |
160 | + | |
161 | + if(sCaptureFormatIndex == NONE) { | |
162 | + logAnomaly("No valid audio capture formats supported"); | |
163 | + } | |
164 | + else { | |
165 | + CaptureFormat& theChosenFormat = sFormatPreferences[sCaptureFormatIndex]; | |
166 | + | |
167 | + WAVEFORMATEX theWaveFormat; | |
168 | + theWaveFormat.wFormatTag = WAVE_FORMAT_PCM; | |
169 | + theWaveFormat.nChannels = theChosenFormat.mStereo ? 2 : 1; | |
170 | + theWaveFormat.nSamplesPerSec = theChosenFormat.mRate; | |
171 | + theWaveFormat.wBitsPerSample = theChosenFormat.m16Bit ? 16 : 8; | |
172 | + theWaveFormat.nBlockAlign = theWaveFormat.nChannels * theWaveFormat.wBitsPerSample / 8; | |
173 | + theWaveFormat.nAvgBytesPerSec = theWaveFormat.nBlockAlign * theWaveFormat.nSamplesPerSec; | |
174 | + theWaveFormat.cbSize = 0; | |
175 | + | |
176 | + // Let's try a half-second buffer. | |
177 | + sCaptureBufferSize = (theWaveFormat.nSamplesPerSec / 2) * theWaveFormat.nBlockAlign; | |
178 | + | |
179 | + DSCBUFFERDESC theRecordingBufferDescription; | |
180 | + ZeroMemory(&theRecordingBufferDescription, sizeof(theRecordingBufferDescription)); | |
181 | + theRecordingBufferDescription.dwSize = sizeof(theRecordingBufferDescription); | |
182 | + theRecordingBufferDescription.dwBufferBytes = sCaptureBufferSize; | |
183 | + theRecordingBufferDescription.lpwfxFormat = &theWaveFormat; | |
184 | + | |
185 | + theResult = sDirectSoundCapture->CreateCaptureBuffer(&theRecordingBufferDescription, &sCaptureBuffer, NULL); | |
186 | + | |
187 | + if(FAILED(theResult)) { | |
188 | + logAnomaly1("CreateCaptureBuffer failed: %d", theResult); | |
189 | + } | |
190 | + else { | |
191 | + if(!announce_microphone_capture_format(theChosenFormat.mRate, theChosenFormat.mStereo, theChosenFormat.m16Bit)) { | |
192 | + logAnomaly3("network microphone support code rejected audio format: %d samp/sec, %s, %d bits/samp", | |
193 | + theChosenFormat.mRate, theChosenFormat.mStereo ? "stereo" : "mono", theChosenFormat.m16Bit ? 16 : 8); | |
194 | + } | |
195 | + else { | |
196 | + // Initialization successful | |
197 | + sCaptureSystemReady = true; | |
198 | + } | |
199 | + } | |
200 | + } | |
201 | + } | |
202 | + | |
203 | +#ifdef SPEEX | |
204 | + if (network_preferences->use_speex_encoder) { | |
205 | + init_speex_encoder(); | |
206 | + } | |
207 | +#endif | |
208 | + | |
209 | + if(!sCaptureSystemReady) | |
210 | + logAnomaly("Could not set up DirectSoundCapture - network microphone disabled"); | |
211 | + | |
212 | + // Here we _lie_, for now, to conform to the same interface the Mac code uses. | |
213 | + return 0; | |
214 | +} | |
215 | + | |
216 | + | |
217 | + | |
218 | +static void | |
219 | +start_transmitting_audio() { | |
220 | + assert(sCaptureSystemReady); | |
221 | + assert(!sTransmittingAudio); | |
222 | + | |
223 | + HRESULT theResult = sCaptureBuffer->Start(DSCBSTART_LOOPING); | |
224 | + | |
225 | + sTransmittingAudio = (theResult == DS_OK); | |
226 | +} | |
227 | + | |
228 | + | |
229 | + | |
230 | +static void | |
231 | +stop_transmitting_audio() { | |
232 | + assert(sCaptureSystemReady); | |
233 | + assert(sTransmittingAudio); | |
234 | + | |
235 | + HRESULT theResult = sCaptureBuffer->Stop(); | |
236 | + | |
237 | + sTransmittingAudio = !(theResult == DS_OK); | |
238 | + | |
239 | + // Flush out the last chunk of stuff | |
240 | + transmit_captured_data(true); | |
241 | +} | |
242 | + | |
243 | + | |
244 | + | |
245 | +void | |
246 | +close_network_microphone() { | |
247 | + if(sCaptureSystemReady && sTransmittingAudio) | |
248 | + stop_transmitting_audio(); | |
249 | + | |
250 | + if(sCaptureBuffer != NULL) { | |
251 | + sCaptureBuffer->Release(); | |
252 | + sCaptureBuffer = NULL; | |
253 | + } | |
254 | + | |
255 | + if(sDirectSoundCapture != NULL) { | |
256 | + sDirectSoundCapture->Release(); | |
257 | + sDirectSoundCapture = NULL; | |
258 | + } | |
259 | + | |
260 | +#ifdef SPEEX | |
261 | + if (network_preferences->use_speex_encoder) { | |
262 | + destroy_speex_encoder(); | |
263 | + } | |
264 | +#endif | |
265 | + | |
266 | + sCaptureSystemReady = false; | |
267 | +} | |
268 | + | |
269 | + | |
270 | + | |
271 | +void | |
272 | +set_network_microphone_state(bool inActive) { | |
273 | + if(sCaptureSystemReady) { | |
274 | + if(inActive && !sTransmittingAudio) | |
275 | + start_transmitting_audio(); | |
276 | + else if(!inActive && sTransmittingAudio) | |
277 | + stop_transmitting_audio(); | |
278 | + } | |
279 | +} | |
280 | + | |
281 | + | |
282 | + | |
283 | +bool | |
284 | +is_network_microphone_implemented() { | |
285 | + return true; | |
286 | +} | |
287 | + | |
288 | + | |
289 | + | |
290 | +bool | |
291 | +has_sound_input_capability() { | |
292 | + // We really probably should actually _check_. | |
293 | + return true; | |
294 | +} | |
295 | + | |
296 | + | |
297 | + | |
298 | +void | |
299 | +network_microphone_idle_proc() { | |
300 | + if(sCaptureSystemReady && sTransmittingAudio) | |
301 | + transmit_captured_data(false); | |
302 | +} | |
303 | + | |
304 | +#endif // !defined(DISABLE_NETWORKING) |
@@ -1,47 +1,47 @@ | ||
1 | -/* | |
2 | - * network_speex.h | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2003 and beyond by Gregory Smith | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - headers to store the speex encoder/decoder | |
23 | - | |
24 | - */ | |
25 | - | |
26 | -#ifndef __NETWORK_SPEEX_H | |
27 | -#define __NETWORK_SPEEX_H | |
28 | - | |
29 | -#include "cseries.h" | |
30 | -#ifdef SPEEX | |
31 | -#include <speex/speex.h> | |
32 | -#include <speex/speex_preprocess.h> | |
33 | - | |
34 | -// encoder | |
35 | -extern void *gEncoderState; | |
36 | -extern SpeexBits gEncoderBits; | |
37 | -extern void *gDecoderState; | |
38 | -extern SpeexBits gDecoderBits; | |
39 | -extern SpeexPreprocessState* gPreprocessState; | |
40 | - | |
41 | -void init_speex_encoder(); | |
42 | -void destroy_speex_encoder(); | |
43 | -void init_speex_decoder(); | |
44 | -void destroy_speex_decoder(); | |
45 | -#endif //def SPEEX | |
46 | - | |
47 | -#endif | |
1 | +/* | |
2 | + * network_speex.h | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2003 and beyond by Gregory Smith | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + headers to store the speex encoder/decoder | |
23 | + | |
24 | + */ | |
25 | + | |
26 | +#ifndef __NETWORK_SPEEX_H | |
27 | +#define __NETWORK_SPEEX_H | |
28 | + | |
29 | +#include "cseries.h" | |
30 | +#ifdef SPEEX | |
31 | +#include <speex/speex.h> | |
32 | +#include <speex/speex_preprocess.h> | |
33 | + | |
34 | +// encoder | |
35 | +extern void *gEncoderState; | |
36 | +extern SpeexBits gEncoderBits; | |
37 | +extern void *gDecoderState; | |
38 | +extern SpeexBits gDecoderBits; | |
39 | +extern SpeexPreprocessState* gPreprocessState; | |
40 | + | |
41 | +void init_speex_encoder(); | |
42 | +void destroy_speex_encoder(); | |
43 | +void init_speex_decoder(); | |
44 | +void destroy_speex_decoder(); | |
45 | +#endif //def SPEEX | |
46 | + | |
47 | +#endif |
@@ -1,474 +1,474 @@ | ||
1 | -/* | |
2 | -NETWORK_SPEAKER.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 | -Sunday, August 14, 1994 1:20:55 AM | |
22 | - | |
23 | -Feb 1, 2003 (Woody Zenfell): | |
24 | - Resurrected for A1. Using Apple's sample CarbonSndPlayDoubleBuffer | |
25 | - to emulate missing SndPlayDoubleBuffer call (in Carbon, anyway). | |
26 | - Using bigger blocks and bigger buffers in general, and SDL-format | |
27 | - network audio (11025Hz 8-bit mono unsigned) instead of MACE 6:1. | |
28 | - | |
29 | - May 28, 2003 (Gregory Smith): | |
30 | - Speex audio decompression | |
31 | - | |
32 | - May 28, 2003 (Woody Zenfell): | |
33 | - Quieter static bursts in netmic audio playback | |
34 | -*/ | |
35 | - | |
36 | -#if !defined(DISABLE_NETWORKING) | |
37 | - | |
38 | -/* | |
39 | -//we should only fill one buffer with static to lower the delay before actually playing incoming data | |
40 | -//we should use our doubleback procedure to fill the buffers | |
41 | -//we should not crap if our initialization fails but queue_ノ() gets called anyway | |
42 | -*/ | |
43 | - | |
44 | -#include <stdlib.h> | |
45 | - | |
46 | -#include "macintosh_cseries.h" | |
47 | - | |
48 | -#if defined(TARGET_API_MAC_CARBON) | |
49 | -#include "CarbonSndPlayDB.h" | |
50 | -#define SndPlayDoubleBuffer CarbonSndPlayDoubleBuffer | |
51 | -#define SndDoImmediate MySndDoImmediate | |
52 | -#endif | |
53 | - | |
54 | -#include "network_sound.h" | |
55 | - | |
56 | -#ifdef SPEEX | |
57 | -#include "network_speex.h" | |
58 | -#endif SPEEX | |
59 | - | |
60 | -#include "Logging.h" | |
61 | - | |
62 | -#ifdef env68k | |
63 | -#pragma segment sound | |
64 | -#endif | |
65 | - | |
66 | -/* ---------- constants */ | |
67 | - | |
68 | -#define MAXIMUM_DOUBLE_BUFFER_SIZE 1024 | |
69 | -#define MAXIMUM_QUEUE_SIZE (16*MAXIMUM_DOUBLE_BUFFER_SIZE) | |
70 | - | |
71 | -// ZZZ: these used to be passed-in at open() time, now we'll just assume them. | |
72 | -// This helps us present a consistent interface with the SDL speaker stuff. | |
73 | -enum { kConnectionThreshhold = 2, kBlockSize = 1024 }; | |
74 | - | |
75 | -// ZZZ: make the static blasts a little less annoying (higher numbers are quieter) | |
76 | -enum { kStaticAmplitudeReduction = 2 }; | |
77 | - | |
78 | -enum /* speaker states */ | |
79 | -{ | |
80 | - _speaker_is_off, /* no sound is being played, weユre waiting for some initial data */ | |
81 | - _speaker_is_turning_on, /* weユve been triggered, but canユt start playing until we get three blocks worth of data */ | |
82 | - _speaker_is_on /* weユre playing sound data in real-time, responding to doubleback requests and everything */ | |
83 | - /* when we donユt get data for a threshold number of doublebacks, we go directly to _speaker_is_off */ | |
84 | -}; | |
85 | - | |
86 | -/* ---------- structures */ | |
87 | - | |
88 | -struct speaker_definition | |
89 | -{ | |
90 | - SndChannelPtr channel; | |
91 | - SndDoubleBufferHeaderPtr header; | |
92 | - | |
93 | - int32 queue_size; /* the number of bytes in the queue */ | |
94 | - Ptr queue; | |
95 | - | |
96 | - short block_size; /* the number of bytes in each of our double buffers */ | |
97 | - short connection_threshold; /* the number of times we can doubleback without data before turning off */ | |
98 | - short connection_status; /* the number of times we have doublebacked without data (reset to zero if we get data) */ | |
99 | - short state; | |
100 | - | |
101 | - uint16 random_seed; | |
102 | -}; | |
103 | - | |
104 | -/* ---------- globals */ | |
105 | - | |
106 | -static struct speaker_definition *speaker= (struct speaker_definition *) NULL; | |
107 | -static SndDoubleBackUPP doubleback_routine_descriptor; | |
108 | - | |
109 | -/* ---------- private code */ | |
110 | - | |
111 | -static void fill_buffer_with_static(byte *buffer, short count); | |
112 | - | |
113 | -static /*pascal*/ void network_speaker_doubleback_procedure(SndChannelPtr channel, SndDoubleBufferPtr doubleBufferPtr); | |
114 | -void fill_network_speaker_buffer(SndDoubleBufferPtr doubleBufferPtr); | |
115 | -static void silence_network_speaker(); | |
116 | -static void reset_network_speaker(); | |
117 | - | |
118 | -/* ---------- code */ | |
119 | - | |
120 | -OSErr open_network_speaker() | |
121 | -{ | |
122 | - short block_size = kBlockSize; | |
123 | - short connection_threshold = kConnectionThreshhold; | |
124 | - OSErr error; | |
125 | - | |
126 | - assert(!speaker); | |
127 | - assert(block_size>0&&block_size<=MAXIMUM_DOUBLE_BUFFER_SIZE); | |
128 | - assert(connection_threshold>1&&connection_threshold<16); | |
129 | - | |
130 | - /* install our atexit cleanup procedure and build a routine descriptor */ | |
131 | - { | |
132 | - static bool initialization= true; | |
133 | - | |
134 | - if (initialization) | |
135 | - { | |
136 | -// ZZZ: Sorry, it looks like the CarbonSndPlayDoubleBuffer stuff really wants a plain C function | |
137 | -// pointer, sitting in the structure typed as a SndDoubleBackUPP. (Don't ask me!) | |
138 | -#if defined(TARGET_API_MAC_CARBON) | |
139 | - doubleback_routine_descriptor= (SndDoubleBackUPP)network_speaker_doubleback_procedure; | |
140 | -#else | |
141 | - // Thomas Herzog fix | |
142 | - #if UNIVERSAL_INTERFACES_VERSION < 0x0340 | |
143 | - doubleback_routine_descriptor= NewSndDoubleBackProc((ProcPtr)network_speaker_doubleback_procedure); | |
144 | - #else | |
145 | - doubleback_routine_descriptor= NewSndDoubleBackUPP(network_speaker_doubleback_procedure); | |
146 | - #endif | |
147 | - // doubleback_routine_descriptor= NewSndDoubleBackProc((ProcPtr)network_speaker_doubleback_procedure); | |
148 | -#endif | |
149 | - assert(doubleback_routine_descriptor); | |
150 | - | |
151 | - atexit(close_network_speaker); | |
152 | - } | |
153 | - } | |
154 | - | |
155 | - speaker= (struct speaker_definition *) NewPtr(sizeof(struct speaker_definition)); | |
156 | - if ((error= MemError())==noErr) | |
157 | - { | |
158 | - speaker->random_seed= 1; | |
159 | - speaker->block_size= block_size; | |
160 | - speaker->connection_threshold= connection_threshold; | |
161 | - | |
162 | - speaker->channel= (SndChannelPtr) NewPtrClear(sizeof(SndChannel)); | |
163 | - speaker->header= (SndDoubleBufferHeaderPtr) NewPtrClear(sizeof(SndDoubleBufferHeader)); | |
164 | - speaker->queue= NewPtr(sizeof(byte)*MAXIMUM_QUEUE_SIZE); | |
165 | - if ((error= MemError())==noErr) | |
166 | - { | |
167 | - SndDoubleBufferHeaderPtr header= speaker->header; | |
168 | - | |
169 | - header->dbhNumChannels= 1; | |
170 | - header->dbhSampleSize= 8; | |
171 | - header->dbhCompressionID= 0; | |
172 | - header->dbhPacketSize= 0; | |
173 | - header->dbhSampleRate= rate11025hz; | |
174 | - header->dbhDoubleBack= doubleback_routine_descriptor; | |
175 | - header->dbhBufferPtr[0]= (SndDoubleBufferPtr) NewPtrClear(sizeof(SndDoubleBuffer)+MAXIMUM_DOUBLE_BUFFER_SIZE); | |
176 | - header->dbhBufferPtr[1]= (SndDoubleBufferPtr) NewPtrClear(sizeof(SndDoubleBuffer)+MAXIMUM_DOUBLE_BUFFER_SIZE); | |
177 | - | |
178 | - if ((error= MemError())==noErr) | |
179 | - { | |
180 | - speaker->channel->qLength= stdQLength; | |
181 | -#ifdef env68k | |
182 | - speaker->channel->userInfo= (long) get_a5(); | |
183 | -#endif | |
184 | - | |
185 | - error= SndNewChannel(&speaker->channel, sampledSynth, initMono|initMACE6, NULL); | |
186 | - if (error==noErr) | |
187 | - { | |
188 | - quiet_network_speaker(); /* to set defaults */ | |
189 | - } | |
190 | - } | |
191 | - } | |
192 | - } | |
193 | - | |
194 | -#ifdef SPEEX | |
195 | - init_speex_decoder(); | |
196 | -#endif | |
197 | - | |
198 | - /* if something went wrong, zero the speaker definition (without freeing any of our memory | |
199 | - like we should) */ | |
200 | - if (error!=noErr) speaker= (struct speaker_definition *) NULL; | |
201 | - | |
202 | - return error; | |
203 | -} | |
204 | - | |
205 | -void close_network_speaker( | |
206 | - void) | |
207 | -{ | |
208 | - if (speaker) | |
209 | - { | |
210 | - OSErr error; | |
211 | - | |
212 | - error= SndDisposeChannel(speaker->channel, true); | |
213 | - warn(error==noErr); | |
214 | - | |
215 | - DisposePtr((Ptr)speaker->header->dbhBufferPtr[0]); | |
216 | - DisposePtr((Ptr)speaker->header->dbhBufferPtr[1]); | |
217 | - DisposePtr((Ptr)speaker->header); | |
218 | - DisposePtr((Ptr)speaker->channel); | |
219 | - DisposePtr((Ptr)speaker->queue); | |
220 | - DisposePtr((Ptr)speaker); | |
221 | - | |
222 | - speaker= (struct speaker_definition *) NULL; | |
223 | - } | |
224 | - | |
225 | -#ifdef SPEEX | |
226 | - destroy_speex_decoder(); | |
227 | -#endif | |
228 | -} | |
229 | - | |
230 | -/* can be called at interrupt time */ | |
231 | -void queue_network_speaker_data( | |
232 | - byte *buffer, | |
233 | - short count) | |
234 | -{ | |
235 | - if (speaker) | |
236 | - { | |
237 | - switch (speaker->state) | |
238 | - { | |
239 | - case _speaker_is_off: | |
240 | - /* we were off but now weユre getting data; fill one double buffer with static and | |
241 | - change our state to _turning_on (weユll wait until we receive another full | |
242 | - double buffer worth of data before beginning to replay) */ | |
243 | - | |
244 | -// ZZZ: CarbonSndPlayDB emulation layer specifically warns against calling | |
245 | -// SndDoImmediate() with a quietCmd at interrupt time - which is what | |
246 | -// quiet_network_speaker() would do. So instead here we try resetting | |
247 | -// the speaker (which ought to be safe I think) now, but delay issuing the | |
248 | -// quiet commands until just before we start playing again. | |
249 | -#if defined(TARGET_API_MAC_CARBON) | |
250 | - reset_network_speaker(); | |
251 | -#else | |
252 | - quiet_network_speaker(); /* we could be playing trailing static */ | |
253 | -#endif | |
254 | - speaker->state= _speaker_is_turning_on; | |
255 | - fill_network_speaker_buffer(speaker->header->dbhBufferPtr[0]); | |
256 | - break; | |
257 | - | |
258 | - case _speaker_is_on: | |
259 | - case _speaker_is_turning_on: | |
260 | - speaker->connection_status= 0; | |
261 | - break; | |
262 | - | |
263 | - default: | |
264 | - vhalt(csprintf(temporary, "what the hell is #%d!?", speaker->state)); | |
265 | - } | |
266 | - | |
267 | - /* move incoming data into queue, NULL buffer means static */ | |
268 | - if (speaker->queue_size+count<=MAXIMUM_QUEUE_SIZE) | |
269 | - { | |
270 | - if (buffer) | |
271 | - { | |
272 | - BlockMove(buffer, speaker->queue+speaker->queue_size, count); | |
273 | - } | |
274 | - else | |
275 | - { | |
276 | - fill_buffer_with_static((byte *)speaker->queue+speaker->queue_size, count); | |
277 | - } | |
278 | - | |
279 | - speaker->queue_size+= count; | |
280 | - } | |
281 | - else | |
282 | - { | |
283 | - // This really shouldn't log in the non-main thread yet... | |
284 | - logAnomaly3("queue_net_speaker_data() is ignoring data: #%d+#%d>#%d", speaker->queue_size, count, MAXIMUM_QUEUE_SIZE); | |
285 | - } | |
286 | - | |
287 | -#ifdef SNDPLAYDOUBLEBUFFER_DOESNT_SUCK | |
288 | - switch (speaker->state) | |
289 | - { | |
290 | - case _speaker_is_turning_on: | |
291 | - /* check and see if we have enough data to turn on */ | |
292 | - if (speaker->queue_size>=speaker->block_size) | |
293 | - { | |
294 | - OSErr error; | |
295 | - | |
296 | - error= SndPlayDoubleBuffer(speaker->channel, speaker->header); | |
297 | - vwarn(error==noErr, csprintf(temporary, "SndPlayDoubleBuffer(%p,%p)==#%d", speaker->channel, speaker->header, error)); | |
298 | - | |
299 | - speaker->state= _speaker_is_on; | |
300 | - } | |
301 | - break; | |
302 | - } | |
303 | -#endif | |
304 | - } | |
305 | -} | |
306 | - | |
307 | -static void | |
308 | -silence_network_speaker() { | |
309 | - if (speaker) | |
310 | - { | |
311 | - SndCommand command; | |
312 | - OSErr error; | |
313 | - | |
314 | - command.cmd= flushCmd; | |
315 | - command.param1= 0; | |
316 | - command.param2= 0; | |
317 | - error= SndDoImmediate(speaker->channel, &command); | |
318 | - assert(error==noErr); | |
319 | - | |
320 | - command.cmd= quietCmd; | |
321 | - command.param1= 0; | |
322 | - command.param2= 0; | |
323 | - error= SndDoImmediate(speaker->channel, &command); | |
324 | - assert(error==noErr); | |
325 | - | |
326 | - speaker->header->dbhBufferPtr[0]->dbFlags= 0; | |
327 | - speaker->header->dbhBufferPtr[1]->dbFlags= 0; | |
328 | - } | |
329 | -} | |
330 | - | |
331 | -static void | |
332 | -reset_network_speaker() { | |
333 | - if (speaker) | |
334 | - { | |
335 | - /* speaker is off, no missed doublebacks, queue is empty, double buffers are not ready */ | |
336 | - speaker->state= _speaker_is_off; | |
337 | - speaker->connection_status= 0; | |
338 | - speaker->queue_size= 0; | |
339 | - } | |
340 | -} | |
341 | - | |
342 | -void quiet_network_speaker( | |
343 | - void) | |
344 | -{ | |
345 | - silence_network_speaker(); | |
346 | - reset_network_speaker(); | |
347 | -} | |
348 | - | |
349 | -/* because SndPlayDoubleBuffer() is not safe at interrupt time */ | |
350 | -void network_speaker_idle_proc( | |
351 | - void) | |
352 | -{ | |
353 | - if (speaker) | |
354 | - { | |
355 | - switch (speaker->state) | |
356 | - { | |
357 | - case _speaker_is_turning_on: | |
358 | - /* check and see if we have enough data to turn on, if so fill the second | |
359 | - double buffer and call SndPlayDoubleBuffer */ | |
360 | - if (speaker->queue_size>=speaker->block_size) | |
361 | - { | |
362 | - OSErr error; | |
363 | - | |
364 | -#if defined(TARGET_API_MAC_CARBON) | |
365 | - silence_network_speaker(); | |
366 | -#endif | |
367 | - | |
368 | - fill_network_speaker_buffer(speaker->header->dbhBufferPtr[1]); | |
369 | - error= SndPlayDoubleBuffer(speaker->channel, speaker->header); | |
370 | - if(error != noErr) | |
371 | - logAnomaly3("SndPlayDoubleBuffer(%p,%p)==#%d", speaker->channel, speaker->header, error); | |
372 | - | |
373 | - speaker->state= _speaker_is_on; | |
374 | - } | |
375 | - break; | |
376 | - } | |
377 | - } | |
378 | -} | |
379 | - | |
380 | -/* ---------- private code */ | |
381 | - | |
382 | -static void fill_buffer_with_static( | |
383 | - byte *buffer, | |
384 | - short count) | |
385 | -{ | |
386 | - uint16 seed= speaker->random_seed; | |
387 | - | |
388 | - while ((count-=1)>=0) | |
389 | - { | |
390 | - *buffer++= static_cast<byte>(seed) / kStaticAmplitudeReduction; | |
391 | - if (seed&1) seed= (seed>>1)^0xb400; else seed= seed>>1; | |
392 | - } | |
393 | - speaker->random_seed= seed; | |
394 | -} | |
395 | - | |
396 | -// ZZZ: Sorry, but it seems Apple's CarbonSndPlayDoubleBuffer stuff wants a plain | |
397 | -// C function pointer, not a UPP or anything. | |
398 | -static | |
399 | -/* | |
400 | -#if !defined(TARGET_API_MAC_CARBON) | |
401 | -pascal | |
402 | -#endif | |
403 | -*/ | |
404 | -void network_speaker_doubleback_procedure( | |
405 | - SndChannelPtr channel, | |
406 | - SndDoubleBufferPtr doubleBufferPtr) | |
407 | -{ | |
408 | -/* | |
409 | -#ifdef env68k | |
410 | - long old_a5= set_a5(channel->userInfo); *//* set our a5 world *//* | |
411 | -#else | |
412 | -*/ | |
413 | - (void)channel; | |
414 | -//#endif | |
415 | - | |
416 | - fill_network_speaker_buffer(doubleBufferPtr); | |
417 | - | |
418 | -/* | |
419 | -#ifdef env68k | |
420 | - set_a5(old_a5); *//* restore a5 *//* | |
421 | -#endif | |
422 | -*/ | |
423 | -} | |
424 | - | |
425 | -void fill_network_speaker_buffer( | |
426 | - SndDoubleBufferPtr doubleBufferPtr) | |
427 | -{ | |
428 | - int32 available_bytes= MIN(speaker->queue_size, speaker->block_size); | |
429 | - int32 missing_bytes= speaker->block_size-available_bytes; | |
430 | - int32 extra_bytes= speaker->queue_size-available_bytes; | |
431 | - | |
432 | - assert(speaker); | |
433 | - assert(speaker->queue_size>=0&&speaker->queue_size<=MAXIMUM_QUEUE_SIZE); | |
434 | - assert(speaker->block_size>0&&speaker->block_size<=MAXIMUM_DOUBLE_BUFFER_SIZE); | |
435 | - | |
436 | - /* if we donユt have any data, check and see if weユre over the threshold of the number of | |
437 | - times we can miss data; if we are, turn the speaker off and tell the sound manager | |
438 | - this is the last buffer itユs going to get from us */ | |
439 | - if (missing_bytes==speaker->block_size) | |
440 | - { | |
441 | - if ((speaker->connection_status+= 1)>speaker->connection_threshold) | |
442 | - { | |
443 | - speaker->state= _speaker_is_off; | |
444 | - } | |
445 | - } | |
446 | - | |
447 | - /* for better or for worse, fill the waiting buffer */ | |
448 | - if (available_bytes) | |
449 | - BlockMove(speaker->queue, doubleBufferPtr->dbSoundData, available_bytes); | |
450 | - if (missing_bytes) | |
451 | - fill_buffer_with_static((byte *)doubleBufferPtr->dbSoundData+available_bytes, missing_bytes); | |
452 | - if (extra_bytes) | |
453 | - BlockMove(speaker->queue+speaker->block_size, speaker->queue, extra_bytes); | |
454 | - speaker->queue_size-= available_bytes; | |
455 | - | |
456 | - switch (speaker->state) | |
457 | - { | |
458 | - case _speaker_is_off: | |
459 | - doubleBufferPtr->dbFlags|= dbLastBuffer|dbBufferReady; | |
460 | - doubleBufferPtr->dbNumFrames= 0; | |
461 | - break; | |
462 | - | |
463 | - case _speaker_is_on: | |
464 | - case _speaker_is_turning_on: | |
465 | - doubleBufferPtr->dbFlags|= dbBufferReady; | |
466 | - doubleBufferPtr->dbNumFrames= speaker->block_size; | |
467 | - break; | |
468 | - | |
469 | - default: | |
470 | - vhalt(csprintf(temporary, "what the hell is #%d!?", speaker->state)); | |
471 | - } | |
472 | -} | |
473 | - | |
474 | -#endif // !defined(DISABLE_NETWORKING) | |
1 | +/* | |
2 | +NETWORK_SPEAKER.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 | +Sunday, August 14, 1994 1:20:55 AM | |
22 | + | |
23 | +Feb 1, 2003 (Woody Zenfell): | |
24 | + Resurrected for A1. Using Apple's sample CarbonSndPlayDoubleBuffer | |
25 | + to emulate missing SndPlayDoubleBuffer call (in Carbon, anyway). | |
26 | + Using bigger blocks and bigger buffers in general, and SDL-format | |
27 | + network audio (11025Hz 8-bit mono unsigned) instead of MACE 6:1. | |
28 | + | |
29 | + May 28, 2003 (Gregory Smith): | |
30 | + Speex audio decompression | |
31 | + | |
32 | + May 28, 2003 (Woody Zenfell): | |
33 | + Quieter static bursts in netmic audio playback | |
34 | +*/ | |
35 | + | |
36 | +#if !defined(DISABLE_NETWORKING) | |
37 | + | |
38 | +/* | |
39 | +//we should only fill one buffer with static to lower the delay before actually playing incoming data | |
40 | +//we should use our doubleback procedure to fill the buffers | |
41 | +//we should not crap if our initialization fails but queue_ノ() gets called anyway | |
42 | +*/ | |
43 | + | |
44 | +#include <stdlib.h> | |
45 | + | |
46 | +#include "macintosh_cseries.h" | |
47 | + | |
48 | +#if defined(TARGET_API_MAC_CARBON) | |
49 | +#include "CarbonSndPlayDB.h" | |
50 | +#define SndPlayDoubleBuffer CarbonSndPlayDoubleBuffer | |
51 | +#define SndDoImmediate MySndDoImmediate | |
52 | +#endif | |
53 | + | |
54 | +#include "network_sound.h" | |
55 | + | |
56 | +#ifdef SPEEX | |
57 | +#include "network_speex.h" | |
58 | +#endif SPEEX | |
59 | + | |
60 | +#include "Logging.h" | |
61 | + | |
62 | +#ifdef env68k | |
63 | +#pragma segment sound | |
64 | +#endif | |
65 | + | |
66 | +/* ---------- constants */ | |
67 | + | |
68 | +#define MAXIMUM_DOUBLE_BUFFER_SIZE 1024 | |
69 | +#define MAXIMUM_QUEUE_SIZE (16*MAXIMUM_DOUBLE_BUFFER_SIZE) | |
70 | + | |
71 | +// ZZZ: these used to be passed-in at open() time, now we'll just assume them. | |
72 | +// This helps us present a consistent interface with the SDL speaker stuff. | |
73 | +enum { kConnectionThreshhold = 2, kBlockSize = 1024 }; | |
74 | + | |
75 | +// ZZZ: make the static blasts a little less annoying (higher numbers are quieter) | |
76 | +enum { kStaticAmplitudeReduction = 2 }; | |
77 | + | |
78 | +enum /* speaker states */ | |
79 | +{ | |
80 | + _speaker_is_off, /* no sound is being played, weユre waiting for some initial data */ | |
81 | + _speaker_is_turning_on, /* weユve been triggered, but canユt start playing until we get three blocks worth of data */ | |
82 | + _speaker_is_on /* weユre playing sound data in real-time, responding to doubleback requests and everything */ | |
83 | + /* when we donユt get data for a threshold number of doublebacks, we go directly to _speaker_is_off */ | |
84 | +}; | |
85 | + | |
86 | +/* ---------- structures */ | |
87 | + | |
88 | +struct speaker_definition | |
89 | +{ | |
90 | + SndChannelPtr channel; | |
91 | + SndDoubleBufferHeaderPtr header; | |
92 | + | |
93 | + int32 queue_size; /* the number of bytes in the queue */ | |
94 | + Ptr queue; | |
95 | + | |
96 | + short block_size; /* the number of bytes in each of our double buffers */ | |
97 | + short connection_threshold; /* the number of times we can doubleback without data before turning off */ | |
98 | + short connection_status; /* the number of times we have doublebacked without data (reset to zero if we get data) */ | |
99 | + short state; | |
100 | + | |
101 | + uint16 random_seed; | |
102 | +}; | |
103 | + | |
104 | +/* ---------- globals */ | |
105 | + | |
106 | +static struct speaker_definition *speaker= (struct speaker_definition *) NULL; | |
107 | +static SndDoubleBackUPP doubleback_routine_descriptor; | |
108 | + | |
109 | +/* ---------- private code */ | |
110 | + | |
111 | +static void fill_buffer_with_static(byte *buffer, short count); | |
112 | + | |
113 | +static /*pascal*/ void network_speaker_doubleback_procedure(SndChannelPtr channel, SndDoubleBufferPtr doubleBufferPtr); | |
114 | +void fill_network_speaker_buffer(SndDoubleBufferPtr doubleBufferPtr); | |
115 | +static void silence_network_speaker(); | |
116 | +static void reset_network_speaker(); | |
117 | + | |
118 | +/* ---------- code */ | |
119 | + | |
120 | +OSErr open_network_speaker() | |
121 | +{ | |
122 | + short block_size = kBlockSize; | |
123 | + short connection_threshold = kConnectionThreshhold; | |
124 | + OSErr error; | |
125 | + | |
126 | + assert(!speaker); | |
127 | + assert(block_size>0&&block_size<=MAXIMUM_DOUBLE_BUFFER_SIZE); | |
128 | + assert(connection_threshold>1&&connection_threshold<16); | |
129 | + | |
130 | + /* install our atexit cleanup procedure and build a routine descriptor */ | |
131 | + { | |
132 | + static bool initialization= true; | |
133 | + | |
134 | + if (initialization) | |
135 | + { | |
136 | +// ZZZ: Sorry, it looks like the CarbonSndPlayDoubleBuffer stuff really wants a plain C function | |
137 | +// pointer, sitting in the structure typed as a SndDoubleBackUPP. (Don't ask me!) | |
138 | +#if defined(TARGET_API_MAC_CARBON) | |
139 | + doubleback_routine_descriptor= (SndDoubleBackUPP)network_speaker_doubleback_procedure; | |
140 | +#else | |
141 | + // Thomas Herzog fix | |
142 | + #if UNIVERSAL_INTERFACES_VERSION < 0x0340 | |
143 | + doubleback_routine_descriptor= NewSndDoubleBackProc((ProcPtr)network_speaker_doubleback_procedure); | |
144 | + #else | |
145 | + doubleback_routine_descriptor= NewSndDoubleBackUPP(network_speaker_doubleback_procedure); | |
146 | + #endif | |
147 | + // doubleback_routine_descriptor= NewSndDoubleBackProc((ProcPtr)network_speaker_doubleback_procedure); | |
148 | +#endif | |
149 | + assert(doubleback_routine_descriptor); | |
150 | + | |
151 | + atexit(close_network_speaker); | |
152 | + } | |
153 | + } | |
154 | + | |
155 | + speaker= (struct speaker_definition *) NewPtr(sizeof(struct speaker_definition)); | |
156 | + if ((error= MemError())==noErr) | |
157 | + { | |
158 | + speaker->random_seed= 1; | |
159 | + speaker->block_size= block_size; | |
160 | + speaker->connection_threshold= connection_threshold; | |
161 | + | |
162 | + speaker->channel= (SndChannelPtr) NewPtrClear(sizeof(SndChannel)); | |
163 | + speaker->header= (SndDoubleBufferHeaderPtr) NewPtrClear(sizeof(SndDoubleBufferHeader)); | |
164 | + speaker->queue= NewPtr(sizeof(byte)*MAXIMUM_QUEUE_SIZE); | |
165 | + if ((error= MemError())==noErr) | |
166 | + { | |
167 | + SndDoubleBufferHeaderPtr header= speaker->header; | |
168 | + | |
169 | + header->dbhNumChannels= 1; | |
170 | + header->dbhSampleSize= 8; | |
171 | + header->dbhCompressionID= 0; | |
172 | + header->dbhPacketSize= 0; | |
173 | + header->dbhSampleRate= rate11025hz; | |
174 | + header->dbhDoubleBack= doubleback_routine_descriptor; | |
175 | + header->dbhBufferPtr[0]= (SndDoubleBufferPtr) NewPtrClear(sizeof(SndDoubleBuffer)+MAXIMUM_DOUBLE_BUFFER_SIZE); | |
176 | + header->dbhBufferPtr[1]= (SndDoubleBufferPtr) NewPtrClear(sizeof(SndDoubleBuffer)+MAXIMUM_DOUBLE_BUFFER_SIZE); | |
177 | + | |
178 | + if ((error= MemError())==noErr) | |
179 | + { | |
180 | + speaker->channel->qLength= stdQLength; | |
181 | +#ifdef env68k | |
182 | + speaker->channel->userInfo= (long) get_a5(); | |
183 | +#endif | |
184 | + | |
185 | + error= SndNewChannel(&speaker->channel, sampledSynth, initMono|initMACE6, NULL); | |
186 | + if (error==noErr) | |
187 | + { | |
188 | + quiet_network_speaker(); /* to set defaults */ | |
189 | + } | |
190 | + } | |
191 | + } | |
192 | + } | |
193 | + | |
194 | +#ifdef SPEEX | |
195 | + init_speex_decoder(); | |
196 | +#endif | |
197 | + | |
198 | + /* if something went wrong, zero the speaker definition (without freeing any of our memory | |
199 | + like we should) */ | |
200 | + if (error!=noErr) speaker= (struct speaker_definition *) NULL; | |
201 | + | |
202 | + return error; | |
203 | +} | |
204 | + | |
205 | +void close_network_speaker( | |
206 | + void) | |
207 | +{ | |
208 | + if (speaker) | |
209 | + { | |
210 | + OSErr error; | |
211 | + | |
212 | + error= SndDisposeChannel(speaker->channel, true); | |
213 | + warn(error==noErr); | |
214 | + | |
215 | + DisposePtr((Ptr)speaker->header->dbhBufferPtr[0]); | |
216 | + DisposePtr((Ptr)speaker->header->dbhBufferPtr[1]); | |
217 | + DisposePtr((Ptr)speaker->header); | |
218 | + DisposePtr((Ptr)speaker->channel); | |
219 | + DisposePtr((Ptr)speaker->queue); | |
220 | + DisposePtr((Ptr)speaker); | |
221 | + | |
222 | + speaker= (struct speaker_definition *) NULL; | |
223 | + } | |
224 | + | |
225 | +#ifdef SPEEX | |
226 | + destroy_speex_decoder(); | |
227 | +#endif | |
228 | +} | |
229 | + | |
230 | +/* can be called at interrupt time */ | |
231 | +void queue_network_speaker_data( | |
232 | + byte *buffer, | |
233 | + short count) | |
234 | +{ | |
235 | + if (speaker) | |
236 | + { | |
237 | + switch (speaker->state) | |
238 | + { | |
239 | + case _speaker_is_off: | |
240 | + /* we were off but now weユre getting data; fill one double buffer with static and | |
241 | + change our state to _turning_on (weユll wait until we receive another full | |
242 | + double buffer worth of data before beginning to replay) */ | |
243 | + | |
244 | +// ZZZ: CarbonSndPlayDB emulation layer specifically warns against calling | |
245 | +// SndDoImmediate() with a quietCmd at interrupt time - which is what | |
246 | +// quiet_network_speaker() would do. So instead here we try resetting | |
247 | +// the speaker (which ought to be safe I think) now, but delay issuing the | |
248 | +// quiet commands until just before we start playing again. | |
249 | +#if defined(TARGET_API_MAC_CARBON) | |
250 | + reset_network_speaker(); | |
251 | +#else | |
252 | + quiet_network_speaker(); /* we could be playing trailing static */ | |
253 | +#endif | |
254 | + speaker->state= _speaker_is_turning_on; | |
255 | + fill_network_speaker_buffer(speaker->header->dbhBufferPtr[0]); | |
256 | + break; | |
257 | + | |
258 | + case _speaker_is_on: | |
259 | + case _speaker_is_turning_on: | |
260 | + speaker->connection_status= 0; | |
261 | + break; | |
262 | + | |
263 | + default: | |
264 | + vhalt(csprintf(temporary, "what the hell is #%d!?", speaker->state)); | |
265 | + } | |
266 | + | |
267 | + /* move incoming data into queue, NULL buffer means static */ | |
268 | + if (speaker->queue_size+count<=MAXIMUM_QUEUE_SIZE) | |
269 | + { | |
270 | + if (buffer) | |
271 | + { | |
272 | + BlockMove(buffer, speaker->queue+speaker->queue_size, count); | |
273 | + } | |
274 | + else | |
275 | + { | |
276 | + fill_buffer_with_static((byte *)speaker->queue+speaker->queue_size, count); | |
277 | + } | |
278 | + | |
279 | + speaker->queue_size+= count; | |
280 | + } | |
281 | + else | |
282 | + { | |
283 | + // This really shouldn't log in the non-main thread yet... | |
284 | + logAnomaly3("queue_net_speaker_data() is ignoring data: #%d+#%d>#%d", speaker->queue_size, count, MAXIMUM_QUEUE_SIZE); | |
285 | + } | |
286 | + | |
287 | +#ifdef SNDPLAYDOUBLEBUFFER_DOESNT_SUCK | |
288 | + switch (speaker->state) | |
289 | + { | |
290 | + case _speaker_is_turning_on: | |
291 | + /* check and see if we have enough data to turn on */ | |
292 | + if (speaker->queue_size>=speaker->block_size) | |
293 | + { | |
294 | + OSErr error; | |
295 | + | |
296 | + error= SndPlayDoubleBuffer(speaker->channel, speaker->header); | |
297 | + vwarn(error==noErr, csprintf(temporary, "SndPlayDoubleBuffer(%p,%p)==#%d", speaker->channel, speaker->header, error)); | |
298 | + | |
299 | + speaker->state= _speaker_is_on; | |
300 | + } | |
301 | + break; | |
302 | + } | |
303 | +#endif | |
304 | + } | |
305 | +} | |
306 | + | |
307 | +static void | |
308 | +silence_network_speaker() { | |
309 | + if (speaker) | |
310 | + { | |
311 | + SndCommand command; | |
312 | + OSErr error; | |
313 | + | |
314 | + command.cmd= flushCmd; | |
315 | + command.param1= 0; | |
316 | + command.param2= 0; | |
317 | + error= SndDoImmediate(speaker->channel, &command); | |
318 | + assert(error==noErr); | |
319 | + | |
320 | + command.cmd= quietCmd; | |
321 | + command.param1= 0; | |
322 | + command.param2= 0; | |
323 | + error= SndDoImmediate(speaker->channel, &command); | |
324 | + assert(error==noErr); | |
325 | + | |
326 | + speaker->header->dbhBufferPtr[0]->dbFlags= 0; | |
327 | + speaker->header->dbhBufferPtr[1]->dbFlags= 0; | |
328 | + } | |
329 | +} | |
330 | + | |
331 | +static void | |
332 | +reset_network_speaker() { | |
333 | + if (speaker) | |
334 | + { | |
335 | + /* speaker is off, no missed doublebacks, queue is empty, double buffers are not ready */ | |
336 | + speaker->state= _speaker_is_off; | |
337 | + speaker->connection_status= 0; | |
338 | + speaker->queue_size= 0; | |
339 | + } | |
340 | +} | |
341 | + | |
342 | +void quiet_network_speaker( | |
343 | + void) | |
344 | +{ | |
345 | + silence_network_speaker(); | |
346 | + reset_network_speaker(); | |
347 | +} | |
348 | + | |
349 | +/* because SndPlayDoubleBuffer() is not safe at interrupt time */ | |
350 | +void network_speaker_idle_proc( | |
351 | + void) | |
352 | +{ | |
353 | + if (speaker) | |
354 | + { | |
355 | + switch (speaker->state) | |
356 | + { | |
357 | + case _speaker_is_turning_on: | |
358 | + /* check and see if we have enough data to turn on, if so fill the second | |
359 | + double buffer and call SndPlayDoubleBuffer */ | |
360 | + if (speaker->queue_size>=speaker->block_size) | |
361 | + { | |
362 | + OSErr error; | |
363 | + | |
364 | +#if defined(TARGET_API_MAC_CARBON) | |
365 | + silence_network_speaker(); | |
366 | +#endif | |
367 | + | |
368 | + fill_network_speaker_buffer(speaker->header->dbhBufferPtr[1]); | |
369 | + error= SndPlayDoubleBuffer(speaker->channel, speaker->header); | |
370 | + if(error != noErr) | |
371 | + logAnomaly3("SndPlayDoubleBuffer(%p,%p)==#%d", speaker->channel, speaker->header, error); | |
372 | + | |
373 | + speaker->state= _speaker_is_on; | |
374 | + } | |
375 | + break; | |
376 | + } | |
377 | + } | |
378 | +} | |
379 | + | |
380 | +/* ---------- private code */ | |
381 | + | |
382 | +static void fill_buffer_with_static( | |
383 | + byte *buffer, | |
384 | + short count) | |
385 | +{ | |
386 | + uint16 seed= speaker->random_seed; | |
387 | + | |
388 | + while ((count-=1)>=0) | |
389 | + { | |
390 | + *buffer++= static_cast<byte>(seed) / kStaticAmplitudeReduction; | |
391 | + if (seed&1) seed= (seed>>1)^0xb400; else seed= seed>>1; | |
392 | + } | |
393 | + speaker->random_seed= seed; | |
394 | +} | |
395 | + | |
396 | +// ZZZ: Sorry, but it seems Apple's CarbonSndPlayDoubleBuffer stuff wants a plain | |
397 | +// C function pointer, not a UPP or anything. | |
398 | +static | |
399 | +/* | |
400 | +#if !defined(TARGET_API_MAC_CARBON) | |
401 | +pascal | |
402 | +#endif | |
403 | +*/ | |
404 | +void network_speaker_doubleback_procedure( | |
405 | + SndChannelPtr channel, | |
406 | + SndDoubleBufferPtr doubleBufferPtr) | |
407 | +{ | |
408 | +/* | |
409 | +#ifdef env68k | |
410 | + long old_a5= set_a5(channel->userInfo); *//* set our a5 world *//* | |
411 | +#else | |
412 | +*/ | |
413 | + (void)channel; | |
414 | +//#endif | |
415 | + | |
416 | + fill_network_speaker_buffer(doubleBufferPtr); | |
417 | + | |
418 | +/* | |
419 | +#ifdef env68k | |
420 | + set_a5(old_a5); *//* restore a5 *//* | |
421 | +#endif | |
422 | +*/ | |
423 | +} | |
424 | + | |
425 | +void fill_network_speaker_buffer( | |
426 | + SndDoubleBufferPtr doubleBufferPtr) | |
427 | +{ | |
428 | + int32 available_bytes= MIN(speaker->queue_size, speaker->block_size); | |
429 | + int32 missing_bytes= speaker->block_size-available_bytes; | |
430 | + int32 extra_bytes= speaker->queue_size-available_bytes; | |
431 | + | |
432 | + assert(speaker); | |
433 | + assert(speaker->queue_size>=0&&speaker->queue_size<=MAXIMUM_QUEUE_SIZE); | |
434 | + assert(speaker->block_size>0&&speaker->block_size<=MAXIMUM_DOUBLE_BUFFER_SIZE); | |
435 | + | |
436 | + /* if we donユt have any data, check and see if weユre over the threshold of the number of | |
437 | + times we can miss data; if we are, turn the speaker off and tell the sound manager | |
438 | + this is the last buffer itユs going to get from us */ | |
439 | + if (missing_bytes==speaker->block_size) | |
440 | + { | |
441 | + if ((speaker->connection_status+= 1)>speaker->connection_threshold) | |
442 | + { | |
443 | + speaker->state= _speaker_is_off; | |
444 | + } | |
445 | + } | |
446 | + | |
447 | + /* for better or for worse, fill the waiting buffer */ | |
448 | + if (available_bytes) | |
449 | + BlockMove(speaker->queue, doubleBufferPtr->dbSoundData, available_bytes); | |
450 | + if (missing_bytes) | |
451 | + fill_buffer_with_static((byte *)doubleBufferPtr->dbSoundData+available_bytes, missing_bytes); | |
452 | + if (extra_bytes) | |
453 | + BlockMove(speaker->queue+speaker->block_size, speaker->queue, extra_bytes); | |
454 | + speaker->queue_size-= available_bytes; | |
455 | + | |
456 | + switch (speaker->state) | |
457 | + { | |
458 | + case _speaker_is_off: | |
459 | + doubleBufferPtr->dbFlags|= dbLastBuffer|dbBufferReady; | |
460 | + doubleBufferPtr->dbNumFrames= 0; | |
461 | + break; | |
462 | + | |
463 | + case _speaker_is_on: | |
464 | + case _speaker_is_turning_on: | |
465 | + doubleBufferPtr->dbFlags|= dbBufferReady; | |
466 | + doubleBufferPtr->dbNumFrames= speaker->block_size; | |
467 | + break; | |
468 | + | |
469 | + default: | |
470 | + vhalt(csprintf(temporary, "what the hell is #%d!?", speaker->state)); | |
471 | + } | |
472 | +} | |
473 | + | |
474 | +#endif // !defined(DISABLE_NETWORKING) |
@@ -1,120 +1,120 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | -*/ | |
21 | -/* | |
22 | - * network_dummy.cpp - Dummy network functions | |
23 | - */ | |
24 | - | |
25 | -#include "cseries.h" | |
26 | -#include "map.h" | |
27 | -#include "network.h" | |
28 | -#include "network_games.h" | |
29 | - | |
30 | - | |
31 | -void NetExit(void) | |
32 | -{ | |
33 | -} | |
34 | - | |
35 | -bool NetSync(void) | |
36 | -{ | |
37 | - return true; | |
38 | -} | |
39 | - | |
40 | -bool NetUnSync(void) | |
41 | -{ | |
42 | - return true; | |
43 | -} | |
44 | - | |
45 | -short NetGetLocalPlayerIndex(void) | |
46 | -{ | |
47 | - return 0; | |
48 | -} | |
49 | - | |
50 | -short NetGetPlayerIdentifier(short player_index) | |
51 | -{ | |
52 | - return 0; | |
53 | -} | |
54 | - | |
55 | -short NetGetNumberOfPlayers(void) | |
56 | -{ | |
57 | - return 1; | |
58 | -} | |
59 | - | |
60 | -void *NetGetPlayerData(short player_index) | |
61 | -{ | |
62 | - return NULL; | |
63 | -} | |
64 | - | |
65 | -void *NetGetGameData(void) | |
66 | -{ | |
67 | - return NULL; | |
68 | -} | |
69 | - | |
70 | -bool NetChangeMap(struct entry_point *entry) | |
71 | -{ | |
72 | - return false; | |
73 | -} | |
74 | - | |
75 | -int32 NetGetNetTime(void) | |
76 | -{ | |
77 | - return 0; | |
78 | -} | |
79 | - | |
80 | -void display_net_game_stats(void) | |
81 | -{ | |
82 | -} | |
83 | - | |
84 | -bool network_gather(void) | |
85 | -{ | |
86 | - return false; | |
87 | -} | |
88 | - | |
89 | -int network_join(void) | |
90 | -{ | |
91 | - return false; | |
92 | -} | |
93 | - | |
94 | -void network_speaker_idle_proc(void) | |
95 | -{ | |
96 | -} | |
97 | - | |
98 | -void network_microphone_idle_proc(void) | |
99 | -{ | |
100 | -} | |
101 | - | |
102 | -bool current_game_has_balls(void) | |
103 | -{ | |
104 | - return false; | |
105 | -} | |
106 | - | |
107 | -bool NetAllowBehindview(void) | |
108 | -{ | |
109 | - return false; | |
110 | -} | |
111 | - | |
112 | -bool NetAllowCrosshair(void) | |
113 | -{ | |
114 | - return false; | |
115 | -} | |
116 | - | |
117 | -bool NetAllowTunnelVision(void) | |
118 | -{ | |
119 | - return false; | |
120 | -} | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | +*/ | |
21 | +/* | |
22 | + * network_dummy.cpp - Dummy network functions | |
23 | + */ | |
24 | + | |
25 | +#include "cseries.h" | |
26 | +#include "map.h" | |
27 | +#include "network.h" | |
28 | +#include "network_games.h" | |
29 | + | |
30 | + | |
31 | +void NetExit(void) | |
32 | +{ | |
33 | +} | |
34 | + | |
35 | +bool NetSync(void) | |
36 | +{ | |
37 | + return true; | |
38 | +} | |
39 | + | |
40 | +bool NetUnSync(void) | |
41 | +{ | |
42 | + return true; | |
43 | +} | |
44 | + | |
45 | +short NetGetLocalPlayerIndex(void) | |
46 | +{ | |
47 | + return 0; | |
48 | +} | |
49 | + | |
50 | +short NetGetPlayerIdentifier(short player_index) | |
51 | +{ | |
52 | + return 0; | |
53 | +} | |
54 | + | |
55 | +short NetGetNumberOfPlayers(void) | |
56 | +{ | |
57 | + return 1; | |
58 | +} | |
59 | + | |
60 | +void *NetGetPlayerData(short player_index) | |
61 | +{ | |
62 | + return NULL; | |
63 | +} | |
64 | + | |
65 | +void *NetGetGameData(void) | |
66 | +{ | |
67 | + return NULL; | |
68 | +} | |
69 | + | |
70 | +bool NetChangeMap(struct entry_point *entry) | |
71 | +{ | |
72 | + return false; | |
73 | +} | |
74 | + | |
75 | +int32 NetGetNetTime(void) | |
76 | +{ | |
77 | + return 0; | |
78 | +} | |
79 | + | |
80 | +void display_net_game_stats(void) | |
81 | +{ | |
82 | +} | |
83 | + | |
84 | +bool network_gather(void) | |
85 | +{ | |
86 | + return false; | |
87 | +} | |
88 | + | |
89 | +int network_join(void) | |
90 | +{ | |
91 | + return false; | |
92 | +} | |
93 | + | |
94 | +void network_speaker_idle_proc(void) | |
95 | +{ | |
96 | +} | |
97 | + | |
98 | +void network_microphone_idle_proc(void) | |
99 | +{ | |
100 | +} | |
101 | + | |
102 | +bool current_game_has_balls(void) | |
103 | +{ | |
104 | + return false; | |
105 | +} | |
106 | + | |
107 | +bool NetAllowBehindview(void) | |
108 | +{ | |
109 | + return false; | |
110 | +} | |
111 | + | |
112 | +bool NetAllowCrosshair(void) | |
113 | +{ | |
114 | + return false; | |
115 | +} | |
116 | + | |
117 | +bool NetAllowTunnelVision(void) | |
118 | +{ | |
119 | + return false; | |
120 | +} |
@@ -1,238 +1,238 @@ | ||
1 | -/* | |
2 | - * network_dialog_widgets_sdl.h | |
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 | - * Custom widgets for network-related dialogs in the SDL version. | |
22 | - * | |
23 | - * Created by Woody Zenfell, III on Fri Sep 28 2001. | |
24 | - * | |
25 | - * Mar 1, 2002 (Woody Zenfell): added w_entry_point_selector widget. | |
26 | - */ | |
27 | - | |
28 | -#ifndef NETWORK_DIALOG_WIDGETS_SDL_H | |
29 | -#define NETWORK_DIALOG_WIDGETS_SDL_H | |
30 | - | |
31 | -#include "sdl_widgets.h" | |
32 | -#include "SSLP_API.h" | |
33 | - | |
34 | -#include "player.h" // for MAXIMUM_PLAYER_NAME_LENGTH | |
35 | -#include "PlayerImage_sdl.h" | |
36 | -#include "network_dialogs.h" // for net_rank | |
37 | - | |
38 | - | |
39 | -//////// w_found_players //////// | |
40 | -// This lists the "players on network" in the Gather box. It manages the SSLP callback information | |
41 | -// to produce/update its list. (Well, I guess it depends on someone setting up simple callbacks | |
42 | -// that forward to w_found_players::found_player(), etc.) | |
43 | -struct player_info; | |
44 | - | |
45 | -class w_found_players; | |
46 | - | |
47 | -typedef void (*player_selected_callback_t)(w_found_players*, prospective_joiner_info player); | |
48 | - | |
49 | -class w_found_players : public w_list<prospective_joiner_info> { | |
50 | -public: | |
51 | - w_found_players(int width, int numRows) : | |
52 | - w_list<prospective_joiner_info>(listed_players, width, numRows, 0), player_selected_callback(NULL) | |
53 | - { num_items = 0; } | |
54 | - // must update num_items here since listed_players had not been initialized earlier when passed to w_list<>() | |
55 | - | |
56 | - void found_player(prospective_joiner_info &player); | |
57 | - void update_player(prospective_joiner_info &player); | |
58 | - void hide_player(const prospective_joiner_info &player); | |
59 | - | |
60 | - void item_selected(); | |
61 | - | |
62 | - // currently, this depends on the callback removing the item passed to it. yuck, but it works for | |
63 | - // our purposes and is a little easier to implement ;) | |
64 | - void callback_on_all_items(); | |
65 | - | |
66 | - void set_player_selected_callback(player_selected_callback_t callback) { player_selected_callback = callback; } | |
67 | - | |
68 | -private: | |
69 | - vector<prospective_joiner_info> found_players; // players that are out there | |
70 | - vector<prospective_joiner_info> hidden_players; // players we don't want displayed - may include some not in found_players. | |
71 | - vector<prospective_joiner_info> listed_players; // {found_players} - {hidden_players} (keyed by particular found instance, not name | |
72 | - // nor address) | |
73 | - | |
74 | - player_selected_callback_t player_selected_callback; // called when a player is clicked on | |
75 | - | |
76 | - void list_player(prospective_joiner_info &player); | |
77 | - void unlist_player(const prospective_joiner_info &player); | |
78 | - | |
79 | - void draw_item(vector<prospective_joiner_info>::const_iterator i, SDL_Surface* s, int16 x, int16 y, uint16 width, bool selected) const; | |
80 | -}; | |
81 | - | |
82 | - | |
83 | -//////// w_players_in_game2 //////// | |
84 | -// This serves both as a "who's in the game?" widget for gather/join AND as the graph widget in | |
85 | -// the Postgame Carnage Report. Yes, there WAS a w_players_in_game, that was used just for the | |
86 | -// former purpose, but it's no longer relevant. | |
87 | -struct player_entry2 { | |
88 | - char player_name[MAXIMUM_PLAYER_NAME_LENGTH + 1]; | |
89 | - uint32 name_pixel_color; | |
90 | - int16 name_width; | |
91 | - PlayerImage* player_image; | |
92 | -}; | |
93 | - | |
94 | -struct bar_info; | |
95 | -class TextLayoutHelper; | |
96 | -class w_players_in_game2; | |
97 | - | |
98 | -typedef void (*element_clicked_callback_t)(w_players_in_game2* inWPIG2, // widget clicked | |
99 | - bool inTeam, // team? (or player) | |
100 | - bool inGraph, // postgame carnage report? | |
101 | - bool inScores, // showing scores? (or carnage) | |
102 | - size_t inDrawIndex, // starting with 0 at left | |
103 | - int inPlayerIndexOrTeamColor); // meaning depends on inTeam | |
104 | - | |
105 | -class w_players_in_game2 : public widget { | |
106 | -public: | |
107 | - // pass "true" for a widget that takes more vertical space (for postgame carnage report) | |
108 | - w_players_in_game2(bool inPostgameLayout); | |
109 | - | |
110 | - // Update from dynamic world (e.g. postgame)? (else from topology) | |
111 | - void update_display(bool inFromDynamicWorld = false); | |
112 | - | |
113 | - // Call this at least once when there is valid topology data (no need if update_display fromDynamicWorld) | |
114 | - void start_displaying_actual_information() { displaying_actual_information = true; } | |
115 | - | |
116 | - virtual void draw(SDL_Surface *s) const; | |
117 | - | |
118 | - // User clicked in widget - element_clicked_callback, if set, will be invoked | |
119 | - // if user clicked reasonably close to a player icon. NOTE currently, despite | |
120 | - // appearances, callback will NOT be invoked if not showing a postgame report. | |
121 | - virtual void click(int x, int y); | |
122 | - | |
123 | - // Widget selectable? | |
124 | - virtual bool is_selectable(void) const {return false;} | |
125 | - | |
126 | - void set_graph_data(const net_rank* inRankings, int inNumRankings, int inSelectedPlayer, | |
127 | - bool inClumpPlayersByTeam, bool inDrawScoresNotCarnage); | |
128 | - | |
129 | - void set_element_clicked_callback(element_clicked_callback_t inCallback) | |
130 | - { element_clicked_callback = inCallback; } | |
131 | - | |
132 | - ~w_players_in_game2(); | |
133 | - | |
134 | - bool placeable_implemented() { return true; } | |
135 | - | |
136 | -protected: | |
137 | - // Local storage | |
138 | - vector<player_entry2> player_entries; | |
139 | - bool displaying_actual_information; | |
140 | - bool postgame_layout; | |
141 | - element_clicked_callback_t element_clicked_callback; | |
142 | - | |
143 | - // Stuff in support of postgame carnage report | |
144 | - bool draw_carnage_graph; | |
145 | - vector<int> players_on_team[NUMBER_OF_TEAM_COLORS]; // (note array of vectors) hold indices into player_entries | |
146 | - net_rank net_rankings[MAXIMUM_NUMBER_OF_PLAYERS]; | |
147 | - size_t num_valid_net_rankings; | |
148 | - int selected_player; | |
149 | - bool clump_players_by_team; | |
150 | - bool draw_scores_not_carnage; | |
151 | - | |
152 | - // Local methods | |
153 | - void draw_player_icon(SDL_Surface* s, size_t rank_index, int center_x) const; | |
154 | - void draw_player_icons_separately(SDL_Surface* s) const; | |
155 | - void draw_player_icons_clumped(SDL_Surface* s) const; | |
156 | - void draw_player_names_separately(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const; | |
157 | - void draw_player_names_clumped(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const; | |
158 | - int find_maximum_bar_value() const; | |
159 | - void draw_bar_or_bars(SDL_Surface* s, size_t rank_index, int center_x, int maximum_value, vector<bar_info>& outBarInfos) const; | |
160 | - void draw_bars_separately(SDL_Surface* s, vector<bar_info>& outBarInfos) const; | |
161 | - void draw_bars_clumped(SDL_Surface* s, vector<bar_info>& outBarInfos) const; | |
162 | - void draw_bar_labels(SDL_Surface* s, const vector<bar_info>& inBarInfos, TextLayoutHelper& ioTextLayoutHelper) const; | |
163 | - void draw_carnage_totals(SDL_Surface* s) const; | |
164 | - void draw_carnage_legend(SDL_Surface* s) const; | |
165 | - | |
166 | - void draw_bar(SDL_Surface* s, int inCenterX, int inBarColorIndex, int inBarValue, int inMaxValue, bar_info& outBarInfo) const; | |
167 | - | |
168 | - void clear_vector(); | |
169 | - | |
170 | - // Class (static) methods | |
171 | -}; | |
172 | - | |
173 | - | |
174 | - | |
175 | -////// w_entry_point_selector ////// | |
176 | -// Helps user choose a level that works for a particular game type | |
177 | -// from the currently active map file | |
178 | -class w_entry_point_selector : public w_select_button { | |
179 | -public: | |
180 | - w_entry_point_selector(size_t inGameType, int16 inLevelNumber) | |
181 | - : w_select_button(mEntryPoint.level_name, gotSelectedCallback, NULL), mGameType(UNONE) | |
182 | - { | |
183 | - mEntryPoint.level_number = inLevelNumber; | |
184 | - set_arg(this); | |
185 | - | |
186 | - setGameType(inGameType); | |
187 | - } | |
188 | - | |
189 | - // Adjusts entry point if new game type is not supported by current entry point. | |
190 | - void setGameType(size_t inGameType) { | |
191 | - if(inGameType != mGameType) { | |
192 | - mGameType = inGameType; | |
193 | - validateEntryPoint(); | |
194 | - } | |
195 | - } | |
196 | - | |
197 | - // Choose first available entry point for current game type (good if map file changed) | |
198 | - void reset() { | |
199 | - mEntryPoint.level_number = NONE; | |
200 | - validateEntryPoint(); | |
201 | - } | |
202 | - | |
203 | - // Choose entry point matching level number, if possible | |
204 | - void setLevelNumber(int16 inLevelNumber) { | |
205 | - mEntryPoint.level_number = inLevelNumber; | |
206 | - validateEntryPoint(); | |
207 | - } | |
208 | - | |
209 | - // Return currently-chosen entry point. | |
210 | - const entry_point& getEntryPoint() { | |
211 | - return mEntryPoint; | |
212 | - } | |
213 | - | |
214 | - // User can cursor left or right to cycle through options. | |
215 | - virtual void event(SDL_Event& e); | |
216 | - | |
217 | -private: | |
218 | - // Pop up a box (if enough choices) and let user choose a level. | |
219 | - void gotSelected(); | |
220 | - | |
221 | - // Bounces callback to arg->gotSelected() | |
222 | - static void gotSelectedCallback(void *arg); | |
223 | - | |
224 | - // This uses the currently-set level number and currently-set game type to | |
225 | - // (re-)lookup the entry point. Use if map file or game type changes. | |
226 | - // Sets entry point to entry point matching level number, if possible, | |
227 | - // or the first available if not. | |
228 | - // If no entry points are available, sets entry point level number to NONE. | |
229 | - void validateEntryPoint(); | |
230 | - | |
231 | - entry_point mEntryPoint; | |
232 | - size_t mGameType; | |
233 | - size_t mCurrentIndex; | |
234 | - vector<entry_point> mEntryPoints; | |
235 | -}; | |
236 | - | |
237 | - | |
238 | -#endif//NETWORK_DIALOG_WIDGETS_SDL_H | |
1 | +/* | |
2 | + * network_dialog_widgets_sdl.h | |
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 | + * Custom widgets for network-related dialogs in the SDL version. | |
22 | + * | |
23 | + * Created by Woody Zenfell, III on Fri Sep 28 2001. | |
24 | + * | |
25 | + * Mar 1, 2002 (Woody Zenfell): added w_entry_point_selector widget. | |
26 | + */ | |
27 | + | |
28 | +#ifndef NETWORK_DIALOG_WIDGETS_SDL_H | |
29 | +#define NETWORK_DIALOG_WIDGETS_SDL_H | |
30 | + | |
31 | +#include "sdl_widgets.h" | |
32 | +#include "SSLP_API.h" | |
33 | + | |
34 | +#include "player.h" // for MAXIMUM_PLAYER_NAME_LENGTH | |
35 | +#include "PlayerImage_sdl.h" | |
36 | +#include "network_dialogs.h" // for net_rank | |
37 | + | |
38 | + | |
39 | +//////// w_found_players //////// | |
40 | +// This lists the "players on network" in the Gather box. It manages the SSLP callback information | |
41 | +// to produce/update its list. (Well, I guess it depends on someone setting up simple callbacks | |
42 | +// that forward to w_found_players::found_player(), etc.) | |
43 | +struct player_info; | |
44 | + | |
45 | +class w_found_players; | |
46 | + | |
47 | +typedef void (*player_selected_callback_t)(w_found_players*, prospective_joiner_info player); | |
48 | + | |
49 | +class w_found_players : public w_list<prospective_joiner_info> { | |
50 | +public: | |
51 | + w_found_players(int width, int numRows) : | |
52 | + w_list<prospective_joiner_info>(listed_players, width, numRows, 0), player_selected_callback(NULL) | |
53 | + { num_items = 0; } | |
54 | + // must update num_items here since listed_players had not been initialized earlier when passed to w_list<>() | |
55 | + | |
56 | + void found_player(prospective_joiner_info &player); | |
57 | + void update_player(prospective_joiner_info &player); | |
58 | + void hide_player(const prospective_joiner_info &player); | |
59 | + | |
60 | + void item_selected(); | |
61 | + | |
62 | + // currently, this depends on the callback removing the item passed to it. yuck, but it works for | |
63 | + // our purposes and is a little easier to implement ;) | |
64 | + void callback_on_all_items(); | |
65 | + | |
66 | + void set_player_selected_callback(player_selected_callback_t callback) { player_selected_callback = callback; } | |
67 | + | |
68 | +private: | |
69 | + vector<prospective_joiner_info> found_players; // players that are out there | |
70 | + vector<prospective_joiner_info> hidden_players; // players we don't want displayed - may include some not in found_players. | |
71 | + vector<prospective_joiner_info> listed_players; // {found_players} - {hidden_players} (keyed by particular found instance, not name | |
72 | + // nor address) | |
73 | + | |
74 | + player_selected_callback_t player_selected_callback; // called when a player is clicked on | |
75 | + | |
76 | + void list_player(prospective_joiner_info &player); | |
77 | + void unlist_player(const prospective_joiner_info &player); | |
78 | + | |
79 | + void draw_item(vector<prospective_joiner_info>::const_iterator i, SDL_Surface* s, int16 x, int16 y, uint16 width, bool selected) const; | |
80 | +}; | |
81 | + | |
82 | + | |
83 | +//////// w_players_in_game2 //////// | |
84 | +// This serves both as a "who's in the game?" widget for gather/join AND as the graph widget in | |
85 | +// the Postgame Carnage Report. Yes, there WAS a w_players_in_game, that was used just for the | |
86 | +// former purpose, but it's no longer relevant. | |
87 | +struct player_entry2 { | |
88 | + char player_name[MAXIMUM_PLAYER_NAME_LENGTH + 1]; | |
89 | + uint32 name_pixel_color; | |
90 | + int16 name_width; | |
91 | + PlayerImage* player_image; | |
92 | +}; | |
93 | + | |
94 | +struct bar_info; | |
95 | +class TextLayoutHelper; | |
96 | +class w_players_in_game2; | |
97 | + | |
98 | +typedef void (*element_clicked_callback_t)(w_players_in_game2* inWPIG2, // widget clicked | |
99 | + bool inTeam, // team? (or player) | |
100 | + bool inGraph, // postgame carnage report? | |
101 | + bool inScores, // showing scores? (or carnage) | |
102 | + size_t inDrawIndex, // starting with 0 at left | |
103 | + int inPlayerIndexOrTeamColor); // meaning depends on inTeam | |
104 | + | |
105 | +class w_players_in_game2 : public widget { | |
106 | +public: | |
107 | + // pass "true" for a widget that takes more vertical space (for postgame carnage report) | |
108 | + w_players_in_game2(bool inPostgameLayout); | |
109 | + | |
110 | + // Update from dynamic world (e.g. postgame)? (else from topology) | |
111 | + void update_display(bool inFromDynamicWorld = false); | |
112 | + | |
113 | + // Call this at least once when there is valid topology data (no need if update_display fromDynamicWorld) | |
114 | + void start_displaying_actual_information() { displaying_actual_information = true; } | |
115 | + | |
116 | + virtual void draw(SDL_Surface *s) const; | |
117 | + | |
118 | + // User clicked in widget - element_clicked_callback, if set, will be invoked | |
119 | + // if user clicked reasonably close to a player icon. NOTE currently, despite | |
120 | + // appearances, callback will NOT be invoked if not showing a postgame report. | |
121 | + virtual void click(int x, int y); | |
122 | + | |
123 | + // Widget selectable? | |
124 | + virtual bool is_selectable(void) const {return false;} | |
125 | + | |
126 | + void set_graph_data(const net_rank* inRankings, int inNumRankings, int inSelectedPlayer, | |
127 | + bool inClumpPlayersByTeam, bool inDrawScoresNotCarnage); | |
128 | + | |
129 | + void set_element_clicked_callback(element_clicked_callback_t inCallback) | |
130 | + { element_clicked_callback = inCallback; } | |
131 | + | |
132 | + ~w_players_in_game2(); | |
133 | + | |
134 | + bool placeable_implemented() { return true; } | |
135 | + | |
136 | +protected: | |
137 | + // Local storage | |
138 | + vector<player_entry2> player_entries; | |
139 | + bool displaying_actual_information; | |
140 | + bool postgame_layout; | |
141 | + element_clicked_callback_t element_clicked_callback; | |
142 | + | |
143 | + // Stuff in support of postgame carnage report | |
144 | + bool draw_carnage_graph; | |
145 | + vector<int> players_on_team[NUMBER_OF_TEAM_COLORS]; // (note array of vectors) hold indices into player_entries | |
146 | + net_rank net_rankings[MAXIMUM_NUMBER_OF_PLAYERS]; | |
147 | + size_t num_valid_net_rankings; | |
148 | + int selected_player; | |
149 | + bool clump_players_by_team; | |
150 | + bool draw_scores_not_carnage; | |
151 | + | |
152 | + // Local methods | |
153 | + void draw_player_icon(SDL_Surface* s, size_t rank_index, int center_x) const; | |
154 | + void draw_player_icons_separately(SDL_Surface* s) const; | |
155 | + void draw_player_icons_clumped(SDL_Surface* s) const; | |
156 | + void draw_player_names_separately(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const; | |
157 | + void draw_player_names_clumped(SDL_Surface* s, TextLayoutHelper& ioTextLayoutHelper) const; | |
158 | + int find_maximum_bar_value() const; | |
159 | + void draw_bar_or_bars(SDL_Surface* s, size_t rank_index, int center_x, int maximum_value, vector<bar_info>& outBarInfos) const; | |
160 | + void draw_bars_separately(SDL_Surface* s, vector<bar_info>& outBarInfos) const; | |
161 | + void draw_bars_clumped(SDL_Surface* s, vector<bar_info>& outBarInfos) const; | |
162 | + void draw_bar_labels(SDL_Surface* s, const vector<bar_info>& inBarInfos, TextLayoutHelper& ioTextLayoutHelper) const; | |
163 | + void draw_carnage_totals(SDL_Surface* s) const; | |
164 | + void draw_carnage_legend(SDL_Surface* s) const; | |
165 | + | |
166 | + void draw_bar(SDL_Surface* s, int inCenterX, int inBarColorIndex, int inBarValue, int inMaxValue, bar_info& outBarInfo) const; | |
167 | + | |
168 | + void clear_vector(); | |
169 | + | |
170 | + // Class (static) methods | |
171 | +}; | |
172 | + | |
173 | + | |
174 | + | |
175 | +////// w_entry_point_selector ////// | |
176 | +// Helps user choose a level that works for a particular game type | |
177 | +// from the currently active map file | |
178 | +class w_entry_point_selector : public w_select_button { | |
179 | +public: | |
180 | + w_entry_point_selector(size_t inGameType, int16 inLevelNumber) | |
181 | + : w_select_button(mEntryPoint.level_name, gotSelectedCallback, NULL), mGameType(UNONE) | |
182 | + { | |
183 | + mEntryPoint.level_number = inLevelNumber; | |
184 | + set_arg(this); | |
185 | + | |
186 | + setGameType(inGameType); | |
187 | + } | |
188 | + | |
189 | + // Adjusts entry point if new game type is not supported by current entry point. | |
190 | + void setGameType(size_t inGameType) { | |
191 | + if(inGameType != mGameType) { | |
192 | + mGameType = inGameType; | |
193 | + validateEntryPoint(); | |
194 | + } | |
195 | + } | |
196 | + | |
197 | + // Choose first available entry point for current game type (good if map file changed) | |
198 | + void reset() { | |
199 | + mEntryPoint.level_number = NONE; | |
200 | + validateEntryPoint(); | |
201 | + } | |
202 | + | |
203 | + // Choose entry point matching level number, if possible | |
204 | + void setLevelNumber(int16 inLevelNumber) { | |
205 | + mEntryPoint.level_number = inLevelNumber; | |
206 | + validateEntryPoint(); | |
207 | + } | |
208 | + | |
209 | + // Return currently-chosen entry point. | |
210 | + const entry_point& getEntryPoint() { | |
211 | + return mEntryPoint; | |
212 | + } | |
213 | + | |
214 | + // User can cursor left or right to cycle through options. | |
215 | + virtual void event(SDL_Event& e); | |
216 | + | |
217 | +private: | |
218 | + // Pop up a box (if enough choices) and let user choose a level. | |
219 | + void gotSelected(); | |
220 | + | |
221 | + // Bounces callback to arg->gotSelected() | |
222 | + static void gotSelectedCallback(void *arg); | |
223 | + | |
224 | + // This uses the currently-set level number and currently-set game type to | |
225 | + // (re-)lookup the entry point. Use if map file or game type changes. | |
226 | + // Sets entry point to entry point matching level number, if possible, | |
227 | + // or the first available if not. | |
228 | + // If no entry points are available, sets entry point level number to NONE. | |
229 | + void validateEntryPoint(); | |
230 | + | |
231 | + entry_point mEntryPoint; | |
232 | + size_t mGameType; | |
233 | + size_t mCurrentIndex; | |
234 | + vector<entry_point> mEntryPoints; | |
235 | +}; | |
236 | + | |
237 | + | |
238 | +#endif//NETWORK_DIALOG_WIDGETS_SDL_H |
@@ -1,220 +1,220 @@ | ||
1 | -/* | |
2 | - * network_speaker_sdl.cpp | |
3 | - * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | - | |
5 | - Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | - * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | - * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | - * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | - * thus must observe the GPL terms. | |
27 | - * | |
28 | - * Realtime network audio playback support for SDL platforms. | |
29 | - * | |
30 | - * Created by woody Mar 3-8, 2002. | |
31 | - * | |
32 | - * 14 January 2003 (Woody Zenfell): reworked memory management so all new/delete are called | |
33 | - * from the main thread: buffers are released by the audio system and returned to us for reuse. | |
34 | - */ | |
35 | - | |
36 | -#if !defined(DISABLE_NETWORKING) | |
37 | - | |
38 | -#include "network_sound.h" | |
39 | -#include "network_speaker_sdl.h" | |
40 | - | |
41 | -#include "network_distribution_types.h" | |
42 | -#include "CircularQueue.h" | |
43 | -#include "world.h" // local_random() | |
44 | -#include "Mixer.h" | |
45 | - | |
46 | -#ifdef SPEEX | |
47 | -#include "network_speex.h" | |
48 | -#endif //def SPEEX | |
49 | - | |
50 | -enum { | |
51 | - kSoundBufferQueueSize = 32, // should never get anywhere near here, but at 12 bytes/struct these are cheap. | |
52 | - kNoiseBufferSize = 1280 * 2, // how big a buffer we should use for noise (at 11025 this is about 1/9th of a second) | |
53 | - kMaxDryDequeues = 1, // how many consecutive empty-buffers before we stop playing? | |
54 | - kNumPumpPrimes = 1, // how many noise-buffers should we start with while buffering incoming data? | |
55 | - kNumSoundDataBuffers = 8, // how many actual audio storage buffers should we have? | |
56 | - kSoundDataBufferSize = 2048 * 2 // how big will each audio storage buffer be? | |
57 | -}; | |
58 | - | |
59 | -// "Send queue" of buffers from us to audio code (with descriptors) | |
60 | -static CircularQueue<NetworkSpeakerSoundBufferDescriptor> sSoundBuffers(kSoundBufferQueueSize); | |
61 | - | |
62 | -// "Return queue" of buffers from audio code to us for reuse | |
63 | -static CircularQueue<byte*> sSoundDataBuffers(kNumSoundDataBuffers + 2); // +2: breathing room | |
64 | - | |
65 | -// We can provide static noise instead of a "real" buffer once in a while if we need to. | |
66 | -// Also, we provide kNumPumpPrimes of static noise before getting to the "meat" as well. | |
67 | -static byte* sNoiseBufferStorage = NULL; | |
68 | -static NetworkSpeakerSoundBufferDescriptor sNoiseBufferDesc; | |
69 | -static int sDryDequeues = 0; | |
70 | -static bool sSpeakerIsOn = false; | |
71 | - | |
72 | - | |
73 | -OSErr | |
74 | -open_network_speaker() { | |
75 | - // Allocate storage for noise data - assume if pointer not NULL, already have storage. | |
76 | - if(sNoiseBufferStorage == NULL) { | |
77 | - assert(kNoiseBufferSize % 2 == 0); | |
78 | - uint16* theBuffer = new uint16[kNoiseBufferSize / 2]; | |
79 | - | |
80 | - // Fill in noise data (use whole width of local_random()) | |
81 | - for(int i = 0; i < kNoiseBufferSize / 2; i++) | |
82 | - theBuffer[i] = local_random() / 4; | |
83 | - | |
84 | - sNoiseBufferStorage = (byte*) theBuffer; | |
85 | - } | |
86 | - | |
87 | - // Fill out the noise-buffer descriptor | |
88 | - sNoiseBufferDesc.mData = sNoiseBufferStorage; | |
89 | - sNoiseBufferDesc.mLength = kNoiseBufferSize; | |
90 | - sNoiseBufferDesc.mFlags = 0; | |
91 | - | |
92 | - // Reset the buffer descriptor queue | |
93 | - sSoundBuffers.reset(); | |
94 | - | |
95 | - // Reset the data buffer queue | |
96 | - sSoundDataBuffers.reset(); | |
97 | - | |
98 | - // Allocate storage for audio data buffers | |
99 | - for(int i = 0; i < kNumSoundDataBuffers; i++) { | |
100 | - byte* theBuffer = new byte[kSoundDataBufferSize]; | |
101 | - sSoundDataBuffers.enqueue(theBuffer); | |
102 | - } | |
103 | - | |
104 | - // Reset a couple others to sane values | |
105 | - sDryDequeues = 0; | |
106 | - sSpeakerIsOn = false; | |
107 | - | |
108 | -#ifdef SPEEX | |
109 | - init_speex_decoder(); | |
110 | -#endif | |
111 | - | |
112 | - return 0; | |
113 | -} | |
114 | - | |
115 | -void | |
116 | -queue_network_speaker_data(byte* inData, short inLength) { | |
117 | - if(inLength > 0) { | |
118 | - if(sSoundDataBuffers.getCountOfElements() > 0) { | |
119 | - // Fill out a descriptor for a new chunk of storage | |
120 | - NetworkSpeakerSoundBufferDescriptor theBufferDesc; | |
121 | - theBufferDesc.mData = sSoundDataBuffers.peek(); | |
122 | - sSoundDataBuffers.dequeue(); | |
123 | - theBufferDesc.mLength = inLength; | |
124 | - theBufferDesc.mFlags = kSoundDataIsDisposable; | |
125 | - | |
126 | - // and copy the data | |
127 | - memcpy(theBufferDesc.mData, inData, inLength); | |
128 | - | |
129 | - // If we're just turning on, prime the queue with a few buffers of noise. | |
130 | - if(!sSpeakerIsOn) { | |
131 | - for(int i = 0; i < kNumPumpPrimes; i++) { | |
132 | - sSoundBuffers.enqueue(sNoiseBufferDesc); | |
133 | - } | |
134 | - | |
135 | - sSpeakerIsOn = true; | |
136 | - } | |
137 | - | |
138 | - // Enqueue the actual sound data. | |
139 | - sSoundBuffers.enqueue(theBufferDesc); | |
140 | - } | |
141 | - else { | |
142 | - fdprintf("No sound data buffer space available - audio discarded"); | |
143 | - } | |
144 | - } | |
145 | -} | |
146 | - | |
147 | - | |
148 | -void | |
149 | -network_speaker_idle_proc() { | |
150 | - if(sSpeakerIsOn) | |
151 | - Mixer::instance()->EnsureNetworkAudioPlaying(); | |
152 | -} | |
153 | - | |
154 | - | |
155 | -NetworkSpeakerSoundBufferDescriptor* | |
156 | -dequeue_network_speaker_data() { | |
157 | - // We need this to stick around between calls | |
158 | - static NetworkSpeakerSoundBufferDescriptor sBufferDesc; | |
159 | - | |
160 | - // If there is actual sound data, reset the "ran dry" count and return a pointer to the buffer descriptor | |
161 | - if(sSoundBuffers.getCountOfElements() > 0) { | |
162 | - sDryDequeues = 0; | |
163 | - sBufferDesc = sSoundBuffers.peek(); | |
164 | - sSoundBuffers.dequeue(); | |
165 | - return &sBufferDesc; | |
166 | - } | |
167 | - // If there's no data available, inc the "ran dry" count and return either a noise buffer or NULL. | |
168 | - else { | |
169 | - sDryDequeues++; | |
170 | - if(sDryDequeues > kMaxDryDequeues) { | |
171 | - sSpeakerIsOn = false; | |
172 | - return NULL; | |
173 | - } | |
174 | - else | |
175 | - return &sNoiseBufferDesc; | |
176 | - } | |
177 | -} | |
178 | - | |
179 | - | |
180 | -void | |
181 | -close_network_speaker() { | |
182 | - // Tell the audio system not to get our data anymore | |
183 | - Mixer::instance()->StopNetworkAudio(); | |
184 | - | |
185 | - // Bleed the queue dry of any leftover data | |
186 | - NetworkSpeakerSoundBufferDescriptor* theDesc; | |
187 | - while((theDesc = dequeue_network_speaker_data()) != NULL) { | |
188 | - if(is_sound_data_disposable(theDesc)) | |
189 | - release_network_speaker_buffer(theDesc->mData); | |
190 | - } | |
191 | - | |
192 | - // Free the sound data buffers | |
193 | - while(sSoundDataBuffers.getCountOfElements() > 0) { | |
194 | - byte* theBuffer = sSoundDataBuffers.peek(); | |
195 | - delete [] theBuffer; | |
196 | - sSoundDataBuffers.dequeue(); | |
197 | - } | |
198 | - | |
199 | - // Free the noise buffer and restore some values | |
200 | - if(sNoiseBufferStorage != NULL) { | |
201 | - delete [] sNoiseBufferStorage; | |
202 | - sNoiseBufferStorage = NULL; | |
203 | - } | |
204 | - sDryDequeues = 0; | |
205 | - sSpeakerIsOn = false; | |
206 | - | |
207 | - #ifdef SPEEX | |
208 | - destroy_speex_decoder(); | |
209 | - #endif | |
210 | -} | |
211 | - | |
212 | - | |
213 | - | |
214 | -void | |
215 | -release_network_speaker_buffer(byte* inBuffer) { | |
216 | - sSoundDataBuffers.enqueue(inBuffer); | |
217 | -} | |
218 | - | |
219 | -#endif // !defined(DISABLE_NETWORKING) | |
220 | - | |
1 | +/* | |
2 | + * network_speaker_sdl.cpp | |
3 | + * created for Marathon: Aleph One <http://source.bungie.org/> | |
4 | + | |
5 | + Copyright (C) 2002 and beyond by Woody Zenfell, III | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + * The code in this file is licensed to you under the GNU GPL. As the copyright holder, | |
23 | + * however, I reserve the right to use this code as I see fit, without being bound by the | |
24 | + * GPL's terms. This exemption is not intended to apply to modified versions of this file - | |
25 | + * if I were to use a modified version, I would be a licensee of whomever modified it, and | |
26 | + * thus must observe the GPL terms. | |
27 | + * | |
28 | + * Realtime network audio playback support for SDL platforms. | |
29 | + * | |
30 | + * Created by woody Mar 3-8, 2002. | |
31 | + * | |
32 | + * 14 January 2003 (Woody Zenfell): reworked memory management so all new/delete are called | |
33 | + * from the main thread: buffers are released by the audio system and returned to us for reuse. | |
34 | + */ | |
35 | + | |
36 | +#if !defined(DISABLE_NETWORKING) | |
37 | + | |
38 | +#include "network_sound.h" | |
39 | +#include "network_speaker_sdl.h" | |
40 | + | |
41 | +#include "network_distribution_types.h" | |
42 | +#include "CircularQueue.h" | |
43 | +#include "world.h" // local_random() | |
44 | +#include "Mixer.h" | |
45 | + | |
46 | +#ifdef SPEEX | |
47 | +#include "network_speex.h" | |
48 | +#endif //def SPEEX | |
49 | + | |
50 | +enum { | |
51 | + kSoundBufferQueueSize = 32, // should never get anywhere near here, but at 12 bytes/struct these are cheap. | |
52 | + kNoiseBufferSize = 1280 * 2, // how big a buffer we should use for noise (at 11025 this is about 1/9th of a second) | |
53 | + kMaxDryDequeues = 1, // how many consecutive empty-buffers before we stop playing? | |
54 | + kNumPumpPrimes = 1, // how many noise-buffers should we start with while buffering incoming data? | |
55 | + kNumSoundDataBuffers = 8, // how many actual audio storage buffers should we have? | |
56 | + kSoundDataBufferSize = 2048 * 2 // how big will each audio storage buffer be? | |
57 | +}; | |
58 | + | |
59 | +// "Send queue" of buffers from us to audio code (with descriptors) | |
60 | +static CircularQueue<NetworkSpeakerSoundBufferDescriptor> sSoundBuffers(kSoundBufferQueueSize); | |
61 | + | |
62 | +// "Return queue" of buffers from audio code to us for reuse | |
63 | +static CircularQueue<byte*> sSoundDataBuffers(kNumSoundDataBuffers + 2); // +2: breathing room | |
64 | + | |
65 | +// We can provide static noise instead of a "real" buffer once in a while if we need to. | |
66 | +// Also, we provide kNumPumpPrimes of static noise before getting to the "meat" as well. | |
67 | +static byte* sNoiseBufferStorage = NULL; | |
68 | +static NetworkSpeakerSoundBufferDescriptor sNoiseBufferDesc; | |
69 | +static int sDryDequeues = 0; | |
70 | +static bool sSpeakerIsOn = false; | |
71 | + | |
72 | + | |
73 | +OSErr | |
74 | +open_network_speaker() { | |
75 | + // Allocate storage for noise data - assume if pointer not NULL, already have storage. | |
76 | + if(sNoiseBufferStorage == NULL) { | |
77 | + assert(kNoiseBufferSize % 2 == 0); | |
78 | + uint16* theBuffer = new uint16[kNoiseBufferSize / 2]; | |
79 | + | |
80 | + // Fill in noise data (use whole width of local_random()) | |
81 | + for(int i = 0; i < kNoiseBufferSize / 2; i++) | |
82 | + theBuffer[i] = local_random() / 4; | |
83 | + | |
84 | + sNoiseBufferStorage = (byte*) theBuffer; | |
85 | + } | |
86 | + | |
87 | + // Fill out the noise-buffer descriptor | |
88 | + sNoiseBufferDesc.mData = sNoiseBufferStorage; | |
89 | + sNoiseBufferDesc.mLength = kNoiseBufferSize; | |
90 | + sNoiseBufferDesc.mFlags = 0; | |
91 | + | |
92 | + // Reset the buffer descriptor queue | |
93 | + sSoundBuffers.reset(); | |
94 | + | |
95 | + // Reset the data buffer queue | |
96 | + sSoundDataBuffers.reset(); | |
97 | + | |
98 | + // Allocate storage for audio data buffers | |
99 | + for(int i = 0; i < kNumSoundDataBuffers; i++) { | |
100 | + byte* theBuffer = new byte[kSoundDataBufferSize]; | |
101 | + sSoundDataBuffers.enqueue(theBuffer); | |
102 | + } | |
103 | + | |
104 | + // Reset a couple others to sane values | |
105 | + sDryDequeues = 0; | |
106 | + sSpeakerIsOn = false; | |
107 | + | |
108 | +#ifdef SPEEX | |
109 | + init_speex_decoder(); | |
110 | +#endif | |
111 | + | |
112 | + return 0; | |
113 | +} | |
114 | + | |
115 | +void | |
116 | +queue_network_speaker_data(byte* inData, short inLength) { | |
117 | + if(inLength > 0) { | |
118 | + if(sSoundDataBuffers.getCountOfElements() > 0) { | |
119 | + // Fill out a descriptor for a new chunk of storage | |
120 | + NetworkSpeakerSoundBufferDescriptor theBufferDesc; | |
121 | + theBufferDesc.mData = sSoundDataBuffers.peek(); | |
122 | + sSoundDataBuffers.dequeue(); | |
123 | + theBufferDesc.mLength = inLength; | |
124 | + theBufferDesc.mFlags = kSoundDataIsDisposable; | |
125 | + | |
126 | + // and copy the data | |
127 | + memcpy(theBufferDesc.mData, inData, inLength); | |
128 | + | |
129 | + // If we're just turning on, prime the queue with a few buffers of noise. | |
130 | + if(!sSpeakerIsOn) { | |
131 | + for(int i = 0; i < kNumPumpPrimes; i++) { | |
132 | + sSoundBuffers.enqueue(sNoiseBufferDesc); | |
133 | + } | |
134 | + | |
135 | + sSpeakerIsOn = true; | |
136 | + } | |
137 | + | |
138 | + // Enqueue the actual sound data. | |
139 | + sSoundBuffers.enqueue(theBufferDesc); | |
140 | + } | |
141 | + else { | |
142 | + fdprintf("No sound data buffer space available - audio discarded"); | |
143 | + } | |
144 | + } | |
145 | +} | |
146 | + | |
147 | + | |
148 | +void | |
149 | +network_speaker_idle_proc() { | |
150 | + if(sSpeakerIsOn) | |
151 | + Mixer::instance()->EnsureNetworkAudioPlaying(); | |
152 | +} | |
153 | + | |
154 | + | |
155 | +NetworkSpeakerSoundBufferDescriptor* | |
156 | +dequeue_network_speaker_data() { | |
157 | + // We need this to stick around between calls | |
158 | + static NetworkSpeakerSoundBufferDescriptor sBufferDesc; | |
159 | + | |
160 | + // If there is actual sound data, reset the "ran dry" count and return a pointer to the buffer descriptor | |
161 | + if(sSoundBuffers.getCountOfElements() > 0) { | |
162 | + sDryDequeues = 0; | |
163 | + sBufferDesc = sSoundBuffers.peek(); | |
164 | + sSoundBuffers.dequeue(); | |
165 | + return &sBufferDesc; | |
166 | + } | |
167 | + // If there's no data available, inc the "ran dry" count and return either a noise buffer or NULL. | |
168 | + else { | |
169 | + sDryDequeues++; | |
170 | + if(sDryDequeues > kMaxDryDequeues) { | |
171 | + sSpeakerIsOn = false; | |
172 | + return NULL; | |
173 | + } | |
174 | + else | |
175 | + return &sNoiseBufferDesc; | |
176 | + } | |
177 | +} | |
178 | + | |
179 | + | |
180 | +void | |
181 | +close_network_speaker() { | |
182 | + // Tell the audio system not to get our data anymore | |
183 | + Mixer::instance()->StopNetworkAudio(); | |
184 | + | |
185 | + // Bleed the queue dry of any leftover data | |
186 | + NetworkSpeakerSoundBufferDescriptor* theDesc; | |
187 | + while((theDesc = dequeue_network_speaker_data()) != NULL) { | |
188 | + if(is_sound_data_disposable(theDesc)) | |
189 | + release_network_speaker_buffer(theDesc->mData); | |
190 | + } | |
191 | + | |
192 | + // Free the sound data buffers | |
193 | + while(sSoundDataBuffers.getCountOfElements() > 0) { | |
194 | + byte* theBuffer = sSoundDataBuffers.peek(); | |
195 | + delete [] theBuffer; | |
196 | + sSoundDataBuffers.dequeue(); | |
197 | + } | |
198 | + | |
199 | + // Free the noise buffer and restore some values | |
200 | + if(sNoiseBufferStorage != NULL) { | |
201 | + delete [] sNoiseBufferStorage; | |
202 | + sNoiseBufferStorage = NULL; | |
203 | + } | |
204 | + sDryDequeues = 0; | |
205 | + sSpeakerIsOn = false; | |
206 | + | |
207 | + #ifdef SPEEX | |
208 | + destroy_speex_decoder(); | |
209 | + #endif | |
210 | +} | |
211 | + | |
212 | + | |
213 | + | |
214 | +void | |
215 | +release_network_speaker_buffer(byte* inBuffer) { | |
216 | + sSoundDataBuffers.enqueue(inBuffer); | |
217 | +} | |
218 | + | |
219 | +#endif // !defined(DISABLE_NETWORKING) | |
220 | + |
@@ -1,260 +1,260 @@ | ||
1 | -/* | |
2 | - * network_microphone_core_audio.cpp | |
3 | - | |
4 | - Copyright (C) 2007 and beyond by Gregory Smith | |
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 | - */ | |
22 | - | |
23 | -#include <Carbon/Carbon.h> | |
24 | -#include <CoreAudio/CoreAudio.h> | |
25 | -#include <AudioUnit/AudioUnit.h> | |
26 | - | |
27 | -#include "cstypes.h" | |
28 | -#include "network_microphone_shared.h" | |
29 | - | |
30 | -#include <vector> | |
31 | - | |
32 | -#ifdef SPEEX | |
33 | -extern void init_speex_encoder(); | |
34 | -extern void destroy_speex_encoder(); | |
35 | -#endif | |
36 | - | |
37 | -static AudioUnit fAudioUnit; | |
38 | -static AudioDeviceID fInputDeviceID; | |
39 | -static AudioStreamBasicDescription fOutputFormat, fDeviceFormat; | |
40 | -static Uint32 fAudioSamples; | |
41 | -static AudioBufferList *fAudioBuffer = NULL; | |
42 | - | |
43 | -static bool initialized = false; | |
44 | - | |
45 | -static std::vector<uint8> captureBuffer; | |
46 | -static Uint32 captureBufferSize = 0; | |
47 | - | |
48 | -static OSStatus audio_input_proc(void *, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *) | |
49 | -{ | |
50 | - // render to a buffer | |
51 | - OSStatus err = noErr; | |
52 | - err = AudioUnitRender(fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, fAudioBuffer); | |
53 | - if (err != noErr) | |
54 | - { | |
55 | - fprintf(stderr, "AudioUnitRender() failed with error %i\n", err); | |
56 | - } | |
57 | - else | |
58 | - { | |
59 | - // copy to the capture buffer | |
60 | - memcpy(&captureBuffer[captureBufferSize], fAudioBuffer->mBuffers[0].mData, inNumberFrames * 2); | |
61 | - captureBufferSize += inNumberFrames * 2; | |
62 | - if (captureBufferSize >= get_capture_byte_count_per_packet()) | |
63 | - { | |
64 | - copy_and_send_audio_data(&captureBuffer.front(), captureBufferSize, NULL, 0, true); | |
65 | - captureBufferSize = 0; | |
66 | - } | |
67 | - } | |
68 | - | |
69 | - return err; | |
70 | -} | |
71 | - | |
72 | -OSErr open_network_microphone() | |
73 | -{ | |
74 | - OSStatus err = noErr; | |
75 | - | |
76 | - // find an AudioOutputUnit (for input) | |
77 | - Component component; | |
78 | - ComponentDescription description; | |
79 | - | |
80 | - description.componentType = kAudioUnitType_Output; | |
81 | - description.componentSubType = kAudioUnitSubType_HALOutput; | |
82 | - description.componentManufacturer = kAudioUnitManufacturer_Apple; | |
83 | - description.componentFlags = 0; | |
84 | - description.componentFlagsMask = 0; | |
85 | - if (component = FindNextComponent(NULL, &description)) | |
86 | - { | |
87 | - err = OpenAComponent(component, &fAudioUnit); | |
88 | - if (err != noErr) | |
89 | - { | |
90 | - fAudioUnit = NULL; | |
91 | - return err; | |
92 | - } | |
93 | - } | |
94 | - | |
95 | - // configure the AudioOutputUnit | |
96 | - UInt32 param = 1; | |
97 | - | |
98 | - // enable input on the AUHAL | |
99 | - err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, ¶m, sizeof(UInt32)); | |
100 | - if (err = noErr) | |
101 | - { | |
102 | - // disable output on the AUHAL | |
103 | - param = 0; | |
104 | - err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, ¶m, sizeof(UInt32)); | |
105 | - } | |
106 | - | |
107 | - // Select the default input device | |
108 | - param = sizeof(AudioDeviceID); | |
109 | - err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, ¶m, &fInputDeviceID); | |
110 | - if (err != noErr) | |
111 | - { | |
112 | - fprintf(stderr, "failed to get default input device\n"); | |
113 | - return err; | |
114 | - } | |
115 | - | |
116 | - const Float64 sampleRates[] = { | |
117 | - 8000.0, | |
118 | - 48000.0, | |
119 | - 44100.0, | |
120 | - 22050.0, | |
121 | - 11025.0 | |
122 | - }; | |
123 | - | |
124 | - Float64 sampleRate; | |
125 | - | |
126 | - for (int i = 0; i < sizeof(sampleRates) / sizeof(Float64); i++) | |
127 | - { | |
128 | - sampleRate = sampleRates[i]; | |
129 | - err = AudioDeviceSetProperty(fInputDeviceID, 0, 0, 0, kAudioDevicePropertyNominalSampleRate, sizeof(Float64), &sampleRate); | |
130 | - if (err == noErr) | |
131 | - break; | |
132 | - } | |
133 | - | |
134 | - if (err != noErr) | |
135 | - { | |
136 | - fprintf(stderr, "failed to set AU sample rate (%i)\n", err); | |
137 | - return err; | |
138 | - } | |
139 | - | |
140 | - // Set the current device to the default input device | |
141 | - err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fInputDeviceID, sizeof(AudioDeviceID)); | |
142 | - if (err != noErr) | |
143 | - { | |
144 | - fprintf(stderr, "failed to set AU input device\n"); | |
145 | - return err; | |
146 | - } | |
147 | - | |
148 | - // setup render callback | |
149 | - AURenderCallbackStruct callback; | |
150 | - callback.inputProc = audio_input_proc; | |
151 | - err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(AURenderCallbackStruct)); | |
152 | - | |
153 | - // get hardware device format | |
154 | - param = sizeof(AudioStreamBasicDescription); | |
155 | - err = AudioUnitGetProperty(fAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fDeviceFormat, ¶m); | |
156 | - if (err != noErr) | |
157 | - { | |
158 | - fprintf(stderr, "failed to get input device ASBD\n"); return err; | |
159 | - } | |
160 | - | |
161 | - // change the format to our liking | |
162 | - fOutputFormat.mChannelsPerFrame = 1; | |
163 | - fOutputFormat.mSampleRate = fDeviceFormat.mSampleRate; | |
164 | - fOutputFormat.mFormatID = kAudioFormatLinearPCM; | |
165 | - fOutputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; | |
166 | -#ifdef __ppc__ | |
167 | - fOutputFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian; | |
168 | -#endif | |
169 | - fOutputFormat.mBitsPerChannel = 16; | |
170 | - fOutputFormat.mBytesPerFrame = 2; | |
171 | - fOutputFormat.mFramesPerPacket = 1; | |
172 | - fOutputFormat.mBytesPerPacket = fOutputFormat.mBytesPerFrame; | |
173 | - | |
174 | - err = AudioUnitSetProperty(fAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fOutputFormat, sizeof(AudioStreamBasicDescription)); | |
175 | - if (err != noErr) | |
176 | - { | |
177 | - fprintf(stderr, "failed to set input device ASBD\n"); | |
178 | - return err; | |
179 | - } | |
180 | - | |
181 | - // Get the number of frames in the IO buffer(s) | |
182 | - param = sizeof(UInt32); | |
183 | - err = AudioUnitGetProperty(fAudioUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &fAudioSamples, ¶m); | |
184 | - if (err != noErr) | |
185 | - { | |
186 | - fprintf(stderr, "failed to get audio sample size\n"); | |
187 | - return err; | |
188 | - } | |
189 | - | |
190 | - err = AudioUnitInitialize(fAudioUnit); | |
191 | - if (err != noErr) | |
192 | - { | |
193 | - fprintf(stderr, "failed to initialize AU %i\n", err); | |
194 | - return err; | |
195 | - } | |
196 | - | |
197 | - // Allocate audio buffer | |
198 | - fAudioBuffer = (AudioBufferList *) calloc (1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); | |
199 | - fAudioBuffer->mNumberBuffers = 1; | |
200 | - fAudioBuffer->mBuffers[0].mNumberChannels = 1; | |
201 | - fAudioBuffer->mBuffers[0].mDataByteSize = fAudioSamples * fOutputFormat.mBytesPerFrame; | |
202 | - fAudioBuffer->mBuffers[0].mData = malloc(fAudioSamples * fOutputFormat.mBytesPerFrame); | |
203 | - | |
204 | - if (!announce_microphone_capture_format(static_cast<uint32>(fOutputFormat.mSampleRate), fOutputFormat.mChannelsPerFrame == 2, fOutputFormat.mBytesPerFrame == 2)) | |
205 | - { | |
206 | - fprintf(stderr, "network microphone support code rejected audio format (rate=%f)\n", fOutputFormat.mSampleRate); | |
207 | - return -1; | |
208 | - } | |
209 | - | |
210 | - captureBuffer.resize(get_capture_byte_count_per_packet() + fAudioBuffer->mBuffers[0].mDataByteSize); | |
211 | - | |
212 | -#ifdef SPEEX | |
213 | - init_speex_encoder(); | |
214 | -#endif | |
215 | - | |
216 | - initialized = true; | |
217 | - | |
218 | - return noErr; | |
219 | -} | |
220 | - | |
221 | -static bool mic_active = false; | |
222 | - | |
223 | -void set_network_microphone_state(bool inActive) | |
224 | -{ | |
225 | - if (!initialized) return; | |
226 | - | |
227 | - if (inActive && !mic_active) | |
228 | - { | |
229 | - AudioOutputUnitStart(fAudioUnit); | |
230 | - mic_active = true; | |
231 | - } | |
232 | - else if (!inActive && mic_active) | |
233 | - { | |
234 | - AudioOutputUnitStop(fAudioUnit); | |
235 | - mic_active = false; | |
236 | - } | |
237 | -} | |
238 | - | |
239 | -void close_network_microphone() | |
240 | -{ | |
241 | - initialized = false; | |
242 | - if (fAudioBuffer) | |
243 | - free(fAudioBuffer->mBuffers[0].mData); | |
244 | - free(fAudioBuffer); | |
245 | - fAudioBuffer = NULL; | |
246 | - | |
247 | -#ifdef SPEEX | |
248 | - destroy_speex_encoder(); | |
249 | -#endif | |
250 | -} | |
251 | - | |
252 | -bool is_network_microphone_implemented() { | |
253 | - return true; | |
254 | -} | |
255 | - | |
256 | -void network_microphone_idle_proc() | |
257 | -{ | |
258 | - // do nothing | |
259 | -} | |
260 | - | |
1 | +/* | |
2 | + * network_microphone_core_audio.cpp | |
3 | + | |
4 | + Copyright (C) 2007 and beyond by Gregory Smith | |
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 | + */ | |
22 | + | |
23 | +#include <Carbon/Carbon.h> | |
24 | +#include <CoreAudio/CoreAudio.h> | |
25 | +#include <AudioUnit/AudioUnit.h> | |
26 | + | |
27 | +#include "cstypes.h" | |
28 | +#include "network_microphone_shared.h" | |
29 | + | |
30 | +#include <vector> | |
31 | + | |
32 | +#ifdef SPEEX | |
33 | +extern void init_speex_encoder(); | |
34 | +extern void destroy_speex_encoder(); | |
35 | +#endif | |
36 | + | |
37 | +static AudioUnit fAudioUnit; | |
38 | +static AudioDeviceID fInputDeviceID; | |
39 | +static AudioStreamBasicDescription fOutputFormat, fDeviceFormat; | |
40 | +static Uint32 fAudioSamples; | |
41 | +static AudioBufferList *fAudioBuffer = NULL; | |
42 | + | |
43 | +static bool initialized = false; | |
44 | + | |
45 | +static std::vector<uint8> captureBuffer; | |
46 | +static Uint32 captureBufferSize = 0; | |
47 | + | |
48 | +static OSStatus audio_input_proc(void *, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *) | |
49 | +{ | |
50 | + // render to a buffer | |
51 | + OSStatus err = noErr; | |
52 | + err = AudioUnitRender(fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, fAudioBuffer); | |
53 | + if (err != noErr) | |
54 | + { | |
55 | + fprintf(stderr, "AudioUnitRender() failed with error %i\n", err); | |
56 | + } | |
57 | + else | |
58 | + { | |
59 | + // copy to the capture buffer | |
60 | + memcpy(&captureBuffer[captureBufferSize], fAudioBuffer->mBuffers[0].mData, inNumberFrames * 2); | |
61 | + captureBufferSize += inNumberFrames * 2; | |
62 | + if (captureBufferSize >= get_capture_byte_count_per_packet()) | |
63 | + { | |
64 | + copy_and_send_audio_data(&captureBuffer.front(), captureBufferSize, NULL, 0, true); | |
65 | + captureBufferSize = 0; | |
66 | + } | |
67 | + } | |
68 | + | |
69 | + return err; | |
70 | +} | |
71 | + | |
72 | +OSErr open_network_microphone() | |
73 | +{ | |
74 | + OSStatus err = noErr; | |
75 | + | |
76 | + // find an AudioOutputUnit (for input) | |
77 | + Component component; | |
78 | + ComponentDescription description; | |
79 | + | |
80 | + description.componentType = kAudioUnitType_Output; | |
81 | + description.componentSubType = kAudioUnitSubType_HALOutput; | |
82 | + description.componentManufacturer = kAudioUnitManufacturer_Apple; | |
83 | + description.componentFlags = 0; | |
84 | + description.componentFlagsMask = 0; | |
85 | + if (component = FindNextComponent(NULL, &description)) | |
86 | + { | |
87 | + err = OpenAComponent(component, &fAudioUnit); | |
88 | + if (err != noErr) | |
89 | + { | |
90 | + fAudioUnit = NULL; | |
91 | + return err; | |
92 | + } | |
93 | + } | |
94 | + | |
95 | + // configure the AudioOutputUnit | |
96 | + UInt32 param = 1; | |
97 | + | |
98 | + // enable input on the AUHAL | |
99 | + err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, ¶m, sizeof(UInt32)); | |
100 | + if (err = noErr) | |
101 | + { | |
102 | + // disable output on the AUHAL | |
103 | + param = 0; | |
104 | + err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, ¶m, sizeof(UInt32)); | |
105 | + } | |
106 | + | |
107 | + // Select the default input device | |
108 | + param = sizeof(AudioDeviceID); | |
109 | + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, ¶m, &fInputDeviceID); | |
110 | + if (err != noErr) | |
111 | + { | |
112 | + fprintf(stderr, "failed to get default input device\n"); | |
113 | + return err; | |
114 | + } | |
115 | + | |
116 | + const Float64 sampleRates[] = { | |
117 | + 8000.0, | |
118 | + 48000.0, | |
119 | + 44100.0, | |
120 | + 22050.0, | |
121 | + 11025.0 | |
122 | + }; | |
123 | + | |
124 | + Float64 sampleRate; | |
125 | + | |
126 | + for (int i = 0; i < sizeof(sampleRates) / sizeof(Float64); i++) | |
127 | + { | |
128 | + sampleRate = sampleRates[i]; | |
129 | + err = AudioDeviceSetProperty(fInputDeviceID, 0, 0, 0, kAudioDevicePropertyNominalSampleRate, sizeof(Float64), &sampleRate); | |
130 | + if (err == noErr) | |
131 | + break; | |
132 | + } | |
133 | + | |
134 | + if (err != noErr) | |
135 | + { | |
136 | + fprintf(stderr, "failed to set AU sample rate (%i)\n", err); | |
137 | + return err; | |
138 | + } | |
139 | + | |
140 | + // Set the current device to the default input device | |
141 | + err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &fInputDeviceID, sizeof(AudioDeviceID)); | |
142 | + if (err != noErr) | |
143 | + { | |
144 | + fprintf(stderr, "failed to set AU input device\n"); | |
145 | + return err; | |
146 | + } | |
147 | + | |
148 | + // setup render callback | |
149 | + AURenderCallbackStruct callback; | |
150 | + callback.inputProc = audio_input_proc; | |
151 | + err = AudioUnitSetProperty(fAudioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(AURenderCallbackStruct)); | |
152 | + | |
153 | + // get hardware device format | |
154 | + param = sizeof(AudioStreamBasicDescription); | |
155 | + err = AudioUnitGetProperty(fAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &fDeviceFormat, ¶m); | |
156 | + if (err != noErr) | |
157 | + { | |
158 | + fprintf(stderr, "failed to get input device ASBD\n"); return err; | |
159 | + } | |
160 | + | |
161 | + // change the format to our liking | |
162 | + fOutputFormat.mChannelsPerFrame = 1; | |
163 | + fOutputFormat.mSampleRate = fDeviceFormat.mSampleRate; | |
164 | + fOutputFormat.mFormatID = kAudioFormatLinearPCM; | |
165 | + fOutputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; | |
166 | +#ifdef __ppc__ | |
167 | + fOutputFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian; | |
168 | +#endif | |
169 | + fOutputFormat.mBitsPerChannel = 16; | |
170 | + fOutputFormat.mBytesPerFrame = 2; | |
171 | + fOutputFormat.mFramesPerPacket = 1; | |
172 | + fOutputFormat.mBytesPerPacket = fOutputFormat.mBytesPerFrame; | |
173 | + | |
174 | + err = AudioUnitSetProperty(fAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &fOutputFormat, sizeof(AudioStreamBasicDescription)); | |
175 | + if (err != noErr) | |
176 | + { | |
177 | + fprintf(stderr, "failed to set input device ASBD\n"); | |
178 | + return err; | |
179 | + } | |
180 | + | |
181 | + // Get the number of frames in the IO buffer(s) | |
182 | + param = sizeof(UInt32); | |
183 | + err = AudioUnitGetProperty(fAudioUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &fAudioSamples, ¶m); | |
184 | + if (err != noErr) | |
185 | + { | |
186 | + fprintf(stderr, "failed to get audio sample size\n"); | |
187 | + return err; | |
188 | + } | |
189 | + | |
190 | + err = AudioUnitInitialize(fAudioUnit); | |
191 | + if (err != noErr) | |
192 | + { | |
193 | + fprintf(stderr, "failed to initialize AU %i\n", err); | |
194 | + return err; | |
195 | + } | |
196 | + | |
197 | + // Allocate audio buffer | |
198 | + fAudioBuffer = (AudioBufferList *) calloc (1, sizeof(AudioBufferList) + sizeof(AudioBuffer)); | |
199 | + fAudioBuffer->mNumberBuffers = 1; | |
200 | + fAudioBuffer->mBuffers[0].mNumberChannels = 1; | |
201 | + fAudioBuffer->mBuffers[0].mDataByteSize = fAudioSamples * fOutputFormat.mBytesPerFrame; | |
202 | + fAudioBuffer->mBuffers[0].mData = malloc(fAudioSamples * fOutputFormat.mBytesPerFrame); | |
203 | + | |
204 | + if (!announce_microphone_capture_format(static_cast<uint32>(fOutputFormat.mSampleRate), fOutputFormat.mChannelsPerFrame == 2, fOutputFormat.mBytesPerFrame == 2)) | |
205 | + { | |
206 | + fprintf(stderr, "network microphone support code rejected audio format (rate=%f)\n", fOutputFormat.mSampleRate); | |
207 | + return -1; | |
208 | + } | |
209 | + | |
210 | + captureBuffer.resize(get_capture_byte_count_per_packet() + fAudioBuffer->mBuffers[0].mDataByteSize); | |
211 | + | |
212 | +#ifdef SPEEX | |
213 | + init_speex_encoder(); | |
214 | +#endif | |
215 | + | |
216 | + initialized = true; | |
217 | + | |
218 | + return noErr; | |
219 | +} | |
220 | + | |
221 | +static bool mic_active = false; | |
222 | + | |
223 | +void set_network_microphone_state(bool inActive) | |
224 | +{ | |
225 | + if (!initialized) return; | |
226 | + | |
227 | + if (inActive && !mic_active) | |
228 | + { | |
229 | + AudioOutputUnitStart(fAudioUnit); | |
230 | + mic_active = true; | |
231 | + } | |
232 | + else if (!inActive && mic_active) | |
233 | + { | |
234 | + AudioOutputUnitStop(fAudioUnit); | |
235 | + mic_active = false; | |
236 | + } | |
237 | +} | |
238 | + | |
239 | +void close_network_microphone() | |
240 | +{ | |
241 | + initialized = false; | |
242 | + if (fAudioBuffer) | |
243 | + free(fAudioBuffer->mBuffers[0].mData); | |
244 | + free(fAudioBuffer); | |
245 | + fAudioBuffer = NULL; | |
246 | + | |
247 | +#ifdef SPEEX | |
248 | + destroy_speex_encoder(); | |
249 | +#endif | |
250 | +} | |
251 | + | |
252 | +bool is_network_microphone_implemented() { | |
253 | + return true; | |
254 | +} | |
255 | + | |
256 | +void network_microphone_idle_proc() | |
257 | +{ | |
258 | + // do nothing | |
259 | +} | |
260 | + |
@@ -1,78 +1,78 @@ | ||
1 | -#ifndef __NETWORK_SOUND_H | |
2 | -#define __NETWORK_SOUND_H | |
3 | - | |
4 | -/* | |
5 | -NETWORK_SOUND.H | |
6 | - | |
7 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | - and the "Aleph One" developers. | |
9 | - | |
10 | - This program is free software; you can redistribute it and/or modify | |
11 | - it under the terms of the GNU General Public License as published by | |
12 | - the Free Software Foundation; either version 3 of the License, or | |
13 | - (at your option) any later version. | |
14 | - | |
15 | - This program is distributed in the hope that it will be useful, | |
16 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | - GNU General Public License for more details. | |
19 | - | |
20 | - This license is contained in the file "COPYING", | |
21 | - which is included with this source code; it is available online at | |
22 | - http://www.gnu.org/licenses/gpl.html | |
23 | - | |
24 | -Sunday, August 14, 1994 3:36:17 AM- go nuts | |
25 | - | |
26 | -Feb 1, 2003 (Woody Zenfell): | |
27 | - Merged SDL-style and Mac-style network audio interfaces. Both use this now. | |
28 | - This is the main interface for external code wanting to use the network audio support. | |
29 | -*/ | |
30 | - | |
31 | -#include "cseries.h" | |
32 | - | |
33 | -/* ---------- constants */ | |
34 | - | |
35 | -/* ---------- prototypes: NETWORK_SPEAKER.C */ | |
36 | - | |
37 | -// Called by main thread to initialize network speaker system | |
38 | -OSErr open_network_speaker(); | |
39 | - | |
40 | -// Called by main thread between game updates | |
41 | -void network_speaker_idle_proc(); | |
42 | - | |
43 | -// Called by main thread to shut down network speaker system | |
44 | -void close_network_speaker(); | |
45 | - | |
46 | -// Called by received_network_audio_proc, but also available to others | |
47 | -void queue_network_speaker_data(byte* inData, short inLength); | |
48 | - | |
49 | -// Called by network routines to store incoming network audio for playback | |
50 | -void received_network_audio_proc(void *buffer, short buffer_size, short player_index); | |
51 | - | |
52 | -void quiet_network_speaker(void); | |
53 | - | |
54 | -void mute_player_mic(short player_index); | |
55 | -void clear_player_mic_mutes(); | |
56 | - | |
57 | -/* ---------- prototypes: NETWORK_MICROPHONE.C */ | |
58 | - | |
59 | -// "true" does not guarantee that the user has a microphone, or even that sound capture will work... | |
60 | -// but "false" means you have no hope whatsoever. :) | |
61 | -bool is_network_microphone_implemented(); | |
62 | - | |
63 | -// This may answer the question a bit more accurately. | |
64 | -bool has_sound_input_capability(void); | |
65 | - | |
66 | -// Setup - don't call twice without intervening close...() | |
67 | -OSErr open_network_microphone(); | |
68 | - | |
69 | -// Activate/deactivate a network microphone that's been open()ed | |
70 | -void set_network_microphone_state(bool inActive); | |
71 | - | |
72 | -// Call this from time to time to let audio get processed | |
73 | -void network_microphone_idle_proc(); | |
74 | - | |
75 | -// Cleanup - multiple calls should be safe. | |
76 | -void close_network_microphone(); | |
77 | - | |
78 | -#endif | |
1 | +#ifndef __NETWORK_SOUND_H | |
2 | +#define __NETWORK_SOUND_H | |
3 | + | |
4 | +/* | |
5 | +NETWORK_SOUND.H | |
6 | + | |
7 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | + and the "Aleph One" developers. | |
9 | + | |
10 | + This program is free software; you can redistribute it and/or modify | |
11 | + it under the terms of the GNU General Public License as published by | |
12 | + the Free Software Foundation; either version 3 of the License, or | |
13 | + (at your option) any later version. | |
14 | + | |
15 | + This program is distributed in the hope that it will be useful, | |
16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + GNU General Public License for more details. | |
19 | + | |
20 | + This license is contained in the file "COPYING", | |
21 | + which is included with this source code; it is available online at | |
22 | + http://www.gnu.org/licenses/gpl.html | |
23 | + | |
24 | +Sunday, August 14, 1994 3:36:17 AM- go nuts | |
25 | + | |
26 | +Feb 1, 2003 (Woody Zenfell): | |
27 | + Merged SDL-style and Mac-style network audio interfaces. Both use this now. | |
28 | + This is the main interface for external code wanting to use the network audio support. | |
29 | +*/ | |
30 | + | |
31 | +#include "cseries.h" | |
32 | + | |
33 | +/* ---------- constants */ | |
34 | + | |
35 | +/* ---------- prototypes: NETWORK_SPEAKER.C */ | |
36 | + | |
37 | +// Called by main thread to initialize network speaker system | |
38 | +OSErr open_network_speaker(); | |
39 | + | |
40 | +// Called by main thread between game updates | |
41 | +void network_speaker_idle_proc(); | |
42 | + | |
43 | +// Called by main thread to shut down network speaker system | |
44 | +void close_network_speaker(); | |
45 | + | |
46 | +// Called by received_network_audio_proc, but also available to others | |
47 | +void queue_network_speaker_data(byte* inData, short inLength); | |
48 | + | |
49 | +// Called by network routines to store incoming network audio for playback | |
50 | +void received_network_audio_proc(void *buffer, short buffer_size, short player_index); | |
51 | + | |
52 | +void quiet_network_speaker(void); | |
53 | + | |
54 | +void mute_player_mic(short player_index); | |
55 | +void clear_player_mic_mutes(); | |
56 | + | |
57 | +/* ---------- prototypes: NETWORK_MICROPHONE.C */ | |
58 | + | |
59 | +// "true" does not guarantee that the user has a microphone, or even that sound capture will work... | |
60 | +// but "false" means you have no hope whatsoever. :) | |
61 | +bool is_network_microphone_implemented(); | |
62 | + | |
63 | +// This may answer the question a bit more accurately. | |
64 | +bool has_sound_input_capability(void); | |
65 | + | |
66 | +// Setup - don't call twice without intervening close...() | |
67 | +OSErr open_network_microphone(); | |
68 | + | |
69 | +// Activate/deactivate a network microphone that's been open()ed | |
70 | +void set_network_microphone_state(bool inActive); | |
71 | + | |
72 | +// Call this from time to time to let audio get processed | |
73 | +void network_microphone_idle_proc(); | |
74 | + | |
75 | +// Cleanup - multiple calls should be safe. | |
76 | +void close_network_microphone(); | |
77 | + | |
78 | +#endif |
@@ -1,68 +1,68 @@ | ||
1 | -/* | |
2 | - * network_capabilities.h -- a versioning system for gatherers and joiners | |
3 | - | |
4 | - Copyright (C) 2005 and beyond by Gregory Smith | |
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 lat |
Ein Teil der Diff wurde aufgrund der Größenbeschränkung abgeschnitten. Verwenden Sie Ihren lokalen Client, um die vollständige Diff.