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>
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>
2022-04-22 23:04:48 +00:00
# include <base/logger.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>
2022-06-16 17:50:46 +00:00
# include <engine/external/json-parser/json.h>
2014-06-05 10:11:41 +00:00
# include <game/client/components/menus.h>
2022-05-29 13:51:38 +00:00
# include <game/generated/protocol.h>
2013-07-11 15:13:45 +00:00
2011-02-27 14:03:57 +00:00
# include <engine/config.h>
# include <engine/console.h>
2022-06-16 17:50:46 +00:00
# include <engine/discord.h>
2011-02-27 14:03:57 +00:00
# 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>
2020-04-14 15:53:53 +00:00
# include <engine/client/notifications.h>
2022-04-22 23:04:48 +00:00
# include <engine/shared/assertion_logger.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/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>
2021-12-24 11:17:46 +00:00
# include <engine/shared/http.h>
Add HTTP masterserver registering and HTTP masterserver
Registering
-----------
The idea is that game servers push their server info to the
masterservers every 15 seconds or when the server info changes, but not
more than once per second.
The game servers do not support the old registering protocol anymore,
the backward compatibility is handled by the masterserver.
The register call is a HTTP POST to a URL like
`https://master1.ddnet.tw/ddnet/15/register` and looks like this:
```json
POST /ddnet/15/register HTTP/1.1
Address: tw-0.6+udp://connecting-address.invalid:8303
Secret: 81fa3955-6f83-4290-818d-31c0906b1118
Challenge-Secret: 81fa3955-6f83-4290-818d-31c0906b1118:tw0.6/ipv6
Info-Serial: 0
{
"max_clients": 64,
"max_players": 64,
"passworded": false,
"game_type": "TestDDraceNetwork",
"name": "My DDNet server",
"map": {
"name": "dm1",
"sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf",
"size": 5805
},
"version": "0.6.4, 16.0.3",
"clients": []
}
```
The `Address` header declares that the server wants to register itself as
a `tw-0.6+udp` server, i.e. a server speaking a Teeworlds-0.6-compatible
protocol.
The free-form `Secret` header is used as a server identity, the server
list will be deduplicated via this secret.
The free-form `Challenge-Secret` is sent back via UDP for a port forward
check. This might have security implications as the masterserver can be
asked to send a UDP packet containing some user-controlled bytes. This
is somewhat mitigated by the fact that it can only go to an
attacker-controlled IP address.
The `Info-Serial` header is an integer field that should increase each
time the server info (in the body) changes. The masterserver uses that
field to ensure that it doesn't use old server infos.
The body is a free-form JSON object set by the game server. It should
contain certain keys in the correct form to be accepted by clients. The
body is optional if the masterserver already confirmed the reception of
the info with the given `Info-Serial`.
Not shown in this payload is the `Connless-Token` header that is used
for Teeworlds 0.7 style communication.
Also not shown is the `Challenge-Token` that should be included once the
server receives the challenge token via UDP.
The masterserver responds with a `200 OK` with a body like this:
```
{"status":"success"}
```
The `status` field can be `success` if the server was successfully
registered on the masterserver, `need_challenge` if the masterserver
wants the correct `Challenge-Token` header before the register process
is successful, `need_info` if the server sent an empty body but the
masterserver doesn't actually know the server info.
It can also be `error` if the request was malformed, only in this case
an HTTP status code except `200 OK` is sent.
Synchronization
---------------
The masterserver keeps state and outputs JSON files every second.
```json
{
"servers": [
{
"addresses": [
"tw-0.6+udp://127.0.0.1:8303",
"tw-0.6+udp://[::1]:8303"
],
"info_serial": 0,
"info": {
"max_clients": 64,
"max_players": 64,
"passworded": false,
"game_type": "TestDDraceNetwork",
"name": "My DDNet server",
"map": {
"name": "dm1",
"sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf",
"size": 5805
},
"version": "0.6.4, 16.0.3",
"clients": []
}
}
]
}
```
`servers.json` (or configured by `--out`) is a server list that is
compatible with DDNet 15.5+ clients. It is a JSON object containing a
single key `servers` with a list of game servers. Each game server is
represented by a JSON object with an `addresses` key containing a list
of all known addresses of the server and an `info` key containing the
free-form server info sent by the game server. The free-form `info` JSON
object re-encoded by the master server and thus canonicalized and
stripped of any whitespace characters outside strings.
```json
{
"kind": "mastersrv",
"now": 1816002,
"secrets": {
"tw-0.6+udp://127.0.0.1:8303": {
"ping_time": 1811999,
"secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb"
},
"tw-0.6+udp://[::1]:8303": {
"ping_time": 1811999,
"secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb"
}
},
"servers": {
"42d8f991-f2fa-46e5-a9ae-ebcc93846feb": {
"info_serial": 0,
"info": {
"max_clients": 64,
"max_players": 64,
"passworded": false,
"game_type": "TestDDraceNetwork",
"name": "My DDNet server",
"map": {
"name": "dm1",
"sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf",
"size": 5805
},
"version": "0.6.4, 16.0.3",
"clients": []
}
}
}
}
```
`--write-dump` outputs a JSON file compatible with `--read-dump-dir`,
this can be used to synchronize servers across different masterservers.
`--read-dump-dir` is also used to ingest servers from the backward
compatibility layer that pings each server for their server info using
the old protocol.
The `kind` field describe that this is `mastersrv` output and not from a
`backcompat`. This is used for prioritizing `mastersrv` information over
`backcompat` information.
The `now` field contains an integer describing the current time in
milliseconds relative an unspecified epoch that is fixed for each JSON
file. This is done instead of using the current time as the epoch for
better compression of non-changing data.
`secrets` is a map from each server address and to a JSON object
containing the last ping time (`ping_time`) in milliseconds relative to
the same epoch as before, and the server secret (`secret`) that is used
to unify server infos from different addresses of the same logical
server.
`servers` is a map from the aforementioned `secret`s to the
corresponding `info_serial` and `info`.
```json
[
"tw-0.6+udp://127.0.0.1:8303",
"tw-0.6+udp://[::1]:8303"
]
```
`--write-addresses` outputs a JSON file containing all addresses
corresponding to servers that are registered to HTTP masterservers. It
does not contain the servers that are obtained via backward
compatibility measures.
This file can be used by an old-style masterserver to also list
new-style servers without the game servers having to register there.
An implementation of this can be found at
https://github.com/heinrich5991/teeworlds/tree/mastersrv_6_backcompat
for Teeworlds 0.5/0.6 masterservers and at
https://github.com/heinrich5991/teeworlds/tree/mastersrv_7_backcompat
for Teeworlds 0.7 masterservers.
All these JSON files can be sent over the network in an efficient way
using https://github.com/heinrich5991/twmaster-collect. It establishes a
zstd-compressed TCP connection authenticated by a string token that is
sent in plain-text. It watches the specified file and transmits it every
time it changes. Due to the zstd-compression, the data sent over the
network is similar to the size of a diff.
Implementation
--------------
The masterserver implementation was done in Rust.
The current gameserver register implementation doesn't support more than
one masterserver for registering.
2022-05-19 20:03:17 +00:00
# include <engine/shared/masterserver.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/snapshot.h>
2017-05-21 23:07:13 +00:00
# include <engine/shared/uuid_manager.h>
2010-05-29 07:25:38 +00:00
2022-05-18 16:00:05 +00:00
# include <base/system.h>
2022-05-29 16:33:38 +00:00
# include <game/localization.h>
2012-01-09 01:02:02 +00:00
# include <game/version.h>
2019-05-31 18:42:28 +00:00
# include <engine/client/demoedit.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
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
# include "base/hash.h"
2021-11-06 12:03:51 +00:00
// for msvc
# ifndef PRIu64
# define PRIu64 "I64u"
# endif
2022-05-18 16:00:05 +00:00
# include <chrono>
# include <thread>
using namespace std : : chrono_literals ;
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 ;
}
2022-04-14 11:31:00 +00:00
bool CGraph : : InsertAt ( int i , float v , float r , float g , float b )
{
if ( i < 0 | | i > MAX_VALUES - 1 )
{
return false ;
}
m_aValues [ i ] = v ;
m_aColors [ i ] [ 0 ] = r ;
m_aColors [ i ] [ 1 ] = g ;
m_aColors [ i ] [ 2 ] = b ;
return true ;
}
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 ;
2022-05-15 19:59:17 +00:00
m_StateStartTime = time_get ( ) ;
2022-05-14 17:30:26 +00:00
m_aConnectAddressStr [ 0 ] = 0 ;
2010-05-29 07:25:38 +00:00
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 ] ) ;
2022-05-14 06:07:36 +00:00
Msg . AddInt ( m_PredTick [ g_Config . m_ClDummy ] ) ;
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 )
2022-06-06 02:17:55 +00:00
return ( int * ) m_aInputs [ d ] [ Best ] . m_aData ;
2010-05-29 07:25:38 +00:00
return 0 ;
}
// ------ 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
{
2022-05-15 19:59:17 +00:00
m_StateStartTime = time_get ( ) ;
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
2022-04-08 17:43:48 +00:00
for ( int 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 ( ) ;
2022-05-14 17:30:26 +00:00
if ( pAddress ! = m_aConnectAddressStr )
str_copy ( m_aConnectAddressStr , pAddress , sizeof ( m_aConnectAddressStr ) ) ;
2010-05-29 07:25:38 +00:00
2022-05-14 17:30:26 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " connecting to '%s' " , m_aConnectAddressStr ) ;
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 ;
2022-05-14 17:30:26 +00:00
if ( strncmp ( m_aConnectAddressStr , " ws:// " , 5 ) = = 0 )
2020-09-12 15:12:00 +00:00
{
is_websocket = true ;
2022-05-14 17:30:26 +00:00
str_copy ( m_aConnectAddressStr , pAddress + 5 , sizeof ( m_aConnectAddressStr ) ) ;
2020-09-12 15:12:00 +00:00
}
2010-05-29 07:25:38 +00:00
ServerInfoRequest ( ) ;
2022-05-14 17:30:26 +00:00
if ( net_host_lookup ( m_aConnectAddressStr , & 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 ) ;
2022-05-15 19:59:17 +00:00
m_NetClient [ CONN_MAIN ] . RefreshStun ( ) ;
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 )
2022-05-15 17:45:14 +00:00
str_copy ( pServerInfo - > m_aGameType , " DDraceNetwork " , sizeof ( pServerInfo - > m_aGameType ) ) ;
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
{
2011-02-12 10:40:36 +00:00
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
2022-04-08 17:43:48 +00:00
CSnapshotItem * i = m_aSnapshots [ g_Config . m_ClDummy ] [ SnapID ] - > m_pAltSnap - > GetItem ( Index ) ;
2014-05-03 18:24:45 +00:00
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
{
2011-02-12 10:40:36 +00:00
dbg_assert ( SnapID > = 0 & & SnapID < NUM_SNAPSHOT_TYPES , " invalid SnapID " ) ;
2022-04-08 17:43:48 +00:00
CSnapshotItem * 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 ;
2022-06-21 15:34:56 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " %5s %20s: %8s %8s %8s " , " ID " , " Name " , " Rate " , " Updates " , " R/U " ) ;
Graphics ( ) - > QuadsText ( 2 , 100 + y * 12 , 16 , aBuffer ) ;
y + + ;
for ( int i = 0 ; i < NUM_NETOBJTYPES ; i + + )
2010-05-29 07:25:38 +00:00
{
if ( m_SnapshotDelta . GetDataRate ( i ) )
{
2022-06-21 15:34:56 +00:00
str_format ( aBuffer , sizeof ( aBuffer ) , " %5d %20s: %8d %8d %8d " , i , GameClient ( ) - > GetItemName ( i ) , m_SnapshotDelta . GetDataRate ( i ) / 8 , m_SnapshotDelta . GetDataUpdates ( i ) ,
2020-09-26 19:41:58 +00:00
( 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 + + ;
}
}
2022-06-21 15:34:56 +00:00
for ( int i = CSnapshot : : MAX_TYPE ; i > ( CSnapshot : : MAX_TYPE - 64 ) ; i - - )
{
if ( m_SnapshotDelta . GetDataRate ( i ) & & m_aSnapshots [ g_Config . m_ClDummy ] [ IClient : : SNAP_CURRENT ] )
{
int Type = m_aSnapshots [ g_Config . m_ClDummy ] [ IClient : : SNAP_CURRENT ] - > m_pAltSnap - > GetExternalItemType ( i ) ;
if ( Type = = - 1 )
{
str_format ( aBuffer , sizeof ( aBuffer ) , " %5d %20s: %8d %8d %8d " , i , " Unknown UUID " , 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 ) ;
y + + ;
}
else if ( Type ! = i )
{
str_format ( aBuffer , sizeof ( aBuffer ) , " %5d %20s: %8d %8d %8d " , Type , GameClient ( ) - > GetItemName ( Type ) , 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 ) ;
y + + ;
}
}
}
2010-05-29 07:25:38 +00:00
}
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
2022-05-05 12:49:25 +00:00
m_pMapdownloadTask = HttpGetFile ( aUrl , Storage ( ) , m_aMapdownloadFilenameTemp , IStorage : : TYPE_SAVE ) ;
2022-05-28 22:13:59 +00:00
m_pMapdownloadTask - > Timeout ( CTimeout { g_Config . m_ClMapDownloadConnectTimeoutMs , 0 , 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 GameTick = Unpacker . GetInt ( ) ;
2020-09-26 19:41:58 +00:00
int DeltaTick = GameTick - Unpacker . GetInt ( ) ;
2011-03-17 16:41:57 +00:00
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 ;
2022-04-08 17:43:48 +00:00
int NumParts = 1 ;
int Part = 0 ;
2011-03-17 16:41:57 +00:00
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
2022-04-08 17:43:48 +00:00
unsigned int Crc = 0 ;
int PartSize = 0 ;
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
2022-04-08 17:43:48 +00:00
const char * 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 ;
2022-06-21 13:26:19 +00:00
m_SnapshotIncomingDataSize [ Conn ] = 0 ;
2010-05-29 07:25:38 +00:00
}
2022-06-21 13:26:19 +00:00
mem_copy ( ( char * ) m_aSnapshotIncomingData [ Conn ] + Part * MAX_SNAPSHOT_PACKSIZE , pData , clamp ( PartSize , 0 , ( int ) sizeof ( m_aSnapshotIncomingData [ Conn ] ) - Part * MAX_SNAPSHOT_PACKSIZE ) ) ;
m_SnapshotParts [ Conn ] | = ( uint64_t ) ( 1 ) < < Part ;
2010-05-29 07:25:38 +00:00
2022-06-21 13:26:19 +00:00
if ( Part = = NumParts - 1 )
{
m_SnapshotIncomingDataSize [ Conn ] = ( NumParts - 1 ) * MAX_SNAPSHOT_PACKSIZE + PartSize ;
}
if ( ( NumParts < CSnapshot : : MAX_PARTS & & m_SnapshotParts [ Conn ] = = ( ( ( uint64_t ) ( 1 ) < < NumParts ) - 1 ) ) | |
( NumParts = = CSnapshot : : MAX_PARTS & & m_SnapshotParts [ Conn ] = = std : : numeric_limits < uint64_t > : : max ( ) ) )
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
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
2022-01-21 21:13:35 +00:00
m_AckGameTick [ Conn ] = - 1 ;
2022-06-21 13:26:19 +00:00
SendInput ( ) ;
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
2022-06-21 13:26:19 +00:00
if ( m_SnapshotIncomingDataSize [ Conn ] )
2011-03-17 16:41:57 +00:00
{
2022-06-21 13:26:19 +00:00
int IntSize = CVariableInt : : Decompress ( m_aSnapshotIncomingData [ Conn ] , m_SnapshotIncomingDataSize [ Conn ] , aTmpBuffer2 , sizeof ( aTmpBuffer2 ) ) ;
2010-05-29 07:25:38 +00:00
2022-06-21 13:26:19 +00:00
if ( IntSize < 0 ) // failure during decompression
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
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 " ,
2022-06-21 13:26:19 +00:00
m_SnapCrcErrors , GameTick , Crc , pTmpBuffer3 - > Crc ( ) , m_SnapshotIncomingDataSize [ Conn ] , DeltaTick ) ;
2011-03-17 16:41:57 +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
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-06-21 15:34:56 +00:00
m_SnapshotStorage [ Conn ] . Add ( GameTick , time_get ( ) , SnapSize , pTmpBuffer3 , true ) ;
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 ;
2022-05-01 08:04:10 +00:00
char aBuf [ 128 ] ;
char aBufMsg [ 256 ] ;
2022-04-18 05:55:50 +00:00
if ( ! g_Config . m_ClRunOnJoin [ 0 ] & & ! g_Config . m_ClDummyDefaultEyes & & ! g_Config . m_ClPlayerDefaultEyes )
2022-05-01 08:04:10 +00:00
str_format ( aBufMsg , sizeof ( aBufMsg ) , " /timeout %s " , m_aTimeoutCodes [ Conn ] ) ;
2022-04-18 05:55:50 +00:00
else
2022-05-01 08:04:10 +00:00
str_format ( aBufMsg , sizeof ( aBufMsg ) , " /mc;timeout %s " , m_aTimeoutCodes [ Conn ] ) ;
2022-04-18 05:55:50 +00:00
2021-11-23 15:39:59 +00:00
if ( g_Config . m_ClRunOnJoin [ 0 ] )
{
2022-05-01 08:04:10 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " ;%s " , g_Config . m_ClRunOnJoin ) ;
str_append ( aBufMsg , aBuf , sizeof ( aBufMsg ) ) ;
2021-11-23 15:39:59 +00:00
}
2022-04-18 05:55:50 +00:00
if ( g_Config . m_ClDummyDefaultEyes | | g_Config . m_ClPlayerDefaultEyes )
2021-11-23 15:39:59 +00:00
{
2022-04-18 05:55:50 +00:00
int Emote = ( ( g_Config . m_ClDummy ) ? ! Dummy : Dummy ) ? g_Config . m_ClDummyDefaultEyes : g_Config . m_ClPlayerDefaultEyes ;
char aBufEmote [ 128 ] ;
aBufEmote [ 0 ] = ' \0 ' ;
switch ( Emote )
{
case EMOTE_NORMAL :
break ;
case EMOTE_PAIN :
str_format ( aBufEmote , sizeof ( aBufEmote ) , " emote pain %d " , g_Config . m_ClEyeDuration ) ;
break ;
case EMOTE_HAPPY :
str_format ( aBufEmote , sizeof ( aBufEmote ) , " emote happy %d " , g_Config . m_ClEyeDuration ) ;
break ;
case EMOTE_SURPRISE :
str_format ( aBufEmote , sizeof ( aBufEmote ) , " emote surprise %d " , g_Config . m_ClEyeDuration ) ;
break ;
case EMOTE_ANGRY :
str_format ( aBufEmote , sizeof ( aBufEmote ) , " emote angry %d " , g_Config . m_ClEyeDuration ) ;
break ;
case EMOTE_BLINK :
str_format ( aBufEmote , sizeof ( aBufEmote ) , " emote blink %d " , g_Config . m_ClEyeDuration ) ;
break ;
}
if ( aBufEmote [ 0 ] )
2022-05-01 08:04:10 +00:00
{
str_format ( aBuf , sizeof ( aBuf ) , " ;%s " , aBufEmote ) ;
str_append ( aBufMsg , aBuf , sizeof ( aBufMsg ) ) ;
}
2021-11-23 15:39:59 +00:00
}
2022-05-01 08:04:10 +00:00
MsgP . m_pMessage = aBufMsg ;
2022-03-20 11:57:50 +00:00
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 ( )
{
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
2022-04-08 17:43:48 +00:00
const char * 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 ;
2022-05-15 19:59:17 +00:00
const json_value & DDNetInfo = * pDDNetInfo ;
const json_value & CurrentVersion = DDNetInfo [ " version " ] ;
if ( CurrentVersion . type = = json_string )
2017-09-03 15:36:51 +00:00
{
2019-02-27 19:24:31 +00:00
char aNewVersionStr [ 64 ] ;
2022-05-15 19:59:17 +00:00
str_copy ( aNewVersionStr , CurrentVersion , sizeof ( aNewVersionStr ) ) ;
2019-02-27 19:24:31 +00:00
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
{
2022-05-15 19:59:17 +00:00
str_copy ( m_aVersionStr , CurrentVersion , sizeof ( m_aVersionStr ) ) ;
2017-09-03 15:36:51 +00:00
}
else
{
m_aVersionStr [ 0 ] = ' 0 ' ;
m_aVersionStr [ 1 ] = ' \0 ' ;
}
}
2022-05-15 19:59:17 +00:00
const json_value & News = DDNetInfo [ " news " ] ;
if ( News . type = = json_string )
2017-09-03 15:36:51 +00:00
{
2020-09-03 12:08:26 +00:00
// Only mark news button if something new was added to the news
2022-05-15 19:59:17 +00:00
if ( m_aNews [ 0 ] & & str_find ( m_aNews , News ) = = nullptr )
2020-09-03 12:08:26 +00:00
g_Config . m_UiUnreadNews = true ;
2017-09-03 15:36:51 +00:00
2022-05-15 19:59:17 +00:00
str_copy ( m_aNews , News , sizeof ( m_aNews ) ) ;
}
const json_value & MapDownloadUrl = DDNetInfo [ " map-download-url " ] ;
if ( MapDownloadUrl . type = = json_string )
{
str_copy ( m_aMapDownloadUrl , MapDownloadUrl , sizeof ( m_aMapDownloadUrl ) ) ;
2017-09-03 15:36:51 +00:00
}
2020-10-23 21:25:58 +00:00
2022-05-15 19:59:17 +00:00
const json_value & Points = DDNetInfo [ " points " ] ;
if ( Points . type = = json_integer )
2021-01-31 10:15:09 +00:00
{
2022-05-15 19:59:17 +00:00
m_Points = Points . u . integer ;
2021-01-31 10:15:09 +00:00
}
2022-05-15 19:59:17 +00:00
const json_value & StunServersIpv6 = DDNetInfo [ " stun-servers-ipv6 " ] ;
if ( StunServersIpv6 . type = = json_array & & StunServersIpv6 [ 0 ] . type = = json_string )
{
NETADDR Addr ;
if ( ! net_addr_from_str ( & Addr , StunServersIpv6 [ 0 ] ) )
{
m_NetClient - > FeedStunServer ( Addr ) ;
}
}
const json_value & StunServersIpv4 = DDNetInfo [ " stun-servers-ipv4 " ] ;
if ( StunServersIpv4 . type = = json_array & & StunServersIpv4 [ 0 ] . type = = json_string )
{
NETADDR Addr ;
if ( ! net_addr_from_str ( & Addr , StunServersIpv4 [ 0 ] ) )
{
m_NetClient - > FeedStunServer ( Addr ) ;
}
}
const json_value & ConnectingIp = DDNetInfo [ " connecting-ip " ] ;
if ( ConnectingIp . type = = json_string )
{
NETADDR Addr ;
if ( ! net_addr_from_str ( & Addr , ConnectingIp ) )
{
m_HaveGlobalTcpAddr = true ;
m_GlobalTcpAddr = Addr ;
log_debug ( " info " , " got global tcp ip address: %s " , ( const char * ) ConnectingIp ) ;
}
}
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
2022-06-15 15:37:22 +00:00
// update editor/gameclient
if ( m_EditorActive )
m_pEditor - > OnUpdate ( ) ;
else
2016-04-29 21:05:20 +00:00
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 )
2022-05-14 17:30:26 +00:00
Connect ( m_aConnectAddressStr ) ;
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-05-28 11:42:26 +00:00
int & PortRef = i = = CONN_MAIN ? g_Config . m_ClPort : i = = CONN_DUMMY ? g_Config . m_ClDummyPort : g_Config . m_ClContactPort ;
if ( PortRef < 1024 ) // Reject users setting ports that we don't want to use
{
PortRef = 0 ;
}
BindAddr . port = PortRef ;
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
2022-06-13 16:07:29 +00:00
auto LastTime = time_get_nanoseconds ( ) ;
2021-06-23 05:05:49 +00:00
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
{
2022-06-15 15:37:22 +00:00
m_pEditor - > OnRender ( ) ;
2012-01-06 11:55:19 +00:00
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
{
2022-06-15 15:37:22 +00:00
m_pEditor - > OnRender ( ) ;
2012-01-06 11:55:19 +00:00
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 ( ) )
2022-06-11 19:38:18 +00:00
m_vWarnings . emplace_back ( SWarning ( Localize ( " Saving ddnet-settings.cfg failed " ) ) ) ;
2020-10-02 13:43:52 +00:00
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
2022-06-11 19:38:18 +00:00
if ( m_vWarnings . empty ( ) & & ! GameClient ( ) - > IsDisplayingWarning ( ) )
2021-01-15 11:14:14 +00:00
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
2022-06-13 16:07:29 +00:00
auto Now = time_get_nanoseconds ( ) ;
2022-05-18 16:00:05 +00:00
decltype ( Now ) SleepTimeInNanoSeconds { 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
{
2022-05-18 16:00:05 +00:00
SleepTimeInNanoSeconds = ( std : : chrono : : nanoseconds ( 1 s ) / ( int64_t ) g_Config . m_ClRefreshRateInactive ) - ( Now - LastTime ) ;
std : : this_thread : : sleep_for ( SleepTimeInNanoSeconds ) ;
2018-03-01 06:34:14 +00:00
Slept = true ;
2017-06-02 19:33:45 +00:00
}
else if ( g_Config . m_ClRefreshRate )
{
2022-05-18 16:00:05 +00:00
SleepTimeInNanoSeconds = ( std : : chrono : : nanoseconds ( 1 s ) / ( int64_t ) g_Config . m_ClRefreshRate ) - ( Now - LastTime ) ;
if ( SleepTimeInNanoSeconds > 0 ns )
2022-06-13 16:07:29 +00:00
net_socket_read_wait ( m_NetClient [ CONN_MAIN ] . m_Socket , SleepTimeInNanoSeconds ) ;
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)
2022-05-18 16:00:05 +00:00
if ( SleepTimeInNanoSeconds < - 16666666 ns )
SleepTimeInNanoSeconds = - 16666666 ns ;
2021-10-07 22:20:23 +00:00
// don't go higher than the frametime of a 60 fps frame
2022-05-18 16:00:05 +00:00
else if ( SleepTimeInNanoSeconds > 16666666 ns )
SleepTimeInNanoSeconds = 16666666 ns ;
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
2022-05-18 16:00:05 +00:00
LastTime = Now + SleepTimeInNanoSeconds ;
2018-03-01 06:34:14 +00:00
}
else
LastTime = Now ;
2010-05-29 07:25:38 +00:00
if ( g_Config . m_DbgHitch )
{
2022-05-18 16:00:05 +00:00
std : : this_thread : : sleep_for ( g_Config . m_DbgHitch * 1 ms ) ;
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 ( ) ;
2022-04-14 09:50:10 +00:00
// close socket
for ( unsigned int i = 0 ; i < std : : size ( m_NetClient ) ; i + + )
m_NetClient [ i ] . Close ( ) ;
2017-09-30 04:27:05 +00:00
delete m_pEditor ;
2022-06-18 09:57:57 +00:00
m_pInput - > Shutdown ( ) ;
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 ( ) ;
2022-06-16 17:15:24 +00:00
new CVideo ( ( CGraphics_Threaded * ) pSelf - > m_pGraphics , pSelf - > Sound ( ) , pSelf - > Storage ( ) , pSelf - > Graphics ( ) - > ScreenWidth ( ) , pSelf - > Graphics ( ) - > ScreenHeight ( ) , " " ) ;
2022-03-02 08:32:51 +00:00
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 ( ) ;
2022-06-16 17:15:24 +00:00
new CVideo ( ( CGraphics_Threaded * ) pSelf - > m_pGraphics , pSelf - > Sound ( ) , pSelf - > Storage ( ) , pSelf - > Graphics ( ) - > ScreenWidth ( ) , pSelf - > Graphics ( ) - > ScreenHeight ( ) , pVideoName ) ;
2022-03-02 08:32:51 +00:00
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 ) ;
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-04-08 17:43:48 +00:00
char aFilename [ IO_MAX_PATH_LENGTH ] ;
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
{
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 ;
2022-04-08 17:43:48 +00:00
const char * 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
{
2022-04-08 17:43:48 +00:00
const char * pError = DemoPlayer_Play ( pFilename , StorageType ) ;
2020-01-04 09:44:12 +00:00
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 )
{
IEngineTextRender * pTextRender = Kernel ( ) - > RequestInterface < IEngineTextRender > ( ) ;
pDefaultFont = pTextRender - > GetFont ( aFilename ) ;
if ( pDefaultFont = = NULL )
2022-06-14 18:36:28 +00:00
{
void * pBuf ;
unsigned Size ;
io_read_all ( File , & pBuf , & Size ) ;
pDefaultFont = pTextRender - > LoadFont ( aFilename , ( unsigned char * ) pBuf , Size ) ;
}
io_close ( File ) ;
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-06-14 18:36:28 +00:00
void * pBuf ;
unsigned Size ;
io_read_all ( File , & pBuf , & Size ) ;
2021-06-04 13:15:29 +00:00
io_close ( File ) ;
2022-06-14 18:36:28 +00:00
FontLoaded = pTextRender - > LoadFallbackFont ( pDefaultFont , aFilename , ( unsigned char * ) pBuf , Size ) ;
2021-06-04 13:15:29 +00:00
}
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
}
2022-06-14 18:36:28 +00:00
pTextRender - > SetDefaultFont ( pDefaultFont ) ;
2019-03-28 20:51:42 +00:00
}
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 ;
}
2022-06-13 20:33:39 +00:00
void CClient : : ConchainLoglevel ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
{
pfnCallback ( pResult , pCallbackUserData ) ;
log_set_loglevel ( ( LEVEL ) g_Config . m_Loglevel ) ;
}
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 " ) ;
2022-06-08 21:48:30 +00:00
m_pConsole - > Register ( " bans " , " ?i[page] " , CFGFLAG_SERVER , 0 , 0 , " Show banlist (page 0 by default, 20 entries per page) " ) ;
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
2022-06-13 20:33:39 +00:00
m_pConsole - > Chain ( " loglevel " , ConchainLoglevel , this ) ;
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
2022-06-13 16:07:29 +00:00
CCmdlineFix CmdlineFix ( & 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
}
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
2022-06-15 17:34:41 +00:00
std : : vector < std : : shared_ptr < ILogger > > vpLoggers ;
2022-04-22 23:04:48 +00:00
# if defined(CONF_PLATFORM_ANDROID)
2022-06-15 17:34:41 +00:00
vpLoggers . push_back ( std : : shared_ptr < ILogger > ( log_logger_android ( ) ) ) ;
2022-04-22 23:04:48 +00:00
# else
if ( ! Silent )
{
2022-06-15 17:34:41 +00:00
vpLoggers . push_back ( std : : shared_ptr < ILogger > ( log_logger_stdout ( ) ) ) ;
2022-04-22 23:04:48 +00:00
}
# endif
std : : shared_ptr < CFutureLogger > pFutureFileLogger = std : : make_shared < CFutureLogger > ( ) ;
2022-06-15 17:34:41 +00:00
vpLoggers . push_back ( pFutureFileLogger ) ;
2022-04-22 23:04:48 +00:00
std : : shared_ptr < CFutureLogger > pFutureConsoleLogger = std : : make_shared < CFutureLogger > ( ) ;
2022-06-15 17:34:41 +00:00
vpLoggers . push_back ( pFutureConsoleLogger ) ;
2022-04-22 23:04:48 +00:00
std : : shared_ptr < CFutureLogger > pFutureAssertionLogger = std : : make_shared < CFutureLogger > ( ) ;
2022-06-15 17:34:41 +00:00
vpLoggers . push_back ( pFutureAssertionLogger ) ;
log_set_global_logger ( log_logger_collection ( std : : move ( vpLoggers ) ) . release ( ) ) ;
2022-04-22 23:04:48 +00:00
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
2022-04-22 23:04:48 +00:00
IEngine * pEngine = CreateEngine ( GAME_NAME , pFutureConsoleLogger , 2 ) ;
2010-06-18 18:32:52 +00:00
IConsole * pConsole = CreateConsole ( CFGFLAG_CLIENT ) ;
2021-12-21 16:19:42 +00:00
IStorage * pStorage = CreateStorage ( 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-04-22 23:04:48 +00:00
pFutureAssertionLogger - > Set ( CreateAssertionLogger ( pStorage , GAME_NAME ) ) ;
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
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
}
2022-06-13 20:33:39 +00:00
log_set_loglevel ( ( LEVEL ) g_Config . m_Loglevel ) ;
2022-04-22 23:04:48 +00:00
if ( g_Config . m_Logfile [ 0 ] )
{
IOHANDLE Logfile = io_open ( g_Config . m_Logfile , IOFLAG_WRITE ) ;
if ( Logfile )
{
pFutureFileLogger - > Set ( log_logger_file ( Logfile ) ) ;
}
else
{
dbg_msg ( " client " , " failed to open '%s' for logging " , g_Config . m_Logfile ) ;
}
}
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
# 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
2022-05-05 12:49:25 +00:00
m_pDDNetInfoTask = HttpGetFile ( aUrl , Storage ( ) , m_aDDNetInfoTmp , IStorage : : TYPE_SAVE ) ;
2022-05-28 22:13:59 +00:00
m_pDDNetInfoTask - > Timeout ( CTimeout { 10000 , 0 , 500 , 10 } ) ;
2022-05-05 12:49:25 +00:00
m_pDDNetInfoTask - > IpResolve ( 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 ( )
{
2022-06-11 19:38:18 +00:00
if ( m_vWarnings . empty ( ) )
2020-10-02 13:43:52 +00:00
{
return NULL ;
}
2022-06-11 19:38:18 +00:00
else if ( m_vWarnings [ 0 ] . m_WasShown )
2020-10-02 13:43:52 +00:00
{
2022-06-11 19:38:18 +00:00
m_vWarnings . erase ( m_vWarnings . begin ( ) ) ;
2020-10-02 13:43:52 +00:00
return NULL ;
}
else
{
2022-06-11 19:38:18 +00:00
return & m_vWarnings [ 0 ] ;
2020-10-02 13:43:52 +00:00
}
}
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
}
2022-05-15 19:59:17 +00:00
int CClient : : UdpConnectivity ( int NetType )
{
NETADDR GlobalUdpAddr ;
CONNECTIVITY Connectivity = m_NetClient - > GetConnectivity ( NetType , & GlobalUdpAddr ) ;
GlobalUdpAddr . port = 0 ;
switch ( Connectivity )
{
case CONNECTIVITY : : UNKNOWN :
return CONNECTIVITY_UNKNOWN ;
case CONNECTIVITY : : CHECKING :
return CONNECTIVITY_CHECKING ;
case CONNECTIVITY : : UNREACHABLE :
return CONNECTIVITY_UNREACHABLE ;
case CONNECTIVITY : : REACHABLE :
return CONNECTIVITY_REACHABLE ;
case CONNECTIVITY : : ADDRESS_KNOWN :
if ( m_HaveGlobalTcpAddr & & NetType = = ( int ) m_GlobalTcpAddr . type & & net_addr_comp ( & m_GlobalTcpAddr , & GlobalUdpAddr ) ! = 0 )
{
return CONNECTIVITY_DIFFERING_UDP_TCP_IP_ADDRESSES ;
}
return CONNECTIVITY_REACHABLE ;
default :
dbg_assert ( 0 , " invalid connectivity value " ) ;
return CONNECTIVITY_UNKNOWN ;
}
}