2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
2009-11-25 18:38:54 +00:00
2016-04-03 18:07:56 +00:00
# include "C4Include.h"
2016-10-22 11:19:22 +00:00
# include "C4ForbidLibraryCompilation.h"
2016-04-03 18:07:56 +00:00
# include "network/C4Network2.h"
2009-05-08 13:28:41 +00:00
2016-04-03 18:07:56 +00:00
# include "C4Version.h"
# include "lib/C4Log.h"
# include "game/C4Application.h"
# include "editor/C4Console.h"
# include "control/C4GameSave.h"
# include "control/C4RoundResults.h"
# include "game/C4Game.h"
# include "game/C4GraphicsSystem.h"
# include "graphics/C4GraphicsResource.h"
2016-10-20 21:42:46 +00:00
# include "graphics/C4Draw.h"
2016-04-03 18:07:56 +00:00
# include "control/C4GameControl.h"
2009-05-08 13:28:41 +00:00
// lobby
2016-04-03 18:07:56 +00:00
# include "gui/C4Gui.h"
# include "gui/C4GameLobby.h"
2009-05-08 13:28:41 +00:00
2016-04-03 18:07:56 +00:00
# include "network/C4Network2Dialogs.h"
# include "network/C4League.h"
2009-05-08 13:28:41 +00:00
# ifdef _WIN32
# include <direct.h>
# endif
# ifndef HAVE_WINSOCK
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# endif
// compile options
# ifdef _MSC_VER
# pragma warning (disable: 4355)
# endif
// *** C4Network2Status
C4Network2Status : : C4Network2Status ( )
2010-03-28 18:58:01 +00:00
: eState ( GS_None ) , iTargetCtrlTick ( - 1 )
2009-05-08 13:28:41 +00:00
{
}
const char * C4Network2Status : : getStateName ( ) const
{
2010-03-28 18:58:01 +00:00
switch ( eState )
2009-05-08 13:28:41 +00:00
{
case GS_None : return " none " ;
case GS_Init : return " init " ;
case GS_Lobby : return " lobby " ;
case GS_Pause : return " pause " ;
case GS_Go : return " go " ;
}
return " ??? " ;
}
const char * C4Network2Status : : getDescription ( ) const
{
2010-03-28 18:58:01 +00:00
switch ( eState )
2009-05-08 13:28:41 +00:00
{
case GS_None : return LoadResStr ( " IDS_DESC_NOTINITED " ) ;
case GS_Init : return LoadResStr ( " IDS_DESC_WAITFORHOST " ) ;
case GS_Lobby : return LoadResStr ( " IDS_DESC_EXPECTING " ) ;
case GS_Pause : return LoadResStr ( " IDS_DESC_GAMEPAUSED " ) ;
case GS_Go : return LoadResStr ( " IDS_DESC_GAMERUNNING " ) ;
}
return LoadResStr ( " IDS_DESC_UNKNOWNGAMESTATE " ) ;
}
void C4Network2Status : : Set ( C4NetGameState enState , int32_t inTargetTick )
{
eState = enState ; iTargetCtrlTick = inTargetTick ;
}
void C4Network2Status : : SetCtrlMode ( int32_t inCtrlMode )
{
iCtrlMode = inCtrlMode ;
}
void C4Network2Status : : SetTargetTick ( int32_t inTargetCtrlTick )
{
iTargetCtrlTick = inTargetCtrlTick ;
}
void C4Network2Status : : Clear ( )
{
eState = GS_None ; iTargetCtrlTick = - 1 ;
}
void C4Network2Status : : CompileFunc ( StdCompiler * pComp )
{
CompileFunc ( pComp , false ) ;
}
void C4Network2Status : : CompileFunc ( StdCompiler * pComp , bool fReference )
{
StdEnumEntry < C4NetGameState > GameStates [ ] =
{
2010-03-28 18:58:01 +00:00
{ " None " , GS_None } ,
{ " Init " , GS_Init } ,
{ " Lobby " , GS_Lobby } ,
{ " Paused " , GS_Pause } ,
{ " Running " , GS_Go } ,
2009-05-08 13:28:41 +00:00
} ;
2010-03-27 16:05:02 +00:00
pComp - > Value ( mkNamingAdapt ( mkEnumAdaptT < uint8_t > ( eState , GameStates ) , " State " , GS_None ) ) ;
pComp - > Value ( mkNamingAdapt ( mkIntPackAdapt ( iCtrlMode ) , " CtrlMode " , - 1 ) ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
if ( ! fReference )
2009-05-08 13:28:41 +00:00
pComp - > Value ( mkNamingAdapt ( mkIntPackAdapt ( iTargetCtrlTick ) , " TargetTick " , - 1 ) ) ;
}
// *** C4Network2
C4Network2 : : C4Network2 ( )
2010-03-28 18:58:01 +00:00
: Clients ( & NetIO ) ,
2009-05-08 13:28:41 +00:00
fAllowJoin ( false ) ,
iDynamicTick ( - 1 ) , fDynamicNeeded ( false ) ,
fStatusAck ( false ) , fStatusReached ( false ) ,
fChasing ( false ) ,
2016-11-02 23:58:02 +00:00
pControl ( nullptr ) ,
pLobby ( nullptr ) , fLobbyRunning ( false ) , pLobbyCountdown ( nullptr ) ,
2009-05-08 13:28:41 +00:00
iNextClientID ( 0 ) ,
iLastChaseTargetUpdate ( 0 ) ,
2013-12-08 17:04:19 +00:00
tLastActivateRequest ( C4TimeMilliseconds : : NegativeInfinity ) ,
2010-01-25 04:00:59 +00:00
iLastReferenceUpdate ( 0 ) ,
iLastLeagueUpdate ( 0 ) ,
2016-11-02 23:58:02 +00:00
pLeagueClient ( nullptr ) ,
2009-05-08 13:28:41 +00:00
fDelayedActivateReq ( false ) ,
2016-11-02 23:58:02 +00:00
pVoteDialog ( nullptr ) ,
2009-05-08 13:28:41 +00:00
fPausedForVote ( false ) ,
iLastOwnVoting ( 0 ) ,
2016-10-20 19:14:08 +00:00
fStreaming ( false ) ,
NetpuncherGameID ( 0 )
2009-05-08 13:28:41 +00:00
{
}
C4Network2 : : ~ C4Network2 ( )
{
Clear ( ) ;
}
bool C4Network2 : : InitHost ( bool fLobby )
{
2010-03-28 18:58:01 +00:00
if ( isEnabled ( ) ) Clear ( ) ;
2009-05-08 13:28:41 +00:00
// initialize everything
2009-06-15 22:06:37 +00:00
Status . Set ( fLobby ? GS_Lobby : GS_Go , : : Control . ControlTick ) ;
2009-05-08 13:28:41 +00:00
Status . SetCtrlMode ( Config . Network . ControlMode ) ;
fHost = true ;
fStatusAck = fStatusReached = true ;
fChasing = false ;
fAllowJoin = false ;
iNextClientID = C4ClientIDStart ;
2016-10-20 19:14:08 +00:00
NetpuncherGameID = 0 ;
NetpuncherAddr = : : Config . Network . PuncherAddress ;
2009-05-08 13:28:41 +00:00
// initialize client list
Clients . Init ( & Game . Clients , true ) ;
// initialize resource list
2010-03-28 18:58:01 +00:00
if ( ! ResList . Init ( Game . Clients . getLocalID ( ) , & NetIO ) )
{ LogFatal ( " Network: failed to initialize resource list! " ) ; Clear ( ) ; return false ; }
if ( ! Game . Parameters . InitNetwork ( & ResList ) )
2009-05-08 13:28:41 +00:00
return false ;
// create initial dynamic
2010-03-28 18:58:01 +00:00
if ( ! CreateDynamic ( true ) )
2009-05-08 13:28:41 +00:00
return false ;
// initialize net i/o
2010-03-28 18:58:01 +00:00
if ( ! InitNetIO ( false , true ) )
2009-05-08 13:28:41 +00:00
{ Clear ( ) ; return false ; }
// init network control
2009-06-15 22:06:37 +00:00
pControl = & : : Control . Network ;
pControl - > Init ( C4ClientIDHost , true , : : Control . getNextControlTick ( ) , true , this ) ;
2010-03-27 16:05:02 +00:00
// init league
2009-05-08 13:28:41 +00:00
bool fCancel = true ;
2010-03-28 18:58:01 +00:00
if ( ! InitLeague ( & fCancel ) | | ! LeagueStart ( & fCancel ) )
2009-05-08 13:28:41 +00:00
{
// deinit league
DeinitLeague ( ) ;
// user cancelled?
2010-03-28 18:58:01 +00:00
if ( fCancel )
2009-05-08 13:28:41 +00:00
return false ;
// in console mode, bail out
# ifdef USE_CONSOLE
return false ;
# endif
}
2010-03-27 16:05:02 +00:00
// allow connect
NetIO . SetAcceptMode ( true ) ;
2009-05-08 13:28:41 +00:00
// timer
Application . Add ( this ) ;
// ok
return true ;
}
C4Network2 : : InitResult C4Network2 : : InitClient ( const C4Network2Reference & Ref , bool fObserver )
{
2010-03-28 18:58:01 +00:00
if ( isEnabled ( ) ) Clear ( ) ;
2009-05-08 13:28:41 +00:00
// Get host core
const C4ClientCore & HostCore = Ref . Parameters . Clients . getHost ( ) - > getCore ( ) ;
2010-03-22 12:41:14 +00:00
// host core revision check
if ( ! SEqualNoCase ( HostCore . getRevision ( ) , Application . GetRevision ( ) ) )
{
StdStrBuf msg ;
2010-03-22 21:49:53 +00:00
msg . Format ( LoadResStr ( " IDS_NET_ERR_VERSIONMISMATCH " ) , HostCore . getRevision ( ) , Application . GetRevision ( ) ) ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2010-03-22 12:41:14 +00:00
{
2016-11-02 23:58:02 +00:00
if ( ! pGUI - > ShowMessageModal ( msg . getData ( ) , " [!]Network warning " , C4GUI : : MessageDialog : : btnOKAbort , C4GUI : : Ico_Notify , nullptr /* do not allow to skip this message! */ ) )
2010-03-22 12:41:14 +00:00
return IR_Fatal ;
}
else
{
Log ( msg . getData ( ) ) ;
}
}
2009-05-08 13:28:41 +00:00
// repeat if wrong password
fWrongPassword = Ref . isPasswordNeeded ( ) ;
2016-10-20 19:14:08 +00:00
NetpuncherGameID = Ref . getNetpuncherGameID ( ) ;
NetpuncherAddr = Ref . getNetpuncherAddr ( ) ;
2009-05-08 13:28:41 +00:00
StdStrBuf Password ;
2010-03-28 18:58:01 +00:00
for ( ; ; )
2009-05-08 13:28:41 +00:00
{
// ask for password (again)?
2010-03-28 18:58:01 +00:00
if ( fWrongPassword )
2009-05-08 13:28:41 +00:00
{
Password . Take ( QueryClientPassword ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! Password . getLength ( ) )
2009-05-08 13:28:41 +00:00
return IR_Error ;
fWrongPassword = false ;
}
// copy addresses
C4Network2Address Addrs [ C4ClientMaxAddr ] ;
2010-03-28 18:58:01 +00:00
for ( int i = 0 ; i < Ref . getAddrCnt ( ) ; i + + )
2013-02-18 14:30:00 +00:00
{
2009-05-08 13:28:41 +00:00
Addrs [ i ] = Ref . getAddr ( i ) ;
2013-02-18 14:30:00 +00:00
Addrs [ i ] . getAddr ( ) . SetScopeId ( Ref . GetSourceAddress ( ) . GetScopeId ( ) ) ;
}
2009-05-08 13:28:41 +00:00
// Try to connect to host
2010-03-28 18:58:01 +00:00
if ( InitClient ( Addrs , Ref . getAddrCnt ( ) , HostCore , Password . getData ( ) ) = = IR_Fatal )
2009-05-08 13:28:41 +00:00
return IR_Fatal ;
// success?
2010-03-28 18:58:01 +00:00
if ( isEnabled ( ) )
2009-05-08 13:28:41 +00:00
break ;
// Retry only for wrong password
2010-03-28 18:58:01 +00:00
if ( ! fWrongPassword )
2009-05-08 13:28:41 +00:00
{
LogSilent ( " Network: Could not connect! " ) ;
return IR_Error ;
}
}
2012-10-21 20:20:43 +00:00
// initialize resources
2010-03-28 18:58:01 +00:00
if ( ! Game . Parameters . InitNetwork ( & ResList ) )
2009-05-08 13:28:41 +00:00
return IR_Fatal ;
2010-03-27 16:05:02 +00:00
// init league
2016-11-02 23:58:02 +00:00
if ( ! InitLeague ( nullptr ) )
2009-05-08 13:28:41 +00:00
{
// deinit league
DeinitLeague ( ) ;
return IR_Fatal ;
}
2010-03-27 16:05:02 +00:00
// allow connect
NetIO . SetAcceptMode ( true ) ;
2009-05-08 13:28:41 +00:00
// timer
Application . Add ( this ) ;
// ok, success
return IR_Success ;
}
C4Network2 : : InitResult C4Network2 : : InitClient ( const class C4Network2Address * pAddrs , int iAddrCount , const C4ClientCore & HostCore , const char * szPassword )
{
// initialization
Status . Set ( GS_Init , - 1 ) ;
fHost = false ;
fStatusAck = fStatusReached = true ;
fChasing = true ;
fAllowJoin = false ;
// initialize client list
Game . Clients . Init ( C4ClientIDUnknown ) ;
Clients . Init ( & Game . Clients , false ) ;
// initialize resource list
2010-03-28 18:58:01 +00:00
if ( ! ResList . Init ( Game . Clients . getLocalID ( ) , & NetIO ) )
2009-05-08 13:28:41 +00:00
{ LogFatal ( LoadResStr ( " IDS_NET_ERR_INITRESLIST " ) ) ; Clear ( ) ; return IR_Fatal ; }
// initialize net i/o
2010-03-28 18:58:01 +00:00
if ( ! InitNetIO ( true , false ) )
2009-05-08 13:28:41 +00:00
{ Clear ( ) ; return IR_Fatal ; }
// set network control
2009-06-15 22:06:37 +00:00
pControl = & : : Control . Network ;
2009-05-08 13:28:41 +00:00
// set exclusive connection mode
NetIO . SetExclusiveConnMode ( true ) ;
2016-10-20 19:14:08 +00:00
// warm up netpuncher
InitPuncher ( ) ;
2009-05-08 13:28:41 +00:00
// try to connect host
StdStrBuf strAddresses ; int iSuccesses = 0 ;
2010-03-28 18:58:01 +00:00
for ( int i = 0 ; i < iAddrCount ; i + + )
if ( ! pAddrs [ i ] . isIPNull ( ) )
{
2017-01-06 12:51:51 +00:00
auto addr = pAddrs [ i ] . getAddr ( ) ;
std : : vector < C4NetIO : : addr_t > addrs ;
if ( addr . IsLocal ( ) )
{
// Local IPv6 addresses need a scope id.
for ( auto & id : Clients . GetLocal ( ) - > getInterfaceIDs ( ) )
{
addr . SetScopeId ( id ) ;
addrs . push_back ( addr ) ;
}
}
else
addrs . push_back ( addr ) ;
2009-05-08 13:28:41 +00:00
// connection
2017-01-06 12:51:51 +00:00
int cnt = 0 ;
for ( auto & a : addrs )
if ( NetIO . Connect ( a , pAddrs [ i ] . getProtocol ( ) , HostCore , szPassword ) )
cnt + + ;
if ( cnt = = 0 ) continue ;
2009-05-08 13:28:41 +00:00
// format for message
2010-03-28 18:58:01 +00:00
if ( strAddresses . getLength ( ) )
2009-05-08 13:28:41 +00:00
strAddresses . Append ( " , " ) ;
strAddresses . Append ( pAddrs [ i ] . toString ( ) ) ;
iSuccesses + + ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// no connection attempt running?
2010-03-28 18:58:01 +00:00
if ( ! iSuccesses )
2009-05-08 13:28:41 +00:00
{ Clear ( ) ; return IR_Error ; }
// log
StdStrBuf strMessage = FormatString ( LoadResStr ( " IDS_NET_CONNECTHOST " ) , strAddresses . getData ( ) ) ;
Log ( strMessage . getData ( ) ) ;
// show box
2016-11-02 23:58:02 +00:00
C4GUI : : MessageDialog * pDlg = nullptr ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2009-05-08 13:28:41 +00:00
{
// create & show
pDlg = new C4GUI : : MessageDialog ( strMessage . getData ( ) , LoadResStr ( " IDS_NET_JOINGAME " ) ,
2010-03-28 18:58:01 +00:00
C4GUI : : MessageDialog : : btnAbort , C4GUI : : Ico_NetWait , C4GUI : : MessageDialog : : dsMedium ) ;
if ( ! pDlg - > Show ( : : pGUI , true ) ) { Clear ( ) ; return IR_Fatal ; }
2009-05-08 13:28:41 +00:00
}
// wait for connect / timeout / abort by user (host will change status on succesful connect)
2010-03-28 18:58:01 +00:00
while ( Status . getState ( ) = = GS_Init )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! Application . ScheduleProcs ( 100 ) )
2010-10-29 23:47:50 +00:00
{ delete pDlg ; return IR_Fatal ; }
2010-03-28 18:58:01 +00:00
if ( pDlg & & pDlg - > IsAborted ( ) )
2010-10-29 23:47:50 +00:00
{ delete pDlg ; return IR_Fatal ; }
2009-05-08 13:28:41 +00:00
}
// Close dialog
2010-10-29 23:47:50 +00:00
delete pDlg ;
2009-05-08 13:28:41 +00:00
// error?
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) )
2009-05-08 13:28:41 +00:00
return IR_Error ;
// deactivate exclusive connection mode
NetIO . SetExclusiveConnMode ( false ) ;
return IR_Success ;
}
bool C4Network2 : : DoLobby ( )
{
// shouldn't do lobby?
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) | | ( ! isHost ( ) & & ! isLobbyActive ( ) ) )
2009-05-08 13:28:41 +00:00
return true ;
// lobby runs
fLobbyRunning = true ;
2010-03-27 16:05:02 +00:00
fAllowJoin = true ;
Log ( LoadResStr ( " IDS_NET_LOBBYWAITING " ) ) ;
2009-05-08 13:28:41 +00:00
// client: lobby status reached, message to host
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) )
2009-05-08 13:28:41 +00:00
CheckStatusReached ( ) ;
// host: set lobby mode
else
ChangeGameStatus ( GS_Lobby , 0 ) ;
// determine lobby type
2013-10-29 13:27:18 +00:00
if ( Console . Active )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// console lobby - update console
2016-10-30 20:35:38 +00:00
Console . UpdateMenus ( ) ;
2009-05-08 13:28:41 +00:00
// init lobby countdown if specified
if ( Game . iLobbyTimeout ) StartLobbyCountdown ( Game . iLobbyTimeout ) ;
// do console lobby
2010-03-28 18:58:01 +00:00
while ( isLobbyActive ( ) )
2009-05-08 13:28:41 +00:00
if ( ! Application . ScheduleProcs ( ) )
{ Clear ( ) ; return false ; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// fullscreen lobby
// init lobby dialog
pLobby = new C4GameLobby : : MainDlg ( isHost ( ) ) ;
2016-11-02 23:58:02 +00:00
if ( ! pLobby - > FadeIn ( : : pGUI ) ) { delete pLobby ; pLobby = nullptr ; Clear ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
// init lobby countdown if specified
if ( Game . iLobbyTimeout ) StartLobbyCountdown ( Game . iLobbyTimeout ) ;
// while state lobby: keep looping
2010-10-29 23:47:50 +00:00
while ( isLobbyActive ( ) & & pLobby & & pLobby - > IsShown ( ) )
2009-05-08 13:28:41 +00:00
if ( ! Application . ScheduleProcs ( ) )
{ Clear ( ) ; return false ; }
2010-10-29 23:47:50 +00:00
// check whether lobby was aborted
2016-11-02 23:58:02 +00:00
if ( pLobby & & pLobby - > IsAborted ( ) ) { delete pLobby ; pLobby = nullptr ; Clear ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
// deinit lobby
if ( pLobby & & pLobby - > IsShown ( ) ) pLobby - > Close ( true ) ;
2016-11-02 23:58:02 +00:00
delete pLobby ; pLobby = nullptr ;
2009-05-08 13:28:41 +00:00
// close any other dialogs
2010-10-29 23:47:50 +00:00
: : pGUI - > CloseAllDialogs ( false ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// lobby end
2016-11-02 23:58:02 +00:00
delete pLobbyCountdown ; pLobbyCountdown = nullptr ;
2009-05-08 13:28:41 +00:00
fLobbyRunning = false ;
2010-03-27 16:05:02 +00:00
fAllowJoin = ! Config . Network . NoRuntimeJoin ;
2009-05-08 13:28:41 +00:00
// notify user that the lobby has ended (for people who tasked out)
Application . NotifyUserIfInactive ( ) ;
// notify lobby end
bool fGameGo = isEnabled ( ) ;
if ( fGameGo ) Log ( LoadResStr ( " IDS_PRC_GAMEGO " ) ) ; ;
// disabled?
return fGameGo ;
}
bool C4Network2 : : Start ( )
{
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) | | ! isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// change mode: go
2009-06-15 22:06:37 +00:00
ChangeGameStatus ( GS_Go , : : Control . ControlTick ) ;
2009-05-08 13:28:41 +00:00
return true ;
}
bool C4Network2 : : Pause ( )
{
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) | | ! isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// change mode: pause
2009-06-15 22:06:37 +00:00
return ChangeGameStatus ( GS_Pause , : : Control . getNextControlTick ( ) ) ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : Sync ( )
{
// host only
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) | | ! isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// already syncing the network?
2010-03-28 18:58:01 +00:00
if ( ! fStatusAck )
{
2009-05-08 13:28:41 +00:00
// maybe we are already sync?
2010-03-28 18:58:01 +00:00
if ( fStatusReached ) CheckStatusAck ( ) ;
2009-05-08 13:28:41 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// already sync?
2010-03-28 18:58:01 +00:00
if ( isFrozen ( ) ) return true ;
2009-05-08 13:28:41 +00:00
// ok, so let's do a sync: change in the same state we are already in
2009-06-15 22:06:37 +00:00
return ChangeGameStatus ( Status . getState ( ) , : : Control . getNextControlTick ( ) ) ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : FinalInit ( )
{
// check reach
CheckStatusReached ( true ) ;
// reached, waiting for ack?
2010-03-28 18:58:01 +00:00
if ( fStatusReached & & ! fStatusAck )
2009-05-08 13:28:41 +00:00
{
// wait for go acknowledgement
Log ( LoadResStr ( " IDS_NET_JOINREADY " ) ) ;
2010-12-24 13:17:42 +00:00
// any pending keyboard commands should not be routed to cancel the wait dialog - flush the message queue!
2010-03-28 18:58:01 +00:00
if ( ! Application . FlushMessages ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// show box
2016-11-02 23:58:02 +00:00
C4GUI : : Dialog * pDlg = nullptr ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2009-05-08 13:28:41 +00:00
{
2010-04-01 21:08:06 +00:00
// separate dlgs for host/client
2009-05-08 13:28:41 +00:00
if ( isHost ( ) )
pDlg = new C4Network2StartWaitDlg ( ) ;
else
pDlg = new C4GUI : : MessageDialog ( LoadResStr ( " IDS_NET_WAITFORSTART " ) , LoadResStr ( " IDS_NET_CAPTION " ) ,
2010-03-28 18:58:01 +00:00
C4GUI : : MessageDialog : : btnAbort , C4GUI : : Ico_NetWait , C4GUI : : MessageDialog : : dsSmall ) ;
2009-05-08 13:28:41 +00:00
// show it
2010-03-28 18:58:01 +00:00
if ( ! pDlg - > Show ( : : pGUI , true ) ) return false ;
2009-05-08 13:28:41 +00:00
}
// wait for acknowledgement
2010-03-28 18:58:01 +00:00
while ( fStatusReached & & ! fStatusAck )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( pDlg )
2009-05-08 13:28:41 +00:00
{
// execute
2010-03-28 18:58:01 +00:00
if ( ! pDlg - > Execute ( ) ) { delete pDlg ; Clear ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
// aborted?
2010-03-28 18:58:01 +00:00
if ( pDlg - > IsAborted ( ) ) { delete pDlg ; Clear ( ) ; return false ; }
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else if ( ! Application . ScheduleProcs ( ) )
2009-05-08 13:28:41 +00:00
{ Clear ( ) ; return false ; }
}
2010-10-29 23:47:50 +00:00
delete pDlg ;
2009-05-08 13:28:41 +00:00
// log
Log ( LoadResStr ( " IDS_NET_START " ) ) ;
}
2010-03-27 16:05:02 +00:00
// synchronize
Game . SyncClearance ( ) ;
Game . Synchronize ( false ) ;
// finished
2009-05-08 13:28:41 +00:00
return isEnabled ( ) ;
}
bool C4Network2 : : RetrieveScenario ( char * szScenario )
{
// client only
2010-03-28 18:58:01 +00:00
if ( isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// wait for scenario
C4Network2Res : : Ref pScenario = RetrieveRes ( * Game . Parameters . Scenario . getResCore ( ) ,
2010-03-28 18:58:01 +00:00
C4NetResRetrieveTimeout , LoadResStr ( " IDS_NET_RES_SCENARIO " ) ) ;
if ( ! pScenario )
2009-05-08 13:28:41 +00:00
return false ;
// wait for dynamic data
C4Network2Res : : Ref pDynamic = RetrieveRes ( ResDynamic , C4NetResRetrieveTimeout , LoadResStr ( " IDS_NET_RES_DYNAMIC " ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! pDynamic )
2009-05-08 13:28:41 +00:00
return false ;
// create unpacked copy of scenario
2011-03-13 15:11:55 +00:00
if ( ! ResList . FindTempResFileName ( FormatString ( " Combined%d.ocs " , Game . Clients . getLocalID ( ) ) . getData ( ) , szScenario ) | |
2010-03-28 18:58:01 +00:00
! C4Group_CopyItem ( pScenario - > getFile ( ) , szScenario ) | |
! C4Group_UnpackDirectory ( szScenario ) )
2009-05-08 13:28:41 +00:00
return false ;
// create unpacked copy of dynamic data
char szTempDynamic [ _MAX_PATH + 1 ] ;
2010-03-28 18:58:01 +00:00
if ( ! ResList . FindTempResFileName ( pDynamic - > getFile ( ) , szTempDynamic ) | |
! C4Group_CopyItem ( pDynamic - > getFile ( ) , szTempDynamic ) | |
! C4Group_UnpackDirectory ( szTempDynamic ) )
2009-05-08 13:28:41 +00:00
return false ;
2011-03-13 15:16:45 +00:00
// unpack Material.ocg if materials need to be merged
2009-05-08 13:28:41 +00:00
StdStrBuf MaterialScenario , MaterialDynamic ;
MaterialScenario . Format ( " %s " DirSep C4CFN_Material , szScenario ) ;
MaterialDynamic . Format ( " %s " DirSep C4CFN_Material , szTempDynamic ) ;
2010-03-28 18:58:01 +00:00
if ( FileExists ( MaterialScenario . getData ( ) ) & & FileExists ( MaterialDynamic . getData ( ) ) )
if ( ! C4Group_UnpackDirectory ( MaterialScenario . getData ( ) ) | |
! C4Group_UnpackDirectory ( MaterialDynamic . getData ( ) ) )
return false ;
2009-05-08 13:28:41 +00:00
// move all dynamic files to scenario
C4Group ScenGrp ;
2010-03-28 18:58:01 +00:00
if ( ! ScenGrp . Open ( szScenario ) | |
! ScenGrp . Merge ( szTempDynamic ) )
2009-05-08 13:28:41 +00:00
return false ;
ScenGrp . Close ( ) ;
// remove dynamic temp file
EraseDirectory ( szTempDynamic ) ;
// remove dynamic - isn't needed any more and will soon be out-of-date
pDynamic - > Remove ( ) ;
return true ;
}
void C4Network2 : : OnSec1Timer ( )
{
Execute ( ) ;
}
void C4Network2 : : Execute ( )
{
// client connections
Clients . DoConnectAttempts ( ) ;
// status reached?
CheckStatusReached ( ) ;
2010-03-28 18:58:01 +00:00
if ( isHost ( ) )
2009-05-08 13:28:41 +00:00
{
// remove dynamic
2010-03-28 18:58:01 +00:00
if ( ! ResDynamic . isNull ( ) & & : : Control . ControlTick > iDynamicTick )
2009-05-08 13:28:41 +00:00
RemoveDynamic ( ) ;
// Set chase target
UpdateChaseTarget ( ) ;
2010-03-27 16:05:02 +00:00
// check for inactive clients and deactivate them
DeactivateInactiveClients ( ) ;
// reference
2016-11-02 23:58:02 +00:00
if ( ! iLastReferenceUpdate | | time ( nullptr ) > ( time_t ) ( iLastReferenceUpdate + C4NetReferenceUpdateInterval ) )
2010-03-28 18:58:01 +00:00
if ( NetIO . IsReferenceNeeded ( ) )
{
// create
C4Network2Reference * pRef = new C4Network2Reference ( ) ;
pRef - > InitLocal ( ) ;
// set
NetIO . SetReference ( pRef ) ;
2016-11-02 23:58:02 +00:00
iLastReferenceUpdate = time ( nullptr ) ;
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// league server reference
2016-11-02 23:58:02 +00:00
if ( ! iLastLeagueUpdate | | time ( nullptr ) > ( time_t ) ( iLastLeagueUpdate + iLeagueUpdateDelay ) )
2010-03-27 16:05:02 +00:00
{
LeagueUpdate ( ) ;
}
2009-05-08 13:28:41 +00:00
// league update reply receive
2010-03-28 18:58:01 +00:00
if ( pLeagueClient & & fHost & & ! pLeagueClient - > isBusy ( ) & & pLeagueClient - > getCurrentAction ( ) = = C4LA_Update )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
LeagueUpdateProcessReply ( ) ;
2009-05-08 13:28:41 +00:00
}
// voting timeout
2016-11-02 23:58:02 +00:00
if ( Votes . firstPkt ( ) & & time ( nullptr ) > ( time_t ) ( iVoteStartTime + C4NetVotingTimeout ) )
2009-05-08 13:28:41 +00:00
{
C4ControlVote * pVote = static_cast < C4ControlVote * > ( Votes . firstPkt ( ) - > getPkt ( ) ) ;
2009-06-15 22:06:37 +00:00
: : Control . DoInput (
2010-03-28 18:58:01 +00:00
CID_VoteEnd ,
new C4ControlVoteEnd ( pVote - > getType ( ) , false , pVote - > getData ( ) ) ,
CDT_Sync ) ;
2016-11-02 23:58:02 +00:00
iVoteStartTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
}
// record streaming
2010-03-28 18:58:01 +00:00
if ( fStreaming )
{
2009-05-08 13:28:41 +00:00
StreamIn ( false ) ;
StreamOut ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
2010-03-27 16:05:02 +00:00
else
{
// request activate, if neccessary
2013-12-08 17:04:19 +00:00
if ( ! tLastActivateRequest . IsInfinite ( ) ) RequestActivate ( ) ;
2010-03-27 16:05:02 +00:00
}
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : Clear ( )
{
// stop timer
Application . Remove ( this ) ;
// stop streaming
StopStreaming ( ) ;
// clear league
2010-03-28 18:58:01 +00:00
if ( pLeagueClient )
{
2009-05-08 13:28:41 +00:00
LeagueEnd ( ) ;
DeinitLeague ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// stop lobby countdown
2016-11-02 23:58:02 +00:00
delete pLobbyCountdown ; pLobbyCountdown = nullptr ;
2009-05-08 13:28:41 +00:00
// cancel lobby
2016-11-02 23:58:02 +00:00
delete pLobby ; pLobby = nullptr ;
2009-05-08 13:28:41 +00:00
fLobbyRunning = false ;
// deactivate
Status . Clear ( ) ;
fStatusAck = fStatusReached = true ;
// if control mode is network: change to local
2010-03-28 18:58:01 +00:00
if ( : : Control . isNetwork ( ) )
2009-06-15 22:06:37 +00:00
: : Control . ChangeToLocal ( ) ;
2009-05-08 13:28:41 +00:00
// clear all player infos
Players . Clear ( ) ;
// remove all clients
Clients . Clear ( ) ;
// close net classes
NetIO . Clear ( ) ;
2012-10-21 20:20:43 +00:00
// clear resources
2009-05-08 13:28:41 +00:00
ResList . Clear ( ) ;
// clear password
sPassword . Clear ( ) ;
// stuff
fAllowJoin = false ;
2010-03-27 16:05:02 +00:00
iDynamicTick = - 1 ; fDynamicNeeded = false ;
2013-12-08 17:04:19 +00:00
tLastActivateRequest = C4TimeMilliseconds : : NegativeInfinity ;
2013-12-04 12:35:07 +00:00
iLastChaseTargetUpdate = iLastReferenceUpdate = iLastLeagueUpdate = 0 ;
2009-05-08 13:28:41 +00:00
fDelayedActivateReq = false ;
2016-11-02 23:58:02 +00:00
delete pVoteDialog ; pVoteDialog = nullptr ;
2009-05-08 13:28:41 +00:00
fPausedForVote = false ;
iLastOwnVoting = 0 ;
2016-10-20 19:14:08 +00:00
NetpuncherGameID = 0 ;
2011-05-08 20:36:48 +00:00
Votes . Clear ( ) ;
2009-05-08 13:28:41 +00:00
// don't clear fPasswordNeeded here, it's needed by InitClient
}
bool C4Network2 : : ToggleAllowJoin ( )
{
// just toggle
AllowJoin ( ! fAllowJoin ) ;
return true ; // toggled
}
bool C4Network2 : : ToggleClientListDlg ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Network2ClientListDlg : : Toggle ( ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : SetPassword ( const char * szToPassword )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fHadPassword = isPassworded ( ) ;
// clear password?
if ( ! szToPassword | | ! * szToPassword )
sPassword . Clear ( ) ;
else
// no? then set it
sPassword . Copy ( szToPassword ) ;
// if the has-password-state has changed, the reference is invalidated
if ( fHadPassword ! = isPassworded ( ) ) InvalidateReference ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
StdStrBuf C4Network2 : : QueryClientPassword ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// ask client for a password; return nothing if user canceled
StdStrBuf sCaption ; sCaption . Copy ( LoadResStr ( " IDS_MSG_ENTERPASSWORD " ) ) ;
2016-11-02 23:58:02 +00:00
C4GUI : : InputDialog * pInputDlg = new C4GUI : : InputDialog ( LoadResStr ( " IDS_MSG_ENTERPASSWORD " ) , sCaption . getData ( ) , C4GUI : : Ico_Ex_Locked , nullptr , false ) ;
2009-05-08 13:28:41 +00:00
pInputDlg - > SetDelOnClose ( false ) ;
2009-06-05 16:53:56 +00:00
if ( ! : : pGUI - > ShowModalDlg ( pInputDlg , false ) )
2010-03-28 18:58:01 +00:00
{
2010-10-30 00:08:58 +00:00
delete pInputDlg ;
2009-05-08 13:28:41 +00:00
return StdStrBuf ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// copy to buffer
StdStrBuf Buf ; Buf . Copy ( pInputDlg - > GetInputText ( ) ) ;
delete pInputDlg ;
return Buf ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : AllowJoin ( bool fAllow )
{
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return ;
2009-05-08 13:28:41 +00:00
fAllowJoin = fAllow ;
if ( Game . IsRunning )
2010-03-28 18:58:01 +00:00
{
2009-06-05 15:14:20 +00:00
: : GraphicsSystem . FlashMessage ( LoadResStr ( fAllowJoin ? " IDS_NET_RUNTIMEJOINFREE " : " IDS_NET_RUNTIMEJOINBARRED " ) ) ;
2009-05-08 13:28:41 +00:00
Config . Network . NoRuntimeJoin = ! fAllowJoin ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : SetAllowObserve ( bool fAllow )
{
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return ;
2010-03-27 16:05:02 +00:00
fAllowObserve = fAllow ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : SetCtrlMode ( int32_t iCtrlMode )
{
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return ;
2010-03-27 16:05:02 +00:00
// no change?
2010-03-28 18:58:01 +00:00
if ( iCtrlMode = = Status . getCtrlMode ( ) ) return ;
2010-03-27 16:05:02 +00:00
// change game status
ChangeGameStatus ( Status . getState ( ) , : : Control . ControlTick , iCtrlMode ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OnConn ( C4Network2IOConnection * pConn )
{
// Nothing to do atm... New pending connections are managed mainly by C4Network2IO
// until they are accepted, see PID_Conn/PID_ConnRe handlers in HandlePacket.
// Note this won't get called anymore because of this (see C4Network2IO::OnConn)
}
void C4Network2 : : OnDisconn ( C4Network2IOConnection * pConn )
{
2010-03-27 16:05:02 +00:00
// could not establish host connection?
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Init & & ! isHost ( ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! NetIO . getConnectionCount ( ) )
2009-05-08 13:28:41 +00:00
Clear ( ) ;
return ;
}
// connection failed?
2010-03-28 18:58:01 +00:00
if ( pConn - > isFailed ( ) )
2009-05-08 13:28:41 +00:00
{
// call handler
OnConnectFail ( pConn ) ;
return ;
}
// search client
C4Network2Client * pClient = Clients . GetClient ( pConn ) ;
// not found? Search by ID (not associated yet, half-accepted connection)
2010-03-28 18:58:01 +00:00
if ( ! pClient ) pClient = Clients . GetClientByID ( pConn - > getClientID ( ) ) ;
2009-05-08 13:28:41 +00:00
// not found? ignore
2010-03-28 18:58:01 +00:00
if ( ! pClient ) return ;
2009-05-08 13:28:41 +00:00
// remove connection
pClient - > RemoveConn ( pConn ) ;
// create post-mortem if needed
C4PacketPostMortem PostMortem ;
2010-03-28 18:58:01 +00:00
if ( pConn - > CreatePostMortem ( & PostMortem ) )
2009-05-08 13:28:41 +00:00
{
LogSilentF ( " Network: Sending %d packets for recovery (%d-%d) " , PostMortem . getPacketCount ( ) , pConn - > getOutPacketCounter ( ) - PostMortem . getPacketCount ( ) , pConn - > getOutPacketCounter ( ) - 1 ) ;
// This might fail because of this disconnect
// (If it's the only host connection. We're toast then anyway.)
2010-03-28 18:58:01 +00:00
if ( ! Clients . SendMsgToClient ( pConn - > getClientID ( ) , MkC4NetIOPacket ( PID_PostMortem , PostMortem ) ) )
2009-05-08 13:28:41 +00:00
assert ( isHost ( ) | | ! Clients . GetHost ( ) - > isConnected ( ) ) ;
}
// call handler
OnDisconnect ( pClient , pConn ) ;
}
void C4Network2 : : HandlePacket ( char cStatus , const C4PacketBase * pPacket , C4Network2IOConnection * pConn )
{
// find associated client
C4Network2Client * pClient = Clients . GetClient ( pConn ) ;
2010-03-28 18:58:01 +00:00
if ( ! pClient ) pClient = Clients . GetClientByID ( pConn - > getClientID ( ) ) ;
2009-05-08 13:28:41 +00:00
// local? ignore
2010-03-28 18:58:01 +00:00
if ( pClient & & pClient - > isLocal ( ) ) { pConn - > Close ( ) ; return ; }
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define GETPKT(type, name) \
assert ( pPacket ) ; const type & name = \
2015-03-25 18:04:04 +00:00
static_cast < const type & > ( * pPacket ) ;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
switch ( cStatus )
2009-05-08 13:28:41 +00:00
{
case PID_Conn : // connection request
{
2010-03-28 18:58:01 +00:00
if ( ! pConn - > isOpen ( ) ) break ;
2009-05-08 13:28:41 +00:00
GETPKT ( C4PacketConn , rPkt ) ;
HandleConn ( rPkt , pConn , pClient ) ;
}
break ;
case PID_ConnRe : // connection request reply
{
GETPKT ( C4PacketConnRe , rPkt ) ;
HandleConnRe ( rPkt , pConn , pClient ) ;
}
break ;
2010-03-27 16:05:02 +00:00
case PID_JoinData :
{
// host->client only
2010-03-28 18:58:01 +00:00
if ( isHost ( ) | | ! pClient | | ! pClient - > isHost ( ) ) break ;
if ( ! pConn - > isOpen ( ) ) break ;
2010-03-27 16:05:02 +00:00
// handle
GETPKT ( C4PacketJoinData , rPkt )
HandleJoinData ( rPkt ) ;
}
break ;
2009-05-08 13:28:41 +00:00
case PID_Status : // status change
{
// by host only
2010-03-28 18:58:01 +00:00
if ( isHost ( ) | | ! pClient | | ! pClient - > isHost ( ) ) break ;
if ( ! pConn - > isOpen ( ) ) break ;
2009-05-08 13:28:41 +00:00
// must be initialized
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Init ) break ;
2009-05-08 13:28:41 +00:00
// handle
GETPKT ( C4Network2Status , rPkt ) ;
HandleStatus ( rPkt ) ;
}
break ;
case PID_StatusAck : // status change acknowledgement
{
// host->client / client->host only
2010-03-28 18:58:01 +00:00
if ( ! pClient ) break ;
if ( ! isHost ( ) & & ! pClient - > isHost ( ) ) break ;
2009-05-08 13:28:41 +00:00
// must be initialized
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Init ) break ;
2009-05-08 13:28:41 +00:00
// handle
GETPKT ( C4Network2Status , rPkt ) ;
HandleStatusAck ( rPkt , pClient ) ;
}
break ;
2010-03-27 16:05:02 +00:00
case PID_ClientActReq : // client activation request
{
// client->host only
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) | | ! pClient | | pClient - > isHost ( ) ) break ;
2009-05-08 13:28:41 +00:00
// must be initialized
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Init ) break ;
2010-03-27 16:05:02 +00:00
// handle
GETPKT ( C4PacketActivateReq , rPkt )
HandleActivateReq ( rPkt . getTick ( ) , pClient ) ;
}
break ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
# undef GETPKT
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : HandleLobbyPacket ( char cStatus , const C4PacketBase * pBasePkt , C4Network2IOConnection * pConn )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// find associated client
C4Network2Client * pClient = Clients . GetClient ( pConn ) ;
2010-03-28 18:58:01 +00:00
if ( ! pClient ) pClient = Clients . GetClientByID ( pConn - > getClientID ( ) ) ;
2009-05-08 13:28:41 +00:00
// forward directly to lobby
if ( pLobby ) pLobby - > HandlePacket ( cStatus , pBasePkt , pClient ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-10-20 19:14:08 +00:00
bool C4Network2 : : HandlePuncherPacket ( C4NetpuncherPacket : : uptr pkt )
{
// TODO: is this all thread-safe?
assert ( pkt ) ;
# define GETPKT(c) dynamic_cast<C4NetpuncherPacket##c*>(pkt.get())
switch ( pkt - > GetType ( ) )
{
case PID_Puncher_CReq :
if ( isHost ( ) )
{
NetIO . Punch ( GETPKT ( CReq ) - > GetAddr ( ) ) ;
return true ;
}
else
{
// The IP/Port should be already in the masterserver list, so just keep trying.
return Status . getState ( ) = = GS_Init ;
}
case PID_Puncher_AssID :
if ( isHost ( ) )
{
NetpuncherGameID = GETPKT ( AssID ) - > GetID ( ) ;
InvalidateReference ( ) ;
}
else
{
// While we don't need the ID as a client, this nicely serves as the signal that we can start using the netpuncher
if ( Status . getState ( ) = = GS_Init & & getNetpuncherGameID ( ) )
NetIO . SendPuncherPacket ( C4NetpuncherPacketSReq ( getNetpuncherGameID ( ) ) ) ;
}
return true ;
default : return false ;
}
}
void C4Network2 : : InitPuncher ( )
{
// We have an internet connection, so let's punch the puncher server here in order to open an udp port
C4NetIO : : addr_t PuncherAddr ;
if ( ResolveAddress ( getNetpuncherAddr ( ) . getData ( ) , & PuncherAddr , C4NetStdPortPuncher ) )
NetIO . InitPuncher ( PuncherAddr ) ;
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : OnGameSynchronized ( )
{
// savegame needed?
2010-03-28 18:58:01 +00:00
if ( fDynamicNeeded )
2009-05-08 13:28:41 +00:00
{
// create dynamic
bool fSuccess = CreateDynamic ( false ) ;
// check for clients that still need join-data
2016-11-02 23:58:02 +00:00
C4Network2Client * pClient = nullptr ;
2010-03-28 18:58:01 +00:00
while ( ( pClient = Clients . GetNextClient ( pClient ) ) )
if ( ! pClient - > hasJoinData ( ) )
2010-01-25 04:00:59 +00:00
{
2010-03-28 18:58:01 +00:00
if ( fSuccess )
2009-05-08 13:28:41 +00:00
// now we can provide join data: send it
SendJoinData ( pClient ) ;
else
// join data could not be created: emergency kick
Game . Clients . CtrlRemove ( pClient - > getClient ( ) , LoadResStr ( " IDS_ERR_ERRORWHILECREATINGJOINDAT " ) ) ;
2010-01-25 04:00:59 +00:00
}
2009-05-08 13:28:41 +00:00
}
}
void C4Network2 : : DrawStatus ( C4TargetFacet & cgo )
{
2010-03-28 18:58:01 +00:00
if ( ! isEnabled ( ) ) return ;
2009-05-08 13:28:41 +00:00
C4Network2Client * pLocal = Clients . GetLocal ( ) ;
2010-03-27 16:05:02 +00:00
StdStrBuf Stat ;
2009-05-08 13:28:41 +00:00
// local client status
Stat . AppendFormat ( " Local: %s %s %s (ID %d) " ,
2010-03-28 18:58:01 +00:00
pLocal - > isObserver ( ) ? " Observing " : pLocal - > isActivated ( ) ? " Active " : " Inactive " , pLocal - > isHost ( ) ? " host " : " client " ,
pLocal - > getName ( ) , pLocal - > getID ( ) ) ;
2009-05-08 13:28:41 +00:00
// game status
Stat . AppendFormat ( " |Game Status: %s (tick %d)%s%s " ,
2010-03-28 18:58:01 +00:00
Status . getStateName ( ) , Status . getTargetCtrlTick ( ) ,
fStatusReached ? " reached " : " " , fStatusAck ? " ack " : " " ) ;
2009-05-08 13:28:41 +00:00
// available protocols
C4NetIO * pMsgIO = NetIO . MsgIO ( ) , * pDataIO = NetIO . DataIO ( ) ;
2010-03-28 18:58:01 +00:00
if ( pMsgIO & & pDataIO )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
C4Network2IOProtocol eMsgProt = NetIO . getNetIOProt ( pMsgIO ) ,
2010-03-28 18:58:01 +00:00
eDataProt = NetIO . getNetIOProt ( pDataIO ) ;
2009-05-08 13:28:41 +00:00
int32_t iMsgPort = 0 , iDataPort = 0 ;
2010-03-28 18:58:01 +00:00
switch ( eMsgProt )
{
2009-05-08 13:28:41 +00:00
case P_TCP : iMsgPort = Config . Network . PortTCP ; break ;
case P_UDP : iMsgPort = Config . Network . PortUDP ; break ;
2010-01-25 04:00:59 +00:00
case P_NONE : assert ( eMsgProt ! = P_NONE ) ; break ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
switch ( eDataProt )
2009-05-08 13:28:41 +00:00
{
case P_TCP : iDataPort = Config . Network . PortTCP ; break ;
case P_UDP : iDataPort = Config . Network . PortUDP ; break ;
2010-01-25 04:00:59 +00:00
case P_NONE : assert ( eMsgProt ! = P_NONE ) ; break ;
2009-05-08 13:28:41 +00:00
}
Stat . AppendFormat ( " |Protocols: %s: %s (%d i%d o%d bc%d) " ,
2010-03-28 18:58:01 +00:00
pMsgIO ! = pDataIO ? " Msg " : " Msg/Data " ,
NetIO . getNetIOName ( pMsgIO ) , iMsgPort ,
NetIO . getProtIRate ( eMsgProt ) , NetIO . getProtORate ( eMsgProt ) , NetIO . getProtBCRate ( eMsgProt ) ) ;
if ( pMsgIO ! = pDataIO )
Stat . AppendFormat ( " , Data: %s (%d i%d o%d bc%d) " ,
NetIO . getNetIOName ( pDataIO ) , iDataPort ,
NetIO . getProtIRate ( eDataProt ) , NetIO . getProtORate ( eDataProt ) , NetIO . getProtBCRate ( eDataProt ) ) ;
2009-05-08 13:28:41 +00:00
}
else
Stat . Append ( " |Protocols: none " ) ;
// some control statistics
Stat . AppendFormat ( " |Control: %s, Tick %d, Behind %d, Rate %d, PreSend %d, ACT: %d " ,
2010-03-28 18:58:01 +00:00
Status . getCtrlMode ( ) = = CNM_Decentral ? " Decentral " : Status . getCtrlMode ( ) = = CNM_Central ? " Central " : " Async " ,
: : Control . ControlTick , pControl - > GetBehind ( : : Control . ControlTick ) ,
: : Control . ControlRate , pControl - > getControlPreSend ( ) , pControl - > getAvgControlSendTime ( ) ) ;
2009-05-08 13:28:41 +00:00
// Streaming statistics
2010-03-28 18:58:01 +00:00
if ( fStreaming )
2010-01-25 04:00:59 +00:00
Stat . AppendFormat ( " |Streaming: %lu waiting, %u in, %lu out, %lu sent " ,
2010-04-28 21:43:25 +00:00
static_cast < unsigned long > ( pStreamedRecord ? pStreamedRecord - > GetStreamingBuf ( ) . getSize ( ) : 0 ) ,
2010-03-28 18:58:01 +00:00
pStreamedRecord ? pStreamedRecord - > GetStreamingPos ( ) : 0 ,
2010-04-28 21:43:25 +00:00
static_cast < unsigned long > ( getPendingStreamData ( ) ) ,
2010-03-28 18:58:01 +00:00
static_cast < unsigned long > ( iCurrentStreamPosition ) ) ;
2009-05-08 13:28:41 +00:00
// clients
Stat . Append ( " |Clients: " ) ;
2016-11-02 23:58:02 +00:00
for ( C4Network2Client * pClient = Clients . GetNextClient ( nullptr ) ; pClient ; pClient = Clients . GetNextClient ( pClient ) )
2009-05-08 13:28:41 +00:00
{
// ignore local
2010-03-28 18:58:01 +00:00
if ( pClient - > isLocal ( ) ) continue ;
2009-05-08 13:28:41 +00:00
// client status
const C4ClientCore & Core = pClient - > getCore ( ) ;
2010-01-25 04:00:59 +00:00
const char * szClientStatus ;
2010-03-28 18:58:01 +00:00
switch ( pClient - > getStatus ( ) )
2009-05-08 13:28:41 +00:00
{
case NCS_Joining : szClientStatus = " (joining) " ; break ;
case NCS_Chasing : szClientStatus = " (chasing) " ; break ;
case NCS_NotReady : szClientStatus = " (!rdy) " ; break ;
case NCS_Remove : szClientStatus = " (removed) " ; break ;
2010-01-25 04:00:59 +00:00
default : szClientStatus = " " ; break ;
2009-05-08 13:28:41 +00:00
}
Stat . AppendFormat ( " |- %s %s %s (ID %d) (wait %d ms, behind %d)%s%s " ,
2010-03-28 18:58:01 +00:00
Core . isObserver ( ) ? " Observing " : Core . isActivated ( ) ? " Active " : " Inactive " , Core . isHost ( ) ? " host " : " client " ,
Core . getName ( ) , Core . getID ( ) ,
pControl - > ClientPerfStat ( pClient - > getID ( ) ) ,
: : Control . ControlTick - pControl - > ClientNextControl ( pClient - > getID ( ) ) ,
szClientStatus ,
pClient - > isActivated ( ) & & ! pControl - > ClientReady ( pClient - > getID ( ) , : : Control . ControlTick ) ? " (!ctrl) " : " " ) ;
2009-05-08 13:28:41 +00:00
// connections
2010-03-28 18:58:01 +00:00
if ( pClient - > isConnected ( ) )
2009-05-08 13:28:41 +00:00
{
2013-02-18 14:30:00 +00:00
Stat . AppendFormat ( " | Connections: %s: %s (%s p%d l%d) " ,
2010-03-28 18:58:01 +00:00
pClient - > getMsgConn ( ) = = pClient - > getDataConn ( ) ? " Msg/Data " : " Msg " ,
NetIO . getNetIOName ( pClient - > getMsgConn ( ) - > getNetClass ( ) ) ,
2013-02-18 14:30:00 +00:00
pClient - > getMsgConn ( ) - > getPeerAddr ( ) . ToString ( ) . getData ( ) ,
2010-03-28 18:58:01 +00:00
pClient - > getMsgConn ( ) - > getPingTime ( ) ,
pClient - > getMsgConn ( ) - > getPacketLoss ( ) ) ;
if ( pClient - > getMsgConn ( ) ! = pClient - > getDataConn ( ) )
Stat . AppendFormat ( " , Data: %s (%s:%d p%d l%d) " ,
NetIO . getNetIOName ( pClient - > getDataConn ( ) - > getNetClass ( ) ) ,
2013-02-18 14:30:00 +00:00
pClient - > getDataConn ( ) - > getPeerAddr ( ) . ToString ( ) . getData ( ) ,
2010-03-28 18:58:01 +00:00
pClient - > getDataConn ( ) - > getPingTime ( ) ,
pClient - > getDataConn ( ) - > getPacketLoss ( ) ) ;
2009-05-08 13:28:41 +00:00
}
else
Stat . Append ( " | Not connected " ) ;
}
2016-11-02 23:58:02 +00:00
if ( ! Clients . GetNextClient ( nullptr ) )
2009-05-08 13:28:41 +00:00
Stat . Append ( " | - none - " ) ;
// draw
2011-10-03 14:30:18 +00:00
pDraw - > TextOut ( Stat . getData ( ) , : : GraphicsResource . FontRegular , 1.0 , cgo . Surface , cgo . X + 20 , cgo . Y + 50 ) ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : InitNetIO ( bool fNoClientID , bool fHost )
{
// clear
NetIO . Clear ( ) ;
2009-06-17 19:41:55 +00:00
Config . Network . CheckPortsForCollisions ( ) ;
2009-05-08 13:28:41 +00:00
// discovery: disable for client
int16_t iPortDiscovery = fHost ? Config . Network . PortDiscovery : - 1 ;
int16_t iPortRefServer = fHost ? Config . Network . PortRefServer : - 1 ;
// init subclass
2016-04-17 02:02:46 +00:00
if ( ! NetIO . Init ( Config . Network . PortTCP , Config . Network . PortUDP , iPortDiscovery , iPortRefServer , fHost , ! ! Config . Network . EnableUPnP ) )
2009-05-08 13:28:41 +00:00
return false ;
// set core (unset ID if sepecified, has to be set later)
C4ClientCore Core = Game . Clients . getLocalCore ( ) ;
2010-03-28 18:58:01 +00:00
if ( fNoClientID ) Core . SetID ( C4ClientIDUnknown ) ;
2009-05-08 13:28:41 +00:00
NetIO . SetLocalCCore ( Core ) ;
// safe addresses of local client
Clients . GetLocal ( ) - > AddLocalAddrs (
2010-03-28 18:58:01 +00:00
NetIO . hasTCP ( ) ? Config . Network . PortTCP : - 1 ,
NetIO . hasUDP ( ) ? Config . Network . PortUDP : - 1 ) ;
2009-05-08 13:28:41 +00:00
// ok
return true ;
}
void C4Network2 : : HandleConn ( const C4PacketConn & Pkt , C4Network2IOConnection * pConn , C4Network2Client * pClient )
{
// security
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return ;
2009-05-08 13:28:41 +00:00
// Handles a connect request (packet PID_Conn).
// Check if this peer should be allowed to connect, make space for the new connection.
// connection is closed?
2010-03-28 18:58:01 +00:00
if ( pConn - > isClosed ( ) )
2009-05-08 13:28:41 +00:00
return ;
// set up core
const C4ClientCore & CCore = Pkt . getCCore ( ) ;
C4ClientCore NewCCore = CCore ;
// accept connection?
2009-05-11 13:09:53 +00:00
StdStrBuf reply ;
2009-05-08 13:28:41 +00:00
bool fOK = false ;
// search client
2010-03-28 18:58:01 +00:00
if ( ! pClient & & Pkt . getCCore ( ) . getID ( ) ! = C4ClientIDUnknown )
2009-05-08 13:28:41 +00:00
pClient = Clients . GetClient ( Pkt . getCCore ( ) ) ;
// check engine version
bool fWrongPassword = false ;
2014-12-12 20:57:52 +00:00
if ( Pkt . getVer ( ) ! = C4XVER1 * 100 + C4XVER2 )
2009-05-08 13:28:41 +00:00
{
2014-12-12 20:57:52 +00:00
reply . Format ( " wrong engine (%d.%d, I have %d.%d) " , Pkt . getVer ( ) / 100 , Pkt . getVer ( ) % 100 , C4XVER1 , C4XVER2 ) ;
2009-05-08 13:28:41 +00:00
fOK = false ;
}
else
{
2010-03-28 18:58:01 +00:00
if ( pClient )
2011-03-28 16:29:19 +00:00
if ( CheckConn ( NewCCore , pConn , pClient , & reply ) )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
// accept
2010-03-28 18:58:01 +00:00
if ( ! reply ) reply = " connection accepted " ;
2009-05-08 13:28:41 +00:00
fOK = true ;
}
// client: host connection?
2010-03-28 18:58:01 +00:00
if ( ! fOK & & ! isHost ( ) & & Status . getState ( ) = = GS_Init & & ! Clients . GetHost ( ) )
2011-03-28 16:29:19 +00:00
if ( HostConnect ( NewCCore , pConn , & reply ) )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
// accept
2010-03-28 18:58:01 +00:00
if ( ! reply ) reply = " host connection accepted " ;
2009-05-08 13:28:41 +00:00
fOK = true ;
}
// host: client join? (NewCCore will be changed by Join()!)
2010-03-28 18:58:01 +00:00
if ( ! fOK & & isHost ( ) & & ! pClient )
2009-05-08 13:28:41 +00:00
{
// check password
2010-03-28 18:58:01 +00:00
if ( ! sPassword . isNull ( ) & & ! SEqual ( Pkt . getPassword ( ) , sPassword . getData ( ) ) )
2009-05-08 13:28:41 +00:00
{
2009-05-11 13:09:53 +00:00
reply = " wrong password " ;
2009-05-08 13:28:41 +00:00
fWrongPassword = true ;
}
// accept join
2011-03-28 16:29:19 +00:00
else if ( Join ( NewCCore , pConn , & reply ) )
2009-05-08 13:28:41 +00:00
{
2010-03-27 16:05:02 +00:00
// save core
2010-03-28 18:58:01 +00:00
pConn - > SetCCore ( NewCCore ) ;
2010-03-27 16:05:02 +00:00
// accept
2010-03-28 18:58:01 +00:00
if ( ! reply ) reply = " join accepted " ;
2009-05-08 13:28:41 +00:00
fOK = true ;
}
}
}
// denied? set default reason
2010-03-28 18:58:01 +00:00
if ( ! fOK & & ! reply ) reply = " connection denied " ;
2009-05-08 13:28:41 +00:00
// OK and already half accepted? Skip (double-checked: ok).
2010-03-28 18:58:01 +00:00
if ( fOK & & pConn - > isHalfAccepted ( ) )
2009-05-08 13:28:41 +00:00
return ;
2010-03-27 16:05:02 +00:00
// send answer
2009-05-11 13:09:53 +00:00
C4PacketConnRe pcr ( fOK , fWrongPassword , reply . getData ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! pConn - > Send ( MkC4NetIOPacket ( PID_ConnRe , pcr ) ) )
2009-05-08 13:28:41 +00:00
return ;
// accepted?
2010-03-28 18:58:01 +00:00
if ( fOK )
2009-05-08 13:28:41 +00:00
{
// set status
2010-03-28 18:58:01 +00:00
if ( ! pConn - > isClosed ( ) )
2009-05-08 13:28:41 +00:00
pConn - > SetHalfAccepted ( ) ;
}
// denied? close
else
{
// log & close
2013-02-18 14:30:00 +00:00
LogSilentF ( " Network: connection by %s (%s) blocked: %s " , CCore . getName ( ) , pConn - > getPeerAddr ( ) . ToString ( ) . getData ( ) , reply . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
pConn - > Close ( ) ;
}
}
2011-03-28 16:29:19 +00:00
bool C4Network2 : : CheckConn ( const C4ClientCore & CCore , C4Network2IOConnection * pConn , C4Network2Client * pClient , StdStrBuf * szReply )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pConn | | ! pClient ) return false ;
2009-05-08 13:28:41 +00:00
// already connected? (shouldn't happen really)
2010-03-28 18:58:01 +00:00
if ( pClient - > hasConn ( pConn ) )
2011-03-28 16:29:19 +00:00
{ * szReply = " already connected " ; return true ; }
2009-05-08 13:28:41 +00:00
// check core
2010-03-28 18:58:01 +00:00
if ( CCore . getDiffLevel ( pClient - > getCore ( ) ) > C4ClientCoreDL_IDMatch )
2011-03-28 16:29:19 +00:00
{ * szReply = " wrong client core " ; return false ; }
2009-05-08 13:28:41 +00:00
// check address
2013-02-18 14:30:00 +00:00
if ( pClient - > isConnected ( ) & & pClient - > getMsgConn ( ) - > getPeerAddr ( ) ! = pConn - > getPeerAddr ( ) )
2011-03-28 16:29:19 +00:00
{ * szReply = " wrong address " ; return false ; }
2009-05-08 13:28:41 +00:00
// accept
return true ;
}
2011-03-28 16:29:19 +00:00
bool C4Network2 : : HostConnect ( const C4ClientCore & CCore , C4Network2IOConnection * pConn , StdStrBuf * szReply )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return false ;
2011-03-28 16:29:19 +00:00
if ( ! CCore . isHost ( ) ) { * szReply = " not host " ; return false ; }
2009-05-08 13:28:41 +00:00
// create client class for host
// (core is unofficial, see InitClient() - will be overwritten later in HandleJoinData)
C4Client * pClient = Game . Clients . Add ( CCore ) ;
2010-03-28 18:58:01 +00:00
if ( ! pClient ) return false ;
2009-05-08 13:28:41 +00:00
// accept
return true ;
}
2011-03-28 16:29:19 +00:00
bool C4Network2 : : Join ( C4ClientCore & CCore , C4Network2IOConnection * pConn , StdStrBuf * szReply )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return false ;
2009-05-08 13:28:41 +00:00
// security
2011-03-28 16:29:19 +00:00
if ( ! isHost ( ) ) { * szReply = " not host " ; return false ; }
if ( ! fAllowJoin & & ! fAllowObserve ) { * szReply = " join denied " ; return false ; }
if ( CCore . getID ( ) ! = C4ClientIDUnknown ) { * szReply = " join with set id not allowed " ; return false ; }
2009-05-08 13:28:41 +00:00
// find free client id
CCore . SetID ( iNextClientID + + ) ;
2010-03-27 16:05:02 +00:00
// observer?
2010-03-28 18:58:01 +00:00
if ( ! fAllowJoin ) CCore . SetObserver ( true ) ;
2010-03-27 16:05:02 +00:00
// deactivate - client will have to ask for activation.
CCore . SetActivated ( false ) ;
2009-05-08 13:28:41 +00:00
// Name already in use? Find unused one
2010-03-28 18:58:01 +00:00
if ( Clients . GetClient ( CCore . getName ( ) ) )
2009-05-08 13:28:41 +00:00
{
char szNameTmpl [ 256 + 1 ] , szNewName [ 256 + 1 ] ;
SCopy ( CCore . getName ( ) , szNameTmpl , 254 ) ; SAppend ( " %d " , szNameTmpl , 256 ) ;
int32_t i = 1 ;
do
sprintf ( szNewName , szNameTmpl , + + i ) ;
2010-03-28 18:58:01 +00:00
while ( Clients . GetClient ( szNewName ) ) ;
2009-05-08 13:28:41 +00:00
CCore . SetName ( szNewName ) ;
}
// join client
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_ClientJoin , new C4ControlClientJoin ( CCore ) , CDT_Direct ) ;
2009-05-08 13:28:41 +00:00
// get client, set status
C4Network2Client * pClient = Clients . GetClient ( CCore ) ;
2010-03-28 18:58:01 +00:00
if ( pClient ) pClient - > SetStatus ( NCS_Joining ) ;
2010-03-22 12:41:14 +00:00
// warn if client revision doesn't match our host revision
if ( ! SEqualNoCase ( CCore . getRevision ( ) , Application . GetRevision ( ) ) )
{
LogF ( " [!]WARNING! Client %s engine revision (%s) differs from local revision (%s). Client might run out of sync. " , CCore . getName ( ) , CCore . getRevision ( ) , Application . GetRevision ( ) ) ;
}
2009-05-08 13:28:41 +00:00
// ok, client joined.
return true ;
// Note that the connection isn't fully accepted at this point and won't be
// associated with the client. The new-created client is waiting for connect.
// Somewhat ironically, the connection may still timeout (resulting in an instant
// removal and maybe some funny message sequences).
// The final client initialization will be done at OnClientConnect.
}
void C4Network2 : : HandleConnRe ( const C4PacketConnRe & Pkt , C4Network2IOConnection * pConn , C4Network2Client * pClient )
{
// Handle the connection request reply. After this handling, the connection should
// be either fully associated with a client (fully accepted) or closed.
// Note that auto-accepted connection have to processed here once, too, as the
// client must get associated with the connection. After doing so, the connection
// auto-accept flag will be reset to mark the connection fully accepted.
// security
2010-03-28 18:58:01 +00:00
if ( ! pConn ) return ;
if ( ! pClient ) { pConn - > Close ( ) ; return ; }
2009-05-08 13:28:41 +00:00
// negative reply?
2010-03-28 18:58:01 +00:00
if ( ! Pkt . isOK ( ) )
2009-05-08 13:28:41 +00:00
{
// wrong password?
fWrongPassword = Pkt . isPasswordWrong ( ) ;
// show message
2013-02-18 14:30:00 +00:00
LogSilentF ( " Network: connection to %s (%s) refused: %s " , pClient - > getName ( ) , pConn - > getPeerAddr ( ) . ToString ( ) . getData ( ) , Pkt . getMsg ( ) ) ;
2009-05-08 13:28:41 +00:00
// close connection
pConn - > Close ( ) ;
return ;
}
// connection is closed?
2010-03-28 18:58:01 +00:00
if ( ! pConn - > isOpen ( ) )
2009-05-08 13:28:41 +00:00
return ;
// already accepted? ignore
2010-03-28 18:58:01 +00:00
if ( pConn - > isAccepted ( ) & & ! pConn - > isAutoAccepted ( ) ) return ;
2009-05-08 13:28:41 +00:00
// first connection?
bool fFirstConnection = ! pClient - > isConnected ( ) ;
// accept connection
pConn - > SetAccepted ( ) ; pConn - > ResetAutoAccepted ( ) ;
// add connection
pConn - > SetCCore ( pClient - > getCore ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( pConn - > getNetClass ( ) = = NetIO . MsgIO ( ) ) pClient - > SetMsgConn ( pConn ) ;
if ( pConn - > getNetClass ( ) = = NetIO . DataIO ( ) ) pClient - > SetDataConn ( pConn ) ;
2009-05-08 13:28:41 +00:00
// add peer connect address to client address list
2013-02-18 14:30:00 +00:00
if ( ! pConn - > getConnectAddr ( ) . IsNull ( ) )
2009-05-08 13:28:41 +00:00
{
C4Network2Address Addr ( pConn - > getConnectAddr ( ) , pConn - > getProtocol ( ) ) ;
pClient - > AddAddr ( Addr , Status . getState ( ) ! = GS_Init ) ;
}
// handle
OnConnect ( pClient , pConn , Pkt . getMsg ( ) , fFirstConnection ) ;
}
void C4Network2 : : HandleStatus ( const C4Network2Status & nStatus )
{
// set
Status = nStatus ;
// log
LogSilentF ( " Network: going into status %s (tick %d) " , Status . getStateName ( ) , nStatus . getTargetCtrlTick ( ) ) ;
// reset flags
fStatusReached = fStatusAck = false ;
// check: reached?
CheckStatusReached ( ) ;
}
void C4Network2 : : HandleStatusAck ( const C4Network2Status & nStatus , C4Network2Client * pClient )
{
// security
2010-03-28 18:58:01 +00:00
if ( ! pClient - > hasJoinData ( ) | | pClient - > isRemoved ( ) ) return ;
2009-05-08 13:28:41 +00:00
// status doesn't match?
2010-03-28 18:58:01 +00:00
if ( nStatus . getState ( ) ! = Status . getState ( ) | | nStatus . getTargetCtrlTick ( ) < Status . getTargetCtrlTick ( ) )
2009-05-08 13:28:41 +00:00
return ;
// host: wait until all clients are ready
2010-03-28 18:58:01 +00:00
if ( isHost ( ) )
2009-05-08 13:28:41 +00:00
{
// check: target tick change?
2010-03-28 18:58:01 +00:00
if ( ! fStatusAck & & nStatus . getTargetCtrlTick ( ) > Status . getTargetCtrlTick ( ) )
2009-05-08 13:28:41 +00:00
// take the new status
ChangeGameStatus ( nStatus . getState ( ) , nStatus . getTargetCtrlTick ( ) ) ;
// already acknowledged? Send another ack
2010-03-28 18:58:01 +00:00
if ( fStatusAck )
2009-05-08 13:28:41 +00:00
pClient - > SendMsg ( MkC4NetIOPacket ( PID_StatusAck , nStatus ) ) ;
// mark as ready (will clear chase-flag)
pClient - > SetStatus ( NCS_Ready ) ;
// check: everyone ready?
2010-03-28 18:58:01 +00:00
if ( ! fStatusAck & & fStatusReached )
2009-05-08 13:28:41 +00:00
CheckStatusAck ( ) ;
}
else
{
// target tick doesn't match? ignore
2010-03-28 18:58:01 +00:00
if ( nStatus . getTargetCtrlTick ( ) ! = Status . getTargetCtrlTick ( ) )
2009-05-08 13:28:41 +00:00
return ;
// reached?
// can be ignored safely otherwise - when the status is reached, we will send
// status ack on which the host should generate another status ack (see above)
2010-03-28 18:58:01 +00:00
if ( fStatusReached )
2009-05-08 13:28:41 +00:00
{
// client: set flags, call handler
fStatusAck = true ; fChasing = false ;
OnStatusAck ( ) ;
}
}
}
void C4Network2 : : HandleActivateReq ( int32_t iTick , C4Network2Client * pByClient )
{
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return ;
2010-03-27 16:05:02 +00:00
// not allowed or already activated? ignore
2010-03-28 18:58:01 +00:00
if ( pByClient - > isObserver ( ) | | pByClient - > isActivated ( ) ) return ;
2009-05-08 13:28:41 +00:00
// not joined completely yet? ignore
2010-03-28 18:58:01 +00:00
if ( ! pByClient - > isWaitedFor ( ) ) return ;
2010-03-27 16:05:02 +00:00
// check behind limit
2010-03-28 18:58:01 +00:00
if ( isRunning ( ) )
2010-03-27 16:05:02 +00:00
{
// make a guess how much the client lags.
2015-02-12 22:05:55 +00:00
int32_t iLagFrames = Clamp ( pByClient - > getMsgConn ( ) - > getPingTime ( ) * Game . FPS / 500 , 0 , 100 ) ;
2010-03-28 18:58:01 +00:00
if ( iTick < Game . FrameCounter - iLagFrames - C4NetMaxBehind4Activation )
2010-03-27 16:05:02 +00:00
return ;
}
// activate him
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_ClientUpdate ,
2010-03-28 18:58:01 +00:00
new C4ControlClientUpdate ( pByClient - > getID ( ) , CUT_Activate , true ) ,
CDT_Sync ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : HandleJoinData ( const C4PacketJoinData & rPkt )
{
// init only
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) ! = GS_Init )
2009-05-08 13:28:41 +00:00
{ LogSilentF ( " Network: unexpected join data received! " ) ; return ; }
// get client ID
2010-03-28 18:58:01 +00:00
if ( rPkt . getClientID ( ) = = C4ClientIDUnknown )
2009-05-08 13:28:41 +00:00
{ LogSilentF ( " Network: host didn't set client ID! " ) ; Clear ( ) ; return ; }
// set local ID
ResList . SetLocalID ( rPkt . getClientID ( ) ) ;
Game . Parameters . Clients . SetLocalID ( rPkt . getClientID ( ) ) ;
// read and validate status
HandleStatus ( rPkt . getStatus ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) ! = GS_Lobby & & Status . getState ( ) ! = GS_Pause & & Status . getState ( ) ! = GS_Go )
2009-05-08 13:28:41 +00:00
{ LogSilentF ( " Network: join data has bad game status: %s " , Status . getStateName ( ) ) ; Clear ( ) ; return ; }
2014-09-23 21:02:03 +00:00
// copy scenario parameter defs for lobby display
: : Game . ScenarioParameterDefs = rPkt . ScenarioParameterDefs ;
2009-05-08 13:28:41 +00:00
// copy parameters
2014-09-23 21:02:03 +00:00
: : Game . Parameters = rPkt . Parameters ;
2009-05-08 13:28:41 +00:00
// set local client
C4Client * pLocalClient = Game . Clients . getClientByID ( rPkt . getClientID ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! pLocalClient )
2009-05-08 13:28:41 +00:00
{ LogSilentF ( " Network: Could not find local client in join data! " ) ; Clear ( ) ; return ; }
// save back dynamic data
ResDynamic = rPkt . getDynamicCore ( ) ;
iDynamicTick = rPkt . getStartCtrlTick ( ) ;
// initialize control
2009-06-15 22:06:37 +00:00
: : Control . ControlRate = rPkt . Parameters . ControlRate ;
2009-05-08 13:28:41 +00:00
pControl - > Init ( rPkt . getClientID ( ) , false , rPkt . getStartCtrlTick ( ) , pLocalClient - > isActivated ( ) , this ) ;
pControl - > CopyClientList ( Game . Parameters . Clients ) ;
// set local core
NetIO . SetLocalCCore ( pLocalClient - > getCore ( ) ) ;
2012-10-21 20:20:43 +00:00
// add the resources to the network resource list
2009-05-08 13:28:41 +00:00
Game . Parameters . GameRes . InitNetwork ( & ResList ) ;
// load dynamic
2010-03-28 18:58:01 +00:00
if ( ! ResList . AddByCore ( ResDynamic ) )
2009-05-08 13:28:41 +00:00
{ LogFatal ( " Network: can not not retrieve dynamic! " ) ; Clear ( ) ; return ; }
2012-10-21 20:20:43 +00:00
// load player resources
2009-05-08 13:28:41 +00:00
Game . Parameters . PlayerInfos . LoadResources ( ) ;
// send additional addresses
2016-11-02 23:58:02 +00:00
Clients . SendAddresses ( nullptr ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OnConnect ( C4Network2Client * pClient , C4Network2IOConnection * pConn , const char * szMsg , bool fFirstConnection )
{
// log
2013-02-18 14:30:00 +00:00
LogSilentF ( " Network: %s %s connected (%s/%s) (%s) " , pClient - > isHost ( ) ? " host " : " client " ,
pClient - > getName ( ) , pConn - > getPeerAddr ( ) . ToString ( ) . getData ( ) ,
2010-03-28 18:58:01 +00:00
NetIO . getNetIOName ( pConn - > getNetClass ( ) ) , szMsg ? szMsg : " " ) ;
2009-05-08 13:28:41 +00:00
// first connection for this peer? call special handler
2010-03-28 18:58:01 +00:00
if ( fFirstConnection ) OnClientConnect ( pClient , pConn ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OnConnectFail ( C4Network2IOConnection * pConn )
{
2013-02-18 14:30:00 +00:00
LogSilentF ( " Network: %s connection to %s failed! " , NetIO . getNetIOName ( pConn - > getNetClass ( ) ) ,
pConn - > getPeerAddr ( ) . ToString ( ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
// maybe client connection failure
// (happens if the connection is not fully accepted and the client disconnects.
// See C4Network2::Join)
C4Network2Client * pClient = Clients . GetClientByID ( pConn - > getClientID ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( pClient & & ! pClient - > isConnected ( ) )
2009-05-08 13:28:41 +00:00
OnClientDisconnect ( pClient ) ;
}
void C4Network2 : : OnDisconnect ( C4Network2Client * pClient , C4Network2IOConnection * pConn )
{
2013-02-18 14:30:00 +00:00
LogSilentF ( " Network: %s connection to %s (%s) lost! " , NetIO . getNetIOName ( pConn - > getNetClass ( ) ) ,
pClient - > getName ( ) , pConn - > getPeerAddr ( ) . ToString ( ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
// connection lost?
2010-03-28 18:58:01 +00:00
if ( ! pClient - > isConnected ( ) )
2009-05-08 13:28:41 +00:00
OnClientDisconnect ( pClient ) ;
}
void C4Network2 : : OnClientConnect ( C4Network2Client * pClient , C4Network2IOConnection * pConn )
{
// host: new client?
2010-03-28 18:58:01 +00:00
if ( isHost ( ) )
2009-05-08 13:28:41 +00:00
{
// dynamic available?
2010-03-28 18:58:01 +00:00
if ( ! pClient - > hasJoinData ( ) )
2009-05-08 13:28:41 +00:00
SendJoinData ( pClient ) ;
// notice lobby (doesn't do anything atm?)
C4GameLobby : : MainDlg * pDlg = GetLobby ( ) ;
if ( isLobbyActive ( ) ) pDlg - > OnClientConnect ( pClient - > getClient ( ) , pConn ) ;
}
// discover resources
ResList . OnClientConnect ( pConn ) ;
}
void C4Network2 : : OnClientDisconnect ( C4Network2Client * pClient )
{
// league: Notify regular client disconnect within the game
if ( pLeagueClient & & ( isHost ( ) | | pClient - > isHost ( ) ) ) LeagueNotifyDisconnect ( pClient - > getID ( ) , C4LDR_ConnectionFailed ) ;
// host? Remove this client from the game.
2010-03-28 18:58:01 +00:00
if ( isHost ( ) )
2009-05-08 13:28:41 +00:00
{
// log
LogSilentF ( LoadResStr ( " IDS_NET_CLIENTDISCONNECTED " ) , pClient - > getName ( ) ) ; // silent, because a duplicate message with disconnect reason will follow
// remove the client
Game . Clients . CtrlRemove ( pClient - > getClient ( ) , LoadResStr ( " IDS_MSG_DISCONNECTED " ) ) ;
// check status ack (disconnected client might be the last that was waited for)
CheckStatusAck ( ) ;
// unreached pause/go? retry setting the state with current control tick
// (client might be the only one claiming to have the given control)
2010-03-28 18:58:01 +00:00
if ( ! fStatusReached )
if ( Status . getState ( ) = = GS_Go | | Status . getState ( ) = = GS_Pause )
2009-06-15 22:06:37 +00:00
ChangeGameStatus ( Status . getState ( ) , : : Control . ControlTick ) ;
2016-10-30 20:35:38 +00:00
# ifdef USE_CONSOLE
2016-11-03 12:09:48 +00:00
// Dedicated server: stop hosting if there is only one client left we're hosting for.
// TODO: Find a better place to do this.
if ( Game . IsRunning & & Clients . Count ( ) < = 3 ) Application . Quit ( ) ; // Off-by-1 error
2016-10-30 20:35:38 +00:00
# endif // USE_CONSOLE
2009-05-08 13:28:41 +00:00
}
// host disconnected? Clear up
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) & & pClient - > isHost ( ) )
2009-05-08 13:28:41 +00:00
{
StdStrBuf sMsg ; sMsg . Format ( LoadResStr ( " IDS_NET_HOSTDISCONNECTED " ) , pClient - > getName ( ) ) ;
Log ( sMsg . getData ( ) ) ;
// host connection lost: clear up everything
Game . RoundResults . EvaluateNetwork ( C4RoundResults : : NR_NetError , sMsg . getData ( ) ) ;
Clear ( ) ;
}
}
void C4Network2 : : SendJoinData ( C4Network2Client * pClient )
{
2010-03-28 18:58:01 +00:00
if ( pClient - > hasJoinData ( ) ) return ;
2009-05-08 13:28:41 +00:00
// host only, scenario must be available
assert ( isHost ( ) ) ;
// dynamic available?
2010-03-28 18:58:01 +00:00
if ( ResDynamic . isNull ( ) | | iDynamicTick < : : Control . ControlTick )
2009-05-08 13:28:41 +00:00
{
fDynamicNeeded = true ;
// add synchronization control (will callback, see C4Game::Synchronize)
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_Synchronize , new C4ControlSynchronize ( false , true ) , CDT_Sync ) ;
2009-05-08 13:28:41 +00:00
return ;
}
// save his client ID
C4PacketJoinData JoinData ;
JoinData . SetClientID ( pClient - > getID ( ) ) ;
// save status into packet
JoinData . SetGameStatus ( Status ) ;
2014-09-23 21:02:03 +00:00
// scenario parameter defs for lobby display (localized in host language)
JoinData . ScenarioParameterDefs = : : Game . ScenarioParameterDefs ;
2009-05-08 13:28:41 +00:00
// parameters
JoinData . Parameters = Game . Parameters ;
// core join data
JoinData . SetStartCtrlTick ( iDynamicTick ) ;
JoinData . SetDynamicCore ( ResDynamic ) ;
// send
pClient - > SendMsg ( MkC4NetIOPacket ( PID_JoinData , JoinData ) ) ;
// send addresses
Clients . SendAddresses ( pClient - > getMsgConn ( ) ) ;
// flag client (he will have to accept the network status sent next)
pClient - > SetStatus ( NCS_Chasing ) ;
2016-11-02 23:58:02 +00:00
if ( ! iLastChaseTargetUpdate ) iLastChaseTargetUpdate = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
}
C4Network2Res : : Ref C4Network2 : : RetrieveRes ( const C4Network2ResCore & Core , int32_t iTimeoutLen , const char * szResName , bool fWaitForCore )
{
2016-11-02 23:58:02 +00:00
C4GUI : : ProgressDialog * pDlg = nullptr ;
2009-05-08 13:28:41 +00:00
bool fLog = false ;
2013-11-29 10:28:04 +00:00
int32_t iProcess = - 1 ;
2013-12-07 14:27:01 +00:00
C4TimeMilliseconds tTimeout = C4TimeMilliseconds : : Now ( ) + iTimeoutLen ;
2012-10-21 20:20:43 +00:00
// wait for resource
2010-03-28 18:58:01 +00:00
while ( isEnabled ( ) )
2009-05-08 13:28:41 +00:00
{
2012-10-21 20:20:43 +00:00
// find resource
2009-05-08 13:28:41 +00:00
C4Network2Res : : Ref pRes = ResList . getRefRes ( Core . getID ( ) ) ;
// res not found?
2010-03-28 18:58:01 +00:00
if ( ! pRes )
2013-12-04 12:35:07 +00:00
{
2010-03-28 18:58:01 +00:00
if ( Core . isNull ( ) )
2009-05-08 13:28:41 +00:00
{
// should wait for core?
2016-11-02 23:58:02 +00:00
if ( ! fWaitForCore ) return nullptr ;
2009-05-08 13:28:41 +00:00
}
else
{
// start loading
pRes = ResList . AddByCore ( Core ) ;
}
2013-12-04 12:35:07 +00:00
}
2009-05-08 13:28:41 +00:00
// res found and loaded completely
2010-03-28 18:58:01 +00:00
else if ( ! pRes - > isLoading ( ) )
2009-05-08 13:28:41 +00:00
{
// log
2010-03-28 18:58:01 +00:00
if ( fLog ) LogF ( LoadResStr ( " IDS_NET_RECEIVED " ) , szResName , pRes - > getCore ( ) . getFileName ( ) ) ;
2009-05-08 13:28:41 +00:00
// return
if ( pDlg ) delete pDlg ;
return pRes ;
}
// check: progress?
2010-03-28 18:58:01 +00:00
if ( pRes & & pRes - > getPresentPercent ( ) ! = iProcess )
2009-05-08 13:28:41 +00:00
{
iProcess = pRes - > getPresentPercent ( ) ;
2013-12-07 14:27:01 +00:00
tTimeout = C4TimeMilliseconds : : Now ( ) + iTimeoutLen ;
2009-05-08 13:28:41 +00:00
}
else
{
// if not: check timeout
2013-12-07 14:27:01 +00:00
if ( C4TimeMilliseconds : : Now ( ) > tTimeout )
2009-05-08 13:28:41 +00:00
{
LogFatal ( FormatString ( LoadResStr ( " IDS_NET_ERR_RESTIMEOUT " ) , szResName ) . getData ( ) ) ;
if ( pDlg ) delete pDlg ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
}
// log
2010-03-28 18:58:01 +00:00
if ( ! fLog )
2009-05-08 13:28:41 +00:00
{
LogF ( LoadResStr ( " IDS_NET_WAITFORRES " ) , szResName ) ;
fLog = true ;
}
// show progress dialog
2010-03-28 18:58:01 +00:00
if ( ! pDlg & & ! Console . Active & & : : pGUI )
2009-05-08 13:28:41 +00:00
{
// create
pDlg = new C4GUI : : ProgressDialog ( FormatString ( LoadResStr ( " IDS_NET_WAITFORRES " ) , szResName ) . getData ( ) ,
2010-03-28 18:58:01 +00:00
LoadResStr ( " IDS_NET_CAPTION " ) , 100 , 0 , C4GUI : : Ico_NetWait ) ;
2009-05-08 13:28:41 +00:00
// show dialog
2016-11-02 23:58:02 +00:00
if ( ! pDlg - > Show ( : : pGUI , true ) ) { delete pDlg ; return nullptr ; }
2009-05-08 13:28:41 +00:00
}
// wait
2010-03-28 18:58:01 +00:00
if ( pDlg )
2009-05-08 13:28:41 +00:00
{
// set progress bar
pDlg - > SetProgress ( iProcess ) ;
// execute (will do message handling)
2010-03-28 18:58:01 +00:00
if ( ! pDlg - > Execute ( ) )
2016-11-02 23:58:02 +00:00
{ if ( pDlg ) delete pDlg ; return nullptr ; }
2009-05-08 13:28:41 +00:00
// aborted?
2010-03-28 18:58:01 +00:00
if ( pDlg - > IsAborted ( ) ) break ;
2009-05-08 13:28:41 +00:00
}
else
{
2013-12-07 14:27:01 +00:00
if ( ! Application . ScheduleProcs ( tTimeout - C4TimeMilliseconds : : Now ( ) ) )
2016-11-02 23:58:02 +00:00
{ return nullptr ; }
2009-05-08 13:28:41 +00:00
}
}
// aborted
delete pDlg ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : CreateDynamic ( bool fInit )
{
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// remove all existing dynamic data
RemoveDynamic ( ) ;
// log
Log ( LoadResStr ( " IDS_NET_SAVING " ) ) ;
// compose file name
char szDynamicBase [ _MAX_PATH + 1 ] , szDynamicFilename [ _MAX_PATH + 1 ] ;
sprintf ( szDynamicBase , Config . AtNetworkPath ( " Dyn%s " ) , GetFilename ( Game . ScenarioFilename ) , _MAX_PATH ) ;
2010-03-28 18:58:01 +00:00
if ( ! ResList . FindTempResFileName ( szDynamicBase , szDynamicFilename ) )
2010-01-25 04:00:59 +00:00
Log ( LoadResStr ( " IDS_NET_SAVE_ERR_CREATEDYNFILE " ) ) ;
2009-05-08 13:28:41 +00:00
// save dynamic data
C4GameSaveNetwork SaveGame ( fInit ) ;
if ( ! SaveGame . Save ( szDynamicFilename ) | | ! SaveGame . Close ( ) )
{ Log ( LoadResStr ( " IDS_NET_SAVE_ERR_SAVEDYNFILE " ) ) ; return false ; }
2012-10-21 20:20:43 +00:00
// add resource
2009-05-08 13:28:41 +00:00
C4Network2Res : : Ref pRes = ResList . AddByFile ( szDynamicFilename , true , NRT_Dynamic ) ;
2010-03-28 18:58:01 +00:00
if ( ! pRes ) { Log ( LoadResStr ( " IDS_NET_SAVE_ERR_ADDDYNDATARES " ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// save
ResDynamic = pRes - > getCore ( ) ;
2009-06-15 22:06:37 +00:00
iDynamicTick = : : Control . getNextControlTick ( ) ;
2009-05-08 13:28:41 +00:00
fDynamicNeeded = false ;
// ok
return true ;
}
void C4Network2 : : RemoveDynamic ( )
{
C4Network2Res : : Ref pRes = ResList . getRefRes ( ResDynamic . getID ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( pRes ) pRes - > Remove ( ) ;
2009-05-08 13:28:41 +00:00
ResDynamic . Clear ( ) ;
iDynamicTick = - 1 ;
}
bool C4Network2 : : isFrozen ( ) const
{
2010-03-27 16:05:02 +00:00
// "frozen" means all clients are garantueed to be in the same tick.
// This is only the case if the game is not started yet (lobby) or the
// tick has been ensured (pause) and acknowledged by all joined clients.
// Note unjoined clients must be ignored here - they can't be faster than
// the host, anyway.
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Lobby ) return true ;
if ( Status . getState ( ) = = GS_Pause & & fStatusAck ) return true ;
2009-05-08 13:28:41 +00:00
return false ;
}
bool C4Network2 : : ChangeGameStatus ( C4NetGameState enState , int32_t iTargetCtrlTick , int32_t iCtrlMode )
{
// change game status, announce. Can only be done by host.
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// set status
Status . Set ( enState , iTargetCtrlTick ) ;
// update reference
InvalidateReference ( ) ;
2010-03-27 16:05:02 +00:00
// control mode change?
2010-03-28 18:58:01 +00:00
if ( iCtrlMode > = 0 ) Status . SetCtrlMode ( iCtrlMode ) ;
2009-05-08 13:28:41 +00:00
// log
2010-03-27 16:05:02 +00:00
LogSilentF ( " Network: going into status %s (tick %d) " , Status . getStateName ( ) , iTargetCtrlTick ) ;
2009-05-08 13:28:41 +00:00
// set flags
Clients . ResetReady ( ) ;
fStatusReached = fStatusAck = false ;
// send new status to all clients
Clients . BroadcastMsgToClients ( MkC4NetIOPacket ( PID_Status , Status ) ) ;
// check reach/ack
CheckStatusReached ( ) ;
// ok
return true ;
}
void C4Network2 : : CheckStatusReached ( bool fFromFinalInit )
{
// already reached?
2010-03-28 18:58:01 +00:00
if ( fStatusReached ) return ;
if ( Status . getState ( ) = = GS_Lobby )
2009-05-08 13:28:41 +00:00
fStatusReached = fLobbyRunning ;
// game go / pause: control must be initialized and target tick reached
2010-03-28 18:58:01 +00:00
else if ( Status . getState ( ) = = GS_Go | | Status . getState ( ) = = GS_Pause )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( Game . IsRunning | | fFromFinalInit )
2010-03-27 16:05:02 +00:00
{
2009-05-08 13:28:41 +00:00
// Make sure we have reached the tick and the control queue is empty (except for chasing)
2010-03-28 18:58:01 +00:00
if ( : : Control . CtrlTickReached ( Status . getTargetCtrlTick ( ) ) & &
( fChasing | | ! pControl - > CtrlReady ( : : Control . ControlTick ) ) )
2009-05-08 13:28:41 +00:00
fStatusReached = true ;
else
2010-03-27 16:05:02 +00:00
{
2009-05-08 13:28:41 +00:00
// run ctrl so the tick can be reached
pControl - > SetRunning ( true , Status . getTargetCtrlTick ( ) ) ;
Game . HaltCount = 0 ;
2010-03-27 16:05:02 +00:00
Console . UpdateHaltCtrls ( ! ! Game . HaltCount ) ;
}
}
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
if ( ! fStatusReached ) return ;
2009-05-08 13:28:41 +00:00
// call handler
OnStatusReached ( ) ;
// host?
2010-03-28 18:58:01 +00:00
if ( isHost ( ) )
2009-05-08 13:28:41 +00:00
// all clients ready?
CheckStatusAck ( ) ;
else
{
2009-06-15 22:06:37 +00:00
Status . SetTargetTick ( : : Control . ControlTick ) ;
2009-05-08 13:28:41 +00:00
// send response to host
Clients . SendMsgToHost ( MkC4NetIOPacket ( PID_StatusAck , Status ) ) ;
// do delayed activation request
2010-03-28 18:58:01 +00:00
if ( fDelayedActivateReq )
{
2009-05-08 13:28:41 +00:00
fDelayedActivateReq = false ;
RequestActivate ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
}
void C4Network2 : : CheckStatusAck ( )
{
// host only
2010-03-28 18:58:01 +00:00
if ( ! isHost ( ) ) return ;
2009-05-08 13:28:41 +00:00
// status must be reached and not yet acknowledged
2010-03-28 18:58:01 +00:00
if ( ! fStatusReached | | fStatusAck ) return ;
2009-05-08 13:28:41 +00:00
// all clients ready?
2010-03-28 18:58:01 +00:00
if ( ( fStatusAck = Clients . AllClientsReady ( ) ) )
2009-05-08 13:28:41 +00:00
{
// pause/go: check for sync control that can be executed
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Go | | Status . getState ( ) = = GS_Pause )
2009-05-08 13:28:41 +00:00
pControl - > ExecSyncControl ( ) ;
// broadcast ack
Clients . BroadcastMsgToClients ( MkC4NetIOPacket ( PID_StatusAck , Status ) ) ;
// handle
OnStatusAck ( ) ;
}
}
void C4Network2 : : OnStatusReached ( )
{
// stop ctrl, wait for ack
2010-03-28 18:58:01 +00:00
if ( pControl - > IsEnabled ( ) )
2010-03-27 16:05:02 +00:00
{
Console . UpdateHaltCtrls ( ! ! + + Game . HaltCount ) ;
2010-03-28 18:58:01 +00:00
pControl - > SetRunning ( false ) ;
2010-03-27 16:05:02 +00:00
}
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OnStatusAck ( )
{
// log it
LogSilentF ( " Network: status %s (tick %d) reached " , Status . getStateName ( ) , Status . getTargetCtrlTick ( ) ) ;
2010-03-27 16:05:02 +00:00
// pause?
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Pause )
2010-03-27 16:05:02 +00:00
{
// set halt-flag (show hold message)
Console . UpdateHaltCtrls ( ! ! + + Game . HaltCount ) ;
}
// go?
2010-03-28 18:58:01 +00:00
if ( Status . getState ( ) = = GS_Go )
2009-05-08 13:28:41 +00:00
{
// set mode
pControl - > SetCtrlMode ( static_cast < C4GameControlNetworkMode > ( Status . getCtrlMode ( ) ) ) ;
// notify player list of reached status - will add some input to the queue
Players . OnStatusGoReached ( ) ;
// start ctrl
pControl - > SetRunning ( true ) ;
2010-03-27 16:05:02 +00:00
// reset halt-flag
2009-05-08 13:28:41 +00:00
Game . HaltCount = 0 ;
2010-03-27 16:05:02 +00:00
Console . UpdateHaltCtrls ( ! ! Game . HaltCount ) ;
2009-05-08 13:28:41 +00:00
}
}
void C4Network2 : : RequestActivate ( )
{
2010-03-27 16:05:02 +00:00
// neither observer nor activated?
2010-03-28 18:58:01 +00:00
if ( Game . Clients . getLocal ( ) - > isObserver ( ) | | Game . Clients . getLocal ( ) - > isActivated ( ) )
2010-03-27 16:05:02 +00:00
{
2013-12-08 17:04:19 +00:00
tLastActivateRequest = C4TimeMilliseconds : : NegativeInfinity ;
2010-03-27 16:05:02 +00:00
return ;
}
2009-05-08 13:28:41 +00:00
// host? just do it
2010-03-28 18:58:01 +00:00
if ( fHost )
2009-05-08 13:28:41 +00:00
{
// activate him
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_ClientUpdate ,
2010-03-28 18:58:01 +00:00
new C4ControlClientUpdate ( C4ClientIDHost , CUT_Activate , true ) ,
CDT_Sync ) ;
2009-05-08 13:28:41 +00:00
return ;
}
2010-03-27 16:05:02 +00:00
// ensure interval
2013-12-08 17:04:19 +00:00
if ( C4TimeMilliseconds : : Now ( ) < tLastActivateRequest + C4NetActivationReqInterval )
return ;
2009-05-08 13:28:41 +00:00
// status not reached yet? May be chasing, let's delay this.
2010-03-28 18:58:01 +00:00
if ( ! fStatusReached )
{
2009-05-08 13:28:41 +00:00
fDelayedActivateReq = true ;
return ;
2010-03-28 18:58:01 +00:00
}
2010-03-27 16:05:02 +00:00
// request
Clients . SendMsgToHost ( MkC4NetIOPacket ( PID_ClientActReq , C4PacketActivateReq ( Game . FrameCounter ) ) ) ;
// store time
2013-12-08 17:04:19 +00:00
tLastActivateRequest = C4TimeMilliseconds : : Now ( ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : DeactivateInactiveClients ( )
{
2016-08-07 02:05:25 +00:00
// host only and not in editor
if ( ! isHost ( ) | | : : Application . isEditor ) return ;
2010-03-27 16:05:02 +00:00
// update activity
Clients . UpdateClientActivity ( ) ;
// find clients to deactivate
2016-11-02 23:58:02 +00:00
for ( C4Network2Client * pClient = Clients . GetNextClient ( nullptr ) ; pClient ; pClient = Clients . GetNextClient ( pClient ) )
2010-03-28 18:58:01 +00:00
if ( ! pClient - > isLocal ( ) & & pClient - > isActivated ( ) )
if ( pClient - > getLastActivity ( ) + C4NetDeactivationDelay < Game . FrameCounter )
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_ClientUpdate , new C4ControlClientUpdate ( pClient - > getID ( ) , CUT_Activate , false ) , CDT_Sync ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : UpdateChaseTarget ( )
{
// no chasing clients?
C4Network2Client * pClient ;
2016-11-02 23:58:02 +00:00
for ( pClient = Clients . GetNextClient ( nullptr ) ; pClient ; pClient = Clients . GetNextClient ( pClient ) )
2010-03-28 18:58:01 +00:00
if ( pClient - > isChasing ( ) )
2009-05-08 13:28:41 +00:00
break ;
2010-03-28 18:58:01 +00:00
if ( ! pClient )
2009-05-08 13:28:41 +00:00
{
iLastChaseTargetUpdate = 0 ;
return ;
}
// not time for an update?
2016-11-02 23:58:02 +00:00
if ( ! iLastChaseTargetUpdate | | long ( iLastChaseTargetUpdate + C4NetChaseTargetUpdateInterval ) > time ( nullptr ) )
2009-05-08 13:28:41 +00:00
return ;
// copy status, set current tick
C4Network2Status ChaseTarget = Status ;
2009-06-15 22:06:37 +00:00
ChaseTarget . SetTargetTick ( : : Control . ControlTick ) ;
2009-05-08 13:28:41 +00:00
// send to everyone involved
2016-11-02 23:58:02 +00:00
for ( pClient = Clients . GetNextClient ( nullptr ) ; pClient ; pClient = Clients . GetNextClient ( pClient ) )
2010-03-28 18:58:01 +00:00
if ( pClient - > isChasing ( ) )
2009-05-08 13:28:41 +00:00
pClient - > SendMsg ( MkC4NetIOPacket ( PID_Status , ChaseTarget ) ) ;
2016-11-02 23:58:02 +00:00
iLastChaseTargetUpdate = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : LeagueGameEvaluate ( const char * szRecordName , const BYTE * pRecordSHA )
{
// already off?
if ( ! pLeagueClient ) return ;
// already evaluated?
if ( fLeagueEndSent ) return ;
// end
LeagueEnd ( szRecordName , pRecordSHA ) ;
}
void C4Network2 : : LeagueSignupDisable ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// already off?
if ( ! pLeagueClient ) return ;
// no post-disable if league is active
if ( pLeagueClient & & Game . Parameters . isLeague ( ) ) return ;
// signup end
LeagueEnd ( ) ; DeinitLeague ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : LeagueSignupEnable ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// already running?
if ( pLeagueClient ) return true ;
// Start it!
2016-11-02 23:58:02 +00:00
if ( InitLeague ( nullptr ) & & LeagueStart ( nullptr ) ) return true ;
2009-05-08 13:28:41 +00:00
// Failure :'(
DeinitLeague ( ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : InvalidateReference ( )
{
// Update both local and league reference as soon as possible
iLastReferenceUpdate = 0 ;
iLeagueUpdateDelay = C4NetMinLeagueUpdateInterval ;
}
bool C4Network2 : : InitLeague ( bool * pCancel )
{
2010-03-28 18:58:01 +00:00
if ( fHost )
2009-05-08 13:28:41 +00:00
{
// Clear parameters
MasterServerAddress . Clear ( ) ;
Game . Parameters . League . Clear ( ) ;
Game . Parameters . LeagueAddress . Clear ( ) ;
2016-11-02 23:58:02 +00:00
if ( pLeagueClient ) delete pLeagueClient ; pLeagueClient = nullptr ;
2009-05-08 13:28:41 +00:00
// Not needed?
2010-03-28 18:58:01 +00:00
if ( ! Config . Network . MasterServerSignUp & & ! Config . Network . LeagueServerSignUp )
2009-05-08 13:28:41 +00:00
return true ;
// Save address
MasterServerAddress = Config . Network . GetLeagueServerAddress ( ) ;
if ( Config . Network . LeagueServerSignUp )
{
Game . Parameters . LeagueAddress = MasterServerAddress ;
// enforce some league rules
Game . Parameters . EnforceLeagueRules ( & Game . C4S ) ;
}
}
else
{
// Get league server from parameters
MasterServerAddress = Game . Parameters . LeagueAddress ;
// Not needed?
2010-03-28 18:58:01 +00:00
if ( ! MasterServerAddress . getLength ( ) )
2009-05-08 13:28:41 +00:00
return true ;
}
2010-03-27 16:05:02 +00:00
// Init
pLeagueClient = new C4LeagueClient ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Init ( ) | |
! pLeagueClient - > SetServer ( MasterServerAddress . getData ( ) ) )
2010-03-27 16:05:02 +00:00
{
// Log message
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUEINIT " ) , pLeagueClient - > GetError ( ) ) ;
LogFatal ( Message . getData ( ) ) ;
// Clear league
2016-11-02 23:58:02 +00:00
delete pLeagueClient ; pLeagueClient = nullptr ;
2010-03-28 18:58:01 +00:00
if ( fHost )
2010-03-27 16:05:02 +00:00
Game . Parameters . LeagueAddress . Clear ( ) ;
// Show message, allow abort
2009-05-08 13:28:41 +00:00
bool fResult = true ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2010-03-27 16:05:02 +00:00
fResult = : : pGUI - > ShowMessageModal ( Message . getData ( ) , LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
2010-03-28 18:58:01 +00:00
( pCancel ? C4GUI : : MessageDialog : : btnOK : 0 ) | C4GUI : : MessageDialog : : btnAbort ,
C4GUI : : Ico_Error ) ;
2010-03-27 16:05:02 +00:00
if ( pCancel ) * pCancel = fResult ;
return false ;
}
2009-05-08 13:28:41 +00:00
2009-04-21 21:34:39 +00:00
// Add to message loop
Application . Add ( pLeagueClient ) ;
2010-03-27 16:05:02 +00:00
// OK
return true ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : DeinitLeague ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// league clear
MasterServerAddress . Clear ( ) ;
2010-03-27 16:05:02 +00:00
Game . Parameters . League . Clear ( ) ;
Game . Parameters . LeagueAddress . Clear ( ) ;
if ( pLeagueClient )
2010-03-28 18:58:01 +00:00
{
2009-04-21 21:34:39 +00:00
Application . Remove ( pLeagueClient ) ;
2016-11-02 23:58:02 +00:00
delete pLeagueClient ; pLeagueClient = nullptr ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : LeagueStart ( bool * pCancel )
{
2010-03-27 16:05:02 +00:00
// Not needed?
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient | | ! fHost )
2010-03-27 16:05:02 +00:00
return true ;
2009-05-08 13:28:41 +00:00
// Default
2010-03-28 18:58:01 +00:00
if ( pCancel ) * pCancel = true ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Do update
C4Network2Reference Ref ;
Ref . InitLocal ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Start ( Ref ) )
2010-03-27 16:05:02 +00:00
{
// Log message
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUE_STARTGAME " ) , pLeagueClient - > GetError ( ) ) ;
LogFatal ( Message . getData ( ) ) ;
// Show message
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Show option to cancel, if possible
2009-06-05 16:53:56 +00:00
bool fResult = : : pGUI - > ShowMessageModal (
2010-03-28 18:58:01 +00:00
Message . getData ( ) ,
LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
pCancel ? ( C4GUI : : MessageDialog : : btnOK | C4GUI : : MessageDialog : : btnAbort ) : C4GUI : : MessageDialog : : btnOK ,
C4GUI : : Ico_Error ) ;
if ( pCancel )
2009-05-08 13:28:41 +00:00
* pCancel = ! fResult ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Failed
2010-03-27 16:05:02 +00:00
return false ;
}
2009-05-08 13:28:41 +00:00
2016-10-20 19:14:08 +00:00
InitPuncher ( ) ;
2016-09-07 21:46:22 +00:00
2010-03-27 16:05:02 +00:00
// Let's wait for response
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_LEAGUE_REGGAME " ) , pLeagueClient - > getServerName ( ) ) ;
Log ( Message . getData ( ) ) ;
// Set up a dialog
2016-11-02 23:58:02 +00:00
C4GUI : : MessageDialog * pDlg = nullptr ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2009-05-08 13:28:41 +00:00
{
// create & show
pDlg = new C4GUI : : MessageDialog ( Message . getData ( ) , LoadResStr ( " IDS_NET_LEAGUE_STARTGAME " ) ,
2010-03-28 18:58:01 +00:00
C4GUI : : MessageDialog : : btnAbort , C4GUI : : Ico_NetWait , C4GUI : : MessageDialog : : dsRegular ) ;
if ( ! pDlg | | ! pDlg - > Show ( : : pGUI , true ) ) return false ;
2009-05-08 13:28:41 +00:00
}
// Wait for response
2010-03-28 18:58:01 +00:00
while ( pLeagueClient - > isBusy ( ) )
2010-03-27 16:05:02 +00:00
{
// Execute GUI
2010-03-28 18:58:01 +00:00
if ( ! Application . ScheduleProcs ( ) | |
( pDlg & & pDlg - > IsAborted ( ) ) )
2010-03-27 16:05:02 +00:00
{
2010-03-28 18:58:01 +00:00
// Clear up
2010-10-29 23:47:50 +00:00
if ( pDlg ) delete pDlg ;
2009-05-08 13:28:41 +00:00
return false ;
2010-03-27 16:05:02 +00:00
}
// Check if league server has responded
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Execute ( 100 ) )
2010-03-27 16:05:02 +00:00
break ;
}
// Close dialog
2010-10-29 23:47:50 +00:00
if ( pDlg )
2009-05-08 13:28:41 +00:00
{
pDlg - > Close ( true ) ;
delete pDlg ;
}
// Error?
StdStrBuf LeagueServerMessage , League , StreamingAddr ;
int32_t Seed = Game . RandomSeed , MaxPlayersLeague = 0 ;
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > isSuccess ( ) | |
! pLeagueClient - > GetStartReply ( & LeagueServerMessage , & League , & StreamingAddr , & Seed , & MaxPlayersLeague ) )
2010-03-27 16:05:02 +00:00
{
const char * pError = pLeagueClient - > GetError ( ) ? pLeagueClient - > GetError ( ) :
2014-04-27 13:31:23 +00:00
LeagueServerMessage . getLength ( ) ? LeagueServerMessage . getData ( ) :
2010-03-28 18:58:01 +00:00
LoadResStr ( " IDS_NET_ERR_LEAGUE_EMPTYREPLY " ) ;
2010-03-27 16:05:02 +00:00
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUE_REGGAME " ) , pError ) ;
// Log message
Log ( Message . getData ( ) ) ;
// Show message
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Show option to cancel, if possible
2009-06-05 16:53:56 +00:00
bool fResult = : : pGUI - > ShowMessageModal (
2010-03-28 18:58:01 +00:00
Message . getData ( ) ,
LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
pCancel ? ( C4GUI : : MessageDialog : : btnOK | C4GUI : : MessageDialog : : btnAbort ) : C4GUI : : MessageDialog : : btnOK ,
C4GUI : : Ico_Error ) ;
if ( pCancel )
2009-05-08 13:28:41 +00:00
* pCancel = ! fResult ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Failed
2010-03-27 16:05:02 +00:00
return false ;
}
2009-05-08 13:28:41 +00:00
// Show message
2010-03-28 18:58:01 +00:00
if ( LeagueServerMessage . getLength ( ) )
2009-05-08 13:28:41 +00:00
{
2014-04-27 13:31:23 +00:00
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_MSG_LEAGUEGAMESIGNUP " ) , pLeagueClient - > getServerName ( ) , LeagueServerMessage . getData ( ) ) ;
2010-03-27 16:05:02 +00:00
// Log message
Log ( Message . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
// Show message
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2009-05-08 13:28:41 +00:00
{
// Show option to cancel, if possible
2009-06-05 16:53:56 +00:00
bool fResult = : : pGUI - > ShowMessageModal (
2010-03-28 18:58:01 +00:00
Message . getData ( ) ,
LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
pCancel ? ( C4GUI : : MessageDialog : : btnOK | C4GUI : : MessageDialog : : btnAbort ) : C4GUI : : MessageDialog : : btnOK ,
C4GUI : : Ico_Error ) ;
if ( pCancel )
2009-05-08 13:28:41 +00:00
* pCancel = ! fResult ;
2010-03-28 18:58:01 +00:00
if ( ! fResult )
2009-05-08 13:28:41 +00:00
{
LeagueEnd ( ) ; DeinitLeague ( ) ;
return false ;
}
}
}
// Set game parameters for league game
Game . Parameters . League = League ;
Game . RandomSeed = Seed ;
2010-03-28 18:58:01 +00:00
if ( MaxPlayersLeague )
2009-05-08 13:28:41 +00:00
Game . Parameters . MaxPlayers = MaxPlayersLeague ;
2010-03-28 18:58:01 +00:00
if ( ! League . getLength ( ) )
{
2009-05-08 13:28:41 +00:00
Game . Parameters . LeagueAddress . Clear ( ) ;
Game . Parameters . StreamAddress . Clear ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Game . Parameters . StreamAddress = StreamingAddr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// All ok
2009-05-08 13:28:41 +00:00
fLeagueEndSent = false ;
2010-03-27 16:05:02 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : LeagueUpdate ( )
{
2010-03-27 16:05:02 +00:00
// Not needed?
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient | | ! fHost )
2010-03-27 16:05:02 +00:00
return true ;
2009-05-08 13:28:41 +00:00
// League client currently busy?
2010-03-28 18:58:01 +00:00
if ( pLeagueClient - > isBusy ( ) )
2009-05-08 13:28:41 +00:00
return true ;
2010-03-27 16:05:02 +00:00
// Create reference
C4Network2Reference Ref ;
Ref . InitLocal ( ) ;
2009-05-08 13:28:41 +00:00
2010-03-27 16:05:02 +00:00
// Do update
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Update ( Ref ) )
2010-03-27 16:05:02 +00:00
{
// Log
LogF ( LoadResStr ( " IDS_NET_ERR_LEAGUE_UPDATEGAME " ) , pLeagueClient - > GetError ( ) ) ;
return false ;
}
2009-05-08 13:28:41 +00:00
// Timing
2016-11-02 23:58:02 +00:00
iLastLeagueUpdate = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
iLeagueUpdateDelay = Config . Network . MasterReferencePeriod ;
2010-03-27 16:05:02 +00:00
return true ;
2009-05-08 13:28:41 +00:00
}
bool C4Network2 : : LeagueUpdateProcessReply ( )
{
// safety: A reply must be present
assert ( pLeagueClient ) ;
assert ( fHost ) ;
assert ( ! pLeagueClient - > isBusy ( ) ) ;
assert ( pLeagueClient - > getCurrentAction ( ) = = C4LA_Update ) ;
// check reply success
2010-03-27 16:05:02 +00:00
C4ClientPlayerInfos PlayerLeagueInfos ;
StdStrBuf LeagueServerMessage ;
bool fSucc = pLeagueClient - > isSuccess ( ) & & pLeagueClient - > GetUpdateReply ( & LeagueServerMessage , & PlayerLeagueInfos ) ;
pLeagueClient - > ResetCurrentAction ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! fSucc )
2010-03-27 16:05:02 +00:00
{
const char * pError = pLeagueClient - > GetError ( ) ? pLeagueClient - > GetError ( ) :
2014-04-27 13:31:23 +00:00
LeagueServerMessage . getLength ( ) ? LeagueServerMessage . getData ( ) :
2010-03-28 18:58:01 +00:00
LoadResStr ( " IDS_NET_ERR_LEAGUE_EMPTYREPLY " ) ;
2010-03-27 16:05:02 +00:00
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUE_UPDATEGAME " ) , pError ) ;
// Show message - no dialog, because it's not really fatal and might happen in the running game
Log ( Message . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
return false ;
2010-03-27 16:05:02 +00:00
}
2009-05-08 13:28:41 +00:00
// evaluate reply: Transfer data to players
2010-03-27 16:05:02 +00:00
// Take round results
C4PlayerInfoList & TargetList = Game . PlayerInfos ;
C4ClientPlayerInfos * pInfos ; C4PlayerInfo * pInfo , * pResultInfo ;
2010-03-28 18:58:01 +00:00
for ( int iClient = 0 ; ( pInfos = TargetList . GetIndexedInfo ( iClient ) ) ; iClient + + )
for ( int iInfo = 0 ; ( pInfo = pInfos - > GetPlayerInfo ( iInfo ) ) ; iInfo + + )
if ( ( pResultInfo = PlayerLeagueInfos . GetPlayerInfoByID ( pInfo - > GetID ( ) ) ) )
{
2009-05-08 13:28:41 +00:00
int32_t iLeagueProjectedGain = pResultInfo - > GetLeagueProjectedGain ( ) ;
if ( iLeagueProjectedGain ! = pInfo - > GetLeagueProjectedGain ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pInfo - > SetLeagueProjectedGain ( iLeagueProjectedGain ) ;
pInfos - > SetUpdated ( ) ;
2010-03-27 16:05:02 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// transfer info update to other clients
2010-03-27 16:05:02 +00:00
Players . SendUpdatedPlayers ( ) ;
2009-05-08 13:28:41 +00:00
// if lobby is open, notify lobby of updated players
if ( pLobby ) pLobby - > OnPlayersChange ( ) ;
// OMFG SUCCESS!
return true ;
}
bool C4Network2 : : LeagueEnd ( const char * szRecordName , const BYTE * pRecordSHA )
{
C4RoundResultsPlayers RoundResults ;
StdStrBuf sResultMessage ;
bool fIsError = true ;
// Not needed?
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient | | ! fHost | | fLeagueEndSent )
2009-05-08 13:28:41 +00:00
return true ;
// Make sure league client is available
LeagueWaitNotBusy ( ) ;
// Try until either aborted or successful
const int MAX_RETRIES = 10 ;
2010-03-28 18:58:01 +00:00
for ( int iRetry = 0 ; iRetry < MAX_RETRIES ; iRetry + + )
2009-05-08 13:28:41 +00:00
{
// Do update
C4Network2Reference Ref ;
2009-06-05 15:19:46 +00:00
Ref . InitLocal ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > End ( Ref , szRecordName , pRecordSHA ) )
2009-05-08 13:28:41 +00:00
{
// Log message
sResultMessage = FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUE_FINISHGAME " ) , pLeagueClient - > GetError ( ) ) ;
Log ( sResultMessage . getData ( ) ) ;
// Show message, allow retry
2010-10-29 23:47:50 +00:00
if ( Application . isEditor ) break ;
2009-06-05 16:53:56 +00:00
bool fRetry = : : pGUI - > ShowMessageModal ( sResultMessage . getData ( ) , LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
2010-03-28 18:58:01 +00:00
C4GUI : : MessageDialog : : btnRetryAbort , C4GUI : : Ico_Error ) ;
2009-05-08 13:28:41 +00:00
if ( fRetry ) continue ;
break ;
}
// Let's wait for response
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_NET_LEAGUE_SENDRESULT " ) , pLeagueClient - > getServerName ( ) ) ;
Log ( Message . getData ( ) ) ;
// Wait for response
2010-03-28 18:58:01 +00:00
while ( pLeagueClient - > isBusy ( ) )
2009-05-08 13:28:41 +00:00
{
// Check if league server has responded
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Execute ( 100 ) )
break ;
2009-05-08 13:28:41 +00:00
}
// Error?
StdStrBuf LeagueServerMessage ;
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > isSuccess ( ) | | ! pLeagueClient - > GetEndReply ( & LeagueServerMessage , & RoundResults ) )
2009-05-08 13:28:41 +00:00
{
const char * pError = pLeagueClient - > GetError ( ) ? pLeagueClient - > GetError ( ) :
2014-04-27 13:31:23 +00:00
LeagueServerMessage . getLength ( ) ? LeagueServerMessage . getData ( ) :
2010-03-28 18:58:01 +00:00
LoadResStr ( " IDS_NET_ERR_LEAGUE_EMPTYREPLY " ) ;
2009-05-08 13:28:41 +00:00
sResultMessage . Take ( FormatString ( LoadResStr ( " IDS_NET_ERR_LEAGUE_SENDRESULT " ) , pError ) ) ;
2010-10-29 23:47:50 +00:00
if ( Application . isEditor ) continue ;
2009-05-08 13:28:41 +00:00
// Only retry if we didn't get an answer from the league server
bool fRetry = ! pLeagueClient - > isSuccess ( ) ;
2009-06-05 16:53:56 +00:00
fRetry = : : pGUI - > ShowMessageModal ( sResultMessage . getData ( ) , LoadResStr ( " IDS_NET_ERR_LEAGUE " ) ,
2010-03-28 18:58:01 +00:00
fRetry ? C4GUI : : MessageDialog : : btnRetryAbort : C4GUI : : MessageDialog : : btnAbort ,
C4GUI : : Ico_Error ) ;
2009-05-08 13:28:41 +00:00
if ( fRetry ) continue ;
}
else
{
// All OK!
sResultMessage . Copy ( LoadResStr ( Game . Parameters . isLeague ( ) ? " IDS_MSG_LEAGUEEVALUATIONSUCCESSFU " : " IDS_MSG_INTERNETGAMEEVALUATED " ) ) ;
fIsError = false ;
}
// Done
break ;
}
// Show message
Log ( sResultMessage . getData ( ) ) ;
// Take round results
Game . RoundResults . EvaluateLeague ( sResultMessage . getData ( ) , ! fIsError , RoundResults ) ;
// Send round results to other clients
C4PacketLeagueRoundResults LeagueUpdatePacket ( sResultMessage . getData ( ) , ! fIsError , RoundResults ) ;
Clients . BroadcastMsgToClients ( MkC4NetIOPacket ( PID_LeagueRoundResults , LeagueUpdatePacket ) ) ;
// All done
fLeagueEndSent = true ;
return true ;
}
bool C4Network2 : : LeaguePlrAuth ( C4PlayerInfo * pInfo )
{
2010-03-27 16:05:02 +00:00
// Not possible?
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient )
2010-03-27 16:05:02 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// Make sure league client is avilable
LeagueWaitNotBusy ( ) ;
// Official league?
2014-05-18 13:10:00 +00:00
bool fOfficialLeague = SEqual ( pLeagueClient - > getServerName ( ) , " league.openclonk.org " ) ;
2009-05-08 13:28:41 +00:00
StdStrBuf Account , Password ;
2014-05-18 13:10:00 +00:00
bool fRememberLogin = false ;
2009-05-08 13:28:41 +00:00
2014-05-18 13:10:00 +00:00
// Default password from login token if present
if ( Config . Network . GetLeagueLoginData ( pLeagueClient - > getServerName ( ) , pInfo - > GetName ( ) , & Account , & Password ) )
2009-05-08 13:28:41 +00:00
{
2014-05-18 13:10:00 +00:00
fRememberLogin = ( Password . getLength ( ) > 0 ) ;
}
else
{
Account . Copy ( pInfo - > GetName ( ) ) ;
}
2009-05-08 13:28:41 +00:00
2014-05-18 13:10:00 +00:00
for ( ; ; )
{
// ask for account name and password
if ( ! C4LeagueSignupDialog : : ShowModal ( pInfo - > GetName ( ) , Account . getData ( ) , pLeagueClient - > getServerName ( ) , & Account , & Password , ! fOfficialLeague , false , & fRememberLogin ) )
return false ;
2009-05-08 13:28:41 +00:00
// safety (modal dlg may have deleted network)
if ( ! pLeagueClient ) return false ;
// Send authentication request
2016-11-02 23:58:02 +00:00
if ( ! pLeagueClient - > Auth ( * pInfo , Account . getData ( ) , Password . getData ( ) , nullptr , nullptr , fRememberLogin ) )
2009-05-08 13:28:41 +00:00
return false ;
// safety (modal dlg may have deleted network)
if ( ! pLeagueClient ) return false ;
// Wait for a response
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_MSG_TRYLEAGUESIGNUP " ) , pInfo - > GetName ( ) , Account . getData ( ) , pLeagueClient - > getServerName ( ) ) ;
Log ( Message . getData ( ) ) ;
// Set up a dialog
2016-11-02 23:58:02 +00:00
C4GUI : : MessageDialog * pDlg = nullptr ;
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2009-05-08 13:28:41 +00:00
{
// create & show
pDlg = new C4GUI : : MessageDialog ( Message . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUP " ) , C4GUI : : MessageDialog : : btnAbort , C4GUI : : Ico_NetWait , C4GUI : : MessageDialog : : dsRegular ) ;
2010-03-28 18:58:01 +00:00
if ( ! pDlg | | ! pDlg - > Show ( : : pGUI , true ) ) return false ;
2009-05-08 13:28:41 +00:00
}
// Wait for response
2010-03-28 18:58:01 +00:00
while ( pLeagueClient - > isBusy ( ) )
2009-05-08 13:28:41 +00:00
{
// Execute GUI
2010-03-28 18:58:01 +00:00
if ( ! Application . ScheduleProcs ( ) | |
( pDlg & & pDlg - > IsAborted ( ) ) )
2009-05-08 13:28:41 +00:00
{
// Clear up
2010-10-29 23:47:50 +00:00
if ( pDlg ) delete pDlg ;
2009-05-08 13:28:41 +00:00
return false ;
}
// Check if league server has responded
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > Execute ( 0 ) )
2009-05-08 13:28:41 +00:00
break ;
}
// Close dialog
2010-10-29 23:47:50 +00:00
if ( pDlg )
2009-05-08 13:28:41 +00:00
{
pDlg - > Close ( true ) ;
delete pDlg ;
}
// Success?
2014-05-18 13:10:00 +00:00
StdStrBuf AUID , AccountMaster , LoginToken ; bool fUnregistered = false ;
if ( pLeagueClient - > GetAuthReply ( & Message , & AUID , & AccountMaster , & fUnregistered , & LoginToken ) )
2009-05-08 13:28:41 +00:00
{
// Set AUID
pInfo - > SetAuthID ( AUID . getData ( ) ) ;
2014-05-18 13:10:00 +00:00
// Remember login data; set or clear login token
Config . Network . SetLeagueLoginData ( pLeagueClient - > getServerName ( ) , pInfo - > GetName ( ) , Account . getData ( ) , fRememberLogin ? LoginToken . getData ( ) : " " ) ;
2009-05-08 13:28:41 +00:00
// Show welcome message, if any
bool fSuccess ;
2010-03-28 18:58:01 +00:00
if ( Message . getLength ( ) )
2009-06-05 16:53:56 +00:00
fSuccess = : : pGUI - > ShowMessageModal (
2014-04-27 13:31:23 +00:00
Message . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUPCONFIRM " ) ,
2010-03-28 18:58:01 +00:00
C4GUI : : MessageDialog : : btnOKAbort , C4GUI : : Ico_Ex_League ) ;
else if ( AccountMaster . getLength ( ) )
2009-06-05 16:53:56 +00:00
fSuccess = : : pGUI - > ShowMessageModal (
2010-03-28 18:58:01 +00:00
FormatString ( LoadResStr ( " IDS_MSG_LEAGUEPLAYERSIGNUPAS " ) , pInfo - > GetName ( ) , AccountMaster . getData ( ) , pLeagueClient - > getServerName ( ) ) . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUPCONFIRM " ) ,
C4GUI : : MessageDialog : : btnOKAbort , C4GUI : : Ico_Ex_League ) ;
2009-05-08 13:28:41 +00:00
else
2009-06-05 16:53:56 +00:00
fSuccess = : : pGUI - > ShowMessageModal (
2010-03-28 18:58:01 +00:00
FormatString ( LoadResStr ( " IDS_MSG_LEAGUEPLAYERSIGNUP " ) , pInfo - > GetName ( ) , pLeagueClient - > getServerName ( ) ) . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUPCONFIRM " ) ,
C4GUI : : MessageDialog : : btnOKAbort , C4GUI : : Ico_Ex_League ) ;
2009-05-08 13:28:41 +00:00
// Approved?
2010-03-28 18:58:01 +00:00
if ( fSuccess )
2009-05-08 13:28:41 +00:00
// Done
return true ;
else
// Sign-up was cancelled by user
2009-06-05 16:53:56 +00:00
: : pGUI - > ShowMessageModal ( FormatString ( LoadResStr ( " IDS_MSG_LEAGUESIGNUPCANCELLED " ) , pInfo - > GetName ( ) ) . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUP " ) , C4GUI : : MessageDialog : : btnOK , C4GUI : : Ico_Notify ) ;
2009-05-08 13:28:41 +00:00
}
else
{
2014-05-18 13:10:00 +00:00
// Authentification error
LogF ( LoadResStr ( " IDS_MSG_LEAGUESIGNUPERROR " ) , Message . getData ( ) ) ;
2009-06-05 16:53:56 +00:00
: : pGUI - > ShowMessageModal ( FormatString ( LoadResStr ( " IDS_MSG_LEAGUESERVERMSG " ) , Message . getData ( ) ) . getData ( ) , LoadResStr ( " IDS_DLG_LEAGUESIGNUPFAILED " ) , C4GUI : : MessageDialog : : btnOK , C4GUI : : Ico_Error ) ;
2014-05-18 13:10:00 +00:00
// after a league server error message, always fall-through to try again
2009-05-08 13:28:41 +00:00
}
// Try given account name as default next time
2010-03-28 18:58:01 +00:00
if ( AccountMaster . getLength ( ) )
2009-11-25 18:38:54 +00:00
Account . Take ( std : : move ( AccountMaster ) ) ;
2009-05-08 13:28:41 +00:00
// safety (modal dlg may have deleted network)
if ( ! pLeagueClient ) return false ;
}
}
bool C4Network2 : : LeaguePlrAuthCheck ( C4PlayerInfo * pInfo )
{
2010-03-27 16:05:02 +00:00
// Not possible?
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient )
2010-03-27 16:05:02 +00:00
return false ;
2009-05-08 13:28:41 +00:00
// Make sure league client is available
LeagueWaitNotBusy ( ) ;
// Ask league server to check the code
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > AuthCheck ( * pInfo ) )
2009-05-08 13:28:41 +00:00
return false ;
// Log
StdStrBuf Message = FormatString ( LoadResStr ( " IDS_MSG_LEAGUEJOINING " ) , pInfo - > GetName ( ) ) ;
Log ( Message . getData ( ) ) ;
// Wait for response
2010-03-28 18:58:01 +00:00
while ( pLeagueClient - > isBusy ( ) )
if ( ! pLeagueClient - > Execute ( 100 ) )
2010-03-27 16:05:02 +00:00
break ;
2009-05-08 13:28:41 +00:00
// Check response validity
if ( ! pLeagueClient - > isSuccess ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
LeagueShowError ( pLeagueClient - > GetError ( ) ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Check if league server approves. pInfo will have league info if this call is successful.
2010-03-28 18:58:01 +00:00
if ( ! pLeagueClient - > GetAuthCheckReply ( & Message , Game . Parameters . League . getData ( ) , pInfo ) )
{
2014-04-27 13:31:23 +00:00
LeagueShowError ( FormatString ( LoadResStr ( " IDS_MSG_LEAGUEJOINREFUSED " ) , pInfo - > GetName ( ) , Message . getData ( ) ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
return true ;
}
void C4Network2 : : LeagueNotifyDisconnect ( int32_t iClientID , C4LeagueDisconnectReason eReason )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// league active?
if ( ! pLeagueClient | | ! Game . Parameters . isLeague ( ) ) return ;
// only in running game
if ( ! Game . IsRunning | | Game . GameOver ) return ;
// clients send notifications for their own players; host sends for the affected client players
if ( ! isHost ( ) ) { if ( ! Clients . GetLocal ( ) ) return ; iClientID = Clients . GetLocal ( ) - > getID ( ) ; }
// clients only need notifications if they have players in the game
const C4ClientPlayerInfos * pInfos = Game . PlayerInfos . GetInfoByClientID ( iClientID ) ;
if ( ! pInfos ) return ;
int32_t i = 0 ; C4PlayerInfo * pInfo ;
2010-01-25 04:00:59 +00:00
while ( ( pInfo = pInfos - > GetPlayerInfo ( i + + ) ) ) if ( pInfo - > IsJoined ( ) & & ! pInfo - > IsRemoved ( ) ) break ;
2009-05-08 13:28:41 +00:00
if ( ! pInfo ) return ;
// Make sure league client is avilable
LeagueWaitNotBusy ( ) ;
// report the disconnect!
LogF ( LoadResStr ( " IDS_LEAGUE_LEAGUEREPORTINGUNEXPECTED " ) , ( int ) eReason ) ;
pLeagueClient - > ReportDisconnect ( * pInfos , eReason ) ;
// wait for the reply
LeagueWaitNotBusy ( ) ;
// display it
const char * szMsg ;
StdStrBuf sMessage ;
if ( pLeagueClient - > GetReportDisconnectReply ( & sMessage ) )
szMsg = LoadResStr ( " IDS_MSG_LEAGUEUNEXPECTEDDISCONNEC " ) ;
else
szMsg = LoadResStr ( " IDS_ERR_LEAGUEERRORREPORTINGUNEXP " ) ;
2014-04-27 13:31:23 +00:00
LogF ( szMsg , sMessage . getData ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : LeagueWaitNotBusy ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// league client busy?
if ( ! pLeagueClient | | ! pLeagueClient - > isBusy ( ) ) return ;
// wait for it
Log ( LoadResStr ( " IDS_LEAGUE_WAITINGFORLASTLEAGUESERVE " ) ) ;
2010-03-28 18:58:01 +00:00
while ( pLeagueClient - > isBusy ( ) )
if ( ! pLeagueClient - > Execute ( 100 ) )
2010-03-27 16:05:02 +00:00
break ;
2009-05-08 13:28:41 +00:00
// if last request was an update request, process it
if ( pLeagueClient - > getCurrentAction ( ) = = C4LA_Update )
LeagueUpdateProcessReply ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : LeagueSurrender ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// there's currently no functionality to surrender in the league
// just stop responding so other clients will notify the disconnect
DeinitLeague ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : LeagueShowError ( const char * szMsg )
2010-03-28 18:58:01 +00:00
{
2010-10-29 23:47:50 +00:00
if ( ! Application . isEditor )
2010-03-28 18:58:01 +00:00
{
2009-06-05 16:53:56 +00:00
: : pGUI - > ShowErrorMessage ( szMsg ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
LogF ( LoadResStr ( " IDS_LGA_SERVERFAILURE " ) , szMsg ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : Vote ( C4ControlVoteType eType , bool fApprove , int32_t iData )
{
// Original vote?
2010-03-28 18:58:01 +00:00
if ( ! GetVote ( C4ClientIDUnknown , eType , iData ) )
2009-05-08 13:28:41 +00:00
{
// Too fast?
2016-11-02 23:58:02 +00:00
if ( time ( nullptr ) < ( time_t ) ( iLastOwnVoting + C4NetMinVotingInterval ) )
2009-05-08 13:28:41 +00:00
{
Log ( LoadResStr ( " IDS_TEXT_YOUCANONLYSTARTONEVOTINGE " ) ) ;
2011-05-08 20:36:48 +00:00
if ( ( eType = = VT_Kick & & iData = = Game . Clients . getLocalID ( ) ) | | eType = = VT_Cancel )
2009-05-08 13:28:41 +00:00
OpenSurrenderDialog ( eType , iData ) ;
return ;
}
// Save timestamp
2016-11-02 23:58:02 +00:00
iLastOwnVoting = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
}
// Already voted? Ignore
2010-03-28 18:58:01 +00:00
if ( GetVote ( : : Control . ClientID ( ) , eType , iData ) )
2009-05-08 13:28:41 +00:00
return ;
// Set pause mode if this is the host
2010-03-28 18:58:01 +00:00
if ( isHost ( ) & & isRunning ( ) )
2009-05-08 13:28:41 +00:00
{
Pause ( ) ;
fPausedForVote = true ;
}
// send vote control
2009-06-15 22:06:37 +00:00
: : Control . DoInput ( CID_Vote , new C4ControlVote ( eType , fApprove , iData ) , CDT_Direct ) ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : AddVote ( const C4ControlVote & Vote )
{
// Save back timestamp
2010-03-28 18:58:01 +00:00
if ( ! Votes . firstPkt ( ) )
2016-11-02 23:58:02 +00:00
iVoteStartTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// Save vote back
Votes . Add ( CID_Vote , new C4ControlVote ( Vote ) ) ;
// Set pause mode if this is the host
2010-03-28 18:58:01 +00:00
if ( isHost ( ) & & isRunning ( ) )
2009-05-08 13:28:41 +00:00
{
Pause ( ) ;
fPausedForVote = true ;
}
// Check if the dialog should be opened
OpenVoteDialog ( ) ;
}
C4IDPacket * C4Network2 : : GetVote ( int32_t iClientID , C4ControlVoteType eType , int32_t iData )
{
C4ControlVote * pVote ;
2010-03-28 18:58:01 +00:00
for ( C4IDPacket * pPkt = Votes . firstPkt ( ) ; pPkt ; pPkt = Votes . nextPkt ( pPkt ) )
if ( pPkt - > getPktType ( ) = = CID_Vote )
if ( ( pVote = static_cast < C4ControlVote * > ( pPkt - > getPkt ( ) ) ) )
if ( iClientID = = C4ClientIDUnknown | | pVote - > getByClient ( ) = = iClientID )
if ( pVote - > getType ( ) = = eType & & pVote - > getData ( ) = = iData )
2009-05-08 13:28:41 +00:00
return pPkt ;
2016-11-02 23:58:02 +00:00
return nullptr ;
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : EndVote ( C4ControlVoteType eType , bool fApprove , int32_t iData )
{
// Remove all vote packets
C4IDPacket * pPkt ; int32_t iOrigin = C4ClientIDUnknown ;
2010-03-28 18:58:01 +00:00
while ( ( pPkt = GetVote ( C4ClientIDAll , eType , iData ) ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
if ( iOrigin = = C4ClientIDUnknown )
2009-05-08 13:28:41 +00:00
iOrigin = static_cast < C4ControlVote * > ( pPkt - > getPkt ( ) ) - > getByClient ( ) ;
Votes . Delete ( pPkt ) ;
}
// Reset timestamp
2016-11-02 23:58:02 +00:00
iVoteStartTime = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// Approved own voting? Reset voting block
2010-03-28 18:58:01 +00:00
if ( fApprove & & iOrigin = = Game . Clients . getLocalID ( ) )
2009-05-08 13:28:41 +00:00
iLastOwnVoting = 0 ;
// Dialog open?
2010-03-28 18:58:01 +00:00
if ( pVoteDialog )
if ( pVoteDialog - > getVoteType ( ) = = eType & & pVoteDialog - > getVoteData ( ) = = iData )
2009-05-08 13:28:41 +00:00
{
// close
delete pVoteDialog ;
2016-11-02 23:58:02 +00:00
pVoteDialog = nullptr ;
2009-05-08 13:28:41 +00:00
}
// Did we try to kick ourself? Ask if we'd like to surrender
2011-05-08 20:36:48 +00:00
bool fCancelVote = ( eType = = VT_Kick & & iData = = Game . Clients . getLocalID ( ) ) | | eType = = VT_Cancel ;
if ( ! fApprove & & fCancelVote & & iOrigin = = Game . Clients . getLocalID ( ) )
2009-05-08 13:28:41 +00:00
OpenSurrenderDialog ( eType , iData ) ;
// Check if the dialog should be opened
OpenVoteDialog ( ) ;
// Pause/unpause voting?
2010-03-28 18:58:01 +00:00
if ( fApprove & & eType = = VT_Pause )
2009-05-08 13:28:41 +00:00
fPausedForVote = ! iData ;
// No voting left? Reset pause.
2010-03-28 18:58:01 +00:00
if ( ! Votes . firstPkt ( ) )
if ( fPausedForVote )
{
2009-05-08 13:28:41 +00:00
Start ( ) ;
fPausedForVote = false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OpenVoteDialog ( )
{
// Dialog already open?
2010-03-28 18:58:01 +00:00
if ( pVoteDialog ) return ;
2009-05-08 13:28:41 +00:00
// No vote available?
2010-03-28 18:58:01 +00:00
if ( ! Votes . firstPkt ( ) ) return ;
2009-05-08 13:28:41 +00:00
// Can't vote?
C4ClientPlayerInfos * pPlayerInfos = Game . PlayerInfos . GetInfoByClientID ( Game . Clients . getLocalID ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! pPlayerInfos | | ! pPlayerInfos - > GetPlayerCount ( ) | | ! pPlayerInfos - > GetJoinedPlayerCount ( ) )
2009-05-08 13:28:41 +00:00
return ;
// Search a voting we have to vote on
2010-03-28 18:58:01 +00:00
for ( C4IDPacket * pPkt = Votes . firstPkt ( ) ; pPkt ; pPkt = Votes . nextPkt ( pPkt ) )
2009-05-08 13:28:41 +00:00
{
// Already voted on this matter?
C4ControlVote * pVote = static_cast < C4ControlVote * > ( pPkt - > getPkt ( ) ) ;
2010-03-28 18:58:01 +00:00
if ( ! GetVote ( : : Control . ClientID ( ) , pVote - > getType ( ) , pVote - > getData ( ) ) )
2009-05-08 13:28:41 +00:00
{
// Compose message
C4Client * pSrcClient = Game . Clients . getClientByID ( pVote - > getByClient ( ) ) ;
StdStrBuf Msg ; Msg . Format ( LoadResStr ( " IDS_VOTE_WANTSTOALLOW " ) , pSrcClient ? pSrcClient - > getName ( ) : " ??? " , pVote - > getDesc ( ) . getData ( ) ) ;
Msg . AppendChar ( ' | ' ) ;
Msg . Append ( pVote - > getDescWarning ( ) ) ;
// Open dialog
pVoteDialog = new C4VoteDialog ( Msg . getData ( ) , pVote - > getType ( ) , pVote - > getData ( ) , false ) ;
pVoteDialog - > SetDelOnClose ( ) ;
2009-06-05 16:53:56 +00:00
pVoteDialog - > Show ( : : pGUI , true ) ;
2009-05-08 13:28:41 +00:00
break ;
}
}
}
void C4Network2 : : OpenSurrenderDialog ( C4ControlVoteType eType , int32_t iData )
{
2010-03-28 18:58:01 +00:00
if ( ! pVoteDialog )
{
2009-05-08 13:28:41 +00:00
pVoteDialog = new C4VoteDialog (
2010-03-28 18:58:01 +00:00
LoadResStr ( " IDS_VOTE_SURRENDERWARNING " ) , eType , iData , true ) ;
2009-05-08 13:28:41 +00:00
pVoteDialog - > SetDelOnClose ( ) ;
2009-06-05 16:53:56 +00:00
pVoteDialog - > Show ( : : pGUI , true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
void C4Network2 : : OnVoteDialogClosed ( )
{
2016-11-02 23:58:02 +00:00
pVoteDialog = nullptr ;
2009-05-08 13:28:41 +00:00
}
// *** C4VoteDialog
C4VoteDialog : : C4VoteDialog ( const char * szText , C4ControlVoteType eVoteType , int32_t iVoteData , bool fSurrender )
2016-11-02 23:58:02 +00:00
: MessageDialog ( szText , LoadResStr ( " IDS_DLG_VOTING " ) , C4GUI : : MessageDialog : : btnYesNo , C4GUI : : Ico_Confirm , C4GUI : : MessageDialog : : dsRegular , nullptr , true ) ,
2010-01-25 04:00:59 +00:00
eVoteType ( eVoteType ) , iVoteData ( iVoteData ) , fSurrender ( fSurrender )
2009-05-08 13:28:41 +00:00
{
}
void C4VoteDialog : : OnClosed ( bool fOK )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fAbortGame = false ;
// notify that this object will be deleted shortly
2009-06-05 15:19:46 +00:00
: : Network . OnVoteDialogClosed ( ) ;
2009-05-08 13:28:41 +00:00
// Was league surrender dialog
if ( fSurrender )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// League surrender accepted
if ( fOK )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// set game leave reason, although round results dialog isn't showing it ATM
Game . RoundResults . EvaluateNetwork ( C4RoundResults : : NR_NetError , LoadResStr ( " IDS_ERR_YOUSURRENDEREDTHELEAGUEGA " ) ) ;
// leave game
2009-06-05 15:19:46 +00:00
: : Network . LeagueSurrender ( ) ;
: : Network . Clear ( ) ;
2009-05-08 13:28:41 +00:00
// We have just league-surrendered. Abort the game - that is what we originally wanted.
// Note: as we are losing league points and this is a relevant game, it would actually be
// nice to show an evaluation dialog which tells us that we have lost and how many league
// points we have lost. But until the evaluation dialog can actually do that, it is better
// to abort completely.
// Note2: The league dialog will never know that, because the game will usually not be over yet.
// Scores are not calculated until after the game.
fAbortGame = true ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Was normal vote dialog
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Vote still active? Then vote.
2009-06-05 15:19:46 +00:00
if ( : : Network . GetVote ( C4ClientIDUnknown , eVoteType , iVoteData ) )
: : Network . Vote ( eVoteType , fOK , iVoteData ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// notify base class
MessageDialog : : OnClosed ( fOK ) ;
// Abort game
if ( fAbortGame )
Game . Abort ( true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
/* Lobby countdown */
void C4Network2 : : StartLobbyCountdown ( int32_t iCountdownTime )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// abort previous
if ( pLobbyCountdown ) AbortLobbyCountdown ( ) ;
// start new
pLobbyCountdown = new C4GameLobby : : Countdown ( iCountdownTime ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Network2 : : AbortLobbyCountdown ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// aboert lobby countdown
if ( pLobbyCountdown )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pLobbyCountdown - > Abort ( ) ;
delete pLobbyCountdown ;
2016-11-02 23:58:02 +00:00
pLobbyCountdown = nullptr ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
/* Streaming */
bool C4Network2 : : StartStreaming ( C4Record * pRecord )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Save back
fStreaming = true ;
pStreamedRecord = pRecord ;
2016-11-02 23:58:02 +00:00
iLastStreamAttempt = time ( nullptr ) ;
2009-05-08 13:28:41 +00:00
// Initialize compressor
ZeroMem ( & StreamCompressor , sizeof ( StreamCompressor ) ) ;
2010-03-28 18:58:01 +00:00
if ( deflateInit ( & StreamCompressor , 9 ) ! = Z_OK )
2009-05-08 13:28:41 +00:00
return false ;
// Create stream buffer
StreamingBuf . New ( C4NetStreamingMaxBlockSize ) ;
StreamCompressor . next_out = reinterpret_cast < BYTE * > ( StreamingBuf . getMData ( ) ) ;
StreamCompressor . avail_out = C4NetStreamingMaxBlockSize ;
// Initialize HTTP client
pStreamer = new C4Network2HTTPClient ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! pStreamer - > Init ( ) )
2009-05-08 13:28:41 +00:00
return false ;
2009-04-21 21:34:39 +00:00
Application . Add ( pStreamer ) ;
2009-05-08 13:28:41 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : FinishStreaming ( )
2010-03-28 18:58:01 +00:00
{
if ( ! fStreaming ) return false ;
2009-05-08 13:28:41 +00:00
// Stream
StreamIn ( true ) ;
// Reset record pointer
2016-11-02 23:58:02 +00:00
pStreamedRecord = nullptr ;
2009-05-08 13:28:41 +00:00
// Try to get rid of remaining data immediately
iLastStreamAttempt = 0 ;
StreamOut ( ) ;
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : StopStreaming ( )
2010-03-28 18:58:01 +00:00
{
if ( ! fStreaming ) return false ;
2009-05-08 13:28:41 +00:00
// Clear
2009-04-21 21:34:39 +00:00
Application . Remove ( pStreamer ) ;
2009-05-08 13:28:41 +00:00
fStreaming = false ;
2016-11-02 23:58:02 +00:00
pStreamedRecord = nullptr ;
2009-05-08 13:28:41 +00:00
deflateEnd ( & StreamCompressor ) ;
StreamingBuf . Clear ( ) ;
delete pStreamer ;
2016-11-02 23:58:02 +00:00
pStreamer = nullptr ;
2009-05-08 13:28:41 +00:00
// ... finalization?
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : StreamIn ( bool fFinish )
2010-03-28 18:58:01 +00:00
{
if ( ! pStreamedRecord ) return false ;
2009-05-08 13:28:41 +00:00
// Get data from record
const StdBuf & Data = pStreamedRecord - > GetStreamingBuf ( ) ;
2010-03-28 18:58:01 +00:00
if ( ! fFinish )
if ( ! Data . getSize ( ) | | ! StreamCompressor . avail_out )
2009-05-08 13:28:41 +00:00
return false ;
do
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Compress
StreamCompressor . next_in = const_cast < BYTE * > ( getBufPtr < BYTE > ( Data ) ) ;
StreamCompressor . avail_in = Data . getSize ( ) ;
int ret = deflate ( & StreamCompressor , fFinish ? Z_FINISH : Z_NO_FLUSH ) ;
// Anything consumed?
unsigned int iInAmount = Data . getSize ( ) - StreamCompressor . avail_in ;
2010-03-28 18:58:01 +00:00
if ( iInAmount > 0 )
2009-05-08 13:28:41 +00:00
pStreamedRecord - > ClearStreamingBuf ( iInAmount ) ;
// Done?
2010-03-28 18:58:01 +00:00
if ( ! fFinish | | ret = = Z_STREAM_END )
2009-05-08 13:28:41 +00:00
break ;
// Error while finishing?
2010-03-28 18:58:01 +00:00
if ( ret ! = Z_OK )
2009-05-08 13:28:41 +00:00
return false ;
// Enlarge buffer, if neccessary
size_t iPending = getPendingStreamData ( ) ;
size_t iGrow = StreamingBuf . getSize ( ) ;
StreamingBuf . Grow ( iGrow ) ;
StreamCompressor . avail_out + = iGrow ;
StreamCompressor . next_out = getMBufPtr < BYTE > ( StreamingBuf , iPending ) ;
2010-03-28 18:58:01 +00:00
}
while ( true ) ;
2009-05-08 13:28:41 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : StreamOut ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Streamer busy?
2010-03-28 18:58:01 +00:00
if ( ! pStreamer | | pStreamer - > isBusy ( ) )
2009-05-08 13:28:41 +00:00
return false ;
// Streamer done?
2010-03-28 18:58:01 +00:00
if ( pStreamer - > isSuccess ( ) )
{
2009-05-08 13:28:41 +00:00
// Move new data to front of buffer
2010-03-28 18:58:01 +00:00
if ( getPendingStreamData ( ) ! = iCurrentStreamAmount )
2009-05-08 13:28:41 +00:00
StreamingBuf . Move ( iCurrentStreamAmount , getPendingStreamData ( ) - iCurrentStreamAmount ) ;
// Free buffer space
StreamCompressor . next_out - = iCurrentStreamAmount ;
StreamCompressor . avail_out + = iCurrentStreamAmount ;
// Advance stream
iCurrentStreamPosition + = iCurrentStreamAmount ;
// Get input
StreamIn ( false ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Clear streamer
pStreamer - > Clear ( ) ;
// Record is still running?
2010-03-28 18:58:01 +00:00
if ( pStreamedRecord )
{
2009-05-08 13:28:41 +00:00
// Enough available to send?
2010-03-28 18:58:01 +00:00
if ( getPendingStreamData ( ) < C4NetStreamingMinBlockSize )
2009-05-08 13:28:41 +00:00
return false ;
// Overflow protection
2016-11-02 23:58:02 +00:00
if ( iLastStreamAttempt & & iLastStreamAttempt + C4NetStreamingInterval > = time ( nullptr ) )
2009-05-08 13:28:41 +00:00
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// All data finished?
else if ( ! getPendingStreamData ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Then we're done.
StopStreaming ( ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Set stream address
StdStrBuf StreamAddr ;
StreamAddr . Copy ( Game . Parameters . StreamAddress ) ;
StreamAddr . AppendFormat ( " pos=%d&end=%d " , iCurrentStreamPosition , ! pStreamedRecord ) ;
pStreamer - > SetServer ( StreamAddr . getData ( ) ) ;
// Send data
size_t iStreamAmount = getPendingStreamData ( ) ;
iCurrentStreamAmount = iStreamAmount ;
2016-11-02 23:58:02 +00:00
iLastStreamAttempt = time ( nullptr ) ;
2010-03-28 18:58:01 +00:00
return pStreamer - > Query ( StdBuf ( StreamingBuf . getData ( ) , iStreamAmount ) , false ) ;
}
2009-05-08 13:28:41 +00:00
bool C4Network2 : : isStreaming ( ) const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Streaming must be active and there must still be anything to stream
return fStreaming ;
2010-03-28 18:58:01 +00:00
}