• R/O
  • SSH
  • HTTPS

marathon: Commit


Commit MetaInfo

Revision528 (tree)
Zeit2012-06-02 14:12:51
Autorookawa_mi

Log Message

改行コード修正

Ändern Zusammenfassung

Diff

--- marathon/trunk/Source_Files/Network/network_dialogs.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_dialogs.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_dialog_widgets_sdl.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_dialog_widgets_sdl.cpp (revision 528)
@@ -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)
--- marathon/trunk/Source_Files/Network/network_capabilities.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_capabilities.cpp (revision 528)
@@ -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+
--- marathon/trunk/Source_Files/Network/network_microphone_shared.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_microphone_shared.cpp (revision 528)
@@ -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+
--- marathon/trunk/Source_Files/Network/network_audio_shared.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_audio_shared.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_speaker_sdl.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_speaker_sdl.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_names.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_names.cpp (revision 528)
@@ -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+
--- marathon/trunk/Source_Files/Network/network_microphone_sdl_alsa.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_microphone_sdl_alsa.cpp (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/RingGameProtocol.h (revision 527)
+++ marathon/trunk/Source_Files/Network/RingGameProtocol.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_distribution_types.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_distribution_types.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_data_formats.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_data_formats.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_star_spoke.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_star_spoke.cpp (revision 528)
@@ -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)
--- marathon/trunk/Source_Files/Network/network_messages.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_messages.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/Update.h (revision 527)
+++ marathon/trunk/Source_Files/Network/Update.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_private.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_private.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/ConnectPool.h (revision 527)
+++ marathon/trunk/Source_Files/Network/ConnectPool.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_speaker_shared.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_speaker_shared.cpp (revision 528)
@@ -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)
--- marathon/trunk/Source_Files/Network/network_microphone_sdl_dummy.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_microphone_sdl_dummy.cpp (revision 528)
@@ -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+}
--- marathon/trunk/Source_Files/Network/network_lookup_sdl.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_lookup_sdl.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/StarGameProtocol.h (revision 527)
+++ marathon/trunk/Source_Files/Network/StarGameProtocol.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_dialogs.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_dialogs.cpp (revision 528)
@@ -586,7 +586,7 @@
586586 m_chatChoiceWidget->set_callback(boost::bind(&JoinDialog::chatChoiceHit, this));
587587 m_chatEntryWidget->set_callback(boost::bind(&JoinDialog::chatTextEntered, this, _1));
588588
589- getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string);
589+ getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string);
590590 m_messagesWidget->set_text(temporary);
591591
592592 CStringPref joinAddressPref (network_preferences->join_address, 255);
@@ -659,7 +659,7 @@
659659 m_joinWidget->deactivate ();
660660 m_joinMetaserverWidget->deactivate ();
661661
662- getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string);
662+ getcstr(temporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string);
663663 m_messagesWidget->set_text(temporary);
664664
665665 if (!m_joinByAddressWidget->get_value()) {
--- marathon/trunk/Source_Files/Network/network.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_udp_opentransport.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_udp_opentransport.cpp (revision 528)
@@ -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+}
--- marathon/trunk/Source_Files/Network/network_microphone_sdl_win32.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_microphone_sdl_win32.cpp (revision 528)
@@ -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)
--- marathon/trunk/Source_Files/Network/network_speex.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_speex.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_speaker.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_speaker.cpp (revision 528)
@@ -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)
--- marathon/trunk/Source_Files/Network/network_dummy.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_dummy.cpp (revision 528)
@@ -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+}
--- marathon/trunk/Source_Files/Network/network_dialog_widgets_sdl.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_dialog_widgets_sdl.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_speaker_sdl.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_speaker_sdl.cpp (revision 528)
@@ -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+
--- marathon/trunk/Source_Files/Network/network_microphone_coreaudio.cpp (revision 527)
+++ marathon/trunk/Source_Files/Network/network_microphone_coreaudio.cpp (revision 528)
@@ -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, &param, 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, &param, sizeof(UInt32));
105- }
106-
107- // Select the default input device
108- param = sizeof(AudioDeviceID);
109- err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &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, &param);
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, &param);
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, &param, 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, &param, sizeof(UInt32));
105+ }
106+
107+ // Select the default input device
108+ param = sizeof(AudioDeviceID);
109+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &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, &param);
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, &param);
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+
--- marathon/trunk/Source_Files/Network/network_sound.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_sound.h (revision 528)
@@ -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
--- marathon/trunk/Source_Files/Network/network_capabilities.h (revision 527)
+++ marathon/trunk/Source_Files/Network/network_capabilities.h (revision 528)
@@ -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.

Show on old repository browser