2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2010-05-29 07:25:38 +00:00
2017-03-12 13:45:57 +00:00
# define _WIN32_WINNT 0x0501
2022-02-08 17:18:39 +00:00
# include <climits>
2017-03-12 13:49:18 +00:00
# include <new>
2022-02-14 23:22:52 +00:00
# include <cstdarg>
2019-02-27 19:24:31 +00:00
# include <tuple>
2010-05-29 07:25:38 +00:00
2019-04-06 00:46:56 +00:00
# include <base/hash_ctxt.h>
2010-12-11 17:55:28 +00:00
# include <base/math.h>
2010-05-29 07:25:38 +00:00
# include <base/system.h>
2020-09-26 19:41:58 +00:00
# include <base/vmath.h>
2010-05-29 07:25:38 +00:00
2014-06-05 10:11:41 +00:00
# include <game/client/components/menus.h>
2013-07-11 15:13:45 +00:00
# include <game/client/gameclient.h>
2016-09-05 15:54:35 +00:00
# include <game/editor/editor.h>
2013-07-11 15:13:45 +00:00
2011-02-27 14:03:57 +00:00
# include <engine/client.h>
# include <engine/config.h>
# include <engine/console.h>
# include <engine/editor.h>
# include <engine/engine.h>
# include <engine/graphics.h>
# include <engine/input.h>
# include <engine/keys.h>
# include <engine/map.h>
# include <engine/serverbrowser.h>
# include <engine/sound.h>
2020-08-20 10:17:44 +00:00
# include <engine/steam.h>
2011-02-27 14:03:57 +00:00
# include <engine/storage.h>
# include <engine/textrender.h>
2018-12-12 08:59:42 +00:00
# include <engine/client/http.h>
2020-04-14 15:53:53 +00:00
# include <engine/client/notifications.h>
2010-05-29 07:25:38 +00:00
# include <engine/shared/compression.h>
2020-09-26 19:41:58 +00:00
# include <engine/shared/config.h>
2011-02-27 14:03:57 +00:00
# include <engine/shared/datafile.h>
# include <engine/shared/demo.h>
2020-09-26 19:41:58 +00:00
# include <engine/shared/fifo.h>
2011-07-30 16:29:40 +00:00
# include <engine/shared/filecollection.h>
2020-01-01 19:07:04 +00:00
# include <engine/shared/json.h>
2010-05-29 07:25:38 +00:00
# include <engine/shared/network.h>
# include <engine/shared/packer.h>
# include <engine/shared/protocol.h>
2017-05-21 23:07:13 +00:00
# include <engine/shared/protocol_ex.h>
2011-02-27 14:03:57 +00:00
# include <engine/shared/ringbuffer.h>
# include <engine/shared/snapshot.h>
2017-05-21 23:07:13 +00:00
# include <engine/shared/uuid_manager.h>
2010-05-29 07:25:38 +00:00
2012-01-09 01:02:02 +00:00
# include <game/version.h>
2010-05-29 07:25:38 +00:00
# include <mastersrv/mastersrv.h>
2019-05-31 18:42:28 +00:00
# include <engine/client/demoedit.h>
2020-09-26 19:41:58 +00:00
# include <engine/client/serverbrowser.h>
2014-01-08 05:15:56 +00:00
2014-10-29 12:37:38 +00:00
# if defined(CONF_FAMILY_WINDOWS)
2020-09-26 19:41:58 +00:00
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
2014-10-29 12:37:38 +00:00
# endif
2020-09-10 18:14:47 +00:00
# include "client.h"
2011-03-23 12:06:35 +00:00
# include "friends.h"
2011-03-27 16:00:54 +00:00
# include "serverbrowser.h"
2010-05-29 07:25:38 +00:00
2016-08-27 19:10:27 +00:00
# if defined(CONF_VIDEORECORDER)
2020-09-26 19:41:58 +00:00
# include "video.h"
2016-08-27 19:10:27 +00:00
# endif
2016-08-27 15:51:23 +00:00
2014-09-20 09:36:46 +00:00
# include <zlib.h>
2012-01-03 21:01:37 +00:00
# include "SDL.h"
# ifdef main
# undef main
# endif
2010-05-29 07:25:38 +00:00
2021-08-24 10:18:20 +00:00
// for android
# include "SDL_rwops.h"
# include "base/hash.h"
2021-11-06 12:03:51 +00:00
// for msvc
# ifndef PRIu64
# define PRIu64 "I64u"
# endif
2021-03-08 03:41:27 +00:00
static const ColorRGBA ClientNetworkPrintColor { 0.7f , 1 , 0.7f , 1.0f } ;
static const ColorRGBA ClientNetworkErrPrintColor { 1.0f , 0.25f , 0.25f , 1.0f } ;
2010-05-29 07:25:38 +00:00
void CGraph : : Init ( float Min , float Max )
{
2015-11-08 15:25:14 +00:00
m_MinRange = m_Min = Min ;
m_MaxRange = m_Max = Max ;
2010-05-29 07:25:38 +00:00
m_Index = 0 ;
}
void CGraph : : ScaleMax ( )
{
int i = 0 ;
2015-11-08 15:25:14 +00:00
m_Max = m_MaxRange ;
2010-05-29 07:25:38 +00:00
for ( i = 0 ; i < MAX_VALUES ; i + + )
{
if ( m_aValues [ i ] > m_Max )
m_Max = m_aValues [ i ] ;
}
}
void CGraph : : ScaleMin ( )
{
int i = 0 ;
2015-11-08 15:25:14 +00:00
m_Min = m_MinRange ;
2010-05-29 07:25:38 +00:00
for ( i = 0 ; i < MAX_VALUES ; i + + )
{
if ( m_aValues [ i ] < m_Min )
m_Min = m_aValues [ i ] ;
}
}
void CGraph : : Add ( float v , float r , float g , float b )
{
2020-09-26 19:41:58 +00:00
m_Index = ( m_Index + 1 ) & ( MAX_VALUES - 1 ) ;
2010-05-29 07:25:38 +00:00
m_aValues [ m_Index ] = v ;
m_aColors [ m_Index ] [ 0 ] = r ;
m_aColors [ m_Index ] [ 1 ] = g ;
m_aColors [ m_Index ] [ 2 ] = b ;
}
2012-08-12 10:41:50 +00:00
void CGraph : : Render ( IGraphics * pGraphics , IGraphics : : CTextureHandle FontTexture , float x , float y , float w , float h , const char * pDescription )
2010-05-29 07:25:38 +00:00
{
//m_pGraphics->BlendNormal();
2012-08-12 10:41:50 +00:00
pGraphics - > TextureClear ( ) ;
2010-05-29 07:25:38 +00:00
pGraphics - > QuadsBegin ( ) ;
pGraphics - > SetColor ( 0 , 0 , 0 , 0.75f ) ;
IGraphics : : CQuadItem QuadItem ( x , y , w , h ) ;
pGraphics - > QuadsDrawTL ( & QuadItem , 1 ) ;
pGraphics - > QuadsEnd ( ) ;
pGraphics - > LinesBegin ( ) ;
pGraphics - > SetColor ( 0.95f , 0.95f , 0.95f , 1.00f ) ;
2020-09-26 19:41:58 +00:00
IGraphics : : CLineItem LineItem ( x , y + h / 2 , x + w , y + h / 2 ) ;
2010-05-29 07:25:38 +00:00
pGraphics - > LinesDraw ( & LineItem , 1 ) ;
pGraphics - > SetColor ( 0.5f , 0.5f , 0.5f , 0.75f ) ;
IGraphics : : CLineItem Array [ 2 ] = {
2020-09-26 19:41:58 +00:00
IGraphics : : CLineItem ( x , y + ( h * 3 ) / 4 , x + w , y + ( h * 3 ) / 4 ) ,
IGraphics : : CLineItem ( x , y + h / 4 , x + w , y + h / 4 ) } ;
2010-05-29 07:25:38 +00:00
pGraphics - > LinesDraw ( Array , 2 ) ;
for ( int i = 1 ; i < MAX_VALUES ; i + + )
{
2020-09-26 19:41:58 +00:00
float a0 = ( i - 1 ) / ( float ) MAX_VALUES ;
float a1 = i / ( float ) MAX_VALUES ;
int i0 = ( m_Index + i - 1 ) & ( MAX_VALUES - 1 ) ;
int i1 = ( m_Index + i ) & ( MAX_VALUES - 1 ) ;
2010-05-29 07:25:38 +00:00
2020-09-26 19:41:58 +00:00
float v0 = ( m_aValues [ i0 ] - m_Min ) / ( m_Max - m_Min ) ;
float v1 = ( m_aValues [ i1 ] - m_Min ) / ( m_Max - m_Min ) ;
2010-05-29 07:25:38 +00:00
2022-03-20 11:57:50 +00:00
IGraphics : : CColorVertex ArrayV [ 2 ] = {
2010-05-29 07:25:38 +00:00
IGraphics : : CColorVertex ( 0 , m_aColors [ i0 ] [ 0 ] , m_aColors [ i0 ] [ 1 ] , m_aColors [ i0 ] [ 2 ] , 0.75f ) ,
IGraphics : : CColorVertex ( 1 , m_aColors [ i1 ] [ 0 ] , m_aColors [ i1 ] [ 1 ] , m_aColors [ i1 ] [ 2 ] , 0.75f ) } ;
2022-03-20 11:57:50 +00:00
pGraphics - > SetColorVertex ( ArrayV , 2 ) ;
IGraphics : : CLineItem LineItem2 ( x + a0 * w , y + h - v0 * h , x + a1 * w , y + h - v1 * h ) ;
pGraphics - > LinesDraw ( & LineItem2 , 1 ) ;
2010-05-29 07:25:38 +00:00
}
pGraphics - > LinesEnd ( ) ;
2012-08-12 10:41:50 +00:00
pGraphics - > TextureSet ( FontTexture ) ;
2012-10-14 12:04:48 +00:00
pGraphics - > QuadsBegin ( ) ;
2020-09-26 19:41:58 +00:00
pGraphics - > QuadsText ( x + 2 , y + h - 16 , 16 , pDescription ) ;
2010-05-29 07:25:38 +00:00
char aBuf [ 32 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " %.2f " , m_Max ) ;
2020-09-26 19:41:58 +00:00
pGraphics - > QuadsText ( x + w - 8 * str_length ( aBuf ) - 8 , y + 2 , 16 , aBuf ) ;
2010-05-29 07:25:38 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " %.2f " , m_Min ) ;
2020-09-26 19:41:58 +00:00
pGraphics - > QuadsText ( x + w - 8 * str_length ( aBuf ) - 8 , y + h - 16 , 16 , aBuf ) ;
2012-10-14 12:04:48 +00:00
pGraphics - > QuadsEnd ( ) ;
2010-05-29 07:25:38 +00:00
}
2021-06-23 05:05:49 +00:00
void CSmoothTime : : Init ( int64_t Target )
2010-05-29 07:25:38 +00:00
{
m_Snap = time_get ( ) ;
m_Current = Target ;
m_Target = Target ;
2022-01-09 12:06:41 +00:00
m_SnapMargin = m_Snap ;
m_CurrentMargin = 0 ;
m_TargetMargin = 0 ;
2010-05-29 07:25:38 +00:00
m_aAdjustSpeed [ 0 ] = 0.3f ;
m_aAdjustSpeed [ 1 ] = 0.3f ;
m_Graph . Init ( 0.0f , 0.5f ) ;
}
void CSmoothTime : : SetAdjustSpeed ( int Direction , float Value )
{
m_aAdjustSpeed [ Direction ] = Value ;
}
2021-06-23 05:05:49 +00:00
int64_t CSmoothTime : : Get ( int64_t Now )
2010-05-29 07:25:38 +00:00
{
2021-06-23 05:05:49 +00:00
int64_t c = m_Current + ( Now - m_Snap ) ;
int64_t t = m_Target + ( Now - m_Snap ) ;
2010-05-29 07:25:38 +00:00
// it's faster to adjust upward instead of downward
// we might need to adjust these abit
float AdjustSpeed = m_aAdjustSpeed [ 0 ] ;
if ( t > c )
AdjustSpeed = m_aAdjustSpeed [ 1 ] ;
2020-09-26 19:41:58 +00:00
float a = ( ( Now - m_Snap ) / ( float ) time_freq ( ) ) * AdjustSpeed ;
2010-05-29 07:25:38 +00:00
if ( a > 1.0f )
a = 1.0f ;
2021-06-23 05:05:49 +00:00
int64_t r = c + ( int64_t ) ( ( t - c ) * a ) ;
2010-05-29 07:25:38 +00:00
2020-09-26 19:41:58 +00:00
m_Graph . Add ( a + 0.5f , 1 , 1 , 1 ) ;
2010-05-29 07:25:38 +00:00
2022-01-09 12:06:41 +00:00
return r + GetMargin ( Now ) ;
2010-05-29 07:25:38 +00:00
}
2021-06-23 05:05:49 +00:00
void CSmoothTime : : UpdateInt ( int64_t Target )
2010-05-29 07:25:38 +00:00
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2022-01-09 12:06:41 +00:00
m_Current = Get ( Now ) - GetMargin ( Now ) ;
2010-05-29 07:25:38 +00:00
m_Snap = Now ;
2022-01-09 12:06:41 +00:00
m_Target = Target - GetMargin ( Now ) ;
2010-05-29 07:25:38 +00:00
}
2021-06-23 05:05:49 +00:00
void CSmoothTime : : Update ( CGraph * pGraph , int64_t Target , int TimeLeft , int AdjustDirection )
2010-05-29 07:25:38 +00:00
{
int UpdateTimer = 1 ;
if ( TimeLeft < 0 )
{
int IsSpike = 0 ;
if ( TimeLeft < - 50 )
{
IsSpike = 1 ;
m_SpikeCounter + = 5 ;
if ( m_SpikeCounter > 50 )
m_SpikeCounter = 50 ;
}
if ( IsSpike & & m_SpikeCounter < 15 )
{
// ignore this ping spike
UpdateTimer = 0 ;
2020-09-26 19:41:58 +00:00
pGraph - > Add ( TimeLeft , 1 , 1 , 0 ) ;
2010-05-29 07:25:38 +00:00
}
else
{
2020-09-26 19:41:58 +00:00
pGraph - > Add ( TimeLeft , 1 , 0 , 0 ) ;
2010-05-29 07:25:38 +00:00
if ( m_aAdjustSpeed [ AdjustDirection ] < 30.0f )
m_aAdjustSpeed [ AdjustDirection ] * = 2.0f ;
}
}
else
{
if ( m_SpikeCounter )
m_SpikeCounter - - ;
2020-09-26 19:41:58 +00:00
pGraph - > Add ( TimeLeft , 0 , 1 , 0 ) ;
2010-05-29 07:25:38 +00:00
m_aAdjustSpeed [ AdjustDirection ] * = 0.95f ;
if ( m_aAdjustSpeed [ AdjustDirection ] < 2.0f )
m_aAdjustSpeed [ AdjustDirection ] = 2.0f ;
}
if ( UpdateTimer )
UpdateInt ( Target ) ;
}
2022-01-09 12:06:41 +00:00
int64_t CSmoothTime : : GetMargin ( int64_t Now )
{
int64_t TimePassed = Now - m_SnapMargin ;
int64_t Diff = m_TargetMargin - m_CurrentMargin ;
float a = clamp ( TimePassed / ( float ) time_freq ( ) , - 1.f , 1.f ) ;
int64_t Lim = maximum ( ( int64_t ) ( a * absolute ( Diff ) ) , 1 + TimePassed / 100 ) ;
return m_CurrentMargin + ( int64_t ) clamp ( Diff , - Lim , Lim ) ;
}
void CSmoothTime : : UpdateMargin ( int64_t TargetMargin )
{
int64_t Now = time_get ( ) ;
m_CurrentMargin = GetMargin ( Now ) ;
m_SnapMargin = Now ;
m_TargetMargin = TargetMargin ;
}
2020-09-26 19:41:58 +00:00
CClient : : CClient ( ) :
2021-07-19 18:14:12 +00:00
m_DemoPlayer ( & m_SnapshotDelta , [ & ] ( ) { UpdateDemoIntraTimers ( ) ; } )
2010-05-29 07:25:38 +00:00
{
2020-10-26 14:14:07 +00:00
for ( auto & DemoRecorder : m_DemoRecorder )
DemoRecorder = CDemoRecorder ( & m_SnapshotDelta ) ;
2014-10-16 15:42:13 +00:00
2010-05-29 07:25:38 +00:00
m_pEditor = 0 ;
m_pInput = 0 ;
m_pGraphics = 0 ;
m_pSound = 0 ;
m_pGameClient = 0 ;
m_pMap = 0 ;
2021-01-10 12:47:07 +00:00
m_pConfigManager = 0 ;
m_pConfig = 0 ;
2010-05-29 07:25:38 +00:00
m_pConsole = 0 ;
2012-01-01 12:38:46 +00:00
m_RenderFrameTime = 0.0001f ;
m_RenderFrameTimeLow = 1.0f ;
m_RenderFrameTimeHigh = 0.0f ;
m_RenderFrames = 0 ;
m_LastRenderTime = time_get ( ) ;
2010-05-29 07:25:38 +00:00
m_GameTickSpeed = SERVER_TICK_SPEED ;
m_SnapCrcErrors = 0 ;
2010-12-12 15:48:13 +00:00
m_AutoScreenshotRecycle = false ;
2015-05-19 22:51:02 +00:00
m_AutoStatScreenshotRecycle = false ;
2017-04-26 03:10:31 +00:00
m_AutoCSVRecycle = false ;
2011-01-17 11:28:37 +00:00
m_EditorActive = false ;
2010-05-29 07:25:38 +00:00
2014-04-28 15:26:31 +00:00
m_AckGameTick [ 0 ] = - 1 ;
m_AckGameTick [ 1 ] = - 1 ;
m_CurrentRecvTick [ 0 ] = 0 ;
m_CurrentRecvTick [ 1 ] = 0 ;
2014-04-27 11:44:04 +00:00
m_RconAuthed [ 0 ] = 0 ;
m_RconAuthed [ 1 ] = 0 ;
2014-08-22 11:54:13 +00:00
m_RconPassword [ 0 ] = ' \0 ' ;
2017-07-15 15:29:20 +00:00
m_Password [ 0 ] = ' \0 ' ;
2010-05-29 07:25:38 +00:00
// version-checking
m_aVersionStr [ 0 ] = ' 0 ' ;
2017-09-03 15:36:51 +00:00
m_aVersionStr [ 1 ] = ' \0 ' ;
2010-05-29 07:25:38 +00:00
// pinging
m_PingStartTime = 0 ;
m_aCurrentMap [ 0 ] = 0 ;
m_aCmdConnect [ 0 ] = 0 ;
// map download
m_aMapdownloadFilename [ 0 ] = 0 ;
2020-04-14 10:00:20 +00:00
m_aMapdownloadFilenameTemp [ 0 ] = 0 ;
2010-05-29 07:25:38 +00:00
m_aMapdownloadName [ 0 ] = 0 ;
2017-04-11 23:20:39 +00:00
m_pMapdownloadTask = NULL ;
2020-04-14 10:00:20 +00:00
m_MapdownloadFileTemp = 0 ;
2010-05-29 07:25:38 +00:00
m_MapdownloadChunk = 0 ;
2018-06-05 19:22:40 +00:00
m_MapdownloadSha256Present = false ;
m_MapdownloadSha256 = SHA256_ZEROED ;
2010-05-29 07:25:38 +00:00
m_MapdownloadCrc = 0 ;
m_MapdownloadAmount = - 1 ;
m_MapdownloadTotalsize = - 1 ;
2017-09-03 15:36:51 +00:00
2018-06-05 19:22:40 +00:00
m_MapDetailsPresent = false ;
m_aMapDetailsName [ 0 ] = 0 ;
m_MapDetailsSha256 = SHA256_ZEROED ;
m_MapDetailsCrc = 0 ;
2021-12-20 14:00:21 +00:00
IStorage : : FormatTmpPath ( m_aDDNetInfoTmp , sizeof ( m_aDDNetInfoTmp ) , DDNET_INFO ) ;
2017-09-03 15:36:51 +00:00
m_pDDNetInfoTask = NULL ;
m_aNews [ 0 ] = ' \0 ' ;
2021-01-31 10:15:09 +00:00
m_aMapDownloadUrl [ 0 ] = ' \0 ' ;
2020-10-23 21:25:58 +00:00
m_Points = - 1 ;
2010-05-29 07:25:38 +00:00
m_CurrentServerInfoRequestTime = - 1 ;
2021-04-23 21:12:16 +00:00
m_CurrentServerPingInfoType = - 1 ;
m_CurrentServerPingBasicToken = - 1 ;
m_CurrentServerPingToken = - 1 ;
2021-04-24 12:23:35 +00:00
mem_zero ( & m_CurrentServerPingUuid , sizeof ( m_CurrentServerPingUuid ) ) ;
2021-04-23 21:12:16 +00:00
m_CurrentServerCurrentPingTime = - 1 ;
m_CurrentServerNextPingTime = - 1 ;
2010-05-29 07:25:38 +00:00
2014-10-12 15:52:53 +00:00
m_CurrentInput [ 0 ] = 0 ;
m_CurrentInput [ 1 ] = 0 ;
2022-02-14 23:12:52 +00:00
m_LastDummy = false ;
2014-05-07 13:24:53 +00:00
2014-07-16 12:45:53 +00:00
mem_zero ( & m_aInputs , sizeof ( m_aInputs ) ) ;
2010-05-29 07:25:38 +00:00
m_State = IClient : : STATE_OFFLINE ;
m_aServerAddressStr [ 0 ] = 0 ;
mem_zero ( m_aSnapshots , sizeof ( m_aSnapshots ) ) ;
2014-05-03 18:24:45 +00:00
m_SnapshotStorage [ 0 ] . Init ( ) ;
m_SnapshotStorage [ 1 ] . Init ( ) ;
2016-01-23 20:44:45 +00:00
m_ReceivedSnapshots [ 0 ] = 0 ;
m_ReceivedSnapshots [ 1 ] = 0 ;
2019-04-02 20:02:55 +00:00
m_SnapshotParts [ 0 ] = 0 ;
m_SnapshotParts [ 1 ] = 0 ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
m_VersionInfo . m_State = CVersionInfo : : STATE_INIT ;
2014-05-17 17:06:33 +00:00
2018-03-12 14:43:31 +00:00
if ( g_Config . m_ClDummy = = 0 )
2014-07-25 00:41:36 +00:00
m_LastDummyConnectTime = 0 ;
2014-09-20 09:36:46 +00:00
2016-05-04 16:23:00 +00:00
m_ReconnectTime = 0 ;
2016-10-02 09:31:11 +00:00
m_GenerateTimeoutSeed = true ;
2019-04-11 22:46:54 +00:00
m_FrameTimeAvg = 0.0001f ;
2020-11-19 15:15:07 +00:00
m_BenchmarkFile = 0 ;
m_BenchmarkStopTime = 0 ;
2022-01-31 02:11:47 +00:00
mem_zero ( & m_Checksum , sizeof ( m_Checksum ) ) ;
2010-05-29 07:25:38 +00:00
}
// ----- send functions -----
2020-04-13 13:34:53 +00:00
static inline bool RepackMsg ( const CMsgPacker * pMsg , CPacker & Packer )
{
Packer . Reset ( ) ;
if ( pMsg - > m_MsgID < OFFSET_UUID )
{
2020-09-26 19:41:58 +00:00
Packer . AddInt ( ( pMsg - > m_MsgID < < 1 ) | ( pMsg - > m_System ? 1 : 0 ) ) ;
2020-04-13 13:34:53 +00:00
}
else
{
2020-09-26 19:41:58 +00:00
Packer . AddInt ( ( 0 < < 1 ) | ( pMsg - > m_System ? 1 : 0 ) ) ; // NETMSG_EX, NETMSGTYPE_EX
2020-04-13 13:34:53 +00:00
g_UuidManager . PackUuid ( pMsg - > m_MsgID , & Packer ) ;
}
Packer . AddRaw ( pMsg - > Data ( ) , pMsg - > Size ( ) ) ;
return false ;
}
2022-01-21 21:13:35 +00:00
int CClient : : SendMsg ( int Conn , CMsgPacker * pMsg , int Flags )
2010-05-29 07:25:38 +00:00
{
CNetChunk Packet ;
if ( State ( ) = = IClient : : STATE_OFFLINE )
return 0 ;
2020-04-13 13:34:53 +00:00
// repack message (inefficient)
CPacker Pack ;
if ( RepackMsg ( pMsg , Pack ) )
return 0 ;
2010-05-29 07:25:38 +00:00
mem_zero ( & Packet , sizeof ( CNetChunk ) ) ;
Packet . m_ClientID = 0 ;
2020-04-13 13:34:53 +00:00
Packet . m_pData = Pack . Data ( ) ;
Packet . m_DataSize = Pack . Size ( ) ;
2010-05-29 07:25:38 +00:00
2020-09-26 19:41:58 +00:00
if ( Flags & MSGFLAG_VITAL )
2010-05-29 07:25:38 +00:00
Packet . m_Flags | = NETSENDFLAG_VITAL ;
2020-09-26 19:41:58 +00:00
if ( Flags & MSGFLAG_FLUSH )
2010-05-29 07:25:38 +00:00
Packet . m_Flags | = NETSENDFLAG_FLUSH ;
2022-01-21 21:13:35 +00:00
if ( ( Flags & MSGFLAG_RECORD ) & & Conn = = g_Config . m_ClDummy )
2010-05-29 07:25:38 +00:00
{
2020-10-26 13:11:11 +00:00
for ( auto & i : m_DemoRecorder )
if ( i . IsRecording ( ) )
i . RecordMessage ( Packet . m_pData , Packet . m_DataSize ) ;
2010-05-29 07:25:38 +00:00
}
2020-09-26 19:41:58 +00:00
if ( ! ( Flags & MSGFLAG_NOSEND ) )
2014-04-26 18:29:42 +00:00
{
2022-01-21 21:13:35 +00:00
m_NetClient [ Conn ] . Send ( & Packet ) ;
2014-04-26 18:29:42 +00:00
}
2010-05-29 07:25:38 +00:00
return 0 ;
}
2022-01-21 00:54:14 +00:00
int CClient : : SendMsgActive ( CMsgPacker * pMsg , int Flags )
{
return SendMsg ( g_Config . m_ClDummy , pMsg , Flags ) ;
}
2010-05-29 07:25:38 +00:00
void CClient : : SendInfo ( )
{
2020-05-22 15:58:41 +00:00
CMsgPacker MsgVer ( NETMSG_CLIENTVER , true ) ;
MsgVer . AddRaw ( & m_ConnectionID , sizeof ( m_ConnectionID ) ) ;
MsgVer . AddInt ( GameClient ( ) - > DDNetVersion ( ) ) ;
MsgVer . AddString ( GameClient ( ) - > DDNetVersionStr ( ) , 0 ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_MAIN , & MsgVer , MSGFLAG_VITAL ) ;
2020-05-22 15:58:41 +00:00
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_INFO , true ) ;
2010-05-29 07:25:38 +00:00
Msg . AddString ( GameClient ( ) - > NetVersion ( ) , 128 ) ;
2017-07-15 15:29:20 +00:00
Msg . AddString ( m_Password , 128 ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_MAIN , & Msg , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2010-05-29 07:25:38 +00:00
}
2022-01-21 21:13:35 +00:00
void CClient : : SendEnterGame ( int Conn )
2010-05-29 07:25:38 +00:00
{
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_ENTERGAME , true ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( Conn , & Msg , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : SendReady ( )
{
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_READY , true ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_MAIN , & Msg , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2010-05-29 07:25:38 +00:00
}
2014-10-29 12:37:38 +00:00
void CClient : : SendMapRequest ( )
{
2020-04-14 10:00:20 +00:00
if ( m_MapdownloadFileTemp )
2020-04-13 21:23:05 +00:00
{
2020-04-14 10:00:20 +00:00
io_close ( m_MapdownloadFileTemp ) ;
Storage ( ) - > RemoveFile ( m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE ) ;
2020-04-13 21:23:05 +00:00
}
2020-04-14 10:00:20 +00:00
m_MapdownloadFileTemp = Storage ( ) - > OpenFile ( m_aMapdownloadFilenameTemp , IOFLAG_WRITE , IStorage : : TYPE_SAVE ) ;
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_REQUEST_MAP_DATA , true ) ;
2014-10-29 12:37:38 +00:00
Msg . AddInt ( m_MapdownloadChunk ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_MAIN , & Msg , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2014-10-29 12:37:38 +00:00
}
2010-05-29 07:25:38 +00:00
void CClient : : RconAuth ( const char * pName , const char * pPassword )
{
2014-04-27 11:44:04 +00:00
if ( RconAuthed ( ) )
return ;
2011-04-13 18:37:12 +00:00
2017-06-02 16:37:29 +00:00
if ( pPassword ! = m_RconPassword )
str_copy ( m_RconPassword , pPassword , sizeof ( m_RconPassword ) ) ;
2014-08-22 11:54:13 +00:00
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_RCON_AUTH , true ) ;
2010-05-29 07:25:38 +00:00
Msg . AddString ( pName , 32 ) ;
Msg . AddString ( pPassword , 32 ) ;
2011-07-14 20:07:21 +00:00
Msg . AddInt ( 1 ) ;
2022-01-21 00:54:14 +00:00
SendMsgActive ( & Msg , MSGFLAG_VITAL ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : Rcon ( const char * pCmd )
{
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_RCON_CMD , true ) ;
2010-05-29 07:25:38 +00:00
Msg . AddString ( pCmd , 256 ) ;
2022-01-21 00:54:14 +00:00
SendMsgActive ( & Msg , MSGFLAG_VITAL ) ;
2010-05-29 07:25:38 +00:00
}
2021-02-08 21:26:26 +00:00
bool CClient : : ConnectionProblems ( ) const
2010-05-29 07:25:38 +00:00
{
2022-01-09 12:06:41 +00:00
return m_NetClient [ g_Config . m_ClDummy ] . GotProblems ( MaxLatencyTicks ( ) * time_freq ( ) / SERVER_TICK_SPEED ) ! = 0 ;
2010-05-29 07:25:38 +00:00
}
void CClient : : DirectInput ( int * pInput , int Size )
{
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_INPUT , true ) ;
2014-04-28 15:26:31 +00:00
Msg . AddInt ( m_AckGameTick [ g_Config . m_ClDummy ] ) ;
Msg . AddInt ( m_PredTick [ g_Config . m_ClDummy ] ) ;
2010-05-29 07:25:38 +00:00
Msg . AddInt ( Size ) ;
2020-09-26 19:41:58 +00:00
for ( int i = 0 ; i < Size / 4 ; i + + )
2010-05-29 07:25:38 +00:00
Msg . AddInt ( pInput [ i ] ) ;
2022-01-21 00:54:14 +00:00
SendMsgActive ( & Msg , 0 ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : SendInput ( )
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2010-05-29 07:25:38 +00:00
2014-04-28 15:26:31 +00:00
if ( m_PredTick [ g_Config . m_ClDummy ] < = 0 )
2010-05-29 07:25:38 +00:00
return ;
2017-02-28 09:08:14 +00:00
bool Force = false ;
// fetch input
2020-10-18 14:51:26 +00:00
for ( int Dummy = 0 ; Dummy < NUM_DUMMIES ; Dummy + + )
2014-05-10 18:23:26 +00:00
{
2017-02-28 09:08:14 +00:00
if ( ! m_DummyConnected & & Dummy ! = 0 )
2014-07-07 23:41:45 +00:00
{
2017-02-28 09:08:14 +00:00
break ;
2014-07-07 23:41:45 +00:00
}
2017-02-28 09:08:14 +00:00
int i = g_Config . m_ClDummy ^ Dummy ;
int Size = GameClient ( ) - > OnSnapInput ( m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_aData , Dummy , Force ) ;
2014-04-28 18:43:08 +00:00
2017-02-28 09:08:14 +00:00
if ( Size )
2014-04-28 20:12:50 +00:00
{
// pack input
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_INPUT , true ) ;
2017-02-28 09:08:14 +00:00
Msg . AddInt ( m_AckGameTick [ i ] ) ;
2019-04-13 22:19:10 +00:00
Msg . AddInt ( m_PredTick [ i ] ) ;
2017-02-28 09:08:14 +00:00
Msg . AddInt ( Size ) ;
2014-04-28 20:12:50 +00:00
2020-04-18 20:16:25 +00:00
m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_Tick = m_PredTick [ g_Config . m_ClDummy ] ;
2017-02-28 09:08:14 +00:00
m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_PredictedTime = m_PredictedTime . Get ( Now ) ;
2022-01-09 12:06:41 +00:00
m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_PredictionMargin = m_PredictedTime . GetMargin ( Now ) ;
2017-02-28 09:08:14 +00:00
m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_Time = Now ;
2017-02-28 07:15:35 +00:00
// pack it
2020-09-26 19:41:58 +00:00
for ( int k = 0 ; k < Size / 4 ; k + + )
2017-02-28 09:08:14 +00:00
Msg . AddInt ( m_aInputs [ i ] [ m_CurrentInput [ i ] ] . m_aData [ k ] ) ;
2017-02-28 07:15:35 +00:00
2017-02-28 09:08:14 +00:00
m_CurrentInput [ i ] + + ;
m_CurrentInput [ i ] % = 200 ;
2022-01-21 00:54:14 +00:00
SendMsg ( i , & Msg , MSGFLAG_FLUSH ) ;
2017-07-15 16:59:33 +00:00
// ugly workaround for dummy. we need to send input with dummy to prevent
// prediction time resets. but if we do it too often, then it's
// impossible to use grenade with frozen dummy that gets hammered...
if ( g_Config . m_ClDummyCopyMoves | | m_CurrentInput [ i ] % 2 )
Force = true ;
2014-04-28 20:12:50 +00:00
}
}
2010-05-29 07:25:38 +00:00
}
2021-02-08 21:26:26 +00:00
const char * CClient : : LatestVersion ( ) const
2010-05-29 07:25:38 +00:00
{
return m_aVersionStr ;
}
2018-02-04 15:00:47 +00:00
// TODO: OPT: do this a lot smarter!
2021-02-08 21:26:26 +00:00
int * CClient : : GetInput ( int Tick , int IsDummy ) const
2010-05-29 07:25:38 +00:00
{
int Best = - 1 ;
2020-04-18 20:16:25 +00:00
const int d = IsDummy ^ g_Config . m_ClDummy ;
2010-05-29 07:25:38 +00:00
for ( int i = 0 ; i < 200 ; i + + )
{
2020-04-18 20:16:25 +00:00
if ( m_aInputs [ d ] [ i ] . m_Tick < = Tick & & ( Best = = - 1 | | m_aInputs [ d ] [ Best ] . m_Tick < m_aInputs [ d ] [ i ] . m_Tick ) )
2010-05-29 07:25:38 +00:00
Best = i ;
}
if ( Best ! = - 1 )
2014-10-12 15:52:53 +00:00
return ( int * ) m_aInputs [ g_Config . m_ClDummy ] [ Best ] . m_aData ;
2010-05-29 07:25:38 +00:00
return 0 ;
}
2021-02-08 21:26:26 +00:00
int * CClient : : GetDirectInput ( int Tick , int IsDummy ) const
2014-12-01 00:31:58 +00:00
{
2020-04-18 20:16:25 +00:00
const int d = IsDummy ^ g_Config . m_ClDummy ;
2014-12-01 00:31:58 +00:00
for ( int i = 0 ; i < 200 ; i + + )
2020-04-18 20:16:25 +00:00
if ( m_aInputs [ d ] [ i ] . m_Tick = = Tick )
return ( int * ) m_aInputs [ d ] [ i ] . m_aData ;
2019-04-11 22:46:54 +00:00
return 0 ;
2014-12-01 00:31:58 +00:00
}
2010-05-29 07:25:38 +00:00
// ------ state handling -----
void CClient : : SetState ( int s )
{
2020-10-02 13:44:27 +00:00
if ( m_State = = IClient : : STATE_QUITTING | | m_State = = IClient : : STATE_RESTARTING )
2012-01-06 19:03:57 +00:00
return ;
2010-05-29 07:25:38 +00:00
int Old = m_State ;
if ( g_Config . m_Debug )
2010-08-17 22:06:00 +00:00
{
char aBuf [ 128 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " state change. last=%d current=%d " , m_State , s ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client " , aBuf ) ;
}
2010-05-29 07:25:38 +00:00
m_State = s ;
if ( Old ! = s )
2016-05-04 16:23:00 +00:00
{
2010-05-29 07:25:38 +00:00
GameClient ( ) - > OnStateChange ( m_State , Old ) ;
2016-05-04 16:23:00 +00:00
if ( s = = IClient : : STATE_OFFLINE & & m_ReconnectTime = = 0 )
{
if ( g_Config . m_ClReconnectFull > 0 & & ( str_find_nocase ( ErrorString ( ) , " full " ) | | str_find_nocase ( ErrorString ( ) , " reserved " ) ) )
m_ReconnectTime = time_get ( ) + time_freq ( ) * g_Config . m_ClReconnectFull ;
2016-06-19 00:17:23 +00:00
else if ( g_Config . m_ClReconnectTimeout > 0 & & ( str_find_nocase ( ErrorString ( ) , " Timeout " ) | | str_find_nocase ( ErrorString ( ) , " Too weak connection " ) ) )
2016-05-04 16:23:00 +00:00
m_ReconnectTime = time_get ( ) + time_freq ( ) * g_Config . m_ClReconnectTimeout ;
}
2020-09-01 22:32:22 +00:00
if ( s = = IClient : : STATE_ONLINE )
{
2021-01-17 23:52:58 +00:00
Discord ( ) - > SetGameInfo ( m_ServerAddress , m_aCurrentMap ) ;
2020-09-01 22:32:22 +00:00
Steam ( ) - > SetGameInfo ( m_ServerAddress , m_aCurrentMap ) ;
}
else if ( Old = = IClient : : STATE_ONLINE )
{
2021-01-17 23:52:58 +00:00
Discord ( ) - > ClearGameInfo ( ) ;
2020-09-01 22:32:22 +00:00
Steam ( ) - > ClearGameInfo ( ) ;
}
2016-05-04 16:23:00 +00:00
}
2010-05-29 07:25:38 +00:00
}
// called when the map is loaded and we should init for a new round
2021-12-20 22:17:09 +00:00
void CClient : : OnEnterGame ( bool Dummy )
2010-05-29 07:25:38 +00:00
{
// reset input
int i ;
for ( i = 0 ; i < 200 ; i + + )
2014-10-12 15:52:53 +00:00
{
2021-12-20 22:17:09 +00:00
m_aInputs [ Dummy ] [ i ] . m_Tick = - 1 ;
2014-10-12 15:52:53 +00:00
}
2021-12-20 22:17:09 +00:00
m_CurrentInput [ Dummy ] = 0 ;
2010-05-29 07:25:38 +00:00
// reset snapshots
2021-12-20 22:17:09 +00:00
m_aSnapshots [ Dummy ] [ SNAP_CURRENT ] = 0 ;
m_aSnapshots [ Dummy ] [ SNAP_PREV ] = 0 ;
m_SnapshotStorage [ Dummy ] . PurgeAll ( ) ;
m_ReceivedSnapshots [ Dummy ] = 0 ;
m_SnapshotParts [ Dummy ] = 0 ;
m_PredTick [ Dummy ] = 0 ;
m_CurrentRecvTick [ Dummy ] = 0 ;
m_CurGameTick [ Dummy ] = 0 ;
m_PrevGameTick [ Dummy ] = 0 ;
if ( Dummy = = 0 )
{
2014-07-25 00:41:36 +00:00
m_LastDummyConnectTime = 0 ;
2021-12-20 22:17:09 +00:00
}
2022-01-07 23:52:32 +00:00
GameClient ( ) - > OnEnterGame ( ) ;
2010-05-29 07:25:38 +00:00
}
2022-01-21 21:13:35 +00:00
void CClient : : EnterGame ( int Conn )
2010-05-29 07:25:38 +00:00
{
if ( State ( ) = = IClient : : STATE_DEMOPLAYBACK )
return ;
2022-01-21 21:13:35 +00:00
m_CodeRunAfterJoin [ Conn ] = false ;
2021-12-20 22:17:09 +00:00
2010-05-29 07:25:38 +00:00
// now we will wait for two snapshots
// to finish the connection
2022-01-21 21:13:35 +00:00
SendEnterGame ( Conn ) ;
OnEnterGame ( Conn ) ;
2014-08-17 17:10:08 +00:00
ServerInfoRequest ( ) ; // fresh one for timeout protection
2021-04-23 21:12:16 +00:00
m_CurrentServerNextPingTime = time_get ( ) + time_freq ( ) / 2 ;
2016-10-02 09:31:11 +00:00
}
void GenerateTimeoutCode ( char * pBuffer , unsigned Size , char * pSeed , const NETADDR & Addr , bool Dummy )
{
2019-04-06 00:46:56 +00:00
MD5_CTX Md5 ;
2016-10-02 09:31:11 +00:00
md5_init ( & Md5 ) ;
const char * pDummy = Dummy ? " dummy " : " normal " ;
2019-04-06 00:46:56 +00:00
md5_update ( & Md5 , ( unsigned char * ) pDummy , str_length ( pDummy ) + 1 ) ;
md5_update ( & Md5 , ( unsigned char * ) pSeed , str_length ( pSeed ) + 1 ) ;
md5_update ( & Md5 , ( unsigned char * ) & Addr , sizeof ( Addr ) ) ;
MD5_DIGEST Digest = md5_finish ( & Md5 ) ;
2016-10-02 09:31:11 +00:00
unsigned short Random [ 8 ] ;
2019-04-06 00:46:56 +00:00
mem_copy ( Random , Digest . data , sizeof ( Random ) ) ;
2016-10-02 09:31:11 +00:00
generate_password ( pBuffer , Size , Random , 8 ) ;
}
void CClient : : GenerateTimeoutSeed ( )
{
2016-10-03 11:56:15 +00:00
secure_random_password ( g_Config . m_ClTimeoutSeed , sizeof ( g_Config . m_ClTimeoutSeed ) , 16 ) ;
2016-10-02 09:31:11 +00:00
}
void CClient : : GenerateTimeoutCodes ( )
{
if ( g_Config . m_ClTimeoutSeed [ 0 ] )
{
for ( int i = 0 ; i < 2 ; i + + )
{
GenerateTimeoutCode ( m_aTimeoutCodes [ i ] , sizeof ( m_aTimeoutCodes [ i ] ) , g_Config . m_ClTimeoutSeed , m_ServerAddress , i ) ;
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " timeout code '%s' (%s) " , m_aTimeoutCodes [ i ] , i = = 0 ? " normal " : " dummy " ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , aBuf ) ;
}
}
else
{
str_copy ( m_aTimeoutCodes [ 0 ] , g_Config . m_ClTimeoutCode , sizeof ( m_aTimeoutCodes [ 0 ] ) ) ;
2016-10-03 10:31:11 +00:00
str_copy ( m_aTimeoutCodes [ 1 ] , g_Config . m_ClDummyTimeoutCode , sizeof ( m_aTimeoutCodes [ 1 ] ) ) ;
2016-10-02 09:31:11 +00:00
}
2010-05-29 07:25:38 +00:00
}
2017-07-15 15:29:20 +00:00
void CClient : : Connect ( const char * pAddress , const char * pPassword )
2010-05-29 07:25:38 +00:00
{
char aBuf [ 512 ] ;
int Port = 8303 ;
Disconnect ( ) ;
2020-05-22 15:58:41 +00:00
m_ConnectionID = RandomUuid ( ) ;
2020-09-25 13:43:00 +00:00
if ( pAddress ! = m_aServerAddressStr )
str_copy ( m_aServerAddressStr , pAddress , sizeof ( m_aServerAddressStr ) ) ;
2010-05-29 07:25:38 +00:00
2010-08-17 22:06:00 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " connecting to '%s' " , m_aServerAddressStr ) ;
2021-03-08 03:41:27 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf , ClientNetworkPrintColor ) ;
2020-09-12 15:12:00 +00:00
bool is_websocket = false ;
if ( strncmp ( m_aServerAddressStr , " ws:// " , 5 ) = = 0 )
{
is_websocket = true ;
str_copy ( m_aServerAddressStr , pAddress + 5 , sizeof ( m_aServerAddressStr ) ) ;
}
2010-05-29 07:25:38 +00:00
ServerInfoRequest ( ) ;
2022-01-21 21:13:35 +00:00
if ( net_host_lookup ( m_aServerAddressStr , & m_ServerAddress , m_NetClient [ CONN_MAIN ] . NetType ( ) ) ! = 0 )
2010-07-02 11:15:35 +00:00
{
2014-04-26 19:10:39 +00:00
char aBufMsg [ 256 ] ;
str_format ( aBufMsg , sizeof ( aBufMsg ) , " could not find the address of %s, connecting to localhost " , aBuf ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBufMsg ) ;
2022-01-21 21:13:35 +00:00
net_host_lookup ( " localhost " , & m_ServerAddress , m_NetClient [ CONN_MAIN ] . NetType ( ) ) ;
2010-07-02 11:15:35 +00:00
}
2010-05-29 07:25:38 +00:00
2018-06-15 23:41:55 +00:00
if ( m_SendPassword )
{
str_copy ( m_Password , g_Config . m_Password , sizeof ( m_Password ) ) ;
m_SendPassword = false ;
}
else if ( ! pPassword )
2017-07-15 15:29:20 +00:00
m_Password [ 0 ] = 0 ;
else
str_copy ( m_Password , pPassword , sizeof ( m_Password ) ) ;
2021-04-24 12:12:15 +00:00
m_CanReceiveServerCapabilities = true ;
2019-04-10 21:30:01 +00:00
// Deregister Rcon commands from last connected server, might not have called
// DisconnectWithReason if the server was shut down
2014-04-27 11:44:04 +00:00
m_RconAuthed [ 0 ] = 0 ;
2019-04-10 21:30:01 +00:00
m_UseTempRconCommands = 0 ;
m_pConsole - > DeregisterTempAll ( ) ;
2011-03-28 18:11:28 +00:00
if ( m_ServerAddress . port = = 0 )
m_ServerAddress . port = Port ;
2020-09-12 15:12:00 +00:00
if ( is_websocket )
{
m_ServerAddress . type = NETTYPE_WEBSOCKET_IPV4 ;
}
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_MAIN ] . Connect ( & m_ServerAddress ) ;
2010-05-29 07:25:38 +00:00
SetState ( IClient : : STATE_CONNECTING ) ;
2014-10-16 15:42:13 +00:00
for ( int i = 0 ; i < RECORDER_MAX ; i + + )
if ( m_DemoRecorder [ i ] . IsRecording ( ) )
DemoRecorder_Stop ( i ) ;
2010-05-29 07:25:38 +00:00
m_InputtimeMarginGraph . Init ( - 150.0f , 150.0f ) ;
m_GametimeMarginGraph . Init ( - 150.0f , 150.0f ) ;
2016-10-02 09:31:11 +00:00
GenerateTimeoutCodes ( ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : DisconnectWithReason ( const char * pReason )
{
2010-08-17 22:06:00 +00:00
char aBuf [ 512 ] ;
2020-09-26 19:41:58 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " disconnecting. reason='%s' " , pReason ? pReason : " unknown " ) ;
2021-03-08 03:41:27 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf , ClientNetworkPrintColor ) ;
2010-08-17 22:06:00 +00:00
2010-05-29 07:25:38 +00:00
// stop demo playback and recorder
m_DemoPlayer . Stop ( ) ;
2014-10-16 15:42:13 +00:00
for ( int i = 0 ; i < RECORDER_MAX ; i + + )
DemoRecorder_Stop ( i ) ;
2010-05-29 07:25:38 +00:00
//
2014-04-27 11:44:04 +00:00
m_RconAuthed [ 0 ] = 0 ;
2019-06-03 19:52:14 +00:00
m_ServerSentCapabilities = false ;
2011-12-30 18:21:00 +00:00
m_UseTempRconCommands = 0 ;
2011-07-14 20:07:21 +00:00
m_pConsole - > DeregisterTempAll ( ) ;
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_MAIN ] . Disconnect ( pReason ) ;
2010-05-29 07:25:38 +00:00
SetState ( IClient : : STATE_OFFLINE ) ;
m_pMap - > Unload ( ) ;
2021-04-23 21:12:16 +00:00
m_CurrentServerPingInfoType = - 1 ;
m_CurrentServerPingBasicToken = - 1 ;
m_CurrentServerPingToken = - 1 ;
2021-04-24 12:23:35 +00:00
mem_zero ( & m_CurrentServerPingUuid , sizeof ( m_CurrentServerPingUuid ) ) ;
2021-04-23 21:12:16 +00:00
m_CurrentServerCurrentPingTime = - 1 ;
m_CurrentServerNextPingTime = - 1 ;
2010-05-29 07:25:38 +00:00
// disable all downloads
m_MapdownloadChunk = 0 ;
2015-01-28 12:13:56 +00:00
if ( m_pMapdownloadTask )
m_pMapdownloadTask - > Abort ( ) ;
2020-04-14 10:00:20 +00:00
if ( m_MapdownloadFileTemp )
2020-04-12 15:12:20 +00:00
{
2020-04-14 10:00:20 +00:00
io_close ( m_MapdownloadFileTemp ) ;
Storage ( ) - > RemoveFile ( m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE ) ;
2020-04-12 15:12:20 +00:00
}
2020-04-14 10:00:20 +00:00
m_MapdownloadFileTemp = 0 ;
2018-06-05 19:22:40 +00:00
m_MapdownloadSha256Present = false ;
m_MapdownloadSha256 = SHA256_ZEROED ;
2010-05-29 07:25:38 +00:00
m_MapdownloadCrc = 0 ;
m_MapdownloadTotalsize = - 1 ;
m_MapdownloadAmount = 0 ;
2018-06-24 14:56:09 +00:00
m_MapDetailsPresent = false ;
2010-05-29 07:25:38 +00:00
// clear the current server info
mem_zero ( & m_CurrentServerInfo , sizeof ( m_CurrentServerInfo ) ) ;
mem_zero ( & m_ServerAddress , sizeof ( m_ServerAddress ) ) ;
// clear snapshots
2014-05-03 18:24:45 +00:00
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] = 0 ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] = 0 ;
2016-01-23 20:44:45 +00:00
m_ReceivedSnapshots [ g_Config . m_ClDummy ] = 0 ;
2010-05-29 07:25:38 +00:00
}
void CClient : : Disconnect ( )
{
2019-09-27 09:16:48 +00:00
m_ButtonRender = false ;
2014-04-26 18:29:42 +00:00
if ( m_DummyConnected )
DummyDisconnect ( 0 ) ;
2016-05-02 19:35:32 +00:00
if ( m_State ! = IClient : : STATE_OFFLINE )
DisconnectWithReason ( 0 ) ;
2019-05-20 21:55:40 +00:00
// make sure to remove replay tmp demo
2019-06-05 17:17:55 +00:00
if ( g_Config . m_ClReplays )
2019-05-20 21:55:40 +00:00
{
2019-05-21 10:49:19 +00:00
Storage ( ) - > RemoveFile ( ( & m_DemoRecorder [ RECORDER_REPLAYS ] ) - > GetCurrentFilename ( ) , IStorage : : TYPE_SAVE ) ;
2019-05-20 21:55:40 +00:00
}
2010-05-29 07:25:38 +00:00
}
2014-04-26 18:29:42 +00:00
bool CClient : : DummyConnected ( )
{
return m_DummyConnected ;
}
2015-04-19 12:40:05 +00:00
bool CClient : : DummyConnecting ( )
{
2020-02-19 10:24:58 +00:00
return ! m_DummyConnected & & m_LastDummyConnectTime > 0 & & m_LastDummyConnectTime + GameTickSpeed ( ) * 5 > GameTick ( g_Config . m_ClDummy ) ;
2015-04-19 12:40:05 +00:00
}
2014-04-28 13:19:57 +00:00
void CClient : : DummyConnect ( )
2014-04-26 18:29:42 +00:00
{
2020-02-19 10:24:58 +00:00
if ( m_LastDummyConnectTime > 0 & & m_LastDummyConnectTime + GameTickSpeed ( ) * 5 > GameTick ( g_Config . m_ClDummy ) )
2014-04-26 18:29:42 +00:00
return ;
2022-01-21 21:13:35 +00:00
if ( m_NetClient [ CONN_MAIN ] . State ( ) ! = NET_CONNSTATE_ONLINE & & m_NetClient [ CONN_MAIN ] . State ( ) ! = NET_CONNSTATE_PENDING )
2014-05-10 18:40:54 +00:00
return ;
2021-11-17 19:35:39 +00:00
if ( m_DummyConnected | | ! DummyAllowed ( ) )
2014-05-24 19:34:06 +00:00
return ;
2014-04-26 18:29:42 +00:00
2020-02-19 10:24:58 +00:00
m_LastDummyConnectTime = GameTick ( g_Config . m_ClDummy ) ;
2014-07-25 00:41:36 +00:00
2014-04-27 11:44:04 +00:00
m_RconAuthed [ 1 ] = 0 ;
2014-04-26 18:29:42 +00:00
2015-04-19 17:53:37 +00:00
m_DummySendConnInfo = true ;
2016-10-26 02:31:13 +00:00
g_Config . m_ClDummyCopyMoves = 0 ;
g_Config . m_ClDummyHammer = 0 ;
2014-04-26 18:29:42 +00:00
//connecting to the server
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_DUMMY ] . Connect ( & m_ServerAddress ) ;
2014-04-26 18:29:42 +00:00
}
void CClient : : DummyDisconnect ( const char * pReason )
{
2014-05-24 19:34:06 +00:00
if ( ! m_DummyConnected )
return ;
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_DUMMY ] . Disconnect ( pReason ) ;
2014-04-26 18:29:42 +00:00
g_Config . m_ClDummy = 0 ;
2014-04-27 11:44:04 +00:00
m_RconAuthed [ 1 ] = 0 ;
2020-07-12 15:32:56 +00:00
m_aSnapshots [ 1 ] [ SNAP_CURRENT ] = 0 ;
m_aSnapshots [ 1 ] [ SNAP_PREV ] = 0 ;
m_ReceivedSnapshots [ 1 ] = 0 ;
2014-05-13 18:26:59 +00:00
m_DummyConnected = false ;
2014-04-28 14:47:44 +00:00
GameClient ( ) - > OnDummyDisconnect ( ) ;
2014-04-26 18:29:42 +00:00
}
2021-11-17 19:35:39 +00:00
bool CClient : : DummyAllowed ( )
{
return m_ServerCapabilities . m_AllowDummy ;
}
2019-03-25 19:02:50 +00:00
int CClient : : GetCurrentRaceTime ( )
{
if ( GameClient ( ) - > GetLastRaceTick ( ) < 0 )
return 0 ;
2020-02-19 10:24:58 +00:00
return ( GameTick ( g_Config . m_ClDummy ) - GameClient ( ) - > GetLastRaceTick ( ) ) / 50 ;
2019-03-25 19:02:50 +00:00
}
2021-02-08 21:26:26 +00:00
void CClient : : GetServerInfo ( CServerInfo * pServerInfo ) const
2010-05-29 07:25:38 +00:00
{
mem_copy ( pServerInfo , & m_CurrentServerInfo , sizeof ( m_CurrentServerInfo ) ) ;
2014-11-25 19:46:21 +00:00
if ( m_DemoPlayer . IsPlaying ( ) & & g_Config . m_ClDemoAssumeRace )
str_copy ( pServerInfo - > m_aGameType , " DDraceNetwork " , 14 ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : ServerInfoRequest ( )
{
mem_zero ( & m_CurrentServerInfo , sizeof ( m_CurrentServerInfo ) ) ;
m_CurrentServerInfoRequestTime = 0 ;
}
int CClient : : LoadData ( )
{
2021-05-21 12:57:55 +00:00
m_DebugFont = Graphics ( ) - > LoadTexture ( " debug_font.png " , IStorage : : TYPE_ALL , CImageInfo : : FORMAT_AUTO , 0 ) ;
2010-05-29 07:25:38 +00:00
return 1 ;
}
// ---
2021-02-08 21:26:26 +00:00
void * CClient : : SnapGetItem ( int SnapID , int Index , CSnapItem * pItem ) const
2010-05-29 07:25:38 +00:00
{
CSnapshotItem * i ;
2011-02-12 10:40:36 +00:00
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
2014-05-03 18:24:45 +00:00
i = m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItem ( Index ) ;
pItem - > m_DataSize = m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItemSize ( Index ) ;
2017-05-21 23:07:13 +00:00
pItem - > m_Type = m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItemType ( Index ) ;
2011-02-12 10:40:36 +00:00
pItem - > m_ID = i - > ID ( ) ;
2010-05-29 07:25:38 +00:00
return ( void * ) i - > Data ( ) ;
}
2021-02-08 21:26:26 +00:00
int CClient : : SnapItemSize ( int SnapID , int Index ) const
2019-06-03 19:52:14 +00:00
{
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
return m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItemSize ( Index ) ;
}
2011-02-12 10:40:36 +00:00
void CClient : : SnapInvalidateItem ( int SnapID , int Index )
2010-05-29 07:25:38 +00:00
{
CSnapshotItem * i ;
2011-02-12 10:40:36 +00:00
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
2014-05-03 18:24:45 +00:00
i = m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItem ( Index ) ;
2010-05-29 07:25:38 +00:00
if ( i )
{
2014-05-03 18:24:45 +00:00
if ( ( char * ) i < ( char * ) m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap | | ( char * ) i > ( char * ) m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap + m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_SnapSize )
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client " , " snap invalidate problem " ) ;
2014-05-03 18:24:45 +00:00
if ( ( char * ) i > = ( char * ) m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pSnap & & ( char * ) i < ( char * ) m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pSnap + m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_SnapSize )
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client " , " snap invalidate problem " ) ;
2010-05-29 07:25:38 +00:00
i - > m_TypeAndID = - 1 ;
}
}
2021-02-08 21:26:26 +00:00
void * CClient : : SnapFindItem ( int SnapID , int Type , int ID ) const
2010-05-29 07:25:38 +00:00
{
2014-05-03 18:24:45 +00:00
if ( ! m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] )
2010-05-29 07:25:38 +00:00
return 0x0 ;
2021-11-07 00:02:20 +00:00
return m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > FindItem ( Type , ID ) ;
2010-05-29 07:25:38 +00:00
}
2021-02-08 21:26:26 +00:00
int CClient : : SnapNumItems ( int SnapID ) const
2010-05-29 07:25:38 +00:00
{
2011-02-12 10:40:36 +00:00
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
2014-05-03 18:24:45 +00:00
if ( ! m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] )
2010-05-29 07:25:38 +00:00
return 0 ;
2014-05-03 18:24:45 +00:00
return m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pSnap - > NumItems ( ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : SnapSetStaticsize ( int ItemType , int Size )
{
m_SnapshotDelta . SetStaticsize ( ItemType , Size ) ;
}
void CClient : : DebugRender ( )
{
static NETSTATS Prev , Current ;
2021-06-23 05:05:49 +00:00
static int64_t LastSnap = 0 ;
2010-05-29 07:25:38 +00:00
static float FrameTimeAvg = 0 ;
char aBuffer [ 512 ] ;
if ( ! g_Config . m_Debug )
return ;
//m_pGraphics->BlendNormal();
Graphics ( ) - > TextureSet ( m_DebugFont ) ;
2020-09-26 19:41:58 +00:00
Graphics ( ) - > MapScreen ( 0 , 0 , Graphics ( ) - > ScreenWidth ( ) , Graphics ( ) - > ScreenHeight ( ) ) ;
2012-10-14 12:04:48 +00:00
Graphics ( ) - > QuadsBegin ( ) ;
2010-05-29 07:25:38 +00:00
2020-09-26 19:41:58 +00:00
if ( time_get ( ) - LastSnap > time_freq ( ) )
2010-05-29 07:25:38 +00:00
{
LastSnap = time_get ( ) ;
Prev = Current ;
net_stats ( & Current ) ;
}
/*
eth = 14
ip = 20
udp = 8
total = 42
*/
2020-09-26 19:41:58 +00:00
FrameTimeAvg = FrameTimeAvg * 0.9f + m_RenderFrameTime * 0.1f ;
2022-03-20 17:03:25 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " ticks: %8d %8d gfx mem(tex/buff/stream/staging): (% " PRIu64 " k/% " PRIu64 " k/% " PRIu64 " k/% " PRIu64 " k) fps: %3d " ,
2014-04-28 15:26:31 +00:00
m_CurGameTick [ g_Config . m_ClDummy ] , m_PredTick [ g_Config . m_ClDummy ] ,
2022-03-20 17:03:25 +00:00
( Graphics ( ) - > TextureMemoryUsage ( ) / 1024 ) ,
( Graphics ( ) - > BufferMemoryUsage ( ) / 1024 ) ,
( Graphics ( ) - > StreamedMemoryUsage ( ) / 1024 ) ,
( Graphics ( ) - > StagingMemoryUsage ( ) / 1024 ) ,
2020-09-26 19:41:58 +00:00
( int ) ( 1.0f / FrameTimeAvg + 0.5f ) ) ;
2012-10-14 12:04:48 +00:00
Graphics ( ) - > QuadsText ( 2 , 2 , 16 , aBuffer ) ;
2010-05-29 07:25:38 +00:00
{
2021-11-06 12:03:51 +00:00
uint64_t SendPackets = ( Current . sent_packets - Prev . sent_packets ) ;
uint64_t SendBytes = ( Current . sent_bytes - Prev . sent_bytes ) ;
uint64_t SendTotal = SendBytes + SendPackets * 42 ;
uint64_t RecvPackets = ( Current . recv_packets - Prev . recv_packets ) ;
uint64_t RecvBytes = ( Current . recv_bytes - Prev . recv_bytes ) ;
uint64_t RecvTotal = RecvBytes + RecvPackets * 42 ;
2010-05-29 07:25:38 +00:00
2020-09-26 19:41:58 +00:00
if ( ! SendPackets )
SendPackets + + ;
if ( ! RecvPackets )
RecvPackets + + ;
2021-11-06 12:03:51 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " send: %3 " PRIu64 " %5 " PRIu64 " +%4 " PRIu64 " =%5 " PRIu64 " (%3 " PRIu64 " kbps) avg: %5 " PRIu64 " \n recv: %3 " PRIu64 " %5 " PRIu64 " +%4 " PRIu64 " =%5 " PRIu64 " (%3 " PRIu64 " kbps) avg: %5 " PRIu64 ,
2020-09-26 19:41:58 +00:00
SendPackets , SendBytes , SendPackets * 42 , SendTotal , ( SendTotal * 8 ) / 1024 , SendBytes / SendPackets ,
RecvPackets , RecvBytes , RecvPackets * 42 , RecvTotal , ( RecvTotal * 8 ) / 1024 , RecvBytes / RecvPackets ) ;
2012-10-14 12:04:48 +00:00
Graphics ( ) - > QuadsText ( 2 , 14 , 16 , aBuffer ) ;
2010-05-29 07:25:38 +00:00
}
// render rates
{
int y = 0 ;
int i ;
for ( i = 0 ; i < 256 ; i + + )
{
if ( m_SnapshotDelta . GetDataRate ( i ) )
{
2020-09-26 19:41:58 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " %4d %20s: %8d %8d %8d " , i , GameClient ( ) - > GetItemName ( i ) , m_SnapshotDelta . GetDataRate ( i ) / 8 , m_SnapshotDelta . GetDataUpdates ( i ) ,
( m_SnapshotDelta . GetDataRate ( i ) / m_SnapshotDelta . GetDataUpdates ( i ) ) / 8 ) ;
Graphics ( ) - > QuadsText ( 2 , 100 + y * 12 , 16 , aBuffer ) ;
2010-05-29 07:25:38 +00:00
y + + ;
}
}
}
2016-05-05 16:07:00 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " pred: %d ms " , GetPredictionTime ( ) ) ;
2012-10-14 12:04:48 +00:00
Graphics ( ) - > QuadsText ( 2 , 70 , 16 , aBuffer ) ;
Graphics ( ) - > QuadsEnd ( ) ;
2010-05-29 07:25:38 +00:00
// render graphs
if ( g_Config . m_DbgGraphs )
{
//Graphics()->MapScreen(0,0,400.0f,300.0f);
2020-09-26 19:41:58 +00:00
float w = Graphics ( ) - > ScreenWidth ( ) / 4.0f ;
float h = Graphics ( ) - > ScreenHeight ( ) / 6.0f ;
float sp = Graphics ( ) - > ScreenWidth ( ) / 100.0f ;
float x = Graphics ( ) - > ScreenWidth ( ) - w - sp ;
2010-05-29 07:25:38 +00:00
m_FpsGraph . ScaleMax ( ) ;
m_FpsGraph . ScaleMin ( ) ;
2020-09-26 19:41:58 +00:00
m_FpsGraph . Render ( Graphics ( ) , m_DebugFont , x , sp * 5 , w , h , " FPS " ) ;
2015-11-08 15:25:14 +00:00
m_InputtimeMarginGraph . ScaleMin ( ) ;
m_InputtimeMarginGraph . ScaleMax ( ) ;
2020-09-26 19:41:58 +00:00
m_InputtimeMarginGraph . Render ( Graphics ( ) , m_DebugFont , x , sp * 5 + h + sp , w , h , " Prediction Margin " ) ;
2015-11-08 15:25:14 +00:00
m_GametimeMarginGraph . ScaleMin ( ) ;
m_GametimeMarginGraph . ScaleMax ( ) ;
2020-09-26 19:41:58 +00:00
m_GametimeMarginGraph . Render ( Graphics ( ) , m_DebugFont , x , sp * 5 + h + sp + h + sp , w , h , " Gametime Margin " ) ;
2010-05-29 07:25:38 +00:00
}
}
2014-12-31 14:35:49 +00:00
void CClient : : Restart ( )
{
2020-01-25 16:48:32 +00:00
SetState ( IClient : : STATE_RESTARTING ) ;
2014-12-31 14:35:49 +00:00
}
2010-05-29 07:25:38 +00:00
void CClient : : Quit ( )
{
2020-10-02 13:44:27 +00:00
SetState ( IClient : : STATE_QUITTING ) ;
2010-05-29 07:25:38 +00:00
}
2021-02-08 21:26:26 +00:00
const char * CClient : : PlayerName ( ) const
2020-08-20 10:19:03 +00:00
{
if ( g_Config . m_PlayerName [ 0 ] )
{
return g_Config . m_PlayerName ;
}
if ( g_Config . m_SteamName [ 0 ] )
{
return g_Config . m_SteamName ;
}
return " nameless tee " ;
}
2021-02-08 21:26:26 +00:00
const char * CClient : : DummyName ( ) const
2020-08-25 14:22:03 +00:00
{
if ( g_Config . m_ClDummyName [ 0 ] )
{
return g_Config . m_ClDummyName ;
}
const char * pBase = 0 ;
if ( g_Config . m_PlayerName [ 0 ] )
{
pBase = g_Config . m_PlayerName ;
}
else if ( g_Config . m_SteamName [ 0 ] )
{
pBase = g_Config . m_SteamName ;
}
if ( pBase )
{
2021-02-08 21:26:26 +00:00
static char aDummyNameBuf [ 16 ] ;
str_format ( aDummyNameBuf , sizeof ( aDummyNameBuf ) , " [D] %s " , pBase ) ;
return aDummyNameBuf ;
2020-08-25 14:22:03 +00:00
}
return " brainless tee " ;
}
2021-02-08 21:26:26 +00:00
const char * CClient : : ErrorString ( ) const
2010-05-29 07:25:38 +00:00
{
2022-01-21 21:13:35 +00:00
return m_NetClient [ CONN_MAIN ] . ErrorString ( ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : Render ( )
{
2014-05-01 15:44:35 +00:00
if ( g_Config . m_ClOverlayEntities )
2013-08-29 15:14:03 +00:00
{
2019-05-10 21:34:21 +00:00
ColorRGBA bg = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClBackgroundEntitiesColor ) ) ;
2013-08-29 15:14:03 +00:00
Graphics ( ) - > Clear ( bg . r , bg . g , bg . b ) ;
}
2013-10-09 15:11:34 +00:00
else
2013-08-29 15:14:03 +00:00
{
2019-05-10 21:34:21 +00:00
ColorRGBA bg = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClBackgroundColor ) ) ;
2013-07-11 15:13:45 +00:00
Graphics ( ) - > Clear ( bg . r , bg . g , bg . b ) ;
2013-06-23 11:41:13 +00:00
}
2010-05-29 07:25:38 +00:00
GameClient ( ) - > OnRender ( ) ;
DebugRender ( ) ;
2015-06-21 16:00:09 +00:00
if ( State ( ) = = IClient : : STATE_ONLINE & & g_Config . m_ClAntiPingLimit )
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2020-09-26 19:41:58 +00:00
g_Config . m_ClAntiPing = ( m_PredictedTime . Get ( Now ) - m_GameTime [ g_Config . m_ClDummy ] . Get ( Now ) ) * 1000 / ( float ) time_freq ( ) > g_Config . m_ClAntiPingLimit ;
2015-06-21 16:00:09 +00:00
}
2010-05-29 07:25:38 +00:00
}
2018-06-05 19:22:40 +00:00
const char * CClient : : LoadMap ( const char * pName , const char * pFilename , SHA256_DIGEST * pWantedSha256 , unsigned WantedCrc )
2010-05-29 07:25:38 +00:00
{
2017-03-21 10:24:44 +00:00
static char s_aErrorMsg [ 128 ] ;
2010-05-29 07:25:38 +00:00
SetState ( IClient : : STATE_LOADING ) ;
if ( ! m_pMap - > Load ( pFilename ) )
{
2017-03-21 10:24:44 +00:00
str_format ( s_aErrorMsg , sizeof ( s_aErrorMsg ) , " map '%s' not found " , pFilename ) ;
return s_aErrorMsg ;
2010-05-29 07:25:38 +00:00
}
2018-06-05 19:22:40 +00:00
if ( pWantedSha256 & & m_pMap - > Sha256 ( ) ! = * pWantedSha256 )
{
char aWanted [ SHA256_MAXSTRSIZE ] ;
char aGot [ SHA256_MAXSTRSIZE ] ;
sha256_str ( * pWantedSha256 , aWanted , sizeof ( aWanted ) ) ;
sha256_str ( m_pMap - > Sha256 ( ) , aGot , sizeof ( aWanted ) ) ;
str_format ( s_aErrorMsg , sizeof ( s_aErrorMsg ) , " map differs from the server. %s != %s " , aGot , aWanted ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , s_aErrorMsg ) ;
m_pMap - > Unload ( ) ;
return s_aErrorMsg ;
}
2020-10-10 20:06:47 +00:00
// Only check CRC if we don't have the secure SHA256.
if ( ! pWantedSha256 & & m_pMap - > Crc ( ) ! = WantedCrc )
2010-05-29 07:25:38 +00:00
{
2017-03-21 10:24:44 +00:00
str_format ( s_aErrorMsg , sizeof ( s_aErrorMsg ) , " map differs from the server. %08x != %08x " , m_pMap - > Crc ( ) , WantedCrc ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , s_aErrorMsg ) ;
2011-04-07 16:07:22 +00:00
m_pMap - > Unload ( ) ;
2017-03-21 10:24:44 +00:00
return s_aErrorMsg ;
2010-05-29 07:25:38 +00:00
}
// stop demo recording if we loaded a new map
2014-10-16 15:42:13 +00:00
for ( int i = 0 ; i < RECORDER_MAX ; i + + )
2019-08-21 20:49:21 +00:00
DemoRecorder_Stop ( i , i = = RECORDER_REPLAYS ) ;
2010-05-29 07:25:38 +00:00
2010-08-17 22:06:00 +00:00
char aBuf [ 256 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " loaded map '%s' " , pFilename ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , aBuf ) ;
2016-01-23 20:44:45 +00:00
m_ReceivedSnapshots [ g_Config . m_ClDummy ] = 0 ;
2010-05-29 07:25:38 +00:00
str_copy ( m_aCurrentMap , pName , sizeof ( m_aCurrentMap ) ) ;
2016-08-23 01:08:36 +00:00
str_copy ( m_aCurrentMapPath , pFilename , sizeof ( m_aCurrentMapPath ) ) ;
2010-05-29 07:25:38 +00:00
2018-06-05 19:22:40 +00:00
return 0 ;
2010-05-29 07:25:38 +00:00
}
2020-04-14 10:00:20 +00:00
static void FormatMapDownloadFilename ( const char * pName , const SHA256_DIGEST * pSha256 , int Crc , bool Temp , char * pBuffer , int BufferSize )
2010-05-29 07:25:38 +00:00
{
2020-04-14 10:00:20 +00:00
char aSuffix [ 32 ] ;
if ( Temp )
{
2021-12-20 14:00:21 +00:00
IStorage : : FormatTmpPath ( aSuffix , sizeof ( aSuffix ) , " " ) ;
2020-04-14 10:00:20 +00:00
}
else
{
str_copy ( aSuffix , " .map " , sizeof ( aSuffix ) ) ;
}
2018-06-05 19:22:40 +00:00
2020-04-14 10:00:20 +00:00
if ( pSha256 )
2018-06-05 19:22:40 +00:00
{
2020-04-14 10:00:20 +00:00
char aSha256 [ SHA256_MAXSTRSIZE ] ;
sha256_str ( * pSha256 , aSha256 , sizeof ( aSha256 ) ) ;
str_format ( pBuffer , BufferSize , " downloadedmaps/%s_%s%s " , pName , aSha256 , aSuffix ) ;
2020-10-10 20:06:47 +00:00
}
else
{
2020-04-14 10:00:20 +00:00
str_format ( pBuffer , BufferSize , " downloadedmaps/%s_%08x%s " , pName , Crc , aSuffix ) ;
2018-06-05 19:22:40 +00:00
}
2020-04-14 10:00:20 +00:00
}
2018-06-05 19:22:40 +00:00
2020-04-14 10:00:20 +00:00
const char * CClient : : LoadMapSearch ( const char * pMapName , SHA256_DIGEST * pWantedSha256 , int WantedCrc )
{
const char * pError = 0 ;
char aBuf [ 512 ] ;
char aWanted [ SHA256_MAXSTRSIZE + 16 ] ;
aWanted [ 0 ] = 0 ;
if ( pWantedSha256 )
{
char aWantedSha256 [ SHA256_MAXSTRSIZE ] ;
sha256_str ( * pWantedSha256 , aWantedSha256 , sizeof ( aWantedSha256 ) ) ;
str_format ( aWanted , sizeof ( aWanted ) , " sha256=%s " , aWantedSha256 ) ;
}
str_format ( aBuf , sizeof ( aBuf ) , " loading map, map=%s wanted %scrc=%08x " , pMapName , aWanted , WantedCrc ) ;
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , aBuf ) ;
2010-05-29 07:25:38 +00:00
SetState ( IClient : : STATE_LOADING ) ;
// try the normal maps folder
str_format ( aBuf , sizeof ( aBuf ) , " maps/%s.map " , pMapName ) ;
2018-06-05 19:22:40 +00:00
pError = LoadMap ( pMapName , aBuf , pWantedSha256 , WantedCrc ) ;
2010-05-29 07:25:38 +00:00
if ( ! pError )
return pError ;
// try the downloaded maps
2020-04-14 10:00:20 +00:00
FormatMapDownloadFilename ( pMapName , pWantedSha256 , WantedCrc , false , aBuf , sizeof ( aBuf ) ) ;
pError = LoadMap ( pMapName , aBuf , pWantedSha256 , WantedCrc ) ;
if ( ! pError )
return pError ;
// backward compatibility with old names
2018-06-24 14:56:09 +00:00
if ( pWantedSha256 )
{
2020-04-14 10:00:20 +00:00
FormatMapDownloadFilename ( pMapName , 0 , WantedCrc , false , aBuf , sizeof ( aBuf ) ) ;
2018-06-24 14:56:09 +00:00
pError = LoadMap ( pMapName , aBuf , pWantedSha256 , WantedCrc ) ;
if ( ! pError )
return pError ;
}
2011-02-21 10:23:30 +00:00
// search for the map within subfolders
2021-09-13 08:06:34 +00:00
char aFilename [ IO_MAX_PATH_LENGTH ] ;
2011-02-21 10:23:30 +00:00
str_format ( aFilename , sizeof ( aFilename ) , " %s.map " , pMapName ) ;
2011-04-04 14:53:02 +00:00
if ( Storage ( ) - > FindFile ( aFilename , " maps " , IStorage : : TYPE_ALL , aBuf , sizeof ( aBuf ) ) )
2018-06-05 19:22:40 +00:00
pError = LoadMap ( pMapName , aBuf , pWantedSha256 , WantedCrc ) ;
2011-02-21 10:23:30 +00:00
2010-05-29 07:25:38 +00:00
return pError ;
}
2011-03-17 16:41:57 +00:00
void CClient : : ProcessConnlessPacket ( CNetChunk * pPacket )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
// server info
2017-03-29 10:56:13 +00:00
if ( pPacket - > m_DataSize > = ( int ) sizeof ( SERVERBROWSE_INFO ) )
2010-05-29 07:25:38 +00:00
{
2017-03-29 10:56:13 +00:00
int Type = - 1 ;
if ( mem_comp ( pPacket - > m_pData , SERVERBROWSE_INFO , sizeof ( SERVERBROWSE_INFO ) ) = = 0 )
Type = SERVERINFO_VANILLA ;
else if ( mem_comp ( pPacket - > m_pData , SERVERBROWSE_INFO_64_LEGACY , sizeof ( SERVERBROWSE_INFO_64_LEGACY ) ) = = 0 )
Type = SERVERINFO_64_LEGACY ;
else if ( mem_comp ( pPacket - > m_pData , SERVERBROWSE_INFO_EXTENDED , sizeof ( SERVERBROWSE_INFO_EXTENDED ) ) = = 0 )
Type = SERVERINFO_EXTENDED ;
else if ( mem_comp ( pPacket - > m_pData , SERVERBROWSE_INFO_EXTENDED_MORE , sizeof ( SERVERBROWSE_INFO_EXTENDED_MORE ) ) = = 0 )
Type = SERVERINFO_EXTENDED_MORE ;
if ( Type ! = - 1 )
{
void * pData = ( unsigned char * ) pPacket - > m_pData + sizeof ( SERVERBROWSE_INFO ) ;
int DataSize = pPacket - > m_DataSize - sizeof ( SERVERBROWSE_INFO ) ;
ProcessServerInfo ( Type , & pPacket - > m_Address , pData , DataSize ) ;
}
}
}
2011-04-13 18:37:12 +00:00
2017-03-29 10:56:13 +00:00
static int SavedServerInfoType ( int Type )
{
if ( Type = = SERVERINFO_EXTENDED_MORE )
return SERVERINFO_EXTENDED ;
2014-01-09 14:40:11 +00:00
2017-03-29 10:56:13 +00:00
return Type ;
}
2011-03-17 16:41:57 +00:00
2017-03-29 10:56:13 +00:00
void CClient : : ProcessServerInfo ( int RawType , NETADDR * pFrom , const void * pData , int DataSize )
{
CServerBrowser : : CServerEntry * pEntry = m_ServerBrowser . Find ( * pFrom ) ;
CServerInfo Info = { 0 } ;
int SavedType = SavedServerInfoType ( RawType ) ;
2020-09-25 16:11:59 +00:00
if ( ( SavedType = = SERVERINFO_64_LEGACY | | SavedType = = SERVERINFO_EXTENDED ) & &
pEntry & & pEntry - > m_GotInfo & & SavedType = = pEntry - > m_Info . m_Type )
2017-03-29 10:56:13 +00:00
{
Info = pEntry - > m_Info ;
}
Info . m_Type = SavedType ;
net_addr_str ( pFrom , Info . m_aAddress , sizeof ( Info . m_aAddress ) , true ) ;
CUnpacker Up ;
Up . Reset ( pData , DataSize ) ;
2020-09-26 19:41:58 +00:00
# define GET_STRING(array) str_copy(array, Up.GetString(CUnpacker::SANITIZE_CC | CUnpacker::SKIP_START_WHITESPACES), sizeof(array))
# define GET_INT(integer) (integer) = str_toint(Up.GetString())
2010-05-29 07:25:38 +00:00
2017-03-29 10:56:13 +00:00
int Offset = 0 ; // Only used for SavedType == SERVERINFO_64_LEGACY
int Token ;
int PacketNo = 0 ; // Only used if SavedType == SERVERINFO_EXTENDED
2010-05-29 07:25:38 +00:00
2017-03-29 10:56:13 +00:00
GET_INT ( Token ) ;
if ( RawType ! = SERVERINFO_EXTENDED_MORE )
{
GET_STRING ( Info . m_aVersion ) ;
GET_STRING ( Info . m_aName ) ;
GET_STRING ( Info . m_aMap ) ;
if ( SavedType = = SERVERINFO_EXTENDED )
2011-03-17 16:41:57 +00:00
{
2017-03-29 10:56:13 +00:00
GET_INT ( Info . m_MapCrc ) ;
GET_INT ( Info . m_MapSize ) ;
2011-03-17 16:41:57 +00:00
}
2010-05-29 07:25:38 +00:00
2017-03-29 10:56:13 +00:00
GET_STRING ( Info . m_aGameType ) ;
GET_INT ( Info . m_Flags ) ;
GET_INT ( Info . m_NumPlayers ) ;
GET_INT ( Info . m_MaxPlayers ) ;
GET_INT ( Info . m_NumClients ) ;
GET_INT ( Info . m_MaxClients ) ;
2017-08-30 19:34:01 +00:00
if ( Info . m_aMap [ 0 ] )
Info . m_HasRank = m_ServerBrowser . HasRank ( Info . m_aMap ) ;
2010-05-29 07:25:38 +00:00
2017-03-29 10:56:13 +00:00
// don't add invalid info to the server browser list
if ( Info . m_NumClients < 0 | | Info . m_MaxClients < 0 | |
Info . m_NumPlayers < 0 | | Info . m_MaxPlayers < 0 | |
Info . m_NumPlayers > Info . m_NumClients | | Info . m_MaxPlayers > Info . m_MaxClients )
{
return ;
}
2014-04-04 22:07:58 +00:00
2017-03-29 10:56:13 +00:00
switch ( SavedType )
{
case SERVERINFO_VANILLA :
if ( Info . m_MaxPlayers > VANILLA_MAX_CLIENTS | |
Info . m_MaxClients > VANILLA_MAX_CLIENTS )
2011-03-17 16:41:57 +00:00
{
2017-03-29 10:56:13 +00:00
return ;
2011-03-17 16:41:57 +00:00
}
2017-03-29 10:56:13 +00:00
break ;
case SERVERINFO_64_LEGACY :
if ( Info . m_MaxPlayers > MAX_CLIENTS | |
Info . m_MaxClients > MAX_CLIENTS )
2014-01-14 20:40:55 +00:00
{
2017-03-29 10:56:13 +00:00
return ;
2014-01-14 20:40:55 +00:00
}
2017-03-29 10:56:13 +00:00
break ;
case SERVERINFO_EXTENDED :
if ( Info . m_NumPlayers > Info . m_NumClients )
return ;
break ;
default :
dbg_assert ( false , " unknown serverinfo type " ) ;
2011-03-17 16:41:57 +00:00
}
2014-01-08 05:15:56 +00:00
2017-03-29 10:56:13 +00:00
if ( SavedType = = SERVERINFO_64_LEGACY )
Offset = Up . GetInt ( ) ;
2014-01-08 05:15:56 +00:00
2017-03-29 10:56:13 +00:00
// Check for valid offset.
if ( Offset < 0 )
return ;
if ( SavedType = = SERVERINFO_EXTENDED )
PacketNo = 0 ;
}
else
{
GET_INT ( PacketNo ) ;
// 0 needs to be excluded because that's reserved for the main packet.
if ( PacketNo < = 0 | | PacketNo > = 64 )
2014-01-08 05:15:56 +00:00
return ;
2017-03-29 10:56:13 +00:00
}
2014-01-08 05:15:56 +00:00
2017-03-29 10:56:13 +00:00
bool DuplicatedPacket = false ;
if ( SavedType = = SERVERINFO_EXTENDED )
{
Up . GetString ( ) ; // extra info, reserved
2014-01-08 05:15:56 +00:00
2021-06-23 05:05:49 +00:00
uint64_t Flag = ( uint64_t ) 1 < < PacketNo ;
2020-09-26 19:41:58 +00:00
DuplicatedPacket = Info . m_ReceivedPackets & Flag ;
2017-03-29 10:56:13 +00:00
Info . m_ReceivedPackets | = Flag ;
}
2014-01-08 05:15:56 +00:00
2017-03-29 10:56:13 +00:00
bool IgnoreError = false ;
for ( int i = Offset ; i < MAX_CLIENTS & & Info . m_NumReceivedClients < MAX_CLIENTS & & ! Up . Error ( ) ; i + + )
{
CServerInfo : : CClient * pClient = & Info . m_aClients [ Info . m_NumReceivedClients ] ;
GET_STRING ( pClient - > m_aName ) ;
if ( Up . Error ( ) )
2014-01-08 05:15:56 +00:00
{
2017-03-29 10:56:13 +00:00
// Packet end, no problem unless it happens during one
// player info, so ignore the error.
IgnoreError = true ;
break ;
}
GET_STRING ( pClient - > m_aClan ) ;
GET_INT ( pClient - > m_Country ) ;
GET_INT ( pClient - > m_Score ) ;
GET_INT ( pClient - > m_Player ) ;
if ( SavedType = = SERVERINFO_EXTENDED )
{
Up . GetString ( ) ; // extra info, reserved
2014-01-08 05:15:56 +00:00
}
if ( ! Up . Error ( ) )
{
2017-03-29 10:56:13 +00:00
if ( SavedType = = SERVERINFO_64_LEGACY )
{
2021-06-23 05:05:49 +00:00
uint64_t Flag = ( uint64_t ) 1 < < i ;
2020-09-26 19:41:58 +00:00
if ( ! ( Info . m_ReceivedPackets & Flag ) )
2017-03-29 10:56:13 +00:00
{
Info . m_ReceivedPackets | = Flag ;
Info . m_NumReceivedClients + + ;
}
}
else
{
Info . m_NumReceivedClients + + ;
}
}
}
2014-01-08 05:15:56 +00:00
2013-09-04 14:44:04 +00:00
str_clean_whitespaces ( Info . m_aName ) ;
2017-03-29 10:56:13 +00:00
if ( ! Up . Error ( ) | | IgnoreError )
{
if ( ! DuplicatedPacket & & ( ! pEntry | | ! pEntry - > m_GotInfo | | SavedType > = pEntry - > m_Info . m_Type ) )
{
m_ServerBrowser . Set ( * pFrom , IServerBrowser : : SET_TOKEN , Token , & Info ) ;
}
// Player info is irrelevant for the client (while connected),
// it gets its info from elsewhere.
//
// SERVERINFO_EXTENDED_MORE doesn't carry any server
// information, so just skip it.
if ( net_addr_comp ( & m_ServerAddress , pFrom ) = = 0 & & RawType ! = SERVERINFO_EXTENDED_MORE )
{
// Only accept server info that has a type that is
// newer or equal to something the server already sent
// us.
if ( SavedType > = m_CurrentServerInfo . m_Type )
2014-01-08 05:15:56 +00:00
{
mem_copy ( & m_CurrentServerInfo , & Info , sizeof ( m_CurrentServerInfo ) ) ;
m_CurrentServerInfo . m_NetAddr = m_ServerAddress ;
m_CurrentServerInfoRequestTime = - 1 ;
}
2021-04-23 21:12:16 +00:00
bool ValidPong = false ;
2021-04-24 12:23:35 +00:00
if ( ! m_ServerCapabilities . m_PingEx & & m_CurrentServerCurrentPingTime > = 0 & & SavedType > = m_CurrentServerPingInfoType )
2021-04-23 21:12:16 +00:00
{
if ( RawType = = SERVERINFO_VANILLA )
{
ValidPong = Token = = m_CurrentServerPingBasicToken ;
}
else if ( RawType = = SERVERINFO_EXTENDED )
{
ValidPong = Token = = m_CurrentServerPingToken ;
}
}
if ( ValidPong )
{
int LatencyMs = ( time_get ( ) - m_CurrentServerCurrentPingTime ) * 1000 / time_freq ( ) ;
m_ServerBrowser . SetCurrentServerPing ( m_ServerAddress , LatencyMs ) ;
m_CurrentServerPingInfoType = SavedType ;
m_CurrentServerCurrentPingTime = - 1 ;
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " got pong from current server, latency=%dms " , LatencyMs ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf ) ;
}
2014-01-08 05:15:56 +00:00
}
}
2017-08-30 19:34:01 +00:00
2020-09-26 19:41:58 +00:00
# undef GET_STRING
# undef GET_INT
2011-03-17 16:41:57 +00:00
}
2010-05-29 07:25:38 +00:00
2019-06-03 19:52:14 +00:00
bool CClient : : ShouldSendChatTimeoutCodeHeuristic ( )
{
if ( m_ServerSentCapabilities )
{
return false ;
}
return IsDDNet ( & m_CurrentServerInfo ) ;
}
static CServerCapabilities GetServerCapabilities ( int Version , int Flags )
{
CServerCapabilities Result ;
bool DDNet = false ;
if ( Version > = 1 )
{
2020-09-26 19:41:58 +00:00
DDNet = Flags & SERVERCAPFLAG_DDNET ;
2019-06-03 19:52:14 +00:00
}
Result . m_ChatTimeoutCode = DDNet ;
2021-01-10 16:23:00 +00:00
Result . m_AnyPlayerFlag = DDNet ;
2021-04-23 21:29:01 +00:00
Result . m_PingEx = false ;
2021-11-17 19:35:39 +00:00
Result . m_AllowDummy = true ;
2022-01-30 10:47:30 +00:00
Result . m_SyncWeaponInput = false ;
2019-06-03 19:52:14 +00:00
if ( Version > = 1 )
{
2020-09-26 19:41:58 +00:00
Result . m_ChatTimeoutCode = Flags & SERVERCAPFLAG_CHATTIMEOUTCODE ;
2019-06-03 19:52:14 +00:00
}
2021-01-10 16:23:00 +00:00
if ( Version > = 2 )
{
Result . m_AnyPlayerFlag = Flags & SERVERCAPFLAG_ANYPLAYERFLAG ;
}
2021-04-23 21:29:01 +00:00
if ( Version > = 3 )
{
Result . m_PingEx = Flags & SERVERCAPFLAG_PINGEX ;
}
2021-11-17 19:35:39 +00:00
if ( Version > = 4 )
{
Result . m_AllowDummy = Flags & SERVERCAPFLAG_ALLOWDUMMY ;
}
2022-01-09 13:14:45 +00:00
if ( Version > = 5 )
{
Result . m_SyncWeaponInput = Flags & SERVERCAPFLAG_SYNCWEAPONINPUT ;
}
2019-06-03 19:52:14 +00:00
return Result ;
}
2022-01-21 21:13:35 +00:00
void CClient : : ProcessServerPacket ( CNetChunk * pPacket , int Conn , bool Dummy )
2011-03-17 16:41:57 +00:00
{
CUnpacker Unpacker ;
Unpacker . Reset ( pPacket - > m_pData , pPacket - > m_DataSize ) ;
2012-08-09 08:30:04 +00:00
CMsgPacker Packer ( NETMSG_EX , true ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
// unpack msgid and system flag
2017-05-21 23:07:13 +00:00
int Msg ;
bool Sys ;
CUuid Uuid ;
2010-05-29 07:25:38 +00:00
2017-05-21 23:07:13 +00:00
int Result = UnpackMessageID ( & Msg , & Sys , & Uuid , & Unpacker , & Packer ) ;
if ( Result = = UNPACKMESSAGE_ERROR )
{
2011-03-17 16:41:57 +00:00
return ;
2017-05-21 23:07:13 +00:00
}
else if ( Result = = UNPACKMESSAGE_ANSWER )
{
2022-01-21 21:13:35 +00:00
SendMsg ( Conn , & Packer , MSGFLAG_VITAL ) ;
2017-05-21 23:07:13 +00:00
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( Sys )
{
// system message
2022-03-04 18:20:36 +00:00
if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_MAP_DETAILS )
2011-03-17 16:41:57 +00:00
{
2020-09-26 19:41:58 +00:00
const char * pMap = Unpacker . GetString ( CUnpacker : : SANITIZE_CC | CUnpacker : : SKIP_START_WHITESPACES ) ;
2018-06-05 19:22:40 +00:00
SHA256_DIGEST * pMapSha256 = ( SHA256_DIGEST * ) Unpacker . GetRaw ( sizeof ( * pMapSha256 ) ) ;
int MapCrc = Unpacker . GetInt ( ) ;
if ( Unpacker . Error ( ) )
{
return ;
}
m_MapDetailsPresent = true ;
str_copy ( m_aMapDetailsName , pMap , sizeof ( m_aMapDetailsName ) ) ;
m_MapDetailsSha256 = * pMapSha256 ;
m_MapDetailsCrc = MapCrc ;
}
2022-03-04 18:20:36 +00:00
else if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_CAPABILITIES )
2019-06-03 19:52:14 +00:00
{
2019-06-13 22:24:50 +00:00
if ( ! m_CanReceiveServerCapabilities )
2019-06-03 19:52:14 +00:00
{
return ;
}
int Version = Unpacker . GetInt ( ) ;
int Flags = Unpacker . GetInt ( ) ;
if ( Version < = 0 )
{
return ;
}
m_ServerCapabilities = GetServerCapabilities ( Version , Flags ) ;
2019-06-13 22:24:50 +00:00
m_CanReceiveServerCapabilities = false ;
2019-06-03 19:52:14 +00:00
m_ServerSentCapabilities = true ;
}
2022-03-04 18:20:36 +00:00
else if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_MAP_CHANGE )
2018-06-05 19:22:40 +00:00
{
2019-06-13 22:24:50 +00:00
if ( m_CanReceiveServerCapabilities )
2019-06-03 19:52:14 +00:00
{
m_ServerCapabilities = GetServerCapabilities ( 0 , 0 ) ;
2019-06-13 22:24:50 +00:00
m_CanReceiveServerCapabilities = false ;
2019-06-03 19:52:14 +00:00
}
2018-06-05 19:22:40 +00:00
bool MapDetailsWerePresent = m_MapDetailsPresent ;
m_MapDetailsPresent = false ;
2020-09-26 19:41:58 +00:00
const char * pMap = Unpacker . GetString ( CUnpacker : : SANITIZE_CC | CUnpacker : : SKIP_START_WHITESPACES ) ;
2011-03-17 16:41:57 +00:00
int MapCrc = Unpacker . GetInt ( ) ;
int MapSize = Unpacker . GetInt ( ) ;
const char * pError = 0 ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( Unpacker . Error ( ) )
return ;
2010-05-29 07:25:38 +00:00
2014-08-16 10:55:37 +00:00
if ( m_DummyConnected )
DummyDisconnect ( 0 ) ;
2014-05-10 18:25:29 +00:00
2011-03-17 16:41:57 +00:00
for ( int i = 0 ; pMap [ i ] ; i + + ) // protect the player from nasty map names
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
if ( pMap [ i ] = = ' / ' | | pMap [ i ] = = ' \\ ' )
pError = " strange character in map name " ;
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( MapSize < 0 )
pError = " invalid map size " ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( pError )
DisconnectWithReason ( pError ) ;
else
{
2018-06-05 19:22:40 +00:00
SHA256_DIGEST * pMapSha256 = 0 ;
if ( MapDetailsWerePresent & & str_comp ( m_aMapDetailsName , pMap ) = = 0 & & m_MapDetailsCrc = = MapCrc )
{
pMapSha256 = & m_MapDetailsSha256 ;
}
pError = LoadMapSearch ( pMap , pMapSha256 , MapCrc ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( ! pError )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client/network " , " loading done " ) ;
SendReady ( ) ;
}
else
2010-05-29 07:25:38 +00:00
{
2020-04-14 10:00:20 +00:00
if ( m_MapdownloadFileTemp )
{
io_close ( m_MapdownloadFileTemp ) ;
Storage ( ) - > RemoveFile ( m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE ) ;
}
2018-06-30 07:47:45 +00:00
2020-04-14 10:00:20 +00:00
// start map download
FormatMapDownloadFilename ( pMap , pMapSha256 , MapCrc , false , m_aMapdownloadFilename , sizeof ( m_aMapdownloadFilename ) ) ;
FormatMapDownloadFilename ( pMap , pMapSha256 , MapCrc , true , m_aMapdownloadFilenameTemp , sizeof ( m_aMapdownloadFilenameTemp ) ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
char aBuf [ 256 ] ;
2020-04-14 10:00:20 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " starting to download map to '%s' " , m_aMapdownloadFilenameTemp ) ;
2011-03-17 16:41:57 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client/network " , aBuf ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
m_MapdownloadChunk = 0 ;
str_copy ( m_aMapdownloadName , pMap , sizeof ( m_aMapdownloadName ) ) ;
2014-10-29 12:37:38 +00:00
2018-06-05 19:22:40 +00:00
m_MapdownloadSha256Present = ( bool ) pMapSha256 ;
m_MapdownloadSha256 = pMapSha256 ? * pMapSha256 : SHA256_ZEROED ;
2011-03-17 16:41:57 +00:00
m_MapdownloadCrc = MapCrc ;
m_MapdownloadTotalsize = MapSize ;
2010-05-29 07:25:38 +00:00
m_MapdownloadAmount = 0 ;
2015-01-28 12:17:39 +00:00
ResetMapDownload ( ) ;
2018-06-05 19:22:40 +00:00
if ( pMapSha256 & & g_Config . m_ClHttpMapDownload )
2015-01-19 23:25:02 +00:00
{
char aUrl [ 256 ] ;
2018-06-05 19:22:40 +00:00
char aEscaped [ 256 ] ;
2020-04-14 10:00:20 +00:00
EscapeUrl ( aEscaped , sizeof ( aEscaped ) , m_aMapdownloadFilename + 15 ) ; // cut off downloadedmaps/
2021-01-31 10:15:09 +00:00
bool UseConfigUrl = str_comp ( g_Config . m_ClMapDownloadUrl , " https://maps2.ddnet.tw " ) ! = 0 | | m_aMapDownloadUrl [ 0 ] = = ' \0 ' ;
str_format ( aUrl , sizeof ( aUrl ) , " %s/%s " , UseConfigUrl ? g_Config . m_ClMapDownloadUrl : m_aMapDownloadUrl , aEscaped ) ;
2017-07-16 09:24:45 +00:00
2020-04-14 10:00:20 +00:00
m_pMapdownloadTask = std : : make_shared < CGetFile > ( Storage ( ) , aUrl , m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE , CTimeout { g_Config . m_ClMapDownloadConnectTimeoutMs , g_Config . m_ClMapDownloadLowSpeedLimit , g_Config . m_ClMapDownloadLowSpeedTime } ) ;
2017-11-23 14:47:38 +00:00
Engine ( ) - > AddJob ( m_pMapdownloadTask ) ;
2015-01-19 23:25:02 +00:00
}
else
SendMapRequest ( ) ;
2010-05-29 07:25:38 +00:00
}
}
2011-03-17 16:41:57 +00:00
}
2022-03-04 18:20:36 +00:00
else if ( Conn = = CONN_MAIN & & Msg = = NETMSG_MAP_DATA )
2011-03-17 16:41:57 +00:00
{
int Last = Unpacker . GetInt ( ) ;
int MapCRC = Unpacker . GetInt ( ) ;
int Chunk = Unpacker . GetInt ( ) ;
int Size = Unpacker . GetInt ( ) ;
const unsigned char * pData = Unpacker . GetRaw ( Size ) ;
2015-01-19 23:21:38 +00:00
// check for errors
2020-04-14 10:00:20 +00:00
if ( Unpacker . Error ( ) | | Size < = 0 | | MapCRC ! = m_MapdownloadCrc | | Chunk ! = m_MapdownloadChunk | | ! m_MapdownloadFileTemp )
2011-03-17 16:41:57 +00:00
return ;
2020-04-14 10:00:20 +00:00
io_write ( m_MapdownloadFileTemp , pData , Size ) ;
2011-03-17 16:41:57 +00:00
m_MapdownloadAmount + = Size ;
if ( Last )
2015-01-19 22:51:03 +00:00
{
2020-04-14 10:00:20 +00:00
if ( m_MapdownloadFileTemp )
2017-07-08 11:38:27 +00:00
{
2020-04-14 10:00:20 +00:00
io_close ( m_MapdownloadFileTemp ) ;
m_MapdownloadFileTemp = 0 ;
2017-07-08 11:38:27 +00:00
}
2014-10-29 12:37:38 +00:00
FinishMapDownload ( ) ;
2015-01-19 22:51:03 +00:00
}
2011-03-17 16:41:57 +00:00
else
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
// request new chunk
m_MapdownloadChunk + + ;
2022-03-20 11:57:50 +00:00
CMsgPacker MsgP ( NETMSG_REQUEST_MAP_DATA , true ) ;
MsgP . AddInt ( m_MapdownloadChunk ) ;
SendMsg ( CONN_MAIN , & MsgP , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( g_Config . m_Debug )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
char aBuf [ 256 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " requested chunk %d " , m_MapdownloadChunk ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client/network " , aBuf ) ;
2010-05-29 07:25:38 +00:00
}
}
2011-03-17 16:41:57 +00:00
}
2022-03-04 18:20:36 +00:00
else if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_CON_READY )
2011-03-17 16:41:57 +00:00
{
GameClient ( ) - > OnConnected ( ) ;
}
2022-03-04 18:20:36 +00:00
else if ( Conn = = CONN_DUMMY & & Msg = = NETMSG_CON_READY )
2022-01-21 00:54:14 +00:00
{
m_DummyConnected = true ;
g_Config . m_ClDummy = 1 ;
Rcon ( " crashmeplx " ) ;
if ( m_RconAuthed [ 0 ] )
RconAuth ( " " , m_RconPassword ) ;
}
2022-01-21 01:02:08 +00:00
else if ( Msg = = NETMSG_PING )
2011-03-17 16:41:57 +00:00
{
2022-03-20 11:57:50 +00:00
CMsgPacker MsgP ( NETMSG_PING_REPLY , true ) ;
SendMsg ( Conn , & MsgP , 0 ) ;
2011-03-17 16:41:57 +00:00
}
2022-01-21 01:02:08 +00:00
else if ( Msg = = NETMSG_PINGEX )
2021-04-23 21:29:01 +00:00
{
CUuid * pID = ( CUuid * ) Unpacker . GetRaw ( sizeof ( * pID ) ) ;
if ( Unpacker . Error ( ) )
{
return ;
}
2022-03-20 11:57:50 +00:00
CMsgPacker MsgP ( NETMSG_PONGEX , true ) ;
MsgP . AddRaw ( pID , sizeof ( * pID ) ) ;
SendMsg ( Conn , & MsgP , MSGFLAG_FLUSH ) ;
2021-04-23 21:29:01 +00:00
}
2022-01-21 21:13:35 +00:00
else if ( Conn = = CONN_MAIN & & Msg = = NETMSG_PONGEX )
2021-04-24 12:23:35 +00:00
{
CUuid * pID = ( CUuid * ) Unpacker . GetRaw ( sizeof ( * pID ) ) ;
if ( Unpacker . Error ( ) )
{
return ;
}
if ( m_ServerCapabilities . m_PingEx & & m_CurrentServerCurrentPingTime > = 0 & & * pID = = m_CurrentServerPingUuid )
{
int LatencyMs = ( time_get ( ) - m_CurrentServerCurrentPingTime ) * 1000 / time_freq ( ) ;
m_ServerBrowser . SetCurrentServerPing ( m_ServerAddress , LatencyMs ) ;
m_CurrentServerCurrentPingTime = - 1 ;
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " got pong from current server, latency=%dms " , LatencyMs ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf ) ;
}
}
2022-01-31 02:11:47 +00:00
else if ( Msg = = NETMSG_CHECKSUM_REQUEST )
{
CUuid * pUuid = ( CUuid * ) Unpacker . GetRaw ( sizeof ( * pUuid ) ) ;
if ( Unpacker . Error ( ) )
{
return ;
}
2022-03-20 11:57:50 +00:00
int ResultCheck = HandleChecksum ( Conn , * pUuid , & Unpacker ) ;
if ( ResultCheck )
2022-01-31 02:11:47 +00:00
{
2022-03-20 11:57:50 +00:00
CMsgPacker MsgP ( NETMSG_CHECKSUM_ERROR , true ) ;
MsgP . AddRaw ( pUuid , sizeof ( * pUuid ) ) ;
MsgP . AddInt ( ResultCheck ) ;
SendMsg ( Conn , & MsgP , MSGFLAG_VITAL ) ;
2022-01-31 02:11:47 +00:00
}
}
2022-01-24 14:27:54 +00:00
else if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_RCON_CMD_ADD )
2011-07-14 20:07:21 +00:00
{
2022-01-21 00:54:14 +00:00
const char * pName = Unpacker . GetString ( CUnpacker : : SANITIZE_CC ) ;
const char * pHelp = Unpacker . GetString ( CUnpacker : : SANITIZE_CC ) ;
const char * pParams = Unpacker . GetString ( CUnpacker : : SANITIZE_CC ) ;
if ( Unpacker . Error ( ) = = 0 )
m_pConsole - > RegisterTemp ( pName , pParams , CFGFLAG_SERVER , pHelp ) ;
2011-07-14 20:07:21 +00:00
}
2022-01-24 14:27:54 +00:00
else if ( Conn = = CONN_MAIN & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_RCON_CMD_REM )
2011-07-14 20:07:21 +00:00
{
2022-01-21 00:54:14 +00:00
const char * pName = Unpacker . GetString ( CUnpacker : : SANITIZE_CC ) ;
if ( Unpacker . Error ( ) = = 0 )
m_pConsole - > DeregisterTemp ( pName ) ;
2011-07-14 20:07:21 +00:00
}
2022-02-23 19:42:08 +00:00
else if ( ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_RCON_AUTH_STATUS )
2011-03-17 16:41:57 +00:00
{
2022-03-20 11:57:50 +00:00
int ResultInt = Unpacker . GetInt ( ) ;
2011-03-17 16:41:57 +00:00
if ( Unpacker . Error ( ) = = 0 )
2022-03-20 11:57:50 +00:00
m_RconAuthed [ Conn ] = ResultInt ;
2022-02-23 19:42:08 +00:00
if ( Conn = = CONN_MAIN )
{
int Old = m_UseTempRconCommands ;
m_UseTempRconCommands = Unpacker . GetInt ( ) ;
if ( Unpacker . Error ( ) ! = 0 )
m_UseTempRconCommands = 0 ;
if ( Old ! = 0 & & m_UseTempRconCommands = = 0 )
m_pConsole - > DeregisterTempAll ( ) ;
}
2011-03-17 16:41:57 +00:00
}
2022-01-21 00:54:14 +00:00
else if ( ! Dummy & & ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 & & Msg = = NETMSG_RCON_LINE )
2011-03-17 16:41:57 +00:00
{
const char * pLine = Unpacker . GetString ( ) ;
if ( Unpacker . Error ( ) = = 0 )
GameClient ( ) - > OnRconLine ( pLine ) ;
}
2022-01-21 21:13:35 +00:00
else if ( Conn = = CONN_MAIN & & Msg = = NETMSG_PING_REPLY )
2011-03-17 16:41:57 +00:00
{
char aBuf [ 256 ] ;
2020-09-26 19:41:58 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " latency %.2f " , ( time_get ( ) - m_PingStartTime ) * 1000 / ( float ) time_freq ( ) ) ;
2011-03-17 16:41:57 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client/network " , aBuf ) ;
}
2022-01-21 01:02:08 +00:00
else if ( Msg = = NETMSG_INPUTTIMING )
2011-03-17 16:41:57 +00:00
{
int InputPredTick = Unpacker . GetInt ( ) ;
int TimeLeft = Unpacker . GetInt ( ) ;
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2011-03-17 16:41:57 +00:00
// adjust our prediction time
2021-06-23 05:05:49 +00:00
int64_t Target = 0 ;
2011-03-17 16:41:57 +00:00
for ( int k = 0 ; k < 200 ; k + + )
2010-05-29 07:25:38 +00:00
{
2022-01-21 21:13:35 +00:00
if ( m_aInputs [ Conn ] [ k ] . m_Tick = = InputPredTick )
2010-05-29 07:25:38 +00:00
{
2022-01-21 21:13:35 +00:00
Target = m_aInputs [ Conn ] [ k ] . m_PredictedTime + ( Now - m_aInputs [ Conn ] [ k ] . m_Time ) ;
Target = Target - ( int64_t ) ( ( TimeLeft / 1000.0f ) * time_freq ( ) ) + m_aInputs [ Conn ] [ k ] . m_PredictionMargin ;
2011-03-17 16:41:57 +00:00
break ;
2010-05-29 07:25:38 +00:00
}
}
2011-03-17 16:41:57 +00:00
if ( Target )
2014-05-03 21:28:48 +00:00
m_PredictedTime . Update ( & m_InputtimeMarginGraph , Target , TimeLeft , 1 ) ;
2011-03-17 16:41:57 +00:00
}
else if ( Msg = = NETMSG_SNAP | | Msg = = NETMSG_SNAPSINGLE | | Msg = = NETMSG_SNAPEMPTY )
{
int NumParts = 1 ;
int Part = 0 ;
int GameTick = Unpacker . GetInt ( ) ;
2020-09-26 19:41:58 +00:00
int DeltaTick = GameTick - Unpacker . GetInt ( ) ;
2011-03-17 16:41:57 +00:00
int PartSize = 0 ;
2020-10-10 10:30:02 +00:00
unsigned int Crc = 0 ;
2011-03-17 16:41:57 +00:00
const char * pData = 0 ;
2014-02-26 00:25:22 +00:00
// only allow packets from the server we actually want
if ( net_addr_comp ( & pPacket - > m_Address , & m_ServerAddress ) )
return ;
2011-03-17 16:41:57 +00:00
// we are not allowed to process snapshot yet
if ( State ( ) < IClient : : STATE_LOADING )
return ;
if ( Msg = = NETMSG_SNAP )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
NumParts = Unpacker . GetInt ( ) ;
Part = Unpacker . GetInt ( ) ;
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
if ( Msg ! = NETMSG_SNAPEMPTY )
2010-08-17 22:06:00 +00:00
{
2011-03-17 16:41:57 +00:00
Crc = Unpacker . GetInt ( ) ;
PartSize = Unpacker . GetInt ( ) ;
2010-08-17 22:06:00 +00:00
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
pData = ( const char * ) Unpacker . GetRaw ( PartSize ) ;
2010-05-29 07:25:38 +00:00
2020-01-25 06:37:28 +00:00
if ( Unpacker . Error ( ) | | NumParts < 1 | | NumParts > CSnapshot : : MAX_PARTS | | Part < 0 | | Part > = NumParts | | PartSize < 0 | | PartSize > MAX_SNAPSHOT_PACKSIZE )
2011-03-17 16:41:57 +00:00
return ;
2010-05-29 07:25:38 +00:00
2022-01-21 21:13:35 +00:00
if ( GameTick > = m_CurrentRecvTick [ Conn ] )
2011-03-17 16:41:57 +00:00
{
2022-01-21 21:13:35 +00:00
if ( GameTick ! = m_CurrentRecvTick [ Conn ] )
2010-05-29 07:25:38 +00:00
{
2022-01-21 21:13:35 +00:00
m_SnapshotParts [ Conn ] = 0 ;
m_CurrentRecvTick [ Conn ] = GameTick ;
2010-05-29 07:25:38 +00:00
}
2020-09-26 19:41:58 +00:00
mem_copy ( ( char * ) m_aSnapshotIncomingData + Part * MAX_SNAPSHOT_PACKSIZE , pData , clamp ( PartSize , 0 , ( int ) sizeof ( m_aSnapshotIncomingData ) - Part * MAX_SNAPSHOT_PACKSIZE ) ) ;
2022-01-21 21:13:35 +00:00
m_SnapshotParts [ Conn ] | = 1 < < Part ;
2010-05-29 07:25:38 +00:00
2022-01-21 21:13:35 +00:00
if ( m_SnapshotParts [ Conn ] = = ( unsigned ) ( ( 1 < < NumParts ) - 1 ) )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
static CSnapshot Emptysnap ;
CSnapshot * pDeltaShot = & Emptysnap ;
unsigned char aTmpBuffer2 [ CSnapshot : : MAX_SIZE ] ;
unsigned char aTmpBuffer3 [ CSnapshot : : MAX_SIZE ] ;
2020-09-26 19:41:58 +00:00
CSnapshot * pTmpBuffer3 = ( CSnapshot * ) aTmpBuffer3 ; // Fix compiler warning for strict-aliasing
2022-01-14 16:52:23 +00:00
const int CompleteSize = ( NumParts - 1 ) * MAX_SNAPSHOT_PACKSIZE + PartSize ;
2011-03-17 16:41:57 +00:00
// reset snapshoting
2022-01-21 21:13:35 +00:00
m_SnapshotParts [ Conn ] = 0 ;
2011-03-17 16:41:57 +00:00
// find snapshot that we should use as delta
Emptysnap . Clear ( ) ;
// find delta
if ( DeltaTick > = 0 )
2010-05-29 07:25:38 +00:00
{
2022-01-21 21:13:35 +00:00
int DeltashotSize = m_SnapshotStorage [ Conn ] . Get ( DeltaTick , 0 , & pDeltaShot , 0 ) ;
2010-05-29 07:25:38 +00:00
2014-05-03 18:24:45 +00:00
if ( DeltashotSize < 0 )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
// couldn't find the delta snapshots that the server used
// to compress this snapshot. force the server to resync
2010-05-29 07:25:38 +00:00
if ( g_Config . m_Debug )
{
2010-08-17 22:06:00 +00:00
char aBuf [ 256 ] ;
2011-03-17 16:41:57 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " error, couldn't find the delta snapshot " ) ;
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client " , aBuf ) ;
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
// ack snapshot
// TODO: combine this with the input message
2022-01-21 21:13:35 +00:00
m_AckGameTick [ Conn ] = - 1 ;
2010-05-29 07:25:38 +00:00
return ;
}
2011-03-17 16:41:57 +00:00
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
// decompress snapshot
2022-01-14 16:52:23 +00:00
const void * pDeltaData = m_SnapshotDelta . EmptyDelta ( ) ;
int DeltaSize = sizeof ( int ) * 3 ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( CompleteSize )
{
2017-09-16 17:30:08 +00:00
int IntSize = CVariableInt : : Decompress ( m_aSnapshotIncomingData , CompleteSize , aTmpBuffer2 , sizeof ( aTmpBuffer2 ) ) ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( IntSize < 0 ) // failure during decompression, bail
return ;
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
pDeltaData = aTmpBuffer2 ;
DeltaSize = IntSize ;
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
// unpack delta
2022-01-14 16:52:23 +00:00
const int SnapSize = m_SnapshotDelta . UnpackDelta ( pDeltaShot , pTmpBuffer3 , pDeltaData , DeltaSize ) ;
2011-03-17 16:41:57 +00:00
if ( SnapSize < 0 )
{
2017-05-21 23:07:13 +00:00
dbg_msg ( " client " , " delta unpack failed!=%d " , SnapSize ) ;
2011-03-17 16:41:57 +00:00
return ;
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
if ( Msg ! = NETMSG_SNAPEMPTY & & pTmpBuffer3 - > Crc ( ) ! = Crc )
{
if ( g_Config . m_Debug )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
char aBuf [ 256 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d " ,
m_SnapCrcErrors , GameTick , Crc , pTmpBuffer3 - > Crc ( ) , CompleteSize , DeltaTick ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client " , aBuf ) ;
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
m_SnapCrcErrors + + ;
if ( m_SnapCrcErrors > 10 )
2010-05-29 07:25:38 +00:00
{
2011-03-17 16:41:57 +00:00
// to many errors, send reset
2022-01-21 21:13:35 +00:00
m_AckGameTick [ Conn ] = - 1 ;
2011-03-17 16:41:57 +00:00
SendInput ( ) ;
m_SnapCrcErrors = 0 ;
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
return ;
}
else
{
if ( m_SnapCrcErrors )
m_SnapCrcErrors - - ;
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
// purge old snapshots
2022-01-14 16:52:23 +00:00
int PurgeTick = DeltaTick ;
2022-01-21 21:13:35 +00:00
if ( m_aSnapshots [ Conn ] [ SNAP_PREV ] & & m_aSnapshots [ Conn ] [ SNAP_PREV ] - > m_Tick < PurgeTick )
PurgeTick = m_aSnapshots [ Conn ] [ SNAP_PREV ] - > m_Tick ;
if ( m_aSnapshots [ Conn ] [ SNAP_CURRENT ] & & m_aSnapshots [ Conn ] [ SNAP_CURRENT ] - > m_Tick < PurgeTick )
PurgeTick = m_aSnapshots [ Conn ] [ SNAP_CURRENT ] - > m_Tick ;
m_SnapshotStorage [ Conn ] . PurgeUntil ( PurgeTick ) ;
2011-03-17 16:41:57 +00:00
// add new
2022-01-21 21:13:35 +00:00
m_SnapshotStorage [ Conn ] . Add ( GameTick , time_get ( ) , SnapSize , pTmpBuffer3 , 1 ) ;
2011-03-17 16:41:57 +00:00
2022-01-26 00:28:20 +00:00
if ( ! Dummy )
2011-03-17 16:41:57 +00:00
{
2022-01-26 00:28:20 +00:00
// for antiping: if the projectile netobjects from the server contains extra data, this is removed and the original content restored before recording demo
unsigned char aExtraInfoRemoved [ CSnapshot : : MAX_SIZE ] ;
mem_copy ( aExtraInfoRemoved , pTmpBuffer3 , SnapSize ) ;
SnapshotRemoveExtraProjectileInfo ( aExtraInfoRemoved ) ;
// add snapshot to demo
for ( auto & DemoRecorder : m_DemoRecorder )
2014-10-16 15:42:13 +00:00
{
2022-01-26 00:28:20 +00:00
if ( DemoRecorder . IsRecording ( ) )
{
// write snapshot
DemoRecorder . RecordSnapshot ( GameTick , aExtraInfoRemoved , SnapSize ) ;
}
2014-10-16 15:42:13 +00:00
}
2011-03-17 16:41:57 +00:00
}
2010-05-29 07:25:38 +00:00
2011-03-17 16:41:57 +00:00
// apply snapshot, cycle pointers
2022-01-21 21:13:35 +00:00
m_ReceivedSnapshots [ Conn ] + + ;
2011-03-17 16:41:57 +00:00
2022-01-21 21:13:35 +00:00
m_CurrentRecvTick [ Conn ] = GameTick ;
2011-03-17 16:41:57 +00:00
// we got two snapshots until we see us self as connected
2022-01-21 21:13:35 +00:00
if ( m_ReceivedSnapshots [ Conn ] = = 2 )
2011-03-17 16:41:57 +00:00
{
// start at 200ms and work from there
2022-01-21 00:54:14 +00:00
if ( ! Dummy )
{
m_PredictedTime . Init ( GameTick * time_freq ( ) / 50 ) ;
m_PredictedTime . SetAdjustSpeed ( 1 , 1000.0f ) ;
m_PredictedTime . UpdateMargin ( PredictionMargin ( ) * time_freq ( ) / 1000 ) ;
}
2022-01-21 21:13:35 +00:00
m_GameTime [ Conn ] . Init ( ( GameTick - 1 ) * time_freq ( ) / 50 ) ;
m_aSnapshots [ Conn ] [ SNAP_PREV ] = m_SnapshotStorage [ Conn ] . m_pFirst ;
m_aSnapshots [ Conn ] [ SNAP_CURRENT ] = m_SnapshotStorage [ Conn ] . m_pLast ;
2022-01-21 00:54:14 +00:00
if ( ! Dummy )
{
2022-01-21 01:02:08 +00:00
m_LocalStartTime = time_get ( ) ;
# if defined(CONF_VIDEORECORDER)
IVideo : : SetLocalStartTime ( m_LocalStartTime ) ;
# endif
2022-01-21 00:54:14 +00:00
GameClient ( ) - > OnNewSnapshot ( ) ;
}
2011-03-17 16:41:57 +00:00
SetState ( IClient : : STATE_ONLINE ) ;
2022-01-21 00:54:14 +00:00
if ( ! Dummy )
{
DemoRecorder_HandleAutoStart ( ) ;
}
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
// adjust game time
2022-01-21 21:13:35 +00:00
if ( m_ReceivedSnapshots [ Conn ] > 2 )
2011-03-17 16:41:57 +00:00
{
2022-01-21 21:13:35 +00:00
int64_t Now = m_GameTime [ Conn ] . Get ( time_get ( ) ) ;
2021-06-23 05:05:49 +00:00
int64_t TickStart = GameTick * time_freq ( ) / 50 ;
int64_t TimeLeft = ( TickStart - Now ) * 1000 / time_freq ( ) ;
2022-01-21 21:13:35 +00:00
m_GameTime [ Conn ] . Update ( & m_GametimeMarginGraph , ( GameTick - 1 ) * time_freq ( ) / 50 , TimeLeft , 0 ) ;
2010-05-29 07:25:38 +00:00
}
2011-03-17 16:41:57 +00:00
2022-01-21 21:13:35 +00:00
if ( m_ReceivedSnapshots [ Conn ] > 50 & & ! m_CodeRunAfterJoin [ Conn ] )
2014-11-27 00:59:55 +00:00
{
2019-06-03 19:52:14 +00:00
if ( m_ServerCapabilities . m_ChatTimeoutCode | | ShouldSendChatTimeoutCodeHeuristic ( ) )
2014-11-27 00:59:55 +00:00
{
2022-03-20 11:57:50 +00:00
CNetMsg_Cl_Say MsgP ;
MsgP . m_Team = 0 ;
2014-11-27 00:59:55 +00:00
char aBuf [ 256 ] ;
2021-11-23 15:39:59 +00:00
if ( g_Config . m_ClRunOnJoin [ 0 ] )
{
2022-01-21 21:13:35 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " /mc;timeout %s;%s " , m_aTimeoutCodes [ Conn ] , g_Config . m_ClRunOnJoin ) ;
2021-11-23 15:39:59 +00:00
}
else
{
2022-01-21 21:13:35 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " /timeout %s " , m_aTimeoutCodes [ Conn ] ) ;
2021-11-23 15:39:59 +00:00
}
2022-03-20 11:57:50 +00:00
MsgP . m_pMessage = aBuf ;
CMsgPacker PackerTimeout ( MsgP . MsgID ( ) , false ) ;
MsgP . Pack ( & PackerTimeout ) ;
SendMsg ( Conn , & PackerTimeout , MSGFLAG_VITAL ) ;
2014-11-27 00:59:55 +00:00
}
2022-01-21 21:13:35 +00:00
m_CodeRunAfterJoin [ Conn ] = true ;
2014-11-27 00:59:55 +00:00
}
2011-03-17 16:41:57 +00:00
// ack snapshot
2022-01-21 21:13:35 +00:00
m_AckGameTick [ Conn ] = GameTick ;
2010-05-29 07:25:38 +00:00
}
}
}
2022-01-21 21:13:35 +00:00
else if ( Conn = = CONN_MAIN & & Msg = = NETMSG_RCONTYPE )
2017-07-24 19:43:55 +00:00
{
bool UsernameReq = Unpacker . GetInt ( ) & 1 ;
GameClient ( ) - > OnRconType ( UsernameReq ) ;
}
2011-03-17 16:41:57 +00:00
}
else
{
2020-12-06 18:58:04 +00:00
if ( ( pPacket - > m_Flags & NET_CHUNKFLAG_VITAL ) ! = 0 )
2015-03-19 08:57:47 +00:00
{
// game message
2022-01-21 00:54:14 +00:00
if ( ! Dummy )
2020-09-26 22:30:47 +00:00
{
2022-01-21 00:54:14 +00:00
for ( auto & DemoRecorder : m_DemoRecorder )
if ( DemoRecorder . IsRecording ( ) )
DemoRecorder . RecordMessage ( pPacket - > m_pData , pPacket - > m_DataSize ) ;
2020-09-26 22:30:47 +00:00
}
2014-05-03 18:24:45 +00:00
2022-01-21 21:13:35 +00:00
GameClient ( ) - > OnMessage ( Msg , & Unpacker , Conn , Dummy ) ;
2014-05-03 00:30:05 +00:00
}
}
}
2014-10-29 12:37:38 +00:00
void CClient : : ResetMapDownload ( )
{
2017-03-04 14:43:49 +00:00
if ( m_pMapdownloadTask )
{
2017-04-11 19:47:27 +00:00
m_pMapdownloadTask - > Abort ( ) ;
2017-04-11 23:20:39 +00:00
m_pMapdownloadTask = NULL ;
2014-10-29 12:37:38 +00:00
}
2020-04-14 10:00:20 +00:00
m_MapdownloadFileTemp = 0 ;
2014-10-29 12:37:38 +00:00
m_MapdownloadAmount = 0 ;
}
void CClient : : FinishMapDownload ( )
{
const char * pError ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client/network " , " download complete, loading map " ) ;
2018-06-19 12:28:53 +00:00
int Prev = m_MapdownloadTotalsize ;
2014-10-29 12:37:38 +00:00
m_MapdownloadTotalsize = - 1 ;
2018-06-05 19:22:40 +00:00
SHA256_DIGEST * pSha256 = m_MapdownloadSha256Present ? & m_MapdownloadSha256 : 0 ;
2020-04-14 10:00:20 +00:00
Storage ( ) - > RemoveFile ( m_aMapdownloadFilename , IStorage : : TYPE_SAVE ) ;
Storage ( ) - > RenameFile ( m_aMapdownloadFilenameTemp , m_aMapdownloadFilename , IStorage : : TYPE_SAVE ) ;
2014-10-29 12:37:38 +00:00
// load map
2020-04-14 10:00:20 +00:00
pError = LoadMap ( m_aMapdownloadName , m_aMapdownloadFilename , pSha256 , m_MapdownloadCrc ) ;
2014-10-29 12:37:38 +00:00
if ( ! pError )
{
2015-01-19 22:51:03 +00:00
ResetMapDownload ( ) ;
2014-10-29 12:37:38 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client/network " , " loading done " ) ;
SendReady ( ) ;
}
2017-04-11 19:47:27 +00:00
else if ( m_pMapdownloadTask ) // fallback
2015-01-19 21:13:44 +00:00
{
ResetMapDownload ( ) ;
2018-06-19 12:28:53 +00:00
m_MapdownloadTotalsize = Prev ;
2015-01-19 21:13:44 +00:00
SendMapRequest ( ) ;
}
2017-03-04 14:43:49 +00:00
else
{
2020-04-14 10:00:20 +00:00
if ( m_MapdownloadFileTemp )
2017-07-08 11:38:27 +00:00
{
2020-04-14 10:00:20 +00:00
io_close ( m_MapdownloadFileTemp ) ;
m_MapdownloadFileTemp = 0 ;
Storage ( ) - > RemoveFile ( m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE ) ;
2017-07-08 11:38:27 +00:00
}
2015-01-19 22:51:03 +00:00
ResetMapDownload ( ) ;
2014-10-29 12:37:38 +00:00
DisconnectWithReason ( pError ) ;
2015-01-19 22:51:03 +00:00
}
2014-10-29 12:37:38 +00:00
}
2017-09-03 15:36:51 +00:00
void CClient : : ResetDDNetInfo ( )
2017-08-30 19:34:01 +00:00
{
2017-09-03 15:36:51 +00:00
if ( m_pDDNetInfoTask )
2017-08-30 19:34:01 +00:00
{
2017-09-03 15:36:51 +00:00
m_pDDNetInfoTask - > Abort ( ) ;
m_pDDNetInfoTask = NULL ;
2017-08-30 19:34:01 +00:00
}
}
2020-09-09 15:49:29 +00:00
bool CClient : : IsDDNetInfoChanged ( )
{
2021-12-17 20:58:28 +00:00
IOHANDLE OldFile = m_pStorage - > OpenFile ( DDNET_INFO , IOFLAG_READ | IOFLAG_SKIP_BOM , IStorage : : TYPE_SAVE ) ;
2020-09-09 15:49:29 +00:00
if ( ! OldFile )
return true ;
2021-12-17 20:58:28 +00:00
IOHANDLE NewFile = m_pStorage - > OpenFile ( m_aDDNetInfoTmp , IOFLAG_READ | IOFLAG_SKIP_BOM , IStorage : : TYPE_SAVE ) ;
2020-09-09 15:49:29 +00:00
if ( NewFile )
{
char aOldData [ 4096 ] ;
char aNewData [ 4096 ] ;
unsigned OldBytes ;
unsigned NewBytes ;
do
{
OldBytes = io_read ( OldFile , aOldData , sizeof ( aOldData ) ) ;
NewBytes = io_read ( NewFile , aNewData , sizeof ( aNewData ) ) ;
if ( OldBytes ! = NewBytes | | mem_comp ( aOldData , aNewData , OldBytes ) ! = 0 )
{
io_close ( NewFile ) ;
io_close ( OldFile ) ;
return true ;
}
2020-09-26 19:41:58 +00:00
} while ( OldBytes > 0 ) ;
2020-09-09 15:49:29 +00:00
io_close ( NewFile ) ;
}
io_close ( OldFile ) ;
return false ;
}
2017-09-03 15:36:51 +00:00
void CClient : : FinishDDNetInfo ( )
2017-08-30 19:34:01 +00:00
{
2017-09-03 15:36:51 +00:00
ResetDDNetInfo ( ) ;
2020-09-09 15:49:29 +00:00
if ( IsDDNetInfoChanged ( ) )
{
m_pStorage - > RenameFile ( m_aDDNetInfoTmp , DDNET_INFO , IStorage : : TYPE_SAVE ) ;
LoadDDNetInfo ( ) ;
if ( g_Config . m_UiPage = = CMenus : : PAGE_DDNET )
m_ServerBrowser . Refresh ( IServerBrowser : : TYPE_DDNET ) ;
else if ( g_Config . m_UiPage = = CMenus : : PAGE_KOG )
m_ServerBrowser . Refresh ( IServerBrowser : : TYPE_KOG ) ;
}
else
{
m_pStorage - > RemoveFile ( m_aDDNetInfoTmp , IStorage : : TYPE_SAVE ) ;
}
2017-09-03 15:36:51 +00:00
}
2019-02-27 19:24:31 +00:00
typedef std : : tuple < int , int , int > Version ;
static const Version InvalidVersion = std : : make_tuple ( - 1 , - 1 , - 1 ) ;
Version ToVersion ( char * pStr )
{
int version [ 3 ] = { 0 , 0 , 0 } ;
const char * p = strtok ( pStr , " . " ) ;
for ( int i = 0 ; i < 3 & & p ; + + i )
{
if ( ! str_isallnum ( p ) )
return InvalidVersion ;
version [ i ] = str_toint ( p ) ;
p = strtok ( NULL , " . " ) ;
}
if ( p )
return InvalidVersion ;
return std : : make_tuple ( version [ 0 ] , version [ 1 ] , version [ 2 ] ) ;
}
2017-09-03 15:36:51 +00:00
void CClient : : LoadDDNetInfo ( )
{
const json_value * pDDNetInfo = m_ServerBrowser . LoadDDNetInfo ( ) ;
if ( ! pDDNetInfo )
return ;
const json_value * pVersion = json_object_get ( pDDNetInfo , " version " ) ;
2018-07-11 18:17:21 +00:00
if ( pVersion - > type = = json_string )
2017-09-03 15:36:51 +00:00
{
2019-02-27 19:24:31 +00:00
char aNewVersionStr [ 64 ] ;
str_copy ( aNewVersionStr , json_string_get ( pVersion ) , sizeof ( aNewVersionStr ) ) ;
char aCurVersionStr [ 64 ] ;
2019-03-06 20:02:06 +00:00
str_copy ( aCurVersionStr , GAME_RELEASE_VERSION , sizeof ( aCurVersionStr ) ) ;
2019-02-27 19:24:31 +00:00
if ( ToVersion ( aNewVersionStr ) > ToVersion ( aCurVersionStr ) )
2017-09-03 15:36:51 +00:00
{
2019-02-27 19:24:31 +00:00
str_copy ( m_aVersionStr , json_string_get ( pVersion ) , sizeof ( m_aVersionStr ) ) ;
2017-09-03 15:36:51 +00:00
}
else
{
m_aVersionStr [ 0 ] = ' 0 ' ;
m_aVersionStr [ 1 ] = ' \0 ' ;
}
}
const json_value * pNews = json_object_get ( pDDNetInfo , " news " ) ;
2018-07-11 18:17:21 +00:00
if ( pNews - > type = = json_string )
2017-09-03 15:36:51 +00:00
{
const char * pNewsString = json_string_get ( pNews ) ;
2020-09-03 12:08:26 +00:00
// Only mark news button if something new was added to the news
2020-09-03 10:32:13 +00:00
if ( m_aNews [ 0 ] & & str_find ( m_aNews , pNewsString ) = = nullptr )
2020-09-03 12:08:26 +00:00
g_Config . m_UiUnreadNews = true ;
2017-09-03 15:36:51 +00:00
str_copy ( m_aNews , pNewsString , sizeof ( m_aNews ) ) ;
}
2020-10-23 21:25:58 +00:00
2021-01-31 10:15:09 +00:00
const json_value * pMapDownloadUrl = json_object_get ( pDDNetInfo , " map-download-url " ) ;
if ( pMapDownloadUrl - > type = = json_string )
{
const char * pMapDownloadUrlString = json_string_get ( pMapDownloadUrl ) ;
str_copy ( m_aMapDownloadUrl , pMapDownloadUrlString , sizeof ( m_aMapDownloadUrl ) ) ;
}
2020-10-23 21:25:58 +00:00
const json_value * pPoints = json_object_get ( pDDNetInfo , " points " ) ;
if ( pPoints - > type = = json_integer )
m_Points = pPoints - > u . integer ;
2017-08-30 19:34:01 +00:00
}
2010-05-29 07:25:38 +00:00
void CClient : : PumpNetwork ( )
{
2020-10-26 14:14:07 +00:00
for ( auto & NetClient : m_NetClient )
2014-04-26 18:29:42 +00:00
{
2020-10-26 14:14:07 +00:00
NetClient . Update ( ) ;
2014-04-26 18:29:42 +00:00
}
2010-05-29 07:25:38 +00:00
if ( State ( ) ! = IClient : : STATE_DEMOPLAYBACK )
{
// check for errors
2022-01-21 21:13:35 +00:00
if ( State ( ) ! = IClient : : STATE_OFFLINE & & State ( ) < IClient : : STATE_QUITTING & & m_NetClient [ CONN_MAIN ] . State ( ) = = NETSTATE_OFFLINE )
2010-05-29 07:25:38 +00:00
{
SetState ( IClient : : STATE_OFFLINE ) ;
Disconnect ( ) ;
2010-08-17 22:06:00 +00:00
char aBuf [ 256 ] ;
2022-01-21 21:13:35 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " offline error='%s' " , m_NetClient [ CONN_MAIN ] . ErrorString ( ) ) ;
2021-03-08 03:41:27 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf , ClientNetworkErrPrintColor ) ;
2010-05-29 07:25:38 +00:00
}
2020-10-02 13:44:27 +00:00
if ( State ( ) ! = IClient : : STATE_OFFLINE & & State ( ) < IClient : : STATE_QUITTING & & m_DummyConnected & &
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_DUMMY ] . State ( ) = = NETSTATE_OFFLINE )
2020-06-11 10:33:08 +00:00
{
DummyDisconnect ( 0 ) ;
char aBuf [ 256 ] ;
2022-01-21 21:13:35 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " offline dummy error='%s' " , m_NetClient [ CONN_DUMMY ] . ErrorString ( ) ) ;
2021-03-08 03:41:27 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf , ClientNetworkErrPrintColor ) ;
2020-06-11 10:33:08 +00:00
}
2010-05-29 07:25:38 +00:00
//
2022-01-21 21:13:35 +00:00
if ( State ( ) = = IClient : : STATE_CONNECTING & & m_NetClient [ CONN_MAIN ] . State ( ) = = NETSTATE_ONLINE )
2010-05-29 07:25:38 +00:00
{
// we switched to online
2021-03-08 03:41:27 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , " connected, sending info " , ClientNetworkPrintColor ) ;
2010-05-29 07:25:38 +00:00
SetState ( IClient : : STATE_LOADING ) ;
SendInfo ( ) ;
}
}
// process packets
CNetChunk Packet ;
2022-01-21 21:13:35 +00:00
for ( int i = 0 ; i < NUM_CONNS ; i + + )
2011-03-17 16:41:57 +00:00
{
2014-04-26 18:29:42 +00:00
while ( m_NetClient [ i ] . Recv ( & Packet ) )
{
2022-01-21 00:54:14 +00:00
if ( Packet . m_ClientID = = - 1 )
2014-04-26 18:29:42 +00:00
{
ProcessConnlessPacket ( & Packet ) ;
2022-01-21 00:54:14 +00:00
continue ;
2014-04-26 18:29:42 +00:00
}
2022-01-21 00:54:14 +00:00
if ( i > 1 )
2014-04-26 18:29:42 +00:00
{
2022-01-21 00:54:14 +00:00
continue ;
2014-04-26 18:29:42 +00:00
}
2022-01-21 00:54:14 +00:00
ProcessServerPacket ( & Packet , i , g_Config . m_ClDummy ^ i ) ;
2014-04-26 18:29:42 +00:00
}
2011-03-17 16:41:57 +00:00
}
2010-05-29 07:25:38 +00:00
}
void CClient : : OnDemoPlayerSnapshot ( void * pData , int Size )
{
// update ticks, they could have changed
const CDemoPlayer : : CPlaybackInfo * pInfo = m_DemoPlayer . Info ( ) ;
CSnapshotStorage : : CHolder * pTemp ;
2014-04-28 15:26:31 +00:00
m_CurGameTick [ g_Config . m_ClDummy ] = pInfo - > m_Info . m_CurrentTick ;
m_PrevGameTick [ g_Config . m_ClDummy ] = pInfo - > m_PreviousTick ;
2010-05-29 07:25:38 +00:00
// handle snapshots
2014-05-03 18:24:45 +00:00
pTemp = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] = pTemp ;
2010-05-29 07:25:38 +00:00
2014-05-03 18:24:45 +00:00
mem_copy ( m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pSnap , pData , Size ) ;
mem_copy ( m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pAltSnap , pData , Size ) ;
2010-05-29 07:25:38 +00:00
GameClient ( ) - > OnNewSnapshot ( ) ;
}
void CClient : : OnDemoPlayerMessage ( void * pData , int Size )
{
CUnpacker Unpacker ;
Unpacker . Reset ( pData , Size ) ;
2021-12-08 15:50:41 +00:00
CMsgPacker Packer ( NETMSG_EX , true ) ;
2010-05-29 07:25:38 +00:00
// unpack msgid and system flag
2021-12-08 15:50:41 +00:00
int Msg ;
bool Sys ;
CUuid Uuid ;
2010-05-29 07:25:38 +00:00
2021-12-08 15:50:41 +00:00
int Result = UnpackMessageID ( & Msg , & Sys , & Uuid , & Unpacker , & Packer ) ;
if ( Result = = UNPACKMESSAGE_ERROR )
{
2010-05-29 07:25:38 +00:00
return ;
2021-12-08 15:50:41 +00:00
}
2010-05-29 07:25:38 +00:00
if ( ! Sys )
2022-01-21 21:13:35 +00:00
GameClient ( ) - > OnMessage ( Msg , & Unpacker , CONN_MAIN , false ) ;
2010-05-29 07:25:38 +00:00
}
/*
const IDemoPlayer : : CInfo * client_demoplayer_getinfo ( )
{
static DEMOPLAYBACK_INFO ret ;
const DEMOREC_PLAYBACKINFO * info = m_DemoPlayer . Info ( ) ;
ret . first_tick = info - > first_tick ;
ret . last_tick = info - > last_tick ;
ret . current_tick = info - > current_tick ;
ret . paused = info - > paused ;
ret . speed = info - > speed ;
return & ret ;
} */
/*
void DemoPlayer ( ) - > SetPos ( float percent )
{
demorec_playback_set ( percent ) ;
}
void DemoPlayer ( ) - > SetSpeed ( float speed )
{
demorec_playback_setspeed ( speed ) ;
}
void DemoPlayer ( ) - > SetPause ( int paused )
{
if ( paused )
demorec_playback_pause ( ) ;
else
demorec_playback_unpause ( ) ;
} */
2021-07-19 18:14:12 +00:00
void CClient : : UpdateDemoIntraTimers ( )
{
// update timers
const CDemoPlayer : : CPlaybackInfo * pInfo = m_DemoPlayer . Info ( ) ;
m_CurGameTick [ g_Config . m_ClDummy ] = pInfo - > m_Info . m_CurrentTick ;
m_PrevGameTick [ g_Config . m_ClDummy ] = pInfo - > m_PreviousTick ;
m_GameIntraTick [ g_Config . m_ClDummy ] = pInfo - > m_IntraTick ;
m_GameTickTime [ g_Config . m_ClDummy ] = pInfo - > m_TickTime ;
m_GameIntraTickSincePrev [ g_Config . m_ClDummy ] = pInfo - > m_IntraTickSincePrev ;
} ;
2010-05-29 07:25:38 +00:00
void CClient : : Update ( )
{
if ( State ( ) = = IClient : : STATE_DEMOPLAYBACK )
{
2016-08-30 23:39:59 +00:00
# if defined(CONF_VIDEORECORDER)
2020-09-26 19:41:58 +00:00
if ( m_DemoPlayer . IsPlaying ( ) & & IVideo : : Current ( ) )
2020-09-06 13:43:02 +00:00
{
2022-03-02 08:32:51 +00:00
IVideo : : Current ( ) - > NextVideoFrame ( ) ;
IVideo : : Current ( ) - > NextAudioFrameTimeline ( Sound ( ) - > GetSoundMixFunc ( ) ) ;
2020-09-06 13:43:02 +00:00
}
else if ( m_ButtonRender )
Disconnect ( ) ;
2016-08-30 23:39:59 +00:00
# endif
2010-05-29 07:25:38 +00:00
m_DemoPlayer . Update ( ) ;
2016-08-30 23:39:59 +00:00
2010-05-29 07:25:38 +00:00
if ( m_DemoPlayer . IsPlaying ( ) )
{
// update timers
const CDemoPlayer : : CPlaybackInfo * pInfo = m_DemoPlayer . Info ( ) ;
2014-04-28 15:26:31 +00:00
m_CurGameTick [ g_Config . m_ClDummy ] = pInfo - > m_Info . m_CurrentTick ;
m_PrevGameTick [ g_Config . m_ClDummy ] = pInfo - > m_PreviousTick ;
m_GameIntraTick [ g_Config . m_ClDummy ] = pInfo - > m_IntraTick ;
m_GameTickTime [ g_Config . m_ClDummy ] = pInfo - > m_TickTime ;
2010-05-29 07:25:38 +00:00
}
else
{
// disconnect on error
Disconnect ( ) ;
}
}
2020-07-12 15:32:56 +00:00
else if ( State ( ) = = IClient : : STATE_ONLINE )
2010-05-29 07:25:38 +00:00
{
2020-07-12 15:32:56 +00:00
if ( m_LastDummy ! = ( bool ) g_Config . m_ClDummy )
{
// Invalidate references to !m_ClDummy snapshots
GameClient ( ) - > InvalidateSnapshot ( ) ;
GameClient ( ) - > OnDummySwap ( ) ;
}
2016-01-23 20:44:45 +00:00
if ( m_ReceivedSnapshots [ ! g_Config . m_ClDummy ] > = 3 )
2016-01-23 20:42:35 +00:00
{
// switch dummy snapshot
2021-06-23 05:05:49 +00:00
int64_t Now = m_GameTime [ ! g_Config . m_ClDummy ] . Get ( time_get ( ) ) ;
2022-02-14 23:12:52 +00:00
while ( true )
2016-01-23 20:42:35 +00:00
{
CSnapshotStorage : : CHolder * pCur = m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_CURRENT ] ;
2021-06-23 05:05:49 +00:00
int64_t TickStart = ( pCur - > m_Tick ) * time_freq ( ) / 50 ;
2016-01-23 20:42:35 +00:00
if ( TickStart < Now )
{
CSnapshotStorage : : CHolder * pNext = m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pNext ;
if ( pNext )
{
m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_PREV ] = m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_CURRENT ] ;
m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_CURRENT ] = pNext ;
// set ticks
m_CurGameTick [ ! g_Config . m_ClDummy ] = m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_Tick ;
m_PrevGameTick [ ! g_Config . m_ClDummy ] = m_aSnapshots [ ! g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_Tick ;
}
else
break ;
}
else
break ;
}
}
2020-07-12 15:32:56 +00:00
if ( m_ReceivedSnapshots [ g_Config . m_ClDummy ] > = 3 )
2010-05-29 07:25:38 +00:00
{
2020-07-12 15:32:56 +00:00
// switch snapshot
2022-04-08 17:40:28 +00:00
bool Repredict = false ;
2021-06-23 05:05:49 +00:00
int64_t Now = m_GameTime [ g_Config . m_ClDummy ] . Get ( time_get ( ) ) ;
int64_t PredNow = m_PredictedTime . Get ( time_get ( ) ) ;
2010-05-29 07:25:38 +00:00
2020-07-12 15:32:56 +00:00
if ( m_LastDummy ! = ( bool ) g_Config . m_ClDummy & & m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] & & m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] )
2010-05-29 07:25:38 +00:00
{
2020-07-12 15:32:56 +00:00
// Load snapshot for m_ClDummy
GameClient ( ) - > OnNewSnapshot ( ) ;
2022-04-08 17:40:28 +00:00
Repredict = true ;
2020-07-12 15:32:56 +00:00
}
2010-05-29 07:25:38 +00:00
2022-02-14 23:12:52 +00:00
while ( true )
2020-07-12 15:32:56 +00:00
{
CSnapshotStorage : : CHolder * pCur = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] ;
2021-06-23 05:05:49 +00:00
int64_t TickStart = ( pCur - > m_Tick ) * time_freq ( ) / 50 ;
2010-05-29 07:25:38 +00:00
2020-07-12 15:32:56 +00:00
if ( TickStart < Now )
{
CSnapshotStorage : : CHolder * pNext = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pNext ;
if ( pNext )
2010-05-29 07:25:38 +00:00
{
2020-07-12 15:32:56 +00:00
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] = pNext ;
// set ticks
m_CurGameTick [ g_Config . m_ClDummy ] = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_Tick ;
m_PrevGameTick [ g_Config . m_ClDummy ] = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_Tick ;
if ( m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] & & m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] )
{
GameClient ( ) - > OnNewSnapshot ( ) ;
2022-04-08 17:40:28 +00:00
Repredict = true ;
2020-07-12 15:32:56 +00:00
}
2010-05-29 07:25:38 +00:00
}
2020-07-12 15:32:56 +00:00
else
break ;
2010-05-29 07:25:38 +00:00
}
else
break ;
}
2020-07-12 15:32:56 +00:00
if ( m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] & & m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] )
{
2022-01-09 12:06:41 +00:00
int64_t CurTickStart = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_Tick * time_freq ( ) / SERVER_TICK_SPEED ;
int64_t PrevTickStart = m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_Tick * time_freq ( ) / SERVER_TICK_SPEED ;
int PrevPredTick = ( int ) ( PredNow * SERVER_TICK_SPEED / time_freq ( ) ) ;
2020-09-26 19:41:58 +00:00
int NewPredTick = PrevPredTick + 1 ;
2010-05-29 07:25:38 +00:00
2022-01-09 12:06:41 +00:00
m_GameIntraTick [ g_Config . m_ClDummy ] = ( Now - PrevTickStart ) / ( float ) ( CurTickStart - PrevTickStart ) ;
m_GameTickTime [ g_Config . m_ClDummy ] = ( Now - PrevTickStart ) / ( float ) time_freq ( ) ;
m_GameIntraTickSincePrev [ g_Config . m_ClDummy ] = ( Now - PrevTickStart ) / ( float ) ( time_freq ( ) / SERVER_TICK_SPEED ) ;
2010-05-29 07:25:38 +00:00
2022-01-09 12:06:41 +00:00
int64_t CurPredTickStart = NewPredTick * time_freq ( ) / SERVER_TICK_SPEED ;
int64_t PrevPredTickStart = PrevPredTick * time_freq ( ) / SERVER_TICK_SPEED ;
m_PredIntraTick [ g_Config . m_ClDummy ] = ( PredNow - PrevPredTickStart ) / ( float ) ( CurPredTickStart - PrevPredTickStart ) ;
2010-05-29 07:25:38 +00:00
2022-01-09 12:06:41 +00:00
if ( absolute ( NewPredTick - m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_Tick ) > MaxLatencyTicks ( ) )
2020-07-12 15:32:56 +00:00
{
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , " prediction time reset! " ) ;
2022-01-09 12:06:41 +00:00
m_PredictedTime . Init ( CurTickStart + 2 * time_freq ( ) / SERVER_TICK_SPEED ) ;
2020-07-12 15:32:56 +00:00
}
if ( NewPredTick > m_PredTick [ g_Config . m_ClDummy ] )
{
m_PredTick [ g_Config . m_ClDummy ] = NewPredTick ;
2022-04-08 17:40:28 +00:00
Repredict = true ;
2020-07-12 15:32:56 +00:00
// send input
SendInput ( ) ;
}
2010-05-29 07:25:38 +00:00
}
2020-07-12 15:32:56 +00:00
// only do sane predictions
if ( Repredict )
2010-05-29 07:25:38 +00:00
{
2022-01-09 12:06:41 +00:00
if ( m_PredTick [ g_Config . m_ClDummy ] > m_CurGameTick [ g_Config . m_ClDummy ] & & m_PredTick [ g_Config . m_ClDummy ] < m_CurGameTick [ g_Config . m_ClDummy ] + MaxLatencyTicks ( ) )
2020-07-12 15:32:56 +00:00
GameClient ( ) - > OnPredict ( ) ;
2010-05-29 07:25:38 +00:00
}
2020-07-12 15:32:56 +00:00
// fetch server info if we don't have it
if ( State ( ) > = IClient : : STATE_LOADING & &
m_CurrentServerInfoRequestTime > = 0 & &
time_get ( ) > m_CurrentServerInfoRequestTime )
{
2021-06-09 15:29:06 +00:00
m_ServerBrowser . RequestCurrentServer ( m_ServerAddress ) ;
2020-09-26 19:41:58 +00:00
m_CurrentServerInfoRequestTime = time_get ( ) + time_freq ( ) * 2 ;
2020-07-12 15:32:56 +00:00
}
2021-04-23 21:12:16 +00:00
// periodically ping server
if ( State ( ) = = IClient : : STATE_ONLINE & &
m_CurrentServerNextPingTime > = 0 & &
time_get ( ) > m_CurrentServerNextPingTime )
{
2022-03-20 11:57:50 +00:00
int64_t NowPing = time_get ( ) ;
2021-06-23 05:05:49 +00:00
int64_t Freq = time_freq ( ) ;
2021-04-23 21:12:16 +00:00
2021-04-24 12:23:35 +00:00
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " pinging current server%s " , ! m_ServerCapabilities . m_PingEx ? " , using fallback via server info " : " " ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_ADDINFO , " client " , aBuf ) ;
m_CurrentServerPingUuid = RandomUuid ( ) ;
if ( ! m_ServerCapabilities . m_PingEx )
{
2021-06-09 15:29:06 +00:00
m_ServerBrowser . RequestCurrentServerWithRandomToken ( m_ServerAddress , & m_CurrentServerPingBasicToken , & m_CurrentServerPingToken ) ;
2021-04-24 12:23:35 +00:00
}
else
{
CMsgPacker Msg ( NETMSG_PINGEX , true ) ;
Msg . AddRaw ( & m_CurrentServerPingUuid , sizeof ( m_CurrentServerPingUuid ) ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_MAIN , & Msg , MSGFLAG_FLUSH ) ;
2021-04-24 12:23:35 +00:00
}
2022-03-20 11:57:50 +00:00
m_CurrentServerCurrentPingTime = NowPing ;
m_CurrentServerNextPingTime = NowPing + 600 * Freq ; // ping every 10 minutes
2021-04-23 21:12:16 +00:00
}
2010-05-29 07:25:38 +00:00
}
2020-07-12 15:32:56 +00:00
m_LastDummy = ( bool ) g_Config . m_ClDummy ;
2010-05-29 07:25:38 +00:00
}
// STRESS TEST: join the server again
2017-06-02 18:45:09 +00:00
# ifdef CONF_DEBUG
2010-05-29 07:25:38 +00:00
if ( g_Config . m_DbgStress )
{
2021-06-23 05:05:49 +00:00
static int64_t ActionTaken = 0 ;
int64_t Now = time_get ( ) ;
2010-05-29 07:25:38 +00:00
if ( State ( ) = = IClient : : STATE_OFFLINE )
{
2020-09-26 19:41:58 +00:00
if ( Now > ActionTaken + time_freq ( ) * 2 )
2010-05-29 07:25:38 +00:00
{
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " stress " , " reconnecting! " ) ;
2010-05-29 07:25:38 +00:00
Connect ( g_Config . m_DbgStressServer ) ;
ActionTaken = Now ;
}
}
2011-02-13 16:59:51 +00:00
else
{
2020-09-26 19:41:58 +00:00
if ( Now > ActionTaken + time_freq ( ) * ( 10 + g_Config . m_DbgStress ) )
2011-02-13 16:59:51 +00:00
{
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " stress " , " disconnecting! " ) ;
Disconnect ( ) ;
ActionTaken = Now ;
}
}
2010-05-29 07:25:38 +00:00
}
2017-06-02 18:45:09 +00:00
# endif
2010-05-29 07:25:38 +00:00
// pump the network
PumpNetwork ( ) ;
2017-08-30 19:34:01 +00:00
2015-01-28 12:13:56 +00:00
if ( m_pMapdownloadTask )
{
2018-06-19 12:45:53 +00:00
if ( m_pMapdownloadTask - > State ( ) = = HTTP_DONE )
2014-10-29 12:37:38 +00:00
FinishMapDownload ( ) ;
2018-06-19 12:45:53 +00:00
else if ( m_pMapdownloadTask - > State ( ) = = HTTP_ERROR )
2015-01-28 12:13:56 +00:00
{
2016-05-02 19:35:32 +00:00
dbg_msg ( " webdl " , " http failed, falling back to gameserver " ) ;
2014-10-29 12:37:38 +00:00
ResetMapDownload ( ) ;
SendMapRequest ( ) ;
}
2018-06-19 12:45:53 +00:00
else if ( m_pMapdownloadTask - > State ( ) = = HTTP_ABORTED )
2015-01-28 12:13:56 +00:00
{
2017-04-11 23:20:39 +00:00
m_pMapdownloadTask = NULL ;
2015-01-28 12:13:56 +00:00
}
2014-10-29 12:37:38 +00:00
}
2017-09-03 15:36:51 +00:00
if ( m_pDDNetInfoTask )
2017-08-30 19:34:01 +00:00
{
2018-06-19 12:45:53 +00:00
if ( m_pDDNetInfoTask - > State ( ) = = HTTP_DONE )
2017-09-03 15:36:51 +00:00
FinishDDNetInfo ( ) ;
2018-06-19 12:45:53 +00:00
else if ( m_pDDNetInfoTask - > State ( ) = = HTTP_ERROR )
2017-08-30 19:34:01 +00:00
{
2020-09-04 13:32:35 +00:00
Storage ( ) - > RemoveFile ( m_aDDNetInfoTmp , IStorage : : TYPE_SAVE ) ;
2017-09-03 15:36:51 +00:00
ResetDDNetInfo ( ) ;
2017-08-30 19:34:01 +00:00
}
2018-06-19 12:45:53 +00:00
else if ( m_pDDNetInfoTask - > State ( ) = = HTTP_ABORTED )
2017-08-30 19:34:01 +00:00
{
2020-09-04 13:32:35 +00:00
Storage ( ) - > RemoveFile ( m_aDDNetInfoTmp , IStorage : : TYPE_SAVE ) ;
2017-09-03 15:36:51 +00:00
m_pDDNetInfoTask = NULL ;
2017-08-30 19:34:01 +00:00
}
}
2010-05-29 07:25:38 +00:00
2019-05-28 11:24:55 +00:00
if ( State ( ) = = IClient : : STATE_ONLINE )
{
2022-01-22 12:54:25 +00:00
if ( ! m_EditJobs . empty ( ) )
2019-05-28 11:24:55 +00:00
{
std : : shared_ptr < CDemoEdit > e = m_EditJobs . front ( ) ;
if ( e - > Status ( ) = = IJob : : STATE_DONE )
{
2019-05-31 18:42:28 +00:00
char aBuf [ 256 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " Successfully saved the replay to %s! " , e - > Destination ( ) ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , aBuf ) ;
2019-06-05 17:17:55 +00:00
GameClient ( ) - > Echo ( Localize ( " Successfully saved the replay! " ) ) ;
2019-05-28 11:24:55 +00:00
m_EditJobs . pop_front ( ) ;
}
}
}
2010-05-29 07:25:38 +00:00
// update the server browser
2011-03-18 18:03:13 +00:00
m_ServerBrowser . Update ( m_ResortServerBrowser ) ;
m_ResortServerBrowser = false ;
2016-04-29 21:05:20 +00:00
// update gameclient
if ( ! m_EditorActive )
GameClient ( ) - > OnUpdate ( ) ;
2016-05-04 16:23:00 +00:00
2021-01-17 23:52:58 +00:00
Discord ( ) - > Update ( ) ;
2020-09-06 15:08:38 +00:00
Steam ( ) - > Update ( ) ;
if ( Steam ( ) - > GetConnectAddress ( ) )
{
2021-12-21 09:31:34 +00:00
HandleConnectAddress ( Steam ( ) - > GetConnectAddress ( ) ) ;
2020-09-06 15:08:38 +00:00
Steam ( ) - > ClearConnectAddress ( ) ;
}
2016-05-04 16:23:00 +00:00
if ( m_ReconnectTime > 0 & & time_get ( ) > m_ReconnectTime )
{
2021-04-15 10:44:24 +00:00
if ( State ( ) ! = STATE_ONLINE )
Connect ( m_aServerAddressStr ) ;
2016-05-04 16:23:00 +00:00
m_ReconnectTime = 0 ;
}
2022-01-09 12:06:41 +00:00
2022-01-09 13:14:45 +00:00
m_PredictedTime . UpdateMargin ( PredictionMargin ( ) * time_freq ( ) / 1000 ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : RegisterInterfaces ( )
{
2020-09-26 19:41:58 +00:00
Kernel ( ) - > RegisterInterface ( static_cast < IDemoRecorder * > ( & m_DemoRecorder [ RECORDER_MANUAL ] ) , false ) ;
Kernel ( ) - > RegisterInterface ( static_cast < IDemoPlayer * > ( & m_DemoPlayer ) , false ) ;
Kernel ( ) - > RegisterInterface ( static_cast < IGhostRecorder * > ( & m_GhostRecorder ) , false ) ;
Kernel ( ) - > RegisterInterface ( static_cast < IGhostLoader * > ( & m_GhostLoader ) , false ) ;
Kernel ( ) - > RegisterInterface ( static_cast < IServerBrowser * > ( & m_ServerBrowser ) , false ) ;
2018-09-20 12:06:18 +00:00
# if defined(CONF_AUTOUPDATE)
2020-09-26 19:41:58 +00:00
Kernel ( ) - > RegisterInterface ( static_cast < IUpdater * > ( & m_Updater ) , false ) ;
2015-02-27 21:08:34 +00:00
# endif
2020-09-26 19:41:58 +00:00
Kernel ( ) - > RegisterInterface ( static_cast < IFriends * > ( & m_Friends ) , false ) ;
Kernel ( ) - > ReregisterInterface ( static_cast < IFriends * > ( & m_Foes ) ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : InitInterfaces ( )
{
// fetch interfaces
2011-02-27 14:03:57 +00:00
m_pEngine = Kernel ( ) - > RequestInterface < IEngine > ( ) ;
2010-05-29 07:25:38 +00:00
m_pEditor = Kernel ( ) - > RequestInterface < IEditor > ( ) ;
2017-09-01 06:01:26 +00:00
//m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
2010-05-29 07:25:38 +00:00
m_pSound = Kernel ( ) - > RequestInterface < IEngineSound > ( ) ;
m_pGameClient = Kernel ( ) - > RequestInterface < IGameClient > ( ) ;
m_pInput = Kernel ( ) - > RequestInterface < IEngineInput > ( ) ;
m_pMap = Kernel ( ) - > RequestInterface < IEngineMap > ( ) ;
2021-01-10 12:47:07 +00:00
m_pConfigManager = Kernel ( ) - > RequestInterface < IConfigManager > ( ) ;
m_pConfig = m_pConfigManager - > Values ( ) ;
2018-09-20 12:06:18 +00:00
# if defined(CONF_AUTOUPDATE)
2015-04-18 19:17:27 +00:00
m_pUpdater = Kernel ( ) - > RequestInterface < IUpdater > ( ) ;
2015-02-27 21:08:34 +00:00
# endif
2021-01-17 23:52:58 +00:00
m_pDiscord = Kernel ( ) - > RequestInterface < IDiscord > ( ) ;
2020-08-20 10:17:44 +00:00
m_pSteam = Kernel ( ) - > RequestInterface < ISteam > ( ) ;
2010-05-29 07:25:38 +00:00
m_pStorage = Kernel ( ) - > RequestInterface < IStorage > ( ) ;
2014-08-12 14:21:06 +00:00
m_DemoEditor . Init ( m_pGameClient - > NetVersion ( ) , & m_SnapshotDelta , m_pConsole , m_pStorage ) ;
2015-07-09 00:08:14 +00:00
2022-01-21 21:13:35 +00:00
m_ServerBrowser . SetBaseInfo ( & m_NetClient [ CONN_CONTACT ] , m_pGameClient - > NetVersion ( ) ) ;
2014-08-12 14:21:06 +00:00
2018-07-11 18:17:21 +00:00
HttpInit ( m_pStorage ) ;
2015-02-27 21:08:34 +00:00
2018-09-20 12:06:18 +00:00
# if defined(CONF_AUTOUPDATE)
2015-04-18 19:17:27 +00:00
m_Updater . Init ( ) ;
2015-02-27 21:08:34 +00:00
# endif
2014-10-29 12:37:38 +00:00
2011-03-23 12:06:35 +00:00
m_Friends . Init ( ) ;
2015-07-22 20:16:49 +00:00
m_Foes . Init ( true ) ;
2017-10-28 12:23:24 +00:00
m_GhostRecorder . Init ( ) ;
m_GhostLoader . Init ( ) ;
2010-05-29 07:25:38 +00:00
}
void CClient : : Run ( )
{
m_LocalStartTime = time_get ( ) ;
2016-08-30 23:39:59 +00:00
# if defined(CONF_VIDEORECORDER)
IVideo : : SetLocalStartTime ( m_LocalStartTime ) ;
# endif
2019-09-30 13:03:37 +00:00
m_SnapshotParts [ 0 ] = 0 ;
m_SnapshotParts [ 1 ] = 0 ;
2010-05-29 07:25:38 +00:00
2016-10-03 11:56:15 +00:00
if ( m_GenerateTimeoutSeed )
{
GenerateTimeoutSeed ( ) ;
}
2016-10-02 09:31:11 +00:00
unsigned int Seed ;
secure_random_fill ( & Seed , sizeof ( Seed ) ) ;
srand ( Seed ) ;
2014-08-13 10:58:53 +00:00
2017-05-21 23:07:13 +00:00
if ( g_Config . m_Debug )
{
g_UuidManager . DebugDump ( ) ;
}
2012-01-03 20:39:10 +00:00
// init SDL
{
if ( SDL_Init ( 0 ) < 0 )
{
dbg_msg ( " client " , " unable to init SDL base: %s " , SDL_GetError ( ) ) ;
return ;
}
2021-08-24 10:18:20 +00:00
# ifndef CONF_PLATFORM_ANDROID
2022-02-16 19:54:11 +00:00
atexit ( SDL_Quit ) ;
2021-08-24 10:18:20 +00:00
# endif
2012-01-03 20:39:10 +00:00
}
2017-09-01 06:01:26 +00:00
// init graphics
{
m_pGraphics = CreateEngineGraphicsThreaded ( ) ;
bool RegisterFail = false ;
2019-04-11 10:21:42 +00:00
RegisterFail = RegisterFail | | ! Kernel ( ) - > RegisterInterface ( m_pGraphics ) ; // IEngineGraphics
2020-09-26 19:41:58 +00:00
RegisterFail = RegisterFail | | ! Kernel ( ) - > RegisterInterface ( static_cast < IGraphics * > ( m_pGraphics ) , false ) ;
2017-09-01 06:01:26 +00:00
if ( RegisterFail | | m_pGraphics - > Init ( ) ! = 0 )
{
dbg_msg ( " client " , " couldn't init graphics " ) ;
return ;
}
}
2020-10-11 09:09:24 +00:00
// make sure the first frame just clears everything to prevent undesired colors when waiting for io
Graphics ( ) - > Clear ( 0 , 0 , 0 ) ;
Graphics ( ) - > Swap ( ) ;
2012-01-03 20:39:10 +00:00
// init sound, allowed to fail
m_SoundInitFailed = Sound ( ) - > Init ( ) ! = 0 ;
2010-05-29 07:25:38 +00:00
2016-08-27 19:10:27 +00:00
# if defined(CONF_VIDEORECORDER)
// init video recorder aka ffmpeg
CVideo : : Init ( ) ;
# endif
2022-03-25 08:26:37 +00:00
# ifndef CONF_WEBASM
2011-04-04 16:20:05 +00:00
// open socket
{
NETADDR BindAddr ;
2012-03-04 11:47:16 +00:00
if ( g_Config . m_Bindaddr [ 0 ] & & net_host_lookup ( g_Config . m_Bindaddr , & BindAddr , NETTYPE_ALL ) = = 0 )
{
// got bindaddr
BindAddr . type = NETTYPE_ALL ;
}
else
2012-01-06 18:17:14 +00:00
{
mem_zero ( & BindAddr , sizeof ( BindAddr ) ) ;
BindAddr . type = NETTYPE_ALL ;
}
2022-03-30 13:16:19 +00:00
for ( unsigned int i = 0 ; i < std : : size ( m_NetClient ) ; i + + )
2011-04-04 16:20:05 +00:00
{
2022-01-21 21:13:35 +00:00
BindAddr . port = i = = CONN_MAIN ? g_Config . m_ClPort : i = = CONN_DUMMY ? g_Config . m_ClDummyPort : g_Config . m_ClContactPort ;
2022-03-01 16:34:57 +00:00
while ( BindAddr . port = = 0 | | ! m_NetClient [ i ] . Open ( BindAddr ) )
2014-04-26 18:29:42 +00:00
{
2016-01-02 14:37:44 +00:00
BindAddr . port = ( secure_rand ( ) % 64511 ) + 1024 ;
2021-12-21 11:49:04 +00:00
}
2011-04-04 16:20:05 +00:00
}
}
2022-03-25 08:26:37 +00:00
# endif
2011-04-04 16:20:05 +00:00
2010-05-29 07:25:38 +00:00
// init font rendering
Kernel ( ) - > RequestInterface < IEngineTextRender > ( ) - > Init ( ) ;
// init the input
Input ( ) - > Init ( ) ;
// init the editor
2012-01-06 11:55:19 +00:00
m_pEditor - > Init ( ) ;
2010-05-29 07:25:38 +00:00
2017-06-11 17:53:55 +00:00
// load and save a map to fix it
2017-06-11 18:18:28 +00:00
/*if(m_pEditor->Load(arg, IStorage::TYPE_ALL))
m_pEditor - > Save ( arg ) ;
return ; */
2010-05-29 07:25:38 +00:00
// load data
if ( ! LoadData ( ) )
return ;
2020-08-20 10:19:03 +00:00
if ( Steam ( ) - > GetPlayerName ( ) )
{
str_copy ( g_Config . m_SteamName , Steam ( ) - > GetPlayerName ( ) , sizeof ( g_Config . m_SteamName ) ) ;
}
2010-05-29 07:25:38 +00:00
GameClient ( ) - > OnInit ( ) ;
2021-06-02 22:27:00 +00:00
m_ServerBrowser . OnInit ( ) ;
2011-12-30 15:02:22 +00:00
2021-12-21 22:05:44 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , " version " GAME_RELEASE_VERSION " on " CONF_PLATFORM_STRING " " CONF_ARCH_STRING , ColorRGBA ( 0.7f , 0.7f , 1 , 1.0f ) ) ;
if ( GIT_SHORTREV_HASH )
{
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " git revision hash: %s " , GIT_SHORTREV_HASH ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " server " , aBuf , ColorRGBA ( 0.7f , 0.7f , 1 , 1.0f ) ) ;
}
2010-05-29 07:25:38 +00:00
// connect to the server if wanted
/*
if ( config . cl_connect [ 0 ] ! = 0 )
Connect ( config . cl_connect ) ;
config . cl_connect [ 0 ] = 0 ;
*/
//
2015-11-08 15:25:14 +00:00
m_FpsGraph . Init ( 0.0f , 120.0f ) ;
2010-05-29 07:25:38 +00:00
// never start with the editor
g_Config . m_ClEditor = 0 ;
2010-08-07 18:22:25 +00:00
// process pending commands
2011-08-13 00:11:06 +00:00
m_pConsole - > StoreCommands ( false ) ;
2015-07-09 00:08:14 +00:00
2016-05-02 21:36:21 +00:00
# if defined(CONF_FAMILY_UNIX)
m_Fifo . Init ( m_pConsole , g_Config . m_ClInputFifo , CFGFLAG_CLIENT ) ;
# endif
2022-01-31 02:11:47 +00:00
InitChecksum ( ) ;
m_pConsole - > InitChecksum ( ChecksumData ( ) ) ;
2018-08-23 07:57:35 +00:00
// loads the existing ddnet info file if it exists
2017-09-03 15:36:51 +00:00
LoadDDNetInfo ( ) ;
// but still request the new one from server
2017-09-07 18:51:46 +00:00
if ( g_Config . m_ClShowWelcome )
2017-09-08 20:16:00 +00:00
g_Config . m_ClShowWelcome = 0 ;
else
2017-09-07 18:51:46 +00:00
RequestDDNetInfo ( ) ;
2017-09-03 07:00:57 +00:00
2014-01-31 00:41:57 +00:00
bool LastD = false ;
bool LastE = false ;
bool LastG = false ;
2018-12-07 22:52:33 +00:00
2021-06-23 05:05:49 +00:00
int64_t LastTime = time_get_microseconds ( ) ;
int64_t LastRenderTime = time_get ( ) ;
2017-06-02 19:33:45 +00:00
2022-02-14 23:12:52 +00:00
while ( true )
2010-05-29 07:25:38 +00:00
{
2016-06-28 21:35:59 +00:00
set_new_tick ( ) ;
2010-05-29 07:25:38 +00:00
// handle pending connects
if ( m_aCmdConnect [ 0 ] )
{
str_copy ( g_Config . m_UiServerAddress , m_aCmdConnect , sizeof ( g_Config . m_UiServerAddress ) ) ;
Connect ( m_aCmdConnect ) ;
m_aCmdConnect [ 0 ] = 0 ;
}
2019-04-08 19:42:49 +00:00
// handle pending demo play
if ( m_aCmdPlayDemo [ 0 ] )
{
2019-04-10 06:56:20 +00:00
const char * pError = DemoPlayer_Play ( m_aCmdPlayDemo , IStorage : : TYPE_ABSOLUTE ) ;
if ( pError )
dbg_msg ( " demo_player " , " playing passed demo file '%s' failed: %s " , m_aCmdPlayDemo , pError ) ;
2019-04-08 19:42:49 +00:00
m_aCmdPlayDemo [ 0 ] = 0 ;
}
2020-08-02 11:00:01 +00:00
// handle pending map edits
if ( m_aCmdEditMap [ 0 ] )
{
int Result = m_pEditor - > Load ( m_aCmdEditMap , IStorage : : TYPE_ABSOLUTE ) ;
if ( Result )
g_Config . m_ClEditor = true ;
else
dbg_msg ( " demo_player " , " editing passed map file '%s' failed " , m_aCmdEditMap ) ;
m_aCmdEditMap [ 0 ] = 0 ;
}
2015-04-19 21:09:55 +00:00
// progress on dummy connect if security token handshake skipped/passed
2022-01-21 21:13:35 +00:00
if ( m_DummySendConnInfo & & ! m_NetClient [ CONN_DUMMY ] . SecurityTokenUnknown ( ) )
2015-04-19 17:53:37 +00:00
{
m_DummySendConnInfo = false ;
// send client info
2020-05-22 15:58:41 +00:00
CMsgPacker MsgVer ( NETMSG_CLIENTVER , true ) ;
MsgVer . AddRaw ( & m_ConnectionID , sizeof ( m_ConnectionID ) ) ;
MsgVer . AddInt ( GameClient ( ) - > DDNetVersion ( ) ) ;
MsgVer . AddString ( GameClient ( ) - > DDNetVersionStr ( ) , 0 ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_DUMMY , & MsgVer , MSGFLAG_VITAL ) ;
2020-05-22 15:58:41 +00:00
2012-08-09 08:30:04 +00:00
CMsgPacker MsgInfo ( NETMSG_INFO , true ) ;
2015-04-19 17:53:37 +00:00
MsgInfo . AddString ( GameClient ( ) - > NetVersion ( ) , 128 ) ;
2017-07-15 15:29:20 +00:00
MsgInfo . AddString ( m_Password , 128 ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_DUMMY , & MsgInfo , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2015-04-19 17:53:37 +00:00
// update netclient
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_DUMMY ] . Update ( ) ;
2015-04-19 17:53:37 +00:00
// send ready
2012-08-09 08:30:04 +00:00
CMsgPacker MsgReady ( NETMSG_READY , true ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_DUMMY , & MsgReady , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2015-04-19 17:53:37 +00:00
// startinfo
GameClient ( ) - > SendDummyInfo ( true ) ;
// send enter game an finish the connection
2012-08-09 08:30:04 +00:00
CMsgPacker MsgEnter ( NETMSG_ENTERGAME , true ) ;
2022-01-21 21:13:35 +00:00
SendMsg ( CONN_DUMMY , & MsgEnter , MSGFLAG_VITAL | MSGFLAG_FLUSH ) ;
2015-04-19 17:53:37 +00:00
}
2010-05-29 07:25:38 +00:00
// update input
2010-12-11 21:04:50 +00:00
if ( Input ( ) - > Update ( ) )
2020-10-02 16:02:10 +00:00
{
if ( State ( ) = = IClient : : STATE_QUITTING )
break ;
else
SetState ( IClient : : STATE_QUITTING ) ; // SDL_QUIT
}
2018-09-20 12:06:18 +00:00
# if defined(CONF_AUTOUPDATE)
2015-04-18 19:17:27 +00:00
Updater ( ) - > Update ( ) ;
2015-02-27 21:08:34 +00:00
# endif
2014-12-31 14:29:34 +00:00
2010-05-29 07:25:38 +00:00
// update sound
Sound ( ) - > Update ( ) ;
2015-08-24 20:46:28 +00:00
if ( CtrlShiftKey ( KEY_D , LastD ) )
2010-05-29 07:25:38 +00:00
g_Config . m_Debug ^ = 1 ;
2015-08-24 20:46:28 +00:00
if ( CtrlShiftKey ( KEY_G , LastG ) )
2010-05-29 07:25:38 +00:00
g_Config . m_DbgGraphs ^ = 1 ;
2015-08-24 20:46:28 +00:00
if ( CtrlShiftKey ( KEY_E , LastE ) )
2010-05-29 07:25:38 +00:00
{
2020-09-26 19:41:58 +00:00
g_Config . m_ClEditor = g_Config . m_ClEditor ^ 1 ;
2021-10-23 11:48:21 +00:00
Input ( ) - > MouseModeRelative ( ) ;
2016-08-26 18:29:57 +00:00
Input ( ) - > SetIMEState ( true ) ;
2010-05-29 07:25:38 +00:00
}
// render
{
2012-01-06 11:55:19 +00:00
if ( g_Config . m_ClEditor )
2011-01-17 11:28:37 +00:00
{
2012-01-06 11:55:19 +00:00
if ( ! m_EditorActive )
{
2021-10-23 11:48:21 +00:00
Input ( ) - > MouseModeRelative ( ) ;
2012-01-06 11:55:19 +00:00
GameClient ( ) - > OnActivateEditor ( ) ;
2019-04-05 23:15:02 +00:00
m_pEditor - > ResetMentions ( ) ;
2012-01-06 11:55:19 +00:00
m_EditorActive = true ;
}
2011-01-17 11:28:37 +00:00
}
2012-01-06 11:55:19 +00:00
else if ( m_EditorActive )
2011-01-17 11:28:37 +00:00
m_EditorActive = false ;
2010-05-29 07:25:38 +00:00
Update ( ) ;
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2015-07-09 00:08:14 +00:00
2020-04-06 01:18:30 +00:00
bool IsRenderActive = ( g_Config . m_GfxBackgroundRender | | m_pGraphics - > WindowOpen ( ) ) ;
2022-01-20 15:02:02 +00:00
bool AsyncRenderOld = g_Config . m_GfxAsyncRenderOld ;
2021-12-20 16:26:48 +00:00
2022-03-02 08:32:51 +00:00
int GfxRefreshRate = g_Config . m_GfxRefreshRate ;
# if defined(CONF_VIDEORECORDER)
// keep rendering synced
if ( IVideo : : Current ( ) )
{
AsyncRenderOld = false ;
GfxRefreshRate = 0 ;
}
# endif
2020-09-25 16:11:59 +00:00
if ( IsRenderActive & &
2021-12-20 16:26:48 +00:00
( ! AsyncRenderOld | | m_pGraphics - > IsIdle ( ) ) & &
2022-03-02 08:32:51 +00:00
( ! GfxRefreshRate | | ( time_freq ( ) / ( int64_t ) g_Config . m_GfxRefreshRate ) < = Now - LastRenderTime ) )
2010-05-29 07:25:38 +00:00
{
2012-01-01 12:38:46 +00:00
m_RenderFrames + + ;
// update frametime
m_RenderFrameTime = ( Now - m_LastRenderTime ) / ( float ) time_freq ( ) ;
if ( m_RenderFrameTime < m_RenderFrameTimeLow )
m_RenderFrameTimeLow = m_RenderFrameTime ;
if ( m_RenderFrameTime > m_RenderFrameTimeHigh )
m_RenderFrameTimeHigh = m_RenderFrameTime ;
2020-09-26 19:41:58 +00:00
m_FpsGraph . Add ( 1.0f / m_RenderFrameTime , 1 , 1 , 1 ) ;
2012-01-01 12:38:46 +00:00
2020-11-19 15:15:07 +00:00
if ( m_BenchmarkFile )
{
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " Frametime %d us \n " , ( int ) ( m_RenderFrameTime * 1000000 ) ) ;
2021-06-05 14:46:01 +00:00
io_write ( m_BenchmarkFile , aBuf , str_length ( aBuf ) ) ;
2020-11-19 15:15:07 +00:00
if ( time_get ( ) > m_BenchmarkStopTime )
{
io_close ( m_BenchmarkFile ) ;
m_BenchmarkFile = 0 ;
Quit ( ) ;
}
}
2020-09-26 19:41:58 +00:00
m_FrameTimeAvg = m_FrameTimeAvg * 0.9f + m_RenderFrameTime * 0.1f ;
2019-04-11 22:46:54 +00:00
2018-03-01 06:34:14 +00:00
// keep the overflow time - it's used to make sure the gfx refreshrate is reached
2021-06-23 05:05:49 +00:00
int64_t AdditionalTime = g_Config . m_GfxRefreshRate ? ( ( Now - LastRenderTime ) - ( time_freq ( ) / ( int64_t ) g_Config . m_GfxRefreshRate ) ) : 0 ;
2021-10-07 22:20:23 +00:00
// if the value is over the frametime of a 60 fps frame, reset the additional time (drop the frames, that are lost already)
if ( AdditionalTime > ( time_freq ( ) / 60 ) )
AdditionalTime = ( time_freq ( ) / 60 ) ;
2018-03-01 06:34:14 +00:00
LastRenderTime = Now - AdditionalTime ;
2012-01-01 12:38:46 +00:00
m_LastRenderTime = Now ;
2017-06-02 18:45:09 +00:00
# ifdef CONF_DEBUG
2011-12-31 09:04:46 +00:00
if ( g_Config . m_DbgStress )
{
2020-09-26 19:41:58 +00:00
if ( ( m_RenderFrames % 10 ) = = 0 )
2011-12-31 09:04:46 +00:00
{
2012-01-06 11:55:19 +00:00
if ( ! m_EditorActive )
Render ( ) ;
else
{
m_pEditor - > UpdateAndRender ( ) ;
DebugRender ( ) ;
}
2011-12-31 09:04:46 +00:00
m_pGraphics - > Swap ( ) ;
}
}
else
2017-06-02 18:45:09 +00:00
# endif
2010-05-29 07:25:38 +00:00
{
2012-01-06 11:55:19 +00:00
if ( ! m_EditorActive )
Render ( ) ;
else
{
m_pEditor - > UpdateAndRender ( ) ;
DebugRender ( ) ;
}
2010-05-29 07:25:38 +00:00
m_pGraphics - > Swap ( ) ;
}
}
2020-04-06 01:18:30 +00:00
else if ( ! IsRenderActive )
{
// if the client does not render, it should reset its render time to a time where it would render the first frame, when it wakes up again
2021-06-23 05:05:49 +00:00
LastRenderTime = g_Config . m_GfxRefreshRate ? ( Now - ( time_freq ( ) / ( int64_t ) g_Config . m_GfxRefreshRate ) ) : Now ;
2020-04-06 01:18:30 +00:00
}
2018-03-12 15:51:31 +00:00
2014-06-16 11:29:18 +00:00
if ( Input ( ) - > VideoRestartNeeded ( ) )
{
m_pGraphics - > Init ( ) ;
LoadData ( ) ;
GameClient ( ) - > OnInit ( ) ;
}
2010-05-29 07:25:38 +00:00
}
2010-12-12 15:48:13 +00:00
AutoScreenshot_Cleanup ( ) ;
2019-01-11 11:07:09 +00:00
AutoStatScreenshot_Cleanup ( ) ;
2017-04-26 03:10:31 +00:00
AutoCSV_Cleanup ( ) ;
2010-12-12 15:48:13 +00:00
2010-05-29 07:25:38 +00:00
// check conditions
2020-10-02 13:44:27 +00:00
if ( State ( ) = = IClient : : STATE_QUITTING | | State ( ) = = IClient : : STATE_RESTARTING )
2020-10-02 13:43:52 +00:00
{
static bool s_SavedConfig = false ;
if ( ! s_SavedConfig )
{
// write down the config and quit
2021-01-10 12:47:07 +00:00
if ( ! m_pConfigManager - > Save ( ) )
2020-10-02 13:43:52 +00:00
m_Warnings . emplace_back ( SWarning ( Localize ( " Saving ddnet-settings.cfg failed " ) ) ) ;
s_SavedConfig = true ;
}
2021-12-17 20:58:28 +00:00
IOHANDLE File = m_pStorage - > OpenFile ( m_aDDNetInfoTmp , IOFLAG_READ | IOFLAG_SKIP_BOM , IStorage : : TYPE_SAVE ) ;
2021-01-15 11:27:01 +00:00
if ( File )
{
2021-01-15 22:59:48 +00:00
io_close ( File ) ;
2021-01-15 11:07:16 +00:00
m_pStorage - > RemoveFile ( m_aDDNetInfoTmp , IStorage : : TYPE_SAVE ) ;
2021-01-15 11:27:01 +00:00
}
2021-01-15 11:14:14 +00:00
if ( m_Warnings . empty ( ) & & ! GameClient ( ) - > IsDisplayingWarning ( ) )
break ;
2020-10-02 13:43:52 +00:00
}
2010-05-29 07:25:38 +00:00
2016-05-02 21:36:21 +00:00
# if defined(CONF_FAMILY_UNIX)
m_Fifo . Update ( ) ;
# endif
2010-05-29 07:25:38 +00:00
// beNice
2021-06-23 05:05:49 +00:00
int64_t Now = time_get_microseconds ( ) ;
int64_t SleepTimeInMicroSeconds = 0 ;
2018-03-01 06:34:14 +00:00
bool Slept = false ;
2017-06-02 19:33:45 +00:00
if (
2017-06-02 18:45:09 +00:00
# ifdef CONF_DEBUG
2017-06-02 19:33:45 +00:00
g_Config . m_DbgStress | |
2017-06-02 18:45:09 +00:00
# endif
2017-06-02 21:27:35 +00:00
( g_Config . m_ClRefreshRateInactive & & ! m_pGraphics - > WindowActive ( ) ) )
2017-06-02 19:33:45 +00:00
{
2021-06-23 05:05:49 +00:00
SleepTimeInMicroSeconds = ( ( int64_t ) 1000000 / ( int64_t ) g_Config . m_ClRefreshRateInactive ) - ( Now - LastTime ) ;
if ( SleepTimeInMicroSeconds / ( int64_t ) 1000 > ( int64_t ) 0 )
2018-12-17 19:05:50 +00:00
thread_sleep ( SleepTimeInMicroSeconds ) ;
2018-03-01 06:34:14 +00:00
Slept = true ;
2017-06-02 19:33:45 +00:00
}
else if ( g_Config . m_ClRefreshRate )
{
2021-06-23 05:05:49 +00:00
SleepTimeInMicroSeconds = ( ( int64_t ) 1000000 / ( int64_t ) g_Config . m_ClRefreshRate ) - ( Now - LastTime ) ;
if ( SleepTimeInMicroSeconds > ( int64_t ) 0 )
2022-01-21 21:13:35 +00:00
net_socket_read_wait ( m_NetClient [ CONN_MAIN ] . m_Socket , SleepTimeInMicroSeconds ) ;
2018-03-01 06:34:14 +00:00
Slept = true ;
2017-06-02 19:33:45 +00:00
}
2018-03-01 06:34:14 +00:00
if ( Slept )
{
// if the diff gets too small it shouldn't get even smaller (drop the updates, that could not be handled)
2021-10-07 22:20:23 +00:00
if ( SleepTimeInMicroSeconds < ( int64_t ) - 16666 )
SleepTimeInMicroSeconds = ( int64_t ) - 16666 ;
// don't go higher than the frametime of a 60 fps frame
else if ( SleepTimeInMicroSeconds > ( int64_t ) 16666 )
SleepTimeInMicroSeconds = ( int64_t ) 16666 ;
2018-03-12 14:10:49 +00:00
// the time diff between the time that was used actually used and the time the thread should sleep/wait
// will be calculated in the sleep time of the next update tick by faking the time it should have slept/wait.
// so two cases (and the case it slept exactly the time it should):
// - the thread slept/waited too long, then it adjust the time to sleep/wait less in the next update tick
// - the thread slept/waited too less, then it adjust the time to sleep/wait more in the next update tick
LastTime = Now + SleepTimeInMicroSeconds ;
2018-03-01 06:34:14 +00:00
}
else
LastTime = Now ;
2010-05-29 07:25:38 +00:00
if ( g_Config . m_DbgHitch )
{
2020-09-26 19:41:58 +00:00
thread_sleep ( g_Config . m_DbgHitch * 1000 ) ;
2010-05-29 07:25:38 +00:00
g_Config . m_DbgHitch = 0 ;
}
2012-01-01 12:38:46 +00:00
// update local time
2020-09-26 19:41:58 +00:00
m_LocalTime = ( time_get ( ) - m_LocalStartTime ) / ( float ) time_freq ( ) ;
2010-05-29 07:25:38 +00:00
}
2016-05-02 21:36:21 +00:00
# if defined(CONF_FAMILY_UNIX)
m_Fifo . Shutdown ( ) ;
# endif
2010-05-29 07:25:38 +00:00
GameClient ( ) - > OnShutdown ( ) ;
Disconnect ( ) ;
2017-09-30 04:27:05 +00:00
delete m_pEditor ;
2010-05-29 07:25:38 +00:00
m_pGraphics - > Shutdown ( ) ;
2012-01-03 20:39:10 +00:00
// shutdown SDL
2021-07-11 09:02:16 +00:00
SDL_Quit ( ) ;
2010-05-29 07:25:38 +00:00
}
2014-01-31 00:41:57 +00:00
bool CClient : : CtrlShiftKey ( int Key , bool & Last )
{
2021-12-18 11:23:20 +00:00
if ( Input ( ) - > ModifierIsPressed ( ) & & ( Input ( ) - > KeyIsPressed ( KEY_LSHIFT ) | | Input ( ) - > KeyIsPressed ( KEY_RSHIFT ) ) & & ! Last & & Input ( ) - > KeyIsPressed ( Key ) )
2014-01-31 00:41:57 +00:00
{
Last = true ;
return true ;
}
2018-03-12 14:43:31 +00:00
else if ( Last & & ! Input ( ) - > KeyIsPressed ( Key ) )
2014-01-31 00:41:57 +00:00
Last = false ;
return false ;
}
2010-05-29 07:25:38 +00:00
2011-08-13 00:11:06 +00:00
void CClient : : Con_Connect ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
str_copy ( pSelf - > m_aCmdConnect , pResult - > GetString ( 0 ) , sizeof ( pSelf - > m_aCmdConnect ) ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Disconnect ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > Disconnect ( ) ;
}
2014-04-26 18:29:42 +00:00
void CClient : : Con_DummyConnect ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
2014-04-28 13:19:57 +00:00
pSelf - > DummyConnect ( ) ;
2014-04-26 18:29:42 +00:00
}
void CClient : : Con_DummyDisconnect ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > DummyDisconnect ( 0 ) ;
}
2021-03-17 15:09:39 +00:00
void CClient : : Con_DummyResetInput ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > GameClient ( ) - > DummyResetInput ( ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Quit ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > Quit ( ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Minimize ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > Graphics ( ) - > Minimize ( ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Ping ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2012-08-09 08:30:04 +00:00
CMsgPacker Msg ( NETMSG_PING , true ) ;
2022-01-21 21:13:35 +00:00
pSelf - > SendMsg ( CONN_MAIN , & Msg , 0 ) ;
2010-05-29 07:25:38 +00:00
pSelf - > m_PingStartTime = time_get ( ) ;
}
2010-12-12 15:48:13 +00:00
void CClient : : AutoScreenshot_Start ( )
{
if ( g_Config . m_ClAutoScreenshot )
{
Graphics ( ) - > TakeScreenshot ( " auto/autoscreen " ) ;
m_AutoScreenshotRecycle = true ;
}
}
2015-05-19 22:51:02 +00:00
void CClient : : AutoStatScreenshot_Start ( )
{
2015-05-21 09:55:51 +00:00
if ( g_Config . m_ClAutoStatboardScreenshot )
2015-05-19 22:51:02 +00:00
{
Graphics ( ) - > TakeScreenshot ( " auto/stats/autoscreen " ) ;
m_AutoStatScreenshotRecycle = true ;
}
}
2010-12-12 15:48:13 +00:00
void CClient : : AutoScreenshot_Cleanup ( )
{
if ( m_AutoScreenshotRecycle )
{
if ( g_Config . m_ClAutoScreenshotMax )
{
// clean up auto taken screens
CFileCollection AutoScreens ;
AutoScreens . Init ( Storage ( ) , " screenshots/auto " , " autoscreen " , " .png " , g_Config . m_ClAutoScreenshotMax ) ;
}
m_AutoScreenshotRecycle = false ;
}
}
2015-05-19 22:51:02 +00:00
void CClient : : AutoStatScreenshot_Cleanup ( )
{
if ( m_AutoStatScreenshotRecycle )
{
2015-05-21 09:55:51 +00:00
if ( g_Config . m_ClAutoStatboardScreenshotMax )
2015-05-19 22:51:02 +00:00
{
// clean up auto taken screens
CFileCollection AutoScreens ;
2015-05-21 09:55:51 +00:00
AutoScreens . Init ( Storage ( ) , " screenshots/auto/stats " , " autoscreen " , " .png " , g_Config . m_ClAutoStatboardScreenshotMax ) ;
2015-05-19 22:51:02 +00:00
}
m_AutoStatScreenshotRecycle = false ;
}
}
2017-04-26 03:10:31 +00:00
void CClient : : AutoCSV_Start ( )
{
2018-03-12 14:43:31 +00:00
if ( g_Config . m_ClAutoCSV )
2017-04-26 03:10:31 +00:00
m_AutoCSVRecycle = true ;
}
void CClient : : AutoCSV_Cleanup ( )
{
2018-03-12 14:43:31 +00:00
if ( m_AutoCSVRecycle )
2017-04-26 03:10:31 +00:00
{
2018-03-12 14:43:31 +00:00
if ( g_Config . m_ClAutoCSVMax )
2017-04-26 03:10:31 +00:00
{
// clean up auto csvs
CFileCollection AutoRecord ;
AutoRecord . Init ( Storage ( ) , " record/csv " , " autorecord " , " .csv " , g_Config . m_ClAutoCSVMax ) ;
}
m_AutoCSVRecycle = false ;
}
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Screenshot ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2010-12-12 15:48:13 +00:00
pSelf - > Graphics ( ) - > TakeScreenshot ( 0 ) ;
2010-05-29 07:25:38 +00:00
}
2021-12-27 00:13:09 +00:00
void CClient : : Con_Reset ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > m_pConfigManager - > Reset ( pResult - > GetString ( 0 ) ) ;
}
2016-08-27 19:10:27 +00:00
# if defined(CONF_VIDEORECORDER)
2016-08-27 15:51:23 +00:00
void CClient : : Con_StartVideo ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
2020-09-26 19:41:58 +00:00
if ( pSelf - > State ( ) ! = IClient : : STATE_DEMOPLAYBACK )
2022-01-28 20:12:31 +00:00
{
2016-08-30 23:39:59 +00:00
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " videorecorder " , " Can not start videorecorder outside of demoplayer. " ) ;
2022-01-28 20:12:31 +00:00
return ;
}
2016-08-30 23:39:59 +00:00
2020-09-26 19:41:58 +00:00
if ( ! IVideo : : Current ( ) )
2016-08-27 15:51:23 +00:00
{
2022-03-02 08:32:51 +00:00
// wait for idle, so there is no data race
pSelf - > Graphics ( ) - > WaitForIdle ( ) ;
// pause the sound device while creating the video instance
pSelf - > Sound ( ) - > PauseAudioDevice ( ) ;
new CVideo ( ( CGraphics_Threaded * ) pSelf - > m_pGraphics , pSelf - > Sound ( ) , pSelf - > Storage ( ) , pSelf - > m_pConsole , pSelf - > Graphics ( ) - > ScreenWidth ( ) , pSelf - > Graphics ( ) - > ScreenHeight ( ) , " " ) ;
pSelf - > Sound ( ) - > UnpauseAudioDevice ( ) ;
2020-06-22 21:59:37 +00:00
IVideo : : Current ( ) - > Start ( ) ;
2020-03-26 07:51:09 +00:00
bool paused = pSelf - > m_DemoPlayer . Info ( ) - > m_Info . m_Paused ;
if ( paused )
2020-06-22 21:59:37 +00:00
IVideo : : Current ( ) - > Pause ( true ) ;
2019-09-27 03:06:02 +00:00
}
else
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " videorecorder " , " Videorecorder already running. " ) ;
}
void CClient : : StartVideo ( IConsole : : IResult * pResult , void * pUserData , const char * pVideoName )
{
CClient * pSelf = ( CClient * ) pUserData ;
2020-09-26 19:41:58 +00:00
if ( pSelf - > State ( ) ! = IClient : : STATE_DEMOPLAYBACK )
2019-09-27 03:06:02 +00:00
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " videorecorder " , " Can not start videorecorder outside of demoplayer. " ) ;
2019-09-27 03:07:50 +00:00
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " demo_render " , pVideoName ) ;
2020-09-26 19:41:58 +00:00
if ( ! IVideo : : Current ( ) )
2019-09-27 03:06:02 +00:00
{
2022-03-02 08:32:51 +00:00
// wait for idle, so there is no data race
pSelf - > Graphics ( ) - > WaitForIdle ( ) ;
// pause the sound device while creating the video instance
pSelf - > Sound ( ) - > PauseAudioDevice ( ) ;
new CVideo ( ( CGraphics_Threaded * ) pSelf - > m_pGraphics , pSelf - > Sound ( ) , pSelf - > Storage ( ) , pSelf - > m_pConsole , pSelf - > Graphics ( ) - > ScreenWidth ( ) , pSelf - > Graphics ( ) - > ScreenHeight ( ) , pVideoName ) ;
pSelf - > Sound ( ) - > UnpauseAudioDevice ( ) ;
2020-06-22 21:59:37 +00:00
IVideo : : Current ( ) - > Start ( ) ;
2016-08-27 15:51:23 +00:00
}
2016-08-30 23:39:59 +00:00
else
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " videorecorder " , " Videorecorder already running. " ) ;
2016-08-27 15:51:23 +00:00
}
void CClient : : Con_StopVideo ( IConsole : : IResult * pResult , void * pUserData )
{
2020-09-26 19:41:58 +00:00
if ( IVideo : : Current ( ) )
2020-06-22 21:59:37 +00:00
IVideo : : Current ( ) - > Stop ( ) ;
2016-08-27 15:51:23 +00:00
}
2016-08-27 19:10:27 +00:00
# endif
2011-08-13 00:11:06 +00:00
void CClient : : Con_Rcon ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > Rcon ( pResult - > GetString ( 0 ) ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_RconAuth ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > RconAuth ( " " , pResult - > GetString ( 0 ) ) ;
}
2017-03-06 09:31:05 +00:00
void CClient : : Con_RconLogin ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > RconAuth ( pResult - > GetString ( 0 ) , pResult - > GetString ( 1 ) ) ;
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_AddFavorite ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
NETADDR Addr ;
2021-05-12 19:06:08 +00:00
if ( net_addr_from_str ( & Addr , pResult - > GetString ( 0 ) ) ! = 0 )
{
char aBuf [ 128 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " invalid address '%s' " , pResult - > GetString ( 0 ) ) ;
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " client " , aBuf ) ;
return ;
}
pSelf - > m_ServerBrowser . AddFavorite ( Addr ) ;
if ( pResult - > NumArguments ( ) > 1 & & str_find ( pResult - > GetString ( 1 ) , " allow_ping " ) )
{
pSelf - > m_ServerBrowser . FavoriteAllowPing ( Addr , true ) ;
}
2010-05-29 07:25:38 +00:00
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_RemoveFavorite ( IConsole : : IResult * pResult , void * pUserData )
2011-03-27 16:05:11 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
NETADDR Addr ;
if ( net_addr_from_str ( & Addr , pResult - > GetString ( 0 ) ) = = 0 )
pSelf - > m_ServerBrowser . RemoveFavorite ( Addr ) ;
}
2014-08-13 14:35:15 +00:00
void CClient : : DemoSliceBegin ( )
2014-08-12 14:21:06 +00:00
{
2014-08-13 14:35:15 +00:00
const CDemoPlayer : : CPlaybackInfo * pInfo = m_DemoPlayer . Info ( ) ;
2014-08-13 15:32:03 +00:00
g_Config . m_ClDemoSliceBegin = pInfo - > m_Info . m_CurrentTick ;
2014-08-12 14:21:06 +00:00
}
2014-08-13 14:35:15 +00:00
void CClient : : DemoSliceEnd ( )
2014-08-12 14:21:06 +00:00
{
2014-08-13 14:35:15 +00:00
const CDemoPlayer : : CPlaybackInfo * pInfo = m_DemoPlayer . Info ( ) ;
2014-08-12 14:21:06 +00:00
g_Config . m_ClDemoSliceEnd = pInfo - > m_Info . m_CurrentTick ;
}
2014-08-13 14:35:15 +00:00
void CClient : : Con_DemoSliceBegin ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
2015-07-09 00:08:14 +00:00
pSelf - > DemoSliceBegin ( ) ;
2014-08-13 14:35:15 +00:00
}
void CClient : : Con_DemoSliceEnd ( IConsole : : IResult * pResult , void * pUserData )
2014-08-12 14:21:06 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2014-08-13 14:35:15 +00:00
pSelf - > DemoSliceEnd ( ) ;
}
2014-08-12 14:21:06 +00:00
2019-05-20 21:55:40 +00:00
void CClient : : Con_SaveReplay ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
2019-06-02 13:34:01 +00:00
if ( pResult - > NumArguments ( ) )
{
int Length = pResult - > GetInteger ( 0 ) ;
if ( Length < = 0 )
2021-08-03 14:14:54 +00:00
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , " ERROR: length must be greater than 0 second. " ) ;
2019-06-02 13:34:01 +00:00
else
2022-03-13 14:17:44 +00:00
{
if ( pResult - > NumArguments ( ) > = 2 )
pSelf - > SaveReplay ( Length , pResult - > GetString ( 1 ) ) ;
else
pSelf - > SaveReplay ( Length ) ;
}
2019-06-02 13:34:01 +00:00
}
else
pSelf - > SaveReplay ( g_Config . m_ClReplayLength ) ;
2019-05-20 21:55:40 +00:00
}
2022-03-14 04:56:44 +00:00
void CClient : : SaveReplay ( const int Length , const char * pFilename )
2019-05-20 21:55:40 +00:00
{
2019-06-05 17:17:55 +00:00
if ( ! g_Config . m_ClReplays )
2019-05-20 21:55:40 +00:00
{
2019-06-02 13:34:01 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , " Feature is disabled. Please enable it via configuration. " ) ;
2019-06-05 17:17:55 +00:00
GameClient ( ) - > Echo ( Localize ( " Replay feature is disabled! " ) ) ;
2019-06-05 17:49:00 +00:00
return ;
2019-05-21 21:59:57 +00:00
}
2019-10-13 16:46:28 +00:00
2019-06-05 17:49:00 +00:00
if ( ! DemoRecorder ( RECORDER_REPLAYS ) - > IsRecording ( ) )
2021-08-03 14:14:54 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , " ERROR: demorecorder isn't recording. Try to rejoin to fix that. " ) ;
2019-06-05 17:49:00 +00:00
else if ( DemoRecorder ( RECORDER_REPLAYS ) - > Length ( ) < 1 )
2021-08-03 14:14:54 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , " ERROR: demorecorder isn't recording for at least 1 second. " ) ;
2019-05-21 21:59:57 +00:00
else
{
2019-06-05 17:49:00 +00:00
// First we stop the recorder to slice correctly the demo after
DemoRecorder_Stop ( RECORDER_REPLAYS ) ;
2021-09-13 08:06:34 +00:00
char aFilename [ IO_MAX_PATH_LENGTH ] ;
2019-05-20 21:55:40 +00:00
2019-06-05 17:49:00 +00:00
char aDate [ 64 ] ;
str_timestamp ( aDate , sizeof ( aDate ) ) ;
2019-05-20 21:55:40 +00:00
2022-03-14 04:56:44 +00:00
if ( str_comp ( pFilename , " " ) = = 0 )
2022-03-13 14:17:44 +00:00
str_format ( aFilename , sizeof ( aFilename ) , " demos/replays/%s_%s (replay).demo " , m_aCurrentMap , aDate ) ;
else
2022-03-14 04:56:44 +00:00
str_format ( aFilename , sizeof ( aFilename ) , " demos/replays/%s.demo " , pFilename ) ;
2022-03-13 14:17:44 +00:00
2019-06-05 17:49:00 +00:00
char * pSrc = ( & m_DemoRecorder [ RECORDER_REPLAYS ] ) - > GetCurrentFilename ( ) ;
2019-05-20 21:55:40 +00:00
2019-06-05 17:49:00 +00:00
// Slice the demo to get only the last cl_replay_length seconds
2020-02-19 10:24:58 +00:00
const int EndTick = GameTick ( g_Config . m_ClDummy ) ;
2019-06-05 17:49:00 +00:00
const int StartTick = EndTick - Length * GameTickSpeed ( ) ;
2019-05-21 15:21:53 +00:00
2019-06-05 17:49:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " replay " , " Saving replay... " ) ;
2019-06-05 17:17:55 +00:00
2019-06-05 17:49:00 +00:00
// Create a job to do this slicing in background because it can be a bit long depending on the file size
std : : shared_ptr < CDemoEdit > pDemoEditTask = std : : make_shared < CDemoEdit > ( GameClient ( ) - > NetVersion ( ) , & m_SnapshotDelta , m_pStorage , pSrc , aFilename , StartTick , EndTick ) ;
Engine ( ) - > AddJob ( pDemoEditTask ) ;
m_EditJobs . push_back ( pDemoEditTask ) ;
2019-05-28 11:24:55 +00:00
2019-06-05 17:49:00 +00:00
// And we restart the recorder
DemoRecorder_StartReplayRecorder ( ) ;
2019-05-20 21:55:40 +00:00
}
}
2017-02-28 09:08:14 +00:00
void CClient : : DemoSlice ( const char * pDstPath , CLIENTFUNC_FILTER pfnFilter , void * pUser )
2014-08-13 14:35:15 +00:00
{
2018-03-12 14:43:31 +00:00
if ( m_DemoPlayer . IsPlaying ( ) )
2014-08-12 14:21:06 +00:00
{
2014-08-13 14:35:15 +00:00
const char * pDemoFileName = m_DemoPlayer . GetDemoFileName ( ) ;
2017-02-28 09:08:14 +00:00
m_DemoEditor . Slice ( pDemoFileName , pDstPath , g_Config . m_ClDemoSliceBegin , g_Config . m_ClDemoSliceEnd , pfnFilter , pUser ) ;
2014-08-12 14:21:06 +00:00
}
}
2010-10-06 21:07:35 +00:00
const char * CClient : : DemoPlayer_Play ( const char * pFilename , int StorageType )
2010-05-29 07:25:38 +00:00
{
const char * pError ;
2020-11-09 08:10:25 +00:00
IOHANDLE File = Storage ( ) - > OpenFile ( pFilename , IOFLAG_READ , StorageType ) ;
if ( ! File )
return " error opening demo file " ;
io_close ( File ) ;
2010-05-29 07:25:38 +00:00
Disconnect ( ) ;
2022-01-21 21:13:35 +00:00
m_NetClient [ CONN_MAIN ] . ResetErrorString ( ) ;
2010-05-29 07:25:38 +00:00
// try to start playback
2016-04-27 15:05:30 +00:00
m_DemoPlayer . SetListener ( this ) ;
2010-05-29 07:25:38 +00:00
2010-10-06 21:07:35 +00:00
if ( m_DemoPlayer . Load ( Storage ( ) , m_pConsole , pFilename , StorageType ) )
2010-05-29 07:25:38 +00:00
return " error loading demo " ;
// load map
2021-11-08 19:21:02 +00:00
int Crc = m_DemoPlayer . GetMapInfo ( ) - > m_Crc ;
2019-10-14 00:27:08 +00:00
SHA256_DIGEST Sha = m_DemoPlayer . GetMapInfo ( ) - > m_Sha256 ;
2019-12-18 12:51:08 +00:00
pError = LoadMapSearch ( m_DemoPlayer . Info ( ) - > m_Header . m_aMapName , Sha ! = SHA256_ZEROED ? & Sha : nullptr , Crc ) ;
2010-05-29 07:25:38 +00:00
if ( pError )
{
2019-12-18 12:51:08 +00:00
if ( ! m_DemoPlayer . ExtractMap ( Storage ( ) ) )
return pError ;
2019-10-14 00:27:08 +00:00
Sha = m_DemoPlayer . GetMapInfo ( ) - > m_Sha256 ;
2019-10-13 16:46:28 +00:00
pError = LoadMapSearch ( m_DemoPlayer . Info ( ) - > m_Header . m_aMapName , & Sha , Crc ) ;
if ( pError )
{
DisconnectWithReason ( pError ) ;
return pError ;
}
2010-05-29 07:25:38 +00:00
}
GameClient ( ) - > OnConnected ( ) ;
// setup buffers
mem_zero ( m_aDemorecSnapshotData , sizeof ( m_aDemorecSnapshotData ) ) ;
2014-05-03 18:24:45 +00:00
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] = & m_aDemorecSnapshotHolders [ SNAP_CURRENT ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] = & m_aDemorecSnapshotHolders [ SNAP_PREV ] ;
2010-05-29 07:25:38 +00:00
2014-05-03 18:24:45 +00:00
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pSnap = ( CSnapshot * ) m_aDemorecSnapshotData [ SNAP_CURRENT ] [ 0 ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_pAltSnap = ( CSnapshot * ) m_aDemorecSnapshotData [ SNAP_CURRENT ] [ 1 ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_SnapSize = 0 ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_CURRENT ] - > m_Tick = - 1 ;
2010-05-29 07:25:38 +00:00
2014-05-03 18:24:45 +00:00
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_pSnap = ( CSnapshot * ) m_aDemorecSnapshotData [ SNAP_PREV ] [ 0 ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_pAltSnap = ( CSnapshot * ) m_aDemorecSnapshotData [ SNAP_PREV ] [ 1 ] ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_SnapSize = 0 ;
m_aSnapshots [ g_Config . m_ClDummy ] [ SNAP_PREV ] - > m_Tick = - 1 ;
2010-05-29 07:25:38 +00:00
// enter demo playback state
SetState ( IClient : : STATE_DEMOPLAYBACK ) ;
m_DemoPlayer . Play ( ) ;
GameClient ( ) - > OnEnterGame ( ) ;
return 0 ;
}
2019-09-27 07:22:50 +00:00
# if defined(CONF_VIDEORECORDER)
2019-09-28 04:18:38 +00:00
const char * CClient : : DemoPlayer_Render ( const char * pFilename , int StorageType , const char * pVideoName , int SpeedIndex )
2019-09-27 03:07:50 +00:00
{
2020-01-04 09:44:12 +00:00
const char * pError ;
pError = DemoPlayer_Play ( pFilename , StorageType ) ;
if ( pError )
return pError ;
2019-09-27 09:16:48 +00:00
m_ButtonRender = true ;
2019-09-27 03:07:50 +00:00
this - > CClient : : StartVideo ( NULL , this , pVideoName ) ;
m_DemoPlayer . Play ( ) ;
2019-09-28 04:18:38 +00:00
m_DemoPlayer . SetSpeed ( g_aSpeeds [ SpeedIndex ] ) ;
2019-09-27 03:07:50 +00:00
//m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "demo_recorder", "demo eof");
return 0 ;
}
2019-09-27 07:22:50 +00:00
# endif
2019-09-27 03:06:02 +00:00
2011-08-13 00:11:06 +00:00
void CClient : : Con_Play ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2020-11-09 08:10:25 +00:00
const char * pError = pSelf - > DemoPlayer_Play ( pResult - > GetString ( 0 ) , IStorage : : TYPE_ALL ) ;
if ( pError )
pSelf - > m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " demo_player " , pError ) ;
2010-05-29 07:25:38 +00:00
}
2015-08-12 12:26:48 +00:00
void CClient : : Con_DemoPlay ( IConsole : : IResult * pResult , void * pUserData )
2015-08-12 09:59:34 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2017-03-04 14:43:49 +00:00
if ( pSelf - > m_DemoPlayer . IsPlaying ( ) )
{
if ( pSelf - > m_DemoPlayer . BaseInfo ( ) - > m_Paused )
{
2015-08-12 10:17:19 +00:00
pSelf - > m_DemoPlayer . Unpause ( ) ;
}
2017-03-04 14:43:49 +00:00
else
{
2015-08-12 10:17:19 +00:00
pSelf - > m_DemoPlayer . Pause ( ) ;
}
2015-08-12 09:59:34 +00:00
}
}
2016-04-27 15:21:40 +00:00
void CClient : : Con_DemoSpeed ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pSelf - > m_DemoPlayer . SetSpeed ( pResult - > GetFloat ( 0 ) ) ;
}
2014-10-16 15:42:13 +00:00
void CClient : : DemoRecorder_Start ( const char * pFilename , bool WithTimestamp , int Recorder )
2010-05-29 07:25:38 +00:00
{
2010-08-09 12:14:15 +00:00
if ( State ( ) ! = IClient : : STATE_ONLINE )
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " demorec/record " , " client is not online " ) ;
2010-05-29 07:25:38 +00:00
else
{
2021-09-13 08:06:34 +00:00
char aFilename [ IO_MAX_PATH_LENGTH ] ;
2010-12-07 23:42:32 +00:00
if ( WithTimestamp )
{
char aDate [ 20 ] ;
str_timestamp ( aDate , sizeof ( aDate ) ) ;
str_format ( aFilename , sizeof ( aFilename ) , " demos/%s_%s.demo " , pFilename , aDate ) ;
}
else
str_format ( aFilename , sizeof ( aFilename ) , " demos/%s.demo " , pFilename ) ;
2020-01-03 09:12:37 +00:00
SHA256_DIGEST Sha256 = m_pMap - > Sha256 ( ) ;
m_DemoRecorder [ Recorder ] . Start ( Storage ( ) , m_pConsole , aFilename , GameClient ( ) - > NetVersion ( ) , m_aCurrentMap , & Sha256 , m_pMap - > Crc ( ) , " client " , m_pMap - > MapSize ( ) , 0 , m_pMap - > File ( ) ) ;
2010-05-29 07:25:38 +00:00
}
}
2010-12-08 00:42:32 +00:00
void CClient : : DemoRecorder_HandleAutoStart ( )
{
if ( g_Config . m_ClAutoDemoRecord )
2010-12-11 17:55:28 +00:00
{
2014-10-16 15:42:13 +00:00
DemoRecorder_Stop ( RECORDER_AUTO ) ;
2015-08-22 15:21:00 +00:00
char aBuf [ 512 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " auto/%s " , m_aCurrentMap ) ;
DemoRecorder_Start ( aBuf , true , RECORDER_AUTO ) ;
2010-12-11 17:55:28 +00:00
if ( g_Config . m_ClAutoDemoMax )
{
// clean up auto recorded demos
CFileCollection AutoDemos ;
2015-08-22 15:21:00 +00:00
AutoDemos . Init ( Storage ( ) , " demos/auto " , " " /* empty for wild card */ , " .demo " , g_Config . m_ClAutoDemoMax ) ;
2010-12-11 17:55:28 +00:00
}
}
2019-05-21 15:21:53 +00:00
if ( ! DemoRecorder ( RECORDER_REPLAYS ) - > IsRecording ( ) )
{
DemoRecorder_StartReplayRecorder ( ) ;
}
2010-12-08 00:42:32 +00:00
}
2019-05-21 15:21:53 +00:00
void CClient : : DemoRecorder_StartReplayRecorder ( )
{
2019-06-05 17:17:55 +00:00
if ( g_Config . m_ClReplays )
2019-05-20 21:55:40 +00:00
{
2019-05-21 10:49:19 +00:00
DemoRecorder_Stop ( RECORDER_REPLAYS ) ;
2019-05-20 21:55:40 +00:00
char aBuf [ 512 ] ;
2019-05-21 15:21:53 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " replays/replay_tmp-%s " , m_aCurrentMap ) ;
2019-05-21 10:49:19 +00:00
DemoRecorder_Start ( aBuf , true , RECORDER_REPLAYS ) ;
2019-05-20 21:55:40 +00:00
}
2010-12-08 00:42:32 +00:00
}
2019-05-21 15:21:53 +00:00
void CClient : : DemoRecorder_Stop ( int Recorder , bool RemoveFile )
2014-10-16 15:42:13 +00:00
{
m_DemoRecorder [ Recorder ] . Stop ( ) ;
2019-05-21 21:59:57 +00:00
if ( RemoveFile )
{
2019-08-21 20:49:21 +00:00
const char * pFilename = ( & m_DemoRecorder [ Recorder ] ) - > GetCurrentFilename ( ) ;
2020-11-12 13:53:38 +00:00
if ( pFilename [ 0 ] ! = ' \0 ' )
Storage ( ) - > RemoveFile ( pFilename , IStorage : : TYPE_SAVE ) ;
2019-05-21 15:21:53 +00:00
}
2014-10-16 15:42:13 +00:00
}
void CClient : : DemoRecorder_AddDemoMarker ( int Recorder )
2010-12-07 23:02:24 +00:00
{
2014-10-16 15:42:13 +00:00
m_DemoRecorder [ Recorder ] . AddDemoMarker ( ) ;
2010-12-07 23:02:24 +00:00
}
2014-10-16 15:42:13 +00:00
class IDemoRecorder * CClient : : DemoRecorder ( int Recorder )
2012-01-10 22:13:19 +00:00
{
2014-10-16 15:42:13 +00:00
return & m_DemoRecorder [ Recorder ] ;
2012-01-10 22:13:19 +00:00
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_Record ( IConsole : : IResult * pResult , void * pUserData )
2010-08-09 12:14:15 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2010-12-07 23:42:32 +00:00
if ( pResult - > NumArguments ( ) )
2014-10-16 15:42:13 +00:00
pSelf - > DemoRecorder_Start ( pResult - > GetString ( 0 ) , false , RECORDER_MANUAL ) ;
2010-12-07 23:42:32 +00:00
else
2015-08-22 15:36:47 +00:00
pSelf - > DemoRecorder_Start ( pSelf - > m_aCurrentMap , true , RECORDER_MANUAL ) ;
2010-08-09 12:14:15 +00:00
}
2011-08-13 00:11:06 +00:00
void CClient : : Con_StopRecord ( IConsole : : IResult * pResult , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CClient * pSelf = ( CClient * ) pUserData ;
2014-10-16 15:42:13 +00:00
pSelf - > DemoRecorder_Stop ( RECORDER_MANUAL ) ;
2010-05-29 07:25:38 +00:00
}
2012-01-10 22:13:19 +00:00
void CClient : : Con_AddDemoMarker ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
2014-10-16 15:42:13 +00:00
pSelf - > DemoRecorder_AddDemoMarker ( RECORDER_MANUAL ) ;
2015-07-22 13:37:59 +00:00
pSelf - > DemoRecorder_AddDemoMarker ( RECORDER_RACE ) ;
pSelf - > DemoRecorder_AddDemoMarker ( RECORDER_AUTO ) ;
2019-05-21 10:49:19 +00:00
pSelf - > DemoRecorder_AddDemoMarker ( RECORDER_REPLAYS ) ;
2012-01-10 22:13:19 +00:00
}
2020-11-19 15:15:07 +00:00
void CClient : : Con_BenchmarkQuit ( IConsole : : IResult * pResult , void * pUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
int Seconds = pResult - > GetInteger ( 0 ) ;
const char * pFilename = pResult - > GetString ( 1 ) ;
pSelf - > BenchmarkQuit ( Seconds , pFilename ) ;
}
void CClient : : BenchmarkQuit ( int Seconds , const char * pFilename )
{
2021-09-13 08:06:34 +00:00
char aBuf [ IO_MAX_PATH_LENGTH ] ;
2020-11-19 15:15:07 +00:00
m_BenchmarkFile = Storage ( ) - > OpenFile ( pFilename , IOFLAG_WRITE , IStorage : : TYPE_ABSOLUTE , aBuf , sizeof ( aBuf ) ) ;
m_BenchmarkStopTime = time_get ( ) + time_freq ( ) * Seconds ;
}
2022-04-02 11:37:59 +00:00
void CClient : : UpdateAndSwap ( )
{
Input ( ) - > Update ( ) ;
Graphics ( ) - > Swap ( ) ;
Graphics ( ) - > Clear ( 0 , 0 , 0 ) ;
}
2011-03-18 18:03:13 +00:00
void CClient : : ServerBrowserUpdate ( )
{
m_ResortServerBrowser = true ;
}
void CClient : : ConchainServerBrowserUpdate ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
2011-08-13 00:11:06 +00:00
pfnCallback ( pResult , pCallbackUserData ) ;
2011-03-18 18:03:13 +00:00
if ( pResult - > NumArguments ( ) )
( ( CClient * ) pUserData ) - > ServerBrowserUpdate ( ) ;
}
2022-01-31 02:11:47 +00:00
void CClient : : InitChecksum ( )
{
CChecksumData * pData = & m_Checksum . m_Data ;
pData - > m_SizeofData = sizeof ( * pData ) ;
str_copy ( pData - > m_aVersionStr , GAME_NAME " " GAME_RELEASE_VERSION " ( " CONF_PLATFORM_STRING " ; " CONF_ARCH_STRING " ) " , sizeof ( pData - > m_aVersionStr ) ) ;
pData - > m_Start = time_get ( ) ;
os_version_str ( pData - > m_aOsVersion , sizeof ( pData - > m_aOsVersion ) ) ;
secure_random_fill ( & pData - > m_Random , sizeof ( pData - > m_Random ) ) ;
pData - > m_Version = GameClient ( ) - > DDNetVersion ( ) ;
pData - > m_SizeofClient = sizeof ( * this ) ;
pData - > m_SizeofConfig = sizeof ( pData - > m_Config ) ;
}
# ifndef DDNET_CHECKSUM_SALT
// salt@checksum.ddnet.tw: db877f2b-2ddb-3ba6-9f67-a6d169ec671d
# define DDNET_CHECKSUM_SALT \
{ \
{ \
0xdb , 0x87 , 0x7f , 0x2b , 0x2d , 0xdb , 0x3b , 0xa6 , \
0x9f , 0x67 , 0xa6 , 0xd1 , 0x69 , 0xec , 0x67 , 0x1d , \
} \
}
# endif
int CClient : : HandleChecksum ( int Conn , CUuid Uuid , CUnpacker * pUnpacker )
{
int Start = pUnpacker - > GetInt ( ) ;
int Length = pUnpacker - > GetInt ( ) ;
if ( pUnpacker - > Error ( ) )
{
return 1 ;
}
if ( Start < 0 | | Length < 0 | | Start > INT_MAX - Length )
{
return 2 ;
}
int End = Start + Length ;
int ChecksumBytesEnd = minimum ( End , ( int ) sizeof ( m_Checksum . m_aBytes ) ) ;
int FileStart = maximum ( Start , ( int ) sizeof ( m_Checksum . m_aBytes ) ) ;
unsigned char aStartBytes [ 4 ] ;
unsigned char aEndBytes [ 4 ] ;
int_to_bytes_be ( aStartBytes , Start ) ;
int_to_bytes_be ( aEndBytes , End ) ;
if ( Start < = ( int ) sizeof ( m_Checksum . m_aBytes ) )
{
mem_zero ( & m_Checksum . m_Data . m_Config , sizeof ( m_Checksum . m_Data . m_Config ) ) ;
# define CHECKSUM_RECORD(Flags) (((Flags)&CFGFLAG_CLIENT) == 0 || ((Flags)&CFGFLAG_INSENSITIVE) != 0)
# define MACRO_CONFIG_INT(Name, ScriptName, Def, Min, Max, Flags, Desc) \
if ( CHECKSUM_RECORD ( Flags ) ) \
{ \
m_Checksum . m_Data . m_Config . m_ # # Name = g_Config . m_ # # Name ; \
}
# define MACRO_CONFIG_COL(Name, ScriptName, Def, Flags, Desc) \
if ( CHECKSUM_RECORD ( Flags ) ) \
{ \
m_Checksum . m_Data . m_Config . m_ # # Name = g_Config . m_ # # Name ; \
}
# define MACRO_CONFIG_STR(Name, ScriptName, Len, Def, Flags, Desc) \
if ( CHECKSUM_RECORD ( Flags ) ) \
{ \
str_copy ( m_Checksum . m_Data . m_Config . m_ # # Name , g_Config . m_ # # Name , sizeof ( m_Checksum . m_Data . m_Config . m_ # # Name ) ) ; \
}
# include <engine/shared/config_variables.h>
# undef CHECKSUM_RECORD
# undef MACRO_CONFIG_INT
# undef MACRO_CONFIG_COL
# undef MACRO_CONFIG_STR
}
if ( End > ( int ) sizeof ( m_Checksum . m_aBytes ) )
{
if ( m_OwnExecutableSize = = 0 )
{
m_OwnExecutable = io_current_exe ( ) ;
// io_length returns -1 on error.
m_OwnExecutableSize = m_OwnExecutable ? io_length ( m_OwnExecutable ) : - 1 ;
}
// Own executable not available.
if ( m_OwnExecutableSize < 0 )
{
return 3 ;
}
if ( End - ( int ) sizeof ( m_Checksum . m_aBytes ) > m_OwnExecutableSize )
{
return 4 ;
}
}
SHA256_CTX Sha256Ctxt ;
sha256_init ( & Sha256Ctxt ) ;
CUuid Salt = DDNET_CHECKSUM_SALT ;
sha256_update ( & Sha256Ctxt , & Salt , sizeof ( Salt ) ) ;
sha256_update ( & Sha256Ctxt , & Uuid , sizeof ( Uuid ) ) ;
sha256_update ( & Sha256Ctxt , aStartBytes , sizeof ( aStartBytes ) ) ;
sha256_update ( & Sha256Ctxt , aEndBytes , sizeof ( aEndBytes ) ) ;
sha256_update ( & Sha256Ctxt , m_Checksum . m_aBytes + Start , ChecksumBytesEnd - Start ) ;
if ( End > ( int ) sizeof ( m_Checksum . m_aBytes ) )
{
unsigned char aBuf [ 2048 ] ;
if ( io_seek ( m_OwnExecutable , FileStart - sizeof ( m_Checksum . m_aBytes ) , IOSEEK_START ) )
{
return 5 ;
}
for ( int i = FileStart ; i < End ; i + = sizeof ( aBuf ) )
{
int Read = io_read ( m_OwnExecutable , aBuf , minimum ( ( int ) sizeof ( aBuf ) , End - i ) ) ;
sha256_update ( & Sha256Ctxt , aBuf , Read ) ;
}
}
SHA256_DIGEST Sha256 = sha256_finish ( & Sha256Ctxt ) ;
CMsgPacker Msg ( NETMSG_CHECKSUM_RESPONSE , true ) ;
Msg . AddRaw ( & Uuid , sizeof ( Uuid ) ) ;
Msg . AddRaw ( & Sha256 , sizeof ( Sha256 ) ) ;
SendMsg ( Conn , & Msg , MSGFLAG_VITAL ) ;
return 0 ;
}
2016-04-29 22:34:12 +00:00
void CClient : : SwitchWindowScreen ( int Index )
{
// Todo SDL: remove this when fixed (changing screen when in fullscreen is bugged)
if ( g_Config . m_GfxFullscreen )
{
2022-02-04 10:13:38 +00:00
SetWindowParams ( 0 , g_Config . m_GfxBorderless , g_Config . m_GfxFullscreen ! = 3 ) ;
2016-04-29 22:34:12 +00:00
if ( Graphics ( ) - > SetWindowScreen ( Index ) )
g_Config . m_GfxScreen = Index ;
2022-02-04 10:13:38 +00:00
SetWindowParams ( g_Config . m_GfxFullscreen , g_Config . m_GfxBorderless , g_Config . m_GfxFullscreen ! = 3 ) ;
2016-04-29 22:34:12 +00:00
}
else
{
if ( Graphics ( ) - > SetWindowScreen ( Index ) )
g_Config . m_GfxScreen = Index ;
}
}
void CClient : : ConchainWindowScreen ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
if ( pSelf - > Graphics ( ) & & pResult - > NumArguments ( ) )
{
if ( g_Config . m_GfxScreen ! = pResult - > GetInteger ( 0 ) )
pSelf - > SwitchWindowScreen ( pResult - > GetInteger ( 0 ) ) ;
}
else
pfnCallback ( pResult , pCallbackUserData ) ;
}
2022-02-04 10:13:38 +00:00
void CClient : : SetWindowParams ( int FullscreenMode , bool IsBorderless , bool AllowResizing )
2016-04-29 22:34:12 +00:00
{
2022-02-04 10:13:38 +00:00
g_Config . m_GfxFullscreen = clamp ( FullscreenMode , 0 , 3 ) ;
2021-01-31 20:54:04 +00:00
g_Config . m_GfxBorderless = ( int ) IsBorderless ;
2022-02-04 10:13:38 +00:00
Graphics ( ) - > SetWindowParams ( FullscreenMode , IsBorderless , AllowResizing ) ;
2016-04-29 22:34:12 +00:00
}
void CClient : : ConchainFullscreen ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
if ( pSelf - > Graphics ( ) & & pResult - > NumArguments ( ) )
{
if ( g_Config . m_GfxFullscreen ! = pResult - > GetInteger ( 0 ) )
2022-02-04 10:13:38 +00:00
pSelf - > SetWindowParams ( pResult - > GetInteger ( 0 ) , g_Config . m_GfxBorderless , pResult - > GetInteger ( 0 ) ! = 3 ) ;
2016-04-29 22:34:12 +00:00
}
else
pfnCallback ( pResult , pCallbackUserData ) ;
}
void CClient : : ConchainWindowBordered ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
if ( pSelf - > Graphics ( ) & & pResult - > NumArguments ( ) )
{
if ( ! g_Config . m_GfxFullscreen & & ( g_Config . m_GfxBorderless ! = pResult - > GetInteger ( 0 ) ) )
2022-02-04 10:13:38 +00:00
pSelf - > SetWindowParams ( g_Config . m_GfxFullscreen , ! g_Config . m_GfxBorderless , g_Config . m_GfxFullscreen ! = 3 ) ;
2016-04-29 22:34:12 +00:00
}
else
pfnCallback ( pResult , pCallbackUserData ) ;
}
void CClient : : ToggleWindowVSync ( )
{
2020-09-26 19:41:58 +00:00
if ( Graphics ( ) - > SetVSync ( g_Config . m_GfxVsync ^ 1 ) )
2016-04-29 22:34:12 +00:00
g_Config . m_GfxVsync ^ = 1 ;
}
2019-03-28 20:51:42 +00:00
void CClient : : LoadFont ( )
{
static CFont * pDefaultFont = 0 ;
2021-09-13 08:06:34 +00:00
char aFilename [ IO_MAX_PATH_LENGTH ] ;
2021-06-04 13:15:29 +00:00
char aBuff [ 1024 ] ;
2020-08-20 09:21:45 +00:00
const char * pFontFile = " fonts/DejaVuSans.ttf " ;
2021-06-04 13:15:29 +00:00
const char * apFallbackFontFiles [ ] =
{
" fonts/GlowSansJCompressed-Book.otf " ,
" fonts/SourceHanSansSC-Regular.otf " ,
} ;
2019-03-28 20:51:42 +00:00
IOHANDLE File = Storage ( ) - > OpenFile ( pFontFile , IOFLAG_READ , IStorage : : TYPE_ALL , aFilename , sizeof ( aFilename ) ) ;
if ( File )
{
2020-09-13 21:02:01 +00:00
size_t Size = io_length ( File ) ;
unsigned char * pBuf = ( unsigned char * ) malloc ( Size ) ;
io_read ( File , pBuf , Size ) ;
2019-03-28 20:51:42 +00:00
io_close ( File ) ;
IEngineTextRender * pTextRender = Kernel ( ) - > RequestInterface < IEngineTextRender > ( ) ;
pDefaultFont = pTextRender - > GetFont ( aFilename ) ;
if ( pDefaultFont = = NULL )
2020-09-13 21:02:01 +00:00
pDefaultFont = pTextRender - > LoadFont ( aFilename , pBuf , Size ) ;
2020-08-20 06:54:59 +00:00
2021-06-04 13:15:29 +00:00
for ( auto & pFallbackFontFile : apFallbackFontFiles )
2020-08-20 06:54:59 +00:00
{
2021-06-04 13:15:29 +00:00
bool FontLoaded = false ;
File = Storage ( ) - > OpenFile ( pFallbackFontFile , IOFLAG_READ , IStorage : : TYPE_ALL , aFilename , sizeof ( aFilename ) ) ;
if ( File )
{
2022-03-20 11:57:50 +00:00
Size = io_length ( File ) ;
pBuf = ( unsigned char * ) malloc ( Size ) ;
2021-06-04 13:15:29 +00:00
io_read ( File , pBuf , Size ) ;
io_close ( File ) ;
2022-03-20 11:57:50 +00:00
pTextRender = Kernel ( ) - > RequestInterface < IEngineTextRender > ( ) ;
2021-06-04 13:15:29 +00:00
FontLoaded = pTextRender - > LoadFallbackFont ( pDefaultFont , aFilename , pBuf , Size ) ;
}
if ( ! FontLoaded )
{
2022-03-30 13:16:19 +00:00
str_format ( aBuff , std : : size ( aBuff ) , " failed to load the fallback font. filename='%s' " , pFallbackFontFile ) ;
2021-06-04 13:15:29 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " gameclient " , aBuff ) ;
}
2020-08-20 06:54:59 +00:00
}
2019-03-28 20:51:42 +00:00
Kernel ( ) - > RequestInterface < IEngineTextRender > ( ) - > SetDefaultFont ( pDefaultFont ) ;
}
2021-03-08 00:08:38 +00:00
2019-03-28 20:51:42 +00:00
if ( ! pDefaultFont )
2021-03-08 00:08:38 +00:00
{
2022-03-30 13:16:19 +00:00
str_format ( aBuff , std : : size ( aBuff ) , " failed to load font. filename='%s' " , pFontFile ) ;
2021-03-08 00:08:38 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " gameclient " , aBuff ) ;
}
2019-03-28 20:51:42 +00:00
}
2020-04-14 15:53:53 +00:00
void CClient : : Notify ( const char * pTitle , const char * pMessage )
{
2020-04-14 21:34:04 +00:00
if ( m_pGraphics - > WindowActive ( ) | | ! g_Config . m_ClShowNotifications )
2020-04-14 15:53:53 +00:00
return ;
NotificationsNotify ( pTitle , pMessage ) ;
Graphics ( ) - > NotifyWindow ( ) ;
}
2016-04-29 22:34:12 +00:00
void CClient : : ConchainWindowVSync ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
if ( pSelf - > Graphics ( ) & & pResult - > NumArguments ( ) )
{
if ( g_Config . m_GfxVsync ! = pResult - > GetInteger ( 0 ) )
pSelf - > ToggleWindowVSync ( ) ;
}
else
pfnCallback ( pResult , pCallbackUserData ) ;
}
2016-10-02 09:31:11 +00:00
void CClient : : ConchainTimeoutSeed ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pfnCallback ( pResult , pCallbackUserData ) ;
if ( pResult - > NumArguments ( ) )
pSelf - > m_GenerateTimeoutSeed = false ;
}
2018-06-20 06:43:55 +00:00
void CClient : : ConchainPassword ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pfnCallback ( pResult , pCallbackUserData ) ;
if ( pResult - > NumArguments ( ) & & pSelf - > m_LocalStartTime ) //won't set m_SendPassword before game has started
pSelf - > m_SendPassword = true ;
}
2019-06-05 17:17:55 +00:00
void CClient : : ConchainReplays ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
CClient * pSelf = ( CClient * ) pUserData ;
pfnCallback ( pResult , pCallbackUserData ) ;
if ( pResult - > NumArguments ( ) )
{
int Status = pResult - > GetInteger ( 0 ) ;
if ( Status = = 0 )
{
// stop recording and remove the tmp demo file
pSelf - > DemoRecorder_Stop ( RECORDER_REPLAYS , true ) ;
}
else
{
// start recording
pSelf - > DemoRecorder_HandleAutoStart ( ) ;
}
}
}
2010-05-29 07:25:38 +00:00
void CClient : : RegisterCommands ( )
{
m_pConsole = Kernel ( ) - > RequestInterface < IConsole > ( ) ;
// register server dummy commands for tab completion
2015-12-28 15:14:52 +00:00
m_pConsole - > Register ( " kick " , " i[id] ?r[reason] " , CFGFLAG_SERVER , 0 , 0 , " Kick player with specified id for any reason " ) ;
m_pConsole - > Register ( " ban " , " s[ip|id] ?i[minutes] r[reason] " , CFGFLAG_SERVER , 0 , 0 , " Ban player with ip/id for x minutes for any reason " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " unban " , " r[ip] " , CFGFLAG_SERVER , 0 , 0 , " Unban ip " ) ;
2011-08-13 00:11:06 +00:00
m_pConsole - > Register ( " bans " , " " , CFGFLAG_SERVER , 0 , 0 , " Show banlist " ) ;
2020-01-24 00:55:09 +00:00
m_pConsole - > Register ( " status " , " ?r[name] " , CFGFLAG_SERVER , 0 , 0 , " List players containing name or all players " ) ;
2011-08-13 00:11:06 +00:00
m_pConsole - > Register ( " shutdown " , " " , CFGFLAG_SERVER , 0 , 0 , " Shut down " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " record " , " r[file] " , CFGFLAG_SERVER , 0 , 0 , " Record to a file " ) ;
2011-08-13 00:11:06 +00:00
m_pConsole - > Register ( " stoprecord " , " " , CFGFLAG_SERVER , 0 , 0 , " Stop recording " ) ;
m_pConsole - > Register ( " reload " , " " , CFGFLAG_SERVER , 0 , 0 , " Reload the map " ) ;
2021-03-17 15:09:39 +00:00
m_pConsole - > Register ( " dummy_connect " , " " , CFGFLAG_CLIENT , Con_DummyConnect , this , " Connect dummy " ) ;
m_pConsole - > Register ( " dummy_disconnect " , " " , CFGFLAG_CLIENT , Con_DummyDisconnect , this , " Disconnect dummy " ) ;
m_pConsole - > Register ( " dummy_reset " , " " , CFGFLAG_CLIENT , Con_DummyResetInput , this , " Reset dummy " ) ;
2014-04-26 18:29:42 +00:00
2020-09-26 19:41:58 +00:00
m_pConsole - > Register ( " quit " , " " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_Quit , this , " Quit Teeworlds " ) ;
m_pConsole - > Register ( " exit " , " " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_Quit , this , " Quit Teeworlds " ) ;
m_pConsole - > Register ( " minimize " , " " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_Minimize , this , " Minimize Teeworlds " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " connect " , " r[host|ip] " , CFGFLAG_CLIENT , Con_Connect , this , " Connect to the specified host/ip " ) ;
2011-08-13 00:11:06 +00:00
m_pConsole - > Register ( " disconnect " , " " , CFGFLAG_CLIENT , Con_Disconnect , this , " Disconnect from the server " ) ;
m_pConsole - > Register ( " ping " , " " , CFGFLAG_CLIENT , Con_Ping , this , " Ping the current server " ) ;
m_pConsole - > Register ( " screenshot " , " " , CFGFLAG_CLIENT , Con_Screenshot , this , " Take a screenshot " ) ;
2021-12-27 00:13:09 +00:00
m_pConsole - > Register ( " reset " , " s[config-name] " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_Reset , this , " Reset a config its default value " ) ;
2016-08-27 19:10:27 +00:00
# if defined(CONF_VIDEORECORDER)
2016-08-27 15:51:23 +00:00
m_pConsole - > Register ( " start_video " , " " , CFGFLAG_CLIENT , Con_StartVideo , this , " Start recording a video " ) ;
m_pConsole - > Register ( " stop_video " , " " , CFGFLAG_CLIENT , Con_StopVideo , this , " Stop recording a video " ) ;
2016-08-27 19:10:27 +00:00
# endif
2015-12-28 15:14:52 +00:00
m_pConsole - > Register ( " rcon " , " r[rcon-command] " , CFGFLAG_CLIENT , Con_Rcon , this , " Send specified command to rcon " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " rcon_auth " , " r[password] " , CFGFLAG_CLIENT , Con_RconAuth , this , " Authenticate to rcon " ) ;
2017-03-10 11:30:41 +00:00
m_pConsole - > Register ( " rcon_login " , " s[username] r[password] " , CFGFLAG_CLIENT , Con_RconLogin , this , " Authenticate to rcon with a username " ) ;
2020-09-26 19:41:58 +00:00
m_pConsole - > Register ( " play " , " r[file] " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_Play , this , " Play the file specified " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " record " , " ?r[file] " , CFGFLAG_CLIENT , Con_Record , this , " Record to the file " ) ;
2011-08-13 00:11:06 +00:00
m_pConsole - > Register ( " stoprecord " , " " , CFGFLAG_CLIENT , Con_StopRecord , this , " Stop recording " ) ;
2012-01-10 22:13:19 +00:00
m_pConsole - > Register ( " add_demomarker " , " " , CFGFLAG_CLIENT , Con_AddDemoMarker , this , " Add demo timeline marker " ) ;
2021-05-12 19:06:08 +00:00
m_pConsole - > Register ( " add_favorite " , " s[host|ip] ?s['allow_ping'] " , CFGFLAG_CLIENT , Con_AddFavorite , this , " Add a server as a favorite " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " remove_favorite " , " r[host|ip] " , CFGFLAG_CLIENT , Con_RemoveFavorite , this , " Remove a server from favorites " ) ;
2014-08-13 14:35:15 +00:00
m_pConsole - > Register ( " demo_slice_start " , " " , CFGFLAG_CLIENT , Con_DemoSliceBegin , this , " " ) ;
2014-08-12 14:21:06 +00:00
m_pConsole - > Register ( " demo_slice_end " , " " , CFGFLAG_CLIENT , Con_DemoSliceEnd , this , " " ) ;
2015-08-12 12:26:48 +00:00
m_pConsole - > Register ( " demo_play " , " " , CFGFLAG_CLIENT , Con_DemoPlay , this , " Play demo " ) ;
2016-04-27 15:21:40 +00:00
m_pConsole - > Register ( " demo_speed " , " i[speed] " , CFGFLAG_CLIENT , Con_DemoSpeed , this , " Set demo speed " ) ;
2011-03-18 18:03:13 +00:00
2022-03-13 14:17:44 +00:00
m_pConsole - > Register ( " save_replay " , " ?i[length] ?s[filename] " , CFGFLAG_CLIENT , Con_SaveReplay , this , " Save a replay of the last defined amount of seconds " ) ;
2020-11-19 15:15:07 +00:00
m_pConsole - > Register ( " benchmark_quit " , " i[seconds] r[file] " , CFGFLAG_CLIENT | CFGFLAG_STORE , Con_BenchmarkQuit , this , " Benchmark frame times for number of seconds to file, then quit " ) ;
2019-05-20 21:55:40 +00:00
2016-10-02 09:31:11 +00:00
m_pConsole - > Chain ( " cl_timeout_seed " , ConchainTimeoutSeed , this ) ;
2019-06-05 17:17:55 +00:00
m_pConsole - > Chain ( " cl_replays " , ConchainReplays , this ) ;
2018-12-07 22:52:33 +00:00
2018-06-20 06:43:55 +00:00
m_pConsole - > Chain ( " password " , ConchainPassword , this ) ;
2018-12-07 22:52:33 +00:00
2018-06-20 06:43:55 +00:00
// used for server browser update
2011-03-18 18:03:13 +00:00
m_pConsole - > Chain ( " br_filter_string " , ConchainServerBrowserUpdate , this ) ;
m_pConsole - > Chain ( " br_filter_gametype " , ConchainServerBrowserUpdate , this ) ;
m_pConsole - > Chain ( " br_filter_serveraddress " , ConchainServerBrowserUpdate , this ) ;
2011-02-13 05:35:13 +00:00
2016-04-29 22:34:12 +00:00
m_pConsole - > Chain ( " gfx_screen " , ConchainWindowScreen , this ) ;
m_pConsole - > Chain ( " gfx_fullscreen " , ConchainFullscreen , this ) ;
m_pConsole - > Chain ( " gfx_borderless " , ConchainWindowBordered , this ) ;
m_pConsole - > Chain ( " gfx_vsync " , ConchainWindowVSync , this ) ;
2011-02-13 05:35:13 +00:00
// DDRace
2011-04-09 06:41:31 +00:00
2020-09-26 19:41:58 +00:00
# define CONSOLE_COMMAND(name, params, flags, callback, userdata, help) m_pConsole->Register(name, params, flags, 0, 0, help);
# include <game/ddracecommands.h>
2010-05-29 07:25:38 +00:00
}
2011-07-31 16:39:48 +00:00
static CClient * CreateClient ( )
{
2018-04-09 09:56:39 +00:00
CClient * pClient = static_cast < CClient * > ( malloc ( sizeof ( * pClient ) ) ) ;
2011-07-31 16:39:48 +00:00
mem_zero ( pClient , sizeof ( CClient ) ) ;
return new ( pClient ) CClient ;
}
2010-05-29 07:25:38 +00:00
2020-09-06 11:37:48 +00:00
void CClient : : HandleConnectAddress ( const NETADDR * pAddr )
{
net_addr_str ( pAddr , m_aCmdConnect , sizeof ( m_aCmdConnect ) , true ) ;
}
2019-04-08 19:42:49 +00:00
void CClient : : HandleConnectLink ( const char * pLink )
{
str_copy ( m_aCmdConnect , pLink + sizeof ( CONNECTLINK ) - 1 , sizeof ( m_aCmdConnect ) ) ;
}
2019-04-10 06:56:20 +00:00
void CClient : : HandleDemoPath ( const char * pPath )
2018-12-13 18:57:32 +00:00
{
2019-04-10 06:56:20 +00:00
str_copy ( m_aCmdPlayDemo , pPath , sizeof ( m_aCmdPlayDemo ) ) ;
2018-12-13 18:57:32 +00:00
}
2020-08-02 11:00:01 +00:00
void CClient : : HandleMapPath ( const char * pPath )
{
str_copy ( m_aCmdEditMap , pPath , sizeof ( m_aCmdEditMap ) ) ;
}
2010-05-29 07:25:38 +00:00
/*
Server Time
Client Mirror Time
Client Predicted Time
Snapshot Latency
Downstream latency
Prediction Latency
Upstream latency
*/
2021-02-12 12:40:29 +00:00
# if defined(CONF_PLATFORM_MACOS)
2022-02-16 19:54:11 +00:00
extern " C " int TWMain ( int argc , const char * * argv )
2021-08-24 10:18:20 +00:00
# elif defined(CONF_PLATFORM_ANDROID)
2022-01-20 09:49:31 +00:00
extern " C " __attribute__ ( ( visibility ( " default " ) ) ) int SDL_main ( int argc , char * argv [ ] ) ;
2021-08-24 10:18:20 +00:00
extern " C " void InitAndroid ( ) ;
2022-01-20 09:49:31 +00:00
int SDL_main ( int argc , char * argv2 [ ] )
2017-06-09 17:34:01 +00:00
# else
2022-02-16 19:54:11 +00:00
int main ( int argc , const char * * argv )
2017-06-09 17:34:01 +00:00
# endif
2020-03-27 23:18:24 +00:00
{
2022-01-20 09:49:31 +00:00
# if defined(CONF_PLATFORM_ANDROID)
const char * * argv = const_cast < const char * * > ( argv2 ) ;
# endif
2021-11-21 17:16:14 +00:00
cmdline_fix ( & argc , & argv ) ;
2017-09-03 08:37:24 +00:00
bool Silent = false ;
2018-01-07 11:03:33 +00:00
bool RandInitFailed = false ;
2017-09-03 08:37:24 +00:00
2022-02-16 19:54:11 +00:00
for ( int i = 1 ; i < argc ; i + + )
2010-09-06 19:55:09 +00:00
{
2022-02-16 19:54:11 +00:00
if ( str_comp ( " -s " , argv [ i ] ) = = 0 | | str_comp ( " --silent " , argv [ i ] ) = = 0 )
2010-09-06 19:55:09 +00:00
{
2017-09-03 08:37:24 +00:00
Silent = true ;
2021-03-01 14:05:46 +00:00
}
2022-02-16 19:54:11 +00:00
else if ( str_comp ( " -c " , argv [ i ] ) = = 0 | | str_comp ( " --console " , argv [ i ] ) = = 0 )
2021-03-01 14:05:46 +00:00
{
2017-09-03 08:37:24 +00:00
# if defined(CONF_FAMILY_WINDOWS)
2021-03-01 14:05:46 +00:00
AllocConsole ( ) ;
2017-09-03 08:37:24 +00:00
# endif
2010-09-06 19:55:09 +00:00
}
}
2021-08-24 10:18:20 +00:00
# if defined(CONF_PLATFORM_ANDROID)
InitAndroid ( ) ;
# endif
2022-02-18 09:46:05 +00:00
# if defined(CONF_EXCEPTION_HANDLING)
init_exception_handler ( ) ;
# endif
2016-01-02 14:37:44 +00:00
if ( secure_random_init ( ) ! = 0 )
{
2018-01-07 11:03:33 +00:00
RandInitFailed = true ;
2016-01-02 14:37:44 +00:00
}
2020-04-14 15:53:53 +00:00
NotificationsInit ( ) ;
2011-07-30 11:50:22 +00:00
CClient * pClient = CreateClient ( ) ;
2010-05-29 07:25:38 +00:00
IKernel * pKernel = IKernel : : Create ( ) ;
2017-07-21 17:46:31 +00:00
pKernel - > RegisterInterface ( pClient , false ) ;
2011-07-30 11:50:22 +00:00
pClient - > RegisterInterfaces ( ) ;
2010-05-29 07:25:38 +00:00
// create the components
2021-12-09 14:21:55 +00:00
IEngine * pEngine = CreateEngine ( GAME_NAME , Silent , 2 ) ;
2010-06-18 18:32:52 +00:00
IConsole * pConsole = CreateConsole ( CFGFLAG_CLIENT ) ;
2022-02-16 19:54:11 +00:00
IStorage * pStorage = CreateStorage ( " Teeworlds " , IStorage : : STORAGETYPE_CLIENT , argc , ( const char * * ) argv ) ;
2021-01-10 12:47:07 +00:00
IConfigManager * pConfigManager = CreateConfigManager ( ) ;
2010-05-29 07:25:38 +00:00
IEngineSound * pEngineSound = CreateEngineSound ( ) ;
IEngineInput * pEngineInput = CreateEngineInput ( ) ;
IEngineTextRender * pEngineTextRender = CreateEngineTextRender ( ) ;
IEngineMap * pEngineMap = CreateEngineMap ( ) ;
2021-01-17 23:52:58 +00:00
IDiscord * pDiscord = CreateDiscord ( ) ;
2020-09-06 11:37:48 +00:00
ISteam * pSteam = CreateSteam ( ) ;
2010-05-29 07:25:38 +00:00
2022-02-18 09:46:05 +00:00
# if defined(CONF_EXCEPTION_HANDLING)
2022-03-21 15:07:46 +00:00
char aBufPath [ IO_MAX_PATH_LENGTH ] ;
2022-02-18 09:46:05 +00:00
char aBufName [ IO_MAX_PATH_LENGTH ] ;
char aDate [ 64 ] ;
str_timestamp ( aDate , sizeof ( aDate ) ) ;
str_format ( aBufName , sizeof ( aBufName ) , " dumps/ " GAME_NAME " _crash_log_%d_%s.RTP " , pid ( ) , aDate ) ;
2022-03-21 15:07:46 +00:00
pStorage - > GetCompletePath ( IStorage : : TYPE_SAVE , aBufName , aBufPath , sizeof ( aBufPath ) ) ;
set_exception_handler_log_file ( aBufPath ) ;
2022-02-18 09:46:05 +00:00
# endif
2018-01-07 11:03:33 +00:00
if ( RandInitFailed )
2018-01-04 14:33:21 +00:00
{
dbg_msg ( " secure " , " could not initialize secure RNG " ) ;
return - 1 ;
}
2010-05-29 07:25:38 +00:00
{
bool RegisterFail = false ;
2011-02-27 14:03:57 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pEngine ) ;
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pConsole ) ;
2021-01-10 12:47:07 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pConfigManager ) ;
2010-05-29 07:25:38 +00:00
2019-04-11 10:21:42 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pEngineSound ) ; // IEngineSound
2020-09-26 19:41:58 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( static_cast < ISound * > ( pEngineSound ) , false ) ;
2010-05-29 07:25:38 +00:00
2019-04-11 10:21:42 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pEngineInput ) ; // IEngineInput
2020-09-26 19:41:58 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( static_cast < IInput * > ( pEngineInput ) , false ) ;
2010-05-29 07:25:38 +00:00
2019-04-11 10:21:42 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pEngineTextRender ) ; // IEngineTextRender
2020-09-26 19:41:58 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( static_cast < ITextRender * > ( pEngineTextRender ) , false ) ;
2010-05-29 07:25:38 +00:00
2019-04-11 10:21:42 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pEngineMap ) ; // IEngineMap
2020-09-26 19:41:58 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( static_cast < IMap * > ( pEngineMap ) , false ) ;
2010-05-29 07:25:38 +00:00
2017-09-30 04:27:05 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( CreateEditor ( ) , false ) ;
2021-07-12 10:04:45 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( CreateGameClient ( ) ) ;
2010-05-29 07:25:38 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pStorage ) ;
2021-01-17 23:52:58 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pDiscord ) ;
2020-09-06 11:37:48 +00:00
RegisterFail = RegisterFail | | ! pKernel - > RegisterInterface ( pSteam ) ;
2010-05-29 07:25:38 +00:00
if ( RegisterFail )
2017-07-21 17:46:31 +00:00
{
delete pKernel ;
pClient - > ~ CClient ( ) ;
2018-04-09 09:56:39 +00:00
free ( pClient ) ;
2010-05-29 07:25:38 +00:00
return - 1 ;
2017-07-21 17:46:31 +00:00
}
2010-05-29 07:25:38 +00:00
}
2011-03-05 10:46:24 +00:00
pEngine - > Init ( ) ;
2021-01-10 12:47:07 +00:00
pConfigManager - > Init ( ) ;
2021-01-10 13:02:54 +00:00
pConsole - > Init ( ) ;
2010-05-29 07:25:38 +00:00
// register all console commands
2011-07-30 11:50:22 +00:00
pClient - > RegisterCommands ( ) ;
2010-05-29 07:25:38 +00:00
pKernel - > RequestInterface < IGameClient > ( ) - > OnConsoleInit ( ) ;
// init client's interfaces
2011-07-30 11:50:22 +00:00
pClient - > InitInterfaces ( ) ;
2010-05-29 07:25:38 +00:00
2010-08-06 18:47:45 +00:00
// execute config file
2015-09-10 11:30:29 +00:00
IOHANDLE File = pStorage - > OpenFile ( CONFIG_FILE , IOFLAG_READ , IStorage : : TYPE_ALL ) ;
if ( File )
2014-08-26 20:25:22 +00:00
{
2015-09-10 11:30:29 +00:00
io_close ( File ) ;
2015-07-22 16:23:40 +00:00
pConsole - > ExecuteFile ( CONFIG_FILE ) ;
2014-08-26 20:25:22 +00:00
}
2010-08-06 18:47:45 +00:00
2021-03-01 14:05:46 +00:00
# if defined(CONF_FAMILY_WINDOWS)
if ( g_Config . m_ClShowConsole )
2021-07-26 09:10:39 +00:00
{
2021-03-01 14:05:46 +00:00
AllocConsole ( ) ;
2021-07-26 09:10:39 +00:00
HANDLE hInput ;
DWORD prev_mode ;
hInput = GetStdHandle ( STD_INPUT_HANDLE ) ;
GetConsoleMode ( hInput , & prev_mode ) ;
SetConsoleMode ( hInput , prev_mode & ENABLE_EXTENDED_FLAGS ) ;
}
2021-03-01 14:05:46 +00:00
# endif
2010-05-29 07:25:38 +00:00
// execute autoexec file
2015-09-10 11:30:29 +00:00
File = pStorage - > OpenFile ( AUTOEXEC_CLIENT_FILE , IOFLAG_READ , IStorage : : TYPE_ALL ) ;
if ( File )
2015-08-12 11:49:16 +00:00
{
2015-09-10 11:30:29 +00:00
io_close ( File ) ;
2015-08-12 11:49:16 +00:00
pConsole - > ExecuteFile ( AUTOEXEC_CLIENT_FILE ) ;
}
else // fallback
{
pConsole - > ExecuteFile ( AUTOEXEC_FILE ) ;
}
2010-05-29 07:25:38 +00:00
2015-09-10 11:30:29 +00:00
if ( g_Config . m_ClConfigVersion < 1 )
{
2015-10-06 14:59:11 +00:00
if ( g_Config . m_ClAntiPing = = 0 )
{
g_Config . m_ClAntiPingPlayers = 1 ;
g_Config . m_ClAntiPingGrenade = 1 ;
g_Config . m_ClAntiPingWeapons = 1 ;
}
2015-09-10 11:30:29 +00:00
}
g_Config . m_ClConfigVersion = 1 ;
2010-05-29 07:25:38 +00:00
// parse the command line arguments
2018-12-13 18:57:32 +00:00
if ( argc = = 2 & & str_startswith ( argv [ 1 ] , CONNECTLINK ) )
pClient - > HandleConnectLink ( argv [ 1 ] ) ;
2019-04-10 06:56:20 +00:00
else if ( argc = = 2 & & str_endswith ( argv [ 1 ] , " .demo " ) )
pClient - > HandleDemoPath ( argv [ 1 ] ) ;
2020-08-02 11:00:01 +00:00
else if ( argc = = 2 & & str_endswith ( argv [ 1 ] , " .map " ) )
pClient - > HandleMapPath ( argv [ 1 ] ) ;
2022-02-16 19:54:11 +00:00
else if ( argc > 1 )
pConsole - > ParseArguments ( argc - 1 , ( const char * * ) & argv [ 1 ] ) ;
2010-05-29 07:25:38 +00:00
2020-09-06 15:08:38 +00:00
if ( pSteam - > GetConnectAddress ( ) )
2020-09-06 11:37:48 +00:00
{
2020-09-06 15:08:38 +00:00
pClient - > HandleConnectAddress ( pSteam - > GetConnectAddress ( ) ) ;
pSteam - > ClearConnectAddress ( ) ;
2020-09-06 11:37:48 +00:00
}
2011-07-30 11:50:22 +00:00
pClient - > Engine ( ) - > InitLogfile ( ) ;
2010-08-06 18:38:13 +00:00
2010-05-29 07:25:38 +00:00
// run the client
2011-02-27 14:03:57 +00:00
dbg_msg ( " client " , " starting... " ) ;
2011-07-30 11:50:22 +00:00
pClient - > Run ( ) ;
2010-05-29 07:25:38 +00:00
2020-01-25 16:48:32 +00:00
bool Restarting = pClient - > State ( ) = = CClient : : STATE_RESTARTING ;
2017-07-21 17:46:31 +00:00
pClient - > ~ CClient ( ) ;
2018-04-09 09:56:39 +00:00
free ( pClient ) ;
2017-07-21 14:46:29 +00:00
2020-04-14 15:53:53 +00:00
NotificationsUninit ( ) ;
2022-02-13 19:04:17 +00:00
secure_random_uninit ( ) ;
2020-04-14 15:53:53 +00:00
2020-01-25 16:48:32 +00:00
if ( Restarting )
{
char aBuf [ 512 ] ;
shell_execute ( pStorage - > GetBinaryPath ( PLAT_CLIENT_EXEC , aBuf , sizeof aBuf ) ) ;
}
delete pKernel ;
2021-08-24 10:18:20 +00:00
2021-11-21 17:16:14 +00:00
cmdline_free ( argc , argv ) ;
2021-08-24 10:18:20 +00:00
# ifdef CONF_PLATFORM_ANDROID
// properly close this native thread, so globals are destructed
std : : exit ( 0 ) ;
# endif
2010-05-29 07:25:38 +00:00
return 0 ;
}
2011-02-04 17:25:04 +00:00
2011-04-09 06:41:31 +00:00
// DDRace
2021-02-08 21:26:26 +00:00
const char * CClient : : GetCurrentMap ( ) const
2011-02-04 17:25:04 +00:00
{
return m_aCurrentMap ;
}
2021-02-08 21:26:26 +00:00
const char * CClient : : GetCurrentMapPath ( ) const
2011-02-04 17:25:04 +00:00
{
2017-09-09 21:10:42 +00:00
return m_aCurrentMapPath ;
2011-02-04 17:25:04 +00:00
}
2021-02-08 21:26:26 +00:00
SHA256_DIGEST CClient : : GetCurrentMapSha256 ( ) const
2020-10-10 20:58:33 +00:00
{
return m_pMap - > Sha256 ( ) ;
}
2021-02-08 21:26:26 +00:00
unsigned CClient : : GetCurrentMapCrc ( ) const
2016-08-23 01:08:36 +00:00
{
2017-09-28 17:13:20 +00:00
return m_pMap - > Crc ( ) ;
2016-08-23 01:08:36 +00:00
}
2017-09-28 17:13:20 +00:00
void CClient : : RaceRecord_Start ( const char * pFilename )
2011-02-04 17:25:04 +00:00
{
2017-09-09 21:10:42 +00:00
if ( State ( ) ! = IClient : : STATE_ONLINE )
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " demorec/record " , " client is not online " ) ;
2011-02-04 17:25:04 +00:00
else
2020-01-03 09:12:37 +00:00
{
SHA256_DIGEST Sha256 = m_pMap - > Sha256 ( ) ;
m_DemoRecorder [ RECORDER_RACE ] . Start ( Storage ( ) , m_pConsole , pFilename , GameClient ( ) - > NetVersion ( ) , m_aCurrentMap , & Sha256 , m_pMap - > Crc ( ) , " client " , m_pMap - > MapSize ( ) , 0 , m_pMap - > File ( ) ) ;
}
2011-02-04 17:25:04 +00:00
}
2017-09-09 21:10:42 +00:00
void CClient : : RaceRecord_Stop ( )
2011-02-04 17:25:04 +00:00
{
2014-10-16 15:42:13 +00:00
if ( m_DemoRecorder [ RECORDER_RACE ] . IsRecording ( ) )
m_DemoRecorder [ RECORDER_RACE ] . Stop ( ) ;
2011-02-04 17:25:04 +00:00
}
2017-09-09 21:10:42 +00:00
bool CClient : : RaceRecord_IsRecording ( )
2011-02-04 17:25:04 +00:00
{
2014-10-16 15:42:13 +00:00
return m_DemoRecorder [ RECORDER_RACE ] . IsRecording ( ) ;
2011-02-04 17:25:04 +00:00
}
2014-09-19 22:36:22 +00:00
2017-09-03 15:36:51 +00:00
void CClient : : RequestDDNetInfo ( )
2017-08-30 19:34:01 +00:00
{
char aUrl [ 256 ] ;
2021-08-23 10:05:01 +00:00
str_copy ( aUrl , " https://info2.ddnet.tw/info " , sizeof ( aUrl ) ) ;
2017-08-30 19:34:01 +00:00
2017-09-07 18:51:46 +00:00
if ( g_Config . m_BrIndicateFinished )
{
char aEscaped [ 128 ] ;
2020-08-29 10:39:57 +00:00
EscapeUrl ( aEscaped , sizeof ( aEscaped ) , PlayerName ( ) ) ;
2017-09-07 18:51:46 +00:00
str_append ( aUrl , " ?name= " , sizeof ( aUrl ) ) ;
str_append ( aUrl , aEscaped , sizeof ( aUrl ) ) ;
}
2017-08-30 19:34:01 +00:00
2022-03-07 14:01:37 +00:00
// Use ipv4 so we can know the ingame ip addresses of players before they join game servers
m_pDDNetInfoTask = std : : make_shared < CGetFile > ( Storage ( ) , aUrl , m_aDDNetInfoTmp , IStorage : : TYPE_SAVE , CTimeout { 10000 , 500 , 10 } , HTTPLOG : : ALL , IPRESOLVE : : V4 ) ;
2017-11-23 14:47:38 +00:00
Engine ( ) - > AddJob ( m_pDDNetInfoTask ) ;
2017-08-30 19:34:01 +00:00
}
2016-05-05 16:07:00 +00:00
int CClient : : GetPredictionTime ( )
{
2021-06-23 05:05:49 +00:00
int64_t Now = time_get ( ) ;
2020-09-26 19:41:58 +00:00
return ( int ) ( ( m_PredictedTime . Get ( Now ) - m_GameTime [ g_Config . m_ClDummy ] . Get ( Now ) ) * 1000 / ( float ) time_freq ( ) ) ;
2016-05-05 16:07:00 +00:00
}
2019-04-11 22:46:54 +00:00
void CClient : : GetSmoothTick ( int * pSmoothTick , float * pSmoothIntraTick , float MixAmount )
{
2021-06-23 05:05:49 +00:00
int64_t GameTime = m_GameTime [ g_Config . m_ClDummy ] . Get ( time_get ( ) ) ;
int64_t PredTime = m_PredictedTime . Get ( time_get ( ) ) ;
int64_t SmoothTime = clamp ( GameTime + ( int64_t ) ( MixAmount * ( PredTime - GameTime ) ) , GameTime , PredTime ) ;
2019-04-11 22:46:54 +00:00
2020-09-26 19:41:58 +00:00
* pSmoothTick = ( int ) ( SmoothTime * 50 / time_freq ( ) ) + 1 ;
* pSmoothIntraTick = ( SmoothTime - ( * pSmoothTick - 1 ) * time_freq ( ) / 50 ) / ( float ) ( time_freq ( ) / 50 ) ;
2019-04-11 22:46:54 +00:00
}
2020-10-02 13:43:52 +00:00
SWarning * CClient : : GetCurWarning ( )
{
if ( m_Warnings . empty ( ) )
{
return NULL ;
}
else if ( m_Warnings [ 0 ] . m_WasShown )
{
m_Warnings . erase ( m_Warnings . begin ( ) ) ;
return NULL ;
}
else
{
return & m_Warnings [ 0 ] ;
}
}
2022-01-09 12:06:41 +00:00
int CClient : : MaxLatencyTicks ( ) const
{
2022-01-09 13:14:45 +00:00
return SERVER_TICK_SPEED + ( PredictionMargin ( ) * SERVER_TICK_SPEED ) / 1000 ;
}
int CClient : : PredictionMargin ( ) const
{
return m_ServerCapabilities . m_SyncWeaponInput ? g_Config . m_ClPredictionMargin : 10 ;
2022-01-09 12:06:41 +00:00
}