1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (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 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "nConfig.h"
29 #include "nNetObject.h"
30 #include "tConsole.h"
31 #include "tSysTime.h"
32 #include <set>
33 #include <string.h>
34 
nConfItemBase()35 nConfItemBase::nConfItemBase()
36         :tConfItemBase(""), lastChangeTime_(-10000), lastChangeMessage_(0), watcher_(0){}
37 
38 //nConfItemBase::nConfItemBase(const char *title,const char *help)
39 //  :tConfItemBase(title, help){}
40 
nConfItemBase(const char * title)41 nConfItemBase::nConfItemBase(const char *title)
42         :tConfItemBase(title), lastChangeTime_(-10000), lastChangeMessage_(0), watcher_(0){}
43 
~nConfItemBase()44 nConfItemBase::~nConfItemBase(){}
45 
s_GetConfigMessage(nMessage & m)46 void nConfItemBase::s_GetConfigMessage(nMessage &m){
47     if (sn_GetNetState()==nSERVER){
48         nReadError(); // never accept config messages from the clients
49     }
50     else{
51         tString name;
52         m >> name;
53 
54         //con << "got conf message for " << name << "\n";
55 
56         tConfItemMap & confmap = ConfItemMap();
57         tConfItemMap::iterator iter = confmap.find( name );
58         if ( iter != confmap.end() )
59         {
60             tConfItemBase * item = (*iter).second;
61             nConfItemBase *netitem = dynamic_cast<nConfItemBase*> (item);
62             if (netitem)
63             {
64                 // check if message was new
65                 if ( tSysTimeFloat() > netitem->lastChangeTime_ + 100 || sn_Update( netitem->lastChangeMessage_, m.MessageIDBig() ) )
66                 {
67                     netitem->lastChangeMessage_ = m.MessageIDBig();
68                     netitem->lastChangeTime_ = tSysTimeFloat();
69                     netitem->NetReadVal(m);
70                 }
71                 else
72                 {
73                     static bool warn = true;
74                     if ( warn )
75                         con << tOutput( "$nconfig_error_ignoreold", name );
76                     warn = false;
77                 }
78             }
79             else
80             {
81                 static bool warn = true;
82                 if ( warn )
83                     con << tOutput( "$nconfig_error_nonet", name );
84                 warn = false;
85             }
86         }
87         else
88         {
89             static bool warn = true;
90             if ( warn )
91                 con << tOutput( "$nconfig_error_unknown", name );
92             warn = false;
93         }
94     }
95 }
96 
97 
98 static nDescriptor transferConfig(60,nConfItemBase::s_GetConfigMessage,
99                                   "transfer config");
100 
s_SendConfig(bool force,int peer)101 void nConfItemBase::s_SendConfig(bool force, int peer){
102     if(sn_GetNetState()==nSERVER){
103         tConfItemMap & confmap = ConfItemMap();
104         for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
105         {
106             tConfItemBase * item = (*iter).second;
107 
108             nConfItemBase *netitem = dynamic_cast<nConfItemBase*> (item);
109             if (netitem)
110                 netitem->SendConfig(force, peer);
111         }
112     }
113 }
114 
SendConfig(bool force,int peer)115 void nConfItemBase::SendConfig(bool force, int peer){
116     if ( (changed || force) && sn_GetNetState()==nSERVER)
117     {
118         //con << "sending conf message for " << tConfItems(i)->title << "\n";
119         nMessage *m=new nMessage(transferConfig);
120         *m << title;
121         NetWriteVal(*m);
122         if (peer==-1)
123         {
124             m->BroadCast();
125             changed = false;
126         }
127         else
128             m->Send(peer);
129     }
130 }
131 
WasChanged(bool nonDefault)132 void nConfItemBase::WasChanged( bool nonDefault ){
133     // inform watcher
134     if (this->watcher_ )
135         this->watcher_->Change( nonDefault );
136 
137     SendConfig();
138 }
139 
Writable()140 bool nConfItemBase::Writable()
141 {
142     // network settings are read only on the client
143     if ( sn_GetNetState() == nCLIENT )
144         return false;
145 
146     // on the server, we need to check for a watcher...
147     if ( !watcher_ )
148         return true;
149 
150     // delegate
151     return watcher_->Writable();
152 }
153 
154 // *******************************************************************************************
155 // *
156 // *	s_RevertToDefaults
157 // *
158 // *******************************************************************************************
159 //!
160 //!
161 // *******************************************************************************************
162 
s_RevertToDefaults(void)163 void nConfItemBase::s_RevertToDefaults( void )
164 {
165     tConfItemMap & confmap = ConfItemMap();
166     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
167     {
168         tConfItemBase * item = (*iter).second;
169         nConfItemBase *netitem = dynamic_cast<nConfItemBase*> (item);
170         if (netitem)
171         {
172             netitem->RevertToDefaults();
173         }
174     }
175 }
176 
177 // *******************************************************************************************
178 // *
179 // *	s_SaveValues
180 // *
181 // *******************************************************************************************
182 //!
183 //!
184 // *******************************************************************************************
185 
s_SaveValues(void)186 void nConfItemBase::s_SaveValues( void )
187 {
188     tConfItemMap & confmap = ConfItemMap();
189     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
190     {
191         tConfItemBase * item = (*iter).second;
192         nConfItemBase *netitem = dynamic_cast<nConfItemBase*> (item);
193         if (netitem)
194         {
195             netitem->SaveValue();
196         }
197     }
198 }
199 
200 // *******************************************************************************************
201 // *
202 // *	s_RevertToSavedValues
203 // *
204 // *******************************************************************************************
205 //!
206 //!
207 // *******************************************************************************************
208 
s_RevertToSavedValues(void)209 void nConfItemBase::s_RevertToSavedValues( void )
210 {
211     tConfItemMap & confmap = ConfItemMap();
212     for(tConfItemMap::iterator iter = confmap.begin(); iter != confmap.end() ; ++iter)
213     {
214         tConfItemBase * item = (*iter).second;
215         nConfItemBase *netitem = dynamic_cast<nConfItemBase*> (item);
216         if (netitem)
217         {
218             netitem->RevertToSavedValue();
219         }
220     }
221 }
222 
nConfItemLine(const char * title,tString & s)223 nConfItemLine::nConfItemLine(const char *title,tString &s)
224         :tConfItemBase(title),nConfItem<tString>(s){}
225 
~nConfItemLine()226 nConfItemLine::~nConfItemLine(){}
227 
ReadVal(std::istream & s)228 void nConfItemLine::ReadVal(std::istream & s)
229 {
230     tString dummy;
231     dummy.ReadLine(s,true);
232     if(strcmp(dummy,*target)){
233         if (printChange)
234         {
235             tOutput o;
236             o.SetTemplateParameter(1, title);
237             o.SetTemplateParameter(2, *target);
238             o.SetTemplateParameter(3, dummy);
239             o << "$config_value_changed";
240             con << o;
241         }
242         *target=dummy;
243         changed=true;
244     }
245 
246     *target=dummy;
247 }
248 
249 // *******************************************************************************************
250 // *
251 // *	nIConfItemWatcher
252 // *
253 // *******************************************************************************************
254 //!
255 //!		@param	item	the item to watch
256 //!
257 // *******************************************************************************************
258 
nIConfItemWatcher(nConfItemBase & item)259 nIConfItemWatcher::nIConfItemWatcher( nConfItemBase & item )
260         :watched_( item )
261 {
262     item.watcher_ = this;
263 }
264 
265 // *******************************************************************************************
266 // *
267 // *	~nIConfItemWatcher
268 // *
269 // *******************************************************************************************
270 //!
271 //!
272 // *******************************************************************************************
273 
~nIConfItemWatcher(void)274 nIConfItemWatcher::~nIConfItemWatcher( void )
275 {
276     watched_.watcher_ = NULL;
277 }
278 
279 // *******************************************************************************************
280 // *
281 // *	OnChange
282 // *
283 // *******************************************************************************************
284 //!
285 //!		@param	nonDefault	flag indicating whether the change was away from the default
286 //!
287 // *******************************************************************************************
288 
OnChange(bool nonDefault)289 void nIConfItemWatcher::OnChange( bool nonDefault )
290 {
291 }
292 
293 // *******************************************************************************************
294 // *
295 // *	nIConfItemWatcher
296 // *
297 // *******************************************************************************************
298 //!
299 //!
300 // *******************************************************************************************
301 
302 //nIConfItemWatcher::nIConfItemWatcher( void )
303 //{
304 //}
305 
306 // *******************************************************************************************
307 // *
308 // *	nIConfItemWatcher
309 // *
310 // *******************************************************************************************
311 //!
312 //!		@param	other object to copy from
313 //!
314 // *******************************************************************************************
315 
316 //nIConfItemWatcher::nIConfItemWatcher( nIConfItemWatcher const & other )
317 //{
318 //}
319 
320 // *******************************************************************************************
321 // *
322 // *	operator =
323 // *
324 // *******************************************************************************************
325 //!
326 //!     @param  other object to copy from
327 //!		@return reference to self
328 //!
329 // *******************************************************************************************
330 
331 //nIConfItemWatcher & nIConfItemWatcher::operator =( nIConfItemWatcher const & other )
332 //{
333 //	return *this;
334 //}
335 
336 typedef std::set< nConfItemVersionWatcher * > nStrongWatcherList;
337 static nStrongWatcherList * sn_watchers=0;
338 static int sn_refcount=0;
339 
sn_GetStrongWatchers()340 static nStrongWatcherList & sn_GetStrongWatchers()
341 {
342     if (!sn_watchers)
343     {
344         sn_watchers = tNEW(nStrongWatcherList)();
345     }
346 
347     return *sn_watchers;
348 }
349 
sn_StrongWatchersAddRef()350 void sn_StrongWatchersAddRef()
351 {
352     sn_refcount++;
353 }
354 
sn_StrongWatchersRelease()355 void sn_StrongWatchersRelease()
356 {
357     if ( --sn_refcount <= 0 )
358     {
359         tDESTROY( sn_watchers );
360     }
361 }
362 
363 // *******************************************************************************************
364 // *
365 // *	nConfItemVersionWatcher
366 // *
367 // *******************************************************************************************
368 //!
369 //!		@param	item
370 //!		@param	feature
371 //!
372 // *******************************************************************************************
373 
nConfItemVersionWatcher(nConfItemBase & item,Group c,int min,int max)374 nConfItemVersionWatcher::nConfItemVersionWatcher( nConfItemBase & item, Group c, int min, int max )
375         : nIConfItemWatcher( item )
376         , version_( min, max > 0 ? max : 0x7FFFFFFF )
377         , nonDefault_( false )
378         , reverted_( false )
379         , group_( c )
380         , overrideGroupBehavior_( Behavior_Default )
381         , overrideGroupBehaviorConf_( item.GetTitle() + "_OVERRIDE", overrideGroupBehavior_ )
382 {
383     sn_StrongWatchersAddRef();
384     sn_GetStrongWatchers().insert(this);
385 }
386 
387 // *******************************************************************************************
388 // *
389 // *	~nConfItemVersionWatcher
390 // *
391 // *******************************************************************************************
392 //!
393 //!
394 // *******************************************************************************************
395 
~nConfItemVersionWatcher(void)396 nConfItemVersionWatcher::~nConfItemVersionWatcher( void )
397 {
398     sn_GetStrongWatchers().erase(this);
399     sn_StrongWatchersRelease();
400 }
401 
402 // *******************************************************************************************
403 // *
404 // *	OnChange
405 // *
406 // *******************************************************************************************
407 //!
408 //!     @param  nonDefault  flag indicating whether the change was away from the default
409 //!
410 // *******************************************************************************************
411 
OnChange(bool nonDefault)412 void nConfItemVersionWatcher::OnChange( bool nonDefault )
413 {
414     bool changed = ( nonDefault != nonDefault_ );
415 
416     nonDefault_ = nonDefault;
417     if ( changed )
418     {
419         sn_UpdateCurrentVersion();
420     }
421 }
422 
423 static char const * sn_groupName[ nConfItemVersionWatcher::Group_Max ] =
424     {
425         "Breaking",
426         "Bumpy",
427         "Annoying",
428         "Cheating",
429         "Visual"
430     };
431 
432 // mapping network version to program version
433 static char const * sn_versionString[] =
434     {
435         "0.2.0",   // 0
436         "0.2.0",   // 1
437         "0.2.5.0", // 2
438         "0.2.6.0", // 3
439         "0.2.7.1", // 4
440         "0.2.8_beta1", // 5
441         "0.2.8_beta1", // 6
442         "0.2.8_beta2", // 7
443         "0.2.8_beta3", // 8
444         "0.2.8_beta4",   // 9
445         "0.2.8_rc1",     // 10
446         "0.2.8.0",       // 11
447         "0.2.8_alpha20060414", // 12
448         "0.2.8.2", // 13
449         "0.2.8.3_alpha", // 14
450         "0.2.8.3_alpha_auth", // 15
451         "0.2.8.3.X", // 16, was: 0.2.8.3_beta2
452        0
453     };
454 
sn_GetCurrentProtocolVersion()455 int sn_GetCurrentProtocolVersion()
456 {
457     return (sizeof(sn_versionString)/sizeof(char const *)) - 2;
458 }
459 
sn_GetVersionString(int version)460 static char const * sn_GetVersionString( int version )
461 {
462     tVERIFY ( version * sizeof( char * ) < sizeof sn_versionString );
463     tVERIFY ( version >= 0 );
464 
465     return sn_versionString[ version ];
466 }
467 
sn_GetClientVersionString(int version)468 tOutput sn_GetClientVersionString(int version) {
469     if(version >= 0 && version * sizeof(char *) < sizeof(sn_versionString)) {
470         tOutput ret;
471         ret.AddLiteral(sn_GetVersionString(version));
472         return ret;
473     }
474     return tOutput("$network_unknown_version", version);
475 }
476 
477 // *******************************************************************************************
478 // *
479 // *	AdaptVersion
480 // *
481 // *******************************************************************************************
482 //!
483 //!		@param	version	the version to adapt to the settings
484 //!
485 // *******************************************************************************************
486 
487 // the last version we warned the user about
488 static nVersion lastVersion = sn_MyVersion();
489 
AdaptVersion(nVersion & version)490 void nConfItemVersionWatcher::AdaptVersion( nVersion & version )
491 {
492     if ( sn_GetNetState() != nSERVER )
493         return;
494 
495     // iterate over all watchers
496     nStrongWatcherList & watchers = sn_GetStrongWatchers();
497     for ( nStrongWatcherList::iterator iter = watchers.begin(); iter != watchers.end(); ++iter )
498     {
499         nConfItemVersionWatcher * run = *iter;
500 
501         // adapt version to needs
502         if ( run->nonDefault_ && run->GetBehavior() >= Behavior_Block )
503         {
504             tVERIFY( version.Merge( version, run->version_ ) );
505             if ( version.Min() > lastVersion.Min() )
506             {
507                 // inform user about potential problem
508                 tOutput o;
509                 run->FillTemplateParameters(o);
510                 o << "$setting_legacy_clientblock";
511                 con << o;
512 
513                 lastVersion = version;
514             }
515         }
516     }
517 }
518 
519 // *******************************************************************************************
520 // *
521 // *	OnVersionChange
522 // *
523 // *******************************************************************************************
524 //!
525 //!		@param	version	the version to adapt the settings to
526 //!
527 // *******************************************************************************************
528 
OnVersionChange(nVersion const & version)529 void nConfItemVersionWatcher::OnVersionChange( nVersion const & version )
530 {
531     // store version for reference
532     lastVersion = version;
533 
534     if ( sn_GetNetState() != nSERVER )
535         return;
536 
537     // iterate over all watchers
538     nStrongWatcherList & watchers = sn_GetStrongWatchers();
539     for ( nStrongWatcherList::iterator iter = watchers.begin(); iter != watchers.end(); ++iter )
540     {
541         nConfItemVersionWatcher * run = *iter;
542 
543         // warn about settings that will revert or be ignored
544         Behavior behavior = run->GetBehavior();
545         if (run->nonDefault_ && behavior != Behavior_Block )
546         {
547             // don't warn twice for the same group and behavior
548             static int warnedRevert[ Behavior_Default ][ Group_Max ];
549             {
550                 static bool inited = false;
551                 if (!inited)
552                 {
553                     inited = true;
554                     for ( int i = Behavior_Default-1; i>=0; --i )
555                         for ( int j = Group_Max-1; j>=0; --j )
556                             warnedRevert[i][j] = sn_MyVersion().Min();
557                 }
558             }
559             int & warned = warnedRevert[ behavior ][ run->group_ ];
560 
561             if ( warned < run->version_.Min() && run->version_.Min() > version.Min() )
562             {
563                 warned = run->version_.Min();
564 
565                 // inform user about potential problem with nondefault settings
566                 tOutput o;
567                 run->FillTemplateParameters(o);
568                 o << ( ( behavior == Behavior_Revert ) ? "$setting_legacy_revert" : "$setting_legacy_ignore" );
569                 con << o;
570             }
571         }
572 
573         // ignore settings where this is desired
574         if ( run->GetBehavior() == Behavior_Nothing )
575             continue;
576 
577         // if version is supported..
578         if ( run->version_.Min() <= version.Max() )
579         {
580             // ...restore saved value of config item
581             if ( run->reverted_ )
582             {
583                 run->reverted_ = false;
584                 run->watched_.RevertToSavedValue();
585             }
586         }
587         else
588         {
589             // version is not supported. Revert to defaults.
590             if ( !run->reverted_ && run->nonDefault_ )
591             {
592                 run->reverted_ = true;
593                 run->watched_.SaveValue();
594                 run->watched_.RevertToDefaults();
595             }
596         }
597 
598     }
599 }
600 
601 static nConfItemVersionWatcher::Behavior sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Max ] =
602     {
603         Behavior_Block,
604         Behavior_Block,
605         Behavior_Nothing,
606         Behavior_Block,
607         Behavior_Nothing,
608     };
609 
610 static tSettingItem< nConfItemVersionWatcher::Behavior > sn_GroupBehaviorBreaks( "SETTING_LEGACY_BEHAVIOR_BREAKING", sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Breaking] );
611 static tSettingItem< nConfItemVersionWatcher::Behavior > sn_GroupBehaviorBumpy( "SETTING_LEGACY_BEHAVIOR_BUMPY", sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Bumpy] );
612 static tSettingItem< nConfItemVersionWatcher::Behavior > sn_GroupBehaviorAnnoyance( "SETTING_LEGACY_BEHAVIOR_ANNOYING", sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Annoying] );
613 static tSettingItem< nConfItemVersionWatcher::Behavior > sn_GroupBehaviorCheat( "SETTING_LEGACY_BEHAVIOR_CHEATING", sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Cheating] );
614 static tSettingItem< nConfItemVersionWatcher::Behavior > sn_GroupBehaviorDisplay( "SETTING_LEGACY_BEHAVIOR_VISUAL", sn_GroupBehaviors[ nConfItemVersionWatcher::Group_Visual] );
615 
616 // *******************************************************************************************
617 // *
618 // *	GetBehavior
619 // *
620 // *******************************************************************************************
621 //!
622 //!		@return		behavior if this setting is on non-default and a client that does not support it connects
623 //!
624 // *******************************************************************************************
625 
GetBehavior(void) const626 nConfItemVersionWatcher::Behavior nConfItemVersionWatcher::GetBehavior( void ) const
627 {
628     // look up default behavior
629     tASSERT( 0 <= group_ && group_ < nConfItemVersionWatcher::Group_Max );
630     Behavior behavior = sn_GroupBehaviors[ group_ ];
631 
632     // override it
633     if ( Behavior_Default != this->overrideGroupBehavior_ )
634         behavior = this->overrideGroupBehavior_;
635 
636     return behavior;
637 }
638 
639 // *******************************************************************************************
640 // *
641 // *	GetBehavior
642 // *
643 // *******************************************************************************************
644 //!
645 //!		@param	behavior	behavior if this setting is on non-default and a client that does not support it connects to fill
646 //!		@return		A reference to this to allow chaining
647 //!
648 // *******************************************************************************************
649 
GetBehavior(Behavior & behavior) const650 nConfItemVersionWatcher const & nConfItemVersionWatcher::GetBehavior( Behavior & behavior ) const
651 {
652     behavior = this->GetBehavior();
653     return *this;
654 }
655 
656 // *******************************************************************************************
657 // *
658 // *	DoWritable
659 // *
660 // *******************************************************************************************
661 //!
662 //!		@return		true if the watched item is changable
663 //!
664 // *******************************************************************************************
665 
DoWritable(void) const666 bool nConfItemVersionWatcher::DoWritable( void ) const
667 {
668     // if we're not set to revert, the setting is writable
669     if ( GetBehavior() != Behavior_Revert )
670         return true;
671 
672     // and if the setting is currently supported by all parites, it's writable too
673     if ( version_.Min() <= sn_CurrentVersion().Max() )
674         return true;
675 
676     // inform user about impossible change
677     tOutput o;
678     FillTemplateParameters(o);
679     o << "$setting_legacy_change_blocked";
680     con << o;
681 
682     // only if it's not, it needs to be protected.
683     return false;
684 
685 }
686 
687 // *******************************************************************************************
688 // *
689 // *	FillTemplateParameters
690 // *
691 // *******************************************************************************************
692 //!
693 //!		@param	o	output to fill. Template parameter 1 will be the setting name, parameter 2 the setting's group, and parameter 3 the first version supporting the setting
694 //!
695 // *******************************************************************************************
696 
FillTemplateParameters(tOutput & o) const697 void nConfItemVersionWatcher::FillTemplateParameters( tOutput & o ) const
698 {
699     o.SetTemplateParameter(1, watched_.GetTitle() );
700     o.SetTemplateParameter(2, sn_groupName[group_] );
701     o.SetTemplateParameter(3, sn_GetVersionString( version_.Min() ) );
702 }
703 
704 
705